@arrirpc/client 0.79.1 → 0.80.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/dist/index.cjs CHANGED
@@ -220,23 +220,36 @@ function arriSseRequest(opts, options) {
220
220
  maxRetryCount: options.maxRetryCount,
221
221
  maxRetryInterval: options.maxRetryInterval
222
222
  });
223
+ let interval;
224
+ let intervalDuration;
225
+ const timeoutMultiplier = options.heartbeatTimeoutMultiplier ?? 2;
226
+ if (timeoutMultiplier < 1) {
227
+ throw new Error(`heartbeatTimeoutMultiplier cannot be less than 1`);
228
+ }
229
+ function resetInterval() {
230
+ if (interval) clearInterval(interval);
231
+ if (!intervalDuration) return;
232
+ interval = setInterval(() => {
233
+ if (controller.signal.aborted) return;
234
+ controller.reconnect();
235
+ }, intervalDuration);
236
+ }
223
237
  const controller = eventSource.listen({
224
238
  onMessage(message) {
239
+ resetInterval();
225
240
  if (message.event === "message" || message.event === void 0 || message.event === "") {
226
241
  options.onMessage?.(opts.responseFromString(message.data));
227
242
  return;
228
243
  }
229
244
  if (message.event === "done") {
245
+ if (interval) clearInterval(interval);
230
246
  controller.abort();
231
247
  }
232
248
  },
233
- onRequest(context) {
234
- options.onRequest?.(context);
235
- },
249
+ onRequest: options.onRequest,
236
250
  onRequestError(context) {
237
- if (opts.onError) {
238
- opts.onError(context.error);
239
- }
251
+ clearInterval(interval);
252
+ if (opts.onError) opts.onError(context.error);
240
253
  options.onRequestError?.({
241
254
  ...context,
242
255
  error: new ArriErrorInstance({
@@ -247,15 +260,19 @@ function arriSseRequest(opts, options) {
247
260
  });
248
261
  },
249
262
  onResponse(context) {
263
+ const heartbeatIntervalHeader = Number(
264
+ context.response?.headers.get("heartbeat-interval") ?? 0
265
+ );
266
+ if (context.response.ok && !Number.isNaN(heartbeatIntervalHeader) && heartbeatIntervalHeader > 0) {
267
+ intervalDuration = heartbeatIntervalHeader * timeoutMultiplier;
268
+ resetInterval();
269
+ }
250
270
  options.onResponse?.(context);
251
271
  },
252
272
  async onResponseError(context) {
253
- if (opts.onError) {
254
- opts.onError(context.error);
255
- }
256
- if (!options.onResponseError) {
257
- return;
258
- }
273
+ clearInterval(interval);
274
+ if (opts.onError) opts.onError(context.error);
275
+ if (!options.onResponseError) return;
259
276
  try {
260
277
  const arriError = ArriErrorInstance.fromJson(
261
278
  await context.response.json()
package/dist/index.d.cts CHANGED
@@ -38,7 +38,13 @@ interface ArriRequestOptions {
38
38
  onRequestError?: FetchOptions['onRequestError'];
39
39
  onResponse?: FetchOptions['onResponse'];
40
40
  onResponseError?: FetchOptions['onResponseError'];
41
+ /**
42
+ * Set a timeout in milliseconds to wait for a response before throwing an error
43
+ */
41
44
  timeout?: FetchOptions['timeout'];
45
+ /**
46
+ * An AbortController signal - used for cancelling an individual request
47
+ */
42
48
  signal?: FetchOptions['signal'];
43
49
  }
44
50
  interface ArriRequestConfig<TType, TParams extends Record<any, any> | undefined = undefined> {
@@ -110,9 +116,16 @@ interface SseOptions<TData> {
110
116
  onResponseError?: (context: Omit<OnResponseErrorContext, 'error'> & {
111
117
  error: ArriErrorInstance;
112
118
  }) => any;
113
- onClose?: () => any;
114
119
  maxRetryCount?: number;
115
120
  maxRetryInterval?: number;
121
+ /**
122
+ * Defines a multiplier for the Arri server's "Heartbeat-Interval" header.
123
+ * The client will wait for (Heartbeat-Interval * heartbeatTimeoutMultiplier)
124
+ * milliseconds without receiving a heartbeat before considering the connection disconnected.
125
+ *
126
+ * Default is 2.0 (e.g. if the server sends 10s interval, client waits 20s)
127
+ */
128
+ heartbeatTimeoutMultiplier?: number;
116
129
  }
117
130
  declare function arriSseRequest<TType, TParams extends Record<any, any> | undefined = undefined>(opts: ArriRequestConfig<TType, TParams>, options: SseOptions<TType>): EventSourceController;
118
131
 
package/dist/index.d.mts CHANGED
@@ -38,7 +38,13 @@ interface ArriRequestOptions {
38
38
  onRequestError?: FetchOptions['onRequestError'];
39
39
  onResponse?: FetchOptions['onResponse'];
40
40
  onResponseError?: FetchOptions['onResponseError'];
41
+ /**
42
+ * Set a timeout in milliseconds to wait for a response before throwing an error
43
+ */
41
44
  timeout?: FetchOptions['timeout'];
45
+ /**
46
+ * An AbortController signal - used for cancelling an individual request
47
+ */
42
48
  signal?: FetchOptions['signal'];
43
49
  }
44
50
  interface ArriRequestConfig<TType, TParams extends Record<any, any> | undefined = undefined> {
@@ -110,9 +116,16 @@ interface SseOptions<TData> {
110
116
  onResponseError?: (context: Omit<OnResponseErrorContext, 'error'> & {
111
117
  error: ArriErrorInstance;
112
118
  }) => any;
113
- onClose?: () => any;
114
119
  maxRetryCount?: number;
115
120
  maxRetryInterval?: number;
121
+ /**
122
+ * Defines a multiplier for the Arri server's "Heartbeat-Interval" header.
123
+ * The client will wait for (Heartbeat-Interval * heartbeatTimeoutMultiplier)
124
+ * milliseconds without receiving a heartbeat before considering the connection disconnected.
125
+ *
126
+ * Default is 2.0 (e.g. if the server sends 10s interval, client waits 20s)
127
+ */
128
+ heartbeatTimeoutMultiplier?: number;
116
129
  }
117
130
  declare function arriSseRequest<TType, TParams extends Record<any, any> | undefined = undefined>(opts: ArriRequestConfig<TType, TParams>, options: SseOptions<TType>): EventSourceController;
118
131
 
package/dist/index.d.ts CHANGED
@@ -38,7 +38,13 @@ interface ArriRequestOptions {
38
38
  onRequestError?: FetchOptions['onRequestError'];
39
39
  onResponse?: FetchOptions['onResponse'];
40
40
  onResponseError?: FetchOptions['onResponseError'];
41
+ /**
42
+ * Set a timeout in milliseconds to wait for a response before throwing an error
43
+ */
41
44
  timeout?: FetchOptions['timeout'];
45
+ /**
46
+ * An AbortController signal - used for cancelling an individual request
47
+ */
42
48
  signal?: FetchOptions['signal'];
43
49
  }
44
50
  interface ArriRequestConfig<TType, TParams extends Record<any, any> | undefined = undefined> {
@@ -110,9 +116,16 @@ interface SseOptions<TData> {
110
116
  onResponseError?: (context: Omit<OnResponseErrorContext, 'error'> & {
111
117
  error: ArriErrorInstance;
112
118
  }) => any;
113
- onClose?: () => any;
114
119
  maxRetryCount?: number;
115
120
  maxRetryInterval?: number;
121
+ /**
122
+ * Defines a multiplier for the Arri server's "Heartbeat-Interval" header.
123
+ * The client will wait for (Heartbeat-Interval * heartbeatTimeoutMultiplier)
124
+ * milliseconds without receiving a heartbeat before considering the connection disconnected.
125
+ *
126
+ * Default is 2.0 (e.g. if the server sends 10s interval, client waits 20s)
127
+ */
128
+ heartbeatTimeoutMultiplier?: number;
116
129
  }
117
130
  declare function arriSseRequest<TType, TParams extends Record<any, any> | undefined = undefined>(opts: ArriRequestConfig<TType, TParams>, options: SseOptions<TType>): EventSourceController;
118
131
 
package/dist/index.mjs CHANGED
@@ -216,23 +216,36 @@ function arriSseRequest(opts, options) {
216
216
  maxRetryCount: options.maxRetryCount,
217
217
  maxRetryInterval: options.maxRetryInterval
218
218
  });
219
+ let interval;
220
+ let intervalDuration;
221
+ const timeoutMultiplier = options.heartbeatTimeoutMultiplier ?? 2;
222
+ if (timeoutMultiplier < 1) {
223
+ throw new Error(`heartbeatTimeoutMultiplier cannot be less than 1`);
224
+ }
225
+ function resetInterval() {
226
+ if (interval) clearInterval(interval);
227
+ if (!intervalDuration) return;
228
+ interval = setInterval(() => {
229
+ if (controller.signal.aborted) return;
230
+ controller.reconnect();
231
+ }, intervalDuration);
232
+ }
219
233
  const controller = eventSource.listen({
220
234
  onMessage(message) {
235
+ resetInterval();
221
236
  if (message.event === "message" || message.event === void 0 || message.event === "") {
222
237
  options.onMessage?.(opts.responseFromString(message.data));
223
238
  return;
224
239
  }
225
240
  if (message.event === "done") {
241
+ if (interval) clearInterval(interval);
226
242
  controller.abort();
227
243
  }
228
244
  },
229
- onRequest(context) {
230
- options.onRequest?.(context);
231
- },
245
+ onRequest: options.onRequest,
232
246
  onRequestError(context) {
233
- if (opts.onError) {
234
- opts.onError(context.error);
235
- }
247
+ clearInterval(interval);
248
+ if (opts.onError) opts.onError(context.error);
236
249
  options.onRequestError?.({
237
250
  ...context,
238
251
  error: new ArriErrorInstance({
@@ -243,15 +256,19 @@ function arriSseRequest(opts, options) {
243
256
  });
244
257
  },
245
258
  onResponse(context) {
259
+ const heartbeatIntervalHeader = Number(
260
+ context.response?.headers.get("heartbeat-interval") ?? 0
261
+ );
262
+ if (context.response.ok && !Number.isNaN(heartbeatIntervalHeader) && heartbeatIntervalHeader > 0) {
263
+ intervalDuration = heartbeatIntervalHeader * timeoutMultiplier;
264
+ resetInterval();
265
+ }
246
266
  options.onResponse?.(context);
247
267
  },
248
268
  async onResponseError(context) {
249
- if (opts.onError) {
250
- opts.onError(context.error);
251
- }
252
- if (!options.onResponseError) {
253
- return;
254
- }
269
+ clearInterval(interval);
270
+ if (opts.onError) opts.onError(context.error);
271
+ if (!options.onResponseError) return;
255
272
  try {
256
273
  const arriError = ArriErrorInstance.fromJson(
257
274
  await context.response.json()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrirpc/client",
3
- "version": "0.79.1",
3
+ "version": "0.80.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -22,10 +22,10 @@
22
22
  "directory": "languages/ts/ts-client"
23
23
  },
24
24
  "dependencies": {
25
- "event-source-plus": "^0.1.8",
25
+ "event-source-plus": "^0.1.11",
26
26
  "ofetch": "^1.4.1",
27
27
  "ws": "^8.18.2",
28
- "@arrirpc/schema": "0.79.1"
28
+ "@arrirpc/schema": "0.80.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.18.1"