experimental-a2 0.16.1 → 0.18.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 (406) hide show
  1. package/CHANGELOG.md +373 -0
  2. package/README.md +1 -1
  3. package/dist/{actor-ohPC-81x.d.ts → actor-Bjce9_5U.d.ts} +21 -11
  4. package/dist/actor-Bjce9_5U.d.ts.map +1 -0
  5. package/dist/actor-client.d.ts +2 -5
  6. package/dist/actor-client.d.ts.map +1 -1
  7. package/dist/actor-client.js +13 -4
  8. package/dist/actor-client.js.map +1 -1
  9. package/dist/actor-react.d.ts +5 -3
  10. package/dist/actor-react.d.ts.map +1 -1
  11. package/dist/actor-react.js +20 -10
  12. package/dist/actor-react.js.map +1 -1
  13. package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
  14. package/dist/actor-shared-t7w8xIKP.js.map +1 -0
  15. package/dist/actor.d.ts +1 -1
  16. package/dist/actor.js +42 -23
  17. package/dist/actor.js.map +1 -1
  18. package/dist/{ai-DgOBltJ_.js → ai-BpOGm-zB.js} +621 -216
  19. package/dist/ai-BpOGm-zB.js.map +1 -0
  20. package/dist/{ai-CrEf6p_W.d.ts → ai-DrUaMyRt.d.ts} +114 -100
  21. package/dist/ai-DrUaMyRt.d.ts.map +1 -0
  22. package/dist/ai-client.d.ts +27 -0
  23. package/dist/ai-client.d.ts.map +1 -0
  24. package/dist/ai-client.js +29 -0
  25. package/dist/ai-client.js.map +1 -0
  26. package/dist/ai-fx.browser.d.ts +1 -0
  27. package/dist/ai-fx.browser.js +6 -0
  28. package/dist/ai-fx.browser.js.map +1 -0
  29. package/dist/ai-fx.d.ts +10 -0
  30. package/dist/ai-fx.d.ts.map +1 -0
  31. package/dist/ai-fx.js +634 -0
  32. package/dist/ai-fx.js.map +1 -0
  33. package/dist/ai-harness-BuLh_EYi.js +273 -0
  34. package/dist/ai-harness-BuLh_EYi.js.map +1 -0
  35. package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
  36. package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
  37. package/dist/ai-pi.browser.d.ts +1 -0
  38. package/dist/ai-pi.browser.js +6 -0
  39. package/dist/ai-pi.browser.js.map +1 -0
  40. package/dist/ai-pi.d.ts +17 -0
  41. package/dist/ai-pi.d.ts.map +1 -0
  42. package/dist/ai-pi.js +904 -0
  43. package/dist/ai-pi.js.map +1 -0
  44. package/dist/ai-react.d.ts +96 -0
  45. package/dist/ai-react.d.ts.map +1 -0
  46. package/dist/ai-react.js +142 -0
  47. package/dist/ai-react.js.map +1 -0
  48. package/dist/ai-server.browser.js +2 -2
  49. package/dist/ai-server.browser.js.map +1 -1
  50. package/dist/ai-server.d.ts +109 -43
  51. package/dist/ai-server.d.ts.map +1 -1
  52. package/dist/ai-server.js +526 -283
  53. package/dist/ai-server.js.map +1 -1
  54. package/dist/ai.d.ts +2 -2
  55. package/dist/ai.js +2 -2
  56. package/dist/bounded-cache-CPHWF-8h.js +41 -0
  57. package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
  58. package/dist/{client-BYzHjkwU.js → client-BReKqwZC.js} +447 -80
  59. package/dist/client-BReKqwZC.js.map +1 -0
  60. package/dist/{client-CzyacQpJ.d.ts → client-h_DKFAjj.d.ts} +14 -13
  61. package/dist/client-h_DKFAjj.d.ts.map +1 -0
  62. package/dist/client.d.ts +4 -2
  63. package/dist/client.js +1 -1
  64. package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
  65. package/dist/contract-BYKASlCV.js.map +1 -0
  66. package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
  67. package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
  68. package/dist/index.d.ts +6 -5
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4 -3
  71. package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +7 -3
  72. package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +1 -0
  73. package/dist/{internal-Dq2qYxou.js → internal-B2T3U4Rq.js} +29 -2
  74. package/dist/internal-B2T3U4Rq.js.map +1 -0
  75. package/dist/otel.d.ts +1 -1
  76. package/dist/react-BBXgHWFc.d.ts +86 -0
  77. package/dist/react-BBXgHWFc.d.ts.map +1 -0
  78. package/dist/react.d.ts +2 -122
  79. package/dist/react.js +107 -100
  80. package/dist/react.js.map +1 -1
  81. package/dist/reducer-BAXc8JyF.js +87 -0
  82. package/dist/reducer-BAXc8JyF.js.map +1 -0
  83. package/dist/{reducer-otzHjuJj.d.ts → reducer-CqA59Q6U.d.ts} +46 -30
  84. package/dist/reducer-CqA59Q6U.d.ts.map +1 -0
  85. package/dist/reducer-runtime-DQwz_J59.js +28 -0
  86. package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
  87. package/dist/scheduler-qstash.d.ts +3 -3
  88. package/dist/scheduler-qstash.d.ts.map +1 -1
  89. package/dist/scheduler-qstash.js +33 -10
  90. package/dist/scheduler-qstash.js.map +1 -1
  91. package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
  92. package/dist/scheduler-task-X1SToDjP.js.map +1 -0
  93. package/dist/scheduler-vercel.d.ts +4 -9
  94. package/dist/scheduler-vercel.d.ts.map +1 -1
  95. package/dist/scheduler-vercel.js +25 -44
  96. package/dist/scheduler-vercel.js.map +1 -1
  97. package/dist/{server-BD5ckxZb.js → server-Dw_k_7kO.js} +904 -219
  98. package/dist/server-Dw_k_7kO.js.map +1 -0
  99. package/dist/{server-DbD7IJVK.d.ts → server-En7cbi3v.d.ts} +116 -26
  100. package/dist/server-En7cbi3v.d.ts.map +1 -0
  101. package/dist/server.d.ts +4 -4
  102. package/dist/server.js +2 -2
  103. package/dist/session-clear-Bo-XTFEy.d.ts +12 -0
  104. package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
  105. package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
  106. package/dist/session-clear-DGI4jTQ5.js.map +1 -0
  107. package/dist/{store-DyZM6fS5.d.ts → store-Djdv0bnZ.d.ts} +73 -3
  108. package/dist/store-Djdv0bnZ.d.ts.map +1 -0
  109. package/dist/store-N8PXxDAS.js.map +1 -1
  110. package/dist/store-memory.d.ts +1 -1
  111. package/dist/store-memory.d.ts.map +1 -1
  112. package/dist/store-memory.js +205 -66
  113. package/dist/store-memory.js.map +1 -1
  114. package/dist/{store-polling-CmxUbV93.js → store-polling-B5FcwJfo.js} +21 -4
  115. package/dist/store-polling-B5FcwJfo.js.map +1 -0
  116. package/dist/store-postgres.d.ts +1 -1
  117. package/dist/store-postgres.d.ts.map +1 -1
  118. package/dist/store-postgres.js +450 -108
  119. package/dist/store-postgres.js.map +1 -1
  120. package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-BAwS8RWn.js} +3 -3
  121. package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
  122. package/dist/store-redis-http.d.ts +1 -1
  123. package/dist/store-redis-http.d.ts.map +1 -1
  124. package/dist/store-redis-http.js +293 -11
  125. package/dist/store-redis-http.js.map +1 -1
  126. package/dist/store-redis-notify-BRITti_o.js +2493 -0
  127. package/dist/store-redis-notify-BRITti_o.js.map +1 -0
  128. package/dist/store-redis.d.ts +1 -1
  129. package/dist/store-redis.d.ts.map +1 -1
  130. package/dist/store-redis.js +11 -11
  131. package/dist/store-redis.js.map +1 -1
  132. package/dist/store-sqlite.d.ts +1 -1
  133. package/dist/store-sqlite.d.ts.map +1 -1
  134. package/dist/store-sqlite.js +197 -33
  135. package/dist/store-sqlite.js.map +1 -1
  136. package/dist/stream-buffer-Cx7AAPGq.js +23 -0
  137. package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
  138. package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
  139. package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
  140. package/dist/{wire-BO5wWCb1.js → wire-rSUglYo8.js} +132 -10
  141. package/dist/wire-rSUglYo8.js.map +1 -0
  142. package/docs/01-quickstart.mdx +50 -84
  143. package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
  144. package/docs/actors/01-introduction.mdx +48 -82
  145. package/docs/actors/02-concurrency.mdx +43 -70
  146. package/docs/actors/03-timers.mdx +28 -42
  147. package/docs/actors/04-routes.mdx +110 -137
  148. package/docs/actors/meta.ts +1 -1
  149. package/docs/agents/01-introduction.mdx +257 -0
  150. package/docs/agents/02-tools.mdx +247 -0
  151. package/docs/agents/03-conversations.mdx +538 -0
  152. package/docs/agents/04-harnesses.mdx +136 -0
  153. package/docs/agents/meta.ts +1 -0
  154. package/docs/client/01-react.mdx +294 -0
  155. package/docs/client/02-transports.mdx +86 -0
  156. package/docs/client/03-presence.mdx +224 -0
  157. package/docs/client/meta.ts +1 -0
  158. package/docs/contract/01-events.mdx +57 -0
  159. package/docs/contract/02-state.mdx +142 -0
  160. package/docs/contract/03-sessions.mdx +175 -0
  161. package/docs/index.mdx +112 -156
  162. package/docs/reference/01-core.mdx +139 -0
  163. package/docs/reference/02-server.mdx +855 -0
  164. package/docs/reference/03-client.mdx +301 -0
  165. package/docs/reference/04-ai.mdx +722 -0
  166. package/docs/reference/05-adapters.mdx +351 -0
  167. package/docs/reference/06-errors.mdx +138 -0
  168. package/docs/reference/meta.ts +1 -1
  169. package/docs/server/01-handlers.mdx +200 -0
  170. package/docs/server/02-timers.mdx +130 -0
  171. package/docs/server/03-cancellation.mdx +141 -0
  172. package/docs/server/04-routes.mdx +243 -0
  173. package/docs/server/05-production.mdx +322 -0
  174. package/docs/server/06-guarantees.mdx +123 -0
  175. package/examples/playground/DEPLOY.md +53 -7
  176. package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
  177. package/examples/playground/app/a0/[appId]/page.tsx +53 -0
  178. package/examples/playground/app/a0/a0.css +818 -0
  179. package/examples/playground/app/a0/app-list.tsx +218 -0
  180. package/examples/playground/app/a0/app-view.tsx +63 -0
  181. package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
  182. package/examples/playground/app/a0/apps/[appId]/route.ts +28 -0
  183. package/examples/playground/app/a0/apps.test.ts +128 -0
  184. package/examples/playground/app/a0/apps.ts +102 -0
  185. package/examples/playground/app/a0/chat.tsx +410 -0
  186. package/examples/playground/app/a0/columns.tsx +109 -0
  187. package/examples/playground/app/a0/context-menu.tsx +116 -0
  188. package/examples/playground/app/a0/cookies.ts +46 -0
  189. package/examples/playground/app/a0/crash-on-request.ts +72 -0
  190. package/examples/playground/app/a0/crashes/route.ts +3 -0
  191. package/examples/playground/app/a0/crashes.test.ts +33 -0
  192. package/examples/playground/app/a0/crashes.ts +28 -0
  193. package/examples/playground/app/a0/devtools.tsx +164 -0
  194. package/examples/playground/app/a0/events/route.ts +6 -0
  195. package/examples/playground/app/a0/flags.ts +30 -0
  196. package/examples/playground/app/a0/fx/events/route.ts +9 -0
  197. package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
  198. package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
  199. package/examples/playground/app/a0/layout.tsx +38 -0
  200. package/examples/playground/app/a0/list/route.ts +10 -0
  201. package/examples/playground/app/a0/model.test.ts +73 -0
  202. package/examples/playground/app/a0/model.ts +95 -0
  203. package/examples/playground/app/a0/no-photon.cjs +4 -0
  204. package/examples/playground/app/a0/no-quickjs.cjs +3 -0
  205. package/examples/playground/app/a0/page.tsx +14 -0
  206. package/examples/playground/app/a0/pi/events/route.ts +9 -0
  207. package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
  208. package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
  209. package/examples/playground/app/a0/preview-pane.tsx +145 -0
  210. package/examples/playground/app/a0/preview.test.ts +53 -0
  211. package/examples/playground/app/a0/preview.ts +26 -0
  212. package/examples/playground/app/a0/sandbox-log.ts +75 -0
  213. package/examples/playground/app/a0/sandbox-session.ts +152 -0
  214. package/examples/playground/app/a0/sandbox.ts +191 -0
  215. package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
  216. package/examples/playground/app/a0/scheduler/route.ts +7 -0
  217. package/examples/playground/app/a0/server.ts +213 -0
  218. package/examples/playground/app/a0/session.ts +59 -0
  219. package/examples/playground/app/a0/shell.tsx +158 -0
  220. package/examples/playground/app/a0/short/events/route.ts +9 -0
  221. package/examples/playground/app/a0/splitter.tsx +120 -0
  222. package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
  223. package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
  224. package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
  225. package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
  226. package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
  227. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
  228. package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
  229. package/examples/playground/app/agent/events/route.ts +5 -2
  230. package/examples/playground/app/agent/ingress.test.ts +101 -0
  231. package/examples/playground/app/agent/model-input.test.tsx +58 -0
  232. package/examples/playground/app/agent/model-input.tsx +115 -0
  233. package/examples/playground/app/agent/model.test.ts +73 -0
  234. package/examples/playground/app/agent/model.ts +58 -26
  235. package/examples/playground/app/agent/new-agent-session.tsx +37 -19
  236. package/examples/playground/app/agent/page.tsx +4 -4
  237. package/examples/playground/app/agent/server.ts +19 -15
  238. package/examples/playground/app/agent/session.ts +5 -6
  239. package/examples/playground/app/agent/telemetry.test.ts +189 -0
  240. package/examples/playground/app/agent/telemetry.ts +30 -0
  241. package/examples/playground/app/agent/trace.test.ts +23 -0
  242. package/examples/playground/app/agent/trace.ts +18 -0
  243. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +12 -12
  244. package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
  245. package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
  246. package/examples/playground/app/canvas/model.ts +53 -53
  247. package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
  248. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
  249. package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
  250. package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
  251. package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
  252. package/examples/playground/app/chat/[chatId]/session.ts +2 -19
  253. package/examples/playground/app/chat/agent/route.ts +9 -0
  254. package/examples/playground/app/chat/model.test.ts +46 -119
  255. package/examples/playground/app/chat/model.ts +82 -128
  256. package/examples/playground/app/chat/scheduler/route.ts +2 -2
  257. package/examples/playground/app/chat/server.test.ts +112 -0
  258. package/examples/playground/app/chat/server.ts +113 -117
  259. package/examples/playground/app/components/activity-feed.tsx +30 -1
  260. package/examples/playground/app/components/activity-history.test.tsx +35 -0
  261. package/examples/playground/app/components/demo-icons.tsx +89 -0
  262. package/examples/playground/app/components/demos.tsx +33 -0
  263. package/examples/playground/app/components/fast-link.test.tsx +58 -0
  264. package/examples/playground/app/components/fast-link.tsx +55 -0
  265. package/examples/playground/app/components/site-header.tsx +96 -0
  266. package/examples/playground/app/counter/counter-client.tsx +3 -3
  267. package/examples/playground/app/counter/model.test.ts +3 -3
  268. package/examples/playground/app/counter/model.ts +11 -21
  269. package/examples/playground/app/counter/page.tsx +2 -15
  270. package/examples/playground/app/counter/session.ts +2 -2
  271. package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
  272. package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
  273. package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
  274. package/examples/playground/app/documents/[documentId]/session.ts +2 -2
  275. package/examples/playground/app/documents/model.ts +27 -27
  276. package/examples/playground/app/globals.css +420 -207
  277. package/examples/playground/app/initial-state.test.tsx +54 -0
  278. package/examples/playground/app/layout.tsx +7 -23
  279. package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
  280. package/examples/playground/app/orders/[orderId]/session.ts +2 -2
  281. package/examples/playground/app/orders/model.ts +49 -49
  282. package/examples/playground/app/page.tsx +13 -102
  283. package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
  284. package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
  285. package/examples/playground/app/recovery/model.ts +30 -30
  286. package/examples/playground/instrumentation.ts +5 -0
  287. package/examples/playground/next.config.ts +17 -5
  288. package/examples/playground/package.json +13 -2
  289. package/examples/playground/scripts/a0-template.ts +77 -0
  290. package/examples/playground/vercel.json +36 -0
  291. package/package.json +29 -3
  292. package/src/actor-client.ts +16 -4
  293. package/src/actor-react.ts +25 -11
  294. package/src/actor-shared.ts +21 -6
  295. package/src/actor.ts +76 -31
  296. package/src/ai-agent-actions.ts +219 -0
  297. package/src/ai-agent.ts +2517 -0
  298. package/src/ai-app-state.ts +91 -0
  299. package/src/ai-client-state.ts +6 -3
  300. package/src/ai-client.ts +62 -0
  301. package/src/ai-context-schema.ts +25 -5
  302. package/src/ai-context.ts +90 -40
  303. package/src/ai-control-server.ts +92 -72
  304. package/src/ai-coordinator.ts +23 -22
  305. package/src/ai-fx.browser.ts +5 -0
  306. package/src/ai-fx.ts +833 -0
  307. package/src/ai-harness.ts +518 -0
  308. package/src/ai-message-base.ts +209 -0
  309. package/src/ai-message-projection.ts +262 -64
  310. package/src/ai-model-metadata.ts +11 -6
  311. package/src/ai-pi.browser.ts +5 -0
  312. package/src/ai-pi.ts +1263 -0
  313. package/src/ai-progress-batches.ts +26 -0
  314. package/src/ai-projector.ts +95 -14
  315. package/src/ai-react.ts +146 -0
  316. package/src/ai-sdk-step.ts +2 -2
  317. package/src/ai-server.browser.ts +2 -2
  318. package/src/ai-server.ts +23 -1965
  319. package/src/ai-stored-state.ts +48 -163
  320. package/src/ai.ts +339 -155
  321. package/src/bootstrap-stream.ts +72 -0
  322. package/src/bounded-cache.ts +55 -0
  323. package/src/client-render-session.ts +49 -0
  324. package/src/client.ts +688 -151
  325. package/src/contract.ts +92 -42
  326. package/src/deterministic-id.ts +37 -0
  327. package/src/equal-json.ts +32 -0
  328. package/src/errors.ts +13 -1
  329. package/src/index.ts +5 -1
  330. package/src/initial-snapshot.ts +39 -0
  331. package/src/internal.ts +52 -0
  332. package/src/libfx.d.ts +34 -0
  333. package/src/postgres-notification-scope.ts +14 -3
  334. package/src/react.ts +171 -206
  335. package/src/redis-http-reply.ts +256 -0
  336. package/src/redis-http-stream.ts +73 -0
  337. package/src/redis-http-subscriptions.ts +13 -5
  338. package/src/reducer-runtime.ts +52 -0
  339. package/src/reducer.ts +93 -11
  340. package/src/scheduler-qstash.ts +49 -20
  341. package/src/scheduler-task.ts +34 -2
  342. package/src/scheduler-vercel.ts +32 -84
  343. package/src/server-fetch.ts +139 -35
  344. package/src/server.ts +1133 -255
  345. package/src/session-clear.ts +23 -0
  346. package/src/session-socket.ts +154 -49
  347. package/src/sse.ts +71 -4
  348. package/src/store-memory.ts +252 -75
  349. package/src/store-polling.ts +18 -4
  350. package/src/store-postgres.ts +541 -114
  351. package/src/store-redis-core.ts +1450 -493
  352. package/src/store-redis-http.ts +18 -2
  353. package/src/store-redis-notify.ts +262 -85
  354. package/src/store-redis.ts +19 -9
  355. package/src/store-sqlite.ts +319 -45
  356. package/src/store.ts +79 -1
  357. package/src/stream-buffer.ts +43 -0
  358. package/src/telemetry.ts +14 -1
  359. package/src/wire.ts +241 -13
  360. package/dist/actor-ohPC-81x.d.ts.map +0 -1
  361. package/dist/actor-shared-B5tJfzt-.js.map +0 -1
  362. package/dist/ai-CrEf6p_W.d.ts.map +0 -1
  363. package/dist/ai-DgOBltJ_.js.map +0 -1
  364. package/dist/client-BYzHjkwU.js.map +0 -1
  365. package/dist/client-CzyacQpJ.d.ts.map +0 -1
  366. package/dist/contract-CKRg_E4q.js.map +0 -1
  367. package/dist/errors-DCk6ch5n.js.map +0 -1
  368. package/dist/errors-DvhSXnxk.d.ts.map +0 -1
  369. package/dist/internal-Dq2qYxou.js.map +0 -1
  370. package/dist/react.d.ts.map +0 -1
  371. package/dist/reducer-DEMjEY_O.js +0 -29
  372. package/dist/reducer-DEMjEY_O.js.map +0 -1
  373. package/dist/reducer-otzHjuJj.d.ts.map +0 -1
  374. package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
  375. package/dist/server-BD5ckxZb.js.map +0 -1
  376. package/dist/server-DbD7IJVK.d.ts.map +0 -1
  377. package/dist/store-DyZM6fS5.d.ts.map +0 -1
  378. package/dist/store-polling-CmxUbV93.js.map +0 -1
  379. package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
  380. package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
  381. package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
  382. package/dist/validate-XKT4FSNn.js +0 -30
  383. package/dist/validate-XKT4FSNn.js.map +0 -1
  384. package/dist/wire-BO5wWCb1.js.map +0 -1
  385. package/docs/concepts/01-contracts.mdx +0 -142
  386. package/docs/concepts/02-handlers.mdx +0 -284
  387. package/docs/concepts/03-durability.mdx +0 -314
  388. package/docs/concepts/04-state.mdx +0 -209
  389. package/docs/guides/01-timers.mdx +0 -185
  390. package/docs/guides/02-cancellation.mdx +0 -161
  391. package/docs/guides/03-react.mdx +0 -384
  392. package/docs/guides/04-local-first.mdx +0 -88
  393. package/docs/guides/05-production.mdx +0 -521
  394. package/docs/guides/06-ai-agents.mdx +0 -1318
  395. package/docs/guides/08-application-data.mdx +0 -113
  396. package/docs/guides/09-presence.mdx +0 -266
  397. package/docs/guides/10-transports.mdx +0 -221
  398. package/docs/reference/01-api.mdx +0 -1896
  399. package/docs/reference/02-errors.mdx +0 -149
  400. package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
  401. package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
  402. package/examples/playground/app/agent/compaction-settings.ts +0 -49
  403. package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
  404. package/examples/playground/app/agent/compaction-timeline.ts +0 -208
  405. /package/docs/{concepts → contract}/meta.ts +0 -0
  406. /package/docs/{guides → server}/meta.ts +0 -0
