@heyo-sh/heyo-cms 0.2.0 → 0.3.0

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 (45) 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 +1 -1
  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/editor.js +1 -1
  13. package/dist/engine/documents.d.ts.map +1 -1
  14. package/dist/fields/builtin.d.ts +44 -1
  15. package/dist/fields/builtin.d.ts.map +1 -1
  16. package/dist/fields/registry.d.ts.map +1 -1
  17. package/dist/fields.js +1 -1
  18. package/dist/http/routes/ai.d.ts +1 -1
  19. package/dist/http.js +1 -1
  20. package/dist/index-1xz3pmtk.js +3 -0
  21. package/dist/{index-8x2em6c8.js → index-63x90ejh.js} +5 -5
  22. package/dist/{index-g041kfa5.js → index-a1jv6396.js} +5 -5
  23. package/dist/index-axpzy2hj.js +2 -0
  24. package/dist/index-b79c3k6g.js +2 -0
  25. package/dist/{index-rg9tpg3p.js → index-dtwe2z69.js} +2 -2
  26. package/dist/{index-2prbs05k.js → index-eqfcxn71.js} +1 -1
  27. package/dist/{index-vkwzdfte.js → index-mpkhjgvk.js} +2 -2
  28. package/dist/{index-hbx67b20.js → index-ptttzgtp.js} +2 -2
  29. package/dist/index-vf163r10.js +2 -0
  30. package/dist/index-wbc2xh4h.js +40 -0
  31. package/dist/{index-kc1j3h6z.js → index-zyg6bfdq.js} +2 -2
  32. package/dist/index.js +1 -1
  33. package/dist/migrations.js +1 -1
  34. package/dist/{run-bjge3v8e.js → run-rqmtv3cn.js} +2 -2
  35. package/dist/types/fields.d.ts +2 -2
  36. package/dist/types/fields.d.ts.map +1 -1
  37. package/dist/types/panel.d.ts +2 -0
  38. package/dist/types/panel.d.ts.map +1 -1
  39. package/dist/uploads.js +2 -2
  40. package/package.json +1 -1
  41. package/dist/index-67e458ws.js +0 -2
  42. package/dist/index-6w10rch0.js +0 -3
  43. package/dist/index-cv0hpz3d.js +0 -2
  44. package/dist/index-hpast878.js +0 -40
  45. package/dist/index-sgdnp83t.js +0 -2
