@edge-base/server 0.2.4 → 0.2.6

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 (131) hide show
  1. package/admin-build/_app/immutable/chunks/{DILS_-VJ.js → B3CvhH3c.js} +1 -1
  2. package/admin-build/_app/immutable/chunks/BN_-k-Ck.js +1 -0
  3. package/admin-build/_app/immutable/chunks/{Dt4vL4Df.js → BYL_uBga.js} +1 -1
  4. package/admin-build/_app/immutable/chunks/{C72lTcG0.js → Bcs4KYNp.js} +1 -1
  5. package/admin-build/_app/immutable/chunks/{B8s_s9QY.js → BkZCgsc3.js} +1 -1
  6. package/admin-build/_app/immutable/chunks/{BgDzp0i0.js → BvoGcDFV.js} +1 -1
  7. package/admin-build/_app/immutable/chunks/{BME_U9TJ.js → CCUxCptE.js} +1 -1
  8. package/admin-build/_app/immutable/chunks/CLHN9MVr.js +1 -0
  9. package/admin-build/_app/immutable/chunks/{DYaCRWMA.js → CR37B8DX.js} +1 -1
  10. package/admin-build/_app/immutable/chunks/CbfX3ELZ.js +1 -0
  11. package/admin-build/_app/immutable/chunks/CjcrXziO.js +2 -0
  12. package/admin-build/_app/immutable/chunks/CrwlCAM0.js +1 -0
  13. package/admin-build/_app/immutable/chunks/{B0HRJ657.js → DOOPbWwG.js} +1 -1
  14. package/admin-build/_app/immutable/chunks/DQVP4KC-.js +1 -0
  15. package/admin-build/_app/immutable/chunks/{Dj0QUuOf.js → DdvsFblq.js} +1 -1
  16. package/admin-build/_app/immutable/chunks/DemDWbs-.js +128 -0
  17. package/admin-build/_app/immutable/chunks/{XQM1k9PM.js → DmDTovpg.js} +1 -1
  18. package/admin-build/_app/immutable/chunks/{fYEKMQ-Z.js → Ff90owjx.js} +1 -1
  19. package/admin-build/_app/immutable/chunks/{5RQRbp5q.js → LL3ulaxa.js} +1 -1
  20. package/admin-build/_app/immutable/chunks/{DBsVqhuh.js → Q3vAxeY-.js} +1 -1
  21. package/admin-build/_app/immutable/chunks/{D__dwMuW.js → SQVAC3Cv.js} +1 -1
  22. package/admin-build/_app/immutable/chunks/{Z41NK6i6.js → bguI1TeA.js} +1 -1
  23. package/admin-build/_app/immutable/chunks/{_teD5ji5.js → nlAMTi52.js} +1 -1
  24. package/admin-build/_app/immutable/chunks/{BjWZuf8W.js → qBm6xof8.js} +1 -1
  25. package/admin-build/_app/immutable/entry/{app.C8ylfBe6.js → app.CP83Ni80.js} +2 -2
  26. package/admin-build/_app/immutable/entry/start.DY6YakU0.js +1 -0
  27. package/admin-build/_app/immutable/nodes/{0.CJJ6HZbp.js → 0.DiRq7puO.js} +1 -1
  28. package/admin-build/_app/immutable/nodes/1.BFeyKLGT.js +1 -0
  29. package/admin-build/_app/immutable/nodes/10.zcee7hJx.js +1 -0
  30. package/admin-build/_app/immutable/nodes/11.BW7wLs2Y.js +1 -0
  31. package/admin-build/_app/immutable/nodes/12.CxJRlYSd.js +1 -0
  32. package/admin-build/_app/immutable/nodes/13.pp0F_5hn.js +110 -0
  33. package/admin-build/_app/immutable/nodes/14.t3AfGiGo.js +3 -0
  34. package/admin-build/_app/immutable/nodes/15.B3agc7NX.js +1 -0
  35. package/admin-build/_app/immutable/nodes/{16.D0xkPUBW.js → 16.C4uG2-i8.js} +1 -1
  36. package/admin-build/_app/immutable/nodes/{17.CebNqPeh.js → 17.CwGxi1Bn.js} +1 -1
  37. package/admin-build/_app/immutable/nodes/18.CrQyN_gU.js +1 -0
  38. package/admin-build/_app/immutable/nodes/19.NEPUOXl7.js +2 -0
  39. package/admin-build/_app/immutable/nodes/{20.DYb-q3W8.js → 20.DGHO8ipr.js} +1 -1
  40. package/admin-build/_app/immutable/nodes/21.UVKBDvp4.js +1 -0
  41. package/admin-build/_app/immutable/nodes/22.Dri5It7a.js +1 -0
  42. package/admin-build/_app/immutable/nodes/{23.BLgq21om.js → 23.BPQP_Zte.js} +2 -2
  43. package/admin-build/_app/immutable/nodes/24.D580FdSS.js +2 -0
  44. package/admin-build/_app/immutable/nodes/25.BMNPOZwF.js +2 -0
  45. package/admin-build/_app/immutable/nodes/26.XcpEcbiz.js +1 -0
  46. package/admin-build/_app/immutable/nodes/27.C1zHHcYv.js +1 -0
  47. package/admin-build/_app/immutable/nodes/28.CuKzzrY8.js +1 -0
  48. package/admin-build/_app/immutable/nodes/29.nLpBMXnM.js +1 -0
  49. package/admin-build/_app/immutable/nodes/{3.z8ut3jS-.js → 3.5G_aseoL.js} +1 -1
  50. package/admin-build/_app/immutable/nodes/30.CQC4nLoU.js +1 -0
  51. package/admin-build/_app/immutable/nodes/31.Bet8kxOK.js +1 -0
  52. package/admin-build/_app/immutable/nodes/4.nmJDYJpC.js +1 -0
  53. package/admin-build/_app/immutable/nodes/5.CnbYLG4E.js +1 -0
  54. package/admin-build/_app/immutable/nodes/6.KA01b-3y.js +1 -0
  55. package/admin-build/_app/immutable/nodes/7.CP9fkn1L.js +1 -0
  56. package/admin-build/_app/immutable/nodes/8.BTzDb---.js +1 -0
  57. package/admin-build/_app/immutable/nodes/9.DkNJg_J6.js +1 -0
  58. package/admin-build/_app/version.json +1 -1
  59. package/admin-build/index.html +7 -7
  60. package/package.json +3 -3
  61. package/src/__tests__/database-do-route-validation.test.ts +10 -7
  62. package/src/__tests__/meta-route-registration.test.ts +20 -15
  63. package/src/__tests__/push-handlers.test.ts +1 -1
  64. package/src/__tests__/room-auth-state-loss.test.ts +122 -0
  65. package/src/__tests__/room-handler-context.test.ts +4 -4
  66. package/src/__tests__/room-rate-limit-scopes.test.ts +38 -0
  67. package/src/__tests__/room-runtime-routing.test.ts +23 -0
  68. package/src/__tests__/route-parser.test.ts +6 -0
  69. package/src/__tests__/runtime-startup.test.ts +49 -0
  70. package/src/__tests__/schema.test.ts +15 -6
  71. package/src/durable-objects/database-do.ts +21 -1
  72. package/src/durable-objects/database-live-do.ts +15 -0
  73. package/src/durable-objects/room-runtime-base.ts +436 -169
  74. package/src/durable-objects/rooms-do.ts +63 -25
  75. package/src/index.ts +340 -280
  76. package/src/lib/d1-handler.ts +32 -17
  77. package/src/lib/postgres-handler.ts +24 -12
  78. package/src/lib/route-parser.ts +3 -0
  79. package/src/lib/runtime-startup.ts +53 -0
  80. package/src/lib/schemas.ts +12 -2
  81. package/src/middleware/captcha-verify.ts +16 -3
  82. package/src/middleware/error-handler.ts +1 -1
  83. package/src/middleware/rules.ts +28 -9
  84. package/src/routes/admin-auth.ts +3 -3
  85. package/src/routes/admin.ts +13 -8
  86. package/src/routes/analytics-api.ts +3 -3
  87. package/src/routes/auth.ts +1 -1
  88. package/src/routes/backup.ts +1 -1
  89. package/src/routes/d1.ts +14 -7
  90. package/src/routes/database-live.ts +13 -6
  91. package/src/routes/kv.ts +21 -10
  92. package/src/routes/oauth.ts +1 -1
  93. package/src/routes/push.ts +119 -77
  94. package/src/routes/room.ts +215 -7
  95. package/src/routes/schema-endpoint.ts +2 -2
  96. package/src/routes/sql.ts +10 -6
  97. package/src/routes/storage.ts +4 -2
  98. package/src/routes/vectorize.ts +16 -4
  99. package/admin-build/_app/immutable/chunks/BYI6CUvd.js +0 -1
  100. package/admin-build/_app/immutable/chunks/C6lpZLE2.js +0 -1
  101. package/admin-build/_app/immutable/chunks/CoI6jjbg.js +0 -2
  102. package/admin-build/_app/immutable/chunks/D5GswVnI.js +0 -128
  103. package/admin-build/_app/immutable/chunks/Dj-E9-FO.js +0 -1
  104. package/admin-build/_app/immutable/chunks/g_-Kpxu3.js +0 -1
  105. package/admin-build/_app/immutable/chunks/wCNueVYy.js +0 -1
  106. package/admin-build/_app/immutable/entry/start.CtsqDyfj.js +0 -1
  107. package/admin-build/_app/immutable/nodes/1.B4sI5cB4.js +0 -1
  108. package/admin-build/_app/immutable/nodes/10.D6hvCer6.js +0 -1
  109. package/admin-build/_app/immutable/nodes/11.Dx7b8aQ5.js +0 -1
  110. package/admin-build/_app/immutable/nodes/12.Bqmy5KIF.js +0 -1
  111. package/admin-build/_app/immutable/nodes/13.CC6KpXgS.js +0 -110
  112. package/admin-build/_app/immutable/nodes/14.yCo1Ix8E.js +0 -3
  113. package/admin-build/_app/immutable/nodes/15.co0UfPlh.js +0 -1
  114. package/admin-build/_app/immutable/nodes/18.JUoLOZxh.js +0 -1
  115. package/admin-build/_app/immutable/nodes/19.ND8kmQJe.js +0 -2
  116. package/admin-build/_app/immutable/nodes/21.cz3IN9Cc.js +0 -1
  117. package/admin-build/_app/immutable/nodes/22.UOzm8WYV.js +0 -1
  118. package/admin-build/_app/immutable/nodes/24.DN9usmUs.js +0 -2
  119. package/admin-build/_app/immutable/nodes/25.BddRfAyE.js +0 -2
  120. package/admin-build/_app/immutable/nodes/26.Dl6XHIeT.js +0 -1
  121. package/admin-build/_app/immutable/nodes/27.D0iNwALG.js +0 -1
  122. package/admin-build/_app/immutable/nodes/28.9dKQmdGi.js +0 -1
  123. package/admin-build/_app/immutable/nodes/29.wXzfJUXp.js +0 -1
  124. package/admin-build/_app/immutable/nodes/30.BtZETNsL.js +0 -1
  125. package/admin-build/_app/immutable/nodes/31.CYonj2Jh.js +0 -1
  126. package/admin-build/_app/immutable/nodes/4.COtDPQ9b.js +0 -1
  127. package/admin-build/_app/immutable/nodes/5.CTRCeIhp.js +0 -1
  128. package/admin-build/_app/immutable/nodes/6.ChHi3QkR.js +0 -1
  129. package/admin-build/_app/immutable/nodes/7.CCMtr6Ac.js +0 -1
  130. package/admin-build/_app/immutable/nodes/8.DpWJ-X_-.js +0 -1
  131. package/admin-build/_app/immutable/nodes/9.DOkvfmir.js +0 -1
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{g as Nt,o as Gt}from"../chunks/Bn2NtlTj.js";import{p as at,d as u,M as Qe,a as rt,f as me,t as k,s,g as e,b as t,c as l,u as x,ae as j,r,w as Pt}from"../chunks/BdTBlfLy.js";import{d as ft,e as vt,a as W,s as M}from"../chunks/DtZk82gG.js";import{i as ie}from"../chunks/Y22E1hJM.js";import{t as Pe,b as et,e as Xe,i as Mt,a as Ye}from"../chunks/CrwlCAM0.js";import{a as n,t as ne,f as h,c as Tt}from"../chunks/DEELgv7K.js";import{d as Ee,s as Me,a as q,g as Zt,r as tt,h as xt,f as zt}from"../chunks/CjcrXziO.js";import{s as Bt,a as ut}from"../chunks/Cp8V0Xy2.js";import{p as qt}from"../chunks/DdvsFblq.js";import{g as Dt,a as pt,c as Jt,d as Qt}from"../chunks/CbfX3ELZ.js";import{d as Xt}from"../chunks/wJsUhbfZ.js";import{P as Yt}from"../chunks/BkZCgsc3.js";import{s as ea}from"../chunks/Q2nPFxS6.js";import{B as ce}from"../chunks/Bcs4KYNp.js";import{E as ta}from"../chunks/m9eWh0Cd.js";import{C as Ut}from"../chunks/B3CvhH3c.js";import{p as He}from"../chunks/F9_4wRrd.js";import{M as ht}from"../chunks/BYL_uBga.js";import{I as aa}from"../chunks/nlAMTi52.js";import{S as ra}from"../chunks/Ff90owjx.js";var la=h("<!> <!>",1),sa=h('<div class="key-input svelte-1myht9e"><!></div>'),oa=h('<img class="file-item__thumb svelte-1myht9e"/>'),ia=h('<div class="file-item__icon svelte-1myht9e"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg></div>'),na=h('<div class="file-item svelte-1myht9e"><!> <div class="file-item__info svelte-1myht9e"><span class="file-item__name svelte-1myht9e"> </span> <span class="file-item__meta svelte-1myht9e"> </span></div> <button class="file-item__remove svelte-1myht9e" aria-label="Remove"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button></div>'),ca=h('<div class="file-list svelte-1myht9e"></div>'),da=h('<label><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg> <p class="dropzone__text svelte-1myht9e">Drag & drop files here</p> <p class="dropzone__hint svelte-1myht9e">or click to browse</p> <input type="file" multiple="" class="dropzone__input svelte-1myht9e"/></label> <!> <!>',1);function va(ke,m){at(m,!0);const J=()=>ut(pt,"$authStore",Q),[Q,B]=Bt();let L=He(m,"open",15,!1),D=He(m,"onUploaded",3,()=>{}),_=u(Qe([])),K=u(""),de=u(!1),z=u(!1);const w=`file-upload-${Math.random().toString(36).slice(2,9)}`;let o=x(()=>e(_).map(d=>({name:d.name,size:O(d.size),type:d.type,isImage:d.type.startsWith("image/"),url:d.type.startsWith("image/")?URL.createObjectURL(d):""})));function F(){t(_,[],!0),t(K,""),t(z,!1)}function I(d){var f;d.preventDefault(),t(z,!1),(f=d.dataTransfer)!=null&&f.files&&t(_,[...e(_),...Array.from(d.dataTransfer.files)],!0)}function R(d){const f=d.target;f.files&&t(_,[...e(_),...Array.from(f.files)],!0),f.value=""}function y(d){t(_,e(_).filter((f,v)=>v!==d),!0)}async function ve(){if(e(_).length===0)return;t(de,!0);let d=0;for(const f of e(_))try{const v=new FormData;v.append("file",f),e(_).length===1&&e(K).trim()&&v.append("key",e(K).trim());const c=await fetch(Dt(`data/storage/buckets/${m.bucket}/upload`),{method:"POST",headers:{Authorization:`Bearer ${J().accessToken}`},body:v});if(!c.ok){const b=await c.json().catch(()=>({message:"Upload failed"}));throw new Error(b.message||"Upload failed")}d++}catch(v){Pe(Ee(v,`Failed to upload ${f.name}.`,{prefixKnownMessage:`Failed to upload ${f.name}`}))}t(de,!1),d>0&&(et(`Uploaded ${d} file${d>1?"s":""}`),L(!1),F(),D()())}function O(d){if(d===0)return"0 B";const f=["B","KB","MB","GB"],v=Math.floor(Math.log(d)/Math.log(1024));return`${(d/Math.pow(1024,v)).toFixed(v===0?0:1)} ${f[v]}`}ht(ke,{title:"Upload Files",onclose:F,get open(){return L()},set open(f){L(f)},footer:f=>{var v=la(),c=me(v);ce(c,{variant:"secondary",onclick:()=>{L(!1),F()},children:(N,ue)=>{j();var fe=ne("Cancel");n(N,fe)},$$slots:{default:!0}});var b=s(c,2);{let N=x(()=>e(_).length===0);ce(b,{variant:"primary",onclick:ve,get loading(){return e(de)},get disabled(){return e(N)},children:(ue,fe)=>{j();var U=ne();k(()=>M(U,`Upload ${e(_).length>0?`(${e(_).length})`:""}`)),n(ue,U)},$$slots:{default:!0}})}n(f,v)},children:(f,v)=>{var c=da(),b=me(c);let N;var ue=s(l(b),6);r(b);var fe=s(b,2);{var U=T=>{var E=sa(),Y=l(E);aa(Y,{label:"Custom key (optional)",get placeholder(){return e(_)[0].name},get value(){return e(K)},set value(H){t(K,H,!0)}}),r(E),n(T,E)};ie(fe,T=>{e(_).length===1&&T(U)})}var re=s(fe,2);{var X=T=>{var E=ca();Xe(E,21,()=>e(o),Mt,(Y,H,Te)=>{var De=na(),Ve=l(De);{var lt=pe=>{var ge=oa();k(()=>{q(ge,"src",e(H).url),q(ge,"alt",e(H).name)}),n(pe,ge)},Ce=pe=>{var ge=ia();n(pe,ge)};ie(Ve,pe=>{e(H).isImage?pe(lt):pe(Ce,!1)})}var Le=s(Ve,2),Be=l(Le),We=l(Be,!0);r(Be);var ee=s(Be,2),je=l(ee);r(ee),r(Le);var Ne=s(Le,2);r(De),k(()=>{M(We,e(H).name),M(je,`${e(H).size??""} · ${(e(H).type||"unknown")??""}`)}),W("click",Ne,()=>y(Te)),n(Y,De)}),r(E),n(T,E)};ie(re,T=>{e(o).length>0&&T(X)})}k(()=>{N=Me(b,1,"dropzone svelte-1myht9e",null,N,{"dropzone--active":e(z)}),q(b,"for",w),q(ue,"id",w)}),vt("dragover",b,T=>{T.preventDefault(),t(z,!0)}),vt("dragleave",b,()=>{t(z,!1)}),vt("drop",b,I),W("change",ue,R),n(f,c)},$$slots:{footer:!0,default:!0}}),rt(),B()}ft(["change","click"]);var ua=h("<img/>"),fa=h('<span><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" class="svelte-zoylhi"><rect x="2" y="2" width="12" height="12" rx="1.5" class="svelte-zoylhi"></rect><circle cx="5.5" cy="5.5" r="1" class="svelte-zoylhi"></circle><path d="M2 11l3-3 2 2 3-3 4 4" class="svelte-zoylhi"></path></svg></span>'),pa=h("<span></span>");function St(ke,m){at(m,!0);let J=He(m,"alt",3,""),Q=He(m,"class",3,""),B=u(null),L=u(!1);Pt(()=>{const w=m.src;let o=!1,F=null;t(B,null),t(L,!1);const I=Nt(pt);return fetch(w,{headers:I.accessToken?{Authorization:`Bearer ${I.accessToken}`}:{}}).then(R=>o||!R.ok?(o||t(L,!0),null):R.blob()).then(R=>{o||!R||(F=URL.createObjectURL(R),t(B,F,!0))}).catch(()=>{o||t(L,!0)}),()=>{o=!0,F&&URL.revokeObjectURL(F)}});var D=Tt(),_=me(D);{var K=w=>{var o=ua();k(()=>{q(o,"src",e(B)),q(o,"alt",J()),Me(o,1,Zt(Q()),"svelte-zoylhi")}),n(w,o)},de=w=>{var o=fa();k(()=>{Me(o,1,`auth-img-fallback ${Q()??""}`,"svelte-zoylhi"),q(o,"aria-label",J())}),n(w,o)},z=w=>{var o=pa();k(()=>Me(o,1,`auth-img-loading ${Q()??""}`,"svelte-zoylhi")),n(w,o)};ie(_,w=>{e(B)?w(K):e(L)?w(de,1):w(z,!1)})}n(ke,D),rt()}var ha=h('<div class="share__url-row svelte-vlp39"><input class="share__url-input svelte-vlp39" type="text" readonly=""/> <!></div>'),_a=h('<div class="share svelte-vlp39"><p class="share__filename svelte-vlp39"> </p> <div class="share__section svelte-vlp39"><div class="share__label svelte-vlp39"><span class="share__label-text svelte-vlp39">Public URL</span> <span class="share__hint svelte-vlp39">Works if bucket read rule allows public access</span></div> <div class="share__url-row svelte-vlp39"><input class="share__url-input svelte-vlp39" type="text" readonly=""/> <!></div></div> <div class="share__section svelte-vlp39"><div class="share__label svelte-vlp39"><span class="share__label-text svelte-vlp39">Signed URL</span> <span class="share__hint svelte-vlp39">Time-limited access, no auth required</span></div> <div class="share__signed-row svelte-vlp39"><!> <!></div> <!></div> <div class="share__usage svelte-vlp39"><p class="share__usage-title svelte-vlp39">Usage in HTML</p> <code class="share__code svelte-vlp39">&lt;img src="<span class="share__code-url svelte-vlp39">…copied URL…</span>" /&gt;</code></div></div>');function ma(ke,m){at(m,!0);let J=He(m,"open",15,!1),Q=u("1h"),B=u(""),L=u(!1),D=u(null);const _=[{value:"1h",label:"1 hour"},{value:"1d",label:"1 day"},{value:"7d",label:"7 days"},{value:"30d",label:"30 days"}];let K=x(()=>Jt(`/api/storage/${m.bucket}/${m.fileKey}`)),de=x(()=>m.fileKey.includes("/")?m.fileKey.split("/").pop()??m.fileKey:m.fileKey);async function z(){t(L,!0),t(B,"");try{const o=await Ye.fetch(`data/storage/buckets/${m.bucket}/signed-url`,{method:"POST",body:{key:m.fileKey,expiresIn:e(Q)}});t(B,o.url,!0)}catch(o){Pe(Ee(o,"Failed to create signed URL."))}finally{t(L,!1)}}async function w(o,F){try{await navigator.clipboard.writeText(o),t(D,F,!0),et("URL copied to clipboard"),setTimeout(()=>{t(D,null)},2e3)}catch{Pe("Failed to copy URL")}}Pt(()=>{J()&&(t(B,""),t(D,null))}),ht(ke,{title:"Share File",get open(){return J()},set open(o){J(o)},children:(o,F)=>{var I=_a(),R=l(I),y=l(R,!0);r(R);var ve=s(R,2),O=s(l(ve),2),d=l(O);tt(d);var f=s(d,2);{let U=x(()=>e(D)==="public"?"primary":"secondary");ce(f,{get variant(){return e(U)},size:"sm",onclick:()=>w(e(K),"public"),children:(re,X)=>{j();var T=ne();k(()=>M(T,e(D)==="public"?"Copied!":"Copy")),n(re,T)},$$slots:{default:!0}})}r(O),r(ve);var v=s(ve,2),c=s(l(v),2),b=l(c);ra(b,{get options(){return _},get value(){return e(Q)},set value(U){t(Q,U,!0)}});var N=s(b,2);ce(N,{variant:"primary",size:"sm",get loading(){return e(L)},onclick:z,children:(U,re)=>{j();var X=ne("Generate");n(U,X)},$$slots:{default:!0}}),r(c);var ue=s(c,2);{var fe=U=>{var re=ha(),X=l(re);tt(X);var T=s(X,2);{let E=x(()=>e(D)==="signed"?"primary":"secondary");ce(T,{get variant(){return e(E)},size:"sm",onclick:()=>w(e(B),"signed"),children:(Y,H)=>{j();var Te=ne();k(()=>M(Te,e(D)==="signed"?"Copied!":"Copy")),n(Y,Te)},$$slots:{default:!0}})}r(re),k(()=>xt(X,e(B))),W("click",X,E=>E.currentTarget.select()),n(U,re)};ie(ue,U=>{e(B)&&U(fe)})}r(v),j(2),r(I),k(()=>{M(y,e(de)),xt(d,e(K))}),W("click",d,U=>U.currentTarget.select()),n(o,I)},$$slots:{default:!0}}),rt()}ft(["click"]);var ga=h("<!> <a><!></a>",1),wa=h('<span class="breadcrumb__sep svelte-f2o7w5">/</span> <button> </button>',1),ya=h('<div class="bulk-bar svelte-f2o7w5"><span class="bulk-bar__count svelte-f2o7w5"> </span> <div class="bulk-bar__actions svelte-f2o7w5"><!></div> <button class="bulk-bar__clear svelte-f2o7w5">Clear</button></div>'),ba=h('<div class="loading-state svelte-f2o7w5">Loading files...</div>'),ka=h('<tr class="folder-row svelte-f2o7w5"><td class="col-checkbox svelte-f2o7w5"></td><td class="col-preview svelte-f2o7w5"><span class="folder-icon svelte-f2o7w5"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 4V13H14V5H8L6.5 3H2V4Z" stroke-linejoin="round"></path></svg></span></td><td class="col-key svelte-f2o7w5" colspan="4"><button class="folder-btn svelte-f2o7w5"> </button></td><td class="col-actions svelte-f2o7w5"></td></tr>'),$a=h('<button type="button" class="thumb-button svelte-f2o7w5"><!></button>'),xa=h('<span class="file-icon svelte-f2o7w5"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 2H10L13 5V14H4V2Z" stroke-linejoin="round"></path><path d="M10 2V5H13"></path></svg></span>'),za=h('<tr><td class="col-checkbox svelte-f2o7w5"><input type="checkbox" class="svelte-f2o7w5"/></td><td class="col-preview svelte-f2o7w5"><!></td><td class="col-key svelte-f2o7w5"><span class="file-key svelte-f2o7w5"> </span></td><td class="col-size svelte-f2o7w5"> </td><td class="col-type svelte-f2o7w5"><span class="mime-type svelte-f2o7w5"> </span></td><td class="col-date svelte-f2o7w5"> </td><td class="col-actions svelte-f2o7w5"><div class="action-btns svelte-f2o7w5"><button class="icon-btn svelte-f2o7w5" title="Download"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 2v9M4 8l4 4 4-4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M2 13h12" stroke-linecap="round"></path></svg></button> <button class="icon-btn svelte-f2o7w5" title="Share"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="4" cy="8" r="2"></circle><circle cx="12" cy="4" r="2"></circle><circle cx="12" cy="12" r="2"></circle><path d="M5.7 7l4.6-2M5.7 9l4.6 2"></path></svg></button> <!></div></td></tr>'),Ua=h('<div class="pagination svelte-f2o7w5"><!></div>'),Sa=h('<div class="table-wrapper svelte-f2o7w5"><table class="file-table svelte-f2o7w5"><thead class="svelte-f2o7w5"><tr><th class="col-checkbox svelte-f2o7w5"><input type="checkbox" aria-label="Select all files" class="svelte-f2o7w5"/></th><th class="col-preview svelte-f2o7w5"></th><th class="col-key svelte-f2o7w5">Name</th><th class="col-size svelte-f2o7w5">Size</th><th class="col-type svelte-f2o7w5">Type</th><th class="col-date svelte-f2o7w5">Uploaded</th><th class="col-actions svelte-f2o7w5"></th></tr></thead><tbody class="svelte-f2o7w5"><!><!></tbody></table></div> <!>',1),Pa=h('<nav class="breadcrumb svelte-f2o7w5"><button> </button> <!></nav> <!> <!>',1),Ma=h('<div class="image-preview svelte-f2o7w5"><div class="image-preview__frame svelte-f2o7w5"><!></div> <dl class="image-preview__meta svelte-f2o7w5"><div class="image-preview__meta-row svelte-f2o7w5"><dt class="svelte-f2o7w5">Path</dt> <dd class="svelte-f2o7w5"> </dd></div> <div class="image-preview__meta-row svelte-f2o7w5"><dt class="svelte-f2o7w5">Type</dt> <dd class="svelte-f2o7w5"> </dd></div> <div class="image-preview__meta-row svelte-f2o7w5"><dt class="svelte-f2o7w5">Size</dt> <dd class="svelte-f2o7w5"> </dd></div> <div class="image-preview__meta-row svelte-f2o7w5"><dt class="svelte-f2o7w5">Uploaded</dt> <dd class="svelte-f2o7w5"> </dd></div></dl></div>'),Ta=h("<!> <!> <!> <!> <!> <!>",1);function Xa(ke,m){at(m,!0);const J=()=>ut(qt,"$page",B),Q=()=>ut(pt,"$authStore",B),[B,L]=Bt();let D=u(!1),_=u(!1),K=u("");function de(a){return!!a&&a.startsWith("image/")}let z=x(()=>J().params.bucket??""),w=u(!0),o=u(Qe([])),F=u(Qe([])),I=u(null),R=u(!1),y=u(""),ve=u(!1),O=u(null),d=u(!1),f=u(!1),v=u(null),c=u(Qe(new Set)),b=u(!1),N=u(!1);function ue(a){const i=new Set(e(c));i.has(a)?i.delete(a):i.add(a),t(c,i,!0)}function fe(){e(c).size===e(o).length?t(c,new Set,!0):t(c,new Set(e(o).map(a=>a.key)),!0)}function U(){t(c,new Set,!0)}async function re(){t(b,!1),t(N,!0);const a=[...e(c)],i=await Promise.allSettled(a.map(async S=>{await Ye.fetch(`data/storage/buckets/${e(z)}/objects/${encodeURIComponent(S)}`,{method:"DELETE"})})),g=i.filter(S=>S.status==="fulfilled").length,$=i.filter(S=>S.status==="rejected").length;$===0?et(`Deleted ${g} file${g!==1?"s":""}`):Pe(`${g} succeeded, ${$} failed`),t(c,new Set,!0),t(N,!1),Y()}let X=x(()=>(()=>{if(!e(y))return[];const a=e(y).replace(/\/$/,"").split("/");return a.map((i,g)=>({label:i,prefix:a.slice(0,g+1).join("/")+"/"}))})());function T(a){return a?a.endsWith("/")?a:`${a}/`:""}function E(a){const i=new URL(window.location.href);a?i.searchParams.set("prefix",a):i.searchParams.delete("prefix"),history.replaceState(history.state,"",i.toString())}async function Y(a=!1){a?t(R,!0):t(w,!0);try{let i=`data/storage/buckets/${e(z)}/objects?limit=50&delimiter=/`;e(y)&&(i+=`&prefix=${encodeURIComponent(e(y))}`),a&&e(I)&&(i+=`&cursor=${encodeURIComponent(e(I))}`);const g=await Ye.fetch(i),$=(g.objects||[]).filter(S=>(e(y)?S.key.replace(e(y),""):S.key).length>0);a?t(o,[...e(o),...$],!0):(t(o,$,!0),t(F,g.folders||[],!0)),t(I,g.cursor,!0)}catch(i){Pe(Ee(i,"Failed to load storage objects."))}finally{t(w,!1),t(R,!1)}}function H(a){t(y,T(a),!0),E(e(y)),t(I,null),t(c,new Set,!0),Y()}function Te(a){t(O,a,!0),t(ve,!0)}function De(a){t(v,a,!0),t(f,!0)}function Ve(){t(f,!1),t(v,null)}async function lt(){if(e(O)){t(d,!0);try{await Ye.fetch(`data/storage/buckets/${e(z)}/objects/${encodeURIComponent(e(O))}`,{method:"DELETE"}),t(o,e(o).filter(a=>a.key!==e(O)),!0),e(c).delete(e(O)),t(c,new Set(e(c)),!0),et(`Deleted ${e(O)}`)}catch(a){Pe(Ee(a,"Failed to delete the storage object."))}finally{t(d,!1),t(O,null)}}}function Ce(a){return Dt(`data/storage/buckets/${e(z)}/objects/${encodeURIComponent(a)}`)}async function Le(a){try{const i=await fetch(Ce(a),{headers:{Authorization:`Bearer ${Q().accessToken}`}});if(!i.ok){const $=await i.json().catch(()=>({message:"Download failed"}));throw new Error($.message||"Download failed")}const g=await i.blob();Xt(g,ee(a))}catch(i){Pe(Ee(i,"Failed to download the file."))}}function Be(a){if(a===0)return"0 B";const i=["B","KB","MB","GB","TB"],g=Math.floor(Math.log(a)/Math.log(1024));return`${(a/Math.pow(1024,g)).toFixed(g===0?0:1)} ${i[g]}`}function We(a){try{return new Date(a).toLocaleString()}catch{return a}}function ee(a){return e(y)&&a.startsWith(e(y))?a.slice(e(y).length):a}Gt(()=>{t(y,T(J().url.searchParams.get("prefix")??""),!0),Y()});var je=Ta(),Ne=me(je);Yt(Ne,{get title(){return e(z)},description:"Browse files in this bucket",get docsHref(){return ea},actions:i=>{var g=ga(),$=me(g);ce($,{variant:"primary",size:"sm",onclick:()=>t(D,!0),children:(se,$e)=>{j();var he=ne("Upload Files");n(se,he)},$$slots:{default:!0}});var S=s($,2),le=l(S);ce(le,{variant:"secondary",size:"sm",children:(se,$e)=>{j();var he=ne("Back to Buckets");n(se,he)},$$slots:{default:!0}}),r(S),k(()=>q(S,"href",`${Qt}/storage`)),n(i,g)},children:(i,g)=>{var $=Pa(),S=me($),le=l(S);let se;var $e=l(le,!0);r(le);var he=s(le,2);Xe(he,17,()=>e(X),Mt,(P,C)=>{var A=wa(),G=s(me(A),2);let Z;var _e=l(G,!0);r(G),k(()=>{Z=Me(G,1,"breadcrumb__item svelte-f2o7w5",null,Z,{"breadcrumb__item--active":e(C).prefix===e(y)}),M(_e,e(C).label)}),W("click",G,()=>H(e(C).prefix)),n(P,A)}),r(S);var Ge=s(S,2);{var Ze=P=>{var C=ya(),A=l(C),G=l(A);r(A);var Z=s(A,2),_e=l(Z);ce(_e,{variant:"danger",size:"sm",onclick:()=>t(b,!0),get disabled(){return e(N)},children:(ze,Ue)=>{j();var we=ne("Delete Selected");n(ze,we)},$$slots:{default:!0}}),r(Z);var xe=s(Z,2);r(C),k(()=>M(G,`${e(c).size??""} selected`)),W("click",xe,U),n(P,C)};ie(Ge,P=>{e(c).size>0&&P(Ze)})}var Fe=s(Ge,2);{var qe=P=>{var C=ba();n(P,C)},st=P=>{{let C=x(()=>e(y)?"This folder is empty.":"This bucket is empty.");ta(P,{title:"No files",get description(){return e(C)}})}},Re=P=>{var C=Sa(),A=me(C),G=l(A),Z=l(G),_e=l(Z),xe=l(_e),ze=l(xe);tt(ze),r(xe),j(6),r(_e),r(Z);var Ue=s(Z),we=l(Ue);Xe(we,16,()=>e(F),te=>te,(te,p)=>{var oe=ka(),Se=s(l(oe),2),ye=l(Se),be=l(ye);r(ye),r(Se),j(),r(oe),k(Ie=>M(be,`${Ie??""}/`),[()=>p.replace(e(y),"").replace(/\/$/,"")]),W("click",ye,()=>H(p)),n(te,oe)});var Je=s(we);Xe(Je,17,()=>e(o),te=>te.key,(te,p)=>{var oe=za();let Se;var ye=l(oe),be=l(ye);tt(be),r(ye);var Ie=s(ye),Rt=l(Ie);{var It=V=>{var ae=$a(),Ae=l(ae);{let Ke=x(()=>Ce(e(p).key)),Oe=x(()=>ee(e(p).key));St(Ae,{class:"file-thumb",get src(){return e(Ke)},get alt(){return e(Oe)}})}r(ae),k((Ke,Oe)=>{q(ae,"aria-label",Ke),q(ae,"title",Oe)},[()=>`Preview ${ee(e(p).key)}`,()=>`Preview ${ee(e(p).key)}`]),W("click",ae,()=>De(e(p))),n(V,ae)},At=x(()=>{var V;return de((V=e(p).httpMetadata)==null?void 0:V.contentType)}),Kt=V=>{var ae=xa();n(V,ae)};ie(Rt,V=>{e(At)?V(It):V(Kt,!1)})}r(Ie);var ot=s(Ie),it=l(ot),Ot=l(it,!0);r(it),r(ot);var nt=s(ot),Et=l(nt,!0);r(nt);var ct=s(nt),gt=l(ct),Ht=l(gt,!0);r(gt),r(ct);var dt=s(ct),Vt=l(dt,!0);r(dt);var wt=s(dt),yt=l(wt),bt=l(yt),kt=s(bt,2),Wt=s(kt,2);ce(Wt,{variant:"danger",size:"sm",onclick:()=>Te(e(p).key),children:(V,ae)=>{j();var Ae=ne("Delete");n(V,Ae)},$$slots:{default:!0}}),r(yt),r(wt),r(oe),k((V,ae,Ae,Ke,Oe,jt)=>{var $t;Se=Me(oe,1,"svelte-f2o7w5",null,Se,V),zt(be,ae),q(be,"aria-label",`Select ${Ae??""}`),q(it,"title",e(p).key),M(Ot,Ke),M(Et,Oe),M(Ht,(($t=e(p).httpMetadata)==null?void 0:$t.contentType)??"--"),M(Vt,jt)},[()=>({"row--selected":e(c).has(e(p).key)}),()=>e(c).has(e(p).key),()=>ee(e(p).key),()=>ee(e(p).key),()=>Be(e(p).size),()=>We(e(p).uploaded)]),W("change",be,()=>ue(e(p).key)),W("click",bt,()=>Le(e(p).key)),W("click",kt,()=>{t(K,e(p).key,!0),t(_,!0)}),n(te,oe)}),r(Ue),r(G),r(A);var Lt=s(A,2);{var Ft=te=>{var p=Ua(),oe=l(p);ce(oe,{variant:"secondary",get loading(){return e(R)},onclick:()=>Y(!0),children:(Se,ye)=>{j();var be=ne("Load More");n(Se,be)},$$slots:{default:!0}}),r(p),n(te,p)};ie(Lt,te=>{e(I)&&te(Ft)})}k(()=>zt(ze,e(o).length>0&&e(c).size===e(o).length)),W("change",ze,fe),n(P,C)};ie(Fe,P=>{e(w)?P(qe):e(o).length===0&&e(F).length===0?P(st,1):P(Re,!1)})}k(()=>{se=Me(le,1,"breadcrumb__item svelte-f2o7w5",null,se,{"breadcrumb__item--active":!e(y)}),M($e,e(z))}),W("click",le,()=>H("")),n(i,$)}});var pe=s(Ne,2);{let a=x(()=>`Are you sure you want to delete "${e(O)??""}"? This action cannot be undone.`);Ut(pe,{title:"Delete File",get message(){return e(a)},confirmLabel:"Delete",confirmVariant:"danger",onconfirm:lt,get open(){return e(ve)},set open(i){t(ve,i,!0)}})}var ge=s(pe,2);{let a=x(()=>e(c).size),i=x(()=>e(c).size!==1?"s":""),g=x(()=>`Are you sure you want to delete ${e(c).size} selected file${e(c).size!==1?"s":""}? This action cannot be undone.`);Ut(ge,{get title(){return`Delete ${e(a)??""} File${e(i)??""}`},get message(){return e(g)},confirmLabel:"Delete All",confirmVariant:"danger",onconfirm:re,get open(){return e(b)},set open($){t(b,$,!0)}})}var _t=s(ge,2);va(_t,{get bucket(){return e(z)},onUploaded:()=>Y(),get open(){return e(D)},set open(a){t(D,a,!0)}});var mt=s(_t,2);{let a=x(()=>e(v)?ee(e(v).key):"Image Preview");ht(mt,{get title(){return e(a)},maxWidth:"min(92vw, 960px)",onclose:Ve,get open(){return e(f)},set open(i){t(f,i,!0)},children:(i,g)=>{var $=Tt(),S=me($);{var le=se=>{var $e=Ma(),he=l($e),Ge=l(he);{let Ue=x(()=>Ce(e(v).key)),we=x(()=>`Preview of ${ee(e(v).key)}`);St(Ge,{class:"image-preview__image",get src(){return e(Ue)},get alt(){return e(we)}})}r(he);var Ze=s(he,2),Fe=l(Ze),qe=s(l(Fe),2),st=l(qe,!0);r(qe),r(Fe);var Re=s(Fe,2),P=s(l(Re),2),C=l(P,!0);r(P),r(Re);var A=s(Re,2),G=s(l(A),2),Z=l(G,!0);r(G),r(A);var _e=s(A,2),xe=s(l(_e),2),ze=l(xe,!0);r(xe),r(_e),r(Ze),r($e),k((Ue,we)=>{var Je;M(st,e(v).key),M(C,((Je=e(v).httpMetadata)==null?void 0:Je.contentType)??"--"),M(Z,Ue),M(ze,we)},[()=>Be(e(v).size),()=>We(e(v).uploaded)]),n(se,$e)};ie(S,se=>{e(v)&&se(le)})}n(i,$)},$$slots:{default:!0}})}var Ct=s(mt,2);ma(Ct,{get bucket(){return e(z)},get fileKey(){return e(K)},get open(){return e(_)},set open(a){t(_,a,!0)}}),n(ke,je),rt(),L()}ft(["click","change"]);export{Xa as component};
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{o as $e}from"../chunks/Bn2NtlTj.js";import{p as Me,f as Y,a as ye,b as D,g as e,d as q,c as n,s as l,r as s,ae as je,t as H,u as v}from"../chunks/BdTBlfLy.js";import{s as m}from"../chunks/DtZk82gG.js";import{i as B}from"../chunks/Y22E1hJM.js";import{a as He,t as Be,e as ee,i as te}from"../chunks/CrwlCAM0.js";import{h as Ve}from"../chunks/DHWOQ9Ui.js";import{c as Te,a as h,f as p}from"../chunks/DEELgv7K.js";import{d as Re,a as F,s as re}from"../chunks/CjcrXziO.js";import{s as Se,a as Pe}from"../chunks/Cp8V0Xy2.js";import{b as L}from"../chunks/Q3vAxeY-.js";import{a as Ae}from"../chunks/CbfX3ELZ.js";import{g as De}from"../chunks/R6arueIl.js";import{P as qe}from"../chunks/BkZCgsc3.js";import{M as V,T as Fe}from"../chunks/LL3ulaxa.js";import{b as ae}from"../chunks/Q2nPFxS6.js";var Ee=p('<div class="overview-panel__loading svelte-1uha8ag">Loading...</div>'),Ue=p('<div class="overview-service svelte-1uha8ag"><span></span> <span class="overview-service__name svelte-1uha8ag"> </span> <span class="overview-service__detail svelte-1uha8ag"> </span></div>'),Oe=p('<div class="overview-services svelte-1uha8ag"></div>'),Ze=p('<div class="overview-info__row svelte-1uha8ag"><span class="overview-info__label svelte-1uha8ag">Auth</span> <span class="overview-info__value svelte-1uha8ag"> </span></div>'),Ke=p('<div class="overview-info__row svelte-1uha8ag"><span class="overview-info__label svelte-1uha8ag">Buckets</span> <span class="overview-info__value svelte-1uha8ag"> </span></div>'),Qe=p('<div class="overview-info svelte-1uha8ag"><!> <!> <div class="overview-info__row svelte-1uha8ag"><span class="overview-info__label svelte-1uha8ag">Service Keys</span> <span class="overview-info__value svelte-1uha8ag"> </span></div> <div class="overview-info__row svelte-1uha8ag"><span class="overview-info__label svelte-1uha8ag">Mode</span> <span> </span></div></div>'),ze=p('<a class="overview-action svelte-1uha8ag"><span class="overview-action__icon svelte-1uha8ag"><!></span> <span class="overview-action__label svelte-1uha8ag"> </span> <span class="overview-action__arrow svelte-1uha8ag"><svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg></span></a>'),Ge=p('<div class="overview-metrics svelte-1uha8ag"><!> <!> <!> <!> <!></div> <div class="overview-chart svelte-1uha8ag"><!></div> <div class="overview-panels svelte-1uha8ag"><div class="overview-panel svelte-1uha8ag"><h3 class="overview-panel__title svelte-1uha8ag">Services</h3> <!> <!></div> <div class="overview-panel svelte-1uha8ag"><h3 class="overview-panel__title svelte-1uha8ag">Quick Actions</h3> <div class="overview-actions svelte-1uha8ag"></div></div></div>',1);function ct(oe,se){Me(se,!0);const ie=()=>Pe(Ae,"$authStore",ne),[ne,le]=Se();let E=q(!1),u=q(!0),i=q(null);const ve={"1h":{cardLabel:"1H",chartLabel:"Last 1 hour"},"6h":{cardLabel:"6H",chartLabel:"Last 6 hours"},"24h":{cardLabel:"24H",chartLabel:"Last 24 hours"}};async function ce(){try{const r=await He.fetch("data/overview");D(i,r,!0)}catch(r){Be(Re(r,"Failed to load the dashboard overview.",{hint:"Check your server connection and try reloading."}))}finally{D(u,!1)}}const de=v(()=>()=>{var _,$;if(!(($=(_=e(i))==null?void 0:_.traffic)!=null&&$.summary))return 0;const{totalRequests:r,totalErrors:b}=e(i).traffic.summary;return r>0?b/r*100:0}),he=v(()=>()=>{var r,b;return(b=(r=e(i))==null?void 0:r.traffic)!=null&&b.timeSeries?e(i).traffic.timeSeries.map(_=>({timestamp:_.timestamp,value:_.requests??_.value??0})):[]}),U=v(()=>{var r;return((r=e(i))==null?void 0:r.traffic.appliedRange)??"24h"}),O=v(()=>ve[e(U)]),ue=v(()=>De(e(U)));$e(()=>{ie().accessToken&&(D(E,!0),ce())});const ge=[{label:"Users",href:`${L}/auth`,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="2.5" stroke="currentColor" stroke-width="1.5"/><path d="M3 14C3 11.2386 5.23858 9 8 9C10.7614 9 13 11.2386 13 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'},{label:"Schema",href:`${L}/database/tables`,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="2" y="2" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="9" y="9" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><path d="M7 4.5H9.5V9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>'},{label:"ERD",href:`${L}/database/erd`,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="1" y="2" width="5" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="10" y="10" width="5" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/><path d="M6 4H8.5V12H10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>'},{label:"Files",href:`${L}/storage`,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><path d="M2 4L8 2L14 4V12L8 14L2 12V4Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M2 4L8 6L14 4" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M8 6V14" stroke="currentColor" stroke-width="1.5"/></svg>'},{label:"Analytics",href:`${L}/analytics`,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="2" y="9" width="3" height="5" rx="0.5" stroke="currentColor" stroke-width="1.5"/><rect x="6.5" y="5" width="3" height="9" rx="0.5" stroke="currentColor" stroke-width="1.5"/><rect x="11" y="2" width="3" height="12" rx="0.5" stroke="currentColor" stroke-width="1.5"/></svg>'},{label:"Logs",href:`${L}/logs`,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><path d="M4 4H12M4 7H10M4 10H12M4 13H8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'},{label:"Docs",href:ae,external:!0,icon:'<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><path d="M3 2H10L13 5V14H3V2Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M10 2V5H13" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 8H10M6 10.5H9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'}],pe=v(()=>()=>{if(!e(i))return[];const r=e(i).project;return[{name:"Auth",active:!0,detail:`${r.authProviders.length} provider${r.authProviders.length!==1?"s":""}`},{name:"Database",active:r.totalTables>0,detail:`${r.totalTables} table${r.totalTables!==1?"s":""}`},{name:"Storage",active:r.storageBuckets.length>0,detail:`${r.storageBuckets.length} bucket${r.storageBuckets.length!==1?"s":""}`},{name:"Live",active:!0,detail:`${r.liveConnections} conn${r.liveConnections!==1?"s":""}`}]});var Z=Te(),_e=Y(Z);{var we=r=>{qe(r,{title:"Overview",description:"Project dashboard",get docsHref(){return ae},children:(b,_)=>{var $=Ge(),T=Y($),K=n(T);{let a=v(()=>{var t,o;return((o=(t=e(i))==null?void 0:t.project)==null?void 0:o.totalUsers)??0});V(K,{label:"Total Users",get value(){return e(a)},get loading(){return e(u)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="2.5" stroke="currentColor" stroke-width="1.5"/><path d="M3 14C3 11.2386 5.23858 9 8 9C10.7614 9 13 11.2386 13 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'})}var Q=l(K,2);{let a=v(()=>{var t,o;return((o=(t=e(i))==null?void 0:t.project)==null?void 0:o.totalTables)??0});V(Q,{label:"Tables",get value(){return e(a)},get loading(){return e(u)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="2" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><rect x="9" y="9" width="5" height="5" rx="1" stroke="currentColor" stroke-width="1.5"/><path d="M7 4.5H9.5V9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>'})}var z=l(Q,2);{let a=v(()=>`Requests (${e(O).cardLabel})`),t=v(()=>{var o,c,d;return((d=(c=(o=e(i))==null?void 0:o.traffic)==null?void 0:c.summary)==null?void 0:d.totalRequests)??0});V(z,{get label(){return e(a)},get value(){return e(t)},get loading(){return e(u)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14V8L5 4L8 7L11 2L14 6V14H2Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>'})}var G=l(z,2);{let a=v(()=>e(de)().toFixed(1));V(G,{label:"5xx Rate",get value(){return e(a)},unit:"%",get loading(){return e(u)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M8 5V9M8 11V11.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'})}var fe=l(G,2);{let a=v(()=>{var t,o;return((o=(t=e(i))==null?void 0:t.project)==null?void 0:o.liveConnections)??0});V(fe,{label:"Live",get value(){return e(a)},unit:"conns",get loading(){return e(u)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M1 8H3L5 3L8 13L11 6L13 8H15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>'})}s(T);var R=l(T,2),ke=n(R);{let a=v(()=>e(he)()),t=v(()=>`Requests — ${e(O).chartLabel}`);Fe(ke,{get data(){return e(a)},type:"line",height:240,get label(){return e(t)},get formatTime(){return e(ue)},get loading(){return e(u)}})}s(R);var I=l(R,2),S=n(I),J=l(n(S),2);{var me=a=>{var t=Ee();h(a,t)},be=a=>{var t=Oe();ee(t,21,()=>e(pe)(),te,(o,c)=>{var d=Ue(),w=n(d);let g;var x=l(w,2),P=n(x,!0);s(x);var M=l(x,2),y=n(M,!0);s(M),s(d),H(()=>{g=re(w,1,"overview-service__dot svelte-1uha8ag",null,g,{"overview-service__dot--active":e(c).active}),m(P,e(c).name),m(y,e(c).detail)}),h(o,d)}),s(t),h(a,t)};B(J,a=>{e(u)?a(me):a(be,!1)})}var xe=l(J,2);{var Ce=a=>{var t=Qe(),o=n(t);{var c=f=>{var k=Ze(),j=l(n(k),2),A=n(j,!0);s(j),s(k),H(C=>m(A,C),[()=>e(i).project.authProviders.map(C=>C.charAt(0).toUpperCase()+C.slice(1)).join(", ")]),h(f,k)};B(o,f=>{e(i).project.authProviders.length>0&&f(c)})}var d=l(o,2);{var w=f=>{var k=Ke(),j=l(n(k),2),A=n(j,!0);s(j),s(k),H(C=>m(A,C),[()=>e(i).project.storageBuckets.join(", ")]),h(f,k)};B(d,f=>{e(i).project.storageBuckets.length>0&&f(w)})}var g=l(d,2),x=l(n(g),2),P=n(x,!0);s(x),s(g);var M=l(g,2),y=l(n(M),2);let X;var Le=n(y,!0);s(y),s(M),s(t),H(()=>{m(P,e(i).project.serviceKeyCount),X=re(y,1,"overview-info__value overview-info__badge svelte-1uha8ag",null,X,{"overview-info__badge--dev":e(i).project.devMode}),m(Le,e(i).project.devMode?"Development":"Production")}),h(a,t)};B(xe,a=>{var t;(t=e(i))!=null&&t.project&&a(Ce)})}s(S);var N=l(S,2),W=l(n(N),2);ee(W,21,()=>ge,te,(a,t)=>{var o=ze(),c=n(o),d=n(c);Ve(d,()=>e(t).icon),s(c);var w=l(c,2),g=n(w,!0);s(w),je(2),s(o),H(()=>{F(o,"href",e(t).href),F(o,"target",e(t).external?"_blank":void 0),F(o,"rel",e(t).external?"noreferrer":void 0),m(g,e(t).label)}),h(a,o)}),s(W),s(N),s(I),h(b,$)}})};B(_e,r=>{e(E)&&r(we)})}h(oe,Z),ye(),le()}export{ct as component};
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{o as nt}from"../chunks/Bn2NtlTj.js";import{p as it,a as lt,g as t,f as ct,c as h,s as c,r as d,u as l,t as S,b as m,d as y,ae as ut}from"../chunks/BdTBlfLy.js";import{d as vt,s as j,a as U}from"../chunks/DtZk82gG.js";import{i as B}from"../chunks/Y22E1hJM.js";import{a as dt,t as z,e as mt,i as gt}from"../chunks/CrwlCAM0.js";import{a as x,f as C}from"../chunks/DEELgv7K.js";import{d as pt,s as Z,a as ht,r as G}from"../chunks/CjcrXziO.js";import{b as J}from"../chunks/CYatlt7w.js";import{g as ft}from"../chunks/R6arueIl.js";import{P as _t}from"../chunks/BkZCgsc3.js";import{a as bt}from"../chunks/Q2nPFxS6.js";import{M as H,T as yt}from"../chunks/LL3ulaxa.js";import{D as kt,T as wt}from"../chunks/BvoGcDFV.js";var xt=C("<button> </button>"),Ct=C('<div class="analytics-actions__custom svelte-1m0gshv"><input type="datetime-local" class="custom-date-input svelte-1m0gshv"/> <span class="custom-date-sep svelte-1m0gshv">→</span> <input type="datetime-local" class="custom-date-input svelte-1m0gshv"/> <button class="custom-date-apply svelte-1m0gshv">Apply</button></div>'),Tt=C('<span class="analytics-actions__updated svelte-1m0gshv"> </span>'),Dt=C('<span class="analytics-actions__dot svelte-1m0gshv"></span>'),$t=C('<div class="analytics-actions svelte-1m0gshv"><div class="analytics-actions__ranges svelte-1m0gshv"></div> <button type="button">Exclude admin traffic</button> <!> <div class="analytics-actions__status svelte-1m0gshv"><!> <button><!> ⟳</button></div></div>'),At=C('<p class="analytics-custom-error svelte-1m0gshv"> </p>'),Mt=C('<div class="analytics-grid svelte-1m0gshv"><!> <!> <!> <!></div> <div class="analytics-chart svelte-1m0gshv"><!></div> <!> <div class="analytics-bottom svelte-1m0gshv"><!> <!></div>',1);function jt(K,Q){it(Q,!0);let v=y(!0),u=y(null),f=y("24h"),T=y(!0),P=y(""),E=y(!1);const W=[{value:"1h",label:"1H"},{value:"6h",label:"6H"},{value:"24h",label:"24H"},{value:"7d",label:"7D"},{value:"30d",label:"30D"},{value:"custom",label:"Custom"}];let g=y(""),p=y("");const D=l(()=>()=>{if(t(f)!=="custom"||!t(g)||!t(p))return"";const a=new Date(t(g)).getTime(),r=new Date(t(p)).getTime();return Number.isNaN(a)||Number.isNaN(r)?"Enter a valid start and end time.":r<a?"End time must be after the start time.":""});async function M(){try{const a=new URLSearchParams({metric:"overview"});t(f)==="custom"&&t(g)&&t(p)?(a.set("start",new Date(t(g)).toISOString()),a.set("end",new Date(t(p)).toISOString())):a.set("range",t(f)),t(E)&&a.set("excludeCategory","admin");const r=`data/analytics?${a.toString()}`,_=await dt.fetch(r);m(u,_,!0),m(P,new Date().toLocaleTimeString(),!0)}catch(a){z(pt(a,"Failed to load analytics data."))}finally{m(v,!1)}}function X(a){m(f,a,!0),a!=="custom"&&(m(v,!0),M())}function Y(){m(E,!t(E)),(t(f)!=="custom"||t(g)&&t(p)&&!t(D)())&&(m(v,!0),M())}function tt(){if(!(!t(g)||!t(p))){if(t(D)()){z(t(D)());return}m(v,!0),M()}}nt(()=>{M();const a=setInterval(()=>{t(T)&&M()},3e4);return()=>clearInterval(a)});const et=l(()=>()=>{var _;if(!((_=t(u))!=null&&_.summary))return 0;const{totalRequests:a,totalErrors:r}=t(u).summary;return a>0?r/a*100:0}),at=l(()=>()=>{var a;return(a=t(u))!=null&&a.timeSeries?t(u).timeSeries.map(r=>({timestamp:r.timestamp,value:r.requests??r.value??0})):[]}),rt=l(()=>ft(t(f),t(g),t(p))),st=l(()=>()=>{var a;return(a=t(u))!=null&&a.breakdown?t(u).breakdown.map(r=>({label:r.label||"other",value:r.count})):[]}),ot=l(()=>()=>{var a;return(a=t(u))!=null&&a.topItems?t(u).topItems.map(r=>({label:r.label,count:r.count,secondary:`${Math.round(r.avgLatency)}ms avg · ${r.errorRate.toFixed(1)}% 5xx`})):[]});_t(K,{title:"Analytics",description:"API traffic and performance overview",get docsHref(){return bt},actions:r=>{var _=$t(),$=h(_);mt($,21,()=>W,gt,(o,n)=>{var e=xt();let s;var i=h(e,!0);d(e),S(()=>{s=Z(e,1,"analytics-actions__range-btn svelte-1m0gshv",null,s,{"analytics-actions__range-btn--active":t(f)===t(n).value}),j(i,t(n).label)}),U("click",e,()=>X(t(n).value)),x(o,e)}),d($);var k=c($,2);let L;var R=c(k,2);{var I=o=>{var n=Ct(),e=h(n);G(e);var s=c(e,4);G(s);var i=c(s,2);d(n),S(w=>i.disabled=w,[()=>!t(g)||!t(p)||!!t(D)()]),J(e,()=>t(g),w=>m(g,w)),J(s,()=>t(p),w=>m(p,w)),U("click",i,tt),x(o,n)};B(R,o=>{t(f)==="custom"&&o(I)})}var q=c(R,2),A=h(q);{var V=o=>{var n=Tt(),e=h(n);d(n),S(()=>j(e,`Updated ${t(P)??""}`)),x(o,n)};B(A,o=>{t(P)&&o(V)})}var b=c(A,2);let F;var O=h(b);{var N=o=>{var n=Dt();x(o,n)};B(O,o=>{t(T)&&o(N)})}ut(),d(b),d(q),d(_),S(()=>{L=Z(k,1,"analytics-actions__filter-btn svelte-1m0gshv",null,L,{"analytics-actions__filter-btn--active":t(E)}),F=Z(b,1,"analytics-actions__refresh-btn svelte-1m0gshv",null,F,{"analytics-actions__refresh-btn--active":t(T)}),ht(b,"title",t(T)?"Auto-refresh ON (30s)":"Auto-refresh OFF")}),U("click",k,Y),U("click",b,()=>m(T,!t(T))),x(r,_)},children:(r,_)=>{var $=Mt(),k=ct($),L=h(k);{let e=l(()=>{var s,i;return((i=(s=t(u))==null?void 0:s.summary)==null?void 0:i.totalRequests)??0});H(L,{label:"Total Requests",get value(){return t(e)},get loading(){return t(v)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14V8L5 4L8 7L11 2L14 6V14H2Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>'})}var R=c(L,2);{let e=l(()=>{var s,i;return((i=(s=t(u))==null?void 0:s.summary)==null?void 0:i.uniqueUsers)??0});H(R,{label:"Unique Users",get value(){return t(e)},get loading(){return t(v)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="2.5" stroke="currentColor" stroke-width="1.5"/><path d="M3 14C3 11.2386 5.23858 9 8 9C10.7614 9 13 11.2386 13 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'})}var I=c(R,2);{let e=l(()=>t(et)().toFixed(1));H(I,{label:"5xx Rate",get value(){return t(e)},unit:"%",get loading(){return t(v)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M8 5V9M8 11V11.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'})}var q=c(I,2);{let e=l(()=>{var s,i;return Math.round(((i=(s=t(u))==null?void 0:s.summary)==null?void 0:i.avgLatency)??0)});H(q,{label:"Avg Latency",get value(){return t(e)},unit:"ms",get loading(){return t(v)},icon:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M8 4V8L10.5 10.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>'})}d(k);var A=c(k,2),V=h(A);{let e=l(()=>t(at)());yt(V,{get data(){return t(e)},type:"line",height:260,label:"Requests over time",get formatTime(){return t(rt)},get loading(){return t(v)}})}d(A);var b=c(A,2);{var F=e=>{var s=At(),i=h(s,!0);d(s),S(w=>j(i,w),[()=>t(D)()]),x(e,s)},O=l(()=>t(f)==="custom"&&t(D)());B(b,e=>{t(O)&&e(F)})}var N=c(b,2),o=h(N);{let e=l(()=>t(st)());kt(o,{get items(){return t(e)},title:"Category Distribution",get loading(){return t(v)}})}var n=c(o,2);{let e=l(()=>t(ot)());wt(n,{get items(){return t(e)},title:"Top Endpoints",get loading(){return t(v)}})}d(N),x(r,$)}}),lt()}vt(["click"]);export{jt as component};
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{o as z}from"../chunks/Bn2NtlTj.js";import{p as B,a as O,f as j,c as f,g as t,s as m,r as g,u as s,b as y,d as x,t as G}from"../chunks/BdTBlfLy.js";import{d as J,s as K,a as N}from"../chunks/DtZk82gG.js";import{a as Q,t as V,e as W,i as X}from"../chunks/CrwlCAM0.js";import{a as D,f as T}from"../chunks/DEELgv7K.js";import{d as Y,s as Z}from"../chunks/CjcrXziO.js";import{g as tt}from"../chunks/R6arueIl.js";import{P as et}from"../chunks/BkZCgsc3.js";import{a as at}from"../chunks/Q2nPFxS6.js";import{M as _,T as rt}from"../chunks/LL3ulaxa.js";import{D as st,T as ot}from"../chunks/BvoGcDFV.js";var nt=T("<button> </button>"),it=T('<div class="range-selector"></div>'),lt=T('<div class="analytics-grid"><!> <!> <!> <!></div> <div class="analytics-chart"><!></div> <div class="analytics-bottom"><!> <!></div>',1);function At(F,I){B(I,!0);let o=x(!0),n=x(null),h=x("24h");const M=[{value:"1h",label:"1H"},{value:"6h",label:"6H"},{value:"24h",label:"24H"},{value:"7d",label:"7D"},{value:"30d",label:"30D"}];async function A(){try{const e=await Q.fetch(`data/analytics?range=${t(h)}&category=auth&metric=overview`);y(n,e,!0)}catch(e){V(Y(e,"Failed to load auth analytics."))}finally{y(o,!1)}}function k(e){y(h,e,!0),y(o,!0),A()}z(()=>{A();const e=setInterval(()=>void A(),3e4);return()=>clearInterval(e)});const w=s(()=>()=>{var c;if(!((c=t(n))!=null&&c.summary))return 0;const{totalRequests:e,totalErrors:a}=t(n).summary;return e>0?a/e*100:0}),H=s(()=>()=>{var e;return(((e=t(n))==null?void 0:e.timeSeries)||[]).map(a=>({timestamp:a.timestamp,value:a.requests??a.value??0}))}),L=s(()=>tt(t(h))),C=s(()=>()=>{var e;return(((e=t(n))==null?void 0:e.breakdown)||[]).map(a=>({label:a.label||"other",value:a.count}))}),E=s(()=>()=>{var e;return(((e=t(n))==null?void 0:e.topItems)||[]).map(a=>({label:a.label,count:a.count,secondary:`${Math.round(a.avgLatency)}ms · ${a.errorRate.toFixed(1)}% 5xx`}))});et(F,{title:"Auth Analytics",description:"Authentication and authorization metrics",get docsHref(){return at},actions:a=>{var c=it();W(c,21,()=>M,X,(p,d)=>{var i=nt();let v;var b=f(i,!0);g(i),G(()=>{v=Z(i,1,"range-btn",null,v,{"range-btn--active":t(h)===t(d).value}),K(b,t(d).label)}),N("click",i,()=>k(t(d).value)),D(p,i)}),g(c),D(a,c)},children:(a,c)=>{var p=lt(),d=j(p),i=f(d);{let r=s(()=>{var l,u;return((u=(l=t(n))==null?void 0:l.summary)==null?void 0:u.totalRequests)??0});_(i,{label:"Auth Requests",get value(){return t(r)},get loading(){return t(o)}})}var v=m(i,2);{let r=s(()=>{var l,u;return((u=(l=t(n))==null?void 0:l.summary)==null?void 0:u.uniqueUsers)??0});_(v,{label:"Unique Users",get value(){return t(r)},get loading(){return t(o)}})}var b=m(v,2);{let r=s(()=>t(w)().toFixed(1));_(b,{label:"5xx Rate",get value(){return t(r)},unit:"%",get loading(){return t(o)}})}var P=m(b,2);{let r=s(()=>{var l,u;return Math.round(((u=(l=t(n))==null?void 0:l.summary)==null?void 0:u.avgLatency)??0)});_(P,{label:"Avg Latency",get value(){return t(r)},unit:"ms",get loading(){return t(o)}})}g(d);var $=m(d,2),U=f($);{let r=s(()=>t(H)());rt(U,{get data(){return t(r)},type:"line",height:240,label:"Auth requests over time",get formatTime(){return t(L)},get loading(){return t(o)},color:"#7c3aed"})}g($);var q=m($,2),R=f(q);{let r=s(()=>t(C)());st(R,{get items(){return t(r)},title:"Action Distribution (signup, signin, oauth...)",get loading(){return t(o)}})}var S=m(R,2);{let r=s(()=>t(E)());ot(S,{get items(){return t(r)},title:"Top Auth Operations",get loading(){return t(o)}})}g(q),D(a,p)}}),O()}J(["click"]);export{At as component};
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{o as S}from"../chunks/Bn2NtlTj.js";import{p as O,a as Q,f as j,c as h,g as t,s as m,r as g,u as s,b as y,d as x,t as z}from"../chunks/BdTBlfLy.js";import{d as G,s as J,a as K}from"../chunks/DtZk82gG.js";import{a as N,t as V,e as W,i as X}from"../chunks/CrwlCAM0.js";import{a as A,f as R}from"../chunks/DEELgv7K.js";import{d as Y,s as Z}from"../chunks/CjcrXziO.js";import{g as tt}from"../chunks/R6arueIl.js";import{P as et}from"../chunks/BkZCgsc3.js";import{a as at}from"../chunks/Q2nPFxS6.js";import{M as _,T as rt}from"../chunks/LL3ulaxa.js";import{D as st,T as ot}from"../chunks/BvoGcDFV.js";var lt=R("<button> </button>"),nt=R('<div class="range-selector"></div>'),it=R('<div class="analytics-grid"><!> <!> <!> <!></div> <div class="analytics-chart"><!></div> <div class="analytics-bottom"><!> <!></div>',1);function Dt(C,F){O(F,!0);let o=x(!0),l=x(null),b=x("24h");const I=[{value:"1h",label:"1H"},{value:"6h",label:"6H"},{value:"24h",label:"24H"},{value:"7d",label:"7D"},{value:"30d",label:"30D"}];async function D(){try{const e=await N.fetch(`data/analytics?range=${t(b)}&category=db&metric=overview`);y(l,e,!0)}catch(e){V(Y(e,"Failed to load database analytics."))}finally{y(o,!1)}}function k(e){y(b,e,!0),y(o,!0),D()}S(()=>{D();const e=setInterval(()=>void D(),3e4);return()=>clearInterval(e)});const q=s(()=>()=>{var u;if(!((u=t(l))!=null&&u.summary))return 0;const{totalRequests:e,totalErrors:a}=t(l).summary;return e>0?a/e*100:0}),w=s(()=>()=>{var e;return(((e=t(l))==null?void 0:e.timeSeries)||[]).map(a=>({timestamp:a.timestamp,value:a.requests??a.value??0}))}),H=s(()=>tt(t(b))),L=s(()=>()=>{var e;return(((e=t(l))==null?void 0:e.breakdown)||[]).map(a=>({label:a.label||"other",value:a.count}))}),U=s(()=>()=>{var e;return(((e=t(l))==null?void 0:e.topItems)||[]).map(a=>({label:a.label,count:a.count,secondary:`${Math.round(a.avgLatency)}ms · ${a.errorRate.toFixed(1)}% 5xx`}))});et(C,{title:"Database Analytics",description:"CRUD operations and table usage metrics",get docsHref(){return at},actions:a=>{var u=nt();W(u,21,()=>I,X,(p,d)=>{var n=lt();let v;var f=h(n,!0);g(n),z(()=>{v=Z(n,1,"range-btn",null,v,{"range-btn--active":t(b)===t(d).value}),J(f,t(d).label)}),K("click",n,()=>k(t(d).value)),A(p,n)}),g(u),A(a,u)},children:(a,u)=>{var p=it(),d=j(p),n=h(d);{let r=s(()=>{var i,c;return((c=(i=t(l))==null?void 0:i.summary)==null?void 0:c.totalRequests)??0});_(n,{label:"DB Queries",get value(){return t(r)},get loading(){return t(o)}})}var v=m(n,2);{let r=s(()=>{var i,c;return((c=(i=t(l))==null?void 0:i.summary)==null?void 0:c.uniqueUsers)??0});_(v,{label:"Active Users",get value(){return t(r)},get loading(){return t(o)}})}var f=m(v,2);{let r=s(()=>t(q)().toFixed(1));_(f,{label:"5xx Rate",get value(){return t(r)},unit:"%",get loading(){return t(o)}})}var E=m(f,2);{let r=s(()=>{var i,c;return Math.round(((c=(i=t(l))==null?void 0:i.summary)==null?void 0:c.avgLatency)??0)});_(E,{label:"Avg Latency",get value(){return t(r)},unit:"ms",get loading(){return t(o)}})}g(d);var $=m(d,2),P=h($);{let r=s(()=>t(w)());rt(P,{get data(){return t(r)},type:"bar",height:240,label:"Database operations over time",get formatTime(){return t(H)},get loading(){return t(o)},color:"#16a34a"})}g($);var T=m($,2),M=h(T);{let r=s(()=>t(L)());st(M,{get items(){return t(r)},title:"Operation Distribution (CRUD)",get loading(){return t(o)}})}var B=m(M,2);{let r=s(()=>t(U)());ot(B,{get items(){return t(r)},title:"Most Accessed Tables",get loading(){return t(o)}})}g(T),A(a,p)}}),Q()}G(["click"]);export{Dt as component};
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{o as be}from"../chunks/Bn2NtlTj.js";import{p as Se,d as g,M as q,a as we,f as De,c as o,b as c,g as e,s as d,r as i,ae as z,t as h,u as G}from"../chunks/BdTBlfLy.js";import{d as Ee,s as p,a as Ie}from"../chunks/DtZk82gG.js";import{i as E}from"../chunks/Y22E1hJM.js";import{e as $e,a as Be,t as Le}from"../chunks/CrwlCAM0.js";import{t as K,a as u,f as m}from"../chunks/DEELgv7K.js";import{d as Te,s as Oe,a as Pe}from"../chunks/CjcrXziO.js";import{P as Me}from"../chunks/BkZCgsc3.js";import{a as ke}from"../chunks/Q2nPFxS6.js";import{B as Ae}from"../chunks/Bcs4KYNp.js";import{S as Q}from"../chunks/Ff90owjx.js";import{I as Ce}from"../chunks/nlAMTi52.js";import{E as He}from"../chunks/m9eWh0Cd.js";import{B as Ne}from"../chunks/DOOPbWwG.js";var Re=m('<div class="loading-state svelte-1tn2f4x">Loading events...</div>'),Fe=m('<span class="timeline-item__line svelte-1tn2f4x"></span>'),Ue=m('<span class="timeline-item__user svelte-1tn2f4x"> </span>'),Je=m('<span class="timeline-item__user svelte-1tn2f4x"> </span>'),je=m('<pre class="timeline-item__detail svelte-1tn2f4x"> </pre>'),Ve=m('<pre class="timeline-item__detail svelte-1tn2f4x"> </pre>'),qe=m('<button type="button"><div class="timeline-item__connector svelte-1tn2f4x"><span class="timeline-item__icon svelte-1tn2f4x"> </span> <!></div> <div class="timeline-item__content svelte-1tn2f4x"><div class="timeline-item__header svelte-1tn2f4x"><!> <!> <span class="timeline-item__time svelte-1tn2f4x"> </span></div> <!></div></button>'),ze=m('<div class="timeline svelte-1tn2f4x"></div>'),Ge=m('<div class="controls svelte-1tn2f4x"><div class="controls__fields svelte-1tn2f4x"><!> <!> <!></div> <div class="controls__actions svelte-1tn2f4x"><!></div></div> <!>',1);function ut(W,X){Se(X,!0);let I=g(!0),x=g(q([])),S=g("all"),$=g("24h"),w=g(""),D=g(q(new Set));const Y=[{value:"all",label:"All Events"},{value:"signup",label:"Signup"},{value:"signin",label:"Sign In"},{value:"signout",label:"Sign Out"},{value:"password_reset",label:"Password Reset"},{value:"oauth",label:"OAuth"},{value:"custom",label:"Custom"}],Z=[{value:"1h",label:"Last 1 Hour"},{value:"6h",label:"Last 6 Hours"},{value:"24h",label:"Last 24 Hours"},{value:"7d",label:"Last 7 Days"},{value:"30d",label:"Last 30 Days"}];async function C(){c(I,!0);try{let r=`data/analytics/events?range=${e($)}&limit=100`;e(S)!=="all"&&(r+=`&type=${encodeURIComponent(e(S))}`),e(w).trim()&&(r+=`&userId=${encodeURIComponent(e(w).trim())}`);const n=await Be.fetch(r);c(x,n.events??n.data??[],!0)}catch(r){Le(Te(r,"Failed to load analytics events.")),c(x,[],!0)}finally{c(I,!1)}}function ee(){c(D,new Set,!0),C()}function te(r){const n=new Set(e(D));n.has(r)?n.delete(r):n.add(r),c(D,n,!0)}be(()=>{C()});function ae(r){try{return new Date(r).toLocaleString()}catch{return String(r)}}function re(r){try{const n=Date.now()-new Date(r).getTime();return n<6e4?"just now":n<36e5?`${Math.floor(n/6e4)}m ago`:n<864e5?`${Math.floor(n/36e5)}h ago`:`${Math.floor(n/864e5)}d ago`}catch{return""}}function se(r){switch(r){case"signup":return"success";case"signin":return"primary";case"signout":return"default";case"password_reset":return"warning";case"oauth":return"primary";case"error":case"failed":return"danger";default:return"default"}}function ne(r){switch(r){case"signup":return"➕";case"signin":return"→";case"signout":return"←";case"password_reset":return"🔑";case"oauth":return"🔗";default:return"○"}}Me(W,{title:"Event Timeline",description:"Browse auth and custom events chronologically",get docsHref(){return ke},children:(r,n)=>{var H=Ge(),B=De(H),L=o(B),N=o(L);Q(N,{label:"Event Type",get options(){return Y},get value(){return e(S)},set value(s){c(S,s,!0)}});var R=d(N,2);Q(R,{label:"Time Range",get options(){return Z},get value(){return e($)},set value(s){c($,s,!0)}});var le=d(R,2);Ce(le,{label:"User ID",placeholder:"Filter by user...",get value(){return e(w)},set value(s){c(w,s,!0)}}),i(L);var F=d(L,2),ie=o(F);Ae(ie,{variant:"primary",onclick:ee,children:(s,f)=>{z();var y=K("Search");u(s,y)},$$slots:{default:!0}}),i(F),i(B);var oe=d(B,2);{var ue=s=>{var f=Re();u(s,f)},ve=s=>{He(s,{title:"No events found",description:"No events match the current filters."})},ce=s=>{var f=ze();$e(f,23,()=>e(x),(y,l)=>y.id??l,(y,l,T)=>{const O=G(()=>e(D).has(e(T)));var b=qe();let U;var P=o(b),M=o(P),de=o(M,!0);i(M);var me=d(M,2);{var pe=t=>{var a=Fe();u(t,a)};E(me,t=>{e(T)<e(x).length-1&&t(pe)})}i(P);var J=d(P,2),k=o(J),j=o(k);{let t=G(()=>se(e(l).type));Ne(j,{get variant(){return e(t)},children:(a,v)=>{z();var _=K();h(()=>p(_,e(l).type)),u(a,_)},$$slots:{default:!0}})}var V=d(j,2);{var fe=t=>{var a=Ue(),v=o(a,!0);i(a),h(()=>p(v,e(l).userEmail)),u(t,a)},_e=t=>{var a=Je(),v=o(a,!0);i(a),h(()=>p(v,e(l).userId)),u(t,a)};E(V,t=>{e(l).userEmail?t(fe):e(l).userId&&t(_e,1)})}var A=d(V,2),ge=o(A,!0);i(A),i(k);var he=d(k,2);{var xe=t=>{var a=je(),v=o(a,!0);i(a),h(_=>p(v,_),[()=>JSON.stringify(e(l).metadata,null,2)]),u(t,a)},ye=t=>{var a=Ve(),v=o(a,!0);i(a),h(_=>p(v,_),[()=>JSON.stringify(e(l),null,2)]),u(t,a)};E(he,t=>{e(O)&&e(l).metadata?t(xe):e(O)&&t(ye,1)})}i(J),i(b),h((t,a,v)=>{U=Oe(b,1,"timeline-item svelte-1tn2f4x",null,U,{"timeline-item--expanded":e(O)}),p(de,t),Pe(A,"title",a),p(ge,v)},[()=>ne(e(l).type),()=>ae(e(l).timestamp),()=>re(e(l).timestamp)]),Ie("click",b,()=>te(e(T))),u(y,b)}),i(f),u(s,f)};E(oe,s=>{e(I)?s(ue):e(x).length===0?s(ve,1):s(ce,!1)})}u(r,H)}}),we()}Ee(["click"]);export{ut as component};
@@ -0,0 +1 @@
1
+ import"../chunks/CWj6FrbW.js";import{o as U}from"../chunks/Bn2NtlTj.js";import{p as j,a as z,f as G,c as b,g as t,s as m,r as g,u as n,b as y,d as D,t as J}from"../chunks/BdTBlfLy.js";import{d as K,s as N,a as O}from"../chunks/DtZk82gG.js";import{a as Q,t as V,e as W,i as X}from"../chunks/CrwlCAM0.js";import{a as F,f as I}from"../chunks/DEELgv7K.js";import{d as Y,s as Z}from"../chunks/CjcrXziO.js";import{P as tt}from"../chunks/BkZCgsc3.js";import{a as et}from"../chunks/Q2nPFxS6.js";import{M as _,T as at}from"../chunks/LL3ulaxa.js";import{D as rt,T as nt}from"../chunks/BvoGcDFV.js";var st=I("<button> </button>"),ot=I('<div class="range-selector"></div>'),lt=I('<div class="analytics-grid"><!> <!> <!> <!></div> <div class="analytics-chart"><!></div> <div class="analytics-bottom"><!> <!></div>',1);function yt(w,A){j(A,!0);let s=D(!0),o=D(null),p=D("24h");const M=[{value:"1h",label:"1H"},{value:"6h",label:"6H"},{value:"24h",label:"24H"},{value:"7d",label:"7D"},{value:"30d",label:"30D"}],T=n(()=>()=>{switch(t(p)){case"1h":return"minute";case"6h":case"24h":return"hour";default:return"day"}});async function $(){try{const e=await Q.fetch(`data/analytics?range=${t(p)}&category=function&metric=overview&groupBy=${t(T)()}`);y(o,e,!0)}catch(e){V(Y(e,"Failed to load functions analytics."))}finally{y(s,!1)}}function k(e){y(p,e,!0),y(s,!0),$()}U(()=>{$();const e=setInterval($,3e4);return()=>clearInterval(e)});const H=n(()=>()=>{var c;if(!((c=t(o))!=null&&c.summary))return 0;const{totalRequests:e,totalErrors:a}=t(o).summary;return e>0?a/e*100:0}),B=n(()=>()=>{var e;return(((e=t(o))==null?void 0:e.timeSeries)||[]).map(a=>({timestamp:a.timestamp,value:a.requests??a.value??0}))}),C=n(()=>()=>{var e;return(((e=t(o))==null?void 0:e.breakdown)||[]).map(a=>({label:a.label||"other",value:a.count}))}),E=n(()=>()=>{var e;return(((e=t(o))==null?void 0:e.topItems)||[]).map(a=>({label:a.label,count:a.count,secondary:`${Math.round(a.avgLatency)}ms · ${a.errorRate.toFixed(1)}% 5xx`}))});tt(w,{title:"Functions Analytics",description:"Serverless function execution and performance metrics",get docsHref(){return et},actions:a=>{var c=ot();W(c,21,()=>M,X,(f,d)=>{var l=st();let v;var h=b(l,!0);g(l),J(()=>{v=Z(l,1,"range-btn",null,v,{"range-btn--active":t(p)===t(d).value}),N(h,t(d).label)}),O("click",l,()=>k(t(d).value)),F(f,l)}),g(c),F(a,c)},children:(a,c)=>{var f=lt(),d=G(f),l=b(d);{let r=n(()=>{var i,u;return((u=(i=t(o))==null?void 0:i.summary)==null?void 0:u.totalRequests)??0});_(l,{label:"Invocations",get value(){return t(r)},get loading(){return t(s)}})}var v=m(l,2);{let r=n(()=>{var i,u;return((u=(i=t(o))==null?void 0:i.summary)==null?void 0:u.uniqueUsers)??0});_(v,{label:"Unique Users",get value(){return t(r)},get loading(){return t(s)}})}var h=m(v,2);{let r=n(()=>t(H)().toFixed(1));_(h,{label:"5xx Rate",get value(){return t(r)},unit:"%",get loading(){return t(s)}})}var L=m(h,2);{let r=n(()=>{var i,u;return Math.round(((u=(i=t(o))==null?void 0:i.summary)==null?void 0:u.avgLatency)??0)});_(L,{label:"Avg Duration",get value(){return t(r)},unit:"ms",get loading(){return t(s)}})}g(d);var x=m(d,2),P=b(x);{let r=n(()=>t(B)());at(P,{get data(){return t(r)},type:"bar",height:240,label:"Function invocations over time",get loading(){return t(s)},color:"#dc2626"})}g(x);var R=m(x,2),q=b(R);{let r=n(()=>t(C)());rt(q,{get items(){return t(r)},title:"Functions by invocation count",get loading(){return t(s)}})}var S=m(q,2);{let r=n(()=>t(E)());nt(S,{get items(){return t(r)},title:"Top Functions",get loading(){return t(s)}})}g(R),F(a,f)}}),z()}K(["click"]);export{yt as component};
@@ -1 +1 @@
1
- {"version":"1774395752593"}
1
+ {"version":"1774560919084"}
@@ -5,12 +5,12 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>EdgeBase Admin</title>
7
7
  <link rel="icon" href="/admin/favicon.svg" type="image/svg+xml" />
8
- <link href="/admin/_app/immutable/entry/start.CtsqDyfj.js" rel="modulepreload">
9
- <link href="/admin/_app/immutable/chunks/D__dwMuW.js" rel="modulepreload">
8
+ <link href="/admin/_app/immutable/entry/start.DY6YakU0.js" rel="modulepreload">
9
+ <link href="/admin/_app/immutable/chunks/SQVAC3Cv.js" rel="modulepreload">
10
10
  <link href="/admin/_app/immutable/chunks/BdTBlfLy.js" rel="modulepreload">
11
11
  <link href="/admin/_app/immutable/chunks/Bn2NtlTj.js" rel="modulepreload">
12
- <link href="/admin/_app/immutable/chunks/DBsVqhuh.js" rel="modulepreload">
13
- <link href="/admin/_app/immutable/entry/app.C8ylfBe6.js" rel="modulepreload">
12
+ <link href="/admin/_app/immutable/chunks/Q3vAxeY-.js" rel="modulepreload">
13
+ <link href="/admin/_app/immutable/entry/app.CP83Ni80.js" rel="modulepreload">
14
14
  <link href="/admin/_app/immutable/chunks/B2bEC_Hm.js" rel="modulepreload">
15
15
  <link href="/admin/_app/immutable/chunks/Bb0e0sAP.js" rel="modulepreload">
16
16
  <link href="/admin/_app/immutable/chunks/DtZk82gG.js" rel="modulepreload">
@@ -26,7 +26,7 @@
26
26
  <div style="display: contents">
27
27
  <script>
28
28
  {
29
- __sveltekit_x4yv2o = {
29
+ __sveltekit_hkuxmm = {
30
30
  base: "/admin",
31
31
  assets: "/admin"
32
32
  };
@@ -34,8 +34,8 @@
34
34
  const element = document.currentScript.parentElement;
35
35
 
36
36
  Promise.all([
37
- import("/admin/_app/immutable/entry/start.CtsqDyfj.js"),
38
- import("/admin/_app/immutable/entry/app.C8ylfBe6.js")
37
+ import("/admin/_app/immutable/entry/start.DY6YakU0.js"),
38
+ import("/admin/_app/immutable/entry/app.CP83Ni80.js")
39
39
  ]).then(([kit, app]) => {
40
40
  kit.start(app, element);
41
41
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edge-base/server",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "EdgeBase runtime assets consumed by the EdgeBase CLI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,8 +34,8 @@
34
34
  "jose": "^6.0.0",
35
35
  "pg": "^8.16.3",
36
36
  "zod": "^4.3.6",
37
- "@edge-base/core": "0.2.4",
38
- "@edge-base/shared": "0.2.4"
37
+ "@edge-base/core": "0.2.6",
38
+ "@edge-base/shared": "0.2.6"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@cloudflare/vitest-pool-workers": "^0.8.71",
@@ -25,11 +25,12 @@ function createCtx() {
25
25
  } as unknown as DurableObjectState;
26
26
  }
27
27
 
28
- function createEnv() {
28
+ function createEnv(config?: unknown) {
29
29
  return {
30
30
  DATABASE_LIVE: {} as DurableObjectNamespace,
31
31
  DATABASE: {} as DurableObjectNamespace,
32
32
  AUTH: {} as DurableObjectNamespace,
33
+ EDGEBASE_CONFIG: config,
33
34
  };
34
35
  }
35
36
 
@@ -39,7 +40,7 @@ describe('DatabaseDO route validation', () => {
39
40
  });
40
41
 
41
42
  it('rejects id-suffixed doNames for single-instance namespaces', async () => {
42
- setConfig(defineConfig({
43
+ const config = defineConfig({
43
44
  release: true,
44
45
  databases: {
45
46
  app: {
@@ -49,11 +50,12 @@ describe('DatabaseDO route validation', () => {
49
50
  },
50
51
  },
51
52
  },
52
- }));
53
+ });
54
+ setConfig(config);
53
55
 
54
56
  const { DatabaseDO } = await import('../durable-objects/database-do.js');
55
57
  const ctx = createCtx();
56
- const databaseDo = new DatabaseDO(ctx, createEnv() as never);
58
+ const databaseDo = new DatabaseDO(ctx, createEnv(config) as never);
57
59
 
58
60
  const response = await databaseDo.fetch(new Request('http://do/tables/posts', {
59
61
  headers: {
@@ -71,7 +73,7 @@ describe('DatabaseDO route validation', () => {
71
73
  });
72
74
 
73
75
  it('rejects missing instance ids for dynamic namespaces', async () => {
74
- setConfig(defineConfig({
76
+ const config = defineConfig({
75
77
  release: true,
76
78
  databases: {
77
79
  workspace: {
@@ -82,11 +84,12 @@ describe('DatabaseDO route validation', () => {
82
84
  },
83
85
  },
84
86
  },
85
- }));
87
+ });
88
+ setConfig(config);
86
89
 
87
90
  const { DatabaseDO } = await import('../durable-objects/database-do.js');
88
91
  const ctx = createCtx();
89
- const databaseDo = new DatabaseDO(ctx, createEnv() as never);
92
+ const databaseDo = new DatabaseDO(ctx, createEnv(config) as never);
90
93
 
91
94
  const response = await databaseDo.fetch(new Request('http://do/tables/users', {
92
95
  headers: {
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Meta-test: route registration completeness.
3
3
  *
4
- * Ensures every route file imported in index.ts is also registered via app.route().
5
- * The expected route exports are derived from the current route files.
4
+ * Ensures every route module imported in index.ts is also registered via app.route().
5
+ * The expected route exports are derived from the route modules the entrypoint actually loads.
6
6
  */
7
- import { readFileSync, readdirSync } from 'fs';
7
+ import { readFileSync } from 'fs';
8
8
  import { resolve } from 'path';
9
9
  import { fileURLToPath } from 'url';
10
10
  import { describe, it, expect } from 'vitest';
@@ -15,9 +15,11 @@ describe('index.ts route registration completeness', () => {
15
15
  'utf-8',
16
16
  );
17
17
  const routesDir = resolve(fileURLToPath(new URL('../routes', import.meta.url)));
18
- const EXPECTED_ROUTES = readdirSync(routesDir)
19
- .filter((fileName) => fileName.endsWith('.ts'))
20
- .sort()
18
+ const ROUTE_IMPORTS = [...source.matchAll(/import\('\.\/routes\/([^']+\.js)'\)/g)]
19
+ .map((match) => match[1])
20
+ .sort();
21
+ const ROUTE_FILES = ROUTE_IMPORTS.map((fileName) => fileName.replace(/\.js$/, '.ts'));
22
+ const EXPECTED_ROUTES = ROUTE_FILES
21
23
  .flatMap((fileName) => {
22
24
  const routeSource = readFileSync(resolve(routesDir, fileName), 'utf-8');
23
25
  const directExports = [...routeSource.matchAll(/export const (\w+)\s*=\s*new OpenAPIHono/g)].map(
@@ -29,20 +31,23 @@ describe('index.ts route registration completeness', () => {
29
31
  return [...directExports, ...aliasExports];
30
32
  });
31
33
 
32
- const EXPECTED_COUNT = EXPECTED_ROUTES.length;
33
-
34
- it(`total route imports = ${EXPECTED_COUNT}`, () => {
35
- const importMatches = source.match(/import \{ \w+ \} from '\.\/routes\//g) || [];
36
- expect(importMatches.length).toBe(EXPECTED_COUNT);
34
+ it(`total route module imports = ${ROUTE_FILES.length}`, () => {
35
+ expect(ROUTE_IMPORTS.length).toBe(ROUTE_FILES.length);
37
36
  });
38
37
 
39
- for (const routeVar of EXPECTED_ROUTES) {
40
- it(`${routeVar} is imported`, () => {
41
- expect(source).toMatch(new RegExp(`import\\s*\\{[^}]*\\b${routeVar}\\b[^}]*\\}\\s*from '\\./routes/`));
38
+ for (const routeFile of ROUTE_FILES) {
39
+ const routePath = routeFile.replace(/\.ts$/, '.js');
40
+
41
+ it(`${routeFile} is dynamically imported`, () => {
42
+ expect(source).toContain(`import('./routes/${routePath}')`);
42
43
  });
44
+ }
43
45
 
46
+ for (const routeVar of EXPECTED_ROUTES) {
44
47
  it(`${routeVar} is registered via app.route()`, () => {
45
- expect(source).toMatch(new RegExp(`app\\.route\\([^)]+,\\s*${routeVar}\\)`));
48
+ const directRegistration = new RegExp(`app\\.route\\([^)]+,\\s*${routeVar}\\)`);
49
+ const moduleRegistration = new RegExp(`app\\.route\\([^)]+,\\s*\\w+\\.${routeVar}\\)`);
50
+ expect(directRegistration.test(source) || moduleRegistration.test(source)).toBe(true);
46
51
  });
47
52
  }
48
53
  });
@@ -173,7 +173,7 @@ describe('Push handlers route integration', () => {
173
173
 
174
174
  expect(response.status).toBe(400);
175
175
  await expect(response.json()).resolves.toMatchObject({
176
- message: 'beforeSend must return topic and payload',
176
+ message: 'push.hooks.beforeSend must return a topic and payload when overriding topic delivery.',
177
177
  });
178
178
  });
179
179
  });
@@ -5,6 +5,128 @@ vi.mock('cloudflare:workers', () => ({
5
5
  }));
6
6
 
7
7
  describe('room auth-state loss recovery', () => {
8
+ it('treats ephemeral timer persistence failures as non-fatal', async () => {
9
+ const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
10
+
11
+ const room: any = Object.create(RoomRuntimeBaseDO.prototype);
12
+ const pending: Promise<unknown>[] = [];
13
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
14
+
15
+ room.pendingAuth = new Map([['conn-1', Date.now() + 5_000]]);
16
+ room.disconnectTimers = new Map();
17
+ room.namespace = 'game';
18
+ room.roomId = 'room-1';
19
+ room.ctx = {
20
+ storage: {
21
+ put: vi.fn().mockRejectedValue(new Error('Exceeded allowed rows written in Durable Objects free tier.')),
22
+ delete: vi.fn(),
23
+ },
24
+ waitUntil: vi.fn((promise: Promise<unknown>) => {
25
+ pending.push(promise);
26
+ }),
27
+ };
28
+
29
+ expect(() => room.syncEphemeralTimersToStorage()).not.toThrow();
30
+ await Promise.allSettled(pending);
31
+
32
+ expect(warnSpy).toHaveBeenCalledWith(
33
+ '[Room] Ephemeral timer persistence skipped',
34
+ expect.objectContaining({
35
+ room: 'game::room-1',
36
+ pendingAuthCount: 1,
37
+ disconnectCount: 0,
38
+ message: 'Exceeded allowed rows written in Durable Objects free tier.',
39
+ }),
40
+ );
41
+
42
+ warnSpy.mockRestore();
43
+ });
44
+
45
+ it('persists alarm-backed room deadlines alongside auth and disconnect timers', async () => {
46
+ const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
47
+
48
+ const pending: Promise<unknown>[] = [];
49
+ const putSpy = vi.fn().mockResolvedValue(undefined);
50
+ const room: any = Object.create(RoomRuntimeBaseDO.prototype);
51
+ room.pendingAuth = new Map([['conn-1', 11_111]]);
52
+ room.disconnectTimers = new Map([['user-1', { fireAt: 22_222, connectionId: 'conn-1' }]]);
53
+ room._stateSaveAt = 33_333;
54
+ room._emptyRoomCleanupAt = 44_444;
55
+ room._stateTTLAlarmAt = 55_555;
56
+ room.ctx = {
57
+ storage: {
58
+ put: putSpy,
59
+ delete: vi.fn(),
60
+ },
61
+ waitUntil: vi.fn((promise: Promise<unknown>) => {
62
+ pending.push(promise);
63
+ }),
64
+ };
65
+
66
+ room.syncEphemeralTimersToStorage();
67
+ await Promise.allSettled(pending);
68
+
69
+ expect(putSpy).toHaveBeenCalledWith('roomEphemeralTimers', {
70
+ pendingAuth: { 'conn-1': 11_111 },
71
+ disconnects: { 'user-1': { fireAt: 22_222, connectionId: 'conn-1' } },
72
+ stateSaveAt: 33_333,
73
+ emptyRoomCleanupAt: 44_444,
74
+ stateTTLAlarmAt: 55_555,
75
+ });
76
+ });
77
+
78
+ it('does not rewrite ephemeral timer storage when state is already dirty', async () => {
79
+ const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
80
+
81
+ const room: any = Object.create(RoomRuntimeBaseDO.prototype);
82
+ room.dirty = false;
83
+ room._stateSaveAt = 33_333;
84
+ room.namespaceConfig = {};
85
+ room.syncEphemeralTimersToStorage = vi.fn();
86
+ room._scheduleNextAlarm = vi.fn();
87
+
88
+ room.markDirty();
89
+
90
+ expect(room.dirty).toBe(true);
91
+ expect(room._stateSaveAt).toBe(33_333);
92
+ expect(room.syncEphemeralTimersToStorage).not.toHaveBeenCalled();
93
+ expect(room._scheduleNextAlarm).toHaveBeenCalledTimes(1);
94
+ });
95
+
96
+ it('recovers persisted timers before alarm processing after a cold wake without sockets', async () => {
97
+ const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
98
+
99
+ const room: any = Object.create(RoomRuntimeBaseDO.prototype);
100
+ room.stateRecoveryNeeded = false;
101
+ room.roomCreated = false;
102
+ room.sharedState = {};
103
+ room.playerStates = new Map();
104
+ room.serverState = {};
105
+ room.players = new Map();
106
+ room.userToConnections = new Map();
107
+ room.pendingAuth = new Map();
108
+ room.disconnectTimers = new Map();
109
+ room._timers = new Map();
110
+ room._stateSaveAt = null;
111
+ room._emptyRoomCleanupAt = null;
112
+ room._stateTTLAlarmAt = null;
113
+ room._metadata = {};
114
+ room.config = {};
115
+ room.ctx = {
116
+ getWebSockets: vi.fn(() => []),
117
+ };
118
+ room.ensureRuntimeReady = vi.fn(async () => {});
119
+ room.recoverFromStorage = vi.fn(async () => {});
120
+ room.findWebSocketByConnectionId = vi.fn(() => null);
121
+ room.finalizePlayerLeave = vi.fn(async () => {});
122
+ room.syncEphemeralTimersToStorage = vi.fn();
123
+ room._scheduleNextAlarm = vi.fn();
124
+
125
+ await room.alarm();
126
+
127
+ expect(room.recoverFromStorage).toHaveBeenCalledTimes(1);
128
+ });
129
+
8
130
  it('marks websocket metadata rebuilt from hibernation tags as auth-state-lost', async () => {
9
131
  const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
10
132
 
@@ -48,7 +48,7 @@ describe('RoomsDO handler context', () => {
48
48
  },
49
49
  };
50
50
 
51
- const ctx = room.buildHandlerContext();
51
+ const ctx = await room.buildHandlerContext();
52
52
  const inserted = await ctx.admin.db('shared').table('signals').insert({ title: 'Room inserted' });
53
53
 
54
54
  expect(inserted).toEqual({ id: 'sig-1', title: 'Room inserted' });
@@ -63,7 +63,7 @@ describe('RoomsDO handler context', () => {
63
63
  }),
64
64
  }),
65
65
  );
66
- });
66
+ }, 15_000);
67
67
 
68
68
  it('routes admin.db().upsert() through the database durable object', async () => {
69
69
  const { RoomsDO } = await import('../durable-objects/rooms-do.js');
@@ -104,7 +104,7 @@ describe('RoomsDO handler context', () => {
104
104
  },
105
105
  };
106
106
 
107
- const ctx = room.buildHandlerContext();
107
+ const ctx = await room.buildHandlerContext();
108
108
  const upserted = await ctx.admin.db('shared').table('signals').upsert({
109
109
  id: 'sig-1',
110
110
  title: 'Room upserted',
@@ -126,7 +126,7 @@ describe('RoomsDO handler context', () => {
126
126
  }),
127
127
  }),
128
128
  );
129
- });
129
+ }, 15_000);
130
130
 
131
131
  it('returns 409 when creating a Cloudflare RealtimeKit session while media is already published', async () => {
132
132
  const { RoomsDO } = await import('../durable-objects/rooms-do.js');
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ vi.mock('cloudflare:workers', () => ({
4
+ DurableObject: class DurableObject {},
5
+ }));
6
+
7
+ describe('room rate-limit scopes', () => {
8
+ it('keeps signal/media/admin buckets independent per connection', async () => {
9
+ const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
10
+
11
+ const room: any = Object.create(RoomRuntimeBaseDO.prototype);
12
+ room.namespaceConfig = {
13
+ rateLimit: {
14
+ actions: 2,
15
+ signals: 4,
16
+ media: 1,
17
+ admin: 1,
18
+ },
19
+ };
20
+ room.rateBuckets = new Map();
21
+
22
+ expect(room.checkRateLimit('conn-1', 'signals')).toBe(true);
23
+ expect(room.checkRateLimit('conn-1', 'signals')).toBe(true);
24
+ expect(room.checkRateLimit('conn-1', 'signals')).toBe(true);
25
+ expect(room.checkRateLimit('conn-1', 'signals')).toBe(true);
26
+ expect(room.checkRateLimit('conn-1', 'signals')).toBe(false);
27
+
28
+ expect(room.checkRateLimit('conn-1', 'media')).toBe(true);
29
+ expect(room.checkRateLimit('conn-1', 'media')).toBe(false);
30
+
31
+ expect(room.checkRateLimit('conn-1', 'admin')).toBe(true);
32
+ expect(room.checkRateLimit('conn-1', 'admin')).toBe(false);
33
+
34
+ expect(room.checkRateLimit('conn-1', 'actions')).toBe(true);
35
+ expect(room.checkRateLimit('conn-1', 'actions')).toBe(true);
36
+ expect(room.checkRateLimit('conn-1', 'actions')).toBe(false);
37
+ });
38
+ });
@@ -98,6 +98,29 @@ describe('room route runtime routing', () => {
98
98
  await expect(response.json()).resolves.toEqual({ runtime: 'rooms' });
99
99
  });
100
100
 
101
+ it('routes summary requests to the rooms runtime', async () => {
102
+ setConfig(defineConfig({
103
+ rooms: {
104
+ game: {
105
+ runtime: {
106
+ target: 'rooms',
107
+ },
108
+ public: {
109
+ metadata: true,
110
+ },
111
+ },
112
+ },
113
+ }));
114
+
115
+ const app = createRoomApp();
116
+ const response = await app.request('/api/room/summary?namespace=game&id=room-1', {
117
+ method: 'GET',
118
+ }, createRoomRuntimeEnv());
119
+
120
+ expect(response.status).toBe(201);
121
+ await expect(response.json()).resolves.toEqual({ runtime: 'rooms' });
122
+ });
123
+
101
124
  it('routes websocket upgrades to the rooms runtime', async () => {
102
125
  setConfig(defineConfig({
103
126
  rooms: {
@@ -360,6 +360,12 @@ describe('parseRoute — room', () => {
360
360
  expect(r.subcategory).toBe('metadata');
361
361
  expect(r.operation).toBe('getMetadata');
362
362
  });
363
+
364
+ it('GET /api/room/summary', () => {
365
+ const r = parseRoute('GET', '/api/room/summary');
366
+ expect(r.subcategory).toBe('summary');
367
+ expect(r.operation).toBe('getSummary');
368
+ });
363
369
  });
364
370
 
365
371
  // ─── K. Other feature routes ────────────────────────────────────────────────