@obelism/content 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,9 +43,43 @@ await content.getEntries({
43
43
  await content.getContentModel() // the model file
44
44
  ```
45
45
 
46
- Each method takes `{ locale, depth, richText, init }` to override the
46
+ Each method takes `{ locale, depth, richText, drafts, init }` to override the
47
47
  defaults.
48
48
 
49
+ ### Without a stored token
50
+
51
+ On Vercel, or in GitHub Actions, the client can send the identity token its
52
+ host gives it instead of a delivery or preview token. Connect the deployment
53
+ first, under **Connected deployments** on the space's page on
54
+ www.obelism.studio: each of its environments (Vercel's production, preview,
55
+ development) maps to one environment of the space, with published content,
56
+ drafts too, or management. Then pass a token source:
57
+
58
+ ```ts
59
+ import { ContentClient } from '@obelism/content'
60
+ import { vercelOidc } from '@obelism/content/oidc' // needs @vercel/oidc installed
61
+
62
+ const content = new ContentClient({
63
+ token: vercelOidc(), // or githubOidc() in a workflow
64
+ space: process.env.CONTENT_SPACE_ID, // picks one when the deployment is connected to several
65
+ environment: process.env.CONTENT_ENVIRONMENT, // refused if the rule maps it elsewhere
66
+ })
67
+
68
+ // A preview route: drafts, when the connection's rule has them
69
+ const draft = await content.getPage(path, { drafts: true })
70
+ ```
71
+
72
+ A host token reads published content unless the client or the request sets
73
+ `drafts: true`, so a site's pages and its preview can share one deployment.
74
+ `token` takes any function that gives a token per request. Requests with a
75
+ host token skip the edge cache, since the token changes every hour or two.
76
+
77
+ Locally, `vercelOidc()` works after `vercel link` and `vercel login`: it takes
78
+ the token `vercel env pull` wrote, or asks Vercel for a new one with your
79
+ sign-in. That token's environment is `development`, so the connection needs a
80
+ rule for it, and `environment` names the space environment that rule maps to.
81
+ Anyone outside the Vercel team uses a delivery token.
82
+
49
83
  Pages and folders can have a slug per locale. `getPage` looks the path up
50
84
  in the requested locale's paths, `entry.path` is the path in that locale and
51
85
  `entry.paths` the path in every locale (`{ en: 'about-us', nl: 'over-ons' }`),
@@ -63,8 +97,10 @@ with generics: `content.getPage<Entry<{ title: string }>>('home')`.
63
97
  The visual editor loads your site with
64
98
  `?cms_preview=1&cms_entry=…&cms_environment=…&cms_token=…`. `cms_token` is
65
99
  made for the person editing and reads drafts for 8 hours; read the page with
66
- it, server-side. Never show drafts because `cms_preview` is there: anyone can
67
- add it to a URL.
100
+ it, server-side, naming your space and environment: anyone with a space of
101
+ their own can make a `cms_token` for it, and only a request that names yours
102
+ refuses one for another. Never show drafts because `cms_preview` is there:
103
+ anyone can add it to a URL.
68
104
 
69
105
  ```ts
70
106
  import {
@@ -75,11 +111,16 @@ import {
75
111
 
76
112
  const preview = readPreviewParams(request.url)
77
113
  const content = preview?.token
78
- ? new ContentClient({ token: preview.token, init: { cache: 'no-store' } })
114
+ ? new ContentClient({
115
+ token: preview.token,
116
+ space: process.env.CONTENT_SPACE_ID,
117
+ environment: process.env.CONTENT_ENVIRONMENT,
118
+ init: { cache: 'no-store' },
119
+ })
79
120
  : deliveryClient
80
121
 
81
122
  const page = await content.getPage(path).catch((error) => {
82
- // A made-up or expired token: show what's published.
123
+ // A made-up, expired or other space's token: show what's published.
83
124
  if (error instanceof ContentApiError && error.status === 401) {
84
125
  return deliveryClient.getPage(path)
85
126
  }
@@ -481,7 +522,10 @@ npx @obelism/content model apply # apply it, all or nothing
481
522
  Options: `--file`, `--url` (or `$CONTENT_URL`), `--prune`, and for `apply`
482
523
  `--lock` (the environment's model becomes managed in code: read-only in the
483
524
  dashboard, the agent API and MCP) or `--unlock`. In GitHub Actions
484
- the plan is also written to the job summary.
525
+ the plan is also written to the job summary, and without
526
+ `CONTENT_MANAGEMENT_TOKEN` the job's own token is used, when the job has
527
+ `permissions: id-token: write` and the repository is connected to the space
528
+ with management for its environment.
485
529
 
486
530
  ### Types
487
531
 
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  var node_util = require('node:util');
3
3
  var config = require('../config-ci2hxmas.js');
4
+ var oidc = require('../oidc.js');
4
5
  var node_child_process = require('node:child_process');
5
6
  var promises = require('node:fs/promises');
6
7
  var node_path = require('node:path');
@@ -407,6 +408,14 @@ const ENV_VARIABLES = [
407
408
  'CONTENT_PREVIEW_TOKEN',
408
409
  'also reads drafts, server-side only (cms_p_…)'
409
410
  ],
411
+ [
412
+ 'CONTENT_SPACE_ID',
413
+ "the space the tokens are for (space_…): the editor's cms_token for any other reads nothing"
414
+ ],
415
+ [
416
+ 'CONTENT_ENVIRONMENT',
417
+ 'the environment the tokens are for'
418
+ ],
410
419
  [
411
420
  'CONTENT_MANAGEMENT_TOKEN',
412
421
  'changes the content model: content model pull|plan|apply (cms_m_…)'
@@ -536,6 +545,7 @@ const runInitCommand = async ({ url, token, dir, install: shouldInstall, cwd = p
536
545
  '',
537
546
  'Next:',
538
547
  '- Create a delivery token in Space settings and set CONTENT_DELIVERY_TOKEN (a preview token only if the site builds from drafts; the visual editor brings its own, as cms_token).',
548
+ '- Set CONTENT_SPACE_ID and CONTENT_ENVIRONMENT, and pass them as space and environment to the ContentClient you read cms_token with, so a cms_token for another space reads nothing.',
539
549
  '- Fetch content with ContentClient from @obelism/content; the examples show what each type looks like.',
540
550
  ...improve.enabled ? [
541
551
  "- Render _improve blocks: ask improve's SDK for the visitor's option (getTestValue / getFlagValue with the block's slug) and render pickImproveVariant(block, option)."
@@ -742,7 +752,10 @@ const USAGE = `Usage: content init [--dir content] [--url origin] [--skip-instal
742
752
  --unlock apply also gives the model back to the dashboard
743
753
 
744
754
  Needs CONTENT_MANAGEMENT_TOKEN: a management token from Space settings
745
- (not for types --file).`;
755
+ (not for types --file). In GitHub Actions with \`permissions: id-token:
756
+ write\`, the job's own token works instead, once the repository is connected
757
+ to the space with management (Connected deployments, on its page on
758
+ www.obelism.studio).`;
746
759
  const main = async ()=>{
747
760
  let parsed;
748
761
  try {
@@ -795,7 +808,7 @@ const main = async ()=>{
795
808
  const isTypes = group === 'types' && command === undefined;
796
809
  const modelCommand = group === 'model' && (command === 'pull' || command === 'plan' || command === 'apply') ? command : null;
797
810
  if (!isInit && !isTypes && !modelCommand) throw new CliError(USAGE, 2);
798
- const token = process.env.CONTENT_MANAGEMENT_TOKEN;
811
+ const token = process.env.CONTENT_MANAGEMENT_TOKEN || (process.env.ACTIONS_ID_TOKEN_REQUEST_URL ? await oidc.githubOidc()() : undefined);
799
812
  const url = config.normalizeUrl(values.url ?? process.env.CONTENT_URL ?? config.DEFAULT_URL);
800
813
  // With --file, types needs no token.
801
814
  if (isTypes) {
@@ -257,4 +257,4 @@ button.chip:disabled { opacity: .5; }
257
257
  .empty { padding: 8px 12px; color: rgb(var(--theme-foreground-muted-rgb)); }
258
258
  .empty.error { color: var(--theme-error); }
259
259
  [hidden] { display: none !important; }
260
- `,N=(e,t,r={},...o)=>{let n=e.createElement(t);for(let t of(Object.assign(n,r),o))n.appendChild("string"==typeof t?e.createTextNode(t):t);return n},M=(e,t)=>{let r=N(e,"span",{className:"icon"});return r.setAttribute("aria-hidden","true"),r.innerHTML=`<svg viewBox="0 0 24 24">${L[t]}</svg>`,r},I=(e,t,r={})=>{let o,n={...P,...r.labels},a=N(e,"div");a.setAttribute("data-obelism-content-prompt","");let i=a.attachShadow({mode:"open"}),l="site"===t.mode,s=N(e,"span",{textContent:n.open}),d=N(e,"button",{className:"pill",type:"button"},s,((o=M(e,"mark")).classList.add("mark"),o)),c=N(e,"span",{className:"pill compact"},`${n.viewingDrafts} \xb7 `),p=N(e,"button",{className:"link",type:"button",textContent:n.signOut});c.appendChild(p);let g=N(e,"p",{className:"status"});g.setAttribute("role","status");let u=N(e,"textarea",{placeholder:n.placeholder,rows:1});u.setAttribute("aria-label",n.open);let h=N(e,"div",{className:"form-loader",textContent:n.formLoading});h.setAttribute("role","status");let m=N(e,"div",{className:"form"},h),b=M(e,"page"),f=M(e,"block"),v=N(e,"span"),y=N(e,"button",{type:"button",title:n.clearBlock});y.setAttribute("aria-label",n.clearBlock),y.appendChild(M(e,"close"));let x=N(e,"span",{className:"chip scope"},b,f,v,y),w=N(e,"button",{className:"action chip",type:"button"},M(e,"undo"),n.undo),k=t=>{let r=N(e,"button",{type:"button",textContent:t});return r.setAttribute("aria-pressed","false"),r},E=k(n.modePrompt),C=k(n.form),L=N(e,"span",{className:"modes"},E,C);L.setAttribute("role","group"),L.setAttribute("aria-label",n.modes);let I=N(e,"button",{className:"action chip",type:"button"},M(e,"newPage"),n.newPage),U=N(e,"button",{className:"action chip product",type:"button"},M(e,"newPage"),n.addProductPage),$=N(e,"button",{type:"button",title:n.newPageCancel});$.setAttribute("aria-label",n.newPageCancel),$.appendChild(M(e,"close"));let O=N(e,"span",{className:"chip scope"},M(e,"newPage"),N(e,"span",{textContent:n.newPage}),$),R=N(e,"input",{type:"text",placeholder:n.pathPlaceholder,autocomplete:"off",spellcheck:!1}),j=N(e,"label",{className:"path"},N(e,"span",{textContent:n.path}),R),z=N(e,"button",{className:"close icon-button",type:"button",title:n.close},M(e,"close"));z.setAttribute("aria-label",n.close);let T=M(e,"minimize"),D=M(e,"expand"),F=N(e,"button",{className:"icon-button",type:"button"},T,D),q=N(e,"button",{className:"action primary icon-button",type:"submit"},N(e,"span",{className:"visually-hidden",textContent:n.send}),M(e,"send"));q.title=n.send;let V=N(e,"span",{className:"divider"}),B=N(e,"div",{className:"toolbar"},x,O,L,w,U,I,N(e,"span",{className:"spacer"}),F,z,V,q),H=N(e,"span",{textContent:n.pages}),W=N(e,"button",{className:"chip page-chip",type:"button",title:n.pages},M(e,"page"),H,M(e,"chevrons"));W.setAttribute("aria-haspopup","listbox"),W.setAttribute("aria-expanded","false"),W.setAttribute("aria-controls","obelism-pages");let Y=N(e,"input",{type:"text",placeholder:n.pagesSearch,autocomplete:"off",spellcheck:!1});Y.setAttribute("role","combobox"),Y.setAttribute("aria-label",n.pagesSearch),Y.setAttribute("aria-autocomplete","list"),Y.setAttribute("aria-expanded","true"),Y.setAttribute("aria-controls","obelism-pages");let X=N(e,"ul",{className:"options",id:"obelism-pages"});X.setAttribute("role","listbox"),X.setAttribute("aria-label",n.pages);let _=N(e,"p",{className:"empty"}),J=N(e,"div",{className:"pages"},N(e,"label",{className:"search"},M(e,"search"),Y),X,_),Z=N(e,"slot");Z.name="actions-footer";let K=N(e,"slot");K.name="actions-bar",r.actions&&(r.actions.slot="actions-bar",a.appendChild(r.actions));let G=N(e,"span",{className:"unsaved"});G.appendChild(N(e,"span",{className:"dot"})),G.appendChild(e.createTextNode(n.unsaved));let Q=N(e,"button",{className:"link",type:"button",textContent:n.signOut}),ee=N(e,"button",{className:"action chip",type:"button",textContent:n.save}),et=N(e,"button",{className:"action chip",type:"button",textContent:n.publish}),er=N(e,"div",{className:"footer"},W,G,N(e,"span",{className:"spacer"}),Z,Q,ee,et),eo=N(e,"form",{className:"panel"},g,j,u,m,B,J,er),en=N(e,"div",{className:"root"},N(e,"div",{className:"bar"},d,c,K),eo);i.append(N(e,"style",{textContent:S}),en);let ea=e.defaultView,ei=e=>{"center"===e?delete en.dataset.side:en.dataset.side=e};ei((()=>{try{let e=ea?.localStorage.getItem(A);return"left"===e||"right"===e?e:"center"}catch{return"center"}})());let el=null,es=!1;en.addEventListener("pointerdown",e=>{if(es=!1,0!==e.button||!e.isPrimary)return;let t=e.target.closest("button, input, textarea, select, iframe, a, label, .pages, .form");if(t&&t!==d)return;let{left:r,right:o}=en.getBoundingClientRect();el={id:e.pointerId,startX:e.clientX,x:e.clientX,velocity:0,time:e.timeStamp,moved:!1,min:Math.min(0,16-r),max:Math.max(0,(ea?.innerWidth??0)-16-o)}}),en.addEventListener("pointermove",e=>{if(!el||e.pointerId!==el.id)return;let t=e.clientX-el.startX;if(!el.moved){if(6>Math.abs(t))return;el.moved=!0,en.classList.add("dragging");try{en.setPointerCapture(e.pointerId)}catch{}}let r=e.timeStamp-el.time;r>0&&(el.velocity=(e.clientX-el.x)/r),el.x=e.clientX,el.time=e.timeStamp;let o=Math.min(Math.max(t,el.min),el.max);en.style.translate=`${o+(t-o)/3}px 0`});let ed=e=>{if(!el||e.pointerId!==el.id)return;let{moved:t,x:r,velocity:o,time:n}=el;if(el=null,!t)return;es=!0;let a=en.getBoundingClientRect().left,i=ea?.innerWidth??0,l=r+(e.timeStamp-n>80?0:Math.min(Math.max(150*o,-120),120)),s=l<i/3?"left":l>2*i/3?"right":"center";ei(s),en.style.translate="";let d=en.getBoundingClientRect().left;en.style.translate=`${a-d}px 0`,en.offsetWidth,en.classList.remove("dragging"),en.style.translate="";try{ea?.localStorage.setItem(A,s)}catch{}};en.addEventListener("pointerup",ed),en.addEventListener("pointercancel",ed),en.addEventListener("click",e=>{es&&(es=!1,e.preventDefault(),e.stopPropagation())},!0);let ec=!1,ep=!1,eg=!1,eu=null,eh=!1,em=null,eb=!1,ef=!1,ev=null,ey=null,ex=null,ew=0,ek=e=>({text:e instanceof Error?e.message:String(e),error:!0}),eE=e=>{let o=e.features.includes("prompt"),a=e.saving||e.publishing,i=e.prompting||a;en.hidden=!o&&!l,d.hidden=ec||!o,c.hidden=o||!l,eo.hidden=!ec||!o;let k=l&&e.features.includes("create"),P=l&&null===t.entryId,A=P&&k?t.product?"product":t.collection?"collection":null:null,S=null!==A,N="collection"===A?{add:n.addCollectionPage,adding:n.addingCollectionPage,hint:n.collectionPageHint}:{add:n.addProductPage,adding:n.addingProductPage,hint:n.productPageHint};U.lastChild.textContent=N.add;let M=P&&k&&!S&&!r.promptWithoutPage;k||(eg=!1),M&&(eg=!0),(eg||P||!l)&&(eh=!1);let z=ec&&o&&eh;z&&!em?eA():!z&&em&&eL();let Y=S&&!eg;s.textContent=Y?N.add:M?n.newPage:n.open,$.hidden=M,x.hidden=eg||Y,O.hidden=!eg,U.hidden=!Y,U.disabled=i,I.hidden=eg||eh||!k,I.disabled=i,ep&&(ef=!1),j.hidden=!eg||ef||ep,R.disabled=i,u.hidden=eh||ef||ep||Y,m.hidden=!eh||ef||ep,m.classList.toggle("loading",eb),h.hidden=!eb,B.hidden=ef,J.hidden=!ef,u.placeholder=eg?n.newPagePlaceholder:n.placeholder,v.textContent=e.selectedUid?n.scopeBlock:P?n.scopeNoPage:n.scopeEntry,y.hidden=!e.selectedUid,b.hidden=!!e.selectedUid,f.hidden=!e.selectedUid,L.hidden=!l||eg||P||!e.features.includes("form"),E.setAttribute("aria-pressed",String(!eh)),C.setAttribute("aria-pressed",String(eh)),L.querySelectorAll("button").forEach(e=>{e.disabled=i}),w.hidden=eg||!e.canUndo,w.disabled=i,q.hidden=eh||ep||Y,V.hidden=eh||ep||Y;let X=ep?n.expand:n.minimize;F.title=X,F.setAttribute("aria-label",X),F.setAttribute("aria-expanded",String(!ep)),T.hidden=ep,D.hidden=!ep,q.disabled=i||""===u.value.trim()||eg&&""===R.value.trim(),u.disabled=i,ee.hidden=!e.features.includes("save")||P,ee.disabled=i||!e.unsaved,ee.textContent=e.saving?n.saving:n.save,et.hidden=!e.features.includes("publish")||P,et.disabled=i,et.textContent=e.publishing?n.publishing:n.publish,Q.hidden=!l||!1===r.signOut,p.hidden=!1===r.signOut,r.actions&&(r.actions.slot=ec&&o?"actions-footer":"actions-bar"),er.hidden=!l,G.hidden=!e.unsaved,W.hidden=!l,W.setAttribute("aria-expanded",String(ef)),H.textContent=ev?.find(({entryId:e})=>e===t.entryId)?.name??n.pages;let _=eh&&eb&&!ef,Z=e.prompting,K=e.prompting?eg?n.creating:Y?N.adding:n.working:_?"":eu?.text??(Y?N.hint:!l||eg||ef||e.selectedUid?"":n.selectHint);g.textContent=K,g.hidden=!K||ep,g.className=`status${Z?" working":""}${!i&&!_&&eu?.error?" error":""}`},eC=()=>eE(t.getState()),eP=()=>{u.style.height="auto",u.style.height=`${u.scrollHeight}px`},eA=()=>{eb=!0,m.classList.add("loading");let e=t.openForm(m,{title:n.formTitle,onReady:()=>{em===e&&(eb=!1,eu=null,eC())},onError:t=>{em===e&&(eb=!1,eu={text:t,error:!0},eC())}});em=e},eL=()=>{em?.close(),em=null,eb=!1},eS=e=>{eh!==e&&(eh=e,eu=null,eC(),eh||u.focus())},eN=async()=>{if(eh)return;let e=u.value.trim();if(e&&!t.getState().prompting){eu=null;try{if(eg){let r=R.value.trim();if(!r)return R.focus();await t.createPage(r,e),ev=null,ey=null,eu={text:n.created,error:!1}}else{let{summary:r}=await t.prompt(e);eu={text:r,error:!1}}u.value="",eP()}catch(e){eu=ek(e)}eC(),u.focus()}},eM=()=>(ey??(ey=t.pages().then(e=>{ev=e,ex=null,eU(),eC()},e=>{ey=null,ex=ek(e).text,eU()})),ey),eI=()=>{let e=Y.value.trim().toLowerCase();return(ev??[]).filter(t=>!e||t.name.toLowerCase().includes(e)||t.url.toLowerCase().includes(e)||`/${t.path}`.toLowerCase().includes(e))},eU=()=>{let r=eI();ew=Math.max(0,Math.min(ew,r.length-1)),X.textContent="",r.forEach((r,o)=>{let a=r.entryId===t.entryId,i=N(e,"li",{className:`option${o===ew?" active":""}`,id:`obelism-page-${o}`},N(e,"span",{className:"option-text"},N(e,"span",{textContent:r.name}),N(e,"span",{className:"option-path",textContent:r.url})));if(i.setAttribute("role","option"),i.setAttribute("aria-selected",String(o===ew)),"published"!==r.status&&i.appendChild(N(e,"span",{className:"badge",textContent:"draft"===r.status?n.pageDraft:n.pageChanged})),a){i.setAttribute("aria-current","page");let t=M(e,"check");t.removeAttribute("aria-hidden"),t.setAttribute("role","img"),t.setAttribute("aria-label",n.pageCurrent),i.appendChild(t)}i.addEventListener("click",()=>eO(r)),X.appendChild(i)});let o=X.children[ew];o?(Y.setAttribute("aria-activedescendant",o.id),o.scrollIntoView?.({block:"nearest"})):Y.removeAttribute("aria-activedescendant"),_.textContent=(ex??(ev?"":n.pagesLoading))||(0===r.length?n.pagesEmpty:""),_.hidden=!_.textContent,_.className=`empty${ex?" error":""}`},e$=e=>{eu=null,(ef=e)&&(Y.value="",ew=Math.max(0,(ev??[]).findIndex(({entryId:e})=>e===t.entryId)),eM(),eU()),eC(),(ef?Y:W).focus()},eO=e=>{if(e.entryId===t.entryId)return e$(!1);t.openPage(e)},eR=e=>{(ec=e)||(ef=!1,ep=!1),eC(),l&&t.setSelecting(ec&&!eg),ec&&l&&eM()},ej=e=>{eg=e,eu=null,t.setSelecting(ec&&!eg),eg&&t.select(null),eC(),(eg?R:u).focus()},ez=async e=>{eu=null;try{await t[e](),eu={text:"save"===e?n.saved:n.published,error:!1}}catch(e){eu=ek(e)}eC()};d.addEventListener("click",()=>{eR(!0),eh||(eg?R:u).focus()}),z.addEventListener("click",()=>eR(!1)),F.addEventListener("click",()=>{ep=!ep,eC(),ep||(eh?F:u).focus()}),eo.addEventListener("click",e=>{if(!ep)return;let t=e.target.closest("button");t&&t!==F&&t!==z&&(ep=!1,eC())},!0),ee.addEventListener("click",()=>void ez("save")),et.addEventListener("click",()=>void ez("publish")),Q.addEventListener("click",()=>t.signOut()),p.addEventListener("click",()=>t.signOut()),y.addEventListener("click",()=>t.select(null)),I.addEventListener("click",()=>ej(!0)),U.addEventListener("click",async()=>{eu=null;try{t.product?(await t.createProductPage(),eu={text:n.addedProductPage,error:!1}):(await t.createCollectionPage(),eu={text:n.addedCollectionPage,error:!1})}catch(e){eu=ek(e)}eC()}),$.addEventListener("click",()=>ej(!1)),E.addEventListener("click",()=>eS(!1)),C.addEventListener("click",()=>eS(!0)),W.addEventListener("click",()=>e$(!ef)),R.addEventListener("input",eC),R.addEventListener("keydown",e=>{"Enter"!==e.key||e.isComposing?"Escape"===e.key&&eR(!1):(e.preventDefault(),u.focus())}),Y.addEventListener("input",()=>{ew=0,eU()}),Y.addEventListener("keydown",e=>{let t=X.children.length;if("ArrowDown"===e.key||"ArrowUp"===e.key){if(e.preventDefault(),0===t)return;ew=(ew+("ArrowDown"===e.key?1:-1)+t)%t,eU()}else if("Enter"!==e.key||e.isComposing)"Escape"===e.key&&(e.preventDefault(),e$(!1));else{e.preventDefault();let t=eI()[ew];t&&eO(t)}}),w.addEventListener("click",()=>{eu=null,t.undo()}),eo.addEventListener("submit",e=>{e.preventDefault(),eN()}),u.addEventListener("input",()=>{eP(),eC()}),u.addEventListener("keydown",e=>{"Enter"!==e.key||e.shiftKey||e.isComposing?"Escape"===e.key&&eR(!1):(e.preventDefault(),eN())});let eT=t.on("change",eE);return eC(),e.body.appendChild(a),()=>{eT(),eL(),a.remove()}},U="https://content.obelism.studio",$=(e,t)=>JSON.stringify(e)===JSON.stringify(t),O=Symbol.for("@obelism/content/bridge"),R=C({isEditing:!1,entryId:null,on:()=>{}},new URLSearchParams),j=({url:e,prompt:c=!0,pagePath:p,productPath:m,collectionPath:y}={})=>{let x;if("u"<typeof window)return R;let P=window;if(P[O])return P[O];if(P.ObelismBridge)x=C(P.ObelismBridge,new URLSearchParams(P.location.search));else{let C=(x=((e,r)=>{let o,n,c,p,m,y=new URLSearchParams(e.location.search),x=e.parent!==e&&y.has("cms_preview"),C=v(),P=[],A={...f,selecting:!0},L=e=>{A={...A,...e},C.emit("change",A)},S={isEditing:x,mode:x?"editor":null,entryId:y.get("cms_entry"),environment:y.get(a),product:null,collection:null,on:C.on,getState:()=>A,...w,destroy:()=>{P.splice(0).forEach(e=>e())}};if(!x)return S;let N=e.document,M=(t,o={})=>{e.parent.postMessage({source:h,type:t,...o},r)},I=N.createElement("style");I.textContent=b(),N.head.appendChild(I),P.push(()=>I.remove());let U=(e,t)=>{if(N.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),A.selectedUid!==(e??null)&&L({selectedUid:e??null}),!e)return;let r=N.querySelector(E(e));r&&(r.setAttribute("data-cms-selected",""),t&&r.scrollIntoView({behavior:"smooth",block:"center"}))},$=e=>{let r=e.target,o=r?.closest?.(`[${t}]`);if(!o)return;e.preventDefault(),e.stopPropagation();let n=o.getAttribute(t);U(n,!1),M("select",{uid:n})};N.addEventListener("click",$,!0),P.push(()=>N.removeEventListener("click",$,!0));let O=()=>Math.ceil(Math.max(N.documentElement.scrollHeight,N.body?.scrollHeight??0)),R=0,j=()=>{R=O(),M("ready",{url:e.location.href,height:R})},z=e.ResizeObserver;if("function"==typeof z){let e=new z(()=>{let e=O();e!==R&&(R=e,M("size",{height:e}))});e.observe(N.documentElement),N.body&&e.observe(N.body),P.push(()=>e.disconnect())}let T=new Map,D=0,F=e=>{let t=T.get(e);return t?(clearTimeout(t.timer),T.delete(e),0===T.size&&L({prompting:!1}),t):null};S.prompt=(e,t={})=>{let r=e.trim();if(!r)return Promise.reject(Error("Say what to change"));if(!A.features.includes("prompt"))return Promise.reject(Error("The editor does not offer prompts here"));let o=`prompt_${++D}`,n=void 0===t.uid?A.selectedUid:t.uid;return new Promise((e,t)=>{let a=setTimeout(()=>{F(o)?.reject(Error("The editor did not answer in time"))},12e4);T.set(o,{resolve:e,reject:t,timer:a}),L({prompting:!0}),M("prompt",{id:o,prompt:r,uid:n})})},S.undo=()=>M("undo"),S.select=e=>{U(e,!1),M("select",{uid:e})};let q=null;P.push(()=>q?.destroy()),P.push(()=>{for(let e of T.keys())F(e)?.reject(Error("The bridge was destroyed"))});let V=(o=new WeakSet,n=null,c=null,p=!1,m=()=>{if(null!==n){for(let e of Array.from(N.styleSheets)){let t;if(!o.has(e)){try{t=e.cssRules}catch{continue}o.add(e),u(t,n)}}N.querySelectorAll('[style*="vh"]').forEach(e=>{g(e.style,n)})}},{freeze(e){if(null!==n||!(e>0))return;n=e,m();let t=N.defaultView?.MutationObserver;t&&(c=new t(()=>{if(p)return;p=!0;let e=()=>{p=!1,m()};N.defaultView?.requestAnimationFrame?N.defaultView.requestAnimationFrame(e):e()})).observe(N.documentElement,{childList:!0,subtree:!0})},destroy(){c?.disconnect()}});P.push(()=>V.destroy());let B=t=>{if(t.origin!==r||t.source!==e.parent)return;let o=t.data;if(o&&"obelism-content-editor"===o.source){if("connect"===o.type)j();else if("update"===o.type&&o.entry)C.emit("update",{entry:o.entry,selectedUid:o.selectedUid??null}),e.requestAnimationFrame(()=>U(o.selectedUid,!1)),q?.refresh();else if("select"===o.type)U(o.uid,!0);else if("saved"===o.type||"published"===o.type)C.emit(o.type,{}),0===C.listeners.update.size&&e.location.reload();else if("features"===o.type&&Array.isArray(o.features))L({features:o.features.filter(e=>"string"==typeof e)});else if("stats"===o.type)L({stats:(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return{};let t={};for(let[r,o]of Object.entries(e)){let{clicks:e,clickers:n,viewers:a,rate:i}=o??{};k(e)&&k(n)&&k(a)&&k(i)&&(t[r]={clicks:e,clickers:n,viewers:a,rate:i})}return t})(o.stats),statsVisible:!1!==o.visible}),(q||Object.keys(A.stats).length)&&(q??(q=((e,{selector:t,onSelect:r,labels:o})=>{let n=e.document,a={...i,...o},c=n.createElement("div");c.setAttribute("data-obelism-stats","");let p=c.attachShadow({mode:"open"}),g=n.createElement("style");g.textContent=s;let u=n.createElement("div");u.className="layer",p.append(g,u),n.body.appendChild(c);let h={},m=!1,b=null,f=()=>{if(b=null,u.replaceChildren(),u.hidden=!m,!m)return;let o=e.innerHeight;for(let[i,s]of Object.entries(h)){let c=n.querySelector(t(i));if(!c)continue;let p=c.getBoundingClientRect();if(!p.width&&!p.height||p.bottom<0||p.top>o)continue;let g=n.createElement("button");g.type="button",g.className="badge",g.dataset.uid=i,g.textContent=l(a.clicked,{rate:Math.round(100*s.rate)}),g.title=l(a.title,{clickers:s.clickers,viewers:s.viewers,clicks:s.clicks});let{place:h,top:m,left:b}=d(p,e.innerWidth);g.dataset.place=h,g.style.top=`${m}px`,g.style.left=`${b}px`,g.addEventListener("click",()=>r(i)),u.appendChild(g)}},v=()=>{null===b&&m&&(b=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(n.body,{childList:!0,subtree:!0}),{update(t,r){h=t,m=r,null!==b&&(e.cancelAnimationFrame?.(b),b=null),f()},refresh:v,destroy(){null!==b&&e.cancelAnimationFrame?.(b),e.removeEventListener("scroll",v,{capture:!0}),e.removeEventListener("resize",v),x?.disconnect(),c.remove()}}})(e,{selector:E,onSelect:e=>S.select(e)})),q.update(A.stats,A.statsVisible));else if("viewport"===o.type&&"number"==typeof o.height)V.freeze(o.height);else if("promptState"===o.type)L({canUndo:!0===o.canUndo});else if("promptResult"===o.type&&"string"==typeof o.id){let e=F(o.id);if(!e)return;!0===o.ok?e.resolve({summary:"string"==typeof o.summary?o.summary:""}):e.reject(Error("string"==typeof o.error?o.error:"The prompt failed"))}}};return e.addEventListener("message",B),P.push(()=>e.removeEventListener("message",B)),"loading"===N.readyState?(N.addEventListener("DOMContentLoaded",j,{once:!0}),P.push(()=>N.removeEventListener("DOMContentLoaded",j))):j(),S})(P,new URL(e||U).origin)).isEditing||P.parent!==P?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})(P.document);if(C&&(x=((e,a,{pagePath:i=e=>`/${e}`,productPath:l,collectionPath:s}={})=>{let d=e.document,c=v(),p=[],g=new Map,u=[],m=new Set,y=null,x=null,w=-1/0,k=new Map,C=f,P=e=>{C={...C,...e},c.emit("change",C)},A=()=>P({unsaved:[...g.values()].some(({content:e,saved:t})=>!$(e,t)),canUndo:u.length>0}),L=async(t,r)=>{let o=await e.fetch(`${a}/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)}),n=await o.json().catch(()=>({}));if(401===o.status)throw P({features:[]}),Error("You were signed out. Sign in again to edit.");if(!o.ok)throw Error("string"==typeof n.error?n.error:`Obelism Content answered ${o.status}`);return n},S=()=>d.querySelector(`[${r}]`)?.getAttribute(r)??null,N=()=>{let e=d.querySelector(`[${o}]`),t=e?.getAttribute(o),r=e?.getAttribute("data-cms-product-handle");return t&&r?{id:t,handle:r}:null},M=()=>{let e=d.querySelector(`[${n}]`),t=e?.getAttribute(n),r=e?.getAttribute("data-cms-collection-handle");return t&&r?{id:t,handle:r}:null},I=e=>{let t=e?d.querySelector(E(e)):null;return t?.closest(`[${r}]`)?.getAttribute(r)??S()},U=()=>{if(!y)return;let e=y.space.locales.map(({code:e})=>e),t=d.documentElement.lang;if(e.includes(t))return t;let r=t.split("-")[0]??"";return e.includes(r)?r:y.space.defaultLocale},O=new Map,R=async e=>{let t=g.get(e);if(t)return t;let r=O.get(e);return r||(r=L(`entries/${encodeURIComponent(e)}`).then(t=>{let r=g.get(e)??{content:t.content,saved:t.content,revision:t.draftRevision};return g.set(e,r),r}).finally(()=>O.delete(e)),O.set(e,r)),r},j=e=>{let t=void 0===e?C.selectedUid:e,r=I(t);if(!r)throw Error("Nothing on this page is marked as an entry: add editable(entry)");return{uid:t,entryId:r}},z=e=>{d.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),C.selectedUid!==e&&P({selectedUid:e}),e&&d.querySelector(E(e))?.setAttribute("data-cms-selected","")},T=async t=>{if(0===c.listeners.update.size)return;let r=g.get(t)?.content,{entry:o}=await L(`entries/${encodeURIComponent(t)}/preview`,{content:r,locale:U()});c.emit("update",{entry:o,selectedUid:C.selectedUid}),e.requestAnimationFrame(()=>z(C.selectedUid))},D=e=>console.error("[@obelism/content bridge]",e),F=t=>{c.emit(t,{}),0===c.listeners.update.size&&e.location.reload()},q=async()=>{for(let[e,t]of g){if($(t.content,t.saved))continue;let{draftRevision:r}=await L(`entries/${encodeURIComponent(e)}/save`,{content:t.content,baseRevision:t.revision});t.saved=t.content,t.revision=r,m.add(e)}u.length=0,w=-1/0,A()},V={isEditing:!0,mode:"site",get entryId(){return S()},get product(){return N()},get collection(){return M()},environment:null,on:c.on,getState:()=>C,async prompt(e,t={}){let r=e.trim();if(!r)throw Error("Say what to change");if(!C.features.includes("prompt"))throw Error("You may not edit content here");let{uid:o,entryId:n}=j(t.uid);P({prompting:!0});try{let e=await R(n),t=await L(`entries/${encodeURIComponent(n)}/prompt`,{prompt:r,content:e.content,locale:U(),uid:o});return u.push({entryId:n,content:e.content}),w=-1/0,e.content=t.content,A(),await T(n),{summary:t.summary}}finally{P({prompting:!1})}},async createPage(t,r){let o=r.trim();if(!o)throw Error("Say what the page is for");if(!t.trim())throw Error("Say where the page goes, like /pricing");if(!C.features.includes("create"))throw Error("You may not add pages here");if(C.unsaved)throw Error("Save or undo your changes before adding a page");P({prompting:!0});try{let r=await L("entries",{path:t,prompt:o,locale:U(),from:S()}),n=i(r.path,U()??"");return e.location.assign(n),{summary:r.summary,entryId:r.entryId,path:r.path,url:n}}finally{P({prompting:!1})}},async createProductPage(t=N()??void 0){if(!t)throw Error("Nothing on this page is marked as a product: add editableProduct(product)");if(!C.features.includes("create"))throw Error("You may not add pages here");if(C.unsaved)throw Error("Save or undo your changes before adding a page");P({prompting:!0});try{let r=await L("product-pages",{product:{id:t.id,handle:t.handle}});return e.location.reload(),r}finally{P({prompting:!1})}},async createCollectionPage(t=M()??void 0){if(!t)throw Error("Nothing on this page is marked as a collection: add editableCollection(collection)");if(!C.features.includes("create"))throw Error("You may not add pages here");if(C.unsaved)throw Error("Save or undo your changes before adding a page");P({prompting:!0});try{let r=await L("collection-pages",{collection:{id:t.id,handle:t.handle}});return e.location.reload(),r}finally{P({prompting:!1})}},async view(e={}){let{uid:t,entryId:r}=j(e.uid),o=await R(r),{view:n}=await L(`entries/${encodeURIComponent(r)}/view`,{content:o.content,locale:U(),uid:t});return n},async applyJson(e,t={}){if(!C.features.includes("prompt"))throw Error("You may not edit content here");if(!e||"object"!=typeof e||Array.isArray(e))throw Error("Send the fields to change as an object");let{uid:r,entryId:o}=j(t.uid);P({prompting:!0});try{let t=await R(o),n=await L(`entries/${encodeURIComponent(o)}/apply`,{changes:e,content:t.content,locale:U(),uid:r});return $(n.content,t.content)||(u.push({entryId:o,content:t.content}),w=-1/0,t.content=n.content,A(),await T(o)),{changed:n.changed??[],...n.view&&{view:n.view}}}finally{P({prompting:!1})}},openForm(t,r={}){let{title:o="Content form",onReady:n,onError:a}=r;x?.();let i=!1,l=null,s=null,d=()=>{},p=e=>{i||a?.(e instanceof Error?e.message:String(e))},m=()=>{i||(i=!0,d(),l?.destroy(),x===m&&(x=null))};x=m;let b=()=>{if(i||!l)return;let e=I(C.selectedUid);if(!e)return;let t=g.get(e);if(!t)return void R(e).then(b,p);let r={entryId:e,content:t.content,locale:U(),selectedUid:C.selectedUid};s&&s.entryId===r.entryId&&s.content===r.content&&s.locale===r.locale&&s.selectedUid===r.selectedUid||(s=r,l.show(r))},f=(e,t)=>{let r=t??s?.entryId,o=void 0===r?void 0:g.get(r);if(i||void 0===r||!o||$(e,o.content))return;let n=Date.now();n-w>=1e3&&u.push({entryId:r,content:o.content}),w=n,o.content=e,s?.entryId===r&&(s={...s,content:e}),A(),clearTimeout(k.get(r)),k.set(r,setTimeout(()=>{k.delete(r),T(r).catch(D)},300))};return(async()=>{if(!C.features.includes("form"))throw Error("You may not edit content here");await R(j(void 0).entryId);let{url:r}=await L("form",{});i||(l=((e,t,r,o,n)=>{let a=null;try{a=new URL(r)}catch{}if(!a||!/^https?:$/.test(a.protocol))throw Error("Obelism Content sent no form to open");let i=a.origin,l=t.ownerDocument.createElement("iframe");l.title=o,l.src=r;let s=!1,d=!1,c=null,p=()=>{s&&c&&l.contentWindow?.postMessage({source:h,type:"content",...c},i)},g=setTimeout(()=>{d||n.onError("The form did not open")},3e4),u=()=>{d=!0,clearTimeout(g),n.onReady()},m=e=>{if(e.origin!==i||!l.contentWindow||e.source!==l.contentWindow)return;let t=e.data;if(t&&"obelism-content-form"===t.source)if("ready"===t.type)s=!0,p(),!0!==t.loading&&u();else if("loaded"===t.type)s&&u();else{let e;if("change"!==t.type||!(e=t.content)||"object"!=typeof e||Array.isArray(e))if("height"===t.type){let e=t.height;if("number"==typeof e&&Number.isFinite(e)){let t=Math.round(Math.min(Math.max(e,80),4e3));l.style.height=`min(${t}px, 60vh)`}}else"error"===t.type&&n.onError("string"==typeof t.message&&t.message?t.message:"The form failed");else n.onChange(t.content,"string"==typeof t.entryId?t.entryId:void 0)}};return e.addEventListener("message",m),t.appendChild(l),{show(e){c=e,p()},destroy(){clearTimeout(g),e.removeEventListener("message",m),l.remove()}}})(e,t,"string"==typeof r?r:"",o,{onReady:()=>n?.(),onChange:f,onError:e=>a?.(e)}),d=c.on("change",b),b())})().catch(p),{close:m}},async pages(){let{pages:e}=await L("pages"),t=[U(),y?.space.defaultLocale];return e.flatMap(e=>{let r,o,n=t.find(t=>void 0!==t&&void 0!==e.paths[t])??Object.keys(e.paths)[0],a=void 0===n?void 0:e.paths[n];return void 0===n||void 0===a?[]:[{entryId:e.entryId,name:e.name,contentType:e.contentType,status:e.status,path:a,url:e.product?(r=e.product.handle,l?l(r,n):(y?.productPath??"/products/{handle}").replaceAll("{handle}",encodeURIComponent(r))):e.collection?(o=e.collection.handle,s?s(o,n):(y?.collectionPath??"/collections/{handle}").replaceAll("{handle}",encodeURIComponent(o))):i(a,n),product:e.product??null,collection:e.collection??null}]})},openPage(t){(!C.unsaved||e.confirm("Leave this page and lose the changes you have not saved?"))&&(g.clear(),u.length=0,A(),e.location.assign(t.url))},undo(){let e=u.pop(),t=e&&g.get(e.entryId);e&&t&&(w=-1/0,t.content=e.content,A(),T(e.entryId).catch(D))},async save(){if(!C.features.includes("save"))throw Error("You may not edit content here");P({saving:!0});try{await q()}finally{P({saving:!1})}F("saved")},async publish(){if(!C.features.includes("publish"))throw Error("You may not publish here");P({publishing:!0});try{await q();let e=S(),t=m.size>0?[...m]: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 L(`entries/${encodeURIComponent(e)}/publish`,{}),m.delete(e)}finally{P({publishing:!1})}F("published")},signOut(){if(C.unsaved&&!e.confirm("Sign out and lose the changes you have not saved?"))return;g.clear(),A();let t=e.location.pathname+e.location.search;e.location.href=`${a}/logout?return_to=${encodeURIComponent(t)}`},select(e){z(e)},setSelecting(e){e?d.documentElement.setAttribute("data-cms-selecting",""):d.documentElement.removeAttribute("data-cms-selecting"),P({selecting:e})},destroy(){x?.(),k.forEach(e=>clearTimeout(e)),k.clear(),p.splice(0).forEach(e=>e())}},B=d.createElement("style");B.textContent=b("html[data-cms-selecting] "),d.head.appendChild(B),p.push(()=>B.remove()),p.push(()=>d.documentElement.removeAttribute("data-cms-selecting"));let H=e=>{if(!C.selecting)return;let r=e.target,o=r?.closest?.(`[${t}]`);o&&(e.preventDefault(),e.stopPropagation(),z(o.getAttribute(t)))};d.addEventListener("click",H,!0),p.push(()=>d.removeEventListener("click",H,!0));let W=e=>{C.unsaved&&(e.preventDefault(),e.returnValue="")};return e.addEventListener("beforeunload",W),p.push(()=>e.removeEventListener("beforeunload",W)),L("session").then(e=>{y=e,P({features:e.features})}).catch(e=>console.error("[@obelism/content bridge]",e)),V})(P,C,{pagePath:p,productPath:m,collectionPath:y})),x.isEditing&&c){let e=P.document,t=()=>{},r=!1,o=()=>{r||(t=I(e,x,!0===c?{}:c))};e.body?o():e.addEventListener("DOMContentLoaded",o,{once:!0});let n=x.destroy;x.destroy=()=>{r=!0,e.removeEventListener("DOMContentLoaded",o),t(),n()}}}return P[O]=x,x};(e=window).ObelismBridge=j((e=>{let t=e?.dataset.url,r=t||U;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:j,mountPromptBox:I}}();
260
+ `,N=(e,t,r={},...o)=>{let n=e.createElement(t);for(let t of(Object.assign(n,r),o))n.appendChild("string"==typeof t?e.createTextNode(t):t);return n},M=(e,t)=>{let r=N(e,"span",{className:"icon"});return r.setAttribute("aria-hidden","true"),r.innerHTML=`<svg viewBox="0 0 24 24">${L[t]}</svg>`,r},I=(e,t,r={})=>{let o,n={...P,...r.labels},a=N(e,"div");a.setAttribute("data-obelism-content-prompt","");let i=a.attachShadow({mode:"open"}),l="site"===t.mode,s=N(e,"span",{textContent:n.open}),d=N(e,"button",{className:"pill",type:"button"},s,((o=M(e,"mark")).classList.add("mark"),o)),c=N(e,"span",{className:"pill compact"},`${n.viewingDrafts} \xb7 `),p=N(e,"button",{className:"link",type:"button",textContent:n.signOut});c.appendChild(p);let g=N(e,"p",{className:"status"});g.setAttribute("role","status");let u=N(e,"textarea",{placeholder:n.placeholder,rows:1});u.setAttribute("aria-label",n.open);let h=N(e,"div",{className:"form-loader",textContent:n.formLoading});h.setAttribute("role","status");let m=N(e,"div",{className:"form"},h),b=M(e,"page"),f=M(e,"block"),v=N(e,"span"),y=N(e,"button",{type:"button",title:n.clearBlock});y.setAttribute("aria-label",n.clearBlock),y.appendChild(M(e,"close"));let x=N(e,"span",{className:"chip scope"},b,f,v,y),w=N(e,"button",{className:"action chip",type:"button"},M(e,"undo"),n.undo),k=t=>{let r=N(e,"button",{type:"button",textContent:t});return r.setAttribute("aria-pressed","false"),r},E=k(n.modePrompt),C=k(n.form),L=N(e,"span",{className:"modes"},E,C);L.setAttribute("role","group"),L.setAttribute("aria-label",n.modes);let I=N(e,"button",{className:"action chip",type:"button"},M(e,"newPage"),n.newPage),U=N(e,"button",{className:"action chip product",type:"button"},M(e,"newPage"),n.addProductPage),$=N(e,"button",{type:"button",title:n.newPageCancel});$.setAttribute("aria-label",n.newPageCancel),$.appendChild(M(e,"close"));let O=N(e,"span",{className:"chip scope"},M(e,"newPage"),N(e,"span",{textContent:n.newPage}),$),R=N(e,"input",{type:"text",placeholder:n.pathPlaceholder,autocomplete:"off",spellcheck:!1}),j=N(e,"label",{className:"path"},N(e,"span",{textContent:n.path}),R),z=N(e,"button",{className:"close icon-button",type:"button",title:n.close},M(e,"close"));z.setAttribute("aria-label",n.close);let T=M(e,"minimize"),D=M(e,"expand"),F=N(e,"button",{className:"icon-button",type:"button"},T,D),q=N(e,"button",{className:"action primary icon-button",type:"submit"},N(e,"span",{className:"visually-hidden",textContent:n.send}),M(e,"send"));q.title=n.send;let V=N(e,"span",{className:"divider"}),B=N(e,"div",{className:"toolbar"},x,O,L,w,U,I,N(e,"span",{className:"spacer"}),F,z,V,q),H=N(e,"span",{textContent:n.pages}),W=N(e,"button",{className:"chip page-chip",type:"button",title:n.pages},M(e,"page"),H,M(e,"chevrons"));W.setAttribute("aria-haspopup","listbox"),W.setAttribute("aria-expanded","false"),W.setAttribute("aria-controls","obelism-pages");let Y=N(e,"input",{type:"text",placeholder:n.pagesSearch,autocomplete:"off",spellcheck:!1});Y.setAttribute("role","combobox"),Y.setAttribute("aria-label",n.pagesSearch),Y.setAttribute("aria-autocomplete","list"),Y.setAttribute("aria-expanded","true"),Y.setAttribute("aria-controls","obelism-pages");let X=N(e,"ul",{className:"options",id:"obelism-pages"});X.setAttribute("role","listbox"),X.setAttribute("aria-label",n.pages);let _=N(e,"p",{className:"empty"}),J=N(e,"div",{className:"pages"},N(e,"label",{className:"search"},M(e,"search"),Y),X,_),Z=N(e,"slot");Z.name="actions-footer";let K=N(e,"slot");K.name="actions-bar",r.actions&&(r.actions.slot="actions-bar",a.appendChild(r.actions));let G=N(e,"span",{className:"unsaved"});G.appendChild(N(e,"span",{className:"dot"})),G.appendChild(e.createTextNode(n.unsaved));let Q=N(e,"button",{className:"link",type:"button",textContent:n.signOut}),ee=N(e,"button",{className:"action chip",type:"button",textContent:n.save}),et=N(e,"button",{className:"action chip",type:"button",textContent:n.publish}),er=N(e,"div",{className:"footer"},W,G,N(e,"span",{className:"spacer"}),Z,Q,ee,et),eo=N(e,"form",{className:"panel"},g,j,u,m,B,J,er),en=N(e,"div",{className:"root"},N(e,"div",{className:"bar"},d,c,K),eo);i.append(N(e,"style",{textContent:S}),en);let ea=e.defaultView,ei=e=>{"center"===e?delete en.dataset.side:en.dataset.side=e};ei((()=>{try{let e=ea?.localStorage.getItem(A);return"left"===e||"right"===e?e:"center"}catch{return"center"}})());let el=null,es=!1;en.addEventListener("pointerdown",e=>{if(es=!1,0!==e.button||!e.isPrimary)return;let t=e.target.closest("button, input, textarea, select, iframe, a, label, .pages, .form");if(t&&t!==d)return;let{left:r,right:o}=en.getBoundingClientRect();el={id:e.pointerId,startX:e.clientX,x:e.clientX,velocity:0,time:e.timeStamp,moved:!1,min:Math.min(0,16-r),max:Math.max(0,(ea?.innerWidth??0)-16-o)}}),en.addEventListener("pointermove",e=>{if(!el||e.pointerId!==el.id)return;let t=e.clientX-el.startX;if(!el.moved){if(6>Math.abs(t))return;el.moved=!0,en.classList.add("dragging");try{en.setPointerCapture(e.pointerId)}catch{}}let r=e.timeStamp-el.time;r>0&&(el.velocity=(e.clientX-el.x)/r),el.x=e.clientX,el.time=e.timeStamp;let o=Math.min(Math.max(t,el.min),el.max);en.style.translate=`${o+(t-o)/3}px 0`});let ed=e=>{if(!el||e.pointerId!==el.id)return;let{moved:t,x:r,velocity:o,time:n}=el;if(el=null,!t)return;es=!0;let a=en.getBoundingClientRect().left,i=ea?.innerWidth??0,l=r+(e.timeStamp-n>80?0:Math.min(Math.max(150*o,-120),120)),s=l<i/3?"left":l>2*i/3?"right":"center";ei(s),en.style.translate="";let d=en.getBoundingClientRect().left;en.style.translate=`${a-d}px 0`,en.offsetWidth,en.classList.remove("dragging"),en.style.translate="";try{ea?.localStorage.setItem(A,s)}catch{}};en.addEventListener("pointerup",ed),en.addEventListener("pointercancel",ed),en.addEventListener("click",e=>{es&&(es=!1,e.preventDefault(),e.stopPropagation())},!0);let ec=!1,ep=!1,eg=!1,eu=null,eh=!1,em=null,eb=!1,ef=!1,ev=null,ey=null,ex=null,ew=0,ek=e=>({text:e instanceof Error?e.message:String(e),error:!0}),eE=e=>{let o=e.features.includes("prompt"),a=e.saving||e.publishing,i=e.prompting||a;en.hidden=!o&&!l,d.hidden=ec||!o,c.hidden=o||!l,eo.hidden=!ec||!o;let k=l&&e.features.includes("create"),P=l&&null===t.entryId,A=P&&k?t.product?"product":t.collection?"collection":null:null,S=null!==A,N="collection"===A?{add:n.addCollectionPage,adding:n.addingCollectionPage,hint:n.collectionPageHint}:{add:n.addProductPage,adding:n.addingProductPage,hint:n.productPageHint};U.lastChild.textContent=N.add;let M=P&&k&&!S&&!r.promptWithoutPage;k||(eg=!1),M&&(eg=!0),(eg||P||!l)&&(eh=!1);let z=ec&&o&&eh;z&&!em?eA():!z&&em&&eL();let Y=S&&!eg;s.textContent=Y?N.add:M?n.newPage:n.open,$.hidden=M,x.hidden=eg||Y,O.hidden=!eg,U.hidden=!Y,U.disabled=i,I.hidden=eg||eh||!k,I.disabled=i,ep&&(ef=!1),j.hidden=!eg||ef||ep,R.disabled=i,u.hidden=eh||ef||ep||Y,m.hidden=!eh||ef||ep,m.classList.toggle("loading",eb),h.hidden=!eb,B.hidden=ef,J.hidden=!ef,u.placeholder=eg?n.newPagePlaceholder:n.placeholder,v.textContent=e.selectedUid?n.scopeBlock:P?n.scopeNoPage:n.scopeEntry,y.hidden=!e.selectedUid,b.hidden=!!e.selectedUid,f.hidden=!e.selectedUid,L.hidden=!l||eg||P||!e.features.includes("form"),E.setAttribute("aria-pressed",String(!eh)),C.setAttribute("aria-pressed",String(eh)),L.querySelectorAll("button").forEach(e=>{e.disabled=i}),w.hidden=eg||!e.canUndo,w.disabled=i,q.hidden=eh||ep||Y,V.hidden=eh||ep||Y;let X=ep?n.expand:n.minimize;F.title=X,F.setAttribute("aria-label",X),F.setAttribute("aria-expanded",String(!ep)),T.hidden=ep,D.hidden=!ep,q.disabled=i||""===u.value.trim()||eg&&""===R.value.trim(),u.disabled=i,ee.hidden=!e.features.includes("save")||P,ee.disabled=i||!e.unsaved,ee.textContent=e.saving?n.saving:n.save,et.hidden=!e.features.includes("publish")||P,et.disabled=i,et.textContent=e.publishing?n.publishing:n.publish,Q.hidden=!l||!1===r.signOut,p.hidden=!1===r.signOut,r.actions&&(r.actions.slot=ec&&o?"actions-footer":"actions-bar"),er.hidden=!l,G.hidden=!e.unsaved,W.hidden=!l,W.setAttribute("aria-expanded",String(ef)),H.textContent=ev?.find(({entryId:e})=>e===t.entryId)?.name??n.pages;let _=eh&&eb&&!ef,Z=e.prompting,K=e.prompting?eg?n.creating:Y?N.adding:n.working:_?"":eu?.text??(Y?N.hint:!l||eg||ef||e.selectedUid?"":n.selectHint);g.textContent=K,g.hidden=!K||ep,g.className=`status${Z?" working":""}${!i&&!_&&eu?.error?" error":""}`},eC=()=>eE(t.getState()),eP=()=>{u.style.height="auto",u.style.height=`${u.scrollHeight}px`},eA=()=>{eb=!0,m.classList.add("loading");let e=t.openForm(m,{title:n.formTitle,onReady:()=>{em===e&&(eb=!1,eu=null,eC())},onError:t=>{em===e&&(eb=!1,eu={text:t,error:!0},eC())}});em=e},eL=()=>{em?.close(),em=null,eb=!1},eS=e=>{eh!==e&&(eh=e,eu=null,eC(),eh||u.focus())},eN=async()=>{if(eh)return;let e=u.value.trim();if(e&&!t.getState().prompting){eu=null;try{if(eg){let r=R.value.trim();if(!r)return R.focus();await t.createPage(r,e),ev=null,ey=null,eu={text:n.created,error:!1}}else{let{summary:r}=await t.prompt(e);eu={text:r,error:!1}}u.value="",eP()}catch(e){eu=ek(e)}eC(),u.focus()}},eM=()=>(ey??(ey=t.pages().then(e=>{ev=e,ex=null,eU(),eC()},e=>{ey=null,ex=ek(e).text,eU()})),ey),eI=()=>{let e=Y.value.trim().toLowerCase();return(ev??[]).filter(t=>!e||t.name.toLowerCase().includes(e)||t.url.toLowerCase().includes(e)||`/${t.path}`.toLowerCase().includes(e))},eU=()=>{let r=eI();ew=Math.max(0,Math.min(ew,r.length-1)),X.textContent="",r.forEach((r,o)=>{let a=r.entryId===t.entryId,i=N(e,"li",{className:`option${o===ew?" active":""}`,id:`obelism-page-${o}`},N(e,"span",{className:"option-text"},N(e,"span",{textContent:r.name}),N(e,"span",{className:"option-path",textContent:r.url})));if(i.setAttribute("role","option"),i.setAttribute("aria-selected",String(o===ew)),"published"!==r.status&&i.appendChild(N(e,"span",{className:"badge",textContent:"draft"===r.status?n.pageDraft:n.pageChanged})),a){i.setAttribute("aria-current","page");let t=M(e,"check");t.removeAttribute("aria-hidden"),t.setAttribute("role","img"),t.setAttribute("aria-label",n.pageCurrent),i.appendChild(t)}i.addEventListener("click",()=>eO(r)),X.appendChild(i)});let o=X.children[ew];o?(Y.setAttribute("aria-activedescendant",o.id),o.scrollIntoView?.({block:"nearest"})):Y.removeAttribute("aria-activedescendant"),_.textContent=(ex??(ev?"":n.pagesLoading))||(0===r.length?n.pagesEmpty:""),_.hidden=!_.textContent,_.className=`empty${ex?" error":""}`},e$=e=>{eu=null,(ef=e)&&(Y.value="",ew=Math.max(0,(ev??[]).findIndex(({entryId:e})=>e===t.entryId)),eM(),eU()),eC(),(ef?Y:W).focus()},eO=e=>{if(e.entryId===t.entryId)return e$(!1);t.openPage(e)},eR=e=>{(ec=e)||(ef=!1,ep=!1),eC(),l&&t.setSelecting(ec&&!eg),ec&&l&&eM()},ej=e=>{eg=e,eu=null,t.setSelecting(ec&&!eg),eg&&t.select(null),eC(),(eg?R:u).focus()},ez=async e=>{eu=null;try{await t[e](),eu={text:"save"===e?n.saved:n.published,error:!1}}catch(e){eu=ek(e)}eC()};d.addEventListener("click",()=>{eR(!0),eh||(eg?R:u).focus()}),z.addEventListener("click",()=>eR(!1)),F.addEventListener("click",()=>{ep=!ep,eC(),ep||(eh?F:u).focus()}),eo.addEventListener("click",e=>{if(!ep)return;let t=e.target.closest("button");t&&t!==F&&t!==z&&(ep=!1,eC())},!0),ee.addEventListener("click",()=>void ez("save")),et.addEventListener("click",()=>void ez("publish")),Q.addEventListener("click",()=>t.signOut()),p.addEventListener("click",()=>t.signOut()),y.addEventListener("click",()=>t.select(null)),I.addEventListener("click",()=>ej(!0)),U.addEventListener("click",async()=>{eu=null;try{t.product?(await t.createProductPage(),eu={text:n.addedProductPage,error:!1}):(await t.createCollectionPage(),eu={text:n.addedCollectionPage,error:!1})}catch(e){eu=ek(e)}eC()}),$.addEventListener("click",()=>ej(!1)),E.addEventListener("click",()=>eS(!1)),C.addEventListener("click",()=>eS(!0)),W.addEventListener("click",()=>e$(!ef)),R.addEventListener("input",eC),R.addEventListener("keydown",e=>{"Enter"!==e.key||e.isComposing?"Escape"===e.key&&eR(!1):(e.preventDefault(),u.focus())}),Y.addEventListener("input",()=>{ew=0,eU()}),Y.addEventListener("keydown",e=>{let t=X.children.length;if("ArrowDown"===e.key||"ArrowUp"===e.key){if(e.preventDefault(),0===t)return;ew=(ew+("ArrowDown"===e.key?1:-1)+t)%t,eU()}else if("Enter"!==e.key||e.isComposing)"Escape"===e.key&&(e.preventDefault(),e$(!1));else{e.preventDefault();let t=eI()[ew];t&&eO(t)}}),w.addEventListener("click",()=>{eu=null,t.undo()}),eo.addEventListener("submit",e=>{e.preventDefault(),eN()}),u.addEventListener("input",()=>{eP(),eC()}),u.addEventListener("keydown",e=>{"Enter"!==e.key||e.shiftKey||e.isComposing?"Escape"===e.key&&eR(!1):(e.preventDefault(),eN())});let eT=t.on("change",eE);return eC(),e.body.appendChild(a),()=>{eT(),eL(),a.remove()}},U="https://content.obelism.studio",$=/^(?:\/[\w.~-]+)+$/,O=e=>{let{protocol:t}=new URL(e,"https://site.invalid");if("https:"!==t&&"http:"!==t)throw Error(`Not a page address: ${e}`);return e},R=(e,t)=>JSON.stringify(e)===JSON.stringify(t),j=Symbol.for("@obelism/content/bridge"),z=C({isEditing:!1,entryId:null,on:()=>{}},new URLSearchParams),T=({url:e,prompt:c=!0,pagePath:p,productPath:m,collectionPath:y}={})=>{let x;if("u"<typeof window)return z;let P=window;if(P[j])return P[j];if(P.ObelismBridge)x=C(P.ObelismBridge,new URLSearchParams(P.location.search));else{let C=(x=((e,r)=>{let o,n,c,p,m,y=new URLSearchParams(e.location.search),x=e.parent!==e&&y.has("cms_preview"),C=v(),P=[],A={...f,selecting:!0},L=e=>{A={...A,...e},C.emit("change",A)},S={isEditing:x,mode:x?"editor":null,entryId:y.get("cms_entry"),environment:y.get(a),product:null,collection:null,on:C.on,getState:()=>A,...w,destroy:()=>{P.splice(0).forEach(e=>e())}};if(!x)return S;let N=e.document,M=(t,o={})=>{e.parent.postMessage({source:h,type:t,...o},r)},I=N.createElement("style");I.textContent=b(),N.head.appendChild(I),P.push(()=>I.remove());let U=(e,t)=>{if(N.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),A.selectedUid!==(e??null)&&L({selectedUid:e??null}),!e)return;let r=N.querySelector(E(e));r&&(r.setAttribute("data-cms-selected",""),t&&r.scrollIntoView({behavior:"smooth",block:"center"}))},$=e=>{let r=e.target,o=r?.closest?.(`[${t}]`);if(!o)return;e.preventDefault(),e.stopPropagation();let n=o.getAttribute(t);U(n,!1),M("select",{uid:n})};N.addEventListener("click",$,!0),P.push(()=>N.removeEventListener("click",$,!0));let O=()=>Math.ceil(Math.max(N.documentElement.scrollHeight,N.body?.scrollHeight??0)),R=0,j=()=>{R=O(),M("ready",{url:e.location.href,height:R})},z=e.ResizeObserver;if("function"==typeof z){let e=new z(()=>{let e=O();e!==R&&(R=e,M("size",{height:e}))});e.observe(N.documentElement),N.body&&e.observe(N.body),P.push(()=>e.disconnect())}let T=new Map,D=0,F=e=>{let t=T.get(e);return t?(clearTimeout(t.timer),T.delete(e),0===T.size&&L({prompting:!1}),t):null};S.prompt=(e,t={})=>{let r=e.trim();if(!r)return Promise.reject(Error("Say what to change"));if(!A.features.includes("prompt"))return Promise.reject(Error("The editor does not offer prompts here"));let o=`prompt_${++D}`,n=void 0===t.uid?A.selectedUid:t.uid;return new Promise((e,t)=>{let a=setTimeout(()=>{F(o)?.reject(Error("The editor did not answer in time"))},12e4);T.set(o,{resolve:e,reject:t,timer:a}),L({prompting:!0}),M("prompt",{id:o,prompt:r,uid:n})})},S.undo=()=>M("undo"),S.select=e=>{U(e,!1),M("select",{uid:e})};let q=null;P.push(()=>q?.destroy()),P.push(()=>{for(let e of T.keys())F(e)?.reject(Error("The bridge was destroyed"))});let V=(o=new WeakSet,n=null,c=null,p=!1,m=()=>{if(null!==n){for(let e of Array.from(N.styleSheets)){let t;if(!o.has(e)){try{t=e.cssRules}catch{continue}o.add(e),u(t,n)}}N.querySelectorAll('[style*="vh"]').forEach(e=>{g(e.style,n)})}},{freeze(e){if(null!==n||!(e>0))return;n=e,m();let t=N.defaultView?.MutationObserver;t&&(c=new t(()=>{if(p)return;p=!0;let e=()=>{p=!1,m()};N.defaultView?.requestAnimationFrame?N.defaultView.requestAnimationFrame(e):e()})).observe(N.documentElement,{childList:!0,subtree:!0})},destroy(){c?.disconnect()}});P.push(()=>V.destroy());let B=t=>{if(t.origin!==r||t.source!==e.parent)return;let o=t.data;if(o&&"obelism-content-editor"===o.source){if("connect"===o.type)j();else if("update"===o.type&&o.entry)C.emit("update",{entry:o.entry,selectedUid:o.selectedUid??null}),e.requestAnimationFrame(()=>U(o.selectedUid,!1)),q?.refresh();else if("select"===o.type)U(o.uid,!0);else if("saved"===o.type||"published"===o.type)C.emit(o.type,{}),0===C.listeners.update.size&&e.location.reload();else if("features"===o.type&&Array.isArray(o.features))L({features:o.features.filter(e=>"string"==typeof e)});else if("stats"===o.type)L({stats:(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return{};let t={};for(let[r,o]of Object.entries(e)){let{clicks:e,clickers:n,viewers:a,rate:i}=o??{};k(e)&&k(n)&&k(a)&&k(i)&&(t[r]={clicks:e,clickers:n,viewers:a,rate:i})}return t})(o.stats),statsVisible:!1!==o.visible}),(q||Object.keys(A.stats).length)&&(q??(q=((e,{selector:t,onSelect:r,labels:o})=>{let n=e.document,a={...i,...o},c=n.createElement("div");c.setAttribute("data-obelism-stats","");let p=c.attachShadow({mode:"open"}),g=n.createElement("style");g.textContent=s;let u=n.createElement("div");u.className="layer",p.append(g,u),n.body.appendChild(c);let h={},m=!1,b=null,f=()=>{if(b=null,u.replaceChildren(),u.hidden=!m,!m)return;let o=e.innerHeight;for(let[i,s]of Object.entries(h)){let c=n.querySelector(t(i));if(!c)continue;let p=c.getBoundingClientRect();if(!p.width&&!p.height||p.bottom<0||p.top>o)continue;let g=n.createElement("button");g.type="button",g.className="badge",g.dataset.uid=i,g.textContent=l(a.clicked,{rate:Math.round(100*s.rate)}),g.title=l(a.title,{clickers:s.clickers,viewers:s.viewers,clicks:s.clicks});let{place:h,top:m,left:b}=d(p,e.innerWidth);g.dataset.place=h,g.style.top=`${m}px`,g.style.left=`${b}px`,g.addEventListener("click",()=>r(i)),u.appendChild(g)}},v=()=>{null===b&&m&&(b=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(n.body,{childList:!0,subtree:!0}),{update(t,r){h=t,m=r,null!==b&&(e.cancelAnimationFrame?.(b),b=null),f()},refresh:v,destroy(){null!==b&&e.cancelAnimationFrame?.(b),e.removeEventListener("scroll",v,{capture:!0}),e.removeEventListener("resize",v),x?.disconnect(),c.remove()}}})(e,{selector:E,onSelect:e=>S.select(e)})),q.update(A.stats,A.statsVisible));else if("viewport"===o.type&&"number"==typeof o.height)V.freeze(o.height);else if("promptState"===o.type)L({canUndo:!0===o.canUndo});else if("promptResult"===o.type&&"string"==typeof o.id){let e=F(o.id);if(!e)return;!0===o.ok?e.resolve({summary:"string"==typeof o.summary?o.summary:""}):e.reject(Error("string"==typeof o.error?o.error:"The prompt failed"))}}};return e.addEventListener("message",B),P.push(()=>e.removeEventListener("message",B)),"loading"===N.readyState?(N.addEventListener("DOMContentLoaded",j,{once:!0}),P.push(()=>N.removeEventListener("DOMContentLoaded",j))):j(),S})(P,new URL(e||U).origin)).isEditing||P.parent!==P?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 $.test(e)?e:null}catch{break}}return null})(P.document);if(C&&(x=((e,a,{pagePath:i=e=>`/${e.replace(/^\/+/,"")}`,productPath:l,collectionPath:s}={})=>{let d=e.document,c=v(),p=[],g=new Map,u=[],m=new Set,y=null,x=null,w=-1/0,k=new Map,C=f,P=e=>{C={...C,...e},c.emit("change",C)},A=()=>P({unsaved:[...g.values()].some(({content:e,saved:t})=>!R(e,t)),canUndo:u.length>0}),L=async(t,r)=>{let o=await e.fetch(`${a}/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)}),n=await o.json().catch(()=>({}));if(401===o.status)throw P({features:[]}),Error("You were signed out. Sign in again to edit.");if(!o.ok)throw Error("string"==typeof n.error?n.error:`Obelism Content answered ${o.status}`);return n},S=()=>d.querySelector(`[${r}]`)?.getAttribute(r)??null,N=()=>{let e=d.querySelector(`[${o}]`),t=e?.getAttribute(o),r=e?.getAttribute("data-cms-product-handle");return t&&r?{id:t,handle:r}:null},M=()=>{let e=d.querySelector(`[${n}]`),t=e?.getAttribute(n),r=e?.getAttribute("data-cms-collection-handle");return t&&r?{id:t,handle:r}:null},I=e=>{let t=e?d.querySelector(E(e)):null;return t?.closest(`[${r}]`)?.getAttribute(r)??S()},U=()=>{if(!y)return;let e=y.space.locales.map(({code:e})=>e),t=d.documentElement.lang;if(e.includes(t))return t;let r=t.split("-")[0]??"";return e.includes(r)?r:y.space.defaultLocale},$=new Map,j=async e=>{let t=g.get(e);if(t)return t;let r=$.get(e);return r||(r=L(`entries/${encodeURIComponent(e)}`).then(t=>{let r=g.get(e)??{content:t.content,saved:t.content,revision:t.draftRevision};return g.set(e,r),r}).finally(()=>$.delete(e)),$.set(e,r)),r},z=e=>{let t=void 0===e?C.selectedUid:e,r=I(t);if(!r)throw Error("Nothing on this page is marked as an entry: add editable(entry)");return{uid:t,entryId:r}},T=e=>{d.querySelectorAll("[data-cms-selected]").forEach(e=>{e.removeAttribute("data-cms-selected")}),C.selectedUid!==e&&P({selectedUid:e}),e&&d.querySelector(E(e))?.setAttribute("data-cms-selected","")},D=async t=>{if(0===c.listeners.update.size)return;let r=g.get(t)?.content,{entry:o}=await L(`entries/${encodeURIComponent(t)}/preview`,{content:r,locale:U()});c.emit("update",{entry:o,selectedUid:C.selectedUid}),e.requestAnimationFrame(()=>T(C.selectedUid))},F=e=>console.error("[@obelism/content bridge]",e),q=t=>{c.emit(t,{}),0===c.listeners.update.size&&e.location.reload()},V=async()=>{for(let[e,t]of g){if(R(t.content,t.saved))continue;let{draftRevision:r}=await L(`entries/${encodeURIComponent(e)}/save`,{content:t.content,baseRevision:t.revision});t.saved=t.content,t.revision=r,m.add(e)}u.length=0,w=-1/0,A()},B={isEditing:!0,mode:"site",get entryId(){return S()},get product(){return N()},get collection(){return M()},environment:null,on:c.on,getState:()=>C,async prompt(e,t={}){let r=e.trim();if(!r)throw Error("Say what to change");if(!C.features.includes("prompt"))throw Error("You may not edit content here");let{uid:o,entryId:n}=z(t.uid);P({prompting:!0});try{let e=await j(n),t=await L(`entries/${encodeURIComponent(n)}/prompt`,{prompt:r,content:e.content,locale:U(),uid:o});return u.push({entryId:n,content:e.content}),w=-1/0,e.content=t.content,A(),await D(n),{summary:t.summary}}finally{P({prompting:!1})}},async createPage(t,r){let o=r.trim();if(!o)throw Error("Say what the page is for");if(!t.trim())throw Error("Say where the page goes, like /pricing");if(!C.features.includes("create"))throw Error("You may not add pages here");if(C.unsaved)throw Error("Save or undo your changes before adding a page");P({prompting:!0});try{let r=await L("entries",{path:t,prompt:o,locale:U(),from:S()}),n=i(r.path,U()??"");return e.location.assign(O(n)),{summary:r.summary,entryId:r.entryId,path:r.path,url:n}}finally{P({prompting:!1})}},async createProductPage(t=N()??void 0){if(!t)throw Error("Nothing on this page is marked as a product: add editableProduct(product)");if(!C.features.includes("create"))throw Error("You may not add pages here");if(C.unsaved)throw Error("Save or undo your changes before adding a page");P({prompting:!0});try{let r=await L("product-pages",{product:{id:t.id,handle:t.handle}});return e.location.reload(),r}finally{P({prompting:!1})}},async createCollectionPage(t=M()??void 0){if(!t)throw Error("Nothing on this page is marked as a collection: add editableCollection(collection)");if(!C.features.includes("create"))throw Error("You may not add pages here");if(C.unsaved)throw Error("Save or undo your changes before adding a page");P({prompting:!0});try{let r=await L("collection-pages",{collection:{id:t.id,handle:t.handle}});return e.location.reload(),r}finally{P({prompting:!1})}},async view(e={}){let{uid:t,entryId:r}=z(e.uid),o=await j(r),{view:n}=await L(`entries/${encodeURIComponent(r)}/view`,{content:o.content,locale:U(),uid:t});return n},async applyJson(e,t={}){if(!C.features.includes("prompt"))throw Error("You may not edit content here");if(!e||"object"!=typeof e||Array.isArray(e))throw Error("Send the fields to change as an object");let{uid:r,entryId:o}=z(t.uid);P({prompting:!0});try{let t=await j(o),n=await L(`entries/${encodeURIComponent(o)}/apply`,{changes:e,content:t.content,locale:U(),uid:r});return R(n.content,t.content)||(u.push({entryId:o,content:t.content}),w=-1/0,t.content=n.content,A(),await D(o)),{changed:n.changed??[],...n.view&&{view:n.view}}}finally{P({prompting:!1})}},openForm(t,r={}){let{title:o="Content form",onReady:n,onError:a}=r;x?.();let i=!1,l=null,s=null,d=()=>{},p=e=>{i||a?.(e instanceof Error?e.message:String(e))},m=()=>{i||(i=!0,d(),l?.destroy(),x===m&&(x=null))};x=m;let b=()=>{if(i||!l)return;let e=I(C.selectedUid);if(!e)return;let t=g.get(e);if(!t)return void j(e).then(b,p);let r={entryId:e,content:t.content,locale:U(),selectedUid:C.selectedUid};s&&s.entryId===r.entryId&&s.content===r.content&&s.locale===r.locale&&s.selectedUid===r.selectedUid||(s=r,l.show(r))},f=(e,t)=>{let r=t??s?.entryId,o=void 0===r?void 0:g.get(r);if(i||void 0===r||!o||R(e,o.content))return;let n=Date.now();n-w>=1e3&&u.push({entryId:r,content:o.content}),w=n,o.content=e,s?.entryId===r&&(s={...s,content:e}),A(),clearTimeout(k.get(r)),k.set(r,setTimeout(()=>{k.delete(r),D(r).catch(F)},300))};return(async()=>{if(!C.features.includes("form"))throw Error("You may not edit content here");await j(z(void 0).entryId);let{url:r}=await L("form",{});i||(l=((e,t,r,o,n)=>{let a=null;try{a=new URL(r)}catch{}if(!a||!/^https?:$/.test(a.protocol))throw Error("Obelism Content sent no form to open");let i=a.origin,l=t.ownerDocument.createElement("iframe");l.title=o,l.src=r;let s=!1,d=!1,c=null,p=()=>{s&&c&&l.contentWindow?.postMessage({source:h,type:"content",...c},i)},g=setTimeout(()=>{d||n.onError("The form did not open")},3e4),u=()=>{d=!0,clearTimeout(g),n.onReady()},m=e=>{if(e.origin!==i||!l.contentWindow||e.source!==l.contentWindow)return;let t=e.data;if(t&&"obelism-content-form"===t.source)if("ready"===t.type)s=!0,p(),!0!==t.loading&&u();else if("loaded"===t.type)s&&u();else{let e;if("change"!==t.type||!(e=t.content)||"object"!=typeof e||Array.isArray(e))if("height"===t.type){let e=t.height;if("number"==typeof e&&Number.isFinite(e)){let t=Math.round(Math.min(Math.max(e,80),4e3));l.style.height=`min(${t}px, 60vh)`}}else"error"===t.type&&n.onError("string"==typeof t.message&&t.message?t.message:"The form failed");else n.onChange(t.content,"string"==typeof t.entryId?t.entryId:void 0)}};return e.addEventListener("message",m),t.appendChild(l),{show(e){c=e,p()},destroy(){clearTimeout(g),e.removeEventListener("message",m),l.remove()}}})(e,t,"string"==typeof r?r:"",o,{onReady:()=>n?.(),onChange:f,onError:e=>a?.(e)}),d=c.on("change",b),b())})().catch(p),{close:m}},async pages(){let{pages:e}=await L("pages"),t=[U(),y?.space.defaultLocale];return e.flatMap(e=>{let r,o,n=t.find(t=>void 0!==t&&void 0!==e.paths[t])??Object.keys(e.paths)[0],a=void 0===n?void 0:e.paths[n];return void 0===n||void 0===a?[]:[{entryId:e.entryId,name:e.name,contentType:e.contentType,status:e.status,path:a,url:e.product?(r=e.product.handle,l?l(r,n):(y?.productPath??"/products/{handle}").replaceAll("{handle}",encodeURIComponent(r))):e.collection?(o=e.collection.handle,s?s(o,n):(y?.collectionPath??"/collections/{handle}").replaceAll("{handle}",encodeURIComponent(o))):i(a,n),product:e.product??null,collection:e.collection??null}]})},openPage(t){(!C.unsaved||e.confirm("Leave this page and lose the changes you have not saved?"))&&(g.clear(),u.length=0,A(),e.location.assign(O(t.url)))},undo(){let e=u.pop(),t=e&&g.get(e.entryId);e&&t&&(w=-1/0,t.content=e.content,A(),D(e.entryId).catch(F))},async save(){if(!C.features.includes("save"))throw Error("You may not edit content here");P({saving:!0});try{await V()}finally{P({saving:!1})}q("saved")},async publish(){if(!C.features.includes("publish"))throw Error("You may not publish here");P({publishing:!0});try{await V();let e=S(),t=m.size>0?[...m]: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 L(`entries/${encodeURIComponent(e)}/publish`,{}),m.delete(e)}finally{P({publishing:!1})}q("published")},signOut(){if(C.unsaved&&!e.confirm("Sign out and lose the changes you have not saved?"))return;g.clear(),A();let t=e.location.pathname+e.location.search;e.location.href=`${a}/logout?return_to=${encodeURIComponent(t)}`},select(e){T(e)},setSelecting(e){e?d.documentElement.setAttribute("data-cms-selecting",""):d.documentElement.removeAttribute("data-cms-selecting"),P({selecting:e})},destroy(){x?.(),k.forEach(e=>clearTimeout(e)),k.clear(),p.splice(0).forEach(e=>e())}},H=d.createElement("style");H.textContent=b("html[data-cms-selecting] "),d.head.appendChild(H),p.push(()=>H.remove()),p.push(()=>d.documentElement.removeAttribute("data-cms-selecting"));let W=e=>{if(!C.selecting)return;let r=e.target,o=r?.closest?.(`[${t}]`);o&&(e.preventDefault(),e.stopPropagation(),T(o.getAttribute(t)))};d.addEventListener("click",W,!0),p.push(()=>d.removeEventListener("click",W,!0));let Y=e=>{C.unsaved&&(e.preventDefault(),e.returnValue="")};return e.addEventListener("beforeunload",Y),p.push(()=>e.removeEventListener("beforeunload",Y)),L("session").then(e=>{y=e,P({features:e.features})}).catch(e=>console.error("[@obelism/content bridge]",e)),B})(P,C,{pagePath:p,productPath:m,collectionPath:y})),x.isEditing&&c){let e=P.document,t=()=>{},r=!1,o=()=>{r||(t=I(e,x,!0===c?{}:c))};e.body?o():e.addEventListener("DOMContentLoaded",o,{once:!0});let n=x.destroy;x.destroy=()=>{r=!0,e.removeEventListener("DOMContentLoaded",o),t(),n()}}}return P[j]=x,x};(e=window).ObelismBridge=T((e=>{let t=e?.dataset.url,r=t||U;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:T,mountPromptBox:I}}();
package/dist/bridge.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
- var preview = require('./preview-ooq8w3jv.js');
3
+ var preview = require('./preview-c4giufnr.js');
4
4
  var site = require('./site.js');
5
5
  var config = require('./config-ci2hxmas.js');
6
6
 
@@ -1807,6 +1807,18 @@ const mountFormFrame = (win, container, url, title, handlers)=>{
1807
1807
  // kept in step with the content here, see formFrame.ts), edits JSON (`view`,
1808
1808
  // then `applyJson`: the raw alternative), and lists the space's pages to go
1809
1809
  // to one.
1810
+ // Plain path segments only: the URL parser drops tabs and newlines and reads
1811
+ // \ as /, so anything looser can resolve to another origin.
1812
+ const BASE_PATH = /^(?:\/[\w.~-]+)+$/;
1813
+ // Page addresses come from the CMS's session and pages: never a javascript:
1814
+ // or data: URL.
1815
+ const webUrl = (url)=>{
1816
+ const { protocol } = new URL(url, 'https://site.invalid');
1817
+ if (protocol !== 'https:' && protocol !== 'http:') {
1818
+ throw new Error(`Not a page address: ${url}`);
1819
+ }
1820
+ return url;
1821
+ };
1810
1822
  // The base path of the site's handler while signed in to edit, else null.
1811
1823
  const readSiteEditing = (doc)=>{
1812
1824
  for (const part of doc.cookie.split(';')){
@@ -1814,7 +1826,7 @@ const readSiteEditing = (doc)=>{
1814
1826
  if (name !== site.SITE_EDITING_COOKIE) continue;
1815
1827
  try {
1816
1828
  const basePath = decodeURIComponent(value.join('='));
1817
- return basePath.startsWith('/') && !basePath.startsWith('//') ? basePath : null;
1829
+ return BASE_PATH.test(basePath) ? basePath : null;
1818
1830
  } catch {
1819
1831
  return null;
1820
1832
  }
@@ -1827,7 +1839,7 @@ const FORM_BURST_MS = 1000;
1827
1839
  // How long the page waits for the form's edits to settle before it shows
1828
1840
  // them.
1829
1841
  const FORM_RENDER_MS = 300;
1830
- const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, productPath: ownProductPath, collectionPath: ownCollectionPath } = {})=>{
1842
+ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path.replace(/^\/+/, '')}`, productPath: ownProductPath, collectionPath: ownCollectionPath } = {})=>{
1831
1843
  const doc = win.document;
1832
1844
  const emitter = createEmitter();
1833
1845
  const cleanup = [];
@@ -2072,7 +2084,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, produc
2072
2084
  from: pageEntryId()
2073
2085
  });
2074
2086
  const url = pagePath(created.path, locale() ?? '');
2075
- win.location.assign(url);
2087
+ win.location.assign(webUrl(url));
2076
2088
  return {
2077
2089
  summary: created.summary,
2078
2090
  entryId: created.entryId,
@@ -2309,7 +2321,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, produc
2309
2321
  working.clear();
2310
2322
  history.length = 0;
2311
2323
  refreshUnsaved();
2312
- win.location.assign(page.url);
2324
+ win.location.assign(webUrl(page.url));
2313
2325
  },
2314
2326
  undo () {
2315
2327
  const last = history.pop();
package/dist/bridge.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { P as PREVIEW_PARAMS, U as UID_ATTRIBUTE, E as ENTRY_ATTRIBUTE, C as COLLECTION_ATTRIBUTE, a as COLLECTION_HANDLE_ATTRIBUTE, b as PRODUCT_ATTRIBUTE, c as PRODUCT_HANDLE_ATTRIBUTE } from './preview-fu3qtgfx.mjs';
1
+ import { P as PREVIEW_PARAMS, U as UID_ATTRIBUTE, E as ENTRY_ATTRIBUTE, C as COLLECTION_ATTRIBUTE, a as COLLECTION_HANDLE_ATTRIBUTE, b as PRODUCT_ATTRIBUTE, c as PRODUCT_HANDLE_ATTRIBUTE } from './preview-el7sfk3h.mjs';
2
2
  import { SITE_EDITING_COOKIE, SITE_REQUEST_HEADER } from './site.mjs';
3
3
  import { n as normalizeUrl, D as DEFAULT_URL } from './config-c6qktlz6.mjs';
4
4
 
@@ -1805,6 +1805,18 @@ const mountFormFrame = (win, container, url, title, handlers)=>{
1805
1805
  // kept in step with the content here, see formFrame.ts), edits JSON (`view`,
1806
1806
  // then `applyJson`: the raw alternative), and lists the space's pages to go
1807
1807
  // to one.
1808
+ // Plain path segments only: the URL parser drops tabs and newlines and reads
1809
+ // \ as /, so anything looser can resolve to another origin.
1810
+ const BASE_PATH = /^(?:\/[\w.~-]+)+$/;
1811
+ // Page addresses come from the CMS's session and pages: never a javascript:
1812
+ // or data: URL.
1813
+ const webUrl = (url)=>{
1814
+ const { protocol } = new URL(url, 'https://site.invalid');
1815
+ if (protocol !== 'https:' && protocol !== 'http:') {
1816
+ throw new Error(`Not a page address: ${url}`);
1817
+ }
1818
+ return url;
1819
+ };
1808
1820
  // The base path of the site's handler while signed in to edit, else null.
1809
1821
  const readSiteEditing = (doc)=>{
1810
1822
  for (const part of doc.cookie.split(';')){
@@ -1812,7 +1824,7 @@ const readSiteEditing = (doc)=>{
1812
1824
  if (name !== SITE_EDITING_COOKIE) continue;
1813
1825
  try {
1814
1826
  const basePath = decodeURIComponent(value.join('='));
1815
- return basePath.startsWith('/') && !basePath.startsWith('//') ? basePath : null;
1827
+ return BASE_PATH.test(basePath) ? basePath : null;
1816
1828
  } catch {
1817
1829
  return null;
1818
1830
  }
@@ -1825,7 +1837,7 @@ const FORM_BURST_MS = 1000;
1825
1837
  // How long the page waits for the form's edits to settle before it shows
1826
1838
  // them.
1827
1839
  const FORM_RENDER_MS = 300;
1828
- const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, productPath: ownProductPath, collectionPath: ownCollectionPath } = {})=>{
1840
+ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path.replace(/^\/+/, '')}`, productPath: ownProductPath, collectionPath: ownCollectionPath } = {})=>{
1829
1841
  const doc = win.document;
1830
1842
  const emitter = createEmitter();
1831
1843
  const cleanup = [];
@@ -2070,7 +2082,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, produc
2070
2082
  from: pageEntryId()
2071
2083
  });
