1"use strict";(self.webpackChunkofficial=self.webpackChunkofficial||[]).push([[8677],{3004:function(e,t,n){n.r(t),n.d(t,{Head:function(){return y},default:function(){return v}});var a=n(1151),l=n(7294);function r(e){const t=Object.assign({p:"p",code:"code",h2:"h2",pre:"pre",em:"em",strong:"strong",table:"table",thead:"thead",tr:"tr",th:"th",tbody:"tbody",td:"td"},(0,a.ah)(),e.components),{Callout:n,Steps:r,Step:o}=t;return n||c("Callout",!0),o||c("Step",!0),r||c("Steps",!0),l.createElement(l.Fragment,null,l.createElement(t.p,null,"The editor supports two persistence models. Which one you're in is decided by a\nsingle config field: pass ",l.createElement(t.code,null,"access")," and you're online, omit it and you're\noffline."),"\n",l.createElement(t.h2,null,"Offline: you own the document"),"\n",l.createElement(t.p,null,"The default. You hand in ",l.createElement(t.code,null,"components"),", you get change notifications through\n",l.createElement(t.code,null,"onSave"),", and nothing ever leaves the browser except through your code."),"\n",l.createElement(t.pre,null,l.createElement(t.code,{className:"language-js"},'new cdtrStarter({\n target: document.getElementById("root"),\n components: await loadFromMyApi(),\n savedDebounce: 1000,\n onSave: async ({ toJSON }) => {\n await fetch("/api/pages/42", {\n method: "PUT",\n headers: { "Content-Type": "application/json" },\n body: JSON.stringify({ document: await toJSON() }),\n });\n },\n});\n')),"\n",l.createElement(t.p,null,"Use this when you already have a database, when documents belong to your\ntenants, or when the editor must work behind your own auth."),"\n",l.createElement(t.h2,null,"Online: Creaditor stores it"),"\n",l.createElement(t.p,null,"You mint a site-scoped, one-time edit token on your backend and pass it as\n",l.createElement(t.code,null,"access"),". On boot the editor exchanges it for a real access token plus the\nsite's document, loads that document, and opens an autosave socket. Edits\nstream to the server as they happen; you write no persistence code."),"\n",l.createElement(t.pre,null,l.createElement(t.code,{className:"language-js"},'new cdtrStarter({\n target: document.getElementById("root"),\n access: "<one-time edit token from your backend>",\n});\n')),"\n",l.createElement(t.p,null,"Note what you ",l.createElement(t.em,null,"don't")," pass: ",l.createElement(t.code,null,"components"),". The document comes from the server,\nand passing your own would be replaced by the loaded one."),"\n",l.createElement(n,{type:"warning",title:"The access token is single-use and site-scoped"},l.createElement(t.p,null,"Mint it server-side, per editing session, for the specific site being edited.\nIf the exchange fails the editor logs the failure and reveals an empty canvas\nrather than hanging on the splash. Check the console before assuming the\neditor broke.")),"\n",l.createElement(t.h2,null,"How edits are batched"),"\n",l.createElement(t.p,null,"Every change produces one or more ",l.createElement(t.strong,null,"edit steps")," (a component id, a prop, the\nnew value). Steps accumulate and flush together:"),"\n",l.createElement(r,null,l.createElement(o,{title:"The user acts"},l.createElement(t.p,null,"Dragging a slider produces a stream of steps, not one write per pixel.")),l.createElement(o,{title:"The debounce window closes"},l.createElement(t.p,null,l.createElement(t.code,null,"savedDebounce")," milliseconds after the last step, 1000 by default.")),l.createElement(o,{title:"Everything commits at once"},l.createElement(t.p,null,"Pending steps are packed into a transaction, sent over the socket when\nonline, and handed to ",l.createElement(t.code,null,"onSave")," in both modes."))),"\n",l.createElement(t.p,null,"Lowering ",l.createElement(t.code,null,"savedDebounce")," means more writes and fresher data; raising it means\nfewer, larger writes. Below ~300 ms you're mostly persisting intermediate\nstates."),"\n",l.createElement(t.h2,null,"What onSave receives"),"\n",l.createElement(t.pre,null,l.createElement(t.code,{className:"language-js"},"onSave: ({ transactions, toJSON, toHTML }) =>
1 {\n transactions; // the edit steps that just committed\n toJSON(); // Promise<document JSON> , the full document\n toHTML(); // Promise<string> , rendered output\n};\n")),"\n",l.createElement(t.p,null,"It's ",l.createElement(t.strong,null,"not")," the document. That's deliberate: most commits touch one prop, and\nserializing the whole tree on every keystroke would be wasteful. Ask for what\nyou need, when you need it."),"\n",l.createElement(t.p,null,l.createElement(t.code,null,"transactions"),' is useful for change detection (mark dirty, show "savingâ¦", keep\nan audit trail). Don\'t try to apply them yourself as patches; treat them as a\nsignal and store the result of ',l.createElement(t.code,null,"toJSON()"),"."),"\n",l.createElement(n,{type:"tip",title:"Debounce your network call too"},l.createElement(t.p,null,l.createElement(t.code,null,"onSave")," is already debounced, but a user rearranging a page for a minute will\nstill produce many commits. Most integrations coalesce further: mark dirty on\n",l.createElement(t.code,null,"onSave"),", flush to the API on an interval or on blur.")),"\n",l.createElement(t.h2,null,"Collaboration"),"\n",l.createElement(t.p,null,l.createElement(t.code,null,"collaborator")," opens a realtime presence session, cursors and selection borders\nfor other people in the same document:"),"\n",l.createElement(t.pre,null,l.createElement(t.code,{className:"language-js"},'collaborator: {\n userId: "u-42",\n sessionId: "s-99",\n name: "Dana",\n avatar: "https://example.com/dana.png",\n}\n')),"\n",l.createElement(t.p,null,"Presence is independent of saving: ",l.createElement(t.code,null,"collaborator")," alone does not persist\nanything, and ",l.createElement(t.code,null,"access")," alone does not show other people. Pass both for a shared,\nsaved session."),"\n",l.createElement(t.h2,null,"Choosing"),"\n",l.createElement(t.table,null,l.createElement(t.thead,null,l.createElement(t.tr,null,l.createElement(t.th,null,"You haveâ¦"),l.createElement(t.th,null,"Use"))),l.createElement(t.tbody,null,l.createElement(t.tr,null,l.createElement(t.td,null,"Your own database and auth"),l.createElement(t.td,null,"Offline (",l.createElement(t.code,null,"components")," + ",l.createElement(t.code,null,"onSave"),")")),l.createElement(t.tr,null,l.createElement(t.td,null,"No storage, want hosting handled"),l.createElement(t.td,null,"Online (",l.createElement(t.code,null,"access"),")")),l.createElement(t.tr,null,l.createElement(t.td,null,"Multiple people in one document"),l.createElement(t.td,null,"Online + ",l.createElement(t.code,null,"collaborator"))),l.createElement(t.tr,null,l.createElement(t.td,null,"Strict data-residency requirements"),l.createElement(t.td,null,"Offline")))))}var o=function(e){void 0===e&&(e={});const{wrapper:t}=Object.assign({},(0,a.ah)(),e.components);return t?l.createElement(t,e,l.createElement(r,e)):r(e)};function c(e,t){throw new Error("Expected "+(t?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}var s=n(4160),i=n(9872),m=n(3414),u=n(332),d=n(1073),p=n(9298);const h=n(7926),E="https://github.com/Creaditor/website-official/edit/main/",g=e=>"https://www.npmjs.com/package/"+e;function f(e){let{title:t}=e;const[n,a]=l.useState(null);if(n)return l.createElement("span",{className:h.feedback},"yes"===n?"Thanks, glad it helped.":"Thanks, we'll take another pass at this page.");const r=e=>{var n,l;a(e),null===(n=(l=window).gtag)||void 0===n||n.call(l,"event","docs_feedback",{event_category:"docs",event_label:t,value:"yes"===e?1:0})};return l.createElement("span",{className:h.feedback},"Was this page helpful?",l.createElement("button",{type:"button",className:h.feedbackButton,onClick:()=>r("yes")},l.createElement(p.Z7,null)," Yes"),l.createElement("button",{type:"button",className:h.feedbackButton,onClick:()=>r("no")},l.createElement(p.C_,null)," No"))}function b(e){let{data:t,pageContext:n,children:r,location:o}=e;const{frontmatter:c,fields:d}=t.mdx,{previous:p,next:b}=n;return l.createElement(i.Z,{currentPath:o.pathname},l.createElement("nav",{className:h.breadcrumbs,"aria-label":"Breadcrumb"},l.createElement(s.rU,{to:"/docs"},"Docs"),l.createElement("span",null,"/"),l.createElement("span",null,(0,u.R)(d.section))),l.createElement("h1",{className:h.pageTitle},c.title),c.description&&l.createElement("p",{className:h.pageLede},c.description),c.npmPackage&&l.createElement("p",{className:h.npmSource},l.createElement("a",{href:g(c.npmPackage),target:"_blank",rel:"noreferrer noopener"},c.npmPackage),c.npmVersion&&l.createElement("span",{className:h.navBadge},"v",c.npmVersion)),l.createElement("div",{className:h.prose},l.createElement(a.Zo,{components:m.ZP},r)),l.createElement("footer",{className:h.pageFooter},l.createElement("div",{className:h.footerMeta},l.createElement(f,{title:c.title}),c.npmPackage?l.createElement("a",{className:h.footerLink,href:g(c.npmPackage),target:"_blank",rel:"noreferrer noopener"},"View on npm"):d.sourcePath&&l.createElement("a",{className:h.footerLink,href:""+E+d.sourcePath,target:"_blank",rel:"noreferrer noopener"},"Edit this page")),l.createElement("div",{className:h.prevNext},p&&l.createElement(s.rU,{className:h.prevNextCard,to:p.slug},l.createElement("span",{className:h.prevNextLabel},"Previous"),l.createElement("span",{className:h.prevNextTitle},p.title)),b&&l.createElement(s.rU,{className:h.prevNextCard+" "+h.prevNextEnd,to:b.slug},l.createElement("span",{className:h.prevNextLabel},"Next"),l.createElement("span",{className:h.prevNextTitle},b.title)))))}function v(e){return l.createElement(b,e,l.createElement(o,e))}const y=e=>{var t;let{data:n}=e;return l.createElement(d.Z,{title:n.mdx.frontmatter.title+" | Creaditor Docs",description:null!==(t=n.mdx.frontmatter.description)&&void 0!==t?t:"Creaditor developer documentation, embed the editor, build custom elements, integrate with any stack.",pathname:n.mdx.fields.slug})}},3414:function(e,t,n){var a=n(3366),l=n(7294),r=n(4160),o=n(7902),c=n(9298),s=n(7926);const i=["children"],m=["href","children"],u=["children","className"],d=["children"],p=s;function h(e){return null==e||"boolean"==typeof e?"":"string"==typeof e||"number"==typeof e?String(e):Array.isArray(e)?e.map(h).join(""):l.isValidElement(e)?h(e.props.children):""}const E=e=>function(t){let{children:n}=t,r=(0,a.Z)(t,i);const o=h(n).toLowerCase().replace(/[`*_~]/g,"").replace(/[^a-z0-9Ö-׿]+/g,"-").replace(/^-+|-+$/g,"");return l.createElement(e,Object.assign({id:o},r),n,l.createElement("a",{className:p.headingAnchor,href:"#"+o,"aria-label":"Link to this section"},l.createElement(c.xP,null)))};const g={note:c.sz,tip:c.ag,warning:c.aN,danger:c.cS},f={note:"calloutNote",tip:"calloutTip",warning:"calloutWarning",danger:"calloutDanger"};const b={rocket:c.WC,cube:c.FM,book:c.vJ,terminal:c.Fx,spark:c.xT};const v={h2:E("h2"),h3:E("h3"),h4:E("h4"),a:function(e){let{href:t="",children:n}=e,o=(0,a.Z)(e,m);return/^\/(?!\/)/.test(t)?l.createElement(r.rU,Object.assign({to:t},o),n):l.createElement("a",Object.assign({href:t,target:"_blank",rel:"noreferrer noopener"},o),n)},pre:function(e){let{children:t}=e;const n=l.Children.toArray(t)[0];if(!l.isValidElement(n))return l.createElement("pre",null,t);const{className:a,meta:r,children:c}=n.props,{language:s,highlightLines:i,filename:m}=(0,o.J)(a,r);return l.createElement(o.Z,{code:String(null!=c?c:"").replace(/\n$/,""),language:s,filename:m,highlightLines:i})},code:function(e){let{children:t,className:n}=e,r=(0,a.Z)(e,u);return n?l.createElement("code",Object.assign({className:n},r),t):l.createElement("code",Object.assign({className:p.inlineCode},r),t)},table:function(e){let{children:t}=e,n=(0,a.Z)(e,d);return l.createElement("div",{className:p.tableWrap},l.createElement("table",Object.assign({className:p.table},n),t))},Callout:function(e){let{type:t="note",title:n,children:a}=e;const r=g[t];return l.createElement("div",{className:p.callout+" "+p[f[t]]},l.createElement("span",{className:p.calloutIcon}
1,l.createElement(r,null)),l.createElement("div",{className:p.calloutBody},n&&l.createElement("strong",{className:p.calloutTitle},n),a))},Steps:function(e){let{children:t}=e;return l.createElement("div",{className:p.steps},t)},Step:function(e){let{title:t,children:n}=e;return l.createElement("div",{className:p.step},t&&l.createElement("p",{className:p.stepTitle},t),n)},Card:function(e){let{title:t,href:n,icon:a,children:o}=e;const c=a?b[a]:null,s=/^\/(?!\/)/.test(n),i=l.createElement(l.Fragment,null,c&&l.createElement("span",{className:p.cardIcon},l.createElement(c,null)),l.createElement("p",{className:p.cardTitle},t),o&&l.createElement("p",{className:p.cardDesc},o));return s?l.createElement(r.rU,{className:p.card,to:n},i):l.createElement("a",{className:p.card,href:n,target:"_blank",rel:"noreferrer noopener"},i)},CardGrid:function(e){let{children:t}=e;return l.createElement("div",{className:p.cardGrid},t)},Tabs:function(e){let{labels:t,children:n}=e;const[a,r]=l.useState(0),o=l.Children.toArray(n);return l.createElement("div",{className:p.tabs},l.createElement("div",{className:p.tabList,role:"tablist"},t.map(((e,t)=>l.createElement("button",{key:e,type:"button",role:"tab","aria-selected":t===a,className:p.tab+" "+(t===a?p.tabActive:""),onClick:()=>r(t)},e)))),l.createElement("div",{className:p.tabPanel,role:"tabpanel"},o[a]))}};t.ZP=v},1151:function(e,t,n){n.d(t,{Zo:function(){return c},ah:function(){return r}});var a=n(7294);const l=a.createContext({});function r(e){const t=a.useContext(l);return a.useMemo((()=>"function"==typeof e?e(t):{...t,...e}),[t,e])}const o={};function c({components:e,children:t,disableParentContext:n}){let c;return c=n?"function"==typeof e?e({}):e||o:r(e),a.createElement(l.Provider,{value:c},t)}}}]); 2//# sourceMappingURL=component---src-templates-doc-tsx-content-file-path-src-content-docs-02-concepts-04-persistence-mdx-bd25a332f0a4c79fa337.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.