@rest-vir/define-service 0.1.0 → 0.3.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.
@@ -78,7 +78,7 @@ export declare function createMockClientWebSocketConstructor<const WebSocketToCo
78
78
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
79
79
  */
80
80
  export type MockClientWebSocketListeners = Partial<{
81
- [EventName in keyof CommonWebSocketEventMap]: Set<(event: CommonWebSocketEventMap[EventName]) => void>;
81
+ [EventName in keyof CommonWebSocketEventMap]: Set<(event: CommonWebSocketEventMap[EventName]) => MaybePromise<void>>;
82
82
  }>;
83
83
  /**
84
84
  * A mock WebSocket constructor for connections from the client side. This can be passed to
@@ -165,14 +165,14 @@ export declare class MockClientWebSocket<const WebSocketToConnect extends WebSoc
165
165
  *
166
166
  * @see https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener
167
167
  */
168
- addEventListener<const EventName extends keyof CommonWebSocketEventMap>(eventName: EventName, listener: (event: CommonWebSocketEventMap[EventName]) => void): void;
168
+ addEventListener<const EventName extends keyof CommonWebSocketEventMap>(eventName: EventName, listener: (event: CommonWebSocketEventMap[EventName]) => MaybePromise<void>): void;
169
169
  /**
170
170
  * Implements and mocks the standard `WebSocket.removeEventListener` property but with message
171
171
  * type safety.
172
172
  *
173
173
  * @see https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener
174
174
  */
175
- removeEventListener<const EventName extends keyof CommonWebSocketEventMap>(eventName: EventName, listener: (event: CommonWebSocketEventMap[EventName]) => void): void;
175
+ removeEventListener<const EventName extends keyof CommonWebSocketEventMap>(eventName: EventName, listener: (event: CommonWebSocketEventMap[EventName]) => MaybePromise<void>): void;
176
176
  /**
177
177
  * Implements and mocks the standard `WebSocket.send` property but with message type safety.
178
178
  *
@@ -139,11 +139,13 @@ export class MockClientWebSocket {
139
139
  * methods instead (which appropriately dispatch their events).
140
140
  */
141
141
  dispatchEvent(eventName, event) {
142
- this.listeners[eventName]?.forEach((listener) => listener({
143
- ...event,
144
- target: this,
145
- type: eventName,
146
- }));
142
+ this.listeners[eventName]?.forEach((listener) => {
143
+ void listener({
144
+ ...event,
145
+ target: this,
146
+ type: eventName,
147
+ });
148
+ });
147
149
  }
148
150
  /**
149
151
  * Implements and mocks the standard `WebSocket.addEventListener` property but with message type
@@ -187,7 +189,7 @@ export class MockClientWebSocket {
187
189
  return;
188
190
  }
189
191
  this.dispatchEvent('message', {
190
- data,
192
+ data: JSON.stringify(data),
191
193
  });
192
194
  void this.sendCallback?.({
193
195
  messageData: data,
@@ -69,6 +69,13 @@ export type SendAndWaitForReplyParams<Location extends WebSocketLocation, WebSoc
69
69
  * @default {seconds: 10}
70
70
  */
71
71
  timeout?: Readonly<AnyDuration> | undefined;
72
+ /**
73
+ * An optional function to check if the current reply is the one you were waiting for.
74
+ *
75
+ * If this is set, `sendAndWaitForReply` will wait until a reply is received that matches this
76
+ * condition. If this not set, the first reply is used.
77
+ */
78
+ replyCheck?: (messageFromHost: WebSocketToConnect extends NoParam ? any : Exclude<WebSocketToConnect, NoParam>['MessageFromHostType']) => MaybePromise<boolean>;
72
79
  };
73
80
  /**
74
81
  * Collapsed version of {@link SendAndWaitForReplyParams} for the `sendAndWaitForReply` method that
@@ -95,6 +102,11 @@ export type OverwriteWebSocketMethods<WebSocketClass extends CommonWebSocket, Lo
95
102
  MessageFromHostType: true;
96
103
  SearchParamsType: true;
97
104
  }>> | NoParam = NoParam> = Overwrite<WebSocketClass, {
105
+ /**
106
+ * Closes the WebSocket and waits it to actually close (so that you _know_ it's been closed
107
+ * once this resolves).
108
+ */
109
+ close(): Promise<void>;
98
110
  /**
99
111
  * Adds an event listener that's wrapped in assertions to verify that message events have
100
112
  * the expected contents.
@@ -1,5 +1,5 @@
1
1
  import { waitUntil } from '@augment-vir/assert';
2
- import { DeferredPromise, ensureErrorAndPrependMessage, getOrSet, stringify, } from '@augment-vir/common';
2
+ import { callAsynchronously, DeferredPromise, ensureErrorAndPrependMessage, getOrSet, stringify, wrapInTry, } from '@augment-vir/common';
3
3
  import { convertDuration } from 'date-vir';
4
4
  import { assertValidShape } from 'object-shape-tester';
5
5
  import { parseJsonWithUndefined } from '../augments/json.js';
@@ -44,12 +44,31 @@ export function getOppositeWebSocketLocation(originalWebSocketLocation) {
44
44
  */
45
45
  export function overwriteWebSocketMethods(webSocketDefinition, rawWebSocket, webSocketLocation) {
46
46
  const originalSend = rawWebSocket.send;
47
+ const originalClose = rawWebSocket.close;
47
48
  const originalAddEventListener = rawWebSocket.addEventListener;
48
49
  const originalRemoveEventListener = rawWebSocket.removeEventListener;
49
50
  const webSocket = rawWebSocket;
51
+ const deferredClosePromise = new DeferredPromise();
52
+ webSocket.addEventListener('close', () => {
53
+ /**
54
+ * Call this asynchronously so the other `close` event listeners get fired before this
55
+ * resolves.
56
+ */
57
+ void callAsynchronously(() => {
58
+ deferredClosePromise.resolve();
59
+ });
60
+ });
50
61
  const originalListenerMap = {};
51
62
  Object.assign(webSocket, {
52
63
  originalListenerMap,
64
+ async close() {
65
+ originalClose.call(webSocket);
66
+ /**
67
+ * Closing takes a _long time_ for some reason, so we want to wait until it's actually
68
+ * done before proceeding with other operations.
69
+ */
70
+ await deferredClosePromise.promise;
71
+ },
53
72
  addEventListener(eventName, listener) {
54
73
  function newListener(event) {
55
74
  const baseParams = {
@@ -84,11 +103,16 @@ export function overwriteWebSocketMethods(webSocketDefinition, rawWebSocket, web
84
103
  originalRemoveEventListener.call(webSocket, eventName, existing);
85
104
  }
86
105
  },
87
- async sendAndWaitForReply({ message, timeout = { seconds: 10 }, } = {}) {
106
+ async sendAndWaitForReply({ message, timeout = { seconds: 10 }, replyCheck, } = {}) {
88
107
  const deferredReply = new DeferredPromise();
89
- function listener({ message, }) {
108
+ async function listener({ message, }) {
90
109
  if (!deferredReply.isSettled) {
91
- deferredReply.resolve(message);
110
+ const matchesChecker = replyCheck
111
+ ? (await wrapInTry(() => replyCheck(message))) === true
112
+ : true;
113
+ if (matchesChecker) {
114
+ deferredReply.resolve(message);
115
+ }
92
116
  }
93
117
  }
94
118
  setTimeout(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rest-vir/define-service",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Define an connect to a declarative and type safe REST and WebSocket service.",
5
5
  "keywords": [
6
6
  "rest",