@edge-base/server 0.2.6 → 0.2.8

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 (78) hide show
  1. package/admin-build/_app/immutable/chunks/{CbfX3ELZ.js → B9efkx2V.js} +1 -1
  2. package/admin-build/_app/immutable/chunks/{CLHN9MVr.js → BMXWUTG-.js} +1 -1
  3. package/admin-build/_app/immutable/chunks/{DemDWbs-.js → Bt4AyT3o.js} +3 -3
  4. package/admin-build/_app/immutable/chunks/CKVjMXZi.js +1 -0
  5. package/admin-build/_app/immutable/chunks/{BvoGcDFV.js → CMYgGhZR.js} +1 -1
  6. package/admin-build/_app/immutable/chunks/{LL3ulaxa.js → CTRjWhGs.js} +1 -1
  7. package/admin-build/_app/immutable/chunks/{BN_-k-Ck.js → CwyE59Yt.js} +1 -1
  8. package/admin-build/_app/immutable/chunks/{DQVP4KC-.js → D8aeTKry.js} +1 -1
  9. package/admin-build/_app/immutable/chunks/{Ff90owjx.js → DGAHkap7.js} +1 -1
  10. package/admin-build/_app/immutable/chunks/{CR37B8DX.js → DPgR4-0v.js} +1 -1
  11. package/admin-build/_app/immutable/chunks/{DdvsFblq.js → DYtrHeVQ.js} +1 -1
  12. package/admin-build/_app/immutable/chunks/{CrwlCAM0.js → DcVb45Ds.js} +1 -1
  13. package/admin-build/_app/immutable/chunks/Djnkhy-S.js +1 -0
  14. package/admin-build/_app/immutable/chunks/{DmDTovpg.js → fPy6xmgG.js} +1 -1
  15. package/admin-build/_app/immutable/chunks/{CCUxCptE.js → j4jxnAKj.js} +1 -1
  16. package/admin-build/_app/immutable/chunks/{qBm6xof8.js → zl2AUKMP.js} +1 -1
  17. package/admin-build/_app/immutable/entry/{app.CP83Ni80.js → app.Cmz0WjMl.js} +2 -2
  18. package/admin-build/_app/immutable/entry/start.JE7dcbK1.js +1 -0
  19. package/admin-build/_app/immutable/nodes/{0.DiRq7puO.js → 0.y6D_QyUb.js} +1 -1
  20. package/admin-build/_app/immutable/nodes/{1.BFeyKLGT.js → 1.CndRxhbH.js} +1 -1
  21. package/admin-build/_app/immutable/nodes/{10.zcee7hJx.js → 10.CdA5FmXy.js} +1 -1
  22. package/admin-build/_app/immutable/nodes/{11.BW7wLs2Y.js → 11.DG8SzMp_.js} +1 -1
  23. package/admin-build/_app/immutable/nodes/{12.CxJRlYSd.js → 12.CvmQqpFa.js} +1 -1
  24. package/admin-build/_app/immutable/nodes/{13.pp0F_5hn.js → 13.BbGNdswT.js} +1 -1
  25. package/admin-build/_app/immutable/nodes/{14.t3AfGiGo.js → 14.CZKsN7-O.js} +1 -1
  26. package/admin-build/_app/immutable/nodes/{15.B3agc7NX.js → 15.A7-CYgkG.js} +1 -1
  27. package/admin-build/_app/immutable/nodes/{16.C4uG2-i8.js → 16.hgJT9H-x.js} +1 -1
  28. package/admin-build/_app/immutable/nodes/{17.CwGxi1Bn.js → 17.DkWZbcN2.js} +1 -1
  29. package/admin-build/_app/immutable/nodes/{18.CrQyN_gU.js → 18.sX3Fb5gh.js} +1 -1
  30. package/admin-build/_app/immutable/nodes/{19.NEPUOXl7.js → 19.VAZUW-1K.js} +1 -1
  31. package/admin-build/_app/immutable/nodes/{20.DGHO8ipr.js → 20.DkIKxacG.js} +1 -1
  32. package/admin-build/_app/immutable/nodes/21.DOjJlQKc.js +1 -0
  33. package/admin-build/_app/immutable/nodes/{22.Dri5It7a.js → 22.BDaHvtaw.js} +1 -1
  34. package/admin-build/_app/immutable/nodes/{23.BPQP_Zte.js → 23.BVRzw_pD.js} +1 -1
  35. package/admin-build/_app/immutable/nodes/{24.D580FdSS.js → 24.CVhSJyG0.js} +1 -1
  36. package/admin-build/_app/immutable/nodes/{25.BMNPOZwF.js → 25.Bme-9bZn.js} +1 -1
  37. package/admin-build/_app/immutable/nodes/{26.XcpEcbiz.js → 26.Dsx7RIIs.js} +1 -1
  38. package/admin-build/_app/immutable/nodes/{27.C1zHHcYv.js → 27.DMGQnzFM.js} +1 -1
  39. package/admin-build/_app/immutable/nodes/{28.CuKzzrY8.js → 28.GGwFmEhZ.js} +1 -1
  40. package/admin-build/_app/immutable/nodes/{29.nLpBMXnM.js → 29.Dnghr0nk.js} +1 -1
  41. package/admin-build/_app/immutable/nodes/{3.5G_aseoL.js → 3.Cg7zZJP1.js} +1 -1
  42. package/admin-build/_app/immutable/nodes/{30.CQC4nLoU.js → 30.C0J24z3I.js} +1 -1
  43. package/admin-build/_app/immutable/nodes/{31.Bet8kxOK.js → 31.MdxFI8v6.js} +1 -1
  44. package/admin-build/_app/immutable/nodes/{4.nmJDYJpC.js → 4.DCAOVzGE.js} +1 -1
  45. package/admin-build/_app/immutable/nodes/{5.CnbYLG4E.js → 5.DzUQ-cTc.js} +1 -1
  46. package/admin-build/_app/immutable/nodes/{6.KA01b-3y.js → 6.CptBYTVj.js} +1 -1
  47. package/admin-build/_app/immutable/nodes/{7.CP9fkn1L.js → 7.DfeeQ0Rg.js} +1 -1
  48. package/admin-build/_app/immutable/nodes/{8.BTzDb---.js → 8.CIcvctW7.js} +1 -1
  49. package/admin-build/_app/immutable/nodes/{9.DkNJg_J6.js → 9.QKrvq4RA.js} +1 -1
  50. package/admin-build/_app/version.json +1 -1
  51. package/admin-build/index.html +7 -7
  52. package/openapi.json +6 -1941
  53. package/package.json +3 -3
  54. package/src/__tests__/admin-assets.test.ts +7 -7
  55. package/src/__tests__/frontend-assets.test.ts +75 -0
  56. package/src/__tests__/frontend-config.test.ts +16 -0
  57. package/src/__tests__/frontend-routing.test.ts +200 -0
  58. package/src/__tests__/openapi-coverage.test.ts +0 -6
  59. package/src/__tests__/room-auth-state-loss.test.ts +6 -0
  60. package/src/__tests__/room-handler-context.test.ts +0 -31
  61. package/src/__tests__/room-rate-limit-scopes.test.ts +1 -5
  62. package/src/__tests__/room-runtime-routing.test.ts +1 -111
  63. package/src/__tests__/smoke-skip-report.test.ts +1 -1
  64. package/src/durable-objects/room-runtime-base.ts +243 -17
  65. package/src/durable-objects/rooms-do.ts +190 -1345
  66. package/src/index.ts +97 -3
  67. package/src/lib/admin-assets.ts +5 -5
  68. package/src/lib/frontend-assets.ts +129 -0
  69. package/src/lib/frontend-config.ts +11 -0
  70. package/src/lib/openapi.ts +1 -4
  71. package/src/routes/room.ts +0 -285
  72. package/src/types.ts +1 -14
  73. package/admin-build/_app/immutable/chunks/Q3vAxeY-.js +0 -1
  74. package/admin-build/_app/immutable/chunks/SQVAC3Cv.js +0 -1
  75. package/admin-build/_app/immutable/entry/start.DY6YakU0.js +0 -1
  76. package/admin-build/_app/immutable/nodes/21.UVKBDvp4.js +0 -1
  77. package/src/__tests__/cloudflare-realtime.test.ts +0 -113
  78. package/src/lib/cloudflare-realtime.ts +0 -251
