@mapled/vanilla 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/mapled.js +18 -0
- package/dist/mapled.js.sha384 +1 -0
- package/dist/markdown.d.ts +15 -0
- package/dist/markdown.js +577 -0
- package/dist/paths.d.ts +21 -0
- package/dist/paths.js +60 -0
- package/dist/runtime.d.ts +85 -0
- package/dist/runtime.js +725 -0
- package/dist/sinks.d.ts +41 -0
- package/dist/sinks.js +142 -0
- package/dist/url.d.ts +30 -0
- package/dist/url.js +66 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +3 -0
- package/package.json +40 -5
package/README.md
CHANGED
|
@@ -1,10 +1,197 @@
|
|
|
1
1
|
# @mapled/vanilla
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
8
|
+
## Add it to a page
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
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.1/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; 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 |
|
|
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 — or preview just ended and what is published is on its way); 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` |
|
package/dist/index.d.ts
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, 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.1 | MIT | https://mapled.io */
|
|
2
|
+
"use strict";(()=>{var $t=Object.defineProperty;var Ct=(e,t,n)=>t in e?$t(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var me=(e,t,n)=>Ct(e,typeof t!="symbol"?t+"":t,n);var ge="mapled-preview",ne="mapled:preview",Lt=/^mpv_[A-Za-z0-9]{16,96}$/,Ge=/^mps_[A-Za-z0-9]{16,96}$/,Xe=new WeakMap,Ut=4e3;function he(){if(typeof window>"u"||typeof document>"u")return null;try{let e=window.sessionStorage,t=Xe.get(window);return t||Xe.set(window,t={memory:null,exchanging:null,epoch:0}),{location:window.location,history:window.history,sessionStorage:e,page:t}}catch{return null}}function Nt(e){try{let t=e.getItem(ne);if(!t)return null;let n=JSON.parse(t),r=typeof n?.expiresAt=="string"?Date.parse(n.expiresAt):NaN;return typeof n?.draftToken!="string"||!Ge.test(n.draftToken)||!(r>Date.now())?(e.removeItem(ne),null):{draftToken:n.draftToken,expiresAt:n.expiresAt}}catch{return null}}function D(e){let t=Nt(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 _(){let e=he();return e?D(e):null}function Ve(e){let t;try{t=new URL(e.location.href)}catch{return null}let n=t.searchParams.get(ge);if(n===null)return null;t.searchParams.delete(ge);try{e.history.replaceState(e.history.state,"",`${t.pathname}${t.search}${t.hash}`)}catch{}return n}function ye(){let e=he();if(e){e.page.memory=null,e.page.epoch++,e.page.exchanging=null,Ve(e);try{e.sessionStorage.removeItem(ne)}catch{}}}function Ze(e={}){let t=he();if(!t)return Promise.resolve(null);let n=Ve(t);if(n===null)return t.page.exchanging?t.page.exchanging.done:Promise.resolve(D(t));if(!Lt.test(n))return Promise.resolve(D(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,a=(async()=>{try{let o=await l(`${r}/v1/delivery/preview/exchange`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:n}),cache:"no-store"});if(o.ok){let i=await o.json();if(typeof i.draftToken!="string"||typeof i.expiresAt!="string"||!Ge.test(i.draftToken)||!(Date.parse(i.expiresAt)>Date.now())||t.page.epoch!==c)return D(t);let d={draftToken:i.draftToken,expiresAt:i.expiresAt};t.page.memory=d;try{t.sessionStorage.setItem(ne,JSON.stringify(d))}catch{}return d}if(o.status===401&&t.page.epoch===c){let i=await o.json().catch(()=>null),d=i?.error?.projectId;if(t.page.epoch===c&&i?.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(p=>setTimeout(p,Ut)),D(t)}return D(t)}catch{return D(t)}})(),f={token:n,done:a};return t.page.exchanging=f,a.then(()=>{t.page.exchanging===f&&(t.page.exchanging=null)}),a}async function Ye(e){return(await Ze(e))?.draftToken??null}function be(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}}function Je(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,c=typeof document>"u"?null:document,a,f=0,o=!1,i,d=null,p=()=>!c||c.visibilityState!=="hidden",k=()=>{o||!p()||(i=setTimeout(T,Math.min(s*2**f,3e5)))};async function T(){if(o||d)return;let M=new AbortController;d=M;let S=null;try{let C=await l(n,{cache:"no-cache",headers:r,signal:M.signal}),O=C.ok?be(await C.json()):void 0;if(o)return;O===void 0?f+=1:(f=0,a===void 0?a=O?O.release:null:O&&O.release>(a??0)&&(S={next:O,previous:a},a=O.release))}catch{if(o)return;f+=1}finally{d===M&&(d=null)}k(),S&&e.onPublish(S.next,S.previous)}let x=()=>{clearTimeout(i),p()&&T()};return c?.addEventListener("visibilitychange",x),p()&&T(),()=>{o=!0,clearTimeout(i),d?.abort(),c?.removeEventListener("visibilitychange",x)}}function Qe(e){let t={};return e.forEach((n,r)=>{typeof n!="string"||r in t||(t[r]=n)}),t}async function we(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?Qe(new FormData(t)):typeof FormData<"u"&&t instanceof FormData?Qe(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 a;try{a=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(c)})}catch{return{ok:!1,status:0,code:"NETWORK",message:"Mapled couldn't be reached. Try again in a minute."}}if(a.ok)return{ok:!0};let f=`The submission wasn't accepted (${a.status}).`,o;try{let i=await a.json();i.error?.message&&(f=i.error.message),typeof i.error?.code=="string"&&(o=i.error.code)}catch{}return{ok:!1,status:a.status,code:o,message:f}}var ke=/^[a-z0-9][a-z0-9-]{0,63}$/,et=new Set;function It(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"||!ke.test(n)))for(let[s,l]of Object.entries(r)){if(typeof l!="string"||!ke.test(s)||!ke.test(l))continue;let c=`${n}.${s}`;et.has(c)||(et.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 rt(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 j=class extends Error{constructor(n,r,s){super(r);me(this,"status");me(this,"code");this.name="MapledError",this.status=n,this.code=s}},tt=e=>Number.isInteger(e)&&e>=1,nt=()=>new Error("Mapled: release must be a positive whole number.");function st(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&&!tt(e.release))throw nt();let n=(o,i)=>(e.fetch??fetch)(o,i);async function r(){return e.draft===null?null:typeof e.draft=="string"?e.draft||null:typeof e.draft=="function"?await e.draft()||null:Ye({apiUrl:t,appUrl:e.appUrl,fetch:e.fetch})}async function s(o){let i=`Mapled request failed (${o.status}).`,d;try{let p=await o.json();p.error?.message&&(i=p.error.message),typeof p.error?.code=="string"&&(d=p.error.code)}catch{}return new j(o.status,i,d)}async function l(o,i,d){let p=await r(),k=d.release??e.release;if(k!==void 0&&!tt(k))throw nt();k!==void 0&&!p&&i.set("release",String(k));let T=i.toString(),x=await n(`${t}${o}${T?`?${T}`:""}`,{headers:p?{"x-mapled-key":e.key,"x-mapled-draft":p}:{"x-mapled-key":e.key},...p?{cache:"no-store"}:{},...d.signal?{signal:d.signal}:{}});if(!x.ok)throw await s(x);let M=await x.json();return It(M),M}let c=(o,i)=>(i.expand?.length&&o.set("expand",i.expand.join(",")),i.fields?.length&&o.set("fields",i.fields.join(",")),o),a=async(o,i)=>{try{return(await l(o,c(new URLSearchParams,i),i)).record}catch(d){if(d instanceof j&&d.status===404&&d.code!=="RELEASE_NOT_FOUND")return null;throw d}};return{async getRecords(o,i={}){let d=new URLSearchParams;for(let[p,k]of Object.entries(i.filter??{}))if(k!==null&&typeof k=="object")for(let[T,x]of Object.entries(k))x!==void 0&&d.set(`filter[${p}][${T}]`,Array.isArray(x)?x.join(","):String(x));else d.set(`filter[${p}]`,String(k));return i.sort&&d.set("sort",i.sort),i.limit!==void 0&&d.set("limit",String(i.limit)),i.offset!==void 0&&d.set("offset",String(i.offset)),c(d,i),l(`/v1/delivery/collections/${encodeURIComponent(o)}/records`,d,i)},getRecord(o,i,d={}){return a(`/v1/delivery/collections/${encodeURIComponent(o)}/records/${encodeURIComponent(i)}`,d)},getRecordBySlug(o,i,d={}){return a(`/v1/delivery/collections/${encodeURIComponent(o)}/records/by-slug/${encodeURIComponent(i)}`,d)},getSingle(o,i={}){return a(`/v1/delivery/singles/${encodeURIComponent(o)}`,i)},async isPreview(){return await r()!==null},watch(o,i={}){return Je({key:e.key,apiUrl:t,interval:i.interval,onPublish:o,fetch:e.fetch})},async getRelease(o={}){let i=await n(`${t}/v1/delivery/release`,{headers:{"x-mapled-key":e.key},cache:"no-cache",...o.signal?{signal:o.signal}:{}});if(!i.ok)throw await s(i);let d=be(await i.json());if(d===void 0)throw new Error("Mapled: the release pointer didn't come back as one \u2014 check the API origin.");return d}}}var Dt=/^[a-z0-9][a-z0-9_-]{0,63}$/;function H(e){if(typeof e!="string")return null;let t=e.trim().split(".");if(t.length>8)return null;for(let n of t)if(!Dt.test(n))return null;return t}var re=(e,t)=>e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;function Se(e,t){let n=e.data,r=e.expanded;for(let s=0;s<t.length;s++){let l=t[s],c=s<t.length-1?re(r,l):void 0;if(c!==void 0){if(c===null||Array.isArray(c))return;n=re(c,"data"),r=re(c,"expanded");continue}if(n=re(n,l),r=void 0,n===void 0)return}return n}var W=e=>e==null||e===""||Array.isArray(e)&&e.length===0;function Ae(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 Ft=new Set(["http:","https:","mailto:","tel:"]),_t=new Set(["http:","https:"]),jt=2048;function F(e,t,n){if(typeof e!="string"||e.length>jt)return null;let r;try{r=new URL(e,t)}catch{return null}return!(n==="link"?Ft:_t).has(r.protocol)||r.username!==""||r.password!==""?null:r.href}var Kt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ot=e=>typeof e=="string"&&Kt.test(e);function it(e,t,n){let r=[],s=e.replace(/\{([^{}]*)\}/g,(c,a)=>{let f=H(a);if(!f)return r.push({ok:!1,reason:"bad-path",path:a.trim().slice(0,80)}),"";let o=n(f);if(typeof o!="string"&&typeof o!="number"||o==="")return r.push({ok:!1,reason:"empty",path:f.join(".")}),"";try{return encodeURIComponent(String(o))}catch{return r.push({ok:!1,reason:"unsafe-url",path:f.join(".")}),""}});if(r[0])return r[0];let l=F(s,t,"link");return l===null?{ok:!1,reason:"unsafe-url",path:e.slice(0,80)}:{ok:!0,url:l}}var Bt=1e5,lt=8,ut=2048,qt="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",at={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",copy:"\xA9",reg:"\xAE",trade:"\u2122",mdash:"\u2014",ndash:"\u2013",hellip:"\u2026",laquo:"\xAB",raquo:"\xBB"};function ct(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>Bt){for(let c of s.split(/\n{2,}/))c.trim()!==""&&(P(l,"p",r).textContent=c.trim());return r}return Pe(l,s.split(`
|
|
4
|
+
`),r,0),r}function P(e,t,n){let r=e.doc.createElement(t);return n.appendChild(r),r}var Re=e=>e.trim()==="",G=e=>e.length-e.trimStart().length;function dt(e){let t=G(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 xe(e){if(G(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 ve(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 X(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 Te=e=>/^ {0,3}>/.test(e);function zt(e){if(dt(e)!==null||ve(e)!==null||Te(e)||xe(e))return!0;let t=X(e);return t!==null&&t.rest!==""&&(!t.ordered||t.start===1)}function Pe(e,t,n,r){if(r>=lt){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(Re(l)){s++;continue}let c=ve(l);if(c){let i=[],d=G(l);for(s++;s<t.length;){let p=t[s],k=ve(p);if(k&&k.mark===c.mark&&k.size>=c.size&&p.trim().length===k.size)break;i.push(p.slice(Math.min(d,G(p)))),s++}s++,P(e,"code",P(e,"pre",n)).textContent=i.length>0?`${i.join(`
|
|
6
|
+
`)}
|
|
7
|
+
`:"";continue}let a=dt(l);if(a){oe(e,a.text,P(e,`h${a.level}`,n),r),s++;continue}if(xe(l)){P(e,"hr",n),s++;continue}if(Te(l)){let i=[];for(;s<t.length&&Te(t[s]);)i.push(t[s].replace(/^ {0,3}> ?/,"")),s++;Pe(e,i,P(e,"blockquote",n),r+1);continue}let f=X(l);if(f){s=Ht(e,t,s,f,n,r);continue}let o=[l.trimStart()];for(s++;s<t.length&&!Re(t[s])&&!zt(t[s]);)o.push(t[s].trimStart()),s++;oe(e,o.join(`
|
|
8
|
+
`),P(e,"p",n),r)}}function Ht(e,t,n,r,s,l){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 o=X(t[n]);if(!o||o.ordered!==r.ordered||o.indent!==r.indent||xe(t[n]))break;let i=o.indent+o.width,d=[o.rest];n++;let p=!1;for(;n<t.length;){let k=t[n];if(Re(k)){p=!0,d.push(""),n++;continue}if(G(k)<i)break;p&&d.some(T=>T!=="")&&!X(k.slice(i))&&(f=!0),p=!1,d.push(k.slice(i)),n++}for(;d.length>0&&d[d.length-1]==="";)d.pop();if(p&&n<t.length){let k=X(t[n]);k&&k.ordered===r.ordered&&k.indent===r.indent&&(f=!0)}a.push({lines:d,el:P(e,"li",c)})}for(let o of a)if(Pe(e,o.lines,o.el,l+1),!f){for(let i of Array.from(o.el.childNodes))if(i.nodeName==="P"){for(;i.firstChild;)o.el.insertBefore(i.firstChild,i);o.el.removeChild(i)}}return n}var Me=e=>e===void 0||e===" "||e===`
|
|
9
|
+
`,ft=e=>e!==void 0&&/[\p{L}\p{N}]/u.test(e);function se(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 V(e,t,n){let r=t;for(;e[r]===n;)r++;return r}function pt(e,t){if(e[t]==="\\")return t+2;if(e[t]==="`"){let n=V(e,t,"`"),r=mt(e,n,n-t);return r===-1?n:r+(n-t)}return-1}function mt(e,t,n){let r=e.indexOf("`",t);for(;r!==-1;){let s=V(e,r,"`");if(s-r===n)return r;r=e.indexOf("`",s)}return-1}function Wt(e,t,n){let r=n[0],s=t;for(;s<e.length;){let l=pt(e,s);if(l!==-1){s=l;continue}if(e[s]!==r){s++;continue}let c=V(e,s,r),a=c-s;if((n.length===1?a===1||a>=3:a>=n.length)&&s>t&&!Me(e[s-1])&&(r!=="_"||!ft(e[c])))return c-n.length;s=c}return-1}function Xt(e,t){let n=se(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 a=parseInt(c,l?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(at,r)?at[r]:void 0;return s===void 0?null:{char:s,end:n+1}}var gt=e=>e.replace(/\\([!-/:-@[-`{-~])/g,"$1");function Gt(e,t){if(e[t]!=="(")return null;let n=Math.min(e.length,t+ut+1024),r=t+1;for(;r<n&&Me(e[r])&&e[r]!==void 0;)r++;let s="";if(e[r]==="<"){let c=se(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 l=e[r];if(l==='"'||l==="'"||l==="("){let c=se(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:gt(s),end:r+1}}function Ee(e,t,n=!1){let r=t+1;for(;r<e.length;){let l=pt(e,r);if(l!==-1){r=l;continue}if(!n&&e[r]==="!"&&e[r+1]==="["){let c=Ee(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=Gt(e,r+1);return s?{label:e.slice(t+1,r),address:s.address,end:s.end}:null}var Vt=/^(?:[a-z][a-z0-9+.-]{1,31}:[^\s<>]*|[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+)$/i;function oe(e,t,n,r){if(r>=lt){n.appendChild(e.doc.createTextNode(t));return}let s="",l=()=>{s!==""&&n.appendChild(e.doc.createTextNode(s)),s=""},c=new Set,a=0;for(;a<t.length;){let f=t[a];if(f==="\\"){let o=t[a+1];if(o===`
|
|
15
|
+
`)for(l(),P(e,"br",n),a+=2;t[a]===" ";)a++;else o!==void 0&&qt.includes(o)?(s+=o,a+=2):(s+=f,a++);continue}if(f===`
|
|
16
|
+
`){let o=s.endsWith(" "),i=s.length;for(;i>0&&s[i-1]===" ";)i--;for(s=s.slice(0,i),o?(l(),P(e,"br",n)):s+=`
|
|
17
|
+
`,a++;t[a]===" ";)a++;continue}if(f==="`"){let o=V(t,a,"`"),i=mt(t,o,o-a);if(i===-1){s+=t.slice(a,o),a=o;continue}let d=t.slice(o,i).replace(/\n/g," ");d.length>1&&d.startsWith(" ")&&d.endsWith(" ")&&d.trim()!==""&&(d=d.slice(1,-1)),l(),P(e,"code",n).textContent=d,a=i+(o-a);continue}if(f==="!"&&t[a+1]==="["){let o=Ee(t,a+1);if(o){let i=gt(o.label),d=F(o.address.trim(),e.base,"media");if(d===null)s+=i;else{l();let p=P(e,"img",n);p.setAttribute("src",d),p.setAttribute("alt",i),p.setAttribute("loading","lazy")}a=o.end;continue}}if(f==="["){let o=Ee(t,a);if(o){let i=F(o.address.trim(),e.base,"link");l();let d=i===null?n:P(e,"a",n);i!==null&&d.setAttribute("href",i),oe(e,o.label,d,r+1),a=o.end;continue}}if(f==="<"){let o=se(t,">",a+1,a+ut,`
|
|
18
|
+
<`),i=o===-1?null:t.slice(a+1,o);if(i!==null&&Vt.test(i)){let d=F(i.includes(":")?i:`mailto:${i}`,e.base,"link");if(d!==null){l();let p=P(e,"a",n);p.setAttribute("href",d),p.textContent=i,a=o+1;continue}}}if(f==="&"){let o=Xt(t,a);if(o){s+=o.char,a=o.end;continue}}if(f==="*"||f==="_"||f==="~"){let o=V(t,a,f),i=o-a,d=!Me(t[o])&&(f!=="_"||!ft(t[a-1])),p=f==="~"?i>=2?[["~~",["del"]]]:[]:[...i>=3?[[f.repeat(3),["em","strong"]]]:[],...i>=2?[[f.repeat(2),["strong"]]]:[],[f,["em"]]],k=!1;if(d)for(let[T,x]of p){if(c.has(T))continue;let M=a+T.length,S=Wt(t,M,T);if(S===-1){c.add(T);continue}if(S===M)continue;l();let C=n;for(let O of x)C=P(e,O,C);oe(e,t.slice(M,S),C,r+1),a=S+T.length,k=!0;break}k||(s+=t.slice(a,o),a=o);continue}s+=f,a++}l()}var Zt="http://www.w3.org/1999/xhtml",Yt=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"}},ae=e=>e.namespaceURI===Zt,Z=e=>!Yt.has(e.localName.toLowerCase());function Oe(e,t){if(!Z(e))return"refused-element";let n=Ae(t);return n===null?"type":(e.textContent=n,null)}function ht(e,t,n){return!Z(e)||!ae(e)?"refused-element":typeof t!="string"?"type":(e.replaceChildren(ct(t,e.ownerDocument,n.base)),null)}function yt(e,t,n){return typeof e!="string"?null:ot(e)?rt(e,{...t.apiUrl?{apiUrl:t.apiUrl}:{},...n?{width:n}:{}}):F(e,t.base,"media")}function bt(e,t,n,r){if(!ae(e)||e.localName!=="img")return"refused-element";if(typeof t!="string")return"type";let s=yt(t,n,r);return s===null?"unsafe-url":(e.removeAttribute("srcset"),e.setAttribute("src",s),null)}function wt(e,t,n,r,s){let l=t.toLowerCase(),c=Object.prototype.hasOwnProperty.call(ie,l)?ie[l]:void 0;if(!c)return"unsupported";if(!Z(e)||!ae(e))return"refused-element";if(c.on!==null&&!c.on.includes(e.localName))return"unsupported";if(c.holds==="text"){let f=Ae(n);return f===null?"type":(e.setAttribute(l,f),null)}if(typeof n!="string")return"type";let a=c.holds==="media"?yt(n,r,e.localName==="img"?s:void 0):F(n,r.base,"link");return a===null?"unsafe-url":(e.setAttribute(l,a),null)}function kt(e,t){return!ae(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 St=["text","markdown","date","datetime","number"],Jt=/^\d{4}-\d{2}-\d{2}$/,Qt=/^\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 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"||!Qt.test(e))return null;let s=new Date(e);if(Number.isNaN(s.getTime()))return null;let l=Jt.test(e);return new Intl.DateTimeFormat(r,{dateStyle:"long",...t==="datetime"&&!l?{timeStyle:"short"}:{},...l?{timeZone:"UTC"}:{}}).format(s)}catch{return null}}var w="data-mapled-",en=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"]),Ce=/^[a-z0-9][a-z0-9_-]{0,63}$/,tn=/^mk_[A-Za-z0-9_]{8,128}$/,nn=4,rn=100,sn=2;function J(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>=sn?s.length===0:s.every(l=>l===null||(Array.isArray(l)?l.every(c=>J(c,t+1)):J(l,t+1)))}var on=e=>Array.isArray(e)&&e.every(t=>J(t));function N(e){let t=e.getAttribute("id");return`${e.localName}${t&&/^[\w-]{1,40}$/.test(t)?`#${t}`:""}`}var ue=e=>(e??"").split(",").map(t=>t.trim()).filter(t=>/^[a-z0-9][a-z0-9_.-]{0,127}$/.test(t)),Rt=e=>Array.from(e.children).find(t=>t.localName==="template")??null;function $e(e,t){let n=null,r=()=>n??(n={format:"text",attrs:[],expand:[]});for(let a of Array.from(e.attributes)){if(!a.name.startsWith(w))continue;let f=a.name.slice(w.length);if(f==="bind")r().bind=a.value;else if(f.startsWith("bind-")){let o=f.slice(5);Object.prototype.hasOwnProperty.call(ie,o)?r().attrs.push([o,a.value]):t.push({path:a.value.slice(0,80),reason:"unsupported",on:`${N(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:en.has(f)||t.push({path:a.value.slice(0,80),reason:"unsupported",on:`${N(e)}[${a.name.slice(0,60)}]`})}if(!n)return null;let s=n,l=e.getAttribute(`${w}format`);l!==null&&(St.includes(l)?s.format=l:t.push({path:l.slice(0,80),reason:"bad-path",on:`${N(e)}[${w}format]`}));let c=parseInt(e.getAttribute(`${w}width`)??"",10);return c>=16&&c<=4096&&(s.width=c),s.expand=ue(e.getAttribute(`${w}expand`)),s}function an(e,t){let n={},r=e.getAttribute(`${w}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:`${N(e)}[${w}sort]`}));let s=parseInt(e.getAttribute(`${w}limit`)??"",10);s>=1&&s<=100&&(n.limit=s);let l=e.getAttribute(`${w}filter`);if(l!==null)for(let a of l.split(";")){if(a.trim()==="")continue;let f=a.indexOf("="),o=f===-1?"":a.slice(0,f).trim(),i=f===-1?"":a.slice(f+1).trim();Ce.test(o)&&i!==""&&i.length<=200?(n.filter??(n.filter={}))[o]=i:t.push({path:a.trim().slice(0,80),reason:"bad-path",on:`${N(e)}[${w}filter]`})}let c=ue(e.getAttribute(`${w}expand`));return c.length>0&&(n.expand=c),n}function le(e,t){let n={bound:[],eaches:[],lists:[],records:[],issues:[]},r=s=>{if(s.hasAttributes()){let l=s.getAttribute(`${w}list`),c=s.getAttribute(`${w}record`),a=s.getAttribute(`${w}each`);if(l!==null||c!==null){let o=(l??c??"").trim();if(!t)n.issues.push({path:o.slice(0,80),reason:"unsupported",on:N(s)});else if(!Ce.test(o))n.issues.push({path:o.slice(0,80),reason:"bad-path",on:N(s)});else if(l!==null)n.lists.push({el:s,collection:o,options:an(s,n.issues)});else{let i=s.getAttribute(`${w}slug-param`),d=s.getAttribute(`${w}slug`)??(i===null?null:new URLSearchParams(s.ownerDocument.location?.search??"").get(i)),p=le(s,!0);for(let T of p.records)n.issues.push({path:T.collection,reason:"unsupported",on:N(T.el)});n.lists.push(...p.lists),n.issues.push(...p.issues);let k=$e(s,n.issues);k&&p.bound.unshift({el:s,d:k}),n.records.push({el:s,collection:o,slug:d!==null&&d.trim()!==""&&d.length<=200?d.trim():null,expand:ue(s.getAttribute(`${w}expand`)),block:{...p,lists:[],records:[],issues:[]}})}return}if(a!==null){n.eaches.push({el:s,path:a});return}let f=$e(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 vt(e){let t=new Map,n=(r,s=[])=>{let l=H(r)?.[0];if(!l)return;let c=t.get(l)??new Set;for(let a of s)c.add(a);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,ue(r.el.getAttribute(`${w}expand`)));return t}function Y(e,t){let n=H(t);if(!n)return{status:"bad-path"};if(e.kind==="record")return{status:"value",value:Se(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:Se(r.value,n.slice(1))}}function Le(e){if(typeof e?.key!="string"||!tn.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=st({key:e.key,apiUrl:e.apiUrl,appUrl:e.appUrl,fetch:e.fetch}),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},a=u=>{let m=c(u);m.nodes??(m.nodes=Array.from(u.childNodes))},f=(u,m)=>{let A=c(u);A.attrs.has(m)||A.attrs.set(m,u.getAttribute(m))},o=()=>{for(let[u,m]of l){m.nodes&&u.replaceChildren(...m.nodes);for(let[A,g]of m.attrs)g===null?u.removeAttribute(A):u.setAttribute(A,g)}l.clear()},i=[],d=0,p=(u,m,A)=>i.push({path:(m??"").trim().slice(0,120),reason:A,on:N(u)});function k(u,m,A){if(m.bind!==void 0){let g=Y(A,m.bind);if(g.status!=="value")p(u,m.bind,g.status);else if(!Z(u))p(u,m.bind,"refused-element");else{let y=u.localName==="img";if(y?(f(u,"src"),f(u,"srcset"),f(u,"hidden")):a(u),W(g.value))y?K(u,!1):u.textContent="",p(u,m.bind,"empty");else{let b;if(y)b=bt(u,g.value,s,m.width);else if(m.format==="markdown")b=ht(u,g.value,s);else if(m.format==="text")b=Oe(u,g.value);else{let R=At(g.value,m.format,u.closest("[lang]")?.getAttribute("lang")??void 0);b=R===null?"type":Oe(u,R)}b?p(u,m.bind,b):d++}}}for(let[g,y]of m.attrs){let b=Y(A,y);if(b.status!=="value"){p(u,y,b.status);continue}if(f(u,g),W(b.value)){g==="alt"?u.setAttribute("alt",""):u.removeAttribute(g),p(u,y,"empty");continue}let R=u.getAttribute(g),v=wt(u,g,b.value,s,m.width);v?(R===null&&u.removeAttribute(g),p(u,y,v)):d++}for(let[g,y]of[[m.show,!0],[m.hide,!1]]){if(g===void 0)continue;let b=Y(A,g);if(b.status!=="value"){p(u,g,b.status);continue}f(u,"hidden");let R=!W(b.value)&&b.value!==!1;K(u,R===y),d++}if(m.link!==void 0){let g=[],y=it(m.link,s.base,b=>{let R=Y(A,b.join("."));if(R.status==="value")return R.value;g.push(R.status)});if(g[0])p(u,m.link,g[0]);else if(!y.ok)p(u,y.path,y.reason);else{f(u,"href");let b=kt(u,y.url);b?p(u,m.link,b):d++}}}function T(u,m,A){let g=Rt(u);if(!g){p(u,"template","unsupported");return}a(u);let y=[g];for(let R of Array.from(u.children))R.hasAttribute(`${w}empty`)&&(f(R,"hidden"),K(R,m.length===0),y.push(R));let b=[];for(let R of m.slice(0,rn)){let v=g.content.cloneNode(!0);x(le(v,!1),{kind:"record",source:R},A+1),b.push(...Array.from(v.childNodes))}u.replaceChildren(...y,...b),d++}function x(u,m,A){i.push(...u.issues);for(let{el:g,d:y}of u.bound)k(g,y,m);for(let g of u.eaches){let y=Y(m,g.path);if(y.status!=="value"){p(g.el,g.path,y.status);continue}if(A>=nn){p(g.el,g.path,"unsupported");continue}let b=y.value,R=Array.isArray(b)?b:W(b)?[]:[b];if(R.some(v=>v===null||typeof v!="object"||Array.isArray(v))){p(g.el,g.path,"type");continue}R.length===0&&p(g.el,g.path,"empty"),T(g.el,R.map(v=>({data:v})),A)}}async function M(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 j&&A.status===404&&A.code!=="RELEASE_NOT_FOUND"?{status:"missing"}:{status:"failed"}}}let S=null,C="",O=null,q,Ue=!1,Q=0,Ne=-1,xt=3,Pt=typeof e.live=="number"?e.live:void 0;function Ie(){o(),S={state:"fallback",release:null,preview:!1,bound:0,issues:[]},t.setAttribute(`${w}state`,"fallback"),t.removeAttribute(`${w}preview`)}function De(){Q++,clearTimeout(q),S?.preview&&Ie()}function Mt(){clearTimeout(q);let u=_();if(!u||Ue)return;let m=Date.parse(u.expiresAt)-Date.now();q=setTimeout(()=>{_()===null&&De(),de()},Math.min(Math.max(m,0)+1,2147483647))}async function Fe(u,m=1){let A=S===null;A&&t.setAttribute(`${w}state`,"loading");let g=Q,y=await r.isPreview().catch(()=>!1),b=y?_()?.draftToken??null:null;if(S!==null&&S.preview&&!y&&Ie(),!y&&u!==void 0&&S!==null&&S.state==="ready"&&!S.preview&&S.release!==null&&u<=S.release)return S;let R=h=>{S=h,Ne=g,t.setAttribute(`${w}state`,h.state),t.toggleAttribute(`${w}preview`,h.preview);let E=h.issues.filter(U=>U.reason!=="empty"),$=E.map(U=>`${U.path} (${U.reason}) on ${U.on}`).join("; ");return $!==C&&(C=$,E.length>0&&console.warn(`[mapled] ${E.length===1?"1 place":`${E.length} places`} couldn't be filled: ${$}`)),n.dispatchEvent(new CustomEvent(A?"mapled:ready":"mapled:update",{detail:h})),h.preview?Mt():(clearTimeout(q),e.live&&O===null&&(O=r.watch(U=>{de(U.release)},{interval:Pt}))),h},v=null;if(!y)if(u!==void 0)v=u;else try{let h=await r.getRelease();if(h===null)return o(),R({state:"unpublished",release:null,preview:y,bound:0,issues:[]});v=h.release}catch{return S!==null&&S.state==="ready"?S:R({state:"fallback",release:null,preview:y,bound:0,issues:[]})}let L=v===null?{}:{release:v},I=le(t,!0),ze=$e(t,I.issues);ze&&I.bound.unshift({el:t,d:ze});let fe=new Map,pe=new Map,ee=new Map,te=[];for(let[h,E]of vt(I))te.push(M(()=>r.getSingle(h,{...L,...E.size>0?{expand:[...E]}:{}}),J).then($=>fe.set(h,$)));for(let h of I.lists){let E=Rt(h.el),$=E?[...vt(le(E.content,!1)).keys()]:[];te.push(M(async()=>(await r.getRecords(h.collection,{...h.options,...L,...$.length>0?{fields:$}:{}})).records,on).then(U=>pe.set(h,U)))}for(let h of I.records){if(h.slug===null){ee.set(h,{status:"missing"});continue}let E=h.slug;te.push(M(()=>r.getRecordBySlug(h.collection,E,{...L,...h.expand.length>0?{expand:h.expand}:{}}),J).then($=>ee.set(h,$)))}await Promise.all(te);let He=await r.isPreview().catch(()=>!1)?_()?.draftToken??null:null;if(g!==Q||He!==b)return m<xt?Fe(u,m+1):(o(),R({state:"fallback",release:null,preview:He!==null,bound:0,issues:[]}));let We=[...fe.values(),...pe.values(),...ee.values()];if(We.length>0&&We.every(h=>h.status==="failed"&&!h.answered))return S!==null&&S.state==="ready"?S:R({state:"fallback",release:v,preview:y,bound:0,issues:[]});o(),i=[],d=0,x(I,{kind:"page",singles:fe},0);for(let h of I.lists){let E=pe.get(h);E?.status==="ok"?T(h.el,E.value,0):p(h.el,h.collection,E?.status??"failed")}for(let h of I.records){let E=ee.get(h);E?.status==="ok"?x(h.block,{kind:"record",source:E.value},0):p(h.el,h.collection,E?.status??"failed")}return Ke(),R({state:"ready",release:v,preview:y,bound:d,issues:i})}let _e=new WeakSet,je=new WeakSet;function Ke(){for(let u of Array.from(t.querySelectorAll(`form[${w}form]`)))_e.has(u)||(_e.add(u),u.addEventListener("submit",m=>{m.preventDefault(),Ot(u)}))}async function Ot(u){if(u.getAttribute(`${w}form-state`)==="sending")return;let m=(u.getAttribute(`${w}form`)??"").trim(),A=u.querySelector(`[${w}form-sent]`),g=u.querySelector(`[${w}form-failed]`),y=Array.from(u.querySelectorAll("button, input[type=submit]")).filter(L=>!L.disabled);u.setAttribute(`${w}form-state`,"sending");for(let L of y)L.disabled=!0;let b=u.querySelector(`input[type=checkbox][${w}consent]`),R=b?.checked?(b.labels?.[0]?.textContent??b.closest("label")?.textContent??"").trim().slice(0,500):"",v=Ce.test(m)?await we(m,u,{key:e.key,apiUrl:e.apiUrl,fetch:e.fetch,...R?{consent:R}:{}}):{ok:!1,status:0,code:"BAD_FORM",message:"This form isn't set up yet."};for(let L of y)L.disabled=!1;u.setAttribute(`${w}form-state`,v.ok?"sent":"failed"),v.ok&&u.reset(),A&&K(A,v.ok),g&&(!v.ok&&(je.has(g)||(g.textContent??"").trim()==="")&&(je.add(g),g.textContent=v.message),K(g,!v.ok)),n.dispatchEvent(new CustomEvent("mapled:form",{detail:v.ok?{form:m,ok:!0}:{form:m,ok:!1,status:v.status,code:v.code}}))}Ke();let ce=async u=>{try{return await Fe(u)}catch(m){return console.error(`[mapled] ${m instanceof Error?m.message:"The page couldn't be filled."}`),S??{state:"fallback",release:null,preview:!1,bound:0,issues:[]}}},z=ce(),de=u=>z=z.then(()=>ce(u)),Be=()=>{ye(),De();let u=Q;return z=z.then(()=>Ne>=u&&S!==null?S:ce())},qe=u=>{u.target?.closest?.(`[${w}end-preview]`)&&(u.preventDefault(),Be())};return n.addEventListener("click",qe),{ready:z,get report(){return S},refresh:()=>de(),endPreview:Be,stop:()=>{Ue=!0,O?.(),O=()=>{},clearTimeout(q),n.removeEventListener("click",qe)},client:r}}var Tt="0.1.1";var Et={version:Tt,mount:Le,page:null};window.Mapled=Et;var B=document.currentScript;if(B instanceof HTMLScriptElement&&B.hasAttribute("data-mapled-key")){let e=B.getAttribute("data-mapled-live"),t=e===null?NaN:parseInt(e,10),n=()=>{try{Et.page=Le({key:B.getAttribute("data-mapled-key")??"",...B.getAttribute("data-mapled-api")?{apiUrl:B.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-2vgQ4g9re/eiYxEa/H3WKpWI5C9L4wCB/62c8Kq+LOfZDzmqc2ozqMGC8mZoOUqk
|
|
@@ -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;
|