export-runtime 0.0.22 → 0.0.24

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 (2) hide show
  1. package/handler.js +46 -55
  2. package/package.json +1 -1
package/handler.js CHANGED
@@ -323,7 +323,6 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
323
323
  // Track session state and connection state for this WebSocket connection
324
324
  const wsSession = { token: null };
325
325
  let isClosed = false;
326
- const pendingOps = new Set();
327
326
 
328
327
  // Safe send that ignores errors when connection is closed
329
328
  const safeSend = (data) => {
@@ -335,63 +334,66 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
335
334
  }
336
335
  };
337
336
 
338
- // Wrap async operations to track them
339
- const trackOp = async (op) => {
340
- const promise = op();
341
- pendingOps.add(promise);
342
- try {
343
- return await promise;
344
- } finally {
345
- pendingOps.delete(promise);
337
+ const handleClose = () => {
338
+ if (isClosed) return;
339
+ isClosed = true;
340
+ if (onClose) {
341
+ try { onClose(); } catch {}
346
342
  }
347
343
  };
348
344
 
349
345
  server.addEventListener("message", (event) => {
350
- if (isClosed) return;
346
+ // Wrap entire handler in try-catch to prevent any unhandled errors
347
+ try {
348
+ if (isClosed) return;
351
349
 
352
- // Handle message asynchronously but don't await it
353
- trackOp(async () => {
350
+ // Parse message synchronously to get the id
351
+ let msg;
354
352
  let id;
355
353
  try {
356
- if (isClosed) return;
357
- const msg = parse(event.data);
354
+ msg = parse(event.data);
358
355
  id = msg.id;
356
+ } catch {
357
+ return;
358
+ }
359
359
 
360
- // Handle auth token updates (on reconnect or explicit setToken)
361
- if (msg.type === "auth" && msg.token && !msg.method) {
362
- wsSession.token = msg.token;
363
- safeSend(stringify({ type: "auth-result", id, success: true }));
364
- return;
365
- }
366
-
367
- if (isClosed) return;
368
- const result = await dispatchMessage(dispatcher, msg, env, wsSession);
369
-
370
- if (isClosed) return;
371
-
372
- // Extract token from auth responses
373
- if (result?.value?.token && msg.type === "auth") {
374
- wsSession.token = result.value.token;
375
- }
360
+ // Handle ping synchronously - no async needed
361
+ if (msg.type === "ping") {
362
+ safeSend(stringify({ type: "pong", id }));
363
+ return;
364
+ }
376
365
 
377
- if (result) safeSend(stringify({ ...result, id }));
378
- } catch (err) {
379
- if (isClosed || String(err).includes("Connection closed")) return;
380
- if (id !== undefined) safeSend(stringify({ type: "error", id, error: String(err) }));
366
+ // Handle auth token updates synchronously
367
+ if (msg.type === "auth" && msg.token && !msg.method) {
368
+ wsSession.token = msg.token;
369
+ safeSend(stringify({ type: "auth-result", id, success: true }));
370
+ return;
381
371
  }
382
- });
383
- });
384
372
 
385
- server.addEventListener("close", () => {
386
- isClosed = true;
387
- if (onClose) onClose();
373
+ // Handle other messages asynchronously with full error protection
374
+ (async () => {
375
+ try {
376
+ if (isClosed) return;
377
+ const result = await dispatchMessage(dispatcher, msg, env, wsSession);
378
+ if (isClosed) return;
379
+
380
+ // Extract token from auth responses
381
+ if (result?.value?.token && msg.type === "auth") {
382
+ wsSession.token = result.value.token;
383
+ }
384
+
385
+ if (result) safeSend(stringify({ ...result, id }));
386
+ } catch {
387
+ // Silently ignore all errors - connection may be closing
388
+ }
389
+ })().catch(() => {}); // Double-catch to ensure no unhandled promise rejection
390
+ } catch {
391
+ // Silently ignore outer errors too
392
+ }
388
393
  });
389
394
 
390
- server.addEventListener("error", (err) => {
391
- isClosed = true;
392
- if (onClose) onClose();
393
- try { server.close(); } catch {}
394
- });
395
+ server.addEventListener("close", handleClose);
396
+ server.addEventListener("error", handleClose);
395
397
  };
396
398
 
397
399
  return {
@@ -419,12 +421,6 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
419
421
  const pair = new WebSocketPair();
420
422
  const [client, server] = Object.values(pair);
421
423
 
422
- // Create a promise that resolves when the WebSocket closes
423
- const wsLifetime = new Promise((resolve) => {
424
- server.addEventListener("close", resolve, { once: true });
425
- server.addEventListener("error", resolve, { once: true });
426
- });
427
-
428
424
  server.accept();
429
425
 
430
426
  if (isShared && env?.SHARED_EXPORT) {
@@ -437,11 +433,6 @@ export const createHandler = (moduleMap, generatedTypes, minifiedCore, coreId, m
437
433
  wireWebSocket(server, dispatcher, env, () => dispatcher.clearAll());
438
434
  }
439
435
 
440
- // Keep worker alive for the WebSocket lifetime
441
- if (ctx?.waitUntil) {
442
- ctx.waitUntil(wsLifetime);
443
- }
444
-
445
436
  return new Response(null, { status: 101, webSocket: client });
446
437
  }
447
438
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "export-runtime",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "Cloudflare Workers ESM Export Framework Runtime",
5
5
  "keywords": [
6
6
  "cloudflare",