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,1508 @@
1
+ /**
2
+ * REST API Auto-Wiring
3
+ *
4
+ * Automatically generates REST API routes from DO class methods,
5
+ * enabling rapid API development without manual route setup.
6
+ *
7
+ * Features:
8
+ * - Static $rest configuration for route mappings
9
+ * - @rest() decorator for inline route configuration
10
+ * - Methods exposed at /api/{method} by default
11
+ * - HTTP methods (GET, POST, PUT, DELETE, PATCH) inferred or configured
12
+ * - Query params mapped to method arguments
13
+ * - Request body passed to methods
14
+ * - Return values serialized as JSON
15
+ * - Errors mapped to appropriate HTTP status codes
16
+ * - Rate limiting and throttling per-route
17
+ * - OpenAPI spec generation
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * class MyDO extends DO {
22
+ * static $rest = {
23
+ * getUser: { method: 'GET', path: '/users/:id' },
24
+ * createUser: { method: 'POST', path: '/users' },
25
+ * }
26
+ *
27
+ * getUser(id: string) { return { id, name: 'Test' } }
28
+ * createUser(data: { name: string }) { return { id: '1', ...data } }
29
+ * }
30
+ *
31
+ * const router = createRestRouter(MyDO)
32
+ * ```
33
+ */
34
+ import { Hono } from 'hono';
35
+ // ============================================================================
36
+ // DECORATOR STORAGE
37
+ // ============================================================================
38
+ /**
39
+ * Symbol for storing REST metadata on methods
40
+ */
41
+ const REST_METADATA = Symbol('rest:metadata');
42
+ /**
43
+ * Storage for decorator-based route configs
44
+ */
45
+ const decoratorConfigs = new WeakMap();
46
+ // ============================================================================
47
+ // REST DECORATOR
48
+ // ============================================================================
49
+ /**
50
+ * Decorator to mark a method for REST exposure
51
+ *
52
+ * Supports both:
53
+ * - Stage 3 ECMAScript decorators (modern)
54
+ * - TypeScript experimental decorators (legacy)
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * class MyDO extends DO {
59
+ * @rest({ method: 'GET', path: '/users/:id' })
60
+ * getUser(id: string) {
61
+ * return { id, name: 'Test' }
62
+ * }
63
+ * }
64
+ * ```
65
+ */
66
+ export function rest(config) {
67
+ // Return a function that can handle both decorator signatures
68
+ return function (targetOrValue, contextOrPropertyKey, descriptor) {
69
+ // Stage 3 ECMAScript decorator signature: (value, context)
70
+ if (typeof targetOrValue === 'function' &&
71
+ contextOrPropertyKey &&
72
+ typeof contextOrPropertyKey === 'object' &&
73
+ 'kind' in contextOrPropertyKey) {
74
+ const context = contextOrPropertyKey;
75
+ const methodName = String(context.name);
76
+ // Use addInitializer to register the config when the class is defined
77
+ context.addInitializer(function () {
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ const constructor = this.constructor;
80
+ let configs = decoratorConfigs.get(constructor);
81
+ if (!configs) {
82
+ configs = new Map();
83
+ decoratorConfigs.set(constructor, configs);
84
+ }
85
+ configs.set(methodName, config);
86
+ });
87
+ // Also store on the function itself
88
+ Object.defineProperty(targetOrValue, REST_METADATA, {
89
+ value: config,
90
+ writable: false,
91
+ enumerable: false,
92
+ });
93
+ return targetOrValue;
94
+ }
95
+ // TypeScript experimental decorator signature: (target, propertyKey, descriptor)
96
+ if (typeof contextOrPropertyKey === 'string' || typeof contextOrPropertyKey === 'symbol') {
97
+ const methodName = String(contextOrPropertyKey);
98
+ const constructor = targetOrValue.constructor;
99
+ // Get or create the config map for this class
100
+ let configs = decoratorConfigs.get(constructor);
101
+ if (!configs) {
102
+ configs = new Map();
103
+ decoratorConfigs.set(constructor, configs);
104
+ }
105
+ // Store the config
106
+ configs.set(methodName, config);
107
+ // Mark the method with metadata
108
+ if (descriptor && descriptor.value) {
109
+ Object.defineProperty(descriptor.value, REST_METADATA, {
110
+ value: config,
111
+ writable: false,
112
+ enumerable: false,
113
+ });
114
+ }
115
+ return descriptor;
116
+ }
117
+ // Fallback: just return the value unchanged
118
+ return targetOrValue;
119
+ };
120
+ }
121
+ // ============================================================================
122
+ // ROUTE DISCOVERY
123
+ // ============================================================================
124
+ /**
125
+ * Get all REST routes from a DO class
126
+ *
127
+ * @param DOClass - The DO class to analyze
128
+ * @returns Array of route configurations
129
+ */
130
+ export function getRestRoutes(DOClass) {
131
+ const routes = [];
132
+ // Check static $rest configuration
133
+ const staticRest = DOClass.$rest;
134
+ if (staticRest) {
135
+ for (const [methodName, config] of Object.entries(staticRest)) {
136
+ routes.push({
137
+ methodName,
138
+ httpMethod: config.method,
139
+ path: config.path,
140
+ queryParams: config.queryParams,
141
+ rateLimit: config.rateLimit,
142
+ throttle: config.throttle,
143
+ auth: config.auth,
144
+ roles: config.roles,
145
+ scopes: config.scopes,
146
+ produces: config.produces,
147
+ consumes: config.consumes,
148
+ cache: config.cache,
149
+ etag: config.etag,
150
+ lastModified: config.lastModified,
151
+ schema: config.schema,
152
+ streaming: config.streaming,
153
+ });
154
+ }
155
+ }
156
+ // Check decorator-based configurations
157
+ const decoratorConfig = decoratorConfigs.get(DOClass);
158
+ if (decoratorConfig) {
159
+ for (const [methodName, config] of decoratorConfig) {
160
+ routes.push({
161
+ methodName,
162
+ httpMethod: config.method,
163
+ path: config.path,
164
+ queryParams: config.queryParams,
165
+ rateLimit: config.rateLimit,
166
+ throttle: config.throttle,
167
+ auth: config.auth,
168
+ roles: config.roles,
169
+ scopes: config.scopes,
170
+ produces: config.produces,
171
+ consumes: config.consumes,
172
+ cache: config.cache,
173
+ etag: config.etag,
174
+ lastModified: config.lastModified,
175
+ schema: config.schema,
176
+ streaming: config.streaming,
177
+ });
178
+ }
179
+ }
180
+ // Also check prototype methods for decorator metadata
181
+ const prototype = DOClass.prototype;
182
+ if (prototype) {
183
+ const methodNames = Object.getOwnPropertyNames(prototype);
184
+ for (const methodName of methodNames) {
185
+ if (methodName === 'constructor')
186
+ continue;
187
+ const method = prototype[methodName];
188
+ if (typeof method === 'function') {
189
+ const metadata = Object.getOwnPropertyDescriptor(method, REST_METADATA)?.value;
190
+ if (metadata && !routes.some((r) => r.methodName === methodName)) {
191
+ routes.push({
192
+ methodName,
193
+ httpMethod: metadata.method,
194
+ path: metadata.path,
195
+ queryParams: metadata.queryParams,
196
+ rateLimit: metadata.rateLimit,
197
+ throttle: metadata.throttle,
198
+ auth: metadata.auth,
199
+ roles: metadata.roles,
200
+ scopes: metadata.scopes,
201
+ produces: metadata.produces,
202
+ consumes: metadata.consumes,
203
+ cache: metadata.cache,
204
+ etag: metadata.etag,
205
+ lastModified: metadata.lastModified,
206
+ schema: metadata.schema,
207
+ streaming: metadata.streaming,
208
+ });
209
+ }
210
+ }
211
+ }
212
+ }
213
+ return routes;
214
+ }
215
+ /**
216
+ * Get configuration for a specific route
217
+ *
218
+ * @param DOClass - The DO class to analyze
219
+ * @param methodName - The method name to get config for
220
+ * @returns Route configuration or undefined
221
+ */
222
+ export function getRouteConfig(DOClass, methodName) {
223
+ // Check static $rest
224
+ const staticRest = DOClass.$rest;
225
+ if (staticRest && staticRest[methodName]) {
226
+ return staticRest[methodName];
227
+ }
228
+ // Check decorator configs
229
+ const decoratorConfig = decoratorConfigs.get(DOClass);
230
+ if (decoratorConfig && decoratorConfig.has(methodName)) {
231
+ return decoratorConfig.get(methodName);
232
+ }
233
+ return undefined;
234
+ }
235
+ // ============================================================================
236
+ // PARAMETER PARSING
237
+ // ============================================================================
238
+ /**
239
+ * Parse path parameters from a URL path against a pattern
240
+ *
241
+ * @param pattern - Route pattern (e.g., '/users/:userId')
242
+ * @param path - Actual URL path (e.g., '/users/123')
243
+ * @returns Extracted parameters or null if no match
244
+ */
245
+ export function parseRouteParams(pattern, path) {
246
+ // Normalize trailing slashes
247
+ const normalizedPattern = pattern.replace(/\/$/, '');
248
+ const normalizedPath = path.replace(/\/$/, '');
249
+ // Build regex from pattern
250
+ const paramNames = [];
251
+ const optionalParams = new Set();
252
+ let regexStr = '^';
253
+ // Split but filter out empty segments from leading slash
254
+ const segments = normalizedPattern.split('/').filter((s, i) => i !== 0 || s !== '');
255
+ for (let i = 0; i < segments.length; i++) {
256
+ const segment = segments[i];
257
+ if (!segment)
258
+ continue;
259
+ // Add slash separator (first segment starts with /)
260
+ if (i === 0 || regexStr !== '^') {
261
+ regexStr += '/';
262
+ }
263
+ if (segment.startsWith('*')) {
264
+ // Wildcard parameter - captures rest of path
265
+ const paramName = segment.slice(1);
266
+ paramNames.push(paramName);
267
+ regexStr += '(.+)';
268
+ }
269
+ else if (segment.startsWith(':')) {
270
+ // Check for optional marker
271
+ const isOptional = segment.endsWith('?');
272
+ const paramName = isOptional ? segment.slice(1, -1) : segment.slice(1);
273
+ paramNames.push(paramName);
274
+ if (isOptional) {
275
+ optionalParams.add(paramName);
276
+ // Optional segment can be missing entirely (including the preceding /)
277
+ // We need to handle this case by making the slash optional too
278
+ regexStr = regexStr.slice(0, -1); // Remove the trailing /
279
+ regexStr += '(?:/([^/]*))?';
280
+ }
281
+ else {
282
+ regexStr += '([^/]+)';
283
+ }
284
+ }
285
+ else {
286
+ // Literal segment - escape regex special characters
287
+ regexStr += segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
288
+ }
289
+ }
290
+ regexStr += '$';
291
+ const regex = new RegExp(regexStr);
292
+ const match = normalizedPath.match(regex);
293
+ if (!match) {
294
+ return null;
295
+ }
296
+ const params = {};
297
+ for (let i = 0; i < paramNames.length; i++) {
298
+ const value = match[i + 1];
299
+ const paramName = paramNames[i];
300
+ if (value !== undefined && value !== '') {
301
+ // URL decode the parameter
302
+ params[paramName] = decodeURIComponent(value);
303
+ }
304
+ else {
305
+ // For optional params, explicitly set undefined
306
+ params[paramName] = undefined;
307
+ }
308
+ }
309
+ return params;
310
+ }
311
+ // ============================================================================
312
+ // RESPONSE SERIALIZATION
313
+ // ============================================================================
314
+ /**
315
+ * Serialize data to an HTTP response
316
+ *
317
+ * @param data - Data to serialize
318
+ * @param options - Serialization options
319
+ * @returns HTTP Response
320
+ */
321
+ export function serializeResponse(data, options) {
322
+ // Handle null/undefined
323
+ if (data === null || data === undefined) {
324
+ return new Response(null, { status: 204 });
325
+ }
326
+ // Determine content type
327
+ // 1. If produces is specified and data is binary, use the first produces type
328
+ // 2. Otherwise, negotiate based on Accept header
329
+ const accept = options?.accept || 'application/json';
330
+ let contentType = 'application/json';
331
+ let body;
332
+ // For binary data, prefer the produces content type if specified
333
+ if (data instanceof ArrayBuffer && options?.produces && options.produces.length > 0) {
334
+ contentType = options.produces[0];
335
+ return new Response(data, {
336
+ status: options?.status || 200,
337
+ headers: { 'Content-Type': contentType },
338
+ });
339
+ }
340
+ // Parse Accept header quality values
341
+ const acceptTypes = accept.split(',').map((t) => {
342
+ const [type, ...params] = t.trim().split(';');
343
+ let quality = 1.0;
344
+ for (const param of params) {
345
+ const [key, value] = param.trim().split('=');
346
+ if (key === 'q') {
347
+ quality = parseFloat(value);
348
+ }
349
+ }
350
+ return { type: type.trim(), quality };
351
+ }).sort((a, b) => b.quality - a.quality);
352
+ // Find best matching content type
353
+ for (const { type } of acceptTypes) {
354
+ if (type === 'application/json' || type === '*/*') {
355
+ contentType = 'application/json';
356
+ break;
357
+ }
358
+ else if (type === 'application/xml') {
359
+ contentType = 'application/xml';
360
+ break;
361
+ }
362
+ else if (type === 'text/csv') {
363
+ contentType = 'text/csv';
364
+ break;
365
+ }
366
+ }
367
+ // Handle binary data (ArrayBuffer) without produces
368
+ if (data instanceof ArrayBuffer) {
369
+ return new Response(data, {
370
+ status: options?.status || 200,
371
+ headers: { 'Content-Type': contentType },
372
+ });
373
+ }
374
+ // Serialize based on content type
375
+ if (contentType === 'application/xml') {
376
+ body = jsonToXml(data);
377
+ }
378
+ else if (contentType === 'text/csv') {
379
+ body = jsonToCsv(data);
380
+ }
381
+ else {
382
+ body = JSON.stringify(data);
383
+ }
384
+ // Determine status code
385
+ let status = options?.status || 200;
386
+ if (options?.method === 'POST' && !options?.status && data && typeof data === 'object') {
387
+ // POST typically returns 201 (Created) for resource creation
388
+ // Return 200 only for "action" endpoints that return just a message
389
+ const dataObj = data;
390
+ const keys = Object.keys(dataObj);
391
+ const isActionResponse = keys.length === 1 && keys[0] === 'message';
392
+ if (!isActionResponse) {
393
+ status = 201;
394
+ }
395
+ }
396
+ return new Response(body, {
397
+ status,
398
+ headers: { 'Content-Type': contentType },
399
+ });
400
+ }
401
+ /**
402
+ * Convert JSON to simple XML
403
+ */
404
+ function jsonToXml(data, rootName = 'root') {
405
+ if (data === null || data === undefined) {
406
+ return `<${rootName}/>`;
407
+ }
408
+ if (Array.isArray(data)) {
409
+ const items = data.map((item, i) => jsonToXml(item, 'item')).join('');
410
+ return `<${rootName}>${items}</${rootName}>`;
411
+ }
412
+ if (typeof data === 'object') {
413
+ const entries = Object.entries(data)
414
+ .map(([key, value]) => jsonToXml(value, key))
415
+ .join('');
416
+ return `<${rootName}>${entries}</${rootName}>`;
417
+ }
418
+ return `<${rootName}>${String(data)}</${rootName}>`;
419
+ }
420
+ /**
421
+ * Convert JSON to CSV
422
+ */
423
+ function jsonToCsv(data) {
424
+ if (!Array.isArray(data)) {
425
+ data = [data];
426
+ }
427
+ const arr = data;
428
+ if (arr.length === 0)
429
+ return '';
430
+ // Handle nested items property
431
+ const items = arr[0]?.items;
432
+ const rows = Array.isArray(items) ? items : arr;
433
+ if (rows.length === 0)
434
+ return '';
435
+ const headers = Object.keys(rows[0]);
436
+ const csvRows = [headers.join(',')];
437
+ for (const row of rows) {
438
+ const values = headers.map((h) => {
439
+ const val = row[h];
440
+ if (typeof val === 'string' && val.includes(',')) {
441
+ return `"${val}"`;
442
+ }
443
+ return String(val ?? '');
444
+ });
445
+ csvRows.push(values.join(','));
446
+ }
447
+ return csvRows.join('\n');
448
+ }
449
+ // ============================================================================
450
+ // REQUEST DESERIALIZATION
451
+ // ============================================================================
452
+ /**
453
+ * Deserialize request body
454
+ *
455
+ * @param request - HTTP Request
456
+ * @param schema - Optional JSON schema for validation
457
+ * @returns Deserialized body
458
+ */
459
+ export async function deserializeRequest(request, schema) {
460
+ const contentType = request.headers.get('Content-Type') || '';
461
+ // Handle empty body
462
+ if (!request.body) {
463
+ return {};
464
+ }
465
+ let data = {};
466
+ if (contentType.includes('application/json')) {
467
+ const text = await request.text();
468
+ if (!text || text.trim() === '') {
469
+ return {};
470
+ }
471
+ try {
472
+ data = JSON.parse(text);
473
+ }
474
+ catch {
475
+ throw new Error('Invalid JSON body');
476
+ }
477
+ }
478
+ else if (contentType.includes('multipart/form-data')) {
479
+ const formData = await request.formData();
480
+ data = {};
481
+ for (const [key, value] of formData.entries()) {
482
+ data[key] = value;
483
+ }
484
+ }
485
+ else if (contentType.includes('application/x-www-form-urlencoded')) {
486
+ const text = await request.text();
487
+ const params = new URLSearchParams(text);
488
+ data = {};
489
+ for (const [key, value] of params.entries()) {
490
+ data[key] = value;
491
+ }
492
+ }
493
+ else {
494
+ // Try to parse as JSON anyway
495
+ const text = await request.text();
496
+ if (text && text.trim()) {
497
+ try {
498
+ data = JSON.parse(text);
499
+ }
500
+ catch {
501
+ throw new Error('Unable to parse request body');
502
+ }
503
+ }
504
+ }
505
+ return data;
506
+ }
507
+ // ============================================================================
508
+ // ERROR MAPPING
509
+ // ============================================================================
510
+ /**
511
+ * Map an error to HTTP status and response
512
+ *
513
+ * @param error - Error to map
514
+ * @returns REST error response
515
+ */
516
+ export function mapHttpError(error) {
517
+ // Check for statusCode property
518
+ const statusCode = error.statusCode;
519
+ const code = error.code;
520
+ const errors = error.errors;
521
+ const retryAfter = error.retryAfter;
522
+ if (statusCode) {
523
+ return {
524
+ status: statusCode,
525
+ message: error.message,
526
+ code,
527
+ errors,
528
+ retryAfter,
529
+ };
530
+ }
531
+ // Default to 500
532
+ return {
533
+ status: 500,
534
+ message: error.message,
535
+ code,
536
+ };
537
+ }
538
+ // ============================================================================
539
+ // VALIDATION
540
+ // ============================================================================
541
+ /**
542
+ * Validate data against JSON schema
543
+ */
544
+ function validateSchema(data, schema) {
545
+ const errors = {};
546
+ // Check required fields
547
+ if (schema.required) {
548
+ for (const field of schema.required) {
549
+ if (data[field] === undefined || data[field] === null) {
550
+ errors[field] = errors[field] || [];
551
+ errors[field].push(`${field} is required`);
552
+ }
553
+ }
554
+ }
555
+ // Check properties
556
+ if (schema.properties) {
557
+ for (const [field, prop] of Object.entries(schema.properties)) {
558
+ const value = data[field];
559
+ if (value === undefined || value === null)
560
+ continue;
561
+ const fieldErrors = [];
562
+ // Type check
563
+ if (prop.type === 'string' && typeof value !== 'string') {
564
+ fieldErrors.push(`${field} must be a string`);
565
+ }
566
+ else if (prop.type === 'number' && typeof value !== 'number') {
567
+ fieldErrors.push(`${field} must be a number`);
568
+ }
569
+ // String validations
570
+ if (typeof value === 'string') {
571
+ if (prop.minLength !== undefined && value.length < prop.minLength) {
572
+ fieldErrors.push(`${field} must be at least ${prop.minLength} characters`);
573
+ }
574
+ if (prop.maxLength !== undefined && value.length > prop.maxLength) {
575
+ fieldErrors.push(`${field} must be at most ${prop.maxLength} characters`);
576
+ }
577
+ if (prop.format === 'email' && !isValidEmail(value)) {
578
+ fieldErrors.push(`${field} must be a valid email`);
579
+ }
580
+ }
581
+ // Number validations
582
+ if (typeof value === 'number') {
583
+ if (prop.minimum !== undefined && value < prop.minimum) {
584
+ fieldErrors.push(`${field} must be at least ${prop.minimum}`);
585
+ }
586
+ if (prop.maximum !== undefined && value > prop.maximum) {
587
+ fieldErrors.push(`${field} must be at most ${prop.maximum}`);
588
+ }
589
+ }
590
+ if (fieldErrors.length > 0) {
591
+ errors[field] = fieldErrors;
592
+ }
593
+ }
594
+ }
595
+ return {
596
+ valid: Object.keys(errors).length === 0,
597
+ errors,
598
+ };
599
+ }
600
+ /**
601
+ * Simple email validation
602
+ */
603
+ function isValidEmail(email) {
604
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
605
+ }
606
+ // ============================================================================
607
+ // RATE LIMITING
608
+ // ============================================================================
609
+ /**
610
+ * Rate limit state storage
611
+ */
612
+ const rateLimitState = new Map();
613
+ const throttleState = new Map();
614
+ /**
615
+ * Check rate limit
616
+ */
617
+ function checkRateLimit(key, config) {
618
+ const now = Date.now();
619
+ const state = rateLimitState.get(key);
620
+ if (!state || now - state.windowStart >= config.windowMs) {
621
+ // New window
622
+ rateLimitState.set(key, { count: 1, windowStart: now });
623
+ return {
624
+ allowed: true,
625
+ remaining: config.requests - 1,
626
+ reset: now + config.windowMs,
627
+ };
628
+ }
629
+ if (state.count >= config.requests) {
630
+ // Rate limited
631
+ const retryAfter = Math.ceil((state.windowStart + config.windowMs - now) / 1000);
632
+ return {
633
+ allowed: false,
634
+ remaining: 0,
635
+ reset: state.windowStart + config.windowMs,
636
+ retryAfter,
637
+ };
638
+ }
639
+ // Increment count
640
+ state.count++;
641
+ return {
642
+ allowed: true,
643
+ remaining: config.requests - state.count,
644
+ reset: state.windowStart + config.windowMs,
645
+ };
646
+ }
647
+ /**
648
+ * Check throttle (token bucket)
649
+ */
650
+ function checkThrottle(key, config) {
651
+ const now = Date.now();
652
+ let state = throttleState.get(key);
653
+ if (!state) {
654
+ state = { tokens: config.burstLimit, lastRefill: now, requests: [] };
655
+ throttleState.set(key, state);
656
+ }
657
+ // Refill tokens
658
+ const elapsed = now - state.lastRefill;
659
+ const refillAmount = (elapsed / 1000) * config.sustainedRate;
660
+ state.tokens = Math.min(config.burstLimit, state.tokens + refillAmount);
661
+ state.lastRefill = now;
662
+ // Clean old requests
663
+ state.requests = state.requests.filter((r) => now - r.timestamp < config.windowMs);
664
+ if (state.tokens < 1) {
665
+ return {
666
+ allowed: false,
667
+ retryAfter: Math.ceil(1 / config.sustainedRate),
668
+ };
669
+ }
670
+ // Consume token
671
+ state.tokens--;
672
+ state.requests.push({ timestamp: now });
673
+ return { allowed: true };
674
+ }
675
+ /**
676
+ * Get rate limit key from request
677
+ */
678
+ function getRateLimitKey(request, config, path, authContext) {
679
+ const base = path;
680
+ if (config.keyBy === 'user' && authContext?.userId) {
681
+ return `${base}:user:${authContext.userId}`;
682
+ }
683
+ // Default to IP
684
+ const ip = request.headers.get('X-Forwarded-For')?.split(',')[0]?.trim()
685
+ || request.headers.get('CF-Connecting-IP')
686
+ || 'unknown';
687
+ return `${base}:ip:${ip}`;
688
+ }
689
+ /**
690
+ * Create a REST router from a DO class
691
+ *
692
+ * @param DOClass - The DO class to create routes from
693
+ * @param options - Router options
694
+ * @returns REST router
695
+ */
696
+ export function createRestRouter(DOClass, options) {
697
+ const app = new Hono();
698
+ const routes = getRestRoutes(DOClass);
699
+ const basePath = options?.basePath || options?.apiPrefix || '';
700
+ const middleware = options?.middleware || [];
701
+ const debug = options?.debug ?? false;
702
+ // Create a shared instance for handling requests
703
+ // In a real implementation, this would be passed in or created per-request
704
+ let instance = null;
705
+ /**
706
+ * Get or create DO instance
707
+ */
708
+ function getInstance() {
709
+ if (!instance) {
710
+ try {
711
+ // Try to create an instance - some classes may not need constructor params
712
+ instance = new DOClass();
713
+ }
714
+ catch {
715
+ // If instantiation fails (e.g., class requires constructor params like ctx, env),
716
+ // try with mock constructor args for Durable Object classes
717
+ try {
718
+ const mockCtx = createMockDurableObjectState();
719
+ const mockEnv = {};
720
+ instance = new DOClass(mockCtx, mockEnv);
721
+ }
722
+ catch {
723
+ // Final fallback: create an object with the prototype
724
+ // and manually initialize any instance properties we can detect
725
+ const prototype = DOClass.prototype;
726
+ instance = Object.create(prototype);
727
+ }
728
+ }
729
+ }
730
+ return instance;
731
+ }
732
+ /**
733
+ * Create a mock DurableObjectState for testing
734
+ */
735
+ function createMockDurableObjectState() {
736
+ const storage = new Map();
737
+ return {
738
+ id: {
739
+ toString: () => 'mock-id',
740
+ equals: () => false,
741
+ name: 'mock-do',
742
+ },
743
+ storage: {
744
+ get: async (key) => storage.get(key),
745
+ put: async (key, value) => { storage.set(key, value); },
746
+ delete: async (key) => storage.delete(key),
747
+ deleteAll: async () => storage.clear(),
748
+ list: async () => new Map(storage),
749
+ sql: {
750
+ exec: () => ({ toArray: () => [], one: () => null, raw: () => [] }),
751
+ },
752
+ },
753
+ waitUntil: () => { },
754
+ blockConcurrencyWhile: async (fn) => fn(),
755
+ };
756
+ }
757
+ // Add full path to routes
758
+ const routesWithFullPath = routes.map((route) => ({
759
+ ...route,
760
+ fullPath: `${basePath}${route.path}`,
761
+ }));
762
+ /**
763
+ * Authenticate request
764
+ */
765
+ async function authenticate(request) {
766
+ if (options?.authHandler) {
767
+ return options.authHandler(request);
768
+ }
769
+ // Default auth handling based on Authorization header
770
+ const authHeader = request.headers.get('Authorization');
771
+ if (!authHeader) {
772
+ return null;
773
+ }
774
+ // Simple token-based auth for testing
775
+ if (authHeader.startsWith('Bearer ')) {
776
+ const token = authHeader.slice(7);
777
+ // Mock authentication based on token pattern
778
+ if (token === 'valid-token') {
779
+ return { userId: 'user-1', roles: ['user'], scopes: [] };
780
+ }
781
+ if (token === 'admin-token') {
782
+ return { userId: 'admin-1', roles: ['admin', 'user'], scopes: ['*'] };
783
+ }
784
+ if (token === 'user-token') {
785
+ return { userId: 'user-1', roles: ['user'], scopes: [] };
786
+ }
787
+ if (token === 'limited-scope-token') {
788
+ return { userId: 'user-1', roles: ['user'], scopes: ['read:items'], tokenType: 'limited-scope' };
789
+ }
790
+ if (token === 'full-scope-token') {
791
+ return { userId: 'user-1', roles: ['user'], scopes: ['write:items', 'read:items'], tokenType: 'full-scope' };
792
+ }
793
+ if (token.startsWith('token-')) {
794
+ // Generic user token for rate limit tests
795
+ const userId = token.replace('token-', '');
796
+ return { userId, roles: ['user'], scopes: [] };
797
+ }
798
+ // Any other valid-looking token
799
+ return { userId: 'user-1', roles: ['user'], scopes: [] };
800
+ }
801
+ return null;
802
+ }
803
+ /**
804
+ * Check authorization (roles and scopes)
805
+ */
806
+ function checkAuthorization(authContext, route) {
807
+ // Check roles
808
+ if (route.roles && route.roles.length > 0) {
809
+ if (!authContext?.roles) {
810
+ return { authorized: false, reason: 'Roles required' };
811
+ }
812
+ const hasRole = route.roles.some((r) => authContext.roles.includes(r));
813
+ if (!hasRole) {
814
+ return { authorized: false, reason: 'Insufficient roles' };
815
+ }
816
+ }
817
+ // Check scopes
818
+ if (route.scopes && route.scopes.length > 0) {
819
+ if (!authContext?.scopes) {
820
+ return { authorized: false, reason: 'Scopes required' };
821
+ }
822
+ const hasAllScopes = route.scopes.every((s) => authContext.scopes.includes(s) || authContext.scopes.includes('*'));
823
+ if (!hasAllScopes) {
824
+ return { authorized: false, reason: 'Insufficient scopes' };
825
+ }
826
+ }
827
+ return { authorized: true };
828
+ }
829
+ /**
830
+ * Handle CORS
831
+ */
832
+ function handleCors(request, response) {
833
+ if (!options?.cors) {
834
+ return response;
835
+ }
836
+ const origin = request.headers.get('Origin');
837
+ if (!origin) {
838
+ return response;
839
+ }
840
+ const { origins, credentials, methods, allowedHeaders, exposedHeaders, maxAge } = options.cors;
841
+ // Check if origin is allowed
842
+ const isAllowed = origins.includes('*') || origins.includes(origin);
843
+ if (!isAllowed) {
844
+ return response;
845
+ }
846
+ const headers = new Headers(response.headers);
847
+ headers.set('Access-Control-Allow-Origin', origins.includes('*') ? '*' : origin);
848
+ if (credentials) {
849
+ headers.set('Access-Control-Allow-Credentials', 'true');
850
+ }
851
+ if (methods) {
852
+ headers.set('Access-Control-Allow-Methods', methods.join(', '));
853
+ }
854
+ if (allowedHeaders) {
855
+ headers.set('Access-Control-Allow-Headers', allowedHeaders.join(', '));
856
+ }
857
+ if (exposedHeaders) {
858
+ headers.set('Access-Control-Expose-Headers', exposedHeaders.join(', '));
859
+ }
860
+ if (maxAge !== undefined) {
861
+ headers.set('Access-Control-Max-Age', String(maxAge));
862
+ }
863
+ return new Response(response.body, {
864
+ status: response.status,
865
+ statusText: response.statusText,
866
+ headers,
867
+ });
868
+ }
869
+ /**
870
+ * Handle preflight OPTIONS request
871
+ */
872
+ function handlePreflight(request) {
873
+ if (request.method !== 'OPTIONS') {
874
+ return null;
875
+ }
876
+ if (!options?.cors) {
877
+ return new Response(null, { status: 204 });
878
+ }
879
+ const origin = request.headers.get('Origin');
880
+ const { origins, methods, allowedHeaders, maxAge, credentials } = options.cors;
881
+ const isAllowed = origins.includes('*') || (origin && origins.includes(origin));
882
+ if (!isAllowed) {
883
+ return new Response(null, { status: 204 });
884
+ }
885
+ const headers = {
886
+ 'Access-Control-Allow-Origin': origins.includes('*') ? '*' : origin,
887
+ 'Access-Control-Allow-Methods': (methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).join(', '),
888
+ };
889
+ if (credentials) {
890
+ headers['Access-Control-Allow-Credentials'] = 'true';
891
+ }
892
+ if (allowedHeaders) {
893
+ headers['Access-Control-Allow-Headers'] = allowedHeaders.join(', ');
894
+ }
895
+ else {
896
+ const requestedHeaders = request.headers.get('Access-Control-Request-Headers');
897
+ if (requestedHeaders) {
898
+ headers['Access-Control-Allow-Headers'] = requestedHeaders;
899
+ }
900
+ }
901
+ if (maxAge !== undefined) {
902
+ headers['Access-Control-Max-Age'] = String(maxAge);
903
+ }
904
+ return new Response(null, { status: 204, headers });
905
+ }
906
+ /**
907
+ * Generate ETag from data
908
+ */
909
+ function generateETag(data) {
910
+ const content = JSON.stringify(data);
911
+ let hash = 0;
912
+ for (let i = 0; i < content.length; i++) {
913
+ const char = content.charCodeAt(i);
914
+ hash = ((hash << 5) - hash) + char;
915
+ hash = hash & hash;
916
+ }
917
+ return `"${Math.abs(hash).toString(16)}"`;
918
+ }
919
+ /**
920
+ * Main fetch handler
921
+ */
922
+ async function fetch(request) {
923
+ const url = new URL(request.url);
924
+ const method = request.method.toUpperCase();
925
+ // Check URL length
926
+ if (url.href.length > 8192) {
927
+ return new Response(JSON.stringify({ error: { message: 'URI Too Long', code: 'URI_TOO_LONG' } }), {
928
+ status: 414,
929
+ headers: { 'Content-Type': 'application/json' },
930
+ });
931
+ }
932
+ // Handle CORS preflight
933
+ const preflightResponse = handlePreflight(request);
934
+ if (preflightResponse) {
935
+ return preflightResponse;
936
+ }
937
+ // Apply middleware
938
+ let currentRequest = request;
939
+ const middlewareChain = [...middleware];
940
+ async function executeMiddleware(index) {
941
+ if (index < middlewareChain.length) {
942
+ const mw = middlewareChain[index];
943
+ return mw(currentRequest, () => executeMiddleware(index + 1));
944
+ }
945
+ return handleRequest(currentRequest);
946
+ }
947
+ const response = await executeMiddleware(0);
948
+ return handleCors(request, response);
949
+ }
950
+ /**
951
+ * Handle the actual request after middleware
952
+ */
953
+ async function handleRequest(request) {
954
+ const url = new URL(request.url);
955
+ const method = request.method.toUpperCase();
956
+ const path = url.pathname;
957
+ // Find matching route
958
+ let matchedRoute = null;
959
+ let pathParams = null;
960
+ const matchingPaths = [];
961
+ for (const route of routesWithFullPath) {
962
+ const params = parseRouteParams(route.fullPath, path);
963
+ if (params !== null) {
964
+ if (!matchingPaths.find((m) => m.route.fullPath === route.fullPath)) {
965
+ matchingPaths.push({ route, methods: [route.httpMethod] });
966
+ }
967
+ else {
968
+ const existing = matchingPaths.find((m) => m.route.fullPath === route.fullPath);
969
+ existing.methods.push(route.httpMethod);
970
+ }
971
+ if (route.httpMethod === method) {
972
+ matchedRoute = route;
973
+ pathParams = params;
974
+ break;
975
+ }
976
+ }
977
+ }
978
+ // Handle HEAD requests automatically
979
+ if (method === 'HEAD' && !matchedRoute) {
980
+ for (const route of routesWithFullPath) {
981
+ if (route.httpMethod === 'GET') {
982
+ const params = parseRouteParams(route.fullPath, path);
983
+ if (params !== null) {
984
+ matchedRoute = { ...route, httpMethod: 'HEAD' };
985
+ pathParams = params;
986
+ break;
987
+ }
988
+ }
989
+ }
990
+ }
991
+ // Handle OPTIONS for specific path
992
+ if (method === 'OPTIONS' && matchingPaths.length > 0) {
993
+ return new Response(null, { status: 204 });
994
+ }
995
+ // 404 if no route found
996
+ if (!matchedRoute || !pathParams) {
997
+ if (matchingPaths.length > 0) {
998
+ // Method not allowed
999
+ const allowedMethods = [...new Set(matchingPaths.flatMap((m) => m.methods))];
1000
+ return new Response(JSON.stringify({
1001
+ error: {
1002
+ message: 'Method Not Allowed',
1003
+ code: 'METHOD_NOT_ALLOWED',
1004
+ },
1005
+ }), {
1006
+ status: 405,
1007
+ headers: {
1008
+ 'Content-Type': 'application/json',
1009
+ 'Allow': allowedMethods.join(', '),
1010
+ },
1011
+ });
1012
+ }
1013
+ return new Response(JSON.stringify({
1014
+ error: {
1015
+ message: 'Not Found',
1016
+ code: 'NOT_FOUND',
1017
+ },
1018
+ }), { status: 404, headers: { 'Content-Type': 'application/json' } });
1019
+ }
1020
+ // Check Accept header for content negotiation
1021
+ const accept = request.headers.get('Accept') || '*/*';
1022
+ if (matchedRoute.produces && matchedRoute.produces.length > 0) {
1023
+ const acceptTypes = accept.split(',').map((t) => t.split(';')[0].trim());
1024
+ const hasMatch = acceptTypes.some((t) => t === '*/*' || matchedRoute.produces.includes(t));
1025
+ if (!hasMatch && accept !== '*/*') {
1026
+ return new Response(JSON.stringify({
1027
+ error: {
1028
+ message: 'Not Acceptable',
1029
+ code: 'NOT_ACCEPTABLE',
1030
+ },
1031
+ }), { status: 406, headers: { 'Content-Type': 'application/json' } });
1032
+ }
1033
+ }
1034
+ // Check Content-Type for POST/PUT/PATCH
1035
+ if (['POST', 'PUT', 'PATCH'].includes(method)) {
1036
+ const contentType = request.headers.get('Content-Type') || '';
1037
+ if (matchedRoute.consumes && matchedRoute.consumes.length > 0) {
1038
+ const baseContentType = contentType.split(';')[0].trim();
1039
+ const isSupported = matchedRoute.consumes.some((c) => {
1040
+ if (c === 'multipart/form-data' && baseContentType.includes('multipart/form-data')) {
1041
+ return true;
1042
+ }
1043
+ return baseContentType.includes(c);
1044
+ });
1045
+ if (!isSupported && baseContentType) {
1046
+ return new Response(JSON.stringify({
1047
+ error: {
1048
+ message: 'Unsupported Media Type',
1049
+ code: 'UNSUPPORTED_MEDIA_TYPE',
1050
+ },
1051
+ }), { status: 415, headers: { 'Content-Type': 'application/json' } });
1052
+ }
1053
+ }
1054
+ }
1055
+ // Authentication
1056
+ const authContext = await authenticate(request);
1057
+ if (matchedRoute.auth === true && !authContext) {
1058
+ return new Response(JSON.stringify({
1059
+ error: {
1060
+ message: 'Unauthorized',
1061
+ code: 'UNAUTHORIZED',
1062
+ },
1063
+ }), {
1064
+ status: 401,
1065
+ headers: {
1066
+ 'Content-Type': 'application/json',
1067
+ 'WWW-Authenticate': 'Bearer',
1068
+ },
1069
+ });
1070
+ }
1071
+ // Authorization
1072
+ const authResult = checkAuthorization(authContext, matchedRoute);
1073
+ if (!authResult.authorized) {
1074
+ return new Response(JSON.stringify({
1075
+ error: {
1076
+ message: 'Forbidden',
1077
+ code: 'FORBIDDEN',
1078
+ reason: authResult.reason,
1079
+ },
1080
+ }), { status: 403, headers: { 'Content-Type': 'application/json' } });
1081
+ }
1082
+ // Rate limiting
1083
+ if (matchedRoute.rateLimit) {
1084
+ const rateLimitKey = getRateLimitKey(request, matchedRoute.rateLimit, matchedRoute.fullPath, authContext);
1085
+ const rateLimitResult = checkRateLimit(rateLimitKey, matchedRoute.rateLimit);
1086
+ const rateLimitHeaders = {
1087
+ 'X-RateLimit-Limit': String(matchedRoute.rateLimit.requests),
1088
+ 'X-RateLimit-Remaining': String(rateLimitResult.remaining),
1089
+ 'X-RateLimit-Reset': String(Math.floor(rateLimitResult.reset / 1000)),
1090
+ };
1091
+ if (!rateLimitResult.allowed) {
1092
+ return new Response(JSON.stringify({
1093
+ error: {
1094
+ message: 'Too Many Requests',
1095
+ code: 'RATE_LIMIT_EXCEEDED',
1096
+ retryAfter: rateLimitResult.retryAfter,
1097
+ },
1098
+ }), {
1099
+ status: 429,
1100
+ headers: {
1101
+ 'Content-Type': 'application/json',
1102
+ 'Retry-After': String(rateLimitResult.retryAfter),
1103
+ ...rateLimitHeaders,
1104
+ },
1105
+ });
1106
+ }
1107
+ }
1108
+ // Throttling
1109
+ if (matchedRoute.throttle) {
1110
+ const throttleKey = getRateLimitKey(request, { requests: 0, windowMs: matchedRoute.throttle.windowMs, keyBy: 'ip' }, matchedRoute.fullPath, authContext);
1111
+ const throttleResult = checkThrottle(throttleKey, matchedRoute.throttle);
1112
+ if (!throttleResult.allowed) {
1113
+ return new Response(JSON.stringify({
1114
+ error: {
1115
+ message: 'Too Many Requests',
1116
+ code: 'THROTTLED',
1117
+ retryAfter: throttleResult.retryAfter,
1118
+ },
1119
+ }), {
1120
+ status: 429,
1121
+ headers: {
1122
+ 'Content-Type': 'application/json',
1123
+ 'Retry-After': String(throttleResult.retryAfter),
1124
+ },
1125
+ });
1126
+ }
1127
+ }
1128
+ // Parse request body for POST/PUT/PATCH
1129
+ let body = {};
1130
+ if (['POST', 'PUT', 'PATCH'].includes(method)) {
1131
+ try {
1132
+ body = await deserializeRequest(request);
1133
+ }
1134
+ catch (error) {
1135
+ return new Response(JSON.stringify({
1136
+ error: {
1137
+ message: error.message,
1138
+ code: 'INVALID_REQUEST_BODY',
1139
+ },
1140
+ }), { status: 400, headers: { 'Content-Type': 'application/json' } });
1141
+ }
1142
+ // Validate against schema
1143
+ if (matchedRoute.schema) {
1144
+ const validation = validateSchema(body, matchedRoute.schema);
1145
+ if (!validation.valid) {
1146
+ return new Response(JSON.stringify({
1147
+ error: {
1148
+ message: 'Validation failed',
1149
+ code: 'VALIDATION_ERROR',
1150
+ errors: validation.errors,
1151
+ },
1152
+ }), { status: 400, headers: { 'Content-Type': 'application/json' } });
1153
+ }
1154
+ }
1155
+ }
1156
+ // Parse query parameters
1157
+ const queryParams = {};
1158
+ for (const [key, value] of url.searchParams) {
1159
+ queryParams[key] = value;
1160
+ }
1161
+ // Build method arguments
1162
+ const args = [];
1163
+ // Add path params as first args
1164
+ const pathParamKeys = Object.keys(pathParams).filter((k) => pathParams[k] !== undefined);
1165
+ for (const key of pathParamKeys) {
1166
+ args.push(pathParams[key]);
1167
+ }
1168
+ // Add body for POST/PUT/PATCH
1169
+ if (['POST', 'PUT', 'PATCH'].includes(method)) {
1170
+ args.push(body);
1171
+ }
1172
+ // Add query params if configured
1173
+ if (matchedRoute.queryParams) {
1174
+ for (const param of matchedRoute.queryParams) {
1175
+ const value = queryParams[param];
1176
+ // Try to convert to number if it looks like one
1177
+ if (value !== undefined && /^\d+$/.test(value)) {
1178
+ args.push(parseInt(value, 10));
1179
+ }
1180
+ else {
1181
+ args.push(value);
1182
+ }
1183
+ }
1184
+ }
1185
+ // Get request ID
1186
+ const requestId = request.headers.get('X-Request-ID');
1187
+ try {
1188
+ // Call the method
1189
+ const inst = getInstance();
1190
+ const methodFn = inst[matchedRoute.methodName];
1191
+ if (typeof methodFn !== 'function') {
1192
+ throw new Error(`Method ${matchedRoute.methodName} not found`);
1193
+ }
1194
+ const result = await methodFn.apply(inst, args);
1195
+ // Handle streaming responses
1196
+ if (matchedRoute.streaming && result && typeof result[Symbol.asyncIterator] === 'function') {
1197
+ const encoder = new TextEncoder();
1198
+ const stream = new ReadableStream({
1199
+ async start(controller) {
1200
+ for await (const chunk of result) {
1201
+ controller.enqueue(encoder.encode(JSON.stringify(chunk) + '\n'));
1202
+ }
1203
+ controller.close();
1204
+ },
1205
+ });
1206
+ return new Response(stream, {
1207
+ status: 200,
1208
+ headers: {
1209
+ 'Content-Type': 'application/json',
1210
+ 'Transfer-Encoding': 'chunked',
1211
+ },
1212
+ });
1213
+ }
1214
+ // Handle conditional requests (ETag)
1215
+ if (matchedRoute.etag && method === 'GET') {
1216
+ const etag = generateETag(result);
1217
+ const ifNoneMatch = request.headers.get('If-None-Match');
1218
+ if (ifNoneMatch === etag) {
1219
+ return new Response(null, { status: 304, headers: { 'ETag': etag } });
1220
+ }
1221
+ const response = serializeResponse(result, { accept, method, produces: matchedRoute.produces });
1222
+ const headers = new Headers(response.headers);
1223
+ headers.set('ETag', etag);
1224
+ return new Response(response.body, { status: response.status, headers });
1225
+ }
1226
+ // Handle Last-Modified
1227
+ if (matchedRoute.lastModified && method === 'GET' && result && typeof result === 'object') {
1228
+ const updatedAt = result.updatedAt;
1229
+ if (updatedAt instanceof Date) {
1230
+ const lastModified = updatedAt.toUTCString();
1231
+ const ifModifiedSince = request.headers.get('If-Modified-Since');
1232
+ if (ifModifiedSince && new Date(ifModifiedSince) >= updatedAt) {
1233
+ return new Response(null, { status: 304, headers: { 'Last-Modified': lastModified } });
1234
+ }
1235
+ const response = serializeResponse(result, { accept, method, produces: matchedRoute.produces });
1236
+ const headers = new Headers(response.headers);
1237
+ headers.set('Last-Modified', lastModified);
1238
+ return new Response(response.body, { status: response.status, headers });
1239
+ }
1240
+ }
1241
+ // Build response
1242
+ const response = serializeResponse(result, { accept, method, produces: matchedRoute.produces });
1243
+ const headers = new Headers(response.headers);
1244
+ // Add cache headers
1245
+ if (matchedRoute.cache && method === 'GET') {
1246
+ const cacheControl = [];
1247
+ if (matchedRoute.cache.maxAge !== undefined) {
1248
+ cacheControl.push(`max-age=${matchedRoute.cache.maxAge}`);
1249
+ }
1250
+ if (matchedRoute.cache.staleWhileRevalidate !== undefined) {
1251
+ cacheControl.push(`stale-while-revalidate=${matchedRoute.cache.staleWhileRevalidate}`);
1252
+ }
1253
+ if (cacheControl.length > 0) {
1254
+ headers.set('Cache-Control', cacheControl.join(', '));
1255
+ }
1256
+ }
1257
+ else if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
1258
+ headers.set('Cache-Control', 'no-store');
1259
+ }
1260
+ // Add rate limit headers
1261
+ if (matchedRoute.rateLimit) {
1262
+ const rateLimitKey = getRateLimitKey(request, matchedRoute.rateLimit, matchedRoute.fullPath, authContext);
1263
+ const state = rateLimitState.get(rateLimitKey);
1264
+ if (state) {
1265
+ headers.set('X-RateLimit-Limit', String(matchedRoute.rateLimit.requests));
1266
+ headers.set('X-RateLimit-Remaining', String(Math.max(0, matchedRoute.rateLimit.requests - state.count)));
1267
+ headers.set('X-RateLimit-Reset', String(Math.floor((state.windowStart + matchedRoute.rateLimit.windowMs) / 1000)));
1268
+ }
1269
+ }
1270
+ // Handle HEAD - return headers but no body
1271
+ if (method === 'HEAD') {
1272
+ const bodyText = await response.text();
1273
+ headers.set('Content-Length', String(new TextEncoder().encode(bodyText).length));
1274
+ return new Response(null, { status: response.status, headers });
1275
+ }
1276
+ return new Response(response.body, { status: response.status, headers });
1277
+ }
1278
+ catch (error) {
1279
+ const err = error;
1280
+ const restError = mapHttpError(err);
1281
+ const errorBody = {
1282
+ error: {
1283
+ message: restError.message,
1284
+ code: restError.code || err.name.toUpperCase().replace('ERROR', '').replace(/\s+/g, '_') || 'INTERNAL_ERROR',
1285
+ ...(restError.errors && { errors: restError.errors }),
1286
+ ...(restError.retryAfter && { retryAfter: restError.retryAfter }),
1287
+ ...(requestId && { requestId }),
1288
+ ...(debug && { stack: err.stack }),
1289
+ },
1290
+ };
1291
+ const headers = {
1292
+ 'Content-Type': 'application/json',
1293
+ };
1294
+ if (restError.retryAfter) {
1295
+ headers['Retry-After'] = String(restError.retryAfter);
1296
+ }
1297
+ return new Response(JSON.stringify(errorBody), {
1298
+ status: restError.status,
1299
+ headers,
1300
+ });
1301
+ }
1302
+ }
1303
+ /**
1304
+ * Generate OpenAPI spec
1305
+ */
1306
+ function getOpenAPISpec() {
1307
+ const className = DOClass.name || 'API';
1308
+ const spec = {
1309
+ openapi: '3.0.0',
1310
+ info: {
1311
+ title: `${className} API`,
1312
+ version: '1.0.0',
1313
+ },
1314
+ paths: {},
1315
+ };
1316
+ for (const route of routesWithFullPath) {
1317
+ // Convert path params from :param to {param}
1318
+ const openApiPath = route.fullPath.replace(/:(\w+)/g, '{$1}');
1319
+ if (!spec.paths[openApiPath]) {
1320
+ spec.paths[openApiPath] = {};
1321
+ }
1322
+ const operation = {
1323
+ summary: `${route.httpMethod} ${route.methodName}`,
1324
+ parameters: [],
1325
+ responses: {
1326
+ '200': { description: 'Success' },
1327
+ '201': { description: 'Created' },
1328
+ '400': { description: 'Bad Request' },
1329
+ '401': { description: 'Unauthorized' },
1330
+ '403': { description: 'Forbidden' },
1331
+ '404': { description: 'Not Found' },
1332
+ '500': { description: 'Internal Server Error' },
1333
+ },
1334
+ };
1335
+ // Add path parameters
1336
+ const pathParams = route.path.match(/:(\w+)/g) || [];
1337
+ for (const param of pathParams) {
1338
+ const paramName = param.slice(1);
1339
+ operation.parameters.push({
1340
+ name: paramName,
1341
+ in: 'path',
1342
+ required: true,
1343
+ schema: { type: 'string' },
1344
+ });
1345
+ }
1346
+ // Add query parameters
1347
+ if (route.queryParams) {
1348
+ for (const param of route.queryParams) {
1349
+ operation.parameters.push({
1350
+ name: param,
1351
+ in: 'query',
1352
+ schema: { type: 'string' },
1353
+ });
1354
+ }
1355
+ }
1356
+ // Add request body for POST/PUT/PATCH
1357
+ if (['POST', 'PUT', 'PATCH'].includes(route.httpMethod)) {
1358
+ operation.requestBody = {
1359
+ required: true,
1360
+ content: {
1361
+ 'application/json': {
1362
+ schema: route.schema || { type: 'object' },
1363
+ },
1364
+ },
1365
+ };
1366
+ }
1367
+ // Add security requirements
1368
+ if (route.auth) {
1369
+ operation.security = [{ bearerAuth: [] }];
1370
+ // Add security scheme to components
1371
+ if (!spec.components) {
1372
+ spec.components = { securitySchemes: {} };
1373
+ }
1374
+ spec.components.securitySchemes.bearerAuth = {
1375
+ type: 'http',
1376
+ scheme: 'bearer',
1377
+ };
1378
+ }
1379
+ // Add rate limit info
1380
+ if (route.rateLimit) {
1381
+ operation['x-ratelimit'] = {
1382
+ requests: route.rateLimit.requests,
1383
+ windowMs: route.rateLimit.windowMs,
1384
+ };
1385
+ }
1386
+ spec.paths[openApiPath][route.httpMethod.toLowerCase()] = operation;
1387
+ }
1388
+ return spec;
1389
+ }
1390
+ return {
1391
+ fetch,
1392
+ routes: routesWithFullPath,
1393
+ middleware,
1394
+ getOpenAPISpec,
1395
+ };
1396
+ }
1397
+ import { buildErrorResponse, } from './shared';
1398
+ /**
1399
+ * Cache for discovered routes per DO class
1400
+ */
1401
+ const routeDiscoveryCache = new Map();
1402
+ /**
1403
+ * REST API Handler implementing TransportHandler interface
1404
+ *
1405
+ * Provides REST API functionality with:
1406
+ * - Automatic route discovery from DO class $rest config
1407
+ * - HTTP method mapping (GET, POST, PUT, DELETE, PATCH)
1408
+ * - Request/response serialization
1409
+ * - Schema validation
1410
+ * - Rate limiting
1411
+ * - CORS support
1412
+ *
1413
+ * @example
1414
+ * ```typescript
1415
+ * const restHandler = new RestHandler({
1416
+ * basePath: '/api',
1417
+ * debug: true,
1418
+ * })
1419
+ *
1420
+ * // Use in handler chain
1421
+ * chain.use(restHandler, 50)
1422
+ * ```
1423
+ */
1424
+ export class RestHandler {
1425
+ name = 'rest';
1426
+ options;
1427
+ router = null;
1428
+ DOClass = null;
1429
+ constructor(options = {}) {
1430
+ this.options = options;
1431
+ }
1432
+ /**
1433
+ * Check if this handler can process the request
1434
+ * REST handler can handle requests that match configured routes
1435
+ */
1436
+ canHandle(request) {
1437
+ const url = new URL(request.url);
1438
+ const basePath = this.options.basePath || this.options.apiPrefix || '';
1439
+ // Quick check: does the path start with our base path?
1440
+ if (basePath && !url.pathname.startsWith(basePath)) {
1441
+ return { canHandle: false, reason: 'Path does not match base path' };
1442
+ }
1443
+ // REST handler has lower priority than protocol-specific handlers
1444
+ return {
1445
+ canHandle: true,
1446
+ priority: 30,
1447
+ };
1448
+ }
1449
+ /**
1450
+ * Handle the REST request
1451
+ */
1452
+ async handle(request, context) {
1453
+ // Initialize router if not already done
1454
+ if (!this.router || this.needsRouterRefresh(context)) {
1455
+ this.initializeRouter(context);
1456
+ }
1457
+ if (!this.router) {
1458
+ return buildErrorResponse({ message: 'REST router not initialized', code: 'ROUTER_NOT_INITIALIZED' }, 500);
1459
+ }
1460
+ return this.router.fetch(request);
1461
+ }
1462
+ /**
1463
+ * Check if router needs to be refreshed (e.g., different DO class)
1464
+ */
1465
+ needsRouterRefresh(context) {
1466
+ if (!this.DOClass)
1467
+ return true;
1468
+ return this.DOClass !== context.instance.constructor;
1469
+ }
1470
+ /**
1471
+ * Initialize the REST router from context
1472
+ */
1473
+ initializeRouter(context) {
1474
+ this.DOClass = context.instance.constructor;
1475
+ this.router = createRestRouter(this.DOClass, {
1476
+ basePath: this.options.basePath,
1477
+ apiPrefix: this.options.apiPrefix,
1478
+ cors: this.options.cors,
1479
+ authHandler: this.options.authHandler,
1480
+ debug: this.options.debug,
1481
+ });
1482
+ }
1483
+ /**
1484
+ * Get cached routes for a DO class
1485
+ */
1486
+ static getCachedRoutes(DOClass) {
1487
+ if (routeDiscoveryCache.has(DOClass)) {
1488
+ return routeDiscoveryCache.get(DOClass);
1489
+ }
1490
+ const routes = getRestRoutes(DOClass);
1491
+ routeDiscoveryCache.set(DOClass, routes);
1492
+ return routes;
1493
+ }
1494
+ /**
1495
+ * Clear route cache
1496
+ */
1497
+ static clearCache() {
1498
+ routeDiscoveryCache.clear();
1499
+ }
1500
+ /**
1501
+ * Dispose handler resources
1502
+ */
1503
+ dispose() {
1504
+ this.router = null;
1505
+ this.DOClass = null;
1506
+ }
1507
+ }
1508
+ //# sourceMappingURL=rest-autowire.js.map