experimental-a2 0.10.0 → 0.12.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/actor-DJi3RsNu.d.ts +310 -0
  3. package/dist/actor-DJi3RsNu.d.ts.map +1 -0
  4. package/dist/actor-client.d.ts +13 -4
  5. package/dist/actor-client.d.ts.map +1 -1
  6. package/dist/actor-client.js +63 -7
  7. package/dist/actor-client.js.map +1 -1
  8. package/dist/actor-react.d.ts +5 -4
  9. package/dist/actor-react.d.ts.map +1 -1
  10. package/dist/actor-react.js +16 -2
  11. package/dist/actor-react.js.map +1 -1
  12. package/dist/actor.d.ts +2 -176
  13. package/dist/actor.js +13 -2
  14. package/dist/actor.js.map +1 -1
  15. package/dist/ai-server.d.ts +1 -1
  16. package/dist/ai-server.js +2 -2
  17. package/dist/ai.d.ts +1 -1
  18. package/dist/ai.js +1 -1
  19. package/dist/client-Bf6uSEAk.js +1342 -0
  20. package/dist/client-Bf6uSEAk.js.map +1 -0
  21. package/dist/client-P_NNNRM-.d.ts +243 -0
  22. package/dist/client-P_NNNRM-.d.ts.map +1 -0
  23. package/dist/client.d.ts +2 -208
  24. package/dist/client.js +2 -1206
  25. package/dist/errors-DCk6ch5n.js.map +1 -1
  26. package/dist/errors-DvhSXnxk.d.ts +28 -0
  27. package/dist/errors-DvhSXnxk.d.ts.map +1 -0
  28. package/dist/index.d.ts +3 -35
  29. package/dist/{internal-DRXJ56EI.js → internal-Dq2qYxou.js} +2 -2
  30. package/dist/{internal-DRXJ56EI.js.map → internal-Dq2qYxou.js.map} +1 -1
  31. package/dist/platform-B4TnJtWu.js +34 -0
  32. package/dist/platform-B4TnJtWu.js.map +1 -0
  33. package/dist/react.d.ts +3 -1
  34. package/dist/react.d.ts.map +1 -1
  35. package/dist/react.js +2 -1
  36. package/dist/react.js.map +1 -1
  37. package/dist/scheduler-qstash.d.ts +2 -2
  38. package/dist/scheduler-qstash.js +3 -2
  39. package/dist/scheduler-qstash.js.map +1 -1
  40. package/dist/scheduler-vercel.d.ts +2 -2
  41. package/dist/scheduler-vercel.js +2 -2
  42. package/dist/{server-DlLyvaSH.js → server-BeNADlCI.js} +171 -65
  43. package/dist/server-BeNADlCI.js.map +1 -0
  44. package/dist/{server-DgCrSuhB.d.ts → server-DjZZa1wr.d.ts} +9 -3
  45. package/dist/{server-DgCrSuhB.d.ts.map → server-DjZZa1wr.d.ts.map} +1 -1
  46. package/dist/server.d.ts +3 -3
  47. package/dist/server.js +2 -2
  48. package/dist/{store-DGHeBtIQ.d.ts → store-DtDOWLSn.d.ts} +4 -5
  49. package/dist/{store-DGHeBtIQ.d.ts.map → store-DtDOWLSn.d.ts.map} +1 -1
  50. package/dist/store-N8PXxDAS.js.map +1 -1
  51. package/dist/store-memory.d.ts +1 -1
  52. package/dist/store-memory.js +1 -1
  53. package/dist/{store-polling-6DW7F1DT.js → store-polling-CmxUbV93.js} +56 -7
  54. package/dist/store-polling-CmxUbV93.js.map +1 -0
  55. package/dist/store-postgres.d.ts +6 -4
  56. package/dist/store-postgres.d.ts.map +1 -1
  57. package/dist/store-postgres.js +701 -53
  58. package/dist/store-postgres.js.map +1 -1
  59. package/dist/store-presence-polling-C7-XZyW9.js +94 -0
  60. package/dist/store-presence-polling-C7-XZyW9.js.map +1 -0
  61. package/dist/store-redis-http.d.ts +2 -2
  62. package/dist/store-redis-http.d.ts.map +1 -1
  63. package/dist/store-redis-http.js +184 -38
  64. package/dist/store-redis-http.js.map +1 -1
  65. package/dist/{store-redis-core-z-ykbyMg.js → store-redis-notify-BUCyXOn0.js} +491 -27
  66. package/dist/store-redis-notify-BUCyXOn0.js.map +1 -0
  67. package/dist/store-redis.d.ts +5 -13
  68. package/dist/store-redis.d.ts.map +1 -1
  69. package/dist/store-redis.js +27 -271
  70. package/dist/store-redis.js.map +1 -1
  71. package/dist/store-sqlite.d.ts +1 -1
  72. package/dist/store-sqlite.js +1 -1
  73. package/dist/{wire--yji6mO3.js → wire-BO5wWCb1.js} +18 -16
  74. package/dist/wire-BO5wWCb1.js.map +1 -0
  75. package/docs/actors/04-routes.mdx +40 -8
  76. package/docs/guides/03-react.mdx +52 -10
  77. package/docs/guides/05-production.mdx +54 -5
  78. package/docs/guides/09-presence.mdx +12 -7
  79. package/docs/guides/10-transports.mdx +15 -2
  80. package/docs/reference/01-api.mdx +124 -17
  81. package/docs/reference/02-errors.mdx +45 -11
  82. package/examples/playground/package.json +1 -1
  83. package/package.json +1 -1
  84. package/src/actor-client.ts +114 -14
  85. package/src/actor-react.ts +18 -3
  86. package/src/actor.ts +16 -1
  87. package/src/client-errors.ts +51 -0
  88. package/src/client.ts +253 -96
  89. package/src/errors.ts +4 -9
  90. package/src/index.ts +1 -1
  91. package/src/internal.ts +1 -1
  92. package/src/postgres-notification-scope.ts +134 -0
  93. package/src/postgres-notifications.ts +244 -0
  94. package/src/postgres-pool.ts +65 -0
  95. package/src/postgres-resources.ts +185 -0
  96. package/src/presence-recovery.ts +120 -0
  97. package/src/react.ts +3 -0
  98. package/src/redis-http-subscriptions.ts +199 -0
  99. package/src/server.ts +160 -22
  100. package/src/session-socket.ts +25 -7
  101. package/src/sse.ts +17 -0
  102. package/src/store-polling.ts +32 -12
  103. package/src/store-postgres.ts +292 -75
  104. package/src/store-presence-polling.ts +125 -0
  105. package/src/store-redis-core.ts +37 -30
  106. package/src/store-redis-http.ts +51 -45
  107. package/src/store-redis-notify.ts +464 -0
  108. package/src/store-redis.ts +9 -364
  109. package/src/store.ts +3 -4
  110. package/src/stream-activity.ts +32 -0
  111. package/src/wire.ts +39 -15
  112. package/dist/actor-shared-USo5MyuF.d.ts +0 -136
  113. package/dist/actor-shared-USo5MyuF.d.ts.map +0 -1
  114. package/dist/actor.d.ts.map +0 -1
  115. package/dist/client.d.ts.map +0 -1
  116. package/dist/client.js.map +0 -1
  117. package/dist/index.d.ts.map +0 -1
  118. package/dist/server-DlLyvaSH.js.map +0 -1
  119. package/dist/store-polling-6DW7F1DT.js.map +0 -1
  120. package/dist/store-redis-core-z-ykbyMg.js.map +0 -1
  121. package/dist/wire--yji6mO3.js.map +0 -1
