@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/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
- let abortStreaming = () => void 0;
131
- const abortSignal = new Promise((_, reject) => {
132
- abortStreaming = reject;
133
- });
134
- abortSignal.catch(() => void 0);
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", abort);
139
- res.off?.("finish", markFinished);
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
- cleanup();
161
- const normalizedError = error instanceof Error ? error : new Error("CSV response streaming failed");
162
- abortStreaming(normalizedError);
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
- onBeforeOutputError?.(normalizedError);
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 abort = () => {
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 markFinished = () => {
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.race([(0, import_events.once)(stream, "drain"), abortSignal]);
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", abort);
189
- res.on?.("finish", markFinished);
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
- const firstRow = first.done ? void 0 : this.processor(first.value);
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 (!first.done) {
201
- await writeRow(firstRow);
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 row = await Promise.race([iterator.next(), abortSignal]);
205
- if (row.done) {
306
+ const result = await iterator.next();
307
+ if (failed) {
308
+ return;
309
+ }
310
+ if (result.done) {
206
311
  break;
207
312
  }
208
- await writeRow(this.processor(row.value));
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: (...args) => new OK(...args),
308
- created: (...args) => new Created(...args),
309
- accepted: (...args) => new Accepted(...args),
310
- nonAuthoritativeInfo: (...args) => new NonAuthoritativeInfo(...args),
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: (...args) => new ResetContent(...args),
313
- partialContent: (...args) => new PartialContent(...args),
314
- multiStatus: (...args) => new MultiStatus(...args),
315
- alreadyReported: (...args) => new AlreadyReported(...args),
316
- imUsed: (...args) => new IMUsed(...args),
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: (...args) => new OK(...args),
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
- import_assert.default.ok(fns[0].length > 0, "at least one middleware handler is required");
552
- fns[0].forEach(assertMiddleware);
553
- return fns[0];
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, rebuild) => {
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 noopRebuild = () => {
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 reportFailure = (failure) => {
664
- terminalErrorBoundary(res, next, failure, event);
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
- reportFailure(senderFailure === void 0 ? failure : senderFailure);
831
+ reportTerminal(senderFailure === void 0 ? failure : senderFailure);
672
832
  return;
673
833
  }
674
- if (didSend && postErrorHook) {
675
- invokePostHookOnFinish(res, postErrorHook, failure, reportFailure);
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, onSerialized) => {
692
- const reportFailure = (failure) => {
693
- terminalErrorBoundary(res, next, failure, event);
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
- const sendFormattedError = (failure) => {
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
- reportFailure(senderFailure === void 0 ? failure : senderFailure);
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, sendFormattedError);
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
- reportFailure(failure);
972
+ reportTerminal(failure);
710
973
  return;
711
974
  }
712
- sendFormattedError(failure);
975
+ runFallbackLifecycle(failure);
713
976
  return;
714
977
  }
715
- if (didSend && postJsonHook) {
716
- invokePostHookOnFinish(res, postJsonHook, data, reportFailure);
978
+ if (!didSend) {
979
+ if (res.headersSent) {
980
+ delegateTerminalError(res, next, data, event);
981
+ }
982
+ return;
717
983
  }
718
- onSerialized();
984
+ claimSlot(event, "canceled");
985
+ autoSendClaimed = true;
986
+ schedulePostJson(data);
719
987
  };
720
988
  if (preJsonHook) {
721
989
  preJsonHook(data).then(
722
- runSender,
723
- (hookErr) => sendFormattedError(hookErr === void 0 ? new Error("pre-json hook failed") : hookErr)
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.canceled) {
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
- if (event.nextError) {
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(err);
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, nextError: null };
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(err);
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
- fn,
805
- "pre-json",
806
- (syncHook, asyncHook) => {
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
- fn,
819
- "post-json",
820
- (syncHook, asyncHook) => {
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
- fn,
833
- "pre-error",
834
- (syncHook, asyncHook) => {
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
- fn,
847
- "post-error",
848
- (syncHook, asyncHook) => {
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;