@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/index.js
CHANGED
|
@@ -57,7 +57,6 @@ var import_utils3 = require("@web-ts-toolkit/utils");
|
|
|
57
57
|
// src/responses/csv.ts
|
|
58
58
|
var import_format = require("@fast-csv/format");
|
|
59
59
|
var import_utils = require("@web-ts-toolkit/utils");
|
|
60
|
-
var import_events = require("events");
|
|
61
60
|
var CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
|
|
62
61
|
var isIterable = (value) => typeof value === "object" && value !== null && Symbol.iterator in value;
|
|
63
62
|
var isAsyncIterable = (value) => typeof value === "object" && value !== null && Symbol.asyncIterator in value;
|
|
@@ -121,28 +120,66 @@ var CSVResponse = class {
|
|
|
121
120
|
this.headers = (0, import_utils.isPlainObject)(this.dataset);
|
|
122
121
|
}
|
|
123
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Streams the dataset as CSV into `res`.
|
|
125
|
+
*
|
|
126
|
+
* Contract choice (B-ERH-06): minimal runtime-safe fallback, no breaking
|
|
127
|
+
* signature change. `onBeforeOutputError` stays optional.
|
|
128
|
+
*
|
|
129
|
+
* - Pre-output failure (invalid filename, failed first read, first-row
|
|
130
|
+
* processor throw, before headers are sent) with an error owner:
|
|
131
|
+
* the owner is invoked exactly once with the normalized failure and owns
|
|
132
|
+
* destination termination (the handler owner renders a single redacted
|
|
133
|
+
* JSON error; direct callers must end/destroy `res` themselves). A
|
|
134
|
+
* throwing owner is contained and the destination is then destroyed with
|
|
135
|
+
* the original failure so it never stays open.
|
|
136
|
+
* - Pre-output failure without an error owner: deterministic fallback —
|
|
137
|
+
* the destination is destroyed with the normalized failure (or ended
|
|
138
|
+
* when `destroy` is unavailable), observable via `error` + `close`.
|
|
139
|
+
* - Post-output failure (after piping starts): the destination is always
|
|
140
|
+
* destroyed with the failure, with or without an owner.
|
|
141
|
+
*
|
|
142
|
+
* Non-`Error` failures are wrapped in an `Error` with the original kept as
|
|
143
|
+
* `cause`. A throwing owner never displaces the original failure and never
|
|
144
|
+
* escapes as a process-level error.
|
|
145
|
+
*/
|
|
124
146
|
streamCsv(res, onBeforeOutputError) {
|
|
125
147
|
const stream = (0, import_format.format)({ headers: this.headers });
|
|
126
148
|
let outputStarted = false;
|
|
127
149
|
let failed = false;
|
|
128
150
|
let finished = false;
|
|
151
|
+
let cleaned = false;
|
|
129
152
|
let activeIterator = null;
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
153
|
+
const abortWaiters = /* @__PURE__ */ new Set();
|
|
154
|
+
const notifyAborted = () => {
|
|
155
|
+
for (const notify of Array.from(abortWaiters)) {
|
|
156
|
+
abortWaiters.delete(notify);
|
|
157
|
+
try {
|
|
158
|
+
notify();
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
135
163
|
const cleanup = () => {
|
|
164
|
+
if (cleaned) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
cleaned = true;
|
|
136
168
|
stream.off("error", fail);
|
|
137
169
|
res.off?.("error", fail);
|
|
138
|
-
res.off?.("close",
|
|
139
|
-
res.off?.("finish",
|
|
170
|
+
res.off?.("close", handleClose);
|
|
171
|
+
res.off?.("finish", handleFinish);
|
|
140
172
|
};
|
|
141
173
|
const closeActiveIterator = () => {
|
|
142
174
|
const iterator = activeIterator;
|
|
143
175
|
activeIterator = null;
|
|
144
|
-
if (iterator?.return) {
|
|
176
|
+
if (!iterator?.return) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
145
180
|
void Promise.resolve(iterator.return()).catch(() => void 0);
|
|
181
|
+
} catch {
|
|
182
|
+
return;
|
|
146
183
|
}
|
|
147
184
|
};
|
|
148
185
|
const closeResponse = (error) => {
|
|
@@ -157,58 +194,129 @@ var CSVResponse = class {
|
|
|
157
194
|
return;
|
|
158
195
|
}
|
|
159
196
|
failed = true;
|
|
160
|
-
|
|
161
|
-
const normalizedError = error instanceof Error ? error :
|
|
162
|
-
|
|
197
|
+
notifyAborted();
|
|
198
|
+
const normalizedError = error instanceof Error ? error : (() => {
|
|
199
|
+
const wrapped = new Error("CSV response streaming failed");
|
|
200
|
+
try {
|
|
201
|
+
wrapped.cause = error;
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
return wrapped;
|
|
205
|
+
})();
|
|
163
206
|
closeActiveIterator();
|
|
164
207
|
if (!outputStarted && !res.headersSent) {
|
|
165
|
-
|
|
208
|
+
stream.destroy();
|
|
209
|
+
if (onBeforeOutputError) {
|
|
210
|
+
cleanup();
|
|
211
|
+
try {
|
|
212
|
+
onBeforeOutputError(normalizedError);
|
|
213
|
+
} catch {
|
|
214
|
+
try {
|
|
215
|
+
res.on?.("error", () => void 0);
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
closeResponse(normalizedError);
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
closeResponse(normalizedError);
|
|
227
|
+
} catch {
|
|
228
|
+
cleanup();
|
|
229
|
+
}
|
|
166
230
|
return;
|
|
167
231
|
}
|
|
168
232
|
stream.destroy();
|
|
169
233
|
closeResponse(normalizedError);
|
|
170
234
|
};
|
|
171
|
-
const
|
|
172
|
-
if (!finished) {
|
|
235
|
+
const handleClose = () => {
|
|
236
|
+
if (!finished && !failed) {
|
|
173
237
|
fail(new Error("CSV response streaming aborted"));
|
|
238
|
+
cleanup();
|
|
239
|
+
return;
|
|
174
240
|
}
|
|
241
|
+
cleanup();
|
|
175
242
|
};
|
|
176
|
-
const
|
|
243
|
+
const handleFinish = () => {
|
|
177
244
|
finished = true;
|
|
245
|
+
cleanup();
|
|
178
246
|
};
|
|
179
247
|
const writeRow = async (row) => {
|
|
248
|
+
if (failed) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
180
251
|
outputStarted = true;
|
|
181
252
|
if (!stream.write(row)) {
|
|
182
|
-
await Promise
|
|
253
|
+
await new Promise((resolve) => {
|
|
254
|
+
if (failed) {
|
|
255
|
+
resolve();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const onDrain = () => {
|
|
259
|
+
stream.off("drain", onDrain);
|
|
260
|
+
abortWaiters.delete(onAbort);
|
|
261
|
+
resolve();
|
|
262
|
+
};
|
|
263
|
+
const onAbort = () => {
|
|
264
|
+
stream.off("drain", onDrain);
|
|
265
|
+
resolve();
|
|
266
|
+
};
|
|
267
|
+
abortWaiters.add(onAbort);
|
|
268
|
+
stream.once("drain", onDrain);
|
|
269
|
+
});
|
|
183
270
|
}
|
|
184
271
|
};
|
|
185
272
|
stream.on("error", fail);
|
|
186
|
-
stream.on("end", cleanup);
|
|
187
273
|
res.on?.("error", fail);
|
|
188
|
-
res.on?.("close",
|
|
189
|
-
res.on?.("finish",
|
|
274
|
+
res.on?.("close", handleClose);
|
|
275
|
+
res.on?.("finish", handleFinish);
|
|
190
276
|
const pump = async () => {
|
|
191
277
|
try {
|
|
192
278
|
const iterator = createAsyncIterator(this.dataset);
|
|
193
279
|
activeIterator = iterator;
|
|
194
280
|
const first = await iterator.next();
|
|
195
|
-
|
|
281
|
+
if (failed) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
let firstRow;
|
|
285
|
+
if (!first.done) {
|
|
286
|
+
firstRow = this.processor(first.value);
|
|
287
|
+
if (failed) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
196
291
|
const contentDisposition = createAttachmentContentDisposition(this.filename);
|
|
292
|
+
if (failed) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
197
295
|
res.set("Content-Type", "text/csv");
|
|
198
296
|
res.set("Content-Disposition", contentDisposition);
|
|
199
297
|
stream.pipe(res);
|
|
200
|
-
if (
|
|
201
|
-
|
|
298
|
+
if (first.done) {
|
|
299
|
+
if (!failed) {
|
|
300
|
+
stream.end();
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
202
303
|
}
|
|
304
|
+
await writeRow(firstRow);
|
|
203
305
|
while (!failed) {
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
306
|
+
const result = await iterator.next();
|
|
307
|
+
if (failed) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (result.done) {
|
|
206
311
|
break;
|
|
207
312
|
}
|
|
208
|
-
|
|
313
|
+
const processed = this.processor(result.value);
|
|
314
|
+
if (failed) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
await writeRow(processed);
|
|
209
318
|
}
|
|
210
319
|
if (!failed) {
|
|
211
|
-
finished = true;
|
|
212
320
|
stream.end();
|
|
213
321
|
}
|
|
214
322
|
} catch (error) {
|
|
@@ -217,7 +325,7 @@ var CSVResponse = class {
|
|
|
217
325
|
activeIterator = null;
|
|
218
326
|
}
|
|
219
327
|
};
|
|
220
|
-
void pump();
|
|
328
|
+
void pump().then(void 0, () => void 0);
|
|
221
329
|
}
|
|
222
330
|
};
|
|
223
331
|
|
|
@@ -304,16 +412,16 @@ var IMUsed = class extends Response {
|
|
|
304
412
|
// src/http-response.ts
|
|
305
413
|
var import_http_errors = require("@web-ts-toolkit/http-errors");
|
|
306
414
|
var HttpResponse = {
|
|
307
|
-
ok: (
|
|
308
|
-
created: (
|
|
309
|
-
accepted: (
|
|
310
|
-
nonAuthoritativeInfo: (
|
|
415
|
+
ok: (data) => new OK(data),
|
|
416
|
+
created: (data) => new Created(data),
|
|
417
|
+
accepted: (data) => new Accepted(data),
|
|
418
|
+
nonAuthoritativeInfo: (data) => new NonAuthoritativeInfo(data),
|
|
311
419
|
noContent: (...args) => new NoContent(...args),
|
|
312
|
-
resetContent: (
|
|
313
|
-
partialContent: (
|
|
314
|
-
multiStatus: (
|
|
315
|
-
alreadyReported: (
|
|
316
|
-
imUsed: (
|
|
420
|
+
resetContent: (data) => new ResetContent(data),
|
|
421
|
+
partialContent: (data) => new PartialContent(data),
|
|
422
|
+
multiStatus: (data) => new MultiStatus(data),
|
|
423
|
+
alreadyReported: (data) => new AlreadyReported(data),
|
|
424
|
+
imUsed: (data) => new IMUsed(data),
|
|
317
425
|
badRequest: (...args) => new import_http_errors.BadRequestError(...args),
|
|
318
426
|
unauthorized: (...args) => new import_http_errors.UnauthorizedError(...args),
|
|
319
427
|
forbidden: (...args) => new import_http_errors.ForbiddenError(...args),
|
|
@@ -341,7 +449,7 @@ var HttpResponse = {
|
|
|
341
449
|
tooManyRequests: (...args) => new import_http_errors.TooManyRequestsError(...args),
|
|
342
450
|
requestHeaderFieldsTooLarge: (...args) => new import_http_errors.RequestHeaderFieldsTooLargeError(...args),
|
|
343
451
|
unavailableForLegalReasons: (...args) => new import_http_errors.UnavailableForLegalReasonsError(...args),
|
|
344
|
-
json: (
|
|
452
|
+
json: (data) => new OK(data),
|
|
345
453
|
csv: (...args) => new CSVResponse(...args)
|
|
346
454
|
};
|
|
347
455
|
|
|
@@ -548,9 +656,10 @@ var normalizeMiddlewareList = (fns) => {
|
|
|
548
656
|
return fns;
|
|
549
657
|
}
|
|
550
658
|
if ((0, import_utils3.isArray)(fns[0])) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
659
|
+
const nested = fns[0];
|
|
660
|
+
import_assert.default.ok(nested.length > 0, "at least one middleware handler is required");
|
|
661
|
+
nested.forEach(assertMiddleware);
|
|
662
|
+
return [...nested];
|
|
554
663
|
}
|
|
555
664
|
assertMiddleware(fns[0]);
|
|
556
665
|
return [fns[0]];
|
|
@@ -581,17 +690,68 @@ function createHandler(options = {}) {
|
|
|
581
690
|
let postJsonHook = null;
|
|
582
691
|
let preErrorHook = null;
|
|
583
692
|
let postErrorHook = null;
|
|
584
|
-
const updateHook = (fn, name, setState
|
|
693
|
+
const updateHook = (fn, name, setState) => {
|
|
585
694
|
if (fn === null) {
|
|
586
695
|
setState(null, null);
|
|
587
|
-
rebuild();
|
|
588
696
|
return;
|
|
589
697
|
}
|
|
590
698
|
import_assert.default.ok((0, import_utils3.isFunction)(fn), `${name} hook must be a function`);
|
|
591
699
|
setState(fn, promisify(fn));
|
|
592
|
-
rebuild();
|
|
593
700
|
};
|
|
594
|
-
const
|
|
701
|
+
const describeTerminalFailure = (failure) => {
|
|
702
|
+
try {
|
|
703
|
+
if (failure === void 0) {
|
|
704
|
+
return "undefined";
|
|
705
|
+
}
|
|
706
|
+
if (failure === null) {
|
|
707
|
+
return "null";
|
|
708
|
+
}
|
|
709
|
+
if (typeof failure === "symbol") {
|
|
710
|
+
return failure.toString();
|
|
711
|
+
}
|
|
712
|
+
if (typeof failure === "string") {
|
|
713
|
+
return failure;
|
|
714
|
+
}
|
|
715
|
+
if (typeof failure === "object" && failure !== null) {
|
|
716
|
+
const message = failure.message;
|
|
717
|
+
if (typeof message === "string" && message.length > 0) {
|
|
718
|
+
return message;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return String(failure);
|
|
722
|
+
} catch {
|
|
723
|
+
return "Internal Server Error";
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
const toTerminalError = (failure) => {
|
|
727
|
+
if (failure instanceof Error) {
|
|
728
|
+
return failure;
|
|
729
|
+
}
|
|
730
|
+
const error = new Error(describeTerminalFailure(failure));
|
|
731
|
+
try {
|
|
732
|
+
error.cause = failure;
|
|
733
|
+
} catch {
|
|
734
|
+
}
|
|
735
|
+
return error;
|
|
736
|
+
};
|
|
737
|
+
const claimSlot = (event, slot) => {
|
|
738
|
+
if (event[slot]) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
event[slot] = true;
|
|
742
|
+
return true;
|
|
743
|
+
};
|
|
744
|
+
const delegateTerminalError = (_res, next, terminalError, event) => {
|
|
745
|
+
if (!claimSlot(event, "canceled")) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
next(toTerminalError(terminalError));
|
|
749
|
+
};
|
|
750
|
+
const reportHookObservation = (_res, next, observation, event) => {
|
|
751
|
+
if (!claimSlot(event, "reported")) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
next(toTerminalError(observation));
|
|
595
755
|
};
|
|
596
756
|
const sendBaseJson = function(res, data, event, onBeforeOutputError) {
|
|
597
757
|
if (shouldSkipResponse(res, event)) {
|
|
@@ -653,26 +813,33 @@ function createHandler(options = {}) {
|
|
|
653
813
|
}
|
|
654
814
|
invokePostHook(hook, value, onFailure);
|
|
655
815
|
};
|
|
656
|
-
const terminalErrorBoundary = (res, next, terminalError, event) => {
|
|
657
|
-
if (event.canceled) {
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
next(terminalError);
|
|
661
|
-
};
|
|
662
816
|
const dispatchError = (res, next, err, event) => {
|
|
663
|
-
const
|
|
664
|
-
|
|
817
|
+
const reportTerminal = (failure) => {
|
|
818
|
+
delegateTerminalError(res, next, failure, event);
|
|
819
|
+
};
|
|
820
|
+
const reportObservation = (failure) => {
|
|
821
|
+
reportHookObservation(res, next, failure, event);
|
|
665
822
|
};
|
|
666
823
|
const sendFormatted = (failure) => {
|
|
824
|
+
if (event.canceled) {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
667
827
|
let didSend;
|
|
668
828
|
try {
|
|
669
829
|
didSend = sendBaseError(res, failure, event);
|
|
670
830
|
} catch (senderFailure) {
|
|
671
|
-
|
|
831
|
+
reportTerminal(senderFailure === void 0 ? failure : senderFailure);
|
|
672
832
|
return;
|
|
673
833
|
}
|
|
674
|
-
if (didSend
|
|
675
|
-
|
|
834
|
+
if (!didSend) {
|
|
835
|
+
if (res.headersSent) {
|
|
836
|
+
reportTerminal(failure);
|
|
837
|
+
}
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
claimSlot(event, "canceled");
|
|
841
|
+
if (postErrorHook) {
|
|
842
|
+
invokePostHookOnFinish(res, postErrorHook, failure, reportObservation);
|
|
676
843
|
}
|
|
677
844
|
};
|
|
678
845
|
if (event.canceled) {
|
|
@@ -688,39 +855,151 @@ function createHandler(options = {}) {
|
|
|
688
855
|
}
|
|
689
856
|
runSender();
|
|
690
857
|
};
|
|
691
|
-
const dispatchValue = (res, next, data, event
|
|
692
|
-
|
|
693
|
-
|
|
858
|
+
const dispatchValue = (res, next, data, event) => {
|
|
859
|
+
if (data === void 0) {
|
|
860
|
+
if (event.canceled) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (res.headersSent) {
|
|
864
|
+
claimSlot(event, "canceled");
|
|
865
|
+
}
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
if (event.canceled) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (res.headersSent) {
|
|
872
|
+
delegateTerminalError(res, next, data, event);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const reportTerminal = (failure) => {
|
|
876
|
+
if (!event.canceled) {
|
|
877
|
+
delegateTerminalError(res, next, failure, event);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
reportHookObservation(res, next, failure, event);
|
|
881
|
+
};
|
|
882
|
+
const reportObservation = (failure) => {
|
|
883
|
+
reportHookObservation(res, next, failure, event);
|
|
694
884
|
};
|
|
695
|
-
|
|
885
|
+
let fallbackOccurred = false;
|
|
886
|
+
let autoSendClaimed = false;
|
|
887
|
+
const schedulePostJson = (value) => {
|
|
888
|
+
const activePostJsonHook = postJsonHook;
|
|
889
|
+
if (!activePostJsonHook) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const guardedHook = (observed) => {
|
|
893
|
+
if (fallbackOccurred) {
|
|
894
|
+
return Promise.resolve();
|
|
895
|
+
}
|
|
896
|
+
return activePostJsonHook(observed);
|
|
897
|
+
};
|
|
898
|
+
invokePostHookOnFinish(res, guardedHook, value, reportObservation);
|
|
899
|
+
};
|
|
900
|
+
const scheduleFallbackPostError = (failure) => {
|
|
901
|
+
const activePostErrorHook = postErrorHook;
|
|
902
|
+
if (!activePostErrorHook) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
invokePostHookOnFinish(res, activePostErrorHook, failure, reportObservation);
|
|
906
|
+
};
|
|
907
|
+
const sendFallbackFormatted = (failure) => {
|
|
908
|
+
if (!autoSendClaimed && event.canceled) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
if (res.headersSent) {
|
|
912
|
+
reportTerminal(failure);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (autoSendClaimed) {
|
|
916
|
+
const bypassEvent = { canceled: false, reported: event.reported };
|
|
917
|
+
try {
|
|
918
|
+
const didFallbackSend = sendBaseError(res, failure, bypassEvent);
|
|
919
|
+
if (didFallbackSend) {
|
|
920
|
+
scheduleFallbackPostError(failure);
|
|
921
|
+
} else {
|
|
922
|
+
reportTerminal(failure);
|
|
923
|
+
}
|
|
924
|
+
} catch (senderFailure) {
|
|
925
|
+
reportTerminal(senderFailure === void 0 ? failure : senderFailure);
|
|
926
|
+
}
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
696
929
|
try {
|
|
697
|
-
sendBaseError(res, failure, event);
|
|
930
|
+
const didFallbackSend = sendBaseError(res, failure, event);
|
|
931
|
+
if (didFallbackSend) {
|
|
932
|
+
claimSlot(event, "canceled");
|
|
933
|
+
scheduleFallbackPostError(failure);
|
|
934
|
+
} else if (res.headersSent) {
|
|
935
|
+
reportTerminal(failure);
|
|
936
|
+
}
|
|
698
937
|
} catch (senderFailure) {
|
|
699
|
-
|
|
938
|
+
reportTerminal(senderFailure === void 0 ? failure : senderFailure);
|
|
700
939
|
}
|
|
701
940
|
};
|
|
941
|
+
const runFallbackLifecycle = (originalFailure) => {
|
|
942
|
+
if (fallbackOccurred) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
fallbackOccurred = true;
|
|
946
|
+
if (!preErrorHook) {
|
|
947
|
+
sendFallbackFormatted(originalFailure);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
preErrorHook(originalFailure).then(
|
|
951
|
+
() => sendFallbackFormatted(originalFailure),
|
|
952
|
+
(hookErr) => sendFallbackFormatted(hookErr === void 0 ? originalFailure : hookErr)
|
|
953
|
+
);
|
|
954
|
+
};
|
|
955
|
+
const sendFallbackError = (failure) => {
|
|
956
|
+
runFallbackLifecycle(failure);
|
|
957
|
+
};
|
|
702
958
|
const runSender = () => {
|
|
959
|
+
if (event.canceled) {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
if (res.headersSent) {
|
|
963
|
+
delegateTerminalError(res, next, data, event);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
703
966
|
let didSend;
|
|
704
967
|
try {
|
|
705
|
-
didSend = sendBaseJson(res, data, event,
|
|
968
|
+
didSend = sendBaseJson(res, data, event, sendFallbackError);
|
|
706
969
|
} catch (senderFailure) {
|
|
707
970
|
const failure = senderFailure === void 0 ? new Error("response serialization failed") : senderFailure;
|
|
708
971
|
if (res.headersSent) {
|
|
709
|
-
|
|
972
|
+
reportTerminal(failure);
|
|
710
973
|
return;
|
|
711
974
|
}
|
|
712
|
-
|
|
975
|
+
runFallbackLifecycle(failure);
|
|
713
976
|
return;
|
|
714
977
|
}
|
|
715
|
-
if (didSend
|
|
716
|
-
|
|
978
|
+
if (!didSend) {
|
|
979
|
+
if (res.headersSent) {
|
|
980
|
+
delegateTerminalError(res, next, data, event);
|
|
981
|
+
}
|
|
982
|
+
return;
|
|
717
983
|
}
|
|
718
|
-
|
|
984
|
+
claimSlot(event, "canceled");
|
|
985
|
+
autoSendClaimed = true;
|
|
986
|
+
schedulePostJson(data);
|
|
719
987
|
};
|
|
720
988
|
if (preJsonHook) {
|
|
721
989
|
preJsonHook(data).then(
|
|
722
|
-
|
|
723
|
-
|
|
990
|
+
() => {
|
|
991
|
+
runSender();
|
|
992
|
+
},
|
|
993
|
+
(hookErr) => {
|
|
994
|
+
if (event.canceled) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
if (res.headersSent) {
|
|
998
|
+
runFallbackLifecycle(hookErr === void 0 ? new Error("pre-json hook failed") : hookErr);
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
runFallbackLifecycle(hookErr === void 0 ? new Error("pre-json hook failed") : hookErr);
|
|
1002
|
+
}
|
|
724
1003
|
);
|
|
725
1004
|
return;
|
|
726
1005
|
}
|
|
@@ -728,15 +1007,13 @@ function createHandler(options = {}) {
|
|
|
728
1007
|
};
|
|
729
1008
|
const nextFn = function(event, next) {
|
|
730
1009
|
return function(error) {
|
|
731
|
-
if (event
|
|
1010
|
+
if (!claimSlot(event, "canceled")) {
|
|
732
1011
|
return;
|
|
733
1012
|
}
|
|
734
1013
|
if (error === void 0) {
|
|
735
|
-
event.canceled = true;
|
|
736
1014
|
next();
|
|
737
1015
|
return;
|
|
738
1016
|
}
|
|
739
|
-
event.canceled = true;
|
|
740
1017
|
next(error);
|
|
741
1018
|
};
|
|
742
1019
|
};
|
|
@@ -745,18 +1022,17 @@ function createHandler(options = {}) {
|
|
|
745
1022
|
if (event.canceled) {
|
|
746
1023
|
return;
|
|
747
1024
|
}
|
|
748
|
-
|
|
749
|
-
dispatchError(res, next, event.nextError, event);
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
dispatchValue(res, next, resolved, event, () => void 0);
|
|
1025
|
+
dispatchValue(res, next, resolved, event);
|
|
753
1026
|
};
|
|
754
1027
|
if ((0, import_utils3.isPromise)(result)) {
|
|
755
1028
|
Promise.resolve(result).then(
|
|
756
1029
|
(resolved) => finalize(resolved),
|
|
757
1030
|
(err) => {
|
|
1031
|
+
if (event.canceled) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
758
1034
|
if (res.headersSent) {
|
|
759
|
-
next
|
|
1035
|
+
delegateTerminalError(res, next, err, event);
|
|
760
1036
|
return;
|
|
761
1037
|
}
|
|
762
1038
|
dispatchError(res, next, err, event);
|
|
@@ -768,13 +1044,16 @@ function createHandler(options = {}) {
|
|
|
768
1044
|
};
|
|
769
1045
|
const routerFn = function(fn) {
|
|
770
1046
|
return function(req, res, next) {
|
|
771
|
-
const event = { canceled: false,
|
|
1047
|
+
const event = { canceled: false, reported: false };
|
|
772
1048
|
try {
|
|
773
1049
|
const result = fn(req, res, nextFn(event, next));
|
|
774
1050
|
runLifecycle(res, next, result, event);
|
|
775
1051
|
} catch (err) {
|
|
1052
|
+
if (event.canceled) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
776
1055
|
if (res.headersSent) {
|
|
777
|
-
next
|
|
1056
|
+
delegateTerminalError(res, next, err, event);
|
|
778
1057
|
return;
|
|
779
1058
|
}
|
|
780
1059
|
dispatchError(res, next, err, event);
|
|
@@ -800,57 +1079,37 @@ function createHandler(options = {}) {
|
|
|
800
1079
|
return preJson;
|
|
801
1080
|
},
|
|
802
1081
|
set preJson(fn) {
|
|
803
|
-
updateHook(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
preJson = syncHook;
|
|
808
|
-
preJsonHook = asyncHook;
|
|
809
|
-
},
|
|
810
|
-
noopRebuild
|
|
811
|
-
);
|
|
1082
|
+
updateHook(fn, "pre-json", (syncHook, asyncHook) => {
|
|
1083
|
+
preJson = syncHook;
|
|
1084
|
+
preJsonHook = asyncHook;
|
|
1085
|
+
});
|
|
812
1086
|
},
|
|
813
1087
|
get postJson() {
|
|
814
1088
|
return postJson;
|
|
815
1089
|
},
|
|
816
1090
|
set postJson(fn) {
|
|
817
|
-
updateHook(
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
postJson = syncHook;
|
|
822
|
-
postJsonHook = asyncHook;
|
|
823
|
-
},
|
|
824
|
-
noopRebuild
|
|
825
|
-
);
|
|
1091
|
+
updateHook(fn, "post-json", (syncHook, asyncHook) => {
|
|
1092
|
+
postJson = syncHook;
|
|
1093
|
+
postJsonHook = asyncHook;
|
|
1094
|
+
});
|
|
826
1095
|
},
|
|
827
1096
|
get preError() {
|
|
828
1097
|
return preError;
|
|
829
1098
|
},
|
|
830
1099
|
set preError(fn) {
|
|
831
|
-
updateHook(
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
preError = syncHook;
|
|
836
|
-
preErrorHook = asyncHook;
|
|
837
|
-
},
|
|
838
|
-
noopRebuild
|
|
839
|
-
);
|
|
1100
|
+
updateHook(fn, "pre-error", (syncHook, asyncHook) => {
|
|
1101
|
+
preError = syncHook;
|
|
1102
|
+
preErrorHook = asyncHook;
|
|
1103
|
+
});
|
|
840
1104
|
},
|
|
841
1105
|
get postError() {
|
|
842
1106
|
return postError;
|
|
843
1107
|
},
|
|
844
1108
|
set postError(fn) {
|
|
845
|
-
updateHook(
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
postError = syncHook;
|
|
850
|
-
postErrorHook = asyncHook;
|
|
851
|
-
},
|
|
852
|
-
noopRebuild
|
|
853
|
-
);
|
|
1109
|
+
updateHook(fn, "post-error", (syncHook, asyncHook) => {
|
|
1110
|
+
postError = syncHook;
|
|
1111
|
+
postErrorHook = asyncHook;
|
|
1112
|
+
});
|
|
854
1113
|
}
|
|
855
1114
|
};
|
|
856
1115
|
return handler;
|