@@ -1,40 +1,10 @@
1
1
  import { n as validateSync } from "./validate-XKT4FSNn.js";
2
2
  import { n as NonRetriableError, r as asStoreUnavailable, t as A2Error } from "./errors-DCk6ch5n.js";
3
- import { c as STREAM_TIMINGS, g as serverInternals, h as nullProtoRecord, i as POLL_TIMINGS, m as markSchedulerSendFailure, o as RESERVED_PARTICIPANT_IDS, s as SOCKET_TIMINGS, t as DRAIN_TIMINGS } from "./internal-DRXJ56EI.js";
4
- import { t as defaultSleep } from "./store-polling-6DW7F1DT.js";
5
- import { _ as socketErrorAckFor, a as errorToWire, b as socketUnsubscribedFor, g as socketAckFor, h as presenceSnapshotToWire, i as errorStatus, n as asA2Error, p as presencePatchToWire, s as eventToWire, t as SOCKET_PING_FRAME, v as socketFrameFor, y as socketSubscribedFor } from "./wire--yji6mO3.js";
3
+ import { c as STREAM_TIMINGS, g as serverInternals, h as nullProtoRecord, i as POLL_TIMINGS, m as markSchedulerSendFailure, o as RESERVED_PARTICIPANT_IDS, s as SOCKET_TIMINGS, t as DRAIN_TIMINGS } from "./internal-Dq2qYxou.js";
4
+ import { i as streamActivities, r as createStreamActivity, t as defaultSleep } from "./store-polling-CmxUbV93.js";
5
+ import { r as platformWaitUntil, t as invocationDeadlineMs } from "./platform-B4TnJtWu.js";
6
+ import { _ as socketErrorAckFor, a as errorToWire, b as socketUnsubscribedFor, g as socketAckFor, h as presenceSnapshotToWire, i as errorStatus, n as asA2Error, p as presencePatchToWire, s as eventToWire, t as SOCKET_PING_FRAME, v as socketFrameFor, y as socketSubscribedFor } from "./wire-BO5wWCb1.js";
6
7
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
7
- //#region src/platform.ts
8
- const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
9
- function requestContext() {
10
- try {
11
- return Reflect.get(globalThis, SYMBOL_FOR_REQ_CONTEXT)?.get?.() ?? {};
12
- } catch {
13
- return {};
14
- }
15
- }
16
- /** The current invocation's Vercel deployment identity, when available. */
17
- function platformVercelOidcToken() {
18
- try {
19
- const token = requestContext().headers?.["x-vercel-oidc-token"];
20
- return typeof token === "string" && token.length > 0 ? token : null;
21
- } catch {
22
- return null;
23
- }
24
- }
25
- function platformWaitUntil(promise) {
26
- try {
27
- requestContext().waitUntil?.(promise);
28
- } catch {}
29
- }
30
- /** The invocation's termination time in epoch ms, or null if unknowable. */
31
- function invocationDeadlineMs() {
32
- const deadline = requestContext().deadline;
33
- if (deadline === void 0 || deadline === null) return null;
34
- const ms = new Date(deadline).getTime();
35
- return Number.isNaN(ms) ? null : ms;
36
- }
37
- //#endregion
38
8
  //#region src/push-envelope.ts
