@statewalker/cellb 0.0.0-stage → 0.1.1

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 (365) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +323 -2
  3. package/dist/adapters/files-backup.d.ts +16 -0
  4. package/dist/adapters/files-backup.js +32 -0
  5. package/dist/adapters/files-backup.js.map +1 -0
  6. package/dist/adapters/files-changelog.d.ts +33 -0
  7. package/dist/adapters/files-changelog.js +96 -0
  8. package/dist/adapters/files-changelog.js.map +1 -0
  9. package/dist/adapters/files-ltx.d.ts +35 -0
  10. package/dist/adapters/files-ltx.js +9 -0
  11. package/dist/adapters/files-ltx.js.map +1 -0
  12. package/dist/adapters/ltx-vfs.d.ts +21 -0
  13. package/dist/adapters/ltx-vfs.js +286 -0
  14. package/dist/adapters/ltx-vfs.js.map +1 -0
  15. package/dist/adapters/node-sqlite.d.ts +45 -0
  16. package/dist/adapters/node-sqlite.js +254 -0
  17. package/dist/adapters/node-sqlite.js.map +1 -0
  18. package/dist/adapters/wasm-page-hook.d.ts +26 -0
  19. package/dist/adapters/wasm-page-hook.js +55 -0
  20. package/dist/adapters/wasm-page-hook.js.map +1 -0
  21. package/dist/adapters/wasm-session.d.ts +11 -0
  22. package/dist/adapters/wasm-session.js +76 -0
  23. package/dist/adapters/wasm-session.js.map +1 -0
  24. package/dist/adapters/wasm-sqlite.d.ts +89 -0
  25. package/dist/adapters/wasm-sqlite.js +258 -0
  26. package/dist/adapters/wasm-sqlite.js.map +1 -0
  27. package/dist/bindings/assets.d.ts +26 -0
  28. package/dist/bindings/assets.js +260 -0
  29. package/dist/bindings/assets.js.map +1 -0
  30. package/dist/bindings/cache.d.ts +16 -0
  31. package/dist/bindings/cache.js +34 -0
  32. package/dist/bindings/cache.js.map +1 -0
  33. package/dist/bindings/celld-kv.d.ts +26 -0
  34. package/dist/bindings/celld-kv.js +81 -0
  35. package/dist/bindings/celld-kv.js.map +1 -0
  36. package/dist/bindings/celld-queues.d.ts +56 -0
  37. package/dist/bindings/celld-queues.js +223 -0
  38. package/dist/bindings/celld-queues.js.map +1 -0
  39. package/dist/bindings/celld-workflows.d.ts +22 -0
  40. package/dist/bindings/celld-workflows.js +54 -0
  41. package/dist/bindings/celld-workflows.js.map +1 -0
  42. package/dist/bindings/cron.d.ts +27 -0
  43. package/dist/bindings/cron.js +232 -0
  44. package/dist/bindings/cron.js.map +1 -0
  45. package/dist/bindings/d1-migrations.d.ts +22 -0
  46. package/dist/bindings/d1-migrations.js +32 -0
  47. package/dist/bindings/d1-migrations.js.map +1 -0
  48. package/dist/bindings/html-rewriter.d.ts +8 -0
  49. package/dist/bindings/html-rewriter.js +75 -0
  50. package/dist/bindings/html-rewriter.js.map +1 -0
  51. package/dist/bindings/r2-celld.d.ts +3 -0
  52. package/dist/bindings/r2-celld.js +113 -0
  53. package/dist/bindings/r2-celld.js.map +1 -0
  54. package/dist/bindings/r2.d.ts +236 -0
  55. package/dist/bindings/r2.js +419 -0
  56. package/dist/bindings/r2.js.map +1 -0
  57. package/dist/bindings/worker-loader.d.ts +83 -0
  58. package/dist/bindings/worker-loader.js +183 -0
  59. package/dist/bindings/worker-loader.js.map +1 -0
  60. package/dist/bindings/workers-ai.d.ts +39 -0
  61. package/dist/bindings/workers-ai.js +175 -0
  62. package/dist/bindings/workers-ai.js.map +1 -0
  63. package/dist/browser.d.ts +7 -0
  64. package/dist/browser.js +8 -0
  65. package/dist/browser.js.map +1 -0
  66. package/dist/bucket/celld-cells.d.ts +86 -0
  67. package/dist/bucket/celld-cells.js +273 -0
  68. package/dist/bucket/celld-cells.js.map +1 -0
  69. package/dist/bucket/celld-format.d.ts +10 -0
  70. package/dist/bucket/celld-format.js +27 -0
  71. package/dist/bucket/celld-format.js.map +1 -0
  72. package/dist/bucket/idb-object-store.d.ts +9 -0
  73. package/dist/bucket/idb-object-store.js +73 -0
  74. package/dist/bucket/idb-object-store.js.map +1 -0
  75. package/dist/bucket/ltx-log.d.ts +34 -0
  76. package/dist/bucket/ltx-log.js +289 -0
  77. package/dist/bucket/ltx-log.js.map +1 -0
  78. package/dist/bucket/object-store.d.ts +51 -0
  79. package/dist/bucket/object-store.js +114 -0
  80. package/dist/bucket/object-store.js.map +1 -0
  81. package/dist/bucket/s3-object-store.d.ts +25 -0
  82. package/dist/bucket/s3-object-store.js +151 -0
  83. package/dist/bucket/s3-object-store.js.map +1 -0
  84. package/dist/bucket/sqlite-object-store.d.ts +10 -0
  85. package/dist/bucket/sqlite-object-store.js +107 -0
  86. package/dist/bucket/sqlite-object-store.js.map +1 -0
  87. package/dist/core/alarms.d.ts +27 -0
  88. package/dist/core/alarms.js +88 -0
  89. package/dist/core/alarms.js.map +1 -0
  90. package/dist/core/async-context.d.ts +34 -0
  91. package/dist/core/async-context.js +50 -0
  92. package/dist/core/async-context.js.map +1 -0
  93. package/dist/core/celld-schema.d.ts +14 -0
  94. package/dist/core/celld-schema.js +34 -0
  95. package/dist/core/celld-schema.js.map +1 -0
  96. package/dist/core/durable-object.d.ts +28 -0
  97. package/dist/core/durable-object.js +31 -0
  98. package/dist/core/durable-object.js.map +1 -0
  99. package/dist/core/gates.d.ts +33 -0
  100. package/dist/core/gates.js +89 -0
  101. package/dist/core/gates.js.map +1 -0
  102. package/dist/core/handles.d.ts +90 -0
  103. package/dist/core/handles.js +224 -0
  104. package/dist/core/handles.js.map +1 -0
  105. package/dist/core/id.d.ts +19 -0
  106. package/dist/core/id.js +64 -0
  107. package/dist/core/id.js.map +1 -0
  108. package/dist/core/namespace.d.ts +38 -0
  109. package/dist/core/namespace.js +141 -0
  110. package/dist/core/namespace.js.map +1 -0
  111. package/dist/core/object-host.d.ts +91 -0
  112. package/dist/core/object-host.js +616 -0
  113. package/dist/core/object-host.js.map +1 -0
  114. package/dist/core/request-body.d.ts +9 -0
  115. package/dist/core/request-body.js +55 -0
  116. package/dist/core/request-body.js.map +1 -0
  117. package/dist/core/runtime.d.ts +121 -0
  118. package/dist/core/runtime.js +366 -0
  119. package/dist/core/runtime.js.map +1 -0
  120. package/dist/core/shard.d.ts +7 -0
  121. package/dist/core/shard.js +10 -0
  122. package/dist/core/shard.js.map +1 -0
  123. package/dist/core/sql-split.d.ts +15 -0
  124. package/dist/core/sql-split.js +101 -0
  125. package/dist/core/sql-split.js.map +1 -0
  126. package/dist/core/sql-storage.d.ts +53 -0
  127. package/dist/core/sql-storage.js +407 -0
  128. package/dist/core/sql-storage.js.map +1 -0
  129. package/dist/core/state.d.ts +44 -0
  130. package/dist/core/state.js +93 -0
  131. package/dist/core/state.js.map +1 -0
  132. package/dist/core/storage.d.ts +103 -0
  133. package/dist/core/storage.js +351 -0
  134. package/dist/core/storage.js.map +1 -0
  135. package/dist/core/worker-rejections.d.ts +21 -0
  136. package/dist/core/worker-rejections.js +55 -0
  137. package/dist/core/worker-rejections.js.map +1 -0
  138. package/dist/index.d.ts +22 -0
  139. package/dist/index.js +23 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/isolate/browser.d.ts +16 -0
  142. package/dist/isolate/browser.js +37 -0
  143. package/dist/isolate/browser.js.map +1 -0
  144. package/dist/isolate/host.d.ts +24 -0
  145. package/dist/isolate/host.js +74 -0
  146. package/dist/isolate/host.js.map +1 -0
  147. package/dist/isolate/isolate-worker.d.ts +1 -0
  148. package/dist/isolate/isolate-worker.js +53 -0
  149. package/dist/isolate/isolate-worker.js.map +1 -0
  150. package/dist/isolate/isolate.d.ts +10 -0
  151. package/dist/isolate/isolate.js +169 -0
  152. package/dist/isolate/isolate.js.map +1 -0
  153. package/dist/isolate/node-entry.d.ts +1 -0
  154. package/dist/isolate/node-entry.js +12 -0
  155. package/dist/isolate/node-entry.js.map +1 -0
  156. package/dist/isolate/protocol.d.ts +66 -0
  157. package/dist/isolate/protocol.js +205 -0
  158. package/dist/isolate/protocol.js.map +1 -0
  159. package/dist/ltx/crc64.d.ts +6 -0
  160. package/dist/ltx/crc64.js +46 -0
  161. package/dist/ltx/crc64.js.map +1 -0
  162. package/dist/ltx/ltx-page-server.d.ts +65 -0
  163. package/dist/ltx/ltx-page-server.js +309 -0
  164. package/dist/ltx/ltx-page-server.js.map +1 -0
  165. package/dist/ltx/ltx-pages.d.ts +81 -0
  166. package/dist/ltx/ltx-pages.js +315 -0
  167. package/dist/ltx/ltx-pages.js.map +1 -0
  168. package/dist/ltx/ltx-store-worker.d.ts +1 -0
  169. package/dist/ltx/ltx-store-worker.js +38 -0
  170. package/dist/ltx/ltx-store-worker.js.map +1 -0
  171. package/dist/ltx/ltx.d.ts +45 -0
  172. package/dist/ltx/ltx.js +203 -0
  173. package/dist/ltx/ltx.js.map +1 -0
  174. package/dist/ltx/lz4.d.ts +9 -0
  175. package/dist/ltx/lz4.js +156 -0
  176. package/dist/ltx/lz4.js.map +1 -0
  177. package/dist/ltx/opfs-sync-files.d.ts +23 -0
  178. package/dist/ltx/opfs-sync-files.js +191 -0
  179. package/dist/ltx/opfs-sync-files.js.map +1 -0
  180. package/dist/ltx/xxh32.d.ts +1 -0
  181. package/dist/ltx/xxh32.js +43 -0
  182. package/dist/ltx/xxh32.js.map +1 -0
  183. package/dist/miniflare-compat/blob-service.d.ts +13 -0
  184. package/dist/miniflare-compat/blob-service.js +66 -0
  185. package/dist/miniflare-compat/blob-service.js.map +1 -0
  186. package/dist/miniflare-compat/kv-binding.d.ts +44 -0
  187. package/dist/miniflare-compat/kv-binding.js +99 -0
  188. package/dist/miniflare-compat/kv-binding.js.map +1 -0
  189. package/dist/miniflare-compat/shims/assert.d.ts +15 -0
  190. package/dist/miniflare-compat/shims/assert.js +24 -0
  191. package/dist/miniflare-compat/shims/assert.js.map +1 -0
  192. package/dist/miniflare-compat/shims/workerd-cloudflare.d.ts +7 -0
  193. package/dist/miniflare-compat/shims/workerd-cloudflare.js +6 -0
  194. package/dist/miniflare-compat/shims/workerd-cloudflare.js.map +1 -0
  195. package/dist/miniflare-compat/shims/workerd-globals.d.ts +1 -0
  196. package/dist/miniflare-compat/shims/workerd-globals.js +30 -0
  197. package/dist/miniflare-compat/shims/workerd-globals.js.map +1 -0
  198. package/dist/miniflare-compat/shims/workerd-internal.d.ts +20 -0
  199. package/dist/miniflare-compat/shims/workerd-internal.js +23 -0
  200. package/dist/miniflare-compat/shims/workerd-internal.js.map +1 -0
  201. package/dist/miniflare-compat/shims/workerd-performance.d.ts +4 -0
  202. package/dist/miniflare-compat/shims/workerd-performance.js +17 -0
  203. package/dist/miniflare-compat/shims/workerd-performance.js.map +1 -0
  204. package/dist/miniflare-compat/simulator-fetcher.d.ts +9 -0
  205. package/dist/miniflare-compat/simulator-fetcher.js +14 -0
  206. package/dist/miniflare-compat/simulator-fetcher.js.map +1 -0
  207. package/dist/miniflare-compat/vendored.d.ts +15 -0
  208. package/dist/miniflare-compat/vendored.js +27 -0
  209. package/dist/miniflare-compat/vendored.js.map +1 -0
  210. package/dist/node-compat/async_hooks.d.ts +22 -0
  211. package/dist/node-compat/async_hooks.js +176 -0
  212. package/dist/node-compat/async_hooks.js.map +1 -0
  213. package/dist/node-compat/browser-registry.d.ts +3 -0
  214. package/dist/node-compat/browser-registry.js +39 -0
  215. package/dist/node-compat/browser-registry.js.map +1 -0
  216. package/dist/node-compat/crypto.d.ts +60 -0
  217. package/dist/node-compat/crypto.js +143 -0
  218. package/dist/node-compat/crypto.js.map +1 -0
  219. package/dist/node-compat/diagnostics_channel.d.ts +27 -0
  220. package/dist/node-compat/diagnostics_channel.js +46 -0
  221. package/dist/node-compat/diagnostics_channel.js.map +1 -0
  222. package/dist/node-compat/fs.d.ts +35 -0
  223. package/dist/node-compat/fs.js +40 -0
  224. package/dist/node-compat/fs.js.map +1 -0
  225. package/dist/node-compat/globals.d.ts +3 -0
  226. package/dist/node-compat/globals.js +4 -0
  227. package/dist/node-compat/globals.js.map +1 -0
  228. package/dist/node-compat/install.d.ts +5 -0
  229. package/dist/node-compat/install.js +12 -0
  230. package/dist/node-compat/install.js.map +1 -0
  231. package/dist/node-compat/module.d.ts +12 -0
  232. package/dist/node-compat/module.js +36 -0
  233. package/dist/node-compat/module.js.map +1 -0
  234. package/dist/node-compat/modules.d.ts +8 -0
  235. package/dist/node-compat/modules.js +23 -0
  236. package/dist/node-compat/modules.js.map +1 -0
  237. package/dist/node-compat/os.d.ts +36 -0
  238. package/dist/node-compat/os.js +36 -0
  239. package/dist/node-compat/os.js.map +1 -0
  240. package/dist/node-compat/process.d.ts +14 -0
  241. package/dist/node-compat/process.js +16 -0
  242. package/dist/node-compat/process.js.map +1 -0
  243. package/dist/node-compat/shims.d.ts +4 -0
  244. package/dist/node.d.ts +5 -0
  245. package/dist/node.js +6 -0
  246. package/dist/node.js.map +1 -0
  247. package/dist/peer/celld-peer.d.ts +53 -0
  248. package/dist/peer/celld-peer.js +555 -0
  249. package/dist/peer/celld-peer.js.map +1 -0
  250. package/dist/ports.d.ts +183 -0
  251. package/dist/ports.js +10 -0
  252. package/dist/ports.js.map +1 -0
  253. package/dist/project/browser-bundle.d.ts +17 -0
  254. package/dist/project/browser-bundle.js +125 -0
  255. package/dist/project/browser-bundle.js.map +1 -0
  256. package/dist/project/browser-cookies.d.ts +12 -0
  257. package/dist/project/browser-cookies.js +90 -0
  258. package/dist/project/browser-cookies.js.map +1 -0
  259. package/dist/project/browser-host.d.ts +173 -0
  260. package/dist/project/browser-host.js +200 -0
  261. package/dist/project/browser-host.js.map +1 -0
  262. package/dist/project/browser-modules.d.ts +17 -0
  263. package/dist/project/browser-modules.js +89 -0
  264. package/dist/project/browser-modules.js.map +1 -0
  265. package/dist/project/browser-shards.d.ts +62 -0
  266. package/dist/project/browser-shards.js +210 -0
  267. package/dist/project/browser-shards.js.map +1 -0
  268. package/dist/project/browser-tabs.d.ts +103 -0
  269. package/dist/project/browser-tabs.js +418 -0
  270. package/dist/project/browser-tabs.js.map +1 -0
  271. package/dist/project/cloudflare-email.d.ts +8 -0
  272. package/dist/project/cloudflare-email.js +11 -0
  273. package/dist/project/cloudflare-email.js.map +1 -0
  274. package/dist/project/cloudflare-workers.d.ts +12 -0
  275. package/dist/project/cloudflare-workers.js +16 -0
  276. package/dist/project/cloudflare-workers.js.map +1 -0
  277. package/dist/project/host-modules.d.ts +4 -0
  278. package/dist/project/host-modules.js +40 -0
  279. package/dist/project/host-modules.js.map +1 -0
  280. package/dist/project/jsonc.d.ts +2 -0
  281. package/dist/project/jsonc.js +27 -0
  282. package/dist/project/jsonc.js.map +1 -0
  283. package/dist/project/lower-await.d.ts +2 -0
  284. package/dist/project/lower-await.js +151 -0
  285. package/dist/project/lower-await.js.map +1 -0
  286. package/dist/project/node-project.d.ts +38 -0
  287. package/dist/project/node-project.js +373 -0
  288. package/dist/project/node-project.js.map +1 -0
  289. package/dist/project/own-module.d.ts +6 -0
  290. package/dist/project/own-module.js +13 -0
  291. package/dist/project/own-module.js.map +1 -0
  292. package/dist/project/scheduler.d.ts +2 -0
  293. package/dist/project/scheduler.js +3 -0
  294. package/dist/project/scheduler.js.map +1 -0
  295. package/dist/project/site.d.ts +74 -0
  296. package/dist/project/site.js +206 -0
  297. package/dist/project/site.js.map +1 -0
  298. package/dist/project/virtual-modules.d.ts +3 -0
  299. package/dist/project/worker-scope.d.ts +27 -0
  300. package/dist/project/worker-scope.js +46 -0
  301. package/dist/project/worker-scope.js.map +1 -0
  302. package/dist/project/workerd-message-channel.d.ts +13 -0
  303. package/dist/project/workerd-message-channel.js +137 -0
  304. package/dist/project/workerd-message-channel.js.map +1 -0
  305. package/dist/project/workers-global-names.d.ts +6 -0
  306. package/dist/project/workers-global-names.js +15 -0
  307. package/dist/project/workers-global-names.js.map +1 -0
  308. package/dist/project/workers-globals-module.d.ts +8 -0
  309. package/dist/project/workers-globals-module.js +9 -0
  310. package/dist/project/workers-globals-module.js.map +1 -0
  311. package/dist/project/workers-globals.d.ts +9 -0
  312. package/dist/project/workers-globals.js +19 -0
  313. package/dist/project/workers-globals.js.map +1 -0
  314. package/dist/project/wrangler.d.ts +116 -0
  315. package/dist/project/wrangler.js +342 -0
  316. package/dist/project/wrangler.js.map +1 -0
  317. package/dist/router/cell-router.d.ts +72 -0
  318. package/dist/router/cell-router.js +247 -0
  319. package/dist/router/cell-router.js.map +1 -0
  320. package/dist/router/shard-worker.d.ts +44 -0
  321. package/dist/router/shard-worker.js +163 -0
  322. package/dist/router/shard-worker.js.map +1 -0
  323. package/dist/transport/cells-http.d.ts +25 -0
  324. package/dist/transport/cells-http.js +141 -0
  325. package/dist/transport/cells-http.js.map +1 -0
  326. package/dist/transport/cells-link.d.ts +27 -0
  327. package/dist/transport/cells-link.js +71 -0
  328. package/dist/transport/cells-link.js.map +1 -0
  329. package/dist/transport/port.d.ts +26 -0
  330. package/dist/transport/port.js +100 -0
  331. package/dist/transport/port.js.map +1 -0
  332. package/dist/v8/codec.d.ts +6 -0
  333. package/dist/v8/codec.js +9 -0
  334. package/dist/v8/codec.js.map +1 -0
  335. package/dist/v8/serialize.d.ts +10 -0
  336. package/dist/v8/serialize.js +601 -0
  337. package/dist/v8/serialize.js.map +1 -0
  338. package/dist/ws/client.d.ts +25 -0
  339. package/dist/ws/client.js +203 -0
  340. package/dist/ws/client.js.map +1 -0
  341. package/dist/ws/control.d.ts +48 -0
  342. package/dist/ws/control.js +44 -0
  343. package/dist/ws/control.js.map +1 -0
  344. package/dist/ws/frames.d.ts +38 -0
  345. package/dist/ws/frames.js +39 -0
  346. package/dist/ws/frames.js.map +1 -0
  347. package/dist/ws/server.d.ts +99 -0
  348. package/dist/ws/server.js +295 -0
  349. package/dist/ws/server.js.map +1 -0
  350. package/dist/ws/sw-connector.d.ts +10 -0
  351. package/dist/ws/sw-connector.js +25 -0
  352. package/dist/ws/sw-connector.js.map +1 -0
  353. package/dist/ws/upgrade.d.ts +28 -0
  354. package/dist/ws/upgrade.js +75 -0
  355. package/dist/ws/upgrade.js.map +1 -0
  356. package/package.json +111 -3
  357. package/vendor/celld/LICENSE +202 -0
  358. package/vendor/celld/kv.js +1131 -0
  359. package/vendor/celld/queues.js +1656 -0
  360. package/vendor/celld/workflows.js +1610 -0
  361. package/vendor/html-rewriter-wasm/html_rewriter.js +925 -0
  362. package/vendor/html-rewriter-wasm/html_rewriter_bg.wasm +0 -0
  363. package/vendor/miniflare/d1-api.js +738 -0
  364. package/vendor/miniflare/d1-database.js +6978 -0
  365. package/vendor/miniflare/kv-namespace.js +3061 -0