2072
2084
  const url = pagePath(created.path, locale() ?? '');
2073
- win.location.assign(url);
2085
+ win.location.assign(webUrl(url));
2074
2086
  return {
2075
2087
  summary: created.summary,
2076
2088
  entryId: created.entryId,
@@ -2307,7 +2319,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, produc
2307
2319
  working.clear();
2308
2320
  history.length = 0;
2309
2321
  refreshUnsaved();
2310
- win.location.assign(page.url);
2322
+ win.location.assign(webUrl(page.url));
2311
2323
  },
2312
2324
  undo () {
2313
2325
  const last = history.pop();
package/dist/index.d.mts CHANGED
@@ -1,8 +1,12 @@
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, S as ShopifyItem, L as LinkTarget, d as Link, e as EntryStub, f as RichTextNode } from './types-mtjco3a5.js';
2
- export { C as ContentTypeDefinition, g as ContentTypeKind, h as Environment, F as FieldDefinition, i as FieldType, j as RichTextDocument, k as RichTextMark } from './types-mtjco3a5.js';
1
+ import { TokenSource } from './oidc.mjs';
2
+ 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, S as ShopifyItem, L as LinkTarget, d as Link, e as EntryStub, f as RichTextNode } from './types-exvugblj.js';
3
+ export { g as AssetProduct, C as ContentTypeDefinition, h as ContentTypeKind, i as Environment, F as FieldDefinition, j as FieldType, k as RichTextDocument, l as RichTextMark } from './types-exvugblj.js';
3
4
 