@@ -1 +0,0 @@
1
- var x=t=>{throw TypeError(t)};var B=(t,e,n)=>e.has(t)||x("Cannot "+n);var a=(t,e,n)=>(B(t,e,"read from private field"),n?n.call(t):e.get(t)),c=(t,e,n)=>e.has(t)?x("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,n);import{w as G,o as I}from"./Bn2NtlTj.js";import{d as u,g as f,b as d}from"./BdTBlfLy.js";new URL("sveltekit-internal://");function se(t,e){return t==="/"||e==="ignore"?t:e==="never"?t.endsWith("/")?t.slice(0,-1):t:e==="always"&&!t.endsWith("/")?t+"/":t}function ae(t){return t.split("%25").map(decodeURI).join("%25")}function oe(t){for(const e in t)t[e]=decodeURIComponent(t[e]);return t}function ie({href:t}){return t.split("#")[0]}function W(...t){let e=5381;for(const n of t)if(typeof n=="string"){let r=n.length;for(;r;)e=e*33^n.charCodeAt(--r)}else if(ArrayBuffer.isView(n)){const r=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let s=r.length;for(;s;)e=e*33^r[--s]}else throw new TypeError("value must be a string or TypedArray");return(e>>>0).toString(36)}new TextEncoder;new TextDecoder;function X(t){const e=atob(t),n=new Uint8Array(e.length);for(let r=0;r<e.length;r++)n[r]=e.charCodeAt(r);return n}const z=window.fetch;window.fetch=(t,e)=>((t instanceof Request?t.method:(e==null?void 0:e.method)||"GET")!=="GET"&&b.delete(U(t)),z(t,e));const b=new Map;function le(t,e){const n=U(t,e),r=document.querySelector(n);if(r!=null&&r.textContent){r.remove();let{body:s,...l}=JSON.parse(r.textContent);const o=r.getAttribute("data-ttl");return o&&b.set(n,{body:s,init:l,ttl:1e3*Number(o)}),r.getAttribute("data-b64")!==null&&(s=X(s)),Promise.resolve(new Response(s,l))}return window.fetch(t,e)}function ce(t,e,n){if(b.size>0){const r=U(t,n),s=b.get(r);if(s){if(performance.now()<s.ttl&&["default","force-cache","only-if-cached",void 0].includes(n==null?void 0:n.cache))return new Response(s.body,s.init);b.delete(r)}}return window.fetch(e,n)}function U(t,e){let r=`script[data-sveltekit-fetched][data-url=${JSON.stringify(t instanceof Request?t.url:t)}]`;if(e!=null&&e.headers||e!=null&&e.body){const s=[];e.headers&&s.push([...new Headers(e.headers)].join(",")),e.body&&(typeof e.body=="string"||ArrayBuffer.isView(e.body))&&s.push(e.body),r+=`[data-hash="${W(...s)}"]`}return r}var $;const J=(($=globalThis.__sveltekit_hkuxmm)==null?void 0:$.base)??"/admin";var C;const M=((C=globalThis.__sveltekit_hkuxmm)==null?void 0:C.assets)??J??"",F="1774560919084",ue="sveltekit:snapshot",fe="sveltekit:scroll",de="sveltekit:states",he="sveltekit:pageurl",ge="sveltekit:history",be="sveltekit:navigation",N={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},Y=location.origin;function _e(t){if(t instanceof URL)return t;let e=document.baseURI;if(!e){const n=document.getElementsByTagName("base");e=n.length?n[0].href:document.URL}return new URL(t,e)}function me(){return{x:pageXOffset,y:pageYOffset}}function g(t,e){return t.getAttribute(`data-sveltekit-${e}`)}const L={...N,"":N.hover};function q(t){let e=t.assignedSlot??t.parentNode;return(e==null?void 0:e.nodeType)===11&&(e=e.host),e}function we(t,e){for(;t&&t!==e;){if(t.nodeName.toUpperCase()==="A"&&t.hasAttribute("href"))return t;t=q(t)}}function pe(t,e,n){let r;try{if(r=new URL(t instanceof SVGAElement?t.href.baseVal:t.href,document.baseURI),n&&r.hash.match(/^#[^/]/)){const i=location.hash.split("#")[1]||"/";r.hash=`#${i}${r.hash}`}}catch{}const s=t instanceof SVGAElement?t.target.baseVal:t.target,l=!r||!!s||Q(r,e,n)||(t.getAttribute("rel")||"").split(/\s+/).includes("external"),o=(r==null?void 0:r.origin)===Y&&t.hasAttribute("download");return{url:r,external:l,target:s,download:o}}function ve(t){let e=null,n=null,r=null,s=null,l=null,o=null,i=t;for(;i&&i!==document.documentElement;)r===null&&(r=g(i,"preload-code")),s===null&&(s=g(i,"preload-data")),e===null&&(e=g(i,"keepfocus")),n===null&&(n=g(i,"noscroll")),l===null&&(l=g(i,"reload")),o===null&&(o=g(i,"replacestate")),i=q(i);function h(K){switch(K){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:L[r??"off"],preload_data:L[s??"off"],keepfocus:h(e),noscroll:h(n),reload:h(l),replace_state:h(o)}}function ye(t){const e=G(t);let n=!0;function r(){n=!0,e.update(o=>o)}function s(o){n=!1,e.set(o)}function l(o){let i;return e.subscribe(h=>{(i===void 0||n&&h!==i)&&o(i=h)})}return{notify:r,set:s,subscribe:l}}const D={v:()=>{}};function ke(){const{set:t,subscribe:e}=G(!1);let n;async function r(){clearTimeout(n);try{const s=await fetch(`${M}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!s.ok)return!1;const o=(await s.json()).version!==F;return o&&(t(!0),D.v(),clearTimeout(n)),o}catch{return!1}}return{subscribe:e,check:r}}function Q(t,e,n){return t.origin!==Y||!t.pathname.startsWith(e)?!0:n?t.pathname!==location.pathname:!1}function Ae(t){}const H=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...H];const Z=new Set([...H]);[...Z];let E,O,T;const ee=I.toString().includes("$$")||/function \w+\(\) \{\}/.test(I.toString());var _,m,w,p,v,y,k,A,P,R,V,S,j;ee?(E={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL("https://example.com")},O={current:null},T={current:!1}):(E=new(P=class{constructor(){c(this,_,u({}));c(this,m,u(null));c(this,w,u(null));c(this,p,u({}));c(this,v,u({id:null}));c(this,y,u({}));c(this,k,u(-1));c(this,A,u(new URL("https://example.com")))}get data(){return f(a(this,_))}set data(e){d(a(this,_),e)}get form(){return f(a(this,m))}set form(e){d(a(this,m),e)}get error(){return f(a(this,w))}set error(e){d(a(this,w),e)}get params(){return f(a(this,p))}set params(e){d(a(this,p),e)}get route(){return f(a(this,v))}set route(e){d(a(this,v),e)}get state(){return f(a(this,y))}set state(e){d(a(this,y),e)}get status(){return f(a(this,k))}set status(e){d(a(this,k),e)}get url(){return f(a(this,A))}set url(e){d(a(this,A),e)}},_=new WeakMap,m=new WeakMap,w=new WeakMap,p=new WeakMap,v=new WeakMap,y=new WeakMap,k=new WeakMap,A=new WeakMap,P),O=new(V=class{constructor(){c(this,R,u(null))}get current(){return f(a(this,R))}set current(e){d(a(this,R),e)}},R=new WeakMap,V),T=new(j=class{constructor(){c(this,S,u(!1))}get current(){return f(a(this,S))}set current(e){d(a(this,S),e)}},S=new WeakMap,j),D.v=()=>T.current=!0);function Ee(t){Object.assign(E,t)}export{ge as H,be as N,he as P,de as S,ve as a,J as b,ie as c,oe as d,se as e,we as f,pe as g,ke as h,Q as i,ye as j,N as k,ae as l,ue as m,O as n,Y as o,E as p,ce as q,_e as r,me as s,le as t,fe as u,Ee as v,Ae as w};
@@ -1 +0,0 @@
1
- import{ab as J,bm as ee}from"./BdTBlfLy.js";import{w as ae}from"./Bn2NtlTj.js";import{H as N,N as M,r as gt,o as $t,i as _t,b as L,s as C,p as x,n as ft,f as Nt,g as ut,a as X,c as it,S as Dt,P as ne,d as re,e as oe,h as se,j as Pt,k as q,l as ie,m as qt,q as ce,t as le,u as Kt,v as fe}from"./Q3vAxeY-.js";class wt{constructor(a,e){this.status=a,typeof e=="string"?this.body={message:e}:e?this.body=e:this.body={message:`Error: ${a}`}}toString(){return JSON.stringify(this.body)}}class vt{constructor(a,e){this.status=a,this.location=e}}class yt extends Error{constructor(a,e,r){super(r),this.status=a,this.text=e}}const ue=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;function he(t){const a=[];return{pattern:t==="/"?/^\/$/:new RegExp(`^${pe(t).map(r=>{const n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(r);if(n)return a.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const o=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(r);if(o)return a.push({name:o[1],matcher:o[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!r)return;const s=r.split(/\[(.+?)\](?!\])/);return"/"+s.map((c,l)=>{if(l%2){if(c.startsWith("x+"))return ct(String.fromCharCode(parseInt(c.slice(2),16)));if(c.startsWith("u+"))return ct(String.fromCharCode(...c.slice(2).split("-").map(_=>parseInt(_,16))));const h=ue.exec(c),[,u,w,f,d]=h;return a.push({name:f,matcher:d,optional:!!u,rest:!!w,chained:w?l===1&&s[0]==="":!1}),w?"([^]*?)":u?"([^/]*)?":"([^/]+?)"}return ct(c)}).join("")}).join("")}/?$`),params:a}}function de(t){return t!==""&&!/^\([^)]+\)$/.test(t)}function pe(t){return t.slice(1).split("/").filter(de)}function me(t,a,e){const r={},n=t.slice(1),o=n.filter(i=>i!==void 0);let s=0;for(let i=0;i<a.length;i+=1){const c=a[i];let l=n[i-s];if(c.chained&&c.rest&&s&&(l=n.slice(i-s,i+1).filter(h=>h).join("/"),s=0),l===void 0)if(c.rest)l="";else continue;if(!c.matcher||e[c.matcher](l)){r[c.name]=l;const h=a[i+1],u=n[i+1];h&&!h.rest&&h.optional&&u&&c.chained&&(s=0),!h&&!u&&Object.keys(r).length===o.length&&(s=0);continue}if(c.optional&&c.chained){s++;continue}return}if(!s)return r}function ct(t){return t.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function ge({nodes:t,server_loads:a,dictionary:e,matchers:r}){const n=new Set(a);return Object.entries(e).map(([i,[c,l,h]])=>{const{pattern:u,params:w}=he(i),f={id:i,exec:d=>{const _=u.exec(d);if(_)return me(_,w,r)},errors:[1,...h||[]].map(d=>t[d]),layouts:[0,...l||[]].map(s),leaf:o(c)};return f.errors.length=f.layouts.length=Math.max(f.errors.length,f.layouts.length),f});function o(i){const c=i<0;return c&&(i=~i),[c,t[i]]}function s(i){return i===void 0?i:[n.has(i),t[i]]}}function Ft(t,a=JSON.parse){try{return a(sessionStorage[t])}catch{}}function It(t,a,e=JSON.stringify){const r=e(a);try{sessionStorage[t]=r}catch{}}function _e(t){return t.filter(a=>a!=null)}function bt(t){return t instanceof wt||t instanceof yt?t.status:500}function we(t){return t instanceof yt?t.text:"Internal Error"}const ve=new Set(["icon","shortcut icon","apple-touch-icon"]),I=Ft(Kt)??{},V=Ft(qt)??{},P={url:Pt({}),page:Pt({}),navigating:ae(null),updated:se()};function Et(t){I[t]=C()}function ye(t,a){let e=t+1;for(;I[e];)delete I[e],e+=1;for(e=a+1;V[e];)delete V[e],e+=1}function B(t,a=!1){return a?location.replace(t.href):location.href=t.href,new Promise(()=>{})}async function Mt(){if("serviceWorker"in navigator){const t=await navigator.serviceWorker.getRegistration(L||"/");t&&await t.update()}}function Tt(){}let kt,ht,Q,U,dt,E;const Z=[],tt=[];let v=null;function pt(){var t;(t=v==null?void 0:v.fork)==null||t.then(a=>a==null?void 0:a.discard()),v=null}const G=new Map,Vt=new Set,be=new Set,F=new Set;let g={branch:[],error:null,url:null},Bt=!1,et=!1,Ot=!0,H=!1,K=!1,Ht=!1,St=!1,Yt,b,R,O;const at=new Set,Ct=new Map;async function Fe(t,a,e){var o,s,i,c,l;(o=globalThis.__sveltekit_hkuxmm)!=null&&o.data&&globalThis.__sveltekit_hkuxmm.data,document.URL!==location.href&&(location.href=location.href),E=t,await((i=(s=t.hooks).init)==null?void 0:i.call(s)),kt=ge(t),U=document.documentElement,dt=a,ht=t.nodes[0],Q=t.nodes[1],ht(),Q(),b=(c=history.state)==null?void 0:c[N],R=(l=history.state)==null?void 0:l[M],b||(b=R=Date.now(),history.replaceState({...history.state,[N]:b,[M]:R},""));const r=I[b];function n(){r&&(history.scrollRestoration="manual",scrollTo(r.x,r.y))}e?(n(),await Ce(dt,e)):(await D({type:"enter",url:gt(E.hash?Ne(new URL(location.href)):location.href),replace_state:!0}),n()),Oe()}function Ee(){Z.length=0,St=!1}function zt(t){tt.some(a=>a==null?void 0:a.snapshot)&&(V[t]=tt.map(a=>{var e;return(e=a==null?void 0:a.snapshot)==null?void 0:e.capture()}))}function Wt(t){var a;(a=V[t])==null||a.forEach((e,r)=>{var n,o;(o=(n=tt[r])==null?void 0:n.snapshot)==null||o.restore(e)})}function jt(){Et(b),It(Kt,I),zt(R),It(qt,V)}async function Gt(t,a,e,r){let n;a.invalidateAll&&pt(),await D({type:"goto",url:gt(t),keepfocus:a.keepFocus,noscroll:a.noScroll,replace_state:a.replaceState,state:a.state,redirect_count:e,nav_token:r,accept:()=>{a.invalidateAll&&(St=!0,n=[...Ct.keys()]),a.invalidate&&a.invalidate.forEach(Te)}}),a.invalidateAll&&J().then(J).then(()=>{Ct.forEach(({resource:o},s)=>{var i;n!=null&&n.includes(s)&&((i=o.refresh)==null||i.call(o))})})}async function ke(t){if(t.id!==(v==null?void 0:v.id)){pt();const a={};at.add(a),v={id:t.id,token:a,promise:Xt({...t,preload:a}).then(e=>(at.delete(a),e.type==="loaded"&&e.state.error&&pt(),e)),fork:null}}return v.promise}async function lt(t){var e;const a=(e=await ot(t,!1))==null?void 0:e.route;a&&await Promise.all([...a.layouts,a.leaf].map(r=>r==null?void 0:r[1]()))}async function Jt(t,a,e){var n;g=t.state;const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(x,t.props.page),Yt=new E.root({target:a,props:{...t.props,stores:P,components:tt},hydrate:e,sync:!1}),await Promise.resolve(),Wt(R),e){const o={from:null,to:{params:g.params,route:{id:((n=g.route)==null?void 0:n.id)??null},url:new URL(location.href),scroll:I[b]??C()},willUnload:!1,type:"enter",complete:Promise.resolve()};F.forEach(s=>s(o))}et=!0}function nt({url:t,params:a,branch:e,status:r,error:n,route:o,form:s}){let i="never";if(L&&(t.pathname===L||t.pathname===L+"/"))i="always";else for(const f of e)(f==null?void 0:f.slash)!==void 0&&(i=f.slash);t.pathname=oe(t.pathname,i),t.search=t.search;const c={type:"loaded",state:{url:t,params:a,branch:e,error:n,route:o},props:{constructors:_e(e).map(f=>f.node.component),page:At(x)}};s!==void 0&&(c.props.form=s);let l={},h=!x,u=0;for(let f=0;f<Math.max(e.length,g.branch.length);f+=1){const d=e[f],_=g.branch[f];(d==null?void 0:d.data)!==(_==null?void 0:_.data)&&(h=!0),d&&(l={...l,...d.data},h&&(c.props[`data_${u}`]=l),u+=1)}return(!g.url||t.href!==g.url.href||g.error!==n||s!==void 0&&s!==x.form||h)&&(c.props.page={error:n,params:a,route:{id:(o==null?void 0:o.id)??null},state:{},status:r,url:new URL(t),form:s??null,data:h?l:x.data}),c}async function Rt({loader:t,parent:a,url:e,params:r,route:n,server_data_node:o}){var l,h;let s=null;const i={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},c=await t();return{node:c,loader:t,server:o,universal:(l=c.universal)!=null&&l.load?{type:"data",data:s,uses:i}:null,data:s??(o==null?void 0:o.data)??null,slash:((h=c.universal)==null?void 0:h.trailingSlash)??(o==null?void 0:o.slash)}}function Se(t,a,e){let r=t instanceof Request?t.url:t;const n=new URL(r,e);n.origin===e.origin&&(r=n.href.slice(e.origin.length));const o=et?ce(r,n.href,a):le(r,a);return{resolved:n,promise:o}}function Re(t,a,e,r,n,o){if(St)return!0;if(!n)return!1;if(n.parent&&t||n.route&&a||n.url&&e)return!0;for(const s of n.search_params)if(r.has(s))return!0;for(const s of n.params)if(o[s]!==g.params[s])return!0;for(const s of n.dependencies)if(Z.some(i=>i(new URL(s))))return!0;return!1}function xt(t,a){return(t==null?void 0:t.type)==="data"?t:(t==null?void 0:t.type)==="skip"?a??null:null}function xe(t,a){if(!t)return new Set(a.searchParams.keys());const e=new Set([...t.searchParams.keys(),...a.searchParams.keys()]);for(const r of e){const n=t.searchParams.getAll(r),o=a.searchParams.getAll(r);n.every(s=>o.includes(s))&&o.every(s=>n.includes(s))&&e.delete(r)}return e}function Le({error:t,url:a,route:e,params:r}){return{type:"loaded",state:{error:t,url:a,route:e,params:r,branch:[]},props:{page:At(x),constructors:[]}}}async function Xt({id:t,invalidating:a,url:e,params:r,route:n,preload:o}){if((v==null?void 0:v.id)===t)return at.delete(v.token),v.promise;const{errors:s,layouts:i,leaf:c}=n,l=[...i,c];s.forEach(m=>m==null?void 0:m().catch(()=>{})),l.forEach(m=>m==null?void 0:m[1]().catch(()=>{}));const h=g.url?t!==rt(g.url):!1,u=g.route?n.id!==g.route.id:!1,w=xe(g.url,e);let f=!1;const d=l.map(async(m,p)=>{var A;if(!m)return;const y=g.branch[p];return m[1]===(y==null?void 0:y.loader)&&!Re(f,u,h,w,(A=y.universal)==null?void 0:A.uses,r)?y:(f=!0,Rt({loader:m[1],url:e,params:r,route:n,parent:async()=>{var z;const T={};for(let j=0;j<p;j+=1)Object.assign(T,(z=await d[j])==null?void 0:z.data);return T},server_data_node:xt(m[0]?{type:"skip"}:null,m[0]?y==null?void 0:y.server:void 0)}))});for(const m of d)m.catch(()=>{});const _=[];for(let m=0;m<l.length;m+=1)if(l[m])try{_.push(await d[m])}catch(p){if(p instanceof vt)return{type:"redirect",location:p.location};if(at.has(o))return Le({error:await Y(p,{params:r,url:e,route:{id:n.id}}),url:e,params:r,route:n});let y=bt(p),S;if(p instanceof wt)S=p.body;else{if(await P.updated.check())return await Mt(),await B(e);S=await Y(p,{params:r,url:e,route:{id:n.id}})}const A=await Ue(m,_,s);return A?nt({url:e,params:r,branch:_.slice(0,A.idx).concat(A.node),status:y,error:S,route:n}):await Zt(e,{id:n.id},S,y)}else _.push(void 0);return nt({url:e,params:r,branch:_,status:200,error:null,route:n,form:a?void 0:null})}async function Ue(t,a,e){for(;t--;)if(e[t]){let r=t;for(;!a[r];)r-=1;try{return{idx:r+1,node:{node:await e[t](),loader:e[t],data:{},server:null,universal:null}}}catch{continue}}}async function Lt({status:t,error:a,url:e,route:r}){const n={};let o=null;try{const s=await Rt({loader:ht,url:e,params:n,route:r,parent:()=>Promise.resolve({}),server_data_node:xt(o)}),i={node:await Q(),loader:Q,universal:null,server:null,data:null};return nt({url:e,params:n,branch:[s,i],status:t,error:a,route:null})}catch(s){if(s instanceof vt)return Gt(new URL(s.location,location.href),{},0);throw s}}async function Ae(t){const a=t.href;if(G.has(a))return G.get(a);let e;try{const r=(async()=>{let n=await E.hooks.reroute({url:new URL(t),fetch:async(o,s)=>Se(o,s,t).promise})??t;if(typeof n=="string"){const o=new URL(t);E.hash?o.hash=n:o.pathname=n,n=o}return n})();G.set(a,r),e=await r}catch{G.delete(a);return}return e}async function ot(t,a){if(t&&!_t(t,L,E.hash)){const e=await Ae(t);if(!e)return;const r=Pe(e);for(const n of kt){const o=n.exec(r);if(o)return{id:rt(t),invalidating:a,route:n,params:re(o),url:t}}}}function Pe(t){return ie(E.hash?t.hash.replace(/^#/,"").replace(/[?#].+/,""):t.pathname.slice(L.length))||"/"}function rt(t){return(E.hash?t.hash.replace(/^#/,""):t.pathname)+t.search}function Qt({url:t,type:a,intent:e,delta:r,event:n,scroll:o}){let s=!1;const i=Ut(g,e,t,a,o??null);r!==void 0&&(i.navigation.delta=r),n!==void 0&&(i.navigation.event=n);const c={...i.navigation,cancel:()=>{s=!0,i.reject(new Error("navigation cancelled"))}};return H||Vt.forEach(l=>l(c)),s?null:i}async function D({type:t,url:a,popped:e,keepfocus:r,noscroll:n,replace_state:o,state:s={},redirect_count:i=0,nav_token:c={},accept:l=Tt,block:h=Tt,event:u}){var j;const w=O;O=c;const f=await ot(a,!1),d=t==="enter"?Ut(g,f,a,t):Qt({url:a,type:t,delta:e==null?void 0:e.delta,intent:f,scroll:e==null?void 0:e.scroll,event:u});if(!d){h(),O===c&&(O=w);return}const _=b,m=R;l(),H=!0,et&&d.navigation.type!=="enter"&&P.navigating.set(ft.current=d.navigation);let p=f&&await Xt(f);if(!p){if(_t(a,L,E.hash))return await B(a,o);p=await Zt(a,{id:null},await Y(new yt(404,"Not Found",`Not found: ${a.pathname}`),{url:a,params:{},route:{id:null}}),404,o)}if(a=(f==null?void 0:f.url)||a,O!==c)return d.reject(new Error("navigation aborted")),!1;if(p.type==="redirect"){if(i<20){await D({type:t,url:new URL(p.location,a),popped:e,keepfocus:r,noscroll:n,replace_state:o,state:s,redirect_count:i+1,nav_token:c}),d.fulfil(void 0);return}p=await Lt({status:500,error:await Y(new Error("Redirect loop"),{url:a,params:{},route:{id:null}}),url:a,route:{id:null}})}else p.props.page.status>=400&&await P.updated.check()&&(await Mt(),await B(a,o));if(Ee(),Et(_),zt(m),p.props.page.url.pathname!==a.pathname&&(a.pathname=p.props.page.url.pathname),s=e?e.state:s,!e){const k=o?0:1,W={[N]:b+=k,[M]:R+=k,[Dt]:s};(o?history.replaceState:history.pushState).call(history,W,"",a),o||ye(b,R)}const y=f&&(v==null?void 0:v.id)===f.id?v.fork:null;v=null,p.props.page.state=s;let S;if(et){const k=(await Promise.all(Array.from(be,$=>$(d.navigation)))).filter($=>typeof $=="function");if(k.length>0){let $=function(){k.forEach(st=>{F.delete(st)})};k.push($),k.forEach(st=>{F.add(st)})}g=p.state,p.props.page&&(p.props.page.url=a);const W=y&&await y;W?S=W.commit():(Yt.$set(p.props),fe(p.props.page),S=(j=ee)==null?void 0:j()),Ht=!0}else await Jt(p,dt,!1);const{activeElement:A}=document;await S,await J(),await J();let T=null;if(Ot){const k=e?e.scroll:n?C():null;k?scrollTo(k.x,k.y):(T=a.hash&&document.getElementById(te(a)))?T.scrollIntoView():scrollTo(0,0)}const z=document.activeElement!==A&&document.activeElement!==document.body;!r&&!z&&$e(a,!T),Ot=!0,p.props.page&&Object.assign(x,p.props.page),H=!1,t==="popstate"&&Wt(R),d.fulfil(void 0),d.navigation.to&&(d.navigation.to.scroll=C()),F.forEach(k=>k(d.navigation)),P.navigating.set(ft.current=null)}async function Zt(t,a,e,r,n){return t.origin===$t&&t.pathname===location.pathname&&!Bt?await Lt({status:r,error:e,url:t,route:a}):await B(t,n)}function Ie(){let t,a={element:void 0,href:void 0},e;U.addEventListener("mousemove",i=>{const c=i.target;clearTimeout(t),t=setTimeout(()=>{o(c,q.hover)},20)});function r(i){i.defaultPrevented||o(i.composedPath()[0],q.tap)}U.addEventListener("mousedown",r),U.addEventListener("touchstart",r,{passive:!0});const n=new IntersectionObserver(i=>{for(const c of i)c.isIntersecting&&(lt(new URL(c.target.href)),n.unobserve(c.target))},{threshold:0});async function o(i,c){const l=Nt(i,U),h=l===a.element&&(l==null?void 0:l.href)===a.href&&c>=e;if(!l||h)return;const{url:u,external:w,download:f}=ut(l,L,E.hash);if(w||f)return;const d=X(l),_=u&&rt(g.url)===rt(u);if(!(d.reload||_))if(c<=d.preload_data){a={element:l,href:l.href},e=q.tap;const m=await ot(u,!1);if(!m)return;ke(m)}else c<=d.preload_code&&(a={element:l,href:l.href},e=c,lt(u))}function s(){n.disconnect();for(const i of U.querySelectorAll("a")){const{url:c,external:l,download:h}=ut(i,L,E.hash);if(l||h)continue;const u=X(i);u.reload||(u.preload_code===q.viewport&&n.observe(i),u.preload_code===q.eager&&lt(c))}}F.add(s),s()}function Y(t,a){if(t instanceof wt)return t.body;const e=bt(t),r=we(t);return E.hooks.handleError({error:t,event:a,status:e,message:r})??{message:r}}function Me(t,a={}){return t=new URL(gt(t)),t.origin!==$t?Promise.reject(new Error("goto: invalid URL")):Gt(t,a,0)}function Te(t){if(typeof t=="function")Z.push(t);else{const{href:a}=new URL(t,location.href);Z.push(e=>e.href===a)}}function Oe(){var a;history.scrollRestoration="manual",addEventListener("beforeunload",e=>{let r=!1;if(jt(),!H){const n=Ut(g,void 0,null,"leave"),o={...n.navigation,cancel:()=>{r=!0,n.reject(new Error("navigation cancelled"))}};Vt.forEach(s=>s(o))}r?(e.preventDefault(),e.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&jt()}),(a=navigator.connection)!=null&&a.saveData||Ie(),U.addEventListener("click",async e=>{if(e.button||e.which!==1||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.defaultPrevented)return;const r=Nt(e.composedPath()[0],U);if(!r)return;const{url:n,external:o,target:s,download:i}=ut(r,L,E.hash);if(!n)return;if(s==="_parent"||s==="_top"){if(window.parent!==window)return}else if(s&&s!=="_self")return;const c=X(r);if(!(r instanceof SVGAElement)&&n.protocol!==location.protocol&&!(n.protocol==="https:"||n.protocol==="http:")||i)return;const[h,u]=(E.hash?n.hash.replace(/^#/,""):n.href).split("#"),w=h===it(location);if(o||c.reload&&(!w||!u)){Qt({url:n,type:"link",event:e})?H=!0:e.preventDefault();return}if(u!==void 0&&w){const[,f]=g.url.href.split("#");if(f===u){if(e.preventDefault(),u===""||u==="top"&&r.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const d=r.ownerDocument.getElementById(decodeURIComponent(u));d&&(d.scrollIntoView(),d.focus())}return}if(K=!0,Et(b),t(n),!c.replace_state)return;K=!1}e.preventDefault(),await new Promise(f=>{requestAnimationFrame(()=>{setTimeout(f,0)}),setTimeout(f,100)}),await D({type:"link",url:n,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??n.href===location.href,event:e})}),U.addEventListener("submit",e=>{if(e.defaultPrevented)return;const r=HTMLFormElement.prototype.cloneNode.call(e.target),n=e.submitter;if(((n==null?void 0:n.formTarget)||r.target)==="_blank"||((n==null?void 0:n.formMethod)||r.method)!=="get")return;const i=new URL((n==null?void 0:n.hasAttribute("formaction"))&&(n==null?void 0:n.formAction)||r.action);if(_t(i,L,!1))return;const c=e.target,l=X(c);if(l.reload)return;e.preventDefault(),e.stopPropagation();const h=new FormData(c,n);i.search=new URLSearchParams(h).toString(),D({type:"form",url:i,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??i.href===location.href,event:e})}),addEventListener("popstate",async e=>{var r;if(!mt){if((r=e.state)!=null&&r[N]){const n=e.state[N];if(O={},n===b)return;const o=I[n],s=e.state[Dt]??{},i=new URL(e.state[ne]??location.href),c=e.state[M],l=g.url?it(location)===it(g.url):!1;if(c===R&&(Ht||l)){s!==x.state&&(x.state=s),t(i),I[b]=C(),o&&scrollTo(o.x,o.y),b=n;return}const u=n-b;await D({type:"popstate",url:i,popped:{state:s,scroll:o,delta:u},accept:()=>{b=n,R=c},block:()=>{history.go(-u)},nav_token:O,event:e})}else if(!K){const n=new URL(location.href);t(n),E.hash&&location.reload()}}}),addEventListener("hashchange",()=>{K&&(K=!1,history.replaceState({...history.state,[N]:++b,[M]:R},"",location.href))});for(const e of document.querySelectorAll("link"))ve.has(e.rel)&&(e.href=e.href);addEventListener("pageshow",e=>{e.persisted&&P.navigating.set(ft.current=null)});function t(e){g.url=x.url=e,P.page.set(At(x)),P.page.notify()}}async function Ce(t,{status:a=200,error:e,node_ids:r,params:n,route:o,server_route:s,data:i,form:c}){Bt=!0;const l=new URL(location.href);let h;({params:n={},route:o={id:null}}=await ot(l,!1)||{}),h=kt.find(({id:f})=>f===o.id);let u,w=!0;try{const f=r.map(async(_,m)=>{const p=i[m];return p!=null&&p.uses&&(p.uses=je(p.uses)),Rt({loader:E.nodes[_],url:l,params:n,route:o,parent:async()=>{const y={};for(let S=0;S<m;S+=1)Object.assign(y,(await f[S]).data);return y},server_data_node:xt(p)})}),d=await Promise.all(f);if(h){const _=h.layouts;for(let m=0;m<_.length;m++)_[m]||d.splice(m,0,void 0)}u=nt({url:l,params:n,branch:d,status:a,error:e,form:c,route:h??null})}catch(f){if(f instanceof vt){await B(new URL(f.location,location.href));return}u=await Lt({status:bt(f),error:await Y(f,{url:l,params:n,route:o}),url:l,route:o}),t.textContent="",w=!1}u.props.page&&(u.props.page.state={}),await Jt(u,t,w)}function je(t){return{dependencies:new Set((t==null?void 0:t.dependencies)??[]),params:new Set((t==null?void 0:t.params)??[]),parent:!!(t!=null&&t.parent),route:!!(t!=null&&t.route),url:!!(t!=null&&t.url),search_params:new Set((t==null?void 0:t.search_params)??[])}}let mt=!1;function $e(t,a=!0){const e=document.querySelector("[autofocus]");if(e)e.focus();else{const r=te(t);if(r&&document.getElementById(r)){const{x:o,y:s}=C();setTimeout(()=>{const i=history.state;mt=!0,location.replace(new URL(`#${r}`,location.href)),history.replaceState(i,"",t),a&&scrollTo(o,s),mt=!1})}else{const o=document.body,s=o.getAttribute("tabindex");o.tabIndex=-1,o.focus({preventScroll:!0,focusVisible:!1}),s!==null?o.setAttribute("tabindex",s):o.removeAttribute("tabindex")}const n=getSelection();if(n&&n.type!=="None"){const o=[];for(let s=0;s<n.rangeCount;s+=1)o.push(n.getRangeAt(s));setTimeout(()=>{if(n.rangeCount===o.length){for(let s=0;s<n.rangeCount;s+=1){const i=o[s],c=n.getRangeAt(s);if(i.commonAncestorContainer!==c.commonAncestorContainer||i.startContainer!==c.startContainer||i.endContainer!==c.endContainer||i.startOffset!==c.startOffset||i.endOffset!==c.endOffset)return}n.removeAllRanges()}})}}}function Ut(t,a,e,r,n=null){var l,h;let o,s;const i=new Promise((u,w)=>{o=u,s=w});return i.catch(()=>{}),{navigation:{from:{params:t.params,route:{id:((l=t.route)==null?void 0:l.id)??null},url:t.url,scroll:C()},to:e&&{params:(a==null?void 0:a.params)??null,route:{id:((h=a==null?void 0:a.route)==null?void 0:h.id)??null},url:e,scroll:n},willUnload:!a,type:r,complete:i},fulfil:o,reject:s}}function At(t){return{data:t.data,error:t.error,form:t.form,params:t.params,route:t.route,state:t.state,status:t.status,url:t.url}}function Ne(t){const a=new URL(t);return a.hash=decodeURIComponent(t.hash),a}function te(t){let a;if(E.hash){const[,,e]=t.hash.split("#",3);a=e??""}else a=t.hash.slice(1);return decodeURIComponent(a)}export{Fe as a,Me as g,P as s};
@@ -1 +0,0 @@
1
- import{a as r}from"../chunks/SQVAC3Cv.js";import{w as t}from"../chunks/Q3vAxeY-.js";export{t as load_css,r as start};
@@ -1 +0,0 @@
1
- import{_ as m}from"../chunks/DemDWbs-.js";export{m as component};
@@ -1,113 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import {
3
- CloudflareRealtimeClient,
4
- assertCloudflareRealtimeConfig,
5
- createCloudflareRealtimeClient,
6
- hasCloudflareRealtimeConfig,
7
- } from '../lib/cloudflare-realtime.js';
8
-
9
- describe('cloudflare realtime helpers', () => {
10
- afterEach(() => {
11
- vi.restoreAllMocks();
12
- vi.unstubAllGlobals();
13
- });
14
-
15
- it('detects whether the required realtime config is present', () => {
16
- expect(hasCloudflareRealtimeConfig({})).toBe(false);
17
- expect(hasCloudflareRealtimeConfig({
18
- CF_REALTIME_APP_ID: ' app-123 ',
19
- CF_REALTIME_APP_SECRET: ' secret-456 ',
20
- })).toBe(true);
21
- });
22
-
23
- it('normalizes realtime config and defaults the base URL', () => {
24
- expect(assertCloudflareRealtimeConfig({
25
- CF_REALTIME_APP_ID: ' app-123 ',
26
- CF_REALTIME_APP_SECRET: ' secret-456 ',
27
- CF_REALTIME_TURN_KEY_ID: ' key-1 ',
28
- CF_REALTIME_TURN_API_TOKEN: ' token-1 ',
29
- })).toEqual({
30
- appId: 'app-123',
31
- appSecret: 'secret-456',
32
- baseUrl: 'https://rtc.live.cloudflare.com/v1',
33
- turnKeyId: 'key-1',
34
- turnApiToken: 'token-1',
35
- });
36
-
37
- expect(() => assertCloudflareRealtimeConfig({
38
- CF_REALTIME_APP_ID: 'missing-secret',
39
- })).toThrow('Cloudflare Realtime is not configured');
40
- });
41
-
42
- it('creates a realtime client and sends authenticated session requests', async () => {
43
- const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) =>
44
- new Response(JSON.stringify({ sessionId: 'sess-1' }), {
45
- status: 200,
46
- headers: { 'Content-Type': 'application/json' },
47
- }));
48
- vi.stubGlobal('fetch', fetchMock);
49
-
50
- const client = createCloudflareRealtimeClient({
51
- CF_REALTIME_APP_ID: 'app-123',
52
- CF_REALTIME_APP_SECRET: 'secret-456',
53
- } as never);
54
-
55
- expect(client).toBeInstanceOf(CloudflareRealtimeClient);
56
- await expect(client.createSession({
57
- sessionDescription: {
58
- sdp: 'offer-sdp',
59
- type: 'offer',
60
- },
61
- }, {
62
- thirdparty: true,
63
- correlationId: 'corr-1',
64
- })).resolves.toEqual({ sessionId: 'sess-1' });
65
-
66
- expect(fetchMock).toHaveBeenCalledTimes(1);
67
- expect(fetchMock.mock.calls[0]?.[0]).toBe(
68
- 'https://rtc.live.cloudflare.com/v1/apps/app-123/sessions/new?thirdparty=true&correlationId=corr-1',
69
- );
70
- expect(fetchMock.mock.calls[0]?.[1]).toMatchObject({
71
- method: 'POST',
72
- headers: {
73
- Authorization: 'Bearer secret-456',
74
- 'Content-Type': 'application/json',
75
- },
76
- });
77
- });
78
-
79
- it('uses TURN credentials when generating ICE servers', async () => {
80
- const fetchMock = vi.fn(async () =>
81
- new Response(JSON.stringify({
82
- iceServers: [{ urls: 'turn:global.example.com', username: 'user', credential: 'pass' }],
83
- }), {
84
- status: 200,
85
- headers: { 'Content-Type': 'application/json' },
86
- }));
87
- vi.stubGlobal('fetch', fetchMock);
88
-
89
- const client = new CloudflareRealtimeClient({
90
- CF_REALTIME_APP_ID: 'app-123',
91
- CF_REALTIME_APP_SECRET: 'secret-456',
92
- CF_REALTIME_BASE_URL: 'https://rtc.example.com/base/',
93
- CF_REALTIME_TURN_KEY_ID: 'turn-key',
94
- CF_REALTIME_TURN_API_TOKEN: 'turn-token',
95
- });
96
-
97
- await expect(client.generateIceServers(120)).resolves.toEqual({
98
- iceServers: [{ urls: 'turn:global.example.com', username: 'user', credential: 'pass' }],
99
- });
100
-
101
- expect(fetchMock).toHaveBeenCalledWith(
102
- 'https://rtc.example.com/base/turn/keys/turn-key/credentials/generate-ice-servers',
103
- {
104
- method: 'POST',
105
- headers: {
106
- Authorization: 'Bearer turn-token',
107
- 'Content-Type': 'application/json',
108
- },
109
- body: JSON.stringify({ ttl: 120 }),
110
- },
111
- );
112
- });
113
- });
@@ -1,251 +0,0 @@
1
- import type { Env } from '../types.js';
2
-
3
- const DEFAULT_CLOUDFLARE_REALTIME_BASE_URL = 'https://rtc.live.cloudflare.com/v1';
4
-
5
- export interface CloudflareRealtimeSessionDescription {
6
- sdp: string;
7
- type: 'offer' | 'answer';
8
- }
9
-
10
- export interface CloudflareRealtimeTrackObject {
11
- location: 'local' | 'remote';
12
- mid?: string;
13
- sessionId?: string;
14
- trackName?: string;
15
- bidirectionalMediaStream?: boolean;
16
- kind?: string;
17
- simulcast?: {
18
- preferredRid?: string;
19
- priorityOrdering?: 'none' | 'asciibetical';
20
- ridNotAvailable?: 'none' | 'asciibetical';
21
- };
22
- }
23
-
24
- export interface CloudflareRealtimeNewSessionRequest {
25
- sessionDescription?: CloudflareRealtimeSessionDescription;
26
- }
27
-
28
- export interface CloudflareRealtimeNewSessionResponse {
29
- errorCode?: string;
30
- errorDescription?: string;
31
- sessionDescription?: CloudflareRealtimeSessionDescription;
32
- sessionId: string;
33
- }
34
-
35
- export interface CloudflareRealtimeTracksRequest {
36
- sessionDescription?: CloudflareRealtimeSessionDescription;
37
- tracks: CloudflareRealtimeTrackObject[];
38
- autoDiscover?: boolean;
39
- }
40
-
41
- export interface CloudflareRealtimeTracksResponse {
42
- errorCode?: string;
43
- errorDescription?: string;
44
- requiresImmediateRenegotiation?: boolean;
45
- sessionDescription?: CloudflareRealtimeSessionDescription;
46
- tracks?: Array<CloudflareRealtimeTrackObject & {
47
- errorCode?: string;
48
- errorDescription?: string;
49
- }>;
50
- }
51
-
52
- export interface CloudflareRealtimeRenegotiateRequest {
53
- sessionDescription: CloudflareRealtimeSessionDescription;
54
- }
55
-
56
- export interface CloudflareRealtimeCloseTracksRequest {
57
- sessionDescription?: CloudflareRealtimeSessionDescription;
58
- tracks: Array<{ mid: string }>;
59
- force?: boolean;
60
- }
61
-
62
- export interface CloudflareRealtimeIceServer {
63
- urls: string[] | string;
64
- username?: string;
65
- credential?: string;
66
- }
67
-
68
- export interface CloudflareRealtimeIceServersResponse {
69
- iceServers: CloudflareRealtimeIceServer[];
70
- }
71
-
72
- export interface CloudflareRealtimeEnv {
73
- CF_REALTIME_APP_ID?: string;
74
- CF_REALTIME_APP_SECRET?: string;
75
- CF_REALTIME_BASE_URL?: string;
76
- CF_REALTIME_TURN_KEY_ID?: string;
77
- CF_REALTIME_TURN_API_TOKEN?: string;
78
- }
79
-
80
- function trimString(value: unknown): string | undefined {
81
- return typeof value === 'string' && value.trim() ? value.trim() : undefined;
82
- }
83
-
84
- async function parseRealtimeResponse<T>(response: Response): Promise<T> {
85
- const data = (await response.json().catch(() => ({}))) as Record<string, unknown>;
86
- if (!response.ok) {
87
- const message =
88
- (typeof data.errorDescription === 'string' && data.errorDescription)
89
- || (typeof data.message === 'string' && data.message)
90
- || `Cloudflare Realtime request failed (${response.status})`;
91
- throw new Error(message);
92
- }
93
- return data as T;
94
- }
95
-
96
- export function hasCloudflareRealtimeConfig(env: CloudflareRealtimeEnv): boolean {
97
- return !!trimString(env.CF_REALTIME_APP_ID) && !!trimString(env.CF_REALTIME_APP_SECRET);
98
- }
99
-
100
- export function assertCloudflareRealtimeConfig(env: CloudflareRealtimeEnv): {
101
- appId: string;
102
- appSecret: string;
103
- baseUrl: string;
104
- turnKeyId?: string;
105
- turnApiToken?: string;
106
- } {
107
- const appId = trimString(env.CF_REALTIME_APP_ID);
108
- const appSecret = trimString(env.CF_REALTIME_APP_SECRET);
109
- if (!appId || !appSecret) {
110
- throw new Error('Cloudflare Realtime is not configured. Set CF_REALTIME_APP_ID and CF_REALTIME_APP_SECRET.');
111
- }
112
-
113
- return {
114
- appId,
115
- appSecret,
116
- baseUrl: trimString(env.CF_REALTIME_BASE_URL) ?? DEFAULT_CLOUDFLARE_REALTIME_BASE_URL,
117
- turnKeyId: trimString(env.CF_REALTIME_TURN_KEY_ID),
118
- turnApiToken: trimString(env.CF_REALTIME_TURN_API_TOKEN),
119
- };
120
- }
121
-
122
- export class CloudflareRealtimeClient {
123
- private readonly appId: string;
124
- private readonly appSecret: string;
125
- private readonly baseUrl: string;
126
- private readonly turnKeyId?: string;
127
- private readonly turnApiToken?: string;
128
-
129
- constructor(env: CloudflareRealtimeEnv) {
130
- const config = assertCloudflareRealtimeConfig(env);
131
- this.appId = config.appId;
132
- this.appSecret = config.appSecret;
133
- this.baseUrl = config.baseUrl;
134
- this.turnKeyId = config.turnKeyId;
135
- this.turnApiToken = config.turnApiToken;
136
- }
137
-
138
- async createSession(
139
- body: CloudflareRealtimeNewSessionRequest = {},
140
- query?: { thirdparty?: boolean; correlationId?: string },
141
- ): Promise<CloudflareRealtimeNewSessionResponse> {
142
- const url = this.buildSessionUrl('/sessions/new', query);
143
- const response = await fetch(url, {
144
- method: 'POST',
145
- headers: this.buildAuthHeaders(),
146
- body: JSON.stringify(body),
147
- });
148
- return parseRealtimeResponse<CloudflareRealtimeNewSessionResponse>(response);
149
- }
150
-
151
- async addTracks(
152
- sessionId: string,
153
- body: CloudflareRealtimeTracksRequest,
154
- ): Promise<CloudflareRealtimeTracksResponse> {
155
- const response = await fetch(
156
- this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}/tracks/new`),
157
- {
158
- method: 'POST',
159
- headers: this.buildAuthHeaders(),
160
- body: JSON.stringify(body),
161
- },
162
- );
163
- return parseRealtimeResponse<CloudflareRealtimeTracksResponse>(response);
164
- }
165
-
166
- async renegotiate(
167
- sessionId: string,
168
- body: CloudflareRealtimeRenegotiateRequest,
169
- ): Promise<CloudflareRealtimeTracksResponse> {
170
- const response = await fetch(
171
- this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}/renegotiate`),
172
- {
173
- method: 'PUT',
174
- headers: this.buildAuthHeaders(),
175
- body: JSON.stringify(body),
176
- },
177
- );
178
- return parseRealtimeResponse<CloudflareRealtimeTracksResponse>(response);
179
- }
180
-
181
- async closeTracks(
182
- sessionId: string,
183
- body: CloudflareRealtimeCloseTracksRequest,
184
- ): Promise<CloudflareRealtimeTracksResponse> {
185
- const response = await fetch(
186
- this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}/tracks/close`),
187
- {
188
- method: 'PUT',
189
- headers: this.buildAuthHeaders(),
190
- body: JSON.stringify(body),
191
- },
192
- );
193
- return parseRealtimeResponse<CloudflareRealtimeTracksResponse>(response);
194
- }
195
-
196
- async getSession(sessionId: string): Promise<{
197
- tracks?: Array<CloudflareRealtimeTrackObject & { status?: string }>;
198
- }> {
199
- const response = await fetch(
200
- this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}`),
201
- {
202
- method: 'GET',
203
- headers: this.buildAuthHeaders(),
204
- },
205
- );
206
- return parseRealtimeResponse<{ tracks?: Array<CloudflareRealtimeTrackObject & { status?: string }> }>(response);
207
- }
208
-
209
- async generateIceServers(ttl = 3600): Promise<CloudflareRealtimeIceServersResponse> {
210
- if (!this.turnKeyId || !this.turnApiToken) {
211
- throw new Error('Cloudflare TURN is not configured. Set CF_REALTIME_TURN_KEY_ID and CF_REALTIME_TURN_API_TOKEN.');
212
- }
213
-
214
- const response = await fetch(
215
- `${this.baseUrl.replace(/\/$/, '')}/turn/keys/${encodeURIComponent(this.turnKeyId)}/credentials/generate-ice-servers`,
216
- {
217
- method: 'POST',
218
- headers: {
219
- Authorization: `Bearer ${this.turnApiToken}`,
220
- 'Content-Type': 'application/json',
221
- },
222
- body: JSON.stringify({ ttl }),
223
- },
224
- );
225
- return parseRealtimeResponse<CloudflareRealtimeIceServersResponse>(response);
226
- }
227
-
228
- private buildSessionUrl(pathname: string, query?: { thirdparty?: boolean; correlationId?: string }): string {
229
- const url = new URL(
230
- `${this.baseUrl.replace(/\/$/, '')}/apps/${encodeURIComponent(this.appId)}${pathname}`,
231
- );
232
- if (query?.thirdparty !== undefined) {
233
- url.searchParams.set('thirdparty', String(query.thirdparty));
234
- }
235
- if (query?.correlationId) {
236
- url.searchParams.set('correlationId', query.correlationId);
237
- }
238
- return url.toString();
239
- }
240
-
241
- private buildAuthHeaders(): HeadersInit {
242
- return {
243
- Authorization: `Bearer ${this.appSecret}`,
244
- 'Content-Type': 'application/json',
245
- };
246
- }
247
- }
248
-
249
- export function createCloudflareRealtimeClient(env: Env): CloudflareRealtimeClient {
250
- return new CloudflareRealtimeClient(env);
251
- }