@@ -1,5 +1,6 @@
1
+ import { i as StateSnapshot } from "./reducer-CqA59Q6U.js";
1
2
  //#region src/errors.d.ts
2
- type A2ServerErrorCode = "INVALID_PAYLOAD" | "FORBIDDEN" | "UNKNOWN_EVENT_TYPE" | "PARTIAL_DUPLICATE_BATCH" | "SUPERSEDED_ATTEMPT" | "CLAIM_EXPIRED" | "STORE_UNAVAILABLE" | "STORE_NOT_CONFIGURED" | "UNKNOWN_PRESENCE_FIELD" | "PRESENCE_NOT_SUPPORTED";
3
+ type A2ServerErrorCode = "INVALID_PAYLOAD" | "FORBIDDEN" | "UNKNOWN_EVENT_TYPE" | "PARTIAL_DUPLICATE_BATCH" | "SUPERSEDED_ATTEMPT" | "SESSION_CLEARED" | "CLAIM_EXPIRED" | "STORE_UNAVAILABLE" | "STORE_NOT_CONFIGURED" | "UNKNOWN_PRESENCE_FIELD" | "PRESENCE_NOT_SUPPORTED";
3
4
  type A2ErrorCode = A2ServerErrorCode | "TIMEOUT" | "HTTP_ERROR" | "CONNECTION_FAILED" | "UNKNOWN";
