dotdo 0.0.1 → 0.0.2

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