@mapled/vanilla 0.2.3 → 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.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  <html lang="en">
14
14
  <head>
15
15
  <title data-mapled-bind="home.headline">Studio Nord</title>
16
- <script src="https://cdn.jsdelivr.net/npm/@mapled/vanilla@0.2.3/dist/mapled.js" data-mapled-key="mk_live_…" defer></script>
16
+ <script src="https://cdn.jsdelivr.net/npm/@mapled/vanilla@0.3.0/dist/mapled.js" data-mapled-key="mk_live_…" defer></script>
17
17
  </head>
18
18
  <body>
19
19
  <h1 data-mapled-bind="home.headline">Websites, built by hand</h1>
@@ -58,6 +58,35 @@ The key is the project's **delivery key** (Mapled: Integrations → Your site).
58
58
  <li><strong>Designed</strong> by people</li>
59
59
  </ul>
60
60
 
61
+ <!-- a components field: the blocks in their order, each from the template of its type -->
62
+ <main data-mapled-each="home.sections" data-mapled-expand="sections.picks">
63
+ <template data-mapled-type="hero">
64
+ <section class="hero">
65
+ <h2 data-mapled-bind="heading"></h2>
66
+ <a data-mapled-bind="button-label" data-mapled-bind-href="button-link"></a>
67
+ </section>
68
+ </template>
69
+ <template data-mapled-type="text">
70
+ <section data-mapled-bind="body" data-mapled-format="markdown"></section>
71
+ </template>
72
+ <!-- a block's relation: the posts it picks, read along by data-mapled-expand -->
73
+ <template data-mapled-type="featured">
74
+ <section>
75
+ <h2 data-mapled-bind="heading"></h2>
76
+ <ul data-mapled-each="picks">
77
+ <template>
78
+ <li><a data-mapled-link="/post.html?slug={slug}" data-mapled-bind="title"></a></li>
79
+ </template>
80
+ </ul>
81
+ </section>
82
+ </template>
83
+ <!-- every other type of block -->
84
+ <template>
85
+ <section><h2 data-mapled-bind="heading"></h2></section>
86
+ </template>
87
+ <section><h2>Studio Nord</h2></section>
88
+ </main>
89
+
61
90
  <!-- a collection: the template once per record -->
62
91
  <ul data-mapled-list="posts" data-mapled-sort="-date" data-mapled-limit="5" data-mapled-filter="featured=true">
63
92
  <template>
@@ -71,11 +100,12 @@ The key is the project's **delivery key** (Mapled: Integrations → Your site).
71
100
  </ul>
72
101
  ```
73
102
 
74
- - **Paths.** From the document a path starts with a single's key: `home.headline`, `home.seo.title` for a field inside a group. Inside `data-mapled-list`, `data-mapled-each` and `data-mapled-record` it starts at the record: `title`, `author.name` for a linked record — name the relation in `data-mapled-expand="author"` on the list or the record element.
103
+ - **Paths.** From the document a path starts with a single's key: `home.headline`, `home.seo.title` for a field inside a group. Inside `data-mapled-list`, `data-mapled-each` and `data-mapled-record` it starts at the record: `title`, `author.name` for a linked record — name the relation in `data-mapled-expand="author"` on the list or the record element (on the element itself for a single). `data-mapled-each="related"` repeats the records a relation the read expanded links to, the template once per record.
75
104
  - **`data-mapled-bind`** fills the element with text — on an `<img>`, with the picture. `data-mapled-format` says how: `markdown` for a rich text field, `date`, `datetime` and `number` for those, written the way the nearest `lang` attribute says.
76
105
  - **`data-mapled-bind-…`** fills an attribute: `href` (on `a`, `area`), `src` (`img`, `video`, `audio`, `source`), `alt`, `title`, `aria-label`, `placeholder`, `datetime`. Nothing else — no `style`, no `class`, no `on…`.
77
106
  - **`data-mapled-list`** reads a collection: `data-mapled-sort="-date"`, `data-mapled-limit` (1–100), `data-mapled-filter="featured=true; category=news"`. Whatever else sits inside the element is its fallback and leaves when records arrive; an element with `data-mapled-empty` shows when there are none.
78
107
  - **`data-mapled-link`** builds an address from a template. Every `{field}` is percent-encoded before it goes in, so a value fills its place and nothing more.
108
+ - **Blocks.** A components field holds blocks of different types — a hero, a text, a gallery — in the order editors put them, and editors add, remove and reorder them in Mapled. `data-mapled-each` repeats them: each block takes the `<template data-mapled-type="…">` of its type (the type's key, as `MAPLED.md` lists it), or else the `<template>` without a type. A block the page has neither for is left out, and the report says `no-template`. Inside, paths start at the block: `heading`, `button-link`. A block's group — a pricing block's tiers — repeats inside the block's template with a `data-mapled-each` of its own, `data-mapled-each="tiers"`, and its paths start at the item: `name`, `price`. A block's relation — the posts a featured block picks — comes along when the read names the field and the sub-field, `data-mapled-expand="sections.picks"` on the each (on the list or the record element for a collection's records): inside the block's template `data-mapled-each="picks"` repeats the linked records, their paths starting at the record (`title`, `slug`), and `lead.title` reads a relation to one record. A JSON field's objects that carry a `_type` pick a template the same way; a list's records, a group's items and a relation's records have no type and take the `<template>` without one.
79
109
 
80
110
  ### One record per page
81
111
 
@@ -114,10 +144,11 @@ The key is the project's **delivery key** (Mapled: Integrations → Your site).
114
144
  | `empty` | The record is there, a value at this path isn't: an editor left the field empty — or the key is misspelled | Nothing: the text is emptied, a picture is hidden, an attribute is removed (`alt` becomes `""`) |
115
145
  | `failed` | The read didn't come back | The HTML you wrote |
116
146
  | `unsafe-url` | An address that isn't `http:`, `https:` — or `mailto:`, `tel:` in a link — or that carries credentials; a value that can't go into a link template | The HTML you wrote |
117
- | `type` | A value that has no text: a group bound as text, a word where a date goes | The HTML you wrote |
147
+ | `type` | A value that has no text: a group bound as text, a word where a date goes, a relation an each repeats that the read didn't expand | The HTML you wrote |
118
148
  | `refused-element` | `<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<base>`, `<link>`, `<meta>`, `<template>`, `<noscript>` are never written into | The HTML you wrote |
119
149
  | `unsupported` | An attribute Mapled doesn't read — `data-mapled-bind-style`, a misspelled name — or a list without a `<template>` | The HTML you wrote |
120
150
  | `bad-path` | A path, a key or an option that isn't one | The HTML you wrote |
151
+ | `no-template` | A block of a type the page has no `<template data-mapled-type>` for, and no `<template>` without a type to take it | Nothing for that block; the other blocks show |
121
152
 
122
153
  ```html
123
154
  <!-- debug.html -->
@@ -187,11 +218,12 @@ if (report.state === "fallback") console.info("Showing the page as written.");
187
218
  | `data-mapled-link` | `a`, `area` | an address template with `{field}` places |
188
219
  | `data-mapled-list` | an element with a `<template>` | the collection to repeat the template for |
189
220
  | `data-mapled-sort`, `data-mapled-limit`, `data-mapled-filter` | with `data-mapled-list` | the order, how many, which |
190
- | `data-mapled-each` | an element with a `<template>` | the path of a group to repeat the template for |
221
+ | `data-mapled-each` | an element with a `<template>` | the path of a group, a components field or an expanded relation to repeat the template for |
222
+ | `data-mapled-type` | a `<template>` of an each | the type of block it shows |
191
223
  | `data-mapled-empty` | inside a list or an each | shown when there is nothing to repeat |
192
224
  | `data-mapled-record` | an element | the collection of the one record its content shows |
193
225
  | `data-mapled-slug`, `data-mapled-slug-param` | with `data-mapled-record` | the record's slug; the query parameter that holds it |
194
- | `data-mapled-expand` | a list, a record, a bound element | relation fields to read along |
226
+ | `data-mapled-expand` | a list, a record, a bound element, an each | relation fields to read along — `sections.picks` for a relation inside the blocks of `sections` |
195
227
  | `data-mapled-form` | a `<form>` | the key of the Mapled form it posts to |
196
228
  | `data-mapled-consent`, `data-mapled-form-sent`, `data-mapled-form-failed`, `data-mapled-end-preview` | inside a form; anywhere | the consent checkbox; the two outcomes; a way out of preview |
197
229
  | `data-mapled-state`, `data-mapled-preview`, `data-mapled-form-state` | set by the script | `<html>`: how the fill went, whether drafts are shown; a form: `sending`, `sent`, `failed` |
