@heyo-sh/heyo-cms 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/adapters/astro.js +1 -1
  2. package/dist/ai/assistant/schema.d.ts +10 -0
  3. package/dist/ai/assistant/schema.d.ts.map +1 -1
  4. package/dist/ai/service.d.ts.map +1 -1
  5. package/dist/ai.js +1 -1
  6. package/dist/auth.js +1 -1
  7. package/dist/cli.js +2 -2
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +1 -1
  10. package/dist/content/import.d.ts.map +1 -1
  11. package/dist/content.js +1 -1
  12. package/dist/db/d1.js +1 -1
  13. package/dist/db/mongodb.d.ts +2 -1
  14. package/dist/db/mongodb.d.ts.map +1 -1
  15. package/dist/db/mongodb.js +2 -2
  16. package/dist/db/postgres.js +1 -1
  17. package/dist/db/sql/adapter.d.ts.map +1 -1
  18. package/dist/db/sql/dialect.d.ts +3 -0
  19. package/dist/db/sql/dialect.d.ts.map +1 -1
  20. package/dist/db/sqlite.js +1 -1
  21. package/dist/db/unique.d.ts +46 -0
  22. package/dist/db/unique.d.ts.map +1 -0
  23. package/dist/editor.js +1 -1
  24. package/dist/engine/documents.d.ts.map +1 -1
  25. package/dist/fields/builtin.d.ts +44 -1
  26. package/dist/fields/builtin.d.ts.map +1 -1
  27. package/dist/fields/registry.d.ts.map +1 -1
  28. package/dist/fields.js +1 -1
  29. package/dist/http/routes/ai.d.ts +1 -1
  30. package/dist/http.js +1 -1
  31. package/dist/index-0fe3ngv2.js +2 -0
  32. package/dist/index-1xwmw8vb.js +3 -0
  33. package/dist/{index-vkwzdfte.js → index-2djaxtj8.js} +4 -4
  34. package/dist/{index-8x2em6c8.js → index-2j6d79fk.js} +7 -7
  35. package/dist/{index-kc1j3h6z.js → index-2zpnecrk.js} +2 -2
  36. package/dist/{index-761dec5z.js → index-344y6q4n.js} +2 -2
  37. package/dist/index-4j1kkeg2.js +2 -0
  38. package/dist/{index-2prbs05k.js → index-5j0cqm43.js} +2 -2
  39. package/dist/index-5yt6r97r.js +40 -0
  40. package/dist/{index-a8jcwqsf.js → index-78f0ghve.js} +2 -2
  41. package/dist/{index-szaq03qq.js → index-8j4p950b.js} +2 -2
  42. package/dist/{index-466cwb0b.js → index-cakys3fy.js} +2 -2
  43. package/dist/{index-p2cqsmvw.js → index-cnzt9x9x.js} +2 -2
  44. package/dist/{index-abfzb1ve.js → index-e3yknwyr.js} +2 -2
  45. package/dist/index-fy8r8e53.js +2 -0
  46. package/dist/index-gv8j76x3.js +26 -0
  47. package/dist/{index-rg9tpg3p.js → index-mzxsxzkd.js} +4 -4
  48. package/dist/{index-91fa1gzf.js → index-n09v6gx3.js} +2 -2
  49. package/dist/index-pkxgzw6e.js +2 -0
  50. package/dist/{index-vzhxn50e.js → index-q6ak9ph4.js} +2 -2
  51. package/dist/{index-6m1kjq9g.js → index-q6jkbsjp.js} +2 -2
  52. package/dist/{index-8nzmqy0f.js → index-qgtvymh2.js} +2 -2
  53. package/dist/{index-s9x07zb3.js → index-qkqtxr65.js} +2 -2
  54. package/dist/{index-ypcmns0v.js → index-rb9fxtyn.js} +2 -2
  55. package/dist/{index-g041kfa5.js → index-rpdkj3qk.js} +11 -11
  56. package/dist/{index-hbx67b20.js → index-twqpdtfh.js} +3 -3
  57. package/dist/index.js +1 -1
  58. package/dist/jobs.js +1 -1
  59. package/dist/migrations.js +1 -1
  60. package/dist/query.js +1 -1
  61. package/dist/{run-bjge3v8e.js → run-mtgvmy5x.js} +2 -2
  62. package/dist/schema.js +1 -1
  63. package/dist/telemetry.js +1 -1
  64. package/dist/types/fields.d.ts +2 -2
  65. package/dist/types/fields.d.ts.map +1 -1
  66. package/dist/types/panel.d.ts +2 -0
  67. package/dist/types/panel.d.ts.map +1 -1
  68. package/dist/uploads.js +3 -3
  69. package/dist/webhooks.js +1 -1
  70. package/package.json +1 -1
  71. package/dist/index-4qrep5f0.js +0 -26
  72. package/dist/index-67e458ws.js +0 -2
  73. package/dist/index-6w10rch0.js +0 -3
  74. package/dist/index-cv0hpz3d.js +0 -2
  75. package/dist/index-hpast878.js +0 -40
  76. package/dist/index-sgdnp83t.js +0 -2
