@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,272 @@
1
+ /**
2
+ * LogWriter adapter pattern.
3
+ *
4
+ * Provides environment-aware log storage with extended analytics fields:
5
+ * - Cloud: AnalyticsEngineWriter — 10 blobs, 6 doubles
6
+ * - Docker/Self-hosted: SQLiteLogWriter — LogsDO with 3-tier pre-aggregation
7
+ * - Dev fallback: ConsoleLogWriter — structured console output
8
+ *
9
+ * Usage:
10
+ * const logger = createLogWriter(env, executionCtx);
11
+ * logger.write({ method, path, status, duration, userId, category, ... });
12
+ *
13
+ * Data Point Layout (Analytics Engine):
14
+ * index1: userId (or 'anonymous')
15
+ * blob1: method blob6: subcategory
16
+ * blob2: path blob7: target1
17
+ * blob3: status (str) blob8: target2
18
+ * blob4: error blob9: operation
19
+ * blob5: category blob10: region
20
+ * double1: status double4: requestSize
21
+ * double2: duration double5: responseSize
22
+ * double3: timestamp double6: resultCount
23
+ */
24
+
25
+ // ─── Types ───
26
+
27
+ export interface LogEntry {
28
+ // Core fields (original)
29
+ method: string;
30
+ path: string;
31
+ status: number;
32
+ duration: number;
33
+ userId?: string;
34
+ error?: string;
35
+ timestamp?: number;
36
+ // Route classification (from route-parser)
37
+ category?: string;
38
+ subcategory?: string;
39
+ target1?: string;
40
+ target2?: string;
41
+ operation?: string;
42
+ // Request context
43
+ region?: string;
44
+ requestSize?: number;
45
+ responseSize?: number;
46
+ resultCount?: number;
47
+ }
48
+
49
+ export interface QueryResult {
50
+ data: Record<string, unknown>[];
51
+ }
52
+
53
+ export interface LogWriter {
54
+ write(entry: LogEntry): void;
55
+ query(sql: string): Promise<QueryResult>;
56
+ }
57
+
58
+ function isDurableObjectResetError(err: unknown): boolean {
59
+ return (
60
+ typeof err === 'object'
61
+ && err !== null
62
+ && 'durableObjectReset' in err
63
+ && (err as { durableObjectReset?: unknown }).durableObjectReset === true
64
+ );
65
+ }
66
+
67
+ // ─── AnalyticsEngineWriter (Cloud) ───
68
+
69
+ /**
70
+ * Log writer using Cloudflare Analytics Engine.
71
+ * Non-blocking fire-and-forget write. 10 blobs + 6 doubles per data point.
72
+ * SQL API queries are proxied via admin endpoint (requires CF account credentials).
73
+ */
74
+ export class AnalyticsEngineWriter implements LogWriter {
75
+ constructor(private analytics: AnalyticsEngineDataset) {}
76
+
77
+ write(entry: LogEntry): void {
78
+ this.analytics.writeDataPoint({
79
+ indexes: [entry.userId || 'anonymous'],
80
+ blobs: [
81
+ entry.method, // blob1
82
+ entry.path, // blob2
83
+ String(entry.status), // blob3
84
+ entry.error || '', // blob4
85
+ entry.category || '', // blob5
86
+ entry.subcategory || '', // blob6
87
+ entry.target1 || '', // blob7
88
+ entry.target2 || '', // blob8
89
+ entry.operation || '', // blob9
90
+ entry.region || '', // blob10
91
+ ],
92
+ doubles: [
93
+ entry.status, // double1
94
+ entry.duration, // double2
95
+ entry.timestamp || Date.now(), // double3
96
+ entry.requestSize || 0, // double4
97
+ entry.responseSize || 0, // double5
98
+ entry.resultCount || 0, // double6
99
+ ],
100
+ });
101
+ }
102
+
103
+ async query(_sql: string): Promise<QueryResult> {
104
+ // Analytics Engine SQL API requires account-level credentials
105
+ // and is called via the Cloudflare REST API, not from Workers.
106
+ // Admin endpoint /admin/api/data/analytics proxies these queries.
107
+ return { data: [] };
108
+ }
109
+ }
110
+
111
+ // ─── SQLiteLogWriter (Docker / Self-hosted) ───
112
+
113
+ /**
114
+ * Log writer that buffers entries and flushes to LogsDO in batches.
115
+ * LogsDO stores logs in SQLite with 3-tier pre-aggregation:
116
+ * _logs_raw (24h) → _logs_hourly (90d) → _logs_daily (permanent)
117
+ *
118
+ * Buffering: flushes at 50 entries or after 100ms, whichever comes first.
119
+ * Uses ExecutionContext.waitUntil() for non-blocking writes.
120
+ *
121
+ * Retry policy: failed batches are re-queued once (retried = true).
122
+ * If a retried batch fails again, entries fall back to console.
123
+ * Buffer is capped at 1000 entries to prevent memory exhaustion.
124
+ */
125
+ export class SQLiteLogWriter implements LogWriter {
126
+ private static readonly MAX_BUFFER = 1000;
127
+ private buffer: (LogEntry & { _retried?: boolean })[] = [];
128
+ private flushScheduled = false;
129
+
130
+ constructor(
131
+ private logsDO: { fetch: (input: RequestInfo) => Promise<Response> },
132
+ private ctx?: { waitUntil: (promise: Promise<unknown>) => void },
133
+ ) {}
134
+
135
+ write(entry: LogEntry): void {
136
+ if (this.buffer.length >= SQLiteLogWriter.MAX_BUFFER) {
137
+ // Drop oldest to prevent memory exhaustion
138
+ this.buffer.shift();
139
+ }
140
+ this.buffer.push(entry);
141
+
142
+ if (this.buffer.length >= 50) {
143
+ // Immediate flush when buffer is full
144
+ const promise = this.flush();
145
+ if (this.ctx) {
146
+ this.ctx.waitUntil(promise);
147
+ }
148
+ } else if (!this.flushScheduled) {
149
+ // Schedule delayed flush for batching
150
+ this.flushScheduled = true;
151
+ const promise = this.scheduledFlush();
152
+ if (this.ctx) {
153
+ this.ctx.waitUntil(promise);
154
+ }
155
+ }
156
+ }
157
+
158
+ private async scheduledFlush(): Promise<void> {
159
+ await new Promise(r => setTimeout(r, 100));
160
+ this.flushScheduled = false;
161
+ await this.flush();
162
+ }
163
+
164
+ private async flush(): Promise<void> {
165
+ if (!this.buffer.length) return;
166
+ const batch = [...this.buffer];
167
+ this.buffer = [];
168
+
169
+ try {
170
+ await this.logsDO.fetch(
171
+ new Request('http://internal/internal/logs/write', {
172
+ method: 'POST',
173
+ headers: { 'Content-Type': 'application/json' },
174
+ body: JSON.stringify({ entries: batch }),
175
+ }),
176
+ );
177
+ } catch (err) {
178
+ // Split batch: re-queue entries that haven't been retried yet
179
+ const retryable = batch.filter(e => !e._retried);
180
+ const dropped = batch.filter(e => e._retried);
181
+
182
+ if (retryable.length) {
183
+ for (const e of retryable) e._retried = true;
184
+ // Re-queue at front, respecting buffer cap
185
+ const space = SQLiteLogWriter.MAX_BUFFER - this.buffer.length;
186
+ this.buffer.unshift(...retryable.slice(0, space));
187
+ }
188
+
189
+ if (isDurableObjectResetError(err)) {
190
+ return;
191
+ }
192
+
193
+ if (dropped.length) {
194
+ console.error(`[EdgeBase] Failed to write ${dropped.length} logs to LogsDO (retry exhausted):`, err);
195
+ for (const entry of dropped) {
196
+ console.log(`[LOG] ${entry.method} ${entry.path} ${entry.status} ${entry.duration}ms`);
197
+ }
198
+ } else {
199
+ console.warn(`[EdgeBase] LogsDO write failed, ${retryable.length} entries queued for retry:`, err);
200
+ }
201
+ }
202
+ }
203
+
204
+ async query(sql: string): Promise<QueryResult> {
205
+ try {
206
+ const params = new URLSearchParams({ sql });
207
+ const resp = await this.logsDO.fetch(
208
+ new Request(`http://internal/internal/logs/query?${params}`),
209
+ );
210
+ return (await resp.json()) as QueryResult;
211
+ } catch {
212
+ return { data: [] };
213
+ }
214
+ }
215
+ }
216
+
217
+ // ─── ConsoleLogWriter (Dev fallback) ───
218
+
219
+ /**
220
+ * Fallback log writer for local development.
221
+ * Outputs structured log lines to console. No persistence.
222
+ */
223
+ export class ConsoleLogWriter implements LogWriter {
224
+ write(entry: LogEntry): void {
225
+ const ts = new Date(entry.timestamp || Date.now()).toISOString();
226
+ const cat = entry.category ? ` [${entry.category}]` : '';
227
+ const line = `[${ts}]${cat} ${entry.method} ${entry.path} ${entry.status} ${entry.duration}ms${entry.userId ? ` user=${entry.userId}` : ''}${entry.error ? ` error=${entry.error}` : ''}`;
228
+ if (entry.status >= 500) {
229
+ console.error(line);
230
+ } else if (entry.status >= 400) {
231
+ console.warn(line);
232
+ } else {
233
+ console.log(line);
234
+ }
235
+ }
236
+
237
+ async query(_sql: string): Promise<QueryResult> {
238
+ return { data: [] };
239
+ }
240
+ }
241
+
242
+ // ─── Factory ───
243
+
244
+ /**
245
+ * Create the appropriate LogWriter based on environment bindings.
246
+ *
247
+ * Priority:
248
+ * 1. env.ANALYTICS exists → AnalyticsEngineWriter (Cloud)
249
+ * 2. env.LOGS exists → SQLiteLogWriter (Docker / Self-hosted)
250
+ * 3. Otherwise → ConsoleLogWriter (dev fallback)
251
+ */
252
+ export function createLogWriter(
253
+ env: Record<string, unknown>,
254
+ executionCtx?: { waitUntil: (promise: Promise<unknown>) => void },
255
+ ): LogWriter {
256
+ // Cloud: Analytics Engine
257
+ const analytics = env.ANALYTICS as AnalyticsEngineDataset | undefined;
258
+ if (analytics) {
259
+ return new AnalyticsEngineWriter(analytics);
260
+ }
261
+
262
+ // Docker / Self-hosted: LogsDO SQLite
263
+ const logsNs = env.LOGS as { idFromName: (name: string) => { toString: () => string }; get: (id: unknown) => { fetch: (input: RequestInfo) => Promise<Response> } } | undefined;
264
+ if (logsNs) {
265
+ const logsId = logsNs.idFromName('logs:main');
266
+ const logsDO = logsNs.get(logsId);
267
+ return new SQLiteLogWriter(logsDO, executionCtx);
268
+ }
269
+
270
+ // Dev fallback
271
+ return new ConsoleLogWriter();
272
+ }
@@ -0,0 +1,125 @@
1
+ import { EdgeBaseError, type EdgeBaseConfig } from '@edge-base/shared';
2
+ import type { Env } from '../types.js';
3
+ import { callDO, getDbDoName, getD1BindingName, shouldRouteToD1 } from './do-router.js';
4
+ import { ensureD1Schema } from './d1-schema-init.js';
5
+ import { ensurePgSchema } from './postgres-schema-init.js';
6
+ import {
7
+ ensureLocalDevPostgresSchema,
8
+ getLocalDevPostgresExecOptions,
9
+ getProviderBindingName,
10
+ withPostgresConnection,
11
+ } from './postgres-executor.js';
12
+
13
+ interface DumpNamespaceTablesOptions {
14
+ includeMeta?: boolean;
15
+ tableNames?: string[];
16
+ }
17
+
18
+ export async function dumpNamespaceTables(
19
+ env: Env,
20
+ config: EdgeBaseConfig,
21
+ namespace: string,
22
+ options: DumpNamespaceTablesOptions = {},
23
+ ): Promise<Record<string, unknown[]>> {
24
+ const dbBlock = config.databases?.[namespace];
25
+ if (!dbBlock) {
26
+ throw new EdgeBaseError(404, `Namespace '${namespace}' not found in config.`);
27
+ }
28
+
29
+ const tableNames = options.tableNames?.length
30
+ ? options.tableNames
31
+ : Object.keys(dbBlock.tables ?? {});
32
+ const tables: Record<string, unknown[]> = {};
33
+ const includeMeta = options.includeMeta !== false;
34
+
35
+ if (dbBlock.provider === 'neon' || dbBlock.provider === 'postgres') {
36
+ const bindingName = getProviderBindingName(namespace);
37
+ const envRecord = env as unknown as Record<string, unknown>;
38
+ const hyperdrive = envRecord[bindingName] as { connectionString?: string } | undefined;
39
+ const envKey = dbBlock.connectionString ?? `${bindingName}_URL`;
40
+ const connStr = hyperdrive?.connectionString ?? (envRecord[envKey] as string | undefined);
41
+ if (!connStr) {
42
+ throw new EdgeBaseError(500, `PostgreSQL connection not available for '${namespace}'.`);
43
+ }
44
+
45
+ const localDevOptions = getLocalDevPostgresExecOptions(env as unknown as Record<string, unknown>, namespace);
46
+ if (localDevOptions) {
47
+ await ensureLocalDevPostgresSchema(localDevOptions);
48
+ }
49
+ await withPostgresConnection(connStr, async (query) => {
50
+ if (!localDevOptions) {
51
+ await ensurePgSchema(connStr, namespace, dbBlock.tables ?? {}, query);
52
+ }
53
+ for (const tableName of tableNames) {
54
+ try {
55
+ const result = await query(`SELECT * FROM "${tableName}"`, []);
56
+ tables[tableName] = result.rows;
57
+ } catch {
58
+ tables[tableName] = [];
59
+ }
60
+ }
61
+
62
+ if (includeMeta) {
63
+ try {
64
+ const meta = await query('SELECT * FROM "_meta"', []);
65
+ tables['_meta'] = meta.rows;
66
+ } catch {
67
+ tables['_meta'] = [];
68
+ }
69
+ }
70
+ }, localDevOptions);
71
+
72
+ return tables;
73
+ }
74
+
75
+ if (shouldRouteToD1(namespace, config)) {
76
+ const bindingName = getD1BindingName(namespace);
77
+ const db = (env as unknown as Record<string, unknown>)[bindingName] as D1Database | undefined;
78
+ if (!db) {
79
+ throw new EdgeBaseError(500, `D1 binding '${bindingName}' not available for '${namespace}'.`);
80
+ }
81
+
82
+ await ensureD1Schema(db, namespace, dbBlock.tables ?? {});
83
+ for (const tableName of tableNames) {
84
+ try {
85
+ const result = await db.prepare(`SELECT * FROM "${tableName}"`).all();
86
+ tables[tableName] = result.results ?? [];
87
+ } catch {
88
+ tables[tableName] = [];
89
+ }
90
+ }
91
+
92
+ if (includeMeta) {
93
+ try {
94
+ const meta = await db.prepare('SELECT * FROM "_meta"').all();
95
+ tables['_meta'] = meta.results ?? [];
96
+ } catch {
97
+ tables['_meta'] = [];
98
+ }
99
+ }
100
+
101
+ return tables;
102
+ }
103
+
104
+ const doName = getDbDoName(namespace);
105
+ const response = await callDO(env.DATABASE, doName, '/internal/backup/dump', {
106
+ headers: { 'X-DO-Name': doName },
107
+ });
108
+
109
+ if (!response.ok) {
110
+ throw new EdgeBaseError(response.status as 500, `Namespace dump failed: ${await response.text()}`);
111
+ }
112
+
113
+ const payload = (await response.json()) as {
114
+ tables?: Record<string, Array<Record<string, unknown>>>;
115
+ };
116
+
117
+ for (const tableName of tableNames) {
118
+ tables[tableName] = payload.tables?.[tableName] ?? [];
119
+ }
120
+ if (includeMeta) {
121
+ tables['_meta'] = payload.tables?._meta ?? [];
122
+ }
123
+
124
+ return tables;
125
+ }