@mapled/vanilla 0.0.1 → 0.1.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
@@ -1,10 +1,197 @@
1
1
  # @mapled/vanilla
2
2
 
3
- The browser runtime of [Mapled](https://mapled.io), the hosted headless CMS for sites built with AI: published content in a plain HTML site, with no framework and no build step.
3
+ [Mapled](https://mapled.io) — the hosted headless CMS for sites built with AI — in a plain HTML site: one script tag, `data-mapled-*` attributes on the elements it should fill, no framework and no build step. The HTML you already have stays in the page as the fallback; editors change the words, pictures and lists in Mapled and press Publish.
4
4
 
5
- **This version holds no code yet.** 0.0.1 exists so that the releases that follow can be published from CI; the first release is 0.1.0.
5
+ - A **React single-page app** uses [`@mapled/react`](https://www.npmjs.com/package/@mapled/react), a **Next.js** site [`@mapled/next`](https://www.npmjs.com/package/@mapled/next), any other JavaScript [`@mapled/client`](https://www.npmjs.com/package/@mapled/client) — this package is for pages that have no JavaScript of their own to speak of.
6
+ - What search engines and link previews read is the page's own HTML, not what a script fills in later. Keep the fallback meaningful; a site that lives on search traffic is better served by a framework that renders on a server.
6
7
 
7
- Until then:
8
+ ## Add it to a page
8
9
 
9
- - a Next.js site uses [`@mapled/next`](https://www.npmjs.com/package/@mapled/next);
10
- - any other site reads the Delivery API over HTTP — «Without Next.js: the HTTP API» in the [`@mapled/next` README](https://www.npmjs.com/package/@mapled/next).
10
+ ```html
11
+ <!-- index.html -->
12
+ <!doctype html>
13
+ <html lang="en">
14
+ <head>
15
+ <title data-mapled-bind="home.headline">Studio Nord</title>
16
+ <script src="https://cdn.jsdelivr.net/npm/@mapled/vanilla@0.1.0/dist/mapled.js" data-mapled-key="mk_live_…" defer></script>
17
+ </head>
18
+ <body>
19
+ <h1 data-mapled-bind="home.headline">Websites, built by hand</h1>
20
+ </body>
21
+ </html>
22
+ ```
23
+
24
+ `home.headline` is the field `headline` of the single `home`. When the page loads, the script reads the release that is live and puts the headline into both elements; until then — and whenever Mapled can't be reached — the page shows what you wrote.
25
+
26
+ The key is the project's **delivery key** (Mapled: Integrations → Your site). It reads published content of collections with access Public and nothing else, so it is the one key that belongs in a page. The script refuses to start with anything else: a server key or a signing secret never goes into HTML.
27
+
28
+ **Pin the version.** A version on a CDN never changes; an address without one (`…/@mapled/vanilla/dist/mapled.js`, `@latest`) runs whatever is published next. Better still, add `integrity="sha384-…" crossorigin="anonymous"` — the value ships in the package as `dist/mapled.js.sha384` — or copy `dist/mapled.js` next to your pages and load it from there. The file is about 12 KB over the wire and has no dependencies.
29
+
30
+ ## What a page can declare
31
+
32
+ ```html
33
+ <!-- landing.html -->
34
+ <header>
35
+ <h1 data-mapled-bind="home.headline">Websites, built by hand</h1>
36
+
37
+ <!-- rich text: Markdown from Mapled, built into elements -->
38
+ <div data-mapled-bind="home.intro" data-mapled-format="markdown"><p>We make small sites.</p></div>
39
+
40
+ <!-- a picture (an asset of the project) at the width the layout needs, and its description -->
41
+ <img data-mapled-bind="home.hero" data-mapled-width="1280" data-mapled-bind-alt="home.hero-alt" src="/img/hero.jpg" alt="The studio">
42
+
43
+ <!-- an attribute: href, src, alt, title, aria-label, placeholder, datetime -->
44
+ <a data-mapled-bind="home.cta-label" data-mapled-bind-href="home.cta-link" href="/contact.html">Get in touch</a>
45
+
46
+ <!-- dates and numbers in the words of the page's language -->
47
+ <p>Since <time data-mapled-bind="home.founded" data-mapled-format="date" data-mapled-bind-datetime="home.founded">2019</time></p>
48
+
49
+ <!-- shown only while the field has a value (data-mapled-hide is the opposite) -->
50
+ <p data-mapled-show="home.notice" data-mapled-bind="home.notice" hidden></p>
51
+ </header>
52
+
53
+ <!-- a repeatable group of the single: the template once per item -->
54
+ <ul data-mapled-each="home.features">
55
+ <template>
56
+ <li><strong data-mapled-bind="title"></strong> <span data-mapled-bind="text"></span></li>
57
+ </template>
58
+ <li><strong>Designed</strong> by people</li>
59
+ </ul>
60
+
61
+ <!-- a collection: the template once per record -->
62
+ <ul data-mapled-list="posts" data-mapled-sort="-date" data-mapled-limit="5" data-mapled-filter="featured=true">
63
+ <template>
64
+ <li>
65
+ <a data-mapled-link="/post.html?slug={slug}" data-mapled-bind="title"></a>
66
+ <time data-mapled-bind="date" data-mapled-format="date"></time>
67
+ </li>
68
+ </template>
69
+ <li>Journal entries appear here.</li>
70
+ <li data-mapled-empty hidden>Nothing published yet.</li>
71
+ </ul>
72
+ ```
73
+
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.
75
+ - **`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
+ - **`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
+ - **`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
+ - **`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.
79
+
80
+ ### One record per page
81
+
82
+ ```html
83
+ <!-- post.html -->
84
+ <article data-mapled-record="posts" data-mapled-slug-param="slug">
85
+ <h1 data-mapled-bind="title">Journal</h1>
86
+ <div data-mapled-bind="body" data-mapled-format="markdown"></div>
87
+ </article>
88
+ ```
89
+
90
+ `post.html?slug=hello-world` shows the post whose slug field is `hello-world`. `data-mapled-slug="about-us"` names the record in the page instead. Tell Mapled the page's address as `/post.html?slug=[slug]` in the site manifest (with a `route_param` binding on the slug field), and **Preview** in the record's editor opens the right page.
91
+
92
+ ### Forms
93
+
94
+ ```html
95
+ <!-- contact.html -->
96
+ <form data-mapled-form="contact">
97
+ <input name="name" required>
98
+ <input name="email" type="email" required>
99
+ <input name="_gotcha" hidden tabindex="-1" autocomplete="off">
100
+ <label><input type="checkbox" name="consent" data-mapled-consent required> I agree to be contacted about my request.</label>
101
+ <button>Send</button>
102
+ <p data-mapled-form-sent hidden>Thanks — we'll write back.</p>
103
+ <p data-mapled-form-failed hidden></p>
104
+ </form>
105
+ ```
106
+
107
+ `contact` is the form's key in Mapled (Forms → New form, `npx @mapled/cli forms create`, or your AI agent's `create_form`). The script posts the fields, shows one of the two messages — an empty `data-mapled-form-failed` gets Mapled's own words — and sets `data-mapled-form-state` on the form to `sending`, `sent` or `failed`. Keep the hidden `_gotcha` input: people leave it empty, and what bots put there is filed as spam. The label of a ticked `data-mapled-consent` checkbox is kept with the submission. Submissions never wait for Publish.
108
+
109
+ ## What it does when a value isn't there
110
+
111
+ | The report says | What happened | What the page shows |
112
+ |---|---|---|
113
+ | `missing` | No such single, record or collection in the release — or nothing in the address to find a record by | The HTML you wrote |
114
+ | `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
+ | `failed` | The read didn't come back | The HTML you wrote |
116
+ | `unsafe-url` | An address that isn't `http:`, `https:` — or `mailto:`, `tel:` in a link — or that carries credentials | 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 |
118
+ | `refused-element` | `<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<base>`, `<link>`, `<meta>`, `<template>`, `<noscript>` are never written into | The HTML you wrote |
119
+ | `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
+ | `bad-path` | A path, a key or an option that isn't one | The HTML you wrote |
121
+
122
+ ```html
123
+ <!-- debug.html -->
124
+ <script>
125
+ document.addEventListener("mapled:ready", (event) => {
126
+ const { state, release, preview, bound, issues } = event.detail;
127
+ console.table(issues); // path, reason, on — never a value
128
+ });
129
+ </script>
130
+ ```
131
+
132
+ `state` is `ready`, `unpublished` (nothing is published yet) or `fallback` (Mapled couldn't be reached); the same word is on `<html data-mapled-state="…">`, `loading` before the first fill — a hook for CSS, set by the script only, so a page whose script never loaded hides nothing. `mapled:update` follows every later fill. In the console, `Mapled.page.report` is the last report and `Mapled.page.refresh()` fills again — call it after your own script adds elements with attributes. Every fill starts from the HTML you wrote, so a value that stops fitting gives its place back to your fallback, not to the previous release.
133
+
134
+ ## Preview
135
+
136
+ Nothing to set up in the page. **Preview** in Mapled opens the page with a one-time token — `https://your-site/post.html?slug=hello&mapled-preview=mpv_…` — and the script, before its first read, takes the token out of the address bar, trades it for a draft session and keeps the session for the tab. The page shows drafts until the tab closes or the session ends, and `<html>` carries `data-mapled-preview` meanwhile:
137
+
138
+ ```html
139
+ <!-- preview-bar.html -->
140
+ <p class="preview-bar">You are looking at a preview. <button data-mapled-end-preview>Show what is published</button></p>
141
+ <style>
142
+ html:not([data-mapled-preview]) .preview-bar { display: none; }
143
+ </style>
144
+ ```
145
+
146
+ Mapled has to know where the site lives and that it renders in the browser: the site manifest's `framework` is `"plain-html"` (your AI agent's `push_site_manifest`, or `npx @mapled/cli bindings push`) — or the project was created as plain HTML — and **Production domain** in Project settings holds the site's public address (an agent sets it with `set_site_url`). `npx @mapled/cli doctor` checks all of it, the script tag's version included.
147
+
148
+ ## Live updates
149
+
150
+ A publish shows when the page is loaded again. To follow publishes while a page is open, add `data-mapled-live` to the script tag — `data-mapled-live="30"` for the seconds between checks (60 by default, 15 at least). A check is a request of the delivery key's budget (1,200 a minute), made only while the tab is visible; a quiet one costs next to nothing.
151
+
152
+ ## What content can and can't do to the page
153
+
154
+ An editor changes what the site says, never what it runs:
155
+
156
+ - text goes in as text — markup typed into a field is shown, not parsed;
157
+ - rich text is Markdown built into a fixed set of elements (paragraphs, headings, lists, quotes, code, emphasis, links, images); raw HTML in it is shown as text;
158
+ - addresses pass one policy — `http:` and `https:`, plus `mailto:` and `tel:` in links — read by the browser's own URL parser, so `java script:` in any disguise is refused;
159
+ - only the listed attributes are ever set, and never on `<script>`, `<style>`, `<iframe>` and the like;
160
+ - the script uses no `eval` and no HTML-parsing call, and works under a strict Content-Security-Policy and Trusted Types. A site with a CSP adds `connect-src https://api.mapled.io` and `img-src https://api.mapled.io https://media.mapled.io`.
161
+
162
+ Don't undo this with your own `innerHTML = record.data.…` — if you need content in JavaScript, read it with [`@mapled/client`](https://www.npmjs.com/package/@mapled/client) and put it in as text.
163
+
164
+ ## With a bundler
165
+
166
+ ```ts
167
+ // src/main.ts
168
+ import { mount } from "@mapled/vanilla";
169
+
170
+ const page = mount({ key: "mk_live_…", live: true });
171
+ const report = await page.ready;
172
+ if (report.state === "fallback") console.info("Showing the page as written.");
173
+ ```
174
+
175
+ `mount({ key, apiUrl?, root?, live?, fetch? })` answers `{ ready, report, refresh(), endPreview(), stop(), client }` — `client` is the [`@mapled/client`](https://www.npmjs.com/package/@mapled/client) the page reads through, for reads of your own. The script tag does the same from its attributes (`data-mapled-key`, `data-mapled-api`, `data-mapled-live`) and leaves the result at `Mapled.page`; a tag without a key only defines `Mapled.mount`. `VERSION` is the package's version, `Mapled.version` in a page.
176
+
177
+ ## Every attribute
178
+
179
+ | Attribute | On | Says |
180
+ |---|---|---|
181
+ | `data-mapled-key`, `data-mapled-api`, `data-mapled-live` | the script tag | the delivery key; another API origin; follow publishes |
182
+ | `data-mapled-bind` | an element | the path whose value fills it |
183
+ | `data-mapled-format` | with `data-mapled-bind` | `text` (default), `markdown`, `date`, `datetime`, `number` |
184
+ | `data-mapled-width` | an `<img>` | the width of the picture to ask for, 16–4096 |
185
+ | `data-mapled-bind-href`, `-src`, `-alt`, `-title`, `-aria-label`, `-placeholder`, `-datetime` | an element | the path whose value fills the attribute |
186
+ | `data-mapled-show`, `data-mapled-hide` | an element | shown (hidden) while the path has a value; `false` counts as none |
187
+ | `data-mapled-link` | `a`, `area` | an address template with `{field}` places |
188
+ | `data-mapled-list` | an element with a `<template>` | the collection to repeat the template for |
189
+ | `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 |
191
+ | `data-mapled-empty` | inside a list or an each | shown when there is nothing to repeat |
192
+ | `data-mapled-record` | an element | the collection of the one record its content shows |
193
+ | `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 |
195
+ | `data-mapled-form` | a `<form>` | the key of the Mapled form it posts to |
196
+ | `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
+ | `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` |
@@ -0,0 +1,5 @@
1
+ /** Mapled in a plain HTML page: `data-mapled-*` attributes filled from
2
+ the release that is live. A bundler's entry — a page without one
3
+ loads the single file, `dist/mapled.js`, with a script tag. */
4
+ export { mount, type Issue, type IssueReason, type MapledPage, type MountOptions, type Report } from "./runtime.js";
5
+ export { VERSION } from "./version.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /** Mapled in a plain HTML page: `data-mapled-*` attributes filled from
2
+ the release that is live. A bundler's entry — a page without one
3
+ loads the single file, `dist/mapled.js`, with a script tag. */
4
+ export { mount } from "./runtime.js";
5
+ export { VERSION } from "./version.js";
package/dist/mapled.js ADDED
@@ -0,0 +1,18 @@
1
+ /*! @mapled/vanilla 0.1.0 | MIT | https://mapled.io */
2
+ "use strict";(()=>{var pt=Object.defineProperty;var mt=(e,t,n)=>t in e?pt(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var ue=(e,t,n)=>mt(e,typeof t!="symbol"?t+"":t,n);var ce="mapled-preview",Q="mapled:preview",gt=/^mpv_[A-Za-z0-9]{16,96}$/,Le=/^mps_[A-Za-z0-9]{16,96}$/,Me=new WeakMap,ht=4e3;function Ue(){if(typeof window>"u"||typeof document>"u")return null;try{let e=window.sessionStorage,t=Me.get(window);return t||Me.set(window,t={memory:null,exchanging:null}),{location:window.location,history:window.history,sessionStorage:e,page:t}}catch{return null}}function yt(e){try{let t=e.getItem(Q);if(!t)return null;let n=JSON.parse(t),r=typeof n?.expiresAt=="string"?Date.parse(n.expiresAt):NaN;return typeof n?.draftToken!="string"||!Le.test(n.draftToken)||!(r>Date.now())?(e.removeItem(Q),null):{draftToken:n.draftToken,expiresAt:n.expiresAt}}catch{return null}}function F(e){let t=yt(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 ee(){let e=Ue();if(e){e.page.memory=null;try{e.sessionStorage.removeItem(Q)}catch{}}}function Ne(e={}){let t=Ue();if(!t)return Promise.resolve(null);let n;try{n=new URL(t.location.href)}catch{return Promise.resolve(F(t))}let r=n.searchParams.get(ce);if(r===null)return t.page.exchanging?t.page.exchanging.done:Promise.resolve(F(t));n.searchParams.delete(ce);try{t.history.replaceState(t.history.state,"",`${n.pathname}${n.search}${n.hash}`)}catch{}if(!gt.test(r))return Promise.resolve(F(t));if(t.page.exchanging?.token===r)return t.page.exchanging.done;let s=(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),l=(e.appUrl??"https://app.mapled.io").replace(/\/+$/,""),u=e.fetch??fetch,i=(async()=>{try{let o=await u(`${s}/v1/delivery/preview/exchange`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:r}),cache:"no-store"});if(o.ok){let a=await o.json();if(typeof a.draftToken!="string"||typeof a.expiresAt!="string"||!Le.test(a.draftToken))return F(t);let d={draftToken:a.draftToken,expiresAt:a.expiresAt};t.page.memory=d;try{t.sessionStorage.setItem(Q,JSON.stringify(d))}catch{}return d}if(o.status===401){let a=await o.json().catch(()=>null),d=a?.error?.projectId;if(a?.error?.code==="PREVIEW_EXPIRED"&&typeof d=="string"&&/^[0-9a-f-]{8,64}$/i.test(d))return t.location.assign(`${l}/preview/expired?project=${encodeURIComponent(d)}`),await new Promise(m=>setTimeout(m,ht)),F(t)}return F(t)}catch{return F(t)}})(),f={token:r,done:i};return t.page.exchanging=f,i.then(()=>{t.page.exchanging===f&&(t.page.exchanging=null)}),i}async function Ie(e){return(await Ne(e))?.draftToken??null}function bt(e){if(!e||typeof e!="object")return;let{release:t,publishedAt:n}=e;if(t===null)return null;if(!(!Number.isInteger(t)||t<1||typeof n!="string"))return{release:t,publishedAt:n}}function Fe(e){let t=typeof e.key=="string"&&e.key.length>0;if(!t&&!e.endpoint)throw new Error("Mapled: watchRelease needs an endpoint or a delivery key.");let n=t?`${(e.apiUrl??"https://api.mapled.io").replace(/\/+$/,"")}/v1/delivery/release`:e.endpoint,r=t?{accept:"application/json","x-mapled-key":e.key}:{accept:"application/json"},s=(t?Math.max(15,e.interval??60):Math.max(2,e.interval??10))*1e3,l=e.fetch??fetch,u=typeof document>"u"?null:document,i,f=0,o=!1,a,d=null,m=()=>!u||u.visibilityState!=="hidden",w=()=>{o||!m()||(a=setTimeout(v,Math.min(s*2**f,3e5)))};async function v(){if(o||d)return;let O=new AbortController;d=O;let T=null;try{let M=await l(n,{cache:"no-cache",headers:r,signal:O.signal}),$=M.ok?bt(await M.json()):void 0;if(o)return;$===void 0?f+=1:(f=0,i===void 0?i=$?$.release:null:$&&$.release>(i??0)&&(T={next:$,previous:i},i=$.release))}catch{if(o)return;f+=1}finally{d===O&&(d=null)}w(),T&&e.onPublish(T.next,T.previous)}let x=()=>{clearTimeout(a),m()&&v()};return u?.addEventListener("visibilitychange",x),m()&&v(),()=>{o=!0,clearTimeout(a),d?.abort(),u?.removeEventListener("visibilitychange",x)}}function De(e){let t={};return e.forEach((n,r)=>{typeof n!="string"||r in t||(t[r]=n)}),t}async function de(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?De(new FormData(t)):typeof FormData<"u"&&t instanceof FormData?De(t):{...t},l=n.page??(typeof window<"u"&&typeof document<"u"?window.location.pathname:void 0),u={...s};n.consent&&(u._consent=n.consent),l&&u._page===void 0&&(u._page=l),n.test&&(u._test=!0);let i;try{i=await(n.fetch??fetch)(`${r}/v1/delivery/forms/${encodeURIComponent(e)}`,{method:"POST",headers:{"content-type":"application/json","x-mapled-key":n.key},body:JSON.stringify(u)})}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 f=`The submission wasn't accepted (${i.status}).`,o;try{let a=await i.json();a.error?.message&&(f=a.error.message),typeof a.error?.code=="string"&&(o=a.error.code)}catch{}return{ok:!1,status:i.status,code:o,message:f}}var fe=/^[a-z0-9][a-z0-9-]{0,63}$/,Ke=new Set;function wt(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"||!fe.test(n)))for(let[s,l]of Object.entries(r)){if(typeof l!="string"||!fe.test(s)||!fe.test(l))continue;let u=`${n}.${s}`;Ke.has(u)||(Ke.add(u),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 Be(e,t={}){let n=(t.apiUrl??"https://api.mapled.io").replace(/\/+$/,""),r=new URLSearchParams;t.width&&r.set("w",String(t.width)),t.height&&r.set("h",String(t.height)),t.fit&&r.set("fit",t.fit),t.format&&r.set("fmt",t.format),t.quality&&r.set("q",String(t.quality));let s=r.toString();return`${n}/files/${encodeURIComponent(e)}${s?`?${s}`:""}`}var D=class extends Error{constructor(n,r,s){super(r);ue(this,"status");ue(this,"code");this.name="MapledError",this.status=n,this.code=s}},_e=e=>Number.isInteger(e)&&e>=1,je=()=>new Error("Mapled: release must be a positive whole number.");function qe(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&&!_e(e.release))throw je();let n=(o,a)=>(e.fetch??fetch)(o,a);async function r(){return e.draft===null?null:typeof e.draft=="string"?e.draft||null:typeof e.draft=="function"?await e.draft()||null:Ie({apiUrl:t,appUrl:e.appUrl,fetch:e.fetch})}async function s(o){let a=`Mapled request failed (${o.status}).`,d;try{let m=await o.json();m.error?.message&&(a=m.error.message),typeof m.error?.code=="string"&&(d=m.error.code)}catch{}return new D(o.status,a,d)}async function l(o,a,d){let m=await r(),w=d.release??e.release;if(w!==void 0&&!_e(w))throw je();w!==void 0&&!m&&a.set("release",String(w));let v=a.toString(),x=await n(`${t}${o}${v?`?${v}`:""}`,{headers:m?{"x-mapled-key":e.key,"x-mapled-draft":m}:{"x-mapled-key":e.key},...m?{cache:"no-store"}:{},...d.signal?{signal:d.signal}:{}});if(!x.ok)throw await s(x);let O=await x.json();return wt(O),O}let u=(o,a)=>(a.expand?.length&&o.set("expand",a.expand.join(",")),a.fields?.length&&o.set("fields",a.fields.join(",")),o),i=async(o,a)=>{try{return(await l(o,u(new URLSearchParams,a),a)).record}catch(d){if(d instanceof D&&d.status===404&&d.code!=="RELEASE_NOT_FOUND")return null;throw d}};return{async getRecords(o,a={}){let d=new URLSearchParams;for(let[m,w]of Object.entries(a.filter??{}))if(w!==null&&typeof w=="object")for(let[v,x]of Object.entries(w))x!==void 0&&d.set(`filter[${m}][${v}]`,Array.isArray(x)?x.join(","):String(x));else d.set(`filter[${m}]`,String(w));return a.sort&&d.set("sort",a.sort),a.limit!==void 0&&d.set("limit",String(a.limit)),a.offset!==void 0&&d.set("offset",String(a.offset)),u(d,a),l(`/v1/delivery/collections/${encodeURIComponent(o)}/records`,d,a)},getRecord(o,a,d={}){return i(`/v1/delivery/collections/${encodeURIComponent(o)}/records/${encodeURIComponent(a)}`,d)},getRecordBySlug(o,a,d={}){return i(`/v1/delivery/collections/${encodeURIComponent(o)}/records/by-slug/${encodeURIComponent(a)}`,d)},getSingle(o,a={}){return i(`/v1/delivery/singles/${encodeURIComponent(o)}`,a)},async isPreview(){return await r()!==null},watch(o,a={}){return Fe({key:e.key,apiUrl:t,interval:a.interval,onPublish:o,fetch:e.fetch})},async getRelease(o={}){let a=await n(`${t}/v1/delivery/release`,{headers:{"x-mapled-key":e.key},cache:"no-cache",...o.signal?{signal:o.signal}:{}});if(!a.ok)throw await s(a);let d=await a.json();return d.release===null||d.publishedAt===null?null:{release:d.release,publishedAt:d.publishedAt}}}}var kt=/^[a-z0-9][a-z0-9_-]{0,63}$/;function j(e){if(typeof e!="string")return null;let t=e.trim().split(".");if(t.length>8)return null;for(let n of t)if(!kt.test(n))return null;return t}var te=(e,t)=>e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;function pe(e,t){let n=e.data,r=e.expanded;for(let s=0;s<t.length;s++){let l=t[s],u=s<t.length-1?te(r,l):void 0;if(u!==void 0){if(u===null||Array.isArray(u))return;n=te(u,"data"),r=te(u,"expanded");continue}if(n=te(n,l),r=void 0,n===void 0)return}return n}var B=e=>e==null||e===""||Array.isArray(e)&&e.length===0;function me(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 St=new Set(["http:","https:","mailto:","tel:"]),Rt=new Set(["http:","https:"]),At=2048;function N(e,t,n){if(typeof e!="string"||e.length>At)return null;let r;try{r=new URL(e,t)}catch{return null}return!(n==="link"?St:Rt).has(r.protocol)||r.username!==""||r.password!==""?null:r.href}var vt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ze=e=>typeof e=="string"&&vt.test(e);function He(e,t,n){let r=[],s=e.replace(/\{([^{}]*)\}/g,(u,i)=>{let f=j(i);if(!f)return r.push({ok:!1,reason:"bad-path",path:i.trim().slice(0,80)}),"";let o=n(f);return typeof o!="string"&&typeof o!="number"||o===""?(r.push({ok:!1,reason:"empty",path:f.join(".")}),""):encodeURIComponent(String(o))});if(r[0])return r[0];let l=N(s,t,"link");return l===null?{ok:!1,reason:"unsafe-url",path:e.slice(0,80)}:{ok:!0,url:l}}var Tt=1e5,Ve=8,Ge=2048,Et="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",We={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",copy:"\xA9",reg:"\xAE",trade:"\u2122",mdash:"\u2014",ndash:"\u2013",hellip:"\u2026",laquo:"\xAB",raquo:"\xBB"};function Xe(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>Tt){for(let u of s.split(/\n{2,}/))u.trim()!==""&&(P(l,"p",r).textContent=u.trim());return r}return ke(l,s.split(`
4
+ `),r,0),r}function P(e,t,n){let r=e.doc.createElement(t);return n.appendChild(r),r}var ge=e=>e.trim()==="",z=e=>e.length-e.trimStart().length;function Ze(e){let t=z(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 we(e){if(z(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 he(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 q(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 ye=e=>/^ {0,3}>/.test(e);function xt(e){if(Ze(e)!==null||he(e)!==null||ye(e)||we(e))return!0;let t=q(e);return t!==null&&t.rest!==""&&(!t.ordered||t.start===1)}function ke(e,t,n,r){if(r>=Ve){let l=t.join(`
5
+ `).trim();l!==""&&(P(e,"p",n).textContent=l);return}let s=0;for(;s<t.length;){let l=t[s];if(ge(l)){s++;continue}let u=he(l);if(u){let a=[],d=z(l);for(s++;s<t.length;){let m=t[s],w=he(m);if(w&&w.mark===u.mark&&w.size>=u.size&&m.trim().length===w.size)break;a.push(m.slice(Math.min(d,z(m)))),s++}s++,P(e,"code",P(e,"pre",n)).textContent=a.length>0?`${a.join(`
6
+ `)}
7
+ `:"";continue}let i=Ze(l);if(i){re(e,i.text,P(e,`h${i.level}`,n),r),s++;continue}if(we(l)){P(e,"hr",n),s++;continue}if(ye(l)){let a=[];for(;s<t.length&&ye(t[s]);)a.push(t[s].replace(/^ {0,3}> ?/,"")),s++;ke(e,a,P(e,"blockquote",n),r+1);continue}let f=q(l);if(f){s=Pt(e,t,s,f,n,r);continue}let o=[l.trimStart()];for(s++;s<t.length&&!ge(t[s])&&!xt(t[s]);)o.push(t[s].trimStart()),s++;re(e,o.join(`
8
+ `),P(e,"p",n),r)}}function Pt(e,t,n,r,s,l){let u=P(e,r.ordered?"ol":"ul",s);r.ordered&&r.start!==1&&u.setAttribute("start",String(r.start));let i=[],f=!1;for(;n<t.length;){let o=q(t[n]);if(!o||o.ordered!==r.ordered||o.indent!==r.indent||we(t[n]))break;let a=o.indent+o.width,d=[o.rest];n++;let m=!1;for(;n<t.length;){let w=t[n];if(ge(w)){m=!0,d.push(""),n++;continue}if(z(w)<a)break;m&&d.some(v=>v!=="")&&!q(w.slice(a))&&(f=!0),m=!1,d.push(w.slice(a)),n++}for(;d.length>0&&d[d.length-1]==="";)d.pop();if(m&&n<t.length){let w=q(t[n]);w&&w.ordered===r.ordered&&w.indent===r.indent&&(f=!0)}i.push({lines:d,el:P(e,"li",u)})}for(let o of i)if(ke(e,o.lines,o.el,l+1),!f){for(let a of Array.from(o.el.childNodes))if(a.nodeName==="P"){for(;a.firstChild;)o.el.insertBefore(a.firstChild,a);o.el.removeChild(a)}}return n}var Se=e=>e===void 0||e===" "||e===`
9
+ `,Ye=e=>e!==void 0&&/[\p{L}\p{N}]/u.test(e);function ne(e,t,n,r,s=""){let l=Math.min(e.length,r);for(let u=n;u<l;u++){if(e[u]===t)return u;if(s.includes(e[u]))return-1}return-1}function H(e,t,n){let r=t;for(;e[r]===n;)r++;return r}function Je(e,t){if(e[t]==="\\")return t+2;if(e[t]==="`"){let n=H(e,t,"`"),r=Qe(e,n,n-t);return r===-1?n:r+(n-t)}return-1}function Qe(e,t,n){let r=e.indexOf("`",t);for(;r!==-1;){let s=H(e,r,"`");if(s-r===n)return r;r=e.indexOf("`",s)}return-1}function Ot(e,t,n){let r=n[0],s=t;for(;s<e.length;){let l=Je(e,s);if(l!==-1){s=l;continue}if(e[s]!==r){s++;continue}let u=H(e,s,r),i=u-s;if((n.length===1?i===1||i>=3:i>=n.length)&&s>t&&!Se(e[s-1])&&(r!=="_"||!Ye(e[u])))return u-n.length;s=u}return-1}function $t(e,t){let n=ne(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",u=r.slice(l?2:1);if(!(l?/^[0-9a-fA-F]{1,6}$/:/^[0-9]{1,7}$/).test(u))return null;let i=parseInt(u,l?16:10);return{char:i>0&&i<=1114111&&!(i>=55296&&i<=57343)?String.fromCodePoint(i):"\uFFFD",end:n+1}}let s=Object.prototype.hasOwnProperty.call(We,r)?We[r]:void 0;return s===void 0?null:{char:s,end:n+1}}var et=e=>e.replace(/\\([!-/:-@[-`{-~])/g,"$1");function Ct(e,t){if(e[t]!=="(")return null;let n=Math.min(e.length,t+Ge+1024),r=t+1;for(;r<n&&Se(e[r])&&e[r]!==void 0;)r++;let s="";if(e[r]==="<"){let u=ne(e,">",r+1,n,`
11
+ <`);if(u===-1)return null;s=e.slice(r+1,u),r=u+1}else{let u=r,i=0;for(;r<n;){let f=e[r];if(f==="\\"&&r+1<n){r+=2;continue}if(f===" "||f===`
12
+ `)break;if(f==="("&&i++,f===")"){if(i===0)break;i--}r++}s=e.slice(u,r)}for(;r<n&&(e[r]===" "||e[r]===`
13
+ `);)r++;let l=e[r];if(l==='"'||l==="'"||l==="("){let u=ne(e,l==="("?")":l,r+1,n);if(u===-1)return null;for(r=u+1;r<n&&(e[r]===" "||e[r]===`
14
+ `);)r++}return e[r]!==")"?null:{address:et(s),end:r+1}}function be(e,t,n=!1){let r=t+1;for(;r<e.length;){let l=Je(e,r);if(l!==-1){r=l;continue}if(!n&&e[r]==="!"&&e[r+1]==="["){let u=be(e,r+1,!0);if(!u)return null;r=u.end;continue}if(e[r]==="[")return null;if(e[r]==="]")break;r++}if(r>=e.length)return null;let s=Ct(e,r+1);return s?{label:e.slice(t+1,r),address:s.address,end:s.end}:null}var Mt=/^(?:[a-z][a-z0-9+.-]{1,31}:[^\s<>]*|[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+)$/i;function re(e,t,n,r){if(r>=Ve){n.appendChild(e.doc.createTextNode(t));return}let s="",l=()=>{s!==""&&n.appendChild(e.doc.createTextNode(s)),s=""},u=new Set,i=0;for(;i<t.length;){let f=t[i];if(f==="\\"){let o=t[i+1];if(o===`
15
+ `)for(l(),P(e,"br",n),i+=2;t[i]===" ";)i++;else o!==void 0&&Et.includes(o)?(s+=o,i+=2):(s+=f,i++);continue}if(f===`
16
+ `){let o=s.endsWith(" ");for(s=s.replace(/ +$/,""),o?(l(),P(e,"br",n)):s+=`
17
+ `,i++;t[i]===" ";)i++;continue}if(f==="`"){let o=H(t,i,"`"),a=Qe(t,o,o-i);if(a===-1){s+=t.slice(i,o),i=o;continue}let d=t.slice(o,a).replace(/\n/g," ");d.length>1&&d.startsWith(" ")&&d.endsWith(" ")&&d.trim()!==""&&(d=d.slice(1,-1)),l(),P(e,"code",n).textContent=d,i=a+(o-i);continue}if(f==="!"&&t[i+1]==="["){let o=be(t,i+1);if(o){let a=et(o.label),d=N(o.address.trim(),e.base,"media");if(d===null)s+=a;else{l();let m=P(e,"img",n);m.setAttribute("src",d),m.setAttribute("alt",a),m.setAttribute("loading","lazy")}i=o.end;continue}}if(f==="["){let o=be(t,i);if(o){let a=N(o.address.trim(),e.base,"link");l();let d=a===null?n:P(e,"a",n);a!==null&&d.setAttribute("href",a),re(e,o.label,d,r+1),i=o.end;continue}}if(f==="<"){let o=ne(t,">",i+1,i+Ge,`
18
+ <`),a=o===-1?null:t.slice(i+1,o);if(a!==null&&Mt.test(a)){let d=N(a.includes(":")?a:`mailto:${a}`,e.base,"link");if(d!==null){l();let m=P(e,"a",n);m.setAttribute("href",d),m.textContent=a,i=o+1;continue}}}if(f==="&"){let o=$t(t,i);if(o){s+=o.char,i=o.end;continue}}if(f==="*"||f==="_"||f==="~"){let o=H(t,i,f),a=o-i,d=!Se(t[o])&&(f!=="_"||!Ye(t[i-1])),m=f==="~"?a>=2?[["~~",["del"]]]:[]:[...a>=3?[[f.repeat(3),["em","strong"]]]:[],...a>=2?[[f.repeat(2),["strong"]]]:[],[f,["em"]]],w=!1;if(d)for(let[v,x]of m){if(u.has(v))continue;let O=i+v.length,T=Ot(t,O,v);if(T===-1){u.add(v);continue}if(T===O)continue;l();let M=n;for(let $ of x)M=P(e,$,M);re(e,t.slice(O,T),M,r+1),i=T+v.length,w=!0;break}w||(s+=t.slice(i,o),i=o);continue}s+=f,i++}l()}var Lt="http://www.w3.org/1999/xhtml",Ut=new Set(["script","style","iframe","frame","frameset","object","embed","applet","base","link","meta","template","noscript"]),se={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"}},oe=e=>e.namespaceURI===Lt,W=e=>!Ut.has(e.localName.toLowerCase());function Re(e,t){if(!W(e))return"refused-element";let n=me(t);return n===null?"type":(e.textContent=n,null)}function tt(e,t,n){return!W(e)||!oe(e)?"refused-element":typeof t!="string"?"type":(e.replaceChildren(Xe(t,e.ownerDocument,n.base)),null)}function nt(e,t,n){return typeof e!="string"?null:ze(e)?Be(e,{...t.apiUrl?{apiUrl:t.apiUrl}:{},...n?{width:n}:{}}):N(e,t.base,"media")}function rt(e,t,n,r){if(!oe(e)||e.localName!=="img")return"refused-element";if(typeof t!="string")return"type";let s=nt(t,n,r);return s===null?"unsafe-url":(e.removeAttribute("srcset"),e.setAttribute("src",s),null)}function st(e,t,n,r,s){let l=t.toLowerCase(),u=Object.prototype.hasOwnProperty.call(se,l)?se[l]:void 0;if(!u)return"unsupported";if(!W(e)||!oe(e))return"refused-element";if(u.on!==null&&!u.on.includes(e.localName))return"unsupported";if(u.holds==="text"){let f=me(n);return f===null?"type":(e.setAttribute(l,f),null)}if(typeof n!="string")return"type";let i=u.holds==="media"?nt(n,r,e.localName==="img"?s:void 0):N(n,r.base,"link");return i===null?"unsafe-url":(e.setAttribute(l,i),null)}function ot(e,t){return!oe(e)||e.localName!=="a"&&e.localName!=="area"?"unsupported":(e.setAttribute("href",t),null)}function K(e,t){t?e.removeAttribute("hidden"):e.setAttribute("hidden","")}var it=["text","markdown","date","datetime","number"],Nt=/^\d{4}-\d{2}-\d{2}$/,It=/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;function at(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 u=typeof e=="number"?e:typeof e=="string"&&e.trim()!==""?Number(e):NaN;return Number.isFinite(u)?new Intl.NumberFormat(r).format(u):null}if(typeof e!="string"||!It.test(e))return null;let s=new Date(e);if(Number.isNaN(s.getTime()))return null;let l=Nt.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-",Ft=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"]),ve=/^[a-z0-9][a-z0-9_-]{0,63}$/,Dt=/^mk_[A-Za-z0-9_]{8,128}$/,Kt=4;function U(e){let t=e.getAttribute("id");return`${e.localName}${t&&/^[\w-]{1,40}$/.test(t)?`#${t}`:""}`}var ae=e=>(e??"").split(",").map(t=>t.trim()).filter(t=>/^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t)),lt=e=>Array.from(e.children).find(t=>t.localName==="template")??null;function Ae(e,t){let n=null,r=()=>n??(n={format:"text",attrs:[],expand:[]});for(let i of Array.from(e.attributes)){if(!i.name.startsWith(S))continue;let f=i.name.slice(S.length);if(f==="bind")r().bind=i.value;else if(f.startsWith("bind-")){let o=f.slice(5);Object.prototype.hasOwnProperty.call(se,o)?r().attrs.push([o,i.value]):t.push({path:i.value.slice(0,80),reason:"unsupported",on:`${U(e)}[${i.name.slice(0,60)}]`})}else f==="show"?r().show=i.value:f==="hide"?r().hide=i.value:f==="link"?r().link=i.value:Ft.has(f)||t.push({path:i.value.slice(0,80),reason:"unsupported",on:`${U(e)}[${i.name.slice(0,60)}]`})}if(!n)return null;let s=n,l=e.getAttribute(`${S}format`);l!==null&&(it.includes(l)?s.format=l:t.push({path:l.slice(0,80),reason:"bad-path",on:`${U(e)}[${S}format]`}));let u=parseInt(e.getAttribute(`${S}width`)??"",10);return u>=16&&u<=4096&&(s.width=u),s.expand=ae(e.getAttribute(`${S}expand`)),s}function _t(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:`${U(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 i of l.split(";")){if(i.trim()==="")continue;let f=i.indexOf("="),o=f===-1?"":i.slice(0,f).trim(),a=f===-1?"":i.slice(f+1).trim();ve.test(o)&&a!==""&&a.length<=200?(n.filter??(n.filter={}))[o]=a:t.push({path:i.trim().slice(0,80),reason:"bad-path",on:`${U(e)}[${S}filter]`})}let u=ae(e.getAttribute(`${S}expand`));return u.length>0&&(n.expand=u),n}function ie(e,t){let n={bound:[],eaches:[],lists:[],records:[],issues:[]},r=s=>{if(s.hasAttributes()){let l=s.getAttribute(`${S}list`),u=s.getAttribute(`${S}record`),i=s.getAttribute(`${S}each`);if(l!==null||u!==null){let o=(l??u??"").trim();if(!t)n.issues.push({path:o.slice(0,80),reason:"unsupported",on:U(s)});else if(!ve.test(o))n.issues.push({path:o.slice(0,80),reason:"bad-path",on:U(s)});else if(l!==null)n.lists.push({el:s,collection:o,options:_t(s,n.issues)});else{let a=s.getAttribute(`${S}slug-param`),d=s.getAttribute(`${S}slug`)??(a===null?null:new URLSearchParams(s.ownerDocument.location?.search??"").get(a)),m=ie(s,!0);for(let v of m.records)n.issues.push({path:v.collection,reason:"unsupported",on:U(v.el)});n.lists.push(...m.lists),n.issues.push(...m.issues);let w=Ae(s,n.issues);w&&m.bound.unshift({el:s,d:w}),n.records.push({el:s,collection:o,slug:d!==null&&d.trim()!==""&&d.length<=200?d.trim():null,expand:ae(s.getAttribute(`${S}expand`)),block:{...m,lists:[],records:[],issues:[]}})}return}if(i!==null){n.eaches.push({el:s,path:i});return}let f=Ae(s,n.issues);f&&n.bound.push({el:s,d:f})}for(let l of Array.from(s.children))r(l)};for(let s of Array.from(e.children))r(s);return n}function ut(e){let t=new Map,n=(r,s=[])=>{let l=j(r)?.[0];if(!l)return;let u=t.get(l)??new Set;for(let i of s)u.add(i);t.set(l,u)};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,ae(r.el.getAttribute(`${S}expand`)));return t}function V(e,t){let n=j(t);if(!n)return{status:"bad-path"};if(e.kind==="record")return{status:"value",value:pe(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:pe(r.value,n.slice(1))}}function Te(e){if(typeof e?.key!="string"||!Dt.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=qe({key:e.key,apiUrl:e.apiUrl,appUrl:e.appUrl,fetch:e.fetch}),s={apiUrl:e.apiUrl,base:n.baseURI},l=new Map,u=c=>{let p=l.get(c);return p||l.set(c,p={attrs:new Map}),p},i=c=>{let p=u(c);p.nodes??(p.nodes=Array.from(c.childNodes))},f=(c,p)=>{let R=u(c);R.attrs.has(p)||R.attrs.set(p,c.getAttribute(p))},o=()=>{for(let[c,p]of l){p.nodes&&c.replaceChildren(...p.nodes);for(let[R,g]of p.attrs)g===null?c.removeAttribute(R):c.setAttribute(R,g)}l.clear()},a=[],d=0,m=(c,p,R)=>a.push({path:(p??"").trim().slice(0,120),reason:R,on:U(c)});function w(c,p,R){if(p.bind!==void 0){let g=V(R,p.bind);if(g.status!=="value")m(c,p.bind,g.status);else if(!W(c))m(c,p.bind,"refused-element");else{let b=c.localName==="img";if(b?(f(c,"src"),f(c,"srcset"),f(c,"hidden")):i(c),B(g.value))b?K(c,!1):c.textContent="",m(c,p.bind,"empty");else{let h;if(b)h=rt(c,g.value,s,p.width);else if(p.format==="markdown")h=tt(c,g.value,s);else if(p.format==="text")h=Re(c,g.value);else{let k=at(g.value,p.format,c.closest("[lang]")?.getAttribute("lang")??void 0);h=k===null?"type":Re(c,k)}h?m(c,p.bind,h):d++}}}for(let[g,b]of p.attrs){let h=V(R,b);if(h.status!=="value"){m(c,b,h.status);continue}if(f(c,g),B(h.value)){g==="alt"?c.setAttribute("alt",""):c.removeAttribute(g),m(c,b,"empty");continue}let k=c.getAttribute(g),A=st(c,g,h.value,s,p.width);A?(k===null&&c.removeAttribute(g),m(c,b,A)):d++}for(let[g,b]of[[p.show,!0],[p.hide,!1]]){if(g===void 0)continue;let h=V(R,g);if(h.status!=="value"){m(c,g,h.status);continue}f(c,"hidden");let k=!B(h.value)&&h.value!==!1;K(c,k===b),d++}if(p.link!==void 0){let g=[],b=He(p.link,s.base,h=>{let k=V(R,h.join("."));if(k.status==="value")return k.value;g.push(k.status)});if(g[0])m(c,p.link,g[0]);else if(!b.ok)m(c,b.path,b.reason);else{f(c,"href");let h=ot(c,b.url);h?m(c,p.link,h):d++}}}function v(c,p,R){let g=lt(c);if(!g){m(c,"template","unsupported");return}i(c);let b=[g];for(let k of Array.from(c.children))k.hasAttribute(`${S}empty`)&&(f(k,"hidden"),K(k,p.length===0),b.push(k));let h=[];for(let k of p){let A=g.content.cloneNode(!0);x(ie(A,!1),{kind:"record",source:k},R+1),h.push(...Array.from(A.childNodes))}c.replaceChildren(...b,...h),d++}function x(c,p,R){a.push(...c.issues);for(let{el:g,d:b}of c.bound)w(g,b,p);for(let g of c.eaches){let b=V(p,g.path);if(b.status!=="value"){m(g.el,g.path,b.status);continue}if(R>=Kt){m(g.el,g.path,"unsupported");continue}let h=b.value,k=Array.isArray(h)?h:B(h)?[]:[h];if(k.some(A=>A===null||typeof A!="object"||Array.isArray(A))){m(g.el,g.path,"type");continue}k.length===0&&m(g.el,g.path,"empty"),v(g.el,k.map(A=>({data:A})),R)}}async function O(c){try{let p=await c();return p===null?{status:"missing"}:{status:"ok",value:p}}catch(p){return p instanceof D&&p.status===404&&p.code!=="RELEASE_NOT_FOUND"?{status:"missing"}:{status:"failed"}}}let T=null,M="";async function $(c){let p=T===null;p&&t.setAttribute(`${S}state`,"loading");let R=await r.isPreview().catch(()=>!1),g=y=>{T=y,t.setAttribute(`${S}state`,y.state),t.toggleAttribute(`${S}preview`,y.preview);let E=y.issues.filter(I=>I.reason!=="empty"),C=E.map(I=>`${I.path} (${I.reason}) on ${I.on}`).join("; ");return C!==M&&(M=C,E.length>0&&console.warn(`[mapled] ${E.length===1?"1 place":`${E.length} places`} couldn't be filled: ${C}`)),n.dispatchEvent(new CustomEvent(p?"mapled:ready":"mapled:update",{detail:y})),y},b=null;if(!R)if(c!==void 0)b=c;else try{let y=await r.getRelease();if(y===null)return g({state:"unpublished",release:null,preview:R,bound:0,issues:[]});b=y.release}catch{return T!==null&&T.state==="ready"?T:g({state:"fallback",release:null,preview:R,bound:0,issues:[]})}let h=b===null?{}:{release:b},k=ie(t,!0),A=Ae(t,k.issues);A&&k.bound.unshift({el:t,d:A});let L=new Map,le=new Map,Y=new Map,J=[];for(let[y,E]of ut(k))J.push(O(()=>r.getSingle(y,{...h,...E.size>0?{expand:[...E]}:{}})).then(C=>L.set(y,C)));for(let y of k.lists){let E=lt(y.el),C=E?[...ut(ie(E.content,!1)).keys()]:[];J.push(O(async()=>(await r.getRecords(y.collection,{...y.options,...h,...C.length>0?{fields:C}:{}})).records).then(I=>le.set(y,I)))}for(let y of k.records){if(y.slug===null){Y.set(y,{status:"missing"});continue}let E=y.slug;J.push(O(()=>r.getRecordBySlug(y.collection,E,{...h,...y.expand.length>0?{expand:y.expand}:{}})).then(C=>Y.set(y,C)))}await Promise.all(J);let Ce=[...L.values(),...le.values(),...Y.values()];if(Ce.length>0&&Ce.every(y=>y.status==="failed"))return T!==null&&T.state==="ready"?T:g({state:"fallback",release:b,preview:R,bound:0,issues:[]});o(),a=[],d=0,x(k,{kind:"page",singles:L},0);for(let y of k.lists){let E=le.get(y);E?.status==="ok"?v(y.el,E.value,0):m(y.el,y.collection,E?.status??"failed")}for(let y of k.records){let E=Y.get(y);E?.status==="ok"?x(y.block,{kind:"record",source:E.value},0):m(y.el,y.collection,E?.status??"failed")}return Pe(),g({state:"ready",release:b,preview:R,bound:d,issues:a})}let Ee=new WeakSet,xe=new WeakSet;function Pe(){for(let c of Array.from(t.querySelectorAll(`form[${S}form]`)))Ee.has(c)||(Ee.add(c),c.addEventListener("submit",p=>{p.preventDefault(),ft(c)}))}async function ft(c){if(c.getAttribute(`${S}form-state`)==="sending")return;let p=(c.getAttribute(`${S}form`)??"").trim(),R=c.querySelector(`[${S}form-sent]`),g=c.querySelector(`[${S}form-failed]`),b=Array.from(c.querySelectorAll("button, input[type=submit]")).filter(L=>!L.disabled);c.setAttribute(`${S}form-state`,"sending");for(let L of b)L.disabled=!0;let h=c.querySelector(`input[type=checkbox][${S}consent]`),k=h?.checked?(h.labels?.[0]?.textContent??h.closest("label")?.textContent??"").trim().slice(0,500):"",A=ve.test(p)?await de(p,c,{key:e.key,apiUrl:e.apiUrl,fetch:e.fetch,...k?{consent:k}:{}}):{ok:!1,status:0,code:"BAD_FORM",message:"This form isn't set up yet."};for(let L of b)L.disabled=!1;c.setAttribute(`${S}form-state`,A.ok?"sent":"failed"),A.ok&&c.reset(),R&&K(R,A.ok),g&&(!A.ok&&(xe.has(g)||(g.textContent??"").trim()==="")&&(xe.add(g),g.textContent=A.message),K(g,!A.ok)),n.dispatchEvent(new CustomEvent("mapled:form",{detail:A.ok?{form:p,ok:!0}:{form:p,ok:!1,status:A.status,code:A.code}}))}Pe();let Oe=async c=>{try{return await $(c)}catch(p){return console.error(`[mapled] ${p instanceof Error?p.message:"The page couldn't be filled."}`),T??{state:"fallback",release:null,preview:!1,bound:0,issues:[]}}},G=Oe(),X=c=>G=G.then(()=>Oe(c)),$e=c=>{c.target?.closest?.(`[${S}end-preview]`)&&(c.preventDefault(),ee(),X())};n.addEventListener("click",$e);let Z=null;if(e.live){let c=typeof e.live=="number"?e.live:void 0;G.then(p=>{!p.preview&&Z===null&&(Z=r.watch(R=>{X(R.release)},{interval:c}))})}return{ready:G,get report(){return T},refresh:()=>X(),endPreview:()=>(ee(),X()),stop:()=>{Z?.(),Z=()=>{},n.removeEventListener("click",$e)},client:r}}var ct="0.1.0";var dt={version:ct,mount:Te,page:null};window.Mapled=dt;var _=document.currentScript;if(_ instanceof HTMLScriptElement&&_.hasAttribute("data-mapled-key")){let e=_.getAttribute("data-mapled-live"),t=e===null?NaN:parseInt(e,10),n=()=>{try{dt.page=Te({key:_.getAttribute("data-mapled-key")??"",..._.getAttribute("data-mapled-api")?{apiUrl:_.getAttribute("data-mapled-api")}:{},...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()}})();
@@ -0,0 +1 @@
1
+ sha384-mbuMcQAueviAbkTzgvxc8oDtspuYH6lFLUgE+gocGexnI8ehgSgdjnNMNL7JPk4q
@@ -0,0 +1,15 @@
1
+ /** Rich text → DOM. Mapled stores and serves rich text as Markdown; this
2
+ builds it into nodes — `createElement` with names from the list below,
3
+ text nodes, and three attributes it validates itself (`href`, `src`,
4
+ `alt`; `start` is a number it parsed, `loading` a constant). There is
5
+ no HTML string anywhere on the way, so whatever the input does to the
6
+ parser, the worst result is wrong formatting: raw HTML in the source
7
+ is shown as the text it is (DOM_SECURITY.md, rule 5).
8
+
9
+ The syntax is what Mapled's editor writes: paragraphs, headings,
10
+ bold / italic / strikethrough, inline code and fenced blocks, quotes,
11
+ bulleted and numbered lists (nested), links, images, rules, hard
12
+ breaks. Tables, reference links and footnotes are shown as text. */
13
+ /** Every element this module can create. */
14
+ export declare const MARKDOWN_ELEMENTS: readonly ["p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "blockquote", "pre", "code", "em", "strong", "del", "a", "img", "br", "hr"];
15
+ export declare function renderMarkdown(source: string, doc: Document, base: string): DocumentFragment;