@@ -1,2 +1,2 @@
1
- import{s,R,t,H,ae,m}from"./index-6m1kjq9g.js";import{ho,rt,jt,Et,l,u}from"./index-sgdnp83t.js";import{Pe,pn,we,re,fo}from"./index-p2cqsmvw.js";import{w}from"./index-rw0hze6x.js";import{x}from"./index-2ddk43me.js";import{b}from"./index-8nzmqy0f.js";import{e}from"./index-x72d1hzh.js";var Nn={github:{id:"github",endpoints:{authorization:"https://github.com/login/oauth/authorize",token:"https://github.com/login/oauth/access_token",userinfo:"https://api.github.com/user"},scope:"read:user user:email",pkce:!1,async profile(c,f){let p=await pe(f,"https://api.github.com/user",c.accessToken),g=typeof p.email==="string"?p.email:await Me(f,c.accessToken);return{id:String(p.id??""),email:g,name:M(p.name)??M(p.login),avatarUrl:M(p.avatar_url),claims:p}}},google:{id:"google",endpoints:{authorization:"https://accounts.google.com/o/oauth2/v2/auth",token:"https://oauth2.googleapis.com/token",userinfo:"https://openidconnect.googleapis.com/v1/userinfo"},scope:"openid email profile",pkce:!0,async profile(c,f){let p=c.idToken?$t(c.idToken):await pe(f,"https://openidconnect.googleapis.com/v1/userinfo",c.accessToken);return X(p)}},microsoft:{id:"microsoft",endpoints:{authorization:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",token:"https://login.microsoftonline.com/common/oauth2/v2.0/token",userinfo:"https://graph.microsoft.com/oidc/userinfo"},scope:"openid email profile",pkce:!0,async profile(c,f){let p=c.idToken?$t(c.idToken):await pe(f,"https://graph.microsoft.com/oidc/userinfo",c.accessToken);return X(p)}}};function X(c){return{id:String(c.sub??""),email:M(c.email),name:M(c.name)??M(c.preferred_username),avatarUrl:M(c.picture),claims:c}}function $t(c){let f=c.split(".")[1];if(!f)return{};let p=f.replaceAll("-","+").replaceAll("_","/");try{let g=atob(p.padEnd(Math.ceil(p.length/4)*4,"=")),k=Uint8Array.from(g,(v)=>v.charCodeAt(0));return JSON.parse(new TextDecoder().decode(k))}catch{return{}}}var fe=new Map;function Ce(c,f){let p=`${x(c)}/.well-known/openid-configuration`,g=fe.get(p);if(g)return g;let k=(async()=>{let v=await f(p,{headers:{accept:"application/json"}});if(!v.ok)throw new e(`OIDC discovery failed for ${c} (${v.status})`,{status:502,code:"oidc_discovery_failed"});let T=await v.json();return{authorization:String(T.authorization_endpoint??""),token:String(T.token_endpoint??""),...typeof T.userinfo_endpoint==="string"?{userinfo:T.userinfo_endpoint}:{}}})();return fe.set(p,k),k.catch(()=>fe.delete(p)),k}function Ue(c){let f=new URL(c.endpoints.authorization);if(f.searchParams.set("response_type","code"),f.searchParams.set("client_id",c.clientId),f.searchParams.set("redirect_uri",c.redirectUri),f.searchParams.set("scope",c.scope),f.searchParams.set("state",c.state),c.codeChallenge)f.searchParams.set("code_challenge",c.codeChallenge),f.searchParams.set("code_challenge_method","S256");for(let[p,g]of Object.entries(c.extra??{}))f.searchParams.set(p,g);return f.toString()}async function Oe(c){return{verifier:c,challenge:await fo(c)}}async function xe(c){let f=new URLSearchParams({grant_type:"authorization_code",code:c.code,redirect_uri:c.redirectUri,client_id:c.clientId,client_secret:c.clientSecret});if(c.codeVerifier)f.set("code_verifier",c.codeVerifier);let p=await c.fetchImpl(c.endpoints.token,{method:"POST",headers:{accept:"application/json","content-type":"application/x-www-form-urlencoded"},body:f.toString()}),g=await p.json().catch(()=>({}));if(!p.ok||typeof g.access_token!=="string"){let k=M(g.error_description)??M(g.error)??`HTTP ${p.status}`;throw new e(`OAuth token exchange failed: ${k}`,{status:401,code:"oauth_exchange_failed"})}return{accessToken:g.access_token,...typeof g.id_token==="string"?{idToken:g.id_token}:{},...typeof g.token_type==="string"?{tokenType:g.token_type}:{}}}async function pe(c,f,p){let g=await c(f,{headers:{authorization:`Bearer ${p}`,accept:"application/json","user-agent":"heyo-cms"}});if(!g.ok)throw new e(`OAuth profile request failed (${g.status})`,{status:502,code:"oauth_profile_failed"});return await g.json()}async function Me(c,f){let p=await c("https://api.github.com/user/emails",{headers:{authorization:`Bearer ${f}`,accept:"application/json","user-agent":"heyo-cms"}});if(!p.ok)return null;let g=await p.json();return(g.find((v)=>v.primary&&v.verified)??g.find((v)=>v.verified))?.email??null}function M(c){return typeof c==="string"&&c.length>0?c:null}function Un(){return qe(crypto.getRandomValues(new Uint8Array(20)))}function Jn(c){let f=`${encodeURIComponent(c.issuer)}:${encodeURIComponent(c.account)}`,p=new URLSearchParams({secret:c.secret,issuer:c.issuer,algorithm:"SHA1",digits:String(6),period:String(30)});return`otpauth://totp/${f}?${p.toString()}`}async function Qo(c,f=Date.now()){return De(c,Math.floor(f/1000/30))}async function cn(c,f,p={}){if(!c)return!1;let g=f.replaceAll(/\s/g,"");if(!/^\d{6}$/.test(g))return!1;let k=Math.floor((p.at??Date.now())/1000/30),v=p.window??1;for(let T=-v;T<=v;T+=1){let C=await De(c,k+T),U=0;for(let E=0;E<C.length;E+=1)U|=C.charCodeAt(E)^g.charCodeAt(E);if(U===0)return!0}return!1}async function De(c,f){let p=await crypto.subtle.importKey("raw",Ne(c),{name:"HMAC",hash:"SHA-1"},!1,["sign"]),g=new Uint8Array(8),k=f;for(let U=7;U>=0;U-=1)g[U]=k&255,k=Math.floor(k/256);let v=new Uint8Array(await crypto.subtle.sign("HMAC",p,g)),T=(v[v.length-1]??0)&15,C=((v[T]??0)&127)<<24|((v[T+1]??0)&255)<<16|((v[T+2]??0)&255)<<8|(v[T+3]??0)&255;return String(C%1e6).padStart(6,"0")}function qe(c){let f=0,p=0,g="";for(let k of c){p=p<<8|k,f+=8;while(f>=5)g+="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[p>>>f-5&31],f-=5}if(f>0)g+="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[p<<5-f&31];return g}function Ne(c){let f=c.replaceAll("=","").replaceAll(/\s/g,"").toUpperCase(),p=[],g=0,k=0;for(let v of f){let T="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(v);if(T===-1)continue;if(k=k<<5|T,g+=5,g>=8)p.push(k>>>g-8&255),g-=8}return new Uint8Array(p)}var A="cms_users",z="cms_sessions",q="cms_tokens",he="cms_accounts",V="cms_api_keys",N="cms_roles",be="heyo_cms_session",dn="hck_",Je={session:"7d",invite:"7d",magicLink:"15m",passwordReset:"1h",twoFactor:"10m",oauth:"10m"},Be=8,$e=5,Ke=/^[^@\s]+@[^@\s.]+\.[^@\s]+$/,He=/^[a-z][a-z0-9_-]*$/,Ve=1e4;function un(c){let{config:f}=c,p=c.fetch??((...n)=>fetch(...n)),g,k=()=>g??=Pe(we(16)),v,T=!1,C=()=>f.admin.auth,U=(n,i)=>w(i??Je[n]),E=(n)=>{let i=C();if(!i||!i.methods.includes(n))throw new e(`The "${n}" sign-in method is not enabled; add it to admin.auth.methods`,{status:400,code:"method_not_enabled"});return i};async function y(n){let i=await c.database();return i.transaction((r)=>n(r,i))}async function O(n,i,r,o){return(await i.query(n,{table:r,where:o,limit:1}))[0]??null}async function B(n,i,r,o){let d=(await i.mutate(n,{kind:"insert",table:r,values:o})).rows[0];if(!d)throw new e(`Insert into ${r} returned no row.`,{status:500,code:"insert_without_row"});return d}async function S(n,i,r,o,a){return(await i.mutate(n,{kind:"update",table:r,where:o,set:a,returning:!1})).count}async function F(n,i,r,o){return(await i.mutate(n,{kind:"delete",table:r,where:o})).count}async function j(n,i,r,o=null){let a={type:i,data:r,previous:null},d={actor:o};await c.emit(n,[a],d)}async function le(){let n=f.rbac?.roles??{};if(f.rbac?.customRoles!==!0)return n;let i=Date.now();if(v&&i-v.at<Ve)return v.map;let r=await y((d,h)=>h.query(d,{table:N})),o={};for(let d of r)o[String(d.name)]=J(d.permissions);let a={...o,...n};return v={at:i,map:a},a}async function Z(n){let i=n.roles.length>0?n.roles:f.rbac?.defaultRole?[f.rbac.defaultRole]:[];return{id:n.id,source:"session",roles:i,permissions:rt(i,await le())}}async function Y(n,i,r){let o=r.token??we(),a=new Date(Date.now()+r.ttlMs);return await B(n,i,q,{id:b("tok"),type:r.type,user_id:r.userId??null,email:r.email??null,token_hash:await re(o),data:r.data??null,expires_at:a,consumed_at:null,created_at:new Date}),{token:o,expiresAt:a}}async function _e(n,i,r,o){let a=await O(n,i,q,s(t("token_hash",await re(o)),t("type",r)));if(!a||ye(a.expires_at))throw e.unauthorized("This link is invalid or has expired");if(a.consumed_at!==null)throw e.unauthorized("This link has already been used");return a}async function ve(n,i,r){if(await S(n,i,q,s(t("id",String(r.id)),t("consumed_at",null)),{consumed_at:new Date})!==1)throw e.unauthorized("This link has already been used")}async function ee(n,i,r,o){let a=await _e(n,i,r,o);return await ve(n,i,a),a}async function te(n,i,r,o){let a=we(),d=new Date,h=new Date(d.getTime()+U("session",C()?.session?.expiresIn)),_=await B(n,i,z,{id:b("ses"),user_id:r.id,token_hash:await re(a),ip:o.ip??null,user_agent:o.userAgent??null,expires_at:h,created_at:d});return await S(n,i,A,t("id",r.id),{last_login_at:d}),{status:"signed-in",token:a,expiresAt:h.toISOString(),session:ge(_),user:{...r,lastLoginAt:d.toISOString()},actor:await Z(r)}}async function ne(n,i,r,o){if(!r.twoFactorEnabled)return te(n,i,r,o);let a=await Y(n,i,{type:"two-factor",ttlMs:U("twoFactor",void 0),userId:r.id,email:r.email});return{status:"two-factor-required",challenge:a.token,expiresAt:a.expiresAt.toISOString(),userId:r.id}}async function D(n,i,r){let o=await O(n,i,A,t("id",r));return o?W(o):null}async function K(n,i,r){let o=await O(n,i,A,t("email",ce(r)));return o?{user:W(o),row:o}:null}function se(n){if(n.status==="disabled")throw e.forbidden("This account has been disabled")}async function ke(n){let i=C();if(n==="oidc"){let a=i?.sso;if(!a)throw new e("SSO is not configured; set admin.auth.sso",{status:400,code:"method_not_enabled"});let d=await Ce(a.issuer,p),h=Ae("oidc",{...a.clientId===void 0?{}:{clientId:a.clientId},...a.clientSecret===void 0?{}:{clientSecret:a.clientSecret}});return{id:"oidc",endpoints:d,scope:a.scope??"openid email profile",pkce:!0,...h,provision:a.provision!==!1,...a.mapRoles?{mapRoles:a.mapRoles}:{},profile:async(_)=>_.idToken?X($t(_.idToken)):X(await je(d,_.accessToken))}}E(n);let r=Nn[n];if(!r)throw new e(`Unknown OAuth provider "${n}"`,{status:400,code:"unknown_provider"});let o=Ae(n);return{id:n,endpoints:r.endpoints,scope:i?.providers?.[n]?.scope??r.scope,pkce:r.pkce,...o,provision:!1,profile:r.profile}}async function je(n,i){if(!n.userinfo)return{};let r=await p(n.userinfo,{headers:{authorization:`Bearer ${i}`,accept:"application/json"}});if(!r.ok)return{};return await r.json()}function Ae(n,i={}){let r=C()?.providers?.[n],o=n.toUpperCase(),a=i.clientId??r?.clientId??ue(`HEYO_CMS_${o}_CLIENT_ID`)??ue(`${o}_CLIENT_ID`),d=i.clientSecret??r?.clientSecret??ue(`HEYO_CMS_${o}_CLIENT_SECRET`)??ue(`${o}_CLIENT_SECRET`);if(!a||!d)throw new e(`No credentials for "${n}": set admin.auth.providers.${n} or ${o}_CLIENT_ID and ${o}_CLIENT_SECRET`,{status:500,code:"provider_not_configured"});return{clientId:a,clientSecret:d}}async function ze(n,i,r,o){let a=await O(n,i,he,s(t("provider",r.id),t("provider_account_id",o.id)));if(a){let P=await D(n,i,String(a.user_id));if(!P)throw e.unauthorized("The linked account no longer exists");return Te(n,i,r,o,P)}let d=o.email?ce(o.email):null;if(!d)throw new e(`${r.id} did not return a verified email address, so the account cannot be matched`,{status:400,code:"oauth_without_email"});let _=(await K(n,i,d))?.user??null;if(!_){if(!r.provision)throw e.forbidden(`No CMS account for ${d}. An administrator has to invite this address first.`);_=await ie(n,i,{email:d,...o.name?{name:o.name}:{},roles:r.mapRoles?r.mapRoles(o.claims):f.rbac?.defaultRole?[f.rbac.defaultRole]:[],status:"active"}),await j(n,"user.joined",{userId:_.id,email:d,provider:r.id,provisioned:!0})}else if(_.status==="invited")await S(n,i,A,t("id",_.id),{status:"active",updated_at:new Date}),_={..._,status:"active"},await j(n,"user.joined",{userId:_.id,email:_.email});return await B(n,i,he,{id:b("acc"),user_id:_.id,provider:r.id,provider_account_id:o.id,email:d,created_at:new Date,updated_at:new Date}),Te(n,i,r,o,_)}async function Te(n,i,r,o,a){if(!r.mapRoles)return a;let d=r.mapRoles(o.claims);if(Fe(d,a.roles))return a;return await S(n,i,A,t("id",a.id),{roles:d,updated_at:new Date}),await j(n,"role.changed",{userId:a.id,roles:d,previous:a.roles,source:r.id}),{...a,roles:d}}async function ie(n,i,r){let o=ce(r.email);if(await O(n,i,A,t("email",o)))throw e.conflict(`A user with the email ${o} already exists`);if(r.password!==void 0)G(r.password);let d=new Date,h=await B(n,i,A,{id:b("usr"),email:o,name:r.name??null,password_hash:r.password===void 0?null:await Pe(r.password),two_factor_secret:null,two_factor_enabled:!1,status:r.status??"active",roles:r.roles??[],avatar_url:null,locale:r.locale??null,last_login_at:null,created_at:d,updated_at:d});return W(h)}function G(n){let i=C()?.password?.minLength??Be;if(n.length<i)throw e.validation(`The password must be at least ${i} characters`)}function Se(){let n=C();if(!n||!n.methods.includes("password"))return null;let i=n.firstUser??!0;if(i===!1)return null;if(i!==!0&&i.roles)return i.roles;let r=f.rbac?.roles;if(!r||"admin"in r)return["admin"];let o=Object.entries(r).find(([,a])=>a.includes(ho));return o?[o[0]]:["admin"]}async function oe(n){if(n.length===0||!f.rbac)return;let i=await le(),r=n.filter((o)=>!(o in i));if(r.length>0)throw e.validation(`Unknown role ${r.map((o)=>`"${o}"`).join(", ")}`)}let Q={async actorFromRequest(n){let i=n.headers.get("authorization"),o=(i?.toLowerCase().startsWith("bearer ")?i.slice(7).trim():null)??We(n,be);return o?Q.actorFromToken(o):null},async actorFromToken(n){if(n.startsWith(dn))return Q.apiKeys.verify(n);return(await Q.sessions.verify(n))?.actor??null},actorForUser:Z,users:{async list(n={}){let i=[];if(n.status)i.push(t("status",n.status));if(n.search){let o=`%${n.search}%`;i.push(R(ae("email",o,!0),ae("name",o,!0)))}return(await y((o,a)=>a.query(o,{table:A,...i.length>0?{where:s(...i)}:{},orderBy:[m("created_at","asc"),m("id","asc")],limit:n.limit??50,...n.offset===void 0?{}:{offset:n.offset}}))).map(W)},async get(n){return y((i,r)=>D(i,r,n))},async byEmail(n){return y(async(i,r)=>(await K(i,r,n))?.user??null)},async count(){let n=await y((i,r)=>r.query(i,{table:A,count:!0}));return Number(n[0]?.count??0)},async create(n){return await oe(n.roles??[]),y((i,r)=>ie(i,r,n))},async update(n,i){if(i.roles)await oe(i.roles);let r=await y(async(o,a)=>{let d=await D(o,a,n);if(!d)throw e.notFound(`No user with id "${n}"`);let h={updated_at:new Date};if(i.name!==void 0)h.name=i.name;if(i.status!==void 0)h.status=i.status;if(i.locale!==void 0)h.locale=i.locale;if(i.avatarUrl!==void 0)h.avatar_url=i.avatarUrl;if(i.roles!==void 0)h.roles=i.roles;if(await S(o,a,A,t("id",n),h),i.roles&&!Fe(i.roles,d.roles))await j(o,"role.changed",{userId:n,roles:i.roles,previous:d.roles},i.actor??null);if(i.status==="disabled")await F(o,a,z,t("user_id",n));return await D(o,a,n)});return c.deliver(),r},async remove(n,i={}){await y(async(r,o)=>{let a=await D(r,o,n);if(!a)throw e.notFound(`No user with id "${n}"`);for(let d of[z,q,he,V])await F(r,o,d,t("user_id",n));await F(r,o,A,t("id",n)),await j(r,"user.removed",{userId:n,email:a.email},i.actor??null)}),c.deliver()},async setPassword(n,i){G(i),await y(async(r,o)=>{let a=await Pe(i);if(await S(r,o,A,t("id",n),{password_hash:a,updated_at:new Date})===0)throw e.notFound(`No user with id "${n}"`);await F(r,o,z,t("user_id",n))})},async invite(n){await oe(n.roles??[]);let i=await y(async(r,o)=>{let a=ce(n.email),d=await K(r,o,a);if(d&&d.user.status==="active")throw e.conflict(`${a} is already an active user`);let h=d?.user??await ie(r,o,{email:a,...n.name?{name:n.name}:{},roles:n.roles??(f.rbac?.defaultRole?[f.rbac.defaultRole]:[]),status:"invited"}),_=await Y(r,o,{type:"invite",ttlMs:U("invite",C()?.invites?.expiresIn),userId:h.id,email:a});return await j(r,"user.invited",{userId:h.id,email:a,roles:h.roles},n.actor??null),{user:h,token:_.token,expiresAt:_.expiresAt.toISOString(),userId:h.id,email:a}});return c.deliver(),i},async acceptInvite(n){let i=await y(async(r,o)=>{let a=await ee(r,o,"invite",n.token),d=await D(r,o,String(a.user_id));if(!d)throw e.unauthorized("This invitation is no longer valid");let h={status:"active",updated_at:new Date};if(n.password!==void 0)G(n.password),h.password_hash=await Pe(n.password);if(n.name!==void 0)h.name=n.name;await S(r,o,A,t("id",d.id),h);let _={...d,status:"active",...n.name===void 0?{}:{name:n.name}};return await j(r,"user.joined",{userId:d.id,email:d.email}),te(r,o,_,n)});return c.deliver(),i}},setup:{async required(){if(Se()===null)return!1;if(T)return!1;let n=await Q.users.count();if(n>0)T=!0;return n===0},async claim(n){let i=Se();if(i===null)throw e.forbidden("This deployment does not accept a first account over HTTP; use heyo-cms create-admin");try{await oe(i)}catch(o){if(o instanceof e&&o.code==="validation_failed")throw e.validation(`The first account would be given the role ${i.map((a)=>`"${a}"`).join(", ")}, which rbac.roles does not define; set admin.auth.firstUser.roles`);throw o}G(n.password);let r=await y(async(o,a)=>{let d=await a.query(o,{table:A,count:!0});if(Number(d[0]?.count??0)>0)throw e.conflict("This CMS already has an account; sign in instead");let h=await ie(o,a,{email:n.email,password:n.password,roles:i,status:"active",...n.name===void 0?{}:{name:n.name}});return await j(o,"user.joined",{userId:h.id,email:h.email,first:!0}),te(o,a,h,n)});return T=!0,c.deliver(),r}},sessions:{async verify(n){return y(async(i,r)=>{let o=await O(i,r,z,t("token_hash",await re(n)));if(!o||ye(o.expires_at))return null;let a=await D(i,r,String(o.user_id));if(!a||a.status!=="active")return null;return{status:"signed-in",token:n,expiresAt:l(o.expires_at)??"",session:ge(o),user:a,actor:await Z(a)}})},async list(n){return(await y((r,o)=>o.query(r,{table:z,where:t("user_id",n),orderBy:[m("created_at","desc")]}))).map(ge)},async revoke(n){let i=await re(n);return await y((o,a)=>F(o,a,z,t("token_hash",i)))>0},async revokeById(n){return await y((r,o)=>F(r,o,z,t("id",n)))>0},async revokeAll(n){return y((i,r)=>F(i,r,z,t("user_id",n)))},async cleanup(){let n=new Date;return y(async(i,r)=>{let o=await F(i,r,z,H("expires_at",n));return await F(i,r,q,H("expires_at",n)),o})}},password:{async signIn(n){E("password");let i=await y(async(r,o)=>{let a=await K(r,o,n.email),d=a?a.row.password_hash:await k(),h=await pn(n.password,d);if(!a||!h)throw e.unauthorized("Invalid email or password");if(se(a.user),a.user.status==="invited")throw e.forbidden("This invitation has not been accepted yet");return ne(r,o,a.user,n)});return c.deliver(),i},async requestReset(n){return E("password"),y(async(i,r)=>{let o=await K(i,r,n.email);if(!o||o.user.status==="disabled")return null;let a=await Y(i,r,{type:"password-reset",ttlMs:U("passwordReset",C()?.passwordReset?.expiresIn),userId:o.user.id,email:o.user.email});return{token:a.token,expiresAt:a.expiresAt.toISOString(),userId:o.user.id,email:o.user.email}})},async reset(n){return E("password"),G(n.password),y(async(i,r)=>{let o=await ee(i,r,"password-reset",n.token),a=String(o.user_id);await S(i,r,A,t("id",a),{password_hash:await Pe(n.password),status:"active",updated_at:new Date}),await F(i,r,z,t("user_id",a));let d=await D(i,r,a);if(!d)throw e.unauthorized("This link is no longer valid");return d})}},magicLink:{async request(n){return E("magic-link"),y(async(i,r)=>{let o=await K(i,r,n.email);if(!o||o.user.status==="disabled")return null;let a=await Y(i,r,{type:"magic-link",ttlMs:U("magicLink",C()?.magicLink?.expiresIn),userId:o.user.id,email:o.user.email});return{token:a.token,expiresAt:a.expiresAt.toISOString(),userId:o.user.id,email:o.user.email}})},async verify(n){E("magic-link");let i=await y(async(r,o)=>{let a=await ee(r,o,"magic-link",n.token),d=await D(r,o,String(a.user_id));if(!d)throw e.unauthorized("This link is no longer valid");if(se(d),d.status==="invited")return await S(r,o,A,t("id",d.id),{status:"active",updated_at:new Date}),await j(r,"user.joined",{userId:d.id,email:d.email}),ne(r,o,{...d,status:"active"},n);return ne(r,o,d,n)});return c.deliver(),i}},oauth:{async authorize(n){let i=await ke(n.provider),r=we(24),o=we(),a=i.pkce?(await Oe(o)).challenge:void 0;return await y((d,h)=>Y(d,h,{type:"oauth",ttlMs:U("oauth",void 0),token:r,data:{provider:i.id,redirectUri:n.redirectUri,verifier:o,redirectTo:n.redirectTo??null}})),{url:Ue({endpoints:i.endpoints,clientId:i.clientId,redirectUri:n.redirectUri,scope:i.scope,state:r,...a===void 0?{}:{codeChallenge:a}}),state:r}},async callback(n){let i=await ke(n.provider),r=await y(async(o,a)=>{let d=await ee(o,a,"oauth",n.state),h=u(d.data);if(h.provider!==i.id)throw e.unauthorized("This sign-in request does not match");if(h.redirectUri!==n.redirectUri)throw e.unauthorized("The redirect URI does not match");let _=await xe({endpoints:i.endpoints,clientId:i.clientId,clientSecret:i.clientSecret,code:n.code,redirectUri:n.redirectUri,...i.pkce&&typeof h.verifier==="string"?{codeVerifier:h.verifier}:{},fetchImpl:p}),P=await i.profile(_,p),I=await ze(o,a,i,P);return se(I),{...await ne(o,a,I,n),redirectTo:typeof h.redirectTo==="string"?h.redirectTo:null}});return c.deliver(),r}},twoFactor:{async begin(n){let i=Un();return y(async(r,o)=>{let a=await D(r,o,n);if(!a)throw e.notFound(`No user with id "${n}"`);if(a.twoFactorEnabled)throw e.conflict("Disable the existing authenticator before enrolling another.");return await S(r,o,A,t("id",n),{two_factor_secret:i,updated_at:new Date}),{secret:i,uri:Jn({secret:i,account:a.email,issuer:f.admin.title??"Heyo CMS"})}})},async enable(n){return y(async(i,r)=>{let o=await O(i,r,A,t("id",n.userId));if(!o)throw e.notFound(`No user with id "${n.userId}"`);let a=o.two_factor_secret;if(!a)throw e.validation("Start the setup before enabling it");if(!await cn(a,n.code))throw e.unauthorized("That code is not valid");return await S(i,r,A,t("id",n.userId),{two_factor_enabled:!0,updated_at:new Date}),{...W(o),twoFactorEnabled:!0}})},async disable(n){return y(async(i,r)=>{if(await S(i,r,A,t("id",n),{two_factor_enabled:!1,two_factor_secret:null,updated_at:new Date})===0)throw e.notFound(`No user with id "${n}"`);return await D(i,r,n)})},async complete(n){let i=await y(async(r,o)=>{let a=await _e(r,o,"two-factor",n.challenge),d=await O(r,o,A,t("id",String(a.user_id)));if(!d)throw e.unauthorized("This challenge is no longer valid");if(!await cn(d.two_factor_secret,n.code)){let P=a;for(let I=0;I<$e;I+=1){let L=u(P.data).attempts,Ie=typeof L==="number"?L:null,Re=Number(Ie??0)+1;if(await S(r,o,q,s(t("id",String(P.id)),t("consumed_at",null),t("data.attempts",Ie)),{data:{...u(P.data),attempts:Re},...Re>=$e?{consumed_at:new Date}:{}})===1)break;let me=await O(r,o,q,t("id",String(P.id)));if(!me||me.consumed_at!==null)break;P=me}return null}await ve(r,o,a);let _=W(d);return se(_),te(r,o,_,n)});if(i===null)throw e.unauthorized("That code is not valid");return c.deliver(),i}},apiKeys:{async create(n){let i=C()?.apiKeys;if(!i?.enabled)throw new e("API keys are not enabled; set admin.auth.apiKeys.enabled",{status:400,code:"method_not_enabled"});let r=n.scopes??i.scopes??["read"],o=i.scopes;if(o){let P=r.filter((I)=>!o.includes(I));if(P.length>0)throw e.validation(`Scope ${P.map((I)=>`"${I}"`).join(", ")} is not in admin.auth.apiKeys.scopes`)}let a=`${dn}${we()}`,d=n.expiresIn??i.expiresIn,h=d?new Date(Date.now()+w(d)):null,_=await y(async(P,I)=>{let L=await B(P,I,V,{id:b("key"),name:n.name,user_id:n.userId??null,key_hash:await re(a),prefix:a.slice(0,dn.length+6),scopes:r,last_used_at:null,expires_at:h,revoked_at:null,created_at:new Date});return await j(P,"apikey.created",{id:String(L.id),name:n.name,scopes:r,userId:n.userId??null},n.actor??null),Ee(L)});return c.deliver(),{..._,key:a}},async list(n={}){return(await y((r,o)=>o.query(r,{table:V,...n.userId?{where:t("user_id",n.userId)}:{},orderBy:[m("created_at","desc")]}))).map(Ee)},async revoke(n,i={}){let r=await y(async(o,a)=>{let d=await S(o,a,V,s(t("id",n),t("revoked_at",null)),{revoked_at:new Date});if(d>0)await j(o,"apikey.revoked",{id:n},i.actor??null);return d>0});return c.deliver(),r},async verify(n){return y(async(i,r)=>{let o=await O(i,r,V,t("key_hash",await re(n)));if(!o)return null;if(o.revoked_at!==null)return null;if(o.expires_at!==null&&ye(o.expires_at))return null;let a=J(o.scopes),d=Et(a),h=[],_=d,P=String(o.id);if(o.user_id!==null&&o.user_id!==void 0){let I=await D(i,r,String(o.user_id));if(!I||I.status!=="active")return null;let L=await Z(I);h=L.roles,_=jt(L.permissions,d),P=I.id}return await S(i,r,V,t("id",String(o.id)),{last_used_at:new Date}),{id:P,source:"api-key",roles:h,permissions:_}})}},roles:{async list(){let n=Object.entries(f.rbac?.roles??{}).map(([a,d])=>({name:a,label:null,permissions:d,source:"config"}));if(f.rbac?.customRoles!==!0)return n;let i=await y((a,d)=>d.query(a,{table:N,orderBy:[m("name")]})),r=new Set(n.map((a)=>a.name)),o=i.filter((a)=>!r.has(String(a.name))).map((a)=>({name:String(a.name),label:a.label??null,permissions:J(a.permissions),source:"database"}));return[...n,...o].sort((a,d)=>a.name.localeCompare(d.name))},async create(n){if(de(),Le(n.name),f.rbac?.roles[n.name])throw e.conflict(`"${n.name}" is defined in the configuration; change it there`);let i=await y(async(r,o)=>{if(await O(r,o,N,t("name",n.name)))throw e.conflict(`The role "${n.name}" already exists`);return B(r,o,N,{id:b("rol"),name:n.name,label:n.label??null,permissions:n.permissions,created_at:new Date,updated_at:new Date})});return v=void 0,{name:String(i.name),label:i.label??null,permissions:J(i.permissions),source:"database"}},async update(n,i){de();let r=await y(async(o,a)=>{let d={updated_at:new Date};if(i.permissions)d.permissions=i.permissions;if(i.label!==void 0)d.label=i.label;if(await S(o,a,N,t("name",n),d)===0)throw e.notFound(`No role named "${n}"`);return O(o,a,N,t("name",n))});return v=void 0,{name:n,label:r?.label??null,permissions:J(r?.permissions),source:"database"}},async remove(n){de();let i=await y((r,o)=>F(r,o,N,t("name",n)));return v=void 0,i>0},async permissionsFor(n){return rt(n,await le())}}};function de(){if(f.rbac?.customRoles!==!0)throw new e("Roles are defined in the configuration; set rbac.customRoles to manage them in the panel",{status:400,code:"custom_roles_disabled"})}function Le(n){if(!He.test(n))throw e.validation(`"${n}" is not a role name; use lowercase letters, digits, dashes and underscores`)}return Q}function W(c){return{id:String(c.id),email:String(c.email),name:c.name??null,status:c.status??"active",roles:J(c.roles),avatarUrl:c.avatar_url??null,locale:c.locale??null,twoFactorEnabled:c.two_factor_enabled===!0,lastLoginAt:l(c.last_login_at),createdAt:l(c.created_at)??"",updatedAt:l(c.updated_at)??""}}function ge(c){return{id:String(c.id),userId:String(c.user_id),expiresAt:l(c.expires_at)??"",createdAt:l(c.created_at)??"",ip:c.ip??null,userAgent:c.user_agent??null}}function Ee(c){return{id:String(c.id),name:String(c.name),prefix:String(c.prefix),userId:c.user_id??null,scopes:J(c.scopes),lastUsedAt:l(c.last_used_at),expiresAt:l(c.expires_at),revokedAt:l(c.revoked_at),createdAt:l(c.created_at)??""}}function J(c){if(!Array.isArray(c))return[];return c.filter((f)=>typeof f==="string")}function ye(c){if(c===null||c===void 0)return!1;return(c instanceof Date?c:new Date(String(c))).getTime()<=Date.now()}function ce(c){let f=c.trim().toLowerCase();if(!Ke.test(f))throw e.validation(`"${c}" is not an email address`);return f}function Fe(c,f){if(c.length!==f.length)return!1;let p=[...f].sort();return[...c].sort().every((g,k)=>g===p[k])}function We(c,f){let p=c.headers.get("cookie");if(!p)return null;for(let g of p.split(";")){let k=g.indexOf("=");if(k===-1)continue;if(g.slice(0,k).trim()!==f)continue;return decodeURIComponent(g.slice(k+1).trim())}return null}function ue(c){return globalThis.process?.env?.[c]}
2
- export{Nn,$t,Un,Jn,Qo,cn,be,dn,un};
1
+ import{s,R,t,H,ae,m}from"./index-q6jkbsjp.js";import{wo,rt,Et,Mt,l,u}from"./index-pkxgzw6e.js";import{Pe,mn,we,re,bo}from"./index-cnzt9x9x.js";import{w}from"./index-rw0hze6x.js";import{x}from"./index-2ddk43me.js";import{b}from"./index-qgtvymh2.js";import{e}from"./index-x72d1hzh.js";var Un={github:{id:"github",endpoints:{authorization:"https://github.com/login/oauth/authorize",token:"https://github.com/login/oauth/access_token",userinfo:"https://api.github.com/user"},scope:"read:user user:email",pkce:!1,async profile(c,f){let p=await pe(f,"https://api.github.com/user",c.accessToken),g=typeof p.email==="string"?p.email:await Me(f,c.accessToken);return{id:String(p.id??""),email:g,name:M(p.name)??M(p.login),avatarUrl:M(p.avatar_url),claims:p}}},google:{id:"google",endpoints:{authorization:"https://accounts.google.com/o/oauth2/v2/auth",token:"https://oauth2.googleapis.com/token",userinfo:"https://openidconnect.googleapis.com/v1/userinfo"},scope:"openid email profile",pkce:!0,async profile(c,f){let p=c.idToken?jt(c.idToken):await pe(f,"https://openidconnect.googleapis.com/v1/userinfo",c.accessToken);return X(p)}},microsoft:{id:"microsoft",endpoints:{authorization:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",token:"https://login.microsoftonline.com/common/oauth2/v2.0/token",userinfo:"https://graph.microsoft.com/oidc/userinfo"},scope:"openid email profile",pkce:!0,async profile(c,f){let p=c.idToken?jt(c.idToken):await pe(f,"https://graph.microsoft.com/oidc/userinfo",c.accessToken);return X(p)}}};function X(c){return{id:String(c.sub??""),email:M(c.email),name:M(c.name)??M(c.preferred_username),avatarUrl:M(c.picture),claims:c}}function jt(c){let f=c.split(".")[1];if(!f)return{};let p=f.replaceAll("-","+").replaceAll("_","/");try{let g=atob(p.padEnd(Math.ceil(p.length/4)*4,"=")),k=Uint8Array.from(g,(v)=>v.charCodeAt(0));return JSON.parse(new TextDecoder().decode(k))}catch{return{}}}var fe=new Map;function Ce(c,f){let p=`${x(c)}/.well-known/openid-configuration`,g=fe.get(p);if(g)return g;let k=(async()=>{let v=await f(p,{headers:{accept:"application/json"}});if(!v.ok)throw new e(`OIDC discovery failed for ${c} (${v.status})`,{status:502,code:"oidc_discovery_failed"});let T=await v.json();return{authorization:String(T.authorization_endpoint??""),token:String(T.token_endpoint??""),...typeof T.userinfo_endpoint==="string"?{userinfo:T.userinfo_endpoint}:{}}})();return fe.set(p,k),k.catch(()=>fe.delete(p)),k}function Ue(c){let f=new URL(c.endpoints.authorization);if(f.searchParams.set("response_type","code"),f.searchParams.set("client_id",c.clientId),f.searchParams.set("redirect_uri",c.redirectUri),f.searchParams.set("scope",c.scope),f.searchParams.set("state",c.state),c.codeChallenge)f.searchParams.set("code_challenge",c.codeChallenge),f.searchParams.set("code_challenge_method","S256");for(let[p,g]of Object.entries(c.extra??{}))f.searchParams.set(p,g);return f.toString()}async function Oe(c){return{verifier:c,challenge:await bo(c)}}async function xe(c){let f=new URLSearchParams({grant_type:"authorization_code",code:c.code,redirect_uri:c.redirectUri,client_id:c.clientId,client_secret:c.clientSecret});if(c.codeVerifier)f.set("code_verifier",c.codeVerifier);let p=await c.fetchImpl(c.endpoints.token,{method:"POST",headers:{accept:"application/json","content-type":"application/x-www-form-urlencoded"},body:f.toString()}),g=await p.json().catch(()=>({}));if(!p.ok||typeof g.access_token!=="string"){let k=M(g.error_description)??M(g.error)??`HTTP ${p.status}`;throw new e(`OAuth token exchange failed: ${k}`,{status:401,code:"oauth_exchange_failed"})}return{accessToken:g.access_token,...typeof g.id_token==="string"?{idToken:g.id_token}:{},...typeof g.token_type==="string"?{tokenType:g.token_type}:{}}}async function pe(c,f,p){let g=await c(f,{headers:{authorization:`Bearer ${p}`,accept:"application/json","user-agent":"heyo-cms"}});if(!g.ok)throw new e(`OAuth profile request failed (${g.status})`,{status:502,code:"oauth_profile_failed"});return await g.json()}async function Me(c,f){let p=await c("https://api.github.com/user/emails",{headers:{authorization:`Bearer ${f}`,accept:"application/json","user-agent":"heyo-cms"}});if(!p.ok)return null;let g=await p.json();return(g.find((v)=>v.primary&&v.verified)??g.find((v)=>v.verified))?.email??null}function M(c){return typeof c==="string"&&c.length>0?c:null}function Jn(){return qe(crypto.getRandomValues(new Uint8Array(20)))}function Wn(c){let f=`${encodeURIComponent(c.issuer)}:${encodeURIComponent(c.account)}`,p=new URLSearchParams({secret:c.secret,issuer:c.issuer,algorithm:"SHA1",digits:String(6),period:String(30)});return`otpauth://totp/${f}?${p.toString()}`}async function nr(c,f=Date.now()){return De(c,Math.floor(f/1000/30))}async function dn(c,f,p={}){if(!c)return!1;let g=f.replaceAll(/\s/g,"");if(!/^\d{6}$/.test(g))return!1;let k=Math.floor((p.at??Date.now())/1000/30),v=p.window??1;for(let T=-v;T<=v;T+=1){let C=await De(c,k+T),U=0;for(let E=0;E<C.length;E+=1)U|=C.charCodeAt(E)^g.charCodeAt(E);if(U===0)return!0}return!1}async function De(c,f){let p=await crypto.subtle.importKey("raw",Ne(c),{name:"HMAC",hash:"SHA-1"},!1,["sign"]),g=new Uint8Array(8),k=f;for(let U=7;U>=0;U-=1)g[U]=k&255,k=Math.floor(k/256);let v=new Uint8Array(await crypto.subtle.sign("HMAC",p,g)),T=(v[v.length-1]??0)&15,C=((v[T]??0)&127)<<24|((v[T+1]??0)&255)<<16|((v[T+2]??0)&255)<<8|(v[T+3]??0)&255;return String(C%1e6).padStart(6,"0")}function qe(c){let f=0,p=0,g="";for(let k of c){p=p<<8|k,f+=8;while(f>=5)g+="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[p>>>f-5&31],f-=5}if(f>0)g+="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[p<<5-f&31];return g}function Ne(c){let f=c.replaceAll("=","").replaceAll(/\s/g,"").toUpperCase(),p=[],g=0,k=0;for(let v of f){let T="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(v);if(T===-1)continue;if(k=k<<5|T,g+=5,g>=8)p.push(k>>>g-8&255),g-=8}return new Uint8Array(p)}var A="cms_users",z="cms_sessions",q="cms_tokens",he="cms_accounts",V="cms_api_keys",N="cms_roles",be="heyo_cms_session",un="hck_",Je={session:"7d",invite:"7d",magicLink:"15m",passwordReset:"1h",twoFactor:"10m",oauth:"10m"},Be=8,$e=5,Ke=/^[^@\s]+@[^@\s.]+\.[^@\s]+$/,He=/^[a-z][a-z0-9_-]*$/,Ve=1e4;function pn(c){let{config:f}=c,p=c.fetch??((...n)=>fetch(...n)),g,k=()=>g??=Pe(we(16)),v,T=!1,C=()=>f.admin.auth,U=(n,i)=>w(i??Je[n]),E=(n)=>{let i=C();if(!i||!i.methods.includes(n))throw new e(`The "${n}" sign-in method is not enabled; add it to admin.auth.methods`,{status:400,code:"method_not_enabled"});return i};async function y(n){let i=await c.database();return i.transaction((r)=>n(r,i))}async function O(n,i,r,o){return(await i.query(n,{table:r,where:o,limit:1}))[0]??null}async function B(n,i,r,o){let d=(await i.mutate(n,{kind:"insert",table:r,values:o})).rows[0];if(!d)throw new e(`Insert into ${r} returned no row.`,{status:500,code:"insert_without_row"});return d}async function S(n,i,r,o,a){return(await i.mutate(n,{kind:"update",table:r,where:o,set:a,returning:!1})).count}async function F(n,i,r,o){return(await i.mutate(n,{kind:"delete",table:r,where:o})).count}async function j(n,i,r,o=null){let a={type:i,data:r,previous:null},d={actor:o};await c.emit(n,[a],d)}async function le(){let n=f.rbac?.roles??{};if(f.rbac?.customRoles!==!0)return n;let i=Date.now();if(v&&i-v.at<Ve)return v.map;let r=await y((d,h)=>h.query(d,{table:N})),o={};for(let d of r)o[String(d.name)]=J(d.permissions);let a={...o,...n};return v={at:i,map:a},a}async function Z(n){let i=n.roles.length>0?n.roles:f.rbac?.defaultRole?[f.rbac.defaultRole]:[];return{id:n.id,source:"session",roles:i,permissions:rt(i,await le())}}async function Y(n,i,r){let o=r.token??we(),a=new Date(Date.now()+r.ttlMs);return await B(n,i,q,{id:b("tok"),type:r.type,user_id:r.userId??null,email:r.email??null,token_hash:await re(o),data:r.data??null,expires_at:a,consumed_at:null,created_at:new Date}),{token:o,expiresAt:a}}async function _e(n,i,r,o){let a=await O(n,i,q,s(t("token_hash",await re(o)),t("type",r)));if(!a||ye(a.expires_at))throw e.unauthorized("This link is invalid or has expired");if(a.consumed_at!==null)throw e.unauthorized("This link has already been used");return a}async function ve(n,i,r){if(await S(n,i,q,s(t("id",String(r.id)),t("consumed_at",null)),{consumed_at:new Date})!==1)throw e.unauthorized("This link has already been used")}async function ee(n,i,r,o){let a=await _e(n,i,r,o);return await ve(n,i,a),a}async function te(n,i,r,o){let a=we(),d=new Date,h=new Date(d.getTime()+U("session",C()?.session?.expiresIn)),_=await B(n,i,z,{id:b("ses"),user_id:r.id,token_hash:await re(a),ip:o.ip??null,user_agent:o.userAgent??null,expires_at:h,created_at:d});return await S(n,i,A,t("id",r.id),{last_login_at:d}),{status:"signed-in",token:a,expiresAt:h.toISOString(),session:ge(_),user:{...r,lastLoginAt:d.toISOString()},actor:await Z(r)}}async function ne(n,i,r,o){if(!r.twoFactorEnabled)return te(n,i,r,o);let a=await Y(n,i,{type:"two-factor",ttlMs:U("twoFactor",void 0),userId:r.id,email:r.email});return{status:"two-factor-required",challenge:a.token,expiresAt:a.expiresAt.toISOString(),userId:r.id}}async function D(n,i,r){let o=await O(n,i,A,t("id",r));return o?W(o):null}async function K(n,i,r){let o=await O(n,i,A,t("email",ce(r)));return o?{user:W(o),row:o}:null}function se(n){if(n.status==="disabled")throw e.forbidden("This account has been disabled")}async function ke(n){let i=C();if(n==="oidc"){let a=i?.sso;if(!a)throw new e("SSO is not configured; set admin.auth.sso",{status:400,code:"method_not_enabled"});let d=await Ce(a.issuer,p),h=Ae("oidc",{...a.clientId===void 0?{}:{clientId:a.clientId},...a.clientSecret===void 0?{}:{clientSecret:a.clientSecret}});return{id:"oidc",endpoints:d,scope:a.scope??"openid email profile",pkce:!0,...h,provision:a.provision!==!1,...a.mapRoles?{mapRoles:a.mapRoles}:{},profile:async(_)=>_.idToken?X(jt(_.idToken)):X(await je(d,_.accessToken))}}E(n);let r=Un[n];if(!r)throw new e(`Unknown OAuth provider "${n}"`,{status:400,code:"unknown_provider"});let o=Ae(n);return{id:n,endpoints:r.endpoints,scope:i?.providers?.[n]?.scope??r.scope,pkce:r.pkce,...o,provision:!1,profile:r.profile}}async function je(n,i){if(!n.userinfo)return{};let r=await p(n.userinfo,{headers:{authorization:`Bearer ${i}`,accept:"application/json"}});if(!r.ok)return{};return await r.json()}function Ae(n,i={}){let r=C()?.providers?.[n],o=n.toUpperCase(),a=i.clientId??r?.clientId??ue(`HEYO_CMS_${o}_CLIENT_ID`)??ue(`${o}_CLIENT_ID`),d=i.clientSecret??r?.clientSecret??ue(`HEYO_CMS_${o}_CLIENT_SECRET`)??ue(`${o}_CLIENT_SECRET`);if(!a||!d)throw new e(`No credentials for "${n}": set admin.auth.providers.${n} or ${o}_CLIENT_ID and ${o}_CLIENT_SECRET`,{status:500,code:"provider_not_configured"});return{clientId:a,clientSecret:d}}async function ze(n,i,r,o){let a=await O(n,i,he,s(t("provider",r.id),t("provider_account_id",o.id)));if(a){let P=await D(n,i,String(a.user_id));if(!P)throw e.unauthorized("The linked account no longer exists");return Te(n,i,r,o,P)}let d=o.email?ce(o.email):null;if(!d)throw new e(`${r.id} did not return a verified email address, so the account cannot be matched`,{status:400,code:"oauth_without_email"});let _=(await K(n,i,d))?.user??null;if(!_){if(!r.provision)throw e.forbidden(`No CMS account for ${d}. An administrator has to invite this address first.`);_=await ie(n,i,{email:d,...o.name?{name:o.name}:{},roles:r.mapRoles?r.mapRoles(o.claims):f.rbac?.defaultRole?[f.rbac.defaultRole]:[],status:"active"}),await j(n,"user.joined",{userId:_.id,email:d,provider:r.id,provisioned:!0})}else if(_.status==="invited")await S(n,i,A,t("id",_.id),{status:"active",updated_at:new Date}),_={..._,status:"active"},await j(n,"user.joined",{userId:_.id,email:_.email});return await B(n,i,he,{id:b("acc"),user_id:_.id,provider:r.id,provider_account_id:o.id,email:d,created_at:new Date,updated_at:new Date}),Te(n,i,r,o,_)}async function Te(n,i,r,o,a){if(!r.mapRoles)return a;let d=r.mapRoles(o.claims);if(Fe(d,a.roles))return a;return await S(n,i,A,t("id",a.id),{roles:d,updated_at:new Date}),await j(n,"role.changed",{userId:a.id,roles:d,previous:a.roles,source:r.id}),{...a,roles:d}}async function ie(n,i,r){let o=ce(r.email);if(await O(n,i,A,t("email",o)))throw e.conflict(`A user with the email ${o} already exists`);if(r.password!==void 0)G(r.password);let d=new Date,h=await B(n,i,A,{id:b("usr"),email:o,name:r.name??null,password_hash:r.password===void 0?null:await Pe(r.password),two_factor_secret:null,two_factor_enabled:!1,status:r.status??"active",roles:r.roles??[],avatar_url:null,locale:r.locale??null,last_login_at:null,created_at:d,updated_at:d});return W(h)}function G(n){let i=C()?.password?.minLength??Be;if(n.length<i)throw e.validation(`The password must be at least ${i} characters`)}function Se(){let n=C();if(!n||!n.methods.includes("password"))return null;let i=n.firstUser??!0;if(i===!1)return null;if(i!==!0&&i.roles)return i.roles;let r=f.rbac?.roles;if(!r||"admin"in r)return["admin"];let o=Object.entries(r).find(([,a])=>a.includes(wo));return o?[o[0]]:["admin"]}async function oe(n){if(n.length===0||!f.rbac)return;let i=await le(),r=n.filter((o)=>!(o in i));if(r.length>0)throw e.validation(`Unknown role ${r.map((o)=>`"${o}"`).join(", ")}`)}let Q={async actorFromRequest(n){let i=n.headers.get("authorization"),o=(i?.toLowerCase().startsWith("bearer ")?i.slice(7).trim():null)??We(n,be);return o?Q.actorFromToken(o):null},async actorFromToken(n){if(n.startsWith(un))return Q.apiKeys.verify(n);return(await Q.sessions.verify(n))?.actor??null},actorForUser:Z,users:{async list(n={}){let i=[];if(n.status)i.push(t("status",n.status));if(n.search){let o=`%${n.search}%`;i.push(R(ae("email",o,!0),ae("name",o,!0)))}return(await y((o,a)=>a.query(o,{table:A,...i.length>0?{where:s(...i)}:{},orderBy:[m("created_at","asc"),m("id","asc")],limit:n.limit??50,...n.offset===void 0?{}:{offset:n.offset}}))).map(W)},async get(n){return y((i,r)=>D(i,r,n))},async byEmail(n){return y(async(i,r)=>(await K(i,r,n))?.user??null)},async count(){let n=await y((i,r)=>r.query(i,{table:A,count:!0}));return Number(n[0]?.count??0)},async create(n){return await oe(n.roles??[]),y((i,r)=>ie(i,r,n))},async update(n,i){if(i.roles)await oe(i.roles);let r=await y(async(o,a)=>{let d=await D(o,a,n);if(!d)throw e.notFound(`No user with id "${n}"`);let h={updated_at:new Date};if(i.name!==void 0)h.name=i.name;if(i.status!==void 0)h.status=i.status;if(i.locale!==void 0)h.locale=i.locale;if(i.avatarUrl!==void 0)h.avatar_url=i.avatarUrl;if(i.roles!==void 0)h.roles=i.roles;if(await S(o,a,A,t("id",n),h),i.roles&&!Fe(i.roles,d.roles))await j(o,"role.changed",{userId:n,roles:i.roles,previous:d.roles},i.actor??null);if(i.status==="disabled")await F(o,a,z,t("user_id",n));return await D(o,a,n)});return c.deliver(),r},async remove(n,i={}){await y(async(r,o)=>{let a=await D(r,o,n);if(!a)throw e.notFound(`No user with id "${n}"`);for(let d of[z,q,he,V])await F(r,o,d,t("user_id",n));await F(r,o,A,t("id",n)),await j(r,"user.removed",{userId:n,email:a.email},i.actor??null)}),c.deliver()},async setPassword(n,i){G(i),await y(async(r,o)=>{let a=await Pe(i);if(await S(r,o,A,t("id",n),{password_hash:a,updated_at:new Date})===0)throw e.notFound(`No user with id "${n}"`);await F(r,o,z,t("user_id",n))})},async invite(n){await oe(n.roles??[]);let i=await y(async(r,o)=>{let a=ce(n.email),d=await K(r,o,a);if(d&&d.user.status==="active")throw e.conflict(`${a} is already an active user`);let h=d?.user??await ie(r,o,{email:a,...n.name?{name:n.name}:{},roles:n.roles??(f.rbac?.defaultRole?[f.rbac.defaultRole]:[]),status:"invited"}),_=await Y(r,o,{type:"invite",ttlMs:U("invite",C()?.invites?.expiresIn),userId:h.id,email:a});return await j(r,"user.invited",{userId:h.id,email:a,roles:h.roles},n.actor??null),{user:h,token:_.token,expiresAt:_.expiresAt.toISOString(),userId:h.id,email:a}});return c.deliver(),i},async acceptInvite(n){let i=await y(async(r,o)=>{let a=await ee(r,o,"invite",n.token),d=await D(r,o,String(a.user_id));if(!d)throw e.unauthorized("This invitation is no longer valid");let h={status:"active",updated_at:new Date};if(n.password!==void 0)G(n.password),h.password_hash=await Pe(n.password);if(n.name!==void 0)h.name=n.name;await S(r,o,A,t("id",d.id),h);let _={...d,status:"active",...n.name===void 0?{}:{name:n.name}};return await j(r,"user.joined",{userId:d.id,email:d.email}),te(r,o,_,n)});return c.deliver(),i}},setup:{async required(){if(Se()===null)return!1;if(T)return!1;let n=await Q.users.count();if(n>0)T=!0;return n===0},async claim(n){let i=Se();if(i===null)throw e.forbidden("This deployment does not accept a first account over HTTP; use heyo-cms create-admin");try{await oe(i)}catch(o){if(o instanceof e&&o.code==="validation_failed")throw e.validation(`The first account would be given the role ${i.map((a)=>`"${a}"`).join(", ")}, which rbac.roles does not define; set admin.auth.firstUser.roles`);throw o}G(n.password);let r=await y(async(o,a)=>{let d=await a.query(o,{table:A,count:!0});if(Number(d[0]?.count??0)>0)throw e.conflict("This CMS already has an account; sign in instead");let h=await ie(o,a,{email:n.email,password:n.password,roles:i,status:"active",...n.name===void 0?{}:{name:n.name}});return await j(o,"user.joined",{userId:h.id,email:h.email,first:!0}),te(o,a,h,n)});return T=!0,c.deliver(),r}},sessions:{async verify(n){return y(async(i,r)=>{let o=await O(i,r,z,t("token_hash",await re(n)));if(!o||ye(o.expires_at))return null;let a=await D(i,r,String(o.user_id));if(!a||a.status!=="active")return null;return{status:"signed-in",token:n,expiresAt:l(o.expires_at)??"",session:ge(o),user:a,actor:await Z(a)}})},async list(n){return(await y((r,o)=>o.query(r,{table:z,where:t("user_id",n),orderBy:[m("created_at","desc")]}))).map(ge)},async revoke(n){let i=await re(n);return await y((o,a)=>F(o,a,z,t("token_hash",i)))>0},async revokeById(n){return await y((r,o)=>F(r,o,z,t("id",n)))>0},async revokeAll(n){return y((i,r)=>F(i,r,z,t("user_id",n)))},async cleanup(){let n=new Date;return y(async(i,r)=>{let o=await F(i,r,z,H("expires_at",n));return await F(i,r,q,H("expires_at",n)),o})}},password:{async signIn(n){E("password");let i=await y(async(r,o)=>{let a=await K(r,o,n.email),d=a?a.row.password_hash:await k(),h=await mn(n.password,d);if(!a||!h)throw e.unauthorized("Invalid email or password");if(se(a.user),a.user.status==="invited")throw e.forbidden("This invitation has not been accepted yet");return ne(r,o,a.user,n)});return c.deliver(),i},async requestReset(n){return E("password"),y(async(i,r)=>{let o=await K(i,r,n.email);if(!o||o.user.status==="disabled")return null;let a=await Y(i,r,{type:"password-reset",ttlMs:U("passwordReset",C()?.passwordReset?.expiresIn),userId:o.user.id,email:o.user.email});return{token:a.token,expiresAt:a.expiresAt.toISOString(),userId:o.user.id,email:o.user.email}})},async reset(n){return E("password"),G(n.password),y(async(i,r)=>{let o=await ee(i,r,"password-reset",n.token),a=String(o.user_id);await S(i,r,A,t("id",a),{password_hash:await Pe(n.password),status:"active",updated_at:new Date}),await F(i,r,z,t("user_id",a));let d=await D(i,r,a);if(!d)throw e.unauthorized("This link is no longer valid");return d})}},magicLink:{async request(n){return E("magic-link"),y(async(i,r)=>{let o=await K(i,r,n.email);if(!o||o.user.status==="disabled")return null;let a=await Y(i,r,{type:"magic-link",ttlMs:U("magicLink",C()?.magicLink?.expiresIn),userId:o.user.id,email:o.user.email});return{token:a.token,expiresAt:a.expiresAt.toISOString(),userId:o.user.id,email:o.user.email}})},async verify(n){E("magic-link");let i=await y(async(r,o)=>{let a=await ee(r,o,"magic-link",n.token),d=await D(r,o,String(a.user_id));if(!d)throw e.unauthorized("This link is no longer valid");if(se(d),d.status==="invited")return await S(r,o,A,t("id",d.id),{status:"active",updated_at:new Date}),await j(r,"user.joined",{userId:d.id,email:d.email}),ne(r,o,{...d,status:"active"},n);return ne(r,o,d,n)});return c.deliver(),i}},oauth:{async authorize(n){let i=await ke(n.provider),r=we(24),o=we(),a=i.pkce?(await Oe(o)).challenge:void 0;return await y((d,h)=>Y(d,h,{type:"oauth",ttlMs:U("oauth",void 0),token:r,data:{provider:i.id,redirectUri:n.redirectUri,verifier:o,redirectTo:n.redirectTo??null}})),{url:Ue({endpoints:i.endpoints,clientId:i.clientId,redirectUri:n.redirectUri,scope:i.scope,state:r,...a===void 0?{}:{codeChallenge:a}}),state:r}},async callback(n){let i=await ke(n.provider),r=await y(async(o,a)=>{let d=await ee(o,a,"oauth",n.state),h=u(d.data);if(h.provider!==i.id)throw e.unauthorized("This sign-in request does not match");if(h.redirectUri!==n.redirectUri)throw e.unauthorized("The redirect URI does not match");let _=await xe({endpoints:i.endpoints,clientId:i.clientId,clientSecret:i.clientSecret,code:n.code,redirectUri:n.redirectUri,...i.pkce&&typeof h.verifier==="string"?{codeVerifier:h.verifier}:{},fetchImpl:p}),P=await i.profile(_,p),I=await ze(o,a,i,P);return se(I),{...await ne(o,a,I,n),redirectTo:typeof h.redirectTo==="string"?h.redirectTo:null}});return c.deliver(),r}},twoFactor:{async begin(n){let i=Jn();return y(async(r,o)=>{let a=await D(r,o,n);if(!a)throw e.notFound(`No user with id "${n}"`);if(a.twoFactorEnabled)throw e.conflict("Disable the existing authenticator before enrolling another.");return await S(r,o,A,t("id",n),{two_factor_secret:i,updated_at:new Date}),{secret:i,uri:Wn({secret:i,account:a.email,issuer:f.admin.title??"Heyo CMS"})}})},async enable(n){return y(async(i,r)=>{let o=await O(i,r,A,t("id",n.userId));if(!o)throw e.notFound(`No user with id "${n.userId}"`);let a=o.two_factor_secret;if(!a)throw e.validation("Start the setup before enabling it");if(!await dn(a,n.code))throw e.unauthorized("That code is not valid");return await S(i,r,A,t("id",n.userId),{two_factor_enabled:!0,updated_at:new Date}),{...W(o),twoFactorEnabled:!0}})},async disable(n){return y(async(i,r)=>{if(await S(i,r,A,t("id",n),{two_factor_enabled:!1,two_factor_secret:null,updated_at:new Date})===0)throw e.notFound(`No user with id "${n}"`);return await D(i,r,n)})},async complete(n){let i=await y(async(r,o)=>{let a=await _e(r,o,"two-factor",n.challenge),d=await O(r,o,A,t("id",String(a.user_id)));if(!d)throw e.unauthorized("This challenge is no longer valid");if(!await dn(d.two_factor_secret,n.code)){let P=a;for(let I=0;I<$e;I+=1){let L=u(P.data).attempts,Ie=typeof L==="number"?L:null,Re=Number(Ie??0)+1;if(await S(r,o,q,s(t("id",String(P.id)),t("consumed_at",null),t("data.attempts",Ie)),{data:{...u(P.data),attempts:Re},...Re>=$e?{consumed_at:new Date}:{}})===1)break;let me=await O(r,o,q,t("id",String(P.id)));if(!me||me.consumed_at!==null)break;P=me}return null}await ve(r,o,a);let _=W(d);return se(_),te(r,o,_,n)});if(i===null)throw e.unauthorized("That code is not valid");return c.deliver(),i}},apiKeys:{async create(n){let i=C()?.apiKeys;if(!i?.enabled)throw new e("API keys are not enabled; set admin.auth.apiKeys.enabled",{status:400,code:"method_not_enabled"});let r=n.scopes??i.scopes??["read"],o=i.scopes;if(o){let P=r.filter((I)=>!o.includes(I));if(P.length>0)throw e.validation(`Scope ${P.map((I)=>`"${I}"`).join(", ")} is not in admin.auth.apiKeys.scopes`)}let a=`${un}${we()}`,d=n.expiresIn??i.expiresIn,h=d?new Date(Date.now()+w(d)):null,_=await y(async(P,I)=>{let L=await B(P,I,V,{id:b("key"),name:n.name,user_id:n.userId??null,key_hash:await re(a),prefix:a.slice(0,un.length+6),scopes:r,last_used_at:null,expires_at:h,revoked_at:null,created_at:new Date});return await j(P,"apikey.created",{id:String(L.id),name:n.name,scopes:r,userId:n.userId??null},n.actor??null),Ee(L)});return c.deliver(),{..._,key:a}},async list(n={}){return(await y((r,o)=>o.query(r,{table:V,...n.userId?{where:t("user_id",n.userId)}:{},orderBy:[m("created_at","desc")]}))).map(Ee)},async revoke(n,i={}){let r=await y(async(o,a)=>{let d=await S(o,a,V,s(t("id",n),t("revoked_at",null)),{revoked_at:new Date});if(d>0)await j(o,"apikey.revoked",{id:n},i.actor??null);return d>0});return c.deliver(),r},async verify(n){return y(async(i,r)=>{let o=await O(i,r,V,t("key_hash",await re(n)));if(!o)return null;if(o.revoked_at!==null)return null;if(o.expires_at!==null&&ye(o.expires_at))return null;let a=J(o.scopes),d=Mt(a),h=[],_=d,P=String(o.id);if(o.user_id!==null&&o.user_id!==void 0){let I=await D(i,r,String(o.user_id));if(!I||I.status!=="active")return null;let L=await Z(I);h=L.roles,_=Et(L.permissions,d),P=I.id}return await S(i,r,V,t("id",String(o.id)),{last_used_at:new Date}),{id:P,source:"api-key",roles:h,permissions:_}})}},roles:{async list(){let n=Object.entries(f.rbac?.roles??{}).map(([a,d])=>({name:a,label:null,permissions:d,source:"config"}));if(f.rbac?.customRoles!==!0)return n;let i=await y((a,d)=>d.query(a,{table:N,orderBy:[m("name")]})),r=new Set(n.map((a)=>a.name)),o=i.filter((a)=>!r.has(String(a.name))).map((a)=>({name:String(a.name),label:a.label??null,permissions:J(a.permissions),source:"database"}));return[...n,...o].sort((a,d)=>a.name.localeCompare(d.name))},async create(n){if(de(),Le(n.name),f.rbac?.roles[n.name])throw e.conflict(`"${n.name}" is defined in the configuration; change it there`);let i=await y(async(r,o)=>{if(await O(r,o,N,t("name",n.name)))throw e.conflict(`The role "${n.name}" already exists`);return B(r,o,N,{id:b("rol"),name:n.name,label:n.label??null,permissions:n.permissions,created_at:new Date,updated_at:new Date})});return v=void 0,{name:String(i.name),label:i.label??null,permissions:J(i.permissions),source:"database"}},async update(n,i){de();let r=await y(async(o,a)=>{let d={updated_at:new Date};if(i.permissions)d.permissions=i.permissions;if(i.label!==void 0)d.label=i.label;if(await S(o,a,N,t("name",n),d)===0)throw e.notFound(`No role named "${n}"`);return O(o,a,N,t("name",n))});return v=void 0,{name:n,label:r?.label??null,permissions:J(r?.permissions),source:"database"}},async remove(n){de();let i=await y((r,o)=>F(r,o,N,t("name",n)));return v=void 0,i>0},async permissionsFor(n){return rt(n,await le())}}};function de(){if(f.rbac?.customRoles!==!0)throw new e("Roles are defined in the configuration; set rbac.customRoles to manage them in the panel",{status:400,code:"custom_roles_disabled"})}function Le(n){if(!He.test(n))throw e.validation(`"${n}" is not a role name; use lowercase letters, digits, dashes and underscores`)}return Q}function W(c){return{id:String(c.id),email:String(c.email),name:c.name??null,status:c.status??"active",roles:J(c.roles),avatarUrl:c.avatar_url??null,locale:c.locale??null,twoFactorEnabled:c.two_factor_enabled===!0,lastLoginAt:l(c.last_login_at),createdAt:l(c.created_at)??"",updatedAt:l(c.updated_at)??""}}function ge(c){return{id:String(c.id),userId:String(c.user_id),expiresAt:l(c.expires_at)??"",createdAt:l(c.created_at)??"",ip:c.ip??null,userAgent:c.user_agent??null}}function Ee(c){return{id:String(c.id),name:String(c.name),prefix:String(c.prefix),userId:c.user_id??null,scopes:J(c.scopes),lastUsedAt:l(c.last_used_at),expiresAt:l(c.expires_at),revokedAt:l(c.revoked_at),createdAt:l(c.created_at)??""}}function J(c){if(!Array.isArray(c))return[];return c.filter((f)=>typeof f==="string")}function ye(c){if(c===null||c===void 0)return!1;return(c instanceof Date?c:new Date(String(c))).getTime()<=Date.now()}function ce(c){let f=c.trim().toLowerCase();if(!Ke.test(f))throw e.validation(`"${c}" is not an email address`);return f}function Fe(c,f){if(c.length!==f.length)return!1;let p=[...f].sort();return[...c].sort().every((g,k)=>g===p[k])}function We(c,f){let p=c.headers.get("cookie");if(!p)return null;for(let g of p.split(";")){let k=g.indexOf("=");if(k===-1)continue;if(g.slice(0,k).trim()!==f)continue;return decodeURIComponent(g.slice(k+1).trim())}return null}function ue(c){return globalThis.process?.env?.[c]}
2
+ export{Un,jt,Jn,Wn,nr,dn,be,un,pn};
@@ -0,0 +1,40 @@
1
+ import{s,R,Hn,t}from"./index-q6jkbsjp.js";import{E}from"./index-pkxgzw6e.js";import{me,_,j}from"./index-2j6d79fk.js";import{so,ue}from"./index-rb9fxtyn.js";import{Qe}from"./index-n09v6gx3.js";import{d}from"./index-9g2n7xa6.js";import{Te,Q}from"./index-twqpdtfh.js";import{p}from"./index-qkqtxr65.js";import{be}from"./index-5j0cqm43.js";import{w,se}from"./index-rw0hze6x.js";import{e,n}from"./index-x72d1hzh.js";import{z as Mt}from"zod";function Ze(l){if(!l)throw e.unauthorized("This endpoint needs a session or an API key.");return l}function g(l,u,b){let m=Ze(u);if(!l.rbac)return m;if(!E(m,b))throw e.forbidden(`This action needs "${b}".`);return m}function Ye(l){let u=Ze(l);if(u.source!=="session")throw e.forbidden("This endpoint needs a signed-in user; an API key cannot issue credentials.");return u}function y(l){return l.headers.get("x-heyo-cms-client")==="admin"?"admin":"rest"}var Ot=new Set(["where","sort","limit","offset","cursor","count","depth","locale","fallbackLocale","includeTrashed","permanent","refresh","force","richText"]),Pt={id:"id",createdAt:"created_at",created_at:"created_at",updatedAt:"updated_at",updated_at:"updated_at",updatedBy:"updated_by",updated_by:"updated_by",deletedAt:"deleted_at",deleted_at:"deleted_at",purgeAfter:"purge_after",purge_after:"purge_after",scheduledAt:"scheduled_at",scheduled_at:"scheduled_at"},St=new Set(["created_at","updated_at","deleted_at","purge_after","scheduled_at"]),he=new Set(["eq","ne","gt","gte","lt","lte","in","nin","like","ilike","contains","exists"]),Pe=100;function I(l,u){let b=l.get("locale"),m=l.get("fallbackLocale"),k=P(l.get("depth"),"depth",0,10);return{actor:u.actor,...u.source===void 0?{}:{source:u.source},...b===null?{}:{locale:b},...m===null?{}:{fallbackLocale:m==="false"?!1:m},...k===void 0?{}:{depth:k},...G(l.get("includeTrashed"),"includeTrashed")===!0?{includeTrashed:!0}:{}}}function Pn(l,u,b){let m=I(l,b),k=m.locale??null,A=oo(l,u,k),J=ro(l.get("sort"),u,k),C=P(l.get("limit"),"limit",1,500),T=P(l.get("offset"),"offset",0,Number.MAX_SAFE_INTEGER),S=l.get("cursor");return{...m,...A===void 0?{}:{where:A},...J===void 0?{}:{orderBy:J},...C===void 0?{}:{limit:C},...T===void 0?{}:{offset:T},...S===null?{}:{cursor:S},...G(l.get("count"),"count")===!0?{count:!0}:{}}}function oo(l,u,b){let m=[],k=l.get("where");if(k!==null&&k.trim().length>0)m.push($t(k,u,b));for(let[A,J]of l.entries()){if(Ot.has(A))continue;let C=/^([A-Za-z_][A-Za-z0-9_.]*)(?:\[([a-z]+)\])?$/.exec(A);if(!C)throw e.validation(`"${A}" is not a filter; see the OpenAPI document for the query syntax.`);let[,T="",S="eq"]=C;if(!he.has(S))throw e.validation(`"${S}" is not a comparison; use one of ${[...he].join(", ")}.`);let O=fe(T,u,b),M=S;m.push({kind:"compare",path:O.path,operator:M,value:M==="in"||M==="nin"?J.split(",").map((D)=>Ce(D,O)):Ce(J,O)})}if(m.length===0)return;return m.length===1?m[0]:s(...m)}function $t(l,u,b){let m;try{m=JSON.parse(l)}catch{throw e.validation('"where" has to be a JSON filter.')}let k=0,A=(J)=>{if(k+=1,k>Pe)throw e.validation(`"where" has more than ${Pe} conditions.`);if(typeof J!=="object"||J===null||Array.isArray(J))throw e.validation('every node of "where" has to be an object.');let C=J;switch(C.kind){case"and":case"or":{let T=C.conditions;if(!Array.isArray(T))throw e.validation(`"${C.kind}" needs a "conditions" array.`);let S=T.map(A);return C.kind==="and"?s(...S):R(...S)}case"not":return Hn(A(C.condition));case"compare":{let{path:T,operator:S}=C;if(typeof T!=="string")throw e.validation('"compare" needs a "path".');if(typeof S!=="string"||!he.has(S))throw e.validation(`"${String(S)}" is not a comparison.`);let O=fe(T,u,b);return{kind:"compare",path:O.path,operator:S,value:$e(C.value,O)}}default:throw e.validation(`"${String(C.kind)}" is not a filter node.`)}};return A(m)}function ro(l,u,b){if(l===null||l.trim().length===0)return;let m=[];for(let k of l.split(",")){let A=k.trim();if(A.length===0)continue;let J=A.startsWith("-"),C=J?A.slice(1):A;m.push({path:fe(C,u,b).path,direction:J?"desc":"asc"})}return m.length===0?void 0:m}function fe(l,u,b){let[m="",...k]=l.split("."),A=Pt[m];if(A!==void 0&&k.length===0)return{path:A,type:St.has(A)?"timestamp":"text"};let J=u.fields[m];if(!J)throw e.validation(`"${l}" is not a field of ${u.name}, and not a system column.`);if(k.length>2)throw e.validation(`"${l}" is nested too deeply to filter.`);let C=J.localized&&k.length===0?[b??""]:k;return{path:[m,...C.filter((S)=>S.length>0)].join("."),type:It(J.column.type)}}function It(l){switch(l){case"integer":case"bigint":case"real":return"number";case"boolean":return"boolean";case"timestamp":return"timestamp";case"json":return"json";default:return"text"}}function Ce(l,u){if(l==="null")return null;switch(u.type){case"number":{let b=Number(l);if(!Number.isFinite(b))throw e.validation(`"${l}" is not a number for ${u.path}.`);return b}case"boolean":{if(l==="true")return!0;if(l==="false")return!1;throw e.validation(`"${l}" is not a boolean for ${u.path}.`)}case"timestamp":return Ie(l,u.path);default:return l}}function $e(l,u){if(Array.isArray(l))return l.map((b)=>$e(b,u));if(u.type==="timestamp"&&typeof l==="string")return Ie(l,u.path);if(l===null||typeof l==="string"||typeof l==="number"||typeof l==="boolean")return l;throw e.validation(`${u.path} cannot be compared to an object.`)}function Ie(l,u){let b=new Date(l);if(Number.isNaN(b.getTime()))throw e.validation(`"${l}" is not a date for ${u}.`);return b}function P(l,u,b,m){if(l===null||l.trim().length===0)return;let k=Number(l);if(!Number.isInteger(k)||k<b||k>m)throw e.validation(`"${u}" has to be an integer between ${b} and ${m}.`);return k}function G(l,u){if(l===null)return;if(l===""||l==="true"||l==="1")return!0;if(l==="false"||l==="0")return!1;throw e.validation(`"${u}" has to be true or false.`)}function ne(l){if(l===null||l==="")return;if(l==="html"||l==="markdown"||l==="text")return l;throw e.validation(`"${l}" is not a rich-text format; use html, markdown or text.`)}function ae(l,u){for(let[b,m]of Object.entries(u.collection.fields)){if(m.type!=="richText")continue;if(!(b in l))continue;let k=l[b];if(k===null||typeof k!=="object")continue;l[b]=Q(k,{format:u.format,mediaUrl:u.mediaUrl,...u.mediaImage===void 0?{}:{mediaImage:u.mediaImage,images:{loading:"lazy",decoding:"async"}}})}return l}function Ee(l,u){for(let b of l)ae(b,u)}function ye(l){return(u)=>`${l}/media/${u}/original`}async function ie(l,u,b,m){if(!l._config.uploads)return;let k=new Set;for(let J of u)for(let[C,T]of Object.entries(b.fields)){if(T.type!=="richText")continue;let S=J[C];if(S===null||typeof S!=="object")continue;for(let O of Qe(S))k.add(O)}if(k.size===0)return;let A=await l.media.getMany([...k]);return(J)=>{let C=A.get(J);return C?l.media.image(C,m===null?{}:{locale:m}):null}}var Et="application/json; charset=utf-8";function i(l,u=200,b){return new Response(JSON.stringify(l??null),{status:u,headers:{"content-type":Et,...b}})}function _n(l,u=200){return new Response(l,{status:u,headers:{"content-type":"text/plain; charset=utf-8"}})}function oe(l){return new Response(null,{status:204,...l?{headers:l}:{}})}function K(){return oe({"x-heyo-cms-stopped":"before-hook"})}function Tt(l){if(l instanceof e){let u={error:{message:l.message,code:l.code,status:l.status}};if(l.details!==void 0)u.error.details=l.details;return i(u,l.status)}return console.error("[heyo-cms] unhandled error while serving a request",l),i({error:{message:"Internal server error.",code:"internal_error",status:500}},500)}async function en(l){let u=await l.text();if(u.trim().length===0)return{};try{return JSON.parse(u)}catch{throw new e("The request body is not valid JSON.",{status:400,code:"invalid_body"})}}async function f(l){let u=await en(l);if(typeof u!=="object"||u===null||Array.isArray(u))throw new e("The request body has to be a JSON object.",{status:400,code:"invalid_body"});return u}function De(l,u="data"){let b=l[u];if(b===void 0)throw e.validation(`The request body needs a "${u}" object.`);if(typeof b!=="object"||b===null||Array.isArray(b))throw e.validation(`"${u}" has to be a JSON object.`);return b}function v(l,u){let b=l[u];if(typeof b!=="string"||b.trim().length===0)throw e.validation(`"${u}" is required.`);return b}function W(l,u){let b=l[u];if(b===void 0||b===null)return;if(typeof b!=="string")throw e.validation(`"${u}" has to be a string.`);return b}function Re(l,u){let b=l[u];if(b===void 0||b===null)return;if(!(Array.isArray(b)&&b.every((k)=>typeof k==="string")))throw e.validation(`"${u}" has to be an array of strings.`);return b}function o(l){return{$ref:`#/components/schemas/${l}`}}function c(l,u){return{name:l,in:"path",required:!0,description:u,schema:{type:"string"}}}function a(l,u,b,m={}){return{name:l,in:"query",required:!1,description:b,schema:{type:u,...m}}}var q=[a("locale","string","Language to resolve the document in. Defaults to i18n.defaultLocale."),a("fallbackLocale","string",'Where missing values come from for this call; "false" turns substitution off. Defaults to i18n.defaultLocale.')],re=a("depth","integer","Relation levels to resolve. Each level is one batched query.",{minimum:0,maximum:10});function $n(l){return[...q,re,a("where","string",`JSON filter AST over ${l}, as produced by the query helpers.`),a("sort","string",'Comma-separated fields, "-" for descending. Defaults to -updatedAt.'),a("limit","integer","Page size. Defaults to 25, capped at 500.",{minimum:1,maximum:500}),a("offset","integer","Rows to skip. Prefer `cursor`."),a("cursor","string","Keyset cursor from the previous page."),a("count","boolean","Runs a second COUNT query and fills totalDocs."),a("includeTrashed","boolean","Includes soft-deleted documents."),{name:"<field>",in:"query",required:!1,description:"Shorthand filter: ?status=draft, ?views[gte]=10, ?tags[in]=news,guide. Any field of the collection or system column (id, createdAt, updatedAt, deletedAt, scheduledAt).",schema:{type:"string"}}]}function h(l,u=!0){return{required:u,content:{"application/json":{schema:l}}}}function r(l,u){return{description:u,content:{"application/json":{schema:l}}}}function N(l,u,b=200){return{[String(b)]:r(l,u),"204":{description:"A `before` hook stopped the operation (x-heyo-cms-stopped)."},"403":r(o("Error"),"Access control refused the operation."),"409":r(o("Error"),"`revision_conflict`: the document changed since the revision the caller sent in `If-Match`. `conflict`: a slug or a `unique` field's value belongs to another document (a trashed one included); `details.fields` names the field."),"422":r(o("Error"),"Validation failed.")}}function Ct(l,u){return{"200":r(l,u),"404":r(o("Error"),"No such document.")}}var we=a("richText","string","Renders rich-text fields as `html`, `markdown` or `text` instead of the ProseMirror document. Images resolve through the media id.",{enum:["html","markdown","text"]}),Je={name:"If-Match",in:"header",required:!1,description:"The `revision` the caller read. The write answers `409` if the document has changed since.",schema:{type:"integer"}};function Me(l,u){let m=l.headers.get("if-match")??u.revision??u.expectedRevision;if(m===void 0||m===null||m===""||m==="*")return;let k=Number(String(m).replace(/^W\/|"/g,""));if(!Number.isInteger(k)||k<1)throw e.validation(`If-Match / revision has to be a positive integer, received ${String(m)}.`);return k}function Fe(l){let{_config:u,api:b}=l,m=[],k=ye(u.basePath);for(let A of Object.values(u.collections)){let J=A.name,C=`/${A.slug}`,T=[A.slug],S=b[J];if(!S)continue;m.push({method:"GET",path:C,source:"core",handler:async(O)=>{let M=await S.find(Pn(O.query,A,{actor:O.actor,source:y(O.request)})),D=ne(O.query.get("richText"));if(D){let F=await ie(l,M.docs,A,O.query.get("locale"));Ee(M.docs,{collection:A,format:D,mediaUrl:k,...F===void 0?{}:{mediaImage:F}})}return i(M)},openapi:{summary:`List ${A.label}`,tags:T,parameters:[...$n(A.slug),we],responses:{"200":r({type:"object",properties:{docs:{type:"array",items:o(x(A))},limit:{type:"integer"},totalDocs:{type:["integer","null"]},hasNextPage:{type:"boolean"},cursor:{type:["string","null"]}}},"One page of documents.")}}},{method:"POST",path:C,source:"core",handler:async(O)=>{let M=await f(O.request),D=await S.create({...I(O.query,{actor:O.actor,source:y(O.request)}),data:De(M)});return D===null?K():i(D,201)},openapi:{summary:`Create a ${A.label}`,tags:T,parameters:q,requestBody:h({type:"object",required:["data"],properties:{data:o(Y(A))}}),responses:N(o(x(A)),"The created document.",201)}},{method:"GET",path:`${C}/:id`,source:"core",handler:async(O)=>{let M=await S.findById(Z(O.params.id),I(O.query,{actor:O.actor,source:y(O.request)}));if(!M)throw e.notFound(`No ${J} with id "${O.params.id}"`);let D=ne(O.query.get("richText"));if(D){let F=await ie(l,[M],A,O.query.get("locale"));ae(M,{collection:A,format:D,mediaUrl:k,...F===void 0?{}:{mediaImage:F}})}return i(M)},openapi:{summary:`Read one ${A.label}`,tags:T,parameters:[c("id","Document id."),...q,re,a("includeTrashed","boolean","Allows reading a document that is in the trash."),we],responses:Ct(o(x(A)),"The document.")}},{method:"PATCH",path:`${C}/:id`,source:"core",handler:async(O)=>{let M=await f(O.request),D=Me(O.request,M),F=await S.update({...I(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id),data:De(M),...D===void 0?{}:{expectedRevision:D}});return F===null?K():i(F)},openapi:{summary:`Update a ${A.label}`,description:"A patch: fields that are absent are left alone. A field the actor may not write is refused, never silently dropped.\n\nSend `If-Match` (or `revision` in the body) with the revision you read to make the write conditional: `409 revision_conflict` instead of overwriting somebody else's save.",tags:T,parameters:[c("id","Document id."),Je,...q],requestBody:h({type:"object",required:["data"],properties:{data:o(Y(A)),revision:{type:"integer",description:"Same as `If-Match`."}}}),responses:N(o(x(A)),"The document as it now stands.")}},{method:"DELETE",path:`${C}/:id`,source:"core",handler:async(O)=>{let M=G(O.query.get("permanent"),"permanent")===!0,D={...I(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id)},F=M?await S.purge(D):await S.delete(D);return F===null?K():i(F)},openapi:{summary:`Delete a ${A.label}`,description:u.trash.enabled?"Moves the document to the trash. `?permanent=true` removes the row instead, which needs the purge permission.":"Removes the row; this deployment has no trash.",tags:T,parameters:[c("id","Document id."),a("permanent","boolean","Skips the trash and removes the row.")],responses:N(o(x(A)),"The document as it was when it was removed.")}},{method:"POST",path:`${C}/:id/restore`,source:"core",handler:async(O)=>{let M=await S.restore({...I(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id)});return M===null?K():i(M)},openapi:{summary:`Restore a ${A.label} from the trash`,tags:T,parameters:[c("id","Document id."),...q],responses:{...N(o(x(A)),"The restored document."),"409":r(o("Error"),"The document is not in the trash.")}}},{method:"POST",path:`${C}/:id/schedule`,source:"core",handler:async(O)=>{let M=await f(O.request),D=await S.schedule({...I(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id),at:v(M,"at"),data:De(M)});return D===null?K():i(D)},openapi:{summary:`Schedule a change to a ${A.label}`,description:"A patch and a time. Checked now — access, field access, validation — and applied later as an ordinary update, so publishing on Friday is refused on Tuesday if the actor may not write the field.",tags:T,parameters:[c("id","Document id."),...q],requestBody:h({type:"object",required:["at","data"],properties:{at:{type:"string",format:"date-time"},data:o(Y(A))}}),responses:N(o(x(A)),"The document carrying the pending change.")}},{method:"DELETE",path:`${C}/:id/schedule`,source:"core",handler:async(O)=>{let M=await S.unschedule({...I(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id)});return M===null?K():i(M)},openapi:{summary:`Cancel the scheduled change on a ${A.label}`,tags:T,parameters:[c("id","Document id.")],responses:{...N(o(x(A)),"The document."),"409":r(o("Error"),"Nothing was scheduled.")}}}),m.push(...Jt(A,S))}return m}var ge={type:"array",items:{type:"object",properties:{slug:{type:"string"},field:{type:"string"},locale:{type:["string","null"]},createdAt:{type:["string","null"],format:"date-time"}}}};function Jt(l,u){let b=Object.entries(l.fields).filter(([,S])=>S.type==="slug");if(b.length===0)return[];let m=`/${l.slug}`,k=[l.slug],A=b.some(([,S])=>S.options.history===!0),J=a("field","string","The slug field; the first one by default."),C=[{method:"GET",path:`${m}/resolve-slug`,source:"core",handler:async(S)=>{let O=S.query.get("slug");if(!O)throw e.validation('"slug" is required.');let M=S.query.get("field"),D=await u.resolveSlug(O,{...I(S.query,{actor:S.actor,source:y(S.request)}),...M===null?{}:{field:M}});if(!D)throw e.notFound(`No ${l.name} has the slug "${O}".`);return i(D)},openapi:{summary:`Find a ${l.label} by its slug, now or before`,description:"`current: false` means the slug is a previous address of the document: redirect to its slug now. `locale` is where the slug matched.",tags:k,parameters:[{...a("slug","string","The slug from the URL."),required:!0},J,...q,re],responses:{"200":r({type:"object",properties:{id:{type:"string"},field:{type:"string"},locale:{type:["string","null"]},current:{type:"boolean"},document:o(x(l))}},"Where the slug leads."),"404":r(o("Error"),"No readable document has this slug.")}}}];if(!A)return C;let T=(S)=>{let O=S.query.get("locale"),M=S.query.get("field");return{actor:S.actor,source:y(S.request),id:Z(S.params.id),...O===null?{}:{locale:O},...M===null?{}:{field:M}}};return C.push({method:"GET",path:`${m}/:id/previous-slugs`,source:"core",handler:async(S)=>i(await u.previousSlugs(T(S))),openapi:{summary:`List a ${l.label}'s previous slugs`,tags:k,parameters:[c("id","Document id."),J,...q],responses:{"200":r(ge,"Newest first.")}}},{method:"POST",path:`${m}/:id/previous-slugs`,source:"core",handler:async(S)=>{let O=await f(S.request);return i(await u.addPreviousSlug({...T(S),slug:v(O,"slug"),...typeof O.field==="string"?{field:O.field}:{},...typeof O.locale==="string"?{locale:O.locale}:{}}),201)},openapi:{summary:`Make a slug lead to this ${l.label}`,description:"How two pages are merged: purge the old page, then add its slug here. 409 while the slug is another document's address (a trashed one included) or already leads elsewhere.",tags:k,parameters:[c("id","Document id."),...q],requestBody:h({type:"object",required:["slug"],properties:{slug:{type:"string"},field:{type:"string"},locale:{type:"string"}}}),responses:{"201":r(ge,"The previous slugs now."),"409":r(o("Error"),"The slug is taken.")}}},{method:"DELETE",path:`${m}/:id/previous-slugs`,source:"core",handler:async(S)=>{let O=S.query.get("slug");if(!O)throw e.validation('"slug" is required.');return i(await u.removePreviousSlug({...T(S),slug:O}))},openapi:{summary:`Stop a previous slug from leading to this ${l.label}`,tags:k,parameters:[c("id","Document id."),{...a("slug","string","The previous slug."),required:!0},J,...q],responses:{"200":r(ge,"The previous slugs now.")}}}),C}function xe(l){let{_config:u,globals:b}=l,m=[],k=ye(u.basePath);for(let A of Object.values(u.globals)){let J=b[A.name];if(!J)continue;let C=`/globals/${A.slug}`,T=["globals"];m.push({method:"GET",path:C,source:"core",handler:async(S)=>{let O=await J.get(I(S.query,{actor:S.actor,source:y(S.request)})),M=ne(S.query.get("richText"));if(M){let D=await ie(l,[O],A,S.query.get("locale"));ae(O,{collection:A,format:M,mediaUrl:k,...D===void 0?{}:{mediaImage:D}})}return i(O)},openapi:{summary:`Read ${A.label}`,description:"Never 404s and never writes: a global nobody has saved reads as its declared defaults with createdAt null.",tags:T,parameters:[...q,re,we],responses:{"200":r(o(x(A)),"The singleton.")}}},{method:"PATCH",path:C,source:"core",handler:async(S)=>{let O=await f(S.request),M=Me(S.request,O),D=await J.update({...I(S.query,{actor:S.actor,source:y(S.request)}),data:De(O),...M===void 0?{}:{expectedRevision:M}});return D===null?K():i(D)},openapi:{summary:`Update ${A.label}`,description:"Send `If-Match` (or `revision` in the body) with the revision you read to make the write conditional.",tags:T,parameters:[Je,...q],requestBody:h({type:"object",required:["data"],properties:{data:o(Y(A)),revision:{type:"integer",description:"Same as `If-Match`."}}}),responses:N(o(x(A)),"The singleton.")}},{method:"POST",path:`${C}/schedule`,source:"core",handler:async(S)=>{let O=await f(S.request),M=await J.schedule({...I(S.query,{actor:S.actor,source:y(S.request)}),at:v(O,"at"),data:De(O)});return M===null?K():i(M)},openapi:{summary:`Schedule a change to ${A.label}`,tags:T,requestBody:h({type:"object",required:["at","data"],properties:{at:{type:"string",format:"date-time"},data:o(Y(A))}}),responses:N(o(x(A)),"The singleton.")}},{method:"DELETE",path:`${C}/schedule`,source:"core",handler:async(S)=>{let O=await J.unschedule(I(S.query,{actor:S.actor,source:y(S.request)}));return O===null?K():i(O)},openapi:{summary:`Cancel the scheduled change to ${A.label}`,tags:T,responses:{...N(o(x(A)),"The singleton."),"409":r(o("Error"),"Nothing was scheduled.")}}})}return m}function x(l){return l.name}function Y(l){return`${l.name}Input`}function Z(l){if(l===void 0||l.length===0)throw e.validation("A document id is required.");return l}var Dt={id:{type:"string"},createdAt:{type:"string",format:"date-time"},updatedAt:{type:"string",format:"date-time"},updatedBy:{type:["string","null"]},deletedAt:{type:["string","null"],format:"date-time",description:"Set while the document is in the trash."},purgeAfter:{type:["string","null"],format:"date-time"},scheduledAt:{type:["string","null"],format:"date-time"},scheduledPatch:{type:["object","null"],description:"The field values a scheduled change will write."},_localeFields:{type:"array",items:{type:"string"},description:"Localized fields with a stored value (including null) in the requested locale, independent of defaults and fallback."},_fallback:{type:"object",additionalProperties:{type:"string"},description:"Which fields were answered from i18n.defaultLocale. Absent when the requested locale had every value."}};function jn(l,u){let b=l._config,m=Object.create(null);for(let k of u){let A=Wt(k.path),J=m[A]??{};J[k.method.toLowerCase()]=Ft(k),m[A]=J}return{openapi:"3.1.0",info:{title:`${b.admin.title??"Heyo CMS"} — content API`,version:Gt(m),description:["Generated from the Heyo CMS configuration. Every endpoint below is served by `cms.handler`.","","Filters accept two syntaxes: `?status=draft` / `?views[gte]=10` for the simple cases, and `?where=` carrying the JSON filter AST for composed ones.","A `204` on a write means a `before` hook stopped the operation; it is not an error."].join(`
2
+ `)},servers:[{url:b.basePath}],tags:Bt(l),security:[{bearerAuth:[]},{sessionCookie:[]}],components:{securitySchemes:{bearerAuth:{type:"http",scheme:"bearer",description:"A session token or an API key. The key prefix tells them apart, so one header serves both."},sessionCookie:{type:"apiKey",in:"cookie",name:be,description:"Set by the sign-in routes; what the panel uses."}},schemas:xt(l)},paths:m,"x-heyo-cms":{basePath:b.basePath,adminPath:`${b.basePath}${b.admin.path}`,collections:Object.values(b.collections).map((k)=>({name:k.name,slug:k.slug})),globals:Object.values(b.globals).map((k)=>({name:k.name,slug:k.slug})),plugins:b.plugins.map((k)=>k.id)}}}function Ft(l){let u=l.openapi,b=[...u?.parameters??[]];for(let m of zt(l.path))if(!b.some((k)=>k.name===m))b.push({name:m,in:"path",required:!0,schema:{type:"string"}});return{operationId:Ht(l),summary:u?.summary??(l.source==="core"?`${l.method} ${l.path}`:`${l.method} ${l.path} (plugin: ${l.source})`),...u?.description===void 0?{}:{description:u.description},tags:u?.tags??[l.source==="core"?"other":l.source],...b.length===0?{}:{parameters:b},...u?.requestBody===void 0?{}:{requestBody:u.requestBody},responses:{...u?.responses??{"200":{description:"Success."}},"400":{description:"The request could not be read.",content:{"application/json":{schema:Ne()}}},"401":{description:"No session, no API key, or a spent credential.",content:{"application/json":{schema:Ne()}}}},...u?.security===!1?{security:[]}:{},...l.source==="core"?{}:{"x-heyo-cms-plugin":l.source}}}function Ne(){return{$ref:"#/components/schemas/Error"}}function xt(l){let u=l._config,b={Error:{type:"object",required:["error"],properties:{error:{type:"object",required:["message","code","status"],properties:{message:{type:"string"},code:{type:"string",description:"Machine-readable: not_found, forbidden, validation_failed, conflict, invalid_cursor, …"},status:{type:"integer"},details:{}}}}},Actor:{type:"object",properties:{id:{type:"string"},source:{type:"string",enum:["session","api-key"]},roles:{type:"array",items:{type:"string"}},permissions:{type:"array",items:{type:"string"},description:"Flattened `collection:action` grants."}}},User:{type:"object",properties:{id:{type:"string"},email:{type:"string",format:"email"},name:{type:["string","null"]},status:{type:"string",enum:["active","invited","disabled"]},roles:{type:"array",items:{type:"string"}},avatarUrl:{type:["string","null"]},locale:{type:["string","null"]},twoFactorEnabled:{type:"boolean"},lastLoginAt:{type:["string","null"],format:"date-time"},createdAt:{type:"string",format:"date-time"},updatedAt:{type:"string",format:"date-time"}}},Invite:{type:"object",properties:{token:{type:"string",description:"Single-use. The CMS does not send email; you deliver it."},expiresAt:{type:"string",format:"date-time"},email:{type:"string",format:"email"},userId:{type:"string"},user:{$ref:"#/components/schemas/User"}}},Role:{type:"object",properties:{name:{type:"string"},label:{type:["string","null"]},permissions:{type:"array",items:{type:"string"}},source:{type:"string",enum:["config","database"],description:"`config` roles are code and cannot be edited here."}}},ApiKey:{type:"object",properties:{id:{type:"string"},name:{type:"string"},prefix:{type:"string"},userId:{type:["string","null"]},scopes:{type:"array",items:{type:"string"}},lastUsedAt:{type:["string","null"],format:"date-time"},expiresAt:{type:["string","null"],format:"date-time"},revokedAt:{type:["string","null"],format:"date-time"},createdAt:{type:"string",format:"date-time"}}},IssuedApiKey:{allOf:[{$ref:"#/components/schemas/ApiKey"},{type:"object",properties:{key:{type:"string",description:"Shown once, never again."}}}]},SignInResult:{oneOf:[{type:"object",required:["status","token"],properties:{status:{type:"string",const:"signed-in"},token:{type:"string"},expiresAt:{type:"string",format:"date-time"},user:{$ref:"#/components/schemas/User"},actor:{$ref:"#/components/schemas/Actor"}}},{type:"object",required:["status","challenge"],properties:{status:{type:"string",const:"two-factor-required"},challenge:{type:"string"},expiresAt:{type:"string",format:"date-time"}},description:"A correct password on an account with a second factor. Worth nothing without a code."}]},Locale:{type:"object",properties:{code:{type:"string"},name:{type:"string",description:'The English name: "Polish".'},rtl:{type:"boolean"},isDefault:{type:"boolean"}}},ActivityEntry:{type:"object",properties:{id:{type:"string"},action:{type:"string"},actorId:{type:["string","null"]},actorSource:{type:["string","null"]},collection:{type:["string","null"]},documentId:{type:["string","null"]},locale:{type:["string","null"]},changes:{type:["object","null"]},ip:{type:["string","null"]},createdAt:{type:["string","null"],format:"date-time"}}},AiProvider:{type:"object",description:"Whether a provider has credentials. Never the credentials.",properties:{provider:{type:"string"},configured:{type:"boolean"},source:{type:["string","null"],enum:["config","database",null]},type:{type:["string","null"]},hint:{type:["string","null"],description:"Last four characters of a stored key."},updatedAt:{type:["string","null"],format:"date-time"}}},AiUsage:{type:"object",properties:{input:{type:"integer"},output:{type:"integer"},total:{type:"integer"},cost:{type:"number",description:"US dollars, as priced by the catalogue."}}},AiResult:{type:"object",description:"A suggestion. The document is unchanged until somebody saves it.",properties:{collection:{type:"string"},field:{type:"string"},action:{type:"string"},value:{description:"Shaped for the field: a string for text, a ProseMirror document for richText, an array for multiselect."},preview:{type:"string",description:"The same thing as text."},model:{type:"object"},usage:{$ref:"#/components/schemas/AiUsage"}}},AiAssistantEvent:{type:"object",description:"One server-sent event from `POST /ai/assistant`. The panel is written against this union; pi's own event vocabulary is never exposed.",properties:{type:{type:"string",enum:["text-delta","tool-start","tool-end","mutation-applied","approval-required","done","error"]},text:{type:"string",description:"`text-delta` only."},id:{type:"string",description:"Tool call id."},label:{type:"string",description:"`tool-start` only."},summary:{type:"string",description:"`tool-end` only. Written by the server from the tool name, never the tool's raw result."},runId:{type:"string"},target:{$ref:"#/components/schemas/AiMutationTarget"},fields:{type:"array",items:{type:"string"}},plan:{$ref:"#/components/schemas/AiPlan"},usage:{$ref:"#/components/schemas/AiUsage"},code:{type:"string",description:"`error` only."},message:{type:"string",description:"`error` only."}}},AiMutationTarget:{type:"object",properties:{kind:{type:"string",enum:["document","global","media"]},collection:{type:"string"},collectionSlug:{type:"string"},id:{type:"string"},locale:{type:["string","null"]},revision:{type:"integer"}}},AiPlan:{type:"object",description:"A change waiting for a human. Field names and labels only: an approval dialog nobody reads is the failure this step exists to prevent.",properties:{id:{type:"string"},runId:{type:"string"},summary:{type:"string"},expiresAt:{type:"string",format:"date-time"},operations:{type:"array",items:{type:"object",properties:{action:{type:"string"},target:{$ref:"#/components/schemas/AiMutationTarget"},label:{type:"string"},fields:{type:"array",items:{type:"string"}}}}}}},Webhook:{type:"object",properties:{id:{type:"string"},url:{type:"string",format:"uri"},events:{type:"array",items:{type:"string"}},filters:{type:["object","null"]},headers:{type:["object","null"]},hasSecret:{type:"boolean",description:"Whether this subscription signs with its own secret."},active:{type:"boolean"},createdAt:{type:["string","null"],format:"date-time"},updatedAt:{type:["string","null"],format:"date-time"}}},WebhookDelivery:{type:"object",properties:{id:{type:"string"},webhookId:{type:"string"},eventId:{type:"string"},eventType:{type:"string"},status:{type:"string",enum:["pending","delivered","failed"]},attempt:{type:"integer"},responseStatus:{type:["integer","null"]},responseBody:{type:["string","null"]},error:{type:["string","null"]},nextAttemptAt:{type:["string","null"],format:"date-time"},deliveredAt:{type:["string","null"],format:"date-time"},createdAt:{type:["string","null"],format:"date-time"},requestBody:{type:["object","null"],description:"The exact body that was signed. Only when asked for."}}},TrashedEntry:{type:"object",properties:{collection:{type:"string"},id:{type:"string"},deletedAt:{type:["string","null"],format:"date-time"},purgeAfter:{type:["string","null"],format:"date-time"},document:{type:"object"}}},JobRunReport:{type:"object",properties:{ranAt:{type:"string",format:"date-time"},processed:{type:"integer"},failed:{type:"integer"},results:{type:"array",items:{type:"object",properties:{job:{type:"string"},processed:{type:"integer"},failed:{type:"integer"},more:{type:"boolean",description:"More was waiting than `limit` allowed."}}}}}},JobFailure:{type:"object",properties:{id:{type:"string"},type:{type:"string"},payload:{type:["object","null"]},error:{type:["string","null"]},attempts:{type:"integer"},createdAt:{type:["string","null"],format:"date-time"}}}};if(u.uploads)b.Media={type:"object",properties:{id:{type:"string"},filename:{type:"string"},mimeType:{type:"string"},size:{type:"integer"},width:{type:["integer","null"]},height:{type:["integer","null"]},storageKey:{type:"string"},checksum:{type:["string","null"]},alt:{type:"object",additionalProperties:{type:"string"}},focalPoint:{type:["object","null"]},crop:{type:["object","null"]},status:{type:"string",enum:["active","orphaned","purging"]},orphanedAt:{type:["string","null"],format:"date-time"},purgeAfter:{type:["string","null"],format:"date-time",description:"When the collector will delete it. `null` means never."},purgeError:{type:["string","null"]},metadata:{type:"object"},variants:{type:"array",items:{type:"string"}},createdBy:{type:["string","null"]},createdAt:{type:["string","null"],format:"date-time"},updatedAt:{type:["string","null"],format:"date-time"},url:{type:"string"}}},b.MediaUsage={type:"object",properties:{mediaId:{type:"string"},collection:{type:"string"},documentId:{type:"string"},field:{type:"string"},locale:{type:"string"},createdAt:{type:["string","null"],format:"date-time"}}};for(let m of[...Object.values(u.collections),...Object.values(u.globals)])b[x(m)]=Nt(m),b[Y(m)]=_t(m);return b}function Nt(l){let u={};for(let[b,m]of Object.entries(l.fields))u[b]=_e(m);return{type:"object",title:l.label,...l.kind==="global"?{description:"A singleton. The timestamps are null until it is first saved."}:{},properties:{...u,...Dt}}}function _t(l){let u={};for(let[b,m]of Object.entries(l.fields))u[b]=_e(m);return{type:"object",title:`${l.label} (input)`,description:"A patch. Absent fields are left alone; a field the actor may not write is refused rather than dropped.",properties:u}}function _e(l){let u;try{let m=Mt.toJSONSchema(l.schema,{io:"input",unrepresentable:"any"});delete m.$schema,u=Lt(m)}catch{u=Ut(l)}let b=l.options;return{...u,...b.label===void 0?{}:{title:b.label},...b.description===void 0?{}:{description:b.description},...l.defaultValue===void 0?{}:{default:l.defaultValue},...b.readOnly===!0?{readOnly:!0}:{},"x-heyo-cms-field":l.type,...l.localized?{"x-heyo-cms-localized":!0}:{},...l.required?{"x-heyo-cms-required":!0}:{},...l.relationTo===void 0?{}:{"x-heyo-cms-relation":l.relationTo}}}function Lt(l){let u=l.anyOf;if(!Array.isArray(u)||u.length!==2)return l;let b=u.find((T)=>typeof T==="object"&&T!==null&&T.type==="null"),m=u.find((T)=>T!==b);if(!b||typeof m!=="object"||m===null)return l;let k=m,A=k.type,{anyOf:J,...C}=l;return{...C,...k,...typeof A==="string"?{type:[A,"null"]}:{}}}function Ut(l){switch(l.column.type){case"integer":case"bigint":return{type:"integer"};case"real":return{type:"number"};case"boolean":return{type:"boolean"};case"timestamp":return{type:"string",format:"date-time"};case"json":return{};default:return{type:"string"}}}function Bt(l){let u=l._config,b=[...Object.values(u.collections).map((m)=>({name:m.slug,description:`${m.label}: CRUD, the trash and scheduled changes.`})),...Object.keys(u.globals).length===0?[]:[{name:"globals",description:"Singletons."}],...u.uploads?[{name:"media",description:"The library, the variants and the lifecycle."}]:[],{name:"auth",description:"Editor sign-in, second factor and API keys."},{name:"access",description:"Editors and roles."},{name:"i18n",description:"The language registry."},...u.trash.enabled?[{name:"trash",description:"Deleted documents."}]:[],{name:"activity",description:"The audit trail."},{name:"jobs",description:"The sweeps."},{name:"admin",description:"The panel."},{name:"system",description:"This document."}];for(let m of u.plugins)if(m.routes?.length)b.push({name:m.id,description:`Routes from the ${m.id} plugin.`});return b}function Wt(l){return l.split("/").map((u)=>u.startsWith(":")?`{${u.slice(1)}}`:u==="*"?"{path}":u).join("/")}function zt(l){return l.split("/").filter((u)=>u.startsWith(":")||u==="*").map((u)=>u==="*"?"path":u.slice(1))}function Ht(l){let u=l.path.split("/").filter((b)=>b.length>0).map((b)=>b.startsWith(":")?`by-${b.slice(1)}`:b==="*"?"asset":b);return[l.method.toLowerCase(),...u].join("-").replaceAll(/[^a-zA-Z0-9-]/g,"-")}function Gt(l){let u=JSON.stringify(Object.keys(l).sort()),b=2166136261;for(let m=0;m<u.length;m+=1)b^=u.charCodeAt(m),b=Math.imul(b,16777619)>>>0;return`0.0.0+${b.toString(16).padStart(8,"0")}`}function In(l){let u=async(b)=>{let m=await l();return m.transaction((k)=>b(m,k))};return{query:(b)=>u((m,k)=>m.query(k,b)),mutate:(b)=>u((m,k)=>m.mutate(k,b)),find:(b,m)=>u((k,A)=>d(k,A).find(b,m)),findOne:(b,m)=>u((k,A)=>d(k,A).findOne(b,m)),insert:(b,m)=>u((k,A)=>d(k,A).insert(b,m)),update:(b,m,k)=>u((A,J)=>d(A,J).update(b,m,k)),delete:(b,m)=>u((k,A)=>d(k,A).delete(b,m))}}class Se{compiled=[];signatures=new Map;add(l){let u=Kt(l.path,l.source),b=u.at(-1)?.kind==="wildcard",m=`${l.method} ${u.map((A)=>A.kind==="static"?A.value:`{${A.kind}}`).join("/")}`,k=this.signatures.get(m);if(k)throw new n(l.source==="core"?"routes":`plugins.${l.source}.routes`,`"${l.method} ${l.path}" collides with a route already registered by "${k}"`);this.signatures.set(m,l.source),this.compiled.push({...l,segments:u,wildcard:b,specificity:u.filter((A)=>A.kind==="static").length}),this.compiled.sort((A,J)=>Number(A.wildcard)-Number(J.wildcard)||J.specificity-A.specificity||J.segments.length-A.segments.length)}list(){return this.compiled.map(({segments:l,wildcard:u,specificity:b,...m})=>m)}routes(l){return this.compiled.map((u)=>({method:u.method,path:u.path==="/"?l||"/":`${l}${u.path}`,source:u.source,middleware:u.middleware?.length??0}))}match(l,u){let b=Le(u),m=new Set,k=l==="HEAD"?"GET":l;for(let A of this.compiled){let J=Vt(A,b);if(!J)continue;if(A.method===k)return{kind:"found",route:A,params:J};m.add(A.method)}if(m.size>0)return{kind:"method-not-allowed",allowed:[...m].sort()};return{kind:"not-found"}}}function Vt(l,u){if(l.wildcard){if(u.length<l.segments.length-1)return null}else if(u.length!==l.segments.length)return null;let b={};for(let[m,k]of l.segments.entries()){if(k.kind==="wildcard")return b["*"]=u.slice(m).join("/"),b;let A=u[m];if(A===void 0)return null;if(k.kind==="static"){if(k.value!==A)return null;continue}if(A.length===0)return null;b[k.name]=Qt(A)}return b}function Kt(l,u){if(!l.startsWith("/"))throw new n(u==="core"?"routes":`plugins.${u}.routes`,`route path "${l}" must start with "/"`);let b=Le(l);return b.map((m,k)=>{if(m==="*"){if(k!==b.length-1)throw new n(u==="core"?"routes":`plugins.${u}.routes`,`route path "${l}" may only use "*" as its last segment`);return{kind:"wildcard"}}if(m.startsWith(":")){let A=m.slice(1);if(A.length===0)throw new n(u==="core"?"routes":`plugins.${u}.routes`,`route path "${l}" has an unnamed parameter`);return{kind:"param",name:A}}return{kind:"static",value:m}})}function Le(l){return l.split("/").filter((u)=>u.length>0)}function Qt(l){try{return decodeURIComponent(l)}catch{return l}}function Be(l){let u=l._config;if(!u.admin.auth)return[];let b=["access"],m=[{method:"GET",path:"/users",source:"core",handler:async(k)=>{g(u,k.actor,"users:read");let A=k.query.get("status"),J=k.query.get("search"),C=P(k.query.get("limit"),"limit",1,200),T=P(k.query.get("offset"),"offset",0,1e5),[S,O]=await Promise.all([l.auth.users.list({...A===null?{}:{status:Ue(A)},...J===null?{}:{search:J},...C===void 0?{}:{limit:C},...T===void 0?{}:{offset:T}}),l.auth.users.count()]);return i({users:S,totalUsers:O})},openapi:{summary:"List editors",tags:b,parameters:[a("status","string","active, invited or disabled.",{enum:["active","invited","disabled"]}),a("search","string","Matches the email or the name."),a("limit","integer","Page size. Defaults to 50."),a("offset","integer","Rows to skip.")],responses:{"200":r({type:"object",properties:{users:{type:"array",items:o("User")},totalUsers:{type:"integer"}}},"The editors of this CMS.")}}},{method:"GET",path:"/users/:id",source:"core",handler:async(k)=>{g(u,k.actor,"users:read");let A=await l.auth.users.get(k.params.id);if(!A)throw e.notFound("No such user.");return i({user:A})},openapi:{summary:"Read one editor",tags:b,parameters:[c("id","User id.")],responses:{"200":r({type:"object",properties:{user:o("User")}},"The user."),"404":r(o("Error"),"No such user.")}}},{method:"PATCH",path:"/users/:id",source:"core",handler:async(k)=>{let A=g(u,k.actor,"users:update"),J=await f(k.request),C=Re(J,"roles"),T=W(J,"name"),S=W(J,"locale"),O=W(J,"status"),M=await l.auth.users.update(k.params.id,{actor:A,...T===void 0?{}:{name:T},...C===void 0?{}:{roles:C},...S===void 0?{}:{locale:S},...O===void 0?{}:{status:Ue(O)}});return i({user:M})},openapi:{summary:"Update an editor",description:"A role change emits `role.changed` and takes effect on the user's next request, because permissions are resolved per request rather than frozen into the session.",tags:b,parameters:[c("id","User id.")],requestBody:h({type:"object",properties:{name:{type:["string","null"]},roles:{type:"array",items:{type:"string"}},status:{type:"string",enum:["active","invited","disabled"]},locale:{type:["string","null"]}}}),responses:{"200":r({type:"object",properties:{user:o("User")}},"The user.")}}},{method:"POST",path:"/users/invite",source:"core",handler:async(k)=>{let A=g(u,k.actor,"users:invite"),J=await f(k.request),C=Re(J,"roles"),T=W(J,"name"),S=await l.auth.users.invite({email:v(J,"email"),actor:A,...C===void 0?{}:{roles:C},...T===void 0?{}:{name:T}});return i(S,201)},openapi:{summary:"Invite an editor",description:"Creates the account in `invited` state and returns the single-use token, because the CMS does not send email. `user.invited` carries who was invited and never the token.",tags:b,requestBody:h({type:"object",required:["email"],properties:{email:{type:"string",format:"email"},name:{type:"string"},roles:{type:"array",items:{type:"string"}}}}),responses:{"201":r(o("Invite"),"The invitation and its token."),"409":r(o("Error"),"That address already has an account.")}}},{method:"DELETE",path:"/users/:id",source:"core",handler:async(k)=>{let A=g(u,k.actor,"users:delete"),J=k.params.id;if(J===A.id)throw e.conflict("You cannot remove your own account.");return await l.auth.users.remove(J,{actor:A}),oe()},openapi:{summary:"Remove an editor",description:"Their sessions, accounts, tokens and API keys go with them. Documents they wrote do not.",tags:b,parameters:[c("id","User id.")],responses:{"204":{description:"Removed."},"409":r(o("Error"),"You cannot remove your own account.")}}},{method:"GET",path:"/roles",source:"core",handler:async(k)=>{g(u,k.actor,"roles:read");let A=await l.auth.roles.list();return i({roles:A})},openapi:{summary:"List roles",description:"Both kinds: the ones declared in `rbac.roles`, which are code, and the ones created in the panel, which are rows. `source` says which is which.",tags:b,responses:{"200":r({type:"object",properties:{roles:{type:"array",items:o("Role")}}},"The roles.")}}}];if(u.rbac?.customRoles===!0)m.push({method:"POST",path:"/roles",source:"core",handler:async(k)=>{g(u,k.actor,"roles:create");let A=await f(k.request),J=Re(A,"permissions")??[],C=W(A,"label"),T=await l.auth.roles.create({name:v(A,"name"),permissions:J,...C===void 0?{}:{label:C}});return i({role:T},201)},openapi:{summary:"Create a role",tags:b,requestBody:h({type:"object",required:["name","permissions"],properties:{name:{type:"string"},label:{type:"string"},permissions:{type:"array",items:{type:"string"},description:"`collection:action`, with `*` allowed on either side."}}}),responses:{"201":r({type:"object",properties:{role:o("Role")}},"The role."),"409":r(o("Error"),"A role with that name exists, or the name is declared in the configuration.")}}},{method:"PATCH",path:"/roles/:name",source:"core",handler:async(k)=>{g(u,k.actor,"roles:update");let A=await f(k.request),J=Re(A,"permissions"),C=W(A,"label"),T=await l.auth.roles.update(k.params.name,{...J===void 0?{}:{permissions:J},...C===void 0?{}:{label:C}});return i({role:T})},openapi:{summary:"Update a role",tags:b,parameters:[c("name","Role name.")],requestBody:h({type:"object",properties:{label:{type:["string","null"]},permissions:{type:"array",items:{type:"string"}}}}),responses:{"200":r({type:"object",properties:{role:o("Role")}},"The role."),"404":r(o("Error"),"No such role, or it is declared in the configuration.")}}},{method:"DELETE",path:"/roles/:name",source:"core",handler:async(k)=>{if(g(u,k.actor,"roles:delete"),!await l.auth.roles.remove(k.params.name))throw e.notFound("No such role.");return oe()},openapi:{summary:"Delete a role",description:"A user still holding it keeps the name and loses the grants, because the safe reading of a grant that no longer exists is no grant.",tags:b,parameters:[c("name","Role name.")],responses:{"204":{description:"Removed."},"404":r(o("Error"),"No such role.")}}});return m}function Ue(l){if(l==="active"||l==="invited"||l==="disabled")return l;throw e.validation(`"${l}" is not a status; use active, invited or disabled.`)}function We(l){let u=l._config,b=u.admin.path===""?"/":u.admin.path,m=u.admin.assets;m?.configure?.({basePath:u.basePath,panelPath:`${u.basePath}${u.admin.path}`,title:u.admin.title??"Heyo CMS"});let k=async(A,J)=>{if(!m)return Xt(l);let C=await m.asset(A)??(Zt(A)?null:await m.asset(""));if(!C)return i({error:{message:"Not found.",code:"not_found",status:404}},404);let T=C.etag;if(T&&J.headers.get("if-none-match")===T)return new Response(null,{status:304,headers:{etag:T}});return new Response(C.body,{status:200,headers:{"content-type":C.contentType,"cache-control":C.cacheControl??(Yt(A)?"public, max-age=31536000, immutable":"no-cache"),...T?{etag:T}:{}}})};return[{method:"GET",path:b,source:"core",handler:(A)=>k("",A.request),openapi:{summary:"The admin panel",description:"The panel's HTML shell. Everything below it is the same single-page application.",tags:["admin"],security:!1,responses:{"200":{description:"HTML."}}}},{method:"GET",path:b==="/"?"/*":`${b}/*`,source:"core",handler:(A)=>k(A.params["*"]??"",A.request),openapi:{summary:"The admin panel's files and deep links",tags:["admin"],security:!1,parameters:[c("*","Asset path, or a panel route.")],responses:{"200":{description:"The asset, or the shell for a panel route."},"304":{description:"The cached copy is current."},"404":{description:"No such asset."}}}}]}function ze(l){return{method:"GET",path:"/openapi.json",source:"core",handler:()=>i(l()),openapi:{summary:"This document",description:"Generated from the configuration and the registered routes, including the ones plugins brought. It is the contract: a generated client, Postman and curl all read the same source.",tags:["system"],security:!1,responses:{"200":{description:"An OpenAPI 3.1 document."}}}}}function Zt(l){return(l.split("/").at(-1)??"").includes(".")}function Yt(l){return/[.-][0-9a-f]{8,}\.[a-z0-9]+$/i.test(l)}function Xt(l){let u=l._config,b=u.basePath,m=Object.values(u.collections),k=`<!doctype html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <title>${V(u.admin.title??"Heyo CMS")}</title>
8
+ <style>
9
+ :root { color-scheme: light dark; }
10
+ body { font: 15px/1.6 ui-sans-serif, system-ui, sans-serif; margin: 0 auto; max-width: 46rem; padding: 4rem 1.5rem; }
11
+ h1 { font-size: 1.4rem; margin: 0 0 .25rem; }
12
+ p { color: color-mix(in oklab, currentColor 70%, transparent); }
13
+ code { font-family: ui-monospace, monospace; font-size: .9em; }
14
+ ul { padding-left: 1.1rem; }
15
+ a { color: inherit; }
16
+ </style>
17
+ </head>
18
+ <body>
19
+ <h1>${V(u.admin.title??"Heyo CMS")}</h1>
20
+ <p>
21
+ The API is mounted at <code>${V(b)}</code> and answering.
22
+ The panel is not installed: <code>admin.assets</code> is not configured.
23
+ Install <code>@heyo-sh/heyo-cms-admin</code> and pass
24
+ <code>assets: adminAssets()</code>.
25
+ </p>
26
+ <ul>
27
+ <li><a href="${V(b)}/openapi.json">openapi.json</a> &mdash; every route this deployment answers</li>
28
+ <li><a href="${V(b)}/panel">panel</a> &mdash; what the panel would render here</li>
29
+ <li><a href="${V(b)}/auth/session">auth/session</a> &mdash; who you are right now</li>
30
+ ${m.map((A)=>` <li><a href="${V(b)}/${V(A.slug)}">${V(A.slug)}</a></li>`).join(`
31
+ `)}
32
+ </ul>
33
+ </body>
34
+ </html>
35
+ `;return new Response(k,{status:200,headers:{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}})}function V(l){return l.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}function Ve(l){let u=l._config;if(!u.ai)return[];let b=[],m=[...Object.values(u.collections),...Object.values(u.globals)];for(let k of m){let A=k.kind==="global"?`/globals/${k.slug}`:`/${k.slug}`,J=Object.values(k.fields).some((C)=>C.aiGenerate!==void 0||C.ai!==void 0&&Object.keys(C.ai).length>0);if(Object.values(k.fields).some((C)=>C.localized))b.push({method:"POST",path:k.kind==="global"?`${A}/ai/translate-variant`:`${A}/:id/ai/translate-variant`,source:"core",handler:async(C)=>{let T=await f(C.request),S=v(T,"locale"),O=v(T,"sourceLocale");if(S===O||!u.i18n.locales.includes(S)||!u.i18n.locales.includes(O))throw e.validation("Choose two different configured languages.");if(!Array.isArray(T.fields)||T.fields.some((D)=>typeof D!=="string"))throw e.validation("fields must be an array of field names.");let M={};for(let D of new Set(T.fields)){let F=k.fields[D];if(!F?.localized||!["text","textarea","richText","email","url"].includes(F.type))throw e.validation(`Cannot translate ${D}.`);let L=await l.ai.run({collection:k.name,field:D,action:"translate",variantTranslation:!0,...k.kind==="global"?{}:{id:C.params.id},actor:C.actor,source:y(C.request),locale:S,sourceLocale:O,signal:C.request.signal});M[D]=L.value}return i({values:M})},openapi:{summary:`Translate localized fields of a ${k.label} language variant`,description:"Returns translated values without writing them. Only localized text fields are translated; the caller creates the variant through the ordinary conditional PATCH.",tags:["ai"],parameters:k.kind==="global"?[]:[c("id","Document ID.")],requestBody:h({type:"object",required:["locale","sourceLocale","fields"],properties:{locale:{type:"string"},sourceLocale:{type:"string"},fields:{type:"array",items:{type:"string"}}}}),responses:{"200":r({type:"object",properties:{values:{type:"object"}}},"Translated field values."),"403":r(o("Error"),"The actor may not update a field."),"422":r(o("Error"),"Invalid languages or fields.")}}});if(J){let C=async(T,S)=>{let O=await nr(T.request),M=O.document,D=T.query.get("locale")??Ge(O.locale),F=T.query.get("sourceLocale")??Ge(O.sourceLocale),L=await l.ai.run({collection:k.name,field:T.params.field,action:T.params.action,actor:T.actor,source:y(T.request),...S===void 0?{}:{id:S},...Ke(M)?{document:M}:{},...D===void 0?{}:{locale:D},...F===void 0?{}:{sourceLocale:F},signal:T.request.signal});return i(L)};b.push({method:"POST",path:`${A}/:id/ai/:field/:action`,source:"core",handler:(T)=>C(T,T.params.id),openapi:He(k,!0)},{method:"POST",path:`${A}/ai/:field/:action`,source:"core",handler:(T)=>C(T),openapi:He(k,!1)})}}return b.push(...er(l)),b.push(...sr(l)),b}function er(l){let u=l._config;if(u.ai?.assistant?.enabled===!1)return[];let b=["ai"];return[{method:"POST",path:"/ai/assistant",source:"core",handler:async(m)=>{let k=g(u,m.actor,"ai:use"),A=await f(m.request),J=so(A.pageContext);return tr((C)=>l.ai.runAssistant({message:v(A,"message"),history:rr(A.history),actor:k,source:y(m.request),signal:C,...typeof A.conversationId==="string"?{conversationId:A.conversationId}:{},...typeof A.locale==="string"?{locale:A.locale}:{},...J===void 0?{}:{pageContext:J}}),m.request)},openapi:{summary:"Talk to the assistant",description:"Server-sent events. The agent reads and writes through the ordinary API with the caller's own permissions, so every change it makes runs the same validation, hooks, events and activity log as a form save — with `source: ai` and the signed-in human as the actor.\n\nCreating, deleting and multi-record changes never happen here: they arrive as `approval-required` and are executed by the approval endpoint.",tags:b,requestBody:h({type:"object",required:["message"],properties:{message:{type:"string",maxLength:12000},conversationId:{type:"string"},locale:{type:"string",description:"The language being edited, for a request with no `pageContext`. `pageContext.locale` wins when both are present."},history:{type:"array",maxItems:20,items:{type:"object",properties:{role:{type:"string",enum:["user","assistant"]},text:{type:"string"}}}},pageContext:{type:"object",description:"Where the editor is looking. A hint: every slug, id and locale in it is re-resolved server-side against the configuration and re-read with the caller's permissions.",properties:{kind:{type:"string",enum:["dashboard","collection-list","document","global","media","trash","settings"]},collectionSlug:{type:"string"},documentId:{type:"string"},mediaId:{type:"string",description:`The file the media screen's details pane is showing, so "this image" resolves.`},tab:{type:"string",description:"Which sub-surface of the screen is in front: a rich-text tab, a settings tab, the media status."},locale:{type:"string"},dirtyFields:{type:"array",items:{type:"string"},description:"Names only. The unsaved values stay in the browser; the assistant refuses to write a record whose form is dirty."},baselineRevision:{type:"integer"}}}}}),responses:{"200":{description:"A stream of `AiAssistantEvent`, one per SSE message.",content:{"text/event-stream":{schema:o("AiAssistantEvent")}}},"403":r(o("Error"),"The actor does not have `ai:use`."),"501":r(o("Error"),"`ai_assistant_runtime_missing` or `ai_credentials_missing`.")}}},{method:"POST",path:"/ai/assistant/plans/:id/approve",source:"core",handler:async(m)=>{let k=g(u,m.actor,"ai:use"),A=await l.ai.approveAssistantPlan({planId:m.params.id,actor:k,source:y(m.request),signal:m.request.signal});return i(A)},openapi:{summary:"Apply a plan the editor approved",description:"Executes the stored plan exactly as it was shown. The model is not consulted: the session, the permissions and the revisions are checked again, and the recorded operations run through the ordinary write path.",tags:b,parameters:[c("id","Plan id from `approval-required`.")],responses:{"200":r({type:"object",properties:{planId:{type:"string"},runId:{type:"string"},applied:{type:"array",items:{type:"object",properties:{target:o("AiMutationTarget"),fields:{type:"array",items:{type:"string"}}}}}}},"What was written."),"404":r(o("Error"),"No such plan for this actor."),"409":r(o("Error"),"Already applied, or a record moved on."),"410":r(o("Error"),"`ai_plan_expired`.")}}}]}function tr(l,u){let b=new TextEncoder,m=new AbortController,k=()=>m.abort(),A=m.signal;u.signal.addEventListener("abort",k,{once:!0});let J=l(A)[Symbol.asyncIterator](),C=new ReadableStream({async start(T){let S=!1,O=(M)=>{if(S)return;try{T.enqueue(b.encode(`data: ${JSON.stringify(M)}
36
+
37
+ `))}catch{S=!0}};try{for(;;){let M=await J.next();if(M.done||A.aborted)break;O(M.value)}}catch(M){if(!A.aborted){let D=M instanceof e?M:null;if(O({type:"error",code:D?.code??"ai_assistant_failed",message:D?.message??"The assistant failed."}),!D)console.error("[heyo-cms] the AI assistant failed",M)}}finally{u.signal.removeEventListener("abort",k),S=!0;try{T.close()}catch{}}},cancel(){k(),J.return?.().catch(()=>{return})}});return new Response(C,{headers:{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive","x-accel-buffering":"no"}})}function rr(l){if(!Array.isArray(l))return[];let u=[];for(let b of l){if(!Ke(b))continue;let{role:m,text:k}=b;if(m!=="user"&&m!=="assistant")continue;if(typeof k!=="string"||k.trim().length===0)continue;u.push({role:m,text:k})}return u}function sr(l){let u=l._config,b=["ai"];return[{method:"GET",path:"/ai/providers",source:"core",handler:async(m)=>{g(u,m.actor,"ai:read");let k=await l.ai.providers();return i({providers:k,defaultModel:u.ai?.defaultModel??null,assistant:u.ai?.assistant?.enabled!==!1,storage:u.ai?.storage?.secret!==void 0})},openapi:{summary:"Which AI providers have credentials",description:"Never the keys themselves: `hint` is the last four characters, `source` says whether the key is in the configuration or in the database.",tags:b,responses:{"200":r({type:"object",properties:{providers:{type:"array",items:o("AiProvider")},defaultModel:{type:["object","null"]},assistant:{type:"boolean"},storage:{type:"boolean",description:"Whether keys may be stored from the panel."}}},"The provider list.")}}},{method:"PUT",path:"/ai/providers/:provider",source:"core",handler:async(m)=>{let k=g(u,m.actor,"ai:manage"),A=await f(m.request),J=await l.ai.setCredential({provider:m.params.provider,token:v(A,"token"),actor:k,source:y(m.request)});return i({provider:J})},openapi:{summary:"Store a provider key",description:"Encrypted with `ai.storage.secret` before it is written. Without that secret the endpoint answers 501, because storing a key in plaintext is not a lesser version of this feature.",tags:b,parameters:[c("provider","Provider id, e.g. `openai`.")],requestBody:h({type:"object",required:["token"],properties:{token:{type:"string"}}}),responses:{"200":r({type:"object",properties:{provider:o("AiProvider")}},"The stored key, described."),"501":r(o("Error"),"ai.storage.secret is not configured.")}}},{method:"DELETE",path:"/ai/providers/:provider",source:"core",handler:async(m)=>{let k=g(u,m.actor,"ai:manage");if(!await l.ai.removeCredential({provider:m.params.provider,actor:k}))throw e.notFound("No stored key for that provider.");return i({removed:!0})},openapi:{summary:"Forget a stored provider key",description:"Removes the database copy. A key in `ai.auth` is in the configuration and is unaffected.",tags:b,parameters:[c("provider","Provider id.")],responses:{"200":r({type:"object",properties:{removed:{type:"boolean"}}},"Removed."),"404":r(o("Error"),"There was no stored key.")}}}]}function He(l,u){let b=Object.entries(l.fields).filter(([,m])=>m.aiGenerate!==void 0||m.ai).map(([m,k])=>`${m}: ${[...k.aiGenerate===void 0?[]:["generate"],...Object.keys(k.ai??{})].join(", ")}`);return{summary:`Run an AI action on a ${l.label} field`,description:["Answers with a value. Nothing is written: the panel inserts the result into the form and the editor saves it, like any other edit (section 8).",u?"The stored document is read first and the draft in the body is merged over it.":"For a document that has not been saved yet; everything the model sees comes from the body.",b.length>0?`Configured here — ${b.join("; ")}.`:""].filter((m)=>m.length>0).join(`
38
+
39
+ `),tags:["ai"],parameters:[c("field","Field name."),c("action","`generate` for aiGenerate, or a key of the field's `ai` map."),a("locale","string","Language the value is written in."),a("sourceLocale","string","Language `translate` reads from.")],requestBody:h({type:"object",properties:{document:{type:"object",description:"The form as it stands in the browser."},locale:{type:"string"},sourceLocale:{type:"string"}}},!1),responses:{"200":r(o("AiResult"),"The suggestion."),"403":r(o("Error"),"The actor may not write that field."),"404":r(o("Error"),"The field has no such action configured."),"501":r(o("Error"),"This deployment has no `ai` section.")}}}async function nr(l){let u=l.headers.get("content-length");if(u==="0"||u===null)return{};try{return await f(l)}catch{return{}}}function Ke(l){return typeof l==="object"&&l!==null&&!Array.isArray(l)}function Ge(l){return typeof l==="string"&&l.length>0?l:void 0}function Xe(l){let u=l._config,b=[];for(let m of[...Object.values(u.collections),...Object.values(u.globals)]){if(!Object.values(m.fields).some((C)=>C.generate))continue;let k=m.kind==="global"?`/globals/${m.slug}`:`/${m.slug}`,A=async(C,T)=>{let S=C.params.field,O=m.fields[S];if(!O?.generate)throw e.notFound(`No generator for ${m.name}.${S}.`);let M=await f(C.request),D=typeof M.locale==="string"?M.locale:u.i18n.defaultLocale;if(!u.i18n.locales.includes(D))throw e.validation(`Unknown locale "${D}".`);let F=C.actor,L=ar(M.document)?M.document:{},U=m.kind==="global"||T?"update":"create",z=U==="update"?await _(m,{actor:F,operation:U,locale:D,rbac:u.rbac}):void 0,B=await or(l,m,T,F,D);if(O.access?.read&&!O.access.read({actor:F,locale:D,record:B}))throw e.notFound(`No generator for ${m.name}.${S}.`);let X=await _(m,{actor:F,operation:U,locale:D,rbac:u.rbac,...T||m.kind==="global"?{record:B}:{}});if(z||X){if(U==="create")throw new e(`access.create on "${m.name}" returned a filter; a create has no row to filter. Return true or false.`,{status:500,code:"invalid_access_rule"});let H=await l.database();if((await H.transaction((qt)=>H.query(qt,{table:p,where:s(t("id",String(B.id)),t("collection",m.slug),t("deleted_at",null),...z?[z]:[],...X?[X]:[]),limit:1}))).length===0)throw e.notFound(`No ${m.name} with id "${B.id}".`)}j(m,{[S]:null},F,D,U==="create"?void 0:B);let Oe={};for(let[H,pe]of Object.entries(m.fields)){if(H===S)continue;if(pe.access?.read&&!pe.access.read({actor:F,locale:D,record:B}))continue;if(H in L||H in B)Oe[H]=H in L?L[H]??null:B[H]??null}let At=await O.generate({fields:Oe,locale:D}),le=O.schema.safeParse(At);if(!le.success)throw e.validation(`${m.name}.${S}: generated value is invalid: ${le.error.issues[0]?.message??"invalid value"}`);return i({value:le.data})},J=(C)=>({summary:`Generate a ${m.label} field without AI`,tags:["content"],parameters:[c("field","Field with a generate function."),...C?[c("id","Document ID.")]:[]],requestBody:h({type:"object",properties:{document:{type:"object"},locale:{type:"string"}}}),responses:{"200":r({type:"object",properties:{value:{}}},"Generated field value.")}});if(m.kind!=="global")b.push({method:"POST",path:`${k}/:id/generate/:field`,source:"core",handler:(C)=>A(C,C.params.id),openapi:J(!0)});b.push({method:"POST",path:`${k}/generate/:field`,source:"core",handler:(C)=>A(C),openapi:J(!1)})}return b}async function or(l,u,b,m,k){if(u.kind==="global"){let C=l.globals[u.name];return C?C.get({actor:m,locale:k}):{}}if(!b)return{};let J=await l.api[u.name]?.findById(b,{actor:m,locale:k});if(!J)throw e.notFound(`No ${u.name} with id "${b}", or it is not readable.`);return J}function ar(l){return typeof l==="object"&&l!==null&&!Array.isArray(l)}function nt(l){let u=l._config,b=u.admin.auth;if(!b)return[];let m=new Set(b.methods),k=["auth"],A=[],J=(T,S)=>{if(T.status!=="signed-in")return i({status:T.status,challenge:T.challenge,expiresAt:T.expiresAt},200);return i({status:T.status,token:T.token,expiresAt:T.expiresAt,user:T.user,actor:T.actor},200,{"set-cookie":tt(T.token,T.expiresAt,S,u.basePath)})};if(m.has("password")&&b.firstUser!==!1)A.push({method:"POST",path:"/auth/setup",source:"core",handler:async(T)=>{let S=await f(T.request),O=W(S,"name"),M=await l.auth.setup.claim({email:v(S,"email"),password:v(S,"password"),...O===void 0?{}:{name:O},...ee(T.request)});return J(M,T.request)},openapi:{summary:"Claim a CMS that has no accounts",description:"Creates the first editor and signs it in. Answers 409 once any account exists, including one created by a request that arrived a millisecond earlier: the count is taken inside the transaction that does the insert. Disable with `admin.auth.firstUser: false`.",tags:k,security:!1,requestBody:h({type:"object",required:["email","password"],properties:{email:{type:"string",format:"email"},password:{type:"string",minLength:8},name:{type:"string"}}}),responses:{"200":r(o("SignInResult"),"A session for the first editor."),"409":r(o("Error"),"Somebody got here first. Sign in.")}}});if(m.has("password"))A.push({method:"POST",path:"/auth/sign-in",source:"core",handler:async(T)=>{let S=await f(T.request),O=await l.auth.password.signIn({email:v(S,"email"),password:v(S,"password"),...ee(T.request)});return J(O,T.request)},openapi:{summary:"Sign in with a password",description:'Answers either a session or `{ status: "two-factor-required", challenge }`. There is no way to turn the second into the first without a valid code.',tags:k,security:!1,requestBody:h({type:"object",required:["email","password"],properties:{email:{type:"string",format:"email"},password:{type:"string"}}}),responses:{"200":r(o("SignInResult"),"A session, or a challenge."),"401":r(o("Error"),"Wrong credentials, in constant time.")}}});if(A.push({method:"GET",path:"/auth/session",source:"core",handler:async(T)=>{if(!T.actor)return i({actor:null,user:null});let S=await l.auth.users.get(T.actor.id);return i({actor:T.actor,user:S})},openapi:{summary:"Who is calling",description:"Resolves the session cookie or the `Authorization` header. Answers `{ actor: null }` rather than 401 when there is no caller.",tags:k,security:!1,responses:{"200":r({type:"object",properties:{actor:{oneOf:[o("Actor"),{type:"null"}]},user:{oneOf:[o("User"),{type:"null"}]}}},"The caller, if any.")}}},{method:"POST",path:"/auth/sign-out",source:"core",handler:async(T)=>{let S=ir(T.request);if(S)await l.auth.sessions.revoke(S);return oe({"set-cookie":cr(T.request,u.basePath)})},openapi:{summary:"Sign out",description:"Revokes the session server-side and clears the cookie. Idempotent: signing out twice is not an error.",tags:k,responses:{"204":{description:"The session is gone."}}}}),m.has("magic-link")){let T=b.deliverToken;if(T)A.push({method:"POST",path:"/auth/magic-link",source:"core",handler:async(S)=>{let O=await f(S.request),M=await l.auth.magicLink.request({email:v(O,"email")});return await et(T,"magic-link",M,S.request,l),i({status:"sent"})},openapi:{summary:"Request a magic link",description:"Mints a single-use token and hands it to `admin.auth.deliverToken`. The response never contains the token and never says whether the address exists.",tags:k,security:!1,requestBody:h({type:"object",required:["email"],properties:{email:{type:"string",format:"email"}}}),responses:{"200":r({type:"object"},'`{ status: "sent" }`.')}}});A.push({method:"POST",path:"/auth/magic-link/verify",source:"core",handler:async(S)=>{let O=await f(S.request),M=await l.auth.magicLink.verify({token:v(O,"token"),...ee(S.request)});return J(M,S.request)},openapi:{summary:"Exchange a magic link for a session",tags:k,security:!1,requestBody:h({type:"object",required:["token"],properties:{token:{type:"string"}}}),responses:{"200":r(o("SignInResult"),"A session, or a challenge."),"401":r(o("Error"),"The token is spent, expired or unknown.")}}})}if(m.has("password")){let T=b.deliverToken;if(T)A.push({method:"POST",path:"/auth/password/forgot",source:"core",handler:async(S)=>{let O=await f(S.request),M=await l.auth.password.requestReset({email:v(O,"email")});return await et(T,"password-reset",M,S.request,l),i({status:"sent"})},openapi:{summary:"Request a password reset",description:"Same shape as the magic link, and the same silence about whether the address exists.",tags:k,security:!1,requestBody:h({type:"object",required:["email"],properties:{email:{type:"string",format:"email"}}}),responses:{"200":r({type:"object"},'`{ status: "sent" }`.')}}});A.push({method:"POST",path:"/auth/password/reset",source:"core",handler:async(S)=>{let O=await f(S.request),M=await l.auth.password.reset({token:v(O,"token"),password:v(O,"password")});return i({user:M})},openapi:{summary:"Set a new password with a reset token",description:"Every session of that user is revoked: a reset is what somebody does when they believe their password is known to a stranger.",tags:k,security:!1,requestBody:h({type:"object",required:["token","password"],properties:{token:{type:"string"},password:{type:"string",minLength:8}}}),responses:{"200":r({type:"object",properties:{user:o("User")}},"The user."),"401":r(o("Error"),"The token is spent, expired or unknown.")}}})}A.push({method:"POST",path:"/auth/invite/accept",source:"core",handler:async(T)=>{let S=await f(T.request),O=W(S,"password"),M=W(S,"name"),D=await l.auth.users.acceptInvite({token:v(S,"token"),...O===void 0?{}:{password:O},...M===void 0?{}:{name:M},...ee(T.request)});return J(D,T.request)},openapi:{summary:"Accept an invitation",description:"Turns an invited account into an active one and signs it in. The token comes from `POST /users/invite`, which an administrator calls.",tags:k,security:!1,requestBody:h({type:"object",required:["token"],properties:{token:{type:"string"},password:{type:"string",minLength:8},name:{type:"string"}}}),responses:{"200":r(o("SignInResult"),"A session."),"401":r(o("Error"),"The invitation is spent, expired or unknown.")}}});let C=[...["github","google","microsoft"].filter((T)=>m.has(T)),...b.sso?["oidc"]:[]];if(C.length>0)A.push({method:"GET",path:"/auth/oauth/:provider",source:"core",handler:async(T)=>{let S=st(T.params.provider,C),O=T.query.get("redirectTo"),M=await l.auth.oauth.authorize({provider:S,redirectUri:rt(T.request,u.basePath,S),...O===null?{}:{redirectTo:O}});return new Response(null,{status:302,headers:{location:M.url}})},openapi:{summary:"Start an OAuth or SSO sign-in",description:`Redirects to the provider. Configured here: ${C.join(", ")}.`,tags:k,security:!1,parameters:[c("provider",C.join(", ")),a("redirectTo","string","Where to land after the callback.")],responses:{"302":{description:"To the provider."},"404":r(o("Error"),"That provider is not configured.")}}},{method:"GET",path:"/auth/oauth/:provider/callback",source:"core",handler:async(T)=>{let S=st(T.params.provider,C),O=T.query.get("code"),M=T.query.get("state");if(!O||!M)throw e.validation("The provider did not return a code and a state.");let D=await l.auth.oauth.callback({provider:S,code:O,state:M,redirectUri:rt(T.request,u.basePath,S),...ee(T.request)}),F=D.redirectTo;if(D.status==="signed-in"&&F)return new Response(null,{status:302,headers:{location:F,"set-cookie":tt(D.token,D.expiresAt,T.request,u.basePath)}});return J(D,T.request)},openapi:{summary:"Finish an OAuth or SSO sign-in",tags:k,security:!1,parameters:[c("provider",C.join(", ")),a("code","string","From the provider."),a("state","string","From the provider; checked against the stored one.")],responses:{"200":r(o("SignInResult"),"A session, or a challenge."),"302":{description:"To `redirectTo`, with the cookie set."},"401":r(o("Error"),"The state did not match, or the account is unknown.")}}});if(b.twoFactor===!0)A.push({method:"POST",path:"/auth/2fa/begin",source:"core",handler:async(T)=>{let S=Ye(T.actor),O=await l.auth.twoFactor.begin(S.id);return i(O)},openapi:{summary:"Start enrolling an authenticator",description:"Returns the secret and the `otpauth://` URI the panel renders as a QR code. The factor is not enabled until a code proves it works.",tags:k,responses:{"200":r({type:"object",properties:{secret:{type:"string"},uri:{type:"string"}}},"The secret and its URI.")}}},{method:"POST",path:"/auth/2fa/enable",source:"core",handler:async(T)=>{let S=Ye(T.actor),O=await f(T.request),M=await l.auth.twoFactor.enable({userId:S.id,code:v(O,"code")});return i({user:M})},openapi:{summary:"Confirm the authenticator",tags:k,requestBody:h({type:"object",required:["code"],properties:{code:{type:"string"}}}),responses:{"200":r({type:"object",properties:{user:o("User")}},"The user."),"401":r(o("Error"),"The code is wrong.")}}},{method:"POST",path:"/auth/2fa/disable",source:"core",handler:async(T)=>{let S=Ye(T.actor),O=await l.auth.twoFactor.disable(S.id);return i({user:O})},openapi:{summary:"Remove the second factor",tags:k,responses:{"200":r({type:"object",properties:{user:o("User")}},"The user.")}}},{method:"POST",path:"/auth/2fa/verify",source:"core",handler:async(T)=>{let S=await f(T.request),O=await l.auth.twoFactor.complete({challenge:v(S,"challenge"),code:v(S,"code"),...ee(T.request)});return J(O,T.request)},openapi:{summary:"Exchange a challenge and a code for a session",description:"The only way a `two-factor-required` answer becomes a session. Five wrong codes destroy the challenge.",tags:k,security:!1,requestBody:h({type:"object",required:["challenge","code"],properties:{challenge:{type:"string"},code:{type:"string"}}}),responses:{"200":r(o("SignInResult"),"A session."),"401":r(o("Error"),"The code is wrong, or the challenge is spent.")}}});if(b.apiKeys?.enabled===!0)A.push({method:"GET",path:"/auth/api-keys",source:"core",handler:async(T)=>{let S=Ze(T.actor),O=await l.auth.apiKeys.list({userId:S.id});return i({keys:O})},openapi:{summary:"List your API keys",description:"Your own keys. The key material is never returned again.",tags:k,responses:{"200":r({type:"object",properties:{keys:{type:"array",items:o("ApiKey")}}},"The caller's keys.")}}},{method:"POST",path:"/auth/api-keys",source:"core",handler:async(T)=>{let S=Ye(T.actor),O=await f(T.request),M=Re(O,"scopes"),D=await l.auth.apiKeys.create({name:v(O,"name"),userId:S.id,actor:S,...M===void 0?{}:{scopes:M}});return i(D,201)},openapi:{summary:"Issue an API key",description:"Bound to the caller, and never wider: the scopes are intersected with the caller's own permissions, so a read-only key on an administrator's account is read-only. `key` is shown once.",tags:k,requestBody:h({type:"object",required:["name"],properties:{name:{type:"string"},scopes:{type:"array",items:{type:"string"}}}}),responses:{"201":r(o("IssuedApiKey"),"The key, once."),"403":r(o("Error"),"An API key cannot issue another.")}}},{method:"DELETE",path:"/auth/api-keys/:id",source:"core",handler:async(T)=>{let S=Ze(T.actor),O=T.params.id;if(!(await l.auth.apiKeys.list({userId:S.id})).some((D)=>D.id===O))throw e.notFound("No such API key.");return await l.auth.apiKeys.revoke(O,{actor:S}),oe()},openapi:{summary:"Revoke one of your API keys",tags:k,parameters:[c("id","Key id.")],responses:{"204":{description:"Revoked."},"404":r(o("Error"),"Not one of your keys.")}}});return A}async function et(l,u,b,m,k){if(!b)return;let A=new URL(m.url),J=u==="magic-link"?"magic-link":"reset-password";await l({type:u,token:b.token,expiresAt:b.expiresAt,email:b.email,userId:b.userId,url:`${A.origin}${k._config.basePath}${k._config.admin.path}/${J}?token=${encodeURIComponent(b.token)}`})}function ee(l){let b=l.headers.get("x-forwarded-for")?.split(",")[0]?.trim()??l.headers.get("cf-connecting-ip");return{ip:b&&b.length>0?b:null,userAgent:l.headers.get("user-agent")}}function ir(l){let u=l.headers.get("authorization");if(u?.toLowerCase().startsWith("bearer "))return u.slice(7).trim();let b=l.headers.get("cookie");if(!b)return null;for(let m of b.split(";")){let[k,...A]=m.trim().split("=");if(k===be)return A.join("=")}return null}function tt(l,u,b,m){let k=new URL(b.url).protocol==="https:";return[`${be}=${l}`,`Path=${m}`,`Expires=${new Date(u).toUTCString()}`,"HttpOnly","SameSite=Lax",...k?["Secure"]:[]].join("; ")}function cr(l,u){let b=new URL(l.url).protocol==="https:";return[`${be}=`,`Path=${u}`,"Max-Age=0","HttpOnly","SameSite=Lax",...b?["Secure"]:[]].join("; ")}function rt(l,u,b){return`${new URL(l.url).origin}${u}/auth/oauth/${b}/callback`}function st(l,u){if(l===void 0||!u.includes(l))throw e.notFound(`"${l??""}" is not a configured sign-in provider.`);return l}function at(l){let u=l._config;if(!u.uploads)return[];let b=["media"],m=Object.keys(u.uploads.images?.variants?.sizes??{});return[{method:"POST",path:"/media/upload",source:"core",handler:async(k)=>{g(u,k.actor,"media:create");let A=await lr(k.request),J=await l.media.upload({...A,actor:k.actor,source:y(k.request)});return i(J,201)},openapi:{summary:"Upload a file",description:"`multipart/form-data` with a `file` part, or the raw bytes with `content-type` and `x-heyo-cms-filename`. The MIME type is taken from the bytes, not from what the client called them.",tags:b,requestBody:{required:!0,content:{"multipart/form-data":{schema:{type:"object",required:["file"],properties:{file:{type:"string",format:"binary"},filename:{type:"string"},alt:{type:"string"},locale:{type:"string"},metadata:{type:"string",description:"JSON object."}}}},"application/octet-stream":{schema:{type:"string",format:"binary"}}}},responses:{"201":r(o("Media"),"The stored file."),"413":r(o("Error"),"The file is larger than uploads.limits.maxFileSize."),"415":r(o("Error"),"The MIME type is not in uploads.limits.accept.")}}},{method:"GET",path:"/media",source:"core",handler:async(k)=>{let A=k.query.get("status"),J=k.query.get("mimeType"),C=k.query.get("search"),T=k.query.get("cursor"),S=P(k.query.get("limit"),"limit",1,200),O=await l.media.list({actor:k.actor,...A===null?{}:{status:fr(A)},...J===null?{}:{mimeType:J},...C===null?{}:{search:C},...T===null?{}:{cursor:T},...S===void 0?{}:{limit:S}});return i(O)},openapi:{summary:"List the library",tags:b,parameters:[a("status","string","active, orphaned or purging.",{enum:["active","orphaned","purging"]}),a("mimeType","string","`image/*` wildcards allowed."),a("search","string","Case-insensitive match on the filename."),a("limit","integer","Page size. Defaults to 25.",{minimum:1,maximum:200}),a("cursor","string","Keyset cursor from the previous page.")],responses:{"200":r({type:"object",properties:{docs:{type:"array",items:o("Media")},limit:{type:"integer"},hasNextPage:{type:"boolean"},cursor:{type:["string","null"]}}},"One page of files.")}}},{method:"GET",path:"/media/:id",source:"core",handler:async(k)=>{let A=await l.media.get(te(k.params.id));if(!A)throw e.notFound("No such file.");return i(A)},openapi:{summary:"Read one file",tags:b,parameters:[c("id","Media id.")],responses:Ct(o("Media"),"The file.")}},{method:"PATCH",path:"/media/:id",source:"core",handler:async(k)=>{g(u,k.actor,"media:update");let A=await f(k.request),J=await l.media.update({id:te(k.params.id),actor:k.actor,source:y(k.request),...pr(A)});return i(J)},openapi:{summary:"Update a file's metadata",description:"Alternative text, filename, focal point, crop and your own metadata keys. Changing the focal point or the crop discards the variants rendered from the old one.",tags:b,parameters:[c("id","Media id.")],requestBody:h({type:"object",properties:{filename:{type:"string"},alt:{oneOf:[{type:"string"},{type:"object"}],description:"A string writes one locale; an object replaces the map."},locale:{type:"string"},focalPoint:{type:["object","null"],properties:{x:{type:"number",minimum:0,maximum:1},y:{type:"number",minimum:0,maximum:1}}},crop:{type:["object","null"],properties:{x:{type:"integer"},y:{type:"integer"},width:{type:"integer"},height:{type:"integer"}}},metadata:{type:"object"}}}),responses:N(o("Media"),"The file.")}},{method:"DELETE",path:"/media/:id",source:"core",handler:async(k)=>{g(u,k.actor,"media:delete");let A=await l.media.delete({id:te(k.params.id),force:G(k.query.get("force"),"force")===!0,actor:k.actor,source:y(k.request)});return i(A)},openapi:{summary:"Delete a file",description:"Refused, naming the documents, when something still points at it — `?force=true` overrides. This is the one destructive media operation an editor can perform; losing the last reference merely marks the file unused.",tags:b,parameters:[c("id","Media id."),a("force","boolean","Deletes a file documents still reference.")],responses:{"200":r(o("Media"),"The file that was removed."),"409":r(o("Error"),"Documents still reference the file.")}}},{method:"POST",path:"/media/:id/keep",source:"core",handler:async(k)=>{g(u,k.actor,"media:update");let A=await l.media.keep({id:te(k.params.id),actor:k.actor,source:y(k.request)});return i(A)},openapi:{summary:`The panel's "Keep" button`,description:"Clears the purge deadline of an unused file without claiming anything references it.",tags:b,parameters:[c("id","Media id.")],responses:{"200":r(o("Media"),"The file.")}}},{method:"GET",path:"/media/:id/usages",source:"core",handler:async(k)=>{let A=await l.media.usages(te(k.params.id));return i({usages:A})},openapi:{summary:"Where a file is used",description:"Empty for an unused file, by definition.",tags:b,parameters:[c("id","Media id.")],responses:{"200":r({type:"object",properties:{usages:{type:"array",items:o("MediaUsage")}}},"One entry per document, field and locale.")}}},{method:"GET",path:"/media/:id/:variant",source:"core",handler:async(k)=>{let A=k.params.variant??"original",J=await l.media.variant({id:te(k.params.id),...A==="original"?{}:{variant:A},refresh:G(k.query.get("refresh"),"refresh")===!0});if(!J)throw e.notFound("No such file.");return it(k.request,J)},openapi:{summary:"Serve a file or one of its sizes",description:m.length===0?"`original` only; this deployment declares no sizes.":`\`original\` or one of: ${m.join(", ")}. Only declared names are accepted — arbitrary dimensions in a URL are how a stranger fills a bucket one pixel at a time.`,tags:b,parameters:[c("id","Media id."),c("variant",`original${m.length===0?"":`, ${m.join(", ")}`}`),a("refresh","boolean","Renders again even if a stored copy exists.")],responses:{"200":{description:"The bytes."},"304":{description:"The cached copy is current."},"404":r(o("Error"),"No such file or no such size.")}}}]}async function it(l,u,b={}){let m=u.processingError!==null,k=`"${u.media.checksum??u.media.id}-${u.variant??"original"}${m?"-fallback":""}"`,A={"content-type":u.contentType,etag:k,"cache-control":m?"public, max-age=300":"public, max-age=31536000, immutable","x-content-type-options":"nosniff","x-heyo-cms-cache":m?"fallback":u.cached?"hit":"miss",...b};if(dr(l.headers.get("if-none-match"),k))return await u.body.cancel(),new Response(null,{status:304,headers:A});if(u.size!==null)A["content-length"]=String(u.size);if(l.method==="HEAD")return await u.body.cancel(),new Response(null,{status:200,headers:A});return new Response(u.body,{status:200,headers:A})}function dr(l,u){if(l===null)return!1;if(l.trim()==="*")return!0;return l.split(",").map((b)=>b.trim().replace(/^W\//,"")).includes(u)}function ct(l){let u=l._config.uploads,b=u?.publicPath;if(!u||!b)return[];let m=new Set(Object.keys(u.images?.variants?.sizes??{})),k=async(A)=>{let J=A.params.id??"",C=A.params.variant;if(!/^[A-Za-z0-9_-]+$/.test(J)||C!==void 0&&!m.has(C))return ot();let T=await l.media.variant({id:J,...C===void 0?{}:{variant:C}}).catch((S)=>{if(S instanceof e&&S.status===404)return null;throw S});if(!T)return ot();return it(A.request,T)};return[{method:"GET",path:`${b}/:id/:file`,source:"core",handler:k},{method:"GET",path:`${b}/:id/:variant/:file`,source:"core",handler:k}]}function ot(){return new Response("Not found",{status:404,headers:{"content-type":"text/plain; charset=utf-8","cache-control":"public, max-age=60"}})}function dt(l){if(!l._config.uploads)return[];return[{method:"POST",path:"/media/collect",source:"core",handler:async(u)=>{g(l._config,u.actor,"jobs:run");let b=P(u.query.get("limit"),"limit",1,1000),m=await l.media.collect(b===void 0?{}:{limit:b});return i(m)},openapi:{summary:"Run the unused-file collector",description:"Deletes files whose deadline has passed, re-checking references immediately before each delete. Also registered as the `media-gc` job.",tags:["jobs"],parameters:[a("limit","integer","Files examined in one pass.")],responses:{"200":r({type:"object",properties:{examined:{type:"integer"},purged:{type:"integer"},reclaimed:{type:"integer"},failed:{type:"integer"}}},"What the sweep did.")}}}]}var io=["usages","keep","collect","upload"];async function lr(l){let u=l.headers.get("content-type")??"";if(u.includes("multipart/form-data")){let C=await l.formData(),T=C.get("file");if(T===null)throw e.validation('The form needs a "file" part.');if(typeof T==="string")throw e.validation('"file" has to be a file, not a string.');let S=C.get("metadata"),O=C.get("filename"),M=C.get("alt"),D=C.get("locale");return{file:T,...typeof O==="string"&&O.length>0?{filename:O}:T.name?{filename:T.name}:{},...T.type?{contentType:T.type}:{},...typeof M==="string"?{alt:M}:{},...typeof D==="string"?{locale:D}:{},...typeof S==="string"&&S.length>0?{metadata:ur(S)}:{}}}let b=new Uint8Array(await l.arrayBuffer());if(b.byteLength===0)throw e.validation("The request body is empty.");let m=new URL(l.url),k=l.headers.get("x-heyo-cms-filename")??m.searchParams.get("filename");if(!k)throw e.validation("A raw upload needs a filename: send `x-heyo-cms-filename` or ?filename=.");let A=m.searchParams.get("alt"),J=m.searchParams.get("locale");return{file:b,filename:k,...u.length>0?{contentType:u}:{},...A===null?{}:{alt:A},...J===null?{}:{locale:J}}}function ur(l){try{let u=JSON.parse(l);if(typeof u!=="object"||u===null||Array.isArray(u))throw Error("not an object");return u}catch{throw e.validation('"metadata" has to be a JSON object.')}}function pr(l){let u={};if(typeof l.filename==="string")u.filename=l.filename;if(typeof l.locale==="string")u.locale=l.locale;if(typeof l.alt==="string")u.alt=l.alt;else if(ce(l.alt))u.alt=l.alt;if(ce(l.metadata))u.metadata=l.metadata;if("focalPoint"in l)u.focalPoint=l.focalPoint===null?null:mr(l.focalPoint,"focalPoint");if("crop"in l)u.crop=l.crop===null?null:hr(l.crop);return u}function mr(l,u){if(!ce(l))throw e.validation(`"${u}" has to be an object.`);let{x:b,y:m}=l;if(typeof b!=="number"||typeof m!=="number")throw e.validation(`"${u}" needs numeric x and y.`);return{x:b,y:m}}function hr(l){if(!ce(l))throw e.validation('"crop" has to be an object.');let{x:u,y:b,width:m,height:k}=l;if(typeof u!=="number"||typeof b!=="number"||typeof m!=="number"||typeof k!=="number")throw e.validation('"crop" needs numeric x, y, width and height.');return{x:u,y:b,width:m,height:k}}function ce(l){return typeof l==="object"&&l!==null&&!Array.isArray(l)}function fr(l){if(l==="active"||l==="orphaned"||l==="purging")return l;throw e.validation(`"${l}" is not a media status; use active, orphaned or purging.`)}function te(l){if(l===void 0||l.length===0)throw e.validation("A media id is required.");return l}function En(l,u,b,m={}){let k=l.admin.auth,A=new Set(k?.methods??[]),J=l.uploads?.images,C=l.uploads?.retention,T=l.trash.purgeAfter,S=C?.purgeAfter??!1,O=l.uploads?.limits?.maxFileSize??null;return{title:l.admin.title??"Heyo CMS",basePath:l.basePath,panelPath:`${l.basePath}${l.admin.path}`,collections:Object.values(l.collections).map((M)=>lt(M,u)),globals:Object.values(l.globals).map((M)=>lt(M,u)),i18n:gr(l,b),media:{enabled:l.uploads!==void 0,variants:Object.keys(J?.variants?.sizes??{}),variantMode:J?.variants?.mode??null,focalPoint:J?.focalPoint===!0,crop:J?.crop===!0,accept:l.uploads?.limits?.accept??[],maxFileSize:O,maxFileSizeBytes:O===null?null:se(O),purgeAfter:S===!1?null:S,purgeAfterMs:S===!1?null:w(S)},trash:{enabled:l.trash.enabled,purgeAfter:T===!1?null:T,purgeAfterMs:T===!1?null:w(T)},auth:{methods:[...A],twoFactor:k?.twoFactor===!0,apiKeys:k?.apiKeys?.enabled===!0,sso:k?.sso?.issuer??null,magicLink:A.has("magic-link")&&k?.deliverToken!==void 0,passwordReset:A.has("password")&&k?.deliverToken!==void 0,setupRequired:m.setupRequired===!0},rbac:{enabled:l.rbac!==void 0,roles:Object.keys(l.rbac?.roles??{}),customRoles:l.rbac?.customRoles===!0},ai:{configured:l.ai!==void 0,runtime:l.ai!==void 0,defaultModel:l.ai?.defaultModel??null,assistant:l.ai!==void 0&&l.ai.assistant?.enabled!==!1,assistantSettings:l.ai!==void 0&&l.ai.assistant?.enabled!==!1?{model:l.ai.assistant?.model??l.ai.defaultModel,maxTurns:l.ai.assistant?.maxTurns??ue.maxTurns,maxToolCalls:l.ai.assistant?.maxToolCalls??ue.maxToolCalls,timeoutMs:l.ai.assistant?.timeoutMs??ue.timeoutMs,customInstructions:Boolean(l.ai.assistant?.instructions)}:null,actions:me(),managedKeys:l.ai?.storage?.secret!==void 0},webhooks:{enabled:l.webhooks?.enabled===!0,managedInAdmin:l.webhooks?.managedInAdmin!==!1,runtime:l.webhooks?.enabled===!0,events:yr(l)},editor:Te(),plugins:l.plugins.map((M)=>({id:M.id,navItems:M.admin?.navItems??[],scripts:M.admin?.scripts??[],fields:Object.keys(M.admin?.fields??{})}))}}function yr(l){if(l.webhooks?.enabled!==!0)return[];return["*","document.*","document.created","document.updated","document.field.changed","document.scheduled","document.schedule.applied","document.schedule.cancelled","document.trashed","document.restored","document.purged","media.*","media.uploaded","media.updated","media.orphaned","media.reclaimed","media.purged","ai.task.completed","ai.task.failed","ai.agent.completed","ai.agent.failed","ai.agent.mutation.applied","user.invited","user.joined","user.removed","role.changed","apikey.created","apikey.revoked","job.failed"]}function gr(l,u){return{locales:u.map((b)=>({code:b.code,name:b.name,rtl:b.rtl,isDefault:b.isDefault})),defaultLocale:l.i18n.defaultLocale}}function lt(l,u){let b=Object.entries(l.fields).map(([m,k])=>pt(m,k,u));return{name:l.name,slug:l.slug,label:l.label,kind:l.kind,...l.admin?.group===void 0?{}:{group:l.admin.group},...l.admin?.description===void 0?{}:{description:l.admin.description},useAsTitle:br(l),defaultColumns:l.admin?.defaultColumns??[],fields:b,can:{read:de(l.access,"read",u),create:l.kind==="collection"&&de(l.access,"create",u),update:de(l.access,"update",u),delete:l.kind==="collection"&&de(l.access,"delete",u)}}}function br(l){let u=l.admin?.useAsTitle;if(u!==void 0&&l.fields[u])return u;for(let m of["title","name","label"])if(l.fields[m])return m;return Object.entries(l.fields).find(([,m])=>m.type==="text")?.[0]??null}function pt(l,u,b){let m=u.options,k=u.admin??{},A=u.type==="list"&&wr(m.fields)?m.fields:null,{fields:J,...C}=m;return{name:l,type:u.type,label:typeof m.label==="string"?m.label:je(l),...typeof m.description==="string"?{description:m.description}:{},required:u.required,localized:u.localized,readOnly:m.readOnly===!0,unique:m.unique===!0,generate:u.generate!==void 0,...u.defaultValue===void 0?{}:{defaultValue:u.defaultValue},options:Rr(A?C:m),admin:{...typeof k.placeholder==="string"?{placeholder:k.placeholder}:{},hidden:k.hidden===!0},ai:vr(u),access:{read:ut(u.access?.read,b),update:m.readOnly===!0?!1:ut(u.access?.update,b)},...A?{fields:Object.entries(A).map(([T,S])=>pt(T,S,b))}:{}}}function wr(l){return typeof l==="object"&&l!==null&&!Array.isArray(l)}function vr(l){let u={...l.ai??{},...l.aiGenerate===void 0?{}:{generate:l.aiGenerate}};return Object.entries(u).map(([b,m])=>({key:b,label:m===!0?je(b):m.label??je(b),...m===!0||m.model===void 0?{}:{model:m.model}}))}function Rr(l){let u=new Set(["label","description","localized","required","unique","index","readOnly","ai","aiGenerate","generate","access","admin"]),b={};for(let[m,k]of Object.entries(l)){if(u.has(m))continue;let A=ve(k);if(A!==void 0)b[m]=A}return b}function ve(l){if(l===null)return null;switch(typeof l){case"string":case"boolean":return l;case"number":return Number.isFinite(l)?l:void 0;case"object":{if(Array.isArray(l))return l.map(ve).filter((k)=>k!==void 0);let u=Object.entries(l),b={};for(let[m,k]of u){let A=ve(k);if(A!==void 0)b[m]=A}return b}default:return}}function de(l,u,b){let m=l[u];if(!m)return!0;try{let k=m({actor:b,operation:u,locale:null});if(k instanceof Promise)return!0;return k!==!1}catch{return!0}}function ut(l,u){if(!l)return!0;try{return l({actor:u,locale:null})}catch{return!0}}function je(l){let u=l.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").trim();return u.charAt(0).toUpperCase()+u.slice(1)}function mt(l){return[{method:"GET",path:"/panel",source:"core",handler:async(u)=>i(En(l._config,u.actor,l.locales.list(),{setupRequired:await jr(l)})),openapi:{summary:"What the panel renders",description:"Collections, fields, languages, media settings and what the caller may do, derived from the configuration. Public, because the sign-in screen is rendered from it; it contains no content and no credentials, and the per-actor flags are advisory — the engine decides every read and write.",tags:["admin"],security:!1,responses:{"200":r({type:"object"},"The panel's description of this deployment.")}}}]}async function jr(l){try{return await l.auth.setup.required()}catch{return!1}}function ht(l){if(!l._config.trash.enabled)return[];let b=["trash"];return[{method:"GET",path:"/trash",source:"core",handler:async(m)=>{let k=m.query.get("collection"),A=P(m.query.get("limit"),"limit",1,200),J=m.query.get("cursor"),C=m.query.get("locale"),T=await l.trash.list({actor:m.actor,source:y(m.request),...k===null?{}:{collection:k},...A===void 0?{}:{limit:A},...J===null?{}:{cursor:J},...C===null?{}:{locale:C}});return i(T)},openapi:{summary:"List deleted documents",description:"Every collection at once, newest deletion first. A collection the caller may not read contributes nothing, so the list is shorter rather than forbidden.",tags:b,parameters:[a("collection","string","Restrict to one collection."),a("limit","integer","Page size. Defaults to 25."),a("cursor","string","Keyset cursor from the previous page."),a("locale","string","Language to resolve the documents in.")],responses:{"200":r({type:"object",properties:{entries:{type:"array",items:o("TrashedEntry")},limit:{type:"integer"},hasNextPage:{type:"boolean"},cursor:{type:["string","null"]}}},"One page of the bin.")}}},{method:"POST",path:"/trash/:collection/:id/restore",source:"core",handler:async(m)=>{let k=await l.trash.restore({collection:m.params.collection,id:m.params.id,actor:m.actor,source:y(m.request)});if(!k)throw e.notFound("Nothing was restored.");return i(k)},openapi:{summary:"Restore a document from the trash",tags:b,parameters:[c("collection","Collection name."),c("id","Document id.")],responses:{"200":r({type:"object"},"The restored document."),"409":r(o("Error"),"The document is not in the trash.")}}},{method:"DELETE",path:"/trash/:collection/:id",source:"core",handler:async(m)=>{let k=await l.trash.purge({collection:m.params.collection,id:m.params.id,actor:m.actor,source:y(m.request)});if(!k)throw e.notFound("Nothing was purged.");return i(k)},openapi:{summary:"Delete a document for good",description:"Needs the `purge` permission rather than `delete`: removing something and removing it so nobody can get it back are different decisions.",tags:b,parameters:[c("collection","Collection name."),c("id","Document id.")],responses:{"200":r({type:"object"},"The document as it last stood."),"403":r(o("Error"),"Access control refused the purge.")}}}]}function ft(l){let u=l._config;return[{method:"GET",path:"/locales",source:"core",handler:()=>i({locales:l.locales.list(),defaultLocale:u.i18n.defaultLocale}),openapi:{summary:"List the languages",description:"Anonymous, because a frontend needs the list to render a language switcher. The list is `i18n.locales` in the configuration; each entry carries the English name of its language and whether it is written right to left.",tags:["i18n"],security:!1,responses:{"200":r({type:"object",properties:{locales:{type:"array",items:o("Locale")},defaultLocale:{type:"string"}}},"The configured languages, and the one reads fall back to.")}}}]}function yt(l){let u=l._config;return[{method:"GET",path:"/activity",source:"core",handler:async(b)=>{g(u,b.actor,"activity:read");let m=b.query.get("collection"),k=b.query.get("documentId"),A=b.query.get("actorId"),J=b.query.get("action"),C=b.query.get("source"),T=b.query.get("cursor"),S=P(b.query.get("limit"),"limit",1,200),O=await l.activity.list({...m===null?{}:{collection:m},...k===null?{}:{documentId:k},...A===null?{}:{actorId:A},...J===null?{}:{action:J},...C===null?{}:{source:C},...T===null?{}:{cursor:T},...S===void 0?{}:{limit:S}});return i(O)},openapi:{summary:"Read the audit trail",description:"Written by the engine inside the transaction that caused each entry, so what is here committed and what is not here did not. Read-only: there is no way to delete a line.",tags:["activity"],parameters:[a("collection","string","Restrict to one collection."),a("documentId","string","Restrict to one document."),a("actorId","string","Restrict to one person or key."),a("action","string","`document.created`, `document.trashed`, …"),a("source","string","Where the write came from: `admin`, `rest`, `job`, `ai`."),a("limit","integer","Page size. Defaults to 50."),a("cursor","string","Keyset cursor from the previous page.")],responses:{"200":r({type:"object",properties:{entries:{type:"array",items:o("ActivityEntry")},limit:{type:"integer"},hasNextPage:{type:"boolean"},cursor:{type:["string","null"]}}},"One page of the trail.")}}}]}function gt(l){let u=l._config,b=["jobs"];return[{method:"POST",path:"/jobs/run",source:"core",handler:async(m)=>{g(u,m.actor,"jobs:run");let k=P(m.query.get("limit"),"limit",1,1000),A=m.query.getAll("only"),J=await l.jobs.run({...k===void 0?{}:{limit:k},...A.length===0?{}:{only:A}});return await l.drainEvents(),i(J)},openapi:{summary:"Run the due background work",description:`Registered sweeps: ${l.jobs.list().join(", ")||"none"}. Reports whether more was waiting than \`limit\` allowed, so a cron can loop instead of watching a backlog build.`,tags:b,parameters:[a("limit","integer","Items examined per job."),a("only","string","Run one job. Repeatable.")],responses:{"200":r(o("JobRunReport"),"What the sweep did."),"401":r(o("Error"),"Needs a session or an API key with `jobs:run`.")}}},{method:"GET",path:"/jobs/failures",source:"core",handler:async(m)=>{g(u,m.actor,"jobs:read");let k=m.query.get("type"),A=P(m.query.get("limit"),"limit",1,200),J=await l.jobs.failures({...k===null?{}:{type:k},...A===void 0?{}:{limit:A}});return i({failures:J})},openapi:{summary:"What failed",description:"Each row carries the payload. For a scheduled change that is the patch itself, and this row is the only remaining copy — the claim that removed it from the document had already happened when the write failed.",tags:b,parameters:[a("type","string","Job name."),a("limit","integer","Page size. Defaults to 50.")],responses:{"200":r({type:"object",properties:{failures:{type:"array",items:o("JobFailure")}}},"Recorded failures, newest first.")}}}]}function Rt(l){let u=l._config;if(u.webhooks?.enabled!==!0)return[];let b=["webhooks"];return[{method:"GET",path:"/webhooks",source:"core",handler:async(m)=>{g(u,m.actor,"webhooks:read");let k=G(m.query.get("active"),"active"),A=await l.webhooks.list(k===void 0?{}:{active:k});return i({webhooks:A})},openapi:{summary:"List the subscriptions",description:"Secrets never leave: `hasSecret` says whether this subscription signs with its own, nothing says what it is.",tags:b,parameters:[a("active","boolean","Only active, or only inactive.")],responses:{"200":r({type:"object",properties:{webhooks:{type:"array",items:o("Webhook")}}},"Every subscription.")}}},{method:"POST",path:"/webhooks",source:"core",handler:async(m)=>{let k=g(u,m.actor,"webhooks:create"),A=await f(m.request),J=await l.webhooks.create({url:v(A,"url"),events:vt(A.events,"events"),...ke(A.filters)===void 0?{}:{filters:ke(A.filters)},...Ae(A.headers)===void 0?{}:{headers:Ae(A.headers)},...typeof A.secret==="string"?{secret:A.secret}:{},...typeof A.active==="boolean"?{active:A.active}:{},actor:k,source:y(m.request)});return i({webhook:J},201)},openapi:{summary:"Subscribe to events",description:"Event types may be exact (`document.updated`), a family (`document.*`) or everything (`*`). A family keeps working when the catalogue grows, which is why it exists.",tags:b,requestBody:h({type:"object",required:["url","events"],properties:{url:{type:"string",format:"uri"},events:{type:"array",items:{type:"string"}},filters:{type:"object",properties:{collections:{type:"array",items:{type:"string"}},locales:{type:"array",items:{type:"string"}}}},headers:{type:"object",additionalProperties:{type:"string"}},secret:{type:"string",description:"Overrides webhooks.signing.secret for this one."},active:{type:"boolean"}}}),responses:{"201":r({type:"object",properties:{webhook:o("Webhook")}},"The subscription."),"422":r(o("Error"),"The URL or the event list is not usable.")}}},{method:"PATCH",path:"/webhooks/:id",source:"core",handler:async(m)=>{let k=g(u,m.actor,"webhooks:update"),A=await f(m.request),J=await l.webhooks.update({id:m.params.id,...typeof A.url==="string"?{url:A.url}:{},...A.events===void 0?{}:{events:vt(A.events,"events")},...A.filters===void 0?{}:{filters:ke(A.filters)??{}},...A.headers===void 0?{}:{headers:Ae(A.headers)??{}},...typeof A.secret==="string"?{secret:A.secret}:{},...typeof A.active==="boolean"?{active:A.active}:{},actor:k,source:y(m.request)});return i({webhook:J})},openapi:{summary:"Change a subscription",tags:b,parameters:[c("id","Webhook id.")],requestBody:h({type:"object"}),responses:{"200":r({type:"object",properties:{webhook:o("Webhook")}},"The subscription."),"404":r(o("Error"),"No such webhook.")}}},{method:"DELETE",path:"/webhooks/:id",source:"core",handler:async(m)=>{let k=g(u,m.actor,"webhooks:delete");if(!await l.webhooks.remove({id:m.params.id,actor:k}))throw e.notFound("No such webhook.");return oe()},openapi:{summary:"Unsubscribe",description:"The delivery history goes with it: it is the history of this subscription and means nothing without it.",tags:b,parameters:[c("id","Webhook id.")],responses:{"204":{description:"Removed."},"404":r(o("Error"),"No such webhook.")}}},{method:"GET",path:"/webhooks/deliveries",source:"core",handler:(m)=>bt(l,m),openapi:wt(!1)},{method:"GET",path:"/webhooks/:id/deliveries",source:"core",handler:(m)=>bt(l,m,m.params.id),openapi:wt(!0)},{method:"POST",path:"/webhooks/:id/test",source:"core",handler:async(m)=>{let k=g(u,m.actor,"webhooks:update"),A=await l.webhooks.test({id:m.params.id,actor:k});return i({delivery:A})},openapi:{summary:"Send a test event",description:"A real delivery carrying a synthetic `webhook.test` event: same signature, same headers, same retry rules. A receiver that accepts this accepts the real thing.",tags:b,parameters:[c("id","Webhook id.")],responses:{"200":r({type:"object",properties:{delivery:o("WebhookDelivery")}},"What the receiver answered.")}}},{method:"POST",path:"/webhooks/deliveries/:id/retry",source:"core",handler:async(m)=>{let k=g(u,m.actor,"webhooks:update"),A=await l.webhooks.retry({id:m.params.id,actor:k});return i({delivery:A})},openapi:{summary:"Replay a delivery",description:"A new attempt carrying the same event id, so a consumer's idempotency key still matches and the original attempt stays readable.",tags:b,parameters:[c("id","Delivery id.")],responses:{"200":r({type:"object",properties:{delivery:o("WebhookDelivery")}},"The replayed delivery."),"404":r(o("Error"),"No such delivery.")}}}]}async function bt(l,u,b){g(l._config,u.actor,"webhooks:read");let m=u.query.get("status"),k=u.query.get("eventId"),A=u.query.get("cursor"),J=P(u.query.get("limit"),"limit",1,200),C=G(u.query.get("includeBody"),"includeBody"),T=await l.webhooks.deliveries({...b===void 0?{}:{webhookId:b},...m===null?{}:{status:m},...k===null?{}:{eventId:k},...A===null?{}:{cursor:A},...J===void 0?{}:{limit:J},...C===void 0?{}:{includeBody:C}});return i(T)}function wt(l){return{summary:l?"Deliveries for one subscription":"Every delivery",description:"Newest first. `failed` is the dead-letter queue: out of attempts, still here, replayable.",tags:["webhooks"],parameters:[...l?[c("id","Webhook id.")]:[],a("status","string","pending, delivered or failed."),a("eventId","string","Every attempt at one event."),a("limit","integer","Page size. Defaults to 25."),a("cursor","string","Keyset cursor from the previous page."),a("includeBody","boolean","Includes the signed request body.")],responses:{"200":r({type:"object",properties:{entries:{type:"array",items:o("WebhookDelivery")},limit:{type:"integer"},hasNextPage:{type:"boolean"},cursor:{type:["string","null"]}}},"One page of deliveries.")}}}function vt(l,u){if(!Array.isArray(l))throw e.validation(`"${u}" has to be an array of strings.`);return l.filter((b)=>typeof b==="string")}function ke(l){if(typeof l!=="object"||l===null)return;let u=l,b=Array.isArray(u.collections)?u.collections.filter((k)=>typeof k==="string"):void 0,m=Array.isArray(u.locales)?u.locales.filter((k)=>typeof k==="string"):void 0;return{...b===void 0?{}:{collections:b},...m===void 0?{}:{locales:m}}}function Ae(l){if(typeof l!=="object"||l===null)return;let u={};for(let[b,m]of Object.entries(l))if(typeof m==="string")u[b]=m;return u}function qe(l){let u=l._config,b=u.basePath,m=new Se,k,A=()=>(k??=jn(l,m.list()),k),J=new Se;for(let O of ct(l))J.add(O);for(let O of Tr(l))m.add(O);for(let O of u.plugins)for(let M of O.routes??[])(M.root===!0?J:m).add(qr(O.id,M));m.add(ze(A));let C=kt(u.hooks?.before??[]),T=kt(u.hooks?.after??[]);return{handler:async(O)=>{let M=new URL(O.url),D=Pr(M.pathname,b);if(D===null&&J.match(O.method,M.pathname).kind!=="not-found")try{return await jt({cms:l,router:J,request:O,url:M,path:M.pathname,actor:null,meta:{}})}catch(z){return Tt(z)}if(D===null)return i({error:{message:`This handler serves ${b}; the request was for ${M.pathname}.`,code:"outside_base_path",status:404}},404);let F=null,L={},U;try{Sr(O),F=await Or(l,O);let z={request:O,path:D,method:O.method,headers:O.headers,actor:F,cms:l,meta:L};for(let B of C){if(!B.matcher(z))continue;let X=await B.handler(z);if(X instanceof Response){U=X;break}}U??=await jt({cms:l,router:m,request:O,url:M,path:D,actor:F,meta:L})}catch(z){U=Tt(z)}if(T.length===0)return U;return Ar({after:T,cms:l,request:O,path:D,actor:F,meta:L,response:U})},routes:()=>[...m.routes(b),...J.routes("")],openapi:A}}async function jt(l){let{cms:u,router:b,request:m,url:k,path:A,actor:J,meta:C}=l,T=b.match(m.method,A);if(T.kind==="method-not-allowed")return i({error:{message:`${m.method} is not allowed on ${A}.`,code:"method_not_allowed",status:405}},405,{allow:[...T.allowed,"OPTIONS"].join(", ")});if(T.kind==="not-found"){if(m.method==="OPTIONS")return new Response(null,{status:204,headers:{allow:"OPTIONS"}});return i({error:{message:`No route for ${m.method} ${A}.`,code:"not_found",status:404}},404)}let S={request:m,params:T.params,query:k.searchParams,actor:J,cms:u,db:In(()=>u.database()),database:()=>u.database(),body:()=>en(m),meta:C,json:(M,D)=>i(M,D),text:(M,D)=>_n(M,D)},O=await kr(T.route,S);if(m.method==="HEAD")return new Response(null,{status:O.status,statusText:O.statusText,headers:O.headers});return O}function kr(l,u){let b=l.middleware??[],m=-1,k=async()=>{m+=1;let A=b[m];if(!A)return l.handler(u);return A(u,k)};return k()}async function Ar(l){let{after:u,cms:b,request:m,path:k,actor:A,meta:J,response:C}=l,T=new Headers(C.headers),S={request:m,path:k,method:m.method,headers:T,status:C.status,actor:A,cms:b,meta:J};try{for(let O of u){if(!O.matcher(S))continue;let M=await O.handler(S);if(M instanceof Response)return M}}catch(O){return Tt(O)}return new Response(C.body,{status:C.status,statusText:C.statusText,headers:T})}function Tr(l){return[...Fe(l),...xe(l),...at(l),...dt(l),...Ve(l),...Xe(l),...Rt(l),...nt(l),...Be(l),...ft(l),...ht(l),...yt(l),...gt(l),...mt(l),...We(l)]}function qr(l,u){return{method:u.method,path:u.path,source:l,handler:u.handler,...u.middleware===void 0?{}:{middleware:u.middleware},...u.openapi===void 0?{}:{openapi:u.openapi}}}async function Or(l,u){try{return await l.auth.actorFromRequest(u)}catch(b){if(b instanceof e&&b.status===401)return null;throw b}}function Pr(l,u){if(u==="/")return l;if(!l.startsWith(u))return null;let b=l.slice(u.length);if(b.length===0)return"/";if(!b.startsWith("/"))return null;return b}function Sr(l){if(["GET","HEAD","OPTIONS"].includes(l.method))return;if(/^Bearer\s+\S+/i.test(l.headers.get("authorization")??""))return;let u=l.headers.get("origin");if(u!==null&&u!==new URL(l.url).origin||l.headers.get("sec-fetch-site")==="cross-site")throw e.forbidden("Cross-origin browser writes are not allowed.")}function kt(l){return[...l].map((u,b)=>({hook:u,index:b})).sort((u,b)=>(u.hook.priority??100)-(b.hook.priority??100)||u.index-b.index).map((u)=>u.hook)}
40
+ export{Ze,g,Ye,y,I,Pn,oo,ro,P,G,i,_n,oe,K,Tt,en,f,De,v,W,Re,o,c,a,q,$n,h,r,N,Ct,jn,In,Se,io,En,qe};
@@ -1,2 +1,2 @@
1
- import{w}from"./index-rw0hze6x.js";async function ct(o,r={}){let{drainEvents:t=!0,...s}=r,i=await o.jobs.run(s);if(t)await o.drainEvents();return i}function bn(o){let{cms:r,interval:t=60000,immediate:s=!0,onReport:i,onError:e,signal:f,...R}=o,b=typeof t==="number"?t:w(t),a=!1,p=null,u,c=async()=>{let n=ct(r,R);p=n;try{let m=await n;return i?.(m),m}finally{p=null}},l=()=>{if(a)return;u=setTimeout(()=>{c().catch((n)=>{if(e)e(n);else console.error("[heyo-cms] job sweep failed:",n)}).finally(l)},b),u.unref?.()},d={runNow:()=>c(),async stop(){if(a=!0,u)clearTimeout(u);await p?.catch(()=>{return})},get running(){return!a}};if(f?.addEventListener("abort",()=>void d.stop(),{once:!0}),s)c().catch((n)=>{if(e)e(n);else console.error("[heyo-cms] job sweep failed:",n)}).finally(l);else l();return d}function wn(o,r={}){return async(t,s,i)=>{let e=ct(o,r);i?.waitUntil?.(e),await e}}
2
- export{ct,bn,wn};
1
+ import{w}from"./index-rw0hze6x.js";async function dt(o,r={}){let{drainEvents:t=!0,...s}=r,i=await o.jobs.run(s);if(t)await o.drainEvents();return i}function wn(o){let{cms:r,interval:t=60000,immediate:s=!0,onReport:i,onError:e,signal:f,...R}=o,b=typeof t==="number"?t:w(t),a=!1,p=null,u,c=async()=>{let n=dt(r,R);p=n;try{let m=await n;return i?.(m),m}finally{p=null}},l=()=>{if(a)return;u=setTimeout(()=>{c().catch((n)=>{if(e)e(n);else console.error("[heyo-cms] job sweep failed:",n)}).finally(l)},b),u.unref?.()},d={runNow:()=>c(),async stop(){if(a=!0,u)clearTimeout(u);await p?.catch(()=>{return})},get running(){return!a}};if(f?.addEventListener("abort",()=>void d.stop(),{once:!0}),s)c().catch((n)=>{if(e)e(n);else console.error("[heyo-cms] job sweep failed:",n)}).finally(l);else l();return d}function vn(o,r={}){return async(t,s,i)=>{let e=dt(o,r);i?.waitUntil?.(e),await e}}
2
+ export{dt,wn,vn};
@@ -1,2 +1,2 @@
1
- import{createHash as u}from"node:crypto";import{arch as f,platform as m,release as p}from"node:os";var wo="https://telemetry.heyo.sh/v1/cms";function Ut(e,n=process.env){if(e?.enabled===!1)return{enabled:!1,reason:"telemetry: { enabled: false } in the configuration"};if(l(n.HEYO_TELEMETRY_DISABLED))return{enabled:!1,reason:"HEYO_TELEMETRY_DISABLED is set"};if(l(n.DO_NOT_TRACK))return{enabled:!1,reason:"DO_NOT_TRACK is set"};if(l(n.CI))return{enabled:!1,reason:"this is CI"};if(n.NODE_ENV==="test")return{enabled:!1,reason:"NODE_ENV is test"};return{enabled:!0,reason:"on by default; see `heyo-cms telemetry`"}}function Jt(e,n,o){let t=n._config,r=Object.values(t.collections),i=Object.values(t.globals),c=r.reduce((s,a)=>s+Object.keys(a.fields).length,0)+i.reduce((s,a)=>s+Object.keys(a.fields).length,0),d=typeof t.database==="function"?"lazy":t.database.id;return{event:e,version:o,project:y([t.basePath,d,...r.map((s)=>s.slug).sort(),...i.map((s)=>s.slug).sort()]),runtime:b(),system:{platform:m(),arch:f(),release:p()},database:d,usage:{collections:r.length,globals:i.length,fields:c,plugins:t.plugins.length,locales:t.i18n.locales.length},features:{panel:t.admin.assets!==void 0,auth:t.admin.auth!==void 0,uploads:g(t.uploads?.storage),ai:t.ai!==void 0,webhooks:t.webhooks?.enabled===!0,trash:t.trash.enabled}}}async function vo(e,n={}){let o=n.fetch??globalThis.fetch;try{return(await o(n.endpoint??wo,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(n.timeout??2000)})).ok?"sent":"failed"}catch{return"failed"}}async function yn(e){let n=Ut(e.config,e.environment);if(!n.enabled)return{decision:n,result:"skipped"};let o=Jt(e.event,e.cms,e.version),t=await vo(o,{...e.send,...e.config?.endpoint?{endpoint:e.config.endpoint}:{}});return{decision:n,result:t}}function g(e){if(e===void 0)return null;return typeof e==="function"?"lazy":e.id}function y(e){return u("sha256").update(e.join("|")).digest("hex").slice(0,16)}function b(){let e=globalThis.Bun;if(e)return{name:"bun",version:e.version};let n=globalThis.Deno;if(n)return{name:"deno",version:n.version.deno};return{name:"node",version:process.versions.node}}function l(e){return e!==void 0&&e!==""&&e!=="0"&&e!=="false"}
2
- export{wo,Ut,Jt,vo,yn};
1
+ import{createHash as u}from"node:crypto";import{arch as f,platform as m,release as p}from"node:os";var ko="https://telemetry.heyo.sh/v1/cms";function Jt(e,n=process.env){if(e?.enabled===!1)return{enabled:!1,reason:"telemetry: { enabled: false } in the configuration"};if(l(n.HEYO_TELEMETRY_DISABLED))return{enabled:!1,reason:"HEYO_TELEMETRY_DISABLED is set"};if(l(n.DO_NOT_TRACK))return{enabled:!1,reason:"DO_NOT_TRACK is set"};if(l(n.CI))return{enabled:!1,reason:"this is CI"};if(n.NODE_ENV==="test")return{enabled:!1,reason:"NODE_ENV is test"};return{enabled:!0,reason:"on by default; see `heyo-cms telemetry`"}}function Wt(e,n,o){let t=n._config,r=Object.values(t.collections),i=Object.values(t.globals),c=r.reduce((s,a)=>s+Object.keys(a.fields).length,0)+i.reduce((s,a)=>s+Object.keys(a.fields).length,0),d=typeof t.database==="function"?"lazy":t.database.id;return{event:e,version:o,project:y([t.basePath,d,...r.map((s)=>s.slug).sort(),...i.map((s)=>s.slug).sort()]),runtime:b(),system:{platform:m(),arch:f(),release:p()},database:d,usage:{collections:r.length,globals:i.length,fields:c,plugins:t.plugins.length,locales:t.i18n.locales.length},features:{panel:t.admin.assets!==void 0,auth:t.admin.auth!==void 0,uploads:g(t.uploads?.storage),ai:t.ai!==void 0,webhooks:t.webhooks?.enabled===!0,trash:t.trash.enabled}}}async function Ro(e,n={}){let o=n.fetch??globalThis.fetch;try{return(await o(n.endpoint??ko,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(n.timeout??2000)})).ok?"sent":"failed"}catch{return"failed"}}async function bn(e){let n=Jt(e.config,e.environment);if(!n.enabled)return{decision:n,result:"skipped"};let o=Wt(e.event,e.cms,e.version),t=await Ro(o,{...e.send,...e.config?.endpoint?{endpoint:e.config.endpoint}:{}});return{decision:n,result:t}}function g(e){if(e===void 0)return null;return typeof e==="function"?"lazy":e.id}function y(e){return u("sha256").update(e.join("|")).digest("hex").slice(0,16)}function b(){let e=globalThis.Bun;if(e)return{name:"bun",version:e.version};let n=globalThis.Deno;if(n)return{name:"deno",version:n.version.deno};return{name:"node",version:process.versions.node}}function l(e){return e!==void 0&&e!==""&&e!=="0"&&e!=="false"}
2
+ export{ko,Jt,Wt,Ro,bn};
@@ -1,2 +1,2 @@
1
- var Ae="x-heyo-signature";async function ke(e,n,t=Math.floor(Date.now()/1000)){let o=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",o,new TextEncoder().encode(`${t}.${e}`));return`t=${t},v1=${u(new Uint8Array(a))}`}async function Cn(e){let n=new Map(e.header.split(",").map((r)=>{let[l,d]=r.split("=");return[l?.trim()??"",d?.trim()??""]})),t=Number(n.get("t")),o=n.get("v1");if(!Number.isFinite(t)||!o)return!1;let a=e.now??Math.floor(Date.now()/1000),c=e.toleranceSeconds??300;if(Math.abs(a-t)>c)return!1;let s=(await ke(e.body,e.secret,t)).split("v1=")[1]??"";if(s.length!==o.length)return!1;let i=0;for(let r=0;r<s.length;r+=1)i|=s.charCodeAt(r)^o.charCodeAt(r);return i===0}function u(e){let n="";for(let t of e)n+=t.toString(16).padStart(2,"0");return n}
2
- export{Ae,ke,Cn};
1
+ var Ae="x-heyo-signature";async function ke(e,n,t=Math.floor(Date.now()/1000)){let o=await crypto.subtle.importKey("raw",new TextEncoder().encode(n),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",o,new TextEncoder().encode(`${t}.${e}`));return`t=${t},v1=${u(new Uint8Array(a))}`}async function On(e){let n=new Map(e.header.split(",").map((r)=>{let[l,d]=r.split("=");return[l?.trim()??"",d?.trim()??""]})),t=Number(n.get("t")),o=n.get("v1");if(!Number.isFinite(t)||!o)return!1;let a=e.now??Math.floor(Date.now()/1000),c=e.toleranceSeconds??300;if(Math.abs(a-t)>c)return!1;let s=(await ke(e.body,e.secret,t)).split("v1=")[1]??"";if(s.length!==o.length)return!1;let i=0;for(let r=0;r<s.length;r+=1)i|=s.charCodeAt(r)^o.charCodeAt(r);return i===0}function u(e){let n="";for(let t of e)n+=t.toString(16).padStart(2,"0");return n}
2
+ export{Ae,ke,On};
@@ -1,2 +1,2 @@
1
- async function Pe(e){let t=crypto.getRandomValues(new Uint8Array(16)),n=await o(e,t,1e5);return["pbkdf2-sha256",1e5,ie(t),ie(n)].join("$")}async function pn(e,t){if(!t)return!1;let[n,r,s,i]=t.split("$");if(n!=="pbkdf2-sha256"||!r||!s||!i)return!1;let a=await o(e,mn(s),Number(r));return c(ie(a),i)}async function o(e,t,n){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),"PBKDF2",!1,["deriveBits"]),s=await crypto.subtle.deriveBits({name:"PBKDF2",salt:t,iterations:n,hash:"SHA-256"},r,256);return new Uint8Array(s)}function we(e=32){return ie(crypto.getRandomValues(new Uint8Array(e)))}async function re(e){let t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e));return l(new Uint8Array(t))}function c(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r+=1)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}function ie(e){let t="";for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function mn(e){let t=e.replaceAll("-","+").replaceAll("_","/"),n=atob(t.padEnd(Math.ceil(t.length/4)*4,"=")),r=new Uint8Array(n.length);for(let s=0;s<n.length;s+=1)r[s]=n.charCodeAt(s);return r}function l(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}async function fo(e){let t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e));return ie(new Uint8Array(t))}
2
- export{Pe,pn,we,re,ie,mn,fo};
1
+ async function Pe(e){let t=crypto.getRandomValues(new Uint8Array(16)),n=await o(e,t,1e5);return["pbkdf2-sha256",1e5,ie(t),ie(n)].join("$")}async function mn(e,t){if(!t)return!1;let[n,r,s,i]=t.split("$");if(n!=="pbkdf2-sha256"||!r||!s||!i)return!1;let a=await o(e,fn(s),Number(r));return c(ie(a),i)}async function o(e,t,n){let r=await crypto.subtle.importKey("raw",new TextEncoder().encode(e),"PBKDF2",!1,["deriveBits"]),s=await crypto.subtle.deriveBits({name:"PBKDF2",salt:t,iterations:n,hash:"SHA-256"},r,256);return new Uint8Array(s)}function we(e=32){return ie(crypto.getRandomValues(new Uint8Array(e)))}async function re(e){let t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e));return l(new Uint8Array(t))}function c(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r+=1)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}function ie(e){let t="";for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"")}function fn(e){let t=e.replaceAll("-","+").replaceAll("_","/"),n=atob(t.padEnd(Math.ceil(t.length/4)*4,"=")),r=new Uint8Array(n.length);for(let s=0;s<n.length;s+=1)r[s]=n.charCodeAt(s);return r}function l(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}async function bo(e){let t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e));return ie(new Uint8Array(t))}
2
+ export{Pe,mn,we,re,ie,fn,bo};
@@ -1,2 +1,2 @@
1
- var l={ß:"ss",æ:"ae",œ:"oe",ø:"o",ð:"d",þ:"th",đ:"d",ł:"l",ı:"i",ĸ:"k",ŋ:"n",ŧ:"t"},a={а:"a",б:"b",в:"v",г:"h",ґ:"g",д:"d",е:"e",є:"ie",ж:"zh",з:"z",и:"y",і:"i",ї:"i",й:"i",к:"k",л:"l",м:"m",н:"n",о:"o",п:"p",р:"r",с:"s",т:"t",у:"u",ў:"u",ф:"f",х:"kh",ц:"ts",ч:"ch",ш:"sh",щ:"shch",ь:"",ю:"iu",я:"ia",ъ:"",ы:"y",э:"e",ё:"e"},u={α:"a",β:"v",γ:"g",δ:"d",ε:"e",ζ:"z",η:"i",θ:"th",ι:"i",κ:"k",λ:"l",μ:"m",ν:"n",ξ:"x",ο:"o",π:"p",ρ:"r",σ:"s",ς:"s",τ:"t",υ:"y",φ:"f",χ:"ch",ψ:"ps",ω:"o"},Mn="^[a-z0-9]+(?:-[a-z0-9]+)*$",p=new RegExp("^[a-z0-9]+(?:-[a-z0-9]+)*$");function sn(e){return p.test(e)}function ye(e,n={}){let t=n.maxLength??96,c=Array.from(e.toLowerCase()).map((r)=>l[r]??a[r]??u[r.normalize("NFD").replace(/[\u0300-\u036f]/g,"")]??r).join("").normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/['’`]/g,"").replace(/[^a-z0-9]+/g,"-"),s=g(c);if(s.length<=t)return s;let i=s.slice(0,t),o=i.lastIndexOf("-");return o>=t/2?i.slice(0,o):g(i)}function g(e){let n=0,t=e.length;while(n<t&&e.charCodeAt(n)===45)n++;while(t>n&&e.charCodeAt(t-1)===45)t--;return e.slice(n,t)}
2
- export{Mn,sn,ye};
1
+ var l={ß:"ss",æ:"ae",œ:"oe",ø:"o",ð:"d",þ:"th",đ:"d",ł:"l",ı:"i",ĸ:"k",ŋ:"n",ŧ:"t"},a={а:"a",б:"b",в:"v",г:"h",ґ:"g",д:"d",е:"e",є:"ie",ж:"zh",з:"z",и:"y",і:"i",ї:"i",й:"i",к:"k",л:"l",м:"m",н:"n",о:"o",п:"p",р:"r",с:"s",т:"t",у:"u",ў:"u",ф:"f",х:"kh",ц:"ts",ч:"ch",ш:"sh",щ:"shch",ь:"",ю:"iu",я:"ia",ъ:"",ы:"y",э:"e",ё:"e"},u={α:"a",β:"v",γ:"g",δ:"d",ε:"e",ζ:"z",η:"i",θ:"th",ι:"i",κ:"k",λ:"l",μ:"m",ν:"n",ξ:"x",ο:"o",π:"p",ρ:"r",σ:"s",ς:"s",τ:"t",υ:"y",φ:"f",χ:"ch",ψ:"ps",ω:"o"},Dn="^[a-z0-9]+(?:-[a-z0-9]+)*$",p=new RegExp("^[a-z0-9]+(?:-[a-z0-9]+)*$");function an(e){return p.test(e)}function ye(e,n={}){let t=n.maxLength??96,c=Array.from(e.toLowerCase()).map((r)=>l[r]??a[r]??u[r.normalize("NFD").replace(/[\u0300-\u036f]/g,"")]??r).join("").normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/['’`]/g,"").replace(/[^a-z0-9]+/g,"-"),s=g(c);if(s.length<=t)return s;let i=s.slice(0,t),o=i.lastIndexOf("-");return o>=t/2?i.slice(0,o):g(i)}function g(e){let n=0,t=e.length;while(n<t&&e.charCodeAt(n)===45)n++;while(t>n&&e.charCodeAt(t-1)===45)t--;return e.slice(n,t)}
2
+ export{Dn,an,ye};
@@ -0,0 +1,2 @@
1
+ import{n}from"./index-x72d1hzh.js";var r=new Map;function go(i){let t=r.get(i.name);if(t!==void 0&&t!==i)throw new n(`fields.${i.name}`,"a different field type with this name is already registered");r.set(i.name,i)}function Xo(i){return r.get(i)}function er(){return[...r.keys()].sort()}function C(i){return go(i),(t)=>{let e=t??{},o=i.optionsSchema?l(i.name,i.optionsSchema,e):e;if(e.admin!==void 0){for(let u of Object.keys(e.admin))if(u!=="hidden"&&u!=="placeholder")throw new n(`fields.${i.name}.admin.${u}`,"unsupported admin option; use only hidden or placeholder")}if(e.generate!==void 0&&typeof e.generate!=="function")throw new n(`fields.${i.name}.generate`,"must be a function");let d=e.required===!0;if(d&&e.admin?.hidden===!0)throw new n(`fields.${i.name}.admin.hidden`,"a hidden field cannot be required; remove required or show the field");let a=e.localized===!0,s=i.column(o);if(a&&(e.unique===!0||s.unique===!0))throw new n(`fields.${i.name}`,"a localized field cannot be unique in v1; enforce it in a hook instead");if(c(i.name,i.aiActions,e.ai),e.aiGenerate!==void 0){if(!["text","textarea","richText","email","url"].includes(i.name))throw new n(`fields.${i.name}.aiGenerate`,"generation currently supports text, textarea, richText, email and url fields")}return{type:i.name,options:e,schema:f(i.schema(o),d),column:{...s,nullable:s.nullable??!d,...e.unique===void 0?{}:{unique:e.unique},...e.index===void 0?{}:{index:e.index},storage:s.storage??"document"},localized:a,required:d,...e.default===void 0?{}:{defaultValue:e.default},...e.generate===void 0?{}:{generate:e.generate},...e.aiGenerate===void 0?{}:{aiGenerate:e.aiGenerate},...e.ai===void 0?{}:{ai:e.ai},...e.access===void 0?{}:{access:e.access},...e.admin===void 0?{}:{admin:e.admin},...typeof e.relationTo==="string"?{relationTo:e.relationTo}:{}}}}function l(i,t,e){let o=t.safeParse(e);if(!o.success){let d=o.error.issues[0],a=d?.path.length?` (${d.path.join(".")})`:"";throw new n(`fields.${i}`,`${d?.message??"invalid options"}${a}`)}return o.data}function f(i,t){return t?i:i.nullable()}function c(i,t,e){if(!e)return;if(e.generate!==void 0)throw new n(`fields.${i}.ai.generate`,"generation is configured with aiGenerate, not as an ai action");if(!t)return;for(let o of Object.keys(e)){let d=t.includes(o),a=typeof e[o]==="object";if(!d&&!a)throw new n(`fields.${i}.ai.${o}`,`unknown AI action; ${i} supports ${t.join(", ")}. Custom actions need an object with a prompt.`)}}
2
+ export{go,Xo,er,C};