package/dist/mapled.js CHANGED
@@ -1,18 +1,18 @@
1
- /*! @mapled/vanilla 0.2.3 | MIT | https://mapled.io */
2
- "use strict";(()=>{var $t=Object.defineProperty;var Lt=(e,t,n)=>t in e?$t(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var be=(e,t,n)=>Lt(e,typeof t!="symbol"?t+"":t,n);var we="mapled-preview",ie="mapled:preview",Ut=/^mpv_[A-Za-z0-9]{16,96}$/,Ze=/^mps_[A-Za-z0-9]{16,96}$/,Ve=new WeakMap,Nt=4e3;function ke(){if(typeof window>"u"||typeof document>"u")return null;try{let e=window.sessionStorage,t=Ve.get(window);return t||Ve.set(window,t={memory:null,exchanging:null,epoch:0}),{location:window.location,history:window.history,sessionStorage:e,page:t}}catch{return null}}function It(e){try{let t=e.getItem(ie);if(!t)return null;let n=JSON.parse(t),r=typeof n?.expiresAt=="string"?Date.parse(n.expiresAt):NaN;return typeof n?.draftToken!="string"||!Ze.test(n.draftToken)||!(r>Date.now())?(e.removeItem(ie),null):{draftToken:n.draftToken,expiresAt:n.expiresAt}}catch{return null}}function K(e){let t=It(e.sessionStorage);if(t)return t;let n=e.page.memory;return n&&Date.parse(n.expiresAt)>Date.now()?n:(e.page.memory=null,null)}function B(){let e=ke();return e?K(e):null}function Ye(e){let t;try{t=new URL(e.location.href)}catch{return null}let n=t.searchParams.get(we);if(n===null)return null;t.searchParams.delete(we);try{e.history.replaceState(e.history.state,"",`${t.pathname}${t.search}${t.hash}`)}catch{}return n}function Se(){let e=ke();if(e){e.page.memory=null,e.page.epoch++,e.page.exchanging=null,Ye(e);try{e.sessionStorage.removeItem(ie)}catch{}}}function Je(e={}){let t=ke();if(!t)return Promise.resolve(null);let n=Ye(t);if(n===null)return t.page.exchanging?t.page.exchanging.done:Promise.resolve(K(t));if(!Ut.test(n))return Promise.resolve(K(t));if(t.page.exchanging?.token===n)return t.page.exchanging.done;let r=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=(e.appUrl??"https://app.mapled.io").replace(/\/+$/,""),o=e.fetch??fetch,c=++t.page.epoch,a=(async()=>{try{let i=await o(`${r}/v1/delivery/preview/exchange`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:n}),cache:"no-store"});if(i.ok){let p=await i.json();if(typeof p.draftToken!="string"||typeof p.expiresAt!="string"||!Ze.test(p.draftToken)||!(Date.parse(p.expiresAt)>Date.now())||t.page.epoch!==c)return K(t);let d={draftToken:p.draftToken,expiresAt:p.expiresAt};t.page.memory=d;try{t.sessionStorage.setItem(ie,JSON.stringify(d))}catch{}return d}if(i.status===401&&t.page.epoch===c){let p=await i.json().catch(()=>null),d=p?.error?.projectId;if(t.page.epoch===c&&p?.error?.code==="PREVIEW_EXPIRED"&&typeof d=="string"&&/^[0-9a-f-]{8,64}$/i.test(d))return t.location.assign(`${s}/preview/expired?project=${encodeURIComponent(d)}`),await new Promise(l=>setTimeout(l,Nt)),K(t)}return K(t)}catch{return K(t)}})(),f={token:n,done:a};return t.page.exchanging=f,a.then(()=>{t.page.exchanging===f&&(t.page.exchanging=null)}),a}async function Qe(e){return(await Je(e))?.draftToken??null}function Ae(e){if(!e||typeof e!="object")return;let{release:t,publishedAt:n}=e;if(t===null)return n===null?null:void 0;if(!(!Number.isInteger(t)||t<1||typeof n!="string"))return{release:t,publishedAt:n}}var Ft=1e4;function Dt(e,t){let n=typeof document>"u"?void 0:globalThis.performance?.timeOrigin;if(n===void 0)return!1;let r=Date.parse(t.get("date")??""),s=Number.isNaN(r)?0:r+(Number(t.get("age"))||0)*1e3-Date.now();return Date.parse(e)>n+s-Ft}function et(e,t){return(n,r)=>{if(e!==void 0)return e;let s=t?.();return s!==void 0?s:Dt(n.publishedAt,r)?null:n.release}}function tt(e,t,n){let r=typeof e.key=="string"&&e.key.length>0;if(!r&&!e.endpoint)throw new Error("Mapled: watchRelease needs an endpoint or a delivery key.");let s=r?`${(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/v1/delivery/release`:e.endpoint,o=r?{accept:"application/json","x-mapled-key":e.key}:{accept:"application/json"},c=(r?Math.max(15,e.interval??60):Math.max(2,e.interval??10))*1e3,a=e.fetch??fetch,f=typeof document>"u"?null:document,i,p=null,d=0,l=null,g=0,S=!1,E,M=null,b=()=>!f||f.visibilityState!=="hidden",$=()=>{S||!b()||(E=setTimeout(L,Math.min(c*2**g,3e5)))};async function L(){if(S||M)return;let U=new AbortController;M=U;let N=null;try{let X=await a(s,{cache:"no-cache",headers:o,signal:U.signal}),O=X.ok?Ae(await X.json()):void 0;if(S)return;O===void 0?g+=1:(g=0,i===void 0&&(i=O?t(O,X.headers):null),O&&O.release>(p?.release??0)&&(p=O),O&&O.release>(i??0)?(N={next:O,previous:i},i=O.release,d=O.release,l=null):O&&l!==null&&typeof i=="number"&&O.release>=i&&i>d&&(N={next:O,previous:l},d=i,l=null))}catch{if(S)return;g+=1}finally{M===U&&(M=null)}$(),N&&e.onPublish(N.next,N.previous)}let C=()=>{clearTimeout(E),b()&&L()};f?.addEventListener("visibilitychange",C);let z=n?.(U=>{if(S||typeof i!="number"||U>=i||i<=d||(l=l===null?U:Math.min(l,U),p===null||p.release<i))return;d=i;let N=l;l=null,e.onPublish(p,N)});return b()&&L(),()=>{S=!0,clearTimeout(E),M?.abort(),z?.(),f?.removeEventListener("visibilitychange",C)}}function nt(e){let t={};return e.forEach((n,r)=>{typeof n!="string"||r in t||(t[r]=n)}),t}async function Re(e,t,n){if(!n?.key)throw new Error("Mapled: a delivery key is required.");let r=(n.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=typeof HTMLFormElement<"u"&&t instanceof HTMLFormElement?nt(new FormData(t)):typeof FormData<"u"&&t instanceof FormData?nt(t):{...t},o=n.page??(typeof window<"u"&&typeof document<"u"?window.location.pathname:void 0),c={...s};n.consent&&(c._consent=n.consent),o&&c._page===void 0&&(c._page=o),n.test&&(c._test=!0);let a=B()?.draftToken,f={"content-type":"application/json","x-mapled-key":n.key};a&&(f["x-mapled-draft"]=a);let i;try{i=await(n.fetch??fetch)(`${r}/v1/delivery/forms/${encodeURIComponent(e)}`,{method:"POST",headers:f,body:JSON.stringify(c)})}catch{return{ok:!1,status:0,code:"NETWORK",message:"Mapled couldn't be reached. Try again in a minute."}}if(i.ok)return{ok:!0};let p=`The submission wasn't accepted (${i.status}).`,d;try{let l=await i.json();l.error?.message&&(p=l.error.message),typeof l.error?.code=="string"&&(d=l.error.code)}catch{}return{ok:!1,status:i.status,code:d,message:p}}var ve=/^[a-z0-9][a-z0-9-]{0,63}$/,rt=new Set;function _t(e){if(typeof process<"u")return;let t=e?.aliases;if(!(!t||typeof t!="object")){for(let[n,r]of Object.entries(t))if(!(!r||typeof r!="object"||!ve.test(n)))for(let[s,o]of Object.entries(r)){if(typeof o!="string"||!ve.test(s)||!ve.test(o))continue;let c=`${n}.${s}`;rt.has(c)||(rt.add(c),console.warn(`[mapled] \u201C${s}\u201D in \u201C${n}\u201D was renamed to \u201C${o}\u201D. The old key keeps working until its alias is removed in Mapled \u2014 use \u201C${o}\u201D instead.`))}}}function it(e,t={}){let n=new URLSearchParams;t.width&&n.set("w",String(t.width)),t.height&&n.set("h",String(t.height)),t.fit&&n.set("fit",t.fit),t.format&&n.set("fmt",t.format),t.quality&&n.set("q",String(t.quality));let r=n.toString(),s=t.files?(t.files[e]??t.files[e.toLowerCase()])?.url:void 0;return typeof s=="string"&&s?r?`${s}${s.includes("?")?"&":"?"}${r}`:s:`${(t.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/files/${encodeURIComponent(e)}${r?`?${r}`:""}`}var W=class extends Error{constructor(n,r,s){super(r);be(this,"status");be(this,"code");this.name="MapledError",this.status=n,this.code=s}},st=e=>Number.isInteger(e)&&e>=1,ot=()=>new Error("Mapled: release must be a positive whole number.");function lt(e){let t=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"");if(!e.key)throw new Error("Mapled: a delivery key is required.");if(e.release!==void 0&&!st(e.release))throw ot();let n=(d,l)=>(e.fetch??fetch)(d,l),r,s=new Set;async function o(){return e.draft===null?null:typeof e.draft=="string"?e.draft||null:typeof e.draft=="function"?await e.draft()||null:Qe({apiUrl:t,appUrl:e.appUrl,fetch:e.fetch})}async function c(d){let l=`Mapled request failed (${d.status}).`,g;try{let S=await d.json();S.error?.message&&(l=S.error.message),typeof S.error?.code=="string"&&(g=S.error.code)}catch{}return new W(d.status,l,g)}async function a(d,l,g){let S=await o(),E=g.release??e.release;if(E!==void 0&&!st(E))throw ot();E!==void 0&&!S&&l.set("release",String(E));let M=g.locale||e.locale;M&&l.set("locale",M);let b=l.toString(),$=await n(`${t}${d}${b?`?${b}`:""}`,{headers:S?{"x-mapled-key":e.key,"x-mapled-draft":S}:{"x-mapled-key":e.key},...S?{cache:"no-store"}:{},...g.signal?{signal:g.signal}:{}});if(!$.ok)throw await c($);let L=await $.json();if(!S){let C=Number($.headers.get("x-mapled-release"));if(Number.isInteger(C)&&C>=1){r=r===void 0?C:Math.min(r,C);for(let z of s)Promise.resolve().then(()=>z(C))}}return _t(L),L}let f=(d,l)=>(l.expand?.length&&d.set("expand",l.expand.join(",")),l.fields?.length&&d.set("fields",l.fields.join(",")),d),i=async(d,l)=>{try{return(await a(d,f(new URLSearchParams,l),l)).record}catch(g){if(g instanceof W&&g.status===404&&g.code!=="RELEASE_NOT_FOUND")return null;throw g}};return{async getRecords(d,l={}){let g=new URLSearchParams;for(let[S,E]of Object.entries(l.filter??{}))if(E!==null&&typeof E=="object")for(let[M,b]of Object.entries(E))b!==void 0&&g.set(`filter[${S}][${M}]`,Array.isArray(b)?b.join(","):String(b));else g.set(`filter[${S}]`,String(E));return l.sort&&g.set("sort",l.sort),l.limit!==void 0&&g.set("limit",String(l.limit)),l.offset!==void 0&&g.set("offset",String(l.offset)),l.search&&g.set("search",l.search),f(g,l),a(`/v1/delivery/collections/${encodeURIComponent(d)}/records`,g,l)},getRecord(d,l,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(d)}/records/${encodeURIComponent(l)}`,g)},getRecordBySlug(d,l,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(d)}/records/by-slug/${encodeURIComponent(l)}`,g)},getSingle(d,l={}){return i(`/v1/delivery/singles/${encodeURIComponent(d)}`,l)},async isPreview(){return await o()!==null},watch(d,l={}){return tt({key:e.key,apiUrl:t,interval:l.interval,onPublish:d,fetch:e.fetch},et(l.from,()=>r),g=>(s.add(g),()=>{s.delete(g)}))},async getRelease(d={}){let l=await n(`${t}/v1/delivery/release`,{headers:{"x-mapled-key":e.key},cache:"no-cache",...d.signal?{signal:d.signal}:{}});if(!l.ok)throw await c(l);let g=Ae(await l.json());if(g===void 0)throw new Error("Mapled: the release pointer didn't come back as one \u2014 check the API origin.");return g}}}var jt=/^[a-z0-9][a-z0-9_-]{0,63}$/;function Z(e){if(typeof e!="string")return null;let t=e.trim().split(".");if(t.length>8)return null;for(let n of t)if(!jt.test(n))return null;return t}var le=(e,t)=>e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;function Te(e,t){let n=e.data,r=e.expanded;for(let s=0;s<t.length;s++){let o=t[s],c=s<t.length-1?le(r,o):void 0;if(c!==void 0){if(c===null||Array.isArray(c))return;n=le(c,"data"),r=le(c,"expanded");continue}if(n=le(n,o),r=void 0,n===void 0)return}return n}var Y=e=>e==null||e===""||Array.isArray(e)&&e.length===0;function xe(e){return typeof e=="string"?e:typeof e=="number"&&Number.isFinite(e)?String(e):typeof e=="boolean"?e?"true":"false":Array.isArray(e)&&e.every(t=>typeof t=="string"||typeof t=="number")?e.join(", "):null}var Kt=new Set(["http:","https:","mailto:","tel:"]),Bt=new Set(["http:","https:"]),qt=2048;function q(e,t,n){if(typeof e!="string"||e.length>qt)return null;let r;try{r=new URL(e,t)}catch{return null}return!(n==="link"?Kt:Bt).has(r.protocol)||r.username!==""||r.password!==""?null:r.href}var Ht=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,at=e=>typeof e=="string"&&Ht.test(e);function ut(e,t,n){let r=[],s=e.replace(/\{([^{}]*)\}/g,(c,a)=>{let f=Z(a);if(!f)return r.push({ok:!1,reason:"bad-path",path:a.trim().slice(0,80)}),"";let i=n(f);if(typeof i!="string"&&typeof i!="number"||i==="")return r.push({ok:!1,reason:"empty",path:f.join(".")}),"";try{return encodeURIComponent(String(i))}catch{return r.push({ok:!1,reason:"unsafe-url",path:f.join(".")}),""}});if(r[0])return r[0];let o=q(s,t,"link");return o===null?{ok:!1,reason:"unsafe-url",path:e.slice(0,80)}:{ok:!0,url:o}}var zt=1e5,dt=8,ft=2048,Wt="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",ct={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",copy:"\xA9",reg:"\xAE",trade:"\u2122",mdash:"\u2014",ndash:"\u2013",hellip:"\u2026",laquo:"\xAB",raquo:"\xBB"};function pt(e,t,n){let r=t.createDocumentFragment();if(typeof e!="string"||e==="")return r;let s=e.replace(/\r\n?/g,`
3
- `).replace(/\t/g," ").replace(/\u0000/g,"\uFFFD"),o={doc:t,base:n};if(s.length>zt){for(let c of s.split(/\n{2,}/))c.trim()!==""&&(P(o,"p",r).textContent=c.trim());return r}return $e(o,s.split(`
4
- `),r,0),r}function P(e,t,n){let r=e.doc.createElement(t);return n.appendChild(r),r}var Ee=e=>e.trim()==="",Q=e=>e.length-e.trimStart().length;function mt(e){let t=Q(e);if(t>3)return null;let n=0;for(;n<7&&e[t+n]==="#";)n++;if(n===0||n>6)return null;let r=e[t+n];if(r!==void 0&&r!==" ")return null;let s=e.slice(t+n).trim(),o=s.length;for(;o>0&&s[o-1]==="#";)o--;return o<s.length&&(o===0||s[o-1]===" ")&&(s=s.slice(0,o).trim()),{level:n,text:s}}function Ce(e){if(Q(e)>3)return!1;let t=e.replace(/ /g,""),n=t[0];if(t.length<3||n!=="-"&&n!=="*"&&n!=="_")return!1;for(let r of t)if(r!==n)return!1;return!0}function Me(e){let t=/^ {0,3}(`{3,}|~{3,})/.exec(e);if(!t)return null;let n=t[1][0];return n==="`"&&e.slice(t[0].length).includes("`")?null:{mark:n,size:t[1].length}}function J(e){let t=/^( {0,3})([-*+]|\d{1,9}[.)])( +|$)/.exec(e);if(!t)return null;let n=t[2],r=t[3].length,s=n.length>1||/\d/.test(n);return{indent:t[1].length,width:n.length+(r>=1&&r<=4?r:1),ordered:s,start:s?parseInt(n,10):1,rest:e.slice(t[0].length)}}var Pe=e=>/^ {0,3}>/.test(e);function Gt(e){if(mt(e)!==null||Me(e)!==null||Pe(e)||Ce(e))return!0;let t=J(e);return t!==null&&t.rest!==""&&(!t.ordered||t.start===1)}function $e(e,t,n,r){if(r>=dt){let o=t.join(`
5
- `).trim();o!==""&&(P(e,"p",n).textContent=o);return}let s=0;for(;s<t.length;){let o=t[s];if(Ee(o)){s++;continue}let c=Me(o);if(c){let p=[],d=Q(o);for(s++;s<t.length;){let l=t[s],g=Me(l);if(g&&g.mark===c.mark&&g.size>=c.size&&l.trim().length===g.size)break;p.push(l.slice(Math.min(d,Q(l)))),s++}s++,P(e,"code",P(e,"pre",n)).textContent=p.length>0?`${p.join(`
1
+ /*! @mapled/vanilla 0.3.0 | MIT | https://mapled.io */
2
+ "use strict";(()=>{var It=Object.defineProperty;var Ft=(e,t,n)=>t in e?It(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Se=(e,t,n)=>Ft(e,typeof t!="symbol"?t+"":t,n);var Re="mapled-preview",pe="mapled:preview",Dt=/^mpv_[A-Za-z0-9]{16,96}$/,Je=/^mps_[A-Za-z0-9]{16,96}$/,Ye=new WeakMap,_t=4e3;function Ae(){if(typeof window>"u"||typeof document>"u")return null;try{let e=window.sessionStorage,t=Ye.get(window);return t||Ye.set(window,t={memory:null,exchanging:null,epoch:0}),{location:window.location,history:window.history,sessionStorage:e,page:t}}catch{return null}}function jt(e){try{let t=e.getItem(pe);if(!t)return null;let n=JSON.parse(t),r=typeof n?.expiresAt=="string"?Date.parse(n.expiresAt):NaN;return typeof n?.draftToken!="string"||!Je.test(n.draftToken)||!(r>Date.now())?(e.removeItem(pe),null):{draftToken:n.draftToken,expiresAt:n.expiresAt}}catch{return null}}function K(e){let t=jt(e.sessionStorage);if(t)return t;let n=e.page.memory;return n&&Date.parse(n.expiresAt)>Date.now()?n:(e.page.memory=null,null)}function B(){let e=Ae();return e?K(e):null}function Qe(e){let t;try{t=new URL(e.location.href)}catch{return null}let n=t.searchParams.get(Re);if(n===null)return null;t.searchParams.delete(Re);try{e.history.replaceState(e.history.state,"",`${t.pathname}${t.search}${t.hash}`)}catch{}return n}function ve(){let e=Ae();if(e){e.page.memory=null,e.page.epoch++,e.page.exchanging=null,Qe(e);try{e.sessionStorage.removeItem(pe)}catch{}}}function et(e={}){let t=Ae();if(!t)return Promise.resolve(null);let n=Qe(t);if(n===null)return t.page.exchanging?t.page.exchanging.done:Promise.resolve(K(t));if(!Dt.test(n))return Promise.resolve(K(t));if(t.page.exchanging?.token===n)return t.page.exchanging.done;let r=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=(e.appUrl??"https://app.mapled.io").replace(/\/+$/,""),l=e.fetch??fetch,c=++t.page.epoch,o=(async()=>{try{let i=await l(`${r}/v1/delivery/preview/exchange`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:n}),cache:"no-store"});if(i.ok){let p=await i.json();if(typeof p.draftToken!="string"||typeof p.expiresAt!="string"||!Je.test(p.draftToken)||!(Date.parse(p.expiresAt)>Date.now())||t.page.epoch!==c)return K(t);let f={draftToken:p.draftToken,expiresAt:p.expiresAt};t.page.memory=f;try{t.sessionStorage.setItem(pe,JSON.stringify(f))}catch{}return f}if(i.status===401&&t.page.epoch===c){let p=await i.json().catch(()=>null),f=p?.error?.projectId;if(t.page.epoch===c&&p?.error?.code==="PREVIEW_EXPIRED"&&typeof f=="string"&&/^[0-9a-f-]{8,64}$/i.test(f))return t.location.assign(`${s}/preview/expired?project=${encodeURIComponent(f)}`),await new Promise(a=>setTimeout(a,_t)),K(t)}return K(t)}catch{return K(t)}})(),d={token:n,done:o};return t.page.exchanging=d,o.then(()=>{t.page.exchanging===d&&(t.page.exchanging=null)}),o}async function tt(e){return(await et(e))?.draftToken??null}function Te(e){if(!e||typeof e!="object")return;let{release:t,publishedAt:n}=e;if(t===null)return n===null?null:void 0;if(!(!Number.isInteger(t)||t<1||typeof n!="string"))return{release:t,publishedAt:n}}var Kt=1e4;function Bt(e,t){let n=typeof document>"u"?void 0:globalThis.performance?.timeOrigin;if(n===void 0)return!1;let r=Date.parse(t.get("date")??""),s=Number.isNaN(r)?0:r+(Number(t.get("age"))||0)*1e3-Date.now();return Date.parse(e)>n+s-Kt}function nt(e,t){return(n,r)=>{if(e!==void 0)return e;let s=t?.();return s!==void 0?s:Bt(n.publishedAt,r)?null:n.release}}function rt(e,t,n){let r=typeof e.key=="string"&&e.key.length>0;if(!r&&!e.endpoint)throw new Error("Mapled: watchRelease needs an endpoint or a delivery key.");let s=r?`${(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/v1/delivery/release`:e.endpoint,l=r?{accept:"application/json","x-mapled-key":e.key}:{accept:"application/json"},c=(r?Math.max(15,e.interval??60):Math.max(2,e.interval??10))*1e3,o=e.fetch??fetch,d=typeof document>"u"?null:document,i,p=null,f=0,a=null,g=0,R=!1,O,P=null,w=()=>!d||d.visibilityState!=="hidden",U=()=>{R||!w()||(O=setTimeout(N,Math.min(c*2**g,3e5)))};async function N(){if(R||P)return;let I=new AbortController;P=I;let F=null;try{let J=await o(s,{cache:"no-cache",headers:l,signal:I.signal}),$=J.ok?Te(await J.json()):void 0;if(R)return;$===void 0?g+=1:(g=0,i===void 0&&(i=$?t($,J.headers):null),$&&$.release>(p?.release??0)&&(p=$),$&&$.release>(i??0)?(F={next:$,previous:i},i=$.release,f=$.release,a=null):$&&a!==null&&typeof i=="number"&&$.release>=i&&i>f&&(F={next:$,previous:a},f=i,a=null))}catch{if(R)return;g+=1}finally{P===I&&(P=null)}U(),F&&e.onPublish(F.next,F.previous)}let L=()=>{clearTimeout(O),w()&&N()};d?.addEventListener("visibilitychange",L);let W=n?.(I=>{if(R||typeof i!="number"||I>=i||i<=f||(a=a===null?I:Math.min(a,I),p===null||p.release<i))return;f=i;let F=a;a=null,e.onPublish(p,F)});return w()&&N(),()=>{R=!0,clearTimeout(O),P?.abort(),W?.(),d?.removeEventListener("visibilitychange",L)}}function st(e){let t={};return e.forEach((n,r)=>{typeof n!="string"||r in t||(t[r]=n)}),t}async function xe(e,t,n){if(!n?.key)throw new Error("Mapled: a delivery key is required.");let r=(n.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),s=typeof HTMLFormElement<"u"&&t instanceof HTMLFormElement?st(new FormData(t)):typeof FormData<"u"&&t instanceof FormData?st(t):{...t},l=n.page??(typeof window<"u"&&typeof document<"u"?window.location.pathname:void 0),c={...s};n.consent&&(c._consent=n.consent),l&&c._page===void 0&&(c._page=l),n.test&&(c._test=!0);let o=B()?.draftToken,d={"content-type":"application/json","x-mapled-key":n.key};o&&(d["x-mapled-draft"]=o);let i;try{i=await(n.fetch??fetch)(`${r}/v1/delivery/forms/${encodeURIComponent(e)}`,{method:"POST",headers:d,body:JSON.stringify(c)})}catch{return{ok:!1,status:0,code:"NETWORK",message:"Mapled couldn't be reached. Try again in a minute."}}if(i.ok)return{ok:!0};let p=`The submission wasn't accepted (${i.status}).`,f;try{let a=await i.json();a.error?.message&&(p=a.error.message),typeof a.error?.code=="string"&&(f=a.error.code)}catch{}return{ok:!1,status:i.status,code:f,message:p}}var Ee=/^[a-z0-9][a-z0-9-]{0,63}$/,ot=new Set;function Ht(e){if(typeof process<"u")return;let t=e?.aliases;if(!(!t||typeof t!="object")){for(let[n,r]of Object.entries(t))if(!(!r||typeof r!="object"||!Ee.test(n)))for(let[s,l]of Object.entries(r)){if(typeof l!="string"||!Ee.test(s)||!Ee.test(l))continue;let c=`${n}.${s}`;ot.has(c)||(ot.add(c),console.warn(`[mapled] \u201C${s}\u201D in \u201C${n}\u201D was renamed to \u201C${l}\u201D. The old key keeps working until its alias is removed in Mapled \u2014 use \u201C${l}\u201D instead.`))}}}function at(e,t={}){let n=new URLSearchParams;t.width&&n.set("w",String(t.width)),t.height&&n.set("h",String(t.height)),t.fit&&n.set("fit",t.fit),t.format&&n.set("fmt",t.format),t.quality&&n.set("q",String(t.quality));let r=n.toString(),s=t.files?(t.files[e]??t.files[e.toLowerCase()])?.url:void 0;return typeof s=="string"&&s?r?`${s}${s.includes("?")?"&":"?"}${r}`:s:`${(t.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/files/${encodeURIComponent(e)}${r?`?${r}`:""}`}var V=class extends Error{constructor(n,r,s){super(r);Se(this,"status");Se(this,"code");this.name="MapledError",this.status=n,this.code=s}},it=e=>Number.isInteger(e)&&e>=1,lt=()=>new Error("Mapled: release must be a positive whole number.");function ut(e){let t=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"");if(!e.key)throw new Error("Mapled: a delivery key is required.");if(e.release!==void 0&&!it(e.release))throw lt();let n=(f,a)=>(e.fetch??fetch)(f,a),r,s=new Set;async function l(){return e.draft===null?null:typeof e.draft=="string"?e.draft||null:typeof e.draft=="function"?await e.draft()||null:tt({apiUrl:t,appUrl:e.appUrl,fetch:e.fetch})}async function c(f){let a=`Mapled request failed (${f.status}).`,g;try{let R=await f.json();R.error?.message&&(a=R.error.message),typeof R.error?.code=="string"&&(g=R.error.code)}catch{}return new V(f.status,a,g)}async function o(f,a,g){let R=await l(),O=g.release??e.release;if(O!==void 0&&!it(O))throw lt();O!==void 0&&!R&&a.set("release",String(O));let P=g.locale||e.locale;P&&a.set("locale",P);let w=a.toString(),U=await n(`${t}${f}${w?`?${w}`:""}`,{headers:R?{"x-mapled-key":e.key,"x-mapled-draft":R}:{"x-mapled-key":e.key},...R?{cache:"no-store"}:{},...g.signal?{signal:g.signal}:{}});if(!U.ok)throw await c(U);let N=await U.json();if(!R){let L=Number(U.headers.get("x-mapled-release"));if(Number.isInteger(L)&&L>=1){r=r===void 0?L:Math.min(r,L);for(let W of s)Promise.resolve().then(()=>W(L))}}return Ht(N),N}let d=(f,a)=>(a.expand?.length&&f.set("expand",a.expand.join(",")),a.fields?.length&&f.set("fields",a.fields.join(",")),f),i=async(f,a)=>{try{return(await o(f,d(new URLSearchParams,a),a)).record}catch(g){if(g instanceof V&&g.status===404&&g.code!=="RELEASE_NOT_FOUND")return null;throw g}};return{async getRecords(f,a={}){let g=new URLSearchParams;for(let[R,O]of Object.entries(a.filter??{}))if(O!==null&&typeof O=="object")for(let[P,w]of Object.entries(O))w!==void 0&&g.set(`filter[${R}][${P}]`,Array.isArray(w)?w.join(","):String(w));else g.set(`filter[${R}]`,String(O));return a.sort&&g.set("sort",a.sort),a.limit!==void 0&&g.set("limit",String(a.limit)),a.offset!==void 0&&g.set("offset",String(a.offset)),a.search&&g.set("search",a.search),d(g,a),o(`/v1/delivery/collections/${encodeURIComponent(f)}/records`,g,a)},getRecord(f,a,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(f)}/records/${encodeURIComponent(a)}`,g)},getRecordBySlug(f,a,g={}){return i(`/v1/delivery/collections/${encodeURIComponent(f)}/records/by-slug/${encodeURIComponent(a)}`,g)},getSingle(f,a={}){return i(`/v1/delivery/singles/${encodeURIComponent(f)}`,a)},async isPreview(){return await l()!==null},watch(f,a={}){return rt({key:e.key,apiUrl:t,interval:a.interval,onPublish:f,fetch:e.fetch},nt(a.from,()=>r),g=>(s.add(g),()=>{s.delete(g)}))},async getRelease(f={}){let a=await n(`${t}/v1/delivery/release`,{headers:{"x-mapled-key":e.key},cache:"no-cache",...f.signal?{signal:f.signal}:{}});if(!a.ok)throw await c(a);let g=Te(await a.json());if(g===void 0)throw new Error("Mapled: the release pointer didn't come back as one \u2014 check the API origin.");return g}}}var qt=/^[a-z0-9][a-z0-9_-]{0,63}$/;function te(e){if(typeof e!="string")return null;let t=e.trim().split(".");if(t.length>8)return null;for(let n of t)if(!qt.test(n))return null;return t}var z=(e,t)=>e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;function Me(e,t){let n=e.data,r=e.expanded,s=e.expandedBlocks;for(let l=0;l<t.length;l++){let c=t[l],o=z(r,c);if(l===t.length-1)return{value:z(n,c),linked:o,blocks:z(s,c)};if(o!==void 0){if(o===null||Array.isArray(o))return{value:void 0};n=z(o,"data"),r=z(o,"expanded"),s=z(o,"expandedBlocks");continue}if(n=z(n,c),r=void 0,s=void 0,n===void 0)return{value:void 0}}return{value:n}}var ne=e=>e==null||e===""||Array.isArray(e)&&e.length===0;function Oe(e){return typeof e=="string"?e:typeof e=="number"&&Number.isFinite(e)?String(e):typeof e=="boolean"?e?"true":"false":Array.isArray(e)&&e.every(t=>typeof t=="string"||typeof t=="number")?e.join(", "):null}var zt=new Set(["http:","https:","mailto:","tel:"]),Wt=new Set(["http:","https:"]),Gt=2048;function H(e,t,n){if(typeof e!="string"||e.length>Gt)return null;let r;try{r=new URL(e,t)}catch{return null}return!(n==="link"?zt:Wt).has(r.protocol)||r.username!==""||r.password!==""?null:r.href}var Xt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ct=e=>typeof e=="string"&&Xt.test(e);function dt(e,t,n){let r=[],s=e.replace(/\{([^{}]*)\}/g,(c,o)=>{let d=te(o);if(!d)return r.push({ok:!1,reason:"bad-path",path:o.trim().slice(0,80)}),"";let i=n(d);if(typeof i!="string"&&typeof i!="number"||i==="")return r.push({ok:!1,reason:"empty",path:d.join(".")}),"";try{return encodeURIComponent(String(i))}catch{return r.push({ok:!1,reason:"unsafe-url",path:d.join(".")}),""}});if(r[0])return r[0];let l=H(s,t,"link");return l===null?{ok:!1,reason:"unsafe-url",path:e.slice(0,80)}:{ok:!0,url:l}}var Vt=1e5,pt=8,mt=2048,Zt="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",ft={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",copy:"\xA9",reg:"\xAE",trade:"\u2122",mdash:"\u2014",ndash:"\u2013",hellip:"\u2026",laquo:"\xAB",raquo:"\xBB"};function gt(e,t,n){let r=t.createDocumentFragment();if(typeof e!="string"||e==="")return r;let s=e.replace(/\r\n?/g,`
3
+ `).replace(/\t/g," ").replace(/\u0000/g,"\uFFFD"),l={doc:t,base:n};if(s.length>Vt){for(let c of s.split(/\n{2,}/))c.trim()!==""&&(C(l,"p",r).textContent=c.trim());return r}return Ne(l,s.split(`
4
+ `),r,0),r}function C(e,t,n){let r=e.doc.createElement(t);return n.appendChild(r),r}var Pe=e=>e.trim()==="",se=e=>e.length-e.trimStart().length;function ht(e){let t=se(e);if(t>3)return null;let n=0;for(;n<7&&e[t+n]==="#";)n++;if(n===0||n>6)return null;let r=e[t+n];if(r!==void 0&&r!==" ")return null;let s=e.slice(t+n).trim(),l=s.length;for(;l>0&&s[l-1]==="#";)l--;return l<s.length&&(l===0||s[l-1]===" ")&&(s=s.slice(0,l).trim()),{level:n,text:s}}function Ue(e){if(se(e)>3)return!1;let t=e.replace(/ /g,""),n=t[0];if(t.length<3||n!=="-"&&n!=="*"&&n!=="_")return!1;for(let r of t)if(r!==n)return!1;return!0}function Ce(e){let t=/^ {0,3}(`{3,}|~{3,})/.exec(e);if(!t)return null;let n=t[1][0];return n==="`"&&e.slice(t[0].length).includes("`")?null:{mark:n,size:t[1].length}}function re(e){let t=/^( {0,3})([-*+]|\d{1,9}[.)])( +|$)/.exec(e);if(!t)return null;let n=t[2],r=t[3].length,s=n.length>1||/\d/.test(n);return{indent:t[1].length,width:n.length+(r>=1&&r<=4?r:1),ordered:s,start:s?parseInt(n,10):1,rest:e.slice(t[0].length)}}var $e=e=>/^ {0,3}>/.test(e);function Yt(e){if(ht(e)!==null||Ce(e)!==null||$e(e)||Ue(e))return!0;let t=re(e);return t!==null&&t.rest!==""&&(!t.ordered||t.start===1)}function Ne(e,t,n,r){if(r>=pt){let l=t.join(`
5
+ `).trim();l!==""&&(C(e,"p",n).textContent=l);return}let s=0;for(;s<t.length;){let l=t[s];if(Pe(l)){s++;continue}let c=Ce(l);if(c){let p=[],f=se(l);for(s++;s<t.length;){let a=t[s],g=Ce(a);if(g&&g.mark===c.mark&&g.size>=c.size&&a.trim().length===g.size)break;p.push(a.slice(Math.min(f,se(a)))),s++}s++,C(e,"code",C(e,"pre",n)).textContent=p.length>0?`${p.join(`
6
6
  `)}
7
- `:"";continue}let a=mt(o);if(a){ue(e,a.text,P(e,`h${a.level}`,n),r),s++;continue}if(Ce(o)){P(e,"hr",n),s++;continue}if(Pe(o)){let p=[];for(;s<t.length&&Pe(t[s]);)p.push(t[s].replace(/^ {0,3}> ?/,"")),s++;$e(e,p,P(e,"blockquote",n),r+1);continue}let f=J(o);if(f){s=Xt(e,t,s,f,n,r);continue}let i=[o.trimStart()];for(s++;s<t.length&&!Ee(t[s])&&!Gt(t[s]);)i.push(t[s].trimStart()),s++;ue(e,i.join(`
8
- `),P(e,"p",n),r)}}function Xt(e,t,n,r,s,o){let c=P(e,r.ordered?"ol":"ul",s);r.ordered&&r.start!==1&&c.setAttribute("start",String(r.start));let a=[],f=!1;for(;n<t.length;){let i=J(t[n]);if(!i||i.ordered!==r.ordered||i.indent!==r.indent||Ce(t[n]))break;let p=i.indent+i.width,d=[i.rest];n++;let l=!1;for(;n<t.length;){let g=t[n];if(Ee(g)){l=!0,d.push(""),n++;continue}if(Q(g)<p)break;l&&d.some(S=>S!=="")&&!J(g.slice(p))&&(f=!0),l=!1,d.push(g.slice(p)),n++}for(;d.length>0&&d[d.length-1]==="";)d.pop();if(l&&n<t.length){let g=J(t[n]);g&&g.ordered===r.ordered&&g.indent===r.indent&&(f=!0)}a.push({lines:d,el:P(e,"li",c)})}for(let i of a)if($e(e,i.lines,i.el,o+1),!f){for(let p of Array.from(i.el.childNodes))if(p.nodeName==="P"){for(;p.firstChild;)i.el.insertBefore(p.firstChild,p);i.el.removeChild(p)}}return n}var Le=e=>e===void 0||e===" "||e===`
9
- `,gt=e=>e!==void 0&&/[\p{L}\p{N}]/u.test(e);function ae(e,t,n,r,s=""){let o=Math.min(e.length,r);for(let c=n;c<o;c++){if(e[c]===t)return c;if(s.includes(e[c]))return-1}return-1}function ee(e,t,n){let r=t;for(;e[r]===n;)r++;return r}function ht(e,t){if(e[t]==="\\")return t+2;if(e[t]==="`"){let n=ee(e,t,"`"),r=yt(e,n,n-t);return r===-1?n:r+(n-t)}return-1}function yt(e,t,n){let r=e.indexOf("`",t);for(;r!==-1;){let s=ee(e,r,"`");if(s-r===n)return r;r=e.indexOf("`",s)}return-1}function Vt(e,t,n){let r=n[0],s=t;for(;s<e.length;){let o=ht(e,s);if(o!==-1){s=o;continue}if(e[s]!==r){s++;continue}let c=ee(e,s,r),a=c-s;if((n.length===1?a===1||a>=3:a>=n.length)&&s>t&&!Le(e[s-1])&&(r!=="_"||!gt(e[c])))return c-n.length;s=c}return-1}function Zt(e,t){let n=ae(e,";",t+1,t+34,`
10
- &`);if(n===-1)return null;let r=e.slice(t+1,n);if(r[0]==="#"){let o=r[1]==="x"||r[1]==="X",c=r.slice(o?2:1);if(!(o?/^[0-9a-fA-F]{1,6}$/:/^[0-9]{1,7}$/).test(c))return null;let a=parseInt(c,o?16:10);return{char:a>0&&a<=1114111&&!(a>=55296&&a<=57343)?String.fromCodePoint(a):"\uFFFD",end:n+1}}let s=Object.prototype.hasOwnProperty.call(ct,r)?ct[r]:void 0;return s===void 0?null:{char:s,end:n+1}}var bt=e=>e.replace(/\\([!-/:-@[-`{-~])/g,"$1");function Yt(e,t){if(e[t]!=="(")return null;let n=Math.min(e.length,t+ft+1024),r=t+1;for(;r<n&&Le(e[r])&&e[r]!==void 0;)r++;let s="";if(e[r]==="<"){let c=ae(e,">",r+1,n,`
11
- <`);if(c===-1)return null;s=e.slice(r+1,c),r=c+1}else{let c=r,a=0;for(;r<n;){let f=e[r];if(f==="\\"&&r+1<n){r+=2;continue}if(f===" "||f===`
12
- `)break;if(f==="("&&a++,f===")"){if(a===0)break;a--}r++}s=e.slice(c,r)}for(;r<n&&(e[r]===" "||e[r]===`
13
- `);)r++;let o=e[r];if(o==='"'||o==="'"||o==="("){let c=ae(e,o==="("?")":o,r+1,n);if(c===-1)return null;for(r=c+1;r<n&&(e[r]===" "||e[r]===`
14
- `);)r++}return e[r]!==")"?null:{address:bt(s),end:r+1}}function Oe(e,t,n=!1){let r=t+1;for(;r<e.length;){let o=ht(e,r);if(o!==-1){r=o;continue}if(!n&&e[r]==="!"&&e[r+1]==="["){let c=Oe(e,r+1,!0);if(!c)return null;r=c.end;continue}if(e[r]==="[")return null;if(e[r]==="]")break;r++}if(r>=e.length)return null;let s=Yt(e,r+1);return s?{label:e.slice(t+1,r),address:s.address,end:s.end}:null}var Jt=/^(?:[a-z][a-z0-9+.-]{1,31}:[^\s<>]*|[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+)$/i;function ue(e,t,n,r){if(r>=dt){n.appendChild(e.doc.createTextNode(t));return}let s="",o=()=>{s!==""&&n.appendChild(e.doc.createTextNode(s)),s=""},c=new Set,a=0;for(;a<t.length;){let f=t[a];if(f==="\\"){let i=t[a+1];if(i===`
15
- `)for(o(),P(e,"br",n),a+=2;t[a]===" ";)a++;else i!==void 0&&Wt.includes(i)?(s+=i,a+=2):(s+=f,a++);continue}if(f===`
16
- `){let i=s.endsWith(" "),p=s.length;for(;p>0&&s[p-1]===" ";)p--;for(s=s.slice(0,p),i?(o(),P(e,"br",n)):s+=`
17
- `,a++;t[a]===" ";)a++;continue}if(f==="`"){let i=ee(t,a,"`"),p=yt(t,i,i-a);if(p===-1){s+=t.slice(a,i),a=i;continue}let d=t.slice(i,p).replace(/\n/g," ");d.length>1&&d.startsWith(" ")&&d.endsWith(" ")&&d.trim()!==""&&(d=d.slice(1,-1)),o(),P(e,"code",n).textContent=d,a=p+(i-a);continue}if(f==="!"&&t[a+1]==="["){let i=Oe(t,a+1);if(i){let p=bt(i.label),d=q(i.address.trim(),e.base,"media");if(d===null)s+=p;else{o();let l=P(e,"img",n);l.setAttribute("src",d),l.setAttribute("alt",p),l.setAttribute("loading","lazy")}a=i.end;continue}}if(f==="["){let i=Oe(t,a);if(i){let p=q(i.address.trim(),e.base,"link");o();let d=p===null?n:P(e,"a",n);p!==null&&d.setAttribute("href",p),ue(e,i.label,d,r+1),a=i.end;continue}}if(f==="<"){let i=ae(t,">",a+1,a+ft,`
18
- <`),p=i===-1?null:t.slice(a+1,i);if(p!==null&&Jt.test(p)){let d=q(p.includes(":")?p:`mailto:${p}`,e.base,"link");if(d!==null){o();let l=P(e,"a",n);l.setAttribute("href",d),l.textContent=p,a=i+1;continue}}}if(f==="&"){let i=Zt(t,a);if(i){s+=i.char,a=i.end;continue}}if(f==="*"||f==="_"||f==="~"){let i=ee(t,a,f),p=i-a,d=!Le(t[i])&&(f!=="_"||!gt(t[a-1])),l=f==="~"?p>=2?[["~~",["del"]]]:[]:[...p>=3?[[f.repeat(3),["em","strong"]]]:[],...p>=2?[[f.repeat(2),["strong"]]]:[],[f,["em"]]],g=!1;if(d)for(let[S,E]of l){if(c.has(S))continue;let M=a+S.length,b=Vt(t,M,S);if(b===-1){c.add(S);continue}if(b===M)continue;o();let $=n;for(let L of E)$=P(e,L,$);ue(e,t.slice(M,b),$,r+1),a=b+S.length,g=!0;break}g||(s+=t.slice(a,i),a=i);continue}s+=f,a++}o()}var Qt="http://www.w3.org/1999/xhtml",en=new Set(["script","style","iframe","frame","frameset","object","embed","applet","base","link","meta","template","noscript"]),ce={href:{on:["a","area"],holds:"link"},src:{on:["img","video","audio","source"],holds:"media"},alt:{on:["img","area"],holds:"text"},title:{on:null,holds:"text"},"aria-label":{on:null,holds:"text"},placeholder:{on:["input","textarea"],holds:"text"},datetime:{on:["time","del","ins"],holds:"text"}},de=e=>e.namespaceURI===Qt,te=e=>!en.has(e.localName.toLowerCase());function Ue(e,t){if(!te(e))return"refused-element";let n=xe(t);return n===null?"type":(e.textContent=n,null)}function wt(e,t,n){return!te(e)||!de(e)?"refused-element":typeof t!="string"?"type":(e.replaceChildren(pt(t,e.ownerDocument,n.base)),null)}function kt(e,t,n){return typeof e!="string"?null:at(e)?it(e,{...t.apiUrl?{apiUrl:t.apiUrl}:{},...n?{width:n}:{}}):q(e,t.base,"media")}function St(e,t,n,r){if(!de(e)||e.localName!=="img")return"refused-element";if(typeof t!="string")return"type";let s=kt(t,n,r);return s===null?"unsafe-url":(e.removeAttribute("srcset"),e.setAttribute("src",s),null)}function At(e,t,n,r,s){let o=t.toLowerCase(),c=Object.prototype.hasOwnProperty.call(ce,o)?ce[o]:void 0;if(!c)return"unsupported";if(!te(e)||!de(e))return"refused-element";if(c.on!==null&&!c.on.includes(e.localName))return"unsupported";if(c.holds==="text"){let f=xe(n);return f===null?"type":(e.setAttribute(o,f),null)}if(typeof n!="string")return"type";let a=c.holds==="media"?kt(n,r,e.localName==="img"?s:void 0):q(n,r.base,"link");return a===null?"unsafe-url":(e.setAttribute(o,a),null)}function Rt(e,t){return!de(e)||e.localName!=="a"&&e.localName!=="area"?"unsupported":(e.setAttribute("href",t),null)}function G(e,t){t?e.removeAttribute("hidden"):e.setAttribute("hidden","")}var vt=["text","markdown","date","datetime","number"],tn=/^\d{4}-\d{2}-\d{2}$/,nn=/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;function Tt(e,t,n){let r=n&&/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(n)?n:void 0;try{if(t==="number"){let c=typeof e=="number"?e:typeof e=="string"&&e.trim()!==""?Number(e):NaN;return Number.isFinite(c)?new Intl.NumberFormat(r).format(c):null}if(typeof e!="string"||!nn.test(e))return null;let s=new Date(e);if(Number.isNaN(s.getTime()))return null;let o=tn.test(e);return new Intl.DateTimeFormat(r,{dateStyle:"long",...t==="datetime"&&!o?{timeStyle:"short"}:{},...o?{timeZone:"UTC"}:{}}).format(s)}catch{return null}}var A="data-mapled-",rn=new Set(["bind","format","width","show","hide","link","list","each","record","slug","slug-param","sort","limit","filter","expand","empty","form","form-state","form-sent","form-failed","consent","end-preview","state","preview","key","api","live","locale"]),Ie=/^[a-z0-9][a-z0-9_-]{0,63}$/,sn=/^mk_[A-Za-z0-9_]{8,128}$/,on=4,ln=100,an=2;function re(e,t=0){if(e===null||typeof e!="object"||Array.isArray(e))return!1;let{data:n,expanded:r}=e;if(n===null||typeof n!="object"||Array.isArray(n))return!1;if(r===void 0)return!0;if(r===null||typeof r!="object"||Array.isArray(r))return!1;let s=Object.values(r);return t>=an?s.length===0:s.every(o=>o===null||(Array.isArray(o)?o.every(c=>re(c,t+1)):re(o,t+1)))}var un=e=>Array.isArray(e)&&e.every(t=>re(t));function _(e){let t=e.getAttribute("id");return`${e.localName}${t&&/^[\w-]{1,40}$/.test(t)?`#${t}`:""}`}var pe=e=>(e??"").split(",").map(t=>t.trim()).filter(t=>/^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t)),xt=e=>Array.from(e.children).find(t=>t.localName==="template")??null;function Ne(e,t){let n=null,r=()=>n??(n={format:"text",attrs:[],expand:[]});for(let a of Array.from(e.attributes)){if(!a.name.startsWith(A))continue;let f=a.name.slice(A.length);if(f==="bind")r().bind=a.value;else if(f.startsWith("bind-")){let i=f.slice(5);Object.prototype.hasOwnProperty.call(ce,i)?r().attrs.push([i,a.value]):t.push({path:a.value.slice(0,80),reason:"unsupported",on:`${_(e)}[${a.name.slice(0,60)}]`})}else f==="show"?r().show=a.value:f==="hide"?r().hide=a.value:f==="link"?r().link=a.value:rn.has(f)||t.push({path:a.value.slice(0,80),reason:"unsupported",on:`${_(e)}[${a.name.slice(0,60)}]`})}if(!n)return null;let s=n,o=e.getAttribute(`${A}format`);o!==null&&(vt.includes(o)?s.format=o:t.push({path:o.slice(0,80),reason:"bad-path",on:`${_(e)}[${A}format]`}));let c=parseInt(e.getAttribute(`${A}width`)??"",10);return c>=16&&c<=4096&&(s.width=c),s.expand=pe(e.getAttribute(`${A}expand`)),s}function cn(e,t){let n={},r=e.getAttribute(`${A}sort`);r!==null&&(/^-?[a-z0-9][a-z0-9_-]{0,63}$/.test(r.trim())?n.sort=r.trim():t.push({path:r.slice(0,80),reason:"bad-path",on:`${_(e)}[${A}sort]`}));let s=parseInt(e.getAttribute(`${A}limit`)??"",10);s>=1&&s<=100&&(n.limit=s);let o=e.getAttribute(`${A}filter`);if(o!==null)for(let a of o.split(";")){if(a.trim()==="")continue;let f=a.indexOf("="),i=f===-1?"":a.slice(0,f).trim(),p=f===-1?"":a.slice(f+1).trim();Ie.test(i)&&p!==""&&p.length<=200?(n.filter??(n.filter={}))[i]=p:t.push({path:a.trim().slice(0,80),reason:"bad-path",on:`${_(e)}[${A}filter]`})}let c=pe(e.getAttribute(`${A}expand`));return c.length>0&&(n.expand=c),n}function fe(e,t){let n={bound:[],eaches:[],lists:[],records:[],issues:[]},r=s=>{if(s.hasAttributes()){let o=s.getAttribute(`${A}list`),c=s.getAttribute(`${A}record`),a=s.getAttribute(`${A}each`);if(o!==null||c!==null){let i=(o??c??"").trim();if(!t)n.issues.push({path:i.slice(0,80),reason:"unsupported",on:_(s)});else if(!Ie.test(i))n.issues.push({path:i.slice(0,80),reason:"bad-path",on:_(s)});else if(o!==null)n.lists.push({el:s,collection:i,options:cn(s,n.issues)});else{let p=s.getAttribute(`${A}slug-param`),d=s.getAttribute(`${A}slug`)??(p===null?null:new URLSearchParams(s.ownerDocument.location?.search??"").get(p)),l=fe(s,!0);for(let S of l.records)n.issues.push({path:S.collection,reason:"unsupported",on:_(S.el)});n.lists.push(...l.lists),n.issues.push(...l.issues);let g=Ne(s,n.issues);g&&l.bound.unshift({el:s,d:g}),n.records.push({el:s,collection:i,slug:d!==null&&d.trim()!==""&&d.length<=200?d.trim():null,expand:pe(s.getAttribute(`${A}expand`)),block:{...l,lists:[],records:[],issues:[]}})}return}if(a!==null){n.eaches.push({el:s,path:a});return}let f=Ne(s,n.issues);f&&n.bound.push({el:s,d:f})}for(let o of Array.from(s.children))r(o)};for(let s of Array.from(e.children))r(s);return n}function Et(e){let t=new Map,n=(r,s=[])=>{let o=Z(r)?.[0];if(!o)return;let c=t.get(o)??new Set;for(let a of s)c.add(a);t.set(o,c)};for(let{d:r}of e.bound){n(r.bind,r.expand);for(let[,s]of r.attrs)n(s,r.expand);n(r.show,r.expand),n(r.hide,r.expand);for(let s of(r.link??"").matchAll(/\{([^{}]*)\}/g))n(s[1],r.expand)}for(let r of e.eaches)n(r.path,pe(r.el.getAttribute(`${A}expand`)));return t}function ne(e,t){let n=Z(t);if(!n)return{status:"bad-path"};if(e.kind==="record")return{status:"value",value:Te(e.source,n)};if(n.length<2)return{status:"bad-path"};let r=e.singles.get(n[0]);return!r||r.status!=="ok"?{status:r?.status==="failed"?"failed":"missing"}:{status:"value",value:Te(r.value,n.slice(1))}}function Fe(e){if(typeof e?.key!="string"||!sn.test(e.key))throw new Error("Mapled: data-mapled-key takes the delivery key (mk_\u2026) \u2014 the one key that is safe in a page. A server key or a secret never goes into HTML.");if(typeof document>"u")throw new Error("Mapled: @mapled/vanilla fills a page \u2014 it runs in a browser. On a server read with @mapled/client.");let t=e.root??document.documentElement,n=t.ownerDocument,r=lt({key:e.key,apiUrl:e.apiUrl,appUrl:e.appUrl,fetch:e.fetch,...e.locale?{locale:e.locale}:{}}),s={apiUrl:e.apiUrl,base:n.baseURI},o=new Map,c=u=>{let m=o.get(u);return m||o.set(u,m={attrs:new Map}),m},a=u=>{let m=c(u);m.nodes??(m.nodes=Array.from(u.childNodes))},f=(u,m)=>{let R=c(u);R.attrs.has(m)||R.attrs.set(m,u.getAttribute(m))},i=()=>{for(let[u,m]of o){m.nodes&&u.replaceChildren(...m.nodes);for(let[R,h]of m.attrs)h===null?u.removeAttribute(R):u.setAttribute(R,h)}o.clear()},p=[],d=0,l=(u,m,R)=>p.push({path:(m??"").trim().slice(0,120),reason:R,on:_(u)});function g(u,m,R){if(m.bind!==void 0){let h=ne(R,m.bind);if(h.status!=="value")l(u,m.bind,h.status);else if(!te(u))l(u,m.bind,"refused-element");else{let w=u.localName==="img";if(w?(f(u,"src"),f(u,"srcset"),f(u,"hidden")):a(u),Y(h.value))w?G(u,!1):u.textContent="",l(u,m.bind,"empty");else{let k;if(w)k=St(u,h.value,s,m.width);else if(m.format==="markdown")k=wt(u,h.value,s);else if(m.format==="text")k=Ue(u,h.value);else{let v=Tt(h.value,m.format,u.closest("[lang]")?.getAttribute("lang")??void 0);k=v===null?"type":Ue(u,v)}k?l(u,m.bind,k):d++}}}for(let[h,w]of m.attrs){let k=ne(R,w);if(k.status!=="value"){l(u,w,k.status);continue}if(f(u,h),Y(k.value)){h==="alt"?u.setAttribute("alt",""):u.removeAttribute(h),l(u,w,"empty");continue}let v=u.getAttribute(h),T=At(u,h,k.value,s,m.width);T?(v===null&&u.removeAttribute(h),l(u,w,T)):d++}for(let[h,w]of[[m.show,!0],[m.hide,!1]]){if(h===void 0)continue;let k=ne(R,h);if(k.status!=="value"){l(u,h,k.status);continue}f(u,"hidden");let v=!Y(k.value)&&k.value!==!1;G(u,v===w),d++}if(m.link!==void 0){let h=[],w=ut(m.link,s.base,k=>{let v=ne(R,k.join("."));if(v.status==="value")return v.value;h.push(v.status)});if(h[0])l(u,m.link,h[0]);else if(!w.ok)l(u,w.path,w.reason);else{f(u,"href");let k=Rt(u,w.url);k?l(u,m.link,k):d++}}}function S(u,m,R){let h=xt(u);if(!h){l(u,"template","unsupported");return}a(u);let w=[h];for(let v of Array.from(u.children))v.hasAttribute(`${A}empty`)&&(f(v,"hidden"),G(v,m.length===0),w.push(v));let k=[];for(let v of m.slice(0,ln)){let T=h.content.cloneNode(!0);E(fe(T,!1),{kind:"record",source:v},R+1),k.push(...Array.from(T.childNodes))}u.replaceChildren(...w,...k),d++}function E(u,m,R){p.push(...u.issues);for(let{el:h,d:w}of u.bound)g(h,w,m);for(let h of u.eaches){let w=ne(m,h.path);if(w.status!=="value"){l(h.el,h.path,w.status);continue}if(R>=on){l(h.el,h.path,"unsupported");continue}let k=w.value,v=Array.isArray(k)?k:Y(k)?[]:[k];if(v.some(T=>T===null||typeof T!="object"||Array.isArray(T))){l(h.el,h.path,"type");continue}v.length===0&&l(h.el,h.path,"empty"),S(h.el,v.map(T=>({data:T})),R)}}async function M(u,m){try{let R=await u();return R===null?{status:"missing"}:m(R)?{status:"ok",value:R}:{status:"failed",answered:!0}}catch(R){return R instanceof W&&R.status===404&&R.code!=="RELEASE_NOT_FOUND"?{status:"missing"}:{status:"failed"}}}let b=null,$="",L=null,C,z=!1,U=0,N=-1,X=3,O=typeof e.live=="number"?e.live:void 0;function De(){i(),b={state:"fallback",release:null,preview:!1,bound:0,issues:[]},t.setAttribute(`${A}state`,"fallback"),t.removeAttribute(`${A}preview`)}function _e(){U++,clearTimeout(C),b?.preview&&De()}function Ot(){clearTimeout(C);let u=B();if(!u||z)return;let m=Date.parse(u.expiresAt)-Date.now();C=setTimeout(()=>{B()===null&&_e(),ge()},Math.min(Math.max(m,0)+1,2147483647))}async function je(u,m=1){let R=b===null;R&&t.setAttribute(`${A}state`,"loading");let h=U,w=await r.isPreview().catch(()=>!1),k=w?B()?.draftToken??null:null;if(b!==null&&b.preview&&!w&&De(),!w&&u!==void 0&&b!==null&&b.state==="ready"&&!b.preview&&b.release!==null&&u<=b.release)return b;let v=y=>{b=y,N=h,t.setAttribute(`${A}state`,y.state),t.toggleAttribute(`${A}preview`,y.preview);let x=y.issues.filter(D=>D.reason!=="empty"),I=x.map(D=>`${D.path} (${D.reason}) on ${D.on}`).join("; ");return I!==$&&($=I,x.length>0&&console.warn(`[mapled] ${x.length===1?"1 place":`${x.length} places`} couldn't be filled: ${I}`)),n.dispatchEvent(new CustomEvent(R?"mapled:ready":"mapled:update",{detail:y})),y.preview?Ot():(clearTimeout(C),e.live&&L===null&&(L=r.watch(D=>{ge(D.release)},{interval:O,from:y.state==="ready"?y.release:null}))),y},T=null;if(!w)if(u!==void 0)T=u;else try{let y=await r.getRelease();if(y===null)return i(),v({state:"unpublished",release:null,preview:w,bound:0,issues:[]});T=y.release}catch{return b!==null&&b.state==="ready"?b:v({state:"fallback",release:null,preview:w,bound:0,issues:[]})}let F=T===null?{}:{release:T},j=fe(t,!0),We=Ne(t,j.issues);We&&j.bound.unshift({el:t,d:We});let he=new Map,ye=new Map,se=new Map,oe=[];for(let[y,x]of Et(j))oe.push(M(()=>r.getSingle(y,{...F,...x.size>0?{expand:[...x]}:{}}),re).then(I=>he.set(y,I)));for(let y of j.lists){let x=xt(y.el),I=x?[...Et(fe(x.content,!1)).keys()]:[];oe.push(M(async()=>(await r.getRecords(y.collection,{...y.options,...F,...I.length>0?{fields:I}:{}})).records,un).then(D=>ye.set(y,D)))}for(let y of j.records){if(y.slug===null){se.set(y,{status:"missing"});continue}let x=y.slug;oe.push(M(()=>r.getRecordBySlug(y.collection,x,{...F,...y.expand.length>0?{expand:y.expand}:{}}),re).then(I=>se.set(y,I)))}await Promise.all(oe);let Ge=await r.isPreview().catch(()=>!1)?B()?.draftToken??null:null;if(h!==U||Ge!==k)return m<X?je(u,m+1):(i(),v({state:"fallback",release:null,preview:Ge!==null,bound:0,issues:[]}));let Xe=[...he.values(),...ye.values(),...se.values()];if(Xe.length>0&&Xe.every(y=>y.status==="failed"&&!y.answered))return b!==null&&b.state==="ready"?b:v({state:"fallback",release:T,preview:w,bound:0,issues:[]});i(),p=[],d=0,E(j,{kind:"page",singles:he},0);for(let y of j.lists){let x=ye.get(y);x?.status==="ok"?S(y.el,x.value,0):l(y.el,y.collection,x?.status??"failed")}for(let y of j.records){let x=se.get(y);x?.status==="ok"?E(y.block,{kind:"record",source:x.value},0):l(y.el,y.collection,x?.status??"failed")}return qe(),v({state:"ready",release:T,preview:w,bound:d,issues:p})}let Ke=new WeakSet,Be=new WeakSet;function qe(){for(let u of Array.from(t.querySelectorAll(`form[${A}form]`)))Ke.has(u)||(Ke.add(u),u.addEventListener("submit",m=>{m.preventDefault(),Ct(u)}))}async function Ct(u){if(u.getAttribute(`${A}form-state`)==="sending")return;let m=(u.getAttribute(`${A}form`)??"").trim(),R=u.querySelector(`[${A}form-sent]`),h=u.querySelector(`[${A}form-failed]`),w=Array.from(u.querySelectorAll("button, input[type=submit]")).filter(F=>!F.disabled);u.setAttribute(`${A}form-state`,"sending");for(let F of w)F.disabled=!0;let k=u.querySelector(`input[type=checkbox][${A}consent]`),v=k?.checked?(k.labels?.[0]?.textContent??k.closest("label")?.textContent??"").trim().slice(0,500):"",T=Ie.test(m)?await Re(m,u,{key:e.key,apiUrl:e.apiUrl,fetch:e.fetch,...v?{consent:v}:{}}):{ok:!1,status:0,code:"BAD_FORM",message:"This form isn't set up yet."};for(let F of w)F.disabled=!1;u.setAttribute(`${A}form-state`,T.ok?"sent":"failed"),T.ok&&u.reset(),R&&G(R,T.ok),h&&(!T.ok&&(Be.has(h)||(h.textContent??"").trim()==="")&&(Be.add(h),h.textContent=T.message),G(h,!T.ok)),n.dispatchEvent(new CustomEvent("mapled:form",{detail:T.ok?{form:m,ok:!0}:{form:m,ok:!1,status:T.status,code:T.code}}))}qe();let me=async u=>{try{return await je(u)}catch(m){return console.error(`[mapled] ${m instanceof Error?m.message:"The page couldn't be filled."}`),b??{state:"fallback",release:null,preview:!1,bound:0,issues:[]}}},V=me(),ge=u=>V=V.then(()=>me(u)),He=()=>{Se(),_e();let u=U;return V=V.then(()=>N>=u&&b!==null?b:me())},ze=u=>{u.target?.closest?.(`[${A}end-preview]`)&&(u.preventDefault(),He())};return n.addEventListener("click",ze),{ready:V,get report(){return b},refresh:()=>ge(),endPreview:He,stop:()=>{z=!0,L?.(),L=()=>{},clearTimeout(C),n.removeEventListener("click",ze)},client:r}}var Mt="0.2.3";var Pt={version:Mt,mount:Fe,page:null};window.Mapled=Pt;var H=document.currentScript;if(H instanceof HTMLScriptElement&&H.hasAttribute("data-mapled-key")){let e=H.getAttribute("data-mapled-live"),t=e===null?NaN:parseInt(e,10),n=()=>{try{Pt.page=Fe({key:H.getAttribute("data-mapled-key")??"",...H.getAttribute("data-mapled-api")?{apiUrl:H.getAttribute("data-mapled-api")}:{},...H.getAttribute("data-mapled-locale")?{locale:H.getAttribute("data-mapled-locale")}:{},...e===null?{}:{live:Number.isFinite(t)?t:!0}})}catch(r){console.error(r instanceof Error?r.message:"Mapled couldn't start.")}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",n,{once:!0}):n()}})();
7
+ `:"";continue}let o=ht(l);if(o){ge(e,o.text,C(e,`h${o.level}`,n),r),s++;continue}if(Ue(l)){C(e,"hr",n),s++;continue}if($e(l)){let p=[];for(;s<t.length&&$e(t[s]);)p.push(t[s].replace(/^ {0,3}> ?/,"")),s++;Ne(e,p,C(e,"blockquote",n),r+1);continue}let d=re(l);if(d){s=Jt(e,t,s,d,n,r);continue}let i=[l.trimStart()];for(s++;s<t.length&&!Pe(t[s])&&!Yt(t[s]);)i.push(t[s].trimStart()),s++;ge(e,i.join(`
8
+ `),C(e,"p",n),r)}}function Jt(e,t,n,r,s,l){let c=C(e,r.ordered?"ol":"ul",s);r.ordered&&r.start!==1&&c.setAttribute("start",String(r.start));let o=[],d=!1;for(;n<t.length;){let i=re(t[n]);if(!i||i.ordered!==r.ordered||i.indent!==r.indent||Ue(t[n]))break;let p=i.indent+i.width,f=[i.rest];n++;let a=!1;for(;n<t.length;){let g=t[n];if(Pe(g)){a=!0,f.push(""),n++;continue}if(se(g)<p)break;a&&f.some(R=>R!=="")&&!re(g.slice(p))&&(d=!0),a=!1,f.push(g.slice(p)),n++}for(;f.length>0&&f[f.length-1]==="";)f.pop();if(a&&n<t.length){let g=re(t[n]);g&&g.ordered===r.ordered&&g.indent===r.indent&&(d=!0)}o.push({lines:f,el:C(e,"li",c)})}for(let i of o)if(Ne(e,i.lines,i.el,l+1),!d){for(let p of Array.from(i.el.childNodes))if(p.nodeName==="P"){for(;p.firstChild;)i.el.insertBefore(p.firstChild,p);i.el.removeChild(p)}}return n}var Ie=e=>e===void 0||e===" "||e===`
9
+ `,yt=e=>e!==void 0&&/[\p{L}\p{N}]/u.test(e);function me(e,t,n,r,s=""){let l=Math.min(e.length,r);for(let c=n;c<l;c++){if(e[c]===t)return c;if(s.includes(e[c]))return-1}return-1}function oe(e,t,n){let r=t;for(;e[r]===n;)r++;return r}function bt(e,t){if(e[t]==="\\")return t+2;if(e[t]==="`"){let n=oe(e,t,"`"),r=wt(e,n,n-t);return r===-1?n:r+(n-t)}return-1}function wt(e,t,n){let r=e.indexOf("`",t);for(;r!==-1;){let s=oe(e,r,"`");if(s-r===n)return r;r=e.indexOf("`",s)}return-1}function Qt(e,t,n){let r=n[0],s=t;for(;s<e.length;){let l=bt(e,s);if(l!==-1){s=l;continue}if(e[s]!==r){s++;continue}let c=oe(e,s,r),o=c-s;if((n.length===1?o===1||o>=3:o>=n.length)&&s>t&&!Ie(e[s-1])&&(r!=="_"||!yt(e[c])))return c-n.length;s=c}return-1}function en(e,t){let n=me(e,";",t+1,t+34,`
10
+ &`);if(n===-1)return null;let r=e.slice(t+1,n);if(r[0]==="#"){let l=r[1]==="x"||r[1]==="X",c=r.slice(l?2:1);if(!(l?/^[0-9a-fA-F]{1,6}$/:/^[0-9]{1,7}$/).test(c))return null;let o=parseInt(c,l?16:10);return{char:o>0&&o<=1114111&&!(o>=55296&&o<=57343)?String.fromCodePoint(o):"\uFFFD",end:n+1}}let s=Object.prototype.hasOwnProperty.call(ft,r)?ft[r]:void 0;return s===void 0?null:{char:s,end:n+1}}var kt=e=>e.replace(/\\([!-/:-@[-`{-~])/g,"$1");function tn(e,t){if(e[t]!=="(")return null;let n=Math.min(e.length,t+mt+1024),r=t+1;for(;r<n&&Ie(e[r])&&e[r]!==void 0;)r++;let s="";if(e[r]==="<"){let c=me(e,">",r+1,n,`
11
+ <`);if(c===-1)return null;s=e.slice(r+1,c),r=c+1}else{let c=r,o=0;for(;r<n;){let d=e[r];if(d==="\\"&&r+1<n){r+=2;continue}if(d===" "||d===`
12
+ `)break;if(d==="("&&o++,d===")"){if(o===0)break;o--}r++}s=e.slice(c,r)}for(;r<n&&(e[r]===" "||e[r]===`
13
+ `);)r++;let l=e[r];if(l==='"'||l==="'"||l==="("){let c=me(e,l==="("?")":l,r+1,n);if(c===-1)return null;for(r=c+1;r<n&&(e[r]===" "||e[r]===`
14
+ `);)r++}return e[r]!==")"?null:{address:kt(s),end:r+1}}function Le(e,t,n=!1){let r=t+1;for(;r<e.length;){let l=bt(e,r);if(l!==-1){r=l;continue}if(!n&&e[r]==="!"&&e[r+1]==="["){let c=Le(e,r+1,!0);if(!c)return null;r=c.end;continue}if(e[r]==="[")return null;if(e[r]==="]")break;r++}if(r>=e.length)return null;let s=tn(e,r+1);return s?{label:e.slice(t+1,r),address:s.address,end:s.end}:null}var nn=/^(?:[a-z][a-z0-9+.-]{1,31}:[^\s<>]*|[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+)$/i;function ge(e,t,n,r){if(r>=pt){n.appendChild(e.doc.createTextNode(t));return}let s="",l=()=>{s!==""&&n.appendChild(e.doc.createTextNode(s)),s=""},c=new Set,o=0;for(;o<t.length;){let d=t[o];if(d==="\\"){let i=t[o+1];if(i===`
15
+ `)for(l(),C(e,"br",n),o+=2;t[o]===" ";)o++;else i!==void 0&&Zt.includes(i)?(s+=i,o+=2):(s+=d,o++);continue}if(d===`
16
+ `){let i=s.endsWith(" "),p=s.length;for(;p>0&&s[p-1]===" ";)p--;for(s=s.slice(0,p),i?(l(),C(e,"br",n)):s+=`
17
+ `,o++;t[o]===" ";)o++;continue}if(d==="`"){let i=oe(t,o,"`"),p=wt(t,i,i-o);if(p===-1){s+=t.slice(o,i),o=i;continue}let f=t.slice(i,p).replace(/\n/g," ");f.length>1&&f.startsWith(" ")&&f.endsWith(" ")&&f.trim()!==""&&(f=f.slice(1,-1)),l(),C(e,"code",n).textContent=f,o=p+(i-o);continue}if(d==="!"&&t[o+1]==="["){let i=Le(t,o+1);if(i){let p=kt(i.label),f=H(i.address.trim(),e.base,"media");if(f===null)s+=p;else{l();let a=C(e,"img",n);a.setAttribute("src",f),a.setAttribute("alt",p),a.setAttribute("loading","lazy")}o=i.end;continue}}if(d==="["){let i=Le(t,o);if(i){let p=H(i.address.trim(),e.base,"link");l();let f=p===null?n:C(e,"a",n);p!==null&&f.setAttribute("href",p),ge(e,i.label,f,r+1),o=i.end;continue}}if(d==="<"){let i=me(t,">",o+1,o+mt,`
18
+ <`),p=i===-1?null:t.slice(o+1,i);if(p!==null&&nn.test(p)){let f=H(p.includes(":")?p:`mailto:${p}`,e.base,"link");if(f!==null){l();let a=C(e,"a",n);a.setAttribute("href",f),a.textContent=p,o=i+1;continue}}}if(d==="&"){let i=en(t,o);if(i){s+=i.char,o=i.end;continue}}if(d==="*"||d==="_"||d==="~"){let i=oe(t,o,d),p=i-o,f=!Ie(t[i])&&(d!=="_"||!yt(t[o-1])),a=d==="~"?p>=2?[["~~",["del"]]]:[]:[...p>=3?[[d.repeat(3),["em","strong"]]]:[],...p>=2?[[d.repeat(2),["strong"]]]:[],[d,["em"]]],g=!1;if(f)for(let[R,O]of a){if(c.has(R))continue;let P=o+R.length,w=Qt(t,P,R);if(w===-1){c.add(R);continue}if(w===P)continue;l();let U=n;for(let N of O)U=C(e,N,U);ge(e,t.slice(P,w),U,r+1),o=w+R.length,g=!0;break}g||(s+=t.slice(o,i),o=i);continue}s+=d,o++}l()}var rn="http://www.w3.org/1999/xhtml",sn=new Set(["script","style","iframe","frame","frameset","object","embed","applet","base","link","meta","template","noscript"]),ie={href:{on:["a","area"],holds:"link"},src:{on:["img","video","audio","source"],holds:"media"},alt:{on:["img","area"],holds:"text"},title:{on:null,holds:"text"},"aria-label":{on:null,holds:"text"},placeholder:{on:["input","textarea"],holds:"text"},datetime:{on:["time","del","ins"],holds:"text"}},le=e=>e.namespaceURI===rn,he=e=>!sn.has(e.localName.toLowerCase());function Fe(e,t){if(!he(e))return"refused-element";let n=Oe(t);return n===null?"type":(e.textContent=n,null)}function St(e,t,n){return!he(e)||!le(e)?"refused-element":typeof t!="string"?"type":(e.replaceChildren(gt(t,e.ownerDocument,n.base)),null)}function Rt(e,t,n){return typeof e!="string"?null:ct(e)?at(e,{...t.apiUrl?{apiUrl:t.apiUrl}:{},...n?{width:n}:{}}):H(e,t.base,"media")}function At(e,t,n,r){if(!le(e)||e.localName!=="img")return"refused-element";if(typeof t!="string")return"type";let s=Rt(t,n,r);return s===null?"unsafe-url":(e.removeAttribute("srcset"),e.setAttribute("src",s),null)}function vt(e,t){return!he(e)||(e.localName==="img"||t==="markdown")&&!le(e)?"refused-element":null}function De(e,t){let n=t.toLowerCase(),r=Object.prototype.hasOwnProperty.call(ie,n)?ie[n]:void 0;return r?!he(e)||!le(e)?"refused-element":r.on!==null&&!r.on.includes(e.localName)?"unsupported":null:"unsupported"}function Tt(e,t,n,r,s){let l=De(e,t);if(l)return l;let c=t.toLowerCase(),o=ie[c];if(o.holds==="text"){let i=Oe(n);return i===null?"type":(e.setAttribute(c,i),null)}if(typeof n!="string")return"type";let d=o.holds==="media"?Rt(n,r,e.localName==="img"?s:void 0):H(n,r.base,"link");return d===null?"unsafe-url":(e.setAttribute(c,d),null)}function xt(e,t){return!le(e)||e.localName!=="a"&&e.localName!=="area"?"unsupported":(e.setAttribute("href",t),null)}function Z(e,t){t?e.removeAttribute("hidden"):e.setAttribute("hidden","")}var Et=["text","markdown","date","datetime","number"],on=/^\d{4}-\d{2}-\d{2}$/,ln=/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;function Mt(e,t,n){let r=n&&/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(n)?n:void 0;try{if(t==="number"){let c=typeof e=="number"?e:typeof e=="string"&&e.trim()!==""?Number(e):NaN;return Number.isFinite(c)?new Intl.NumberFormat(r).format(c):null}if(typeof e!="string"||!ln.test(e))return null;let s=new Date(e);if(Number.isNaN(s.getTime()))return null;let l=on.test(e);return new Intl.DateTimeFormat(r,{dateStyle:"long",...t==="datetime"&&!l?{timeStyle:"short"}:{},...l?{timeZone:"UTC"}:{}}).format(s)}catch{return null}}var S="data-mapled-",an=new Set(["bind","format","width","show","hide","link","list","each","record","slug","slug-param","sort","limit","filter","expand","empty","form","form-state","form-sent","form-failed","consent","end-preview","state","preview","key","api","live","locale","type"]),ce=/^[a-z0-9][a-z0-9_-]{0,63}$/,un=/^mk_[A-Za-z0-9_]{8,128}$/,cn=4,dn=100,Ot=2,Y=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function ue(e,t=0){if(!Y(e))return!1;let{data:n,expanded:r,expandedBlocks:s}=e;if(!Y(n))return!1;let l=o=>Y(o)&&(t>=Ot?Object.keys(o).length===0:Object.values(o).every(d=>d===null||(Array.isArray(d)?d.every(i=>ue(i,t+1)):ue(d,t+1))));if(r!==void 0&&!l(r))return!1;if(s===void 0)return!0;if(!Y(s))return!1;let c=Object.entries(s);return t>=Ot?c.length===0:c.every(([o,d])=>{let i=Object.prototype.hasOwnProperty.call(n,o)?n[o]:void 0;return Array.isArray(d)&&(!Array.isArray(i)||d.length===i.length)&&d.every(l)})}var fn=e=>Array.isArray(e)&&e.every(t=>ue(t));function j(e){let t=e.getAttribute("id");return`${e.localName}${t&&/^[\w-]{1,40}$/.test(t)?`#${t}`:""}`}var be=e=>(e??"").split(",").map(t=>t.trim()).filter(t=>/^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t));function Pt(e){let t={plain:null,typed:new Map,all:[],bad:[]};for(let n of Array.from(e.children)){if(n.localName!=="template")continue;let r=n;t.all.push(r);let s=r.getAttribute(`${S}type`);s===null?t.plain??(t.plain=r):ce.test(s.trim())?t.typed.has(s.trim())||t.typed.set(s.trim(),r):t.bad.push(r)}return t}function pn(e){let t=e.data;if(t===null||typeof t!="object"||Array.isArray(t)||!Object.prototype.hasOwnProperty.call(t,"_type"))return null;let n=t._type;return typeof n=="string"&&ce.test(n)?n:null}function _e(e,t){let n=null,r=()=>n??(n={format:"text",attrs:[],expand:[]});for(let o of Array.from(e.attributes)){if(!o.name.startsWith(S))continue;let d=o.name.slice(S.length);if(d==="bind")r().bind=o.value;else if(d.startsWith("bind-")){let i=d.slice(5);Object.prototype.hasOwnProperty.call(ie,i)?r().attrs.push([i,o.value]):t.push({path:o.value.slice(0,80),reason:"unsupported",on:`${j(e)}[${o.name.slice(0,60)}]`})}else d==="show"?r().show=o.value:d==="hide"?r().hide=o.value:d==="link"?r().link=o.value:an.has(d)||t.push({path:o.value.slice(0,80),reason:"unsupported",on:`${j(e)}[${o.name.slice(0,60)}]`})}if(!n)return null;let s=n,l=e.getAttribute(`${S}format`);l!==null&&(Et.includes(l)?s.format=l:t.push({path:l.slice(0,80),reason:"bad-path",on:`${j(e)}[${S}format]`}));let c=parseInt(e.getAttribute(`${S}width`)??"",10);return c>=16&&c<=4096&&(s.width=c),s.expand=be(e.getAttribute(`${S}expand`)),s}function mn(e,t){let n={},r=e.getAttribute(`${S}sort`);r!==null&&(/^-?[a-z0-9][a-z0-9_-]{0,63}$/.test(r.trim())?n.sort=r.trim():t.push({path:r.slice(0,80),reason:"bad-path",on:`${j(e)}[${S}sort]`}));let s=parseInt(e.getAttribute(`${S}limit`)??"",10);s>=1&&s<=100&&(n.limit=s);let l=e.getAttribute(`${S}filter`);if(l!==null)for(let o of l.split(";")){if(o.trim()==="")continue;let d=o.indexOf("="),i=d===-1?"":o.slice(0,d).trim(),p=d===-1?"":o.slice(d+1).trim();ce.test(i)&&p!==""&&p.length<=200?(n.filter??(n.filter={}))[i]=p:t.push({path:o.trim().slice(0,80),reason:"bad-path",on:`${j(e)}[${S}filter]`})}let c=be(e.getAttribute(`${S}expand`));return c.length>0&&(n.expand=c),n}function ye(e,t){let n={bound:[],eaches:[],lists:[],records:[],issues:[]},r=s=>{if(s.hasAttributes()){let l=s.getAttribute(`${S}list`),c=s.getAttribute(`${S}record`),o=s.getAttribute(`${S}each`);if(l!==null||c!==null){let i=(l??c??"").trim();if(!t)n.issues.push({path:i.slice(0,80),reason:"unsupported",on:j(s)});else if(!ce.test(i))n.issues.push({path:i.slice(0,80),reason:"bad-path",on:j(s)});else if(l!==null)n.lists.push({el:s,collection:i,options:mn(s,n.issues)});else{let p=s.getAttribute(`${S}slug-param`),f=s.getAttribute(`${S}slug`)??(p===null?null:new URLSearchParams(s.ownerDocument.location?.search??"").get(p)),a=ye(s,!0);for(let R of a.records)n.issues.push({path:R.collection,reason:"unsupported",on:j(R.el)});n.lists.push(...a.lists),n.issues.push(...a.issues);let g=_e(s,n.issues);g&&a.bound.unshift({el:s,d:g}),n.records.push({el:s,collection:i,slug:f!==null&&f.trim()!==""&&f.length<=200?f.trim():null,expand:be(s.getAttribute(`${S}expand`)),block:{...a,lists:[],records:[],issues:[]}})}return}if(o!==null){n.eaches.push({el:s,path:o});return}let d=_e(s,n.issues);d&&n.bound.push({el:s,d})}for(let l of Array.from(s.children))r(l)};for(let s of Array.from(e.children))r(s);return n}function Ct(e){let t=new Map,n=(r,s=[])=>{let l=te(r)?.[0];if(!l)return;let c=t.get(l)??new Set;for(let o of s)c.add(o);t.set(l,c)};for(let{d:r}of e.bound){n(r.bind,r.expand);for(let[,s]of r.attrs)n(s,r.expand);n(r.show,r.expand),n(r.hide,r.expand);for(let s of(r.link??"").matchAll(/\{([^{}]*)\}/g))n(s[1],r.expand)}for(let r of e.eaches)n(r.path,be(r.el.getAttribute(`${S}expand`)));return t}function ae(e,t){let n=te(t);if(!n)return{status:"bad-path"};if(e.kind==="record")return{status:"value",...Me(e.source,n)};if(n.length<2)return{status:"bad-path"};let r=e.singles.get(n[0]);return!r||r.status!=="ok"?{status:r?.status==="failed"?"failed":"missing"}:{status:"value",...Me(r.value,n.slice(1))}}function je(e){if(typeof e?.key!="string"||!un.test(e.key))throw new Error("Mapled: data-mapled-key takes the delivery key (mk_\u2026) \u2014 the one key that is safe in a page. A server key or a secret never goes into HTML.");if(typeof document>"u")throw new Error("Mapled: @mapled/vanilla fills a page \u2014 it runs in a browser. On a server read with @mapled/client.");let t=e.root??document.documentElement,n=t.ownerDocument,r=ut({key:e.key,apiUrl:e.apiUrl,appUrl:e.appUrl,fetch:e.fetch,...e.locale?{locale:e.locale}:{}}),s={apiUrl:e.apiUrl,base:n.baseURI},l=new Map,c=u=>{let m=l.get(u);return m||l.set(u,m={attrs:new Map}),m},o=u=>{let m=c(u);m.nodes??(m.nodes=Array.from(u.childNodes))},d=(u,m)=>{let A=c(u);A.attrs.has(m)||A.attrs.set(m,u.getAttribute(m))},i=()=>{for(let[u,m]of l){m.nodes&&u.replaceChildren(...m.nodes);for(let[A,h]of m.attrs)h===null?u.removeAttribute(A):u.setAttribute(A,h)}l.clear()},p=[],f=0,a=(u,m,A)=>p.push({path:(m??"").trim().slice(0,120),reason:A,on:j(u)});function g(u,m,A){if(m.bind!==void 0){let h=ae(A,m.bind),y=vt(u,m.format);if(h.status!=="value")a(u,m.bind,h.status);else if(y)a(u,m.bind,y);else{let k=u.localName==="img";if(k?(d(u,"src"),d(u,"srcset"),d(u,"hidden")):o(u),ne(h.value))k?Z(u,!1):u.textContent="",a(u,m.bind,"empty");else{let v;if(k)v=At(u,h.value,s,m.width);else if(m.format==="markdown")v=St(u,h.value,s);else if(m.format==="text")v=Fe(u,h.value);else{let T=Mt(h.value,m.format,u.closest("[lang]")?.getAttribute("lang")??void 0);v=T===null?"type":Fe(u,T)}v?a(u,m.bind,v):f++}}}for(let[h,y]of m.attrs){let k=ae(A,y);if(k.status!=="value"){a(u,y,k.status);continue}let v=De(u,h);if(v){a(u,y,v);continue}if(d(u,h),ne(k.value)){h==="alt"?u.setAttribute("alt",""):u.removeAttribute(h),a(u,y,"empty");continue}let T=u.getAttribute(h),x=Tt(u,h,k.value,s,m.width);x?(T===null&&u.removeAttribute(h),a(u,y,x)):f++}for(let[h,y]of[[m.show,!0],[m.hide,!1]]){if(h===void 0)continue;let k=ae(A,h);if(k.status!=="value"){a(u,h,k.status);continue}d(u,"hidden");let v=!ne(k.value)&&k.value!==!1;Z(u,v===y),f++}if(m.link!==void 0){let h=[],y=dt(m.link,s.base,k=>{let v=ae(A,k.join("."));if(v.status==="value")return v.value;h.push(v.status)});if(h[0])a(u,m.link,h[0]);else if(!y.ok)a(u,y.path,y.reason);else{d(u,"href");let k=xt(u,y.url);k?a(u,m.link,k):f++}}}function R(u,m,A,h,y){let k=Pt(u);for(let E of k.bad)a(E,E.getAttribute(`${S}type`)??"","bad-path");if(!k.plain&&k.typed.size===0){a(u,"template","unsupported");return}o(u);let v=[...k.all];for(let E of Array.from(u.children))E.hasAttribute(`${S}empty`)&&(d(E,"hidden"),Z(E,m.length===0),v.push(E));let T=[],x=!1;for(let E of m.slice(0,dn)){let ee=y?pn(E):null,G=(ee===null?void 0:k.typed.get(ee))??k.plain;if(!G){x=!0;continue}let X=G.content.cloneNode(!0);O(ye(X,!1),{kind:"record",source:E},A+1),T.push(...Array.from(X.childNodes))}x&&a(u,h,"no-template"),u.replaceChildren(...v,...T),f++}function O(u,m,A){p.push(...u.issues);for(let{el:h,d:y}of u.bound)g(h,y,m);for(let h of u.eaches){let y=ae(m,h.path);if(y.status!=="value"){a(h.el,h.path,y.status);continue}if(A>=cn){a(h.el,h.path,"unsupported");continue}if(y.linked!==void 0){let x=Array.isArray(y.linked)?y.linked:y.linked===null?[]:[y.linked];x.length===0&&a(h.el,h.path,"empty"),R(h.el,x,A,h.path,!1);continue}let k=y.value,v=Array.isArray(k)?k:ne(k)?[]:[k];if(v.some(x=>!Y(x))){a(h.el,h.path,"type");continue}v.length===0&&a(h.el,h.path,"empty");let T=Array.isArray(k)&&Array.isArray(y.blocks)?y.blocks:[];R(h.el,v.map((x,E)=>Y(T[E])?{data:x,expanded:T[E]}:{data:x}),A,h.path,!0)}}async function P(u,m){try{let A=await u();return A===null?{status:"missing"}:m(A)?{status:"ok",value:A}:{status:"failed",answered:!0}}catch(A){return A instanceof V&&A.status===404&&A.code!=="RELEASE_NOT_FOUND"?{status:"missing"}:{status:"failed"}}}let w=null,U="",N=null,L,W=!1,I=0,F=-1,J=3,$=typeof e.live=="number"?e.live:void 0;function Ke(){i(),w={state:"fallback",release:null,preview:!1,bound:0,issues:[]},t.setAttribute(`${S}state`,"fallback"),t.removeAttribute(`${S}preview`)}function Be(){I++,clearTimeout(L),w?.preview&&Ke()}function Ut(){clearTimeout(L);let u=B();if(!u||W)return;let m=Date.parse(u.expiresAt)-Date.now();L=setTimeout(()=>{B()===null&&Be(),ke()},Math.min(Math.max(m,0)+1,2147483647))}async function He(u,m=1){let A=w===null;A&&t.setAttribute(`${S}state`,"loading");let h=I,y=await r.isPreview().catch(()=>!1),k=y?B()?.draftToken??null:null;if(w!==null&&w.preview&&!y&&Ke(),!y&&u!==void 0&&w!==null&&w.state==="ready"&&!w.preview&&w.release!==null&&u<=w.release)return w;let v=b=>{w=b,F=h,t.setAttribute(`${S}state`,b.state),t.toggleAttribute(`${S}preview`,b.preview);let M=b.issues.filter(_=>_.reason!=="empty"),D=M.map(_=>`${_.path} (${_.reason}) on ${_.on}`).join("; ");return D!==U&&(U=D,M.length>0&&console.warn(`[mapled] ${M.length===1?"1 place":`${M.length} places`} couldn't be filled: ${D}`)),n.dispatchEvent(new CustomEvent(A?"mapled:ready":"mapled:update",{detail:b})),b.preview?Ut():(clearTimeout(L),e.live&&N===null&&(N=r.watch(_=>{ke(_.release)},{interval:$,from:b.state==="ready"?b.release:null}))),b},T=null;if(!y)if(u!==void 0)T=u;else try{let b=await r.getRelease();if(b===null)return i(),v({state:"unpublished",release:null,preview:y,bound:0,issues:[]});T=b.release}catch{return w!==null&&w.state==="ready"?w:v({state:"fallback",release:null,preview:y,bound:0,issues:[]})}let x=T===null?{}:{release:T},E=ye(t,!0),ee=_e(t,E.issues);ee&&E.bound.unshift({el:t,d:ee});let G=new Map,X=new Map,de=new Map,fe=[];for(let[b,M]of Ct(E))fe.push(P(()=>r.getSingle(b,{...x,...M.size>0?{expand:[...M]}:{}}),ue).then(D=>G.set(b,D)));for(let b of E.lists){let M=Pt(b.el).plain,D=M?[...Ct(ye(M.content,!1)).keys()]:[];fe.push(P(async()=>(await r.getRecords(b.collection,{...b.options,...x,...D.length>0?{fields:D}:{}})).records,fn).then(_=>X.set(b,_)))}for(let b of E.records){if(b.slug===null){de.set(b,{status:"missing"});continue}let M=b.slug;fe.push(P(()=>r.getRecordBySlug(b.collection,M,{...x,...b.expand.length>0?{expand:b.expand}:{}}),ue).then(D=>de.set(b,D)))}await Promise.all(fe);let Ve=await r.isPreview().catch(()=>!1)?B()?.draftToken??null:null;if(h!==I||Ve!==k)return m<J?He(u,m+1):(i(),v({state:"fallback",release:null,preview:Ve!==null,bound:0,issues:[]}));let Ze=[...G.values(),...X.values(),...de.values()];if(Ze.length>0&&Ze.every(b=>b.status==="failed"&&!b.answered))return w!==null&&w.state==="ready"?w:v({state:"fallback",release:T,preview:y,bound:0,issues:[]});i(),p=[],f=0,O(E,{kind:"page",singles:G},0);for(let b of E.lists){let M=X.get(b);M?.status==="ok"?R(b.el,M.value,0,b.collection,!1):a(b.el,b.collection,M?.status??"failed")}for(let b of E.records){let M=de.get(b);M?.status==="ok"?O(b.block,{kind:"record",source:M.value},0):a(b.el,b.collection,M?.status??"failed")}return We(),v({state:"ready",release:T,preview:y,bound:f,issues:p})}let qe=new WeakSet,ze=new WeakSet;function We(){for(let u of Array.from(t.querySelectorAll(`form[${S}form]`)))qe.has(u)||(qe.add(u),u.addEventListener("submit",m=>{m.preventDefault(),Nt(u)}))}async function Nt(u){if(u.getAttribute(`${S}form-state`)==="sending")return;let m=(u.getAttribute(`${S}form`)??"").trim(),A=u.querySelector(`[${S}form-sent]`),h=u.querySelector(`[${S}form-failed]`),y=Array.from(u.querySelectorAll("button, input[type=submit]")).filter(x=>!x.disabled);u.setAttribute(`${S}form-state`,"sending");for(let x of y)x.disabled=!0;let k=u.querySelector(`input[type=checkbox][${S}consent]`),v=k?.checked?(k.labels?.[0]?.textContent??k.closest("label")?.textContent??"").trim().slice(0,500):"",T=ce.test(m)?await xe(m,u,{key:e.key,apiUrl:e.apiUrl,fetch:e.fetch,...v?{consent:v}:{}}):{ok:!1,status:0,code:"BAD_FORM",message:"This form isn't set up yet."};for(let x of y)x.disabled=!1;u.setAttribute(`${S}form-state`,T.ok?"sent":"failed"),T.ok&&u.reset(),A&&Z(A,T.ok),h&&(!T.ok&&(ze.has(h)||(h.textContent??"").trim()==="")&&(ze.add(h),h.textContent=T.message),Z(h,!T.ok)),n.dispatchEvent(new CustomEvent("mapled:form",{detail:T.ok?{form:m,ok:!0}:{form:m,ok:!1,status:T.status,code:T.code}}))}We();let we=async u=>{try{return await He(u)}catch(m){return console.error(`[mapled] ${m instanceof Error?m.message:"The page couldn't be filled."}`),w??{state:"fallback",release:null,preview:!1,bound:0,issues:[]}}},Q=we(),ke=u=>Q=Q.then(()=>we(u)),Ge=()=>{ve(),Be();let u=I;return Q=Q.then(()=>F>=u&&w!==null?w:we())},Xe=u=>{u.target?.closest?.(`[${S}end-preview]`)&&(u.preventDefault(),Ge())};return n.addEventListener("click",Xe),{ready:Q,get report(){return w},refresh:()=>ke(),endPreview:Ge,stop:()=>{W=!0,N?.(),N=()=>{},clearTimeout(L),n.removeEventListener("click",Xe)},client:r}}var $t="0.3.0";var Lt={version:$t,mount:je,page:null};window.Mapled=Lt;var q=document.currentScript;if(q instanceof HTMLScriptElement&&q.hasAttribute("data-mapled-key")){let e=q.getAttribute("data-mapled-live"),t=e===null?NaN:parseInt(e,10),n=()=>{try{Lt.page=je({key:q.getAttribute("data-mapled-key")??"",...q.getAttribute("data-mapled-api")?{apiUrl:q.getAttribute("data-mapled-api")}:{},...q.getAttribute("data-mapled-locale")?{locale:q.getAttribute("data-mapled-locale")}:{},...e===null?{}:{live:Number.isFinite(t)?t:!0}})}catch(r){console.error(r instanceof Error?r.message:"Mapled couldn't start.")}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",n,{once:!0}):n()}})();
@@ -1 +1 @@
1
- sha384-SuKH/yk2hpNI+Ec2QGKpZ7jVxe+0Ep0x/fchL560g+XuzG5hnVCIhG9OKX6BKdpY
1
+ sha384-evhHWpu15FOXqD3S7ZXIrtTTDv71hVrOE6l9eHWeeo2NzJh4o9eE3VUkQOnkMj/R
package/dist/paths.d.ts CHANGED
@@ -4,16 +4,31 @@
4
4
  content, read by own properties only (DOM_SECURITY.md, rule 7). */
5
5
  /** The segments of a path, or null when it is outside the grammar. */
6
6
  export declare function parsePath(text: string | null | undefined): string[] | null;
7
- /** What paths resolve in: a record as Mapled serves it, or an item of a
8
- repeatable group (a plain object of the record's data). */
7
+ /** What paths resolve in: a record as Mapled serves it — the records its
8
+ relations link to under `expanded`, those of its blocks' relations
9
+ under `expandedBlocks` — or an item of a repeatable group or of a
10
+ components field (a plain object of the record's data; a block's
11
+ linked records are its `expanded`). */
9
12
  export type Source = {
10
13
  data: unknown;
11
14
  expanded?: unknown;
15
+ expandedBlocks?: unknown;
12
16
  };
13
- /** The value at a path, or undefined when there is none. A relation the
14
- read expanded is walked into: `author.name` reads the linked record —
15
- one linked record only; a list of them has no single value. */
16
- export declare function valueAt(source: Source, segments: readonly string[]): unknown;
17
+ /** Where a path ends: the value there; `linked` when it ends at a
18
+ relation the read expanded — the linked record, a list of them or
19
+ null; `blocks` when it ends at a components field whose blocks'
20
+ relations the read expanded — one entry per block, in the list's order. */
21
+ export type Walked = {
22
+ value: unknown;
23
+ linked?: unknown;
24
+ blocks?: unknown;
25
+ };
26
+ /** The path walked through a source. A relation the read expanded is
27
+ walked into: `author.name` reads the linked record — one linked
28
+ record only; a list of them has no single value. */
29
+ export declare function walk(source: Source, segments: readonly string[]): Walked;
30
+ /** The value at a path, or undefined when there is none. */
31
+ export declare const valueAt: (source: Source, segments: readonly string[]) => unknown;
17
32
  /** Nothing to show: no value, or an empty one. */
18
33
  export declare const isEmpty: (value: unknown) => boolean;
19
34
  /** A value as the text a person reads, or null when it isn't one —
package/dist/paths.js CHANGED
@@ -19,30 +19,37 @@ export function parsePath(text) {
19
19
  const own = (value, key) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, key)
20
20
  ? value[key]
21
21
  : undefined;
22
- /** The value at a path, or undefined when there is none. A relation the
23
- read expanded is walked into: `author.name` reads the linked record —
24
- one linked record only; a list of them has no single value. */
25
- export function valueAt(source, segments) {
22
+ /** The path walked through a source. A relation the read expanded is
23
+ walked into: `author.name` reads the linked record — one linked
24
+ record only; a list of them has no single value. */
25
+ export function walk(source, segments) {
26
26
  let current = source.data;
27
27
  let expanded = source.expanded;
28
+ let blocks = source.expandedBlocks;
28
29
  for (let i = 0; i < segments.length; i++) {
29
30
  const segment = segments[i];
30
- const linked = i < segments.length - 1 ? own(expanded, segment) : undefined;
31
+ const linked = own(expanded, segment);
32
+ if (i === segments.length - 1)
33
+ return { value: own(current, segment), linked, blocks: own(blocks, segment) };
31
34
  if (linked !== undefined) {
32
35
  if (linked === null || Array.isArray(linked))
33
- return undefined;
36
+ return { value: undefined };
34
37
  current = own(linked, "data");
35
38
  expanded = own(linked, "expanded");
39
+ blocks = own(linked, "expandedBlocks");
36
40
  continue;
37
41
  }
38
42
  current = own(current, segment);
39
43
  // below a field there are no records to walk into
40
44
  expanded = undefined;
45
+ blocks = undefined;
41
46
  if (current === undefined)
42
- return undefined;
47
+ return { value: undefined };
43
48
  }
44
- return current;
49
+ return { value: current };
45
50
  }
51
+ /** The value at a path, or undefined when there is none. */
52
+ export const valueAt = (source, segments) => walk(source, segments).value;
46
53
  /** Nothing to show: no value, or an empty one. */
47
54
  export const isEmpty = (value) => value === undefined || value === null || value === "" || (Array.isArray(value) && value.length === 0);
48
55
  /** A value as the text a person reads, or null when it isn't one —
package/dist/runtime.d.ts CHANGED
@@ -19,7 +19,10 @@
19
19
  record: data-mapled-sort,
20
20
  -limit, -filter, -expand
21
21
  data-mapled-each="features" the same for the items of a
22
- repeatable group
22
+ repeatable group, and for the
23
+ blocks of a components field:
24
+ <template data-mapled-type=
25
+ "hero"> per type of block
23
26
  data-mapled-record="posts" one record for what is inside:
24
27
  data-mapled-slug, or
25
28
  data-mapled-slug-param="slug"
@@ -32,8 +35,10 @@ import { type MapledClient } from "@mapled/client";
32
35
  collection in the release; `empty` — the record is there, a value at
33
36
  this path isn't; `failed` — the read didn't come back; `bad-path` —
34
37
  the page's attribute isn't a path, a key or an option Mapled reads;
35
- the rest are the sinks' refusals. */
36
- export declare const ISSUE_REASONS: readonly ["missing", "empty", "failed", "bad-path", "unsafe-url", "type", "refused-element", "unsupported"];
38
+ `no-template` — a block whose type the page has no template for (and
39
+ no template without a type to take it); the rest are the sinks'
40
+ refusals. */
41
+ export declare const ISSUE_REASONS: readonly ["missing", "empty", "failed", "bad-path", "unsafe-url", "type", "refused-element", "unsupported", "no-template"];
37
42
  export type IssueReason = (typeof ISSUE_REASONS)[number];
38
43
  /** A place the runtime couldn't fill: the page's own path, why, and the
39
44
  element as the page names it. Never a value. */
package/dist/runtime.js CHANGED
@@ -19,7 +19,10 @@
19
19
  record: data-mapled-sort,
20
20
  -limit, -filter, -expand
21
21
  data-mapled-each="features" the same for the items of a
22
- repeatable group
22
+ repeatable group, and for the
23
+ blocks of a components field:
24
+ <template data-mapled-type=
25
+ "hero"> per type of block
23
26
  data-mapled-record="posts" one record for what is inside:
24
27
  data-mapled-slug, or
25
28
  data-mapled-slug-param="slug"
@@ -28,20 +31,22 @@
28
31
  Paths start at a single from the document (`home.headline`) and at the
29
32
  record inside a list, an each or a record (`title`, `author.name`). */
30
33
  import { createClient, endPreview, MapledError, previewSession, submitForm } from "@mapled/client";
31
- import { isEmpty, parsePath, valueAt } from "./paths.js";
32
- import { ATTRIBUTES, FORMATS, formatted, setVisible, writable, writeAttribute, writeImage, writeLink, writeMarkdown, writeText } from "./sinks.js";
34
+ import { isEmpty, parsePath, walk } from "./paths.js";
35
+ import { ATTRIBUTES, attributeRefusal, bindRefusal, FORMATS, formatted, setVisible, writeAttribute, writeImage, writeLink, writeMarkdown, writeText } from "./sinks.js";
33
36
  import { fillTemplate } from "./url.js";
34
37
  /** Why a place wasn't filled. `missing` — no such single, record or
35
38
  collection in the release; `empty` — the record is there, a value at
36
39
  this path isn't; `failed` — the read didn't come back; `bad-path` —
37
40
  the page's attribute isn't a path, a key or an option Mapled reads;
38
- the rest are the sinks' refusals. */
39
- export const ISSUE_REASONS = ["missing", "empty", "failed", "bad-path", "unsafe-url", "type", "refused-element", "unsupported"];
41
+ `no-template` — a block whose type the page has no template for (and
42
+ no template without a type to take it); the rest are the sinks'
43
+ refusals. */
44
+ export const ISSUE_REASONS = ["missing", "empty", "failed", "bad-path", "unsafe-url", "type", "refused-element", "unsupported", "no-template"];
40
45
  const P = "data-mapled-";
41
46
  /** Every attribute the runtime reads or writes, besides `bind-…`. */
42
47
  export const NAMES = new Set([
43
48
  "bind", "format", "width", "show", "hide", "link", "list", "each", "record", "slug", "slug-param", "sort", "limit", "filter", "expand", "empty",
44
- "form", "form-state", "form-sent", "form-failed", "consent", "end-preview", "state", "preview", "key", "api", "live", "locale",
49
+ "form", "form-state", "form-sent", "form-failed", "consent", "end-preview", "state", "preview", "key", "api", "live", "locale", "type",
45
50
  ]);
46
51
  const KEY = /^[a-z0-9][a-z0-9_-]{0,63}$/;
47
52
  const DELIVERY_KEY = /^mk_[A-Za-z0-9_]{8,128}$/;
@@ -51,24 +56,40 @@ const MAX_EACH_DEPTH = 4;
51
56
  const MAX_ITEMS = 100;
52
57
  /** How deep `expand` reaches: `author`, `related.author`. */
53
58
  const MAX_EXPAND_DEPTH = 2;
59
+ const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
54
60
  /** A record as Mapled serves one — what every path walks: `data` an
55
61
  object; `expanded`, when there, one linked record, a list of them or
56
- null under each key, no deeper than `expand` can ask. An answer is
57
- data, and so is its shape: what isn't a record is read as no answer. */
62
+ null under each key; `expandedBlocks`, when there, a list under each
63
+ key — one object per block of a components field, as many as the
64
+ record's list holds (each block takes the one in its place), holding
65
+ the block's linked records the same way; none deeper than `expand`
66
+ can ask. An answer is data, and so is its shape: what isn't a record
67
+ is read as no answer. */
58
68
  function isRecord(value, depth = 0) {
59
- if (value === null || typeof value !== "object" || Array.isArray(value))
69
+ if (!isObject(value))
60
70
  return false;
61
- const { data, expanded } = value;
62
- if (data === null || typeof data !== "object" || Array.isArray(data))
71
+ const { data, expanded, expandedBlocks } = value;
72
+ if (!isObject(data))
63
73
  return false;
64
- if (expanded === undefined)
74
+ /** an object of linked records — empty past the depth `expand` reaches */
75
+ const linkedIn = (links) => isObject(links) &&
76
+ (depth >= MAX_EXPAND_DEPTH
77
+ ? Object.keys(links).length === 0
78
+ : Object.values(links).every((one) => one === null || (Array.isArray(one) ? one.every((r) => isRecord(r, depth + 1)) : isRecord(one, depth + 1))));
79
+ if (expanded !== undefined && !linkedIn(expanded))
80
+ return false;
81
+ if (expandedBlocks === undefined)
65
82
  return true;
66
- if (expanded === null || typeof expanded !== "object" || Array.isArray(expanded))
83
+ if (!isObject(expandedBlocks))
67
84
  return false;
68
- const linked = Object.values(expanded);
85
+ const lists = Object.entries(expandedBlocks);
69
86
  if (depth >= MAX_EXPAND_DEPTH)
70
- return linked.length === 0;
71
- return linked.every((one) => one === null || (Array.isArray(one) ? one.every((r) => isRecord(r, depth + 1)) : isRecord(one, depth + 1)));
87
+ return lists.length === 0;
88
+ return lists.every(([key, entries]) => {
89
+ // a key the record doesn't hold as a list — a field the read left out — has no blocks to pair with
90
+ const blocks = Object.prototype.hasOwnProperty.call(data, key) ? data[key] : undefined;
91
+ return Array.isArray(entries) && (!Array.isArray(blocks) || entries.length === blocks.length) && entries.every(linkedIn);
92
+ });
72
93
  }
73
94
  const isRecordList = (value) => Array.isArray(value) && value.every((r) => isRecord(r));
74
95
  /** The element as the page names it: `a#cta`. The id is the author's. */
@@ -80,7 +101,33 @@ const listOf = (text) => (text ?? "")
80
101
  .split(",")
81
102
  .map((t) => t.trim())
82
103
  .filter((t) => /^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t));
83
- const templateOf = (el) => Array.from(el.children).find((c) => c.localName === "template") ?? null;
104
+ function templatesOf(el) {
105
+ const out = { plain: null, typed: new Map(), all: [], bad: [] };
106
+ for (const child of Array.from(el.children)) {
107
+ if (child.localName !== "template")
108
+ continue;
109
+ const template = child;
110
+ out.all.push(template);
111
+ const type = template.getAttribute(`${P}type`);
112
+ if (type === null)
113
+ out.plain ??= template;
114
+ else if (!KEY.test(type.trim()))
115
+ out.bad.push(template);
116
+ else if (!out.typed.has(type.trim()))
117
+ out.typed.set(type.trim(), template);
118
+ }
119
+ return out;
120
+ }
121
+ /** The type a block names — or a JSON field's object, the same way —
122
+ read as its own property and held to the grammar of Mapled's keys;
123
+ null for a group's item or a record, whose keys never start with `_`. */
124
+ function typeOf(source) {
125
+ const data = source.data;
126
+ if (data === null || typeof data !== "object" || Array.isArray(data) || !Object.prototype.hasOwnProperty.call(data, "_type"))
127
+ return null;
128
+ const type = data._type;
129
+ return typeof type === "string" && KEY.test(type) ? type : null;
130
+ }
84
131
  function declaredOn(el, issues) {
85
132
  let d = null;
86
133
  const get = () => (d ??= { format: "text", attrs: [], expand: [] });
@@ -241,14 +288,14 @@ function look(scope, path) {
241
288
  if (!segments)
242
289
  return { status: "bad-path" };
243
290
  if (scope.kind === "record")
244
- return { status: "value", value: valueAt(scope.source, segments) };
291
+ return { status: "value", ...walk(scope.source, segments) };
245
292
  // from the document a path names a single, then a field of it
246
293
  if (segments.length < 2)
247
294
  return { status: "bad-path" };
248
295
  const single = scope.singles.get(segments[0]);
249
296
  if (!single || single.status !== "ok")
250
297
  return { status: single?.status === "failed" ? "failed" : "missing" };
251
- return { status: "value", value: valueAt(single.value, segments.slice(1)) };
298
+ return { status: "value", ...walk(single.value, segments.slice(1)) };
252
299
  }
253
300
  /* ---- mount ---- */
254
301
  export function mount(options) {
@@ -304,10 +351,12 @@ export function mount(options) {
304
351
  function fillElement(el, d, scope) {
305
352
  if (d.bind !== undefined) {
306
353
  const found = look(scope, d.bind);
354
+ // a place the sink refuses stays as written, an empty value or not
355
+ const refused = bindRefusal(el, d.format);
307
356
  if (found.status !== "value")
308
357
  note(el, d.bind, found.status);
309
- else if (!writable(el))
310
- note(el, d.bind, "refused-element");
358
+ else if (refused)
359
+ note(el, d.bind, refused);
311
360
  else {
312
361
  const image = el.localName === "img";
313
362
  if (image) {
@@ -350,6 +399,11 @@ export function mount(options) {
350
399
  note(el, path, found.status);
351
400
  continue;
352
401
  }
402
+ const refused = attributeRefusal(el, name);
403
+ if (refused) {
404
+ note(el, path, refused);
405
+ continue;
406
+ }
353
407
  keepAttr(el, name);
354
408
  if (isEmpty(found.value)) {
355
409
  // an image without a description is a decorative one; any other attribute goes
@@ -406,15 +460,19 @@ export function mount(options) {
406
460
  }
407
461
  }
408
462
  }
409
- /** The element's template, once per item; what else it held is its fallback. */
410
- function repeat(el, sources, depth) {
411
- const template = templateOf(el);
412
- if (!template) {
463
+ /** The element's template, once per item — an each's block takes the
464
+ one of its type; what else it held is its fallback. `path` is the
465
+ page's own, for the report. */
466
+ function repeat(el, sources, depth, path, blocks) {
467
+ const templates = templatesOf(el);
468
+ for (const bad of templates.bad)
469
+ note(bad, bad.getAttribute(`${P}type`) ?? "", "bad-path");
470
+ if (!templates.plain && templates.typed.size === 0) {
413
471
  note(el, "template", "unsupported");
414
472
  return;
415
473
  }
416
474
  keepContent(el);
417
- const kept = [template];
475
+ const kept = [...templates.all];
418
476
  for (const child of Array.from(el.children)) {
419
477
  if (!child.hasAttribute(`${P}empty`))
420
478
  continue;
@@ -423,11 +481,23 @@ export function mount(options) {
423
481
  kept.push(child);
424
482
  }
425
483
  const items = [];
484
+ let untemplated = false;
426
485
  for (const source of sources.slice(0, MAX_ITEMS)) {
486
+ // a list's record has no type: only an each's item picks among the typed templates —
487
+ // a block, or a JSON field's object that names its _type (the page doesn't see the schema)
488
+ const type = blocks ? typeOf(source) : null;
489
+ const template = (type === null ? undefined : templates.typed.get(type)) ?? templates.plain;
490
+ // a block of a type the page doesn't show: content picks among the author's templates, never adds one
491
+ if (!template) {
492
+ untemplated = true;
493
+ continue;
494
+ }
427
495
  const clone = template.content.cloneNode(true);
428
496
  fill(compile(clone, false), { kind: "record", source }, depth + 1);
429
497
  items.push(...Array.from(clone.childNodes));
430
498
  }
499
+ if (untemplated)
500
+ note(el, path, "no-template");
431
501
  el.replaceChildren(...kept, ...items);
432
502
  bound++;
433
503
  }
@@ -445,16 +515,26 @@ export function mount(options) {
445
515
  note(each.el, each.path, "unsupported");
446
516
  continue;
447
517
  }
518
+ // a relation the read expanded: its linked records, one for a relation to one — records have no type
519
+ if (found.linked !== undefined) {
520
+ const records = (Array.isArray(found.linked) ? found.linked : found.linked === null ? [] : [found.linked]);
521
+ if (records.length === 0)
522
+ note(each.el, each.path, "empty");
523
+ repeat(each.el, records, depth, each.path, false);
524
+ continue;
525
+ }
448
526
  // a repeatable group is a list of items; one that isn't repeatable is a single item
449
527
  const value = found.value;
450
528
  const items = Array.isArray(value) ? value : isEmpty(value) ? [] : [value];
451
- if (items.some((item) => item === null || typeof item !== "object" || Array.isArray(item))) {
529
+ if (items.some((item) => !isObject(item))) {
452
530
  note(each.el, each.path, "type");
453
531
  continue;
454
532
  }
455
533
  if (items.length === 0)
456
534
  note(each.el, each.path, "empty");
457
- repeat(each.el, items.map((item) => ({ data: item })), depth);
535
+ // the blocks' linked records, when the read expanded their relations: one entry a block, in the list's order
536
+ const links = Array.isArray(value) && Array.isArray(found.blocks) ? found.blocks : [];
537
+ repeat(each.el, items.map((item, i) => (isObject(links[i]) ? { data: item, expanded: links[i] } : { data: item })), depth, each.path, true);
458
538
  }
459
539
  }
460
540
  async function read(load, wellFormed) {
@@ -589,7 +669,8 @@ export function mount(options) {
589
669
  reads.push(read(() => client.getSingle(key, { ...pin, ...(expand.size > 0 ? { expand: [...expand] } : {}) }), isRecord).then((r) => singles.set(key, r)));
590
670
  }
591
671
  for (const list of page.lists) {
592
- const template = templateOf(list.el);
672
+ // a record has no type: it takes the template without one, and the read asks for what that one reads
673
+ const template = templatesOf(list.el).plain;
593
674
  const fields = template ? [...heads(compile(template.content, false)).keys()] : [];
594
675
  reads.push(read(async () => (await client.getRecords(list.collection, { ...list.options, ...pin, ...(fields.length > 0 ? { fields } : {}) })).records, isRecordList).then((r) => lists.set(list, r)));
595
676
  }
@@ -625,7 +706,7 @@ export function mount(options) {
625
706
  for (const list of page.lists) {
626
707
  const answer = lists.get(list);
627
708
  if (answer?.status === "ok")
628
- repeat(list.el, answer.value, 0);
709
+ repeat(list.el, answer.value, 0, list.collection, false);
629
710
  else
630
711
  note(list.el, list.collection, answer?.status ?? "failed");
631
712
  }
package/dist/sinks.d.ts CHANGED
@@ -27,6 +27,15 @@ export declare function writeText(el: Element, value: unknown): Refusal | null;
27
27
  export declare function writeMarkdown(el: Element, value: unknown, ctx: SinkContext): Refusal | null;
28
28
  /** `<img data-mapled-bind>`: the picture. */
29
29
  export declare function writeImage(el: Element, value: unknown, ctx: SinkContext, width?: number): Refusal | null;
30
+ /** Why `data-mapled-bind` never writes into this element, whatever the
31
+ value — or null: a refused element (rule 2), and a picture or rich
32
+ text outside HTML (writeImage, writeMarkdown). The runtime asks before
33
+ an empty value empties the place, so a place it refuses stays as the
34
+ author wrote it. */
35
+ export declare function bindRefusal(el: Element, format: Format): Refusal | null;
36
+ /** Why the attribute never goes onto this element, whatever the value —
37
+ or null (rule 3). The runtime asks before an empty value removes it. */
38
+ export declare function attributeRefusal(el: Element, name: string): Refusal | null;
30
39
  /** One attribute of the table. `name` is the page's spelling: it is
31
40
  looked up, never written as it came. */
32
41
  export declare function writeAttribute(el: Element, name: string, value: unknown, ctx: SinkContext, width?: number): Refusal | null;
package/dist/sinks.js CHANGED
@@ -70,9 +70,21 @@ export function writeImage(el, value, ctx, width) {
70
70
  el.setAttribute("src", url);
71
71
  return null;
72
72
  }
73
- /** One attribute of the table. `name` is the page's spelling: it is
74
- looked up, never written as it came. */
75
- export function writeAttribute(el, name, value, ctx, width) {
73
+ /** Why `data-mapled-bind` never writes into this element, whatever the
74
+ value — or null: a refused element (rule 2), and a picture or rich
75
+ text outside HTML (writeImage, writeMarkdown). The runtime asks before
76
+ an empty value empties the place, so a place it refuses stays as the
77
+ author wrote it. */
78
+ export function bindRefusal(el, format) {
79
+ if (!writable(el))
80
+ return "refused-element";
81
+ if ((el.localName === "img" || format === "markdown") && !isHtml(el))
82
+ return "refused-element";
83
+ return null;
84
+ }
85
+ /** Why the attribute never goes onto this element, whatever the value —
86
+ or null (rule 3). The runtime asks before an empty value removes it. */
87
+ export function attributeRefusal(el, name) {
76
88
  const key = name.toLowerCase();
77
89
  const rule = Object.prototype.hasOwnProperty.call(ATTRIBUTES, key) ? ATTRIBUTES[key] : undefined;
78
90
  if (!rule)
@@ -81,6 +93,16 @@ export function writeAttribute(el, name, value, ctx, width) {
81
93
  return "refused-element";
82
94
  if (rule.on !== null && !rule.on.includes(el.localName))
83
95
  return "unsupported";
96
+ return null;
97
+ }
98
+ /** One attribute of the table. `name` is the page's spelling: it is
99
+ looked up, never written as it came. */
100
+ export function writeAttribute(el, name, value, ctx, width) {
101
+ const refusal = attributeRefusal(el, name);
102
+ if (refusal)
103
+ return refusal;
104
+ const key = name.toLowerCase();
105
+ const rule = ATTRIBUTES[key];
84
106
  if (rule.holds === "text") {
85
107
  const text = asText(value);
86
108
  if (text === null)
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** The package's version — what the banner of the single file and
2
2
  `Mapled.version` say. Kept equal to package.json by version.test.ts. */
3
- export declare const VERSION = "0.2.3";
3
+ export declare const VERSION = "0.3.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** The package's version — what the banner of the single file and
2
2
  `Mapled.version` say. Kept equal to package.json by version.test.ts. */
3
- export const VERSION = "0.2.3";
3
+ export const VERSION = "0.3.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapled/vanilla",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "The browser runtime of Mapled, the hosted headless CMS for sites built with AI — published content in a plain HTML site: one script tag and data-mapled attributes, no framework and no build step.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://mapled.io",
@@ -41,7 +41,7 @@
41
41
  "typecheck": "tsc --noEmit -p tsconfig.json"
42
42
  },
43
43
  "dependencies": {
44
- "@mapled/client": "^0.4.0"
44
+ "@mapled/client": "^0.5.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "esbuild": "^0.28.2",