@obelism/content 0.7.0 → 0.9.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 +42 -6
- package/dist/bin/content.js +2 -1
- package/dist/bridge.d.mts +1 -1
- package/dist/bridge.d.ts +1 -1
- package/dist/bridge.global.js +1 -1
- package/dist/bridge.js +1 -1
- package/dist/bridge.mjs +1 -1
- package/dist/index.d.mts +43 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +43 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +111 -6
- package/dist/index.mjs +110 -7
- package/dist/{types-nyvor49r.d.ts → types-fxvuz29w.d.ts} +18 -2
- package/dist/types-fxvuz29w.d.ts.map +1 -0
- package/dist/webhooks.d.mts +2 -0
- package/dist/webhooks.d.mts.map +1 -1
- package/dist/webhooks.d.ts +2 -0
- package/dist/webhooks.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/types-nyvor49r.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -22,6 +22,11 @@ const content = new ContentClient({
|
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
await content.getPage('blog/hello') // Entry | null
|
|
25
|
+
await content.getPage('blog/hallo', { locale: 'nl' }) // looked up in nl's paths
|
|
26
|
+
await content.findPage('blog/old') // { entry } | { redirect: { path } } | null
|
|
27
|
+
await content.getRedirects({ locale: 'nl' }) // { items: [{ locale, from, to }], total, limit, skip }
|
|
28
|
+
for await (const redirect of content.getAllRedirects()) {
|
|
29
|
+
}
|
|
25
30
|
await content.getEntry('entry_…') // Entry | null
|
|
26
31
|
await content.getEntries({ contentType: 'post', limit: 20, skip: 0 }) // { items, total, limit, skip }
|
|
27
32
|
for await (const post of content.getAllEntries({ contentType: 'post' })) {
|
|
@@ -39,7 +44,17 @@ await content.getContentModel() // the model file
|
|
|
39
44
|
```
|
|
40
45
|
|
|
41
46
|
Each method takes `{ locale, depth, richText, init }` to override the
|
|
42
|
-
defaults.
|
|
47
|
+
defaults.
|
|
48
|
+
|
|
49
|
+
Pages and folders can have a slug per locale. `getPage` looks the path up
|
|
50
|
+
in the requested locale's paths, `entry.path` is the path in that locale and
|
|
51
|
+
`entry.paths` the path in every locale (`{ en: 'about-us', nl: 'over-ons' }`),
|
|
52
|
+
for hreflang links and language switchers. The locale is never part of a
|
|
53
|
+
path: whether it is `/nl/over-ons` or `nl.example.com/over-ons` is your
|
|
54
|
+
routing. A page that moved keeps its old path as a redirect in each locale:
|
|
55
|
+
`findPage` answers it with `{ redirect: { path } }`, so your route can send
|
|
56
|
+
a `301` instead of a 404, and `getAllRedirects` lists them for redirect rules
|
|
57
|
+
built ahead of time. Missing pages and entries are `null`; any other failure throws a
|
|
43
58
|
`ContentApiError` with `status` and the API's `message`. Type your content
|
|
44
59
|
with generics: `content.getPage<Entry<{ title: string }>>('home')`.
|
|
45
60
|
|
|
@@ -131,11 +146,12 @@ bridge.undo()
|
|
|
131
146
|
On a site signed in to edit (`@obelism/content/site`), the box also has **New
|
|
132
147
|
page**: a path (`/pricing`, or under an existing page) and what the page is
|
|
133
148
|
for. The CMS writes the page like the one the editor is on and stores it as a
|
|
134
|
-
draft, and the bridge opens it at `/` + its path.
|
|
135
|
-
|
|
149
|
+
draft, and the bridge opens it at `/` + its path. The path is the one in
|
|
150
|
+
the page's language (`<html lang>`). When your routes put CMS pages
|
|
151
|
+
elsewhere, say where:
|
|
136
152
|
|
|
137
153
|
```ts
|
|
138
|
-
getContentBridge({ pagePath: (path) =>
|
|
154
|
+
getContentBridge({ pagePath: (path, locale) => `/${locale}/${path}` })
|
|
139
155
|
await bridge.createPage('/pricing', 'Our plans, for agencies') // or your own UI
|
|
140
156
|
```
|
|
141
157
|
|
|
@@ -196,7 +212,7 @@ const event = await verifyWebhook(request, process.env.CONTENT_WEBHOOK_SECRET!)
|
|
|
196
212
|
|
|
197
213
|
`WebhookEvent` is a union on `type`: `entry.published`, `entry.unpublished`,
|
|
198
214
|
`entry.deleted` and `entry.moved` (`data.entry`: `{ id, contentType, name,
|
|
199
|
-
path }`, plus `previousPath` when moved), `asset.updated` (`data.asset`,
|
|
215
|
+
path, paths }`, plus `previousPath` and `previousPaths` when moved), `asset.updated` (`data.asset`,
|
|
200
216
|
`data.entries`), `model.changed` (`data.contentTypes`: `{ apiId, action }`),
|
|
201
217
|
`locales.changed` (`data.defaultLocale`, `data.locales`) and `webhook.test`.
|
|
202
218
|
|
|
@@ -225,7 +241,27 @@ bridge.on('change', ({ stats, statsVisible }) => …) // stats[uid].rate, 0–1
|
|
|
225
241
|
## Helpers
|
|
226
242
|
|
|
227
243
|
- `richTextToHtml(doc, { renderEntry?, renderAsset? })`, `richTextToPlainText(doc)`
|
|
228
|
-
- `imageUrl(asset, { w, h, fit, format, q })`, `imageSrcSet(asset, [480, 960])
|
|
244
|
+
- `imageUrl(asset, { w, h, fit, format, q, focus })`, `imageSrcSet(asset, [480, 960])`:
|
|
245
|
+
with `fit: 'cover'` or `'crop'` the crop keeps the asset's `focalPoint` (set
|
|
246
|
+
in the CMS under Media) in view, or the `focus` you pass (`{ x, y }` or
|
|
247
|
+
`'auto'`)
|
|
248
|
+
- `alternateLinks(entry, { url, defaultLocale })`: every locale's URL of a page
|
|
249
|
+
(`entry.paths`), plus `x-default`, for `<link rel="alternate" hreflang>`
|
|
250
|
+
- `sitemapXml(entries, { url, defaultLocale, exclude? })`: a sitemap with one
|
|
251
|
+
`<url>` per page and locale and its alternates; pages with "Hide from search
|
|
252
|
+
engines" (`fields.noIndex`) are left out. `url(path, locale)` decides where
|
|
253
|
+
a locale's path lives on your site:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const pages = []
|
|
257
|
+
for await (const page of content.getAllEntries({ contentType: 'page' })) {
|
|
258
|
+
pages.push(page)
|
|
259
|
+
}
|
|
260
|
+
const xml = sitemapXml(pages, {
|
|
261
|
+
url: (path, locale) => `https://example.com/${locale}/${path}`,
|
|
262
|
+
defaultLocale: 'en',
|
|
263
|
+
})
|
|
264
|
+
```
|
|
229
265
|
|
|
230
266
|
## A/B tests and feature flags
|
|
231
267
|
|
package/dist/bin/content.js
CHANGED
|
@@ -25,7 +25,8 @@ const exampleAsset = (mimeTypes)=>{
|
|
|
25
25
|
width: image ? 1600 : null,
|
|
26
26
|
height: image ? 900 : null,
|
|
27
27
|
title: null,
|
|
28
|
-
alt: image ? 'An example image' : null
|
|
28
|
+
alt: image ? 'An example image' : null,
|
|
29
|
+
focalPoint: null
|
|
29
30
|
};
|
|
30
31
|
};
|
|
31
32
|
const exampleRichText = (name)=>({
|
package/dist/bridge.d.mts
CHANGED
|
@@ -112,7 +112,7 @@ declare const mountPromptBox: (doc: Document, bridge: ContentBridge, options?: P
|
|
|
112
112
|
type BridgeOptions = {
|
|
113
113
|
url?: string | undefined;
|
|
114
114
|
prompt?: boolean | PromptBoxOptions | undefined;
|
|
115
|
-
pagePath?: ((path: string) => string) | undefined;
|
|
115
|
+
pagePath?: ((path: string, locale: string) => string) | undefined;
|
|
116
116
|
};
|
|
117
117
|
declare const peekContentBridge: () => ContentBridge | null;
|
|
118
118
|
declare const getContentBridge: ({ url, prompt, pagePath, }?: BridgeOptions) => ContentBridge;
|
package/dist/bridge.d.ts
CHANGED
|
@@ -112,7 +112,7 @@ declare const mountPromptBox: (doc: Document, bridge: ContentBridge, options?: P
|
|
|
112
112
|
type BridgeOptions = {
|
|
113
113
|
url?: string | undefined;
|
|
114
114
|
prompt?: boolean | PromptBoxOptions | undefined;
|
|
115
|
-
pagePath?: ((path: string) => string) | undefined;
|
|
115
|
+
pagePath?: ((path: string, locale: string) => string) | undefined;
|
|
116
116
|
};
|
|
117
117
|
declare const peekContentBridge: () => ContentBridge | null;
|
|
118
118
|
declare const getContentBridge: ({ url, prompt, pagePath, }?: BridgeOptions) => ContentBridge;
|
package/dist/bridge.global.js
CHANGED
|
@@ -179,4 +179,4 @@ button.chip:disabled { opacity: .5; }
|
|
|
179
179
|
background: rgb(var(--theme-accent-rgb)); margin-right: 8px;
|
|
180
180
|
}
|
|
181
181
|
[hidden] { display: none !important; }
|
|
182
|
-
`,x=(e,t,r={},...n)=>{let o=e.createElement(t);for(let t of(Object.assign(o,r),n))o.appendChild("string"==typeof t?e.createTextNode(t):t);return o},w=(e,t)=>{let r=x(e,"span",{className:"icon"});return r.setAttribute("aria-hidden","true"),r.innerHTML=`<svg viewBox="0 0 24 24">${v[t]}</svg>`,r},k=(e,t,r={})=>{let n={...f,...r.labels},o=x(e,"div");o.setAttribute("data-obelism-content-prompt","");let a=o.attachShadow({mode:"open"}),i="site"===t.mode,s=x(e,"span",{textContent:n.open}),l=x(e,"button",{className:"pill",type:"button"},s,x(e,"span",{className:"mark",textContent:"⁜"})),d=x(e,"span",{className:"pill compact"},`${n.viewingDrafts} \xb7 `),c=x(e,"button",{className:"link",type:"button",textContent:n.signOut});d.appendChild(c);let p=x(e,"p",{className:"status"});p.setAttribute("role","status");let g=x(e,"textarea",{placeholder:n.placeholder,rows:1});g.setAttribute("aria-label",n.open);let u=w(e,"page"),h=w(e,"block"),b=x(e,"span"),m=x(e,"button",{type:"button",title:n.clearBlock});m.setAttribute("aria-label",n.clearBlock),m.appendChild(w(e,"close"));let v=x(e,"span",{className:"chip scope"},u,h,b,m),k=x(e,"button",{className:"action chip",type:"button"},w(e,"undo"),n.undo),E=x(e,"button",{className:"action chip",type:"button"},w(e,"newPage"),n.newPage),S=x(e,"button",{type:"button",title:n.newPageCancel});S.setAttribute("aria-label",n.newPageCancel),S.appendChild(w(e,"close"));let C=x(e,"span",{className:"chip scope"},w(e,"newPage"),x(e,"span",{textContent:n.newPage}),S),N=x(e,"input",{type:"text",placeholder:n.pathPlaceholder,autocomplete:"off",spellcheck:!1}),L=x(e,"label",{className:"path"},x(e,"span",{textContent:n.path}),N),U=x(e,"button",{className:"close icon-button",type:"button",title:n.close},w(e,"close"));U.setAttribute("aria-label",n.close);let P=x(e,"button",{className:"action primary icon-button",type:"submit"},x(e,"span",{className:"visually-hidden",textContent:n.send}),w(e,"send"));P.title=n.send;let $=x(e,"span",{className:"unsaved"});$.appendChild(x(e,"span",{className:"dot"})),$.appendChild(e.createTextNode(n.unsaved));let A=x(e,"button",{className:"link",type:"button",textContent:n.signOut}),O=x(e,"button",{className:"action chip",type:"button",textContent:n.save}),I=x(e,"button",{className:"action chip",type:"button",textContent:n.publish}),M=x(e,"div",{className:"footer"},$,x(e,"span",{className:"spacer"}),A,O,I),j=x(e,"form",{className:"panel"},p,L,g,x(e,"div",{className:"toolbar"},v,C,k,E,x(e,"span",{className:"spacer"}),U,x(e,"span",{className:"divider"}),P),M),z=x(e,"div",{className:"root"},x(e,"div",{className:"bar"},l,d),j);a.append(x(e,"style",{textContent:y}),z);let R=!1,B=!1,D=null,T=e=>{let r=e.features.includes("prompt"),o=e.saving||e.publishing,a=e.prompting||o;z.hidden=!r&&!i,l.hidden=R||!r,d.hidden=r||!i,j.hidden=!R||!r;let c=i&&e.features.includes("create"),f=i&&null===t.entryId,y=f&&c;c||(B=!1),y&&(B=!0),s.textContent=y?n.newPage:n.open,S.hidden=y,v.hidden=B,C.hidden=!B,E.hidden=B||!c,E.disabled=a,L.hidden=!B,N.disabled=a,g.placeholder=B?n.newPagePlaceholder:n.placeholder,b.textContent=e.selectedUid?n.scopeBlock:n.scopeEntry,m.hidden=!e.selectedUid,u.hidden=!!e.selectedUid,h.hidden=!e.selectedUid,k.hidden=B||!e.canUndo,k.disabled=a,P.disabled=a||""===g.value.trim()||B&&""===N.value.trim(),g.disabled=a,O.hidden=!e.features.includes("save")||f,O.disabled=a||!e.unsaved,O.textContent=e.saving?n.saving:n.save,I.hidden=!e.features.includes("publish")||f,I.disabled=a,I.textContent=e.publishing?n.publishing:n.publish,A.hidden=!i,M.hidden=!i,$.hidden=!e.unsaved;let x=e.prompting?B?n.creating:n.working:D?.text??(!i||B||e.selectedUid?"":n.selectHint);p.textContent=x,p.hidden=!x,p.className=`status${e.prompting?" working":""}${!a&&D?.error?" error":""}`},q=()=>T(t.getState()),V=()=>{g.style.height="auto",g.style.height=`${g.scrollHeight}px`},H=async()=>{let e=g.value.trim();if(e&&!t.getState().prompting){D=null;try{if(B){let r=N.value.trim();if(!r)return N.focus();await t.createPage(r,e),D={text:n.created,error:!1}}else{let{summary:r}=await t.prompt(e);D={text:r,error:!1}}g.value="",V()}catch(e){D={text:e instanceof Error?e.message:String(e),error:!0}}q(),g.focus()}},F=e=>{R=e,q(),i&&t.setSelecting(R&&!B)},Y=e=>{B=e,D=null,t.setSelecting(R&&!B),B&&t.select(null),q(),(B?N:g).focus()},_=async e=>{D=null;try{await t[e](),D={text:"save"===e?n.saved:n.published,error:!1}}catch(e){D={text:e instanceof Error?e.message:String(e),error:!0}}q()};l.addEventListener("click",()=>{F(!0),(B?N:g).focus()}),U.addEventListener("click",()=>F(!1)),O.addEventListener("click",()=>void _("save")),I.addEventListener("click",()=>void _("publish")),A.addEventListener("click",()=>t.signOut()),c.addEventListener("click",()=>t.signOut()),m.addEventListener("click",()=>t.select(null)),E.addEventListener("click",()=>Y(!0)),S.addEventListener("click",()=>Y(!1)),N.addEventListener("input",q),N.addEventListener("keydown",e=>{"Enter"!==e.key||e.isComposing?"Escape"===e.key&&F(!1):(e.preventDefault(),g.focus())}),k.addEventListener("click",()=>{D=null,t.undo()}),j.addEventListener("submit",e=>{e.preventDefault(),H()}),g.addEventListener("input",()=>{V(),q()}),g.addEventListener("keydown",e=>{"Enter"!==e.key||e.shiftKey||e.isComposing?"Escape"===e.key&&F(!1):(e.preventDefault(),H())});let W=t.on("change",T);return q(),e.body.appendChild(o),()=>{W(),o.remove()}},E="https://content.obelism.studio",S=(e,t)=>JSON.stringify(e)===JSON.stringify(t),C=Symbol.for("@obelism/content/bridge"),N=m({isEditing:!1,entryId:null,on:()=>{}},new URLSearchParams),L=({url:e,prompt:l=!0,pagePath:g}={})=>{let f;if("u"<typeof window)return N;let v=window;if(v[C])return v[C];if(v.ObelismBridge)f=m(v.ObelismBridge,new URLSearchParams(v.location.search));else{let m=(f=((e,r)=>{let l=new URLSearchParams(e.location.search),g=e.parent!==e&&l.has("cms_preview"),m=p(),f=[],v={...c,selecting:!0},y=e=>{v={...v,...e},m.emit("change",v)},x={isEditing:g,mode:g?"editor":null,entryId:l.get("cms_entry"),environment:l.get(n),on:m.on,getState:()=>v,...u,destroy:()=>{f.splice(0).forEach(e=>e())}};if(!g)return x;let w=e.document,k=(t,n={})=>{e.parent.postMessage({source:"obelism-content-bridge",type:t,...n},r)},E=w.createElement("style");E.textContent=d(),w.head.appendChild(E),f.push(()=>E.remove());let S=(e,t)=>{if(w.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),v.selectedUid!==(e??null)&&y({selectedUid:e??null}),!e)return;let r=w.querySelector(b(e));r&&(r.setAttribute("data-cms-selected",""),t&&r.scrollIntoView({behavior:"smooth",block:"center"}))},C=e=>{let r=e.target,n=r?.closest?.(`[${t}]`);if(!n)return;e.preventDefault(),e.stopPropagation();let o=n.getAttribute(t);S(o,!1),k("select",{uid:o})};w.addEventListener("click",C,!0),f.push(()=>w.removeEventListener("click",C,!0));let N=()=>k("ready",{url:e.location.href}),L=new Map,U=0,P=e=>{let t=L.get(e);return t?(clearTimeout(t.timer),L.delete(e),0===L.size&&y({prompting:!1}),t):null};x.prompt=(e,t={})=>{let r=e.trim();if(!r)return Promise.reject(Error("Say what to change"));if(!v.features.includes("prompt"))return Promise.reject(Error("The editor does not offer prompts here"));let n=`prompt_${++U}`,o=void 0===t.uid?v.selectedUid:t.uid;return new Promise((e,t)=>{let a=setTimeout(()=>{P(n)?.reject(Error("The editor did not answer in time"))},12e4);L.set(n,{resolve:e,reject:t,timer:a}),y({prompting:!0}),k("prompt",{id:n,prompt:r,uid:o})})},x.undo=()=>k("undo"),x.select=e=>{S(e,!1),k("select",{uid:e})};let $=null;f.push(()=>$?.destroy()),f.push(()=>{for(let e of L.keys())P(e)?.reject(Error("The bridge was destroyed"))});let A=t=>{if(t.origin!==r||t.source!==e.parent)return;let n=t.data;if(n&&"obelism-content-editor"===n.source){if("connect"===n.type)N();else if("update"===n.type&&n.entry)m.emit("update",{entry:n.entry,selectedUid:n.selectedUid??null}),e.requestAnimationFrame(()=>S(n.selectedUid,!1)),$?.refresh();else if("select"===n.type)S(n.uid,!0);else if("saved"===n.type||"published"===n.type)m.emit(n.type,{}),0===m.listeners.update.size&&e.location.reload();else if("features"===n.type&&Array.isArray(n.features))y({features:n.features.filter(e=>"string"==typeof e)});else if("stats"===n.type)y({stats:(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return{};let t={};for(let[r,n]of Object.entries(e)){let{clicks:e,clickers:o,viewers:a,rate:i}=n??{};h(e)&&h(o)&&h(a)&&h(i)&&(t[r]={clicks:e,clickers:o,viewers:a,rate:i})}return t})(n.stats),statsVisible:!1!==n.visible}),($||Object.keys(v.stats).length)&&($??($=((e,{selector:t,onSelect:r,labels:n})=>{let l=e.document,d={...o,...n},c=l.createElement("div");c.setAttribute("data-obelism-stats","");let p=c.attachShadow({mode:"open"}),g=l.createElement("style");g.textContent=i;let u=l.createElement("div");u.className="layer",p.append(g,u),l.body.appendChild(c);let h={},b=!1,m=null,f=()=>{if(m=null,u.replaceChildren(),u.hidden=!b,!b)return;let n=e.innerHeight;for(let[o,i]of Object.entries(h)){let c=l.querySelector(t(o));if(!c)continue;let p=c.getBoundingClientRect();if(!p.width&&!p.height||p.bottom<0||p.top>n)continue;let g=l.createElement("button");g.type="button",g.className="badge",g.dataset.uid=o,g.textContent=a(d.clicked,{rate:Math.round(100*i.rate)}),g.title=a(d.title,{clickers:i.clickers,viewers:i.viewers,clicks:i.clicks});let{place:h,top:b,left:m}=s(p,e.innerWidth);g.dataset.place=h,g.style.top=`${b}px`,g.style.left=`${m}px`,g.addEventListener("click",()=>r(o)),u.appendChild(g)}},v=()=>{null===m&&b&&(m=e.requestAnimationFrame(f))};e.addEventListener("scroll",v,{capture:!0,passive:!0}),e.addEventListener("resize",v);let y=e.MutationObserver,x="function"==typeof y?new y(v):null;return x?.observe(l.body,{childList:!0,subtree:!0}),{update(t,r){h=t,b=r,null!==m&&(e.cancelAnimationFrame?.(m),m=null),f()},refresh:v,destroy(){null!==m&&e.cancelAnimationFrame?.(m),e.removeEventListener("scroll",v,{capture:!0}),e.removeEventListener("resize",v),x?.disconnect(),c.remove()}}})(e,{selector:b,onSelect:e=>x.select(e)})),$.update(v.stats,v.statsVisible));else if("promptState"===n.type)y({canUndo:!0===n.canUndo});else if("promptResult"===n.type&&"string"==typeof n.id){let e=P(n.id);if(!e)return;!0===n.ok?e.resolve({summary:"string"==typeof n.summary?n.summary:""}):e.reject(Error("string"==typeof n.error?n.error:"The prompt failed"))}}};return e.addEventListener("message",A),f.push(()=>e.removeEventListener("message",A)),"loading"===w.readyState?(w.addEventListener("DOMContentLoaded",N,{once:!0}),f.push(()=>w.removeEventListener("DOMContentLoaded",N))):N(),x})(v,new URL(e||E).origin)).isEditing||v.parent!==v?null:(e=>{for(let t of e.cookie.split(";")){let[e,...r]=t.trim().split("=");if("obelism_content_editing"===e)try{let e=decodeURIComponent(r.join("="));return e.startsWith("/")&&!e.startsWith("//")?e:null}catch{break}}return null})(v.document);if(m&&(f=((e,n,{pagePath:o=e=>`/${e}`}={})=>{let a=e.document,i=p(),s=[],l=new Map,g=[],u=new Set,h=null,m=c,f=e=>{m={...m,...e},i.emit("change",m)},v=()=>f({unsaved:[...l.values()].some(({content:e,saved:t})=>!S(e,t)),canUndo:g.length>0}),y=async(t,r)=>{let o=await e.fetch(`${n}/api/${t}`,void 0===r?{credentials:"same-origin"}:{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","x-obelism-content":"1"},body:JSON.stringify(r)}),a=await o.json().catch(()=>({}));if(401===o.status)throw f({features:[]}),Error("You were signed out. Sign in again to edit.");if(!o.ok)throw Error("string"==typeof a.error?a.error:`Obelism Content answered ${o.status}`);return a},x=()=>a.querySelector(`[${r}]`)?.getAttribute(r)??null,w=()=>{if(!h)return;let e=h.space.locales.map(({code:e})=>e),t=a.documentElement.lang;if(e.includes(t))return t;let r=t.split("-")[0]??"";return e.includes(r)?r:h.space.defaultLocale},k=async e=>{let t=l.get(e);if(t)return t;let r=await y(`entries/${encodeURIComponent(e)}`),n={content:r.content,saved:r.content,revision:r.draftRevision};return l.set(e,n),n},E=e=>{a.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),m.selectedUid!==e&&f({selectedUid:e}),e&&a.querySelector(b(e))?.setAttribute("data-cms-selected","")},C=async t=>{if(0===i.listeners.update.size)return;let r=l.get(t)?.content,{entry:n}=await y(`entries/${encodeURIComponent(t)}/preview`,{content:r,locale:w()});i.emit("update",{entry:n,selectedUid:m.selectedUid}),e.requestAnimationFrame(()=>E(m.selectedUid))},N=t=>{i.emit(t,{}),0===i.listeners.update.size&&e.location.reload()},L=async()=>{for(let[e,t]of l){if(S(t.content,t.saved))continue;let{draftRevision:r}=await y(`entries/${encodeURIComponent(e)}/save`,{content:t.content,baseRevision:t.revision});t.saved=t.content,t.revision=r,u.add(e)}g.length=0,v()},U={isEditing:!0,mode:"site",get entryId(){return x()},environment:null,on:i.on,getState:()=>m,async prompt(e,t={}){let n,o=e.trim();if(!o)throw Error("Say what to change");if(!m.features.includes("prompt"))throw Error("You may not edit content here");let i=void 0===t.uid?m.selectedUid:t.uid,s=(n=i?a.querySelector(b(i)):null,n?.closest(`[${r}]`)?.getAttribute(r)??x());if(!s)throw Error("Nothing on this page is marked as an entry: add editable(entry)");f({prompting:!0});try{let e=await k(s),t=await y(`entries/${encodeURIComponent(s)}/prompt`,{prompt:o,content:e.content,locale:w(),uid:i});return g.push({entryId:s,content:e.content}),e.content=t.content,v(),await C(s),{summary:t.summary}}finally{f({prompting:!1})}},async createPage(t,r){let n=r.trim();if(!n)throw Error("Say what the page is for");if(!t.trim())throw Error("Say where the page goes, like /pricing");if(!m.features.includes("create"))throw Error("You may not add pages here");if(m.unsaved)throw Error("Save or undo your changes before adding a page");f({prompting:!0});try{let r=await y("entries",{path:t,prompt:n,locale:w(),from:x()}),a=o(r.path);return e.location.assign(a),{summary:r.summary,entryId:r.entryId,path:r.path,url:a}}finally{f({prompting:!1})}},undo(){let e=g.pop(),t=e&&l.get(e.entryId);e&&t&&(t.content=e.content,v(),C(e.entryId).catch(e=>console.error("[@obelism/content bridge]",e)))},async save(){if(!m.features.includes("save"))throw Error("You may not edit content here");f({saving:!0});try{await L()}finally{f({saving:!1})}N("saved")},async publish(){if(!m.features.includes("publish"))throw Error("You may not publish here");f({publishing:!0});try{await L();let e=x(),t=u.size>0?[...u]:e?[e]:[];if(0===t.length)throw Error("Nothing on this page is marked as an entry: add editable(entry)");for(let e of t)await y(`entries/${encodeURIComponent(e)}/publish`,{}),u.delete(e)}finally{f({publishing:!1})}N("published")},signOut(){if(m.unsaved&&!e.confirm("Sign out and lose the changes you have not saved?"))return;l.clear(),v();let t=e.location.pathname+e.location.search;e.location.href=`${n}/logout?return_to=${encodeURIComponent(t)}`},select(e){E(e)},setSelecting(e){e?a.documentElement.setAttribute("data-cms-selecting",""):a.documentElement.removeAttribute("data-cms-selecting"),f({selecting:e})},destroy(){s.splice(0).forEach(e=>e())}},P=a.createElement("style");P.textContent=d("html[data-cms-selecting] "),a.head.appendChild(P),s.push(()=>P.remove()),s.push(()=>a.documentElement.removeAttribute("data-cms-selecting"));let $=e=>{if(!m.selecting)return;let r=e.target,n=r?.closest?.(`[${t}]`);n&&(e.preventDefault(),e.stopPropagation(),E(n.getAttribute(t)))};a.addEventListener("click",$,!0),s.push(()=>a.removeEventListener("click",$,!0));let A=e=>{m.unsaved&&(e.preventDefault(),e.returnValue="")};return e.addEventListener("beforeunload",A),s.push(()=>e.removeEventListener("beforeunload",A)),y("session").then(e=>{h=e,f({features:e.features})}).catch(e=>console.error("[@obelism/content bridge]",e)),U})(v,m,{pagePath:g})),f.isEditing&&l){let e=k(v.document,f,!0===l?{}:l),t=f.destroy;f.destroy=()=>{e(),t()}}}return v[C]=f,f};(e=window).ObelismBridge=L((e=>{let t=e?.dataset.url,r=t||E;if(!t&&e?.src){let t=new URL(e.src);"/bridge.js"===t.pathname&&(r=t.origin)}return{url:r,prompt:e?.dataset.prompt!=="false"}})(document.currentScript)),e.ObelismContent={getContentBridge:L,mountPromptBox:k}}();
|
|
182
|
+
`,x=(e,t,r={},...n)=>{let o=e.createElement(t);for(let t of(Object.assign(o,r),n))o.appendChild("string"==typeof t?e.createTextNode(t):t);return o},w=(e,t)=>{let r=x(e,"span",{className:"icon"});return r.setAttribute("aria-hidden","true"),r.innerHTML=`<svg viewBox="0 0 24 24">${v[t]}</svg>`,r},k=(e,t,r={})=>{let n={...f,...r.labels},o=x(e,"div");o.setAttribute("data-obelism-content-prompt","");let a=o.attachShadow({mode:"open"}),i="site"===t.mode,s=x(e,"span",{textContent:n.open}),l=x(e,"button",{className:"pill",type:"button"},s,x(e,"span",{className:"mark",textContent:"⁜"})),d=x(e,"span",{className:"pill compact"},`${n.viewingDrafts} \xb7 `),c=x(e,"button",{className:"link",type:"button",textContent:n.signOut});d.appendChild(c);let p=x(e,"p",{className:"status"});p.setAttribute("role","status");let g=x(e,"textarea",{placeholder:n.placeholder,rows:1});g.setAttribute("aria-label",n.open);let u=w(e,"page"),h=w(e,"block"),b=x(e,"span"),m=x(e,"button",{type:"button",title:n.clearBlock});m.setAttribute("aria-label",n.clearBlock),m.appendChild(w(e,"close"));let v=x(e,"span",{className:"chip scope"},u,h,b,m),k=x(e,"button",{className:"action chip",type:"button"},w(e,"undo"),n.undo),E=x(e,"button",{className:"action chip",type:"button"},w(e,"newPage"),n.newPage),S=x(e,"button",{type:"button",title:n.newPageCancel});S.setAttribute("aria-label",n.newPageCancel),S.appendChild(w(e,"close"));let C=x(e,"span",{className:"chip scope"},w(e,"newPage"),x(e,"span",{textContent:n.newPage}),S),N=x(e,"input",{type:"text",placeholder:n.pathPlaceholder,autocomplete:"off",spellcheck:!1}),L=x(e,"label",{className:"path"},x(e,"span",{textContent:n.path}),N),U=x(e,"button",{className:"close icon-button",type:"button",title:n.close},w(e,"close"));U.setAttribute("aria-label",n.close);let P=x(e,"button",{className:"action primary icon-button",type:"submit"},x(e,"span",{className:"visually-hidden",textContent:n.send}),w(e,"send"));P.title=n.send;let $=x(e,"span",{className:"unsaved"});$.appendChild(x(e,"span",{className:"dot"})),$.appendChild(e.createTextNode(n.unsaved));let A=x(e,"button",{className:"link",type:"button",textContent:n.signOut}),O=x(e,"button",{className:"action chip",type:"button",textContent:n.save}),I=x(e,"button",{className:"action chip",type:"button",textContent:n.publish}),M=x(e,"div",{className:"footer"},$,x(e,"span",{className:"spacer"}),A,O,I),j=x(e,"form",{className:"panel"},p,L,g,x(e,"div",{className:"toolbar"},v,C,k,E,x(e,"span",{className:"spacer"}),U,x(e,"span",{className:"divider"}),P),M),z=x(e,"div",{className:"root"},x(e,"div",{className:"bar"},l,d),j);a.append(x(e,"style",{textContent:y}),z);let R=!1,B=!1,D=null,T=e=>{let r=e.features.includes("prompt"),o=e.saving||e.publishing,a=e.prompting||o;z.hidden=!r&&!i,l.hidden=R||!r,d.hidden=r||!i,j.hidden=!R||!r;let c=i&&e.features.includes("create"),f=i&&null===t.entryId,y=f&&c;c||(B=!1),y&&(B=!0),s.textContent=y?n.newPage:n.open,S.hidden=y,v.hidden=B,C.hidden=!B,E.hidden=B||!c,E.disabled=a,L.hidden=!B,N.disabled=a,g.placeholder=B?n.newPagePlaceholder:n.placeholder,b.textContent=e.selectedUid?n.scopeBlock:n.scopeEntry,m.hidden=!e.selectedUid,u.hidden=!!e.selectedUid,h.hidden=!e.selectedUid,k.hidden=B||!e.canUndo,k.disabled=a,P.disabled=a||""===g.value.trim()||B&&""===N.value.trim(),g.disabled=a,O.hidden=!e.features.includes("save")||f,O.disabled=a||!e.unsaved,O.textContent=e.saving?n.saving:n.save,I.hidden=!e.features.includes("publish")||f,I.disabled=a,I.textContent=e.publishing?n.publishing:n.publish,A.hidden=!i,M.hidden=!i,$.hidden=!e.unsaved;let x=e.prompting?B?n.creating:n.working:D?.text??(!i||B||e.selectedUid?"":n.selectHint);p.textContent=x,p.hidden=!x,p.className=`status${e.prompting?" working":""}${!a&&D?.error?" error":""}`},q=()=>T(t.getState()),V=()=>{g.style.height="auto",g.style.height=`${g.scrollHeight}px`},H=async()=>{let e=g.value.trim();if(e&&!t.getState().prompting){D=null;try{if(B){let r=N.value.trim();if(!r)return N.focus();await t.createPage(r,e),D={text:n.created,error:!1}}else{let{summary:r}=await t.prompt(e);D={text:r,error:!1}}g.value="",V()}catch(e){D={text:e instanceof Error?e.message:String(e),error:!0}}q(),g.focus()}},F=e=>{R=e,q(),i&&t.setSelecting(R&&!B)},Y=e=>{B=e,D=null,t.setSelecting(R&&!B),B&&t.select(null),q(),(B?N:g).focus()},_=async e=>{D=null;try{await t[e](),D={text:"save"===e?n.saved:n.published,error:!1}}catch(e){D={text:e instanceof Error?e.message:String(e),error:!0}}q()};l.addEventListener("click",()=>{F(!0),(B?N:g).focus()}),U.addEventListener("click",()=>F(!1)),O.addEventListener("click",()=>void _("save")),I.addEventListener("click",()=>void _("publish")),A.addEventListener("click",()=>t.signOut()),c.addEventListener("click",()=>t.signOut()),m.addEventListener("click",()=>t.select(null)),E.addEventListener("click",()=>Y(!0)),S.addEventListener("click",()=>Y(!1)),N.addEventListener("input",q),N.addEventListener("keydown",e=>{"Enter"!==e.key||e.isComposing?"Escape"===e.key&&F(!1):(e.preventDefault(),g.focus())}),k.addEventListener("click",()=>{D=null,t.undo()}),j.addEventListener("submit",e=>{e.preventDefault(),H()}),g.addEventListener("input",()=>{V(),q()}),g.addEventListener("keydown",e=>{"Enter"!==e.key||e.shiftKey||e.isComposing?"Escape"===e.key&&F(!1):(e.preventDefault(),H())});let W=t.on("change",T);return q(),e.body.appendChild(o),()=>{W(),o.remove()}},E="https://content.obelism.studio",S=(e,t)=>JSON.stringify(e)===JSON.stringify(t),C=Symbol.for("@obelism/content/bridge"),N=m({isEditing:!1,entryId:null,on:()=>{}},new URLSearchParams),L=({url:e,prompt:l=!0,pagePath:g}={})=>{let f;if("u"<typeof window)return N;let v=window;if(v[C])return v[C];if(v.ObelismBridge)f=m(v.ObelismBridge,new URLSearchParams(v.location.search));else{let m=(f=((e,r)=>{let l=new URLSearchParams(e.location.search),g=e.parent!==e&&l.has("cms_preview"),m=p(),f=[],v={...c,selecting:!0},y=e=>{v={...v,...e},m.emit("change",v)},x={isEditing:g,mode:g?"editor":null,entryId:l.get("cms_entry"),environment:l.get(n),on:m.on,getState:()=>v,...u,destroy:()=>{f.splice(0).forEach(e=>e())}};if(!g)return x;let w=e.document,k=(t,n={})=>{e.parent.postMessage({source:"obelism-content-bridge",type:t,...n},r)},E=w.createElement("style");E.textContent=d(),w.head.appendChild(E),f.push(()=>E.remove());let S=(e,t)=>{if(w.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),v.selectedUid!==(e??null)&&y({selectedUid:e??null}),!e)return;let r=w.querySelector(b(e));r&&(r.setAttribute("data-cms-selected",""),t&&r.scrollIntoView({behavior:"smooth",block:"center"}))},C=e=>{let r=e.target,n=r?.closest?.(`[${t}]`);if(!n)return;e.preventDefault(),e.stopPropagation();let o=n.getAttribute(t);S(o,!1),k("select",{uid:o})};w.addEventListener("click",C,!0),f.push(()=>w.removeEventListener("click",C,!0));let N=()=>k("ready",{url:e.location.href}),L=new Map,U=0,P=e=>{let t=L.get(e);return t?(clearTimeout(t.timer),L.delete(e),0===L.size&&y({prompting:!1}),t):null};x.prompt=(e,t={})=>{let r=e.trim();if(!r)return Promise.reject(Error("Say what to change"));if(!v.features.includes("prompt"))return Promise.reject(Error("The editor does not offer prompts here"));let n=`prompt_${++U}`,o=void 0===t.uid?v.selectedUid:t.uid;return new Promise((e,t)=>{let a=setTimeout(()=>{P(n)?.reject(Error("The editor did not answer in time"))},12e4);L.set(n,{resolve:e,reject:t,timer:a}),y({prompting:!0}),k("prompt",{id:n,prompt:r,uid:o})})},x.undo=()=>k("undo"),x.select=e=>{S(e,!1),k("select",{uid:e})};let $=null;f.push(()=>$?.destroy()),f.push(()=>{for(let e of L.keys())P(e)?.reject(Error("The bridge was destroyed"))});let A=t=>{if(t.origin!==r||t.source!==e.parent)return;let n=t.data;if(n&&"obelism-content-editor"===n.source){if("connect"===n.type)N();else if("update"===n.type&&n.entry)m.emit("update",{entry:n.entry,selectedUid:n.selectedUid??null}),e.requestAnimationFrame(()=>S(n.selectedUid,!1)),$?.refresh();else if("select"===n.type)S(n.uid,!0);else if("saved"===n.type||"published"===n.type)m.emit(n.type,{}),0===m.listeners.update.size&&e.location.reload();else if("features"===n.type&&Array.isArray(n.features))y({features:n.features.filter(e=>"string"==typeof e)});else if("stats"===n.type)y({stats:(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return{};let t={};for(let[r,n]of Object.entries(e)){let{clicks:e,clickers:o,viewers:a,rate:i}=n??{};h(e)&&h(o)&&h(a)&&h(i)&&(t[r]={clicks:e,clickers:o,viewers:a,rate:i})}return t})(n.stats),statsVisible:!1!==n.visible}),($||Object.keys(v.stats).length)&&($??($=((e,{selector:t,onSelect:r,labels:n})=>{let l=e.document,d={...o,...n},c=l.createElement("div");c.setAttribute("data-obelism-stats","");let p=c.attachShadow({mode:"open"}),g=l.createElement("style");g.textContent=i;let u=l.createElement("div");u.className="layer",p.append(g,u),l.body.appendChild(c);let h={},b=!1,m=null,f=()=>{if(m=null,u.replaceChildren(),u.hidden=!b,!b)return;let n=e.innerHeight;for(let[o,i]of Object.entries(h)){let c=l.querySelector(t(o));if(!c)continue;let p=c.getBoundingClientRect();if(!p.width&&!p.height||p.bottom<0||p.top>n)continue;let g=l.createElement("button");g.type="button",g.className="badge",g.dataset.uid=o,g.textContent=a(d.clicked,{rate:Math.round(100*i.rate)}),g.title=a(d.title,{clickers:i.clickers,viewers:i.viewers,clicks:i.clicks});let{place:h,top:b,left:m}=s(p,e.innerWidth);g.dataset.place=h,g.style.top=`${b}px`,g.style.left=`${m}px`,g.addEventListener("click",()=>r(o)),u.appendChild(g)}},v=()=>{null===m&&b&&(m=e.requestAnimationFrame(f))};e.addEventListener("scroll",v,{capture:!0,passive:!0}),e.addEventListener("resize",v);let y=e.MutationObserver,x="function"==typeof y?new y(v):null;return x?.observe(l.body,{childList:!0,subtree:!0}),{update(t,r){h=t,b=r,null!==m&&(e.cancelAnimationFrame?.(m),m=null),f()},refresh:v,destroy(){null!==m&&e.cancelAnimationFrame?.(m),e.removeEventListener("scroll",v,{capture:!0}),e.removeEventListener("resize",v),x?.disconnect(),c.remove()}}})(e,{selector:b,onSelect:e=>x.select(e)})),$.update(v.stats,v.statsVisible));else if("promptState"===n.type)y({canUndo:!0===n.canUndo});else if("promptResult"===n.type&&"string"==typeof n.id){let e=P(n.id);if(!e)return;!0===n.ok?e.resolve({summary:"string"==typeof n.summary?n.summary:""}):e.reject(Error("string"==typeof n.error?n.error:"The prompt failed"))}}};return e.addEventListener("message",A),f.push(()=>e.removeEventListener("message",A)),"loading"===w.readyState?(w.addEventListener("DOMContentLoaded",N,{once:!0}),f.push(()=>w.removeEventListener("DOMContentLoaded",N))):N(),x})(v,new URL(e||E).origin)).isEditing||v.parent!==v?null:(e=>{for(let t of e.cookie.split(";")){let[e,...r]=t.trim().split("=");if("obelism_content_editing"===e)try{let e=decodeURIComponent(r.join("="));return e.startsWith("/")&&!e.startsWith("//")?e:null}catch{break}}return null})(v.document);if(m&&(f=((e,n,{pagePath:o=e=>`/${e}`}={})=>{let a=e.document,i=p(),s=[],l=new Map,g=[],u=new Set,h=null,m=c,f=e=>{m={...m,...e},i.emit("change",m)},v=()=>f({unsaved:[...l.values()].some(({content:e,saved:t})=>!S(e,t)),canUndo:g.length>0}),y=async(t,r)=>{let o=await e.fetch(`${n}/api/${t}`,void 0===r?{credentials:"same-origin"}:{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","x-obelism-content":"1"},body:JSON.stringify(r)}),a=await o.json().catch(()=>({}));if(401===o.status)throw f({features:[]}),Error("You were signed out. Sign in again to edit.");if(!o.ok)throw Error("string"==typeof a.error?a.error:`Obelism Content answered ${o.status}`);return a},x=()=>a.querySelector(`[${r}]`)?.getAttribute(r)??null,w=()=>{if(!h)return;let e=h.space.locales.map(({code:e})=>e),t=a.documentElement.lang;if(e.includes(t))return t;let r=t.split("-")[0]??"";return e.includes(r)?r:h.space.defaultLocale},k=async e=>{let t=l.get(e);if(t)return t;let r=await y(`entries/${encodeURIComponent(e)}`),n={content:r.content,saved:r.content,revision:r.draftRevision};return l.set(e,n),n},E=e=>{a.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),m.selectedUid!==e&&f({selectedUid:e}),e&&a.querySelector(b(e))?.setAttribute("data-cms-selected","")},C=async t=>{if(0===i.listeners.update.size)return;let r=l.get(t)?.content,{entry:n}=await y(`entries/${encodeURIComponent(t)}/preview`,{content:r,locale:w()});i.emit("update",{entry:n,selectedUid:m.selectedUid}),e.requestAnimationFrame(()=>E(m.selectedUid))},N=t=>{i.emit(t,{}),0===i.listeners.update.size&&e.location.reload()},L=async()=>{for(let[e,t]of l){if(S(t.content,t.saved))continue;let{draftRevision:r}=await y(`entries/${encodeURIComponent(e)}/save`,{content:t.content,baseRevision:t.revision});t.saved=t.content,t.revision=r,u.add(e)}g.length=0,v()},U={isEditing:!0,mode:"site",get entryId(){return x()},environment:null,on:i.on,getState:()=>m,async prompt(e,t={}){let n,o=e.trim();if(!o)throw Error("Say what to change");if(!m.features.includes("prompt"))throw Error("You may not edit content here");let i=void 0===t.uid?m.selectedUid:t.uid,s=(n=i?a.querySelector(b(i)):null,n?.closest(`[${r}]`)?.getAttribute(r)??x());if(!s)throw Error("Nothing on this page is marked as an entry: add editable(entry)");f({prompting:!0});try{let e=await k(s),t=await y(`entries/${encodeURIComponent(s)}/prompt`,{prompt:o,content:e.content,locale:w(),uid:i});return g.push({entryId:s,content:e.content}),e.content=t.content,v(),await C(s),{summary:t.summary}}finally{f({prompting:!1})}},async createPage(t,r){let n=r.trim();if(!n)throw Error("Say what the page is for");if(!t.trim())throw Error("Say where the page goes, like /pricing");if(!m.features.includes("create"))throw Error("You may not add pages here");if(m.unsaved)throw Error("Save or undo your changes before adding a page");f({prompting:!0});try{let r=await y("entries",{path:t,prompt:n,locale:w(),from:x()}),a=o(r.path,w()??"");return e.location.assign(a),{summary:r.summary,entryId:r.entryId,path:r.path,url:a}}finally{f({prompting:!1})}},undo(){let e=g.pop(),t=e&&l.get(e.entryId);e&&t&&(t.content=e.content,v(),C(e.entryId).catch(e=>console.error("[@obelism/content bridge]",e)))},async save(){if(!m.features.includes("save"))throw Error("You may not edit content here");f({saving:!0});try{await L()}finally{f({saving:!1})}N("saved")},async publish(){if(!m.features.includes("publish"))throw Error("You may not publish here");f({publishing:!0});try{await L();let e=x(),t=u.size>0?[...u]:e?[e]:[];if(0===t.length)throw Error("Nothing on this page is marked as an entry: add editable(entry)");for(let e of t)await y(`entries/${encodeURIComponent(e)}/publish`,{}),u.delete(e)}finally{f({publishing:!1})}N("published")},signOut(){if(m.unsaved&&!e.confirm("Sign out and lose the changes you have not saved?"))return;l.clear(),v();let t=e.location.pathname+e.location.search;e.location.href=`${n}/logout?return_to=${encodeURIComponent(t)}`},select(e){E(e)},setSelecting(e){e?a.documentElement.setAttribute("data-cms-selecting",""):a.documentElement.removeAttribute("data-cms-selecting"),f({selecting:e})},destroy(){s.splice(0).forEach(e=>e())}},P=a.createElement("style");P.textContent=d("html[data-cms-selecting] "),a.head.appendChild(P),s.push(()=>P.remove()),s.push(()=>a.documentElement.removeAttribute("data-cms-selecting"));let $=e=>{if(!m.selecting)return;let r=e.target,n=r?.closest?.(`[${t}]`);n&&(e.preventDefault(),e.stopPropagation(),E(n.getAttribute(t)))};a.addEventListener("click",$,!0),s.push(()=>a.removeEventListener("click",$,!0));let A=e=>{m.unsaved&&(e.preventDefault(),e.returnValue="")};return e.addEventListener("beforeunload",A),s.push(()=>e.removeEventListener("beforeunload",A)),y("session").then(e=>{h=e,f({features:e.features})}).catch(e=>console.error("[@obelism/content bridge]",e)),U})(v,m,{pagePath:g})),f.isEditing&&l){let e=k(v.document,f,!0===l?{}:l),t=f.destroy;f.destroy=()=>{e(),t()}}}return v[C]=f,f};(e=window).ObelismBridge=L((e=>{let t=e?.dataset.url,r=t||E;if(!t&&e?.src){let t=new URL(e.src);"/bridge.js"===t.pathname&&(r=t.origin)}return{url:r,prompt:e?.dataset.prompt!=="false"}})(document.currentScript)),e.ObelismContent={getContentBridge:L,mountPromptBox:k}}();
|
package/dist/bridge.js
CHANGED
|
@@ -1219,7 +1219,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
|
|
|
1219
1219
|
locale: locale(),
|
|
1220
1220
|
from: pageEntryId()
|
|
1221
1221
|
});
|
|
1222
|
-
const url = pagePath(created.path);
|
|
1222
|
+
const url = pagePath(created.path, locale() ?? '');
|
|
1223
1223
|
win.location.assign(url);
|
|
1224
1224
|
return {
|
|
1225
1225
|
summary: created.summary,
|
package/dist/bridge.mjs
CHANGED
|
@@ -1217,7 +1217,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
|
|
|
1217
1217
|
locale: locale(),
|
|
1218
1218
|
from: pageEntryId()
|
|
1219
1219
|
});
|
|
1220
|
-
const url = pagePath(created.path);
|
|
1220
|
+
const url = pagePath(created.path, locale() ?? '');
|
|
1221
1221
|
win.location.assign(url);
|
|
1222
1222
|
return {
|
|
1223
1223
|
summary: created.summary,
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RichTextFormat, E as Entry, a as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock,
|
|
2
|
-
export { C as ContentTypeDefinition,
|
|
1
|
+
import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, d as EntryStub, e as RichTextNode } from './types-fxvuz29w.js';
|
|
2
|
+
export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as RichTextDocument, j as RichTextMark } from './types-fxvuz29w.js';
|
|
3
3
|
|
|
4
4
|
type ContentClientOptions = {
|
|
5
5
|
token: string;
|
|
@@ -26,11 +26,29 @@ type EntriesQuery = RequestOptions & {
|
|
|
26
26
|
limit?: number | undefined;
|
|
27
27
|
skip?: number | undefined;
|
|
28
28
|
};
|
|
29
|
+
type RedirectsQuery = {
|
|
30
|
+
locale?: string | undefined;
|
|
31
|
+
limit?: number | undefined;
|
|
32
|
+
skip?: number | undefined;
|
|
33
|
+
init?: RequestInit | undefined;
|
|
34
|
+
};
|
|
35
|
+
type PageLookup<TEntry extends Entry = Entry> = {
|
|
36
|
+
entry: TEntry;
|
|
37
|
+
redirect?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
entry?: undefined;
|
|
40
|
+
redirect: {
|
|
41
|
+
path: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
29
44
|
declare class ContentClient {
|
|
30
45
|
#private;
|
|
31
46
|
readonly url: string;
|
|
32
47
|
constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl, }: ContentClientOptions);
|
|
33
48
|
getPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<TEntry | null>;
|
|
49
|
+
findPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<PageLookup<TEntry> | null>;
|
|
50
|
+
getRedirects: ({ locale, limit, skip, init, }?: RedirectsQuery) => Promise<RedirectList>;
|
|
51
|
+
getAllRedirects(query?: Omit<RedirectsQuery, 'skip'>): AsyncGenerator<Redirect, void, undefined>;
|
|
34
52
|
getEntry: <TEntry extends Entry = Entry>(id: string, options?: RequestOptions) => Promise<TEntry | null>;
|
|
35
53
|
getEntries: <TEntry extends Entry = Entry>({ contentType, parentPath, fields, order, limit, skip, ...options }?: EntriesQuery) => Promise<EntryList<TEntry>>;
|
|
36
54
|
getFirstEntry: <TEntry extends Entry = Entry>(query?: Omit<EntriesQuery, "limit" | "skip">) => Promise<TEntry | null>;
|
|
@@ -66,9 +84,13 @@ type ImageTransform = {
|
|
|
66
84
|
fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad' | undefined;
|
|
67
85
|
format?: 'auto' | 'avif' | 'webp' | 'jpeg' | 'png' | undefined;
|
|
68
86
|
q?: number | undefined;
|
|
87
|
+
focus?: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
} | 'auto' | undefined;
|
|
69
91
|
};
|
|
70
|
-
declare const imageUrl: (asset: Pick<Asset, "url"> | string, transform?: ImageTransform) => string;
|
|
71
|
-
declare const imageSrcSet: (asset: Pick<Asset, "url"> | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
|
|
92
|
+
declare const imageUrl: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, transform?: ImageTransform) => string;
|
|
93
|
+
declare const imageSrcSet: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
|
|
72
94
|
|
|
73
95
|
declare const IMPROVE_BLOCK = "_improve";
|
|
74
96
|
declare const isImproveBlock: (block: Block) => block is ImproveBlock;
|
|
@@ -98,6 +120,21 @@ declare const richTextToHtml: (doc: RichTextNode | null | undefined, { renderEnt
|
|
|
98
120
|
declare const headingLevel: (node: RichTextNode) => 1 | 2 | 3 | 4 | 5 | 6;
|
|
99
121
|
declare const richTextToPlainText: (doc: RichTextNode | null | undefined) => string;
|
|
100
122
|
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
type LocalizedUrlOptions = {
|
|
124
|
+
url: (path: string, locale: string) => string;
|
|
125
|
+
defaultLocale?: string | undefined;
|
|
126
|
+
};
|
|
127
|
+
type AlternateLink = {
|
|
128
|
+
hreflang: string;
|
|
129
|
+
href: string;
|
|
130
|
+
};
|
|
131
|
+
type Page = Pick<Entry, 'paths'> & Partial<Pick<Entry, 'updatedAt' | 'fields'>>;
|
|
132
|
+
declare const alternateLinks: (entry: Pick<Entry, "paths">, { url, defaultLocale }: LocalizedUrlOptions) => AlternateLink[];
|
|
133
|
+
type SitemapOptions = LocalizedUrlOptions & {
|
|
134
|
+
exclude?: ((entry: Page) => boolean) | undefined;
|
|
135
|
+
};
|
|
136
|
+
declare const sitemapXml: (entries: Iterable<Page>, { exclude, ...options }: SitemapOptions) => string;
|
|
137
|
+
|
|
138
|
+
export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, UID_ATTRIBUTE, alternateLinks, editable, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
|
|
139
|
+
export type { AlternateLink, ContentClientOptions, EntriesQuery, ImageTransform, LocalizedUrlOptions, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions, SitemapOptions };
|
|
103
140
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts"],"mappings":";;;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA;AACA,eAAe,cAAc;AAC7B;AACA,WAAW,WAAW;AACtB,mBAAmB,KAAK;AACxB;AACA,KAAK,cAAc;AACnB;AACA;AACA,eAAe,cAAc;AAC7B,WAAW,WAAW;AACtB;AACA,KAAK,gBAAgB;AACrB,KAAK,YAAY,GAAG,cAAc;AAClC;AACA;AACA,aAAa,MAAM,SAAS,gBAAgB,YAAY,gBAAgB;AACxE;AACA;AACA;AACA;AACA,cAAc,aAAa;AAC3B;AACA;AACA,2FAA2F,oBAAoB;AAC/G,6BAA6B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO;AAC9F,8BAA8B,KAAK,GAAG,KAAK,wBAAwB,cAAc,KAAK,OAAO;AAC7F,gCAAgC,KAAK,GAAG,KAAK,wEAAwE,YAAY,KAAK,OAAO,CAAC,SAAS;AACvJ,mCAAmC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,wBAAwB,OAAO;AAC1G,iCAAiC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,YAAY,cAAc;AACnG,gCAAgC,IAAI,CAAC,cAAc,cAAc,OAAO,CAAC,SAAS;AAClF;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":";;;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA;AACA,eAAe,cAAc;AAC7B;AACA,WAAW,WAAW;AACtB,mBAAmB,KAAK;AACxB;AACA,KAAK,cAAc;AACnB;AACA;AACA,eAAe,cAAc;AAC7B,WAAW,WAAW;AACtB;AACA,KAAK,gBAAgB;AACrB,KAAK,YAAY,GAAG,cAAc;AAClC;AACA;AACA,aAAa,MAAM,SAAS,gBAAgB,YAAY,gBAAgB;AACxE;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,WAAW,WAAW;AACtB;AACA,KAAK,UAAU,gBAAgB,KAAK,GAAG,KAAK;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,aAAa;AAC3B;AACA;AACA,2FAA2F,oBAAoB;AAC/G,6BAA6B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO;AAC9F,8BAA8B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO,CAAC,UAAU;AAC1G,oDAAoD,cAAc,KAAK,OAAO,CAAC,YAAY;AAC3F,4BAA4B,IAAI,CAAC,cAAc,YAAY,cAAc,CAAC,QAAQ;AAClF,8BAA8B,KAAK,GAAG,KAAK,wBAAwB,cAAc,KAAK,OAAO;AAC7F,gCAAgC,KAAK,GAAG,KAAK,wEAAwE,YAAY,KAAK,OAAO,CAAC,SAAS;AACvJ,mCAAmC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,wBAAwB,OAAO;AAC1G,iCAAiC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,YAAY,cAAc;AACnG,gCAAgC,IAAI,CAAC,cAAc,cAAc,OAAO,CAAC,SAAS;AAClF;;ACtDA,cAAc,WAAW;;ACAzB,cAAc,aAAa;AAC3B,cAAc,eAAe;AAC7B,KAAK,QAAQ;AACb;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,yBAAyB,QAAQ;;ACXjC,cAAc,eAAe,SAAS,KAAK;AAC3C;AACA;AACA;AACA;AACA;;ACJA,KAAK,cAAc;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ,WAAW,IAAI,CAAC,KAAK,WAAW,OAAO,CAAC,IAAI,CAAC,KAAK,wCAAwC,cAAc;AAC9H,cAAc,WAAW,WAAW,IAAI,CAAC,KAAK,WAAW,OAAO,CAAC,IAAI,CAAC,KAAK,0DAA0D,IAAI,CAAC,cAAc;;ACZxJ,cAAc,aAAa;AAC3B,cAAc,cAAc,UAAU,KAAK,cAAc,YAAY;AACrE,cAAc,kBAAkB,kBAAkB,KAAK,SAAS,YAAY;;ACH5E,cAAc,cAAc;AAC5B;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA;AACA,KAAK,iBAAiB,GAAG,GAAG,GAAG,eAAe,YAAY,MAAM;AAChE,cAAc,iBAAiB,UAAU,iBAAiB,KAAK,aAAa;;ACX5E,cAAc,SAAS,EAAE,MAAM;AAC/B,cAAc,UAAU;AACxB,KAAK,mBAAmB;AACxB,0BAA0B,KAAK,GAAG,SAAS,QAAQ,YAAY;AAC/D,0BAA0B,KAAK,QAAQ,YAAY;AACnD;AACA,cAAc,cAAc,QAAQ,YAAY,oDAAoD,mBAAmB;AACvH,cAAc,YAAY,SAAS,YAAY;AAC/C,cAAc,mBAAmB,QAAQ,YAAY;;ACRrD,KAAK,mBAAmB;AACxB;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,IAAI,GAAG,IAAI,CAAC,KAAK,aAAa,OAAO,CAAC,IAAI,CAAC,KAAK;AACrD,cAAc,cAAc,UAAU,IAAI,CAAC,KAAK,oCAAoC,mBAAmB,KAAK,aAAa;AACzH,KAAK,cAAc,GAAG,mBAAmB;AACzC,uBAAuB,IAAI;AAC3B;AACA,cAAc,UAAU,YAAY,QAAQ,CAAC,IAAI,4BAA4B,cAAc;;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RichTextFormat, E as Entry, a as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock,
|
|
2
|
-
export { C as ContentTypeDefinition,
|
|
1
|
+
import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, d as EntryStub, e as RichTextNode } from './types-fxvuz29w.js';
|
|
2
|
+
export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as RichTextDocument, j as RichTextMark } from './types-fxvuz29w.js';
|
|
3
3
|
|
|
4
4
|
type ContentClientOptions = {
|
|
5
5
|
token: string;
|
|
@@ -26,11 +26,29 @@ type EntriesQuery = RequestOptions & {
|
|
|
26
26
|
limit?: number | undefined;
|
|
27
27
|
skip?: number | undefined;
|
|
28
28
|
};
|
|
29
|
+
type RedirectsQuery = {
|
|
30
|
+
locale?: string | undefined;
|
|
31
|
+
limit?: number | undefined;
|
|
32
|
+
skip?: number | undefined;
|
|
33
|
+
init?: RequestInit | undefined;
|
|
34
|
+
};
|
|
35
|
+
type PageLookup<TEntry extends Entry = Entry> = {
|
|
36
|
+
entry: TEntry;
|
|
37
|
+
redirect?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
entry?: undefined;
|
|
40
|
+
redirect: {
|
|
41
|
+
path: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
29
44
|
declare class ContentClient {
|
|
30
45
|
#private;
|
|
31
46
|
readonly url: string;
|
|
32
47
|
constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl, }: ContentClientOptions);
|
|
33
48
|
getPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<TEntry | null>;
|
|
49
|
+
findPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<PageLookup<TEntry> | null>;
|
|
50
|
+
getRedirects: ({ locale, limit, skip, init, }?: RedirectsQuery) => Promise<RedirectList>;
|
|
51
|
+
getAllRedirects(query?: Omit<RedirectsQuery, 'skip'>): AsyncGenerator<Redirect, void, undefined>;
|
|
34
52
|
getEntry: <TEntry extends Entry = Entry>(id: string, options?: RequestOptions) => Promise<TEntry | null>;
|
|
35
53
|
getEntries: <TEntry extends Entry = Entry>({ contentType, parentPath, fields, order, limit, skip, ...options }?: EntriesQuery) => Promise<EntryList<TEntry>>;
|
|
36
54
|
getFirstEntry: <TEntry extends Entry = Entry>(query?: Omit<EntriesQuery, "limit" | "skip">) => Promise<TEntry | null>;
|
|
@@ -66,9 +84,13 @@ type ImageTransform = {
|
|
|
66
84
|
fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad' | undefined;
|
|
67
85
|
format?: 'auto' | 'avif' | 'webp' | 'jpeg' | 'png' | undefined;
|
|
68
86
|
q?: number | undefined;
|
|
87
|
+
focus?: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
} | 'auto' | undefined;
|
|
69
91
|
};
|
|
70
|
-
declare const imageUrl: (asset: Pick<Asset, "url"> | string, transform?: ImageTransform) => string;
|
|
71
|
-
declare const imageSrcSet: (asset: Pick<Asset, "url"> | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
|
|
92
|
+
declare const imageUrl: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, transform?: ImageTransform) => string;
|
|
93
|
+
declare const imageSrcSet: (asset: (Pick<Asset, "url"> & Partial<Pick<Asset, "focalPoint">>) | string, widths: number[], transform?: Omit<ImageTransform, "w">) => string;
|
|
72
94
|
|
|
73
95
|
declare const IMPROVE_BLOCK = "_improve";
|
|
74
96
|
declare const isImproveBlock: (block: Block) => block is ImproveBlock;
|
|
@@ -98,6 +120,21 @@ declare const richTextToHtml: (doc: RichTextNode | null | undefined, { renderEnt
|
|
|
98
120
|
declare const headingLevel: (node: RichTextNode) => 1 | 2 | 3 | 4 | 5 | 6;
|
|
99
121
|
declare const richTextToPlainText: (doc: RichTextNode | null | undefined) => string;
|
|
100
122
|
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
type LocalizedUrlOptions = {
|
|
124
|
+
url: (path: string, locale: string) => string;
|
|
125
|
+
defaultLocale?: string | undefined;
|
|
126
|
+
};
|
|
127
|
+
type AlternateLink = {
|
|
128
|
+
hreflang: string;
|
|
129
|
+
href: string;
|
|
130
|
+
};
|
|
131
|
+
type Page = Pick<Entry, 'paths'> & Partial<Pick<Entry, 'updatedAt' | 'fields'>>;
|
|
132
|
+
declare const alternateLinks: (entry: Pick<Entry, "paths">, { url, defaultLocale }: LocalizedUrlOptions) => AlternateLink[];
|
|
133
|
+
type SitemapOptions = LocalizedUrlOptions & {
|
|
134
|
+
exclude?: ((entry: Page) => boolean) | undefined;
|
|
135
|
+
};
|
|
136
|
+
declare const sitemapXml: (entries: Iterable<Page>, { exclude, ...options }: SitemapOptions) => string;
|
|
137
|
+
|
|
138
|
+
export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, UID_ATTRIBUTE, alternateLinks, editable, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
|
|
139
|
+
export type { AlternateLink, ContentClientOptions, EntriesQuery, ImageTransform, LocalizedUrlOptions, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions, SitemapOptions };
|
|
103
140
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts"],"mappings":";;;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA;AACA,eAAe,cAAc;AAC7B;AACA,WAAW,WAAW;AACtB,mBAAmB,KAAK;AACxB;AACA,KAAK,cAAc;AACnB;AACA;AACA,eAAe,cAAc;AAC7B,WAAW,WAAW;AACtB;AACA,KAAK,gBAAgB;AACrB,KAAK,YAAY,GAAG,cAAc;AAClC;AACA;AACA,aAAa,MAAM,SAAS,gBAAgB,YAAY,gBAAgB;AACxE;AACA;AACA;AACA;AACA,cAAc,aAAa;AAC3B;AACA;AACA,2FAA2F,oBAAoB;AAC/G,6BAA6B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO;AAC9F,8BAA8B,KAAK,GAAG,KAAK,wBAAwB,cAAc,KAAK,OAAO;AAC7F,gCAAgC,KAAK,GAAG,KAAK,wEAAwE,YAAY,KAAK,OAAO,CAAC,SAAS;AACvJ,mCAAmC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,wBAAwB,OAAO;AAC1G,iCAAiC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,YAAY,cAAc;AACnG,gCAAgC,IAAI,CAAC,cAAc,cAAc,OAAO,CAAC,SAAS;AAClF;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/preview.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":";;;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA;AACA,eAAe,cAAc;AAC7B;AACA,WAAW,WAAW;AACtB,mBAAmB,KAAK;AACxB;AACA,KAAK,cAAc;AACnB;AACA;AACA,eAAe,cAAc;AAC7B,WAAW,WAAW;AACtB;AACA,KAAK,gBAAgB;AACrB,KAAK,YAAY,GAAG,cAAc;AAClC;AACA;AACA,aAAa,MAAM,SAAS,gBAAgB,YAAY,gBAAgB;AACxE;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,WAAW,WAAW;AACtB;AACA,KAAK,UAAU,gBAAgB,KAAK,GAAG,KAAK;AAC5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,aAAa;AAC3B;AACA;AACA,2FAA2F,oBAAoB;AAC/G,6BAA6B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO;AAC9F,8BAA8B,KAAK,GAAG,KAAK,0BAA0B,cAAc,KAAK,OAAO,CAAC,UAAU;AAC1G,oDAAoD,cAAc,KAAK,OAAO,CAAC,YAAY;AAC3F,4BAA4B,IAAI,CAAC,cAAc,YAAY,cAAc,CAAC,QAAQ;AAClF,8BAA8B,KAAK,GAAG,KAAK,wBAAwB,cAAc,KAAK,OAAO;AAC7F,gCAAgC,KAAK,GAAG,KAAK,wEAAwE,YAAY,KAAK,OAAO,CAAC,SAAS;AACvJ,mCAAmC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,wBAAwB,OAAO;AAC1G,iCAAiC,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC,YAAY,YAAY,cAAc;AACnG,gCAAgC,IAAI,CAAC,cAAc,cAAc,OAAO,CAAC,SAAS;AAClF;;ACtDA,cAAc,WAAW;;ACAzB,cAAc,aAAa;AAC3B,cAAc,eAAe;AAC7B,KAAK,QAAQ;AACb;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,yBAAyB,QAAQ;;ACXjC,cAAc,eAAe,SAAS,KAAK;AAC3C;AACA;AACA;AACA;AACA;;ACJA,KAAK,cAAc;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ,WAAW,IAAI,CAAC,KAAK,WAAW,OAAO,CAAC,IAAI,CAAC,KAAK,wCAAwC,cAAc;AAC9H,cAAc,WAAW,WAAW,IAAI,CAAC,KAAK,WAAW,OAAO,CAAC,IAAI,CAAC,KAAK,0DAA0D,IAAI,CAAC,cAAc;;ACZxJ,cAAc,aAAa;AAC3B,cAAc,cAAc,UAAU,KAAK,cAAc,YAAY;AACrE,cAAc,kBAAkB,kBAAkB,KAAK,SAAS,YAAY;;ACH5E,cAAc,cAAc;AAC5B;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA;AACA,KAAK,iBAAiB,GAAG,GAAG,GAAG,eAAe,YAAY,MAAM;AAChE,cAAc,iBAAiB,UAAU,iBAAiB,KAAK,aAAa;;ACX5E,cAAc,SAAS,EAAE,MAAM;AAC/B,cAAc,UAAU;AACxB,KAAK,mBAAmB;AACxB,0BAA0B,KAAK,GAAG,SAAS,QAAQ,YAAY;AAC/D,0BAA0B,KAAK,QAAQ,YAAY;AACnD;AACA,cAAc,cAAc,QAAQ,YAAY,oDAAoD,mBAAmB;AACvH,cAAc,YAAY,SAAS,YAAY;AAC/C,cAAc,mBAAmB,QAAQ,YAAY;;ACRrD,KAAK,mBAAmB;AACxB;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,IAAI,GAAG,IAAI,CAAC,KAAK,aAAa,OAAO,CAAC,IAAI,CAAC,KAAK;AACrD,cAAc,cAAc,UAAU,IAAI,CAAC,KAAK,oCAAoC,mBAAmB,KAAK,aAAa;AACzH,KAAK,cAAc,GAAG,mBAAmB;AACzC,uBAAuB,IAAI;AAC3B;AACA,cAAc,UAAU,YAAY,QAAQ,CAAC,IAAI,4BAA4B,cAAc;;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -42,17 +42,45 @@ class ContentClient {
|
|
|
42
42
|
#fetch;
|
|
43
43
|
constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl }){
|
|
44
44
|
// The page at a path (`blog/hello`), or null when there is none. A site's
|
|
45
|
-
// catch-all route asks for this.
|
|
46
|
-
|
|
45
|
+
// catch-all route asks for this. The path is looked up in `locale`'s
|
|
46
|
+
// paths (the default locale's when left out or `*`): pages can have a
|
|
47
|
+
// slug per locale.
|
|
48
|
+
this.getPage = async (path, options = {})=>(await this.findPage(path, options))?.entry ?? null;
|
|
49
|
+
// Like getPage, but a path a page moved away from answers where it is
|
|
50
|
+
// now, so the site can send a 301 there instead of a 404.
|
|
51
|
+
//
|
|
52
|
+
// const found = await content.findPage(path, { locale })
|
|
53
|
+
// if (found?.redirect) return redirect(`/${locale}/${found.redirect.path}`, 301)
|
|
54
|
+
// if (!found) return notFound()
|
|
55
|
+
this.findPage = async (path, options = {})=>{
|
|
47
56
|
const trimmed = trimSlashes(path);
|
|
48
57
|
if (!trimmed) throw new Error('getPage needs a path');
|
|
49
58
|
const result = await this.#request('/pages', {
|
|
50
59
|
path: trimmed
|
|
51
60
|
}, options, {
|
|
52
|
-
notFound:
|
|
61
|
+
notFound: 'body'
|
|
53
62
|
});
|
|
54
|
-
|
|
63
|
+
if (result && 'entry' in result) return {
|
|
64
|
+
entry: result.entry
|
|
65
|
+
};
|
|
66
|
+
const to = result?.redirect?.path;
|
|
67
|
+
return typeof to === 'string' ? {
|
|
68
|
+
redirect: {
|
|
69
|
+
path: to
|
|
70
|
+
}
|
|
71
|
+
} : null;
|
|
55
72
|
};
|
|
73
|
+
// One page of redirects: every path a page moved away from, per locale,
|
|
74
|
+
// with where it is now. For redirect rules built ahead of time.
|
|
75
|
+
this.getRedirects = ({ locale, limit, skip, init } = {})=>this.#request('/redirects', {
|
|
76
|
+
limit,
|
|
77
|
+
skip
|
|
78
|
+
}, {
|
|
79
|
+
locale,
|
|
80
|
+
init
|
|
81
|
+
}, {
|
|
82
|
+
defaults: false
|
|
83
|
+
});
|
|
56
84
|
// An entry by id, or null when there is none (or it isn't published, for a
|
|
57
85
|
// delivery token).
|
|
58
86
|
this.getEntry = async (id, options = {})=>{
|
|
@@ -93,6 +121,21 @@ class ContentClient {
|
|
|
93
121
|
this.#init = init;
|
|
94
122
|
this.#fetch = fetchImpl ?? ((...args)=>fetch(...args));
|
|
95
123
|
}
|
|
124
|
+
// Every redirect, fetching pages of `limit` (default 1000) as it goes.
|
|
125
|
+
async *getAllRedirects(query = {}) {
|
|
126
|
+
const limit = query.limit ?? 1000;
|
|
127
|
+
let skip = 0;
|
|
128
|
+
while(true){
|
|
129
|
+
const page = await this.getRedirects({
|
|
130
|
+
...query,
|
|
131
|
+
limit,
|
|
132
|
+
skip
|
|
133
|
+
});
|
|
134
|
+
yield* page.items;
|
|
135
|
+
skip += page.items.length;
|
|
136
|
+
if (page.items.length === 0 || skip >= page.total) return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
96
139
|
// Every entry matching the query, fetching pages of `limit` (default 100)
|
|
97
140
|
// as it goes.
|
|
98
141
|
//
|
|
@@ -111,7 +154,8 @@ class ContentClient {
|
|
|
111
154
|
if (page.items.length === 0 || skip >= page.total) return;
|
|
112
155
|
}
|
|
113
156
|
}
|
|
114
|
-
//
|
|
157
|
+
// For a 404, null when `notFound` is set, or the body as is for 'body';
|
|
158
|
+
// throws ContentApiError otherwise.
|
|
115
159
|
async #request(path, query, { init, ...options }, { notFound = false, defaults = true } = {}) {
|
|
116
160
|
// Checked here rather than in the constructor, so a module creating a
|
|
117
161
|
// client still loads (and a build still runs) without the variable.
|
|
@@ -149,7 +193,7 @@ class ContentClient {
|
|
|
149
193
|
timeout
|
|
150
194
|
]) : timeout
|
|
151
195
|
});
|
|
152
|
-
if (notFound && response.status === 404) return null;
|
|
196
|
+
if (notFound === true && response.status === 404) return null;
|
|
153
197
|
const text = await response.text();
|
|
154
198
|
let body = text;
|
|
155
199
|
try {
|
|
@@ -157,6 +201,9 @@ class ContentClient {
|
|
|
157
201
|
} catch {
|
|
158
202
|
// Not JSON: keep the text for the error.
|
|
159
203
|
}
|
|
204
|
+
if (notFound === 'body' && response.status === 404) {
|
|
205
|
+
return typeof body === 'object' && body !== null ? body : null;
|
|
206
|
+
}
|
|
160
207
|
if (!response.ok) throw new ContentApiError(response.status, url, body);
|
|
161
208
|
return body;
|
|
162
209
|
}
|
|
@@ -171,16 +218,25 @@ const KEYS = [
|
|
|
171
218
|
'format',
|
|
172
219
|
'q'
|
|
173
220
|
];
|
|
221
|
+
const CROPS = new Set([
|
|
222
|
+
'cover',
|
|
223
|
+
'crop'
|
|
224
|
+
]);
|
|
174
225
|
// An asset's URL with a resize / re-encode query. PNG, JPEG, GIF, WebP and
|
|
175
226
|
// AVIF are transformed; other files ignore the query.
|
|
176
227
|
//
|
|
177
228
|
// imageUrl(asset, { w: 800 })
|
|
229
|
+
// imageUrl(asset, { w: 800, h: 800, fit: 'cover' }) // around its focal point
|
|
178
230
|
const imageUrl = (asset, transform = {})=>{
|
|
179
231
|
const url = new URL(typeof asset === 'string' ? asset : asset.url);
|
|
180
232
|
for (const key of KEYS){
|
|
181
233
|
const value = transform[key];
|
|
182
234
|
if (value !== undefined) url.searchParams.set(key, String(value));
|
|
183
235
|
}
|
|
236
|
+
const focus = transform.focus ?? (typeof asset === 'string' ? undefined : asset.focalPoint ?? undefined);
|
|
237
|
+
if (focus && CROPS.has(transform.fit ?? '')) {
|
|
238
|
+
url.searchParams.set('focus', focus === 'auto' ? 'auto' : `${focus.x},${focus.y}`);
|
|
239
|
+
}
|
|
184
240
|
return url.toString();
|
|
185
241
|
};
|
|
186
242
|
// A `srcset` with one candidate per width.
|
|
@@ -319,6 +375,53 @@ const richTextToPlainText = (doc)=>{
|
|
|
319
375
|
return parts.join('').replace(/\n+/g, '\n').trim();
|
|
320
376
|
};
|
|
321
377
|
|
|
378
|
+
// Every locale's URL of a page, for `<link rel="alternate" hreflang>` tags
|
|
379
|
+
// and language switchers. Empty for data entries.
|
|
380
|
+
const alternateLinks = (entry, { url, defaultLocale })=>{
|
|
381
|
+
const links = Object.entries(entry.paths).map(([locale, path])=>({
|
|
382
|
+
hreflang: locale,
|
|
383
|
+
href: url(path, locale)
|
|
384
|
+
}));
|
|
385
|
+
const fallback = defaultLocale ? entry.paths[defaultLocale] : undefined;
|
|
386
|
+
if (defaultLocale && fallback !== undefined) {
|
|
387
|
+
links.push({
|
|
388
|
+
hreflang: 'x-default',
|
|
389
|
+
href: url(fallback, defaultLocale)
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return links;
|
|
393
|
+
};
|
|
394
|
+
const escapeXml = (text)=>text.replace(/[<>&'"]/g, (char)=>({
|
|
395
|
+
'<': '<',
|
|
396
|
+
'>': '>',
|
|
397
|
+
'&': '&',
|
|
398
|
+
"'": ''',
|
|
399
|
+
'"': '"'
|
|
400
|
+
})[char]);
|
|
401
|
+
const hidden = (entry)=>entry.fields?.noIndex === true;
|
|
402
|
+
// A sitemap.xml with one `<url>` per page and locale, each listing the page
|
|
403
|
+
// in every locale (`xhtml:link`), and `lastmod` from `updatedAt`. Pages
|
|
404
|
+
// without paths (data entries) are skipped. Fetch the entries with a
|
|
405
|
+
// delivery token: drafts don't belong in a sitemap.
|
|
406
|
+
const sitemapXml = (entries, { exclude = hidden, ...options })=>{
|
|
407
|
+
const urls = [];
|
|
408
|
+
for (const entry of entries){
|
|
409
|
+
if (exclude(entry)) continue;
|
|
410
|
+
const links = alternateLinks(entry, options);
|
|
411
|
+
const alternates = links.map(({ hreflang, href })=>`<xhtml:link rel="alternate" hreflang="${escapeXml(hreflang)}" href="${escapeXml(href)}"/>`).join('');
|
|
412
|
+
for (const { hreflang, href } of links){
|
|
413
|
+
if (hreflang === 'x-default') continue;
|
|
414
|
+
urls.push(`<url><loc>${escapeXml(href)}</loc>${entry.updatedAt ? `<lastmod>${escapeXml(entry.updatedAt)}</lastmod>` : ''}${links.length > 1 ? alternates : ''}</url>`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return [
|
|
418
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
419
|
+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
|
|
420
|
+
...urls,
|
|
421
|
+
'</urlset>'
|
|
422
|
+
].join('\n');
|
|
423
|
+
};
|
|
424
|
+
|
|
322
425
|
exports.DEFAULT_URL = config.DEFAULT_URL;
|
|
323
426
|
exports.ENTRY_ATTRIBUTE = preview.ENTRY_ATTRIBUTE;
|
|
324
427
|
exports.PREVIEW_PARAMS = preview.PREVIEW_PARAMS;
|
|
@@ -329,6 +432,7 @@ exports.ContentApiError = ContentApiError;
|
|
|
329
432
|
exports.ContentClient = ContentClient;
|
|
330
433
|
exports.IMPROVE_BLOCK = IMPROVE_BLOCK;
|
|
331
434
|
exports.SAFE_HREF = SAFE_HREF;
|
|
435
|
+
exports.alternateLinks = alternateLinks;
|
|
332
436
|
exports.escapeHtml = escapeHtml;
|
|
333
437
|
exports.headingLevel = headingLevel;
|
|
334
438
|
exports.imageSrcSet = imageSrcSet;
|
|
@@ -337,3 +441,4 @@ exports.isImproveBlock = isImproveBlock;
|
|
|
337
441
|
exports.pickImproveVariant = pickImproveVariant;
|
|
338
442
|
exports.richTextToHtml = richTextToHtml;
|
|
339
443
|
exports.richTextToPlainText = richTextToPlainText;
|
|
444
|
+
exports.sitemapXml = sitemapXml;
|
package/dist/index.mjs
CHANGED
|
@@ -40,17 +40,45 @@ class ContentClient {
|
|
|
40
40
|
#fetch;
|
|
41
41
|
constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl }){
|
|
42
42
|
// The page at a path (`blog/hello`), or null when there is none. A site's
|
|
43
|
-
// catch-all route asks for this.
|
|
44
|
-
|
|
43
|
+
// catch-all route asks for this. The path is looked up in `locale`'s
|
|
44
|
+
// paths (the default locale's when left out or `*`): pages can have a
|
|
45
|
+
// slug per locale.
|
|
46
|
+
this.getPage = async (path, options = {})=>(await this.findPage(path, options))?.entry ?? null;
|
|
47
|
+
// Like getPage, but a path a page moved away from answers where it is
|
|
48
|
+
// now, so the site can send a 301 there instead of a 404.
|
|
49
|
+
//
|
|
50
|
+
// const found = await content.findPage(path, { locale })
|
|
51
|
+
// if (found?.redirect) return redirect(`/${locale}/${found.redirect.path}`, 301)
|
|
52
|
+
// if (!found) return notFound()
|
|
53
|
+
this.findPage = async (path, options = {})=>{
|
|
45
54
|
const trimmed = trimSlashes(path);
|
|
46
55
|
if (!trimmed) throw new Error('getPage needs a path');
|
|
47
56
|
const result = await this.#request('/pages', {
|
|
48
57
|
path: trimmed
|
|
49
58
|
}, options, {
|
|
50
|
-
notFound:
|
|
59
|
+
notFound: 'body'
|
|
51
60
|
});
|
|
52
|
-
|
|
61
|
+
if (result && 'entry' in result) return {
|
|
62
|
+
entry: result.entry
|
|
63
|
+
};
|
|
64
|
+
const to = result?.redirect?.path;
|
|
65
|
+
return typeof to === 'string' ? {
|
|
66
|
+
redirect: {
|
|
67
|
+
path: to
|
|
68
|
+
}
|
|
69
|
+
} : null;
|
|
53
70
|
};
|
|
71
|
+
// One page of redirects: every path a page moved away from, per locale,
|
|
72
|
+
// with where it is now. For redirect rules built ahead of time.
|
|
73
|
+
this.getRedirects = ({ locale, limit, skip, init } = {})=>this.#request('/redirects', {
|
|
74
|
+
limit,
|
|
75
|
+
skip
|
|
76
|
+
}, {
|
|
77
|
+
locale,
|
|
78
|
+
init
|
|
79
|
+
}, {
|
|
80
|
+
defaults: false
|
|
81
|
+
});
|
|
54
82
|
// An entry by id, or null when there is none (or it isn't published, for a
|
|
55
83
|
// delivery token).
|
|
56
84
|
this.getEntry = async (id, options = {})=>{
|
|
@@ -91,6 +119,21 @@ class ContentClient {
|
|
|
91
119
|
this.#init = init;
|
|
92
120
|
this.#fetch = fetchImpl ?? ((...args)=>fetch(...args));
|
|
93
121
|
}
|
|
122
|
+
// Every redirect, fetching pages of `limit` (default 1000) as it goes.
|
|
123
|
+
async *getAllRedirects(query = {}) {
|
|
124
|
+
const limit = query.limit ?? 1000;
|
|
125
|
+
let skip = 0;
|
|
126
|
+
while(true){
|
|
127
|
+
const page = await this.getRedirects({
|
|
128
|
+
...query,
|
|
129
|
+
limit,
|
|
130
|
+
skip
|
|
131
|
+
});
|
|
132
|
+
yield* page.items;
|
|
133
|
+
skip += page.items.length;
|
|
134
|
+
if (page.items.length === 0 || skip >= page.total) return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
94
137
|
// Every entry matching the query, fetching pages of `limit` (default 100)
|
|
95
138
|
// as it goes.
|
|
96
139
|
//
|
|
@@ -109,7 +152,8 @@ class ContentClient {
|
|
|
109
152
|
if (page.items.length === 0 || skip >= page.total) return;
|
|
110
153
|
}
|
|
111
154
|
}
|
|
112
|
-
//
|
|
155
|
+
// For a 404, null when `notFound` is set, or the body as is for 'body';
|
|
156
|
+
// throws ContentApiError otherwise.
|
|
113
157
|
async #request(path, query, { init, ...options }, { notFound = false, defaults = true } = {}) {
|
|
114
158
|
// Checked here rather than in the constructor, so a module creating a
|
|
115
159
|
// client still loads (and a build still runs) without the variable.
|
|
@@ -147,7 +191,7 @@ class ContentClient {
|
|
|
147
191
|
timeout
|
|
148
192
|
]) : timeout
|
|
149
193
|
});
|
|
150
|
-
if (notFound && response.status === 404) return null;
|
|
194
|
+
if (notFound === true && response.status === 404) return null;
|
|
151
195
|
const text = await response.text();
|
|
152
196
|
let body = text;
|
|
153
197
|
try {
|
|
@@ -155,6 +199,9 @@ class ContentClient {
|
|
|
155
199
|
} catch {
|
|
156
200
|
// Not JSON: keep the text for the error.
|
|
157
201
|
}
|
|
202
|
+
if (notFound === 'body' && response.status === 404) {
|
|
203
|
+
return typeof body === 'object' && body !== null ? body : null;
|
|
204
|
+
}
|
|
158
205
|
if (!response.ok) throw new ContentApiError(response.status, url, body);
|
|
159
206
|
return body;
|
|
160
207
|
}
|
|
@@ -169,16 +216,25 @@ const KEYS = [
|
|
|
169
216
|
'format',
|
|
170
217
|
'q'
|
|
171
218
|
];
|
|
219
|
+
const CROPS = new Set([
|
|
220
|
+
'cover',
|
|
221
|
+
'crop'
|
|
222
|
+
]);
|
|
172
223
|
// An asset's URL with a resize / re-encode query. PNG, JPEG, GIF, WebP and
|
|
173
224
|
// AVIF are transformed; other files ignore the query.
|
|
174
225
|
//
|
|
175
226
|
// imageUrl(asset, { w: 800 })
|
|
227
|
+
// imageUrl(asset, { w: 800, h: 800, fit: 'cover' }) // around its focal point
|
|
176
228
|
const imageUrl = (asset, transform = {})=>{
|
|
177
229
|
const url = new URL(typeof asset === 'string' ? asset : asset.url);
|
|
178
230
|
for (const key of KEYS){
|
|
179
231
|
const value = transform[key];
|
|
180
232
|
if (value !== undefined) url.searchParams.set(key, String(value));
|
|
181
233
|
}
|
|
234
|
+
const focus = transform.focus ?? (typeof asset === 'string' ? undefined : asset.focalPoint ?? undefined);
|
|
235
|
+
if (focus && CROPS.has(transform.fit ?? '')) {
|
|
236
|
+
url.searchParams.set('focus', focus === 'auto' ? 'auto' : `${focus.x},${focus.y}`);
|
|
237
|
+
}
|
|
182
238
|
return url.toString();
|
|
183
239
|
};
|
|
184
240
|
// A `srcset` with one candidate per width.
|
|
@@ -317,4 +373,51 @@ const richTextToPlainText = (doc)=>{
|
|
|
317
373
|
return parts.join('').replace(/\n+/g, '\n').trim();
|
|
318
374
|
};
|
|
319
375
|
|
|
320
|
-
|
|
376
|
+
// Every locale's URL of a page, for `<link rel="alternate" hreflang>` tags
|
|
377
|
+
// and language switchers. Empty for data entries.
|
|
378
|
+
const alternateLinks = (entry, { url, defaultLocale })=>{
|
|
379
|
+
const links = Object.entries(entry.paths).map(([locale, path])=>({
|
|
380
|
+
hreflang: locale,
|
|
381
|
+
href: url(path, locale)
|
|
382
|
+
}));
|
|
383
|
+
const fallback = defaultLocale ? entry.paths[defaultLocale] : undefined;
|
|
384
|
+
if (defaultLocale && fallback !== undefined) {
|
|
385
|
+
links.push({
|
|
386
|
+
hreflang: 'x-default',
|
|
387
|
+
href: url(fallback, defaultLocale)
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return links;
|
|
391
|
+
};
|
|
392
|
+
const escapeXml = (text)=>text.replace(/[<>&'"]/g, (char)=>({
|
|
393
|
+
'<': '<',
|
|
394
|
+
'>': '>',
|
|
395
|
+
'&': '&',
|
|
396
|
+
"'": ''',
|
|
397
|
+
'"': '"'
|
|
398
|
+
})[char]);
|
|
399
|
+
const hidden = (entry)=>entry.fields?.noIndex === true;
|
|
400
|
+
// A sitemap.xml with one `<url>` per page and locale, each listing the page
|
|
401
|
+
// in every locale (`xhtml:link`), and `lastmod` from `updatedAt`. Pages
|
|
402
|
+
// without paths (data entries) are skipped. Fetch the entries with a
|
|
403
|
+
// delivery token: drafts don't belong in a sitemap.
|
|
404
|
+
const sitemapXml = (entries, { exclude = hidden, ...options })=>{
|
|
405
|
+
const urls = [];
|
|
406
|
+
for (const entry of entries){
|
|
407
|
+
if (exclude(entry)) continue;
|
|
408
|
+
const links = alternateLinks(entry, options);
|
|
409
|
+
const alternates = links.map(({ hreflang, href })=>`<xhtml:link rel="alternate" hreflang="${escapeXml(hreflang)}" href="${escapeXml(href)}"/>`).join('');
|
|
410
|
+
for (const { hreflang, href } of links){
|
|
411
|
+
if (hreflang === 'x-default') continue;
|
|
412
|
+
urls.push(`<url><loc>${escapeXml(href)}</loc>${entry.updatedAt ? `<lastmod>${escapeXml(entry.updatedAt)}</lastmod>` : ''}${links.length > 1 ? alternates : ''}</url>`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return [
|
|
416
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
417
|
+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
|
|
418
|
+
...urls,
|
|
419
|
+
'</urlset>'
|
|
420
|
+
].join('\n');
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
export { ContentApiError, ContentClient, DEFAULT_URL, IMPROVE_BLOCK, SAFE_HREF, alternateLinks, escapeHtml, headingLevel, imageSrcSet, imageUrl, isImproveBlock, pickImproveVariant, richTextToHtml, richTextToPlainText, sitemapXml };
|
|
@@ -10,6 +10,10 @@ type Asset = {
|
|
|
10
10
|
height: number | null;
|
|
11
11
|
title: string | Record<string, string> | null;
|
|
12
12
|
alt: string | Record<string, string> | null;
|
|
13
|
+
focalPoint: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
} | null;
|
|
13
17
|
};
|
|
14
18
|
type Entry<TFields extends Record<string, unknown> = Record<string, unknown>, TContentType extends string = string> = {
|
|
15
19
|
id: string;
|
|
@@ -17,6 +21,7 @@ type Entry<TFields extends Record<string, unknown> = Record<string, unknown>, TC
|
|
|
17
21
|
name: string;
|
|
18
22
|
slug: string | null;
|
|
19
23
|
path: string | null;
|
|
24
|
+
paths: Record<string, string>;
|
|
20
25
|
publishedAt: string | null;
|
|
21
26
|
firstPublishedAt: string | null;
|
|
22
27
|
updatedAt: string;
|
|
@@ -55,6 +60,17 @@ type EntryList<TEntry extends Entry = Entry> = {
|
|
|
55
60
|
limit: number;
|
|
56
61
|
skip: number;
|
|
57
62
|
};
|
|
63
|
+
type Redirect = {
|
|
64
|
+
locale: string;
|
|
65
|
+
from: string;
|
|
66
|
+
to: string;
|
|
67
|
+
};
|
|
68
|
+
type RedirectList = {
|
|
69
|
+
items: Redirect[];
|
|
70
|
+
total: number;
|
|
71
|
+
limit: number;
|
|
72
|
+
skip: number;
|
|
73
|
+
};
|
|
58
74
|
type ContentTypeKind = 'page' | 'data' | 'block';
|
|
59
75
|
type BaseField = {
|
|
60
76
|
apiId: string;
|
|
@@ -121,5 +137,5 @@ type ModelFile = {
|
|
|
121
137
|
contentTypes: ContentTypeDefinition[];
|
|
122
138
|
};
|
|
123
139
|
|
|
124
|
-
export type { Asset as A, Block as B, ContentTypeDefinition as C, Entry as E, FieldDefinition as F, ImproveBlock as I, ModelFile as M, RichTextFormat as R,
|
|
125
|
-
//# sourceMappingURL=types-
|
|
140
|
+
export type { Asset as A, Block as B, ContentTypeDefinition as C, Entry as E, FieldDefinition as F, ImproveBlock as I, ModelFile as M, RichTextFormat as R, RedirectList as a, Redirect as b, EntryList as c, EntryStub as d, RichTextNode as e, ContentTypeKind as f, Environment as g, FieldType as h, RichTextDocument as i, RichTextMark as j };
|
|
141
|
+
//# sourceMappingURL=types-fxvuz29w.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-fxvuz29w.d.ts","sources":["../src/types.ts"],"mappings":"AAAA,KAAK,WAAW;AAChB,KAAK,cAAc;AACnB,KAAK,KAAK;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,MAAM;AAC1B,kBAAkB,MAAM;AACxB;AACA;AACA;AACA;AACA;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA,KAAK,YAAY,gBAAgB,KAAK,GAAG,KAAK,IAAI,KAAK;AACvD;AACA;AACA,cAAc,MAAM;AACpB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB,cAAc,YAAY;AAC1B;AACA,YAAY,YAAY;AACxB;AACA,KAAK,gBAAgB,GAAG,YAAY;AACpC;AACA;AACA,KAAK,SAAS,gBAAgB,KAAK,GAAG,KAAK;AAC3C;AACA;AACA;AACA;AACA;AACA,KAAK,QAAQ;AACb;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,eAAe,GAAG,SAAS;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS,GAAG,eAAe;AAChC,KAAK,qBAAqB;AAC1B;AACA;AACA,UAAU,eAAe;AACzB;AACA,YAAY,eAAe;AAC3B;AACA,KAAK,SAAS;AACd;AACA;AACA,kBAAkB,qBAAqB;AACvC;;","names":[]}
|
package/dist/webhooks.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ type WebhookEntry = {
|
|
|
5
5
|
contentType: string;
|
|
6
6
|
name: string;
|
|
7
7
|
path: string | null;
|
|
8
|
+
paths: Record<string, string>;
|
|
8
9
|
};
|
|
9
10
|
type WebhookEventOf<TType extends string, TData> = {
|
|
10
11
|
type: TType;
|
|
@@ -25,6 +26,7 @@ type EntryDeletedEvent = WebhookEventOf<'entry.deleted', {
|
|
|
25
26
|
type EntryMovedEvent = WebhookEventOf<'entry.moved', {
|
|
26
27
|
entry: WebhookEntry;
|
|
27
28
|
previousPath: string | null;
|
|
29
|
+
previousPaths: Record<string, string>;
|
|
28
30
|
}>;
|
|
29
31
|
type AssetUpdatedEvent = WebhookEventOf<'asset.updated', {
|
|
30
32
|
asset: {
|
package/dist/webhooks.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.mts","sources":["../src/webhooks.ts"],"mappings":";;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,iBAAiB,WAAW;AAC5B;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC,WAAW,YAAY;AACvB;AACA,KAAK,qBAAqB,GAAG,cAAc;AAC3C,WAAW,YAAY;AACvB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC,WAAW,YAAY;AACvB;AACA,KAAK,eAAe,GAAG,cAAc;AACrC,WAAW,YAAY;AACvB;AACA;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA,aAAa,YAAY;AACzB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC;AACA;AACA;AACA,KAAK,gBAAgB,GAAG,cAAc,iBAAiB,MAAM;AAC7D,KAAK,YAAY,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,eAAe,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB;AACtL,KAAK,gBAAgB,GAAG,YAAY;AACpC,cAAc,wBAAwB,SAAS,KAAK;AACpD;AACA;AACA,KAAK,cAAc;AACnB;AACA,aAAa,OAAO,GAAG,MAAM,0CAA0C,QAAQ;AAC/E;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA,cAAc,aAAa,YAAY,OAAO,GAAG,cAAc,wCAAwC,oBAAoB,KAAK,OAAO,CAAC,YAAY;AACpJ,KAAK,qBAAqB;AAC1B;AACA,qBAAqB,YAAY;AACjC;AACA;AACA;AACA;AACA,cAAc,oBAAoB,mCAAmC,qBAAqB,eAAe,OAAO,KAAK,OAAO,CAAC,QAAQ","names":[]}
|
|
1
|
+
{"version":3,"file":"webhooks.d.mts","sources":["../src/webhooks.ts"],"mappings":";;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,iBAAiB,WAAW;AAC5B;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC,WAAW,YAAY;AACvB;AACA,KAAK,qBAAqB,GAAG,cAAc;AAC3C,WAAW,YAAY;AACvB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC,WAAW,YAAY;AACvB;AACA,KAAK,eAAe,GAAG,cAAc;AACrC,WAAW,YAAY;AACvB;AACA,mBAAmB,MAAM;AACzB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA,aAAa,YAAY;AACzB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC;AACA;AACA;AACA,KAAK,gBAAgB,GAAG,cAAc,iBAAiB,MAAM;AAC7D,KAAK,YAAY,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,eAAe,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB;AACtL,KAAK,gBAAgB,GAAG,YAAY;AACpC,cAAc,wBAAwB,SAAS,KAAK;AACpD;AACA;AACA,KAAK,cAAc;AACnB;AACA,aAAa,OAAO,GAAG,MAAM,0CAA0C,QAAQ;AAC/E;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA,cAAc,aAAa,YAAY,OAAO,GAAG,cAAc,wCAAwC,oBAAoB,KAAK,OAAO,CAAC,YAAY;AACpJ,KAAK,qBAAqB;AAC1B;AACA,qBAAqB,YAAY;AACjC;AACA;AACA;AACA;AACA,cAAc,oBAAoB,mCAAmC,qBAAqB,eAAe,OAAO,KAAK,OAAO,CAAC,QAAQ","names":[]}
|
package/dist/webhooks.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ type WebhookEntry = {
|
|
|
5
5
|
contentType: string;
|
|
6
6
|
name: string;
|
|
7
7
|
path: string | null;
|
|
8
|
+
paths: Record<string, string>;
|
|
8
9
|
};
|
|
9
10
|
type WebhookEventOf<TType extends string, TData> = {
|
|
10
11
|
type: TType;
|
|
@@ -25,6 +26,7 @@ type EntryDeletedEvent = WebhookEventOf<'entry.deleted', {
|
|
|
25
26
|
type EntryMovedEvent = WebhookEventOf<'entry.moved', {
|
|
26
27
|
entry: WebhookEntry;
|
|
27
28
|
previousPath: string | null;
|
|
29
|
+
previousPaths: Record<string, string>;
|
|
28
30
|
}>;
|
|
29
31
|
type AssetUpdatedEvent = WebhookEventOf<'asset.updated', {
|
|
30
32
|
asset: {
|
package/dist/webhooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sources":["../src/webhooks.ts"],"mappings":";;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,iBAAiB,WAAW;AAC5B;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC,WAAW,YAAY;AACvB;AACA,KAAK,qBAAqB,GAAG,cAAc;AAC3C,WAAW,YAAY;AACvB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC,WAAW,YAAY;AACvB;AACA,KAAK,eAAe,GAAG,cAAc;AACrC,WAAW,YAAY;AACvB;AACA;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA,aAAa,YAAY;AACzB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC;AACA;AACA;AACA,KAAK,gBAAgB,GAAG,cAAc,iBAAiB,MAAM;AAC7D,KAAK,YAAY,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,eAAe,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB;AACtL,KAAK,gBAAgB,GAAG,YAAY;AACpC,cAAc,wBAAwB,SAAS,KAAK;AACpD;AACA;AACA,KAAK,cAAc;AACnB;AACA,aAAa,OAAO,GAAG,MAAM,0CAA0C,QAAQ;AAC/E;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA,cAAc,aAAa,YAAY,OAAO,GAAG,cAAc,wCAAwC,oBAAoB,KAAK,OAAO,CAAC,YAAY;AACpJ,KAAK,qBAAqB;AAC1B;AACA,qBAAqB,YAAY;AACjC;AACA;AACA;AACA;AACA,cAAc,oBAAoB,mCAAmC,qBAAqB,eAAe,OAAO,KAAK,OAAO,CAAC,QAAQ","names":[]}
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sources":["../src/webhooks.ts"],"mappings":";;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB;AACA,KAAK,cAAc;AACnB;AACA;AACA;AACA,iBAAiB,WAAW;AAC5B;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC,WAAW,YAAY;AACvB;AACA,KAAK,qBAAqB,GAAG,cAAc;AAC3C,WAAW,YAAY;AACvB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC,WAAW,YAAY;AACvB;AACA,KAAK,eAAe,GAAG,cAAc;AACrC,WAAW,YAAY;AACvB;AACA,mBAAmB,MAAM;AACzB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA,aAAa,YAAY;AACzB;AACA,KAAK,iBAAiB,GAAG,cAAc;AACvC;AACA;AACA;AACA;AACA;AACA,KAAK,mBAAmB,GAAG,cAAc;AACzC;AACA;AACA;AACA,KAAK,gBAAgB,GAAG,cAAc,iBAAiB,MAAM;AAC7D,KAAK,YAAY,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,eAAe,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB;AACtL,KAAK,gBAAgB,GAAG,YAAY;AACpC,cAAc,wBAAwB,SAAS,KAAK;AACpD;AACA;AACA,KAAK,cAAc;AACnB;AACA,aAAa,OAAO,GAAG,MAAM,0CAA0C,QAAQ;AAC/E;AACA,KAAK,oBAAoB;AACzB;AACA;AACA;AACA,cAAc,aAAa,YAAY,OAAO,GAAG,cAAc,wCAAwC,oBAAoB,KAAK,OAAO,CAAC,YAAY;AACpJ,KAAK,qBAAqB;AAC1B;AACA,qBAAqB,YAAY;AACjC;AACA;AACA;AACA;AACA,cAAc,oBAAoB,mCAAmC,qBAAqB,eAAe,OAAO,KAAK,OAAO,CAAC,QAAQ","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obelism/content",
|
|
3
3
|
"description": "Obelism Content SDK: the delivery API client, the visual editor bridge with prompt editing, editing on your own site, webhook verification, and the content model CLI",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cms",
|
|
7
7
|
"headless-cms",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-nyvor49r.d.ts","sources":["../src/types.ts"],"mappings":"AAAA,KAAK,WAAW;AAChB,KAAK,cAAc;AACnB,KAAK,KAAK;AACV;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,MAAM;AAC1B,kBAAkB,MAAM;AACxB;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS;AACd;AACA;AACA;AACA,KAAK,KAAK,iBAAiB,MAAM,oBAAoB,MAAM;AAC3D;AACA;AACA;AACA,KAAK,YAAY,gBAAgB,KAAK,GAAG,KAAK,IAAI,KAAK;AACvD;AACA;AACA,cAAc,MAAM;AACpB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB;AACA,KAAK,YAAY;AACjB;AACA,YAAY,MAAM;AAClB,cAAc,YAAY;AAC1B;AACA,YAAY,YAAY;AACxB;AACA,KAAK,gBAAgB,GAAG,YAAY;AACpC;AACA;AACA,KAAK,SAAS,gBAAgB,KAAK,GAAG,KAAK;AAC3C;AACA;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,KAAK,SAAS;AACd;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,eAAe,GAAG,SAAS;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,SAAS,GAAG,eAAe;AAChC,KAAK,qBAAqB;AAC1B;AACA;AACA,UAAU,eAAe;AACzB;AACA,YAAY,eAAe;AAC3B;AACA,KAAK,SAAS;AACd;AACA;AACA,kBAAkB,qBAAqB;AACvC;;","names":[]}
|