@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
@@ -34,13 +34,13 @@ var HttpServerTimeoutError = class extends Error {
34
34
  var HttpServerStartTimeoutError = class extends HttpServerTimeoutError {
35
35
  constructor(reachedTimeout) {
36
36
  super(`HTTP server start timed out after ${reachedTimeout}ms.`);
37
- this.name = "HttpServerStartTimeout";
37
+ this.name = "HttpServerStartTimeoutError";
38
38
  }
39
39
  };
40
40
  var HttpServerStopTimeoutError = class extends HttpServerTimeoutError {
41
41
  constructor(reachedTimeout) {
42
42
  super(`HTTP server stop timed out after ${reachedTimeout}ms.`);
43
- this.name = "HttpServerStopTimeout";
43
+ this.name = "HttpServerStopTimeoutError";
44
44
  }
45
45
  };
46
46
  var DEFAULT_HTTP_SERVER_LIFECYCLE_TIMEOUT = 60 * 1e3;
@@ -121,13 +121,13 @@ var WebSocketTimeoutError = class extends Error {
121
121
  var WebSocketOpenTimeoutError = class extends WebSocketTimeoutError {
122
122
  constructor(reachedTimeout) {
123
123
  super(`Web socket open timed out after ${reachedTimeout}ms.`);
124
- this.name = "WebSocketOpenTimeout";
124
+ this.name = "WebSocketOpenTimeoutError";
125
125
  }
126
126
  };
127
127
  var WebSocketMessageTimeoutError = class extends WebSocketTimeoutError {
128
128
  constructor(reachedTimeout) {
129
129
  super(`Web socket message timed out after ${reachedTimeout}ms.`);
130
- this.name = "WebSocketMessageTimeout";
130
+ this.name = "WebSocketMessageTimeoutError";
131
131
  }
132
132
  };
133
133
  var WebSocketMessageAbortError = class extends WebSocketTimeoutError {
@@ -139,7 +139,7 @@ var WebSocketMessageAbortError = class extends WebSocketTimeoutError {
139
139
  var WebSocketCloseTimeoutError = class extends WebSocketTimeoutError {
140
140
  constructor(reachedTimeout) {
141
141
  super(`Web socket close timed out after ${reachedTimeout}ms.`);
142
- this.name = "WebSocketCloseTimeout";
142
+ this.name = "WebSocketCloseTimeoutError";
143
143
  }
144
144
  };
145
145
  var DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT = 60 * 1e3;
@@ -841,14 +841,14 @@ var importCrypto = createCachedDynamicImport_default(async () => {
841
841
  // src/webSocket/constants.ts
842
842
  var WEB_SOCKET_CONTROL_MESSAGES = Object.freeze(["socket:auth:valid"]);
843
843
 
844
- // src/webSocket/errors/InvalidWebSocketMessage.ts
845
- var InvalidWebSocketMessage = class extends Error {
844
+ // src/webSocket/errors/InvalidWebSocketMessageError.ts
845
+ var InvalidWebSocketMessageError = class extends Error {
846
846
  constructor(message) {
847
847
  super(`Web socket message is invalid and could not be parsed: ${message}`);
848
- this.name = "InvalidWebSocketMessage";
848
+ this.name = "InvalidWebSocketMessageError";
849
849
  }
850
850
  };
851
- var InvalidWebSocketMessage_default = InvalidWebSocketMessage;
851
+ var InvalidWebSocketMessageError_default = InvalidWebSocketMessageError;
852
852
 
853
853
  // src/webSocket/errors/NotRunningWebSocketHandlerError.ts
854
854
  var NotRunningWebSocketHandlerError = class extends Error {
@@ -866,7 +866,7 @@ var WebSocketHandler = class {
866
866
  messageTimeout;
867
867
  channelListeners = {};
868
868
  socketListeners = {
869
- messageAbort: /* @__PURE__ */ new Map()
869
+ abortRequests: /* @__PURE__ */ new Map()
870
870
  };
871
871
  constructor(options) {
872
872
  this.socketTimeout = options.socketTimeout ?? DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT;
@@ -887,10 +887,12 @@ var WebSocketHandler = class {
887
887
  }
888
888
  socket.addEventListener("error", handleSocketError);
889
889
  const handleSocketClose = () => {
890
+ this.sockets.delete(socket);
891
+ this.emitSocket("abortRequests", socket);
892
+ this.socketListeners.abortRequests.delete(socket);
890
893
  socket.removeEventListener("message", handleSocketMessage);
891
894
  socket.removeEventListener("close", handleSocketClose);
892
895
  socket.removeEventListener("error", handleSocketError);
893
- this.removeSocket(socket);
894
896
  };
895
897
  socket.addEventListener("close", handleSocketClose);
896
898
  this.sockets.add(socket);
@@ -914,7 +916,7 @@ var WebSocketHandler = class {
914
916
  if (typeof data === "string") {
915
917
  return data;
916
918
  } else {
917
- throw new InvalidWebSocketMessage_default(data);
919
+ throw new InvalidWebSocketMessageError_default(data);
918
920
  }
919
921
  }
920
922
  parseMessage(stringifiedMessage) {
@@ -922,10 +924,10 @@ var WebSocketHandler = class {
922
924
  try {
923
925
  parsedMessage = JSON.parse(stringifiedMessage);
924
926
  } catch {
925
- throw new InvalidWebSocketMessage_default(stringifiedMessage);
927
+ throw new InvalidWebSocketMessageError_default(stringifiedMessage);
926
928
  }
927
929
  if (!this.isMessage(parsedMessage)) {
928
- throw new InvalidWebSocketMessage_default(stringifiedMessage);
930
+ throw new InvalidWebSocketMessageError_default(stringifiedMessage);
929
931
  }
930
932
  if (this.isReplyMessage(parsedMessage)) {
931
933
  return {
@@ -944,6 +946,9 @@ var WebSocketHandler = class {
944
946
  isMessage(message) {
945
947
  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");
946
948
  }
949
+ isChannelEvent(event, channel) {
950
+ return event.channel === channel;
951
+ }
947
952
  async notifyListeners(message, socket) {
948
953
  if (this.isReplyMessage(message)) {
949
954
  await this.notifyReplyListeners(message, socket);
@@ -972,10 +977,6 @@ var WebSocketHandler = class {
972
977
  });
973
978
  await Promise.all(closingPromises);
974
979
  }
975
- removeSocket(socket) {
976
- this.abortSocketMessages([socket]);
977
- this.sockets.delete(socket);
978
- }
979
980
  async createEventMessage(channel, eventData) {
980
981
  const crypto2 = await importCrypto();
981
982
  const eventMessage = {
@@ -992,31 +993,46 @@ var WebSocketHandler = class {
992
993
  async request(channel, requestData, options = {}) {
993
994
  const request = await this.createEventMessage(channel, requestData);
994
995
  this.sendMessage(request, options.sockets);
995
- const response = await this.waitForReply(channel, request.id, options.sockets);
996
+ const response = await this.waitForReply(channel, request, options.sockets);
996
997
  return response.data;
997
998
  }
998
- async waitForReply(channel, requestId, sockets = this.sockets) {
999
+ async waitForReply(channel, request, sockets = this.sockets) {
999
1000
  return new Promise((resolve, reject) => {
1000
1001
  const replyTimeout = setTimeout(() => {
1001
- this.offReply(channel, replyListener);
1002
- this.offAbortSocketMessages(sockets, abortListener);
1002
+ this.offChannel("reply", channel, replyListener);
1003
+ for (const socket of sockets) {
1004
+ this.offSocket("abortRequests", socket, abortRequestsHandler);
1005
+ }
1003
1006
  const timeoutError = new WebSocketMessageTimeoutError(this.messageTimeout);
1004
1007
  reject(timeoutError);
1005
1008
  }, this.messageTimeout);
1006
- const abortListener = this.onAbortSocketMessages(sockets, (error) => {
1009
+ const replyListener = this.onChannel("reply", channel, (message) => {
1010
+ if (message.requestId !== request.id) {
1011
+ return;
1012
+ }
1007
1013
  clearTimeout(replyTimeout);
1008
- this.offReply(channel, replyListener);
1009
- this.offAbortSocketMessages(sockets, abortListener);
1010
- reject(error);
1011
- });
1012
- const replyListener = this.onReply(channel, (message) => {
1013
- if (message.requestId === requestId) {
1014
- clearTimeout(replyTimeout);
1015
- this.offReply(channel, replyListener);
1016
- this.offAbortSocketMessages(sockets, abortListener);
1017
- resolve(message);
1014
+ this.offChannel("reply", channel, replyListener);
1015
+ for (const socket of sockets) {
1016
+ this.offSocket("abortRequests", socket, abortRequestsHandler);
1018
1017
  }
1018
+ resolve(message);
1019
1019
  });
1020
+ const abortRequestsHandler = (options) => {
1021
+ const shouldAbortRequest = options.shouldAbortRequest === void 0 || options.shouldAbortRequest(request);
1022
+ if (!shouldAbortRequest) {
1023
+ return;
1024
+ }
1025
+ clearTimeout(replyTimeout);
1026
+ this.offChannel("reply", channel, replyListener);
1027
+ for (const socket of sockets) {
1028
+ this.offSocket("abortRequests", socket, abortRequestsHandler);
1029
+ }
1030
+ const abortError = new WebSocketMessageAbortError();
1031
+ reject(abortError);
1032
+ };
1033
+ for (const socket of sockets) {
1034
+ this.onSocket("abortRequests", socket, abortRequestsHandler);
1035
+ }
1020
1036
  });
1021
1037
  }
1022
1038
  isReplyMessage(message) {
@@ -1047,9 +1063,9 @@ var WebSocketHandler = class {
1047
1063
  socket.send(stringifiedMessage);
1048
1064
  }
1049
1065
  }
1050
- onEvent(channel, listener) {
1066
+ onChannel(type, channel, listener) {
1051
1067
  const listeners = this.getOrCreateChannelListeners(channel);
1052
- listeners.event.add(listener);
1068
+ listeners[type].add(listener);
1053
1069
  return listener;
1054
1070
  }
1055
1071
  getOrCreateChannelListeners(channel) {
@@ -1062,42 +1078,36 @@ var WebSocketHandler = class {
1062
1078
  }
1063
1079
  return listeners;
1064
1080
  }
1065
- onReply(channel, listener) {
1066
- const listeners = this.getOrCreateChannelListeners(channel);
1067
- listeners.reply.add(listener);
1068
- return listener;
1069
- }
1070
- offEvent(channel, listener) {
1071
- this.channelListeners[channel]?.event.delete(listener);
1081
+ offChannel(type, channel, listener) {
1082
+ const listeners = this.channelListeners[channel];
1083
+ listeners?.[type].delete(listener);
1072
1084
  }
1073
- offReply(channel, listener) {
1074
- this.channelListeners[channel]?.reply.delete(listener);
1075
- }
1076
- removeAllChannelListeners() {
1077
- this.channelListeners = {};
1085
+ onSocket(type, socket, listener) {
1086
+ const listeners = this.getOrCreateSocketListeners(type, socket);
1087
+ listeners.add(listener);
1088
+ return listener;
1078
1089
  }
1079
- onAbortSocketMessages(sockets, listener) {
1080
- for (const socket of sockets) {
1081
- let listeners = this.socketListeners.messageAbort.get(socket);
1082
- if (!listeners) {
1083
- listeners = /* @__PURE__ */ new Set();
1084
- this.socketListeners.messageAbort.set(socket, listeners);
1085
- }
1086
- listeners.add(listener);
1090
+ getOrCreateSocketListeners(type, socket) {
1091
+ const listeners = this.socketListeners[type].get(socket) ?? /* @__PURE__ */ new Set();
1092
+ if (!this.socketListeners[type].has(socket)) {
1093
+ this.socketListeners[type].set(socket, listeners);
1087
1094
  }
1088
- return listener;
1095
+ return listeners;
1089
1096
  }
1090
- offAbortSocketMessages(sockets, listener) {
1091
- for (const socket of sockets) {
1092
- this.socketListeners.messageAbort.get(socket)?.delete(listener);
1097
+ offSocket(type, socket, listener) {
1098
+ const listeners = this.socketListeners[type].get(socket);
1099
+ listeners?.delete(listener);
1100
+ }
1101
+ emitSocket(type, socket, options = {}) {
1102
+ for (const listener of this.socketListeners[type].get(socket) ?? []) {
1103
+ listener(options);
1093
1104
  }
1094
1105
  }
1095
- abortSocketMessages(sockets = this.sockets) {
1096
- const abortError = new WebSocketMessageAbortError();
1097
- for (const socket of sockets) {
1098
- const listeners = this.socketListeners.messageAbort.get(socket) ?? [];
1099
- for (const listener of listeners) {
1100
- listener(abortError);
1106
+ offAny() {
1107
+ this.channelListeners = {};
1108
+ for (const listenersBySocket of Object.values(this.socketListeners)) {
1109
+ for (const listeners of listenersBySocket.values()) {
1110
+ listeners.clear();
1101
1111
  }
1102
1112
  }
1103
1113
  }
@@ -1150,11 +1160,9 @@ var WebSocketServer = class extends WebSocketHandler_default {
1150
1160
  if (!this.webSocketServer || !this.isRunning) {
1151
1161
  return;
1152
1162
  }
1153
- super.removeAllChannelListeners();
1154
- super.abortSocketMessages();
1163
+ super.offAny();
1155
1164
  await super.closeClientSockets();
1156
1165
  await closeServerSocket(this.webSocketServer, { timeout: this.socketTimeout });
1157
- this.webSocketServer.removeAllListeners();
1158
1166
  this.webSocketServer = void 0;
1159
1167
  }
1160
1168
  };
@@ -1504,17 +1512,17 @@ var InterceptorServer = class {
1504
1512
  return void 0;
1505
1513
  }
1506
1514
  async startHttpServer() {
1515
+ this.httpServerOrThrow.on("request", this.handleHttpRequest);
1507
1516
  await startHttpServer(this.httpServerOrThrow, {
1508
1517
  hostname: this.hostname,
1509
1518
  port: this.port
1510
1519
  });
1511
1520
  this.port = getHttpServerPort(this.httpServerOrThrow);
1512
- this.httpServerOrThrow.on("request", this.handleHttpRequest);
1513
1521
  }
1514
1522
  startWebSocketServer() {
1523
+ this.webSocketServerOrThrow.onChannel("event", "interceptors/workers/commit", this.commitWorker);
1524
+ this.webSocketServerOrThrow.onChannel("event", "interceptors/workers/reset", this.resetWorker);
1515
1525
  this.webSocketServerOrThrow.start();
1516
- this.webSocketServerOrThrow.onEvent("interceptors/workers/commit", this.commitWorker);
1517
- this.webSocketServerOrThrow.onEvent("interceptors/workers/reset", this.resetWorker);
1518
1526
  }
1519
1527
  commitWorker = (message, socket) => {
1520
1528
  const commit = message.data;
@@ -1522,18 +1530,31 @@ var InterceptorServer = class {
1522
1530
  this.registerWorkerSocketIfUnknown(socket);
1523
1531
  return {};
1524
1532
  };
1525
- resetWorker = (message, socket) => {
1526
- this.removeHttpHandlersBySocket(socket);
1527
- const handlersToResetTo = message.data;
1528
- const isWorkerNoLongerCommitted = handlersToResetTo === void 0;
1529
- if (isWorkerNoLongerCommitted) {
1530
- this.webSocketServerOrThrow.abortSocketMessages([socket]);
1531
- } else {
1532
- for (const handler of handlersToResetTo) {
1533
- this.registerHttpHandler(handler, socket);
1533
+ resetWorker = ({ data: handlersToRecommit }, socket) => {
1534
+ this.registerWorkerSocketIfUnknown(socket);
1535
+ this.webSocketServerOrThrow.emitSocket("abortRequests", socket, {
1536
+ shouldAbortRequest: (request) => {
1537
+ const isResponseCreationRequest = this.webSocketServerOrThrow.isChannelEvent(
1538
+ request,
1539
+ "interceptors/responses/create"
1540
+ );
1541
+ if (!isResponseCreationRequest) {
1542
+ return false;
1543
+ }
1544
+ const isHandlerStillCommitted = handlersToRecommit.some(
1545
+ /* istanbul ignore next -- @preserve
1546
+ * Ensuring this function is called in tests is difficult because it requires clearing or stopping a worker
1547
+ * at the exact moment a request is being handled, in a scenario when there are other handlers still
1548
+ * committed. */
1549
+ (handler) => request.data.handlerId === handler.id
1550
+ );
1551
+ return !isHandlerStillCommitted;
1534
1552
  }
1553
+ });
1554
+ this.removeHttpHandlersBySocket(socket);
1555
+ for (const handler of handlersToRecommit) {
1556
+ this.registerHttpHandler(handler, socket);
1535
1557
  }
1536
- this.registerWorkerSocketIfUnknown(socket);
1537
1558
  return {};
1538
1559
  };
1539
1560
  registerHttpHandler({ id, baseURL, method, path: path2 }, socket) {
@@ -1574,8 +1595,8 @@ var InterceptorServer = class {
1574
1595
  this.httpServer = void 0;
1575
1596
  }
1576
1597
  async stopWebSocketServer() {
1577
- this.webSocketServerOrThrow.offEvent("interceptors/workers/commit", this.commitWorker);
1578
- this.webSocketServerOrThrow.offEvent("interceptors/workers/reset", this.resetWorker);
1598
+ this.webSocketServerOrThrow.offChannel("event", "interceptors/workers/commit", this.commitWorker);
1599
+ this.webSocketServerOrThrow.offChannel("event", "interceptors/workers/reset", this.resetWorker);
1579
1600
  await this.webSocketServerOrThrow.stop();
1580
1601
  this.webSocketServer = void 0;
1581
1602
  }
@@ -1705,6 +1726,8 @@ function createInterceptorServer(options = {}) {
1705
1726
  /* istanbul ignore next -- @preserve
1706
1727
  * Reply listeners are always present when notified in normal conditions. If they were not present, the request
1707
1728
  * would reach a timeout and not be responded. The empty set serves as a fallback. */
1729
+ /* istanbul ignore if -- @preserve
1730
+ * Aborting requests is highly non-deterministic because it depends on specific timing of socket events. */
1708
1731
  /* istanbul ignore if -- @preserve
1709
1732
  * This should never happen, but let's check that the token identifier is valid after generated. */
1710
1733
  /* istanbul ignore if -- @preserve
@@ -1715,6 +1738,9 @@ function createInterceptorServer(options = {}) {
1715
1738
  * The HTTP server is initialized before using this method in normal conditions. */
1716
1739
  /* istanbul ignore if -- @preserve
1717
1740
  * The web socket server is initialized before using this method in normal conditions. */
1741
+ /* istanbul ignore if -- @preserve
1742
+ * While resetting a worker, there could be other types of requests in progress. These are not guaranteed to
1743
+ * exist and are not related to handler resets, so we let them continue. */
1718
1744
  /* istanbul ignore else -- @preserve
1719
1745
  * This is always true during tests because we force max-age=0 to disable CORS caching. */
1720
1746
  /* istanbul ignore next -- @preserve
@@ -1727,5 +1753,5 @@ function createInterceptorServer(options = {}) {
1727
1753
  * Due to the rare nature of this edge case, we can't reliably reproduce it in tests. */
1728
1754
 
1729
1755
  export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_INTERCEPTOR_TOKENS_DIRECTORY, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default, RunningInterceptorServerError_default, createCachedDynamicImport_default, createInterceptorServer, createInterceptorToken, listInterceptorTokens, logger, readInterceptorTokenFromFile, removeInterceptorToken };
1730
- //# sourceMappingURL=chunk-XCYZ5L2M.mjs.map
1731
- //# sourceMappingURL=chunk-XCYZ5L2M.mjs.map
1756
+ //# sourceMappingURL=chunk-EIYQEPK2.mjs.map
1757
+ //# sourceMappingURL=chunk-EIYQEPK2.mjs.map