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