39
9
  /**
40
10
  * The push envelope's trust boundary for `{ sessionId, events, presence? }`,
@@ -242,7 +212,7 @@ const pumpSubscription = async (shell, subscription, frameFor) => {
242
212
  shell.send(frameFor(value));
243
213
  }
244
214
  };
245
- const handleSocketPush = async (shell, target, sessionId, frame, req, ackSessionId, gatePush) => {
215
+ const handleSocketPush = async (shell, target, sessionId, frame, req, ackSessionId, gatePush, activate) => {
246
216
  try {
247
217
  const events = parsePushEvents(frame["events"]);
248
218
  const denial = await gatePush?.(sessionId, events, void 0) ?? null;
@@ -250,7 +220,9 @@ const handleSocketPush = async (shell, target, sessionId, frame, req, ackSession
250
220
  shell.send(socketErrorAckFor(req, denial, ackSessionId));
251
221
  return;
252
222
  }
223
+ activate();
253
224
  const appended = await target.append(...events);
225
+ activate();
254
226
  shell.send(socketAckFor(req, appended, ackSessionId));
255
227
  } catch (error) {
256
228
  shell.send(socketErrorAckFor(req, asA2Error(error), ackSessionId));
@@ -300,7 +272,7 @@ function sessionSocket(sessionId, session, socket, options) {
300
272
  shell.send(socketErrorAckFor(req, new A2Error("STORE_UNAVAILABLE", "push shed: too many in flight on this socket")));
301
273
  return;
302
274
  }
303
- shell.track(handleSocketPush(shell, target, sessionId, frame, req, void 0, options?.gatePush));
275
+ shell.track(handleSocketPush(shell, target, sessionId, frame, req, void 0, options?.gatePush, () => streamActivities.get(subscription.iterator)?.activate()));
304
276
  return;
305
277
  }
306
278
  case "presence":
@@ -355,29 +327,31 @@ function sessionsSocket(resolve, socket, options) {
355
327
  if (shell.torn() || subscription.stopped) return;
356
328
  retire(id, subscription);
357
329
  shell.send(socketUnsubscribedFor(id));
358
- } catch {
330
+ } catch (error) {
359
331
  if (shell.torn() || subscription.stopped) return;
360
332
  retire(id, subscription);
361
- shell.send(socketUnsubscribedFor(id, "stream failed"));
333
+ shell.send(socketUnsubscribedFor(id, "stream failed", asA2Error(error)));
362
334
  }
363
335
  };
364
336
  const startSubscription = async (id, index) => {
337
+ const existing = subscriptions.get(id);
338
+ if (existing) {
339
+ stopSubscription(existing);
340
+ subscriptions.delete(id);
341
+ }
365
342
  let session;
366
343
  try {
367
344
  session = await resolve(id, index);
368
- } catch {
369
- if (!shell.torn()) shell.send(socketUnsubscribedFor(id, "stream failed"));
345
+ } catch (error) {
346
+ if (!shell.torn()) shell.send(socketUnsubscribedFor(id, "stream failed", asA2Error(error)));
370
347
  return;
371
348
  }
372
349
  if (shell.torn()) return;
373
350
  if (session === null) {
374
- shell.send(socketUnsubscribedFor(id, "subscribe rejected"));
351
+ shell.send(socketUnsubscribedFor(id, "subscribe rejected", new A2Error("FORBIDDEN", "the subscription is not authorized")));
375
352
  return;
376
353
  }
377
- const target = session;
378
- const existing = subscriptions.get(id);
379
- if (existing) stopSubscription(existing);
380
- const subscription = socketSubscription(target, index, presence);
354
+ const subscription = socketSubscription(session, index, presence);
381
355
  subscriptions.set(id, subscription);
382
356
  shell.send(socketSubscribedFor(id));
383
357
  runSubscription(id, subscription);
@@ -392,7 +366,10 @@ function sessionsSocket(resolve, socket, options) {
392
366
  shell.send(socketErrorAckFor(req, new A2Error("STORE_UNAVAILABLE", "session is not subscribed on this socket"), sessionId));
393
367
  return;
394
368
  }
395
- await handleSocketPush(shell, subscription.target, sessionId, frame, req, sessionId, options?.gatePush);
369
+ await handleSocketPush(shell, subscription.target, sessionId, frame, req, sessionId, options?.gatePush, () => {
370
+ const current = subscriptions.get(sessionId);
371
+ if (current) streamActivities.get(current.iterator)?.activate();
372
+ });
396
373
  };
397
374
  const handlePresence = async (frame, sessionId) => {
398
375
  const subscription = subscriptions.get(sessionId);
@@ -493,7 +470,10 @@ function sseResponse(iterable) {
493
470
  let deadlineTimer;
494
471
  let closed = false;
495
472
  let iteratorReturned = false;
473
+ let stopActivity;
496
474
  const stopTimers = () => {
475
+ stopActivity?.();
476
+ stopActivity = void 0;
497
477
  if (heartbeat !== void 0) clearInterval(heartbeat);
498
478
  if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
499
479
  heartbeat = void 0;
@@ -507,6 +487,15 @@ function sseResponse(iterable) {
507
487
  const stream = new ReadableStream({
508
488
  start(controller) {
509
489
  controller.enqueue(encoder.encode(": connected\n\n"));
490
+ const activity = streamActivities.get(iterator);
491
+ if (activity) {
492
+ const announce = (idle) => {
493
+ if (closed) return;
494
+ controller.enqueue(encoder.encode(`event: a2-activity\ndata: ${idle ? "idle" : "active"}\n\n`));
495
+ };
496
+ stopActivity = activity.subscribe(announce);
497
+ if (activity.idle) announce(true);
498
+ }
510
499
  heartbeat = setInterval(() => {
511
500
  try {
512
501
  controller.enqueue(encoder.encode(": ping\n\n"));
@@ -827,6 +816,9 @@ async function deliverSchedulerAppend(server, task) {
827
816
  if (!internals) throw new TypeError(`a2 scheduler: server for contract '${task.contract}' cannot receive scheduled appends`);
828
817
  await internals.schedulerAppend(task.sessionId, task.events);
829
818
  }
819
+ function handler(entry) {
820
+ return typeof entry === "function" ? { handler: entry } : entry;
821
+ }
830
822
  const MAX_FAILURES = 10;
831
823
  /** Default: how long a presence value survives without a refreshing set. */
