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