4
5
  type ContentClientOptions = {
5
- token: string;
6
+ token: TokenSource;
7
+ drafts?: boolean | undefined;
8
+ space?: string | undefined;
9
+ environment?: string | undefined;
6
10
  url?: string | undefined;
7
11
  locale?: string | undefined;
8
12
  depth?: number | undefined;
@@ -15,6 +19,7 @@ type RequestOptions = {
15
19
  locale?: string | undefined;
16
20
  depth?: number | undefined;
17
21
  richText?: RichTextFormat | undefined;
22
+ drafts?: boolean | undefined;
18
23
  init?: RequestInit | undefined;
19
24
  };
20
25
  type FieldFilterValue = string | number | boolean;
@@ -44,7 +49,7 @@ type PageLookup<TEntry extends Entry = Entry> = {
44
49
  declare class ContentClient {
45
50
  #private;
46
51
  readonly url: string;
47
- constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl, }: ContentClientOptions);
52
+ constructor({ token, drafts, space, environment, url, locale, depth, richText, timeout, init, fetch: fetchImpl, }: ContentClientOptions);
48
53
  getPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<TEntry | null>;
49
54
  findPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<PageLookup<TEntry> | null>;
50
55
  getRedirects: ({ locale, limit, skip, init, }?: RedirectsQuery) => Promise<RedirectList>;
@@ -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/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
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/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,12 @@
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, S as ShopifyItem, L as LinkTarget, d as Link, e as EntryStub, f as RichTextNode } from './types-mtjco3a5.js';
2
- export { C as ContentTypeDefinition, g as ContentTypeKind, h as Environment, F as FieldDefinition, i as FieldType, j as RichTextDocument, k as RichTextMark } from './types-mtjco3a5.js';
1
+ import { TokenSource } from './oidc.js';
2
+ 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, S as ShopifyItem, L as LinkTarget, d as Link, e as EntryStub, f as RichTextNode } from './types-exvugblj.js';
3
+ export { g as AssetProduct, C as ContentTypeDefinition, h as ContentTypeKind, i as Environment, F as FieldDefinition, j as FieldType, k as RichTextDocument, l as RichTextMark } from './types-exvugblj.js';
3
4
 
