[{"data":1,"prerenderedAt":1132},["ShallowReactive",2],{"navigation-landing-en":3,"navigation-nuxt-auto-en":180,"navigation-nuxt-protokit-en":338,"/docs/nuxt-protokit/offline-first-en":444,"/docs/nuxt-protokit/offline-first-surround-en":1127},[4,8,13,28,41,51,64,77,94,110,134,150,157,172],{"title":5,"path":6,"stem":7},"Overview","/docs/landing","0.docs/1.landing/001.index",{"title":9,"path":10,"stem":11,"badge":12},"Built-in Features","/docs/landing/built-in-features","0.docs/1.landing/002.built-in-features","New",{"title":14,"path":15,"stem":16,"children":17,"icon":27},"Content Foundation","/docs/landing/content","0.docs/1.landing/02.content/1.index",[18,19,23],{"title":5,"path":15,"stem":16},{"title":20,"path":21,"stem":22},"Details","/docs/landing/content/details","0.docs/1.landing/02.content/2.details",{"title":24,"path":25,"stem":26},"Technical","/docs/landing/content/technical","0.docs/1.landing/02.content/4.technical","i-heroicons-document-text",{"title":29,"path":30,"stem":31,"children":32,"icon":40},"Regional Content","/docs/landing/regional","0.docs/1.landing/03.regional/1.index",[33,34,37],{"title":5,"path":30,"stem":31},{"title":20,"path":35,"stem":36},"/docs/landing/regional/details","0.docs/1.landing/03.regional/2.details",{"title":24,"path":38,"stem":39},"/docs/landing/regional/technical","0.docs/1.landing/03.regional/4.technical","i-heroicons-globe-alt",{"title":42,"path":43,"stem":44,"children":45,"icon":50},"Multi-language","/docs/landing/multilang","0.docs/1.landing/04.multilang/1.index",[46,47],{"title":5,"path":43,"stem":44},{"title":20,"path":48,"stem":49},"/docs/landing/multilang/details","0.docs/1.landing/04.multilang/2.details","i-heroicons-language",{"title":52,"path":53,"stem":54,"children":55,"icon":63},"Blog","/docs/landing/blog","0.docs/1.landing/05.blog/1.index",[56,57,60],{"title":5,"path":53,"stem":54},{"title":20,"path":58,"stem":59},"/docs/landing/blog/details","0.docs/1.landing/05.blog/2.details",{"title":24,"path":61,"stem":62},"/docs/landing/blog/technical","0.docs/1.landing/05.blog/4.technical","i-heroicons-pencil-square",{"title":65,"path":66,"stem":67,"children":68,"icon":76},"Documentation","/docs/landing/docs","0.docs/1.landing/06.docs/1.index",[69,70,73],{"title":5,"path":66,"stem":67},{"title":20,"path":71,"stem":72},"/docs/landing/docs/details","0.docs/1.landing/06.docs/2.details",{"title":24,"path":74,"stem":75},"/docs/landing/docs/technical","0.docs/1.landing/06.docs/4.technical","i-heroicons-book-open",{"title":78,"path":79,"stem":80,"children":81,"icon":93},"Forms","/docs/landing/forms","0.docs/1.landing/07.forms/1.index",[82,83,86,90],{"title":5,"path":79,"stem":80},{"title":20,"path":84,"stem":85},"/docs/landing/forms/details","0.docs/1.landing/07.forms/2.details",{"title":87,"path":88,"stem":89},"Admin","/docs/landing/forms/admin","0.docs/1.landing/07.forms/3.admin",{"title":24,"path":91,"stem":92},"/docs/landing/forms/technical","0.docs/1.landing/07.forms/4.technical","i-heroicons-clipboard-document-list",{"title":95,"path":96,"stem":97,"children":98,"icon":109},"Email","/docs/landing/email","0.docs/1.landing/08.email/1.index",[99,100,103,106],{"title":5,"path":96,"stem":97},{"title":20,"path":101,"stem":102},"/docs/landing/email/details","0.docs/1.landing/08.email/2.details",{"title":87,"path":104,"stem":105},"/docs/landing/email/admin","0.docs/1.landing/08.email/3.admin",{"title":24,"path":107,"stem":108},"/docs/landing/email/technical","0.docs/1.landing/08.email/4.technical","i-heroicons-envelope",{"title":111,"path":112,"stem":113,"children":114,"icon":133},"Feedback Platform","/docs/landing/feedback","0.docs/1.landing/09.feedback/1.index",[115,116,119,122,125,129],{"title":5,"path":112,"stem":113},{"title":20,"path":117,"stem":118},"/docs/landing/feedback/details","0.docs/1.landing/09.feedback/2.details",{"title":87,"path":120,"stem":121},"/docs/landing/feedback/admin","0.docs/1.landing/09.feedback/3.admin",{"title":24,"path":123,"stem":124},"/docs/landing/feedback/technical","0.docs/1.landing/09.feedback/4.technical",{"title":126,"path":127,"stem":128},"Compare vs SaaS","/docs/landing/feedback/compare","0.docs/1.landing/09.feedback/5.compare",{"title":130,"path":131,"stem":132},"FAQ","/docs/landing/feedback/faq","0.docs/1.landing/09.feedback/6.faq","i-heroicons-chat-bubble-left-right",{"title":135,"path":136,"stem":137,"children":138,"icon":149},"Storage","/docs/landing/storage","0.docs/1.landing/10.storage/1.index",[139,140,143,146],{"title":5,"path":136,"stem":137},{"title":20,"path":141,"stem":142},"/docs/landing/storage/details","0.docs/1.landing/10.storage/2.details",{"title":87,"path":144,"stem":145},"/docs/landing/storage/admin","0.docs/1.landing/10.storage/3.admin",{"title":24,"path":147,"stem":148},"/docs/landing/storage/technical","0.docs/1.landing/10.storage/4.technical","i-heroicons-circle-stack",{"title":151,"path":152,"stem":153,"children":154,"icon":156},"Offline First","/docs/landing/offline-first","0.docs/1.landing/11.offline-first/1.index",[155],{"title":151,"path":152,"stem":153},"i-heroicons-users",{"title":158,"path":159,"stem":160,"children":161,"icon":156},"Yjs Sync","/docs/landing/yjs-sync","0.docs/1.landing/12.yjs-sync/1.index",[162,163,166,169],{"title":5,"path":159,"stem":160},{"title":20,"path":164,"stem":165},"/docs/landing/yjs-sync/details","0.docs/1.landing/12.yjs-sync/2.details",{"title":87,"path":167,"stem":168},"/docs/landing/yjs-sync/admin","0.docs/1.landing/12.yjs-sync/3.admin",{"title":24,"path":170,"stem":171},"/docs/landing/yjs-sync/technical","0.docs/1.landing/12.yjs-sync/4.technical",{"title":173,"path":174,"stem":175,"children":176,"badge":178,"icon":179},"Newsletter","/docs/landing/newsletter","0.docs/1.landing/13.newsletter/index",[177],{"title":173,"path":174,"stem":175,"badge":178},"Coming Soon","i-lucide-send",[181,184,202,298],{"title":5,"path":182,"stem":183},"/docs/nuxt-auto","0.docs/3.nuxt-auto/index",{"title":185,"path":186,"stem":187,"children":188,"page":201},"Getting Started","/docs/nuxt-auto/getting-started","0.docs/3.nuxt-auto/1.getting-started",[189,193,197],{"title":190,"path":191,"stem":192},"Introduction","/docs/nuxt-auto/getting-started/introduction","0.docs/3.nuxt-auto/1.getting-started/1.introduction",{"title":194,"path":195,"stem":196},"Installation","/docs/nuxt-auto/getting-started/installation","0.docs/3.nuxt-auto/1.getting-started/2.installation",{"title":198,"path":199,"stem":200},"Quick Start","/docs/nuxt-auto/getting-started/quick-start","0.docs/3.nuxt-auto/1.getting-started/3.quick-start",false,{"title":203,"path":204,"stem":205,"children":206,"page":201},"Auto API","/docs/nuxt-auto/auto-api","0.docs/3.nuxt-auto/2.auto-api",[207,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,270,274,278,282,286,290,294],{"title":185,"path":208,"stem":209},"/docs/nuxt-auto/auto-api/getting-started","0.docs/3.nuxt-auto/2.auto-api/1.getting-started",{"title":211,"path":212,"stem":213},"Aggregations","/docs/nuxt-auto/auto-api/aggregations","0.docs/3.nuxt-auto/2.auto-api/10.aggregations",{"title":215,"path":216,"stem":217},"Lifecycle Hooks","/docs/nuxt-auto/auto-api/lifecycle-hooks","0.docs/3.nuxt-auto/2.auto-api/11.lifecycle-hooks",{"title":219,"path":220,"stem":221},"Many-to-Many (M2M) Relationships","/docs/nuxt-auto/auto-api/m2m-relationships","0.docs/3.nuxt-auto/2.auto-api/12.m2m-relationships",{"title":223,"path":224,"stem":225},"Plugin System","/docs/nuxt-auto/auto-api/plugin-system","0.docs/3.nuxt-auto/2.auto-api/13.plugin-system",{"title":227,"path":228,"stem":229},"Database Adapters","/docs/nuxt-auto/auto-api/database-adapters","0.docs/3.nuxt-auto/2.auto-api/14.database-adapters",{"title":231,"path":232,"stem":233},"Custom Endpoints","/docs/nuxt-auto/auto-api/custom-endpoints","0.docs/3.nuxt-auto/2.auto-api/15.custom-endpoints",{"title":235,"path":236,"stem":237},"Multi-Tenancy","/docs/nuxt-auto/auto-api/multi-tenancy","0.docs/3.nuxt-auto/2.auto-api/16.multi-tenancy",{"title":239,"path":240,"stem":241},"Validation","/docs/nuxt-auto/auto-api/validation","0.docs/3.nuxt-auto/2.auto-api/2.validation",{"title":243,"path":244,"stem":245},"Rate Limiting","/docs/nuxt-auto/auto-api/rate-limiting","0.docs/3.nuxt-auto/2.auto-api/20.rate-limiting",{"title":247,"path":248,"stem":249},"Request Metadata Plugin","/docs/nuxt-auto/auto-api/request-metadata","0.docs/3.nuxt-auto/2.auto-api/21.request-metadata",{"title":251,"path":252,"stem":253},"Plugin Catalog","/docs/nuxt-auto/auto-api/plugin-catalog","0.docs/3.nuxt-auto/2.auto-api/22.plugin-catalog",{"title":255,"path":256,"stem":257},"Handler Overrides","/docs/nuxt-auto/auto-api/handler-overrides","0.docs/3.nuxt-auto/2.auto-api/3.handler-overrides",{"title":259,"path":260,"stem":261},"Cloudflare D1","/docs/nuxt-auto/auto-api/cloudflare-d1","0.docs/3.nuxt-auto/2.auto-api/30.cloudflare-d1",{"title":263,"path":264,"stem":265},"SQLite to D1 Migration","/docs/nuxt-auto/auto-api/migration-sqlite-d1","0.docs/3.nuxt-auto/2.auto-api/31.migration-sqlite-d1",{"title":267,"path":268,"stem":269},"Frontend Composables","/docs/nuxt-auto/auto-api/frontend-composables","0.docs/3.nuxt-auto/2.auto-api/32.frontend-composables",{"title":271,"path":272,"stem":273},"Testing","/docs/nuxt-auto/auto-api/testing","0.docs/3.nuxt-auto/2.auto-api/33.testing",{"title":275,"path":276,"stem":277},"Pagination","/docs/nuxt-auto/auto-api/pagination","0.docs/3.nuxt-auto/2.auto-api/4.pagination",{"title":279,"path":280,"stem":281},"Soft Deletes","/docs/nuxt-auto/auto-api/soft-deletes","0.docs/3.nuxt-auto/2.auto-api/5.soft-deletes",{"title":283,"path":284,"stem":285},"Authentication & Authorization","/docs/nuxt-auto/auto-api/authentication-authorization","0.docs/3.nuxt-auto/2.auto-api/6.authentication-authorization",{"title":287,"path":288,"stem":289},"Better-Auth Integration","/docs/nuxt-auto/auto-api/better-auth","0.docs/3.nuxt-auto/2.auto-api/7.better-auth",{"title":291,"path":292,"stem":293},"Nested Relations","/docs/nuxt-auto/auto-api/nested-relationships","0.docs/3.nuxt-auto/2.auto-api/8.nested-relationships",{"title":295,"path":296,"stem":297},"Bulk Operations","/docs/nuxt-auto/auto-api/bulk-operations","0.docs/3.nuxt-auto/2.auto-api/9.bulk-operations",{"title":299,"path":300,"stem":301,"children":302,"page":201},"Auto Admin","/docs/nuxt-auto/auto-admin","0.docs/3.nuxt-auto/3.auto-admin",[303,306,310,314,318,322,326,330,334],{"title":185,"path":304,"stem":305},"/docs/nuxt-auto/auto-admin/getting-started","0.docs/3.nuxt-auto/3.auto-admin/1.getting-started",{"title":307,"path":308,"stem":309},"Configuration & Theming","/docs/nuxt-auto/auto-admin/configuration-theming","0.docs/3.nuxt-auto/3.auto-admin/2.configuration-theming",{"title":311,"path":312,"stem":313},"Resource Configuration","/docs/nuxt-auto/auto-admin/resource-configuration","0.docs/3.nuxt-auto/3.auto-admin/3.resource-configuration",{"title":315,"path":316,"stem":317},"Form Fields & Widgets","/docs/nuxt-auto/auto-admin/form-fields-widgets","0.docs/3.nuxt-auto/3.auto-admin/4.form-fields-widgets",{"title":319,"path":320,"stem":321},"Permissions","/docs/nuxt-auto/auto-admin/permissions","0.docs/3.nuxt-auto/3.auto-admin/5.permissions",{"title":323,"path":324,"stem":325},"Custom Pages","/docs/nuxt-auto/auto-admin/custom-pages","0.docs/3.nuxt-auto/3.auto-admin/6.custom-pages",{"title":327,"path":328,"stem":329},"M2M Relationships","/docs/nuxt-auto/auto-admin/m2m-relationships","0.docs/3.nuxt-auto/3.auto-admin/7.m2m-relationships",{"title":331,"path":332,"stem":333},"Custom Actions","/docs/nuxt-auto/auto-admin/custom-actions","0.docs/3.nuxt-auto/3.auto-admin/8.custom-actions",{"title":335,"path":336,"stem":337},"Composables","/docs/nuxt-auto/auto-admin/composables","0.docs/3.nuxt-auto/3.auto-admin/9.composables",[339,342,355,382,400,415,425],{"title":5,"path":340,"stem":341},"/docs/nuxt-protokit","0.docs/4.nuxt-protokit/index",{"title":185,"path":343,"stem":344,"children":345,"icon":354},"/docs/nuxt-protokit/getting-started","0.docs/4.nuxt-protokit/1.getting-started/1.index",[346,347,351],{"title":190,"path":343,"stem":344},{"title":348,"path":349,"stem":350},"Core Concepts","/docs/nuxt-protokit/getting-started/concepts","0.docs/4.nuxt-protokit/1.getting-started/2.concepts",{"title":198,"path":352,"stem":353},"/docs/nuxt-protokit/getting-started/quick-start","0.docs/4.nuxt-protokit/1.getting-started/3.quick-start","i-lucide-rocket",{"title":356,"path":357,"stem":358,"children":359,"icon":381},"Schemas","/docs/nuxt-protokit/schemas","0.docs/4.nuxt-protokit/2.schemas/1.index",[360,361,365,369,373,377],{"title":5,"path":357,"stem":358},{"title":362,"path":363,"stem":364},"Field Types","/docs/nuxt-protokit/schemas/fields","0.docs/4.nuxt-protokit/2.schemas/2.fields",{"title":366,"path":367,"stem":368},"Collections","/docs/nuxt-protokit/schemas/collections","0.docs/4.nuxt-protokit/2.schemas/3.collections",{"title":370,"path":371,"stem":372},"Derived & Computed","/docs/nuxt-protokit/schemas/derived-computed","0.docs/4.nuxt-protokit/2.schemas/4.derived-computed",{"title":374,"path":375,"stem":376},"Connections","/docs/nuxt-protokit/schemas/connections","0.docs/4.nuxt-protokit/2.schemas/5.connections",{"title":378,"path":379,"stem":380},"Visualizations & Layouts","/docs/nuxt-protokit/schemas/visualizations","0.docs/4.nuxt-protokit/2.schemas/6.visualizations","i-lucide-file-code",{"title":335,"path":383,"stem":384,"children":385,"icon":399},"/docs/nuxt-protokit/composables","0.docs/4.nuxt-protokit/3.composables/1.index",[386,387,391,395],{"title":5,"path":383,"stem":384},{"title":388,"path":389,"stem":390},"usePrototype","/docs/nuxt-protokit/composables/use-prototype","0.docs/4.nuxt-protokit/3.composables/2.use-prototype",{"title":392,"path":393,"stem":394},"useProtoDoc","/docs/nuxt-protokit/composables/use-proto-doc","0.docs/4.nuxt-protokit/3.composables/3.use-proto-doc",{"title":396,"path":397,"stem":398},"useProtoCollection","/docs/nuxt-protokit/composables/use-proto-collection","0.docs/4.nuxt-protokit/3.composables/4.use-proto-collection","i-lucide-layers",{"title":401,"path":402,"stem":403,"children":404,"icon":414},"Components","/docs/nuxt-protokit/components","0.docs/4.nuxt-protokit/4.components/1.index",[405,406,410],{"title":5,"path":402,"stem":403},{"title":407,"path":408,"stem":409},"ProtoTool","/docs/nuxt-protokit/components/proto-tool","0.docs/4.nuxt-protokit/4.components/2.proto-tool",{"title":411,"path":412,"stem":413},"ProtoCrudModal","/docs/nuxt-protokit/components/proto-crud-modal","0.docs/4.nuxt-protokit/4.components/3.proto-crud-modal","i-lucide-puzzle",{"title":151,"path":416,"stem":417,"children":418,"icon":424},"/docs/nuxt-protokit/offline-first","0.docs/4.nuxt-protokit/5.offline-first/1.index",[419,420],{"title":151,"path":416,"stem":417},{"title":421,"path":422,"stem":423},"Corruption Recovery","/docs/nuxt-protokit/offline-first/corruption-recovery","0.docs/4.nuxt-protokit/5.offline-first/2.corruption-recovery","i-lucide-wifi-off",{"title":426,"icon":427,"path":428,"stem":429,"children":430},"Advanced","i-lucide-graduation-cap","/docs/nuxt-protokit/advanced","0.docs/4.nuxt-protokit/6.advanced/1.index",[431,432,436,440],{"title":5,"path":428,"stem":429},{"title":433,"path":434,"stem":435},"Multi-Tool Apps","/docs/nuxt-protokit/advanced/building-a-toolkit","0.docs/4.nuxt-protokit/6.advanced/1.building-a-toolkit",{"title":437,"path":438,"stem":439},"Schema Patterns","/docs/nuxt-protokit/advanced/custom-schema-patterns","0.docs/4.nuxt-protokit/6.advanced/2.custom-schema-patterns",{"title":441,"path":442,"stem":443},"Custom Fields & Viz","/docs/nuxt-protokit/advanced/extensibility","0.docs/4.nuxt-protokit/6.advanced/3.extensibility",{"page":445,"fallbackPage":1122},{"id":446,"title":447,"body":448,"description":1120,"extension":1121,"links":1122,"meta":1123,"navigation":1124,"ogImage":1122,"path":416,"seo":1125,"stem":417,"__hash__":1126},"nuxt_protokit/0.docs/4.nuxt-protokit/5.offline-first/1.index.md","Offline-First Architecture",{"type":449,"value":450,"toc":1102},"minimark",[451,455,464,469,478,482,493,504,507,511,516,519,523,526,530,537,541,544,548,558,566,580,586,593,639,653,657,715,718,722,725,738,748,752,777,787,793,796,817,824,909,913,981,985,994,1027,1034,1069,1072,1083,1098],[452,453,447],"h1",{"id":454},"offline-first-architecture",[456,457,458,459,463],"p",{},"protokit is ",[460,461,462],"strong",{},"offline-first by design",". Every write goes to the browser's IndexedDB before any network is involved. The server is a resilient backup, not a gatekeeper.",[465,466,468],"h2",{"id":467},"the-short-answer","The short answer",[470,471,472],"blockquote",{},[456,473,474,477],{},[460,475,476],{},"Yes — tools work fully offline."," Y.js + IndexedDB means all reads and writes are local. When the device reconnects, Y.js syncs outstanding changes to the server automatically using CRDT merge — no manual action, no data loss, no conflicts.",[465,479,481],{"id":480},"how-a-write-flows","How a write flows",[483,484,489],"pre",{"className":485,"code":487,"language":488},[486],"language-text","User types in a field\n        │\n        ▼\nY.Doc (in memory)\n        │  Y.js CRDT update (binary delta)\n        ▼\nIndexedDB (browser)    ← immediate, no network needed\n        │\n        │  (when online, 30s debounce)\n        ▼\nPOST /api/yjs/sync     ← queued server push\n","text",[490,491,487],"code",{"__ignoreMap":492},"",[456,494,495,496,499,500,503],{},"Every change produces a small Y.js ",[460,497,498],{},"update"," — a compact binary delta encoding only what changed. This update is applied to the in-memory ",[490,501,502],{},"Y.Doc"," and written to IndexedDB before any Promise resolves. The network push is fire-and-forget.",[456,505,506],{},"The tool works identically on a fast connection, a slow connection, or no connection at all.",[465,508,510],{"id":509},"what-works-offline","What works offline",[512,513,515],"h3",{"id":514},"calculator-and-estimator-tools","Calculator and estimator tools",[456,517,518],{},"Ideal offline case. A user adjusts sliders and inputs on a flight, the tool recalculates, everything saves to IndexedDB. On landing, the device reconnects and silently syncs.",[512,520,522],{"id":521},"form-heavy-tools","Form-heavy tools",[456,524,525],{},"Work identically offline. Field values are Y.Map entries written to IndexedDB immediately. Multi-section forms, segmented controls, date pickers — all local-first.",[512,527,529],{"id":528},"crud-collections","CRUD collections",[456,531,532,533,536],{},"Add, edit, delete — all work offline. Items are written to ",[490,534,535],{},"Y.Array"," in IndexedDB. If two tabs both make edits while offline, their changes merge automatically via CRDT semantics when either tab reconnects.",[512,538,540],{"id":539},"structured-note-taking-and-assessments","Structured note-taking and assessments",[456,542,543],{},"Questionnaire tools, scoring rubrics, and structured data entry work without any connectivity. Changes queue for server sync silently.",[512,545,547],{"id":546},"collaborative-features","Collaborative features",[456,549,550,551,554,555,557],{},"When two users edit the same tool simultaneously (or the same user on two devices), Y.js CRDT semantics handle the merge. For ",[490,552,553],{},"Y.Map"," fields (form state), last-writer-wins per key. For ",[490,556,535],{}," collections, concurrent inserts both appear; concurrent delete + edit resolves with the delete winning.",[465,559,561,562,565],{"id":560},"the-isready-flag","The ",[490,563,564],{},"isReady"," flag",[456,567,568,569,571,572,575,576,579],{},"The only user-visible consequence of offline-first architecture is ",[490,570,564],{},". When a component first mounts, ",[490,573,574],{},"IndexeddbPersistence"," asynchronously loads stored state. Until loading completes, field values show their schema ",[490,577,578],{},"default",".",[483,581,584],{"className":582,"code":583,"language":488},[486],"mount → isReady: false → render skeleton\nIndexedDB loads (10–50 ms) → isReady: true → render stored values\n",[490,585,583],{"__ignoreMap":492},[456,587,588,589,592],{},"Always guard with ",[490,590,591],{},"v-if=\"isReady\""," to prevent a flash of default values:",[483,594,598],{"className":595,"code":596,"language":597,"meta":492,"style":492},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CClientOnly>\n  \u003CProtoTool v-if=\"isReady\" :schema=\"schema\" />\n  \u003CUSkeleton v-else class=\"h-48\" />\n\u003C/ClientOnly>\n","vue",[490,599,600,616,623,629],{"__ignoreMap":492},[601,602,605,609,613],"span",{"class":603,"line":604},"line",1,[601,606,608],{"class":607},"sMK4o","\u003C",[601,610,612],{"class":611},"swJcz","ClientOnly",[601,614,615],{"class":607},">\n",[601,617,619],{"class":603,"line":618},2,[601,620,622],{"class":621},"sTEyZ","  \u003CProtoTool v-if=\"isReady\" :schema=\"schema\" />\n",[601,624,626],{"class":603,"line":625},3,[601,627,628],{"class":621},"  \u003CUSkeleton v-else class=\"h-48\" />\n",[601,630,632,635,637],{"class":603,"line":631},4,[601,633,634],{"class":607},"\u003C/",[601,636,612],{"class":611},[601,638,615],{"class":607},[456,640,641,643,644,646,647,649,650,652],{},[490,642,407],{}," handles this internally. You only need to guard ",[490,645,564],{}," manually when using ",[490,648,388],{}," or ",[490,651,392],{}," directly.",[465,654,656],{"id":655},"yjs-crdt-merge-semantics","Y.js CRDT merge semantics",[658,659,660,673],"table",{},[661,662,663],"thead",{},[664,665,666,670],"tr",{},[667,668,669],"th",{},"Scenario",[667,671,672],{},"Outcome",[674,675,676,688,696,707],"tbody",{},[664,677,678,682],{},[679,680,681],"td",{},"Two tabs edit the same field simultaneously",[679,683,684,685,687],{},"Last-write-wins per ",[490,686,553],{}," key",[664,689,690,693],{},[679,691,692],{},"Tab A adds item while offline, Tab B adds different item",[679,694,695],{},"Both items appear after sync",[664,697,698,701],{},[679,699,700],{},"Tab A edits an item while Tab B deletes it",[679,702,703,704,706],{},"Delete wins (",[490,705,535],{}," semantics)",[664,708,709,712],{},[679,710,711],{},"Network outage for hours — client diverges from server",[679,713,714],{},"Binary diff computed on reconnect, merged without conflicts",[456,716,717],{},"No conflict resolution logic required. Y.js handles it.",[465,719,721],{"id":720},"multi-tab-sync-without-server","Multi-tab sync (without server)",[456,723,724],{},"When the same document is open in two browser tabs:",[726,727,728,735],"ol",{},[729,730,731,732],"li",{},"Tab A makes a change → written to IndexedDB → broadcast via ",[490,733,734],{},"BroadcastChannel",[729,736,737],{},"Tab B receives the broadcast → applies the Y.js update → reactive refs update immediately",[456,739,740,741,744,745,747],{},"Both tabs stay in sync without a server round-trip. This works ",[460,742,743],{},"offline"," — ",[490,746,734],{}," is purely local.",[465,749,751],{"id":750},"server-sync-details","Server sync details",[753,754,757],"callout",{"color":755,"icon":756},"amber","i-lucide-triangle-alert",[456,758,759,762,763,766,767,772,773,776],{},[490,760,761],{},"protokit"," handles the ",[460,764,765],{},"client side"," of persistence only. Server sync requires a compatible backend that implements the Y.js sync API endpoints. You can use the separate ",[460,768,769],{},[490,770,771],{},"yjs-sync"," companion module or build the endpoints yourself. Without a backend, set ",[490,774,775],{},"serverSync: false"," — no HTTP calls will be made and all data persists locally via IndexedDB.",[456,778,779,780,783,784,786],{},"When ",[490,781,782],{},"protokit.serverSync"," is enabled and a compatible backend is available, ",[490,785,392],{}," pushes updates every 30 seconds (debounced on document changes):",[483,788,791],{"className":789,"code":790,"language":488},[486],"POST /api/yjs/sync               Stores binary Y.js updates in D1/SQLite\nPOST /api/yjs/snapshots/create   Creates a deduplicated JSON snapshot\n",[490,792,790],{"__ignoreMap":492},[456,794,795],{},"Server sync provides:",[797,798,799,805,811],"ul",{},[729,800,801,804],{},[460,802,803],{},"Cross-device continuity"," — open on laptop, continue on tablet",[729,806,807,810],{},[460,808,809],{},"Backup against browser storage loss"," — incognito mode, cache clear, browser reinstall",[729,812,813,816],{},[460,814,815],{},"Corruption recovery"," — server snapshots enable restore (see next page)",[456,818,819,820,823],{},"Configure in ",[490,821,822],{},"nuxt.config.ts",":",[483,825,829],{"className":826,"code":827,"language":828,"meta":492,"style":492},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","protokit: {\n  serverSync: true,                    // enabled (default), baseUrl: '/api/yjs'\n  serverSync: false,                   // disabled — no HTTP calls at all\n  serverSync: { baseUrl: '/api/yjs' }, // custom server implementation\n}\n","typescript",[490,830,831,841,859,873,903],{"__ignoreMap":492},[601,832,833,836,838],{"class":603,"line":604},[601,834,761],{"class":835},"sBMFI",[601,837,823],{"class":607},[601,839,840],{"class":607}," {\n",[601,842,843,846,848,852,855],{"class":603,"line":618},[601,844,845],{"class":835},"  serverSync",[601,847,823],{"class":607},[601,849,851],{"class":850},"sfNiH"," true",[601,853,854],{"class":607},",",[601,856,858],{"class":857},"sHwdD","                    // enabled (default), baseUrl: '/api/yjs'\n",[601,860,861,863,865,868,870],{"class":603,"line":625},[601,862,845],{"class":835},[601,864,823],{"class":607},[601,866,867],{"class":850}," false",[601,869,854],{"class":607},[601,871,872],{"class":857},"                   // disabled — no HTTP calls at all\n",[601,874,875,877,879,882,885,887,890,894,897,900],{"class":603,"line":631},[601,876,845],{"class":835},[601,878,823],{"class":607},[601,880,881],{"class":607}," {",[601,883,884],{"class":835}," baseUrl",[601,886,823],{"class":607},[601,888,889],{"class":607}," '",[601,891,893],{"class":892},"sfazB","/api/yjs",[601,895,896],{"class":607},"'",[601,898,899],{"class":607}," },",[601,901,902],{"class":857}," // custom server implementation\n",[601,904,906],{"class":603,"line":905},5,[601,907,908],{"class":607},"}\n",[465,910,912],{"id":911},"what-survives-what","What survives what",[658,914,915,924],{},[661,916,917],{},[664,918,919,922],{},[667,920,921],{},"Event",[667,923,672],{},[674,925,926,934,941,949,957,965,973],{},[664,927,928,931],{},[679,929,930],{},"Page refresh",[679,932,933],{},"✅ All data preserved (IndexedDB)",[664,935,936,939],{},[679,937,938],{},"Tab close and reopen",[679,940,933],{},[664,942,943,946],{},[679,944,945],{},"Browser crash",[679,947,948],{},"✅ All data preserved (IndexedDB writes are synchronous)",[664,950,951,954],{},[679,952,953],{},"Loss of internet for hours",[679,955,956],{},"✅ Changes queue locally, sync on reconnect",[664,958,959,962],{},[679,960,961],{},"Browser storage cleared by user",[679,963,964],{},"⚠️ Local data lost — server copy preserved if sync ran",[664,966,967,970],{},[679,968,969],{},"Private/incognito window",[679,971,972],{},"⚠️ IndexedDB cleared on close — server copy preserved if synced",[664,974,975,978],{},[679,976,977],{},"IndexedDB corruption",[679,979,980],{},"⚠️ Auto-detected — recovery modal guides the user",[465,982,984],{"id":983},"using-without-the-server-module","Using without the server module",[456,986,987,988,990,991,993],{},"Set ",[490,989,775],{}," in ",[490,992,822],{}," to run in local-only mode. No HTTP calls are made — not even silent ones:",[483,995,997],{"className":826,"code":996,"language":828,"meta":492,"style":492},"// nuxt.config.ts\nprotokit: {\n  serverSync: false,\n}\n",[490,998,999,1004,1012,1023],{"__ignoreMap":492},[601,1000,1001],{"class":603,"line":604},[601,1002,1003],{"class":857},"// nuxt.config.ts\n",[601,1005,1006,1008,1010],{"class":603,"line":618},[601,1007,761],{"class":835},[601,1009,823],{"class":607},[601,1011,840],{"class":607},[601,1013,1014,1016,1018,1020],{"class":603,"line":625},[601,1015,845],{"class":835},[601,1017,823],{"class":607},[601,1019,867],{"class":850},[601,1021,1022],{"class":607},",\n",[601,1024,1025],{"class":603,"line":631},[601,1026,908],{"class":607},[456,1028,1029,1030,1033],{},"Or disable sync for a single tool with the ",[490,1031,1032],{},"disable-sync"," prop:",[483,1035,1037],{"className":595,"code":1036,"language":597,"meta":492,"style":492},"\u003CProtoTool :schema=\"mySchema\" disable-sync />\n",[490,1038,1039],{"__ignoreMap":492},[601,1040,1041,1043,1045,1048,1052,1055,1058,1061,1063,1066],{"class":603,"line":604},[601,1042,608],{"class":607},[601,1044,407],{"class":611},[601,1046,1047],{"class":607}," :",[601,1049,1051],{"class":1050},"spNyl","schema",[601,1053,1054],{"class":607},"=",[601,1056,1057],{"class":607},"\"",[601,1059,1060],{"class":621},"mySchema",[601,1062,1057],{"class":607},[601,1064,1065],{"class":1050}," disable-sync",[601,1067,1068],{"class":607}," />\n",[456,1070,1071],{},"The only consequences of disabling server sync:",[797,1073,1074,1077,1080],{},[729,1075,1076],{},"No cross-device sync",[729,1078,1079],{},"No corruption recovery option (no server snapshots to restore from)",[729,1081,1082],{},"The corruption modal shows only \"Start fresh\" if IndexedDB is damaged",[753,1084,1085],{"color":755,"icon":756},[456,1086,1087,1088,1090,1091,1094,1095,1097],{},"If you omit ",[490,1089,771],{}," but leave ",[490,1092,1093],{},"serverSync: true"," (the default), sync calls will 404 silently. This is harmless — local data is safe — but produces unnecessary network requests. Prefer ",[490,1096,775],{}," when you know you have no server.",[1099,1100,1101],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":492,"searchDepth":618,"depth":618,"links":1103},[1104,1105,1106,1113,1115,1116,1117,1118,1119],{"id":467,"depth":618,"text":468},{"id":480,"depth":618,"text":481},{"id":509,"depth":618,"text":510,"children":1107},[1108,1109,1110,1111,1112],{"id":514,"depth":625,"text":515},{"id":521,"depth":625,"text":522},{"id":528,"depth":625,"text":529},{"id":539,"depth":625,"text":540},{"id":546,"depth":625,"text":547},{"id":560,"depth":618,"text":1114},"The isReady flag",{"id":655,"depth":618,"text":656},{"id":720,"depth":618,"text":721},{"id":750,"depth":618,"text":751},{"id":911,"depth":618,"text":912},{"id":983,"depth":618,"text":984},"How protokit works completely offline using Y.js CRDTs and IndexedDB, with automatic conflict-free sync when connectivity is restored.","md",null,{},{"title":151},{"title":447,"description":1120},"vuve4FIn6CVlXaPChnaU2YToMLAasrZ8UKr8nUpctgk",[1128,1130],{"title":411,"path":412,"stem":413,"description":1129,"children":-1},"Modal editor for a single collection item with automatic draft persistence — unsaved changes survive tab switches and browser refreshes.",{"title":421,"path":422,"stem":423,"description":1131,"children":-1},"How protokit detects IndexedDB corruption, queries server snapshots, and guides users through recovery without data loss.",1772977476168]