@edge-base/server 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/admin-build/.gitkeep +0 -0
  2. package/admin-build/_app/env.js +1 -0
  3. package/admin-build/_app/immutable/assets/0.Bm6cF078.css +1 -0
  4. package/admin-build/_app/immutable/assets/1.BfW3pUNa.css +1 -0
  5. package/admin-build/_app/immutable/assets/11.CVmQOewb.css +1 -0
  6. package/admin-build/_app/immutable/assets/12.B1EhbRZT.css +1 -0
  7. package/admin-build/_app/immutable/assets/13.BvwYeuwE.css +1 -0
  8. package/admin-build/_app/immutable/assets/14.CdVfcO0R.css +1 -0
  9. package/admin-build/_app/immutable/assets/15.2yeZ66b-.css +1 -0
  10. package/admin-build/_app/immutable/assets/17.BVg0JEVu.css +1 -0
  11. package/admin-build/_app/immutable/assets/18.Rwnl3x_i.css +1 -0
  12. package/admin-build/_app/immutable/assets/20.DsPWA9AV.css +1 -0
  13. package/admin-build/_app/immutable/assets/21.Dz2RJ56c.css +1 -0
  14. package/admin-build/_app/immutable/assets/22.DwNLk5Ai.css +1 -0
  15. package/admin-build/_app/immutable/assets/23.CFpu0gOO.css +1 -0
  16. package/admin-build/_app/immutable/assets/24.Cy5LBeoJ.css +1 -0
  17. package/admin-build/_app/immutable/assets/25.pUyLVf-h.css +1 -0
  18. package/admin-build/_app/immutable/assets/26.DBcGrlXa.css +1 -0
  19. package/admin-build/_app/immutable/assets/27.BswYyAJD.css +1 -0
  20. package/admin-build/_app/immutable/assets/28.B4ueB1Kf.css +1 -0
  21. package/admin-build/_app/immutable/assets/29.B-qU6PdF.css +1 -0
  22. package/admin-build/_app/immutable/assets/3.Dg81Pgmd.css +1 -0
  23. package/admin-build/_app/immutable/assets/30.CsdWum94.css +1 -0
  24. package/admin-build/_app/immutable/assets/31.U6OwIp50.css +1 -0
  25. package/admin-build/_app/immutable/assets/4.CyawCCux.css +1 -0
  26. package/admin-build/_app/immutable/assets/5.C0YO2HTk.css +1 -0
  27. package/admin-build/_app/immutable/assets/8.Br5jd6kD.css +1 -0
  28. package/admin-build/_app/immutable/assets/Badge.EMYLHBxE.css +1 -0
  29. package/admin-build/_app/immutable/assets/Button.DpzMRTjK.css +1 -0
  30. package/admin-build/_app/immutable/assets/ConfirmDialog.DAnaWRRk.css +1 -0
  31. package/admin-build/_app/immutable/assets/EmptyState.CwKsu57Y.css +1 -0
  32. package/admin-build/_app/immutable/assets/Input.BDUSenmU.css +1 -0
  33. package/admin-build/_app/immutable/assets/Modal.Dm5B0Xie.css +1 -0
  34. package/admin-build/_app/immutable/assets/PageShell.CmU-Xh-b.css +1 -0
  35. package/admin-build/_app/immutable/assets/SchemaFieldEditor.g4NsCdno.css +1 -0
  36. package/admin-build/_app/immutable/assets/Select.BW4Keufm.css +1 -0
  37. package/admin-build/_app/immutable/assets/Skeleton.KWUulTKJ.css +1 -0
  38. package/admin-build/_app/immutable/assets/Tabs.CniGYb67.css +1 -0
  39. package/admin-build/_app/immutable/assets/TimeChart.BTCDAvmT.css +1 -0
  40. package/admin-build/_app/immutable/assets/Toggle.Cy_K12OM.css +1 -0
  41. package/admin-build/_app/immutable/assets/TopList.ClFzmPlA.css +1 -0
  42. package/admin-build/_app/immutable/chunks/7B47DvSx.js +1 -0
  43. package/admin-build/_app/immutable/chunks/7f08Id8e.js +1 -0
  44. package/admin-build/_app/immutable/chunks/8wJeQ7LN.js +1 -0
  45. package/admin-build/_app/immutable/chunks/B-h2afW5.js +1 -0
  46. package/admin-build/_app/immutable/chunks/B8vJP3wz.js +1 -0
  47. package/admin-build/_app/immutable/chunks/BR_fL5Yv.js +1 -0
  48. package/admin-build/_app/immutable/chunks/BY92tFS2.js +1 -0
  49. package/admin-build/_app/immutable/chunks/BcR-Rdj9.js +1 -0
  50. package/admin-build/_app/immutable/chunks/BdrwyZv8.js +1 -0
  51. package/admin-build/_app/immutable/chunks/Bh56EfQ_.js +1 -0
  52. package/admin-build/_app/immutable/chunks/BkrCkgYp.js +1 -0
  53. package/admin-build/_app/immutable/chunks/BmRjiP5k.js +1 -0
  54. package/admin-build/_app/immutable/chunks/BsokvhWC.js +1 -0
  55. package/admin-build/_app/immutable/chunks/C4D51vTW.js +1 -0
  56. package/admin-build/_app/immutable/chunks/C6puvcoR.js +2 -0
  57. package/admin-build/_app/immutable/chunks/CCKNu7m7.js +1 -0
  58. package/admin-build/_app/immutable/chunks/CWj6FrbW.js +1 -0
  59. package/admin-build/_app/immutable/chunks/Ce-ngf4p.js +5 -0
  60. package/admin-build/_app/immutable/chunks/Cs0GwzJA.js +1 -0
  61. package/admin-build/_app/immutable/chunks/CwROoZK0.js +1 -0
  62. package/admin-build/_app/immutable/chunks/CxCPv_Ut.js +1 -0
  63. package/admin-build/_app/immutable/chunks/CxbRue-5.js +1 -0
  64. package/admin-build/_app/immutable/chunks/CyqB6g-D.js +1 -0
  65. package/admin-build/_app/immutable/chunks/D5h5A1cc.js +2 -0
  66. package/admin-build/_app/immutable/chunks/DnyL7Zq-.js +1 -0
  67. package/admin-build/_app/immutable/chunks/DoPXzH7F.js +1 -0
  68. package/admin-build/_app/immutable/chunks/DrQSgw-f.js +1 -0
  69. package/admin-build/_app/immutable/chunks/DttM2zNO.js +1 -0
  70. package/admin-build/_app/immutable/chunks/DuXuUBWN.js +1 -0
  71. package/admin-build/_app/immutable/chunks/MdeqaOQx.js +10 -0
  72. package/admin-build/_app/immutable/chunks/NuUjtcO2.js +1 -0
  73. package/admin-build/_app/immutable/chunks/Q2nPFxS6.js +1 -0
  74. package/admin-build/_app/immutable/chunks/R6arueIl.js +1 -0
  75. package/admin-build/_app/immutable/chunks/UUazaC_N.js +1 -0
  76. package/admin-build/_app/immutable/chunks/cOYbrQxx.js +1 -0
  77. package/admin-build/_app/immutable/chunks/eFQHTGwA.js +1 -0
  78. package/admin-build/_app/immutable/chunks/ehbppgYb.js +1 -0
  79. package/admin-build/_app/immutable/chunks/glwixJlP.js +1 -0
  80. package/admin-build/_app/immutable/chunks/vApWTCBs.js +1 -0
  81. package/admin-build/_app/immutable/chunks/w89G9Xpi.js +1 -0
  82. package/admin-build/_app/immutable/chunks/wJsUhbfZ.js +1 -0
  83. package/admin-build/_app/immutable/chunks/zfauFM8P.js +1 -0
  84. package/admin-build/_app/immutable/entry/app.CcO-Uos3.js +2 -0
  85. package/admin-build/_app/immutable/entry/start.COebYq3I.js +1 -0
  86. package/admin-build/_app/immutable/nodes/0.CjtHKU-6.js +1 -0
  87. package/admin-build/_app/immutable/nodes/1.DEisjlM0.js +1 -0
  88. package/admin-build/_app/immutable/nodes/10.CvhdyWVB.js +1 -0
  89. package/admin-build/_app/immutable/nodes/11.DjHqcOvy.js +1 -0
  90. package/admin-build/_app/immutable/nodes/12.mQLz4Mj_.js +1 -0
  91. package/admin-build/_app/immutable/nodes/13.CBonZZyP.js +110 -0
  92. package/admin-build/_app/immutable/nodes/14.d-oiZL0j.js +3 -0
  93. package/admin-build/_app/immutable/nodes/15.CKPQsUYF.js +1 -0
  94. package/admin-build/_app/immutable/nodes/16.wPzAPQGx.js +1 -0
  95. package/admin-build/_app/immutable/nodes/17.DayhKyEZ.js +1 -0
  96. package/admin-build/_app/immutable/nodes/18.DKwS0Ir0.js +1 -0
  97. package/admin-build/_app/immutable/nodes/19.wPzAPQGx.js +1 -0
  98. package/admin-build/_app/immutable/nodes/2.BKoKrw1i.js +1 -0
  99. package/admin-build/_app/immutable/nodes/20.BvIkkkrW.js +1 -0
  100. package/admin-build/_app/immutable/nodes/21.DMaFhdHk.js +128 -0
  101. package/admin-build/_app/immutable/nodes/22.3xdgwuK1.js +1 -0
  102. package/admin-build/_app/immutable/nodes/23.8Bvgjbsl.js +112 -0
  103. package/admin-build/_app/immutable/nodes/24.DzSSzRhG.js +2 -0
  104. package/admin-build/_app/immutable/nodes/25.9KKYBnAE.js +2 -0
  105. package/admin-build/_app/immutable/nodes/26.Bhn9dfhY.js +1 -0
  106. package/admin-build/_app/immutable/nodes/27.kRLiC24G.js +1 -0
  107. package/admin-build/_app/immutable/nodes/28.BVIN1-7N.js +1 -0
  108. package/admin-build/_app/immutable/nodes/29.3yabZWj4.js +1 -0
  109. package/admin-build/_app/immutable/nodes/3.BFtSOkX7.js +2 -0
  110. package/admin-build/_app/immutable/nodes/30.CyCQlwaP.js +1 -0
  111. package/admin-build/_app/immutable/nodes/31.C4LDXjES.js +1 -0
  112. package/admin-build/_app/immutable/nodes/4.CvbiMlCa.js +1 -0
  113. package/admin-build/_app/immutable/nodes/5.C6BLv2eM.js +1 -0
  114. package/admin-build/_app/immutable/nodes/6.BcXvfl2P.js +1 -0
  115. package/admin-build/_app/immutable/nodes/7.CIuqhPiK.js +1 -0
  116. package/admin-build/_app/immutable/nodes/8.BQOR_JfO.js +1 -0
  117. package/admin-build/_app/immutable/nodes/9.NZqXQxPy.js +1 -0
  118. package/admin-build/_app/version.json +1 -0
  119. package/admin-build/favicon.svg +26 -0
  120. package/admin-build/index.html +45 -0
  121. package/openapi.json +19543 -0
  122. package/package.json +66 -0
  123. package/src/__tests__/admin-assets.test.ts +55 -0
  124. package/src/__tests__/admin-data-routes.test.ts +488 -0
  125. package/src/__tests__/admin-db-target.test.ts +103 -0
  126. package/src/__tests__/admin-routing.test.ts +31 -0
  127. package/src/__tests__/admin-user-management.test.ts +311 -0
  128. package/src/__tests__/analytics-query.test.ts +75 -0
  129. package/src/__tests__/auth-d1.test.ts +749 -0
  130. package/src/__tests__/auth-db-adapter.test.ts +73 -0
  131. package/src/__tests__/auth-jwt.test.ts +440 -0
  132. package/src/__tests__/auth-oauth.test.ts +389 -0
  133. package/src/__tests__/auth-password.test.ts +367 -0
  134. package/src/__tests__/auth-redirect.test.ts +87 -0
  135. package/src/__tests__/backup-restore.test.ts +711 -0
  136. package/src/__tests__/broadcast.test.ts +128 -0
  137. package/src/__tests__/cli.test.ts +178 -0
  138. package/src/__tests__/cloudflare-realtime.test.ts +113 -0
  139. package/src/__tests__/config.test.ts +469 -0
  140. package/src/__tests__/cors.test.ts +154 -0
  141. package/src/__tests__/cron.test.ts +302 -0
  142. package/src/__tests__/d1-handler.test.ts +402 -0
  143. package/src/__tests__/d1-sql.test.ts +120 -0
  144. package/src/__tests__/database-live-config.test.ts +42 -0
  145. package/src/__tests__/database-live-emitter.test.ts +56 -0
  146. package/src/__tests__/database-live-filters.test.ts +63 -0
  147. package/src/__tests__/database-live-route.test.ts +113 -0
  148. package/src/__tests__/db-sql.test.ts +163 -0
  149. package/src/__tests__/do-lifecycle.test.ts +263 -0
  150. package/src/__tests__/do-router.test.ts +729 -0
  151. package/src/__tests__/email-provider.test.ts +128 -0
  152. package/src/__tests__/email-templates.test.ts +528 -0
  153. package/src/__tests__/error-format.test.ts +250 -0
  154. package/src/__tests__/field-ops.test.ts +242 -0
  155. package/src/__tests__/functions-context.test.ts +334 -0
  156. package/src/__tests__/functions-d1-proxy.test.ts +229 -0
  157. package/src/__tests__/functions-registry-runtime-config.test.ts +17 -0
  158. package/src/__tests__/functions-route.test.ts +139 -0
  159. package/src/__tests__/internal-request.test.ts +77 -0
  160. package/src/__tests__/log-writer.test.ts +44 -0
  161. package/src/__tests__/logger.test.ts +58 -0
  162. package/src/__tests__/meta-admin-proxy.test.ts +48 -0
  163. package/src/__tests__/meta-export-coverage.test.ts +191 -0
  164. package/src/__tests__/meta-route-registration.test.ts +47 -0
  165. package/src/__tests__/namespace-dump.test.ts +28 -0
  166. package/src/__tests__/oauth-providers.test.ts +337 -0
  167. package/src/__tests__/openapi-coverage.test.ts +144 -0
  168. package/src/__tests__/pagination.test.ts +59 -0
  169. package/src/__tests__/password-policy.test.ts +191 -0
  170. package/src/__tests__/plugin-migrations.test.ts +379 -0
  171. package/src/__tests__/postgres-batch-compat.test.ts +133 -0
  172. package/src/__tests__/postgres-dialect.test.ts +328 -0
  173. package/src/__tests__/postgres-executor.test.ts +79 -0
  174. package/src/__tests__/postgres-field-ops-compat.test.ts +222 -0
  175. package/src/__tests__/postgres-schema-init.test.ts +105 -0
  176. package/src/__tests__/postgres-table-utils.test.ts +107 -0
  177. package/src/__tests__/presence.test.ts +199 -0
  178. package/src/__tests__/provider.test.ts +550 -0
  179. package/src/__tests__/public-user-profile.test.ts +339 -0
  180. package/src/__tests__/push-handlers.test.ts +179 -0
  181. package/src/__tests__/push-provider.test.ts +80 -0
  182. package/src/__tests__/push-token.test.ts +418 -0
  183. package/src/__tests__/query.test.ts +771 -0
  184. package/src/__tests__/rate-limit.test.ts +260 -0
  185. package/src/__tests__/room-access-policy.test.ts +101 -0
  186. package/src/__tests__/room-handler-context.test.ts +130 -0
  187. package/src/__tests__/room-monitoring.test.ts +138 -0
  188. package/src/__tests__/room-runtime-routing.test.ts +222 -0
  189. package/src/__tests__/room.test.ts +254 -0
  190. package/src/__tests__/route-parser.test.ts +490 -0
  191. package/src/__tests__/rules.test.ts +234 -0
  192. package/src/__tests__/runtime-surface-accounting.test.ts +120 -0
  193. package/src/__tests__/scheduled.test.ts +80 -0
  194. package/src/__tests__/schema.test.ts +1273 -0
  195. package/src/__tests__/security-hardening.test.ts +312 -0
  196. package/src/__tests__/server.unit.test.ts +333 -0
  197. package/src/__tests__/service-key-db-proxy.test.ts +650 -0
  198. package/src/__tests__/service-key-provider-bypass.test.ts +138 -0
  199. package/src/__tests__/service-key.test.ts +757 -0
  200. package/src/__tests__/smoke-skip-report.test.ts +72 -0
  201. package/src/__tests__/sms-provider.test.ts +39 -0
  202. package/src/__tests__/sql-route.test.ts +218 -0
  203. package/src/__tests__/storage-hook-context.test.ts +115 -0
  204. package/src/__tests__/totp.test.ts +200 -0
  205. package/src/__tests__/uuid.test.ts +144 -0
  206. package/src/__tests__/validation.test.ts +773 -0
  207. package/src/__tests__/websocket-pending.test.ts +163 -0
  208. package/src/_functions-registry.ts +51 -0
  209. package/src/bench-entry.ts +9 -0
  210. package/src/cloudflare-test.d.ts +1 -0
  211. package/src/durable-objects/auth-do.ts +49 -0
  212. package/src/durable-objects/database-do.ts +2240 -0
  213. package/src/durable-objects/database-live-do.ts +949 -0
  214. package/src/durable-objects/logs-do.ts +1200 -0
  215. package/src/durable-objects/room-runtime-base.ts +1604 -0
  216. package/src/durable-objects/rooms-do.ts +2191 -0
  217. package/src/generated-config.ts +6 -0
  218. package/src/index.ts +382 -0
  219. package/src/lib/admin-assets.ts +54 -0
  220. package/src/lib/admin-db-target.ts +301 -0
  221. package/src/lib/admin-routing.ts +35 -0
  222. package/src/lib/admin-user-management.ts +464 -0
  223. package/src/lib/analytics-adapter.ts +103 -0
  224. package/src/lib/analytics-query.ts +579 -0
  225. package/src/lib/auth-d1-service.ts +1193 -0
  226. package/src/lib/auth-d1.ts +1056 -0
  227. package/src/lib/auth-db-adapter.ts +289 -0
  228. package/src/lib/auth-redirect.ts +116 -0
  229. package/src/lib/cidr.ts +115 -0
  230. package/src/lib/client-ip.ts +51 -0
  231. package/src/lib/cloudflare-realtime.ts +251 -0
  232. package/src/lib/control-db.ts +36 -0
  233. package/src/lib/cron.ts +163 -0
  234. package/src/lib/d1-handler.ts +1425 -0
  235. package/src/lib/d1-schema-init.ts +255 -0
  236. package/src/lib/d1-sql.ts +33 -0
  237. package/src/lib/database-live-config.ts +24 -0
  238. package/src/lib/database-live-emitter.ts +111 -0
  239. package/src/lib/db-sql.ts +66 -0
  240. package/src/lib/do-retry.ts +36 -0
  241. package/src/lib/do-router.ts +270 -0
  242. package/src/lib/do-sql.ts +73 -0
  243. package/src/lib/email-provider.ts +379 -0
  244. package/src/lib/email-templates.ts +285 -0
  245. package/src/lib/email-translations.ts +422 -0
  246. package/src/lib/errors.ts +151 -0
  247. package/src/lib/functions.ts +2091 -0
  248. package/src/lib/hono.ts +56 -0
  249. package/src/lib/internal-request.ts +56 -0
  250. package/src/lib/jwt.ts +354 -0
  251. package/src/lib/log-writer.ts +272 -0
  252. package/src/lib/namespace-dump.ts +125 -0
  253. package/src/lib/oauth-providers.ts +1225 -0
  254. package/src/lib/op-parser.ts +99 -0
  255. package/src/lib/openapi.ts +146 -0
  256. package/src/lib/pagination.ts +19 -0
  257. package/src/lib/password-policy.ts +102 -0
  258. package/src/lib/password.ts +145 -0
  259. package/src/lib/plugin-migrations.ts +612 -0
  260. package/src/lib/postgres-executor.ts +203 -0
  261. package/src/lib/postgres-handler.ts +1102 -0
  262. package/src/lib/postgres-schema-init.ts +341 -0
  263. package/src/lib/postgres-table-utils.ts +87 -0
  264. package/src/lib/public-user-profile.ts +187 -0
  265. package/src/lib/push-provider.ts +409 -0
  266. package/src/lib/push-token.ts +294 -0
  267. package/src/lib/query-engine.ts +768 -0
  268. package/src/lib/room-monitoring.ts +97 -0
  269. package/src/lib/room-runtime.ts +14 -0
  270. package/src/lib/route-parser.ts +434 -0
  271. package/src/lib/schema.ts +538 -0
  272. package/src/lib/schemas.ts +152 -0
  273. package/src/lib/service-key.ts +419 -0
  274. package/src/lib/sms-provider.ts +230 -0
  275. package/src/lib/startup-config.ts +99 -0
  276. package/src/lib/totp.ts +242 -0
  277. package/src/lib/uuid.ts +87 -0
  278. package/src/lib/validation.ts +205 -0
  279. package/src/lib/version.ts +2 -0
  280. package/src/lib/websocket-pending.ts +40 -0
  281. package/src/middleware/auth.ts +169 -0
  282. package/src/middleware/captcha-verify.ts +217 -0
  283. package/src/middleware/cors.ts +159 -0
  284. package/src/middleware/error-handler.ts +54 -0
  285. package/src/middleware/internal-guard.ts +26 -0
  286. package/src/middleware/logger.ts +126 -0
  287. package/src/middleware/rate-limit.ts +283 -0
  288. package/src/middleware/rules.ts +475 -0
  289. package/src/routes/admin-auth.ts +447 -0
  290. package/src/routes/admin.ts +3501 -0
  291. package/src/routes/analytics-api.ts +290 -0
  292. package/src/routes/auth.ts +4222 -0
  293. package/src/routes/backup.ts +1466 -0
  294. package/src/routes/config.ts +53 -0
  295. package/src/routes/d1.ts +109 -0
  296. package/src/routes/database-live.ts +281 -0
  297. package/src/routes/functions.ts +155 -0
  298. package/src/routes/health.ts +32 -0
  299. package/src/routes/kv.ts +167 -0
  300. package/src/routes/oauth.ts +1055 -0
  301. package/src/routes/push.ts +1465 -0
  302. package/src/routes/room.ts +639 -0
  303. package/src/routes/schema-endpoint.ts +76 -0
  304. package/src/routes/sql.ts +176 -0
  305. package/src/routes/storage.ts +1674 -0
  306. package/src/routes/tables.ts +699 -0
  307. package/src/routes/users.ts +21 -0
  308. package/src/routes/vectorize.ts +372 -0
  309. package/src/types.ts +99 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * DO instance routing utilities.