@@ -1,40 +0,0 @@
1
- import{s,R,zn,t}from"./index-6m1kjq9g.js";import{E}from"./index-sgdnp83t.js";import{me,_,I}from"./index-8x2em6c8.js";import{no,ue}from"./index-ypcmns0v.js";import{Qe}from"./index-91fa1gzf.js";import{d}from"./index-9g2n7xa6.js";import{Te,Q}from"./index-hbx67b20.js";import{p}from"./index-s9x07zb3.js";import{be}from"./index-2prbs05k.js";import{w,se}from"./index-rw0hze6x.js";import{e,n}from"./index-x72d1hzh.js";import{z as Jt}from"zod";function Ye(l){if(!l)throw e.unauthorized("This endpoint needs a session or an API key.");return l}function g(l,u,b){let m=Ye(u);if(!l.rbac)return m;if(!E(m,b))throw e.forbidden(`This action needs "${b}".`);return m}function Ze(l){let u=Ye(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 qt=new Set(["where","sort","limit","offset","cursor","count","depth","locale","fallbackLocale","includeTrashed","permanent","refresh","force","richText"]),Ot={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"},Pt=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 j(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 On(l,u,b){let m=j(l,b),k=m.locale??null,A=Xn(l,u,k),C=eo(l.get("sort"),u,k),J=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},...C===void 0?{}:{orderBy:C},...J===void 0?{}:{limit:J},...T===void 0?{}:{offset:T},...S===null?{}:{cursor:S},...G(l.get("count"),"count")===!0?{count:!0}:{}}}function Xn(l,u,b){let m=[],k=l.get("where");if(k!==null&&k.trim().length>0)m.push(Ct(k,u,b));for(let[A,C]of l.entries()){if(qt.has(A))continue;let J=/^([A-Za-z_][A-Za-z0-9_.]*)(?:\[([a-z]+)\])?$/.exec(A);if(!J)throw e.validation(`"${A}" is not a filter; see the OpenAPI document for the query syntax.`);let[,T="",S="eq"]=J;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"?C.split(",").map((D)=>Ce(D,O)):Ce(C,O)})}if(m.length===0)return;return m.length===1?m[0]:s(...m)}function Ct(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=(C)=>{if(k+=1,k>Pe)throw e.validation(`"where" has more than ${Pe} conditions.`);if(typeof C!=="object"||C===null||Array.isArray(C))throw e.validation('every node of "where" has to be an object.');let J=C;switch(J.kind){case"and":case"or":{let T=J.conditions;if(!Array.isArray(T))throw e.validation(`"${J.kind}" needs a "conditions" array.`);let S=T.map(A);return J.kind==="and"?s(...S):R(...S)}case"not":return zn(A(J.condition));case"compare":{let{path:T,operator:S}=J;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(J.value,O)}}default:throw e.validation(`"${String(J.kind)}" is not a filter node.`)}};return A(m)}function eo(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 C=A.startsWith("-"),J=C?A.slice(1):A;m.push({path:fe(J,u,b).path,direction:C?"desc":"asc"})}return m.length===0?void 0:m}function fe(l,u,b){let[m="",...k]=l.split("."),A=Ot[m];if(A!==void 0&&k.length===0)return{path:A,type:Pt.has(A)?"timestamp":"text"};let C=u.fields[m];if(!C)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 J=C.localized&&k.length===0?[b??""]:k;return{path:[m,...J.filter((S)=>S.length>0)].join("."),type:$t(C.column.type)}}function $t(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 C of u)for(let[J,T]of Object.entries(b.fields)){if(T.type!=="richText")continue;let S=C[J];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(C)=>{let J=A.get(C);return J?l.media.image(J,m===null?{}:{locale:m}):null}}var It="application/json; charset=utf-8";function i(l,u=200,b){return new Response(JSON.stringify(l??null),{status:u,headers:{"content-type":It,...b}})}function Pn(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 St(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 Xt(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 Xt(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`."),"422":r(o("Error"),"Validation failed.")}}function Tt(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 xe(l){let{_config:u,api:b}=l,m=[],k=ye(u.basePath);for(let A of Object.values(u.collections)){let C=A.name,J=`/${A.slug}`,T=[A.slug],S=b[C];if(!S)continue;m.push({method:"GET",path:J,source:"core",handler:async(O)=>{let M=await S.find(On(O.query,A,{actor:O.actor,source:y(O.request)})),D=ne(O.query.get("richText"));if(D){let x=await ie(l,M.docs,A,O.query.get("locale"));Ee(M.docs,{collection:A,format:D,mediaUrl:k,...x===void 0?{}:{mediaImage:x}})}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(F(A))},limit:{type:"integer"},totalDocs:{type:["integer","null"]},hasNextPage:{type:"boolean"},cursor:{type:["string","null"]}}},"One page of documents.")}}},{method:"POST",path:J,source:"core",handler:async(O)=>{let M=await f(O.request),D=await S.create({...j(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(F(A)),"The created document.",201)}},{method:"GET",path:`${J}/:id`,source:"core",handler:async(O)=>{let M=await S.findById(Z(O.params.id),j(O.query,{actor:O.actor,source:y(O.request)}));if(!M)throw e.notFound(`No ${C} with id "${O.params.id}"`);let D=ne(O.query.get("richText"));if(D){let x=await ie(l,[M],A,O.query.get("locale"));ae(M,{collection:A,format:D,mediaUrl:k,...x===void 0?{}:{mediaImage:x}})}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:Tt(o(F(A)),"The document.")}},{method:"PATCH",path:`${J}/:id`,source:"core",handler:async(O)=>{let M=await f(O.request),D=Me(O.request,M),x=await S.update({...j(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id),data:De(M),...D===void 0?{}:{expectedRevision:D}});return x===null?K():i(x)},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(F(A)),"The document as it now stands.")}},{method:"DELETE",path:`${J}/:id`,source:"core",handler:async(O)=>{let M=G(O.query.get("permanent"),"permanent")===!0,D={...j(O.query,{actor:O.actor,source:y(O.request)}),id:Z(O.params.id)},x=M?await S.purge(D):await S.delete(D);return x===null?K():i(x)},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(F(A)),"The document as it was when it was removed.")}},{method:"POST",path:`${J}/:id/restore`,source:"core",handler:async(O)=>{let M=await S.restore({...j(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(F(A)),"The restored document."),"409":r(o("Error"),"The document is not in the trash.")}}},{method:"POST",path:`${J}/:id/schedule`,source:"core",handler:async(O)=>{let M=await f(O.request),D=await S.schedule({...j(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(F(A)),"The document carrying the pending change.")}},{method:"DELETE",path:`${J}/:id/schedule`,source:"core",handler:async(O)=>{let M=await S.unschedule({...j(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(F(A)),"The document."),"409":r(o("Error"),"Nothing was scheduled.")}}}),m.push(...Et(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 Et(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),C=a("field","string","The slug field; the first one by default."),J=[{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,{...j(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},C,...q,re],responses:{"200":r({type:"object",properties:{id:{type:"string"},field:{type:"string"},locale:{type:["string","null"]},current:{type:"boolean"},document:o(F(l))}},"Where the slug leads."),"404":r(o("Error"),"No readable document has this slug.")}}}];if(!A)return J;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 J.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."),C,...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},C,...q],responses:{"200":r(ge,"The previous slugs now.")}}}),J}function Fe(l){let{_config:u,globals:b}=l,m=[],k=ye(u.basePath);for(let A of Object.values(u.globals)){let C=b[A.name];if(!C)continue;let J=`/globals/${A.slug}`,T=["globals"];m.push({method:"GET",path:J,source:"core",handler:async(S)=>{let O=await C.get(j(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(F(A)),"The singleton.")}}},{method:"PATCH",path:J,source:"core",handler:async(S)=>{let O=await f(S.request),M=Me(S.request,O),D=await C.update({...j(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(F(A)),"The singleton.")}},{method:"POST",path:`${J}/schedule`,source:"core",handler:async(S)=>{let O=await f(S.request),M=await C.schedule({...j(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(F(A)),"The singleton.")}},{method:"DELETE",path:`${J}/schedule`,source:"core",handler:async(S)=>{let O=await C.unschedule(j(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(F(A)),"The singleton."),"409":r(o("Error"),"Nothing was scheduled.")}}})}return m}function F(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 Mt={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 $n(l,u){let b=l._config,m=Object.create(null);for(let k of u){let A=Bt(k.path),C=m[A]??{};C[k.method.toLowerCase()]=Dt(k),m[A]=C}return{openapi:"3.1.0",info:{title:`${b.admin.title??"Heyo CMS"} — content API`,version:Ht(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:Ut(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 Dt(l){let u=l.openapi,b=[...u?.parameters??[]];for(let m of Wt(l.path))if(!b.some((k)=>k.name===m))b.push({name:m,in:"path",required:!0,schema:{type:"string"}});return{operationId:zt(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[F(m)]=Ft(m),b[Y(m)]=Nt(m);return b}function Ft(l){let u={};for(let[b,m]of Object.entries(l.fields))u[b]=Le(m);return{type:"object",title:l.label,...l.kind==="global"?{description:"A singleton. The timestamps are null until it is first saved."}:{},properties:{...u,...Mt}}}function Nt(l){let u={};for(let[b,m]of Object.entries(l.fields))u[b]=Le(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 Le(l){let u;try{let m=Jt.toJSONSchema(l.schema,{io:"input",unrepresentable:"any"});delete m.$schema,u=Lt(m)}catch{u=_t(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:C,...J}=l;return{...J,...k,...typeof A==="string"?{type:[A,"null"]}:{}}}function _t(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 Ut(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 Bt(l){return l.split("/").map((u)=>u.startsWith(":")?`{${u.slice(1)}}`:u==="*"?"{path}":u).join("/")}function Wt(l){return l.split("/").filter((u)=>u.startsWith(":")||u==="*").map((u)=>u==="*"?"path":u.slice(1))}function zt(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 Ht(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,C)=>d(A,C).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=Vt(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,C)=>Number(A.wildcard)-Number(C.wildcard)||C.specificity-A.specificity||C.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=_e(u),m=new Set,k=l==="HEAD"?"GET":l;for(let A of this.compiled){let C=Gt(A,b);if(!C)continue;if(A.method===k)return{kind:"found",route:A,params:C};m.add(A.method)}if(m.size>0)return{kind:"method-not-allowed",allowed:[...m].sort()};return{kind:"not-found"}}}function Gt(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]=Kt(A)}return b}function Vt(l,u){if(!l.startsWith("/"))throw new n(u==="core"?"routes":`plugins.${u}.routes`,`route path "${l}" must start with "/"`);let b=_e(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 _e(l){return l.split("/").filter((u)=>u.length>0)}function Kt(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"),C=k.query.get("search"),J=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)},...C===null?{}:{search:C},...J===void 0?{}:{limit:J},...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"),C=await f(k.request),J=Re(C,"roles"),T=W(C,"name"),S=W(C,"locale"),O=W(C,"status"),M=await l.auth.users.update(k.params.id,{actor:A,...T===void 0?{}:{name:T},...J===void 0?{}:{roles:J},...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"),C=await f(k.request),J=Re(C,"roles"),T=W(C,"name"),S=await l.auth.users.invite({email:v(C,"email"),actor:A,...J===void 0?{}:{roles:J},...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"),C=k.params.id;if(C===A.id)throw e.conflict("You cannot remove your own account.");return await l.auth.users.remove(C,{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),C=Re(A,"permissions")??[],J=W(A,"label"),T=await l.auth.roles.create({name:v(A,"name"),permissions:C,...J===void 0?{}:{label:J}});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),C=Re(A,"permissions"),J=W(A,"label"),T=await l.auth.roles.update(k.params.name,{...C===void 0?{}:{permissions:C},...J===void 0?{}:{label:J}});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,C)=>{if(!m)return Yt(l);let J=await m.asset(A)??(Qt(A)?null:await m.asset(""));if(!J)return i({error:{message:"Not found.",code:"not_found",status:404}},404);let T=J.etag;if(T&&C.headers.get("if-none-match")===T)return new Response(null,{status:304,headers:{etag:T}});return new Response(J.body,{status:200,headers:{"content-type":J.contentType,"cache-control":J.cacheControl??(Zt(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 Qt(l){return(l.split("/").at(-1)??"").includes(".")}function Zt(l){return/[.-][0-9a-f]{8,}\.[a-z0-9]+$/i.test(l)}function Yt(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}`,C=Object.values(k.fields).some((J)=>J.aiGenerate!==void 0||J.ai!==void 0&&Object.keys(J.ai).length>0);if(Object.values(k.fields).some((J)=>J.localized))b.push({method:"POST",path:k.kind==="global"?`${A}/ai/translate-variant`:`${A}/:id/ai/translate-variant`,source:"core",handler:async(J)=>{let T=await f(J.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 x=k.fields[D];if(!x?.localized||!["text","textarea","richText","email","url"].includes(x.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:J.params.id},actor:J.actor,source:y(J.request),locale:S,sourceLocale:O,signal:J.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(C){let J=async(T,S)=>{let O=await nr(T.request),M=O.document,D=T.query.get("locale")??Ge(O.locale),x=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},...x===void 0?{}:{sourceLocale:x},signal:T.request.signal});return i(L)};b.push({method:"POST",path:`${A}/:id/ai/:field/:action`,source:"core",handler:(T)=>J(T,T.params.id),openapi:He(k,!0)},{method:"POST",path:`${A}/ai/:field/:action`,source:"core",handler:(T)=>J(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),C=no(A.pageContext);return tr((J)=>l.ai.runAssistant({message:v(A,"message"),history:rr(A.history),actor:k,source:y(m.request),signal:J,...typeof A.conversationId==="string"?{conversationId:A.conversationId}:{},...typeof A.locale==="string"?{locale:A.locale}:{},...C===void 0?{}:{pageContext:C}}),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 C=l(A)[Symbol.asyncIterator](),J=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 C.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(),C.return?.().catch(()=>{return})}});return new Response(J,{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),C=await l.ai.setCredential({provider:m.params.provider,token:v(A,"token"),actor:k,source:y(m.request)});return i({provider:C})},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 legacy `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((J)=>J.generate))continue;let k=m.kind==="global"?`/globals/${m.slug}`:`/${m.slug}`,A=async(J,T)=>{let S=J.params.field,O=m.fields[S];if(!O?.generate)throw e.notFound(`No generator for ${m.name}.${S}.`);let M=await f(J.request),D=typeof M.locale==="string"?M.locale:u.i18n.defaultLocale;if(!u.i18n.locales.includes(D))throw e.validation(`Unknown locale "${D}".`);let x=J.actor,L=ar(M.document)?M.document:{},U=m.kind==="global"||T?"update":"create",z=U==="update"?await _(m,{actor:x,operation:U,locale:D,rbac:u.rbac}):void 0,B=await or(l,m,T,x,D);if(O.access?.read&&!O.access.read({actor:x,locale:D,record:B}))throw e.notFound(`No generator for ${m.name}.${S}.`);let X=await _(m,{actor:x,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((At)=>H.query(At,{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}".`)}I(m,{[S]:null},x,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:x,locale:D,record:B}))continue;if(H in L||H in B)Oe[H]=H in L?L[H]??null:B[H]??null}let jt=await O.generate({fields:Oe,locale:D}),le=O.schema.safeParse(jt);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})},C=(J)=>({summary:`Generate a ${m.label} field without AI`,tags:["content"],parameters:[c("field","Field with a generate function."),...J?[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:(J)=>A(J,J.params.id),openapi:C(!0)});b.push({method:"POST",path:`${k}/generate/:field`,source:"core",handler:(J)=>A(J),openapi:C(!1)})}return b}async function or(l,u,b,m,k){if(u.kind==="global"){let J=l.globals[u.name];return J?J.get({actor:m,locale:k}):{}}if(!b)return{};let C=await l.api[u.name]?.findById(b,{actor:m,locale:k});if(!C)throw e.notFound(`No ${u.name} with id "${b}", or it is not readable.`);return C}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=[],C=(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 C(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 C(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 C(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 C(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 J=[...["github","google","microsoft"].filter((T)=>m.has(T)),...b.sso?["oidc"]:[]];if(J.length>0)A.push({method:"GET",path:"/auth/oauth/:provider",source:"core",handler:async(T)=>{let S=st(T.params.provider,J),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: ${J.join(", ")}.`,tags:k,security:!1,parameters:[c("provider",J.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,J),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)}),x=D.redirectTo;if(D.status==="signed-in"&&x)return new Response(null,{status:302,headers:{location:x,"set-cookie":tt(D.token,D.expiresAt,T.request,u.basePath)}});return C(D,T.request)},openapi:{summary:"Finish an OAuth or SSO sign-in",tags:k,security:!1,parameters:[c("provider",J.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=Ze(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=Ze(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=Ze(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 C(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=Ye(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=Ze(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=Ye(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),C=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}/${C}?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),C=await l.media.upload({...A,actor:k.actor,source:y(k.request)});return i(C,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"),C=k.query.get("mimeType"),J=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)},...C===null?{}:{mimeType:C},...J===null?{}:{search:J},...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:Tt(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),C=await l.media.update({id:te(k.params.id),actor:k.actor,source:y(k.request),...pr(A)});return i(C)},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",C=await l.media.variant({id:te(k.params.id),...A==="original"?{}:{variant:A},refresh:G(k.query.get("refresh"),"refresh")===!0});if(!C)throw e.notFound("No such file.");return it(k.request,C)},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 C=A.params.id??"",J=A.params.variant;if(!/^[A-Za-z0-9_-]+$/.test(C)||J!==void 0&&!m.has(J))return ot();let T=await l.media.variant({id:C,...J===void 0?{}:{variant:J}}).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 to=["usages","keep","collect","upload"];async function lr(l){let u=l.headers.get("content-type")??"";if(u.includes("multipart/form-data")){let J=await l.formData(),T=J.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=J.get("metadata"),O=J.get("filename"),M=J.get("alt"),D=J.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"),C=m.searchParams.get("locale");return{file:b,filename:k,...u.length>0?{contentType:u}:{},...A===null?{}:{alt:A},...C===null?{}:{locale:C}}}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 jn(l,u,b,m={}){let k=l.admin.auth,A=new Set(k?.methods??[]),C=l.uploads?.images,J=l.uploads?.retention,T=l.trash.purgeAfter,S=J?.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(C?.variants?.sizes??{}),variantMode:C?.variants?.mode??null,focalPoint:C?.focalPoint===!0,crop:C?.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])=>wr(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 wr(l,u,b){let m=u.options,k=u.admin??{};return{name:l,type:u.type,label:typeof m.label==="string"?m.label:ke(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(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)}}}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?ke(b):m.label??ke(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 ke(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 pt(l){return[{method:"GET",path:"/panel",source:"core",handler:async(u)=>i(jn(l._config,u.actor,l.locales.list(),{setupRequired:await kr(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 kr(l){try{return await l.auth.setup.required()}catch{return!1}}function mt(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),C=m.query.get("cursor"),J=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},...C===null?{}:{cursor:C},...J===null?{}:{locale:J}});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 ht(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 ft(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"),C=b.query.get("action"),J=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},...C===null?{}:{action:C},...J===null?{}:{source:J},...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 yt(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"),C=await l.jobs.run({...k===void 0?{}:{limit:k},...A.length===0?{}:{only:A}});return await l.drainEvents(),i(C)},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),C=await l.jobs.failures({...k===null?{}:{type:k},...A===void 0?{}:{limit:A}});return i({failures:C})},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 vt(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),C=await l.webhooks.create({url:v(A,"url"),events:wt(A.events,"events"),...je(A.filters)===void 0?{}:{filters:je(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:C},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),C=await l.webhooks.update({id:m.params.id,...typeof A.url==="string"?{url:A.url}:{},...A.events===void 0?{}:{events:wt(A.events,"events")},...A.filters===void 0?{}:{filters:je(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:C})},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)=>gt(l,m),openapi:bt(!1)},{method:"GET",path:"/webhooks/:id/deliveries",source:"core",handler:(m)=>gt(l,m,m.params.id),openapi:bt(!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 gt(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"),C=P(u.query.get("limit"),"limit",1,200),J=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},...C===void 0?{}:{limit:C},...J===void 0?{}:{includeBody:J}});return i(T)}function bt(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 wt(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 je(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??=$n(l,m.list()),k),C=new Se;for(let O of ct(l))C.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?C:m).add(qr(O.id,M));m.add(ze(A));let J=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&&C.match(O.method,M.pathname).kind!=="not-found")try{return await Rt({cms:l,router:C,request:O,url:M,path:M.pathname,actor:null,meta:{}})}catch(z){return St(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 x=null,L={},U;try{Sr(O),x=await Or(l,O);let z={request:O,path:D,method:O.method,headers:O.headers,actor:x,cms:l,meta:L};for(let B of J){if(!B.matcher(z))continue;let X=await B.handler(z);if(X instanceof Response){U=X;break}}U??=await Rt({cms:l,router:m,request:O,url:M,path:D,actor:x,meta:L})}catch(z){U=St(z)}if(T.length===0)return U;return Ar({after:T,cms:l,request:O,path:D,actor:x,meta:L,response:U})},routes:()=>[...m.routes(b),...C.routes("")],openapi:A}}async function Rt(l){let{cms:u,router:b,request:m,url:k,path:A,actor:C,meta:J}=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:C,cms:u,db:In(()=>u.database()),database:()=>u.database(),body:()=>Xt(m),meta:J,json:(M,D)=>i(M,D),text:(M,D)=>Pn(M,D)},O=await jr(T.route,S);if(m.method==="HEAD")return new Response(null,{status:O.status,statusText:O.statusText,headers:O.headers});return O}function jr(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:C,response:J}=l,T=new Headers(J.headers),S={request:m,path:k,method:m.method,headers:T,status:J.status,actor:A,cms:b,meta:C};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 St(O)}return new Response(J.body,{status:J.status,statusText:J.statusText,headers:T})}function Tr(l){return[...xe(l),...Fe(l),...at(l),...dt(l),...Ve(l),...Xe(l),...vt(l),...nt(l),...Be(l),...ht(l),...mt(l),...ft(l),...yt(l),...pt(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{Ye,g,Ze,y,j,On,Xn,eo,P,G,i,Pn,oe,K,St,Xt,f,De,v,W,Re,o,c,a,q,_n,h,r,N,Tt,$n,In,Se,to,jn,qe};
@@ -1,2 +0,0 @@
1
- import{e}from"./index-x72d1hzh.js";var ho="*";function It(t,n){return`${t}:${n}`}function m(t){if(t==="*")return["*","*"];let n=t.indexOf(":");if(n===-1)return[t,"*"];return[t.slice(0,n),t.slice(n+1)]}function y(t,n){return t==="*"||t===n}function Wn(t,n){let[o,r]=m(t),[i,s]=m(n);return y(o,i)&&y(r,s)}function E(t,n){if(!t)return!1;return t.permissions.some((o)=>Wn(o,n))}function rt(t,n){let o=new Set;for(let r of t)for(let i of n[r]??[])o.add(i);return[...o].sort()}function jt(t,n){let o=new Set;for(let r of t){let[i,s]=m(r);for(let a of n){let[c,f]=m(a),p=j(i,c),d=j(s,f);if(p===null||d===null)continue;o.add(p==="*"&&d==="*"?"*":`${p}:${d}`)}}return[...o].sort()}function j(t,n){if(t==="*")return n;if(n==="*")return t;return t===n?t:null}function Et(t){let n=new Set;for(let o of t){if(o==="*"||o.includes(":")){n.add(o);continue}switch(o){case"read":n.add("*:read");break;case"write":n.add("*:read"),n.add("*:create"),n.add("*:update");break;case"delete":n.add("*:delete");break;default:break}}return[...n].sort()}function _e(t){if(t==="*")return!0;if(["read","write","delete"].includes(t))return!0;return/^[a-z0-9_*-]+:[a-z0-9_*-]+$/.test(t)}function C(t,n={}){let o=n.locale??t.i18n.defaultLocale;return{locale:o,chain:[o,...h(t,o,n)]}}function h(t,n,o){if(o.fallbackLocale===!1)return[];if(typeof o.fallbackLocale==="string")return o.fallbackLocale===n?[]:[o.fallbackLocale];let r=t.i18n.defaultLocale;return r===n?[]:[r]}function fn(t,n){let o=O(t)?t:{};for(let r of n.chain){let i=o[r];if(i!==void 0&&i!==null)return{value:i,from:r}}return{value:null,from:null}}function J(t,n,o){let r=O(t)?{...t}:{};return r[n]=o,r}function O(t){return typeof t==="object"&&t!==null&&!Array.isArray(t)}var V=["id","revision","createdAt","updatedAt","updatedBy","deletedAt","purgeAfter","scheduledAt","scheduledPatch","_fallback","_localeFields"],w=new Set(V);function A(t,n,o){let r=u(t.data),i={},s={},a=[];for(let[c,f]of Object.entries(n.fields)){if(f.localized){let d=r[c];if(d&&typeof d==="object"&&!Array.isArray(d)&&Object.hasOwn(d,o.locale))a.push(c);let g=fn(d,o);if(i[c]=g.value??b(f),g.from!==null&&g.from!==o.locale)s[c]=g.from;continue}let p=r[c];i[c]=p===void 0?b(f):p}if(Object.values(n.fields).some((c)=>c.localized))i._localeFields=a;if(i.id=String(t.id),i.revision=hn(t.revision),i.createdAt=l(t.created_at),i.updatedAt=l(t.updated_at),i.updatedBy=t.updated_by===void 0?null:t.updated_by,i.deletedAt=l(t.deleted_at),i.purgeAfter=l(t.purge_after),i.scheduledAt=l(t.scheduled_at),i.scheduledPatch=Ie(t)?.data??null,Object.keys(s).length>0)i._fallback=s;return i}function $e(t,n,o,r){L(t,n);let i=r===void 0,s={...r??{}},a=[];for(let[c,f]of Object.entries(t.fields)){let p=n[c];if(p!==void 0){s[c]=f.localized?J(s[c],o.locale,p):p,a.push(c);continue}if(!i)continue;let d=b(f);if(s[c]=f.localized?d===null?{}:{[o.locale]:d}:d,d!==null)a.push(c)}return{data:s,touched:a}}function Je(t,n,o,r){let i={};for(let s of r){let a=t.fields[s];if(!a)continue;let c=a.localized?u(n[s])[o.locale]??null:n[s]??null;if(c===null&&a.required){i[s]="is required";continue}let f=a.schema.safeParse(c);if(!f.success)i[s]=f.error.issues[0]?.message??"is invalid"}if(Object.keys(i).length>0)throw e.validation(`${t.name}: ${Object.entries(i).map(([s,a])=>`${s} ${a}`).join(", ")}`,{fields:i})}function go(t,n,o){return Object.keys(t.fields).filter((r)=>!R(n[r],o[r]))}function yo(t){let n={};for(let[o,r]of Object.entries(t))n[S(o)]=r instanceof Date?r.toISOString():r instanceof Uint8Array?null:r;return n}function Ie(t){let n=t.scheduled_patch;if(n===null||n===void 0)return null;let o=u(n),r=u(o.data),i=typeof o.locale==="string"?o.locale:null;if(i===null)return null;return{data:r,locale:i,actor:v(o.actor)}}function v(t){if(typeof t!=="object"||t===null||Array.isArray(t))return null;let n=t;if(typeof n.id!=="string")return null;return{id:n.id,source:n.source==="api-key"?"api-key":"session",roles:x(n.roles),permissions:x(n.permissions)}}function x(t){return Array.isArray(t)?t.filter((n)=>typeof n==="string"):[]}function hn(t){let n=Number(t??1);return Number.isFinite(n)&&n>0?Math.trunc(n):1}function l(t){if(t===void 0||t===null)return null;if(t instanceof Date)return t.toISOString();return String(t)}function u(t){return typeof t==="object"&&t!==null&&!Array.isArray(t)?t:{}}function b(t){let n=t.defaultValue??t.column.default;return n===void 0?null:n}function L(t,n){let o=Object.keys(n).filter((r)=>!(r in t.fields)&&!w.has(r));if(o.length>0)throw e.validation(`${t.name} has no field ${o.map((r)=>`"${r}"`).join(", ")}`,{unknown:o})}function R(t,n){if(t===n)return!0;if(t===void 0||n===void 0)return!1;if(typeof t!=="object"||typeof n!=="object")return!1;return JSON.stringify(t)===JSON.stringify(n)}function S(t){return t.replace(/_([a-z0-9])/g,(n,o)=>o.toUpperCase())}
2
- export{ho,It,Wn,E,rt,jt,Et,_e,C,fn,A,$e,Je,go,yo,Ie,hn,l,u};