apienvelope 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/dist/index.js CHANGED
@@ -1,1553 +1,2 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res) => function __init() {
7
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
- };
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
- var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
- }
19
- return to;
20
- };
21
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
-
23
- // src/errors/ApiError.ts
24
- var ApiError;
25
- var init_ApiError = __esm({
26
- "src/errors/ApiError.ts"() {
27
- "use strict";
28
- ApiError = class _ApiError extends Error {
29
- code;
30
- statusCode;
31
- details;
32
- fields;
33
- context;
34
- isOperational;
35
- timestamp;
36
- constructor(message, options = {}) {
37
- super(message);
38
- this.name = this.constructor.name;
39
- this.code = options.code || "INTERNAL_ERROR";
40
- this.statusCode = options.statusCode || 500;
41
- this.details = options.details;
42
- this.fields = options.fields;
43
- this.context = options.context;
44
- this.isOperational = options.isOperational ?? true;
45
- this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
46
- Error.captureStackTrace(this, this.constructor);
47
- if (options.cause) {
48
- this.cause = options.cause;
49
- }
50
- }
51
- /**
52
- * Serialize error for response
53
- */
54
- serialize(includeStack = false) {
55
- const serialized = {
56
- code: this.code,
57
- message: this.message,
58
- statusCode: this.statusCode
59
- };
60
- if (this.details) {
61
- serialized.details = this.details;
62
- }
63
- if (this.fields) {
64
- serialized.fields = this.fields;
65
- }
66
- if (this.context) {
67
- serialized.context = this.context;
68
- }
69
- if (includeStack && this.stack) {
70
- serialized.stack = this.stack;
71
- }
72
- return serialized;
73
- }
74
- /**
75
- * Get error chain for nested errors
76
- */
77
- getErrorChain() {
78
- const chain = [];
79
- let current = this;
80
- const seen = /* @__PURE__ */ new WeakSet();
81
- while (current && !seen.has(current)) {
82
- seen.add(current);
83
- chain.push({
84
- name: current.name,
85
- message: current.message,
86
- code: current instanceof _ApiError ? current.code : void 0
87
- });
88
- current = current.cause;
89
- }
90
- return chain;
91
- }
92
- /**
93
- * Create a new error with additional context
94
- */
95
- withContext(context) {
96
- return new _ApiError(this.message, {
97
- code: this.code,
98
- statusCode: this.statusCode,
99
- details: this.details,
100
- fields: this.fields,
101
- context: { ...this.context, ...context },
102
- cause: this.cause,
103
- isOperational: this.isOperational
104
- });
105
- }
106
- /**
107
- * Convert to JSON for logging
108
- */
109
- toJSON() {
110
- return {
111
- name: this.name,
112
- message: this.message,
113
- code: this.code,
114
- statusCode: this.statusCode,
115
- details: this.details,
116
- fields: this.fields,
117
- context: this.context,
118
- isOperational: this.isOperational,
119
- timestamp: this.timestamp,
120
- stack: this.stack
121
- };
122
- }
123
- };
124
- }
125
- });
126
-
127
- // src/errors/predefined.ts
128
- var predefined_exports = {};
129
- __export(predefined_exports, {
130
- BadRequestError: () => BadRequestError,
131
- ConflictError: () => ConflictError,
132
- ForbiddenError: () => ForbiddenError,
133
- InternalServerError: () => InternalServerError,
134
- NotFoundError: () => NotFoundError,
135
- RateLimitError: () => RateLimitError,
136
- ServiceUnavailableError: () => ServiceUnavailableError,
137
- UnauthorizedError: () => UnauthorizedError,
138
- UnprocessableEntityError: () => UnprocessableEntityError,
139
- ValidationError: () => ValidationError,
140
- createError: () => createError
141
- });
142
- function createError(ErrorClass, message, context) {
143
- const error = new ErrorClass(message);
144
- return context ? error.withContext(context) : error;
145
- }
146
- var ValidationError, NotFoundError, UnauthorizedError, ForbiddenError, ConflictError, InternalServerError, BadRequestError, RateLimitError, ServiceUnavailableError, UnprocessableEntityError;
147
- var init_predefined = __esm({
148
- "src/errors/predefined.ts"() {
149
- "use strict";
150
- init_ApiError();
151
- ValidationError = class extends ApiError {
152
- constructor(message = "Validation failed", fields, options = {}) {
153
- super(message, {
154
- ...options,
155
- code: "VALIDATION_ERROR",
156
- statusCode: 400,
157
- fields
158
- });
159
- }
160
- };
161
- NotFoundError = class extends ApiError {
162
- constructor(message = "Resource not found", options = {}) {
163
- super(message, {
164
- ...options,
165
- code: "NOT_FOUND",
166
- statusCode: 404
167
- });
168
- }
169
- };
170
- UnauthorizedError = class extends ApiError {
171
- constructor(message = "Authentication required", options = {}) {
172
- super(message, {
173
- ...options,
174
- code: "UNAUTHORIZED",
175
- statusCode: 401
176
- });
177
- }
178
- };
179
- ForbiddenError = class extends ApiError {
180
- constructor(message = "Access forbidden", options = {}) {
181
- super(message, {
182
- ...options,
183
- code: "FORBIDDEN",
184
- statusCode: 403
185
- });
186
- }
187
- };
188
- ConflictError = class extends ApiError {
189
- constructor(message = "Resource conflict", options = {}) {
190
- super(message, {
191
- ...options,
192
- code: "CONFLICT",
193
- statusCode: 409
194
- });
195
- }
196
- };
197
- InternalServerError = class extends ApiError {
198
- constructor(message = "Internal server error", options = {}) {
199
- super(message, {
200
- ...options,
201
- code: "INTERNAL_ERROR",
202
- statusCode: 500,
203
- isOperational: false
204
- });
205
- }
206
- };
207
- BadRequestError = class extends ApiError {
208
- constructor(message = "Bad request", options = {}) {
209
- super(message, {
210
- ...options,
211
- code: "BAD_REQUEST",
212
- statusCode: 400
213
- });
214
- }
215
- };
216
- RateLimitError = class extends ApiError {
217
- retryAfter;
218
- constructor(message = "Rate limit exceeded", retryAfter, options = {}) {
219
- super(message, {
220
- ...options,
221
- code: "RATE_LIMIT_EXCEEDED",
222
- statusCode: 429,
223
- details: retryAfter ? { retryAfter } : void 0
224
- });
225
- this.retryAfter = retryAfter;
226
- }
227
- };
228
- ServiceUnavailableError = class extends ApiError {
229
- constructor(message = "Service temporarily unavailable", options = {}) {
230
- super(message, {
231
- ...options,
232
- code: "SERVICE_UNAVAILABLE",
233
- statusCode: 503
234
- });
235
- }
236
- };
237
- UnprocessableEntityError = class extends ApiError {
238
- constructor(message = "Unprocessable entity", options = {}) {
239
- super(message, {
240
- ...options,
241
- code: "UNPROCESSABLE_ENTITY",
242
- statusCode: 422
243
- });
244
- }
245
- };
246
- }
247
- });
248
-
249
- // src/index.ts
250
- var index_exports = {};
251
- __export(index_exports, {
252
- ApiError: () => ApiError,
253
- ApiRoute: () => ApiRoute,
254
- BadRequestError: () => BadRequestError,
255
- ConflictError: () => ConflictError,
256
- CursorPaginate: () => CursorPaginate,
257
- ErrorCodes: () => ErrorCodes,
258
- ErrorHandler: () => ErrorHandler,
259
- ForbiddenError: () => ForbiddenError,
260
- HandleErrors: () => HandleErrors,
261
- InternalServerError: () => InternalServerError,
262
- NotFoundError: () => NotFoundError,
263
- Paginate: () => Paginate,
264
- PaginationHelper: () => PaginationHelper,
265
- RateLimitError: () => RateLimitError,
266
- ResponseFormatter: () => ResponseFormatter,
267
- ServiceUnavailableError: () => ServiceUnavailableError,
268
- StatusCodeMapper: () => StatusCodeMapper,
269
- UnauthorizedError: () => UnauthorizedError,
270
- UnprocessableEntityError: () => UnprocessableEntityError,
271
- Validate: () => Validate,
272
- ValidationError: () => ValidationError,
273
- asyncHandler: () => asyncHandler,
274
- calculateCursorPaginationMeta: () => calculateCursorPaginationMeta,
275
- calculatePaginationMeta: () => calculatePaginationMeta,
276
- catchErrors: () => catchErrors,
277
- createError: () => createError,
278
- createErrorCatcher: () => createErrorCatcher,
279
- createErrorHandler: () => createErrorHandler,
280
- createErrorSerializer: () => createErrorSerializer,
281
- createPaginationHelper: () => createPaginationHelper,
282
- createResponseDecorator: () => createResponseDecorator,
283
- createResponseFormatter: () => createResponseFormatter,
284
- createResponseWrapper: () => createResponseWrapper,
285
- createStatusCodeMapper: () => createStatusCodeMapper,
286
- default: () => index_default,
287
- defaultConfig: () => defaultConfig,
288
- errorCatcher: () => errorCatcher,
289
- errorHandlerMiddleware: () => errorHandlerMiddleware,
290
- extractErrorContext: () => extractErrorContext,
291
- extractFieldErrors: () => extractFieldErrors,
292
- generateCursorPaginationLinks: () => generateCursorPaginationLinks,
293
- generatePaginationLinks: () => generatePaginationLinks,
294
- generateRequestId: () => generateRequestId,
295
- isErrorResponse: () => isErrorResponse,
296
- isPaginatedResponse: () => isPaginatedResponse,
297
- isPlainObject: () => isPlainObject,
298
- isSuccessResponse: () => isSuccessResponse,
299
- responseFormatter: () => responseFormatter,
300
- responseWrapper: () => responseWrapper,
301
- serializeError: () => serializeError,
302
- validateConfig: () => validateConfig,
303
- validatePaginationInput: () => validatePaginationInput
304
- });
305
- module.exports = __toCommonJS(index_exports);
306
-
307
- // src/types/config.ts
308
- var defaultConfig = {
309
- includeStackTraces: false,
310
- environment: "production",
311
- includeTimestamp: true,
312
- maskSensitiveData: true,
313
- requestIdHeader: "x-request-id",
314
- correlationIdHeader: "x-correlation-id",
315
- pagination: {
316
- defaultLimit: 10,
317
- maxLimit: 100,
318
- includeLinks: false
319
- },
320
- preResponseHooks: [],
321
- postResponseHooks: [],
322
- passthroughMode: false,
323
- sensitiveFields: ["password", "token", "secret", "apiKey", "authorization"],
324
- generateRequestId: true
325
- };
326
-
327
- // src/utils/errorSerializer.ts
328
- init_ApiError();
329
- var defaultOptions = {
330
- includeStack: false,
331
- maskSensitiveData: true,
332
- sensitiveFields: ["password", "token", "secret", "apiKey", "authorization"]
333
- };
334
- function maskSensitiveFields(obj, sensitiveFields) {
335
- const masked = {};
336
- const lowerSensitiveFields = sensitiveFields.map((f) => f.toLowerCase());
337
- for (const [key, value] of Object.entries(obj)) {
338
- if (lowerSensitiveFields.includes(key.toLowerCase())) {
339
- masked[key] = "[REDACTED]";
340
- } else if (value && typeof value === "object" && !Array.isArray(value)) {
341
- masked[key] = maskSensitiveFields(value, sensitiveFields);
342
- } else {
343
- masked[key] = value;
344
- }
345
- }
346
- return masked;
347
- }
348
- function safeSerializeDetails(details, seen = /* @__PURE__ */ new WeakSet()) {
349
- if (seen.has(details)) {
350
- return { _circular: true };
351
- }
352
- seen.add(details);
353
- const result = {};
354
- for (const [key, value] of Object.entries(details)) {
355
- if (value === null || value === void 0) {
356
- result[key] = value;
357
- } else if (typeof value === "object") {
358
- if (value instanceof Error) {
359
- result[key] = {
360
- name: value.name,
361
- message: value.message
362
- };
363
- } else if (Array.isArray(value)) {
364
- result[key] = value.map(
365
- (item) => typeof item === "object" && item !== null ? safeSerializeDetails(item, seen) : item
366
- );
367
- } else {
368
- result[key] = safeSerializeDetails(value, seen);
369
- }
370
- } else {
371
- result[key] = value;
372
- }
373
- }
374
- return result;
375
- }
376
- function serializeError(error, options = {}) {
377
- const opts = { ...defaultOptions, ...options };
378
- if (opts.customSerializers) {
379
- for (const [ErrorClass, serializer] of opts.customSerializers) {
380
- if (error instanceof ErrorClass) {
381
- const customResult = serializer(error);
382
- return {
383
- code: customResult.code || "CUSTOM_ERROR",
384
- message: customResult.message || error.message,
385
- details: opts.maskSensitiveData ? maskSensitiveFields(customResult, opts.sensitiveFields) : customResult
386
- };
387
- }
388
- }
389
- }
390
- if (error instanceof ApiError) {
391
- const serialized = error.serialize(opts.includeStack);
392
- let details = serialized.details;
393
- if (details && opts.maskSensitiveData) {
394
- details = maskSensitiveFields(
395
- safeSerializeDetails(details),
396
- opts.sensitiveFields
397
- );
398
- }
399
- const result2 = {
400
- code: serialized.code,
401
- message: serialized.message
402
- };
403
- if (details && Object.keys(details).length > 0) {
404
- result2.details = details;
405
- }
406
- if (serialized.fields) {
407
- result2.fields = serialized.fields;
408
- }
409
- if (opts.includeStack && serialized.stack) {
410
- result2.stack = serialized.stack;
411
- }
412
- return result2;
413
- }
414
- const result = {
415
- code: "INTERNAL_ERROR",
416
- message: error.message || "An unexpected error occurred"
417
- };
418
- if (opts.includeStack && error.stack) {
419
- result.stack = error.stack;
420
- }
421
- return result;
422
- }
423
- function extractFieldErrors(error) {
424
- if (error instanceof ApiError && error.fields) {
425
- return error.fields;
426
- }
427
- return void 0;
428
- }
429
- function extractErrorContext(error) {
430
- if (error instanceof ApiError && error.context) {
431
- return error.context;
432
- }
433
- return void 0;
434
- }
435
- function createErrorSerializer(options = {}) {
436
- const opts = { ...defaultOptions, ...options };
437
- return (error) => serializeError(error, opts);
438
- }
439
-
440
- // src/utils/statusCodeMapper.ts
441
- init_ApiError();
442
- init_predefined();
443
- var defaultErrorMappings = /* @__PURE__ */ new Map([
444
- [ValidationError, 400],
445
- [BadRequestError, 400],
446
- [UnauthorizedError, 401],
447
- [ForbiddenError, 403],
448
- [NotFoundError, 404],
449
- [ConflictError, 409],
450
- [UnprocessableEntityError, 422],
451
- [RateLimitError, 429],
452
- [InternalServerError, 500],
453
- [ServiceUnavailableError, 503]
454
- ]);
455
- var methodStatusCodes = {
456
- GET: 200,
457
- POST: 201,
458
- PUT: 200,
459
- PATCH: 200,
460
- DELETE: 204,
461
- HEAD: 200,
462
- OPTIONS: 200
463
- };
464
- var StatusCodeMapper = class {
465
- customMappings;
466
- constructor(customMappings) {
467
- this.customMappings = customMappings || /* @__PURE__ */ new Map();
468
- }
469
- /**
470
- * Get status code for an error
471
- */
472
- getStatusCode(error) {
473
- if (error instanceof ApiError) {
474
- return error.statusCode;
475
- }
476
- for (const [ErrorClass, statusCode] of this.customMappings) {
477
- if (error instanceof ErrorClass) {
478
- return statusCode;
479
- }
480
- }
481
- for (const [ErrorClass, statusCode] of defaultErrorMappings) {
482
- if (error instanceof ErrorClass) {
483
- return statusCode;
484
- }
485
- }
486
- return 500;
487
- }
488
- /**
489
- * Get success status code based on HTTP method
490
- */
491
- getSuccessStatusCode(method, hasData) {
492
- const upperMethod = method.toUpperCase();
493
- if (upperMethod === "DELETE" && !hasData) {
494
- return 204;
495
- }
496
- return methodStatusCodes[upperMethod] || 200;
497
- }
498
- /**
499
- * Add custom error mapping
500
- */
501
- addMapping(ErrorClass, statusCode) {
502
- this.customMappings.set(ErrorClass, statusCode);
503
- }
504
- /**
505
- * Remove custom error mapping
506
- */
507
- removeMapping(ErrorClass) {
508
- return this.customMappings.delete(ErrorClass);
509
- }
510
- /**
511
- * Check if status code indicates success
512
- */
513
- static isSuccessCode(statusCode) {
514
- return statusCode >= 200 && statusCode < 300;
515
- }
516
- /**
517
- * Check if status code indicates client error
518
- */
519
- static isClientError(statusCode) {
520
- return statusCode >= 400 && statusCode < 500;
521
- }
522
- /**
523
- * Check if status code indicates server error
524
- */
525
- static isServerError(statusCode) {
526
- return statusCode >= 500 && statusCode < 600;
527
- }
528
- };
529
- function createStatusCodeMapper(customMappings) {
530
- return new StatusCodeMapper(customMappings);
531
- }
532
-
533
- // src/utils/validators.ts
534
- function validatePaginationInput(input, config) {
535
- const page = Math.max(1, Math.floor(Number(input.page) || 1));
536
- const limit = Math.min(
537
- config.maxLimit,
538
- Math.max(1, Math.floor(Number(input.limit) || config.defaultLimit))
539
- );
540
- const total = Math.max(0, Math.floor(Number(input.total) || 0));
541
- return { page, limit, total };
542
- }
543
- function calculatePaginationMeta(input, baseUrl) {
544
- const { page, limit, total } = input;
545
- const totalPages = Math.ceil(total / limit) || 1;
546
- const hasNextPage = page < totalPages;
547
- const hasPreviousPage = page > 1;
548
- const meta = {
549
- page,
550
- limit,
551
- total,
552
- totalPages,
553
- hasNextPage,
554
- hasPreviousPage
555
- };
556
- if (baseUrl) {
557
- meta.links = generatePaginationLinks(page, totalPages, limit, baseUrl);
558
- }
559
- return meta;
560
- }
561
- function generatePaginationLinks(page, totalPages, limit, baseUrl) {
562
- const url = new URL(baseUrl);
563
- const createLink = (p) => {
564
- url.searchParams.set("page", String(p));
565
- url.searchParams.set("limit", String(limit));
566
- return url.toString();
567
- };
568
- const links = {
569
- self: createLink(page),
570
- first: createLink(1),
571
- last: createLink(totalPages)
572
- };
573
- if (page < totalPages) {
574
- links.next = createLink(page + 1);
575
- }
576
- if (page > 1) {
577
- links.previous = createLink(page - 1);
578
- }
579
- return links;
580
- }
581
- function calculateCursorPaginationMeta(input, baseUrl) {
582
- const meta = {
583
- limit: input.limit,
584
- hasMore: input.hasMore
585
- };
586
- if (input.cursor) {
587
- meta.cursor = input.cursor;
588
- }
589
- if (input.nextCursor) {
590
- meta.nextCursor = input.nextCursor;
591
- }
592
- if (input.previousCursor) {
593
- meta.previousCursor = input.previousCursor;
594
- }
595
- if (baseUrl) {
596
- meta.links = generateCursorPaginationLinks(input, baseUrl);
597
- }
598
- return meta;
599
- }
600
- function generateCursorPaginationLinks(input, baseUrl) {
601
- const url = new URL(baseUrl);
602
- const createLink = (cursor) => {
603
- if (cursor) {
604
- url.searchParams.set("cursor", cursor);
605
- } else {
606
- url.searchParams.delete("cursor");
607
- }
608
- url.searchParams.set("limit", String(input.limit));
609
- return url.toString();
610
- };
611
- const links = {
612
- self: createLink(input.cursor)
613
- };
614
- if (input.nextCursor && input.hasMore) {
615
- links.next = createLink(input.nextCursor);
616
- }
617
- if (input.previousCursor) {
618
- links.previous = createLink(input.previousCursor);
619
- }
620
- return links;
621
- }
622
- function validateConfig(input) {
623
- const errors = [];
624
- if (input.environment && !["development", "staging", "production", "test"].includes(input.environment)) {
625
- errors.push(`Invalid environment: ${input.environment}`);
626
- }
627
- if (input.pagination) {
628
- if (input.pagination.defaultLimit !== void 0 && input.pagination.defaultLimit < 1) {
629
- errors.push("pagination.defaultLimit must be at least 1");
630
- }
631
- if (input.pagination.maxLimit !== void 0 && input.pagination.maxLimit < 1) {
632
- errors.push("pagination.maxLimit must be at least 1");
633
- }
634
- if (input.pagination.defaultLimit !== void 0 && input.pagination.maxLimit !== void 0 && input.pagination.defaultLimit > input.pagination.maxLimit) {
635
- errors.push("pagination.defaultLimit cannot exceed pagination.maxLimit");
636
- }
637
- }
638
- return errors;
639
- }
640
- function isPlainObject(value) {
641
- return typeof value === "object" && value !== null && !Array.isArray(value);
642
- }
643
- function generateRequestId() {
644
- const timestamp = Date.now().toString(36);
645
- const random = Math.random().toString(36).substring(2, 10);
646
- return `${timestamp}-${random}`;
647
- }
648
-
649
- // src/core/ResponseFormatter.ts
650
- var ResponseFormatter = class {
651
- config;
652
- statusCodeMapper;
653
- constructor(config = {}) {
654
- this.config = this.mergeConfig(config);
655
- this.statusCodeMapper = new StatusCodeMapper(this.config.customErrorMappers);
656
- }
657
- /**
658
- * Merge user config with defaults
659
- */
660
- mergeConfig(input) {
661
- return {
662
- ...defaultConfig,
663
- ...input,
664
- pagination: {
665
- ...defaultConfig.pagination,
666
- ...input.pagination
667
- },
668
- preResponseHooks: input.preResponseHooks || defaultConfig.preResponseHooks,
669
- postResponseHooks: input.postResponseHooks || defaultConfig.postResponseHooks,
670
- sensitiveFields: input.sensitiveFields || defaultConfig.sensitiveFields
671
- };
672
- }
673
- /**
674
- * Get current configuration
675
- */
676
- getConfig() {
677
- return { ...this.config };
678
- }
679
- /**
680
- * Update configuration
681
- */
682
- updateConfig(config) {
683
- this.config = this.mergeConfig({ ...this.config, ...config });
684
- if (config.customErrorMappers) {
685
- this.statusCodeMapper = new StatusCodeMapper(config.customErrorMappers);
686
- }
687
- }
688
- /**
689
- * Generate timestamp
690
- */
691
- getTimestamp() {
692
- return this.config.includeTimestamp ? (/* @__PURE__ */ new Date()).toISOString() : "";
693
- }
694
- /**
695
- * Apply pre-response hooks
696
- */
697
- applyPreHooks(data, meta) {
698
- let result = { data, meta };
699
- for (const hook of this.config.preResponseHooks) {
700
- const hookResult = hook(result.data, result.meta);
701
- result = { data: hookResult.data, meta: hookResult.meta };
702
- }
703
- return result;
704
- }
705
- /**
706
- * Apply post-response hooks
707
- */
708
- applyPostHooks(response) {
709
- let result = response;
710
- for (const hook of this.config.postResponseHooks) {
711
- result = hook(result);
712
- }
713
- return result;
714
- }
715
- /**
716
- * Format a success response
717
- */
718
- formatSuccess(data, meta) {
719
- const processed = this.applyPreHooks(data, meta);
720
- const response = {
721
- success: true,
722
- data: processed.data,
723
- timestamp: this.getTimestamp()
724
- };
725
- if (processed.meta && Object.keys(processed.meta).length > 0) {
726
- response.meta = processed.meta;
727
- }
728
- if (this.config.passthroughMode && this.config.customFormatter) {
729
- return this.config.customFormatter(response);
730
- }
731
- return this.applyPostHooks(response);
732
- }
733
- /**
734
- * Format an error response
735
- */
736
- formatError(error, meta) {
737
- const includeStack = this.config.includeStackTraces || this.config.environment === "development";
738
- const errorDetails = serializeError(error, {
739
- includeStack,
740
- maskSensitiveData: this.config.maskSensitiveData,
741
- sensitiveFields: this.config.sensitiveFields,
742
- customSerializers: this.config.customSerializers
743
- });
744
- const response = {
745
- success: false,
746
- error: errorDetails,
747
- timestamp: this.getTimestamp()
748
- };
749
- if (meta && Object.keys(meta).length > 0) {
750
- response.meta = meta;
751
- }
752
- if (this.config.passthroughMode && this.config.customFormatter) {
753
- return this.config.customFormatter(response);
754
- }
755
- return this.applyPostHooks(response);
756
- }
757
- /**
758
- * Format a paginated response (offset-based)
759
- */
760
- formatPaginated(data, paginationInput, meta, baseUrl) {
761
- const processed = this.applyPreHooks(data, meta);
762
- const validatedInput = validatePaginationInput(paginationInput, this.config.pagination);
763
- const paginationMeta = calculatePaginationMeta(
764
- validatedInput,
765
- this.config.pagination.includeLinks ? baseUrl : void 0
766
- );
767
- const response = {
768
- success: true,
769
- data: processed.data,
770
- pagination: paginationMeta,
771
- timestamp: this.getTimestamp()
772
- };
773
- if (processed.meta && Object.keys(processed.meta).length > 0) {
774
- response.meta = processed.meta;
775
- }
776
- return this.applyPostHooks(response);
777
- }
778
- /**
779
- * Format a cursor-paginated response
780
- */
781
- formatCursorPaginated(data, cursorInput, meta, baseUrl) {
782
- const processed = this.applyPreHooks(data, meta);
783
- const paginationMeta = calculateCursorPaginationMeta(
784
- cursorInput,
785
- this.config.pagination.includeLinks ? baseUrl : void 0
786
- );
787
- const response = {
788
- success: true,
789
- data: processed.data,
790
- pagination: paginationMeta,
791
- timestamp: this.getTimestamp()
792
- };
793
- if (processed.meta && Object.keys(processed.meta).length > 0) {
794
- response.meta = processed.meta;
795
- }
796
- return this.applyPostHooks(response);
797
- }
798
- /**
799
- * Get status code for an error
800
- */
801
- getErrorStatusCode(error) {
802
- return this.statusCodeMapper.getStatusCode(error);
803
- }
804
- /**
805
- * Get success status code based on HTTP method
806
- */
807
- getSuccessStatusCode(method, hasData) {
808
- return this.statusCodeMapper.getSuccessStatusCode(method, hasData);
809
- }
810
- /**
811
- * Generate or extract request ID
812
- */
813
- getRequestId(existingId) {
814
- if (existingId) {
815
- return existingId;
816
- }
817
- return this.config.generateRequestId ? generateRequestId() : "";
818
- }
819
- };
820
- function createResponseFormatter(config) {
821
- return new ResponseFormatter(config);
822
- }
823
-
824
- // src/core/ErrorHandler.ts
825
- init_ApiError();
826
- var defaultErrorHandlerOptions = {
827
- logErrors: true
828
- };
829
- var ErrorHandler = class {
830
- formatter;
831
- options;
832
- config;
833
- constructor(formatter, options = {}) {
834
- this.formatter = formatter;
835
- this.options = { ...defaultErrorHandlerOptions, ...options };
836
- this.config = formatter.getConfig();
837
- }
838
- /**
839
- * Extract request metadata for error context
840
- */
841
- extractMeta(req) {
842
- const meta = {};
843
- const requestId = req.get(this.config.requestIdHeader);
844
- if (requestId) {
845
- meta.requestId = requestId;
846
- }
847
- const correlationId = req.get(this.config.correlationIdHeader);
848
- if (correlationId) {
849
- meta.correlationId = correlationId;
850
- }
851
- return meta;
852
- }
853
- /**
854
- * Log error if enabled
855
- */
856
- logError(error, req) {
857
- if (!this.options.logErrors) return;
858
- if (this.options.errorLogger) {
859
- this.options.errorLogger(error, req);
860
- return;
861
- }
862
- const isOperational = error instanceof ApiError ? error.isOperational : false;
863
- const logLevel = isOperational ? "warn" : "error";
864
- console[logLevel]({
865
- message: error.message,
866
- name: error.name,
867
- stack: error.stack,
868
- path: req.path,
869
- method: req.method,
870
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
871
- });
872
- }
873
- /**
874
- * Check if error is operational (expected)
875
- */
876
- isOperationalError(error) {
877
- if (error instanceof ApiError) {
878
- return error.isOperational;
879
- }
880
- return false;
881
- }
882
- /**
883
- * Handle the error and send response
884
- */
885
- handle(error, req, res, _next) {
886
- this.logError(error, req);
887
- const transformedError = this.options.transformError ? this.options.transformError(error) : error;
888
- if (!this.isOperationalError(transformedError) && this.options.handleNonOperational) {
889
- this.options.handleNonOperational(transformedError, req, res);
890
- return;
891
- }
892
- const statusCode = this.formatter.getErrorStatusCode(transformedError);
893
- const meta = this.extractMeta(req);
894
- const response = this.formatter.formatError(transformedError, meta);
895
- res.status(statusCode).json(response);
896
- }
897
- /**
898
- * Create Express error handling middleware
899
- */
900
- middleware() {
901
- return (error, req, res, next) => {
902
- this.handle(error, req, res, next);
903
- };
904
- }
905
- };
906
- function createErrorHandler(formatter, options) {
907
- return new ErrorHandler(formatter, options);
908
- }
909
- function errorHandlerMiddleware(formatter, options) {
910
- const handler = new ErrorHandler(formatter, options);
911
- return handler.middleware();
912
- }
913
-
914
- // src/core/PaginationHelper.ts
915
- var PaginationHelper = class {
916
- config;
917
- constructor(config = {}) {
918
- this.config = {
919
- defaultLimit: config.defaultLimit ?? 10,
920
- maxLimit: config.maxLimit ?? 100,
921
- includeLinks: config.includeLinks ?? false,
922
- baseUrl: config.baseUrl
923
- };
924
- }
925
- /**
926
- * Extract pagination parameters from request
927
- */
928
- extractFromRequest(req) {
929
- const page = Math.max(1, parseInt(req.query.page, 10) || 1);
930
- const limit = Math.min(
931
- this.config.maxLimit,
932
- Math.max(1, parseInt(req.query.limit, 10) || this.config.defaultLimit)
933
- );
934
- return { page, limit };
935
- }
936
- /**
937
- * Extract cursor pagination parameters from request
938
- */
939
- extractCursorFromRequest(req) {
940
- const cursor = req.query.cursor;
941
- const limit = Math.min(
942
- this.config.maxLimit,
943
- Math.max(1, parseInt(req.query.limit, 10) || this.config.defaultLimit)
944
- );
945
- return { cursor, limit };
946
- }
947
- /**
948
- * Calculate offset for database queries
949
- */
950
- calculateOffset(page, limit) {
951
- return (page - 1) * limit;
952
- }
953
- /**
954
- * Build pagination metadata
955
- */
956
- buildMeta(input, baseUrl) {
957
- const validated = validatePaginationInput(input, this.config);
958
- return calculatePaginationMeta(
959
- validated,
960
- this.config.includeLinks ? baseUrl || this.config.baseUrl : void 0
961
- );
962
- }
963
- /**
964
- * Build cursor pagination metadata
965
- */
966
- buildCursorMeta(input, baseUrl) {
967
- return calculateCursorPaginationMeta(
968
- input,
969
- this.config.includeLinks ? baseUrl || this.config.baseUrl : void 0
970
- );
971
- }
972
- /**
973
- * Create pagination info from data array and total count
974
- */
975
- fromArray(data, page, limit, total) {
976
- const pagination = this.buildMeta({ page, limit, total });
977
- return { data, pagination };
978
- }
979
- /**
980
- * Paginate an in-memory array
981
- */
982
- paginateArray(items, page, limit) {
983
- const total = items.length;
984
- const offset = this.calculateOffset(page, limit);
985
- const data = items.slice(offset, offset + limit);
986
- return this.fromArray(data, page, limit, total);
987
- }
988
- /**
989
- * Check if there are more pages
990
- */
991
- hasNextPage(page, limit, total) {
992
- const totalPages = Math.ceil(total / limit);
993
- return page < totalPages;
994
- }
995
- /**
996
- * Check if there are previous pages
997
- */
998
- hasPreviousPage(page) {
999
- return page > 1;
1000
- }
1001
- /**
1002
- * Get total pages count
1003
- */
1004
- getTotalPages(total, limit) {
1005
- return Math.ceil(total / limit) || 1;
1006
- }
1007
- /**
1008
- * Validate page number is within bounds
1009
- */
1010
- isValidPage(page, total, limit) {
1011
- if (page < 1) return false;
1012
- if (total === 0) return page === 1;
1013
- const totalPages = this.getTotalPages(total, limit);
1014
- return page <= totalPages;
1015
- }
1016
- /**
1017
- * Get configuration
1018
- */
1019
- getConfig() {
1020
- return { ...this.config };
1021
- }
1022
- /**
1023
- * Update configuration
1024
- */
1025
- updateConfig(config) {
1026
- this.config = { ...this.config, ...config };
1027
- }
1028
- };
1029
- function createPaginationHelper(config) {
1030
- return new PaginationHelper(config);
1031
- }
1032
-
1033
- // src/errors/index.ts
1034
- init_ApiError();
1035
- init_predefined();
1036
-
1037
- // src/middleware/responseWrapper.ts
1038
- function responseWrapper(options = {}) {
1039
- const { skipPaths = [], skipCondition, ...formatterConfig } = options;
1040
- const formatter = new ResponseFormatter(formatterConfig);
1041
- const config = formatter.getConfig();
1042
- return (req, res, next) => {
1043
- const formattedReq = req;
1044
- const formattedRes = res;
1045
- if (skipPaths.includes(req.path) || skipCondition && skipCondition(req)) {
1046
- next();
1047
- return;
1048
- }
1049
- let requestId = req.get(config.requestIdHeader);
1050
- if (!requestId && config.generateRequestId) {
1051
- requestId = generateRequestId();
1052
- }
1053
- if (requestId) {
1054
- formattedReq.requestId = requestId;
1055
- res.setHeader(config.requestIdHeader, requestId);
1056
- }
1057
- const correlationId = req.get(config.correlationIdHeader);
1058
- if (correlationId) {
1059
- formattedReq.correlationId = correlationId;
1060
- res.setHeader(config.correlationIdHeader, correlationId);
1061
- }
1062
- formattedRes.formatter = formatter;
1063
- const buildMeta = (additionalMeta) => {
1064
- const meta = {};
1065
- if (requestId) meta.requestId = requestId;
1066
- if (correlationId) meta.correlationId = correlationId;
1067
- return { ...meta, ...additionalMeta };
1068
- };
1069
- const getBaseUrl = () => {
1070
- const protocol = req.protocol;
1071
- const host = req.get("host") || "localhost";
1072
- return `${protocol}://${host}${req.originalUrl.split("?")[0]}`;
1073
- };
1074
- formattedRes.respond = (data, meta, statusCode) => {
1075
- const response = formatter.formatSuccess(data, buildMeta(meta));
1076
- const status = statusCode ?? formatter.getSuccessStatusCode(req.method, data !== void 0);
1077
- res.status(status).json(response);
1078
- };
1079
- formattedRes.respondPaginated = (data, pagination, meta) => {
1080
- const response = formatter.formatPaginated(
1081
- data,
1082
- pagination,
1083
- buildMeta(meta),
1084
- getBaseUrl()
1085
- );
1086
- res.status(200).json(response);
1087
- };
1088
- formattedRes.respondCursorPaginated = (data, pagination, meta) => {
1089
- const response = formatter.formatCursorPaginated(
1090
- data,
1091
- pagination,
1092
- buildMeta(meta),
1093
- getBaseUrl()
1094
- );
1095
- res.status(200).json(response);
1096
- };
1097
- formattedRes.respondError = (error, meta) => {
1098
- const response = formatter.formatError(error, buildMeta(meta));
1099
- const statusCode = formatter.getErrorStatusCode(error);
1100
- res.status(statusCode).json(response);
1101
- };
1102
- next();
1103
- };
1104
- }
1105
- function createResponseWrapper(formatter, options = {}) {
1106
- const { skipPaths = [], skipCondition } = options;
1107
- const config = formatter.getConfig();
1108
- return (req, res, next) => {
1109
- const formattedReq = req;
1110
- const formattedRes = res;
1111
- if (skipPaths.includes(req.path) || skipCondition && skipCondition(req)) {
1112
- next();
1113
- return;
1114
- }
1115
- let requestId = req.get(config.requestIdHeader);
1116
- if (!requestId && config.generateRequestId) {
1117
- requestId = generateRequestId();
1118
- }
1119
- if (requestId) {
1120
- formattedReq.requestId = requestId;
1121
- res.setHeader(config.requestIdHeader, requestId);
1122
- }
1123
- const correlationId = req.get(config.correlationIdHeader);
1124
- if (correlationId) {
1125
- formattedReq.correlationId = correlationId;
1126
- res.setHeader(config.correlationIdHeader, correlationId);
1127
- }
1128
- formattedRes.formatter = formatter;
1129
- const buildMeta = (additionalMeta) => {
1130
- const meta = {};
1131
- if (requestId) meta.requestId = requestId;
1132
- if (correlationId) meta.correlationId = correlationId;
1133
- return { ...meta, ...additionalMeta };
1134
- };
1135
- const getBaseUrl = () => {
1136
- const protocol = req.protocol;
1137
- const host = req.get("host") || "localhost";
1138
- return `${protocol}://${host}${req.originalUrl.split("?")[0]}`;
1139
- };
1140
- formattedRes.respond = (data, meta, statusCode) => {
1141
- const response = formatter.formatSuccess(data, buildMeta(meta));
1142
- const status = statusCode ?? formatter.getSuccessStatusCode(req.method, data !== void 0);
1143
- res.status(status).json(response);
1144
- };
1145
- formattedRes.respondPaginated = (data, pagination, meta) => {
1146
- const response = formatter.formatPaginated(data, pagination, buildMeta(meta), getBaseUrl());
1147
- res.status(200).json(response);
1148
- };
1149
- formattedRes.respondCursorPaginated = (data, pagination, meta) => {
1150
- const response = formatter.formatCursorPaginated(data, pagination, buildMeta(meta), getBaseUrl());
1151
- res.status(200).json(response);
1152
- };
1153
- formattedRes.respondError = (error, meta) => {
1154
- const response = formatter.formatError(error, buildMeta(meta));
1155
- const statusCode = formatter.getErrorStatusCode(error);
1156
- res.status(statusCode).json(response);
1157
- };
1158
- next();
1159
- };
1160
- }
1161
-
1162
- // src/middleware/errorCatcher.ts
1163
- function errorCatcher(options = {}) {
1164
- const {
1165
- logErrors = true,
1166
- errorLogger,
1167
- transformError,
1168
- customHandler,
1169
- ...formatterConfig
1170
- } = options;
1171
- const formatter = new ResponseFormatter(formatterConfig);
1172
- const config = formatter.getConfig();
1173
- return (error, req, res, _next) => {
1174
- if (customHandler && customHandler(error, req, res)) {
1175
- return;
1176
- }
1177
- if (logErrors) {
1178
- if (errorLogger) {
1179
- errorLogger(error, req);
1180
- } else {
1181
- console.error({
1182
- message: error.message,
1183
- name: error.name,
1184
- stack: error.stack,
1185
- path: req.path,
1186
- method: req.method,
1187
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1188
- });
1189
- }
1190
- }
1191
- const finalError = transformError ? transformError(error) : error;
1192
- const formattedReq = req;
1193
- const meta = {};
1194
- if (formattedReq.requestId) {
1195
- meta.requestId = formattedReq.requestId;
1196
- } else {
1197
- const requestId = req.get(config.requestIdHeader);
1198
- if (requestId) {
1199
- meta.requestId = requestId;
1200
- }
1201
- }
1202
- if (formattedReq.correlationId) {
1203
- meta.correlationId = formattedReq.correlationId;
1204
- } else {
1205
- const correlationId = req.get(config.correlationIdHeader);
1206
- if (correlationId) {
1207
- meta.correlationId = correlationId;
1208
- }
1209
- }
1210
- const response = formatter.formatError(finalError, meta);
1211
- const statusCode = formatter.getErrorStatusCode(finalError);
1212
- res.status(statusCode).json(response);
1213
- };
1214
- }
1215
- function createErrorCatcher(formatter, options = {}) {
1216
- const { logErrors = true, errorLogger, transformError, customHandler } = options;
1217
- const config = formatter.getConfig();
1218
- return (error, req, res, _next) => {
1219
- if (customHandler && customHandler(error, req, res)) {
1220
- return;
1221
- }
1222
- if (logErrors) {
1223
- if (errorLogger) {
1224
- errorLogger(error, req);
1225
- } else {
1226
- console.error({
1227
- message: error.message,
1228
- name: error.name,
1229
- stack: error.stack,
1230
- path: req.path,
1231
- method: req.method,
1232
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1233
- });
1234
- }
1235
- }
1236
- const finalError = transformError ? transformError(error) : error;
1237
- const formattedReq = req;
1238
- const meta = {};
1239
- if (formattedReq.requestId) {
1240
- meta.requestId = formattedReq.requestId;
1241
- } else {
1242
- const requestId = req.get(config.requestIdHeader);
1243
- if (requestId) {
1244
- meta.requestId = requestId;
1245
- }
1246
- }
1247
- if (formattedReq.correlationId) {
1248
- meta.correlationId = formattedReq.correlationId;
1249
- } else {
1250
- const correlationId = req.get(config.correlationIdHeader);
1251
- if (correlationId) {
1252
- meta.correlationId = correlationId;
1253
- }
1254
- }
1255
- const response = formatter.formatError(finalError, meta);
1256
- const statusCode = formatter.getErrorStatusCode(finalError);
1257
- res.status(statusCode).json(response);
1258
- };
1259
- }
1260
- function asyncHandler(fn) {
1261
- return (req, res, next) => {
1262
- Promise.resolve(fn(req, res, next)).catch(next);
1263
- };
1264
- }
1265
- function catchErrors(fn) {
1266
- return async (req, res, next) => {
1267
- try {
1268
- await fn(req, res);
1269
- } catch (error) {
1270
- next(error);
1271
- }
1272
- };
1273
- }
1274
-
1275
- // src/decorators/response.ts
1276
- function createResponseDecorator(formatter) {
1277
- return (options = {}) => {
1278
- return (_target, _propertyKey, descriptor) => {
1279
- const originalMethod = descriptor.value;
1280
- descriptor.value = async function(req, res, next) {
1281
- try {
1282
- const result = await originalMethod.call(this, req, res, next);
1283
- if (res.headersSent) {
1284
- return;
1285
- }
1286
- if (options.raw) {
1287
- res.json(result);
1288
- return;
1289
- }
1290
- const formattedReq = req;
1291
- const meta = { ...options.meta };
1292
- if (formattedReq.requestId) {
1293
- meta.requestId = formattedReq.requestId;
1294
- }
1295
- if (formattedReq.correlationId) {
1296
- meta.correlationId = formattedReq.correlationId;
1297
- }
1298
- const response = formatter.formatSuccess(result, meta);
1299
- const statusCode = options.statusCode ?? formatter.getSuccessStatusCode(req.method, result !== void 0);
1300
- res.status(statusCode).json(response);
1301
- } catch (error) {
1302
- if (next) {
1303
- next(error);
1304
- } else {
1305
- throw error;
1306
- }
1307
- }
1308
- };
1309
- return descriptor;
1310
- };
1311
- };
1312
- }
1313
- function HandleErrors() {
1314
- return (_target, _propertyKey, descriptor) => {
1315
- const originalMethod = descriptor.value;
1316
- descriptor.value = async function(req, res, next) {
1317
- try {
1318
- await originalMethod.call(this, req, res, next);
1319
- } catch (error) {
1320
- next(error);
1321
- }
1322
- };
1323
- return descriptor;
1324
- };
1325
- }
1326
- function ApiRoute(formatter, options = {}) {
1327
- return (_target, _propertyKey, descriptor) => {
1328
- const originalMethod = descriptor.value;
1329
- descriptor.value = async function(req, res, next) {
1330
- try {
1331
- const result = await originalMethod.call(this, req, res, next);
1332
- if (res.headersSent) {
1333
- return;
1334
- }
1335
- if (options.raw) {
1336
- res.json(result);
1337
- return;
1338
- }
1339
- const formattedReq = req;
1340
- const meta = { ...options.meta };
1341
- if (formattedReq.requestId) {
1342
- meta.requestId = formattedReq.requestId;
1343
- }
1344
- if (formattedReq.correlationId) {
1345
- meta.correlationId = formattedReq.correlationId;
1346
- }
1347
- const response = formatter.formatSuccess(result, meta);
1348
- const statusCode = options.statusCode ?? formatter.getSuccessStatusCode(req.method, result !== void 0);
1349
- res.status(statusCode).json(response);
1350
- } catch (error) {
1351
- next(error);
1352
- }
1353
- };
1354
- return descriptor;
1355
- };
1356
- }
1357
- function Validate(validator, source = "body") {
1358
- return (_target, _propertyKey, descriptor) => {
1359
- const originalMethod = descriptor.value;
1360
- descriptor.value = async function(req, res, next) {
1361
- const data = req[source];
1362
- const result = validator(data);
1363
- if (!result.valid) {
1364
- const { ValidationError: ValidationError2 } = await Promise.resolve().then(() => (init_predefined(), predefined_exports));
1365
- next(new ValidationError2("Validation failed", result.errors));
1366
- return;
1367
- }
1368
- await originalMethod.call(this, req, res, next);
1369
- };
1370
- return descriptor;
1371
- };
1372
- }
1373
-
1374
- // src/decorators/paginate.ts
1375
- function Paginate(formatter, options = {}) {
1376
- const paginationHelper = new PaginationHelper({
1377
- defaultLimit: options.defaultLimit ?? 10,
1378
- maxLimit: options.maxLimit ?? 100,
1379
- includeLinks: options.includeLinks ?? false
1380
- });
1381
- return (_target, _propertyKey, descriptor) => {
1382
- const originalMethod = descriptor.value;
1383
- descriptor.value = async function(req, res, next) {
1384
- try {
1385
- const { page, limit } = paginationHelper.extractFromRequest(req);
1386
- req.pagination = {
1387
- page,
1388
- limit,
1389
- offset: paginationHelper.calculateOffset(page, limit)
1390
- };
1391
- const result = await originalMethod.call(this, req, res, next);
1392
- if (res.headersSent) {
1393
- return;
1394
- }
1395
- const formattedReq = req;
1396
- const meta = { ...options.meta };
1397
- if (formattedReq.requestId) {
1398
- meta.requestId = formattedReq.requestId;
1399
- }
1400
- if (formattedReq.correlationId) {
1401
- meta.correlationId = formattedReq.correlationId;
1402
- }
1403
- const baseUrl = options.includeLinks ? `${req.protocol}://${req.get("host")}${req.originalUrl.split("?")[0]}` : void 0;
1404
- const response = formatter.formatPaginated(
1405
- result.data,
1406
- { page, limit, total: result.total },
1407
- Object.keys(meta).length > 0 ? meta : void 0,
1408
- baseUrl
1409
- );
1410
- res.status(200).json(response);
1411
- } catch (error) {
1412
- next(error);
1413
- }
1414
- };
1415
- return descriptor;
1416
- };
1417
- }
1418
- function CursorPaginate(formatter, options = {}) {
1419
- const paginationHelper = new PaginationHelper({
1420
- defaultLimit: options.defaultLimit ?? 10,
1421
- maxLimit: options.maxLimit ?? 100,
1422
- includeLinks: options.includeLinks ?? false
1423
- });
1424
- return (_target, _propertyKey, descriptor) => {
1425
- const originalMethod = descriptor.value;
1426
- descriptor.value = async function(req, res, next) {
1427
- try {
1428
- const { cursor, limit } = paginationHelper.extractCursorFromRequest(req);
1429
- req.pagination = {
1430
- cursor,
1431
- limit
1432
- };
1433
- const result = await originalMethod.call(this, req, res, next);
1434
- if (res.headersSent) {
1435
- return;
1436
- }
1437
- const formattedReq = req;
1438
- const meta = { ...options.meta };
1439
- if (formattedReq.requestId) {
1440
- meta.requestId = formattedReq.requestId;
1441
- }
1442
- if (formattedReq.correlationId) {
1443
- meta.correlationId = formattedReq.correlationId;
1444
- }
1445
- const baseUrl = options.includeLinks ? `${req.protocol}://${req.get("host")}${req.originalUrl.split("?")[0]}` : void 0;
1446
- const response = formatter.formatCursorPaginated(
1447
- result.data,
1448
- {
1449
- limit,
1450
- cursor,
1451
- nextCursor: result.nextCursor,
1452
- previousCursor: result.previousCursor,
1453
- hasMore: result.hasMore
1454
- },
1455
- Object.keys(meta).length > 0 ? meta : void 0,
1456
- baseUrl
1457
- );
1458
- res.status(200).json(response);
1459
- } catch (error) {
1460
- next(error);
1461
- }
1462
- };
1463
- return descriptor;
1464
- };
1465
- }
1466
-
1467
- // src/types/responses.ts
1468
- function isSuccessResponse(response) {
1469
- return response.success === true;
1470
- }
1471
- function isErrorResponse(response) {
1472
- return response.success === false;
1473
- }
1474
- function isPaginatedResponse(response) {
1475
- return response.success === true && "pagination" in response;
1476
- }
1477
-
1478
- // src/types/errors.ts
1479
- var ErrorCodes = {
1480
- VALIDATION_ERROR: "VALIDATION_ERROR",
1481
- NOT_FOUND: "NOT_FOUND",
1482
- UNAUTHORIZED: "UNAUTHORIZED",
1483
- FORBIDDEN: "FORBIDDEN",
1484
- CONFLICT: "CONFLICT",
1485
- INTERNAL_ERROR: "INTERNAL_ERROR",
1486
- BAD_REQUEST: "BAD_REQUEST",
1487
- RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
1488
- SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
1489
- UNPROCESSABLE_ENTITY: "UNPROCESSABLE_ENTITY"
1490
- };
1491
-
1492
- // src/index.ts
1493
- function responseFormatter(options = {}) {
1494
- return {
1495
- wrapper: responseWrapper(options),
1496
- errorHandler: errorCatcher(options)
1497
- };
1498
- }
1499
- var index_default = responseFormatter;
1500
- // Annotate the CommonJS export names for ESM import in node:
1501
- 0 && (module.exports = {
1502
- ApiError,
1503
- ApiRoute,
1504
- BadRequestError,
1505
- ConflictError,
1506
- CursorPaginate,
1507
- ErrorCodes,
1508
- ErrorHandler,
1509
- ForbiddenError,
1510
- HandleErrors,
1511
- InternalServerError,
1512
- NotFoundError,
1513
- Paginate,
1514
- PaginationHelper,
1515
- RateLimitError,
1516
- ResponseFormatter,
1517
- ServiceUnavailableError,
1518
- StatusCodeMapper,
1519
- UnauthorizedError,
1520
- UnprocessableEntityError,
1521
- Validate,
1522
- ValidationError,
1523
- asyncHandler,
1524
- calculateCursorPaginationMeta,
1525
- calculatePaginationMeta,
1526
- catchErrors,
1527
- createError,
1528
- createErrorCatcher,
1529
- createErrorHandler,
1530
- createErrorSerializer,
1531
- createPaginationHelper,
1532
- createResponseDecorator,
1533
- createResponseFormatter,
1534
- createResponseWrapper,
1535
- createStatusCodeMapper,
1536
- defaultConfig,
1537
- errorCatcher,
1538
- errorHandlerMiddleware,
1539
- extractErrorContext,
1540
- extractFieldErrors,
1541
- generateCursorPaginationLinks,
1542
- generatePaginationLinks,
1543
- generateRequestId,
1544
- isErrorResponse,
1545
- isPaginatedResponse,
1546
- isPlainObject,
1547
- isSuccessResponse,
1548
- responseFormatter,
1549
- responseWrapper,
1550
- serializeError,
1551
- validateConfig,
1552
- validatePaginationInput
1553
- });
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var xe=Object.defineProperty;var G=(r,e)=>()=>(r&&(e=r(r=0)),e);var Pe=(r,e)=>{for(var t in e)xe(r,t,{get:e[t],enumerable:true});};exports.ApiError=void 0;var q=G(()=>{exports.ApiError=class r extends Error{code;statusCode;details;fields;context;isOperational;timestamp;constructor(e,t={}){super(e),this.name=this.constructor.name,this.code=t.code||"INTERNAL_ERROR",this.statusCode=t.statusCode||500,this.details=t.details,this.fields=t.fields,this.context=t.context,this.isOperational=t.isOperational??true,this.timestamp=new Date().toISOString(),Error.captureStackTrace(this,this.constructor),t.cause&&(this.cause=t.cause);}serialize(e=false){let t={code:this.code,message:this.message,statusCode:this.statusCode};return this.details&&(t.details=this.details),this.fields&&(t.fields=this.fields),this.context&&(t.context=this.context),e&&this.stack&&(t.stack=this.stack),t}getErrorChain(){let e=[],t=this,o=new WeakSet;for(;t&&!o.has(t);)o.add(t),e.push({name:t.name,message:t.message,code:t instanceof r?t.code:void 0}),t=t.cause;return e}withContext(e){return new r(this.message,{code:this.code,statusCode:this.statusCode,details:this.details,fields:this.fields,context:{...this.context,...e},cause:this.cause,isOperational:this.isOperational})}toJSON(){return {name:this.name,message:this.message,code:this.code,statusCode:this.statusCode,details:this.details,fields:this.fields,context:this.context,isOperational:this.isOperational,timestamp:this.timestamp,stack:this.stack}}};});var te={};Pe(te,{BadRequestError:()=>exports.BadRequestError,ConflictError:()=>exports.ConflictError,ForbiddenError:()=>exports.ForbiddenError,InternalServerError:()=>exports.InternalServerError,NotFoundError:()=>exports.NotFoundError,RateLimitError:()=>exports.RateLimitError,ServiceUnavailableError:()=>exports.ServiceUnavailableError,UnauthorizedError:()=>exports.UnauthorizedError,UnprocessableEntityError:()=>exports.UnprocessableEntityError,ValidationError:()=>exports.ValidationError,createError:()=>$});function $(r,e,t){let o=new r(e);return t?o.withContext(t):o}exports.ValidationError=void 0;exports.NotFoundError=void 0;exports.UnauthorizedError=void 0;exports.ForbiddenError=void 0;exports.ConflictError=void 0;exports.InternalServerError=void 0;exports.BadRequestError=void 0;exports.RateLimitError=void 0;exports.ServiceUnavailableError=void 0;exports.UnprocessableEntityError=void 0;var U=G(()=>{q();exports.ValidationError=class extends exports.ApiError{constructor(e="Validation failed",t,o={}){super(e,{...o,code:"VALIDATION_ERROR",statusCode:400,fields:t});}},exports.NotFoundError=class extends exports.ApiError{constructor(e="Resource not found",t={}){super(e,{...t,code:"NOT_FOUND",statusCode:404});}},exports.UnauthorizedError=class extends exports.ApiError{constructor(e="Authentication required",t={}){super(e,{...t,code:"UNAUTHORIZED",statusCode:401});}},exports.ForbiddenError=class extends exports.ApiError{constructor(e="Access forbidden",t={}){super(e,{...t,code:"FORBIDDEN",statusCode:403});}},exports.ConflictError=class extends exports.ApiError{constructor(e="Resource conflict",t={}){super(e,{...t,code:"CONFLICT",statusCode:409});}},exports.InternalServerError=class extends exports.ApiError{constructor(e="Internal server error",t={}){super(e,{...t,code:"INTERNAL_ERROR",statusCode:500,isOperational:false});}},exports.BadRequestError=class extends exports.ApiError{constructor(e="Bad request",t={}){super(e,{...t,code:"BAD_REQUEST",statusCode:400});}},exports.RateLimitError=class extends exports.ApiError{retryAfter;constructor(e="Rate limit exceeded",t,o={}){super(e,{...o,code:"RATE_LIMIT_EXCEEDED",statusCode:429,details:t?{retryAfter:t}:void 0}),this.retryAfter=t;}},exports.ServiceUnavailableError=class extends exports.ApiError{constructor(e="Service temporarily unavailable",t={}){super(e,{...t,code:"SERVICE_UNAVAILABLE",statusCode:503});}},exports.UnprocessableEntityError=class extends exports.ApiError{constructor(e="Unprocessable entity",t={}){super(e,{...t,code:"UNPROCESSABLE_ENTITY",statusCode:422});}};});var I={includeStackTraces:false,environment:"production",includeTimestamp:true,maskSensitiveData:true,requestIdHeader:"x-request-id",correlationIdHeader:"x-correlation-id",pagination:{defaultLimit:10,maxLimit:100,includeLinks:false},preResponseHooks:[],postResponseHooks:[],passthroughMode:false,sensitiveFields:["password","token","secret","apiKey","authorization"],generateRequestId:true};q();var Q={includeStack:false,maskSensitiveData:true,sensitiveFields:["password","token","secret","apiKey","authorization"]};function V(r,e,t=0){if(t>10)return {_truncated:true};let o=Object.create(null),n=e.map(s=>s.toLowerCase());for(let[s,a]of Object.entries(r))s==="__proto__"||s==="constructor"||s==="prototype"||(n.includes(s.toLowerCase())?o[s]="[REDACTED]":a&&typeof a=="object"&&!Array.isArray(a)?o[s]=V(a,e,t+1):o[s]=a);return o}function W(r,e=new WeakSet,t=0){if(t>10)return {_truncated:true};if(e.has(r))return {_circular:true};e.add(r);let o=Object.create(null);for(let[n,s]of Object.entries(r))n==="__proto__"||n==="constructor"||n==="prototype"||(s==null?o[n]=s:typeof s=="object"?s instanceof Error?o[n]={name:s.name,message:s.message}:Array.isArray(s)?o[n]=s.slice(0,100).map(a=>typeof a=="object"&&a!==null?W(a,e,t+1):a):o[n]=W(s,e,t+1):o[n]=s);return o}function j(r,e={}){let t={...Q,...e};if(t.customSerializers){for(let[n,s]of t.customSerializers)if(r instanceof n){let a=s(r);return {code:a.code||"CUSTOM_ERROR",message:a.message||r.message,details:t.maskSensitiveData?V(a,t.sensitiveFields):a}}}if(r instanceof exports.ApiError){let n=r.serialize(t.includeStack),s=n.details;s&&t.maskSensitiveData&&(s=V(W(s),t.sensitiveFields,0));let a={code:n.code,message:n.message};return s&&Object.keys(s).length>0&&(a.details=s),n.fields&&(a.fields=n.fields),t.includeStack&&n.stack&&(a.stack=n.stack),a}let o={code:"INTERNAL_ERROR",message:r.message||"An unexpected error occurred"};return t.includeStack&&r.stack&&(o.stack=r.stack),o}function Y(r){if(r instanceof exports.ApiError&&r.fields)return r.fields}function J(r){if(r instanceof exports.ApiError&&r.context)return r.context}function ee(r={}){let e={...Q,...r};return t=>j(t,e)}q();U();var ye=new Map([[exports.ValidationError,400],[exports.BadRequestError,400],[exports.UnauthorizedError,401],[exports.ForbiddenError,403],[exports.NotFoundError,404],[exports.ConflictError,409],[exports.UnprocessableEntityError,422],[exports.RateLimitError,429],[exports.InternalServerError,500],[exports.ServiceUnavailableError,503]]),Ie={GET:200,POST:201,PUT:200,PATCH:200,DELETE:204,HEAD:200,OPTIONS:200},P=class{customMappings;constructor(e){this.customMappings=e||new Map;}getStatusCode(e){if(e instanceof exports.ApiError)return e.statusCode;for(let[t,o]of this.customMappings)if(e instanceof t)return o;for(let[t,o]of ye)if(e instanceof t)return o;return 500}getSuccessStatusCode(e,t){let o=e.toUpperCase();return o==="DELETE"&&!t?204:Ie[o]||200}addMapping(e,t){this.customMappings.set(e,t);}removeMapping(e){return this.customMappings.delete(e)}static isSuccessCode(e){return e>=200&&e<300}static isClientError(e){return e>=400&&e<500}static isServerError(e){return e>=500&&e<600}};function re(r){return new P(r)}function K(r,e,t=Number.MAX_SAFE_INTEGER){let o=Number(r);return !Number.isFinite(o)||Number.isNaN(o)?e:Math.min(t,Math.max(0,Math.floor(o)))}function A(r,e){let t=Math.max(1,K(r.page,1,1e6)),o=Math.min(e.maxLimit,Math.max(1,K(r.limit,e.defaultLimit,e.maxLimit))),n=K(r.total,0,Number.MAX_SAFE_INTEGER);return {page:t,limit:o,total:n}}function D(r,e){let{page:t,limit:o,total:n}=r,s=Math.ceil(n/o)||1,a=t<s,i=t>1,p={page:t,limit:o,total:n,totalPages:s,hasNextPage:a,hasPreviousPage:i};return e&&(p.links=X(t,s,o,e)),p}function X(r,e,t,o){let n=new URL(o),s=i=>(n.searchParams.set("page",String(i)),n.searchParams.set("limit",String(t)),n.toString()),a={self:s(r),first:s(1),last:s(e)};return r<e&&(a.next=s(r+1)),r>1&&(a.previous=s(r-1)),a}function N(r,e){let t={limit:r.limit,hasMore:r.hasMore};return r.cursor&&(t.cursor=r.cursor),r.nextCursor&&(t.nextCursor=r.nextCursor),r.previousCursor&&(t.previousCursor=r.previousCursor),e&&(t.links=Z(r,e)),t}function Z(r,e){let t=new URL(e),o=s=>(s?t.searchParams.set("cursor",s):t.searchParams.delete("cursor"),t.searchParams.set("limit",String(r.limit)),t.toString()),n={self:o(r.cursor)};return r.nextCursor&&r.hasMore&&(n.next=o(r.nextCursor)),r.previousCursor&&(n.previous=o(r.previousCursor)),n}function oe(r){let e=[];return r.environment&&!["development","staging","production","test"].includes(r.environment)&&e.push(`Invalid environment: ${r.environment}`),r.pagination&&(r.pagination.defaultLimit!==void 0&&r.pagination.defaultLimit<1&&e.push("pagination.defaultLimit must be at least 1"),r.pagination.maxLimit!==void 0&&r.pagination.maxLimit<1&&e.push("pagination.maxLimit must be at least 1"),r.pagination.defaultLimit!==void 0&&r.pagination.maxLimit!==void 0&&r.pagination.defaultLimit>r.pagination.maxLimit&&e.push("pagination.defaultLimit cannot exceed pagination.maxLimit")),e}function se(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function H(){let r=Date.now().toString(36),e;if(typeof globalThis.crypto<"u"&&globalThis.crypto.getRandomValues){let t=new Uint8Array(6);globalThis.crypto.getRandomValues(t),e=Array.from(t,o=>o.toString(16).padStart(2,"0")).join("");}else e=Math.random().toString(36).substring(2,10);return `${r}-${e}`}var x=class{config;statusCodeMapper;constructor(e={}){this.config=this.mergeConfig(e),this.statusCodeMapper=new P(this.config.customErrorMappers);}mergeConfig(e){return {...I,...e,pagination:{...I.pagination,...e.pagination},preResponseHooks:e.preResponseHooks||I.preResponseHooks,postResponseHooks:e.postResponseHooks||I.postResponseHooks,sensitiveFields:e.sensitiveFields||I.sensitiveFields}}getConfig(){return {...this.config}}updateConfig(e){this.config=this.mergeConfig({...this.config,...e}),e.customErrorMappers&&(this.statusCodeMapper=new P(e.customErrorMappers));}getTimestamp(){return this.config.includeTimestamp?new Date().toISOString():""}applyPreHooks(e,t){let o={data:e,meta:t};for(let n of this.config.preResponseHooks){let s=n(o.data,o.meta);o={data:s.data,meta:s.meta};}return o}applyPostHooks(e){let t=e;for(let o of this.config.postResponseHooks)t=o(t);return t}formatSuccess(e,t){let o=this.applyPreHooks(e,t),n={success:true,data:o.data,timestamp:this.getTimestamp()};return o.meta&&Object.keys(o.meta).length>0&&(n.meta=o.meta),this.config.passthroughMode&&this.config.customFormatter?this.config.customFormatter(n):this.applyPostHooks(n)}formatError(e,t){let o=this.config.includeStackTraces||this.config.environment==="development",s={success:false,error:j(e,{includeStack:o,maskSensitiveData:this.config.maskSensitiveData,sensitiveFields:this.config.sensitiveFields,customSerializers:this.config.customSerializers}),timestamp:this.getTimestamp()};return t&&Object.keys(t).length>0&&(s.meta=t),this.config.passthroughMode&&this.config.customFormatter?this.config.customFormatter(s):this.applyPostHooks(s)}formatPaginated(e,t,o,n){let s=this.applyPreHooks(e,o),a=A(t,this.config.pagination),i=D(a,this.config.pagination.includeLinks?n:void 0),p={success:true,data:s.data,pagination:i,timestamp:this.getTimestamp()};return s.meta&&Object.keys(s.meta).length>0&&(p.meta=s.meta),this.applyPostHooks(p)}formatCursorPaginated(e,t,o,n){let s=this.applyPreHooks(e,o),a=N(t,this.config.pagination.includeLinks?n:void 0),i={success:true,data:s.data,pagination:a,timestamp:this.getTimestamp()};return s.meta&&Object.keys(s.meta).length>0&&(i.meta=s.meta),this.applyPostHooks(i)}getErrorStatusCode(e){return this.statusCodeMapper.getStatusCode(e)}getSuccessStatusCode(e,t){return this.statusCodeMapper.getSuccessStatusCode(e,t)}getRequestId(e){return e||(this.config.generateRequestId?H():"")}};function ne(r){return new x(r)}q();var be={logErrors:true},_=class{formatter;options;config;constructor(e,t={}){this.formatter=e,this.options={...be,...t},this.config=e.getConfig();}extractMeta(e){let t={},o=e.get(this.config.requestIdHeader);o&&(t.requestId=o);let n=e.get(this.config.correlationIdHeader);return n&&(t.correlationId=n),t}logError(e,t){if(!this.options.logErrors)return;if(this.options.errorLogger){this.options.errorLogger(e,t);return}let n=(e instanceof exports.ApiError?e.isOperational:false)?"warn":"error";console[n]({message:e.message,name:e.name,stack:e.stack,path:t.path,method:t.method,timestamp:new Date().toISOString()});}isOperationalError(e){return e instanceof exports.ApiError?e.isOperational:false}handle(e,t,o,n){this.logError(e,t);let s=this.options.transformError?this.options.transformError(e):e;if(!this.isOperationalError(s)&&this.options.handleNonOperational){this.options.handleNonOperational(s,t,o);return}let a=this.formatter.getErrorStatusCode(s),i=this.extractMeta(t),p=this.formatter.formatError(s,i);o.status(a).json(p);}middleware(){return (e,t,o,n)=>{this.handle(e,t,o,n);}}};function ae(r,e){return new _(r,e)}function ie(r,e){return new _(r,e).middleware()}var y=class{config;constructor(e={}){this.config={defaultLimit:e.defaultLimit??10,maxLimit:e.maxLimit??100,includeLinks:e.includeLinks??false,baseUrl:e.baseUrl};}safeParseInt(e,t,o){if(typeof e!="string")return t;let n=parseInt(e,10);return !Number.isFinite(n)||Number.isNaN(n)?t:Math.min(o,Math.max(1,n))}extractFromRequest(e){let t=this.safeParseInt(e.query.page,1,1e6),o=Math.min(this.config.maxLimit,this.safeParseInt(e.query.limit,this.config.defaultLimit,this.config.maxLimit));return {page:t,limit:o}}validateCursor(e){if(!(typeof e!="string"||e.length===0)&&!(e.length>512)&&/^[a-zA-Z0-9+/=_-]+$/.test(e))return e}extractCursorFromRequest(e){let t=this.validateCursor(e.query.cursor),o=Math.min(this.config.maxLimit,this.safeParseInt(e.query.limit,this.config.defaultLimit,this.config.maxLimit));return {cursor:t,limit:o}}calculateOffset(e,t){return (e-1)*t}buildMeta(e,t){let o=A(e,this.config);return D(o,this.config.includeLinks?t||this.config.baseUrl:void 0)}buildCursorMeta(e,t){return N(e,this.config.includeLinks?t||this.config.baseUrl:void 0)}fromArray(e,t,o,n){let s=this.buildMeta({page:t,limit:o,total:n});return {data:e,pagination:s}}paginateArray(e,t,o){let n=e.length,s=this.calculateOffset(t,o),a=e.slice(s,s+o);return this.fromArray(a,t,o,n)}hasNextPage(e,t,o){let n=Math.ceil(o/t);return e<n}hasPreviousPage(e){return e>1}getTotalPages(e,t){return Math.ceil(e/t)||1}isValidPage(e,t,o){if(e<1)return false;if(t===0)return e===1;let n=this.getTotalPages(t,o);return e<=n}getConfig(){return {...this.config}}updateConfig(e){this.config={...this.config,...e};}};function pe(r){return new y(r)}q();U();function z(r={}){let{skipPaths:e=[],skipCondition:t,...o}=r,n=new x(o),s=n.getConfig();return (a,i,p)=>{let c=a,u=i;if(e.includes(a.path)||t&&t(a)){p();return}let l=a.get(s.requestIdHeader);!l&&s.generateRequestId&&(l=H()),l&&(c.requestId=l,i.setHeader(s.requestIdHeader,l));let R=a.get(s.correlationIdHeader);R&&(c.correlationId=R,i.setHeader(s.correlationIdHeader,R)),u.formatter=n;let f=d=>{let g={};return l&&(g.requestId=l),R&&(g.correlationId=R),{...g,...d}},m=()=>{let d=a.protocol,g=a.get("host")||"localhost";return `${d}://${g}${a.originalUrl.split("?")[0]}`};u.respond=(d,g,C)=>{let h=n.formatSuccess(d,f(g)),he=C??n.getSuccessStatusCode(a.method,d!==void 0);i.status(he).json(h);},u.respondPaginated=(d,g,C)=>{let h=n.formatPaginated(d,g,f(C),m());i.status(200).json(h);},u.respondCursorPaginated=(d,g,C)=>{let h=n.formatCursorPaginated(d,g,f(C),m());i.status(200).json(h);},u.respondError=(d,g)=>{let C=n.formatError(d,f(g)),h=n.getErrorStatusCode(d);i.status(h).json(C);},p();}}function ue(r,e={}){let{skipPaths:t=[],skipCondition:o}=e,n=r.getConfig();return (s,a,i)=>{let p=s,c=a;if(t.includes(s.path)||o&&o(s)){i();return}let u=s.get(n.requestIdHeader);!u&&n.generateRequestId&&(u=H()),u&&(p.requestId=u,a.setHeader(n.requestIdHeader,u));let l=s.get(n.correlationIdHeader);l&&(p.correlationId=l,a.setHeader(n.correlationIdHeader,l)),c.formatter=r;let R=m=>{let d={};return u&&(d.requestId=u),l&&(d.correlationId=l),{...d,...m}},f=()=>{let m=s.protocol,d=s.get("host")||"localhost";return `${m}://${d}${s.originalUrl.split("?")[0]}`};c.respond=(m,d,g)=>{let C=r.formatSuccess(m,R(d)),h=g??r.getSuccessStatusCode(s.method,m!==void 0);a.status(h).json(C);},c.respondPaginated=(m,d,g)=>{let C=r.formatPaginated(m,d,R(g),f());a.status(200).json(C);},c.respondCursorPaginated=(m,d,g)=>{let C=r.formatCursorPaginated(m,d,R(g),f());a.status(200).json(C);},c.respondError=(m,d)=>{let g=r.formatError(m,R(d)),C=r.getErrorStatusCode(m);a.status(C).json(g);},i();}}function B(r={}){let{logErrors:e=true,errorLogger:t,transformError:o,customHandler:n,...s}=r,a=new x(s),i=a.getConfig();return (p,c,u,l)=>{if(n&&n(p,c,u))return;e&&(t?t(p,c):console.error({message:p.message,name:p.name,stack:p.stack,path:c.path,method:c.method,timestamp:new Date().toISOString()}));let R=o?o(p):p,f=c,m={};if(f.requestId)m.requestId=f.requestId;else {let C=c.get(i.requestIdHeader);C&&(m.requestId=C);}if(f.correlationId)m.correlationId=f.correlationId;else {let C=c.get(i.correlationIdHeader);C&&(m.correlationId=C);}let d=a.formatError(R,m),g=a.getErrorStatusCode(R);u.status(g).json(d);}}function ce(r,e={}){let{logErrors:t=true,errorLogger:o,transformError:n,customHandler:s}=e,a=r.getConfig();return (i,p,c,u)=>{if(s&&s(i,p,c))return;t&&(o?o(i,p):console.error({message:i.message,name:i.name,stack:i.stack,path:p.path,method:p.method,timestamp:new Date().toISOString()}));let l=n?n(i):i,R=p,f={};if(R.requestId)f.requestId=R.requestId;else {let g=p.get(a.requestIdHeader);g&&(f.requestId=g);}if(R.correlationId)f.correlationId=R.correlationId;else {let g=p.get(a.correlationIdHeader);g&&(f.correlationId=g);}let m=r.formatError(l,f),d=r.getErrorStatusCode(l);c.status(d).json(m);}}function de(r){return (e,t,o)=>{Promise.resolve(r(e,t,o)).catch(o);}}function le(r){return async(e,t,o)=>{try{await r(e,t);}catch(n){o(n);}}}function me(r){return (e={})=>(t,o,n)=>{let s=n.value;return n.value=async function(a,i,p){try{let c=await s.call(this,a,i,p);if(i.headersSent)return;if(e.raw){i.json(c);return}let u=a,l={...e.meta};u.requestId&&(l.requestId=u.requestId),u.correlationId&&(l.correlationId=u.correlationId);let R=r.formatSuccess(c,l),f=e.statusCode??r.getSuccessStatusCode(a.method,c!==void 0);i.status(f).json(R);}catch(c){if(p)p(c);else throw c}},n}}function ge(){return (r,e,t)=>{let o=t.value;return t.value=async function(n,s,a){try{await o.call(this,n,s,a);}catch(i){a(i);}},t}}function fe(r,e={}){return (t,o,n)=>{let s=n.value;return n.value=async function(a,i,p){try{let c=await s.call(this,a,i,p);if(i.headersSent)return;if(e.raw){i.json(c);return}let u=a,l={...e.meta};u.requestId&&(l.requestId=u.requestId),u.correlationId&&(l.correlationId=u.correlationId);let R=r.formatSuccess(c,l),f=e.statusCode??r.getSuccessStatusCode(a.method,c!==void 0);i.status(f).json(R);}catch(c){p(c);}},n}}function Re(r,e="body"){return (t,o,n)=>{let s=n.value;return n.value=async function(a,i,p){let c=a[e],u=r(c);if(!u.valid){let{ValidationError:l}=await Promise.resolve().then(()=>(U(),te));p(new l("Validation failed",u.errors));return}await s.call(this,a,i,p);},n}}function Ee(r,e={}){let t=new y({defaultLimit:e.defaultLimit??10,maxLimit:e.maxLimit??100,includeLinks:e.includeLinks??false});return (o,n,s)=>{let a=s.value;return s.value=async function(i,p,c){try{let{page:u,limit:l}=t.extractFromRequest(i);i.pagination={page:u,limit:l,offset:t.calculateOffset(u,l)};let R=await a.call(this,i,p,c);if(p.headersSent)return;let f=i,m={...e.meta};f.requestId&&(m.requestId=f.requestId),f.correlationId&&(m.correlationId=f.correlationId);let d=e.includeLinks?`${i.protocol}://${i.get("host")}${i.originalUrl.split("?")[0]}`:void 0,g=r.formatPaginated(R.data,{page:u,limit:l,total:R.total},Object.keys(m).length>0?m:void 0,d);p.status(200).json(g);}catch(u){c(u);}},s}}function Ce(r,e={}){let t=new y({defaultLimit:e.defaultLimit??10,maxLimit:e.maxLimit??100,includeLinks:e.includeLinks??false});return (o,n,s)=>{let a=s.value;return s.value=async function(i,p,c){try{let{cursor:u,limit:l}=t.extractCursorFromRequest(i);i.pagination={cursor:u,limit:l};let R=await a.call(this,i,p,c);if(p.headersSent)return;let f=i,m={...e.meta};f.requestId&&(m.requestId=f.requestId),f.correlationId&&(m.correlationId=f.correlationId);let d=e.includeLinks?`${i.protocol}://${i.get("host")}${i.originalUrl.split("?")[0]}`:void 0,g=r.formatCursorPaginated(R.data,{limit:l,cursor:u,nextCursor:R.nextCursor,previousCursor:R.previousCursor,hasMore:R.hasMore},Object.keys(m).length>0?m:void 0,d);p.status(200).json(g);}catch(u){c(u);}},s}}function ke(r){return r.success===true}function Me(r){return r.success===false}function Fe(r){return r.success===true&&"pagination"in r}var Se={VALIDATION_ERROR:"VALIDATION_ERROR",NOT_FOUND:"NOT_FOUND",UNAUTHORIZED:"UNAUTHORIZED",FORBIDDEN:"FORBIDDEN",CONFLICT:"CONFLICT",INTERNAL_ERROR:"INTERNAL_ERROR",BAD_REQUEST:"BAD_REQUEST",RATE_LIMIT_EXCEEDED:"RATE_LIMIT_EXCEEDED",SERVICE_UNAVAILABLE:"SERVICE_UNAVAILABLE",UNPROCESSABLE_ENTITY:"UNPROCESSABLE_ENTITY"};function ve(r={}){return {wrapper:z(r),errorHandler:B(r)}}var bt=ve;
2
+ exports.ApiRoute=fe;exports.CursorPaginate=Ce;exports.ErrorCodes=Se;exports.ErrorHandler=_;exports.HandleErrors=ge;exports.Paginate=Ee;exports.PaginationHelper=y;exports.ResponseFormatter=x;exports.StatusCodeMapper=P;exports.Validate=Re;exports.asyncHandler=de;exports.calculateCursorPaginationMeta=N;exports.calculatePaginationMeta=D;exports.catchErrors=le;exports.createError=$;exports.createErrorCatcher=ce;exports.createErrorHandler=ae;exports.createErrorSerializer=ee;exports.createPaginationHelper=pe;exports.createResponseDecorator=me;exports.createResponseFormatter=ne;exports.createResponseWrapper=ue;exports.createStatusCodeMapper=re;exports.default=bt;exports.defaultConfig=I;exports.errorCatcher=B;exports.errorHandlerMiddleware=ie;exports.extractErrorContext=J;exports.extractFieldErrors=Y;exports.generateCursorPaginationLinks=Z;exports.generatePaginationLinks=X;exports.generateRequestId=H;exports.isErrorResponse=Me;exports.isPaginatedResponse=Fe;exports.isPlainObject=se;exports.isSuccessResponse=ke;exports.responseFormatter=ve;exports.responseWrapper=z;exports.serializeError=j;exports.validateConfig=oe;exports.validatePaginationInput=A;