3
+ *
4
+ * Naming convention: `namespace:id`
5
+ * shared — static DB (no id)
6
+ * workspace:ws-456 — dynamic DB (namespace: workspace, id: ws-456)
7
+ * user:user-123 — dynamic DB (namespace: user, id: user-123)
8
+ *
9
+ * Constraint: `id` must NOT contain `:` character.
10
+ */
11
+ import { materializeConfig, type EdgeBaseConfig } from '@edge-base/shared';
12
+
13
+ const RUNTIME_CONFIG_GLOBAL_KEY = '__EDGEBASE_RUNTIME_CONFIG__';
14
+
15
+ // ─── DO Instance ID Generation (§2) ───
16
+
17
+ /**
18
+ * Get the DO instance name for a database namespace + optional id.
19
+ * Format: `namespace:id` or just `namespace` for static DBs.
20
+ *
21
+ * @throws 400-style error if id contains `:` character.
22
+ */
23
+ export function getDbDoName(namespace: string, id?: string): string {
24
+ if (!id) {
25
+ // Static DB (e.g. 'shared') — no id
26
+ return namespace;
27
+ }
28
+
29
+ // Guard against `:` in id — would break DO name parsing (§2)
30
+ if (id.includes(':')) {
31
+ throw new Error(
32
+ `Invalid id '${id}': id must not contain ':' character. ` +
33
+ `Use a different separator (e.g. dash '-') in your id.`,
34
+ );
35
+ }
36
+
37
+ return `${namespace}:${id}`;
38
+ }
39
+
40
+ /**
41
+ * Parse a DO name back into { namespace, id }.
42
+ * Used by database-do.ts to identify which DB block it belongs to. (§23)
43
+ */
44
+ export function parseDbDoName(doName: string): { namespace: string; id?: string } {
45
+ const colonIdx = doName.indexOf(':');
46
+ if (colonIdx === -1) {
47
+ return { namespace: doName };
48
+ }
49
+ return {
50
+ namespace: doName.slice(0, colonIdx),
51
+ id: doName.slice(colonIdx + 1),
52
+ };
53
+ }
54
+
55
+ // ─── DO Stub Call Helper ───
56
+
57
+ /**
58
+ * Call a Durable Object by its instance name.
59
+ * Worker → DO bridge layer.
60
+ */
61
+ export async function callDO(
62
+ namespace: DurableObjectNamespace,
63
+ doName: string,
64
+ path: string,
65
+ options: {
66
+ method?: string;
67
+ body?: unknown;
68
+ headers?: Record<string, string>;
69
+ } = {},
70
+ ): Promise<Response> {
71
+ const doId = namespace.idFromName(doName);
72
+ const stub = namespace.get(doId);
73
+
74
+ const init: RequestInit = {
75
+ method: options.method || 'GET',
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ 'X-DO-Name': doName,
79
+ ...options.headers,
80
+ },
81
+ };
82
+
83
+ if (options.body !== undefined && options.method !== 'GET') {
84
+ init.body = JSON.stringify(options.body);
85
+ }
86
+
87
+ return stub.fetch(`http://do${path}`, init);
88
+ }
89
+
90
+ /**
91
+ * Call a Durable Object by its raw hex ID (from Cloudflare REST API).
92
+ * Used for Edge backup enumeration where CF API returns hex IDs.
93
+ */
94
+ export async function callDOByHexId(
95
+ namespace: DurableObjectNamespace,
96
+ hexId: string,
97
+ path: string,
98
+ options: {
99
+ method?: string;
100
+ body?: unknown;
101
+ headers?: Record<string, string>;
102
+ } = {},
103
+ ): Promise<Response> {
104
+ const doId = namespace.idFromString(hexId);
105
+ const stub = namespace.get(doId);
106
+
107
+ const init: RequestInit = {
108
+ method: options.method || 'GET',
109
+ headers: {
110
+ 'Content-Type': 'application/json',
111
+ ...options.headers,
112
+ },
113
+ };
114
+
115
+ if (options.body !== undefined && options.method !== 'GET') {
116
+ init.body = JSON.stringify(options.body);
117
+ }
118
+
119
+ return stub.fetch(`http://do${path}`, init);
120
+ }
121
+
122
+ // ─── Config Singleton (§13) ───
123
+
124
+ /**
125
+ * Runtime config singleton — set once at Worker startup via setConfig().
126
+ */
127
+ let _runtimeConfig: EdgeBaseConfig | null = null;
128
+
129
+ function readGlobalRuntimeConfig(): EdgeBaseConfig | null {
130
+ if (typeof globalThis !== 'object' || globalThis === null) {
131
+ return null;
132
+ }
133
+
134
+ const candidate = (globalThis as Record<string, unknown>)[RUNTIME_CONFIG_GLOBAL_KEY];
135
+ if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
136
+ return null;
137
+ }
138
+
139
+ return candidate as EdgeBaseConfig;
140
+ }
141
+
142
+ /**
143
+ * Inject the static bundled config into the singleton. (§13)
144
+ * Must be called at Worker startup before any request is served.
145
+ */
146
+ export function setConfig(config: EdgeBaseConfig): void {
147
+ const normalized = materializeConfig(config);
148
+ _runtimeConfig = normalized;
149
+
150
+ if (typeof globalThis === 'object' && globalThis !== null) {
151
+ (globalThis as Record<string, unknown>)[RUNTIME_CONFIG_GLOBAL_KEY] = normalized;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Get the current EdgeBase config.
157
+ * Priority: request-scoped EDGEBASE_CONFIG → setConfig() singleton → {}.
158
+ */
159
+ export function parseConfig(env?: unknown): EdgeBaseConfig {
160
+ const envConfig = parseEnvConfig(env);
161
+ if (envConfig) {
162
+ return envConfig;
163
+ }
164
+
165
+ if (_runtimeConfig !== null) {
166
+ return _runtimeConfig;
167
+ }
168
+
169
+ const globalConfig = readGlobalRuntimeConfig();
170
+ if (globalConfig !== null) {
171
+ _runtimeConfig = globalConfig;
172
+ return globalConfig;
173
+ }
174
+
175
+ return {};
176
+ }
177
+
178
+ function parseEnvConfig(env?: unknown): EdgeBaseConfig | null {
179
+ if (!env || typeof env !== 'object' || Array.isArray(env)) {
180
+ return null;
181
+ }
182
+
183
+ const rawConfig = (env as Record<string, unknown>).EDGEBASE_CONFIG;
184
+ if (rawConfig && typeof rawConfig === 'object' && !Array.isArray(rawConfig)) {
185
+ return materializeConfig(rawConfig as EdgeBaseConfig);
186
+ }
187
+
188
+ if (typeof rawConfig !== 'string' || rawConfig.trim().length === 0) {
189
+ return null;
190
+ }
191
+
192
+ try {
193
+ const parsed = JSON.parse(rawConfig) as EdgeBaseConfig;
194
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
195
+ return null;
196
+ }
197
+
198
+ return materializeConfig(parsed);
199
+ } catch {
200
+ return null;
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Get all table names that belong to a specific DB namespace.
206
+ * Used after §1 migration — reads from databases block.
207
+ */
208
+ export function getTablesInNamespace(
209
+ namespace: string,
210
+ config: EdgeBaseConfig,
211
+ ): string[] {
212
+ const dbBlock = config.databases?.[namespace];
213
+ if (!dbBlock?.tables) return [];
214
+ return Object.keys(dbBlock.tables);
215
+ }
216
+
217
+ /**
218
+ * Find which DB namespace a table belongs to.
219
+ * Returns undefined if not found in any namespace.
220
+ */
221
+ export function findTableNamespace(
222
+ tableName: string,
223
+ config: EdgeBaseConfig,
224
+ ): string | undefined {
225
+ if (!config.databases) return undefined;
226
+ for (const [ns, dbBlock] of Object.entries(config.databases)) {
227
+ if (dbBlock.tables && Object.hasOwn(dbBlock.tables, tableName)) {
228
+ return ns;
229
+ }
230
+ }
231
+ return undefined;
232
+ }
233
+
234
+ // ─── D1 Routing Helpers ───
235
+
236
+ /**
237
+ * Determine if a namespace should route to D1.
238
+ * Single-instance namespaces (no instance flag, no canCreate/access rules)
239
+ * default to D1 unless provider is explicitly 'do'.
240
+ */
241
+ export function shouldRouteToD1(namespace: string, config: EdgeBaseConfig): boolean {
242
+ const normalized = materializeConfig(config);
243
+ const dbBlock = normalized.databases?.[namespace];
244
+ if (!dbBlock) return false;
245
+
246
+ // Explicit D1 provider
247
+ if (dbBlock.provider === 'd1') return true;
248
+
249
+ // Explicit non-D1 providers — respect the user's choice
250
+ if (dbBlock.provider === 'neon' || dbBlock.provider === 'postgres' || dbBlock.provider === 'do') return false;
251
+
252
+ // Auto-detect: multi-tenant namespaces stay in DO
253
+ if (dbBlock.instance) return false;
254
+ if (dbBlock.access?.canCreate || dbBlock.access?.access) return false;
255
+
256
+ // Default: single-instance → D1
257
+ return true;
258
+ }
259
+
260
+ /**
261
+ * Get the D1 binding name for a single-instance namespace.
262
+ * Convention: DB_D1_{NAMESPACE_UPPER}
263
+ */
264
+ export function getD1BindingName(namespace: string): string {
265
+ return `DB_D1_${namespace.toUpperCase()}`;
266
+ }
267
+
268
+ // Note: table-level DO routing is handled directly by tablesRoute via
269
+ // getDbDoName(namespace, instanceId) — no per-table DO name lookup needed.
270
+ // (#133 §1: DB-first routing — DO instances are keyed by namespace:id, not by table name.)
@@ -0,0 +1,73 @@
1
+ import { getDbDoName } from './do-router.js';
2
+
3
+ interface ExecuteDoSqlOptions {
4
+ databaseNamespace: DurableObjectNamespace;
5
+ namespace: string;
6
+ id?: string;
7
+ query: string;
8
+ params?: unknown[];
9
+ internal?: boolean;
10
+ }
11
+
12
+ interface SqlResultPayload {
13
+ rows?: Record<string, unknown>[];
14
+ items?: Record<string, unknown>[];
15
+ results?: Record<string, unknown>[];
16
+ message?: string;
17
+ }
18
+
19
+ function createSqlRequest(url: string, headers: Headers, query: string, params: unknown[]): Request {
20
+ return new Request(url, {
21
+ method: 'POST',
22
+ headers,
23
+ body: JSON.stringify({ query, params }),
24
+ });
25
+ }
26
+
27
+ function normalizeRows(payload: SqlResultPayload): Record<string, unknown>[] {
28
+ if (Array.isArray(payload.rows)) return payload.rows;
29
+ if (Array.isArray(payload.items)) return payload.items;
30
+ if (Array.isArray(payload.results)) return payload.results;
31
+ return [];
32
+ }
33
+
34
+ export async function executeDoSql({
35
+ databaseNamespace,
36
+ namespace,
37
+ id,
38
+ query,
39
+ params = [],
40
+ internal = false,
41
+ }: ExecuteDoSqlOptions): Promise<Record<string, unknown>[]> {
42
+ const doName = getDbDoName(namespace, id);
43
+ const doId = databaseNamespace.idFromName(doName);
44
+ const stub = databaseNamespace.get(doId);
45
+ const url = 'http://do/internal/sql';
46
+ const headers = new Headers({
47
+ 'Content-Type': 'application/json',
48
+ 'X-DO-Name': doName,
49
+ });
50
+ if (internal) {
51
+ headers.set('X-EdgeBase-Internal', 'true');
52
+ }
53
+
54
+ let response = await stub.fetch(createSqlRequest(url, headers, query, params));
55
+
56
+ if (response.status === 201) {
57
+ const handshake = await response.clone().json().catch(() => null) as
58
+ | { needsCreate?: boolean }
59
+ | null;
60
+ if (handshake?.needsCreate) {
61
+ headers.set('X-DO-Create-Authorized', '1');
62
+ response = await stub.fetch(createSqlRequest(url, headers, query, params));
63
+ }
64
+ }
65
+
66
+ if (!response.ok) {
67
+ const err = await response.json().catch(() => ({ message: 'SQL execution failed' })) as SqlResultPayload;
68
+ throw new Error(err.message || 'SQL execution failed');
69
+ }
70
+
71
+ const payload = await response.json() as SqlResultPayload;
72
+ return normalizeRows(payload);
73
+ }