@web-ts-toolkit/express-response-handler 0.42.2 → 0.44.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 +29 -8
- package/create-handler.js +376 -117
- package/create-handler.mjs +376 -117
- package/http-response.d.mts +10 -10
- package/http-response.d.ts +10 -10
- package/http-response.js +146 -38
- package/http-response.mjs +146 -38
- package/index.js +376 -117
- package/index.mjs +376 -117
- package/llms.txt +21 -2
- package/package.json +28 -8
- package/public-types.d.mts +11 -2
- package/public-types.d.ts +11 -2
- package/responses/csv.d.mts +23 -0
- package/responses/csv.d.ts +23 -0
- package/responses/csv.js +136 -28
- package/responses/csv.mjs +136 -28
package/create-handler.js
CHANGED
|
@@ -39,7 +39,6 @@ var import_utils3 = require("@web-ts-toolkit/utils");
|
|
|
39
39
|
// src/responses/csv.ts
|
|
40
40
|
var import_format = require("@fast-csv/format");
|
|
41
41
|
var import_utils = require("@web-ts-toolkit/utils");
|
|
42
|
-
var import_events = require("events");
|
|
43
42
|
var CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
|
|
44
43
|
var isIterable = (value) => typeof value === "object" && value !== null && Symbol.iterator in value;
|
|
45
44
|
var isAsyncIterable = (value) => typeof value === "object" && value !== null && Symbol.asyncIterator in value;
|
|
@@ -103,28 +102,66 @@ var CSVResponse = class {
|
|
|
103
102
|
this.headers = (0, import_utils.isPlainObject)(this.dataset);
|
|
104
103
|
}
|
|
105
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Streams the dataset as CSV into `res`.
|
|
107
|
+
*
|
|
108
|
+
* Contract choice (B-ERH-06): minimal runtime-safe fallback, no breaking
|
|
109
|
+
* signature change. `onBeforeOutputError` stays optional.
|
|
110
|
+
*
|
|
111
|
+
* - Pre-output failure (invalid filename, failed first read, first-row
|
|
112
|
+
* processor throw, before headers are sent) with an error owner:
|
|
113
|
+
* the owner is invoked exactly once with the normalized failure and owns
|
|
114
|
+
* destination termination (the handler owner renders a single redacted
|
|
115
|
+
* JSON error; direct callers must end/destroy `res` themselves). A
|
|
116
|
+
* throwing owner is contained and the destination is then destroyed with
|
|
117
|
+
* the original failure so it never stays open.
|
|
118
|
+
* - Pre-output failure without an error owner: deterministic fallback —
|
|
119
|
+
* the destination is destroyed with the normalized failure (or ended
|
|
120
|
+
* when `destroy` is unavailable), observable via `error` + `close`.
|
|
121
|
+
* - Post-output failure (after piping starts): the destination is always
|
|
122
|
+
* destroyed with the failure, with or without an owner.
|
|
123
|
+
*
|
|
124
|
+
* Non-`Error` failures are wrapped in an `Error` with the original kept as
|
|
125
|
+
* `cause`. A throwing owner never displaces the original failure and never
|
|
126
|
+
* escapes as a process-level error.
|
|
127
|
+
*/
|
|
106
128
|
streamCsv(res, onBeforeOutputError) {
|
|
107
129
|
const stream = (0, import_format.format)({ headers: this.headers });
|
|
108
130
|
let outputStarted = false;
|
|
109
131
|
let failed = false;
|
|
110
132
|
let finished = false;
|
|
133
|
+
let cleaned = false;
|
|
111
134
|
let activeIterator = null;
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
135
|
+
const abortWaiters = /* @__PURE__ */ new Set();
|
|
136
|
+
const notifyAborted = () => {
|
|
137
|
+
for (const notify of Array.from(abortWaiters)) {
|
|
138
|
+
abortWaiters.delete(notify);
|
|
139
|
+
try {
|
|
140
|
+
notify();
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
117
145
|
const cleanup = () => {
|
|
146
|
+
if (cleaned) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
cleaned = true;
|
|
118
150
|
stream.off("error", fail);
|
|
119
151
|
res.off?.("error", fail);
|
|
120
|
-
res.off?.("close",
|
|
121
|
-
res.off?.("finish",
|
|
152
|
+
res.off?.("close", handleClose);
|
|
153
|
+
res.off?.("finish", handleFinish);
|
|
122
154
|
};
|
|
123
155
|
const closeActiveIterator = () => {
|
|
124
156
|
const iterator = activeIterator;
|
|
125
157
|
activeIterator = null;
|
|
126
|
-
if (iterator?.return) {
|
|
158
|
+
if (!iterator?.return) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
127
162
|
void Promise.resolve(iterator.return()).catch(() => void 0);
|
|
163
|
+
} catch {
|
|
164
|
+
return;
|
|
128
165
|
}
|
|
129
166
|
};
|
|
130
167
|
const closeResponse = (error) => {
|
|
@@ -139,58 +176,129 @@ var CSVResponse = class {
|
|
|
139
176
|
return;
|
|
140
177
|
}
|
|
141
178
|
failed = true;
|
|
142
|
-
|
|
143
|
-
const normalizedError = error instanceof Error ? error :
|
|
144
|
-
|
|
179
|
+
notifyAborted();
|
|
180
|
+
const normalizedError = error instanceof Error ? error : (() => {
|
|
181
|
+
const wrapped = new Error("CSV response streaming failed");
|
|
182
|
+
try {
|
|
183
|
+
wrapped.cause = error;
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
return wrapped;
|
|
187
|
+
})();
|
|
145
188
|
closeActiveIterator();
|
|
146
189
|
if (!outputStarted && !res.headersSent) {
|
|
147
|
-
|
|
190
|
+
stream.destroy();
|
|
191
|
+
if (onBeforeOutputError) {
|
|
192
|
+
cleanup();
|
|
193
|
+
try {
|
|
194
|
+
onBeforeOutputError(normalizedError);
|
|
195
|
+
} catch {
|
|
196
|
+
try {
|
|
197
|
+
res.on?.("error", () => void 0);
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
closeResponse(normalizedError);
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
closeResponse(normalizedError);
|
|
209
|
+
} catch {
|
|
210
|
+
cleanup();
|
|
211
|
+
}
|
|
148
212
|
return;
|
|
149
213
|
}
|
|
150
214
|
stream.destroy();
|
|
151
215
|
closeResponse(normalizedError);
|
|
152
216
|
};
|
|
153
|
-
const
|
|
154
|
-
if (!finished) {
|
|
217
|
+
const handleClose = () => {
|
|
218
|
+
if (!finished && !failed) {
|
|
155
219
|
fail(new Error("CSV response streaming aborted"));
|
|
220
|
+
cleanup();
|
|
221
|
+
return;
|
|
156
222
|
}
|
|
223
|
+
cleanup();
|
|
157
224
|
};
|
|
158
|
-
const
|
|
225
|
+
const handleFinish = () => {
|
|
159
226
|
finished = true;
|
|
227
|
+
cleanup();
|
|
160
228
|
};
|
|
161
229
|
const writeRow = async (row) => {
|
|
230
|
+
if (failed) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
162
233
|
outputStarted = true;
|
|
163
234
|
if (!stream.write(row)) {
|
|
164
|
-
await Promise
|
|
235
|
+
await new Promise((resolve) => {
|
|
236
|
+
if (failed) {
|
|
237
|
+
resolve();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const onDrain = () => {
|
|
241
|
+
stream.off("drain", onDrain);
|
|
242
|
+
abortWaiters.delete(onAbort);
|
|
243
|
+
resolve();
|
|
244
|
+
};
|
|
245
|
+
const onAbort = () => {
|
|
246
|
+
stream.off("drain", onDrain);
|
|
247
|
+
resolve();
|
|
248
|
+
};
|
|
249
|
+
abortWaiters.add(onAbort);
|
|
250
|
+
stream.once("drain", onDrain);
|
|
251
|
+
});
|
|
165
252
|
}
|
|
166
253
|
};
|
|
167
254
|
stream.on("error", fail);
|
|
168
|
-
stream.on("end", cleanup);
|
|
169
255
|
res.on?.("error", fail);
|
|
170
|
-
res.on?.("close",
|
|
171
|
-
res.on?.("finish",
|
|
256
|
+
res.on?.("close", handleClose);
|
|
257
|
+
res.on?.("finish", handleFinish);
|
|
172
258
|
const pump = async () => {
|
|
173
259
|
try {
|
|
174
260
|
const iterator = createAsyncIterator(this.dataset);
|
|
175
261
|
activeIterator = iterator;
|
|
176
262
|
const first = await iterator.next();
|
|
177
|
-
|
|
263
|
+
if (failed) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
let firstRow;
|
|
267
|
+
if (!first.done) {
|
|
268
|
+
firstRow = this.processor(first.value);
|
|
269
|
+
if (failed) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
178
273
|
const contentDisposition = createAttachmentContentDisposition(this.filename);
|
|
274
|
+
if (failed) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
179
277
|
res.set("Content-Type", "text/csv");
|
|
180
278
|
res.set("Content-Disposition", contentDisposition);
|
|
181
279
|
stream.pipe(res);
|
|
182
|
-
if (
|
|
183
|
-
|
|
280
|
+
if (first.done) {
|
|
281
|
+
if (!failed) {
|
|
282
|
+
stream.end();
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
184
285
|
}
|
|
286
|
+
await writeRow(firstRow);
|
|
185
287
|
while (!failed) {
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
288
|
+
const result = await iterator.next();
|
|
289
|
+
if (failed) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (result.done) {
|
|
188
293
|
break;
|
|
189
294
|
}
|
|
190
|
-
|
|
295
|
+
const processed = this.processor(result.value);
|
|
296
|
+
if (failed) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
await writeRow(processed);
|
|
191
300
|
}
|
|
192
301
|
if (!failed) {
|
|
193
|
-
finished = true;
|
|
194
302
|
stream.end();
|
|
195
303
|
}
|
|
196
304
|
} catch (error) {
|
|
@@ -199,7 +307,7 @@ var CSVResponse = class {
|
|
|
199
307
|
activeIterator = null;
|
|
200
308
|
}
|
|
201
309
|
};
|
|
202
|
-
void pump();
|
|
310
|
+
void pump().then(void 0, () => void 0);
|
|
203
311
|
}
|
|
204
312
|
};
|
|
205
313
|
|
|
@@ -286,16 +394,16 @@ var IMUsed = class extends Response {
|
|
|
286
394
|
// src/http-response.ts
|
|
287
395
|
var import_http_errors = require("@web-ts-toolkit/http-errors");
|
|
288
396
|
var HttpResponse = {
|
|
289
|
-
ok: (
|
|
290
|
-
created: (
|
|
291
|
-
accepted: (
|
|
292
|
-
nonAuthoritativeInfo: (
|
|
397
|
+
ok: (data) => new OK(data),
|
|
398
|
+
created: (data) => new Created(data),
|
|
399
|
+
accepted: (data) => new Accepted(data),
|
|
400
|
+
nonAuthoritativeInfo: (data) => new NonAuthoritativeInfo(data),
|
|
293
401
|
noContent: (...args) => new NoContent(...args),
|
|
294
|
-
resetContent: (
|
|
295
|
-
partialContent: (
|
|
296
|
-
multiStatus: (
|
|
297
|
-
alreadyReported: (
|
|
298
|
-
imUsed: (
|
|
402
|
+
resetContent: (data) => new ResetContent(data),
|
|
403
|
+
partialContent: (data) => new PartialContent(data),
|
|
404
|
+
multiStatus: (data) => new MultiStatus(data),
|
|
405
|
+
alreadyReported: (data) => new AlreadyReported(data),
|
|
406
|
+
imUsed: (data) => new IMUsed(data),
|
|
299
407
|
badRequest: (...args) => new import_http_errors.BadRequestError(...args),
|
|
300
408
|
unauthorized: (...args) => new import_http_errors.UnauthorizedError(...args),
|
|
301
409
|
forbidden: (...args) => new import_http_errors.ForbiddenError(...args),
|
|
@@ -323,7 +431,7 @@ var HttpResponse = {
|
|
|
323
431
|
tooManyRequests: (...args) => new import_http_errors.TooManyRequestsError(...args),
|
|
324
432
|
requestHeaderFieldsTooLarge: (...args) => new import_http_errors.RequestHeaderFieldsTooLargeError(...args),
|
|
325
433
|
unavailableForLegalReasons: (...args) => new import_http_errors.UnavailableForLegalReasonsError(...args),
|
|
326
|
-
json: (
|
|
434
|
+
json: (data) => new OK(data),
|
|
327
435
|
csv: (...args) => new CSVResponse(...args)
|
|
328
436
|
};
|
|
329
437
|
|
|
@@ -530,9 +638,10 @@ var normalizeMiddlewareList = (fns) => {
|
|
|
530
638
|
return fns;
|
|
531
639
|
}
|
|
532
640
|
if ((0, import_utils3.isArray)(fns[0])) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
641
|
+
const nested = fns[0];
|
|
642
|
+
import_assert.default.ok(nested.length > 0, "at least one middleware handler is required");
|
|
643
|
+
nested.forEach(assertMiddleware);
|
|
644
|
+
return [...nested];
|
|
536
645
|
}
|
|
537
646
|
assertMiddleware(fns[0]);
|
|
538
647
|
return [fns[0]];
|
|
@@ -563,17 +672,68 @@ function createHandler(options = {}) {
|
|
|
563
672
|
let postJsonHook = null;
|
|
564
673
|
let preErrorHook = null;
|
|
565
674
|
let postErrorHook = null;
|
|
566
|
-
const updateHook = (fn, name, setState
|
|
675
|
+
const updateHook = (fn, name, setState) => {
|
|
567
676
|
if (fn === null) {
|
|
568
677
|
setState(null, null);
|
|
569
|
-
rebuild();
|
|
570
678
|
return;
|
|
571
679
|
}
|
|
572
680
|
import_assert.default.ok((0, import_utils3.isFunction)(fn), `${name} hook must be a function`);
|
|
573
681
|
setState(fn, promisify(fn));
|
|
574
|
-
rebuild();
|
|
575
682
|
};
|
|
576
|
-
const
|
|
683
|
+
const describeTerminalFailure = (failure) => {
|
|
684
|
+
try {
|
|
685
|
+
if (failure === void 0) {
|
|
686
|
+
return "undefined";
|
|
687
|
+
}
|
|
688
|
+
if (failure === null) {
|
|
689
|
+
return "null";
|
|
690
|
+
}
|
|
691
|
+
if (typeof failure === "symbol") {
|
|
692
|
+
return failure.toString();
|
|
693
|
+
}
|
|
694
|
+
if (typeof failure === "string") {
|
|
695
|
+
return failure;
|
|
696
|
+
}
|
|
697
|
+
if (typeof failure === "object" && failure !== null) {
|
|
698
|
+
const message = failure.message;
|
|
699
|
+
if (typeof message === "string" && message.length > 0) {
|
|
700
|
+
return message;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return String(failure);
|
|
704
|
+
} catch {
|
|
705
|
+
return "Internal Server Error";
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
const toTerminalError = (failure) => {
|
|
709
|
+
if (failure instanceof Error) {
|
|
710
|
+
return failure;
|
|
711
|
+
}
|
|
712
|
+
const error = new Error(describeTerminalFailure(failure));
|
|
713
|
+
try {
|
|
714
|
+
error.cause = failure;
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
return error;
|
|
718
|
+
};
|
|
719
|
+
const claimSlot = (event, slot) => {
|
|
720
|
+
if (event[slot]) {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
event[slot] = true;
|
|
724
|
+
return true;
|
|
725
|
+
};
|
|
726
|
+
const delegateTerminalError = (_res, next, terminalError, event) => {
|
|
727
|
+
if (!claimSlot(event, "canceled")) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
next(toTerminalError(terminalError));
|
|
731
|
+
};
|
|
732
|
+
const reportHookObservation = (_res, next, observation, event) => {
|
|
733
|
+
if (!claimSlot(event, "reported")) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
next(toTerminalError(observation));
|
|
577
737
|
};
|
|
578
738
|
const sendBaseJson = function(res, data, event, onBeforeOutputError) {
|
|
579
739
|
if (shouldSkipResponse(res, event)) {
|
|
@@ -635,26 +795,33 @@ function createHandler(options = {}) {
|
|
|
635
795
|
}
|
|
636
796
|
invokePostHook(hook, value, onFailure);
|
|
637
797
|
};
|
|
638
|
-
const terminalErrorBoundary = (res, next, terminalError, event) => {
|
|
639
|
-
if (event.canceled) {
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
next(terminalError);
|
|
643
|
-
};
|
|
644
798
|
const dispatchError = (res, next, err, event) => {
|
|
645
|
-
const
|
|
646
|
-
|
|
799
|
+
const reportTerminal = (failure) => {
|
|
800
|
+
delegateTerminalError(res, next, failure, event);
|
|
801
|
+
};
|
|
802
|
+
const reportObservation = (failure) => {
|
|
803
|
+
reportHookObservation(res, next, failure, event);
|
|
647
804
|
};
|
|
648
805
|
const sendFormatted = (failure) => {
|
|
806
|
+
if (event.canceled) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
649
809
|
let didSend;
|
|
650
810
|
try {
|
|
651
811
|
didSend = sendBaseError(res, failure, event);
|
|
652
812
|
} catch (senderFailure) {
|
|
653
|
-
|
|
813
|
+
reportTerminal(senderFailure === void 0 ? failure : senderFailure);
|
|
654
814
|
return;
|
|
655
815
|
}
|
|
656
|
-
if (didSend
|
|
657
|
-
|
|
816
|
+
if (!didSend) {
|
|
817
|
+
if (res.headersSent) {
|
|
818
|
+
reportTerminal(failure);
|
|
819
|
+
}
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
claimSlot(event, "canceled");
|
|
823
|
+
if (postErrorHook) {
|
|
824
|
+
invokePostHookOnFinish(res, postErrorHook, failure, reportObservation);
|
|
658
825
|
}
|
|
659
826
|
};
|
|
660
827
|
if (event.canceled) {
|
|
@@ -670,39 +837,151 @@ function createHandler(options = {}) {
|
|
|
670
837
|
}
|
|
671
838
|
runSender();
|
|
672
839
|
};
|
|
673
|
-
const dispatchValue = (res, next, data, event
|
|
674
|
-
|
|
675
|
-
|
|
840
|
+
const dispatchValue = (res, next, data, event) => {
|
|
841
|
+
if (data === void 0) {
|
|
842
|
+
if (event.canceled) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
if (res.headersSent) {
|
|
846
|
+
claimSlot(event, "canceled");
|
|
847
|
+
}
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (event.canceled) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (res.headersSent) {
|
|
854
|
+
delegateTerminalError(res, next, data, event);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
const reportTerminal = (failure) => {
|
|
858
|
+
if (!event.canceled) {
|
|
859
|
+
delegateTerminalError(res, next, failure, event);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
reportHookObservation(res, next, failure, event);
|
|
863
|
+
};
|
|
864
|
+
const reportObservation = (failure) => {
|
|
865
|
+
reportHookObservation(res, next, failure, event);
|
|
676
866
|
};
|
|
677
|
-
|
|
867
|
+
let fallbackOccurred = false;
|
|
868
|
+
let autoSendClaimed = false;
|
|
869
|
+
const schedulePostJson = (value) => {
|
|
870
|
+
const activePostJsonHook = postJsonHook;
|
|
871
|
+
if (!activePostJsonHook) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const guardedHook = (observed) => {
|
|
875
|
+
if (fallbackOccurred) {
|
|
876
|
+
return Promise.resolve();
|
|
877
|
+
}
|
|
878
|
+
return activePostJsonHook(observed);
|
|
879
|
+
};
|
|
880
|
+
invokePostHookOnFinish(res, guardedHook, value, reportObservation);
|
|
881
|
+
};
|
|
882
|
+
const scheduleFallbackPostError = (failure) => {
|
|
883
|
+
const activePostErrorHook = postErrorHook;
|
|
884
|
+
if (!activePostErrorHook) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
invokePostHookOnFinish(res, activePostErrorHook, failure, reportObservation);
|
|
888
|
+
};
|
|
889
|
+
const sendFallbackFormatted = (failure) => {
|
|
890
|
+
if (!autoSendClaimed && event.canceled) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if (res.headersSent) {
|
|
894
|
+
reportTerminal(failure);
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
if (autoSendClaimed) {
|
|
898
|
+
const bypassEvent = { canceled: false, reported: event.reported };
|
|
899
|
+
try {
|
|
900
|
+
const didFallbackSend = sendBaseError(res, failure, bypassEvent);
|
|
901
|
+
if (didFallbackSend) {
|
|
902
|
+
scheduleFallbackPostError(failure);
|
|
903
|
+
} else {
|
|
904
|
+
reportTerminal(failure);
|
|
905
|
+
}
|
|
906
|
+
} catch (senderFailure) {
|
|
907
|
+
reportTerminal(senderFailure === void 0 ? failure : senderFailure);
|
|
908
|
+
}
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
678
911
|
try {
|
|
679
|
-
sendBaseError(res, failure, event);
|
|
912
|
+
const didFallbackSend = sendBaseError(res, failure, event);
|
|
913
|
+
if (didFallbackSend) {
|
|
914
|
+
claimSlot(event, "canceled");
|
|
915
|
+
scheduleFallbackPostError(failure);
|
|
916
|
+
} else if (res.headersSent) {
|
|
917
|
+
reportTerminal(failure);
|
|
918
|
+
}
|
|
680
919
|
} catch (senderFailure) {
|
|
681
|
-
|
|
920
|
+
reportTerminal(senderFailure === void 0 ? failure : senderFailure);
|
|
682
921
|
}
|
|
683
922
|
};
|
|
923
|
+
const runFallbackLifecycle = (originalFailure) => {
|
|
924
|
+
if (fallbackOccurred) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
fallbackOccurred = true;
|
|
928
|
+
if (!preErrorHook) {
|
|
929
|
+
sendFallbackFormatted(originalFailure);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
preErrorHook(originalFailure).then(
|
|
933
|
+
() => sendFallbackFormatted(originalFailure),
|
|
934
|
+
(hookErr) => sendFallbackFormatted(hookErr === void 0 ? originalFailure : hookErr)
|
|
935
|
+
);
|
|
936
|
+
};
|
|
937
|
+
const sendFallbackError = (failure) => {
|
|
938
|
+
runFallbackLifecycle(failure);
|
|
939
|
+
};
|
|
684
940
|
const runSender = () => {
|
|
941
|
+
if (event.canceled) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (res.headersSent) {
|
|
945
|
+
delegateTerminalError(res, next, data, event);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
685
948
|
let didSend;
|
|
686
949
|
try {
|
|
687
|
-
didSend = sendBaseJson(res, data, event,
|
|
950
|
+
didSend = sendBaseJson(res, data, event, sendFallbackError);
|
|
688
951
|
} catch (senderFailure) {
|
|
689
952
|
const failure = senderFailure === void 0 ? new Error("response serialization failed") : senderFailure;
|
|
690
953
|
if (res.headersSent) {
|
|
691
|
-
|
|
954
|
+
reportTerminal(failure);
|
|
692
955
|
return;
|
|
693
956
|
}
|
|
694
|
-
|
|
957
|
+
runFallbackLifecycle(failure);
|
|
695
958
|
return;
|
|
696
959
|
}
|
|
697
|
-
if (didSend
|
|
698
|
-
|
|
960
|
+
if (!didSend) {
|
|
961
|
+
if (res.headersSent) {
|
|
962
|
+
delegateTerminalError(res, next, data, event);
|
|
963
|
+
}
|
|
964
|
+
return;
|
|
699
965
|
}
|
|
700
|
-
|
|
966
|
+
claimSlot(event, "canceled");
|
|
967
|
+
autoSendClaimed = true;
|
|
968
|
+
schedulePostJson(data);
|
|
701
969
|
};
|
|
702
970
|
if (preJsonHook) {
|
|
703
971
|
preJsonHook(data).then(
|
|
704
|
-
|
|
705
|
-
|
|
972
|
+
() => {
|
|
973
|
+
runSender();
|
|
974
|
+
},
|
|
975
|
+
(hookErr) => {
|
|
976
|
+
if (event.canceled) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (res.headersSent) {
|
|
980
|
+
runFallbackLifecycle(hookErr === void 0 ? new Error("pre-json hook failed") : hookErr);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
runFallbackLifecycle(hookErr === void 0 ? new Error("pre-json hook failed") : hookErr);
|
|
984
|
+
}
|
|
706
985
|
);
|
|
707
986
|
return;
|
|
708
987
|
}
|
|
@@ -710,15 +989,13 @@ function createHandler(options = {}) {
|
|
|
710
989
|
};
|
|
711
990
|
const nextFn = function(event, next) {
|
|
712
991
|
return function(error) {
|
|
713
|
-
if (event
|
|
992
|
+
if (!claimSlot(event, "canceled")) {
|
|
714
993
|
return;
|
|
715
994
|
}
|
|
716
995
|
if (error === void 0) {
|
|
717
|
-
event.canceled = true;
|
|
718
996
|
next();
|
|
719
997
|
return;
|
|
720
998
|
}
|
|
721
|
-
event.canceled = true;
|
|
722
999
|
next(error);
|
|
723
1000
|
};
|
|
724
1001
|
};
|
|
@@ -727,18 +1004,17 @@ function createHandler(options = {}) {
|
|
|
727
1004
|
if (event.canceled) {
|
|
728
1005
|
return;
|
|
729
1006
|
}
|
|
730
|
-
|
|
731
|
-
dispatchError(res, next, event.nextError, event);
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
dispatchValue(res, next, resolved, event, () => void 0);
|
|
1007
|
+
dispatchValue(res, next, resolved, event);
|
|
735
1008
|
};
|
|
736
1009
|
if ((0, import_utils3.isPromise)(result)) {
|
|
737
1010
|
Promise.resolve(result).then(
|
|
738
1011
|
(resolved) => finalize(resolved),
|
|
739
1012
|
(err) => {
|
|
1013
|
+
if (event.canceled) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
740
1016
|
if (res.headersSent) {
|
|
741
|
-
next
|
|
1017
|
+
delegateTerminalError(res, next, err, event);
|
|
742
1018
|
return;
|
|
743
1019
|
}
|
|
744
1020
|
dispatchError(res, next, err, event);
|
|
@@ -750,13 +1026,16 @@ function createHandler(options = {}) {
|
|
|
750
1026
|
};
|
|
751
1027
|
const routerFn = function(fn) {
|
|
752
1028
|
return function(req, res, next) {
|
|
753
|
-
const event = { canceled: false,
|
|
1029
|
+
const event = { canceled: false, reported: false };
|
|
754
1030
|
try {
|
|
755
1031
|
const result = fn(req, res, nextFn(event, next));
|
|
756
1032
|
runLifecycle(res, next, result, event);
|
|
757
1033
|
} catch (err) {
|
|
1034
|
+
if (event.canceled) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
758
1037
|
if (res.headersSent) {
|
|
759
|
-
next
|
|
1038
|
+
delegateTerminalError(res, next, err, event);
|
|
760
1039
|
return;
|
|
761
1040
|
}
|
|
762
1041
|
dispatchError(res, next, err, event);
|
|
@@ -782,57 +1061,37 @@ function createHandler(options = {}) {
|
|
|
782
1061
|
return preJson;
|
|
783
1062
|
},
|
|
784
1063
|
set preJson(fn) {
|
|
785
|
-
updateHook(
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
preJson = syncHook;
|
|
790
|
-
preJsonHook = asyncHook;
|
|
791
|
-
},
|
|
792
|
-
noopRebuild
|
|
793
|
-
);
|
|
1064
|
+
updateHook(fn, "pre-json", (syncHook, asyncHook) => {
|
|
1065
|
+
preJson = syncHook;
|
|
1066
|
+
preJsonHook = asyncHook;
|
|
1067
|
+
});
|
|
794
1068
|
},
|
|
795
1069
|
get postJson() {
|
|
796
1070
|
return postJson;
|
|
797
1071
|
},
|
|
798
1072
|
set postJson(fn) {
|
|
799
|
-
updateHook(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
postJson = syncHook;
|
|
804
|
-
postJsonHook = asyncHook;
|
|
805
|
-
},
|
|
806
|
-
noopRebuild
|
|
807
|
-
);
|
|
1073
|
+
updateHook(fn, "post-json", (syncHook, asyncHook) => {
|
|
1074
|
+
postJson = syncHook;
|
|
1075
|
+
postJsonHook = asyncHook;
|
|
1076
|
+
});
|
|
808
1077
|
},
|
|
809
1078
|
get preError() {
|
|
810
1079
|
return preError;
|
|
811
1080
|
},
|
|
812
1081
|
set preError(fn) {
|
|
813
|
-
updateHook(
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
preError = syncHook;
|
|
818
|
-
preErrorHook = asyncHook;
|
|
819
|
-
},
|
|
820
|
-
noopRebuild
|
|
821
|
-
);
|
|
1082
|
+
updateHook(fn, "pre-error", (syncHook, asyncHook) => {
|
|
1083
|
+
preError = syncHook;
|
|
1084
|
+
preErrorHook = asyncHook;
|
|
1085
|
+
});
|
|
822
1086
|
},
|
|
823
1087
|
get postError() {
|
|
824
1088
|
return postError;
|
|
825
1089
|
},
|
|
826
1090
|
set postError(fn) {
|
|
827
|
-
updateHook(
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
postError = syncHook;
|
|
832
|
-
postErrorHook = asyncHook;
|
|
833
|
-
},
|
|
834
|
-
noopRebuild
|
|
835
|
-
);
|
|
1091
|
+
updateHook(fn, "post-error", (syncHook, asyncHook) => {
|
|
1092
|
+
postError = syncHook;
|
|
1093
|
+
postErrorHook = asyncHook;
|
|
1094
|
+
});
|
|
836
1095
|
}
|
|
837
1096
|
};
|
|
838
1097
|
return handler;
|