4
5
  declare class A2Error extends Error {
5
6
  readonly code: A2ErrorCode;
@@ -24,5 +25,8 @@ declare class NonRetriableError extends Error {
24
25
  });
25
26
  }
26
27
  //#endregion
27
- export { NonRetriableError as i, A2ErrorCode as n, A2ServerErrorCode as r, A2Error as t };
28
- //# sourceMappingURL=errors-DvhSXnxk.d.ts.map
28
+ //#region src/initial-snapshot.d.ts
29
+ type InitialSnapshot<S> = StateSnapshot<S> | null | undefined | Promise<StateSnapshot<S> | null | undefined>;
30
+ //#endregion
31
+ export { NonRetriableError as a, A2ServerErrorCode as i, A2Error as n, A2ErrorCode as r, InitialSnapshot as t };
32
+ //# sourceMappingURL=initial-snapshot-Bx-c0pDP.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initial-snapshot-Bx-c0pDP.d.ts","names":[],"sources":["../src/errors.ts","../src/initial-snapshot.ts"],"mappings":";;KAAY;KAaA,cACV;cAEW,gBAAgB;WAClB,MAAM;;WAEN;EAET,YACE,MAAM,aACN,iBACA;IAAY;IAAmB;;;;;;;;;;;cA2CtB,0BAA0B;EACrC,YAAY,iBAAiB;IAAY;;;;;KClE/B,gBAAgB,KACxB,cAAc,wBAGd,QAAQ,cAAc"}
@@ -28,6 +28,12 @@ const RESERVED_PARTICIPANT_IDS = /* @__PURE__ */ new Set([
28
28
  "prototype"
29
29
  ]);
