@zimic/interceptor 1.2.3 → 1.2.4-canary.1

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.
Files changed (34) hide show
  1. package/dist/{chunk-XCYZ5L2M.mjs → chunk-EIYQEPK2.mjs} +111 -85
  2. package/dist/chunk-EIYQEPK2.mjs.map +1 -0
  3. package/dist/{chunk-ZU6IGW27.js → chunk-OKZEX5DQ.js} +111 -85
  4. package/dist/chunk-OKZEX5DQ.js.map +1 -0
  5. package/dist/cli.js +18 -18
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cli.mjs +2 -2
  8. package/dist/cli.mjs.map +1 -1
  9. package/dist/http.js +112 -110
  10. package/dist/http.js.map +1 -1
  11. package/dist/http.mjs +112 -110
  12. package/dist/http.mjs.map +1 -1
  13. package/dist/server.js +6 -6
  14. package/dist/server.mjs +1 -1
  15. package/package.json +1 -1
  16. package/src/http/interceptor/HttpInterceptorClient.ts +1 -1
  17. package/src/http/interceptor/HttpInterceptorStore.ts +6 -3
  18. package/src/http/interceptor/RemoteHttpInterceptor.ts +1 -1
  19. package/src/http/interceptor/errors/UnknownHttpInterceptorPlatformError.ts +1 -1
  20. package/src/http/interceptorWorker/HttpInterceptorWorker.ts +3 -5
  21. package/src/http/interceptorWorker/LocalHttpInterceptorWorker.ts +16 -12
  22. package/src/http/interceptorWorker/RemoteHttpInterceptorWorker.ts +20 -43
  23. package/src/server/InterceptorServer.ts +39 -21
  24. package/src/server/types/schema.ts +1 -1
  25. package/src/utils/http.ts +2 -2
  26. package/src/utils/webSocket.ts +3 -3
  27. package/src/webSocket/WebSocketClient.ts +4 -4
  28. package/src/webSocket/WebSocketHandler.ts +119 -69
  29. package/src/webSocket/WebSocketServer.ts +2 -3
  30. package/src/webSocket/errors/InvalidWebSocketMessageError.ts +8 -0
  31. package/src/webSocket/types.ts +9 -14
  32. package/dist/chunk-XCYZ5L2M.mjs.map +0 -1
  33. package/dist/chunk-ZU6IGW27.js.map +0 -1
  34. package/src/webSocket/errors/InvalidWebSocketMessage.ts +0 -8
