@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/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
- let abortStreaming = () => void 0;
113
- const abortSignal = new Promise((_, reject) => {
114
- abortStreaming = reject;
115
- });
116
- abortSignal.catch(() => void 0);
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", abort);
121
- res.off?.("finish", markFinished);
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
- cleanup();
143
- const normalizedError = error instanceof Error ? error : new Error("CSV response streaming failed");
144
- abortStreaming(normalizedError);
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
- onBeforeOutputError?.(normalizedError);
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 abort = () => {
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 markFinished = () => {
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.race([(0, import_events.once)(stream, "drain"), abortSignal]);
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", abort);
171
- res.on?.("finish", markFinished);
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
- const firstRow = first.done ? void 0 : this.processor(first.value);
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 (!first.done) {
183
- await writeRow(firstRow);
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 row = await Promise.race([iterator.next(), abortSignal]);
187
- if (row.done) {
288
+ const result = await iterator.next();
289
+ if (failed) {
290
+ return;
291
+ }
292
+ if (result.done) {
188
293
  break;
189
294
  }
190
- await writeRow(this.processor(row.value));
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: (...args) => new OK(...args),
290
- created: (...args) => new Created(...args),
291
- accepted: (...args) => new Accepted(...args),
292
- nonAuthoritativeInfo: (...args) => new NonAuthoritativeInfo(...args),
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: (...args) => new ResetContent(...args),
295
- partialContent: (...args) => new PartialContent(...args),
296
- multiStatus: (...args) => new MultiStatus(...args),
297
- alreadyReported: (...args) => new AlreadyReported(...args),
298
- imUsed: (...args) => new IMUsed(...args),
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: (...args) => new OK(...args),
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
- import_assert.default.ok(fns[0].length > 0, "at least one middleware handler is required");
534
- fns[0].forEach(assertMiddleware);
535
- return fns[0];
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, rebuild) => {
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 noopRebuild = () => {
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 reportFailure = (failure) => {
646
- terminalErrorBoundary(res, next, failure, event);
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
- reportFailure(senderFailure === void 0 ? failure : senderFailure);
813
+ reportTerminal(senderFailure === void 0 ? failure : senderFailure);
654
814
  return;
655
815
  }
656
- if (didSend && postErrorHook) {
657
- invokePostHookOnFinish(res, postErrorHook, failure, reportFailure);
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, onSerialized) => {
674
- const reportFailure = (failure) => {
675
- terminalErrorBoundary(res, next, failure, event);
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
- const sendFormattedError = (failure) => {
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
- reportFailure(senderFailure === void 0 ? failure : senderFailure);
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, sendFormattedError);
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
- reportFailure(failure);
954
+ reportTerminal(failure);
692
955
  return;
693
956
  }
694
- sendFormattedError(failure);
957
+ runFallbackLifecycle(failure);
695
958
  return;
696
959
  }
697
- if (didSend && postJsonHook) {
698
- invokePostHookOnFinish(res, postJsonHook, data, reportFailure);
960
+ if (!didSend) {
961
+ if (res.headersSent) {
962
+ delegateTerminalError(res, next, data, event);
963
+ }
964
+ return;
699
965
  }
700
- onSerialized();
966
+ claimSlot(event, "canceled");
967
+ autoSendClaimed = true;
968
+ schedulePostJson(data);
701
969
  };
702
970
  if (preJsonHook) {
703
971
  preJsonHook(data).then(
704
- runSender,
705
- (hookErr) => sendFormattedError(hookErr === void 0 ? new Error("pre-json hook failed") : hookErr)
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.canceled) {
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
- if (event.nextError) {
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(err);
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, nextError: null };
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(err);
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
- fn,
787
- "pre-json",
788
- (syncHook, asyncHook) => {
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
- fn,
801
- "post-json",
802
- (syncHook, asyncHook) => {
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
- fn,
815
- "pre-error",
816
- (syncHook, asyncHook) => {
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
- fn,
829
- "post-error",
830
- (syncHook, asyncHook) => {
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;