30
30
  const serverInternals = /* @__PURE__ */ new WeakMap();
31
+ /**
32
+ * Handler contexts whose previous attempt ended in a failure the handler
33
+ * threw, as opposed to a claim that lapsed (a crash, a timeout, a frozen
34
+ * process). Only the attempt's own outcome is known, not how far it got.
35
+ */
36
+ const failedPreviousAttempt = /* @__PURE__ */ new WeakSet();
31
37
  const objectLike = (value) => typeof value === "object" && value !== null || typeof value === "function";
32
38
  const TOOL_SCOPE_STORAGE = Symbol.for("experimental-a2.ai.tool-scope");
33
39
  function ambientToolScopeStorage() {
@@ -151,6 +157,27 @@ const POLL_TIMINGS = {
151
157
  * white-box test seam.
152
158
  */
153
159
  const NOTIFY_TIMINGS = { safetyReadMs: 1e4 };
160
+ /**
161
+ * Bounds for each server instance's in-memory reducer-state cache. Sizes are
162
+ * encoded JSON lengths, re-measured every `measureEvery` folded events, so
163
+ * the byte budget is approximate. Mutable only as a white-box test seam.
164
+ */
165
+ const STATE_CACHE = {
166
+ maxEntries: 1e3,
167
+ maxBytes: 33554432,
168
+ snapshotEvery: 128,
169
+ maxTail: 255,
170
+ measureEvery: 16
171
+ };
172
+ /**
173
+ * Warm state-read tails for stores that can serve one in a single request:
174
+ * events from `fromIndex` (inclusive) through `throughIndex` or the newest
175
+ * event, or null when that needs more than one request. Like every store
176
+ * read, a tail starts no lower than the latest clear marker. Keyed by the exact
177
+ * store object, so a wrapper built by spreading or proxying a store reads
178
+ * through its own `readHistory` instead.
179
+ */
180
+ const stateTailReaders = /* @__PURE__ */ new WeakMap();
154
181
  const STREAM_TIMINGS = {
155
182
  sseHeartbeatMs: 15e3,
156
183
  sseDeadlineGraceMs: 1e3,
@@ -183,6 +210,6 @@ const SOCKET_TIMINGS = {
183
210
  */
184
211
  const PRESENCE_TIMINGS = { sendIntervalMs: 66 };
185
212
  //#endregion
186
- export { PRESENCE_TIMINGS as a, STREAM_TIMINGS as c, classifySchedulerSendFailure as d, consumeSchedulerSendFailure as f, serverInternals as g, nullProtoRecord as h, POLL_TIMINGS as i, ambientToolScopeStorage as l, markSchedulerSendFailure as m, MAX_DATE_MS as n, RESERVED_PARTICIPANT_IDS as o, installAmbientToolScopeStorage as p, NOTIFY_TIMINGS as r, SOCKET_TIMINGS as s, DRAIN_TIMINGS as t, assertSchedulerTargets as u };
213
+ export { nullProtoRecord as _, PRESENCE_TIMINGS as a, STATE_CACHE as c, assertSchedulerTargets as d, classifySchedulerSendFailure as f, markSchedulerSendFailure as g, installAmbientToolScopeStorage as h, POLL_TIMINGS as i, STREAM_TIMINGS as l, failedPreviousAttempt as m, MAX_DATE_MS as n, RESERVED_PARTICIPANT_IDS as o, consumeSchedulerSendFailure as p, NOTIFY_TIMINGS as r, SOCKET_TIMINGS as s, DRAIN_TIMINGS as t, ambientToolScopeStorage as u, serverInternals as v, stateTailReaders as y };
187
214
 
188
- //# sourceMappingURL=internal-Dq2qYxou.js.map
215
+ //# sourceMappingURL=internal-B2T3U4Rq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-B2T3U4Rq.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/**\n * Private seams shared by a2 entry points and a2's own tests. Not exported\n * from the package. Nothing here is public API.\n */\n\nimport type { ScheduledEvent } from './scheduler-task.ts'\nimport type { DrainableServer } from './server.ts'\nimport type { Event } from './store.ts'\n\nexport type DrainOutcome = 'busy' | 'stalled' | 'settled'\n\n/**\n * A null-prototype record for maps keyed by caller-controlled strings\n * (participant ids, presence field names). On a normal object,\n * assigning to '__proto__' rewrites the prototype instead of creating\n * an own property — the prototype-pollution class. Null-prototype\n * objects still satisfy Record types and JSON.stringify cleanly.\n */\nexport function nullProtoRecord<T extends object>(): T {\n return Object.create(null) as T\n}\n\n/**\n * ECMA-262's maximum Date value (±8.64e15 ms). A presence stamp past\n * it becomes an Invalid Date whose toISOString throws — one hostile\n * stamp would poison every subscriber's snapshot frame — so both\n * seams bound `at` to the representable range.\n */\nexport const MAX_DATE_MS = 8_640_000_000_000_000\n\n/**\n * Participant ids that collide with Object.prototype plumbing.\n * Null-prototype construction already defuses them, but they are\n * rejected at both seams (`parsePushBody`, `setPresence`) so a hostile\n * id never propagates into userland maps either.\n */\nexport const RESERVED_PARTICIPANT_IDS: ReadonlySet<string> = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n])\n\nexport type DrainResult = {\n settled: boolean\n outcome: DrainOutcome\n /** Existing successor arm a scheduler callback may join before acking. */\n schedulerArm?: Promise<void>\n}\n\nexport type ServerInternals = {\n /**\n * Await every in-flight background drain this machine has scheduled,\n * including ones scheduled while waiting. Lets tests flush the\n * fire-and-forget inline path deterministically — no sleeps.\n */\n settle(): Promise<void>\n /** The public `server.drain()` result deliberately remains `{ settled }`. */\n schedulerDrain(\n sessionId: string,\n opts?: { watchdogDueAt?: number },\n ): Promise<DrainResult>\n /** Deliver a scheduler-owned delayed append through the normal root path. */\n schedulerAppend(\n sessionId: string,\n events: readonly ScheduledEvent[],\n originIndex?: number,\n ): Promise<void>\n /** Deliver a scheduler-owned conditional clear, deletion included. */\n schedulerClear(sessionId: string, ifLastIndex: number): Promise<void>\n /** `session.history` with clear markers kept; reads start at the latest one. */\n historyWithMarkers(sessionId: string, gte: number): Promise<Event[]>\n}\n\nexport const serverInternals: WeakMap<object, ServerInternals> = new WeakMap()\n\n/**\n * Handler contexts whose previous attempt ended in a failure the handler\n * threw, as opposed to a claim that lapsed (a crash, a timeout, a frozen\n * process). Only the attempt's own outcome is known, not how far it got.\n */\nexport const failedPreviousAttempt: WeakSet<object> = new WeakSet()\n\nconst objectLike = (value: unknown): value is object =>\n (typeof value === 'object' && value !== null) || typeof value === 'function'\n\n/** The ambient value delivered to `handlerContext` during a tool execution. */\nexport type AmbientToolScope = {\n /** Executing server identity; a test scope from `runWithHandlerContext` has none. */\n contract?: { readonly name: string }\n context: unknown\n}\n\n/**\n * Structural AsyncLocalStorage surface for the ambient tool scope. The\n * instance is created by the server-only AI entry point (the only place\n * `node:async_hooks` may be imported) and shared through a global\n * symbol slot, so `handlerContext` in the isomorphic AI entry point and\n * independently bundled A2 copies all read the same scope.\n */\nexport type AmbientToolScopeStorage = {\n getStore(): AmbientToolScope | undefined\n run<R>(store: AmbientToolScope, callback: () => R): R\n}\n\nconst TOOL_SCOPE_STORAGE = Symbol.for('experimental-a2.ai.tool-scope')\n\nexport function ambientToolScopeStorage(): AmbientToolScopeStorage | undefined {\n const value = Reflect.get(globalThis, TOOL_SCOPE_STORAGE)\n if (\n !objectLike(value) ||\n typeof Reflect.get(value, 'getStore') !== 'function' ||\n typeof Reflect.get(value, 'run') !== 'function'\n ) {\n return undefined\n }\n return value as AmbientToolScopeStorage\n}\n\nexport function installAmbientToolScopeStorage(\n create: () => AmbientToolScopeStorage,\n): AmbientToolScopeStorage {\n const existing = ambientToolScopeStorage()\n if (existing !== undefined) return existing\n const storage = create()\n Reflect.set(globalThis, TOOL_SCOPE_STORAGE, storage)\n return storage\n}\n\nexport type SchedulerSendFailureDisposition = 'retryable' | 'terminal'\n\ntype SchedulerSendFailureMarker = {\n readonly version: 1\n readonly pending: SchedulerSendFailureDisposition[]\n readonly active: SchedulerSendFailureDisposition[]\n}\n\nconst SCHEDULER_SEND_FAILURE = Symbol.for(\n 'experimental-a2.scheduler.send-failure',\n)\n\nconst schedulerSendFailureMarker = (\n value: unknown,\n): SchedulerSendFailureMarker | undefined => {\n if (!objectLike(value)) return undefined\n const marker = Reflect.get(value, SCHEDULER_SEND_FAILURE)\n if (\n !objectLike(marker) ||\n Reflect.get(marker, 'version') !== 1 ||\n !Array.isArray(Reflect.get(marker, 'pending')) ||\n !Array.isArray(Reflect.get(marker, 'active'))\n ) {\n return undefined\n }\n return marker as SchedulerSendFailureMarker\n}\n\nconst schedulerFailureObject = (error: unknown): object => {\n if (\n objectLike(error) &&\n (schedulerSendFailureMarker(error) !== undefined ||\n Object.isExtensible(error))\n ) {\n return error\n }\n return new Error(String(error), { cause: error })\n}\n\nconst schedulerSendFailureState = (\n error: object,\n): SchedulerSendFailureMarker => {\n const existing = schedulerSendFailureMarker(error)\n if (existing !== undefined) return existing\n const marker: SchedulerSendFailureMarker = {\n version: 1,\n pending: [],\n active: [],\n }\n Object.defineProperty(error, SCHEDULER_SEND_FAILURE, {\n value: marker,\n enumerable: false,\n configurable: true,\n writable: false,\n })\n return marker\n}\n\nconst clearSchedulerSendFailureState = (\n error: object,\n marker: SchedulerSendFailureMarker,\n): void => {\n if (marker.pending.length === 0 && marker.active.length === 0) {\n Reflect.deleteProperty(error, SCHEDULER_SEND_FAILURE)\n }\n}\n\n/**\n * Classify an adapter error before throwing it. Unclassified adapter failures\n * are retryable because the caller cannot know whether a remote send landed.\n */\nexport function classifySchedulerSendFailure(\n error: unknown,\n disposition: SchedulerSendFailureDisposition,\n): unknown {\n const classified = schedulerFailureObject(error)\n schedulerSendFailureState(classified).pending.push(disposition)\n return classified\n}\n\n/** Consume a pending adapter classification and count this rejected send. */\nexport function markSchedulerSendFailure(error: unknown): unknown {\n const marked = schedulerFailureObject(error)\n const marker = schedulerSendFailureState(marked)\n marker.active.push(marker.pending.pop() ?? 'retryable')\n return marked\n}\n\nexport function consumeSchedulerSendFailure(\n error: unknown,\n): SchedulerSendFailureDisposition | undefined {\n if (!objectLike(error)) return undefined\n const marker = schedulerSendFailureMarker(error)\n const disposition = marker?.active.pop()\n if (marker !== undefined) clearSchedulerSendFailureState(error, marker)\n return disposition\n}\n\nconst isDrainableServer = (value: unknown): value is DrainableServer => {\n if (value === null || typeof value !== 'object') return false\n const contract = Reflect.get(value, 'contract')\n return (\n contract !== null &&\n typeof contract === 'object' &&\n typeof Reflect.get(contract, 'name') === 'string' &&\n typeof Reflect.get(value, 'drain') === 'function'\n )\n}\n\n/**\n * Validate the server list captured by a scheduler route at construction time.\n * The seam stays structural so custom scheduler integrations can supply their\n * own drainable targets.\n */\nexport function assertSchedulerTargets(\n servers: readonly unknown[],\n): asserts servers is readonly DrainableServer[] {\n const contracts = new Set<string>()\n for (const target of servers) {\n if (!isDrainableServer(target)) {\n throw new TypeError('a2 scheduler: expected a server from createServer()')\n }\n const name = target.contract.name\n if (contracts.has(name)) {\n throw new TypeError(\n `a2 scheduler: duplicate server contract '${name}' in one handler`,\n )\n }\n contracts.add(name)\n }\n}\n\n/**\n * Drain timing knobs. Mutable only as a white-box test seam. Claim\n * heartbeats run on real timers, so tests shrink these to keep suites\n * fast. Production always uses the defaults.\n */\nexport const DRAIN_TIMINGS: {\n /** How long a claim lives without renewal. */\n claimTtlMs: number\n /** Heartbeat cadence while a handler is running. */\n claimHeartbeatMs: number\n /** Base delay before retrying a failed renewal while the lease lives. */\n renewRetryMs: number\n /** Delay between a claim's expiry and its scheduler watchdog. */\n watchdogGraceMs: number\n /** Maximum time a top-level append waits for its initial scheduler arm. */\n schedulerArmTimeoutMs: number\n} = {\n claimTtlMs: 5_000,\n claimHeartbeatMs: 2_000,\n renewRetryMs: 400,\n watchdogGraceMs: 1_000,\n schedulerArmTimeoutMs: 2_000,\n}\n\n/**\n * Stream liveness knobs — the server-side SSE heartbeat and the\n * client-side stall watchdog. Paired by design: the client declares a\n * connection dead after roughly two missed heartbeats. Mutable only as\n * a white-box test seam; production always uses the defaults.\n */\n/**\n * Poll cadence for the poll-based store streams (sqlite, postgres) —\n * owned, not configurable: adaptive polling removed the operator's\n * reason to tune it (the floor keeps active streams smooth; the\n * ceiling bounds idle cost). Mutable only as a white-box test seam so\n * suites don't wait out real idle gaps.\n */\nexport const POLL_TIMINGS: {\n /** Gap between polls while a session is producing events. */\n activeFloorMs: number\n /** The idle ceiling — the longest gap between polls. */\n idleCeilingMs: number\n} = {\n activeFloorMs: 25,\n idleCeilingMs: 2_000,\n}\n\n/**\n * Safety-read cadence for the notify-driven redis live stream. A\n * parked feed re-reads the store at this interval even without a\n * pub/sub wake-up, so a lost notification (subscriber reconnect gap,\n * dropped PUBLISH) costs latency, never an event. Mutable only as a\n * white-box test seam.\n */\nexport const NOTIFY_TIMINGS: {\n safetyReadMs: number\n} = {\n safetyReadMs: 10_000,\n}\n\n/**\n * Bounds for each server instance's in-memory reducer-state cache. Sizes are\n * encoded JSON lengths, re-measured every `measureEvery` folded events, so\n * the byte budget is approximate. Mutable only as a white-box test seam.\n */\nexport const STATE_CACHE: {\n maxEntries: number\n maxBytes: number\n /** Events a cached state may advance before it writes a snapshot. */\n snapshotEvery: number\n /** Events a warm read may fold; longer tails read the stored snapshot. */\n maxTail: number\n /** Events folded between re-measuring a cached state's encoded size. */\n measureEvery: number\n} = {\n maxEntries: 1_000,\n maxBytes: 32 * 1024 * 1024,\n snapshotEvery: 128,\n maxTail: 255,\n measureEvery: 16,\n}\n\n/**\n * Warm state-read tails for stores that can serve one in a single request:\n * events from `fromIndex` (inclusive) through `throughIndex` or the newest\n * event, or null when that needs more than one request. Like every store\n * read, a tail starts no lower than the latest clear marker. Keyed by the exact\n * store object, so a wrapper built by spreading or proxying a store reads\n * through its own `readHistory` instead.\n */\nexport const stateTailReaders: WeakMap<\n object,\n (\n sessionId: string,\n fromIndex: number,\n throughIndex?: number,\n ) => Promise<Event[] | null>\n> = new WeakMap()\n\nexport const STREAM_TIMINGS: {\n /** Cadence of `: ping` comment frames from `sseResponse`. */\n sseHeartbeatMs: number\n /** How far ahead of an invocation deadline an SSE response closes. */\n sseDeadlineGraceMs: number\n /** Client: silence longer than this means the connection is dead. */\n stallTimeoutMs: number\n /** Client reconnect backoff: first retry delay, doubling per attempt. */\n reconnectBaseMs: number\n /** Client reconnect backoff ceiling. */\n reconnectMaxMs: number\n} = {\n sseHeartbeatMs: 15_000,\n sseDeadlineGraceMs: 1_000,\n stallTimeoutMs: 35_000,\n reconnectBaseMs: 500,\n reconnectMaxMs: 5_000,\n}\n\n/**\n * WebSocket down-pump backpressure — `sessionsSocket` parks its pull\n * loop while the socket reports more buffered bytes than the\n * high-water mark and re-checks on the poll cadence, so a slow reader\n * applies backpressure to the store subscription instead of buffering\n * without bound in the socket. Acks and heartbeats stay exempt: they\n * are small and correctness-relevant. Mutable only as a white-box test\n * seam.\n */\nexport const SOCKET_TIMINGS: {\n highWaterMarkBytes: number\n resumePollMs: number\n /** Up-lane bound: concurrent push/presence handlers per socket. POST\n * gets per-request platform limits for free; the socket must bring\n * its own. Over the cap, pushes are answered with a retryable error\n * ack and presence frames are shed (that plane repaints). */\n maxInFlightMessages: number\n /** Hard ceiling on `bufferedAmount`: past it the peer is a\n * non-reader (acks and pings are park-exempt, so only a peer that\n * never reads can reach it) and the socket closes 1008. */\n disconnectWaterMarkBytes: number\n} = {\n highWaterMarkBytes: 1_048_576,\n resumePollMs: 25,\n maxInFlightMessages: 32,\n disconnectWaterMarkBytes: 8_388_608,\n}\n\n/**\n * Client presence send cadence — a leading send immediately, then at\n * most one merged send per interval (~15Hz). Owned, not configurable\n * (decision #36's rule): coupling the cadence to the POST ack tied\n * remote cursor smoothness to RTT, and the ack serves no correctness\n * purpose on a fire-and-forget LWW plane. Mutable only as a white-box\n * test seam.\n */\nexport const PRESENCE_TIMINGS: {\n sendIntervalMs: number\n} = {\n sendIntervalMs: 66,\n}\n"],"mappings":"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"}
package/dist/otel.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as A2Telemetry } from "./telemetry-B5jzpy6y.js";
1
+ import { i as A2Telemetry } from "./telemetry-Cyfqo6Qt.js";
2
2
  import { Tracer } from "@opentelemetry/api";
3
3
  //#region src/otel.d.ts
4
4
  type OtelOptions = {
@@ -0,0 +1,86 @@
1
+ import { c as ContractEvent, d as PresenceDefs, f as PresenceMap, g as WithPresence, o as AppendInput, p as PresencePatch, s as Contract, t as Reducer, u as EventDefs } from "./reducer-CqA59Q6U.js";
2
+ import { t as InitialSnapshot } from "./initial-snapshot-Bx-c0pDP.js";
3
+ import { a as Connection, c as HistoryState, l as LoadHistoryOptions, t as A2Client, u as PushResult } from "./client-h_DKFAjj.js";
4
+ import { ReactElement, ReactNode } from "react";
5
+ //#region src/react.d.ts
6
+ type UseSessionOptions<S> = {
7
+ participant?: string;
8
+ initial?: InitialSnapshot<S>;
9
+ enabled?: boolean;
10
+ suspend?: boolean;
11
+ };
12
+ type SessionProviderProps<D extends EventDefs, S> = {
13
+ sessionId: string;
14
+ api: string;
15
+ initial?: InitialSnapshot<S>;
16
+ initialEvents?: ContractEvent<D>[];
17
+ participant?: string;
18
+ children?: ReactNode;
19
+ };
20
+ type BoundSessionProviderProps<D extends EventDefs, S> = Omit<SessionProviderProps<D, S>, "api">;
21
+ type UseSessionPresence<P extends PresenceDefs> = {
22
+ /** The replicated presence map, this client included (local echo). */
23
+ presence: PresenceMap<P>;
24
+ /**
25
+ * Fire-and-forget: validated locally, echoed into the map
26
+ * immediately, coalesced on the wire to a fixed cadence (a leading
27
+ * send immediately, then at most one merged send per interval;
28
+ * later values win per field), stamped with the frontier at send
29
+ * time as `seen`. Requires the provider's `participant`.
30
+ */
31
+ setPresence: (values: PresencePatch<P>["values"]) => void;
32
+ };
33
+ type UseSessionResult<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = WithPresence<P, UseSessionPresence<P>> & {
34
+ /** The live view, folded through the shared reducer. */
35
+ state: S;
36
+ /** A state/index snapshot has been applied; independent of connection status. */
37
+ hydrated: boolean;
38
+ /** The raw event feed — observed, seeded, or backscrolled. */
39
+ events: ContractEvent<D>[];
40
+ /** The stream frontier — `lastSeenIndex` for cancellation. */
41
+ index: number;
42
+ /**
43
+ * The connection, as a discriminated union — `error` exists only
44
+ * while disconnected; "reconnecting…" is
45
+ * `status === 'connecting' && reconnects > 0`.
46
+ */
47
+ connection: Connection;
48
+ /**
49
+ * Typed optimistic append. Awaiting it gives the server ack;
50
+ * `.confirmed` resolves when the live stream has delivered the batch
51
+ * back. Rejects with the server's A2Error codes.
52
+ */
53
+ push: (...events: AppendInput<D>[]) => PushResult<D>;
54
+ /** Restart the live subscription without resetting its state or cursor. */
55
+ reconnect: () => void;
56
+ /**
57
+ * Backscroll: fetch a bounded slice of older events into `events`.
58
+ * Defaults page backward 50 at a time from the oldest loaded event.
59
+ * Display data only — `state` and the optimistic overlay never move.
60
+ */
61
+ loadHistory: (options?: LoadHistoryOptions) => Promise<ContractEvent<D>[]>;
62
+ /** Backscroll progress: `complete` means the feed reaches index 1. */
63
+ history: HistoryState;
64
+ };
65
+ type BoundSessionOptions<D extends EventDefs, S, P extends PresenceDefs> = UseSessionOptions<S> & {
66
+ sessionId?: string;
67
+ client?: A2Client<D, S, P>;
68
+ };
69
+ type A2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
70
+ SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement;
71
+ useSession: (options?: BoundSessionOptions<D, S, P>) => UseSessionResult<D, S, P>;
72
+ };
73
+ type BoundA2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
74
+ SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement;
75
+ useSession: (options?: BoundSessionOptions<D, S, P>) => UseSessionResult<D, S, P>;
76
+ };
77
+ declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>>(options: {
78
+ client: A2Client<D, S, P>;
79
+ }): BoundA2React<D, S, P>;
80
+ declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>>(options: {
81
+ contract: Contract<D, P, Reducer<D, S, P>>;
82
+ }): A2React<D, S, P>;
83
+ declare function useSession<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>>(client: A2Client<D, S, P>, sessionId: string, options?: UseSessionOptions<NoInfer<S>>): UseSessionResult<D, S, P>;
84
+ //#endregion
85
+ export { SessionProviderProps as a, UseSessionResult as c, BoundSessionProviderProps as i, createReact as l, BoundA2React as n, UseSessionOptions as o, BoundSessionOptions as r, UseSessionPresence as s, A2React as t, useSession as u };
86
+ //# sourceMappingURL=react-BBXgHWFc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-BBXgHWFc.d.ts","names":[],"sources":["../src/react.ts"],"mappings":";;;;;KAwCY,kBAAkB;EAC5B;EACA,UAAU,gBAAgB;EAC1B;EACA;;KAGU,qBAAqB,UAAU,WAAW;EACpD;EACA;EACA,UAAU,gBAAgB;EAC1B,gBAAgB,cAAc;EAC9B;EACA,WAAW;;KAGD,0BAA0B,UAAU,WAAW,KAAK,KAC9D,qBAAqB,GAAG;KAId,mBAAmB,UAAU;;EAEvC,UAAU,YAAY;;;;;;;;EAQtB,cAAc,QAAQ,cAAc;;KAG1B,iBACV,UAAU,WACV,GACA,UAAU,eAAe,wBACvB,aAAa,GAAG,mBAAmB;;EAErC,OAAO;;EAEP;;EAEA,QAAQ,cAAc;;EAEtB;;;;;;EAMA,YAAY;;;;;;EAMZ,UAAU,QAAQ,YAAY,SAAS,WAAW;;EAElD;;;;;;EAMA,cAAc,UAAU,uBAAuB,QAAQ,cAAc;;EAErE,SAAS;;KAGC,oBACV,UAAU,WACV,GACA,UAAU,gBACR,kBAAkB;EACpB;EACA,SAAS,SAAS,GAAG,GAAG;;KAGd,QACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,kBAAkB,OAAO,qBAAqB,GAAG,OAAO;EACxD,aACE,UAAU,oBAAoB,GAAG,GAAG,OACjC,iBAAiB,GAAG,GAAG;;KAGlB,aACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,kBAAkB,OAAO,0BAA0B,GAAG,OAAO;EAC7D,aACE,UAAU,oBAAoB,GAAG,GAAG,OACjC,iBAAiB,GAAG,GAAG;;iBAGd,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB;EAAW,QAAQ,SAAS,GAAG,GAAG;IAAO,aAAa,GAAG,GAAG;iBAC9C,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB;EAAW,UAAU,SAAS,GAAG,GAAG,QAAQ,GAAG,GAAG;IAAQ,QAAQ,GAAG,GAAG;iBA4J1D,WACd,UAAU,WACV,GACA,UAAU,eAAe,sBAEzB,QAAQ,SAAS,GAAG,GAAG,IACvB,mBACA,UAAS,kBAAkB,QAAQ,MAClC,iBAAiB,GAAG,GAAG"}
package/dist/react.d.ts CHANGED
@@ -1,122 +1,2 @@
1
- import { a as ContractEvent, c as PresenceDefs, l as PresenceMap, p as WithPresence, r as AppendInput, s as EventDefs, t as Reducer, u as PresencePatch } from "./reducer-otzHjuJj.js";
2
- import { a as Connection, c as HistoryState, l as LoadHistoryOptions, t as A2Client, u as PushResult } from "./client-CzyacQpJ.js";
3
- import { ReactElement, ReactNode } from "react";
4
- //#region src/react.d.ts
5
- type UseSessionOptions<S> = {
6
- /** Overrides the client's `participant` for this session — for apps
7
- * whose identity is only known at mount time. Bound once per
8
- * session handle. */
9
- participant?: string;
10
- /**
11
- * The server fold this session mounts from: the `{ state, index }`
12
- * pair `session.state(reducer)` returns, fetched by the app through
13
- * its own route. Atomic on purpose — the fold and its frontier
14
- * travel together or not at all. While `undefined` (the app's fetch
15
- * has not landed), the hook holds the stream; when the value
16
- * arrives, the session hydrates through the ordinary re-hydration
17
- * seam and the stream connects at that frontier. To fold from the log's start
18
- * deliberately, hand the fold's true starting point:
19
- * `{ state: reducer.initialState, index: 0 }` — state at index 0 is
20
- * the reducer's seed by definition, so the explicit replay needs no
21
- * special vocabulary. There is no pending/ready echo on the result:
22
- * whether the fold has been handed over is this option — the
23
- * caller's own input — so the data layer's loading state IS the
24
- * pending state.
25
- */
26
- hydrate?: {
27
- state: S;
28
- index: number;
29
- } | undefined;
30
- };
31
- type SessionProviderProps<D extends EventDefs, S> = {
32
- /** Which session to subscribe to. */
33
- sessionId: string;
34
- /** Base path of the route exposing GET (stream) and POST (push). */
35
- api: string;
36
- /** The server-rendered fold — the first paint, no client JS needed. */
37
- initialState: S;
38
- /** The fold's frontier — the stream resumes exactly there. */
39
- initialIndex: number;
40
- /** Server-rendered event history through `initialIndex`. */
41
- initialEvents?: ContractEvent<D>[];
42
- /** Overrides the client's `participant` for this session — for apps
43
- * whose identity is only known at mount time. */
44
- participant?: string;
45
- children?: ReactNode;
46
- };
47
- type BoundSessionProviderProps<D extends EventDefs, S> = Omit<SessionProviderProps<D, S>, "api">;
48
- /**
49
- * The presence members of the hook result — intersected in via
50
- * `WithPresence`, so they exist exactly when the contract declares
51
- * presence fields (the reducer is the client's typed handle on it).
52
- */
53
- type UseSessionPresence<P extends PresenceDefs> = {
54
- /** The replicated presence map, this client included (local echo). */
55
- presence: PresenceMap<P>;
56
- /**
57
- * Fire-and-forget: validated locally, echoed into the map
58
- * immediately, coalesced on the wire to a fixed cadence (a leading
59
- * send immediately, then at most one merged send per interval;
60
- * later values win per field), stamped with the frontier at send
61
- * time as `seen`. Requires the provider's `participant`.
62
- */
63
- setPresence: (values: PresencePatch<P>["values"]) => void;
64
- };
65
- type UseSessionResult<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = WithPresence<P, UseSessionPresence<P>> & {
66
- /** The live view, folded through the shared reducer. */
67
- state: S;
68
- /** The raw event feed — observed, seeded, or backscrolled. */
69
- events: ContractEvent<D>[];
70
- /** The stream frontier — `lastSeenIndex` for cancellation. */
71
- index: number;
72
- /**
73
- * The connection, as a discriminated union — `error` exists only
74
- * while disconnected; "reconnecting…" is
75
- * `status === 'connecting' && reconnects > 0`.
76
- */
77
- connection: Connection;
78
- /**
79
- * Typed optimistic append. Awaiting it gives the server ack;
80
- * `.confirmed` resolves when the live stream has delivered the batch
81
- * back. Rejects with the server's A2Error codes.
82
- */
83
- push: (...events: AppendInput<D>[]) => PushResult<D>;
84
- /** Restart the live subscription without resetting its state or cursor. */
85
- reconnect: () => void;
86
- /**
87
- * Backscroll: fetch a bounded slice of older events into `events`.
88
- * Defaults page backward 50 at a time from the oldest loaded event.
89
- * Display data only — `state` and the optimistic overlay never move.
90
- */
91
- loadHistory: (options?: LoadHistoryOptions) => Promise<ContractEvent<D>[]>;
92
- /** Backscroll progress: `complete` means the feed reaches index 1. */
93
- history: HistoryState;
94
- };
95
- type A2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S> = {
96
- SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement;
97
- useSession: () => UseSessionResult<D, V, P>;
98
- };
99
- type BoundA2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S> = {
100
- SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement;
101
- useSession: () => UseSessionResult<D, V, P>;
102
- };
103
- declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S>(options: {
104
- client: A2Client<D, S, P, V>;
105
- }): BoundA2React<D, S, P, V>;
106
- declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S>(options: {
107
- reducer: Reducer<D, S, P, V>;
108
- }): A2React<D, S, P, V>;
109
- /**
110
- * The standalone, provider-less hook — for apps whose components reach
111
- * sessions ad hoc (a sidebar of channel sessions, a user session read
112
- * from a menu). Takes the shared client and a session id; the handle
113
- * is identity-mapped, so every hook and provider mounting the same
114
- * session shares one runtime and one leased stream. Hydration is
115
- * app-owned: pass `hydrate` when your fetch lands; until then the
116
- * stream is held, and your data layer's loading state is the pending
117
- * state.
118
- */
119
- declare function useSession<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S>(client: A2Client<D, S, P, V>, sessionId: string, options?: UseSessionOptions<S>): UseSessionResult<D, V, P>;
120
- //#endregion
121
- export { A2React, BoundA2React, BoundSessionProviderProps, SessionProviderProps, UseSessionOptions, UseSessionPresence, UseSessionResult, createReact, useSession };
122
- //# sourceMappingURL=react.d.ts.map
1
+ import { a as SessionProviderProps, c as UseSessionResult, i as BoundSessionProviderProps, l as createReact, n as BoundA2React, o as UseSessionOptions, r as BoundSessionOptions, s as UseSessionPresence, t as A2React, u as useSession } from "./react-BBXgHWFc.js";
2
+ export { A2React, BoundA2React, BoundSessionOptions, BoundSessionProviderProps, SessionProviderProps, UseSessionOptions, UseSessionPresence, UseSessionResult, createReact, useSession };
package/dist/react.js CHANGED
@@ -1,138 +1,145 @@
1
1
  "use client";
2
- import { t as createClient } from "./client-BYzHjkwU.js";
3
- import { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
2
+ import { i as isInitialPromise, n as createRenderSession, r as resolveInitialForReact, t as createClient } from "./client-BReKqwZC.js";
3
+ import { createContext, createElement, use, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
4
4
  //#region src/react.ts
5
- /**
6
- * experimental-a2/react — React bindings over experimental-a2/client.
7
- *
8
- * `createReact({ client })` is a factory (like createContext): call it
9
- * once in a `'use client'` module with a shared A2 client and export the
10
- * bound pair. Server components import `SessionProvider` from that module
11
- * as a client reference; client components import `useSession` from the
12
- * same file. The shared client also makes imperative pushes and provider
13
- * renders resolve the same live session. Everything is typed by the
14
- * reducer value — no type arguments, and nothing here ever touches the
15
- * machine module.
16
- *
17
- * The standalone `useSession(client, sessionId, options?)` is the
18
- * provider-less primitive for client-first apps: identity-mapped
19
- * handles, one leased stream per session, and an app-owned hydration
20
- * input — the hook holds the stream until the app hands it a
21
- * `{ state, index }` fold.
22
- */
23
5
  function createReact(options) {
24
6
  const Context = createContext(null);
25
- const resolveSession = "client" in options ? (sessionId, _api, initialState, initialIndex, initialEvents, participant) => options.client.session(sessionId, {
26
- initialState,
27
- initialIndex,
28
- ...initialEvents === void 0 ? {} : { initialEvents },
29
- ...participant === void 0 ? {} : { participant }
30
- }) : (() => {
31
- const clients = /* @__PURE__ */ new Map();
32
- return (sessionId, api, initialState, initialIndex, initialEvents, participant) => {
33
- if (api === void 0) throw new TypeError("SessionProvider requires api when createReact binds a reducer");
34
- let client = clients.get(api);
35
- if (!client) {
36
- client = createClient({
37
- reducer: options.reducer,
38
- api
39
- });
40
- clients.set(api, client);
41
- }
42
- return client.session(sessionId, {
43
- initialState,
44
- initialIndex,
45
- ...initialEvents === void 0 ? {} : { initialEvents },
46
- ...participant === void 0 ? {} : { participant }
7
+ const clients = /* @__PURE__ */ new Map();
8
+ const resolveClient = (api) => {
9
+ if ("client" in options) return options.client;
10
+ if (api === void 0) throw new TypeError("SessionProvider requires api when createReact binds a contract");
11
+ let client = clients.get(api);
12
+ if (!client) {
13
+ client = createClient({
14
+ contract: options.contract,
15
+ api
47
16
  });
48
- };
49
- })();
17
+ clients.set(api, client);
18
+ }
19
+ return client;
20
+ };
50
21
  function SessionProvider(props) {
51
- const { sessionId, initialState, initialIndex, initialEvents, participant } = props;
22
+ const { sessionId, initial, initialEvents, participant } = props;
52
23
  const api = "api" in props ? props.api : void 0;
53
- const client = useMemo(() => resolveSession(sessionId, api, initialState, initialIndex, initialEvents, participant), [
24
+ const scope = useMemo(() => ({
25
+ client: resolveClient(api),
26
+ sessionId,
27
+ settings: {
28
+ ...initial === void 0 ? {} : { initial },
29
+ ...initialEvents === void 0 ? {} : { initialEvents },
30
+ ...participant === void 0 ? {} : { participant }
31
+ }
32
+ }), [
54
33
  sessionId,
55
34
  api,
56
- initialState,
57
- initialIndex,
35
+ initial,
58
36
  initialEvents,
59
37
  participant
60
38
  ]);
61
- useSessionLifecycle(client, true);
62
- return createElement(Context.Provider, { value: client }, props.children);
39
+ return createElement(Context.Provider, { value: scope }, props.children);
63
40
  }
64
- function useBoundSession() {
65
- const client = useContext(Context);
66
- if (!client) throw new Error("useSession must be rendered inside its matching SessionProvider");
67
- return useSessionResult(client);
41
+ function useBoundSession(overrides = {}) {
42
+ const scope = useContext(Context);
43
+ const client = overrides.client ?? scope?.client ?? ("client" in options ? options.client : void 0);
44
+ const sessionId = overrides.sessionId ?? scope?.sessionId;
45
+ if (!client || sessionId === void 0) throw new TypeError("provide client and sessionId through SessionProvider or hook options");
46
+ const inherited = client === scope?.client && sessionId === scope?.sessionId ? scope.settings : void 0;
47
+ const { initial, participant } = overrides;
48
+ return useLiveSession({
49
+ client,
50
+ sessionId,
51
+ settings: useMemo(() => ({
52
+ ...inherited,
53
+ ...initial === void 0 ? {} : { initial },
54
+ ...participant === void 0 ? {} : { participant }
55
+ }), [
56
+ inherited,
57
+ initial,
58
+ participant
59
+ ]),
60
+ enabled: overrides.enabled ?? true,
61
+ suspend: overrides.suspend ?? false
62
+ });
68
63
  }
69
64
  return {
70
65
  SessionProvider,
71
66
  useSession: useBoundSession
72
67
  };
73
68
  }
74
- /**
75
- * Hold a stream lease while mounted and `live` — the client refcounts
76
- * leases per handle, so however many providers and hooks mount one
77
- * session, the first connects and the last release closes
78
- * (StrictMode-safe: the lease is retaken on remount). A standalone
79
- * hook without a `hydrate` value passes `live: false` and holds the
80
- * stream — connecting an unhydrated session would replay the whole
81
- * log, and the library never picks the expensive path silently.
82
- */
83
- function useSessionLifecycle(session, live) {
69
+ function useLiveSession(options) {
70
+ const { client, sessionId, settings, enabled, suspend } = options;
71
+ const browserSession = useMemo(() => {
72
+ if (typeof window === "undefined") {
73
+ if (isInitialPromise(settings.initial)) settings.initial.catch(() => {});
74
+ return;
75
+ }
76
+ return client.session(sessionId, settings);
77
+ }, [
78
+ client,
79
+ sessionId,
80
+ settings
81
+ ]);
82
+ const promise = isInitialPromise(settings.initial) ? settings.initial : void 0;
83
+ const resolved = promise && suspend ? use(promise) : void 0;
84
+ const renderInitial = promise ? resolved : settings.initial;
85
+ const renderSession = useMemo(() => {
86
+ let session;
87
+ return () => {
88
+ session ??= createRenderSession({
89
+ client,
90
+ sessionId,
91
+ settings: {
92
+ ...settings,
93
+ initial: renderInitial
94
+ }
95
+ });
96
+ return session;
97
+ };
98
+ }, [
99
+ client,
100
+ sessionId,
101
+ settings,
102
+ renderInitial
103
+ ]);
104
+ const session = browserSession ?? renderSession();
105
+ if (browserSession && promise && suspend) resolveInitialForReact({
106
+ client,
107
+ session: browserSession,
108
+ source: promise,
109
+ value: resolved
110
+ });
111
+ const serverSnapshot = useMemo(() => () => renderSession().getSnapshot(), [renderSession]);
112
+ const snapshot = useSyncExternalStore(session.subscribe, session.getSnapshot, serverSnapshot);
84
113
  useEffect(() => {
85
- if (!live) return;
86
- return session.connect();
87
- }, [session, live]);
88
- }
89
- function useSessionResult(session) {
90
- const snapshot = useSyncExternalStore(session.subscribe, session.getSnapshot, session.getSnapshot);
114
+ if (enabled) return session.connect();
115
+ }, [session, enabled]);
91
116
  return useMemo(() => {
92
117
  const { setPresence } = session;
93
118
  const { presence } = snapshot;
94
119
  return {
95
- state: snapshot.state,
96
- events: snapshot.events,
97
- index: snapshot.index,
98
- connection: snapshot.connection,
120
+ ...snapshot,
99
121
  push: session.push,
100
122
  reconnect: session.reconnect,
101
123
  loadHistory: session.loadHistory,
102
- history: snapshot.history,
103
124
  ...setPresence && presence ? {
104
125
  presence,
105
126
  setPresence
106
127
  } : {}
107
128
  };
108
- }, [snapshot, session]);
129
+ }, [session, snapshot]);
109
130
  }
110
- /**
111
- * The standalone, provider-less hook — for apps whose components reach
112
- * sessions ad hoc (a sidebar of channel sessions, a user session read
113
- * from a menu). Takes the shared client and a session id; the handle
114
- * is identity-mapped, so every hook and provider mounting the same
115
- * session shares one runtime and one leased stream. Hydration is
116
- * app-owned: pass `hydrate` when your fetch lands; until then the
117
- * stream is held, and your data layer's loading state is the pending
118
- * state.
119
- */
120
- function useSession(client, sessionId, options) {
121
- const { participant, hydrate } = options ?? {};
122
- const session = useMemo(() => client.session(sessionId, {
123
- ...hydrate === void 0 ? {} : {
124
- initialState: hydrate.state,
125
- initialIndex: hydrate.index
126
- },
127
- ...participant === void 0 ? {} : { participant }
128
- }), [
131
+ function useSession(client, sessionId, options = {}) {
132
+ const { initial, participant } = options;
133
+ return useLiveSession({
129
134
  client,
130
135
  sessionId,
131
- hydrate,
132
- participant
133
- ]);
134
- useSessionLifecycle(session, hydrate !== void 0);
135
- return useSessionResult(session);
136
+ settings: useMemo(() => ({
137
+ ...initial === void 0 ? {} : { initial },
138
+ ...participant === void 0 ? {} : { participant }
139
+ }), [initial, participant]),
140
+ enabled: options.enabled ?? true,
141
+ suspend: options.suspend ?? false
142
+ });
136
143
  }
137
144
  //#endregion
138
145
  export { createReact, useSession };