832
824
  const PRESENCE_TTL_MS = 6e4;
@@ -978,8 +970,8 @@ function createServer(options) {
978
970
  for (const [type, entry] of Object.entries(options.handlers ?? {})) {
979
971
  if (entry === void 0) continue;
980
972
  if (!Object.hasOwn(defs, type)) throw new TypeError(`contract '${name}' has no event type '${type}' — cannot register a handler for it`);
981
- const handler = typeof entry === "function" ? entry : entry?.handler;
982
- if (typeof handler !== "function") throw new TypeError(`handler for '${type}' must be a function`);
973
+ const callback = typeof entry === "function" ? entry : entry?.handler;
974
+ if (typeof callback !== "function") throw new TypeError(`handler for '${type}' must be a function`);
983
975
  let abortOn = null;
984
976
  const spec = typeof entry === "function" ? void 0 : entry.abortOn;
985
977
  if (spec !== void 0) {
@@ -995,7 +987,7 @@ function createServer(options) {
995
987
  const lane = typeof entry === "function" ? null : entry.lane ?? null;
996
988
  if (lane !== null && typeof lane !== "function" && (typeof lane !== "string" || lane.length === 0)) throw new TypeError(`lane for '${type}' must be a non-empty string or a function`);
997
989
  handlers.set(type, {
998
- handler,
990
+ handler: callback,
999
991
  abortOn,
1000
992
  lane
1001
993
  });
@@ -1405,12 +1397,57 @@ function createServer(options) {
1405
1397
  createdAt: event.createdAt
1406
1398
  });
1407
1399
  const streamEvents = (id, startAfter) => {
1408
- const store = resolveStore();
1400
+ let closed = false;
1401
+ let inner;
1402
+ let stopActivity;
1403
+ const activity = createStreamActivity(() => {
1404
+ if (inner && !closed) streamActivities.get(inner)?.activate();
1405
+ });
1406
+ let closing;
1407
+ const close = () => {
1408
+ closed = true;
1409
+ closing ??= (async () => {
1410
+ stopActivity?.();
1411
+ await inner?.return?.();
1412
+ })();
1413
+ return closing;
1414
+ };
1409
1415
  const outer = async function* () {
1410
- const inner = (await store).stream(nsId(id), { startAfter });
1411
- for await (const event of inner) yield toSessionEvent(id, event);
1416
+ const store = await resolveStore();
1417
+ if (closed) return;
1418
+ inner = store.stream(nsId(id), { startAfter })[Symbol.asyncIterator]();
1419
+ const sourceActivity = streamActivities.get(inner);
1420
+ if (sourceActivity) {
1421
+ stopActivity = sourceActivity.subscribe(activity.setIdle);
1422
+ activity.setIdle(sourceActivity.idle);
1423
+ }
1424
+ try {
1425
+ for (;;) {
1426
+ if (closed) return;
1427
+ const result = await inner.next();
1428
+ if (closed || result.done) return;
1429
+ yield toSessionEvent(id, result.value);
1430
+ }
1431
+ } finally {
1432
+ await close();
1433
+ }
1412
1434
  };
1413
- return outer();
1435
+ const iterator = outer();
1436
+ return { [Symbol.asyncIterator]() {
1437
+ const stream = {
1438
+ next: () => iterator.next(),
1439
+ async return() {
1440
+ await close();
1441
+ return iterator.return(void 0);
1442
+ },
1443
+ async throw(error) {
1444
+ await close();
1445
+ return iterator.throw(error);
1446
+ }
1447
+ };
1448
+ streamActivities.set(stream, activity);
1449
+ return stream;
1450
+ } };
1414
1451
  };
1415
1452
  const requirePresence = (store) => {
1416
1453
  if (!store.presence) throw presenceNotSupported();
@@ -1462,8 +1499,39 @@ function createServer(options) {
1462
1499
  */
1463
1500
  const streamWithPresence = (id, startAfter) => {
1464
1501
  const ns = nsId(id);
1502
+ let closed = false;
1503
+ let events;
1504
+ let unsubscribe;
1505
+ let sleeper = null;
1506
+ let wakeQueue = null;
1507
+ let presenceInterval = Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs);
1508
+ let presenceActivityVersion = 0;
1509
+ let stopActivity;
1510
+ const activity = createStreamActivity(() => {
1511
+ if (closed) return;
1512
+ presenceActivityVersion += 1;
1513
+ presenceInterval = Math.min(POLL_TIMINGS.activeFloorMs, POLL_TIMINGS.idleCeilingMs);
1514
+ sleeper?.cancel();
1515
+ if (events) streamActivities.get(events)?.activate();
1516
+ });
1517
+ let closing;
1518
+ const close = () => {
1519
+ closed = true;
1520
+ sleeper?.cancel();
1521
+ wakeQueue?.();
1522
+ closing ??= (async () => {
1523
+ try {
1524
+ stopActivity?.();
1525
+ unsubscribe?.();
1526
+ } finally {
1527
+ await events?.return?.();
1528
+ }
1529
+ })();
1530
+ return closing;
1531
+ };
1465
1532
  const outer = async function* () {
1466
1533
  const store = await resolveStore();
1534
+ if (closed) return;
1467
1535
  const api = requirePresence(store);
1468
1536
  const readRows = async () => {
1469
1537
  try {
@@ -1538,8 +1606,9 @@ function createServer(options) {
1538
1606
  if (fields?.size === 0) painted.delete(patch.participant);
1539
1607
  };
1540
1608
  const queue = /* @__PURE__ */ new Map();
1541
- let wakeQueue = null;
1542
1609
  const enqueue = (patch) => {
1610
+ if (closed) return;
1611
+ activity.activate();
1543
1612
  for (const [field, value] of Object.entries(patch.values)) {
1544
1613
  if (value === void 0) continue;
1545
1614
  let fields = queue.get(patch.participant);
@@ -1573,11 +1642,17 @@ function createServer(options) {
1573
1642
  }
1574
1643
  return null;
1575
1644
  };
1576
- const unsubscribe = api.subscribe?.(ns, enqueue);
1577
- const events = store.stream(ns, { startAfter })[Symbol.asyncIterator]();
1578
1645
  let pendingEvent = null;
1579
1646
  try {
1647
+ unsubscribe = api.subscribe?.(ns, enqueue);
1648
+ events = store.stream(ns, { startAfter })[Symbol.asyncIterator]();
1649
+ const sourceActivity = streamActivities.get(events);
1650
+ if (sourceActivity) {
1651
+ stopActivity = sourceActivity.subscribe(activity.setIdle);
1652
+ activity.setIdle(sourceActivity.idle);
1653
+ }
1580
1654
  const snapshotRows = await readRows();
1655
+ if (closed) return;
1581
1656
  for (const row of snapshotRows) {
1582
1657
  let fields = painted.get(row.participant);
1583
1658
  if (!fields) {
@@ -1592,9 +1667,11 @@ function createServer(options) {
1592
1667
  }
1593
1668
  yield { snapshot: foldPresenceRows(snapshotRows) };
1594
1669
  for (;;) {
1670
+ if (closed) return;
1595
1671
  for (;;) {
1596
1672
  const patch = dequeue();
1597
1673
  if (!patch) break;
1674
+ if (closed) return;
1598
1675
  paint(patch);
1599
1676
  yield patch;
1600
1677
  }
@@ -1606,28 +1683,57 @@ function createServer(options) {
1606
1683
  });
1607
1684
  woke = await Promise.race([pendingEvent.then(() => "event"), arrival.then(() => "patch")]);
1608
1685
  wakeQueue = null;
1686
+ if (closed) return;
1609
1687
  if (woke === "patch") continue;
1610
1688
  } else {
1611
- const sleeper = defaultSleep(POLL_TIMINGS.idleCeilingMs);
1689
+ sleeper = defaultSleep(presenceInterval);
1612
1690
  woke = await Promise.race([pendingEvent.then(() => "event"), sleeper.promise.then(() => "tick")]);
1613
1691
  sleeper.cancel();
1692
+ sleeper = null;
1614
1693
  }
1694
+ if (closed) return;
1615
1695
  if (woke === "event") {
1616
1696
  const result = await pendingEvent;
1617
1697
  pendingEvent = null;
1618
- if (result.done) return;
1698
+ if (closed || result.done) return;
1699
+ activity.activate();
1619
1700
  yield toSessionEvent(id, result.value);
1620
1701
  }
1621
- if (!unsubscribe) for (const patch of diffRows(await readRows())) yield patch;
1702
+ if (!closed && !unsubscribe) {
1703
+ const version = presenceActivityVersion;
1704
+ const rows = await readRows();
1705
+ if (closed) return;
1706
+ const patches = diffRows(rows);
1707
+ if (patches.length > 0) activity.activate();
1708
+ else if (version === presenceActivityVersion) presenceInterval = Math.min(presenceInterval * 2, POLL_TIMINGS.idleCeilingMs);
1709
+ for (const patch of patches) {
1710
+ if (closed) return;
1711
+ yield patch;
1712
+ }
1713
+ }
1622
1714
  }
1623
1715
  } finally {
1624
- wakeQueue = null;
1625
- unsubscribe?.();
1626
1716
  pendingEvent?.catch(() => {});
1627
- await events.return?.();
1717
+ await close();
1718
+ wakeQueue = null;
1628
1719
  }
1629
1720
  };
1630
- return outer();
1721
+ const iterator = outer();
1722
+ return { [Symbol.asyncIterator]() {
1723
+ const stream = {
1724
+ next: () => iterator.next(),
1725
+ async return() {
1726
+ await close();
1727
+ return iterator.return(void 0);
1728
+ },
1729
+ async throw(error) {
1730
+ await close();
1731
+ return iterator.throw(error);
1732
+ }
1733
+ };
1734
+ streamActivities.set(stream, activity);
1735
+ return stream;
1736
+ } };
1631
1737
  };
1632
1738
  const makeSession = (id, append, trigger) => ({
1633
1739
  id,
@@ -2216,6 +2322,6 @@ function cloneInitial(initial) {
2216
2322
  }
2217
2323
  }
2218
2324
  //#endregion
2219
- export { parsePresenceSibling as a, sseResponse as i, deliverSchedulerAppend as n, platformVercelOidcToken as o, setServerFetchHooks as r, createServer as t };
2325
+ export { sseResponse as a, setServerFetchHooks as i, deliverSchedulerAppend as n, parsePresenceSibling as o, handler as r, createServer as t };
2220
2326
 
2221
- //# sourceMappingURL=server-DlLyvaSH.js.map
2327
+ //# sourceMappingURL=server-BeNADlCI.js.map