dotdo 0.0.1 → 0.1.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 (667) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +446 -315
  3. package/cli/README.md +238 -0
  4. package/cli/agent.ts +72 -0
  5. package/cli/bin.js +44 -0
  6. package/cli/bin.ts +38 -0
  7. package/cli/build.ts +157 -0
  8. package/cli/commands/auth/login.ts +14 -0
  9. package/cli/commands/auth/logout.ts +6 -0
  10. package/cli/commands/auth/whoami.ts +16 -0
  11. package/cli/commands/deploy-multi.ts +245 -0
  12. package/cli/commands/dev/deploy.ts +100 -0
  13. package/cli/commands/dev/dev.ts +95 -0
  14. package/cli/commands/dev/logs.ts +91 -0
  15. package/cli/commands/dev-local.ts +88 -0
  16. package/cli/commands/do-ops.ts +314 -0
  17. package/cli/commands/index.ts +100 -0
  18. package/cli/commands/init.ts +247 -0
  19. package/cli/commands/introspect/emitter.ts +315 -0
  20. package/cli/commands/introspect/index.ts +193 -0
  21. package/cli/commands/link.ts +598 -0
  22. package/cli/commands/snippets.ts +415 -0
  23. package/cli/commands/tunnel.ts +239 -0
  24. package/cli/device-auth.ts +289 -0
  25. package/cli/fallback.ts +12 -0
  26. package/cli/index.ts +121 -0
  27. package/cli/main.ts +246 -0
  28. package/cli/mcp-stdio.ts +790 -0
  29. package/cli/package.json +62 -0
  30. package/cli/runtime/do-registry.ts +193 -0
  31. package/cli/runtime/embedded-db.ts +344 -0
  32. package/cli/runtime/index.ts +9 -0
  33. package/cli/runtime/miniflare-adapter.ts +162 -0
  34. package/cli/sandbox.ts +82 -0
  35. package/cli/src/args.ts +174 -0
  36. package/cli/src/auth.ts +55 -0
  37. package/cli/src/commands/call.ts +84 -0
  38. package/cli/src/commands/charge.ts +96 -0
  39. package/cli/src/commands/config.ts +115 -0
  40. package/cli/src/commands/email.ts +112 -0
  41. package/cli/src/commands/llm.ts +115 -0
  42. package/cli/src/commands/queue.ts +134 -0
  43. package/cli/src/commands/text.ts +86 -0
  44. package/cli/src/config.ts +185 -0
  45. package/cli/src/output.ts +246 -0
  46. package/cli/src/rpc.ts +192 -0
  47. package/cli/utils/config.ts +282 -0
  48. package/cli/utils/detect.ts +73 -0
  49. package/cli/utils/index.ts +15 -0
  50. package/cli/utils/logger.ts +232 -0
  51. package/dist/ai/index.js +19 -0
  52. package/dist/ai/index.js.map +1 -0
  53. package/dist/ai/template-literals.js +852 -0
  54. package/dist/ai/template-literals.js.map +1 -0
  55. package/dist/api/middleware/auth-federation.js +573 -0
  56. package/dist/api/middleware/auth-federation.js.map +1 -0
  57. package/dist/api/middleware/auth.js +545 -0
  58. package/dist/api/middleware/auth.js.map +1 -0
  59. package/dist/db/actions.js +212 -0
  60. package/dist/db/actions.js.map +1 -0
  61. package/dist/db/auth.js +506 -0
  62. package/dist/db/auth.js.map +1 -0
  63. package/dist/db/branches.js +65 -0
  64. package/dist/db/branches.js.map +1 -0
  65. package/dist/db/clickhouse.js +1074 -0
  66. package/dist/db/clickhouse.js.map +1 -0
  67. package/dist/db/dlq.js +39 -0
  68. package/dist/db/dlq.js.map +1 -0
  69. package/dist/db/events.js +28 -0
  70. package/dist/db/events.js.map +1 -0
  71. package/dist/db/exec.js +64 -0
  72. package/dist/db/exec.js.map +1 -0
  73. package/dist/db/files.js +85 -0
  74. package/dist/db/files.js.map +1 -0
  75. package/dist/db/flags.js +24 -0
  76. package/dist/db/flags.js.map +1 -0
  77. package/dist/db/git.js +116 -0
  78. package/dist/db/git.js.map +1 -0
  79. package/dist/db/iceberg/inverted-index.js +862 -0
  80. package/dist/db/iceberg/inverted-index.js.map +1 -0
  81. package/dist/db/iceberg/puffin.js +878 -0
  82. package/dist/db/iceberg/puffin.js.map +1 -0
  83. package/dist/db/iceberg/search-manifest.js +422 -0
  84. package/dist/db/iceberg/search-manifest.js.map +1 -0
  85. package/dist/db/iceberg/types.js +8 -0
  86. package/dist/db/iceberg/types.js.map +1 -0
  87. package/dist/db/index.js +121 -0
  88. package/dist/db/index.js.map +1 -0
  89. package/dist/db/integrations.js +368 -0
  90. package/dist/db/integrations.js.map +1 -0
  91. package/dist/db/json-indexes.js +332 -0
  92. package/dist/db/json-indexes.js.map +1 -0
  93. package/dist/db/linked-accounts.js +287 -0
  94. package/dist/db/linked-accounts.js.map +1 -0
  95. package/dist/db/nouns.js +183 -0
  96. package/dist/db/nouns.js.map +1 -0
  97. package/dist/db/objects.js +170 -0
  98. package/dist/db/objects.js.map +1 -0
  99. package/dist/db/primitives/dag-scheduler/index.js +869 -0
  100. package/dist/db/primitives/dag-scheduler/index.js.map +1 -0
  101. package/dist/db/primitives/exactly-once-context.js +237 -0
  102. package/dist/db/primitives/exactly-once-context.js.map +1 -0
  103. package/dist/db/primitives/index.js +62 -0
  104. package/dist/db/primitives/index.js.map +1 -0
  105. package/dist/db/primitives/keyed-router.js +145 -0
  106. package/dist/db/primitives/keyed-router.js.map +1 -0
  107. package/dist/db/primitives/observability.js +162 -0
  108. package/dist/db/primitives/observability.js.map +1 -0
  109. package/dist/db/primitives/schema-evolution.js +643 -0
  110. package/dist/db/primitives/schema-evolution.js.map +1 -0
  111. package/dist/db/primitives/stateful-operator/index.js +770 -0
  112. package/dist/db/primitives/stateful-operator/index.js.map +1 -0
  113. package/dist/db/primitives/temporal-store.js +306 -0
  114. package/dist/db/primitives/temporal-store.js.map +1 -0
  115. package/dist/db/primitives/typed-column-store.js +1229 -0
  116. package/dist/db/primitives/typed-column-store.js.map +1 -0
  117. package/dist/db/primitives/utils/duration.js +162 -0
  118. package/dist/db/primitives/utils/duration.js.map +1 -0
  119. package/dist/db/primitives/utils/murmur3.js +116 -0
  120. package/dist/db/primitives/utils/murmur3.js.map +1 -0
  121. package/dist/db/primitives/watermark-service.js +136 -0
  122. package/dist/db/primitives/watermark-service.js.map +1 -0
  123. package/dist/db/primitives/window-manager.js +764 -0
  124. package/dist/db/primitives/window-manager.js.map +1 -0
  125. package/dist/db/relationships.js +66 -0
  126. package/dist/db/relationships.js.map +1 -0
  127. package/dist/db/schema-minimal.js +61 -0
  128. package/dist/db/schema-minimal.js.map +1 -0
  129. package/dist/db/search.js +28 -0
  130. package/dist/db/search.js.map +1 -0
  131. package/dist/db/stores.js +1665 -0
  132. package/dist/db/stores.js.map +1 -0
  133. package/dist/db/things.js +297 -0
  134. package/dist/db/things.js.map +1 -0
  135. package/dist/db/vault.js +171 -0
  136. package/dist/db/vault.js.map +1 -0
  137. package/dist/db/verbs.js +102 -0
  138. package/dist/db/verbs.js.map +1 -0
  139. package/dist/do/base.js +48 -0
  140. package/dist/do/base.js.map +1 -0
  141. package/dist/do/tiny.js +31 -0
  142. package/dist/do/tiny.js.map +1 -0
  143. package/dist/lib/DOAuth.js +261 -0
  144. package/dist/lib/DOAuth.js.map +1 -0
  145. package/dist/lib/DODispatcher.js +72 -0
  146. package/dist/lib/DODispatcher.js.map +1 -0
  147. package/dist/lib/Modifier.js +189 -0
  148. package/dist/lib/Modifier.js.map +1 -0
  149. package/dist/lib/StateStorage.js +403 -0
  150. package/dist/lib/StateStorage.js.map +1 -0
  151. package/dist/lib/TypeRegistry.js +122 -0
  152. package/dist/lib/TypeRegistry.js.map +1 -0
  153. package/dist/lib/ai/gateway.js +247 -0
  154. package/dist/lib/ai/gateway.js.map +1 -0
  155. package/dist/lib/ai/tool-loop-agent.js +591 -0
  156. package/dist/lib/ai/tool-loop-agent.js.map +1 -0
  157. package/dist/lib/auto-wiring.js +439 -0
  158. package/dist/lib/auto-wiring.js.map +1 -0
  159. package/dist/lib/browse/browserbase.js +163 -0
  160. package/dist/lib/browse/browserbase.js.map +1 -0
  161. package/dist/lib/browse/cloudflare.js +144 -0
  162. package/dist/lib/browse/cloudflare.js.map +1 -0
  163. package/dist/lib/browse/index.js +62 -0
  164. package/dist/lib/browse/index.js.map +1 -0
  165. package/dist/lib/browse/types.js +13 -0
  166. package/dist/lib/browse/types.js.map +1 -0
  167. package/dist/lib/cache/index.js +37 -0
  168. package/dist/lib/cache/index.js.map +1 -0
  169. package/dist/lib/cache/visibility.js +638 -0
  170. package/dist/lib/cache/visibility.js.map +1 -0
  171. package/dist/lib/capabilities.js +268 -0
  172. package/dist/lib/capabilities.js.map +1 -0
  173. package/dist/lib/channels/base.js +106 -0
  174. package/dist/lib/channels/base.js.map +1 -0
  175. package/dist/lib/channels/discord.js +94 -0
  176. package/dist/lib/channels/discord.js.map +1 -0
  177. package/dist/lib/channels/email.js +204 -0
  178. package/dist/lib/channels/email.js.map +1 -0
  179. package/dist/lib/channels/index.js +90 -0
  180. package/dist/lib/channels/index.js.map +1 -0
  181. package/dist/lib/channels/mdxui-chat.js +95 -0
  182. package/dist/lib/channels/mdxui-chat.js.map +1 -0
  183. package/dist/lib/channels/slack-blockkit.js +121 -0
  184. package/dist/lib/channels/slack-blockkit.js.map +1 -0
  185. package/dist/lib/channels/types.js +7 -0
  186. package/dist/lib/channels/types.js.map +1 -0
  187. package/dist/lib/cloudflare/ai.js +654 -0
  188. package/dist/lib/cloudflare/ai.js.map +1 -0
  189. package/dist/lib/cloudflare/index.js +88 -0
  190. package/dist/lib/cloudflare/index.js.map +1 -0
  191. package/dist/lib/cloudflare/kv.js +342 -0
  192. package/dist/lib/cloudflare/kv.js.map +1 -0
  193. package/dist/lib/cloudflare/queues.js +434 -0
  194. package/dist/lib/cloudflare/queues.js.map +1 -0
  195. package/dist/lib/cloudflare/r2.js +604 -0
  196. package/dist/lib/cloudflare/r2.js.map +1 -0
  197. package/dist/lib/cloudflare/vectorize.js +494 -0
  198. package/dist/lib/cloudflare/vectorize.js.map +1 -0
  199. package/dist/lib/cloudflare/workflows.js +569 -0
  200. package/dist/lib/cloudflare/workflows.js.map +1 -0
  201. package/dist/lib/colo/caching.js +196 -0
  202. package/dist/lib/colo/caching.js.map +1 -0
  203. package/dist/lib/colo/detection.js +194 -0
  204. package/dist/lib/colo/detection.js.map +1 -0
  205. package/dist/lib/colo/external-data.js +219 -0
  206. package/dist/lib/colo/external-data.js.map +1 -0
  207. package/dist/lib/colo/globe-data.js +179 -0
  208. package/dist/lib/colo/globe-data.js.map +1 -0
  209. package/dist/lib/colo/index.js +16 -0
  210. package/dist/lib/colo/index.js.map +1 -0
  211. package/dist/lib/decorators.js +37 -0
  212. package/dist/lib/decorators.js.map +1 -0
  213. package/dist/lib/discovery.js +81 -0
  214. package/dist/lib/discovery.js.map +1 -0
  215. package/dist/lib/executors/AgenticFunctionExecutor.js +619 -0
  216. package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -0
  217. package/dist/lib/executors/BaseFunctionExecutor.js +328 -0
  218. package/dist/lib/executors/BaseFunctionExecutor.js.map +1 -0
  219. package/dist/lib/executors/CascadeExecutor.js +418 -0
  220. package/dist/lib/executors/CascadeExecutor.js.map +1 -0
  221. package/dist/lib/executors/CodeFunctionExecutor.js +904 -0
  222. package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -0
  223. package/dist/lib/executors/GenerativeFunctionExecutor.js +904 -0
  224. package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -0
  225. package/dist/lib/executors/HumanFunctionExecutor.js +884 -0
  226. package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -0
  227. package/dist/lib/executors/ParallelStepExecutor.js +308 -0
  228. package/dist/lib/executors/ParallelStepExecutor.js.map +1 -0
  229. package/dist/lib/executors/types.js +12 -0
  230. package/dist/lib/executors/types.js.map +1 -0
  231. package/dist/lib/experiments.js +89 -0
  232. package/dist/lib/experiments.js.map +1 -0
  233. package/dist/lib/flags/store.js +262 -0
  234. package/dist/lib/flags/store.js.map +1 -0
  235. package/dist/lib/functions/FunctionComposition.js +467 -0
  236. package/dist/lib/functions/FunctionComposition.js.map +1 -0
  237. package/dist/lib/functions/FunctionMiddleware.js +457 -0
  238. package/dist/lib/functions/FunctionMiddleware.js.map +1 -0
  239. package/dist/lib/functions/FunctionRegistry.js +426 -0
  240. package/dist/lib/functions/FunctionRegistry.js.map +1 -0
  241. package/dist/lib/functions/createFunction.js +1048 -0
  242. package/dist/lib/functions/createFunction.js.map +1 -0
  243. package/dist/lib/humans/index.js +68 -0
  244. package/dist/lib/humans/index.js.map +1 -0
  245. package/dist/lib/humans/templates.js +117 -0
  246. package/dist/lib/humans/templates.js.map +1 -0
  247. package/dist/lib/identity.js +98 -0
  248. package/dist/lib/identity.js.map +1 -0
  249. package/dist/lib/index.js +9 -0
  250. package/dist/lib/index.js.map +1 -0
  251. package/dist/lib/logging/error-logger.js +163 -0
  252. package/dist/lib/logging/error-logger.js.map +1 -0
  253. package/dist/lib/logging/index.js +160 -0
  254. package/dist/lib/logging/index.js.map +1 -0
  255. package/dist/lib/mixins/bash.js +753 -0
  256. package/dist/lib/mixins/bash.js.map +1 -0
  257. package/dist/lib/mixins/fs.js +648 -0
  258. package/dist/lib/mixins/fs.js.map +1 -0
  259. package/dist/lib/mixins/git.js +1006 -0
  260. package/dist/lib/mixins/git.js.map +1 -0
  261. package/dist/lib/mixins/npm.js +662 -0
  262. package/dist/lib/mixins/npm.js.map +1 -0
  263. package/dist/lib/noun-id.js +278 -0
  264. package/dist/lib/noun-id.js.map +1 -0
  265. package/dist/lib/rate-limit/sliding-window.js +148 -0
  266. package/dist/lib/rate-limit/sliding-window.js.map +1 -0
  267. package/dist/lib/rate-limit.js +110 -0
  268. package/dist/lib/rate-limit.js.map +1 -0
  269. package/dist/lib/rpc/bindings.js +548 -0
  270. package/dist/lib/rpc/bindings.js.map +1 -0
  271. package/dist/lib/rpc/index.js +64 -0
  272. package/dist/lib/rpc/index.js.map +1 -0
  273. package/dist/lib/safe-stringify.js +223 -0
  274. package/dist/lib/safe-stringify.js.map +1 -0
  275. package/dist/lib/sandbox/miniflare-sandbox.js +1007 -0
  276. package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -0
  277. package/dist/lib/sqids.js +110 -0
  278. package/dist/lib/sqids.js.map +1 -0
  279. package/dist/lib/sql/adapters/index.js +10 -0
  280. package/dist/lib/sql/adapters/index.js.map +1 -0
  281. package/dist/lib/sql/adapters/node-sql-parser.js +552 -0
  282. package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -0
  283. package/dist/lib/sql/adapters/pgsql-parser.js +1190 -0
  284. package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -0
  285. package/dist/lib/sql/index.js +277 -0
  286. package/dist/lib/sql/index.js.map +1 -0
  287. package/dist/lib/sql/types.js +56 -0
  288. package/dist/lib/sql/types.js.map +1 -0
  289. package/dist/lib/type-classifier.js +126 -0
  290. package/dist/lib/type-classifier.js.map +1 -0
  291. package/dist/lib/utils/html.js +47 -0
  292. package/dist/lib/utils/html.js.map +1 -0
  293. package/dist/lib/validation.js +48 -0
  294. package/dist/lib/validation.js.map +1 -0
  295. package/dist/lib/vault/store.js +411 -0
  296. package/dist/lib/vault/store.js.map +1 -0
  297. package/dist/metrics/hunch.js +739 -0
  298. package/dist/metrics/hunch.js.map +1 -0
  299. package/dist/objects/API.js +302 -0
  300. package/dist/objects/API.js.map +1 -0
  301. package/dist/objects/Agent.js +179 -0
  302. package/dist/objects/Agent.js.map +1 -0
  303. package/dist/objects/AgenticFunctionExecutor.js +8 -0
  304. package/dist/objects/AgenticFunctionExecutor.js.map +1 -0
  305. package/dist/objects/App.js +83 -0
  306. package/dist/objects/App.js.map +1 -0
  307. package/dist/objects/Browser.js +884 -0
  308. package/dist/objects/Browser.js.map +1 -0
  309. package/dist/objects/Business.js +107 -0
  310. package/dist/objects/Business.js.map +1 -0
  311. package/dist/objects/CLI.js +221 -0
  312. package/dist/objects/CLI.js.map +1 -0
  313. package/dist/objects/CodeFunctionExecutor.js +8 -0
  314. package/dist/objects/CodeFunctionExecutor.js.map +1 -0
  315. package/dist/objects/Collection.js +161 -0
  316. package/dist/objects/Collection.js.map +1 -0
  317. package/dist/objects/DO.js +41 -0
  318. package/dist/objects/DO.js.map +1 -0
  319. package/dist/objects/DOBase.js +2309 -0
  320. package/dist/objects/DOBase.js.map +1 -0
  321. package/dist/objects/DOCache.js +153 -0
  322. package/dist/objects/DOCache.js.map +1 -0
  323. package/dist/objects/DOFull.js +1676 -0
  324. package/dist/objects/DOFull.js.map +1 -0
  325. package/dist/objects/DOTiny.js +207 -0
  326. package/dist/objects/DOTiny.js.map +1 -0
  327. package/dist/objects/Directory.js +199 -0
  328. package/dist/objects/Directory.js.map +1 -0
  329. package/dist/objects/Entity.js +413 -0
  330. package/dist/objects/Entity.js.map +1 -0
  331. package/dist/objects/Function.js +116 -0
  332. package/dist/objects/Function.js.map +1 -0
  333. package/dist/objects/Human.js +231 -0
  334. package/dist/objects/Human.js.map +1 -0
  335. package/dist/objects/HumanFunctionExecutor.js +8 -0
  336. package/dist/objects/HumanFunctionExecutor.js.map +1 -0
  337. package/dist/objects/IcebergMetadataDO.js +938 -0
  338. package/dist/objects/IcebergMetadataDO.js.map +1 -0
  339. package/dist/objects/IntegrationsDO.js +1174 -0
  340. package/dist/objects/IntegrationsDO.js.map +1 -0
  341. package/dist/objects/ObservabilityBroadcaster.js +149 -0
  342. package/dist/objects/ObservabilityBroadcaster.js.map +1 -0
  343. package/dist/objects/Package.js +154 -0
  344. package/dist/objects/Package.js.map +1 -0
  345. package/dist/objects/Product.js +193 -0
  346. package/dist/objects/Product.js.map +1 -0
  347. package/dist/objects/SDK.js +152 -0
  348. package/dist/objects/SDK.js.map +1 -0
  349. package/dist/objects/SaaS.js +235 -0
  350. package/dist/objects/SaaS.js.map +1 -0
  351. package/dist/objects/SandboxDO.js +759 -0
  352. package/dist/objects/SandboxDO.js.map +1 -0
  353. package/dist/objects/Service.js +337 -0
  354. package/dist/objects/Service.js.map +1 -0
  355. package/dist/objects/Site.js +80 -0
  356. package/dist/objects/Site.js.map +1 -0
  357. package/dist/objects/Startup.js +479 -0
  358. package/dist/objects/Startup.js.map +1 -0
  359. package/dist/objects/ThingsDO.js +170 -0
  360. package/dist/objects/ThingsDO.js.map +1 -0
  361. package/dist/objects/VectorShardDO.js +650 -0
  362. package/dist/objects/VectorShardDO.js.map +1 -0
  363. package/dist/objects/Worker.js +144 -0
  364. package/dist/objects/Worker.js.map +1 -0
  365. package/dist/objects/Workflow.js +196 -0
  366. package/dist/objects/Workflow.js.map +1 -0
  367. package/dist/objects/WorkflowFactory.js +313 -0
  368. package/dist/objects/WorkflowFactory.js.map +1 -0
  369. package/dist/objects/WorkflowRuntime.js +863 -0
  370. package/dist/objects/WorkflowRuntime.js.map +1 -0
  371. package/dist/objects/circuit-breaker-bulkhead.js +178 -0
  372. package/dist/objects/circuit-breaker-bulkhead.js.map +1 -0
  373. package/dist/objects/createFunction.js +934 -0
  374. package/dist/objects/createFunction.js.map +1 -0
  375. package/dist/objects/index.js +80 -0
  376. package/dist/objects/index.js.map +1 -0
  377. package/dist/objects/lifecycle/Branch.js +275 -0
  378. package/dist/objects/lifecycle/Branch.js.map +1 -0
  379. package/dist/objects/lifecycle/Clone.js +1499 -0
  380. package/dist/objects/lifecycle/Clone.js.map +1 -0
  381. package/dist/objects/lifecycle/Compact.js +237 -0
  382. package/dist/objects/lifecycle/Compact.js.map +1 -0
  383. package/dist/objects/lifecycle/Promote.js +476 -0
  384. package/dist/objects/lifecycle/Promote.js.map +1 -0
  385. package/dist/objects/lifecycle/Shard.js +560 -0
  386. package/dist/objects/lifecycle/Shard.js.map +1 -0
  387. package/dist/objects/lifecycle/index.js +15 -0
  388. package/dist/objects/lifecycle/index.js.map +1 -0
  389. package/dist/objects/lifecycle/types.js +33 -0
  390. package/dist/objects/lifecycle/types.js.map +1 -0
  391. package/dist/objects/mixins/infrastructure.js +171 -0
  392. package/dist/objects/mixins/infrastructure.js.map +1 -0
  393. package/dist/objects/modules/StoresModule.js +153 -0
  394. package/dist/objects/modules/StoresModule.js.map +1 -0
  395. package/dist/objects/persistence/checkpoint-manager.js +606 -0
  396. package/dist/objects/persistence/checkpoint-manager.js.map +1 -0
  397. package/dist/objects/persistence/index.js +72 -0
  398. package/dist/objects/persistence/index.js.map +1 -0
  399. package/dist/objects/persistence/migration-runner.js +562 -0
  400. package/dist/objects/persistence/migration-runner.js.map +1 -0
  401. package/dist/objects/persistence/replication-manager.js +501 -0
  402. package/dist/objects/persistence/replication-manager.js.map +1 -0
  403. package/dist/objects/persistence/tiered-storage-manager.js +595 -0
  404. package/dist/objects/persistence/tiered-storage-manager.js.map +1 -0
  405. package/dist/objects/persistence/types.js +14 -0
  406. package/dist/objects/persistence/types.js.map +1 -0
  407. package/dist/objects/persistence/wal-manager.js +653 -0
  408. package/dist/objects/persistence/wal-manager.js.map +1 -0
  409. package/dist/objects/presets/index.js +20 -0
  410. package/dist/objects/presets/index.js.map +1 -0
  411. package/dist/objects/presets/primitives.js +188 -0
  412. package/dist/objects/presets/primitives.js.map +1 -0
  413. package/dist/objects/primitives/alarm-adapter.js +141 -0
  414. package/dist/objects/primitives/alarm-adapter.js.map +1 -0
  415. package/dist/objects/primitives/index.js +337 -0
  416. package/dist/objects/primitives/index.js.map +1 -0
  417. package/dist/objects/primitives/storage-adapter.js +182 -0
  418. package/dist/objects/primitives/storage-adapter.js.map +1 -0
  419. package/dist/objects/primitives/with-primitives.js +102 -0
  420. package/dist/objects/primitives/with-primitives.js.map +1 -0
  421. package/dist/objects/services/StoreManager.js +227 -0
  422. package/dist/objects/services/StoreManager.js.map +1 -0
  423. package/dist/objects/services/index.js +13 -0
  424. package/dist/objects/services/index.js.map +1 -0
  425. package/dist/objects/transport/auth-layer.js +1451 -0
  426. package/dist/objects/transport/auth-layer.js.map +1 -0
  427. package/dist/objects/transport/capnweb-target.js +355 -0
  428. package/dist/objects/transport/capnweb-target.js.map +1 -0
  429. package/dist/objects/transport/chain.js +441 -0
  430. package/dist/objects/transport/chain.js.map +1 -0
  431. package/dist/objects/transport/handler.js +58 -0
  432. package/dist/objects/transport/handler.js.map +1 -0
  433. package/dist/objects/transport/index.js +53 -0
  434. package/dist/objects/transport/index.js.map +1 -0
  435. package/dist/objects/transport/mcp-server.js +691 -0
  436. package/dist/objects/transport/mcp-server.js.map +1 -0
  437. package/dist/objects/transport/rest-autowire.js +1508 -0
  438. package/dist/objects/transport/rest-autowire.js.map +1 -0
  439. package/dist/objects/transport/rest-router.js +440 -0
  440. package/dist/objects/transport/rest-router.js.map +1 -0
  441. package/dist/objects/transport/rpc-server.js +1539 -0
  442. package/dist/objects/transport/rpc-server.js.map +1 -0
  443. package/dist/objects/transport/shared.js +576 -0
  444. package/dist/objects/transport/shared.js.map +1 -0
  445. package/dist/objects/transport/sync-engine.js +291 -0
  446. package/dist/objects/transport/sync-engine.js.map +1 -0
  447. package/dist/objects/transport/types.js +8 -0
  448. package/dist/objects/transport/types.js.map +1 -0
  449. package/dist/sandbox/index.js +258 -0
  450. package/dist/sandbox/index.js.map +1 -0
  451. package/dist/snippets/artifacts-config.js +241 -0
  452. package/dist/snippets/artifacts-config.js.map +1 -0
  453. package/dist/snippets/artifacts-ingest.js +832 -0
  454. package/dist/snippets/artifacts-ingest.js.map +1 -0
  455. package/dist/snippets/artifacts-serve.js +1035 -0
  456. package/dist/snippets/artifacts-serve.js.map +1 -0
  457. package/dist/snippets/artifacts-types.js +161 -0
  458. package/dist/snippets/artifacts-types.js.map +1 -0
  459. package/dist/snippets/cache-probe.js +376 -0
  460. package/dist/snippets/cache-probe.js.map +1 -0
  461. package/dist/snippets/cache.js +10 -0
  462. package/dist/snippets/cache.js.map +1 -0
  463. package/dist/snippets/events.js +469 -0
  464. package/dist/snippets/events.js.map +1 -0
  465. package/dist/snippets/index.js +7 -0
  466. package/dist/snippets/index.js.map +1 -0
  467. package/dist/snippets/proxy.js +495 -0
  468. package/dist/snippets/proxy.js.map +1 -0
  469. package/dist/snippets/search.js +1759 -0
  470. package/dist/snippets/search.js.map +1 -0
  471. package/dist/streams/index.js +30 -0
  472. package/dist/streams/index.js.map +1 -0
  473. package/dist/streams/observability.js +68 -0
  474. package/dist/streams/observability.js.map +1 -0
  475. package/dist/types/AI.js +92 -0
  476. package/dist/types/AI.js.map +1 -0
  477. package/dist/types/AIFunction.js +171 -0
  478. package/dist/types/AIFunction.js.map +1 -0
  479. package/dist/types/BrowseVerb.js +89 -0
  480. package/dist/types/BrowseVerb.js.map +1 -0
  481. package/dist/types/Browser.js +31 -0
  482. package/dist/types/Browser.js.map +1 -0
  483. package/dist/types/Chaos.js +15 -0
  484. package/dist/types/Chaos.js.map +1 -0
  485. package/dist/types/CloudflareBindings.js +109 -0
  486. package/dist/types/CloudflareBindings.js.map +1 -0
  487. package/dist/types/Collection.js +50 -0
  488. package/dist/types/Collection.js.map +1 -0
  489. package/dist/types/DO.js +2 -0
  490. package/dist/types/DO.js.map +1 -0
  491. package/dist/types/DOLocation.js +63 -0
  492. package/dist/types/DOLocation.js.map +1 -0
  493. package/dist/types/EventHandler.js +57 -0
  494. package/dist/types/EventHandler.js.map +1 -0
  495. package/dist/types/Experiment.js +33 -0
  496. package/dist/types/Experiment.js.map +1 -0
  497. package/dist/types/Flag.js +57 -0
  498. package/dist/types/Flag.js.map +1 -0
  499. package/dist/types/Lifecycle.js +13 -0
  500. package/dist/types/Lifecycle.js.map +1 -0
  501. package/dist/types/Location.js +169 -0
  502. package/dist/types/Location.js.map +1 -0
  503. package/dist/types/Noun.js +66 -0
  504. package/dist/types/Noun.js.map +1 -0
  505. package/dist/types/SessionEvent.js +194 -0
  506. package/dist/types/SessionEvent.js.map +1 -0
  507. package/dist/types/Thing.js +55 -0
  508. package/dist/types/Thing.js.map +1 -0
  509. package/dist/types/ThingDO.js +153 -0
  510. package/dist/types/ThingDO.js.map +1 -0
  511. package/dist/types/Things.js +2 -0
  512. package/dist/types/Things.js.map +1 -0
  513. package/dist/types/Verb.js +119 -0
  514. package/dist/types/Verb.js.map +1 -0
  515. package/dist/types/WorkflowContext.js +70 -0
  516. package/dist/types/WorkflowContext.js.map +1 -0
  517. package/dist/types/analytics-api.js +13 -0
  518. package/dist/types/analytics-api.js.map +1 -0
  519. package/dist/types/capabilities.js +135 -0
  520. package/dist/types/capabilities.js.map +1 -0
  521. package/dist/types/drizzle.js +12 -0
  522. package/dist/types/drizzle.js.map +1 -0
  523. package/dist/types/event.js +201 -0
  524. package/dist/types/event.js.map +1 -0
  525. package/dist/types/fn.js +12 -0
  526. package/dist/types/fn.js.map +1 -0
  527. package/dist/types/iceberg.js +48 -0
  528. package/dist/types/iceberg.js.map +1 -0
  529. package/dist/types/ids.js +170 -0
  530. package/dist/types/ids.js.map +1 -0
  531. package/dist/types/index.js +41 -0
  532. package/dist/types/index.js.map +1 -0
  533. package/dist/types/introspect.js +54 -0
  534. package/dist/types/introspect.js.map +1 -0
  535. package/dist/types/observability.js +124 -0
  536. package/dist/types/observability.js.map +1 -0
  537. package/dist/types/sync-protocol.js +175 -0
  538. package/dist/types/sync-protocol.js.map +1 -0
  539. package/dist/types/vector.js +13 -0
  540. package/dist/types/vector.js.map +1 -0
  541. package/dist/workflows/ScheduleManager.js +473 -0
  542. package/dist/workflows/ScheduleManager.js.map +1 -0
  543. package/dist/workflows/StepDOBridge.js +149 -0
  544. package/dist/workflows/StepDOBridge.js.map +1 -0
  545. package/dist/workflows/StepResultStorage.js +232 -0
  546. package/dist/workflows/StepResultStorage.js.map +1 -0
  547. package/dist/workflows/WaitForEventManager.js +461 -0
  548. package/dist/workflows/WaitForEventManager.js.map +1 -0
  549. package/dist/workflows/analyzer.js +332 -0
  550. package/dist/workflows/analyzer.js.map +1 -0
  551. package/dist/workflows/compat/activity-router.js +484 -0
  552. package/dist/workflows/compat/activity-router.js.map +1 -0
  553. package/dist/workflows/compat/backends/cloudflare-workflows.js +431 -0
  554. package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -0
  555. package/dist/workflows/compat/backends/index.js +14 -0
  556. package/dist/workflows/compat/backends/index.js.map +1 -0
  557. package/dist/workflows/compat/errors/index.js +375 -0
  558. package/dist/workflows/compat/errors/index.js.map +1 -0
  559. package/dist/workflows/compat/index.js +79 -0
  560. package/dist/workflows/compat/index.js.map +1 -0
  561. package/dist/workflows/compat/inngest/index.js +989 -0
  562. package/dist/workflows/compat/inngest/index.js.map +1 -0
  563. package/dist/workflows/compat/qstash/index.js +1263 -0
  564. package/dist/workflows/compat/qstash/index.js.map +1 -0
  565. package/dist/workflows/compat/temporal/activities.js +739 -0
  566. package/dist/workflows/compat/temporal/activities.js.map +1 -0
  567. package/dist/workflows/compat/temporal/child-workflows.js +154 -0
  568. package/dist/workflows/compat/temporal/child-workflows.js.map +1 -0
  569. package/dist/workflows/compat/temporal/client.js +381 -0
  570. package/dist/workflows/compat/temporal/client.js.map +1 -0
  571. package/dist/workflows/compat/temporal/context.js +309 -0
  572. package/dist/workflows/compat/temporal/context.js.map +1 -0
  573. package/dist/workflows/compat/temporal/determinism.js +216 -0
  574. package/dist/workflows/compat/temporal/determinism.js.map +1 -0
  575. package/dist/workflows/compat/temporal/errors.js +128 -0
  576. package/dist/workflows/compat/temporal/errors.js.map +1 -0
  577. package/dist/workflows/compat/temporal/index.js +2464 -0
  578. package/dist/workflows/compat/temporal/index.js.map +1 -0
  579. package/dist/workflows/compat/temporal/saga.js +504 -0
  580. package/dist/workflows/compat/temporal/saga.js.map +1 -0
  581. package/dist/workflows/compat/temporal/signals.js +364 -0
  582. package/dist/workflows/compat/temporal/signals.js.map +1 -0
  583. package/dist/workflows/compat/temporal/storage.js +271 -0
  584. package/dist/workflows/compat/temporal/storage.js.map +1 -0
  585. package/dist/workflows/compat/temporal/timers.js +347 -0
  586. package/dist/workflows/compat/temporal/timers.js.map +1 -0
  587. package/dist/workflows/compat/temporal/types.js +7 -0
  588. package/dist/workflows/compat/temporal/types.js.map +1 -0
  589. package/dist/workflows/compat/temporal/unified-primitives.js +339 -0
  590. package/dist/workflows/compat/temporal/unified-primitives.js.map +1 -0
  591. package/dist/workflows/compat/trigger/index.js +468 -0
  592. package/dist/workflows/compat/trigger/index.js.map +1 -0
  593. package/dist/workflows/compat/utils/index.js +69 -0
  594. package/dist/workflows/compat/utils/index.js.map +1 -0
  595. package/dist/workflows/context/correlation-capability.js +266 -0
  596. package/dist/workflows/context/correlation-capability.js.map +1 -0
  597. package/dist/workflows/context/correlation.js +484 -0
  598. package/dist/workflows/context/correlation.js.map +1 -0
  599. package/dist/workflows/context/experiment.js +289 -0
  600. package/dist/workflows/context/experiment.js.map +1 -0
  601. package/dist/workflows/context/flag.js +244 -0
  602. package/dist/workflows/context/flag.js.map +1 -0
  603. package/dist/workflows/context/foundation.js +648 -0
  604. package/dist/workflows/context/foundation.js.map +1 -0
  605. package/dist/workflows/context/human-base.js +106 -0
  606. package/dist/workflows/context/human-base.js.map +1 -0
  607. package/dist/workflows/context/human.js +368 -0
  608. package/dist/workflows/context/human.js.map +1 -0
  609. package/dist/workflows/context/measure.js +354 -0
  610. package/dist/workflows/context/measure.js.map +1 -0
  611. package/dist/workflows/context/rate-limit.js +358 -0
  612. package/dist/workflows/context/rate-limit.js.map +1 -0
  613. package/dist/workflows/context/user.js +117 -0
  614. package/dist/workflows/context/user.js.map +1 -0
  615. package/dist/workflows/context/vault.js +360 -0
  616. package/dist/workflows/context/vault.js.map +1 -0
  617. package/dist/workflows/data/entity-events/entity-events.js +489 -0
  618. package/dist/workflows/data/entity-events/entity-events.js.map +1 -0
  619. package/dist/workflows/data/experiment/index.js +599 -0
  620. package/dist/workflows/data/experiment/index.js.map +1 -0
  621. package/dist/workflows/data/goal/context.js +558 -0
  622. package/dist/workflows/data/goal/context.js.map +1 -0
  623. package/dist/workflows/data/goal/index.js +32 -0
  624. package/dist/workflows/data/goal/index.js.map +1 -0
  625. package/dist/workflows/data/measure/index.js +840 -0
  626. package/dist/workflows/data/measure/index.js.map +1 -0
  627. package/dist/workflows/data/stream/index.js +1149 -0
  628. package/dist/workflows/data/stream/index.js.map +1 -0
  629. package/dist/workflows/data/track/context.js +883 -0
  630. package/dist/workflows/data/track/context.js.map +1 -0
  631. package/dist/workflows/data/track/index.js +15 -0
  632. package/dist/workflows/data/track/index.js.map +1 -0
  633. package/dist/workflows/data/view/context.js +864 -0
  634. package/dist/workflows/data/view/context.js.map +1 -0
  635. package/dist/workflows/domain.js +93 -0
  636. package/dist/workflows/domain.js.map +1 -0
  637. package/dist/workflows/flag.js +176 -0
  638. package/dist/workflows/flag.js.map +1 -0
  639. package/dist/workflows/flags.js +217 -0
  640. package/dist/workflows/flags.js.map +1 -0
  641. package/dist/workflows/hash.js +209 -0
  642. package/dist/workflows/hash.js.map +1 -0
  643. package/dist/workflows/index.js +50 -0
  644. package/dist/workflows/index.js.map +1 -0
  645. package/dist/workflows/on.js +378 -0
  646. package/dist/workflows/on.js.map +1 -0
  647. package/dist/workflows/pipeline-promise.js +481 -0
  648. package/dist/workflows/pipeline-promise.js.map +1 -0
  649. package/dist/workflows/pipeline-types.js +20 -0
  650. package/dist/workflows/pipeline-types.js.map +1 -0
  651. package/dist/workflows/proxy.js +76 -0
  652. package/dist/workflows/proxy.js.map +1 -0
  653. package/dist/workflows/runtime.js +310 -0
  654. package/dist/workflows/runtime.js.map +1 -0
  655. package/dist/workflows/schedule-builder.js +327 -0
  656. package/dist/workflows/schedule-builder.js.map +1 -0
  657. package/dist/workflows/visibility/index.js +146 -0
  658. package/dist/workflows/visibility/index.js.map +1 -0
  659. package/dist/workflows/visibility/query-parser.js +150 -0
  660. package/dist/workflows/visibility/query-parser.js.map +1 -0
  661. package/dist/workflows/visibility/store.js +223 -0
  662. package/dist/workflows/visibility/store.js.map +1 -0
  663. package/dist/workflows/visibility/types.js +30 -0
  664. package/dist/workflows/visibility/types.js.map +1 -0
  665. package/dist/workflows/workflow.js +53 -0
  666. package/dist/workflows/workflow.js.map +1 -0
  667. package/package.json +294 -46