@@ -0,0 +1,25 @@
1
+ /** Opens the transport of a socket: resolves with the MessagePort once the server accepted (101). */
2
+ export type WebSocketConnector = (url: string, protocols: string[]) => Promise<{
3
+ port: MessagePort;
4
+ protocol?: string;
5
+ }>;
6
+ export interface CellsWebSocketOptions {
7
+ /** Liveness ping period (ms); 0 disables. The peer is declared dead after 2 missed periods. */
8
+ heartbeatMs?: number;
9
+ /**
10
+ * Interval timers for the heartbeat. On a page's main thread pass worker-timers'
11
+ * `setInterval`/`clearInterval`: background-tab throttling of main-thread timers would
12
+ * otherwise delay pings and make the server drop a live socket. Workers and Node do not
13
+ * throttle, so the built-in timers are the default.
14
+ */
15
+ timers?: {
16
+ setInterval(fn: () => void, ms: number): unknown;
17
+ clearInterval(id: unknown): void;
18
+ };
19
+ }
20
+ /**
21
+ * A WHATWG `WebSocket` class whose sockets reach a Worker through `connect`. Typed as the
22
+ * platform's `WebSocket` (the class implements that interface; its own state is private).
23
+ */
24
+ export declare function createWebSocketClass(connect: WebSocketConnector, options?: CellsWebSocketOptions): typeof WebSocket;
25
+ export type CellsWebSocketClass = ReturnType<typeof createWebSocketClass>;
@@ -0,0 +1,203 @@
1
+ import { byteSize, CellsCloseEvent, checkClose, DEFAULT_HEARTBEAT_MS, toFrameData, } from "./frames.js";
2
+ /**
3
+ * A WHATWG-WebSocket-compatible class whose transport is a MessagePort obtained
4
+ * from `connect` (page → ServiceWorker → owning shard worker, or in-process).
5
+ * Installed for user code through webrun-modules `~deps` (provided `WebSocket`),
6
+ * so the real `globalThis.WebSocket` is left untouched.
7
+ */
8
+ /** RFC 7230 token (subprotocol names). */
9
+ const TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
10
+ /**
11
+ * A WHATWG `WebSocket` class whose sockets reach a Worker through `connect`. Typed as the
12
+ * platform's `WebSocket` (the class implements that interface; its own state is private).
13
+ */
14
+ export function createWebSocketClass(connect, options = {}) {
15
+ const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
16
+ const timers = options.timers ?? {
17
+ setInterval: (fn, ms) => setInterval(fn, ms),
18
+ clearInterval: (id) => clearInterval(id),
19
+ };
20
+ class CellsWebSocket extends EventTarget {
21
+ static CONNECTING = 0;
22
+ static OPEN = 1;
23
+ static CLOSING = 2;
24
+ static CLOSED = 3;
25
+ CONNECTING = 0;
26
+ OPEN = 1;
27
+ CLOSING = 2;
28
+ CLOSED = 3;
29
+ url;
30
+ readyState = 0;
31
+ protocol = "";
32
+ extensions = "";
33
+ #buffered = 0;
34
+ /** Sizes of the messages posted and not acknowledged yet, in order (the port keeps it). */
35
+ #unacked = [];
36
+ /** Bytes sent and not yet received by the server end (it acknowledges each message). */
37
+ get bufferedAmount() {
38
+ return this.#buffered;
39
+ }
40
+ #binaryType = "blob";
41
+ get binaryType() {
42
+ return this.#binaryType;
43
+ }
44
+ /** Only "blob" and "arraybuffer" are taken; any other value is ignored (WHATWG). */
45
+ set binaryType(v) {
46
+ if (v === "blob" || v === "arraybuffer")
47
+ this.#binaryType = v;
48
+ }
49
+ onopen = null;
50
+ onmessage = null;
51
+ onclose = null;
52
+ onerror = null;
53
+ #port;
54
+ #pendingClose;
55
+ #beat;
56
+ #lastPong = 0;
57
+ #startHeartbeat() {
58
+ if (!heartbeatMs)
59
+ return;
60
+ this.#lastPong = Date.now();
61
+ this.#beat = timers.setInterval(() => {
62
+ if (this.readyState >= 3)
63
+ return this.#stopHeartbeat();
64
+ if (Date.now() - this.#lastPong > 2 * heartbeatMs)
65
+ return this.#fail(); // peer gone
66
+ this.#port?.postMessage({ t: "p", hb: heartbeatMs });
67
+ }, heartbeatMs);
68
+ this.#port?.postMessage({ t: "p", hb: heartbeatMs });
69
+ }
70
+ #stopHeartbeat() {
71
+ if (this.#beat !== undefined)
72
+ timers.clearInterval(this.#beat);
73
+ this.#beat = undefined;
74
+ }
75
+ constructor(url, protocols = []) {
76
+ super();
77
+ // WHATWG "new WebSocket(url, protocols)" validation, in spec order.
78
+ let u;
79
+ try {
80
+ u = new URL(String(url), globalThis.location?.href ?? "http://localhost/");
81
+ }
82
+ catch {
83
+ throw new DOMException(`The URL '${String(url)}' is invalid.`, "SyntaxError");
84
+ }
85
+ if (u.protocol === "http:")
86
+ u.protocol = "ws:";
87
+ if (u.protocol === "https:")
88
+ u.protocol = "wss:";
89
+ if (u.protocol !== "ws:" && u.protocol !== "wss:")
90
+ throw new DOMException(`The URL's scheme must be either 'ws' or 'wss'.`, "SyntaxError");
91
+ if (u.hash || String(url).includes("#"))
92
+ throw new DOMException("The URL contains a fragment identifier.", "SyntaxError");
93
+ const list = typeof protocols === "string" ? [protocols] : [...protocols];
94
+ const seen = new Set();
95
+ for (const p of list) {
96
+ if (!TOKEN.test(p))
97
+ throw new DOMException(`The subprotocol '${p}' is invalid.`, "SyntaxError");
98
+ if (seen.has(p.toLowerCase()))
99
+ throw new DOMException(`The subprotocol '${p}' is duplicated.`, "SyntaxError");
100
+ seen.add(p.toLowerCase());
101
+ }
102
+ this.url = u.toString();
103
+ connect(this.url, list).then(({ port, protocol }) => this.#opened(port, protocol ?? ""), () => this.#fail());
104
+ }
105
+ #emit(ev, handler) {
106
+ handler?.call(this, ev);
107
+ this.dispatchEvent(ev);
108
+ }
109
+ #opened(port, protocol) {
110
+ if (this.readyState === 3) {
111
+ port.close();
112
+ return;
113
+ }
114
+ this.#port = port;
115
+ this.protocol = protocol;
116
+ port.onmessage = (e) => this.#frame(e.data);
117
+ port.start?.();
118
+ if (this.#pendingClose) {
119
+ // close() was called while connecting
120
+ this.readyState = 2;
121
+ port.postMessage({
122
+ t: "c",
123
+ code: this.#pendingClose.code ?? 1000,
124
+ reason: this.#pendingClose.reason ?? "",
125
+ });
126
+ return;
127
+ }
128
+ this.readyState = 1;
129
+ this.#startHeartbeat();
130
+ this.#emit(new Event("open"), this.onopen);
131
+ }
132
+ #fail() {
133
+ if (this.readyState === 3)
134
+ return;
135
+ this.#stopHeartbeat();
136
+ this.#port?.close();
137
+ this.readyState = 3;
138
+ this.#emit(new Event("error"), this.onerror);
139
+ this.#emit(new CellsCloseEvent("close", { code: 1006, reason: "", wasClean: false }), this.onclose);
140
+ }
141
+ #frame(f) {
142
+ if (f.t === "q") {
143
+ this.#lastPong = Date.now();
144
+ return;
145
+ }
146
+ if (f.t === "p")
147
+ return;
148
+ if (f.t === "a") {
149
+ this.#buffered -= this.#unacked.shift() ?? 0;
150
+ return;
151
+ }
152
+ if (f.t === "m") {
153
+ if (this.readyState !== 1)
154
+ return;
155
+ let data = f.d;
156
+ if (typeof data !== "string" && this.binaryType === "blob")
157
+ data = new Blob([data]);
158
+ this.#emit(new MessageEvent("message", { data, origin: new URL(this.url).origin }), this.onmessage);
159
+ }
160
+ else {
161
+ const echo = this.readyState === 1;
162
+ if (echo)
163
+ this.#port.postMessage({ t: "c", code: f.code, reason: f.reason }); // close handshake
164
+ this.readyState = 3;
165
+ this.#stopHeartbeat();
166
+ this.#port.close();
167
+ this.#emit(new CellsCloseEvent("close", { code: f.code, reason: f.reason, wasClean: true }), this.onclose);
168
+ }
169
+ }
170
+ send(data) {
171
+ if (this.readyState === 0)
172
+ throw new DOMException("Still in CONNECTING state.", "InvalidStateError");
173
+ if (this.readyState !== 1)
174
+ return; // spec: silently discarded after closing
175
+ if (data instanceof Blob) {
176
+ this.#buffered += data.size;
177
+ void data.arrayBuffer().then((b) => this.readyState === 1 && this.#post(b));
178
+ return;
179
+ }
180
+ const d = toFrameData(data);
181
+ this.#buffered += byteSize(d);
182
+ this.#post(d);
183
+ }
184
+ #post(d) {
185
+ this.#unacked.push(byteSize(d));
186
+ this.#port.postMessage({ t: "m", d }, typeof d === "string" ? [] : [d]);
187
+ }
188
+ close(code, reason) {
189
+ checkClose(code, reason);
190
+ if (this.readyState >= 2)
191
+ return;
192
+ if (this.readyState === 0) {
193
+ this.#pendingClose = { code, reason };
194
+ this.readyState = 2;
195
+ return;
196
+ }
197
+ this.readyState = 2;
198
+ this.#port.postMessage({ t: "c", code: code ?? 1005, reason: reason ?? "" });
199
+ }
200
+ }
201
+ return CellsWebSocket;
202
+ }
203
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ws/client.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n byteSize,\n CellsCloseEvent,\n checkClose,\n DEFAULT_HEARTBEAT_MS,\n type Frame,\n toFrameData,\n} from \"./frames.js\";\n\n/** Opens the transport of a socket: resolves with the MessagePort once the server accepted (101). */\nexport type WebSocketConnector = (\n url: string,\n protocols: string[],\n) => Promise<{ port: MessagePort; protocol?: string }>;\n\ntype Handler = ((ev: any) => void) | null;\n\n/**\n * A WHATWG-WebSocket-compatible class whose transport is a MessagePort obtained\n * from `connect` (page → ServiceWorker → owning shard worker, or in-process).\n * Installed for user code through webrun-modules `~deps` (provided `WebSocket`),\n * so the real `globalThis.WebSocket` is left untouched.\n */\n/** RFC 7230 token (subprotocol names). */\nconst TOKEN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n\nexport interface CellsWebSocketOptions {\n /** Liveness ping period (ms); 0 disables. The peer is declared dead after 2 missed periods. */\n heartbeatMs?: number;\n /**\n * Interval timers for the heartbeat. On a page's main thread pass worker-timers'\n * `setInterval`/`clearInterval`: background-tab throttling of main-thread timers would\n * otherwise delay pings and make the server drop a live socket. Workers and Node do not\n * throttle, so the built-in timers are the default.\n */\n timers?: { setInterval(fn: () => void, ms: number): unknown; clearInterval(id: unknown): void };\n}\n\n/**\n * A WHATWG `WebSocket` class whose sockets reach a Worker through `connect`. Typed as the\n * platform's `WebSocket` (the class implements that interface; its own state is private).\n */\nexport function createWebSocketClass(\n connect: WebSocketConnector,\n options: CellsWebSocketOptions = {},\n): typeof WebSocket {\n const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n const timers = options.timers ?? {\n setInterval: (fn: () => void, ms: number) => setInterval(fn, ms),\n clearInterval: (id: unknown) => clearInterval(id as ReturnType<typeof setInterval>),\n };\n class CellsWebSocket extends EventTarget {\n static readonly CONNECTING = 0;\n static readonly OPEN = 1;\n static readonly CLOSING = 2;\n static readonly CLOSED = 3;\n readonly CONNECTING = 0;\n readonly OPEN = 1;\n readonly CLOSING = 2;\n readonly CLOSED = 3;\n\n readonly url: string;\n readyState = 0;\n protocol = \"\";\n extensions = \"\";\n #buffered = 0;\n /** Sizes of the messages posted and not acknowledged yet, in order (the port keeps it). */\n #unacked: number[] = [];\n /** Bytes sent and not yet received by the server end (it acknowledges each message). */\n get bufferedAmount(): number {\n return this.#buffered;\n }\n #binaryType: \"blob\" | \"arraybuffer\" = \"blob\";\n get binaryType(): \"blob\" | \"arraybuffer\" {\n return this.#binaryType;\n }\n /** Only \"blob\" and \"arraybuffer\" are taken; any other value is ignored (WHATWG). */\n set binaryType(v: \"blob\" | \"arraybuffer\") {\n if (v === \"blob\" || v === \"arraybuffer\") this.#binaryType = v;\n }\n onopen: Handler = null;\n onmessage: Handler = null;\n onclose: Handler = null;\n onerror: Handler = null;\n #port: MessagePort | undefined;\n #pendingClose: { code?: number; reason?: string } | undefined;\n #beat: unknown;\n #lastPong = 0;\n\n #startHeartbeat(): void {\n if (!heartbeatMs) return;\n this.#lastPong = Date.now();\n this.#beat = timers.setInterval(() => {\n if (this.readyState >= 3) return this.#stopHeartbeat();\n if (Date.now() - this.#lastPong > 2 * heartbeatMs) return this.#fail(); // peer gone\n this.#port?.postMessage({ t: \"p\", hb: heartbeatMs } satisfies Frame);\n }, heartbeatMs);\n this.#port?.postMessage({ t: \"p\", hb: heartbeatMs } satisfies Frame);\n }\n #stopHeartbeat(): void {\n if (this.#beat !== undefined) timers.clearInterval(this.#beat);\n this.#beat = undefined;\n }\n\n constructor(url: string | URL, protocols: string | string[] = []) {\n super();\n // WHATWG \"new WebSocket(url, protocols)\" validation, in spec order.\n let u: URL;\n try {\n u = new URL(String(url), (globalThis as any).location?.href ?? \"http://localhost/\");\n } catch {\n throw new DOMException(`The URL '${String(url)}' is invalid.`, \"SyntaxError\");\n }\n if (u.protocol === \"http:\") u.protocol = \"ws:\";\n if (u.protocol === \"https:\") u.protocol = \"wss:\";\n if (u.protocol !== \"ws:\" && u.protocol !== \"wss:\")\n throw new DOMException(`The URL's scheme must be either 'ws' or 'wss'.`, \"SyntaxError\");\n if (u.hash || String(url).includes(\"#\"))\n throw new DOMException(\"The URL contains a fragment identifier.\", \"SyntaxError\");\n const list = typeof protocols === \"string\" ? [protocols] : [...protocols];\n const seen = new Set<string>();\n for (const p of list) {\n if (!TOKEN.test(p))\n throw new DOMException(`The subprotocol '${p}' is invalid.`, \"SyntaxError\");\n if (seen.has(p.toLowerCase()))\n throw new DOMException(`The subprotocol '${p}' is duplicated.`, \"SyntaxError\");\n seen.add(p.toLowerCase());\n }\n this.url = u.toString();\n connect(this.url, list).then(\n ({ port, protocol }) => this.#opened(port, protocol ?? \"\"),\n () => this.#fail(),\n );\n }\n\n #emit(ev: Event, handler: Handler): void {\n handler?.call(this, ev);\n this.dispatchEvent(ev);\n }\n\n #opened(port: MessagePort, protocol: string): void {\n if (this.readyState === 3) {\n port.close();\n return;\n }\n this.#port = port;\n this.protocol = protocol;\n port.onmessage = (e) => this.#frame(e.data as Frame);\n port.start?.();\n if (this.#pendingClose) {\n // close() was called while connecting\n this.readyState = 2;\n port.postMessage({\n t: \"c\",\n code: this.#pendingClose.code ?? 1000,\n reason: this.#pendingClose.reason ?? \"\",\n } satisfies Frame);\n return;\n }\n this.readyState = 1;\n this.#startHeartbeat();\n this.#emit(new Event(\"open\"), this.onopen);\n }\n\n #fail(): void {\n if (this.readyState === 3) return;\n this.#stopHeartbeat();\n this.#port?.close();\n this.readyState = 3;\n this.#emit(new Event(\"error\"), this.onerror);\n this.#emit(\n new CellsCloseEvent(\"close\", { code: 1006, reason: \"\", wasClean: false }),\n this.onclose,\n );\n }\n\n #frame(f: Frame): void {\n if (f.t === \"q\") {\n this.#lastPong = Date.now();\n return;\n }\n if (f.t === \"p\") return;\n if (f.t === \"a\") {\n this.#buffered -= this.#unacked.shift() ?? 0;\n return;\n }\n if (f.t === \"m\") {\n if (this.readyState !== 1) return;\n let data: unknown = f.d;\n if (typeof data !== \"string\" && this.binaryType === \"blob\")\n data = new Blob([data as ArrayBuffer]);\n this.#emit(\n new MessageEvent(\"message\", { data, origin: new URL(this.url).origin }),\n this.onmessage,\n );\n } else {\n const echo = this.readyState === 1;\n if (echo)\n this.#port!.postMessage({ t: \"c\", code: f.code, reason: f.reason } satisfies Frame); // close handshake\n this.readyState = 3;\n this.#stopHeartbeat();\n this.#port!.close();\n this.#emit(\n new CellsCloseEvent(\"close\", { code: f.code, reason: f.reason, wasClean: true }),\n this.onclose,\n );\n }\n }\n\n send(data: string | ArrayBuffer | ArrayBufferView | Blob): void {\n if (this.readyState === 0)\n throw new DOMException(\"Still in CONNECTING state.\", \"InvalidStateError\");\n if (this.readyState !== 1) return; // spec: silently discarded after closing\n if (data instanceof Blob) {\n this.#buffered += data.size;\n void data.arrayBuffer().then((b) => this.readyState === 1 && this.#post(b));\n return;\n }\n const d = toFrameData(data);\n this.#buffered += byteSize(d);\n this.#post(d);\n }\n\n #post(d: string | ArrayBuffer): void {\n this.#unacked.push(byteSize(d));\n this.#port!.postMessage({ t: \"m\", d } satisfies Frame, typeof d === \"string\" ? [] : [d]);\n }\n\n close(code?: number, reason?: string): void {\n checkClose(code, reason);\n if (this.readyState >= 2) return;\n if (this.readyState === 0) {\n this.#pendingClose = { code, reason };\n this.readyState = 2;\n return;\n }\n this.readyState = 2;\n this.#port!.postMessage({ t: \"c\", code: code ?? 1005, reason: reason ?? \"\" } satisfies Frame);\n }\n }\n return CellsWebSocket as unknown as typeof WebSocket;\n}\n\nexport type CellsWebSocketClass = ReturnType<typeof createWebSocketClass>;\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Control channel of a shard link: a raw MessagePort next to the HTTP links,
3
+ * used for what HTTP-over-Duplex cannot carry — transferring WebSocket ports.
4
+ *
5
+ * Messages (the port rides in the payload so this works in browsers and Node):
6
+ * router → shard { t: "ws-connect", id, url, protocols, headers }
7
+ * shard → router { t: "ws-accept", id, protocol, port } (reply, or cross-shard hand-over)
8
+ * shard → router { t: "ws-refuse", id, message }
9
+ * Ids: "r:<n>" = opened by the router (a page's socket); "s<shard>:<n>" = opened by a
10
+ * Worker running in shard <shard> through a stub in another shard.
11
+ */
12
+ export interface ControlPort {
13
+ postMessage(message: unknown, transfer?: Transferable[]): void;
14
+ onmessage: ((ev: MessageEvent) => void) | null;
15
+ start?(): void;
16
+ }
17
+ export type ControlMessage = {
18
+ t: "ws-connect";
19
+ id: string;
20
+ url: string;
21
+ protocols: string[];
22
+ headers?: [string, string][];
23
+ } | {
24
+ t: "ws-accept";
25
+ id: string;
26
+ protocol?: string;
27
+ port: MessagePort;
28
+ } | {
29
+ t: "ws-refuse";
30
+ id: string;
31
+ message: string;
32
+ };
33
+ /** Shard that must receive a control message addressed by id, or undefined for the router. */
34
+ export declare function shardOfControlId(id: string): number | undefined;
35
+ /** Waits for ports by id; tolerates a port arriving before anyone waits for it. */
36
+ export declare class PortRendezvous {
37
+ #private;
38
+ deliver(id: string, value: {
39
+ port: MessagePort;
40
+ protocol?: string;
41
+ } | Error): void;
42
+ wait(id: string, timeoutMs?: number): Promise<{
43
+ port: MessagePort;
44
+ protocol?: string;
45
+ }>;
46
+ /** Fails every waiter whose id matches (e.g. its shard's owner is gone). */
47
+ failAll(match: (id: string) => boolean, error: Error): void;
48
+ }
@@ -0,0 +1,44 @@
1
+ /** Shard that must receive a control message addressed by id, or undefined for the router. */
2
+ export function shardOfControlId(id) {
3
+ const m = /^s(\d+):/.exec(id);
4
+ return m ? Number(m[1]) : undefined;
5
+ }
6
+ /** Waits for ports by id; tolerates a port arriving before anyone waits for it. */
7
+ export class PortRendezvous {
8
+ #arrived = new Map();
9
+ #waiting = new Map();
10
+ deliver(id, value) {
11
+ const w = this.#waiting.get(id);
12
+ if (!w)
13
+ return void this.#arrived.set(id, value);
14
+ this.#waiting.delete(id);
15
+ value instanceof Error ? w.reject(value) : w.resolve(value);
16
+ }
17
+ wait(id, timeoutMs = 15_000) {
18
+ const early = this.#arrived.get(id);
19
+ if (early) {
20
+ this.#arrived.delete(id);
21
+ return early instanceof Error ? Promise.reject(early) : Promise.resolve(early);
22
+ }
23
+ return new Promise((resolve, reject) => {
24
+ const t = setTimeout(() => {
25
+ this.#waiting.delete(id);
26
+ reject(new Error(`WebSocket port ${id} did not arrive within ${timeoutMs} ms`));
27
+ }, timeoutMs);
28
+ this.#waiting.set(id, {
29
+ resolve: (v) => (clearTimeout(t), resolve(v)),
30
+ reject: (e) => (clearTimeout(t), reject(e)),
31
+ });
32
+ });
33
+ }
34
+ /** Fails every waiter whose id matches (e.g. its shard's owner is gone). */
35
+ failAll(match, error) {
36
+ for (const [id, w] of [...this.#waiting]) {
37
+ if (!match(id))
38
+ continue;
39
+ this.#waiting.delete(id);
40
+ w.reject(error);
41
+ }
42
+ }
43
+ }
44
+ //# sourceMappingURL=control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/ws/control.ts"],"names":[],"mappings":"AAsBA,8FAA8F;AAC9F,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED,mFAAmF;AACnF,MAAM,OAAO,cAAc;IACzB,QAAQ,GAAG,IAAI,GAAG,EAA4D,CAAC;IAC/E,QAAQ,GAAG,IAAI,GAAG,EAGf,CAAC;IAEJ,OAAO,CAAC,EAAU,EAAE,KAAuD;QACzE,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,CAAC;YAAE,OAAO,KAAK,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,CAAC,EAAU,EAAE,SAAS,GAAG,MAAM;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACzB,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;gBACxB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACzB,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,EAAE,0BAA0B,SAAS,KAAK,CAAC,CAAC,CAAC;YAClF,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE;gBACpB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC7C,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,OAAO,CAAC,KAA8B,EAAE,KAAY;QAClD,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAAE,SAAS;YACzB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACzB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;CACF","sourcesContent":["/**\n * Control channel of a shard link: a raw MessagePort next to the HTTP links,\n * used for what HTTP-over-Duplex cannot carry — transferring WebSocket ports.\n *\n * Messages (the port rides in the payload so this works in browsers and Node):\n * router → shard { t: \"ws-connect\", id, url, protocols, headers }\n * shard → router { t: \"ws-accept\", id, protocol, port } (reply, or cross-shard hand-over)\n * shard → router { t: \"ws-refuse\", id, message }\n * Ids: \"r:<n>\" = opened by the router (a page's socket); \"s<shard>:<n>\" = opened by a\n * Worker running in shard <shard> through a stub in another shard.\n */\nexport interface ControlPort {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n onmessage: ((ev: MessageEvent) => void) | null;\n start?(): void;\n}\n\nexport type ControlMessage =\n | { t: \"ws-connect\"; id: string; url: string; protocols: string[]; headers?: [string, string][] }\n | { t: \"ws-accept\"; id: string; protocol?: string; port: MessagePort }\n | { t: \"ws-refuse\"; id: string; message: string };\n\n/** Shard that must receive a control message addressed by id, or undefined for the router. */\nexport function shardOfControlId(id: string): number | undefined {\n const m = /^s(\\d+):/.exec(id);\n return m ? Number(m[1]) : undefined;\n}\n\n/** Waits for ports by id; tolerates a port arriving before anyone waits for it. */\nexport class PortRendezvous {\n #arrived = new Map<string, { port: MessagePort; protocol?: string } | Error>();\n #waiting = new Map<\n string,\n { resolve: (v: { port: MessagePort; protocol?: string }) => void; reject: (e: Error) => void }\n >();\n\n deliver(id: string, value: { port: MessagePort; protocol?: string } | Error): void {\n const w = this.#waiting.get(id);\n if (!w) return void this.#arrived.set(id, value);\n this.#waiting.delete(id);\n value instanceof Error ? w.reject(value) : w.resolve(value);\n }\n\n wait(id: string, timeoutMs = 15_000): Promise<{ port: MessagePort; protocol?: string }> {\n const early = this.#arrived.get(id);\n if (early) {\n this.#arrived.delete(id);\n return early instanceof Error ? Promise.reject(early) : Promise.resolve(early);\n }\n return new Promise((resolve, reject) => {\n const t = setTimeout(() => {\n this.#waiting.delete(id);\n reject(new Error(`WebSocket port ${id} did not arrive within ${timeoutMs} ms`));\n }, timeoutMs);\n this.#waiting.set(id, {\n resolve: (v) => (clearTimeout(t), resolve(v)),\n reject: (e) => (clearTimeout(t), reject(e)),\n });\n });\n }\n\n /** Fails every waiter whose id matches (e.g. its shard's owner is gone). */\n failAll(match: (id: string) => boolean, error: Error): void {\n for (const [id, w] of [...this.#waiting]) {\n if (!match(id)) continue;\n this.#waiting.delete(id);\n w.reject(error);\n }\n }\n}\n"]}
@@ -0,0 +1,38 @@
1
+ /** Wire protocol of a WebSocket carried by a MessagePort (both directions). */
2
+ export type Frame = {
3
+ t: "m";
4
+ d: string | ArrayBuffer;
5
+ } | {
6
+ t: "c";
7
+ code: number;
8
+ reason: string;
9
+ } | {
10
+ t: "p";
11
+ hb: number;
12
+ } | {
13
+ t: "q";
14
+ } | {
15
+ t: "a";
16
+ };
17
+ /**
18
+ * MessagePorts never report that the other side died (no `close` event in
19
+ * browsers), so both ends run a heartbeat: the client pings every `hb` ms and
20
+ * closes with 1006 when pongs stop for 2·hb; the server closes with 1006 when
21
+ * pings stop for 3·hb. This is what makes a socket notice a closed tab.
22
+ */
23
+ export declare const DEFAULT_HEARTBEAT_MS = 10000;
24
+ /** What a message adds to the sender's `bufferedAmount` (WHATWG: UTF-8 bytes of a string). */
25
+ export declare function byteSize(d: string | ArrayBuffer): number;
26
+ export declare function toFrameData(data: unknown): string | ArrayBuffer;
27
+ export declare function checkClose(code?: number, reason?: string): void;
28
+ /** CloseEvent is not a global in every runtime (Node 22): a compatible Event subclass. */
29
+ export declare class CellsCloseEvent extends Event {
30
+ readonly code: number;
31
+ readonly reason: string;
32
+ readonly wasClean: boolean;
33
+ constructor(type: string, init?: {
34
+ code?: number;
35
+ reason?: string;
36
+ wasClean?: boolean;
37
+ });
38
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * MessagePorts never report that the other side died (no `close` event in
3
+ * browsers), so both ends run a heartbeat: the client pings every `hb` ms and
4
+ * closes with 1006 when pongs stop for 2·hb; the server closes with 1006 when
5
+ * pings stop for 3·hb. This is what makes a socket notice a closed tab.
6
+ */
7
+ export const DEFAULT_HEARTBEAT_MS = 10_000;
8
+ /** What a message adds to the sender's `bufferedAmount` (WHATWG: UTF-8 bytes of a string). */
9
+ export function byteSize(d) {
10
+ return typeof d === "string" ? new TextEncoder().encode(d).byteLength : d.byteLength;
11
+ }
12
+ export function toFrameData(data) {
13
+ if (typeof data === "string")
14
+ return data;
15
+ if (data instanceof ArrayBuffer)
16
+ return data.slice(0);
17
+ if (ArrayBuffer.isView(data))
18
+ return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
19
+ throw new TypeError("WebSocket.send: unsupported data type (string, ArrayBuffer or ArrayBufferView)");
20
+ }
21
+ export function checkClose(code, reason) {
22
+ if (code !== undefined && code !== 1000 && (code < 3000 || code > 4999))
23
+ throw new DOMException(`The close code must be either 1000, or between 3000 and 4999. ${code} is neither.`, "InvalidAccessError");
24
+ if (reason !== undefined && new TextEncoder().encode(reason).byteLength > 123)
25
+ throw new DOMException("The close reason must not be greater than 123 UTF-8 bytes.", "SyntaxError");
26
+ }
27
+ /** CloseEvent is not a global in every runtime (Node 22): a compatible Event subclass. */
28
+ export class CellsCloseEvent extends Event {
29
+ code;
30
+ reason;
31
+ wasClean;
32
+ constructor(type, init = {}) {
33
+ super(type);
34
+ this.code = init.code ?? 1005;
35
+ this.reason = init.reason ?? "";
36
+ this.wasClean = init.wasClean ?? false;
37
+ }
38
+ }
39
+ //# sourceMappingURL=frames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/ws/frames.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C,8FAA8F;AAC9F,MAAM,UAAU,QAAQ,CAAC,CAAuB;IAC9C,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,IAAI,YAAY,WAAW;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAgB,CAAC;IAC9F,MAAM,IAAI,SAAS,CACjB,gFAAgF,CACjF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,MAAe;IACvD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;QACrE,MAAM,IAAI,YAAY,CACpB,iEAAiE,IAAI,cAAc,EACnF,oBAAoB,CACrB,CAAC;IACJ,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,GAAG,GAAG;QAC3E,MAAM,IAAI,YAAY,CACpB,4DAA4D,EAC5D,aAAa,CACd,CAAC;AACN,CAAC;AAED,0FAA0F;AAC1F,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IACb,MAAM,CAAS;IACf,QAAQ,CAAU;IAC3B,YAAY,IAAY,EAAE,IAAI,GAA2D,EAAE;QACzF,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACzC,CAAC;CACF","sourcesContent":["/** Wire protocol of a WebSocket carried by a MessagePort (both directions). */\nexport type Frame =\n | { t: \"m\"; d: string | ArrayBuffer } // message\n | { t: \"c\"; code: number; reason: string } // close (initiating or echo)\n | { t: \"p\"; hb: number } // ping (client → server), carries the heartbeat period\n | { t: \"q\" } // pong (server → client)\n | { t: \"a\" }; // ack: the server end dispatched the oldest message not acknowledged yet\n\n/**\n * MessagePorts never report that the other side died (no `close` event in\n * browsers), so both ends run a heartbeat: the client pings every `hb` ms and\n * closes with 1006 when pongs stop for 2·hb; the server closes with 1006 when\n * pings stop for 3·hb. This is what makes a socket notice a closed tab.\n */\nexport const DEFAULT_HEARTBEAT_MS = 10_000;\n\n/** What a message adds to the sender's `bufferedAmount` (WHATWG: UTF-8 bytes of a string). */\nexport function byteSize(d: string | ArrayBuffer): number {\n return typeof d === \"string\" ? new TextEncoder().encode(d).byteLength : d.byteLength;\n}\n\nexport function toFrameData(data: unknown): string | ArrayBuffer {\n if (typeof data === \"string\") return data;\n if (data instanceof ArrayBuffer) return data.slice(0);\n if (ArrayBuffer.isView(data))\n return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer;\n throw new TypeError(\n \"WebSocket.send: unsupported data type (string, ArrayBuffer or ArrayBufferView)\",\n );\n}\n\nexport function checkClose(code?: number, reason?: string): void {\n if (code !== undefined && code !== 1000 && (code < 3000 || code > 4999))\n throw new DOMException(\n `The close code must be either 1000, or between 3000 and 4999. ${code} is neither.`,\n \"InvalidAccessError\",\n );\n if (reason !== undefined && new TextEncoder().encode(reason).byteLength > 123)\n throw new DOMException(\n \"The close reason must not be greater than 123 UTF-8 bytes.\",\n \"SyntaxError\",\n );\n}\n\n/** CloseEvent is not a global in every runtime (Node 22): a compatible Event subclass. */\nexport class CellsCloseEvent extends Event {\n readonly code: number;\n readonly reason: string;\n readonly wasClean: boolean;\n constructor(type: string, init: { code?: number; reason?: string; wasClean?: boolean } = {}) {\n super(type);\n this.code = init.code ?? 1005;\n this.reason = init.reason ?? \"\";\n this.wasClean = init.wasClean ?? false;\n }\n}\n"]}
@@ -0,0 +1,99 @@
1
+ declare const kPort: unique symbol;
2
+ /**
3
+ * Cloudflare Workers' server-side WebSocket (one end of a WebSocketPair):
4
+ * events are delivered only after `accept()`; `send`/`close` as in workerd.
5
+ * Hibernation (`ctx.acceptWebSocket`) is a later prototype.
6
+ */
7
+ export declare class CfWebSocket extends EventTarget {
8
+ #private;
9
+ static readonly READY_STATE_CONNECTING = 0;
10
+ static readonly READY_STATE_OPEN = 1;
11
+ static readonly READY_STATE_CLOSING = 2;
12
+ static readonly READY_STATE_CLOSED = 3;
13
+ readyState: number;
14
+ url: string | null;
15
+ protocol: string | null;
16
+ extensions: string | null;
17
+ /** How binary messages arrive; other values are ignored, as in browsers. */
18
+ get binaryType(): "blob" | "arraybuffer";
19
+ set binaryType(v: "blob" | "arraybuffer");
20
+ [kPort]: MessagePort | undefined;
21
+ constructor(port: MessagePort);
22
+ /** The other end of the WebSocketPair (the runtime checks how the server end was accepted). */
23
+ peer?: CfWebSocket;
24
+ /** Accepted through ctx.acceptWebSocket() (hibernatable) rather than accept(). */
25
+ hibernatable: boolean;
26
+ get accepted(): boolean;
27
+ accept(): void;
28
+ serializeAttachment(v: unknown): void;
29
+ deserializeAttachment(): unknown;
30
+ /**
31
+ * Closes every server end accepted in this realm with 1001 (going away). A shard worker that
32
+ * hands its cells to another tab does it before it stops, so that clients reconnect at once
33
+ * instead of noticing through the heartbeat; Worker code cannot send 1001 itself (`close`).
34
+ */
35
+ static goAway(reason?: string): void;
36
+ send(data: string | ArrayBuffer | ArrayBufferView): void;
37
+ close(code?: number, reason?: string): void;
38
+ }
39
+ /**
40
+ * The `WebSocket` global of Worker code. On workerd one class covers both ends of a
41
+ * WebSocketPair and the client sockets `new WebSocket(url)` opens, and libraries rely on that:
42
+ * the Agents SDK (partyserver) reads attachments with
43
+ * `WebSocket.prototype.deserializeAttachment.call(ws)` and compares `readyState` with
44
+ * `WebSocket.READY_STATE_OPEN`. So its prototype is the server end's (`CfWebSocket`), its
45
+ * statics are workerd's, `instanceof` holds for both kinds, and `new WebSocket(url)` still opens
46
+ * the host's client socket.
47
+ */
48
+ export declare const WorkersWebSocket: typeof CfWebSocket & {
49
+ new (url: string, protocols?: string | string[]): CfWebSocket;
50
+ readonly CONNECTING: 0;
51
+ readonly OPEN: 1;
52
+ readonly CLOSING: 2;
53
+ readonly CLOSED: 3;
54
+ };
55
+ /** `new WebSocketPair()` → `{ 0: client, 1: server }`, entangled over a MessageChannel. */
56
+ export declare class WebSocketPair {
57
+ 0: CfWebSocket;
58
+ 1: CfWebSocket;
59
+ constructor();
60
+ /** `const [client, server] = new WebSocketPair()`, as in workerd. */
61
+ [Symbol.iterator](): IterableIterator<CfWebSocket>;
62
+ }
63
+ /**
64
+ * `WebSocketPair` under the `websocket_standard_binary_type` compatibility flag: both ends
65
+ * start with `binaryType` "blob", as browsers' WebSockets do. Without it they start with
66
+ * "arraybuffer", workerd's older default.
67
+ */
68
+ export declare class StandardWebSocketPair extends WebSocketPair {
69
+ constructor();
70
+ }
71
+ /**
72
+ * Detaches the transport of the CLIENT end returned in a 101 response, so the
73
+ * runtime can hand the raw MessagePort to the real client (transferable).
74
+ */
75
+ export declare function takeClientPort(ws: CfWebSocket): MessagePort;
76
+ export declare class CellsResponse extends Response {
77
+ #private;
78
+ readonly webSocket: CfWebSocket | null;
79
+ constructor(body?: BodyInit | null, init?: ResponseInit & {
80
+ webSocket?: CfWebSocket | null;
81
+ });
82
+ get status(): number;
83
+ /**
84
+ * Every platform `Response` is a Workers `Response`, as on workerd: `Response.json()`,
85
+ * `Response.redirect()` and `Response.error()` (the platform's static helpers, which build
86
+ * platform instances, not instances of this subclass) and `fetch()` results pass
87
+ * `instanceof Response` in Worker code. Astro checks every endpoint's result that way.
88
+ */
89
+ static [Symbol.hasInstance](v: unknown): boolean;
90
+ static json(data: unknown, init?: ResponseInit): Response;
91
+ static redirect(url: string | URL, status?: number): Response;
92
+ }
93
+ /** Workers `WebSocketRequestResponsePair` (ctx.setWebSocketAutoResponse). */
94
+ export declare class WebSocketRequestResponsePair {
95
+ readonly request: string;
96
+ readonly response: string;
97
+ constructor(request: string, response: string);
98
+ }
99
+ export {};