[{"data":1,"prerenderedAt":1173},["ShallowReactive",2],{"navigation-landing-en":3,"navigation-nuxt-auto-en":180,"navigation-nuxt-protokit-en":338,"/docs/nuxt-protokit/offline-first/corruption-recovery-en":444,"/docs/nuxt-protokit/offline-first/corruption-recovery-surround-en":1168},[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":1163},{"id":446,"title":421,"body":447,"description":1161,"extension":1162,"links":1163,"meta":1164,"navigation":1165,"ogImage":1163,"path":422,"seo":1166,"stem":423,"__hash__":1167},"nuxt_protokit/0.docs/4.nuxt-protokit/5.offline-first/2.corruption-recovery.md",{"type":448,"value":449,"toc":1151},"minimark",[450,454,463,468,482,488,617,621,629,635,638,650,702,705,725,728,734,743,845,848,973,982,986,993,1033,1036,1039,1052,1057,1060,1087,1090,1107,1111,1114,1126,1129,1135,1139,1147],[451,452,421],"h1",{"id":453},"corruption-recovery",[455,456,457,458,462],"p",{},"IndexedDB corruption is rare but real. Browser crashes mid-write, certain browser extensions, and disk-level errors can leave a ",[459,460,461],"code",{},"y-indexeddb"," store in an inconsistent state. protokit detects this automatically and provides a guided recovery flow.",[464,465,467],"h2",{"id":466},"the-error-signature","The error signature",[455,469,470,471,473,474,477,478,481],{},"The canonical sign of ",[459,472,461],{}," corruption is an unhandled promise rejection with the message ",[459,475,476],{},"\"Unexpected case\"",". It surfaces when ",[459,479,480],{},"IndexeddbPersistence"," tries to load stored updates but finds an unexpected internal structure.",[455,483,484,485,487],{},"This error cannot be caught at the call site — it appears as an unhandled rejection. ",[459,486,392],{}," intercepts it via:",[489,490,495],"pre",{"className":491,"code":492,"language":493,"meta":494,"style":494},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","window.addEventListener('unhandledrejection', (event) => {\n  if (event.reason?.message?.includes('Unexpected case')) {\n    handleCorruption(docKey)\n  }\n})\n","typescript","",[459,496,497,546,589,603,609],{"__ignoreMap":494},[498,499,502,506,510,514,517,520,524,526,529,532,536,539,543],"span",{"class":500,"line":501},"line",1,[498,503,505],{"class":504},"sTEyZ","window",[498,507,509],{"class":508},"sMK4o",".",[498,511,513],{"class":512},"s2Zo4","addEventListener",[498,515,516],{"class":504},"(",[498,518,519],{"class":508},"'",[498,521,523],{"class":522},"sfazB","unhandledrejection",[498,525,519],{"class":508},[498,527,528],{"class":508},",",[498,530,531],{"class":508}," (",[498,533,535],{"class":534},"sHdIc","event",[498,537,538],{"class":508},")",[498,540,542],{"class":541},"spNyl"," =>",[498,544,545],{"class":508}," {\n",[498,547,549,553,556,558,560,563,566,569,571,574,576,578,581,583,586],{"class":500,"line":548},2,[498,550,552],{"class":551},"s7zQu","  if",[498,554,531],{"class":555},"swJcz",[498,557,535],{"class":504},[498,559,509],{"class":508},[498,561,562],{"class":504},"reason",[498,564,565],{"class":508},"?.",[498,567,568],{"class":504},"message",[498,570,565],{"class":508},[498,572,573],{"class":512},"includes",[498,575,516],{"class":555},[498,577,519],{"class":508},[498,579,580],{"class":522},"Unexpected case",[498,582,519],{"class":508},[498,584,585],{"class":555},")) ",[498,587,588],{"class":508},"{\n",[498,590,592,595,597,600],{"class":500,"line":591},3,[498,593,594],{"class":512},"    handleCorruption",[498,596,516],{"class":555},[498,598,599],{"class":504},"docKey",[498,601,602],{"class":555},")\n",[498,604,606],{"class":500,"line":605},4,[498,607,608],{"class":508},"  }\n",[498,610,612,615],{"class":500,"line":611},5,[498,613,614],{"class":508},"}",[498,616,602],{"class":504},[464,618,620],{"id":619},"recovery-flow","Recovery flow",[489,622,627],{"className":623,"code":625,"language":626},[624],"language-text","IndexedDB corruption detected\n        │\n        ▼\nQuery GET /api/yjs/snapshots/{docKey}\n        │\n        ├── Snapshot found ─────────────────────────────►\n        │                                                │\n        │                                   ProtoCorruptionModal:\n        │                                   \"Restore from server backup\"\n        │                                   (shows snapshot age)\n        │                                                │\n        │              ┌──────────────────────────────────┘\n        │              │\n        │      User clicks \"Restore\"\n        │              │\n        │              ▼\n        │    POST /api/yjs/snapshots/restore\n        │    GET  /api/yjs/pull  (all updates)\n        │    Apply to fresh in-memory Y.Doc\n        │    Delete corrupted IndexedDB store\n        │    Create fresh IndexedDB from in-memory doc\n        │\n        └── No snapshot ──────────────────────────────►\n                                                       │\n                                          ProtoCorruptionModal:\n                                          \"Start fresh\"\n                                                       │\n                                          User clicks \"Start Fresh\"\n                                                       │\n                                          Delete corrupted IndexedDB\n                                          Create empty IndexedDB\n","text",[459,628,625],{"__ignoreMap":494},[464,630,632],{"id":631},"protocorruptionmodal",[459,633,634],{},"ProtoCorruptionModal",[455,636,637],{},"A non-dismissible modal that appears over the full page when corruption is detected. The user must choose an action before continuing.",[455,639,640,641,645,646,649],{},"Register it ",[642,643,644],"strong",{},"once"," in ",[459,647,648],{},"app.vue",":",[489,651,655],{"className":652,"code":653,"language":654,"meta":494,"style":494},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003C!-- app.vue -->\n\u003Ctemplate>\n  \u003CNuxtPage />\n  \u003CProtoCorruptionModal />\n\u003C/template>\n","vue",[459,656,657,663,674,685,693],{"__ignoreMap":494},[498,658,659],{"class":500,"line":501},[498,660,662],{"class":661},"sHwdD","\u003C!-- app.vue -->\n",[498,664,665,668,671],{"class":500,"line":548},[498,666,667],{"class":508},"\u003C",[498,669,670],{"class":555},"template",[498,672,673],{"class":508},">\n",[498,675,676,679,682],{"class":500,"line":591},[498,677,678],{"class":508},"  \u003C",[498,680,681],{"class":555},"NuxtPage",[498,683,684],{"class":508}," />\n",[498,686,687,689,691],{"class":500,"line":605},[498,688,678],{"class":508},[498,690,634],{"class":555},[498,692,684],{"class":508},[498,694,695,698,700],{"class":500,"line":611},[498,696,697],{"class":508},"\u003C/",[498,699,670],{"class":555},[498,701,673],{"class":508},[455,703,704],{},"The modal displays:",[706,707,708,712,715],"ul",{},[709,710,711],"li",{},"Which document is affected (tool name or doc key)",[709,713,714],{},"Whether a server snapshot exists and how old it is",[709,716,717,718,721,722],{},"Two options: ",[642,719,720],{},"Restore from server backup"," or ",[642,723,724],{},"Start fresh",[455,726,727],{},"If no server snapshot exists, only \"Start fresh\" is shown.",[464,729,731],{"id":730},"useprotocorruption",[459,732,733],{},"useProtoCorruption",[455,735,736,737,739,740,742],{},"The corruption system uses a module-level singleton queue to decouple detection (in ",[459,738,392],{},") from display (in ",[459,741,634],{},"):",[489,744,746],{"className":491,"code":745,"language":493,"meta":494,"style":494},"interface CorruptionEvent {\n  id:                  string        // UUID for this event\n  docKey:              string        // which document is affected\n  latestSnapshotId:    string | null // null if no server snapshot exists\n  latestSnapshotAge:   number | null // seconds since last snapshot\n  latestSnapshotLabel: string | null // human-readable label\n}\n",[459,747,748,759,772,785,804,821,839],{"__ignoreMap":494},[498,749,750,753,757],{"class":500,"line":501},[498,751,752],{"class":541},"interface",[498,754,756],{"class":755},"sBMFI"," CorruptionEvent",[498,758,545],{"class":508},[498,760,761,764,766,769],{"class":500,"line":548},[498,762,763],{"class":555},"  id",[498,765,649],{"class":508},[498,767,768],{"class":755},"                  string",[498,770,771],{"class":661},"        // UUID for this event\n",[498,773,774,777,779,782],{"class":500,"line":591},[498,775,776],{"class":555},"  docKey",[498,778,649],{"class":508},[498,780,781],{"class":755},"              string",[498,783,784],{"class":661},"        // which document is affected\n",[498,786,787,790,792,795,798,801],{"class":500,"line":605},[498,788,789],{"class":555},"  latestSnapshotId",[498,791,649],{"class":508},[498,793,794],{"class":755},"    string",[498,796,797],{"class":508}," |",[498,799,800],{"class":755}," null",[498,802,803],{"class":661}," // null if no server snapshot exists\n",[498,805,806,809,811,814,816,818],{"class":500,"line":611},[498,807,808],{"class":555},"  latestSnapshotAge",[498,810,649],{"class":508},[498,812,813],{"class":755},"   number",[498,815,797],{"class":508},[498,817,800],{"class":755},[498,819,820],{"class":661}," // seconds since last snapshot\n",[498,822,824,827,829,832,834,836],{"class":500,"line":823},6,[498,825,826],{"class":555},"  latestSnapshotLabel",[498,828,649],{"class":508},[498,830,831],{"class":755}," string",[498,833,797],{"class":508},[498,835,800],{"class":755},[498,837,838],{"class":661}," // human-readable label\n",[498,840,842],{"class":500,"line":841},7,[498,843,844],{"class":508},"}\n",[455,846,847],{},"Internal flow:",[489,849,851],{"className":491,"code":850,"language":493,"meta":494,"style":494},"// In useProtoDoc — called on \"Unexpected case\" detection:\nconst eventId = await reportCorruption({\n  docKey,\n  latestSnapshotId:  snapshotData?.id ?? null,\n  latestSnapshotAge: snapshotData?.ageSeconds ?? null,\n})\n// reportCorruption returns a Promise that resolves when the user makes a choice\n\n// ProtoCorruptionModal calls internally:\nresolveCorruption(eventId, 'restore')  // or 'fresh'\n",[459,852,853,858,879,886,906,924,930,935,942,948],{"__ignoreMap":494},[498,854,855],{"class":500,"line":501},[498,856,857],{"class":661},"// In useProtoDoc — called on \"Unexpected case\" detection:\n",[498,859,860,863,866,869,872,875,877],{"class":500,"line":548},[498,861,862],{"class":541},"const",[498,864,865],{"class":504}," eventId ",[498,867,868],{"class":508},"=",[498,870,871],{"class":551}," await",[498,873,874],{"class":512}," reportCorruption",[498,876,516],{"class":504},[498,878,588],{"class":508},[498,880,881,883],{"class":500,"line":591},[498,882,776],{"class":504},[498,884,885],{"class":508},",\n",[498,887,888,890,892,895,897,900,903],{"class":500,"line":605},[498,889,789],{"class":555},[498,891,649],{"class":508},[498,893,894],{"class":504},"  snapshotData",[498,896,565],{"class":508},[498,898,899],{"class":504},"id ",[498,901,902],{"class":508},"??",[498,904,905],{"class":508}," null,\n",[498,907,908,910,912,915,917,920,922],{"class":500,"line":611},[498,909,808],{"class":555},[498,911,649],{"class":508},[498,913,914],{"class":504}," snapshotData",[498,916,565],{"class":508},[498,918,919],{"class":504},"ageSeconds ",[498,921,902],{"class":508},[498,923,905],{"class":508},[498,925,926,928],{"class":500,"line":823},[498,927,614],{"class":508},[498,929,602],{"class":504},[498,931,932],{"class":500,"line":841},[498,933,934],{"class":661},"// reportCorruption returns a Promise that resolves when the user makes a choice\n",[498,936,938],{"class":500,"line":937},8,[498,939,941],{"emptyLinePlaceholder":940},true,"\n",[498,943,945],{"class":500,"line":944},9,[498,946,947],{"class":661},"// ProtoCorruptionModal calls internally:\n",[498,949,951,954,957,959,962,965,967,970],{"class":500,"line":950},10,[498,952,953],{"class":512},"resolveCorruption",[498,955,956],{"class":504},"(eventId",[498,958,528],{"class":508},[498,960,961],{"class":508}," '",[498,963,964],{"class":522},"restore",[498,966,519],{"class":508},[498,968,969],{"class":504},")  ",[498,971,972],{"class":661},"// or 'fresh'\n",[455,974,975,976,978,979,981],{},"You should not need to call these functions directly — ",[459,977,392],{}," and ",[459,980,634],{}," handle the complete flow.",[464,983,985],{"id":984},"server-snapshot-storage","Server snapshot storage",[455,987,988,989,992],{},"The ",[459,990,991],{},"yjs-sync"," module stores two representations for each snapshot:",[994,995,996,1009],"table",{},[997,998,999],"thead",{},[1000,1001,1002,1006],"tr",{},[1003,1004,1005],"th",{},"Format",[1003,1007,1008],{},"Purpose",[1010,1011,1012,1021],"tbody",{},[1000,1013,1014,1018],{},[1015,1016,1017],"td",{},"Binary Y.js updates",[1015,1019,1020],{},"CRDT-correct restore — exact document state reconstruction",[1000,1022,1023,1026],{},[1015,1024,1025],{},"JSON",[1015,1027,1028,1029,1032],{},"Human-readable, shown in ",[459,1030,1031],{},"ProtoDebugPanel",", used for deduplication",[455,1034,1035],{},"Snapshots are deduplicated server-side — if the document content has not changed since the last snapshot, a new snapshot is not created.",[455,1037,1038],{},"Snapshots are created:",[706,1040,1041,1046],{},[709,1042,1043,1044,538],{},"After every successful server sync (30-second debounce in ",[459,1045,392],{},[709,1047,1048,1049,1051],{},"Manually from ",[459,1050,1031],{}," during development",[464,1053,1055],{"id":1054},"protodebugpanel",[459,1056,1031],{},[455,1058,1059],{},"Enable in development to inspect sync state and test recovery:",[489,1061,1063],{"className":652,"code":1062,"language":654,"meta":494,"style":494},"\u003CClientOnly>\n  \u003CProtoTool :schema=\"mySchema\" :show-debug=\"true\" />\n\u003C/ClientOnly>\n",[459,1064,1065,1074,1079],{"__ignoreMap":494},[498,1066,1067,1069,1072],{"class":500,"line":501},[498,1068,667],{"class":508},[498,1070,1071],{"class":555},"ClientOnly",[498,1073,673],{"class":508},[498,1075,1076],{"class":500,"line":548},[498,1077,1078],{"class":504},"  \u003CProtoTool :schema=\"mySchema\" :show-debug=\"true\" />\n",[498,1080,1081,1083,1085],{"class":500,"line":591},[498,1082,697],{"class":508},[498,1084,1071],{"class":555},[498,1086,673],{"class":508},[455,1088,1089],{},"The panel (triggered by a fixed tab on the viewport edge) lets you:",[706,1091,1092,1095,1098,1101,1104],{},[709,1093,1094],{},"View all active docs and their sync status",[709,1096,1097],{},"Copy the full doc state as JSON",[709,1099,1100],{},"Create a manual server snapshot",[709,1102,1103],{},"Inject garbage into the Y.Doc to simulate corruption",[709,1105,1106],{},"Delete the local IndexedDB store to test cold restore from server",[464,1108,1110],{"id":1109},"when-recovery-cannot-restore-data","When recovery cannot restore data",[455,1112,1113],{},"Recovery from a server backup requires both:",[1115,1116,1117,1123],"ol",{},[709,1118,1119,1120,1122],{},"A compatible sync backend is available and reachable (the ",[459,1121,991],{}," companion module, or a custom implementation)",[709,1124,1125],{},"At least one server sync has completed for that document",[455,1127,1128],{},"If neither condition is met — for example, the tool was used entirely offline from first open — the modal shows only \"Start fresh\". The local data from that session is unrecoverable.",[455,1130,1131,1134],{},[642,1132,1133],{},"Mitigation:"," Encourage users to work online at least once after creating significant data. The 30-second debounce means that after 30 seconds of connectivity, a snapshot almost certainly exists.",[464,1136,1138],{"id":1137},"false-positives","False positives",[455,1140,988,1141,1143,1144,1146],{},[459,1142,476],{}," rejection can occasionally fire spuriously during very fast component mount/unmount cycles. ",[459,1145,392],{}," applies a short debounce and verifies the doc is genuinely unreadable before showing the modal to the user.",[1148,1149,1150],"style",{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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 .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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":494,"searchDepth":548,"depth":548,"links":1152},[1153,1154,1155,1156,1157,1158,1159,1160],{"id":466,"depth":548,"text":467},{"id":619,"depth":548,"text":620},{"id":631,"depth":548,"text":634},{"id":730,"depth":548,"text":733},{"id":984,"depth":548,"text":985},{"id":1054,"depth":548,"text":1031},{"id":1109,"depth":548,"text":1110},{"id":1137,"depth":548,"text":1138},"How protokit detects IndexedDB corruption, queries server snapshots, and guides users through recovery without data loss.","md",null,{},{"title":421},{"title":421,"description":1161},"4WYPaEWaZDonfj1QpheCA5_cAvXmO-NUG2Wp26lePLM",[1169,1171],{"title":151,"path":416,"stem":417,"description":1170,"children":-1},"How protokit works completely offline using Y.js CRDTs and IndexedDB, with automatic conflict-free sync when connectivity is restored.",{"title":5,"path":428,"stem":429,"description":1172,"children":-1},"Advanced patterns for Nuxt Protokit — custom schemas, building reusable toolkits, and extensibility.",1772977477613]