@web-ts-toolkit/express-response-handler 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -2
- package/create-handler.d.mts +2 -1
- package/create-handler.d.ts +2 -1
- package/create-handler.js +358 -125
- package/create-handler.mjs +361 -126
- package/error-format.d.mts +10 -3
- package/error-format.d.ts +10 -3
- package/error-format.js +69 -15
- package/error-format.mjs +65 -15
- package/http-response.d.mts +2 -6
- package/http-response.d.ts +2 -6
- package/http-response.js +140 -13
- package/http-response.mjs +140 -13
- package/index.d.mts +5 -3
- package/index.d.ts +5 -3
- package/index.js +365 -129
- package/index.mjs +366 -129
- package/llms.txt +6 -0
- package/package.json +8 -4
- package/public-types.d.mts +13 -17
- package/public-types.d.ts +13 -17
- package/responses/csv.d.mts +12 -6
- package/responses/csv.d.ts +12 -6
- package/responses/csv.js +133 -12
- package/responses/csv.mjs +133 -12
- package/responses/index.d.mts +2 -1
- package/responses/index.d.ts +2 -1
- package/responses/index.js +11 -3
- package/responses/index.mjs +9 -2
- package/responses/success.js +7 -1
- package/responses/success.mjs +7 -1
package/error-format.d.mts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { Rfc9457ErrorPayload, Aip193ErrorPayload } from '@web-ts-toolkit/http-errors';
|
|
2
|
-
import { ErrorMessageProvider,
|
|
2
|
+
import { ErrorMessageProvider, ErrorWithPayload, ErrorMessageResult } from './public-types.mjs';
|
|
3
|
+
import 'express-serve-static-core';
|
|
3
4
|
import './http-response.mjs';
|
|
5
|
+
import './responses/csv.mjs';
|
|
4
6
|
import './responses/success.mjs';
|
|
5
7
|
import './responses/index.mjs';
|
|
6
|
-
import './responses/csv.mjs';
|
|
7
8
|
import './error-formats.mjs';
|
|
8
9
|
|
|
10
|
+
declare const FALLBACK_ERROR_STATUS = 500;
|
|
11
|
+
declare const FALLBACK_ERROR_MESSAGE = "Internal Server Error";
|
|
12
|
+
declare const isValidErrorStatusCode: (statusCode: unknown) => statusCode is number;
|
|
13
|
+
declare const validateErrorStatusCode: (statusCode: unknown, source: string) => number;
|
|
14
|
+
declare const toErrorStatusCode: (status: unknown, code: unknown, fallbackStatusCode?: number) => number;
|
|
15
|
+
declare const normalizeThrownError: (value: unknown) => ErrorWithPayload;
|
|
9
16
|
declare const defaultErrorMessageProvider: ErrorMessageProvider;
|
|
10
17
|
declare const toSimpleErrorPayload: (result: ErrorMessageResult) => Record<string, unknown>;
|
|
11
18
|
declare const toStructuredHttpErrorPayload: (error: ErrorWithPayload, errorDomain: string) => Aip193ErrorPayload;
|
|
@@ -13,4 +20,4 @@ declare const toRfc9457HttpErrorPayload: (error: ErrorWithPayload, errorDomain:
|
|
|
13
20
|
declare const toStructuredGenericErrorPayload: (result: ErrorMessageResult, errorDomain: string) => Aip193ErrorPayload;
|
|
14
21
|
declare const toRfc9457GenericErrorPayload: (result: ErrorMessageResult) => Rfc9457ErrorPayload;
|
|
15
22
|
|
|
16
|
-
export { defaultErrorMessageProvider, toRfc9457GenericErrorPayload, toRfc9457HttpErrorPayload, toSimpleErrorPayload, toStructuredGenericErrorPayload, toStructuredHttpErrorPayload };
|
|
23
|
+
export { FALLBACK_ERROR_MESSAGE, FALLBACK_ERROR_STATUS, defaultErrorMessageProvider, isValidErrorStatusCode, normalizeThrownError, toErrorStatusCode, toRfc9457GenericErrorPayload, toRfc9457HttpErrorPayload, toSimpleErrorPayload, toStructuredGenericErrorPayload, toStructuredHttpErrorPayload, validateErrorStatusCode };
|
package/error-format.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { Rfc9457ErrorPayload, Aip193ErrorPayload } from '@web-ts-toolkit/http-errors';
|
|
2
|
-
import { ErrorMessageProvider,
|
|
2
|
+
import { ErrorMessageProvider, ErrorWithPayload, ErrorMessageResult } from './public-types.js';
|
|
3
|
+
import 'express-serve-static-core';
|
|
3
4
|
import './http-response.js';
|
|
5
|
+
import './responses/csv.js';
|
|
4
6
|
import './responses/success.js';
|
|
5
7
|
import './responses/index.js';
|
|
6
|
-
import './responses/csv.js';
|
|
7
8
|
import './error-formats.js';
|
|
8
9
|
|
|
10
|
+
declare const FALLBACK_ERROR_STATUS = 500;
|
|
11
|
+
declare const FALLBACK_ERROR_MESSAGE = "Internal Server Error";
|
|
12
|
+
declare const isValidErrorStatusCode: (statusCode: unknown) => statusCode is number;
|
|
13
|
+
declare const validateErrorStatusCode: (statusCode: unknown, source: string) => number;
|
|
14
|
+
declare const toErrorStatusCode: (status: unknown, code: unknown, fallbackStatusCode?: number) => number;
|
|
15
|
+
declare const normalizeThrownError: (value: unknown) => ErrorWithPayload;
|
|
9
16
|
declare const defaultErrorMessageProvider: ErrorMessageProvider;
|
|
10
17
|
declare const toSimpleErrorPayload: (result: ErrorMessageResult) => Record<string, unknown>;
|
|
11
18
|
declare const toStructuredHttpErrorPayload: (error: ErrorWithPayload, errorDomain: string) => Aip193ErrorPayload;
|
|
@@ -13,4 +20,4 @@ declare const toRfc9457HttpErrorPayload: (error: ErrorWithPayload, errorDomain:
|
|
|
13
20
|
declare const toStructuredGenericErrorPayload: (result: ErrorMessageResult, errorDomain: string) => Aip193ErrorPayload;
|
|
14
21
|
declare const toRfc9457GenericErrorPayload: (result: ErrorMessageResult) => Rfc9457ErrorPayload;
|
|
15
22
|
|
|
16
|
-
export { defaultErrorMessageProvider, toRfc9457GenericErrorPayload, toRfc9457HttpErrorPayload, toSimpleErrorPayload, toStructuredGenericErrorPayload, toStructuredHttpErrorPayload };
|
|
23
|
+
export { FALLBACK_ERROR_MESSAGE, FALLBACK_ERROR_STATUS, defaultErrorMessageProvider, isValidErrorStatusCode, normalizeThrownError, toErrorStatusCode, toRfc9457GenericErrorPayload, toRfc9457HttpErrorPayload, toSimpleErrorPayload, toStructuredGenericErrorPayload, toStructuredHttpErrorPayload, validateErrorStatusCode };
|
package/error-format.js
CHANGED
|
@@ -20,21 +20,34 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/error-format.ts
|
|
21
21
|
var error_format_exports = {};
|
|
22
22
|
__export(error_format_exports, {
|
|
23
|
+
FALLBACK_ERROR_MESSAGE: () => FALLBACK_ERROR_MESSAGE,
|
|
24
|
+
FALLBACK_ERROR_STATUS: () => FALLBACK_ERROR_STATUS,
|
|
23
25
|
defaultErrorMessageProvider: () => defaultErrorMessageProvider,
|
|
26
|
+
isValidErrorStatusCode: () => isValidErrorStatusCode,
|
|
27
|
+
normalizeThrownError: () => normalizeThrownError,
|
|
28
|
+
toErrorStatusCode: () => toErrorStatusCode,
|
|
24
29
|
toRfc9457GenericErrorPayload: () => toRfc9457GenericErrorPayload,
|
|
25
30
|
toRfc9457HttpErrorPayload: () => toRfc9457HttpErrorPayload,
|
|
26
31
|
toSimpleErrorPayload: () => toSimpleErrorPayload,
|
|
27
32
|
toStructuredGenericErrorPayload: () => toStructuredGenericErrorPayload,
|
|
28
|
-
toStructuredHttpErrorPayload: () => toStructuredHttpErrorPayload
|
|
33
|
+
toStructuredHttpErrorPayload: () => toStructuredHttpErrorPayload,
|
|
34
|
+
validateErrorStatusCode: () => validateErrorStatusCode
|
|
29
35
|
});
|
|
30
36
|
module.exports = __toCommonJS(error_format_exports);
|
|
31
37
|
var import_http_errors = require("@web-ts-toolkit/http-errors");
|
|
32
38
|
var import_utils = require("@web-ts-toolkit/utils");
|
|
33
|
-
var
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
var FALLBACK_ERROR_STATUS = 500;
|
|
40
|
+
var FALLBACK_ERROR_MESSAGE = "Internal Server Error";
|
|
41
|
+
var isValidErrorStatusCode = import_http_errors.isHttpErrorStatusCode;
|
|
42
|
+
var validateErrorStatusCode = (statusCode, source) => {
|
|
43
|
+
return (0, import_http_errors.validateHttpErrorStatusCode)(statusCode, source);
|
|
44
|
+
};
|
|
45
|
+
var toErrorStatusCode = (status, code, fallbackStatusCode = FALLBACK_ERROR_STATUS) => {
|
|
46
|
+
const statusCode = typeof status === "number" ? status : code;
|
|
47
|
+
if (statusCode === void 0) {
|
|
48
|
+
return fallbackStatusCode;
|
|
36
49
|
}
|
|
37
|
-
return
|
|
50
|
+
return validateErrorStatusCode(statusCode, "error status");
|
|
38
51
|
};
|
|
39
52
|
var toOptionalString = (value) => typeof value === "string" ? value : void 0;
|
|
40
53
|
var toMetadata = (value) => {
|
|
@@ -46,6 +59,41 @@ var toArray = (value) => {
|
|
|
46
59
|
}
|
|
47
60
|
return (0, import_utils.isArray)(value) ? value : [value];
|
|
48
61
|
};
|
|
62
|
+
var describeThrown = (value) => {
|
|
63
|
+
try {
|
|
64
|
+
if (value === null) {
|
|
65
|
+
return "null";
|
|
66
|
+
}
|
|
67
|
+
if (value === void 0) {
|
|
68
|
+
return "undefined";
|
|
69
|
+
}
|
|
70
|
+
if (typeof value === "symbol") {
|
|
71
|
+
return value.toString();
|
|
72
|
+
}
|
|
73
|
+
const stringified = typeof value === "object" && value !== null ? value.message : void 0;
|
|
74
|
+
if (typeof stringified === "string" && stringified.length > 0) {
|
|
75
|
+
return stringified;
|
|
76
|
+
}
|
|
77
|
+
return String(value);
|
|
78
|
+
} catch {
|
|
79
|
+
return FALLBACK_ERROR_MESSAGE;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var normalizeThrownError = (value) => {
|
|
83
|
+
if (value instanceof Error) {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
const message = describeThrown(value);
|
|
87
|
+
if ((0, import_utils.isPlainObject)(value)) {
|
|
88
|
+
const record = value;
|
|
89
|
+
const normalized = {
|
|
90
|
+
...record,
|
|
91
|
+
message
|
|
92
|
+
};
|
|
93
|
+
return normalized;
|
|
94
|
+
}
|
|
95
|
+
return { message };
|
|
96
|
+
};
|
|
49
97
|
var toHttpErrorShape = (error, fallbackDomain) => ({
|
|
50
98
|
statusCode: error.statusCode ?? 500,
|
|
51
99
|
status: error.status,
|
|
@@ -66,8 +114,8 @@ var toProblemDetailsSource = (result) => {
|
|
|
66
114
|
return (0, import_utils.isPlainObject)(result) ? result : void 0;
|
|
67
115
|
};
|
|
68
116
|
var defaultErrorMessageProvider = function(error) {
|
|
69
|
-
|
|
70
|
-
return
|
|
117
|
+
void error;
|
|
118
|
+
return FALLBACK_ERROR_MESSAGE;
|
|
71
119
|
};
|
|
72
120
|
var toSimpleErrorPayload = (result) => (0, import_utils.isString)(result) ? { message: result } : { ...result };
|
|
73
121
|
var toStructuredHttpErrorPayload = (error, errorDomain) => (0, import_http_errors.toAip193ErrorPayload)(toHttpErrorShape(error, errorDomain), errorDomain);
|
|
@@ -75,7 +123,7 @@ var toRfc9457HttpErrorPayload = (error, errorDomain) => (0, import_http_errors.t
|
|
|
75
123
|
var toStructuredGenericErrorPayload = (result, errorDomain) => {
|
|
76
124
|
if ((0, import_utils.isPlainObject)(result) && (0, import_utils.isPlainObject)(result.error)) {
|
|
77
125
|
const error = result.error;
|
|
78
|
-
const statusCode =
|
|
126
|
+
const statusCode = toErrorStatusCode(error.code, void 0);
|
|
79
127
|
const status = typeof error.status === "string" ? error.status : (0, import_http_errors.getCanonicalStatus)(statusCode);
|
|
80
128
|
const message = typeof error.message === "string" ? error.message : "";
|
|
81
129
|
const details = toArray(error.details);
|
|
@@ -89,7 +137,7 @@ var toStructuredGenericErrorPayload = (result, errorDomain) => {
|
|
|
89
137
|
};
|
|
90
138
|
}
|
|
91
139
|
if ((0, import_utils.isPlainObject)(result)) {
|
|
92
|
-
const statusCode =
|
|
140
|
+
const statusCode = toErrorStatusCode(result.code, void 0);
|
|
93
141
|
const status = typeof result.status === "string" ? result.status : (0, import_http_errors.getCanonicalStatus)(statusCode);
|
|
94
142
|
const message = typeof result.message === "string" ? result.message : "";
|
|
95
143
|
const details = toArray(result.details) || [(0, import_http_errors.createAip193ErrorInfoDetail)(status, errorDomain)];
|
|
@@ -104,17 +152,17 @@ var toStructuredGenericErrorPayload = (result, errorDomain) => {
|
|
|
104
152
|
}
|
|
105
153
|
return {
|
|
106
154
|
error: {
|
|
107
|
-
code:
|
|
108
|
-
status: (0, import_http_errors.getCanonicalStatus)(
|
|
155
|
+
code: FALLBACK_ERROR_STATUS,
|
|
156
|
+
status: (0, import_http_errors.getCanonicalStatus)(FALLBACK_ERROR_STATUS),
|
|
109
157
|
message: String(result),
|
|
110
|
-
details: [(0, import_http_errors.createAip193ErrorInfoDetail)((0, import_http_errors.getCanonicalStatus)(
|
|
158
|
+
details: [(0, import_http_errors.createAip193ErrorInfoDetail)((0, import_http_errors.getCanonicalStatus)(FALLBACK_ERROR_STATUS), errorDomain)]
|
|
111
159
|
}
|
|
112
160
|
};
|
|
113
161
|
};
|
|
114
162
|
var toRfc9457GenericErrorPayload = (result) => {
|
|
115
163
|
const problem = toProblemDetailsSource(result);
|
|
116
164
|
if (problem) {
|
|
117
|
-
const statusCode =
|
|
165
|
+
const statusCode = toErrorStatusCode(problem.status, problem.code);
|
|
118
166
|
return (0, import_http_errors.toRfc9457ErrorPayload)({
|
|
119
167
|
statusCode,
|
|
120
168
|
message: toOptionalString(problem.detail) ?? toOptionalString(problem.message) ?? "",
|
|
@@ -125,16 +173,22 @@ var toRfc9457GenericErrorPayload = (result) => {
|
|
|
125
173
|
});
|
|
126
174
|
}
|
|
127
175
|
return (0, import_http_errors.toRfc9457ErrorPayload)({
|
|
128
|
-
statusCode:
|
|
176
|
+
statusCode: FALLBACK_ERROR_STATUS,
|
|
129
177
|
message: String(result)
|
|
130
178
|
});
|
|
131
179
|
};
|
|
132
180
|
// Annotate the CommonJS export names for ESM import in node:
|
|
133
181
|
0 && (module.exports = {
|
|
182
|
+
FALLBACK_ERROR_MESSAGE,
|
|
183
|
+
FALLBACK_ERROR_STATUS,
|
|
134
184
|
defaultErrorMessageProvider,
|
|
185
|
+
isValidErrorStatusCode,
|
|
186
|
+
normalizeThrownError,
|
|
187
|
+
toErrorStatusCode,
|
|
135
188
|
toRfc9457GenericErrorPayload,
|
|
136
189
|
toRfc9457HttpErrorPayload,
|
|
137
190
|
toSimpleErrorPayload,
|
|
138
191
|
toStructuredGenericErrorPayload,
|
|
139
|
-
toStructuredHttpErrorPayload
|
|
192
|
+
toStructuredHttpErrorPayload,
|
|
193
|
+
validateErrorStatusCode
|
|
140
194
|
});
|
package/error-format.mjs
CHANGED
|
@@ -2,15 +2,24 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createAip193ErrorInfoDetail,
|
|
4
4
|
getCanonicalStatus,
|
|
5
|
+
isHttpErrorStatusCode,
|
|
5
6
|
toAip193ErrorPayload,
|
|
6
|
-
toRfc9457ErrorPayload
|
|
7
|
+
toRfc9457ErrorPayload,
|
|
8
|
+
validateHttpErrorStatusCode
|
|
7
9
|
} from "@web-ts-toolkit/http-errors";
|
|
8
10
|
import { isArray, isPlainObject, isString, toStringRecord } from "@web-ts-toolkit/utils";
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
var FALLBACK_ERROR_STATUS = 500;
|
|
12
|
+
var FALLBACK_ERROR_MESSAGE = "Internal Server Error";
|
|
13
|
+
var isValidErrorStatusCode = isHttpErrorStatusCode;
|
|
14
|
+
var validateErrorStatusCode = (statusCode, source) => {
|
|
15
|
+
return validateHttpErrorStatusCode(statusCode, source);
|
|
16
|
+
};
|
|
17
|
+
var toErrorStatusCode = (status, code, fallbackStatusCode = FALLBACK_ERROR_STATUS) => {
|
|
18
|
+
const statusCode = typeof status === "number" ? status : code;
|
|
19
|
+
if (statusCode === void 0) {
|
|
20
|
+
return fallbackStatusCode;
|
|
12
21
|
}
|
|
13
|
-
return
|
|
22
|
+
return validateErrorStatusCode(statusCode, "error status");
|
|
14
23
|
};
|
|
15
24
|
var toOptionalString = (value) => typeof value === "string" ? value : void 0;
|
|
16
25
|
var toMetadata = (value) => {
|
|
@@ -22,6 +31,41 @@ var toArray = (value) => {
|
|
|
22
31
|
}
|
|
23
32
|
return isArray(value) ? value : [value];
|
|
24
33
|
};
|
|
34
|
+
var describeThrown = (value) => {
|
|
35
|
+
try {
|
|
36
|
+
if (value === null) {
|
|
37
|
+
return "null";
|
|
38
|
+
}
|
|
39
|
+
if (value === void 0) {
|
|
40
|
+
return "undefined";
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === "symbol") {
|
|
43
|
+
return value.toString();
|
|
44
|
+
}
|
|
45
|
+
const stringified = typeof value === "object" && value !== null ? value.message : void 0;
|
|
46
|
+
if (typeof stringified === "string" && stringified.length > 0) {
|
|
47
|
+
return stringified;
|
|
48
|
+
}
|
|
49
|
+
return String(value);
|
|
50
|
+
} catch {
|
|
51
|
+
return FALLBACK_ERROR_MESSAGE;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var normalizeThrownError = (value) => {
|
|
55
|
+
if (value instanceof Error) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
const message = describeThrown(value);
|
|
59
|
+
if (isPlainObject(value)) {
|
|
60
|
+
const record = value;
|
|
61
|
+
const normalized = {
|
|
62
|
+
...record,
|
|
63
|
+
message
|
|
64
|
+
};
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
return { message };
|
|
68
|
+
};
|
|
25
69
|
var toHttpErrorShape = (error, fallbackDomain) => ({
|
|
26
70
|
statusCode: error.statusCode ?? 500,
|
|
27
71
|
status: error.status,
|
|
@@ -42,8 +86,8 @@ var toProblemDetailsSource = (result) => {
|
|
|
42
86
|
return isPlainObject(result) ? result : void 0;
|
|
43
87
|
};
|
|
44
88
|
var defaultErrorMessageProvider = function(error) {
|
|
45
|
-
|
|
46
|
-
return
|
|
89
|
+
void error;
|
|
90
|
+
return FALLBACK_ERROR_MESSAGE;
|
|
47
91
|
};
|
|
48
92
|
var toSimpleErrorPayload = (result) => isString(result) ? { message: result } : { ...result };
|
|
49
93
|
var toStructuredHttpErrorPayload = (error, errorDomain) => toAip193ErrorPayload(toHttpErrorShape(error, errorDomain), errorDomain);
|
|
@@ -51,7 +95,7 @@ var toRfc9457HttpErrorPayload = (error, errorDomain) => toRfc9457ErrorPayload(to
|
|
|
51
95
|
var toStructuredGenericErrorPayload = (result, errorDomain) => {
|
|
52
96
|
if (isPlainObject(result) && isPlainObject(result.error)) {
|
|
53
97
|
const error = result.error;
|
|
54
|
-
const statusCode =
|
|
98
|
+
const statusCode = toErrorStatusCode(error.code, void 0);
|
|
55
99
|
const status = typeof error.status === "string" ? error.status : getCanonicalStatus(statusCode);
|
|
56
100
|
const message = typeof error.message === "string" ? error.message : "";
|
|
57
101
|
const details = toArray(error.details);
|
|
@@ -65,7 +109,7 @@ var toStructuredGenericErrorPayload = (result, errorDomain) => {
|
|
|
65
109
|
};
|
|
66
110
|
}
|
|
67
111
|
if (isPlainObject(result)) {
|
|
68
|
-
const statusCode =
|
|
112
|
+
const statusCode = toErrorStatusCode(result.code, void 0);
|
|
69
113
|
const status = typeof result.status === "string" ? result.status : getCanonicalStatus(statusCode);
|
|
70
114
|
const message = typeof result.message === "string" ? result.message : "";
|
|
71
115
|
const details = toArray(result.details) || [createAip193ErrorInfoDetail(status, errorDomain)];
|
|
@@ -80,17 +124,17 @@ var toStructuredGenericErrorPayload = (result, errorDomain) => {
|
|
|
80
124
|
}
|
|
81
125
|
return {
|
|
82
126
|
error: {
|
|
83
|
-
code:
|
|
84
|
-
status: getCanonicalStatus(
|
|
127
|
+
code: FALLBACK_ERROR_STATUS,
|
|
128
|
+
status: getCanonicalStatus(FALLBACK_ERROR_STATUS),
|
|
85
129
|
message: String(result),
|
|
86
|
-
details: [createAip193ErrorInfoDetail(getCanonicalStatus(
|
|
130
|
+
details: [createAip193ErrorInfoDetail(getCanonicalStatus(FALLBACK_ERROR_STATUS), errorDomain)]
|
|
87
131
|
}
|
|
88
132
|
};
|
|
89
133
|
};
|
|
90
134
|
var toRfc9457GenericErrorPayload = (result) => {
|
|
91
135
|
const problem = toProblemDetailsSource(result);
|
|
92
136
|
if (problem) {
|
|
93
|
-
const statusCode =
|
|
137
|
+
const statusCode = toErrorStatusCode(problem.status, problem.code);
|
|
94
138
|
return toRfc9457ErrorPayload({
|
|
95
139
|
statusCode,
|
|
96
140
|
message: toOptionalString(problem.detail) ?? toOptionalString(problem.message) ?? "",
|
|
@@ -101,15 +145,21 @@ var toRfc9457GenericErrorPayload = (result) => {
|
|
|
101
145
|
});
|
|
102
146
|
}
|
|
103
147
|
return toRfc9457ErrorPayload({
|
|
104
|
-
statusCode:
|
|
148
|
+
statusCode: FALLBACK_ERROR_STATUS,
|
|
105
149
|
message: String(result)
|
|
106
150
|
});
|
|
107
151
|
};
|
|
108
152
|
export {
|
|
153
|
+
FALLBACK_ERROR_MESSAGE,
|
|
154
|
+
FALLBACK_ERROR_STATUS,
|
|
109
155
|
defaultErrorMessageProvider,
|
|
156
|
+
isValidErrorStatusCode,
|
|
157
|
+
normalizeThrownError,
|
|
158
|
+
toErrorStatusCode,
|
|
110
159
|
toRfc9457GenericErrorPayload,
|
|
111
160
|
toRfc9457HttpErrorPayload,
|
|
112
161
|
toSimpleErrorPayload,
|
|
113
162
|
toStructuredGenericErrorPayload,
|
|
114
|
-
toStructuredHttpErrorPayload
|
|
163
|
+
toStructuredHttpErrorPayload,
|
|
164
|
+
validateErrorStatusCode
|
|
115
165
|
};
|
package/http-response.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { CsvResponseOptions, CSVResponse } from './responses/csv.mjs';
|
|
1
2
|
import * as _web_ts_toolkit_http_errors from '@web-ts-toolkit/http-errors';
|
|
2
3
|
import { BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, MethodNotAllowedError, NotAcceptableError, ProxyAuthRequiredError, RequestTimeoutError, ConflictError, GoneError, LengthRequiredError, PreconditionFailedError, PayloadTooLargeError, UriTooLongError, UnsupportedMediaTypeError, RequestedRangeNotSatisfiableError, ExpectationFailedError, TeapotError, MisdirectedRequestError, UnprocessableEntityError, LockedError, FailedDependencyError, UpgradeRequiredError, PreconditionRequiredError, TooManyRequestsError, RequestHeaderFieldsTooLargeError, UnavailableForLegalReasonsError } from '@web-ts-toolkit/http-errors';
|
|
3
4
|
import { OK, Created, Accepted, NonAuthoritativeInfo, NoContent, ResetContent, PartialContent, MultiStatus, AlreadyReported, IMUsed } from './responses/success.mjs';
|
|
4
|
-
import { CSVResponse } from './responses/csv.mjs';
|
|
5
5
|
import './responses/index.mjs';
|
|
6
6
|
|
|
7
7
|
declare const HttpResponse: {
|
|
@@ -43,11 +43,7 @@ declare const HttpResponse: {
|
|
|
43
43
|
requestHeaderFieldsTooLarge: (message?: string | undefined, options?: _web_ts_toolkit_http_errors.HttpErrorOptions | undefined) => RequestHeaderFieldsTooLargeError;
|
|
44
44
|
unavailableForLegalReasons: (message?: string | undefined, options?: _web_ts_toolkit_http_errors.HttpErrorOptions | undefined) => UnavailableForLegalReasonsError;
|
|
45
45
|
json: (data: unknown) => OK<unknown>;
|
|
46
|
-
csv: (dataset?: unknown, options?:
|
|
47
|
-
filename?: string;
|
|
48
|
-
headers?: boolean;
|
|
49
|
-
processor?: (value: unknown) => unknown;
|
|
50
|
-
} | undefined) => CSVResponse;
|
|
46
|
+
csv: (dataset?: unknown, options?: CsvResponseOptions | undefined) => CSVResponse;
|
|
51
47
|
};
|
|
52
48
|
type HttpResponseHelpers = typeof HttpResponse;
|
|
53
49
|
|
package/http-response.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { CsvResponseOptions, CSVResponse } from './responses/csv.js';
|
|
1
2
|
import * as _web_ts_toolkit_http_errors from '@web-ts-toolkit/http-errors';
|
|
2
3
|
import { BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, MethodNotAllowedError, NotAcceptableError, ProxyAuthRequiredError, RequestTimeoutError, ConflictError, GoneError, LengthRequiredError, PreconditionFailedError, PayloadTooLargeError, UriTooLongError, UnsupportedMediaTypeError, RequestedRangeNotSatisfiableError, ExpectationFailedError, TeapotError, MisdirectedRequestError, UnprocessableEntityError, LockedError, FailedDependencyError, UpgradeRequiredError, PreconditionRequiredError, TooManyRequestsError, RequestHeaderFieldsTooLargeError, UnavailableForLegalReasonsError } from '@web-ts-toolkit/http-errors';
|
|
3
4
|
import { OK, Created, Accepted, NonAuthoritativeInfo, NoContent, ResetContent, PartialContent, MultiStatus, AlreadyReported, IMUsed } from './responses/success.js';
|
|
4
|
-
import { CSVResponse } from './responses/csv.js';
|
|
5
5
|
import './responses/index.js';
|
|
6
6
|
|
|
7
7
|
declare const HttpResponse: {
|
|
@@ -43,11 +43,7 @@ declare const HttpResponse: {
|
|
|
43
43
|
requestHeaderFieldsTooLarge: (message?: string | undefined, options?: _web_ts_toolkit_http_errors.HttpErrorOptions | undefined) => RequestHeaderFieldsTooLargeError;
|
|
44
44
|
unavailableForLegalReasons: (message?: string | undefined, options?: _web_ts_toolkit_http_errors.HttpErrorOptions | undefined) => UnavailableForLegalReasonsError;
|
|
45
45
|
json: (data: unknown) => OK<unknown>;
|
|
46
|
-
csv: (dataset?: unknown, options?:
|
|
47
|
-
filename?: string;
|
|
48
|
-
headers?: boolean;
|
|
49
|
-
processor?: (value: unknown) => unknown;
|
|
50
|
-
} | undefined) => CSVResponse;
|
|
46
|
+
csv: (dataset?: unknown, options?: CsvResponseOptions | undefined) => CSVResponse;
|
|
51
47
|
};
|
|
52
48
|
type HttpResponseHelpers = typeof HttpResponse;
|
|
53
49
|
|
package/http-response.js
CHANGED
|
@@ -26,12 +26,18 @@ module.exports = __toCommonJS(http_response_exports);
|
|
|
26
26
|
|
|
27
27
|
// src/responses/index.ts
|
|
28
28
|
var responseBrand = /* @__PURE__ */ Symbol.for("@web-ts-toolkit/express-response-handler.response");
|
|
29
|
+
var validateHttpStatusCode = (statusCode) => {
|
|
30
|
+
if (typeof statusCode !== "number" || !Number.isFinite(statusCode) || !Number.isInteger(statusCode) || statusCode < 100 || statusCode > 599) {
|
|
31
|
+
throw new TypeError("statusCode must be an integer HTTP status code between 100 and 599");
|
|
32
|
+
}
|
|
33
|
+
return statusCode;
|
|
34
|
+
};
|
|
29
35
|
var _a;
|
|
30
36
|
_a = responseBrand;
|
|
31
37
|
var Response = class {
|
|
32
38
|
constructor(statusCode = 200, data) {
|
|
33
39
|
this[_a] = true;
|
|
34
|
-
this.statusCode = statusCode;
|
|
40
|
+
this.statusCode = validateHttpStatusCode(statusCode);
|
|
35
41
|
this.data = data;
|
|
36
42
|
}
|
|
37
43
|
};
|
|
@@ -91,36 +97,157 @@ var IMUsed = class extends Response {
|
|
|
91
97
|
// src/responses/csv.ts
|
|
92
98
|
var import_format = require("@fast-csv/format");
|
|
93
99
|
var import_utils = require("@web-ts-toolkit/utils");
|
|
100
|
+
var import_events = require("events");
|
|
101
|
+
var CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
|
|
102
|
+
var isIterable = (value) => typeof value === "object" && value !== null && Symbol.iterator in value;
|
|
103
|
+
var isAsyncIterable = (value) => typeof value === "object" && value !== null && Symbol.asyncIterator in value;
|
|
104
|
+
var isLazyCsvSource = (value) => !Array.isArray(value) && (isIterable(value) || isAsyncIterable(value));
|
|
105
|
+
var createAsyncIterator = (source) => {
|
|
106
|
+
if (isAsyncIterable(source)) {
|
|
107
|
+
return source[Symbol.asyncIterator]();
|
|
108
|
+
}
|
|
109
|
+
const iterator = (isIterable(source) ? source : (0, import_utils.castArray)(source))[Symbol.iterator]();
|
|
110
|
+
return {
|
|
111
|
+
next: () => Promise.resolve(iterator.next()),
|
|
112
|
+
return: (value) => {
|
|
113
|
+
if (iterator.return) {
|
|
114
|
+
return Promise.resolve(iterator.return(value));
|
|
115
|
+
}
|
|
116
|
+
return Promise.resolve({ done: true, value });
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
var encodeRFC5987Value = (value) => encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
121
|
+
var createQuotedFilenameFallback = (filename) => {
|
|
122
|
+
const fallback = Array.from(filename.replace(/[\\/]/g, "_"), (char) => {
|
|
123
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
124
|
+
return codePoint >= 32 && codePoint <= 126 ? char : "_";
|
|
125
|
+
}).join("");
|
|
126
|
+
return fallback.replace(/(["\\])/g, "\\$1");
|
|
127
|
+
};
|
|
128
|
+
var createAttachmentContentDisposition = (filename) => {
|
|
129
|
+
if (CONTROL_CHARACTER_PATTERN.test(filename)) {
|
|
130
|
+
throw new TypeError("CSV filename cannot contain control characters");
|
|
131
|
+
}
|
|
132
|
+
const fallback = createQuotedFilenameFallback(filename);
|
|
133
|
+
return `attachment; filename="${fallback}"; filename*=UTF-8''${encodeRFC5987Value(filename)}`;
|
|
134
|
+
};
|
|
94
135
|
var csvResponseBrand = /* @__PURE__ */ Symbol.for("@web-ts-toolkit/express-response-handler.csv-response");
|
|
95
136
|
var _a2;
|
|
96
137
|
_a2 = csvResponseBrand;
|
|
97
138
|
var CSVResponse = class {
|
|
98
139
|
constructor(dataset = [], options = {}) {
|
|
99
140
|
this[_a2] = true;
|
|
100
|
-
this.dataset =
|
|
141
|
+
this.dataset = dataset;
|
|
101
142
|
this.filename = options.filename || "download.csv";
|
|
102
143
|
this.processor = options.processor || ((value) => value);
|
|
103
|
-
if ((0, import_utils.isBoolean)(options.headers)) {
|
|
144
|
+
if ((0, import_utils.isBoolean)(options.headers) || Array.isArray(options.headers)) {
|
|
104
145
|
this.headers = options.headers;
|
|
105
|
-
} else if (this.dataset.length > 0) {
|
|
146
|
+
} else if (Array.isArray(this.dataset) && this.dataset.length > 0) {
|
|
106
147
|
this.headers = (0, import_utils.isPlainObject)(this.dataset[0]);
|
|
148
|
+
} else if (isLazyCsvSource(this.dataset)) {
|
|
149
|
+
throw new TypeError("CSV lazy sources require an explicit headers option");
|
|
150
|
+
} else {
|
|
151
|
+
this.headers = (0, import_utils.isPlainObject)(this.dataset);
|
|
107
152
|
}
|
|
108
153
|
}
|
|
109
|
-
streamCsv(res) {
|
|
154
|
+
streamCsv(res, onBeforeOutputError) {
|
|
110
155
|
const stream = (0, import_format.format)({ headers: this.headers });
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
156
|
+
let outputStarted = false;
|
|
157
|
+
let failed = false;
|
|
158
|
+
let finished = false;
|
|
159
|
+
let activeIterator = null;
|
|
160
|
+
let abortStreaming = () => void 0;
|
|
161
|
+
const abortSignal = new Promise((_, reject) => {
|
|
162
|
+
abortStreaming = reject;
|
|
163
|
+
});
|
|
164
|
+
abortSignal.catch(() => void 0);
|
|
165
|
+
const cleanup = () => {
|
|
166
|
+
stream.off("error", fail);
|
|
167
|
+
res.off?.("error", fail);
|
|
168
|
+
res.off?.("close", abort);
|
|
169
|
+
res.off?.("finish", markFinished);
|
|
170
|
+
};
|
|
171
|
+
const closeActiveIterator = () => {
|
|
172
|
+
const iterator = activeIterator;
|
|
173
|
+
activeIterator = null;
|
|
174
|
+
if (iterator?.return) {
|
|
175
|
+
void Promise.resolve(iterator.return()).catch(() => void 0);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const closeResponse = (error) => {
|
|
115
179
|
if (res.destroy) {
|
|
116
180
|
res.destroy(error);
|
|
117
181
|
return;
|
|
118
182
|
}
|
|
119
183
|
res.end();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
};
|
|
185
|
+
const fail = (error) => {
|
|
186
|
+
if (failed) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
failed = true;
|
|
190
|
+
cleanup();
|
|
191
|
+
const normalizedError = error instanceof Error ? error : new Error("CSV response streaming failed");
|
|
192
|
+
abortStreaming(normalizedError);
|
|
193
|
+
closeActiveIterator();
|
|
194
|
+
if (!outputStarted && !res.headersSent) {
|
|
195
|
+
onBeforeOutputError?.(normalizedError);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
stream.destroy();
|
|
199
|
+
closeResponse(normalizedError);
|
|
200
|
+
};
|
|
201
|
+
const abort = () => {
|
|
202
|
+
if (!finished) {
|
|
203
|
+
fail(new Error("CSV response streaming aborted"));
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
const markFinished = () => {
|
|
207
|
+
finished = true;
|
|
208
|
+
};
|
|
209
|
+
const writeRow = async (row) => {
|
|
210
|
+
outputStarted = true;
|
|
211
|
+
if (!stream.write(row)) {
|
|
212
|
+
await Promise.race([(0, import_events.once)(stream, "drain"), abortSignal]);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
stream.on("error", fail);
|
|
216
|
+
stream.on("end", cleanup);
|
|
217
|
+
res.on?.("error", fail);
|
|
218
|
+
res.on?.("close", abort);
|
|
219
|
+
res.on?.("finish", markFinished);
|
|
220
|
+
const pump = async () => {
|
|
221
|
+
try {
|
|
222
|
+
const iterator = createAsyncIterator(this.dataset);
|
|
223
|
+
activeIterator = iterator;
|
|
224
|
+
const first = await iterator.next();
|
|
225
|
+
const firstRow = first.done ? void 0 : this.processor(first.value);
|
|
226
|
+
const contentDisposition = createAttachmentContentDisposition(this.filename);
|
|
227
|
+
res.set("Content-Type", "text/csv");
|
|
228
|
+
res.set("Content-Disposition", contentDisposition);
|
|
229
|
+
stream.pipe(res);
|
|
230
|
+
if (!first.done) {
|
|
231
|
+
await writeRow(firstRow);
|
|
232
|
+
}
|
|
233
|
+
while (!failed) {
|
|
234
|
+
const row = await Promise.race([iterator.next(), abortSignal]);
|
|
235
|
+
if (row.done) {
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
await writeRow(this.processor(row.value));
|
|
239
|
+
}
|
|
240
|
+
if (!failed) {
|
|
241
|
+
finished = true;
|
|
242
|
+
stream.end();
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
fail(error);
|
|
246
|
+
} finally {
|
|
247
|
+
activeIterator = null;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
void pump();
|
|
124
251
|
}
|
|
125
252
|
};
|
|
126
253
|
|