4
5
  type ContentClientOptions = {
5
- token: string;
6
+ token: TokenSource;
7
+ drafts?: boolean | undefined;
8
+ space?: string | undefined;
9
+ environment?: string | undefined;
6
10
  url?: string | undefined;
7
11
  locale?: string | undefined;
8
12
  depth?: number | undefined;
@@ -15,6 +19,7 @@ type RequestOptions = {
15
19
  locale?: string | undefined;
16
20
  depth?: number | undefined;
17
21
  richText?: RichTextFormat | undefined;
22
+ drafts?: boolean | undefined;
18
23
  init?: RequestInit | undefined;
19
24
  };
20
25
  type FieldFilterValue = string | number | boolean;
@@ -44,7 +49,7 @@ type PageLookup<TEntry extends Entry = Entry> = {
44
49
  declare class ContentClient {
45
50
  #private;
46
51
  readonly url: string;
47
- constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl, }: ContentClientOptions);
52
+ constructor({ token, drafts, space, environment, url, locale, depth, richText, timeout, init, fetch: fetchImpl, }: ContentClientOptions);
48
53
  getPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<TEntry | null>;
49
54
  findPage: <TEntry extends Entry = Entry>(path: string, options?: RequestOptions) => Promise<PageLookup<TEntry> | null>;
50
55
  getRedirects: ({ locale, limit, skip, init, }?: RedirectsQuery) => Promise<RedirectList>;
@@ -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/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
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/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
3
  var config = require('./config-ci2hxmas.js');
4
- var preview = require('./preview-ooq8w3jv.js');
4
+ var preview = require('./preview-c4giufnr.js');
5
5
 
6
6
  // A response from the API that wasn't a success. `message` is the API's own
7
7
  // `error` text when it sent one.
@@ -16,6 +16,10 @@ class ContentApiError extends Error {
16
16
  }
17
17
  }