@@ -0,0 +1,1539 @@
1
+ /**
2
+ * RPC Server for DO - Legacy RPC Protocol Implementation
3
+ *
4
+ * @deprecated This module is deprecated in favor of the official capnweb library.
5
+ * Use the root endpoint (/) for Cap'n Web RPC via `objects/transport/capnweb-target.ts`.
6
+ *
7
+ * This module is kept for backward compatibility at the /rpc endpoint and supports:
8
+ * - JSON-RPC 2.0: Standard JSON-RPC protocol (still useful for many clients)
9
+ * - Chain RPC: Legacy chain-based format (deprecated, use capnweb)
10
+ * - Custom Cap'n Web emulation: (deprecated, use real capnweb at /)
11
+ *
12
+ * Migration guide:
13
+ * - For new clients: Use capnweb client connecting to root endpoint (/)
14
+ * - For JSON-RPC 2.0: Continue using /rpc endpoint
15
+ * - For Chain RPC: Migrate to capnweb protocol
16
+ *
17
+ * @see {@link ./capnweb-target.ts} for the new capnweb integration
18
+ */
19
+ // Standard JSON-RPC error codes
20
+ const JSON_RPC_ERRORS = {
21
+ PARSE_ERROR: { code: -32700, message: 'Parse error' },
22
+ INVALID_REQUEST: { code: -32600, message: 'Invalid Request' },
23
+ METHOD_NOT_FOUND: { code: -32601, message: 'Method not found' },
24
+ INVALID_PARAMS: { code: -32602, message: 'Invalid params' },
25
+ INTERNAL_ERROR: { code: -32603, message: 'Internal error' },
26
+ };
27
+ /**
28
+ * Chain execution error codes
29
+ */
30
+ const CHAIN_ERRORS = {
31
+ INVALID_CHAIN: { code: 'INVALID_CHAIN', message: 'Chain must be a non-empty array' },
32
+ INVALID_STEP: { code: 'INVALID_STEP', message: 'Invalid step type' },
33
+ NOT_FOUND: { code: 'NOT_FOUND', message: 'Property not found' },
34
+ NOT_CALLABLE: { code: 'NOT_CALLABLE', message: 'Value is not a function' },
35
+ NOT_INDEXABLE: { code: 'NOT_INDEXABLE', message: 'Value is not an array' },
36
+ INDEX_OUT_OF_BOUNDS: { code: 'INDEX_OUT_OF_BOUNDS', message: 'Array index out of bounds' },
37
+ EXECUTION_ERROR: { code: 'EXECUTION_ERROR', message: 'Chain execution failed' },
38
+ BLOCKED_ACCESS: { code: 'BLOCKED_ACCESS', message: 'Access to this property is blocked' },
39
+ };
40
+ // ============================================================================
41
+ // PROMISE STORE - Manages stored promise results for pipelining
42
+ // ============================================================================
43
+ class PromiseStore {
44
+ promises = new Map();
45
+ disposed = new Set();
46
+ depths = new Map();
47
+ set(id, value, depth = 0) {
48
+ this.promises.set(id, value);
49
+ this.depths.set(id, depth);
50
+ }
51
+ get(id) {
52
+ if (this.disposed.has(id)) {
53
+ throw { code: 'DISPOSED_REFERENCE', message: `Promise ${id} has been disposed` };
54
+ }
55
+ return this.promises.get(id);
56
+ }
57
+ getDepth(id) {
58
+ return this.depths.get(id) ?? 0;
59
+ }
60
+ has(id) {
61
+ return this.promises.has(id) && !this.disposed.has(id);
62
+ }
63
+ dispose(id) {
64
+ if (this.promises.has(id)) {
65
+ this.disposed.add(id);
66
+ this.promises.delete(id);
67
+ this.depths.delete(id);
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+ isDisposed(id) {
73
+ return this.disposed.has(id);
74
+ }
75
+ clear() {
76
+ this.promises.clear();
77
+ this.disposed.clear();
78
+ this.depths.clear();
79
+ }
80
+ }
81
+ class SubscriptionManager {
82
+ subscriptions = new Map();
83
+ eventSubscriptions = new Map();
84
+ subscribe(event, callback) {
85
+ const id = `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
86
+ this.subscriptions.set(id, { id, event, callback });
87
+ if (!this.eventSubscriptions.has(event)) {
88
+ this.eventSubscriptions.set(event, new Set());
89
+ }
90
+ this.eventSubscriptions.get(event).add(id);
91
+ return id;
92
+ }
93
+ unsubscribe(subscriptionId) {
94
+ const sub = this.subscriptions.get(subscriptionId);
95
+ if (!sub)
96
+ return false;
97
+ this.subscriptions.delete(subscriptionId);
98
+ this.eventSubscriptions.get(sub.event)?.delete(subscriptionId);
99
+ return true;
100
+ }
101
+ emit(event, data) {
102
+ const subIds = this.eventSubscriptions.get(event);
103
+ if (!subIds)
104
+ return;
105
+ for (const id of subIds) {
106
+ const sub = this.subscriptions.get(id);
107
+ if (sub) {
108
+ try {
109
+ sub.callback(data);
110
+ }
111
+ catch (error) {
112
+ // Log callback errors so broken subscriptions can be detected
113
+ console.error('[rpc] Subscription callback error:', {
114
+ subscriptionId: id,
115
+ event,
116
+ error: error instanceof Error ? error.message : 'unknown',
117
+ stack: error instanceof Error ? error.stack : undefined,
118
+ });
119
+ }
120
+ }
121
+ }
122
+ }
123
+ clear() {
124
+ this.subscriptions.clear();
125
+ this.eventSubscriptions.clear();
126
+ }
127
+ }
128
+ // Default blocked methods - internal DO methods that should never be exposed
129
+ const DEFAULT_BLOCKED_METHODS = new Set([
130
+ // Lifecycle methods
131
+ 'initialize',
132
+ 'fetch',
133
+ 'handleFetch',
134
+ 'alarm',
135
+ // Internal state
136
+ 'db',
137
+ 'ctx',
138
+ 'storage',
139
+ 'env',
140
+ // Private accessors
141
+ '_users',
142
+ '_posts',
143
+ '_things',
144
+ '_rels',
145
+ '_actions',
146
+ '_events',
147
+ '_search',
148
+ '_objects',
149
+ '_dlq',
150
+ '_typeCache',
151
+ '_eventHandlers',
152
+ '_scheduleHandlers',
153
+ '_scheduleManager',
154
+ '_currentActor',
155
+ '_stepCache',
156
+ // Constructor
157
+ 'constructor',
158
+ ]);
159
+ // ============================================================================
160
+ // COLLECTION RPC PATTERN MATCHING
161
+ // ============================================================================
162
+ /**
163
+ * Valid collection methods
164
+ */
165
+ const COLLECTION_METHODS = new Set(['create', 'update', 'delete', 'get', 'list', 'find']);
166
+ /**
167
+ * Pattern for valid PascalCase noun names (no numbers, no special chars)
168
+ */
169
+ const VALID_NOUN_PATTERN = /^[A-Z][a-zA-Z]*$/;
170
+ /**
171
+ * Pattern for {Noun}.{method} format (exactly one dot)
172
+ * Noun must be PascalCase, method can be any valid identifier starting with lowercase
173
+ */
174
+ const COLLECTION_RPC_PATTERN = /^([A-Z][a-zA-Z]*)\.([a-z][a-zA-Z]*)$/;
175
+ /**
176
+ * Check if a method name looks like a collection RPC call
177
+ */
178
+ function isCollectionRpcMethod(method) {
179
+ return method.includes('.') && !method.startsWith('_');
180
+ }
181
+ /**
182
+ * Parse a collection RPC method into noun and action
183
+ * Returns null if invalid format
184
+ */
185
+ function parseCollectionRpcMethod(method) {
186
+ // Check for multiple dots (invalid)
187
+ if ((method.match(/\./g) || []).length !== 1) {
188
+ return null;
189
+ }
190
+ const match = method.match(COLLECTION_RPC_PATTERN);
191
+ if (!match)
192
+ return null;
193
+ const [, noun, action] = match;
194
+ return { noun: noun, action: action };
195
+ }
196
+ /**
197
+ * Validate noun name
198
+ * @returns error message if invalid, null if valid
199
+ */
200
+ function validateNounName(noun) {
201
+ if (!noun || noun.trim() === '') {
202
+ return 'Noun name cannot be empty';
203
+ }
204
+ if (!VALID_NOUN_PATTERN.test(noun)) {
205
+ return `Invalid noun '${noun}': must be PascalCase letters only`;
206
+ }
207
+ return null;
208
+ }
209
+ /**
210
+ * Validate collection method name
211
+ * @returns error message if invalid, null if valid
212
+ */
213
+ function validateCollectionMethod(noun, method) {
214
+ if (!COLLECTION_METHODS.has(method)) {
215
+ return `Unknown method '${method}' on ${noun}. Valid methods: ${Array.from(COLLECTION_METHODS).join(', ')}`;
216
+ }
217
+ return null;
218
+ }
219
+ /**
220
+ * RPC Server class that wraps a DO instance
221
+ */
222
+ export class RPCServer {
223
+ doInstance;
224
+ config;
225
+ exposedMethods;
226
+ blockedMethods;
227
+ sessions = new Map();
228
+ defaultHttpSession;
229
+ constructor(doInstance, config = {}) {
230
+ this.doInstance = doInstance;
231
+ this.config = {
232
+ maxPipelineDepth: config.maxPipelineDepth ?? 20,
233
+ ...config,
234
+ };
235
+ // Build blocked methods set
236
+ this.blockedMethods = new Set([
237
+ ...DEFAULT_BLOCKED_METHODS,
238
+ ...(config.blockedMethods ?? []),
239
+ ]);
240
+ // Build exposed methods set
241
+ this.exposedMethods = new Set();
242
+ if (config.exposedMethods && config.exposedMethods.length > 0) {
243
+ for (const method of config.exposedMethods) {
244
+ if (!this.blockedMethods.has(method)) {
245
+ this.exposedMethods.add(method);
246
+ }
247
+ }
248
+ }
249
+ else {
250
+ // Auto-discover methods from the DO instance
251
+ this.discoverMethods();
252
+ }
253
+ }
254
+ /**
255
+ * Discover exposed methods from the DO instance
256
+ */
257
+ discoverMethods() {
258
+ // Get own property names from instance and prototype chain
259
+ const visited = new Set();
260
+ let obj = this.doInstance;
261
+ while (obj && obj !== Object.prototype) {
262
+ for (const key of Object.getOwnPropertyNames(obj)) {
263
+ if (visited.has(key))
264
+ continue;
265
+ visited.add(key);
266
+ // Skip blocked methods
267
+ if (this.blockedMethods.has(key))
268
+ continue;
269
+ // Skip private methods (starting with _)
270
+ if (key.startsWith('_'))
271
+ continue;
272
+ // Skip getter/setter only properties
273
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
274
+ if (descriptor && (descriptor.get || descriptor.set) && !descriptor.value) {
275
+ continue;
276
+ }
277
+ // Check if it's a function
278
+ const value = this.doInstance[key];
279
+ if (typeof value === 'function') {
280
+ this.exposedMethods.add(key);
281
+ }
282
+ }
283
+ obj = Object.getPrototypeOf(obj);
284
+ }
285
+ }
286
+ /**
287
+ * Get list of exposed methods
288
+ */
289
+ get methods() {
290
+ return Array.from(this.exposedMethods);
291
+ }
292
+ /**
293
+ * Check if a method is exposed
294
+ */
295
+ isRpcExposed(method) {
296
+ if (this.blockedMethods.has(method))
297
+ return false;
298
+ if (method.startsWith('_'))
299
+ return false;
300
+ return this.exposedMethods.has(method);
301
+ }
302
+ /**
303
+ * Check if a method requires authentication and return an error if auth is missing
304
+ */
305
+ checkMethodAuth(method, request) {
306
+ // Get the $auth config from the DO instance's constructor
307
+ const DOClass = this.doInstance.constructor;
308
+ const authConfig = DOClass.$auth?.[method];
309
+ if (!authConfig)
310
+ return null;
311
+ // If method is public, no auth needed
312
+ if (authConfig.public)
313
+ return null;
314
+ // If method requires auth, check for Authorization header
315
+ if (authConfig.requireAuth || authConfig.roles) {
316
+ const authHeader = request.headers.get('Authorization');
317
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
318
+ return {
319
+ code: -32001, // Custom auth error code
320
+ message: 'Authentication required',
321
+ };
322
+ }
323
+ // Basic token validation (could be extended to actually validate JWT)
324
+ const token = authHeader.slice(7);
325
+ if (!token || token.split('.').length !== 3) {
326
+ return {
327
+ code: -32001,
328
+ message: 'Invalid authentication token',
329
+ };
330
+ }
331
+ }
332
+ return null;
333
+ }
334
+ /**
335
+ * Handle collection RPC call pattern: {Noun}.{method}
336
+ * Routes to the DO's collection() method for typed data access.
337
+ *
338
+ * @param method - The method name (e.g., "Task.create")
339
+ * @param args - The resolved arguments
340
+ * @returns The result of the collection operation, or throws an error
341
+ */
342
+ async handleCollectionRpc(method, args) {
343
+ // Check if this looks like a collection RPC call
344
+ if (!isCollectionRpcMethod(method)) {
345
+ return null; // Not a collection RPC, let normal handling proceed
346
+ }
347
+ // Handle multiple dots (invalid pattern)
348
+ if ((method.match(/\./g) || []).length !== 1) {
349
+ throw { code: 'INVALID_METHOD', message: `Invalid method pattern: ${method}` };
350
+ }
351
+ // Parse the method
352
+ const parsed = parseCollectionRpcMethod(method);
353
+ // Check for invalid noun pattern (lowercase, special chars, etc.)
354
+ if (!parsed) {
355
+ // Extract noun part for better error message
356
+ const [nounPart] = method.split('.');
357
+ // Check if it's a lowercase noun issue
358
+ if (nounPart && /^[a-z]/.test(nounPart)) {
359
+ throw { code: 'INVALID_NOUN', message: `Invalid noun '${nounPart}': must start with uppercase letter` };
360
+ }
361
+ // Check for numbers or special chars
362
+ if (nounPart && !/^[A-Za-z]+$/.test(nounPart)) {
363
+ throw { code: 'INVALID_NOUN', message: `Invalid noun '${nounPart}': must contain only letters` };
364
+ }
365
+ // Empty noun
366
+ if (!nounPart || nounPart === '') {
367
+ throw { code: 'INVALID_NOUN', message: 'Noun name cannot be empty' };
368
+ }
369
+ throw { code: 'INVALID_METHOD', message: `Invalid method pattern: ${method}` };
370
+ }
371
+ const { noun, action } = parsed;
372
+ // Validate noun name
373
+ const nounError = validateNounName(noun);
374
+ if (nounError) {
375
+ throw { code: 'INVALID_NOUN', message: nounError };
376
+ }
377
+ // Validate collection method
378
+ const methodError = validateCollectionMethod(noun, action);
379
+ if (methodError) {
380
+ throw { code: 'UNKNOWN_METHOD', message: methodError };
381
+ }
382
+ // Get the collection method from the DO instance
383
+ // The collection method is protected, but we can access it via the instance
384
+ const collectionFn = this.doInstance['collection'];
385
+ if (typeof collectionFn !== 'function') {
386
+ // Fallback: the DO doesn't have a collection method, so we can't handle this
387
+ throw { code: 'NOT_SUPPORTED', message: 'This DO does not support collection operations' };
388
+ }
389
+ // Get the collection for this noun
390
+ const collection = collectionFn.call(this.doInstance, noun);
391
+ if (!collection || typeof collection !== 'object') {
392
+ throw { code: 'COLLECTION_ERROR', message: `Failed to get collection for '${noun}'` };
393
+ }
394
+ const actionFn = collection[action];
395
+ if (typeof actionFn !== 'function') {
396
+ throw { code: 'UNKNOWN_METHOD', message: `Method '${action}' not found on ${noun} collection` };
397
+ }
398
+ // Execute the collection method with the provided arguments
399
+ let result = await actionFn.apply(collection, args);
400
+ // For mutations (create, update, delete), ensure $rowid is included
401
+ if (action === 'create' || action === 'update' || action === 'delete') {
402
+ result = this.ensureRowidInResult(result, action);
403
+ }
404
+ return result;
405
+ }
406
+ /**
407
+ * Ensure $rowid is present in mutation results.
408
+ * If not present, generate a synthetic one for compatibility.
409
+ */
410
+ ensureRowidInResult(result, action) {
411
+ if (result === null || result === undefined) {
412
+ return result;
413
+ }
414
+ if (typeof result === 'object' && !Array.isArray(result)) {
415
+ const obj = result;
416
+ // If $rowid is already present, return as-is
417
+ if ('$rowid' in obj && typeof obj.$rowid === 'number') {
418
+ return result;
419
+ }
420
+ // For delete, also add 'deleted' flag if not present
421
+ if (action === 'delete') {
422
+ return {
423
+ ...obj,
424
+ deleted: obj.deleted ?? true,
425
+ $rowid: obj.$rowid ?? this.generateSyntheticRowid(),
426
+ };
427
+ }
428
+ // For create/update, add $rowid if missing
429
+ return {
430
+ ...obj,
431
+ $rowid: this.generateSyntheticRowid(),
432
+ };
433
+ }
434
+ return result;
435
+ }
436
+ /**
437
+ * Generate a synthetic rowid for collection operations.
438
+ * This is used when the underlying collection doesn't return a rowid.
439
+ */
440
+ syntheticRowidCounter = 1;
441
+ generateSyntheticRowid() {
442
+ return this.syntheticRowidCounter++;
443
+ }
444
+ /**
445
+ * Create a new RPC session
446
+ */
447
+ createSession() {
448
+ const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
449
+ const promiseStore = new PromiseStore();
450
+ const subscriptions = new SubscriptionManager();
451
+ const ctx = {
452
+ promiseStore,
453
+ subscriptions,
454
+ rootObject: this.doInstance,
455
+ sendNotification: () => { },
456
+ sessionId,
457
+ exposedMethods: this.exposedMethods,
458
+ blockedMethods: this.blockedMethods,
459
+ };
460
+ this.sessions.set(sessionId, ctx);
461
+ return ctx;
462
+ }
463
+ /**
464
+ * Get or create a session
465
+ */
466
+ getSession(sessionId) {
467
+ if (sessionId && this.sessions.has(sessionId)) {
468
+ return this.sessions.get(sessionId);
469
+ }
470
+ return this.createSession();
471
+ }
472
+ /**
473
+ * Get the default HTTP session (persists across HTTP requests to same DO)
474
+ */
475
+ getDefaultHttpSession() {
476
+ if (!this.defaultHttpSession) {
477
+ this.defaultHttpSession = this.createSession();
478
+ }
479
+ return this.defaultHttpSession;
480
+ }
481
+ /**
482
+ * Clean up a session
483
+ */
484
+ cleanupSession(sessionId) {
485
+ const ctx = this.sessions.get(sessionId);
486
+ if (ctx) {
487
+ ctx.promiseStore.clear();
488
+ ctx.subscriptions.clear();
489
+ this.sessions.delete(sessionId);
490
+ }
491
+ }
492
+ /**
493
+ * Handle an HTTP RPC request
494
+ */
495
+ async handleRpcRequest(request) {
496
+ // Use persistent session for HTTP requests (state persists across requests to same DO)
497
+ const ctx = this.getDefaultHttpSession();
498
+ // Parse request body
499
+ let body;
500
+ try {
501
+ body = await request.json();
502
+ }
503
+ catch {
504
+ return Response.json({
505
+ id: '',
506
+ type: 'error',
507
+ error: { code: 'PARSE_ERROR', message: 'Invalid JSON' },
508
+ }, { status: 400, headers: { 'Content-Type': 'application/json' } });
509
+ }
510
+ // Check if JSON-RPC 2.0
511
+ if (this.isJSONRPCRequest(body)) {
512
+ // Check if the method requires auth
513
+ const authError = this.checkMethodAuth(body.method, request);
514
+ if (authError) {
515
+ return Response.json({
516
+ jsonrpc: '2.0',
517
+ error: authError,
518
+ id: body.id ?? null,
519
+ }, { status: 401, headers: { 'Content-Type': 'application/json' } });
520
+ }
521
+ const response = await this.handleJSONRPCRequest(body, ctx);
522
+ if (!response) {
523
+ // Notification - no response
524
+ return new Response(null, { status: 204 });
525
+ }
526
+ return Response.json(response, { headers: { 'Content-Type': 'application/json' } });
527
+ }
528
+ // Check if JSON-RPC 2.0 batch
529
+ if (this.isJSONRPCBatch(body)) {
530
+ const responses = [];
531
+ for (const req of body) {
532
+ const response = await this.handleJSONRPCRequest(req, ctx);
533
+ if (response) {
534
+ responses.push(response);
535
+ }
536
+ }
537
+ if (responses.length === 0) {
538
+ return new Response(null, { status: 204 });
539
+ }
540
+ return Response.json(responses, { headers: { 'Content-Type': 'application/json' } });
541
+ }
542
+ // Cap'n Web RPC
543
+ if (this.isCapnWebRequest(body)) {
544
+ const response = await this.executeCapnWebRequest(body, ctx);
545
+ return Response.json(response, { headers: { 'Content-Type': 'application/json' } });
546
+ }
547
+ // Chain RPC - Check for chain request format
548
+ if (this.isChainRequest(body)) {
549
+ // Get the WorkflowContext if available on the DO instance
550
+ const workflowContext = this.doInstance.$;
551
+ return this.executeChainRequest(body, workflowContext);
552
+ }
553
+ // Check for invalid chain format (has 'chain' property but it's not an array)
554
+ if (body !== null && typeof body === 'object' && 'chain' in body) {
555
+ return Response.json({
556
+ error: { message: 'Chain must be a non-empty array', code: 'INVALID_CHAIN' },
557
+ }, { status: 400, headers: { 'Content-Type': 'application/json' } });
558
+ }
559
+ return Response.json({
560
+ id: '',
561
+ type: 'error',
562
+ error: { code: 'INVALID_REQUEST', message: 'Unknown request format' },
563
+ }, { status: 400, headers: { 'Content-Type': 'application/json' } });
564
+ }
565
+ /**
566
+ * Handle WebSocket RPC connection
567
+ */
568
+ handleWebSocketRpc() {
569
+ const pair = new WebSocketPair();
570
+ const [client, server] = Object.values(pair);
571
+ const ctx = this.createSession();
572
+ // Set up notification sender
573
+ ctx.sendNotification = (method, params) => {
574
+ try {
575
+ server.send(JSON.stringify({
576
+ jsonrpc: '2.0',
577
+ method,
578
+ params,
579
+ }));
580
+ }
581
+ catch (error) {
582
+ // Log send errors for debugging WebSocket connection issues
583
+ console.debug('[rpc] WebSocket notification send failed:', {
584
+ method,
585
+ readyState: server.readyState,
586
+ error: error instanceof Error ? error.message : 'unknown',
587
+ });
588
+ }
589
+ };
590
+ server.accept();
591
+ // Send connection acknowledgment
592
+ server.send(JSON.stringify({
593
+ type: 'connected',
594
+ sessionId: ctx.sessionId,
595
+ }));
596
+ server.addEventListener('message', async (event) => {
597
+ const rawData = event.data;
598
+ // Handle binary data
599
+ if (rawData instanceof ArrayBuffer) {
600
+ server.send(JSON.stringify({
601
+ type: 'binary_received',
602
+ size: rawData.byteLength,
603
+ }));
604
+ return;
605
+ }
606
+ // Parse message
607
+ let data;
608
+ try {
609
+ data = JSON.parse(rawData);
610
+ }
611
+ catch {
612
+ server.send(JSON.stringify({
613
+ jsonrpc: '2.0',
614
+ error: JSON_RPC_ERRORS.PARSE_ERROR,
615
+ id: null,
616
+ }));
617
+ return;
618
+ }
619
+ // Handle JSON-RPC 2.0 batch
620
+ if (this.isJSONRPCBatch(data)) {
621
+ const responses = [];
622
+ for (const req of data) {
623
+ const response = await this.handleJSONRPCRequest(req, ctx);
624
+ if (response) {
625
+ responses.push(response);
626
+ }
627
+ }
628
+ if (responses.length > 0) {
629
+ server.send(JSON.stringify(responses));
630
+ }
631
+ return;
632
+ }
633
+ // Handle single JSON-RPC 2.0 request
634
+ if (this.isJSONRPCRequest(data)) {
635
+ const response = await this.handleJSONRPCRequest(data, ctx);
636
+ if (response) {
637
+ server.send(JSON.stringify(response));
638
+ }
639
+ return;
640
+ }
641
+ // Handle Cap'n Web request
642
+ if (this.isCapnWebRequest(data)) {
643
+ const response = await this.executeCapnWebRequest(data, ctx);
644
+ server.send(JSON.stringify(response));
645
+ return;
646
+ }
647
+ // Invalid request format
648
+ server.send(JSON.stringify({
649
+ jsonrpc: '2.0',
650
+ error: JSON_RPC_ERRORS.INVALID_REQUEST,
651
+ id: null,
652
+ }));
653
+ });
654
+ server.addEventListener('close', () => {
655
+ this.cleanupSession(ctx.sessionId);
656
+ });
657
+ server.addEventListener('error', () => {
658
+ this.cleanupSession(ctx.sessionId);
659
+ server.close();
660
+ });
661
+ return new Response(null, {
662
+ status: 101,
663
+ webSocket: client,
664
+ });
665
+ }
666
+ // ============================================================================
667
+ // PROTOCOL DETECTION
668
+ // ============================================================================
669
+ isJSONRPCRequest(data) {
670
+ return (data !== null &&
671
+ typeof data === 'object' &&
672
+ 'jsonrpc' in data &&
673
+ data.jsonrpc === '2.0');
674
+ }
675
+ isJSONRPCBatch(data) {
676
+ return (Array.isArray(data) &&
677
+ data.length > 0 &&
678
+ data.every((item) => this.isJSONRPCRequest(item)));
679
+ }
680
+ isCapnWebRequest(data) {
681
+ return (data !== null &&
682
+ typeof data === 'object' &&
683
+ 'type' in data &&
684
+ typeof data.type === 'string');
685
+ }
686
+ /**
687
+ * Check if request is a Chain RPC request
688
+ */
689
+ isChainRequest(data) {
690
+ return (data !== null &&
691
+ typeof data === 'object' &&
692
+ 'chain' in data &&
693
+ Array.isArray(data.chain));
694
+ }
695
+ // ============================================================================
696
+ // CHAIN RPC HANDLER
697
+ // ============================================================================
698
+ /**
699
+ * Execute a chain-based RPC request.
700
+ *
701
+ * The chain starts from the root object (the DO instance) and executes
702
+ * each step in sequence. If a WorkflowContext ($) is available, the chain
703
+ * can access it via the 'chain' starting from root.
704
+ *
705
+ * @param request - The chain RPC request
706
+ * @param workflowContext - Optional WorkflowContext to use as $ root
707
+ * @returns Response with data or error
708
+ */
709
+ async executeChainRequest(request, workflowContext) {
710
+ const { chain } = request;
711
+ // Validate chain
712
+ if (!Array.isArray(chain) || chain.length === 0) {
713
+ return Response.json({
714
+ error: { message: 'Chain must be a non-empty array', code: 'INVALID_CHAIN' },
715
+ }, { status: 400, headers: { 'Content-Type': 'application/json' } });
716
+ }
717
+ try {
718
+ const result = await this.executeChain(chain, workflowContext);
719
+ return Response.json({
720
+ result,
721
+ }, { headers: { 'Content-Type': 'application/json' } });
722
+ }
723
+ catch (error) {
724
+ const chainError = error;
725
+ const status = chainError.status ?? 400;
726
+ return Response.json({
727
+ error: {
728
+ message: chainError.message ?? 'Chain execution failed',
729
+ code: chainError.code ?? 'EXECUTION_ERROR',
730
+ },
731
+ }, { status, headers: { 'Content-Type': 'application/json' } });
732
+ }
733
+ }
734
+ /**
735
+ * Execute a chain of steps starting from the DO instance.
736
+ *
737
+ * The chain can access:
738
+ * - DO instance properties and methods directly (e.g., config, getStatus)
739
+ * - WorkflowContext via the $ property (e.g., $.things, $.on)
740
+ *
741
+ * @param chain - Array of chain steps
742
+ * @param workflowContext - Optional WorkflowContext accessible via $ property
743
+ * @returns The final result of the chain
744
+ */
745
+ async executeChain(chain, workflowContext) {
746
+ // Always start from the DO instance
747
+ // WorkflowContext ($) is accessible as a property of the instance
748
+ let current = this.doInstance;
749
+ // If the first step is accessing '$', use the WorkflowContext directly
750
+ if (chain.length > 0 && chain[0].type === 'property' && chain[0].key === '$' && workflowContext) {
751
+ current = workflowContext;
752
+ chain = chain.slice(1); // Skip the '$' step
753
+ }
754
+ for (let i = 0; i < chain.length; i++) {
755
+ const step = chain[i];
756
+ switch (step.type) {
757
+ case 'property': {
758
+ const propStep = step;
759
+ // Validate the step
760
+ if (typeof propStep.key !== 'string') {
761
+ throw { code: 'INVALID_STEP', message: 'Property step requires a string key', status: 400 };
762
+ }
763
+ // Block access to private/internal properties
764
+ if (propStep.key.startsWith('_')) {
765
+ throw { code: 'BLOCKED_ACCESS', message: `Access to private property '${propStep.key}' is blocked`, status: 404 };
766
+ }
767
+ // Block access to internal DO methods on first step (from root)
768
+ if (i === 0 && this.blockedMethods.has(propStep.key)) {
769
+ throw { code: 'BLOCKED_ACCESS', message: `Access to '${propStep.key}' is blocked`, status: 404 };
770
+ }
771
+ if (current === null || current === undefined) {
772
+ throw { code: 'NOT_FOUND', message: `Cannot access property '${propStep.key}' of ${current}`, status: 404 };
773
+ }
774
+ // For objects with a Proxy get trap (like WorkflowContext), accessing the property triggers the proxy
775
+ const value = current[propStep.key];
776
+ if (value === undefined && !(propStep.key in current)) {
777
+ throw { code: 'NOT_FOUND', message: `Property '${propStep.key}' not found`, status: 404 };
778
+ }
779
+ current = value;
780
+ break;
781
+ }
782
+ case 'call': {
783
+ const callStep = step;
784
+ const args = callStep.args ?? [];
785
+ // SDK Combined Format: { type: 'call', key: 'methodName', args: [...] }
786
+ // This combines property access and method call in one step.
787
+ // When 'key' is present, first access the property, then call it.
788
+ const combinedKey = step.key;
789
+ if (combinedKey !== undefined) {
790
+ // Combined call step - first access the property, then call it
791
+ if (typeof combinedKey !== 'string') {
792
+ throw { code: 'INVALID_STEP', message: 'Combined call step requires a string key', status: 400 };
793
+ }
794
+ // Block access to private methods
795
+ if (combinedKey.startsWith('_')) {
796
+ throw { code: 'BLOCKED_ACCESS', message: `Access to private method '${combinedKey}' is blocked`, status: 404 };
797
+ }
798
+ // Block access to internal DO methods on first step (from root)
799
+ if (i === 0 && this.blockedMethods.has(combinedKey)) {
800
+ throw { code: 'BLOCKED_ACCESS', message: `Access to '${combinedKey}' is blocked`, status: 404 };
801
+ }
802
+ if (current === null || current === undefined) {
803
+ throw { code: 'NOT_FOUND', message: `Cannot access method '${combinedKey}' of ${current}`, status: 404 };
804
+ }
805
+ // Access the method
806
+ const method = current[combinedKey];
807
+ if (method === undefined && !(combinedKey in current)) {
808
+ throw { code: 'NOT_FOUND', message: `Method '${combinedKey}' not found`, status: 404 };
809
+ }
810
+ if (typeof method !== 'function') {
811
+ throw { code: 'NOT_CALLABLE', message: `'${combinedKey}' is not a function`, status: 400 };
812
+ }
813
+ // Execute the method with 'current' as 'this' context
814
+ const result = method.apply(current, args);
815
+ // Await if it's a promise
816
+ current = result instanceof Promise ? await result : result;
817
+ }
818
+ else {
819
+ // Traditional call step - 'current' should already be a function
820
+ if (typeof current !== 'function') {
821
+ throw { code: 'NOT_CALLABLE', message: 'Value is not a function, cannot call', status: 400 };
822
+ }
823
+ // Find the 'this' context for the call
824
+ // Look back for the last property access to get the parent object
825
+ let thisContext = this.doInstance;
826
+ if (i > 0) {
827
+ // Re-execute chain up to the previous step to get the parent context
828
+ const prevChain = chain.slice(0, i - 1);
829
+ if (prevChain.length > 0) {
830
+ thisContext = await this.executeChain(prevChain, workflowContext);
831
+ }
832
+ else if (i === 1) {
833
+ // First call after property access - use root
834
+ thisContext = workflowContext ?? this.doInstance;
835
+ }
836
+ }
837
+ // Execute the function
838
+ const result = current.apply(thisContext, args);
839
+ // Await if it's a promise
840
+ current = result instanceof Promise ? await result : result;
841
+ }
842
+ break;
843
+ }
844
+ case 'index': {
845
+ const indexStep = step;
846
+ if (typeof indexStep.index !== 'number') {
847
+ throw { code: 'INVALID_STEP', message: 'Index step requires a numeric index', status: 400 };
848
+ }
849
+ if (!Array.isArray(current)) {
850
+ throw { code: 'NOT_INDEXABLE', message: 'Value is not an array, cannot use index access', status: 400 };
851
+ }
852
+ if (indexStep.index < 0 || indexStep.index >= current.length) {
853
+ throw {
854
+ code: 'INDEX_OUT_OF_BOUNDS',
855
+ message: `Index ${indexStep.index} is out of bounds (array length: ${current.length})`,
856
+ status: 404
857
+ };
858
+ }
859
+ current = current[indexStep.index];
860
+ break;
861
+ }
862
+ default: {
863
+ throw {
864
+ code: 'INVALID_STEP',
865
+ message: `Invalid step type: ${step.type}`,
866
+ status: 400
867
+ };
868
+ }
869
+ }
870
+ }
871
+ return current;
872
+ }
873
+ // ============================================================================
874
+ // JSON-RPC 2.0 HANDLER
875
+ // ============================================================================
876
+ async handleJSONRPCRequest(request, ctx) {
877
+ const { method, params, id } = request;
878
+ const isNotification = id === undefined;
879
+ try {
880
+ // Handle subscription methods
881
+ if (method === 'subscribe') {
882
+ const p = params;
883
+ const subscriptionId = ctx.subscriptions.subscribe(p.event, (data) => {
884
+ ctx.sendNotification(p.event, data);
885
+ });
886
+ if (isNotification)
887
+ return null;
888
+ return {
889
+ jsonrpc: '2.0',
890
+ result: { subscriptionId },
891
+ id: id ?? null,
892
+ };
893
+ }
894
+ if (method === 'unsubscribe') {
895
+ const p = params;
896
+ const success = ctx.subscriptions.unsubscribe(p.subscriptionId);
897
+ if (isNotification)
898
+ return null;
899
+ return {
900
+ jsonrpc: '2.0',
901
+ result: { success },
902
+ id: id ?? null,
903
+ };
904
+ }
905
+ // Handle triggerEvent (for testing)
906
+ if (method === 'triggerEvent') {
907
+ const p = params;
908
+ ctx.subscriptions.emit(p.event, p.data);
909
+ if (isNotification)
910
+ return null;
911
+ return {
912
+ jsonrpc: '2.0',
913
+ result: { triggered: true },
914
+ id: id ?? null,
915
+ };
916
+ }
917
+ // Check for collection RPC pattern: {Noun}.{method}
918
+ if (isCollectionRpcMethod(method)) {
919
+ // Convert params to args array
920
+ const args = params === undefined
921
+ ? []
922
+ : Array.isArray(params)
923
+ ? params
924
+ : [params];
925
+ const result = await this.handleCollectionRpc(method, args);
926
+ if (isNotification)
927
+ return null;
928
+ return {
929
+ jsonrpc: '2.0',
930
+ result,
931
+ id: id ?? null,
932
+ };
933
+ }
934
+ // Check if method exists and is exposed
935
+ if (!this.isRpcExposed(method)) {
936
+ if (isNotification)
937
+ return null;
938
+ return {
939
+ jsonrpc: '2.0',
940
+ error: { ...JSON_RPC_ERRORS.METHOD_NOT_FOUND, message: `Method '${method}' not found` },
941
+ id: id ?? null,
942
+ };
943
+ }
944
+ const methodFn = ctx.rootObject[method];
945
+ if (typeof methodFn !== 'function') {
946
+ if (isNotification)
947
+ return null;
948
+ return {
949
+ jsonrpc: '2.0',
950
+ error: { ...JSON_RPC_ERRORS.METHOD_NOT_FOUND, message: `Method '${method}' not found` },
951
+ id: id ?? null,
952
+ };
953
+ }
954
+ // Execute method
955
+ let result;
956
+ if (params === undefined) {
957
+ result = await methodFn.call(ctx.rootObject);
958
+ }
959
+ else if (Array.isArray(params)) {
960
+ result = await methodFn.apply(ctx.rootObject, params);
961
+ }
962
+ else if (typeof params === 'object' && params !== null) {
963
+ // Named parameters - try to extract positional args from object
964
+ const namedParams = params;
965
+ const paramNames = this.extractParamNames(methodFn);
966
+ if (paramNames.length > 0 && paramNames.every((name) => name in namedParams)) {
967
+ // All parameter names found in the object - convert to positional args
968
+ const args = paramNames.map((name) => namedParams[name]);
969
+ result = await methodFn.apply(ctx.rootObject, args);
970
+ }
971
+ else {
972
+ // Fall back to passing the object as a single argument
973
+ result = await methodFn.call(ctx.rootObject, params);
974
+ }
975
+ }
976
+ else {
977
+ result = await methodFn.call(ctx.rootObject, params);
978
+ }
979
+ if (isNotification)
980
+ return null;
981
+ return {
982
+ jsonrpc: '2.0',
983
+ result,
984
+ id: id ?? null,
985
+ };
986
+ }
987
+ catch (error) {
988
+ if (isNotification)
989
+ return null;
990
+ const errorMessage = error instanceof Error ? error.message : String(error);
991
+ return {
992
+ jsonrpc: '2.0',
993
+ error: {
994
+ code: JSON_RPC_ERRORS.INTERNAL_ERROR.code,
995
+ message: errorMessage,
996
+ },
997
+ id: id ?? null,
998
+ };
999
+ }
1000
+ }
1001
+ // ============================================================================
1002
+ // CAP'N WEB RPC HANDLER
1003
+ // ============================================================================
1004
+ async executeCapnWebRequest(request, ctx) {
1005
+ switch (request.type) {
1006
+ case 'call':
1007
+ case 'batch':
1008
+ return this.executeBatch(request, ctx);
1009
+ case 'resolve':
1010
+ return this.executeResolve(request, ctx);
1011
+ case 'dispose':
1012
+ return this.executeDispose(request, ctx);
1013
+ default:
1014
+ return {
1015
+ id: request.id,
1016
+ type: 'error',
1017
+ error: { code: 'INVALID_REQUEST', message: 'Unknown request type' },
1018
+ };
1019
+ }
1020
+ }
1021
+ async executeBatch(request, ctx) {
1022
+ if (!request.calls || request.calls.length === 0) {
1023
+ return {
1024
+ id: request.id,
1025
+ type: 'error',
1026
+ error: { code: 'INVALID_REQUEST', message: 'No calls provided' },
1027
+ };
1028
+ }
1029
+ const results = [];
1030
+ for (const call of request.calls) {
1031
+ const result = await this.executeCall(call, ctx);
1032
+ results.push(result);
1033
+ }
1034
+ return {
1035
+ id: request.id,
1036
+ type: 'batch',
1037
+ results,
1038
+ };
1039
+ }
1040
+ async executeResolve(request, ctx) {
1041
+ if (!request.resolve?.promiseId) {
1042
+ return {
1043
+ id: request.id,
1044
+ type: 'error',
1045
+ error: { code: 'INVALID_REQUEST', message: 'No promiseId provided' },
1046
+ };
1047
+ }
1048
+ const promiseId = request.resolve.promiseId;
1049
+ if (ctx.promiseStore.isDisposed(promiseId)) {
1050
+ return {
1051
+ id: request.id,
1052
+ type: 'error',
1053
+ error: { code: 'DISPOSED_REFERENCE', message: `Promise ${promiseId} has been disposed` },
1054
+ };
1055
+ }
1056
+ if (!ctx.promiseStore.has(promiseId)) {
1057
+ return {
1058
+ id: request.id,
1059
+ type: 'error',
1060
+ error: { code: 'INVALID_PROMISE', message: `Promise ${promiseId} not found` },
1061
+ };
1062
+ }
1063
+ const value = ctx.promiseStore.get(promiseId);
1064
+ return {
1065
+ id: request.id,
1066
+ type: 'result',
1067
+ results: [
1068
+ {
1069
+ promiseId,
1070
+ type: 'value',
1071
+ value,
1072
+ },
1073
+ ],
1074
+ };
1075
+ }
1076
+ async executeDispose(request, ctx) {
1077
+ if (!request.dispose?.promiseIds) {
1078
+ return {
1079
+ id: request.id,
1080
+ type: 'error',
1081
+ error: { code: 'INVALID_REQUEST', message: 'No promiseIds provided' },
1082
+ };
1083
+ }
1084
+ for (const promiseId of request.dispose.promiseIds) {
1085
+ ctx.promiseStore.dispose(promiseId);
1086
+ }
1087
+ return {
1088
+ id: request.id,
1089
+ type: 'result',
1090
+ results: [],
1091
+ };
1092
+ }
1093
+ async executeCall(call, ctx) {
1094
+ // Calculate depth for this call (outside try for catch access)
1095
+ let callDepth = 0;
1096
+ try {
1097
+ if (call.target.type === 'promise') {
1098
+ const baseDepth = ctx.promiseStore.getDepth(call.target.promiseId);
1099
+ callDepth = baseDepth + 1;
1100
+ // Check max pipeline depth
1101
+ const maxDepth = this.config.maxPipelineDepth ?? 20;
1102
+ if (callDepth >= maxDepth) {
1103
+ throw { code: 'MAX_PIPELINE_DEPTH', message: `Pipeline depth ${callDepth} exceeds maximum ${maxDepth}` };
1104
+ }
1105
+ }
1106
+ else if (call.target.type === 'property') {
1107
+ const base = call.target.base;
1108
+ if (base.type === 'promise') {
1109
+ const baseDepth = ctx.promiseStore.getDepth(base.promiseId);
1110
+ callDepth = baseDepth + 1;
1111
+ // Check max pipeline depth
1112
+ if (callDepth > (this.config.maxPipelineDepth ?? 20)) {
1113
+ throw { code: 'MAX_PIPELINE_DEPTH', message: `Pipeline depth ${callDepth} exceeds maximum ${this.config.maxPipelineDepth ?? 20}` };
1114
+ }
1115
+ }
1116
+ }
1117
+ const target = this.resolveTarget(call.target, ctx);
1118
+ const args = call.args.map((arg) => this.resolveArg(arg, ctx));
1119
+ let result;
1120
+ // Handle magic methods
1121
+ if (call.method === '__get__') {
1122
+ // Property getter - target is already the value from property access
1123
+ result = target;
1124
+ }
1125
+ else if (call.method === '__map__' && Array.isArray(target)) {
1126
+ // Magic map operation
1127
+ const mapSpec = args[0];
1128
+ if (mapSpec?.property) {
1129
+ result = target.map((item) => item[mapSpec.property]);
1130
+ }
1131
+ else {
1132
+ result = target;
1133
+ }
1134
+ }
1135
+ else if (call.method === '__filter__' && Array.isArray(target)) {
1136
+ // Magic filter operation
1137
+ const filterSpec = args[0];
1138
+ if (filterSpec?.property) {
1139
+ result = target.filter((item) => item[filterSpec.property] === filterSpec.equals);
1140
+ }
1141
+ else {
1142
+ result = target;
1143
+ }
1144
+ }
1145
+ else if (target === ctx.rootObject) {
1146
+ // Check for collection RPC pattern first: {Noun}.{method}
1147
+ if (isCollectionRpcMethod(call.method)) {
1148
+ result = await this.handleCollectionRpc(call.method, args);
1149
+ }
1150
+ else {
1151
+ // Regular root object method call - MUST check if exposed
1152
+ if (!this.isRpcExposed(call.method)) {
1153
+ throw { code: 'METHOD_NOT_FOUND', message: `Method ${call.method} not found` };
1154
+ }
1155
+ const method = target[call.method];
1156
+ if (typeof method !== 'function') {
1157
+ throw { code: 'METHOD_NOT_FOUND', message: `Method ${call.method} not found` };
1158
+ }
1159
+ result = await method.apply(target, args);
1160
+ }
1161
+ }
1162
+ else if (target && typeof target === 'object' && call.method in target) {
1163
+ // Method call on non-root object (from pipelining)
1164
+ const method = target[call.method];
1165
+ if (typeof method === 'function') {
1166
+ result = await method.apply(target, args);
1167
+ }
1168
+ else {
1169
+ result = method;
1170
+ }
1171
+ }
1172
+ else if (target && typeof target === 'object') {
1173
+ // Property access on object
1174
+ result = target[call.method];
1175
+ if (result === undefined && !(call.method in target)) {
1176
+ throw { code: 'METHOD_NOT_FOUND', message: `Method ${call.method} not found` };
1177
+ }
1178
+ }
1179
+ else {
1180
+ throw { code: 'INVALID_TARGET', message: 'Cannot call method on null or primitive' };
1181
+ }
1182
+ // Store result for pipelining (with depth tracking)
1183
+ ctx.promiseStore.set(call.promiseId, result, callDepth);
1184
+ return {
1185
+ promiseId: call.promiseId,
1186
+ type: 'value',
1187
+ value: result,
1188
+ };
1189
+ }
1190
+ catch (error) {
1191
+ const rpcError = error && typeof error === 'object' && 'code' in error
1192
+ ? error
1193
+ : {
1194
+ code: 'EXECUTION_ERROR',
1195
+ message: error instanceof Error ? error.message : String(error),
1196
+ };
1197
+ // Store error marker for pipeline propagation (with depth tracking)
1198
+ ctx.promiseStore.set(call.promiseId, { __error__: rpcError }, callDepth);
1199
+ return {
1200
+ promiseId: call.promiseId,
1201
+ type: 'error',
1202
+ error: rpcError,
1203
+ };
1204
+ }
1205
+ }
1206
+ resolveTarget(target, ctx) {
1207
+ switch (target.type) {
1208
+ case 'root':
1209
+ return ctx.rootObject;
1210
+ case 'promise': {
1211
+ if (ctx.promiseStore.isDisposed(target.promiseId)) {
1212
+ throw { code: 'DISPOSED_REFERENCE', message: `Promise ${target.promiseId} has been disposed` };
1213
+ }
1214
+ const value = ctx.promiseStore.get(target.promiseId);
1215
+ if (value === undefined && !ctx.promiseStore.has(target.promiseId)) {
1216
+ throw { code: 'INVALID_PROMISE', message: `Promise ${target.promiseId} not found` };
1217
+ }
1218
+ // Check if the value is an error marker (propagate errors through pipeline)
1219
+ if (value && typeof value === 'object' && '__error__' in value) {
1220
+ throw value.__error__;
1221
+ }
1222
+ // Check for null/undefined target
1223
+ if (value === null || value === undefined) {
1224
+ throw { code: 'INVALID_TARGET', message: 'Cannot access property of null or undefined' };
1225
+ }
1226
+ return value;
1227
+ }
1228
+ case 'property': {
1229
+ const base = this.resolveNestedTarget(target.base, ctx);
1230
+ if (base === null || typeof base !== 'object') {
1231
+ throw { code: 'INVALID_TARGET', message: 'Cannot access property of non-object' };
1232
+ }
1233
+ // Handle array index access
1234
+ if (Array.isArray(base) && /^\d+$/.test(target.property)) {
1235
+ return base[parseInt(target.property, 10)];
1236
+ }
1237
+ return base[target.property];
1238
+ }
1239
+ default:
1240
+ throw { code: 'INVALID_TARGET', message: 'Unknown target type' };
1241
+ }
1242
+ }
1243
+ resolveNestedTarget(target, ctx) {
1244
+ switch (target.type) {
1245
+ case 'root':
1246
+ return ctx.rootObject;
1247
+ case 'promise': {
1248
+ if (ctx.promiseStore.isDisposed(target.promiseId)) {
1249
+ throw { code: 'DISPOSED_REFERENCE', message: `Promise ${target.promiseId} has been disposed` };
1250
+ }
1251
+ const value = ctx.promiseStore.get(target.promiseId);
1252
+ if (value === undefined && !ctx.promiseStore.has(target.promiseId)) {
1253
+ throw { code: 'INVALID_PROMISE', message: `Promise ${target.promiseId} not found` };
1254
+ }
1255
+ // Check if the value is an error marker
1256
+ if (value && typeof value === 'object' && '__error__' in value) {
1257
+ throw value.__error__;
1258
+ }
1259
+ return value;
1260
+ }
1261
+ default:
1262
+ throw { code: 'INVALID_TARGET', message: 'Unknown nested target type' };
1263
+ }
1264
+ }
1265
+ resolveArg(arg, ctx) {
1266
+ switch (arg.type) {
1267
+ case 'value':
1268
+ return arg.value;
1269
+ case 'promise': {
1270
+ if (ctx.promiseStore.isDisposed(arg.promiseId)) {
1271
+ throw { code: 'DISPOSED_REFERENCE', message: `Promise ${arg.promiseId} has been disposed` };
1272
+ }
1273
+ const value = ctx.promiseStore.get(arg.promiseId);
1274
+ // If the value is an object with an id, extract it for method calls
1275
+ // This handles the case where we pass a user object to getUserPosts
1276
+ if (value && typeof value === 'object' && 'id' in value) {
1277
+ return value.id;
1278
+ }
1279
+ return value;
1280
+ }
1281
+ case 'callback':
1282
+ throw { code: 'NOT_IMPLEMENTED', message: 'Callbacks not yet supported' };
1283
+ default:
1284
+ throw { code: 'INVALID_ARG', message: 'Unknown argument type' };
1285
+ }
1286
+ }
1287
+ /**
1288
+ * Extract parameter names from a function.
1289
+ * Uses function.toString() parsing to extract parameter names.
1290
+ */
1291
+ extractParamNames(fn) {
1292
+ const fnStr = fn.toString();
1293
+ // Match function parameters - handles various function syntaxes
1294
+ const arrowMatch = fnStr.match(/^\s*\(?([^)=]*)\)?\s*=>/);
1295
+ const funcMatch = fnStr.match(/^(?:async\s+)?(?:function\s*)?\s*\w*\s*\(([^)]*)\)/);
1296
+ const paramsStr = arrowMatch?.[1] || funcMatch?.[1] || '';
1297
+ if (!paramsStr.trim()) {
1298
+ return [];
1299
+ }
1300
+ return paramsStr
1301
+ .split(',')
1302
+ .map((p) => {
1303
+ // Handle destructuring, default values, rest params
1304
+ const cleaned = p.trim()
1305
+ .replace(/\s*=\s*.*$/, '') // Remove default values
1306
+ .replace(/\.\.\.\s*/, '') // Remove rest operator
1307
+ .replace(/[{}[\]]/g, ''); // Remove destructuring braces
1308
+ // Extract the first identifier
1309
+ const match = cleaned.match(/^\s*(\w+)/);
1310
+ return match?.[1] || '';
1311
+ })
1312
+ .filter((p) => p.length > 0);
1313
+ }
1314
+ }
1315
+ // ============================================================================
1316
+ // FACTORY FUNCTIONS
1317
+ // ============================================================================
1318
+ /**
1319
+ * Create an RPC handler for a DO instance
1320
+ */
1321
+ export function createRpcHandler(doInstance, config) {
1322
+ return new RPCServer(doInstance, config);
1323
+ }
1324
+ /**
1325
+ * Handle an RPC request (HTTP)
1326
+ */
1327
+ export async function handleRpcRequest(request, doInstance, config) {
1328
+ const server = createRpcHandler(doInstance, config);
1329
+ return server.handleRpcRequest(request);
1330
+ }
1331
+ /**
1332
+ * Handle WebSocket RPC upgrade
1333
+ */
1334
+ export function handleWebSocketRpc(doInstance, config) {
1335
+ const server = createRpcHandler(doInstance, config);
1336
+ return server.handleWebSocketRpc();
1337
+ }
1338
+ export function withRpcServer(Base, config) {
1339
+ return class extends Base {
1340
+ _rpcServer;
1341
+ get rpcServer() {
1342
+ if (!this._rpcServer) {
1343
+ this._rpcServer = new RPCServer(this, config);
1344
+ }
1345
+ return this._rpcServer;
1346
+ }
1347
+ isRpcExposed(method) {
1348
+ return this.rpcServer.isRpcExposed(method);
1349
+ }
1350
+ /**
1351
+ * Override fetch to handle /rpc endpoint
1352
+ */
1353
+ async fetch(request) {
1354
+ const url = new URL(request.url);
1355
+ // Handle /rpc endpoint
1356
+ if (url.pathname === '/rpc') {
1357
+ // Check for WebSocket upgrade
1358
+ const upgradeHeader = request.headers.get('upgrade');
1359
+ const connectionHeader = request.headers.get('connection')?.toLowerCase() || '';
1360
+ const hasConnectionUpgrade = connectionHeader.includes('upgrade');
1361
+ if (upgradeHeader?.toLowerCase() === 'websocket' && hasConnectionUpgrade) {
1362
+ return this.rpcServer.handleWebSocketRpc();
1363
+ }
1364
+ // HTTP RPC request
1365
+ if (request.method === 'POST') {
1366
+ return this.rpcServer.handleRpcRequest(request);
1367
+ }
1368
+ // GET request - return RPC info
1369
+ return Response.json({
1370
+ message: 'RPC endpoint - use POST for HTTP batch mode or WebSocket for streaming',
1371
+ methods: this.rpcServer.methods,
1372
+ }, { headers: { 'Content-Type': 'application/json' } });
1373
+ }
1374
+ // Delegate to parent fetch
1375
+ return super.fetch(request);
1376
+ }
1377
+ };
1378
+ }
1379
+ /**
1380
+ * Apply RPC integration to an existing DO instance
1381
+ * This is useful when you can't use the mixin pattern
1382
+ */
1383
+ export function applyRpcIntegration(doInstance, config) {
1384
+ const rpcServer = new RPCServer(doInstance, config);
1385
+ // Add rpcServer property
1386
+ Object.defineProperty(doInstance, 'rpcServer', {
1387
+ get() {
1388
+ return rpcServer;
1389
+ },
1390
+ enumerable: true,
1391
+ configurable: false,
1392
+ });
1393
+ // Add isRpcExposed method
1394
+ doInstance.isRpcExposed = (method) => rpcServer.isRpcExposed(method);
1395
+ // Wrap the existing fetch method
1396
+ const originalFetch = doInstance.fetch.bind(doInstance);
1397
+ doInstance.fetch = async (request) => {
1398
+ const url = new URL(request.url);
1399
+ // Handle /rpc endpoint
1400
+ if (url.pathname === '/rpc') {
1401
+ // Check for WebSocket upgrade
1402
+ const upgradeHeader = request.headers.get('upgrade');
1403
+ const connectionHeader = request.headers.get('connection')?.toLowerCase() || '';
1404
+ const hasConnectionUpgrade = connectionHeader.includes('upgrade');
1405
+ if (upgradeHeader?.toLowerCase() === 'websocket' && hasConnectionUpgrade) {
1406
+ return rpcServer.handleWebSocketRpc();
1407
+ }
1408
+ // HTTP RPC request
1409
+ if (request.method === 'POST') {
1410
+ return rpcServer.handleRpcRequest(request);
1411
+ }
1412
+ // GET request - return RPC info
1413
+ return Response.json({
1414
+ message: 'RPC endpoint - use POST for HTTP batch mode or WebSocket for streaming',
1415
+ methods: rpcServer.methods,
1416
+ }, { headers: { 'Content-Type': 'application/json' } });
1417
+ }
1418
+ // Delegate to original fetch
1419
+ return originalFetch(request);
1420
+ };
1421
+ }
1422
+ export default RPCServer;
1423
+ import { buildJsonResponse, buildErrorResponse, } from './shared';
1424
+ /**
1425
+ * Cache for RPC servers per DO instance
1426
+ */
1427
+ const rpcServerCache = new WeakMap();
1428
+ /**
1429
+ * RPC Handler implementing TransportHandler interface
1430
+ *
1431
+ * Provides Cap'n Web RPC functionality:
1432
+ * - Promise pipelining
1433
+ * - JSON-RPC 2.0 support
1434
+ * - WebSocket streaming RPC
1435
+ * - Method discovery and invocation
1436
+ *
1437
+ * @example
1438
+ * ```typescript
1439
+ * const rpcHandler = new RpcHandler({
1440
+ * path: '/rpc',
1441
+ * maxPipelineDepth: 20,
1442
+ * })
1443
+ *
1444
+ * // Use in handler chain
1445
+ * chain.use(rpcHandler, 40)
1446
+ * ```
1447
+ */
1448
+ export class RpcHandler {
1449
+ name = 'rpc';
1450
+ options;
1451
+ server = null;
1452
+ constructor(options = {}) {
1453
+ this.options = {
1454
+ path: '/rpc',
1455
+ maxPipelineDepth: 20,
1456
+ ...options,
1457
+ };
1458
+ }
1459
+ /**
1460
+ * Check if this handler can process the request
1461
+ * RPC handler processes requests to the /rpc endpoint
1462
+ */
1463
+ canHandle(request) {
1464
+ const url = new URL(request.url);
1465
+ const rpcPath = this.options.path || '/rpc';
1466
+ // Check if request is to RPC endpoint
1467
+ if (url.pathname === rpcPath) {
1468
+ // RPC handler has medium-high priority
1469
+ return {
1470
+ canHandle: true,
1471
+ priority: 40,
1472
+ };
1473
+ }
1474
+ return { canHandle: false, reason: 'Path does not match RPC endpoint' };
1475
+ }
1476
+ /**
1477
+ * Handle the RPC request
1478
+ */
1479
+ async handle(request, context) {
1480
+ // Get or create RPC server for this instance
1481
+ const server = this.getOrCreateServer(context.instance);
1482
+ // Check for WebSocket upgrade
1483
+ const upgradeHeader = request.headers.get('upgrade');
1484
+ const connectionHeader = request.headers.get('connection')?.toLowerCase() || '';
1485
+ const hasConnectionUpgrade = connectionHeader.includes('upgrade');
1486
+ if (upgradeHeader?.toLowerCase() === 'websocket' && hasConnectionUpgrade) {
1487
+ return server.handleWebSocketRpc();
1488
+ }
1489
+ // Handle HTTP methods
1490
+ if (request.method === 'POST') {
1491
+ return server.handleRpcRequest(request);
1492
+ }
1493
+ if (request.method === 'GET') {
1494
+ // Return RPC info
1495
+ return buildJsonResponse({
1496
+ message: 'RPC endpoint - use POST for HTTP batch mode or WebSocket for streaming',
1497
+ methods: server.methods,
1498
+ });
1499
+ }
1500
+ return buildErrorResponse({ message: 'Method not allowed', code: 'METHOD_NOT_ALLOWED' }, 405, { headers: { 'Allow': 'GET, POST' } });
1501
+ }
1502
+ /**
1503
+ * Get or create RPC server for an instance
1504
+ */
1505
+ getOrCreateServer(instance) {
1506
+ // Check cache
1507
+ if (rpcServerCache.has(instance)) {
1508
+ return rpcServerCache.get(instance);
1509
+ }
1510
+ // Create new server
1511
+ const server = new RPCServer(instance, {
1512
+ maxPipelineDepth: this.options.maxPipelineDepth,
1513
+ exposedMethods: this.options.exposedMethods,
1514
+ blockedMethods: this.options.blockedMethods,
1515
+ });
1516
+ rpcServerCache.set(instance, server);
1517
+ this.server = server;
1518
+ return server;
1519
+ }
1520
+ /**
1521
+ * Get current RPC server
1522
+ */
1523
+ getServer() {
1524
+ return this.server;
1525
+ }
1526
+ /**
1527
+ * Clear server cache for an instance
1528
+ */
1529
+ static clearCache(instance) {
1530
+ rpcServerCache.delete(instance);
1531
+ }
1532
+ /**
1533
+ * Dispose handler resources
1534
+ */
1535
+ dispose() {
1536
+ this.server = null;
1537
+ }
1538
+ }
1539
+ //# sourceMappingURL=rpc-server.js.map