@@ -65,13 +65,13 @@ var HttpServerTimeoutError = class extends Error {
65
65
  var HttpServerStartTimeoutError = class extends HttpServerTimeoutError {
66
66
  constructor(reachedTimeout) {
67
67
  super(`HTTP server start timed out after ${reachedTimeout}ms.`);
68
- this.name = "HttpServerStartTimeout";
68
+ this.name = "HttpServerStartTimeoutError";
69
69
  }
70
70
  };
71
71
  var HttpServerStopTimeoutError = class extends HttpServerTimeoutError {
72
72
  constructor(reachedTimeout) {
73
73
  super(`HTTP server stop timed out after ${reachedTimeout}ms.`);
74
- this.name = "HttpServerStopTimeout";
74
+ this.name = "HttpServerStopTimeoutError";
75
75
  }
76
76
  };
77
77
  var DEFAULT_HTTP_SERVER_LIFECYCLE_TIMEOUT = 60 * 1e3;
@@ -152,13 +152,13 @@ var WebSocketTimeoutError = class extends Error {
152
152
  var WebSocketOpenTimeoutError = class extends WebSocketTimeoutError {
153
153
  constructor(reachedTimeout) {
154
154
  super(`Web socket open timed out after ${reachedTimeout}ms.`);
155
- this.name = "WebSocketOpenTimeout";
155
+ this.name = "WebSocketOpenTimeoutError";
156
156
  }
157
157
  };
158
158
  var WebSocketMessageTimeoutError = class extends WebSocketTimeoutError {
159
159
  constructor(reachedTimeout) {
160
160
  super(`Web socket message timed out after ${reachedTimeout}ms.`);
161
- this.name = "WebSocketMessageTimeout";
161
+ this.name = "WebSocketMessageTimeoutError";
162
162
  }
163
163
  };
164
164
  var WebSocketMessageAbortError = class extends WebSocketTimeoutError {
@@ -170,7 +170,7 @@ var WebSocketMessageAbortError = class extends WebSocketTimeoutError {
170
170
  var WebSocketCloseTimeoutError = class extends WebSocketTimeoutError {
171
171
  constructor(reachedTimeout) {
172
172
  super(`Web socket close timed out after ${reachedTimeout}ms.`);
173
- this.name = "WebSocketCloseTimeout";
173
+ this.name = "WebSocketCloseTimeoutError";
174
174
  }
175
175
  };
176
176
  var DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT = 60 * 1e3;
@@ -872,14 +872,14 @@ var importCrypto = createCachedDynamicImport_default(async () => {
872
872
  // src/webSocket/constants.ts
873
873
  var WEB_SOCKET_CONTROL_MESSAGES = Object.freeze(["socket:auth:valid"]);
874
874
 
875
- // src/webSocket/errors/InvalidWebSocketMessage.ts
876
- var InvalidWebSocketMessage = class extends Error {
875
+ // src/webSocket/errors/InvalidWebSocketMessageError.ts
876
+ var InvalidWebSocketMessageError = class extends Error {
877
877
  constructor(message) {
878
878
  super(`Web socket message is invalid and could not be parsed: ${message}`);
879
- this.name = "InvalidWebSocketMessage";
879
+ this.name = "InvalidWebSocketMessageError";
880
880
  }
881
881
  };
882
- var InvalidWebSocketMessage_default = InvalidWebSocketMessage;
882
+ var InvalidWebSocketMessageError_default = InvalidWebSocketMessageError;
883
883
 
884
884
  // src/webSocket/errors/NotRunningWebSocketHandlerError.ts
885
885
  var NotRunningWebSocketHandlerError = class extends Error {
@@ -897,7 +897,7 @@ var WebSocketHandler = class {
897
897
  messageTimeout;
898
898
  channelListeners = {};
899
899
  socketListeners = {
900
- messageAbort: /* @__PURE__ */ new Map()
900
+ abortRequests: /* @__PURE__ */ new Map()
901
901
  };
902
902
  constructor(options) {
903
903
  this.socketTimeout = options.socketTimeout ?? DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT;
@@ -918,10 +918,12 @@ var WebSocketHandler = class {
918
918
  }
919
919
  socket.addEventListener("error", handleSocketError);
920
920
  const handleSocketClose = () => {
921
+ this.sockets.delete(socket);
922
+ this.emitSocket("abortRequests", socket);
923
+ this.socketListeners.abortRequests.delete(socket);
921
924
  socket.removeEventListener("message", handleSocketMessage);
922
925
  socket.removeEventListener("close", handleSocketClose);
923
926
  socket.removeEventListener("error", handleSocketError);
924
- this.removeSocket(socket);
925
927
  };
926
928
  socket.addEventListener("close", handleSocketClose);
927
929
  this.sockets.add(socket);
@@ -945,7 +947,7 @@ var WebSocketHandler = class {
945
947
  if (typeof data === "string") {
946
948
  return data;
947
949
  } else {
948
- throw new InvalidWebSocketMessage_default(data);
950
+ throw new InvalidWebSocketMessageError_default(data);
949
951
  }
950
952
  }
951
953
  parseMessage(stringifiedMessage) {
@@ -953,10 +955,10 @@ var WebSocketHandler = class {
953
955
  try {
954
956
  parsedMessage = JSON.parse(stringifiedMessage);
955
957
  } catch {
956
- throw new InvalidWebSocketMessage_default(stringifiedMessage);
958
+ throw new InvalidWebSocketMessageError_default(stringifiedMessage);
957
959
  }
958
960
  if (!this.isMessage(parsedMessage)) {
959
- throw new InvalidWebSocketMessage_default(stringifiedMessage);
961
+ throw new InvalidWebSocketMessageError_default(stringifiedMessage);
960
962
  }
961
963
  if (this.isReplyMessage(parsedMessage)) {
962
964
  return {
@@ -975,6 +977,9 @@ var WebSocketHandler = class {
975
977
  isMessage(message) {
976
978
  return typeof message === "object" && message !== null && "id" in message && typeof message.id === "string" && "channel" in message && typeof message.channel === "string" && (!("requestId" in message) || typeof message.requestId === "string");
977
979
  }
980
+ isChannelEvent(event, channel) {
981
+ return event.channel === channel;
982
+ }
978
983
  async notifyListeners(message, socket) {
979
984
  if (this.isReplyMessage(message)) {
980
985
  await this.notifyReplyListeners(message, socket);
@@ -1003,10 +1008,6 @@ var WebSocketHandler = class {
1003
1008
  });
1004
1009
  await Promise.all(closingPromises);
1005
1010
  }
1006
- removeSocket(socket) {
1007
- this.abortSocketMessages([socket]);
1008
- this.sockets.delete(socket);
1009
- }
1010
1011
  async createEventMessage(channel, eventData) {
1011
1012
  const crypto2 = await importCrypto();
1012
1013
  const eventMessage = {
@@ -1023,31 +1024,46 @@ var WebSocketHandler = class {
1023
1024
  async request(channel, requestData, options = {}) {
1024
1025
  const request = await this.createEventMessage(channel, requestData);
1025
1026
  this.sendMessage(request, options.sockets);
1026
- const response = await this.waitForReply(channel, request.id, options.sockets);
1027
+ const response = await this.waitForReply(channel, request, options.sockets);
1027
1028
  return response.data;
1028
1029
  }
1029
- async waitForReply(channel, requestId, sockets = this.sockets) {
1030
+ async waitForReply(channel, request, sockets = this.sockets) {
1030
1031
  return new Promise((resolve, reject) => {
1031
1032
  const replyTimeout = setTimeout(() => {
1032
- this.offReply(channel, replyListener);
1033
- this.offAbortSocketMessages(sockets, abortListener);
1033
+ this.offChannel("reply", channel, replyListener);
1034
+ for (const socket of sockets) {
1035
+ this.offSocket("abortRequests", socket, abortRequestsHandler);
1036
+ }
1034
1037
  const timeoutError = new WebSocketMessageTimeoutError(this.messageTimeout);
1035
1038
  reject(timeoutError);
1036
1039
  }, this.messageTimeout);
1037
- const abortListener = this.onAbortSocketMessages(sockets, (error) => {
1040
+ const replyListener = this.onChannel("reply", channel, (message) => {
1041
+ if (message.requestId !== request.id) {
1042
+ return;
1043
+ }
1038
1044
  clearTimeout(replyTimeout);
1039
- this.offReply(channel, replyListener);
1040
- this.offAbortSocketMessages(sockets, abortListener);
1041
- reject(error);
1042
- });
1043
- const replyListener = this.onReply(channel, (message) => {
1044
- if (message.requestId === requestId) {
1045
- clearTimeout(replyTimeout);
1046
- this.offReply(channel, replyListener);
1047
- this.offAbortSocketMessages(sockets, abortListener);
1048
- resolve(message);
1045
+ this.offChannel("reply", channel, replyListener);
1046
+ for (const socket of sockets) {
1047
+ this.offSocket("abortRequests", socket, abortRequestsHandler);
1049
1048
  }
1049
+ resolve(message);
1050
1050
  });
1051
+ const abortRequestsHandler = (options) => {
1052
+ const shouldAbortRequest = options.shouldAbortRequest === void 0 || options.shouldAbortRequest(request);
1053
+ if (!shouldAbortRequest) {
1054
+ return;
1055
+ }
1056
+ clearTimeout(replyTimeout);
1057
+ this.offChannel("reply", channel, replyListener);
1058
+ for (const socket of sockets) {
1059
+ this.offSocket("abortRequests", socket, abortRequestsHandler);
1060
+ }
1061
+ const abortError = new WebSocketMessageAbortError();
1062
+ reject(abortError);
1063
+ };
1064
+ for (const socket of sockets) {
1065
+ this.onSocket("abortRequests", socket, abortRequestsHandler);
1066
+ }
1051
1067
  });
1052
1068
  }
1053
1069
  isReplyMessage(message) {
@@ -1078,9 +1094,9 @@ var WebSocketHandler = class {
1078
1094
  socket.send(stringifiedMessage);
1079
1095
  }
1080
1096
  }
1081
- onEvent(channel, listener) {
1097
+ onChannel(type, channel, listener) {
1082
1098
  const listeners = this.getOrCreateChannelListeners(channel);
1083
- listeners.event.add(listener);
1099
+ listeners[type].add(listener);
1084
1100
  return listener;
1085
1101
  }
1086
1102
  getOrCreateChannelListeners(channel) {
@@ -1093,42 +1109,36 @@ var WebSocketHandler = class {
1093
1109
  }
1094
1110
  return listeners;
1095
1111
  }
1096
- onReply(channel, listener) {
1097
- const listeners = this.getOrCreateChannelListeners(channel);
1098
- listeners.reply.add(listener);
1099
- return listener;
1100
- }
1101
- offEvent(channel, listener) {
1102
- this.channelListeners[channel]?.event.delete(listener);
1112
+ offChannel(type, channel, listener) {
1113
+ const listeners = this.channelListeners[channel];
1114
+ listeners?.[type].delete(listener);
1103
1115
  }
1104
- offReply(channel, listener) {
1105
- this.channelListeners[channel]?.reply.delete(listener);
1106
- }
1107
- removeAllChannelListeners() {
1108
- this.channelListeners = {};
1116
+ onSocket(type, socket, listener) {
1117
+ const listeners = this.getOrCreateSocketListeners(type, socket);
1118
+ listeners.add(listener);
1119
+ return listener;
1109
1120
  }
1110
- onAbortSocketMessages(sockets, listener) {
1111
- for (const socket of sockets) {
1112
- let listeners = this.socketListeners.messageAbort.get(socket);
1113
- if (!listeners) {
1114
- listeners = /* @__PURE__ */ new Set();
1115
- this.socketListeners.messageAbort.set(socket, listeners);
1116
- }
1117
- listeners.add(listener);
1121
+ getOrCreateSocketListeners(type, socket) {
1122
+ const listeners = this.socketListeners[type].get(socket) ?? /* @__PURE__ */ new Set();
1123
+ if (!this.socketListeners[type].has(socket)) {
1124
+ this.socketListeners[type].set(socket, listeners);
1118
1125
  }
1119
- return listener;
1126
+ return listeners;
1120
1127
  }
1121
- offAbortSocketMessages(sockets, listener) {
1122
- for (const socket of sockets) {
1123
- this.socketListeners.messageAbort.get(socket)?.delete(listener);
1128
+ offSocket(type, socket, listener) {
1129
+ const listeners = this.socketListeners[type].get(socket);
1130
+ listeners?.delete(listener);
1131
+ }
1132
+ emitSocket(type, socket, options = {}) {
1133
+ for (const listener of this.socketListeners[type].get(socket) ?? []) {
1134
+ listener(options);
1124
1135
  }
1125
1136
  }
1126
- abortSocketMessages(sockets = this.sockets) {
1127
- const abortError = new WebSocketMessageAbortError();
1128
- for (const socket of sockets) {
1129
- const listeners = this.socketListeners.messageAbort.get(socket) ?? [];
1130
- for (const listener of listeners) {
1131
- listener(abortError);
1137
+ offAny() {
1138
+ this.channelListeners = {};
1139
+ for (const listenersBySocket of Object.values(this.socketListeners)) {
1140
+ for (const listeners of listenersBySocket.values()) {
1141
+ listeners.clear();
1132
1142
  }
1133
1143
  }
1134
1144
  }
@@ -1181,11 +1191,9 @@ var WebSocketServer = class extends WebSocketHandler_default {
1181
1191
  if (!this.webSocketServer || !this.isRunning) {
1182
1192
  return;
1183
1193
  }
1184
- super.removeAllChannelListeners();
1185
- super.abortSocketMessages();
1194
+ super.offAny();
1186
1195
  await super.closeClientSockets();
1187
1196
  await closeServerSocket(this.webSocketServer, { timeout: this.socketTimeout });
1188
- this.webSocketServer.removeAllListeners();
1189
1197
  this.webSocketServer = void 0;
1190
1198
  }
1191
1199
  };
@@ -1535,17 +1543,17 @@ var InterceptorServer = class {
1535
1543
  return void 0;
1536
1544
  }
1537
1545
  async startHttpServer() {
1546
+ this.httpServerOrThrow.on("request", this.handleHttpRequest);
1538
1547
  await startHttpServer(this.httpServerOrThrow, {
1539
1548
  hostname: this.hostname,
1540
1549
  port: this.port
1541
1550
  });
1542
1551
  this.port = getHttpServerPort(this.httpServerOrThrow);
1543
- this.httpServerOrThrow.on("request", this.handleHttpRequest);
1544
1552
  }
1545
1553
  startWebSocketServer() {
1554
+ this.webSocketServerOrThrow.onChannel("event", "interceptors/workers/commit", this.commitWorker);
1555
+ this.webSocketServerOrThrow.onChannel("event", "interceptors/workers/reset", this.resetWorker);
1546
1556
  this.webSocketServerOrThrow.start();
1547
- this.webSocketServerOrThrow.onEvent("interceptors/workers/commit", this.commitWorker);
1548
- this.webSocketServerOrThrow.onEvent("interceptors/workers/reset", this.resetWorker);
1549
1557
  }
1550
1558
  commitWorker = (message, socket) => {
1551
1559
  const commit = message.data;
@@ -1553,18 +1561,31 @@ var InterceptorServer = class {
1553
1561
  this.registerWorkerSocketIfUnknown(socket);
1554
1562
  return {};
1555
1563
  };
1556
- resetWorker = (message, socket) => {
1557
- this.removeHttpHandlersBySocket(socket);
1558
- const handlersToResetTo = message.data;
1559
- const isWorkerNoLongerCommitted = handlersToResetTo === void 0;
1560
- if (isWorkerNoLongerCommitted) {
1561
- this.webSocketServerOrThrow.abortSocketMessages([socket]);
1562
- } else {
1563
- for (const handler of handlersToResetTo) {
1564
- this.registerHttpHandler(handler, socket);
1564
+ resetWorker = ({ data: handlersToRecommit }, socket) => {
1565
+ this.registerWorkerSocketIfUnknown(socket);
1566
+ this.webSocketServerOrThrow.emitSocket("abortRequests", socket, {
1567
+ shouldAbortRequest: (request) => {
1568
+ const isResponseCreationRequest = this.webSocketServerOrThrow.isChannelEvent(
1569
+ request,
1570
+ "interceptors/responses/create"
1571
+ );
1572
+ if (!isResponseCreationRequest) {
1573
+ return false;
1574
+ }
1575
+ const isHandlerStillCommitted = handlersToRecommit.some(
1576
+ /* istanbul ignore next -- @preserve
1577
+ * Ensuring this function is called in tests is difficult because it requires clearing or stopping a worker
1578
+ * at the exact moment a request is being handled, in a scenario when there are other handlers still
1579
+ * committed. */
1580
+ (handler) => request.data.handlerId === handler.id
1581
+ );
1582
+ return !isHandlerStillCommitted;
1565
1583
  }
1584
+ });
1585
+ this.removeHttpHandlersBySocket(socket);
1586
+ for (const handler of handlersToRecommit) {
1587
+ this.registerHttpHandler(handler, socket);
1566
1588
  }
1567
- this.registerWorkerSocketIfUnknown(socket);
1568
1589
  return {};
1569
1590
  };
1570
1591
  registerHttpHandler({ id, baseURL, method, path: path2 }, socket) {
@@ -1605,8 +1626,8 @@ var InterceptorServer = class {
1605
1626
  this.httpServer = void 0;
1606
1627
  }
1607
1628
  async stopWebSocketServer() {
1608
- this.webSocketServerOrThrow.offEvent("interceptors/workers/commit", this.commitWorker);
1609
- this.webSocketServerOrThrow.offEvent("interceptors/workers/reset", this.resetWorker);
1629
+ this.webSocketServerOrThrow.offChannel("event", "interceptors/workers/commit", this.commitWorker);
1630
+ this.webSocketServerOrThrow.offChannel("event", "interceptors/workers/reset", this.resetWorker);
1610
1631
  await this.webSocketServerOrThrow.stop();
1611
1632
  this.webSocketServer = void 0;
1612
1633
  }
@@ -1736,6 +1757,8 @@ function createInterceptorServer(options = {}) {
1736
1757
  /* istanbul ignore next -- @preserve
1737
1758
  * Reply listeners are always present when notified in normal conditions. If they were not present, the request
1738
1759
  * would reach a timeout and not be responded. The empty set serves as a fallback. */
1760
+ /* istanbul ignore if -- @preserve
1761
+ * Aborting requests is highly non-deterministic because it depends on specific timing of socket events. */
1739
1762
  /* istanbul ignore if -- @preserve
1740
1763
  * This should never happen, but let's check that the token identifier is valid after generated. */
1741
1764
  /* istanbul ignore if -- @preserve
@@ -1746,6 +1769,9 @@ function createInterceptorServer(options = {}) {
1746
1769
  * The HTTP server is initialized before using this method in normal conditions. */
1747
1770
  /* istanbul ignore if -- @preserve
1748
1771
  * The web socket server is initialized before using this method in normal conditions. */
1772
+ /* istanbul ignore if -- @preserve
1773
+ * While resetting a worker, there could be other types of requests in progress. These are not guaranteed to
1774
+ * exist and are not related to handler resets, so we let them continue. */
1749
1775
  /* istanbul ignore else -- @preserve
1750
1776
  * This is always true during tests because we force max-age=0 to disable CORS caching. */
1751
1777
  /* istanbul ignore next -- @preserve
@@ -1769,5 +1795,5 @@ exports.listInterceptorTokens = listInterceptorTokens;
1769
1795
  exports.logger = logger;
1770
1796
  exports.readInterceptorTokenFromFile = readInterceptorTokenFromFile;
1771
1797
  exports.removeInterceptorToken = removeInterceptorToken;
1772
- //# sourceMappingURL=chunk-ZU6IGW27.js.map
1773
- //# sourceMappingURL=chunk-ZU6IGW27.js.map
1798
+ //# sourceMappingURL=chunk-OKZEX5DQ.js.map
1799
+ //# sourceMappingURL=chunk-OKZEX5DQ.js.map