18
18
 
19
+ // A token, or a function that gives one per request: a host OIDC token from
20
+ // ./oidc, or anything else that rotates.
21
+ const resolveToken = (token)=>typeof token === 'function' ? token() : token;
22
+
19
23
  // `fields` as the API's `fields.<apiId>[op]` parameters.
20
24
  const fieldParams = (fields)=>Object.fromEntries(Object.entries(fields ?? {}).map(([key, value])=>{
21
25
  if (!Array.isArray(value)) return [
@@ -30,17 +34,19 @@ const fieldParams = (fields)=>Object.fromEntries(Object.entries(fields ?? {}).ma
30
34
  }));
31
35
  const trimSlashes = (path)=>path.replace(/^\/+|\/+$/g, '');
32
36
  // Reads one space environment's content through the delivery API. The token
33
- // decides the space, the environment and whether drafts are visible.
37
+ // decides the space, the environment and whether drafts are visible (for a
38
+ // host token, with `drafts`).
34
39
  //
35
40
  // const content = new ContentClient({ token: process.env.CONTENT_TOKEN })
36
41
  // const page = await content.getPage('blog/hello', { locale: 'nl' })
37
42
  class ContentClient {
38
43
  #token;
44
+ #place;
39
45
  #defaults;
40
46
  #timeout;
41
47
  #init;
42
48
  #fetch;
43
- constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl }){
49
+ constructor({ token, drafts, space, environment, url, locale, depth, richText, timeout, init, fetch: fetchImpl }){
44
50
  // The page at a path (`blog/hello`), or null when there is none. A site's
45
51
  // catch-all route asks for this. The path is looked up in `locale`'s
46
52
  // paths (the default locale's when left out or `*`): pages can have a
@@ -112,10 +118,19 @@ class ContentClient {
112
118
  });
113
119
  this.url = config.normalizeUrl(url || config.DEFAULT_URL);
114
120
  this.#token = token;
121
+ this.#place = {
122
+ ...space && {
123
+ 'Obelism-Space': space
124
+ },
125
+ ...environment && {
126
+ 'Obelism-Environment': environment
127
+ }
128
+ };
115
129
  this.#defaults = {
116
130
  locale,
117
131
  depth,
118
- richText
132
+ richText,
133
+ drafts
119
134
  };
120
135
  this.#timeout = timeout ?? config.DEFAULT_TIMEOUT;
121
136
  this.#init = init;
@@ -160,7 +175,7 @@ class ContentClient {
160
175
  // Checked here rather than in the constructor, so a module creating a
161
176
  // client still loads (and a build still runs) without the variable.
162
177
  if (!this.#token) throw new Error('ContentClient needs a token');
163
- const { locale, depth, richText } = {
178
+ const { locale, depth, richText, drafts } = {
164
179
  ...defaults && this.#defaults,
165
180
  ...withoutUndefined(options)
166
181
  };
@@ -189,7 +204,11 @@ class ContentClient {
189
204
  // Not `Authorization`: Cloudflare never caches a request that
190
205
  // carries it, and this header lets the API answer a delivery
191
206
  // token from its edge cache.
192
- 'Obelism-Token': this.#token
207
+ 'Obelism-Token': await resolveToken(this.#token),
208
+ ...this.#place,
209
+ ...drafts && {
210
+ 'Obelism-Drafts': 'true'
211
+ }
193
212
  },
194
213
  signal: merged.signal ? AbortSignal.any([
195
214
  merged.signal,
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as normalizeUrl, a as DEFAULT_TIMEOUT, b as DELIVERY_PATH, D as DEFAULT_URL } from './config-c6qktlz6.mjs';
2
- export { E as ENTRY_ATTRIBUTE, P as PREVIEW_PARAMS, b as PRODUCT_ATTRIBUTE, c as PRODUCT_HANDLE_ATTRIBUTE, U as UID_ATTRIBUTE, e as editable, d as editableCollection, f as editableProduct, r as readPreviewParams } from './preview-fu3qtgfx.mjs';
2
+ export { E as ENTRY_ATTRIBUTE, P as PREVIEW_PARAMS, b as PRODUCT_ATTRIBUTE, c as PRODUCT_HANDLE_ATTRIBUTE, U as UID_ATTRIBUTE, e as editable, d as editableCollection, f as editableProduct, r as readPreviewParams } from './preview-el7sfk3h.mjs';
3
3
 
4
4
  // A response from the API that wasn't a success. `message` is the API's own
5
5
  // `error` text when it sent one.
@@ -14,6 +14,10 @@ class ContentApiError extends Error {
14
14
  }
15
15
  }
16
16
 
17
+ // A token, or a function that gives one per request: a host OIDC token from
18
+ // ./oidc, or anything else that rotates.
19
+ const resolveToken = (token)=>typeof token === 'function' ? token() : token;
20
+
17
21
  // `fields` as the API's `fields.<apiId>[op]` parameters.
18
22
  const fieldParams = (fields)=>Object.fromEntries(Object.entries(fields ?? {}).map(([key, value])=>{
19
23
  if (!Array.isArray(value)) return [
@@ -28,17 +32,19 @@ const fieldParams = (fields)=>Object.fromEntries(Object.entries(fields ?? {}).ma
28
32
  }));
29
33
  const trimSlashes = (path)=>path.replace(/^\/+|\/+$/g, '');
30
34
  // Reads one space environment's content through the delivery API. The token
31
- // decides the space, the environment and whether drafts are visible.
35
+ // decides the space, the environment and whether drafts are visible (for a
36
+ // host token, with `drafts`).
32
37
  //
33
38
  // const content = new ContentClient({ token: process.env.CONTENT_TOKEN })
34
39
  // const page = await content.getPage('blog/hello', { locale: 'nl' })
35
40
  class ContentClient {
36
41
  #token;
42
+ #place;
37
43
  #defaults;
38
44
  #timeout;
39
45
  #init;
40
46
  #fetch;
41
- constructor({ token, url, locale, depth, richText, timeout, init, fetch: fetchImpl }){
47
+ constructor({ token, drafts, space, environment, url, locale, depth, richText, timeout, init, fetch: fetchImpl }){
42
48
  // The page at a path (`blog/hello`), or null when there is none. A site's
43
49
  // catch-all route asks for this. The path is looked up in `locale`'s
44
50
  // paths (the default locale's when left out or `*`): pages can have a
@@ -110,10 +116,19 @@ class ContentClient {
110
116
  });
111
117
  this.url = normalizeUrl(url || DEFAULT_URL);
112
118
  this.#token = token;
119
+ this.#place = {
120
+ ...space && {
121
+ 'Obelism-Space': space
122
+ },
123
+ ...environment && {
124
+ 'Obelism-Environment': environment
125
+ }
126
+ };
113
127
  this.#defaults = {
114
128
  locale,
115
129
  depth,
116
- richText
130
+ richText,
131
+ drafts
117
132
  };
118
133
  this.#timeout = timeout ?? DEFAULT_TIMEOUT;
119
134
  this.#init = init;
@@ -158,7 +173,7 @@ class ContentClient {
158
173
  // Checked here rather than in the constructor, so a module creating a
159
174
  // client still loads (and a build still runs) without the variable.
160
175
  if (!this.#token) throw new Error('ContentClient needs a token');
161
- const { locale, depth, richText } = {
176
+ const { locale, depth, richText, drafts } = {
162
177
  ...defaults && this.#defaults,
163
178
  ...withoutUndefined(options)
164
179
  };
@@ -187,7 +202,11 @@ class ContentClient {
187
202
  // Not `Authorization`: Cloudflare never caches a request that
188
203
  // carries it, and this header lets the API answer a delivery
189
204
  // token from its edge cache.
190
- 'Obelism-Token': this.#token
205
+ 'Obelism-Token': await resolveToken(this.#token),
206
+ ...this.#place,
207
+ ...drafts && {
208
+ 'Obelism-Drafts': 'true'
209
+ }
191
210
  },
192
211
  signal: merged.signal ? AbortSignal.any([
193
212
  merged.signal,
@@ -0,0 +1,13 @@
1
+ export { T as TokenSource } from './tokenSource-my2ymemb.js';
2
+
3
+ declare const DEFAULT_AUDIENCE = "https://content.obelism.studio";
4
+ declare const vercelOidc: ({ audience }?: {
5
+ audience?: string | undefined;
6
+ }) => () => Promise<string>;
7
+ declare const githubOidc: ({ audience, fetch: fetchImpl, }?: {
8
+ audience?: string | undefined;
9
+ fetch?: typeof fetch | undefined;
10
+ }) => () => Promise<string>;
11
+
12
+ export { DEFAULT_AUDIENCE, githubOidc, vercelOidc };
13
+ //# sourceMappingURL=oidc.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.d.mts","sources":["../src/oidc.ts"],"mappings":";;AACA,cAAc,gBAAgB;AAC9B,cAAc,UAAU;AACxB;AACA,YAAY,OAAO;AACnB,cAAc,UAAU;AACxB;AACA,mBAAmB,KAAK;AACxB,YAAY,OAAO;;","names":[]}
package/dist/oidc.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export { T as TokenSource } from './tokenSource-my2ymemb.js';
2
+
3
+ declare const DEFAULT_AUDIENCE = "https://content.obelism.studio";
4
+ declare const vercelOidc: ({ audience }?: {
5
+ audience?: string | undefined;
6
+ }) => () => Promise<string>;
7
+ declare const githubOidc: ({ audience, fetch: fetchImpl, }?: {
8
+ audience?: string | undefined;
9
+ fetch?: typeof fetch | undefined;
10
+ }) => () => Promise<string>;
11
+
12
+ export { DEFAULT_AUDIENCE, githubOidc, vercelOidc };
13
+ //# sourceMappingURL=oidc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.d.ts","sources":["../src/oidc.ts"],"mappings":";;AACA,cAAc,gBAAgB;AAC9B,cAAc,UAAU;AACxB;AACA,YAAY,OAAO;AACnB,cAAc,UAAU;AACxB;AACA,mBAAmB,KAAK;AACxB,YAAY,OAAO;;","names":[]}
package/dist/oidc.js ADDED
@@ -0,0 +1,71 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ // A deployment's host OIDC token instead of a stored API token
4
+ // (Obelism's www docs/adr/0016): pass one of these as `token`. The space
5
+ // needs the Vercel project or GitHub repository connected, under Connected
6
+ // deployments on its page on www.obelism.studio. Same as improve's packages/improve/src/oidc.ts.
7
+ //
8
+ // import { vercelOidc } from '@obelism/content/oidc'
9
+ // token: vercelOidc()
10
+ // What the token must be addressed to: this product.
11
+ const DEFAULT_AUDIENCE = 'https://content.obelism.studio';
12
+ // Renewed this long before it expires, so a request never carries one that
13
+ // expires on the way.
14
+ const RENEW_MARGIN_MS = 60_000;
15
+ const expiryOf = (token)=>{
16
+ try {
17
+ const payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
18
+ return JSON.parse(atob(payload)).exp * 1000;
19
+ } catch {
20
+ return 0;
21
+ }
22
+ };
23
+ // One token at a time, reused until shortly before it expires; concurrent
24
+ // callers share the request for a new one. For GitHub's, which nothing
25
+ // else caches.
26
+ const reused = (fetchToken)=>{
27
+ let token = '';
28
+ let expiresAt = 0;
29
+ let pending = null;
30
+ return ()=>{
31
+ if (token && Date.now() < expiresAt - RENEW_MARGIN_MS) return Promise.resolve(token);
32
+ pending ??= fetchToken().then((fresh)=>{
33
+ token = fresh;
34
+ expiresAt = expiryOf(fresh);
35
+ return fresh;
36
+ }).finally(()=>{
37
+ pending = null;
38
+ });
39
+ return pending;
40
+ };
41
+ };
42
+ // On Vercel: the deployment's token for this product, from @vercel/oidc
43
+ // (install it next to this package), which caches the exchange for the
44
+ // audience itself. In a function it reads the request's token, so call it
45
+ // during a request, not at module level.
46
+ const vercelOidc = ({ audience = DEFAULT_AUDIENCE } = {})=>async ()=>{
47
+ const { getVercelOidcToken } = await import('@vercel/oidc');
48
+ return getVercelOidcToken({
49
+ audience,
50
+ expirationBufferMs: RENEW_MARGIN_MS
51
+ });
52
+ };
53
+ // In GitHub Actions: the job's token for this product. The job needs
54
+ // `permissions: id-token: write`.
55
+ const githubOidc = ({ audience = DEFAULT_AUDIENCE, fetch: fetchImpl = fetch } = {})=>reused(async ()=>{
56
+ const env = globalThis.process?.env;
57
+ const url = env?.ACTIONS_ID_TOKEN_REQUEST_URL;
58
+ const bearer = env?.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
59
+ if (!url || !bearer) throw new Error('githubOidc needs a GitHub Actions job with `permissions: id-token: write`');
60
+ const response = await fetchImpl(`${url}&audience=${encodeURIComponent(audience)}`, {
61
+ headers: {
62
+ Authorization: `Bearer ${bearer}`
63
+ }
64
+ });
65
+ if (!response.ok) throw new Error(`GitHub refused the OIDC token request (${response.status})`);
66
+ return (await response.json()).value;
67
+ });
68
+
69
+ exports.DEFAULT_AUDIENCE = DEFAULT_AUDIENCE;
70
+ exports.githubOidc = githubOidc;
71
+ exports.vercelOidc = vercelOidc;
package/dist/oidc.mjs ADDED
@@ -0,0 +1,67 @@
1
+ // A deployment's host OIDC token instead of a stored API token
2
+ // (Obelism's www docs/adr/0016): pass one of these as `token`. The space
3
+ // needs the Vercel project or GitHub repository connected, under Connected
4
+ // deployments on its page on www.obelism.studio. Same as improve's packages/improve/src/oidc.ts.
5
+ //
6
+ // import { vercelOidc } from '@obelism/content/oidc'
7
+ // token: vercelOidc()
8
+ // What the token must be addressed to: this product.
9
+ const DEFAULT_AUDIENCE = 'https://content.obelism.studio';
10
+ // Renewed this long before it expires, so a request never carries one that
11
+ // expires on the way.
12
+ const RENEW_MARGIN_MS = 60_000;
13
+ const expiryOf = (token)=>{
14
+ try {
15
+ const payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
16
+ return JSON.parse(atob(payload)).exp * 1000;
17
+ } catch {
18
+ return 0;
19
+ }
20
+ };
21
+ // One token at a time, reused until shortly before it expires; concurrent
22
+ // callers share the request for a new one. For GitHub's, which nothing
23
+ // else caches.
24
+ const reused = (fetchToken)=>{
25
+ let token = '';
26
+ let expiresAt = 0;
27
+ let pending = null;
28
+ return ()=>{
29
+ if (token && Date.now() < expiresAt - RENEW_MARGIN_MS) return Promise.resolve(token);
30
+ pending ??= fetchToken().then((fresh)=>{
31
+ token = fresh;
32
+ expiresAt = expiryOf(fresh);
33
+ return fresh;
34
+ }).finally(()=>{
35
+ pending = null;
36
+ });
37
+ return pending;
38
+ };
39
+ };
40
+ // On Vercel: the deployment's token for this product, from @vercel/oidc
41
+ // (install it next to this package), which caches the exchange for the
42
+ // audience itself. In a function it reads the request's token, so call it
43
+ // during a request, not at module level.
44
+ const vercelOidc = ({ audience = DEFAULT_AUDIENCE } = {})=>async ()=>{
45
+ const { getVercelOidcToken } = await import('@vercel/oidc');
46
+ return getVercelOidcToken({
47
+ audience,
48
+ expirationBufferMs: RENEW_MARGIN_MS
49
+ });
50
+ };
51
+ // In GitHub Actions: the job's token for this product. The job needs
52
+ // `permissions: id-token: write`.
53
+ const githubOidc = ({ audience = DEFAULT_AUDIENCE, fetch: fetchImpl = fetch } = {})=>reused(async ()=>{
54
+ const env = globalThis.process?.env;
55
+ const url = env?.ACTIONS_ID_TOKEN_REQUEST_URL;
56
+ const bearer = env?.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
57
+ if (!url || !bearer) throw new Error('githubOidc needs a GitHub Actions job with `permissions: id-token: write`');
58
+ const response = await fetchImpl(`${url}&audience=${encodeURIComponent(audience)}`, {
59
+ headers: {
60
+ Authorization: `Bearer ${bearer}`
61
+ }
62
+ });
63
+ if (!response.ok) throw new Error(`GitHub refused the OIDC token request (${response.status})`);
64
+ return (await response.json()).value;
65
+ });
66
+
67
+ export { DEFAULT_AUDIENCE, githubOidc, vercelOidc };
@@ -83,11 +83,13 @@ const toSearchParams = (input)=>{
83
83
  //
84
84
  // const preview = readPreviewParams(request.url)
85
85
  // const content = preview?.token
86
- // ? new ContentClient({ token: preview.token, init: { cache: 'no-store' } })
86
+ // ? new ContentClient({ token: preview.token, space, environment })
87
87
  // : deliveryClient
88
88
  //
89
- // A made-up or expired token makes the client throw a ContentApiError with
90
- // status 401: answer with `deliveryClient` then.
89
+ // `space` and `environment` are the site's own: anyone can make a cms_token
90
+ // for a space of theirs, and naming yours makes such a token read nothing. A
91
+ // made-up, expired or other space's token makes the client throw a
92
+ // ContentApiError with status 401: answer with `deliveryClient` then.
91
93
  const readPreviewParams = (input)=>{
92
94
  const params = toSearchParams(input);
93
95
  if (!params.has(PREVIEW_PARAMS.preview)) return null;
@@ -83,11 +83,13 @@ const toSearchParams = (input)=>{
83
83
  //
84
84
  // const preview = readPreviewParams(request.url)
85
85
  // const content = preview?.token
86
- // ? new ContentClient({ token: preview.token, init: { cache: 'no-store' } })
86
+ // ? new ContentClient({ token: preview.token, space, environment })
87
87
  // : deliveryClient
88
88
  //
89
- // A made-up or expired token makes the client throw a ContentApiError with
90
- // status 401: answer with `deliveryClient` then.
89
+ // `space` and `environment` are the site's own: anyone can make a cms_token
90
+ // for a space of theirs, and naming yours makes such a token read nothing. A
91
+ // made-up, expired or other space's token makes the client throw a
92
+ // ContentApiError with status 401: answer with `deliveryClient` then.
91
93
  const readPreviewParams = (input)=>{
92
94
  const params = toSearchParams(input);
93
95
  if (!params.has(PREVIEW_PARAMS.preview)) return null;
package/dist/prompt.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
- var standardWebhooks = require('./standardWebhooks-kh5dc0ff.js');
3
+ var standardWebhooks = require('./standardWebhooks-leiz4jdn.js');
4
4
 
5
5
  // Your own model for prompt editing in the visual editor. Set the endpoint
6
6
  // under Space settings → Prompt editing; the CMS then sends each prompt there
package/dist/prompt.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { S as SECRET_PREFIX, v as verifySignature, D as DEFAULT_TOLERANCE } from './standardWebhooks-ew5mhpac.mjs';
1
+ import { S as SECRET_PREFIX, v as verifySignature, D as DEFAULT_TOLERANCE } from './standardWebhooks-ow7vu8g1.mjs';
2
2
 
3
3
  // Your own model for prompt editing in the visual editor. Set the endpoint
4
4
  // under Space settings → Prompt editing; the CMS then sends each prompt there
package/dist/site.js CHANGED
@@ -72,8 +72,14 @@ const base64Url = (bytes)=>{
72
72
  };
73
73
  const randomString = (bytes = 32)=>base64Url(crypto.getRandomValues(new Uint8Array(bytes)));
74
74
  const pkceChallenge = async (verifier)=>base64Url(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))));
75
- // Only a path on this site: never an open redirect.
76
- const safeReturnTo = (value)=>value && value.startsWith('/') && !value.startsWith('//') && !value.includes('\\') ? value : '/';
75
+ const PLACEHOLDER_ORIGIN = 'https://site.invalid';
76
+ // Only a path on this site: never an open redirect. Resolved the way a
77
+ // browser reads Location, which drops tabs and newlines and reads \ as /.
78
+ const safeReturnTo = (value)=>{
79
+ if (!value?.startsWith('/')) return '/';
80
+ const url = new URL(value, PLACEHOLDER_ORIGIN);
81
+ return url.origin === PLACEHOLDER_ORIGIN ? `${url.pathname}${url.search}${url.hash}` : '/';
82
+ };
77
83
  const normalizeBasePath = (basePath)=>`/${basePath.replace(/^\/+|\/+$/g, '')}`;
78
84
  // What the browser may reach through `/cms/api/…`: reads, and the writes
79
85
  // the prompt bar makes. Nothing else of the site API is exposed.
package/dist/site.mjs CHANGED
@@ -70,8 +70,14 @@ const base64Url = (bytes)=>{
70
70
  };
71
71
  const randomString = (bytes = 32)=>base64Url(crypto.getRandomValues(new Uint8Array(bytes)));
72
72
  const pkceChallenge = async (verifier)=>base64Url(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))));
73
- // Only a path on this site: never an open redirect.
74
- const safeReturnTo = (value)=>value && value.startsWith('/') && !value.startsWith('//') && !value.includes('\\') ? value : '/';
73
+ const PLACEHOLDER_ORIGIN = 'https://site.invalid';
74
+ // Only a path on this site: never an open redirect. Resolved the way a
75
+ // browser reads Location, which drops tabs and newlines and reads \ as /.
76
+ const safeReturnTo = (value)=>{
77
+ if (!value?.startsWith('/')) return '/';
78
+ const url = new URL(value, PLACEHOLDER_ORIGIN);
79
+ return url.origin === PLACEHOLDER_ORIGIN ? `${url.pathname}${url.search}${url.hash}` : '/';
80
+ };
75
81
  const normalizeBasePath = (basePath)=>`/${basePath.replace(/^\/+|\/+$/g, '')}`;
76
82
  // What the browser may reach through `/cms/api/…`: reads, and the writes
77
83
  // the prompt bar makes. Nothing else of the site API is exposed.
@@ -6,15 +6,22 @@
6
6
  const SECRET_PREFIX = 'whsec_';
7
7
  // Seconds a request's timestamp may be off, against replays.
8
8
  const DEFAULT_TOLERANCE = 5 * 60;
9
+ // Each signature costs an HMAC over the whole body, and the header is the
10
+ // sender's: a rotation needs two, so anything near this many is not the CMS.
11
+ const MAX_SIGNATURES = 5;
12
+ const SIGNATURE_BYTES = 32;
9
13
  const base64ToBytes = (value)=>Uint8Array.from(atob(value), (char)=>char.charCodeAt(0));
10
14
  // Checks a message's signature and timestamp; calls `fail` (which throws the
11
15
  // caller's own error) when it isn't from the CMS.
12
16
  const verifySignature = async ({ body, id, timestamp, signatures }, secret, { tolerance = DEFAULT_TOLERANCE, now = Date.now(), fail })=>{
13
17
  if (!id || !timestamp || !signatures) fail('Missing signature headers');
14
18
  const seconds = Number(timestamp);
15
- if (!Number.isFinite(seconds) || Math.abs(now / 1000 - seconds) > tolerance) {
19
+ // Written so a NaN tolerance rejects rather than skips the check.
20
+ if (!(Math.abs(now / 1000 - seconds) <= tolerance)) {
16
21
  fail('Timestamp outside the tolerance');
17
22
  }
23
+ const candidates = signatures.split(' ');
24
+ if (candidates.length > MAX_SIGNATURES) fail('Too many signatures');
18
25
  const key = await crypto.subtle.importKey('raw', base64ToBytes(secret.slice(SECRET_PREFIX.length)), {
19
26
  name: 'HMAC',
20
27
  hash: 'SHA-256'
@@ -23,7 +30,7 @@ const verifySignature = async ({ body, id, timestamp, signatures }, secret, { to
23
30
  ]);
24
31
  const signed = new TextEncoder().encode(`${id}.${timestamp}.${body}`);
25
32
  let valid = false;
26
- for (const signature of signatures.split(' ')){
33
+ for (const signature of candidates){
27
34
  const [version, value] = signature.split(',');
28
35
  if (version !== 'v1' || !value) continue;
29
36
  let bytes;
@@ -32,6 +39,7 @@ const verifySignature = async ({ body, id, timestamp, signatures }, secret, { to
32
39
  } catch {
33
40
  continue;
34
41
  }
42
+ if (bytes.length !== SIGNATURE_BYTES) continue;
35
43
  // crypto.subtle.verify compares in constant time.
36
44
  if (await crypto.subtle.verify('HMAC', key, bytes, signed)) valid = true;
37
45
  }
@@ -6,15 +6,22 @@
6
6
  const SECRET_PREFIX = 'whsec_';
7
7
  // Seconds a request's timestamp may be off, against replays.
8
8
  const DEFAULT_TOLERANCE = 5 * 60;
9
+ // Each signature costs an HMAC over the whole body, and the header is the
10
+ // sender's: a rotation needs two, so anything near this many is not the CMS.
11
+ const MAX_SIGNATURES = 5;
12
+ const SIGNATURE_BYTES = 32;
9
13
  const base64ToBytes = (value)=>Uint8Array.from(atob(value), (char)=>char.charCodeAt(0));
10
14
  // Checks a message's signature and timestamp; calls `fail` (which throws the
11
15
  // caller's own error) when it isn't from the CMS.
12
16
  const verifySignature = async ({ body, id, timestamp, signatures }, secret, { tolerance = DEFAULT_TOLERANCE, now = Date.now(), fail })=>{
13
17
  if (!id || !timestamp || !signatures) fail('Missing signature headers');
14
18
  const seconds = Number(timestamp);
15
- if (!Number.isFinite(seconds) || Math.abs(now / 1000 - seconds) > tolerance) {
19
+ // Written so a NaN tolerance rejects rather than skips the check.
20
+ if (!(Math.abs(now / 1000 - seconds) <= tolerance)) {
16
21
  fail('Timestamp outside the tolerance');
17
22
  }
23
+ const candidates = signatures.split(' ');
24
+ if (candidates.length > MAX_SIGNATURES) fail('Too many signatures');
18
25
  const key = await crypto.subtle.importKey('raw', base64ToBytes(secret.slice(SECRET_PREFIX.length)), {
19
26
  name: 'HMAC',
20
27
  hash: 'SHA-256'
@@ -23,7 +30,7 @@ const verifySignature = async ({ body, id, timestamp, signatures }, secret, { to
23
30
  ]);
24
31
  const signed = new TextEncoder().encode(`${id}.${timestamp}.${body}`);
25
32
  let valid = false;
26
- for (const signature of signatures.split(' ')){
33
+ for (const signature of candidates){
27
34
  const [version, value] = signature.split(',');
28
35
  if (version !== 'v1' || !value) continue;
29
36
  let bytes;
@@ -32,6 +39,7 @@ const verifySignature = async ({ body, id, timestamp, signatures }, secret, { to
32
39
  } catch {
33
40
  continue;
34
41
  }
42
+ if (bytes.length !== SIGNATURE_BYTES) continue;
35
43
  // crypto.subtle.verify compares in constant time.
36
44
  if (await crypto.subtle.verify('HMAC', key, bytes, signed)) valid = true;
37
45
  }
@@ -0,0 +1,4 @@
1
+ type TokenSource = string | (() => string | Promise<string>);
2
+
3
+ export type { TokenSource as T };
4
+ //# sourceMappingURL=tokenSource-my2ymemb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenSource-my2ymemb.d.ts","sources":["../src/tokenSource.ts"],"mappings":"AAAA,KAAK,WAAW,4BAA4B,OAAO;;","names":[]}
@@ -14,6 +14,15 @@ type Asset = {
14
14
  x: number;
15
15
  y: number;
16
16
  } | null;
17
+ products?: AssetProduct[];
18
+ };
19
+ type AssetProduct = {
20
+ id: string;
21
+ handle: string;
22
+ position: {
23
+ x: number;
24
+ y: number;
25
+ } | null;
17
26
  };
18
27
  type Entry<TFields extends Record<string, unknown> = Record<string, unknown>, TContentType extends string = string> = {
19
28
  id: string;
@@ -188,5 +197,5 @@ type ModelFile = {
188
197
  contentTypes: ContentTypeDefinition[];
189
198
  };
190
199
 
191
- export type { Asset as A, Block as B, ContentTypeDefinition as C, Entry as E, FieldDefinition as F, ImproveBlock as I, LinkTarget as L, ModelFile as M, RichTextFormat as R, ShopifyItem as S, RedirectList as a, Redirect as b, EntryList as c, Link as d, EntryStub as e, RichTextNode as f, ContentTypeKind as g, Environment as h, FieldType as i, RichTextDocument as j, RichTextMark as k };
192
- //# sourceMappingURL=types-mtjco3a5.d.ts.map
200
+ export type { Asset as A, Block as B, ContentTypeDefinition as C, Entry as E, FieldDefinition as F, ImproveBlock as I, LinkTarget as L, ModelFile as M, RichTextFormat as R, ShopifyItem as S, RedirectList as a, Redirect as b, EntryList as c, Link as d, EntryStub as e, RichTextNode as f, AssetProduct as g, ContentTypeKind as h, Environment as i, FieldType as j, RichTextDocument as k, RichTextMark as l };
201
+ //# sourceMappingURL=types-exvugblj.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-exvugblj.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,eAAe,YAAY;AAC3B;AACA,KAAK,YAAY;AACjB;AACA;AACA;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,UAAU;AACf;AACA,oBAAoB,MAAM;AAC1B;AACA;AACA;AACA,KAAK,IAAI;AACT;AACA,WAAW,UAAU;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,WAAW;AACxB,WAAW,UAAU;AACrB;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,WAAW;AAChB;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;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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
- var standardWebhooks = require('./standardWebhooks-kh5dc0ff.js');
3
+ var standardWebhooks = require('./standardWebhooks-leiz4jdn.js');
4
4
 
5
5
  // Webhooks from Obelism Content (the CMS's docs/guides/webhooks): one signed
6
6
  // POST per event (Standard Webhooks), telling your site what the delivery
package/dist/webhooks.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { S as SECRET_PREFIX, v as verifySignature, D as DEFAULT_TOLERANCE } from './standardWebhooks-ew5mhpac.mjs';
1
+ import { S as SECRET_PREFIX, v as verifySignature, D as DEFAULT_TOLERANCE } from './standardWebhooks-ow7vu8g1.mjs';
2
2
 
3
3
  // Webhooks from Obelism Content (the CMS's docs/guides/webhooks): one signed
4
4
  // POST per event (Standard Webhooks), telling your site what the delivery
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.22.0",
4
+ "version": "0.24.0",
5
5
  "keywords": [
6
6
  "cms",
7
7
  "headless-cms",
@@ -73,6 +73,16 @@
73
73
  "types": "./dist/webhooks.d.ts",
74
74
  "default": "./dist/webhooks.js"
75
75
  }
76
+ },
77
+ "./oidc": {
78
+ "import": {
79
+ "types": "./dist/oidc.d.mts",
80
+ "default": "./dist/oidc.mjs"
81
+ },
82
+ "require": {
83
+ "types": "./dist/oidc.d.ts",
84
+ "default": "./dist/oidc.js"
85
+ }
76
86
  }
77
87
  },
78
88
  "scripts": {
@@ -89,18 +99,27 @@
89
99
  "devDependencies": {
90
100
  "@obelism/sdk-typescript-config": "^0.0.0",
91
101
  "@rollup/plugin-node-resolve": "^16.0.3",
92
- "@types/node": "^24.0.0",
102
+ "@types/node": "^24.19.2",
93
103
  "@typescript/typescript6": "^6.0.2",
104
+ "@vercel/oidc": "4.0.3",
94
105
  "bunchee": "^7.0.2",
95
- "jsdom": "^30.1.2",
96
- "npm-run-all2": "^9.0.2",
97
- "oxlint": "^1.88.0",
98
- "rollup": "^4.64.7",
106
+ "jsdom": "^30.1.3",
107
+ "npm-run-all2": "^9.0.4",
108
+ "oxlint": "^1.89.0",
109
+ "rollup": "^4.65.0",
99
110
  "rollup-plugin-swc3": "^0.12.1",
100
111
  "typescript": "^7.0.2",
101
- "vitest": "^5.0.4"
112
+ "vitest": "^5.0.5"
102
113
  },
103
114
  "engines": {
104
115
  "node": ">=20"
116
+ },
117
+ "peerDependencies": {
118
+ "@vercel/oidc": "^4.0.0"
119
+ },
120
+ "peerDependenciesMeta": {
121
+ "@vercel/oidc": {
122
+ "optional": true
123
+ }
105
124
  }
106
125
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"types-mtjco3a5.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,UAAU;AACf;AACA,oBAAoB,MAAM;AAC1B;AACA;AACA;AACA,KAAK,IAAI;AACT;AACA,WAAW,UAAU;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,WAAW;AACxB,WAAW,UAAU;AACrB;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,WAAW;AAChB;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;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":[]}