[{"data":1,"prerenderedAt":4582},["ShallowReactive",2],{"navigation_docs_fr":3,"-fr-recipes-persistence":342,"-fr-recipes-persistence-surround":4577},[4,45,91,137,195,236,257,291,327],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Prise en main","i-lucide-rocket","/fr/getting-started","fr/01.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Qu'est-ce que Qora ?","/fr/getting-started/what-is-qora","fr/01.getting-started/2.what-is-qora","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","/fr/getting-started/installation","fr/01.getting-started/3.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":6},"Démarrage rapide","/fr/getting-started/quick-start","fr/01.getting-started/4.quick-start",{"title":25,"path":26,"stem":27,"icon":28},"Comparaison","/fr/getting-started/comparison","fr/01.getting-started/5.comparison","i-lucide-scale",{"title":30,"path":31,"stem":32,"icon":33},"Venant de TanStack Query","/fr/getting-started/coming-from-tanstack-query","fr/01.getting-started/6.coming-from-tanstack-query","i-lucide-arrow-left-right",{"title":35,"path":36,"stem":37,"icon":38},"Migrer depuis d'Autres Librairies","/fr/getting-started/migrating-from-other-libraries","fr/01.getting-started/7.migrating-from-other-libraries","i-lucide-arrow-right-left",{"title":40,"path":41,"stem":42,"icon":43},"Assistants IA","/fr/getting-started/ai-assistants","fr/01.getting-started/8.ai-assistants","i-lucide-sparkles",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Concepts clés","i-lucide-book-open","/fr/core-concepts","fr/02.core-concepts",[51,56,61,66,71,76,81,86],{"title":52,"path":53,"stem":54,"icon":55},"Requêtes","/fr/core-concepts/queries","fr/02.core-concepts/1.queries","i-lucide-database",{"title":57,"path":58,"stem":59,"icon":60},"Clés de requête","/fr/core-concepts/query-keys","fr/02.core-concepts/2.query-keys","i-lucide-key-round",{"title":62,"path":63,"stem":64,"icon":65},"États des requêtes","/fr/core-concepts/query-states","fr/02.core-concepts/3.query-states","i-lucide-activity",{"title":67,"path":68,"stem":69,"icon":70},"Cache","/fr/core-concepts/caching","fr/02.core-concepts/4.caching","i-lucide-layers-3",{"title":72,"path":73,"stem":74,"icon":75},"Stale-While-Revalidate","/fr/core-concepts/stale-while-revalidate","fr/02.core-concepts/5.stale-while-revalidate","i-lucide-timer-reset",{"title":77,"path":78,"stem":79,"icon":80},"Déduplication","/fr/core-concepts/deduplication","fr/02.core-concepts/6.deduplication","i-lucide-copy-minus",{"title":82,"path":83,"stem":84,"icon":85},"Mutations et invalidation","/fr/core-concepts/mutations","fr/02.core-concepts/7.mutations","i-lucide-pencil-line",{"title":87,"path":88,"stem":89,"icon":90},"Hors-ligne et réseau","/fr/core-concepts/offline","fr/02.core-concepts/8.offline","i-lucide-wifi-off",{"title":92,"icon":93,"path":94,"stem":95,"children":96,"page":44},"Usage avec Flutter","i-lucide-smartphone","/fr/flutter-integration","fr/03.flutter-integration",[97,102,107,112,117,122,127,132],{"title":98,"path":99,"stem":100,"icon":101},"Configuration","/fr/flutter-integration/setup","fr/03.flutter-integration/1.setup","i-lucide-wrench",{"title":103,"path":104,"stem":105,"icon":106},"QoraScope","/fr/flutter-integration/qora-scope","fr/03.flutter-integration/2.qora-scope","i-lucide-share-2",{"title":108,"path":109,"stem":110,"icon":111},"QoraBuilder","/fr/flutter-integration/qora-builder","fr/03.flutter-integration/3.qora-builder","i-lucide-blocks",{"title":113,"path":114,"stem":115,"icon":116},"QoraMutationBuilder","/fr/flutter-integration/qora-mutation-builder","fr/03.flutter-integration/4.qora-mutation-builder","i-lucide-pencil",{"title":118,"path":119,"stem":120,"icon":121},"InfiniteQueryBuilder","/fr/flutter-integration/infinite-query-builder","fr/03.flutter-integration/5.infinite-query-builder","i-lucide-infinity",{"title":123,"path":124,"stem":125,"icon":126},"Hooks (flutter_hooks)","/fr/flutter-integration/hooks","fr/03.flutter-integration/6.hooks","i-lucide-anchor",{"title":128,"path":129,"stem":130,"icon":131},"Widgets de statut réseau","/fr/flutter-integration/network-status","fr/03.flutter-integration/7.network-status","i-lucide-wifi",{"title":133,"path":134,"stem":135,"icon":136},"Bonnes pratiques","/fr/flutter-integration/best-practices","fr/03.flutter-integration/8.best-practices","i-lucide-badge-check",{"title":138,"icon":139,"path":140,"stem":141,"children":142,"page":44},"Guides Pratiques","i-lucide-chef-hat","/fr/recipes","fr/04.recipes",[143,147,152,157,162,166,171,176,181,186,191],{"title":144,"path":145,"stem":146,"icon":85},"Mutations","/fr/recipes/mutations","fr/04.recipes/02.mutations",{"title":148,"path":149,"stem":150,"icon":151},"Mises à jour optimistes","/fr/recipes/optimistic-updates","fr/04.recipes/03.optimistic-updates","i-lucide-zap",{"title":153,"path":154,"stem":155,"icon":156},"Persistance","/fr/recipes/persistence","fr/04.recipes/04.persistence","i-lucide-hard-drive",{"title":158,"path":159,"stem":160,"icon":161},"Réseau adaptatif","/fr/recipes/network-aware","fr/04.recipes/05.network-aware","i-lucide-signal",{"title":163,"path":164,"stem":165,"icon":121},"Requêtes infinies","/fr/recipes/infinite-queries","fr/04.recipes/06.infinite-queries",{"title":167,"path":168,"stem":169,"icon":170},"Requêtes dépendantes","/fr/recipes/dependent-queries","fr/04.recipes/07.dependent-queries","i-lucide-link-2",{"title":172,"path":173,"stem":174,"icon":175},"Annulation","/fr/recipes/cancel-token","fr/04.recipes/08.cancel-token","i-lucide-circle-slash",{"title":177,"path":178,"stem":179,"icon":180},"Hydratation SSR","/fr/recipes/ssr-hydration","fr/04.recipes/09.ssr-hydration","i-lucide-server",{"title":182,"path":183,"stem":184,"icon":185},"Gestion des erreurs","/fr/recipes/error-handling","fr/04.recipes/10.error-handling","i-lucide-shield-alert",{"title":187,"path":188,"stem":189,"icon":190},"Tests","/fr/recipes/testing","fr/04.recipes/11.testing","i-lucide-flask-conical",{"title":192,"path":193,"stem":194},"Prefetch au Niveau des Routes","/fr/recipes/prefetch","fr/04.recipes/12.prefetch",{"title":196,"icon":197,"path":198,"stem":199,"children":200,"page":44},"Référence de l'API","i-lucide-file-code","/fr/api-reference","fr/05.api-reference",[201,206,210,214,219,224,228,232],{"title":202,"path":203,"stem":204,"icon":205},"QoraClient","/fr/api-reference/qora-client","fr/05.api-reference/1.qora-client","i-lucide-cpu",{"title":207,"path":208,"stem":209,"icon":60},"QoraKey","/fr/api-reference/qora-key","fr/05.api-reference/2.qora-key",{"title":211,"path":212,"stem":213,"icon":65},"QoraState","/fr/api-reference/qora-state","fr/05.api-reference/3.qora-state",{"title":215,"path":216,"stem":217,"icon":218},"QoraOptions","/fr/api-reference/qora-options","fr/05.api-reference/4.qora-options","i-lucide-sliders-horizontal",{"title":220,"path":221,"stem":222,"icon":223},"QoraClientConfig","/fr/api-reference/qora-client-config","fr/05.api-reference/5.qora-client-config","i-lucide-settings-2",{"title":225,"path":226,"stem":227,"icon":85},"API Mutation","/fr/api-reference/qora-mutation","fr/05.api-reference/6.qora-mutation",{"title":229,"path":230,"stem":231,"icon":156},"PersistQoraClient","/fr/api-reference/persist-qora-client","fr/05.api-reference/7.persist-qora-client",{"title":233,"path":234,"stem":235,"icon":121},"Requête infinie","/fr/api-reference/infinite-query","fr/05.api-reference/8.infinite-query",{"title":237,"icon":238,"path":239,"stem":240,"children":241,"page":44},"Outils de debug","i-lucide-bug","/fr/devtools","fr/06.devtools",[242,247,252],{"title":243,"path":244,"stem":245,"icon":246},"Vue d'ensemble","/fr/devtools/overview","fr/06.devtools/1.overview","i-lucide-layout-dashboard",{"title":248,"path":249,"stem":250,"icon":251},"Overlay intégré","/fr/devtools/overlay","fr/06.devtools/2.overlay","i-lucide-layers",{"title":253,"path":254,"stem":255,"icon":256},"Sérialisation des données","/fr/devtools/data-display","fr/06.devtools/4.data-display","i-lucide-eye",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":44},"Intégrations tierces","i-lucide-puzzle","/fr/third-party","fr/07.third-party",[263,268,272,277,282,286],{"title":264,"path":265,"stem":266,"icon":267},"Avec Provider","/fr/third-party/with-provider","fr/07.third-party/1.with-provider","i-lucide-plug",{"title":269,"path":270,"stem":271,"icon":111},"Avec BLoC","/fr/third-party/with-bloc","fr/07.third-party/2.with-bloc",{"title":273,"path":274,"stem":275,"icon":276},"Avec Riverpod","/fr/third-party/with-riverpod","fr/07.third-party/3.with-riverpod","i-lucide-droplets",{"title":278,"path":279,"stem":280,"icon":281},"Avec RxDart","/fr/third-party/with-rxdart","fr/07.third-party/4.with-rxdart","i-lucide-git-branch",{"title":283,"path":284,"stem":285},"Avec Dio","/fr/third-party/with-dio","fr/07.third-party/5.with-dio",{"title":287,"path":288,"stem":289,"icon":290},"Avec GraphQL","/fr/third-party/with-graphql","fr/07.third-party/6.with-graphql","i-lucide-binary",{"title":292,"icon":190,"path":293,"stem":294,"children":295,"page":44},"Exemples","/fr/examples","fr/08.examples",[296,301,305,310,314,318,322],{"title":297,"path":298,"stem":299,"icon":300},"Étude de cas : Liste et détail utilisateur","/fr/examples/basic-query","fr/08.examples/1.basic-query","i-lucide-play-circle",{"title":302,"path":303,"stem":304,"icon":151},"Étude de cas : Mutations optimistes et rollback","/fr/examples/mutations-optimistic","fr/08.examples/2.mutations-optimistic",{"title":306,"path":307,"stem":308,"icon":309},"Étude de cas : Feed à défilement infini","/fr/examples/infinite-scroll","fr/08.examples/3.infinite-scroll","i-lucide-scroll",{"title":311,"path":312,"stem":313,"icon":90},"Étude de cas : Application Todo offline-first","/fr/examples/offline-first","fr/08.examples/4.offline-first",{"title":315,"path":316,"stem":317,"icon":161},"Étude de cas : Conscience réseau","/fr/examples/network-awareness","fr/08.examples/5.network-awareness",{"title":319,"path":320,"stem":321,"icon":126},"Étude de cas : Intégration des hooks","/fr/examples/hooks-integration","fr/08.examples/6.hooks-integration",{"title":323,"path":324,"stem":325,"icon":326},"Étude de cas : Application Todo complète","/fr/examples/complete-todo-app","fr/08.examples/7.complete-todo-app","i-lucide-check-square",{"title":328,"icon":329,"path":330,"stem":331,"children":332,"page":44},"Aide","i-lucide-life-buoy","/fr/help","fr/09.help",[333,338],{"title":334,"path":335,"stem":336,"icon":337},"FAQ","/fr/help/faq","fr/09.help/1.faq","i-lucide-circle-help",{"title":339,"path":340,"stem":341,"icon":101},"Dépannage","/fr/help/troubleshooting","fr/09.help/2.troubleshooting",{"id":343,"title":153,"body":344,"description":4570,"extension":4571,"links":4572,"meta":4573,"navigation":4574,"path":154,"seo":4575,"stem":155,"__hash__":4576},"docs_fr/fr/04.recipes/04.persistence.md",{"type":345,"value":346,"toc":4536},"minimark",[347,356,364,420,423,427,437,617,637,639,643,657,1008,1011,1020,1022,1026,1037,1168,1180,1396,1406,1408,1412,1416,1422,1444,1447,1449,1453,1476,1491,1493,1497,1515,1535,1555,1557,1561,1565,1570,1627,1650,1652,1656,1666,1725,1727,1731,1738,1800,1804,1812,1831,1837,1989,2008,2010,2014,2094,2102,2104,2108,2131,2141,2239,2251,2253,2256,2265,2267,2271,2277,2390,2392,2400,2407,2415,2640,2838,2841,2872,2874,2878,2884,3062,3106,3109,3111,3119,3128,3259,3297,3300,3332,3334,3338,3396,3398,3402,3409,3466,3472,3474,3478,3987,4379,4386,4388,4392,4395,4504,4506,4510,4532],[348,349,350,351,355],"p",{},"La ",[352,353,354],"strong",{},"persistance"," stocke les résultats de requêtes réussies dans un backend de stockage (Hive, Isar, SharedPreferences et autres) et les restaure au démarrage de l'application. Le cache en mémoire est hydraté avant la première requête réseau, y compris lors des sessions hors-ligne.",[348,357,358,359,363],{},"Ce mécanisme repose sur trois composants du package ",[360,361,362],"code",{},"qora"," :",[365,366,367,380],"table",{},[368,369,370],"thead",{},[371,372,373,377],"tr",{},[374,375,376],"th",{},"Classe",[374,378,379],{},"Rôle",[381,382,383,394,408],"tbody",{},[371,384,385,391],{},[386,387,388],"td",{},[360,389,390],{},"StorageAdapter",[386,392,393],{},"Contrat clé/valeur brut pour un backend de stockage",[371,395,396,401],{},[386,397,398],{},[360,399,400],{},"QoraSerializer\u003CT>",[386,402,403,404,407],{},"Convertit ",[360,405,406],{},"T"," ↔ JSON pour un type spécifique",[371,409,410,414],{},[386,411,412],{},[360,413,229],{},[386,415,416,417,419],{},"Remplacement clé-en-main de ",[360,418,202],{}," qui câble tout",[421,422],"hr",{},[424,425,98],"h2",{"id":426},"configuration",[428,429,431,432,434,435],"h3",{"id":430},"_1-remplacer-qoraclient-par-persistqoraclient","1. Remplacer ",[360,433,202],{}," par ",[360,436,229],{},[438,439,445],"pre",{"className":440,"code":441,"filename":442,"language":443,"meta":444,"style":444},"language-dart shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import 'package:qora/qora.dart';\n\nfinal client = PersistQoraClient(\n  storage: InMemoryStorageAdapter(), // remplacer par un vrai adaptateur (voir ci-dessous)\n  persistDuration: const Duration(days: 7),\n  config: const QoraClientConfig(\n    defaultOptions: QoraOptions(staleTime: Duration(minutes: 5)),\n    debugMode: true, // affiche les logs [PersistQora]\n  ),\n);\n","main.dart","dart","",[360,446,447,464,471,492,514,542,557,588,602,610],{"__ignoreMap":444},[448,449,452,456,460],"span",{"class":450,"line":451},"line",1,[448,453,455],{"class":454},"sbssI","import",[448,457,459],{"class":458},"sfazB"," 'package:qora/qora.dart'",[448,461,463],{"class":462},"sMK4o",";\n",[448,465,467],{"class":450,"line":466},2,[448,468,470],{"emptyLinePlaceholder":469},true,"\n",[448,472,474,478,482,485,489],{"class":450,"line":473},3,[448,475,477],{"class":476},"spNyl","final",[448,479,481],{"class":480},"sTEyZ"," client ",[448,483,484],{"class":462},"=",[448,486,488],{"class":487},"sBMFI"," PersistQoraClient",[448,490,491],{"class":480},"(\n",[448,493,495,498,501,504,507,510],{"class":450,"line":494},4,[448,496,497],{"class":480},"  storage",[448,499,500],{"class":462},":",[448,502,503],{"class":487}," InMemoryStorageAdapter",[448,505,506],{"class":480},"()",[448,508,509],{"class":462},",",[448,511,513],{"class":512},"sHwdD"," // remplacer par un vrai adaptateur (voir ci-dessous)\n",[448,515,517,520,522,525,528,531,533,536,539],{"class":450,"line":516},5,[448,518,519],{"class":480},"  persistDuration",[448,521,500],{"class":462},[448,523,524],{"class":476}," const",[448,526,527],{"class":487}," Duration",[448,529,530],{"class":480},"(days",[448,532,500],{"class":462},[448,534,535],{"class":454}," 7",[448,537,538],{"class":480},")",[448,540,541],{"class":462},",\n",[448,543,545,548,550,552,555],{"class":450,"line":544},6,[448,546,547],{"class":480},"  config",[448,549,500],{"class":462},[448,551,524],{"class":476},[448,553,554],{"class":487}," QoraClientConfig",[448,556,491],{"class":480},[448,558,560,563,565,568,571,573,575,578,580,583,586],{"class":450,"line":559},7,[448,561,562],{"class":480},"    defaultOptions",[448,564,500],{"class":462},[448,566,567],{"class":487}," QoraOptions",[448,569,570],{"class":480},"(staleTime",[448,572,500],{"class":462},[448,574,527],{"class":487},[448,576,577],{"class":480},"(minutes",[448,579,500],{"class":462},[448,581,582],{"class":454}," 5",[448,584,585],{"class":480},"))",[448,587,541],{"class":462},[448,589,591,594,596,599],{"class":450,"line":590},8,[448,592,593],{"class":480},"    debugMode",[448,595,500],{"class":462},[448,597,598],{"class":462}," true,",[448,600,601],{"class":512}," // affiche les logs [PersistQora]\n",[448,603,605,608],{"class":450,"line":604},9,[448,606,607],{"class":480},"  )",[448,609,541],{"class":462},[448,611,613,615],{"class":450,"line":612},10,[448,614,538],{"class":480},[448,616,463],{"class":462},[348,618,619,621,622,624,625,628,629,632,633,636],{},[360,620,229],{}," étend ",[360,623,202],{},". Les APIs existantes comme ",[360,626,627],{},"fetchQuery",", ",[360,630,631],{},"watchQuery"," et ",[360,634,635],{},"invalidate"," restent inchangées.",[421,638],{},[428,640,642],{"id":641},"_2-enregistrer-les-sérialiseurs","2. Enregistrer les sérialiseurs",[348,644,645,646,648,649,652,653,656],{},"Enregistrer un ",[360,647,400],{}," pour chaque type à persister. Le faire ",[352,650,651],{},"avant"," d'appeler ",[360,654,655],{},"hydrate",".",[438,658,660],{"className":440,"code":659,"filename":442,"language":443,"meta":444,"style":444},"// Objet unique\nclient.registerSerializer\u003CUser>(\n  QoraSerializer(\n    toJson: (user) => user.toJson(),\n    fromJson: User.fromJson,\n  ),\n  name: 'User', // nom explicite : voir la section Obfuscation ci-dessous\n);\n\n// Collection\nclient.registerSerializer\u003CList\u003CPost>>(\n  QoraSerializer(\n    toJson: (posts) => posts.map((p) => p.toJson()).toList(),\n    fromJson: (json) => (json as List).map((e) => Post.fromJson(e)).toList(),\n  ),\n  name: 'List\u003CPost>',\n);\n\n// Primitif : aucune conversion nécessaire\nclient.registerSerializer\u003Cint>(\n  QoraSerializer(toJson: (n) => n, fromJson: (json) => json as int),\n  name: 'int',\n);\n",[360,661,662,667,687,694,719,736,742,757,763,767,772,794,801,845,895,902,914,921,926,932,948,989,1001],{"__ignoreMap":444},[448,663,664],{"class":450,"line":451},[448,665,666],{"class":512},"// Objet unique\n",[448,668,669,672,674,678,681,684],{"class":450,"line":466},[448,670,671],{"class":480},"client",[448,673,656],{"class":462},[448,675,677],{"class":676},"s2Zo4","registerSerializer",[448,679,680],{"class":480},"\u003C",[448,682,683],{"class":487},"User",[448,685,686],{"class":480},">(\n",[448,688,689,692],{"class":450,"line":473},[448,690,691],{"class":487},"  QoraSerializer",[448,693,491],{"class":480},[448,695,696,699,701,704,707,710,712,715,717],{"class":450,"line":494},[448,697,698],{"class":480},"    toJson",[448,700,500],{"class":462},[448,702,703],{"class":480}," (user) ",[448,705,706],{"class":462},"=>",[448,708,709],{"class":480}," user",[448,711,656],{"class":462},[448,713,714],{"class":676},"toJson",[448,716,506],{"class":480},[448,718,541],{"class":462},[448,720,721,724,726,729,731,734],{"class":450,"line":516},[448,722,723],{"class":480},"    fromJson",[448,725,500],{"class":462},[448,727,728],{"class":487}," User",[448,730,656],{"class":462},[448,732,733],{"class":480},"fromJson",[448,735,541],{"class":462},[448,737,738,740],{"class":450,"line":544},[448,739,607],{"class":480},[448,741,541],{"class":462},[448,743,744,747,749,752,754],{"class":450,"line":559},[448,745,746],{"class":480},"  name",[448,748,500],{"class":462},[448,750,751],{"class":458}," 'User'",[448,753,509],{"class":462},[448,755,756],{"class":512}," // nom explicite : voir la section Obfuscation ci-dessous\n",[448,758,759,761],{"class":450,"line":590},[448,760,538],{"class":480},[448,762,463],{"class":462},[448,764,765],{"class":450,"line":604},[448,766,470],{"emptyLinePlaceholder":469},[448,768,769],{"class":450,"line":612},[448,770,771],{"class":512},"// Collection\n",[448,773,775,777,779,781,783,786,788,791],{"class":450,"line":774},11,[448,776,671],{"class":480},[448,778,656],{"class":462},[448,780,677],{"class":676},[448,782,680],{"class":480},[448,784,785],{"class":487},"List",[448,787,680],{"class":480},[448,789,790],{"class":487},"Post",[448,792,793],{"class":480},">>(\n",[448,795,797,799],{"class":450,"line":796},12,[448,798,691],{"class":487},[448,800,491],{"class":480},[448,802,804,806,808,811,813,816,818,821,824,826,829,831,833,836,838,841,843],{"class":450,"line":803},13,[448,805,698],{"class":480},[448,807,500],{"class":462},[448,809,810],{"class":480}," (posts) ",[448,812,706],{"class":462},[448,814,815],{"class":480}," posts",[448,817,656],{"class":462},[448,819,820],{"class":676},"map",[448,822,823],{"class":480},"((p) ",[448,825,706],{"class":462},[448,827,828],{"class":480}," p",[448,830,656],{"class":462},[448,832,714],{"class":676},[448,834,835],{"class":480},"())",[448,837,656],{"class":462},[448,839,840],{"class":676},"toList",[448,842,506],{"class":480},[448,844,541],{"class":462},[448,846,848,850,852,855,857,860,863,866,868,870,872,875,877,880,882,884,887,889,891,893],{"class":450,"line":847},14,[448,849,723],{"class":480},[448,851,500],{"class":462},[448,853,854],{"class":480}," (json) ",[448,856,706],{"class":462},[448,858,859],{"class":480}," (json ",[448,861,862],{"class":462},"as",[448,864,865],{"class":487}," List",[448,867,538],{"class":480},[448,869,656],{"class":462},[448,871,820],{"class":676},[448,873,874],{"class":480},"((e) ",[448,876,706],{"class":462},[448,878,879],{"class":487}," Post",[448,881,656],{"class":462},[448,883,733],{"class":676},[448,885,886],{"class":480},"(e))",[448,888,656],{"class":462},[448,890,840],{"class":676},[448,892,506],{"class":480},[448,894,541],{"class":462},[448,896,898,900],{"class":450,"line":897},15,[448,899,607],{"class":480},[448,901,541],{"class":462},[448,903,905,907,909,912],{"class":450,"line":904},16,[448,906,746],{"class":480},[448,908,500],{"class":462},[448,910,911],{"class":458}," 'List\u003CPost>'",[448,913,541],{"class":462},[448,915,917,919],{"class":450,"line":916},17,[448,918,538],{"class":480},[448,920,463],{"class":462},[448,922,924],{"class":450,"line":923},18,[448,925,470],{"emptyLinePlaceholder":469},[448,927,929],{"class":450,"line":928},19,[448,930,931],{"class":512},"// Primitif : aucune conversion nécessaire\n",[448,933,935,937,939,941,943,946],{"class":450,"line":934},20,[448,936,671],{"class":480},[448,938,656],{"class":462},[448,940,677],{"class":676},[448,942,680],{"class":480},[448,944,945],{"class":487},"int",[448,947,686],{"class":480},[448,949,951,953,956,958,961,963,966,968,971,973,975,977,980,982,985,987],{"class":450,"line":950},21,[448,952,691],{"class":487},[448,954,955],{"class":480},"(toJson",[448,957,500],{"class":462},[448,959,960],{"class":480}," (n) ",[448,962,706],{"class":462},[448,964,965],{"class":480}," n",[448,967,509],{"class":462},[448,969,970],{"class":480}," fromJson",[448,972,500],{"class":462},[448,974,854],{"class":480},[448,976,706],{"class":462},[448,978,979],{"class":480}," json ",[448,981,862],{"class":462},[448,983,984],{"class":487}," int",[448,986,538],{"class":480},[448,988,541],{"class":462},[448,990,992,994,996,999],{"class":450,"line":991},22,[448,993,746],{"class":480},[448,995,500],{"class":462},[448,997,998],{"class":458}," 'int'",[448,1000,541],{"class":462},[448,1002,1004,1006],{"class":450,"line":1003},23,[448,1005,538],{"class":480},[448,1007,463],{"class":462},[348,1009,1010],{},"Les types sans sérialiseur enregistré sont récupérés et mis en cache en mémoire normalement. Ils ne sont pas persistés sur disque.",[348,1012,1013,1014,1019],{},"Pour les projets avec de nombreux modèles, voir ",[1015,1016,1018],"a",{"href":1017},"#organisation-des-s%C3%A9rialiseurs-%C3%A0-grande-%C3%A9chelle","Organisation des sérialiseurs à grande échelle"," ci-dessous.",[421,1021],{},[428,1023,1025],{"id":1024},"_3-initialiser-le-stockage-puis-hydrater","3. Initialiser le stockage, puis hydrater",[348,1027,1028,1029,1032,1033,1036],{},"Attendre ",[360,1030,1031],{},"hydrate()"," avant ",[360,1034,1035],{},"runApp"," garantit un cache chaud dès la première frame :",[438,1038,1040],{"className":440,"code":1039,"filename":442,"language":443,"meta":444,"style":444},"Future\u003Cvoid> main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  await storage.init();         // ouvrir la base de données / le fichier\n  await client.hydrate();       // chauffer le cache depuis le disque\n\n  runApp(QoraScope(client: client, child: const MyApp()));\n}\n",[360,1041,1042,1068,1082,1086,1107,1125,1129,1163],{"__ignoreMap":444},[448,1043,1044,1047,1049,1052,1055,1058,1061,1065],{"class":450,"line":451},[448,1045,1046],{"class":487},"Future",[448,1048,680],{"class":480},[448,1050,1051],{"class":476},"void",[448,1053,1054],{"class":480},"> ",[448,1056,1057],{"class":676},"main",[448,1059,1060],{"class":480},"() ",[448,1062,1064],{"class":1063},"s7zQu","async",[448,1066,1067],{"class":480}," {\n",[448,1069,1070,1073,1075,1078,1080],{"class":450,"line":466},[448,1071,1072],{"class":487},"  WidgetsFlutterBinding",[448,1074,656],{"class":462},[448,1076,1077],{"class":676},"ensureInitialized",[448,1079,506],{"class":480},[448,1081,463],{"class":462},[448,1083,1084],{"class":450,"line":473},[448,1085,470],{"emptyLinePlaceholder":469},[448,1087,1088,1091,1094,1096,1099,1101,1104],{"class":450,"line":494},[448,1089,1090],{"class":1063},"  await",[448,1092,1093],{"class":480}," storage",[448,1095,656],{"class":462},[448,1097,1098],{"class":676},"init",[448,1100,506],{"class":480},[448,1102,1103],{"class":462},";",[448,1105,1106],{"class":512},"         // ouvrir la base de données / le fichier\n",[448,1108,1109,1111,1114,1116,1118,1120,1122],{"class":450,"line":516},[448,1110,1090],{"class":1063},[448,1112,1113],{"class":480}," client",[448,1115,656],{"class":462},[448,1117,655],{"class":676},[448,1119,506],{"class":480},[448,1121,1103],{"class":462},[448,1123,1124],{"class":512},"       // chauffer le cache depuis le disque\n",[448,1126,1127],{"class":450,"line":544},[448,1128,470],{"emptyLinePlaceholder":469},[448,1130,1131,1134,1137,1139,1142,1144,1146,1148,1151,1153,1155,1158,1161],{"class":450,"line":559},[448,1132,1133],{"class":676},"  runApp",[448,1135,1136],{"class":480},"(",[448,1138,103],{"class":487},[448,1140,1141],{"class":480},"(client",[448,1143,500],{"class":462},[448,1145,1113],{"class":480},[448,1147,509],{"class":462},[448,1149,1150],{"class":480}," child",[448,1152,500],{"class":462},[448,1154,524],{"class":476},[448,1156,1157],{"class":487}," MyApp",[448,1159,1160],{"class":480},"()))",[448,1162,463],{"class":462},[448,1164,1165],{"class":450,"line":590},[448,1166,1167],{"class":480},"}\n",[348,1169,1170,1171,1173,1174,1177,1178,363],{},"Si ",[360,1172,1031],{}," est mesurément lent (grand nombre d'entrées stockées : vérifier avec du profiling avant d'optimiser), afficher un ",[352,1175,1176],{},"écran de démarrage"," pendant l'exécution du Future plutôt que de bloquer ",[360,1179,1035],{},[438,1181,1183],{"className":440,"code":1182,"filename":442,"language":443,"meta":444,"style":444},"void main() {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  runApp(\n    QoraScope(\n      client: client,\n      child: FutureBuilder\u003Cvoid>(\n        future: storage.init().then((_) => client.hydrate()),\n        builder: (context, snapshot) {\n          if (snapshot.connectionState != ConnectionState.done) {\n            return const MaterialApp(home: SplashScreen());\n          }\n          return const MyApp();\n        },\n      ),\n    ),\n  );\n}\n",[360,1184,1185,1195,1207,1211,1217,1224,1235,1251,1286,1301,1325,1347,1352,1365,1372,1379,1386,1392],{"__ignoreMap":444},[448,1186,1187,1189,1192],{"class":450,"line":451},[448,1188,1051],{"class":476},[448,1190,1191],{"class":676}," main",[448,1193,1194],{"class":480},"() {\n",[448,1196,1197,1199,1201,1203,1205],{"class":450,"line":466},[448,1198,1072],{"class":487},[448,1200,656],{"class":462},[448,1202,1077],{"class":676},[448,1204,506],{"class":480},[448,1206,463],{"class":462},[448,1208,1209],{"class":450,"line":473},[448,1210,470],{"emptyLinePlaceholder":469},[448,1212,1213,1215],{"class":450,"line":494},[448,1214,1133],{"class":676},[448,1216,491],{"class":480},[448,1218,1219,1222],{"class":450,"line":516},[448,1220,1221],{"class":487},"    QoraScope",[448,1223,491],{"class":480},[448,1225,1226,1229,1231,1233],{"class":450,"line":544},[448,1227,1228],{"class":480},"      client",[448,1230,500],{"class":462},[448,1232,1113],{"class":480},[448,1234,541],{"class":462},[448,1236,1237,1240,1242,1245,1247,1249],{"class":450,"line":559},[448,1238,1239],{"class":480},"      child",[448,1241,500],{"class":462},[448,1243,1244],{"class":487}," FutureBuilder",[448,1246,680],{"class":480},[448,1248,1051],{"class":476},[448,1250,686],{"class":480},[448,1252,1253,1256,1258,1260,1262,1264,1266,1268,1271,1274,1276,1278,1280,1282,1284],{"class":450,"line":590},[448,1254,1255],{"class":480},"        future",[448,1257,500],{"class":462},[448,1259,1093],{"class":480},[448,1261,656],{"class":462},[448,1263,1098],{"class":676},[448,1265,506],{"class":480},[448,1267,656],{"class":462},[448,1269,1270],{"class":676},"then",[448,1272,1273],{"class":480},"((_) ",[448,1275,706],{"class":462},[448,1277,1113],{"class":480},[448,1279,656],{"class":462},[448,1281,655],{"class":676},[448,1283,835],{"class":480},[448,1285,541],{"class":462},[448,1287,1288,1291,1293,1296,1298],{"class":450,"line":604},[448,1289,1290],{"class":480},"        builder",[448,1292,500],{"class":462},[448,1294,1295],{"class":480}," (context",[448,1297,509],{"class":462},[448,1299,1300],{"class":480}," snapshot) {\n",[448,1302,1303,1306,1309,1311,1314,1317,1320,1322],{"class":450,"line":612},[448,1304,1305],{"class":1063},"          if",[448,1307,1308],{"class":480}," (snapshot",[448,1310,656],{"class":462},[448,1312,1313],{"class":480},"connectionState ",[448,1315,1316],{"class":462},"!=",[448,1318,1319],{"class":487}," ConnectionState",[448,1321,656],{"class":462},[448,1323,1324],{"class":480},"done) {\n",[448,1326,1327,1330,1332,1335,1338,1340,1343,1345],{"class":450,"line":774},[448,1328,1329],{"class":1063},"            return",[448,1331,524],{"class":476},[448,1333,1334],{"class":487}," MaterialApp",[448,1336,1337],{"class":480},"(home",[448,1339,500],{"class":462},[448,1341,1342],{"class":487}," SplashScreen",[448,1344,835],{"class":480},[448,1346,463],{"class":462},[448,1348,1349],{"class":450,"line":796},[448,1350,1351],{"class":480},"          }\n",[448,1353,1354,1357,1359,1361,1363],{"class":450,"line":803},[448,1355,1356],{"class":1063},"          return",[448,1358,524],{"class":476},[448,1360,1157],{"class":487},[448,1362,506],{"class":480},[448,1364,463],{"class":462},[448,1366,1367,1370],{"class":450,"line":847},[448,1368,1369],{"class":480},"        }",[448,1371,541],{"class":462},[448,1373,1374,1377],{"class":450,"line":897},[448,1375,1376],{"class":480},"      )",[448,1378,541],{"class":462},[448,1380,1381,1384],{"class":450,"line":904},[448,1382,1383],{"class":480},"    )",[448,1385,541],{"class":462},[448,1387,1388,1390],{"class":450,"line":916},[448,1389,607],{"class":480},[448,1391,463],{"class":462},[448,1393,1394],{"class":450,"line":923},[448,1395,1167],{"class":480},[348,1397,1398,1399,1401,1402,1405],{},"En pratique, ",[360,1400,1031],{}," lit une poignée de chaînes JSON depuis le stockage local. Pour la plupart des applications, le pattern ",[360,1403,1404],{},"before runApp"," est plus simple et suffisamment rapide.",[421,1407],{},[424,1409,1411],{"id":1410},"fonctionnement","Fonctionnement",[428,1413,1415],{"id":1414},"persistance-automatique-write-through","Persistance automatique (write-through)",[348,1417,1418,1419,1421],{},"Après chaque fetch réussi, qu'il soit déclenché directement ou par la revalidation SWR en arrière-plan, le résultat est écrit dans ",[360,1420,390],{}," sous forme d'enveloppe JSON contenant :",[1423,1424,1425,1429,1435,1441],"ul",{},[1426,1427,1428],"li",{},"les données sérialisées",[1426,1430,1431,1432,538],{},"un discriminateur de type (",[360,1433,1434],{},"typeName",[1426,1436,1437,1438,538],{},"l'horodatage (",[360,1439,1440],{},"persistedAt",[1426,1442,1443],{},"le TTL en millisecondes",[348,1445,1446],{},"Aucun code supplémentaire n'est nécessaire. Tant que le type dispose d'un sérialiseur enregistré, il est persisté automatiquement.",[421,1448],{},[428,1450,1452],{"id":1451},"hydratation-typée-paresseuse","Hydratation typée paresseuse",[348,1454,1455,1457,1458,1461,1462,628,1465,628,1468,1471,1472,1475],{},[360,1456,1031],{}," désérialise les entrées stockées mais ne les injecte ",[352,1459,1460],{},"pas"," immédiatement dans le cache. Il les met en file dans une map interne. Lors du premier appel à ",[360,1463,1464],{},"fetchQuery\u003CT>",[360,1466,1467],{},"watchQuery\u003CT>",[360,1469,1470],{},"getQueryData\u003CT>",", etc., l'entrée en file est consommée et injectée comme ",[360,1473,1474],{},"CacheEntry\u003CT>"," correctement typé.",[348,1477,1478,1479,1482,1483,1486,1487,1490],{},"Cela évite un échec de cast runtime Dart : ",[360,1480,1481],{},"CacheEntry\u003Cdynamic> as CacheEntry\u003CUser>"," lève une exception, mais ",[360,1484,1485],{},"CacheEntry\u003CUser>"," créé par ",[360,1488,1489],{},"hydrateQuery\u003CUser>"," non.",[421,1492],{},[428,1494,1496],{"id":1495},"swr-et-fraîcheur","SWR et fraîcheur",[348,1498,1499,1500,1503,1504,1506,1507,1510,1511,1514],{},"L'état ",[360,1501,1502],{},"Success"," restauré porte le ",[360,1505,1440],{}," original comme horodatage ",[360,1508,1509],{},"updatedAt",". Cela signifie que ",[360,1512,1513],{},"staleTime"," fonctionne exactement comme en mémoire :",[1423,1516,1517,1526],{},[1426,1518,1519,1521,1522,1525],{},[360,1520,1440],{}," est récent → entrée ",[352,1523,1524],{},"fraîche"," → pas d'appel réseau au montage",[1426,1527,1528,1530,1531,1534],{},[360,1529,1440],{}," est ancien → entrée ",[352,1532,1533],{},"périmée"," → widget affiche les données en cache immédiatement, puis revalide en arrière-plan (SWR)",[438,1536,1538],{"className":440,"code":1537,"filename":442,"language":443,"meta":444,"style":444},"// staleTime: 5 minutes\n// Si le dernier fetch était il y a 3 minutes → encore frais → pas de spinner au redémarrage\n// Si le dernier fetch était il y a 8 minutes → afficher les données en cache + refetch en arrière-plan\n",[360,1539,1540,1545,1550],{"__ignoreMap":444},[448,1541,1542],{"class":450,"line":451},[448,1543,1544],{"class":512},"// staleTime: 5 minutes\n",[448,1546,1547],{"class":450,"line":466},[448,1548,1549],{"class":512},"// Si le dernier fetch était il y a 3 minutes → encore frais → pas de spinner au redémarrage\n",[448,1551,1552],{"class":450,"line":473},[448,1553,1554],{"class":512},"// Si le dernier fetch était il y a 8 minutes → afficher les données en cache + refetch en arrière-plan\n",[421,1556],{},[424,1558,1560],{"id":1559},"ttl-time-to-live","TTL (Time To Live)",[428,1562,1564],{"id":1563},"ttl-global","TTL global",[348,1566,1567,1568,363],{},"Défini une fois sur ",[360,1569,229],{},[438,1571,1573],{"className":440,"code":1572,"filename":442,"language":443,"meta":444,"style":444},"final client = PersistQoraClient(\n  storage: adapter,\n  persistDuration: const Duration(days: 7), // valeur par défaut\n);\n",[360,1574,1575,1587,1598,1621],{"__ignoreMap":444},[448,1576,1577,1579,1581,1583,1585],{"class":450,"line":451},[448,1578,477],{"class":476},[448,1580,481],{"class":480},[448,1582,484],{"class":462},[448,1584,488],{"class":487},[448,1586,491],{"class":480},[448,1588,1589,1591,1593,1596],{"class":450,"line":466},[448,1590,497],{"class":480},[448,1592,500],{"class":462},[448,1594,1595],{"class":480}," adapter",[448,1597,541],{"class":462},[448,1599,1600,1602,1604,1606,1608,1610,1612,1614,1616,1618],{"class":450,"line":473},[448,1601,519],{"class":480},[448,1603,500],{"class":462},[448,1605,524],{"class":476},[448,1607,527],{"class":487},[448,1609,530],{"class":480},[448,1611,500],{"class":462},[448,1613,535],{"class":454},[448,1615,538],{"class":480},[448,1617,509],{"class":462},[448,1619,1620],{"class":512}," // valeur par défaut\n",[448,1622,1623,1625],{"class":450,"line":494},[448,1624,538],{"class":480},[448,1626,463],{"class":462},[348,1628,1629,1630,1633,1634,1637,1638,1641,1642,1645,1646,1649],{},"Par défaut, les entrées expirent après 7 jours (",[360,1631,1632],{},"persistDuration: Duration(days: 7)",").\nPassez ",[360,1635,1636],{},"null"," pour persister indéfiniment (jusqu'à ce que ",[360,1639,1640],{},"removeQuery"," ou ",[360,1643,1644],{},"evictFromStorage"," soit appelé explicitement).\nPassez ",[360,1647,1648],{},"Duration.zero"," pour désactiver la persistance (les entrées expirent immédiatement).",[421,1651],{},[428,1653,1655],{"id":1654},"surcharge-ttl-par-requête","Surcharge TTL par requête",[348,1657,1658,1659,1662,1663,363],{},"Passer ",[360,1660,1661],{},"ttl:"," lors d'un appel manuel à ",[360,1664,1665],{},"persistQuery",[438,1667,1669],{"className":440,"code":1668,"filename":442,"language":443,"meta":444,"style":444},"// Forcer la persistance de la valeur courante en cache avec un TTL personnalisé\nawait client.persistQuery\u003CUser>(['users', userId], ttl: const Duration(hours: 1));\n",[360,1670,1671,1676],{"__ignoreMap":444},[448,1672,1673],{"class":450,"line":451},[448,1674,1675],{"class":512},"// Forcer la persistance de la valeur courante en cache avec un TTL personnalisé\n",[448,1677,1678,1681,1683,1685,1687,1689,1691,1694,1697,1699,1702,1704,1707,1709,1711,1713,1716,1718,1721,1723],{"class":450,"line":466},[448,1679,1680],{"class":1063},"await",[448,1682,1113],{"class":480},[448,1684,656],{"class":462},[448,1686,1665],{"class":676},[448,1688,680],{"class":480},[448,1690,683],{"class":487},[448,1692,1693],{"class":480},">([",[448,1695,1696],{"class":458},"'users'",[448,1698,509],{"class":462},[448,1700,1701],{"class":480}," userId]",[448,1703,509],{"class":462},[448,1705,1706],{"class":480}," ttl",[448,1708,500],{"class":462},[448,1710,524],{"class":476},[448,1712,527],{"class":487},[448,1714,1715],{"class":480},"(hours",[448,1717,500],{"class":462},[448,1719,1720],{"class":454}," 1",[448,1722,585],{"class":480},[448,1724,463],{"class":462},[421,1726],{},[428,1728,1730],{"id":1729},"que-se-passe-t-il-avec-les-entrées-expirées-lors-de-lhydratation","Que se passe-t-il avec les entrées expirées lors de l'hydratation ?",[348,1732,1733,1734,1737],{},"Les entrées expirées sont ",[352,1735,1736],{},"supprimées du stockage et ignorées"," : elles ne sont jamais injectées dans le cache. Le prochain fetch réussi écrira une entrée fraîche.",[438,1739,1743],{"className":1740,"code":1741,"language":1742,"meta":444,"style":444},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","graph TD\n    A[hydrate: lecture de toutes les clés du stockage]\n    A --> B{Expirée ?}\n    B -- oui --> C[Supprimer du stockage]\n    B -- non --> D{Sérialiseur trouvé ?}\n    D -- non --> E[Avertir + ignorer]\n    D -- oui --> F{fromJson OK ?}\n    F -- non --> G[Supprimer + avertir]\n    F -- oui --> H[Mettre en file dans _pendingHydration]\n    H --> I[Premier appel fetchQuery / watchQuery]\n    I --> J[hydrateQuery injection typée dans le cache]\n","mermaid",[360,1744,1745,1750,1755,1760,1765,1770,1775,1780,1785,1790,1795],{"__ignoreMap":444},[448,1746,1747],{"class":450,"line":451},[448,1748,1749],{"class":480},"graph TD\n",[448,1751,1752],{"class":450,"line":466},[448,1753,1754],{"class":480},"    A[hydrate: lecture de toutes les clés du stockage]\n",[448,1756,1757],{"class":450,"line":473},[448,1758,1759],{"class":480},"    A --> B{Expirée ?}\n",[448,1761,1762],{"class":450,"line":494},[448,1763,1764],{"class":480},"    B -- oui --> C[Supprimer du stockage]\n",[448,1766,1767],{"class":450,"line":516},[448,1768,1769],{"class":480},"    B -- non --> D{Sérialiseur trouvé ?}\n",[448,1771,1772],{"class":450,"line":544},[448,1773,1774],{"class":480},"    D -- non --> E[Avertir + ignorer]\n",[448,1776,1777],{"class":450,"line":559},[448,1778,1779],{"class":480},"    D -- oui --> F{fromJson OK ?}\n",[448,1781,1782],{"class":450,"line":590},[448,1783,1784],{"class":480},"    F -- non --> G[Supprimer + avertir]\n",[448,1786,1787],{"class":450,"line":604},[448,1788,1789],{"class":480},"    F -- oui --> H[Mettre en file dans _pendingHydration]\n",[448,1791,1792],{"class":450,"line":612},[448,1793,1794],{"class":480},"    H --> I[Premier appel fetchQuery / watchQuery]\n",[448,1796,1797],{"class":450,"line":774},[448,1798,1799],{"class":480},"    I --> J[hydrateQuery injection typée dans le cache]\n",[428,1801,1803],{"id":1802},"versionnement-des-modèles-et-migrations","Versionnement des modèles et migrations",[348,1805,1806,1807,1809,1810,656],{},"Si le schéma du modèle change entre les versions de l'application, champ requis ajouté à ",[360,1808,683],{},", champ renommé, type changé, les entrées persistées par l'ancienne version peuvent échouer à la désérialisation avec le nouveau ",[360,1811,733],{},[348,1813,1814,1816,1817,1819,1820,1822,1823,1826,1827,1830],{},[360,1815,229],{}," intercepte chaque exception ",[360,1818,733],{}," lors de ",[360,1821,655],{},", journalise un avertissement (quand ",[360,1824,1825],{},"debugMode"," est ",[360,1828,1829],{},"true","), supprime l'entrée corrompue du stockage et continue. L'application ne plante jamais à cause d'un schéma obsolète sur le disque.",[348,1832,1833,1834,1836],{},"La responsabilité est d'écrire un ",[360,1835,733],{}," défensif qui gère les anciennes et nouvelles formes :",[438,1838,1841],{"className":440,"code":1839,"filename":1840,"language":443,"meta":444,"style":444},"factory User.fromJson(dynamic json) {\n  final map = json as Map\u003CString, dynamic>;\n  return User(\n    id: map['id'] as int,\n    name: map['name'] as String,\n    // Ajouté en v2 : valeur par défaut chaîne vide pour les entrées persistées par v1\n    avatarUrl: map['avatarUrl'] as String? ?? '',\n  );\n}\n","models/user.dart",[360,1842,1843,1862,1893,1902,1924,1945,1950,1979,1985],{"__ignoreMap":444},[448,1844,1845,1848,1850,1852,1854,1856,1859],{"class":450,"line":451},[448,1846,1847],{"class":462},"factory",[448,1849,728],{"class":487},[448,1851,656],{"class":462},[448,1853,733],{"class":676},[448,1855,1136],{"class":480},[448,1857,1858],{"class":487},"dynamic",[448,1860,1861],{"class":480}," json) {\n",[448,1863,1864,1867,1870,1872,1874,1876,1879,1881,1884,1886,1888,1891],{"class":450,"line":466},[448,1865,1866],{"class":476},"  final",[448,1868,1869],{"class":480}," map ",[448,1871,484],{"class":462},[448,1873,979],{"class":480},[448,1875,862],{"class":462},[448,1877,1878],{"class":487}," Map",[448,1880,680],{"class":480},[448,1882,1883],{"class":487},"String",[448,1885,628],{"class":480},[448,1887,1858],{"class":487},[448,1889,1890],{"class":480},">",[448,1892,463],{"class":462},[448,1894,1895,1898,1900],{"class":450,"line":473},[448,1896,1897],{"class":1063},"  return",[448,1899,728],{"class":487},[448,1901,491],{"class":480},[448,1903,1904,1907,1909,1912,1915,1918,1920,1922],{"class":450,"line":494},[448,1905,1906],{"class":480},"    id",[448,1908,500],{"class":462},[448,1910,1911],{"class":480}," map[",[448,1913,1914],{"class":458},"'id'",[448,1916,1917],{"class":480},"] ",[448,1919,862],{"class":462},[448,1921,984],{"class":487},[448,1923,541],{"class":462},[448,1925,1926,1929,1931,1933,1936,1938,1940,1943],{"class":450,"line":516},[448,1927,1928],{"class":480},"    name",[448,1930,500],{"class":462},[448,1932,1911],{"class":480},[448,1934,1935],{"class":458},"'name'",[448,1937,1917],{"class":480},[448,1939,862],{"class":462},[448,1941,1942],{"class":487}," String",[448,1944,541],{"class":462},[448,1946,1947],{"class":450,"line":544},[448,1948,1949],{"class":512},"    // Ajouté en v2 : valeur par défaut chaîne vide pour les entrées persistées par v1\n",[448,1951,1952,1955,1957,1959,1962,1964,1966,1968,1971,1974,1977],{"class":450,"line":559},[448,1953,1954],{"class":480},"    avatarUrl",[448,1956,500],{"class":462},[448,1958,1911],{"class":480},[448,1960,1961],{"class":458},"'avatarUrl'",[448,1963,1917],{"class":480},[448,1965,862],{"class":462},[448,1967,1942],{"class":487},[448,1969,1970],{"class":462},"?",[448,1972,1973],{"class":462}," ??",[448,1975,1976],{"class":458}," ''",[448,1978,541],{"class":462},[448,1980,1981,1983],{"class":450,"line":590},[448,1982,607],{"class":480},[448,1984,463],{"class":462},[448,1986,1987],{"class":450,"line":604},[448,1988,1167],{"class":480},[348,1990,1991,1992,1995,1996,1999,2000,2003,2004,2007],{},"Pour les ",[352,1993,1994],{},"changements cassants"," (suppression de champ, changement de type sans valeur par défaut sûre), incrémenter le ",[360,1997,1998],{},"name"," du sérialiseur : ex. ",[360,2001,2002],{},"'User'"," → ",[360,2005,2006],{},"'User_v2'",". Les anciennes entrées sont silencieusement ignorées lors de l'hydratation (aucun sérialiseur enregistré pour l'ancien nom), l'utilisateur obtient un fetch réseau frais lors de cette exécution, et les futures écritures utilisent le nouveau schéma. Les anciennes entrées sont finalement purgées par expiration du TTL.",[421,2009],{},[424,2011,2013],{"id":2012},"sémantique-déviction-du-stockage","Sémantique d'éviction du stockage",[365,2015,2016,2029],{},[368,2017,2018],{},[371,2019,2020,2023,2026],{},[374,2021,2022],{},"Opération",[374,2024,2025],{},"Cache en mémoire",[374,2027,2028],{},"Stockage",[381,2030,2031,2046,2058,2072,2083],{},[371,2032,2033,2038,2041],{},[386,2034,2035],{},[360,2036,2037],{},"invalidate(key)",[386,2039,2040],{},"Marqué expiré, déclenche un refetch",[386,2042,2043],{},[352,2044,2045],{},"Inchangé",[371,2047,2048,2053,2056],{},[386,2049,2050],{},[360,2051,2052],{},"removeQuery(key)",[386,2054,2055],{},"Supprimé",[386,2057,2055],{},[371,2059,2060,2065,2068],{},[386,2061,2062],{},[360,2063,2064],{},"clearCache()",[386,2066,2067],{},"Vidé",[386,2069,2070],{},[352,2071,2045],{},[371,2073,2074,2079,2081],{},[386,2075,2076],{},[360,2077,2078],{},"clearStorage()",[386,2080,2045],{},[386,2082,2067],{},[371,2084,2085,2090,2092],{},[386,2086,2087],{},[360,2088,2089],{},"clear()",[386,2091,2067],{},[386,2093,2067],{},[348,2095,2096,2098,2099,2101],{},[360,2097,635],{}," ne supprime ",[352,2100,1460],{}," intentionnellement du stockage. L'intention est de refetcher, pas de purger. Le prochain fetch réussi écrasera l'entrée stockée avec des données fraîches.",[421,2103],{},[424,2105,2107],{"id":2106},"obfuscation-et-flutter-web","Obfuscation et Flutter Web",[348,2109,2110,2111,2114,2115,2118,2119,628,2121,2124,2125,2127,2128,2130],{},"Sur Flutter Web ou quand le flag ",[360,2112,2113],{},"--obfuscate"," de Dart est actif, ",[360,2116,2117],{},"T.toString()"," est remplacé par un identifiant court minifié (",[360,2120,1015],{},[360,2122,2123],{},"b",", …). Si ",[360,2126,677],{}," est appelé sans ",[360,2129,1998],{}," explicite, le discriminateur de type écrit sur le disque devient illisible au prochain démarrage à froid.",[348,2132,2133,2140],{},[352,2134,2135,2136,2139],{},"Toujours passer ",[360,2137,2138],{},"name:"," explicitement"," pour ces configurations :",[438,2142,2144],{"className":440,"code":2143,"filename":442,"language":443,"meta":444,"style":444},"// Correct : Stable : le nom est codé en dur, ne change jamais\nclient.registerSerializer\u003CUser>(userSerializer, name: 'User');\nclient.registerSerializer\u003CList\u003CPost>>(postListSerializer, name: 'List\u003CPost>');\n\n// Incorrect : Non sûr sur Flutter Web / --obfuscate\nclient.registerSerializer\u003CUser>(userSerializer); // name par défaut à User.toString()\n",[360,2145,2146,2151,2179,2210,2214,2219],{"__ignoreMap":444},[448,2147,2148],{"class":450,"line":451},[448,2149,2150],{"class":512},"// Correct : Stable : le nom est codé en dur, ne change jamais\n",[448,2152,2153,2155,2157,2159,2161,2163,2166,2168,2171,2173,2175,2177],{"class":450,"line":466},[448,2154,671],{"class":480},[448,2156,656],{"class":462},[448,2158,677],{"class":676},[448,2160,680],{"class":480},[448,2162,683],{"class":487},[448,2164,2165],{"class":480},">(userSerializer",[448,2167,509],{"class":462},[448,2169,2170],{"class":480}," name",[448,2172,500],{"class":462},[448,2174,751],{"class":458},[448,2176,538],{"class":480},[448,2178,463],{"class":462},[448,2180,2181,2183,2185,2187,2189,2191,2193,2195,2198,2200,2202,2204,2206,2208],{"class":450,"line":473},[448,2182,671],{"class":480},[448,2184,656],{"class":462},[448,2186,677],{"class":676},[448,2188,680],{"class":480},[448,2190,785],{"class":487},[448,2192,680],{"class":480},[448,2194,790],{"class":487},[448,2196,2197],{"class":480},">>(postListSerializer",[448,2199,509],{"class":462},[448,2201,2170],{"class":480},[448,2203,500],{"class":462},[448,2205,911],{"class":458},[448,2207,538],{"class":480},[448,2209,463],{"class":462},[448,2211,2212],{"class":450,"line":494},[448,2213,470],{"emptyLinePlaceholder":469},[448,2215,2216],{"class":450,"line":516},[448,2217,2218],{"class":512},"// Incorrect : Non sûr sur Flutter Web / --obfuscate\n",[448,2220,2221,2223,2225,2227,2229,2231,2234,2236],{"class":450,"line":544},[448,2222,671],{"class":480},[448,2224,656],{"class":462},[448,2226,677],{"class":676},[448,2228,680],{"class":480},[448,2230,683],{"class":487},[448,2232,2233],{"class":480},">(userSerializer)",[448,2235,1103],{"class":462},[448,2237,2238],{"class":512}," // name par défaut à User.toString()\n",[348,2240,2241,2242,2244,2245,2248,2249,656],{},"Le ",[360,2243,1998],{}," doit rester ",[352,2246,2247],{},"stable entre les versions de l'application",". Le changer causera la désérialisation silencieuse de toutes les entrées précédemment persistées pour ce type lors du prochain ",[360,2250,655],{},[421,2252],{},[424,2254,1018],{"id":2255},"organisation-des-sérialiseurs-à-grande-échelle",[348,2257,2258,2259,2261,2262,2264],{},"Pour les petits projets (\u003C 10 modèles), les appels inline à ",[360,2260,677],{}," dans ",[360,2263,442],{}," conviennent. À mesure que l'application croît, les enregistrements nécessitent une structure.",[421,2266],{},[428,2268,2270],{"id":2269},"le-problème-de-lenregistrement-inline","Le problème de l'enregistrement inline",[348,2272,2273,2274,2276],{},"Avec 20+ modèles, ",[360,2275,442],{}," devient difficile à maintenir :",[438,2278,2280],{"className":440,"code":2279,"filename":442,"language":443,"meta":444,"style":444},"// Incorrect : Non gérable à grande échelle\nclient.registerSerializer\u003CUser>(...);\nclient.registerSerializer\u003CPost>(...);\nclient.registerSerializer\u003CComment>(...);\nclient.registerSerializer\u003CProduct>(...);\n// 20 lignes de plus...\nawait client.hydrate();\n",[360,2281,2282,2287,2309,2329,2350,2371,2376],{"__ignoreMap":444},[448,2283,2284],{"class":450,"line":451},[448,2285,2286],{"class":512},"// Incorrect : Non gérable à grande échelle\n",[448,2288,2289,2291,2293,2295,2297,2299,2302,2305,2307],{"class":450,"line":466},[448,2290,671],{"class":480},[448,2292,656],{"class":462},[448,2294,677],{"class":676},[448,2296,680],{"class":480},[448,2298,683],{"class":487},[448,2300,2301],{"class":480},">(",[448,2303,2304],{"class":462},"...",[448,2306,538],{"class":480},[448,2308,463],{"class":462},[448,2310,2311,2313,2315,2317,2319,2321,2323,2325,2327],{"class":450,"line":473},[448,2312,671],{"class":480},[448,2314,656],{"class":462},[448,2316,677],{"class":676},[448,2318,680],{"class":480},[448,2320,790],{"class":487},[448,2322,2301],{"class":480},[448,2324,2304],{"class":462},[448,2326,538],{"class":480},[448,2328,463],{"class":462},[448,2330,2331,2333,2335,2337,2339,2342,2344,2346,2348],{"class":450,"line":494},[448,2332,671],{"class":480},[448,2334,656],{"class":462},[448,2336,677],{"class":676},[448,2338,680],{"class":480},[448,2340,2341],{"class":487},"Comment",[448,2343,2301],{"class":480},[448,2345,2304],{"class":462},[448,2347,538],{"class":480},[448,2349,463],{"class":462},[448,2351,2352,2354,2356,2358,2360,2363,2365,2367,2369],{"class":450,"line":516},[448,2353,671],{"class":480},[448,2355,656],{"class":462},[448,2357,677],{"class":676},[448,2359,680],{"class":480},[448,2361,2362],{"class":487},"Product",[448,2364,2301],{"class":480},[448,2366,2304],{"class":462},[448,2368,538],{"class":480},[448,2370,463],{"class":462},[448,2372,2373],{"class":450,"line":544},[448,2374,2375],{"class":512},"// 20 lignes de plus...\n",[448,2377,2378,2380,2382,2384,2386,2388],{"class":450,"line":559},[448,2379,1680],{"class":1063},[448,2381,1113],{"class":480},[448,2383,656],{"class":462},[448,2385,655],{"class":676},[448,2387,506],{"class":480},[448,2389,463],{"class":462},[421,2391],{},[428,2393,2395,2396,2399],{"id":2394},"recommandé-fichiers-_serializerdart-satellites","Recommandé : fichiers ",[360,2397,2398],{},"_serializer.dart"," satellites",[348,2401,2402,2403,2406],{},"Garder le modèle ",[352,2404,2405],{},"pur"," (sans dépendance Qora) et placer son sérialiseur dans un fichier satellite dédié à côté de lui :",[438,2408,2413],{"className":2409,"code":2411,"language":2412,"meta":444},[2410],"language-text","features/\n  user/\n    models/\n      user.dart             ← classe de données pure, pas d'import Qora\n      user_serializer.dart  ← QoraSerializer ici\n  post/\n    models/\n      post.dart\n      post_serializer.dart\n","text",[360,2414,2411],{"__ignoreMap":444},[438,2416,2419],{"className":440,"code":2417,"filename":2418,"language":443,"meta":444,"style":444},"// Pas d'import Qora : le modèle est entièrement portable\nclass User {\n  final int id;\n  final String name;\n\n  const User({required this.id, required this.name});\n\n  factory User.fromJson(dynamic json) {\n    final map = json as Map\u003CString, dynamic>;\n    return User(id: map['id'] as int, name: map['name'] as String);\n  }\n\n  Map\u003CString, dynamic> toJson() => {'id': id, 'name': name};\n}\n","features/user/models/user.dart",[360,2420,2421,2426,2435,2446,2456,2460,2495,2499,2516,2543,2585,2590,2594,2636],{"__ignoreMap":444},[448,2422,2423],{"class":450,"line":451},[448,2424,2425],{"class":512},"// Pas d'import Qora : le modèle est entièrement portable\n",[448,2427,2428,2431,2433],{"class":450,"line":466},[448,2429,2430],{"class":462},"class",[448,2432,728],{"class":487},[448,2434,1067],{"class":480},[448,2436,2437,2439,2441,2444],{"class":450,"line":473},[448,2438,1866],{"class":476},[448,2440,984],{"class":487},[448,2442,2443],{"class":480}," id",[448,2445,463],{"class":462},[448,2447,2448,2450,2452,2454],{"class":450,"line":494},[448,2449,1866],{"class":476},[448,2451,1942],{"class":487},[448,2453,2170],{"class":480},[448,2455,463],{"class":462},[448,2457,2458],{"class":450,"line":516},[448,2459,470],{"emptyLinePlaceholder":469},[448,2461,2462,2465,2467,2470,2473,2476,2478,2481,2483,2486,2488,2490,2493],{"class":450,"line":544},[448,2463,2464],{"class":476},"  const",[448,2466,728],{"class":487},[448,2468,2469],{"class":480},"({",[448,2471,2472],{"class":476},"required",[448,2474,2475],{"class":480}," this",[448,2477,656],{"class":462},[448,2479,2480],{"class":480},"id",[448,2482,509],{"class":462},[448,2484,2485],{"class":476}," required",[448,2487,2475],{"class":480},[448,2489,656],{"class":462},[448,2491,2492],{"class":480},"name})",[448,2494,463],{"class":462},[448,2496,2497],{"class":450,"line":559},[448,2498,470],{"emptyLinePlaceholder":469},[448,2500,2501,2504,2506,2508,2510,2512,2514],{"class":450,"line":590},[448,2502,2503],{"class":462},"  factory",[448,2505,728],{"class":487},[448,2507,656],{"class":462},[448,2509,733],{"class":676},[448,2511,1136],{"class":480},[448,2513,1858],{"class":487},[448,2515,1861],{"class":480},[448,2517,2518,2521,2523,2525,2527,2529,2531,2533,2535,2537,2539,2541],{"class":450,"line":604},[448,2519,2520],{"class":476},"    final",[448,2522,1869],{"class":480},[448,2524,484],{"class":462},[448,2526,979],{"class":480},[448,2528,862],{"class":462},[448,2530,1878],{"class":487},[448,2532,680],{"class":480},[448,2534,1883],{"class":487},[448,2536,628],{"class":480},[448,2538,1858],{"class":487},[448,2540,1890],{"class":480},[448,2542,463],{"class":462},[448,2544,2545,2548,2550,2553,2555,2557,2559,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579,2581,2583],{"class":450,"line":612},[448,2546,2547],{"class":1063},"    return",[448,2549,728],{"class":487},[448,2551,2552],{"class":480},"(id",[448,2554,500],{"class":462},[448,2556,1911],{"class":480},[448,2558,1914],{"class":458},[448,2560,1917],{"class":480},[448,2562,862],{"class":462},[448,2564,984],{"class":487},[448,2566,509],{"class":462},[448,2568,2170],{"class":480},[448,2570,500],{"class":462},[448,2572,1911],{"class":480},[448,2574,1935],{"class":458},[448,2576,1917],{"class":480},[448,2578,862],{"class":462},[448,2580,1942],{"class":487},[448,2582,538],{"class":480},[448,2584,463],{"class":462},[448,2586,2587],{"class":450,"line":774},[448,2588,2589],{"class":480},"  }\n",[448,2591,2592],{"class":450,"line":796},[448,2593,470],{"emptyLinePlaceholder":469},[448,2595,2596,2599,2601,2603,2605,2607,2609,2611,2613,2615,2618,2620,2622,2624,2626,2629,2631,2634],{"class":450,"line":803},[448,2597,2598],{"class":487},"  Map",[448,2600,680],{"class":480},[448,2602,1883],{"class":487},[448,2604,628],{"class":480},[448,2606,1858],{"class":487},[448,2608,1054],{"class":480},[448,2610,714],{"class":676},[448,2612,1060],{"class":480},[448,2614,706],{"class":462},[448,2616,2617],{"class":480}," {",[448,2619,1914],{"class":458},[448,2621,500],{"class":462},[448,2623,2443],{"class":480},[448,2625,509],{"class":462},[448,2627,2628],{"class":458}," 'name'",[448,2630,500],{"class":462},[448,2632,2633],{"class":480}," name}",[448,2635,463],{"class":462},[448,2637,2638],{"class":450,"line":847},[448,2639,1167],{"class":480},[438,2641,2644],{"className":440,"code":2642,"filename":2643,"language":443,"meta":444,"style":444},"import 'package:qora/qora.dart';\nimport 'user.dart';\n\nfinal userSerializer = QoraSerializer\u003CUser>(\n  toJson: (u) => u.toJson(),\n  fromJson: User.fromJson,\n);\n\nfinal userListSerializer = QoraSerializer\u003CList\u003CUser>>(\n  toJson: (list) => list.map((u) => u.toJson()).toList(),\n  fromJson: (json) => (json as List).map(User.fromJson).toList(),\n);\n","features/user/models/user_serializer.dart",[360,2645,2646,2654,2663,2667,2685,2708,2723,2729,2733,2754,2793,2832],{"__ignoreMap":444},[448,2647,2648,2650,2652],{"class":450,"line":451},[448,2649,455],{"class":454},[448,2651,459],{"class":458},[448,2653,463],{"class":462},[448,2655,2656,2658,2661],{"class":450,"line":466},[448,2657,455],{"class":454},[448,2659,2660],{"class":458}," 'user.dart'",[448,2662,463],{"class":462},[448,2664,2665],{"class":450,"line":473},[448,2666,470],{"emptyLinePlaceholder":469},[448,2668,2669,2671,2674,2676,2679,2681,2683],{"class":450,"line":494},[448,2670,477],{"class":476},[448,2672,2673],{"class":480}," userSerializer ",[448,2675,484],{"class":462},[448,2677,2678],{"class":487}," QoraSerializer",[448,2680,680],{"class":480},[448,2682,683],{"class":487},[448,2684,686],{"class":480},[448,2686,2687,2690,2692,2695,2697,2700,2702,2704,2706],{"class":450,"line":516},[448,2688,2689],{"class":480},"  toJson",[448,2691,500],{"class":462},[448,2693,2694],{"class":480}," (u) ",[448,2696,706],{"class":462},[448,2698,2699],{"class":480}," u",[448,2701,656],{"class":462},[448,2703,714],{"class":676},[448,2705,506],{"class":480},[448,2707,541],{"class":462},[448,2709,2710,2713,2715,2717,2719,2721],{"class":450,"line":544},[448,2711,2712],{"class":480},"  fromJson",[448,2714,500],{"class":462},[448,2716,728],{"class":487},[448,2718,656],{"class":462},[448,2720,733],{"class":480},[448,2722,541],{"class":462},[448,2724,2725,2727],{"class":450,"line":559},[448,2726,538],{"class":480},[448,2728,463],{"class":462},[448,2730,2731],{"class":450,"line":590},[448,2732,470],{"emptyLinePlaceholder":469},[448,2734,2735,2737,2740,2742,2744,2746,2748,2750,2752],{"class":450,"line":604},[448,2736,477],{"class":476},[448,2738,2739],{"class":480}," userListSerializer ",[448,2741,484],{"class":462},[448,2743,2678],{"class":487},[448,2745,680],{"class":480},[448,2747,785],{"class":487},[448,2749,680],{"class":480},[448,2751,683],{"class":487},[448,2753,793],{"class":480},[448,2755,2756,2758,2760,2763,2765,2768,2770,2772,2775,2777,2779,2781,2783,2785,2787,2789,2791],{"class":450,"line":612},[448,2757,2689],{"class":480},[448,2759,500],{"class":462},[448,2761,2762],{"class":480}," (list) ",[448,2764,706],{"class":462},[448,2766,2767],{"class":480}," list",[448,2769,656],{"class":462},[448,2771,820],{"class":676},[448,2773,2774],{"class":480},"((u) ",[448,2776,706],{"class":462},[448,2778,2699],{"class":480},[448,2780,656],{"class":462},[448,2782,714],{"class":676},[448,2784,835],{"class":480},[448,2786,656],{"class":462},[448,2788,840],{"class":676},[448,2790,506],{"class":480},[448,2792,541],{"class":462},[448,2794,2795,2797,2799,2801,2803,2805,2807,2809,2811,2813,2815,2817,2819,2821,2824,2826,2828,2830],{"class":450,"line":774},[448,2796,2712],{"class":480},[448,2798,500],{"class":462},[448,2800,854],{"class":480},[448,2802,706],{"class":462},[448,2804,859],{"class":480},[448,2806,862],{"class":462},[448,2808,865],{"class":487},[448,2810,538],{"class":480},[448,2812,656],{"class":462},[448,2814,820],{"class":676},[448,2816,1136],{"class":480},[448,2818,683],{"class":487},[448,2820,656],{"class":462},[448,2822,2823],{"class":480},"fromJson)",[448,2825,656],{"class":462},[448,2827,840],{"class":676},[448,2829,506],{"class":480},[448,2831,541],{"class":462},[448,2833,2834,2836],{"class":450,"line":796},[448,2835,538],{"class":480},[448,2837,463],{"class":462},[348,2839,2840],{},"Ce pattern présente trois avantages :",[1423,2842,2843,2849,2859],{},[1426,2844,2845,2848],{},[352,2846,2847],{},"Modèle portable"," : réutilisable avec un backend Dart, d'autres packages ou un moteur de cache différent sans traîner une dépendance Qora",[1426,2850,2851,2854,2855,2858],{},[352,2852,2853],{},"Tree-shaking"," : si ",[360,2856,2857],{},"user_serializer.dart"," n'est jamais importé, il n'est pas compilé",[1426,2860,2861,2864,2865,628,2868,2871],{},[352,2862,2863],{},"Multi-format"," : possibilité d'ajouter ",[360,2866,2867],{},"user_firestore.dart",[360,2869,2870],{},"user_supabase.dart"," etc. sans toucher au modèle",[421,2873],{},[428,2875,2877],{"id":2876},"fonction-denregistrement-centralisée","Fonction d'enregistrement centralisée",[348,2879,2880,2881,2883],{},"Collecter tous les sérialiseurs satellites dans un fichier : ",[360,2882,442],{}," appelle une seule fonction :",[438,2885,2888],{"className":440,"code":2886,"filename":2887,"language":443,"meta":444,"style":444},"import 'package:qora/qora.dart';\nimport '../../features/user/models/user_serializer.dart';\nimport '../../features/post/models/post_serializer.dart';\nimport '../../features/product/models/product_serializer.dart';\n\nvoid registerAllSerializers(PersistQoraClient client) {\n  client.registerSerializer\u003CUser>(userSerializer, name: 'User');\n  client.registerSerializer\u003CList\u003CUser>>(userListSerializer, name: 'List\u003CUser>');\n  client.registerSerializer\u003CPost>(postSerializer, name: 'Post');\n  client.registerSerializer\u003CProduct>(productSerializer, name: 'Product');\n}\n","lib/core/persistence/serializers.dart",[360,2889,2890,2898,2907,2916,2925,2929,2943,2970,3002,3030,3058],{"__ignoreMap":444},[448,2891,2892,2894,2896],{"class":450,"line":451},[448,2893,455],{"class":454},[448,2895,459],{"class":458},[448,2897,463],{"class":462},[448,2899,2900,2902,2905],{"class":450,"line":466},[448,2901,455],{"class":454},[448,2903,2904],{"class":458}," '../../features/user/models/user_serializer.dart'",[448,2906,463],{"class":462},[448,2908,2909,2911,2914],{"class":450,"line":473},[448,2910,455],{"class":454},[448,2912,2913],{"class":458}," '../../features/post/models/post_serializer.dart'",[448,2915,463],{"class":462},[448,2917,2918,2920,2923],{"class":450,"line":494},[448,2919,455],{"class":454},[448,2921,2922],{"class":458}," '../../features/product/models/product_serializer.dart'",[448,2924,463],{"class":462},[448,2926,2927],{"class":450,"line":516},[448,2928,470],{"emptyLinePlaceholder":469},[448,2930,2931,2933,2936,2938,2940],{"class":450,"line":544},[448,2932,1051],{"class":476},[448,2934,2935],{"class":676}," registerAllSerializers",[448,2937,1136],{"class":480},[448,2939,229],{"class":487},[448,2941,2942],{"class":480}," client) {\n",[448,2944,2945,2948,2950,2952,2954,2956,2958,2960,2962,2964,2966,2968],{"class":450,"line":559},[448,2946,2947],{"class":480},"  client",[448,2949,656],{"class":462},[448,2951,677],{"class":676},[448,2953,680],{"class":480},[448,2955,683],{"class":487},[448,2957,2165],{"class":480},[448,2959,509],{"class":462},[448,2961,2170],{"class":480},[448,2963,500],{"class":462},[448,2965,751],{"class":458},[448,2967,538],{"class":480},[448,2969,463],{"class":462},[448,2971,2972,2974,2976,2978,2980,2982,2984,2986,2989,2991,2993,2995,2998,3000],{"class":450,"line":590},[448,2973,2947],{"class":480},[448,2975,656],{"class":462},[448,2977,677],{"class":676},[448,2979,680],{"class":480},[448,2981,785],{"class":487},[448,2983,680],{"class":480},[448,2985,683],{"class":487},[448,2987,2988],{"class":480},">>(userListSerializer",[448,2990,509],{"class":462},[448,2992,2170],{"class":480},[448,2994,500],{"class":462},[448,2996,2997],{"class":458}," 'List\u003CUser>'",[448,2999,538],{"class":480},[448,3001,463],{"class":462},[448,3003,3004,3006,3008,3010,3012,3014,3017,3019,3021,3023,3026,3028],{"class":450,"line":604},[448,3005,2947],{"class":480},[448,3007,656],{"class":462},[448,3009,677],{"class":676},[448,3011,680],{"class":480},[448,3013,790],{"class":487},[448,3015,3016],{"class":480},">(postSerializer",[448,3018,509],{"class":462},[448,3020,2170],{"class":480},[448,3022,500],{"class":462},[448,3024,3025],{"class":458}," 'Post'",[448,3027,538],{"class":480},[448,3029,463],{"class":462},[448,3031,3032,3034,3036,3038,3040,3042,3045,3047,3049,3051,3054,3056],{"class":450,"line":612},[448,3033,2947],{"class":480},[448,3035,656],{"class":462},[448,3037,677],{"class":676},[448,3039,680],{"class":480},[448,3041,2362],{"class":487},[448,3043,3044],{"class":480},">(productSerializer",[448,3046,509],{"class":462},[448,3048,2170],{"class":480},[448,3050,500],{"class":462},[448,3052,3053],{"class":458}," 'Product'",[448,3055,538],{"class":480},[448,3057,463],{"class":462},[448,3059,3060],{"class":450,"line":774},[448,3061,1167],{"class":480},[438,3063,3065],{"className":440,"code":3064,"filename":442,"language":443,"meta":444,"style":444},"registerAllSerializers(client); // ← appel unique, quel que soit le nombre de modèles\nawait client.hydrate();\nrunApp(...);\n",[360,3066,3067,3080,3094],{"__ignoreMap":444},[448,3068,3069,3072,3075,3077],{"class":450,"line":451},[448,3070,3071],{"class":676},"registerAllSerializers",[448,3073,3074],{"class":480},"(client)",[448,3076,1103],{"class":462},[448,3078,3079],{"class":512}," // ← appel unique, quel que soit le nombre de modèles\n",[448,3081,3082,3084,3086,3088,3090,3092],{"class":450,"line":466},[448,3083,1680],{"class":1063},[448,3085,1113],{"class":480},[448,3087,656],{"class":462},[448,3089,655],{"class":676},[448,3091,506],{"class":480},[448,3093,463],{"class":462},[448,3095,3096,3098,3100,3102,3104],{"class":450,"line":473},[448,3097,1035],{"class":676},[448,3099,1136],{"class":480},[448,3101,2304],{"class":462},[448,3103,538],{"class":480},[448,3105,463],{"class":462},[348,3107,3108],{},"Ce fichier devient également la liste autoritaire de tous les types persistés : utile pour l'audit et les migrations.",[421,3110],{},[428,3112,3114,3115,3118],{"id":3113},"alternative-static-const-sur-la-classe-du-modèle","Alternative : ",[360,3116,3117],{},"static const"," sur la classe du modèle",[348,3120,3121,3122,3124,3125,3127],{},"Si le modèle vit entièrement dans l'application Flutter et que ",[360,3123,362],{}," est une dépendance principale sans plans de partage du modèle ailleurs, un sérialiseur ",[360,3126,3117],{}," sur la classe elle-même est acceptable et offre la meilleure autocomplétion :",[438,3129,3131],{"className":440,"code":3130,"filename":2418,"language":443,"meta":444,"style":444},"import 'package:qora/qora.dart';\n\nclass User {\n  // ...\n\n  static const serializer = QoraSerializer\u003CUser>(\n    toJson: _encode,\n    fromJson: User.fromJson,\n  );\n\n  static Map\u003CString, dynamic> _encode(User u) => u.toJson();\n}\n",[360,3132,3133,3141,3145,3153,3158,3162,3182,3193,3207,3213,3217,3255],{"__ignoreMap":444},[448,3134,3135,3137,3139],{"class":450,"line":451},[448,3136,455],{"class":454},[448,3138,459],{"class":458},[448,3140,463],{"class":462},[448,3142,3143],{"class":450,"line":466},[448,3144,470],{"emptyLinePlaceholder":469},[448,3146,3147,3149,3151],{"class":450,"line":473},[448,3148,2430],{"class":462},[448,3150,728],{"class":487},[448,3152,1067],{"class":480},[448,3154,3155],{"class":450,"line":494},[448,3156,3157],{"class":512},"  // ...\n",[448,3159,3160],{"class":450,"line":516},[448,3161,470],{"emptyLinePlaceholder":469},[448,3163,3164,3167,3169,3172,3174,3176,3178,3180],{"class":450,"line":544},[448,3165,3166],{"class":476},"  static",[448,3168,524],{"class":476},[448,3170,3171],{"class":480}," serializer ",[448,3173,484],{"class":462},[448,3175,2678],{"class":487},[448,3177,680],{"class":480},[448,3179,683],{"class":487},[448,3181,686],{"class":480},[448,3183,3184,3186,3188,3191],{"class":450,"line":559},[448,3185,698],{"class":480},[448,3187,500],{"class":462},[448,3189,3190],{"class":480}," _encode",[448,3192,541],{"class":462},[448,3194,3195,3197,3199,3201,3203,3205],{"class":450,"line":590},[448,3196,723],{"class":480},[448,3198,500],{"class":462},[448,3200,728],{"class":487},[448,3202,656],{"class":462},[448,3204,733],{"class":480},[448,3206,541],{"class":462},[448,3208,3209,3211],{"class":450,"line":604},[448,3210,607],{"class":480},[448,3212,463],{"class":462},[448,3214,3215],{"class":450,"line":612},[448,3216,470],{"emptyLinePlaceholder":469},[448,3218,3219,3221,3223,3225,3227,3229,3231,3233,3236,3238,3240,3243,3245,3247,3249,3251,3253],{"class":450,"line":774},[448,3220,3166],{"class":476},[448,3222,1878],{"class":487},[448,3224,680],{"class":480},[448,3226,1883],{"class":487},[448,3228,628],{"class":480},[448,3230,1858],{"class":487},[448,3232,1054],{"class":480},[448,3234,3235],{"class":676},"_encode",[448,3237,1136],{"class":480},[448,3239,683],{"class":487},[448,3241,3242],{"class":480}," u) ",[448,3244,706],{"class":462},[448,3246,2699],{"class":480},[448,3248,656],{"class":462},[448,3250,714],{"class":676},[448,3252,506],{"class":480},[448,3254,463],{"class":462},[448,3256,3257],{"class":450,"line":796},[448,3258,1167],{"class":480},[438,3260,3262],{"className":440,"code":3261,"filename":2887,"language":443,"meta":444,"style":444},"client.registerSerializer\u003CUser>(User.serializer, name: 'User');\n",[360,3263,3264],{"__ignoreMap":444},[448,3265,3266,3268,3270,3272,3274,3276,3278,3280,3282,3285,3287,3289,3291,3293,3295],{"class":450,"line":451},[448,3267,671],{"class":480},[448,3269,656],{"class":462},[448,3271,677],{"class":676},[448,3273,680],{"class":480},[448,3275,683],{"class":487},[448,3277,2301],{"class":480},[448,3279,683],{"class":487},[448,3281,656],{"class":462},[448,3283,3284],{"class":480},"serializer",[448,3286,509],{"class":462},[448,3288,2170],{"class":480},[448,3290,500],{"class":462},[448,3292,751],{"class":458},[448,3294,538],{"class":480},[448,3296,463],{"class":462},[348,3298,3299],{},"Cette approche est plus simple mais couple le modèle à Qora. L'éviter si le modèle est partagé entre packages ou avec un backend.",[3301,3302,3303],"blockquote",{},[348,3304,3305,3308,3309,3311,3312,3315,3316,3320,3321,3324,3325,3328,3329,3331],{},[352,3306,3307],{},"Note sur les extensions Dart :"," les membres ",[360,3310,3117],{}," définis dans un bloc ",[360,3313,3314],{},"extension"," Dart sont accédés via le ",[3317,3318,3319],"em",{},"nom de l'extension"," (",[360,3322,3323],{},"UserQoraExt.serializer","), pas via la classe (",[360,3326,3327],{},"User.serializer","). Pour la syntaxe ",[360,3330,3327],{},", le static doit être sur la classe elle-même, pas sur une extension.",[421,3333],{},[424,3335,3337],{"id":3336},"guide-de-décision","Guide de décision",[365,3339,3340,3350],{},[368,3341,3342],{},[371,3343,3344,3347],{},[374,3345,3346],{},"Contexte",[374,3348,3349],{},"Pattern",[381,3351,3352,3362,3373,3386],{},[371,3353,3354,3357],{},[386,3355,3356],{},"\u003C 10 modèles, petite application",[386,3358,3359,3360],{},"Inline dans ",[360,3361,442],{},[371,3363,3364,3367],{},[386,3365,3366],{},"Modèle partagé avec un backend ou d'autres packages",[386,3368,3369,3370,3372],{},"Satellite ",[360,3371,2398],{}," (obligatoire)",[371,3374,3375,3378],{},[386,3376,3377],{},"Architecture feature-first, 15+ modèles",[386,3379,3380,3382,3383],{},[360,3381,2398],{}," + ",[360,3384,3385],{},"registerAllSerializers()",[371,3387,3388,3391],{},[386,3389,3390],{},"Modèle Flutter uniquement, DX prioritaire",[386,3392,3393,3395],{},[360,3394,3117],{}," sur la classe",[421,3397],{},[424,3399,3401],{"id":3400},"adaptateurs-de-stockage","Adaptateurs de stockage",[348,3403,3404,3405,3408],{},"L'",[360,3406,3407],{},"InMemoryStorageAdapter"," intégré est utile pour les tests. Pour les applications réelles, utiliser l'un des packages d'adaptateurs externes :",[365,3410,3411,3421],{},[368,3412,3413],{},[371,3414,3415,3418],{},[374,3416,3417],{},"Package",[374,3419,3420],{},"Backend",[381,3422,3423,3436,3446,3456],{},[371,3424,3425,3430],{},[386,3426,3427],{},[360,3428,3429],{},"qora_storage_shared_prefs",[386,3431,3432,3435],{},[360,3433,3434],{},"SharedPreferences"," : simple, pas de configuration supplémentaire",[371,3437,3438,3443],{},[386,3439,3440],{},[360,3441,3442],{},"qora_storage_hive",[386,3444,3445],{},"Hive : rapide, pas de dépendances natives",[371,3447,3448,3453],{},[386,3449,3450],{},[360,3451,3452],{},"qora_storage_isar",[386,3454,3455],{},"Isar : réactif, format binaire efficace",[371,3457,3458,3463],{},[386,3459,3460],{},[360,3461,3462],{},"qora_storage_drift",[386,3464,3465],{},"Drift / SQLite : relationnel, adapté aux requêtes complexes",[348,3467,3468,3469,3471],{},"Tous les adaptateurs implémentent la même interface ",[360,3470,390],{}," : changer de backend nécessite de modifier une seule ligne.",[421,3473],{},[424,3475,3477],{"id":3476},"exemple-flutter-complet","Exemple Flutter complet",[438,3479,3481],{"className":440,"code":3480,"filename":442,"language":443,"meta":444,"style":444},"import 'package:flutter/material.dart';\nimport 'package:qora_flutter/qora_flutter.dart';\nimport 'package:qora/qora.dart';\n\nFuture\u003Cvoid> main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  final storage = InMemoryStorageAdapter(); // remplacer par HiveStorageAdapter()\n  await storage.init();\n\n  final client = PersistQoraClient(\n    storage: storage,\n    persistDuration: const Duration(days: 7),\n    config: const QoraClientConfig(\n      defaultOptions: QoraOptions(staleTime: Duration(minutes: 5)),\n    ),\n  );\n\n  client.registerSerializer\u003CUser>(\n    QoraSerializer(toJson: (u) => u.toJson(), fromJson: User.fromJson),\n    name: 'User',\n  );\n  client.registerSerializer\u003CList\u003CPost>>(\n    QoraSerializer(\n      toJson: (posts) => posts.map((p) => p.toJson()).toList(),\n      fromJson: (json)  => (json as List).map((e) => Post.fromJson(e)).toList(),\n    ),\n    name: 'List\u003CPost>',\n  );\n\n  await client.hydrate(); // chauffer le cache depuis le disque\n\n  runApp(\n    QoraScope(\n      client: client,\n      lifecycleManager: FlutterLifecycleManager(qoraClient: client),\n      child: const MyApp(),\n    ),\n  );\n}\n",[360,3482,3483,3492,3501,3509,3513,3531,3543,3547,3565,3579,3583,3595,3606,3627,3640,3665,3671,3677,3681,3695,3730,3740,3746,3764,3771,3809,3854,3861,3872,3879,3884,3902,3907,3914,3921,3932,3953,3968,3975,3982],{"__ignoreMap":444},[448,3484,3485,3487,3490],{"class":450,"line":451},[448,3486,455],{"class":454},[448,3488,3489],{"class":458}," 'package:flutter/material.dart'",[448,3491,463],{"class":462},[448,3493,3494,3496,3499],{"class":450,"line":466},[448,3495,455],{"class":454},[448,3497,3498],{"class":458}," 'package:qora_flutter/qora_flutter.dart'",[448,3500,463],{"class":462},[448,3502,3503,3505,3507],{"class":450,"line":473},[448,3504,455],{"class":454},[448,3506,459],{"class":458},[448,3508,463],{"class":462},[448,3510,3511],{"class":450,"line":494},[448,3512,470],{"emptyLinePlaceholder":469},[448,3514,3515,3517,3519,3521,3523,3525,3527,3529],{"class":450,"line":516},[448,3516,1046],{"class":487},[448,3518,680],{"class":480},[448,3520,1051],{"class":476},[448,3522,1054],{"class":480},[448,3524,1057],{"class":676},[448,3526,1060],{"class":480},[448,3528,1064],{"class":1063},[448,3530,1067],{"class":480},[448,3532,3533,3535,3537,3539,3541],{"class":450,"line":544},[448,3534,1072],{"class":487},[448,3536,656],{"class":462},[448,3538,1077],{"class":676},[448,3540,506],{"class":480},[448,3542,463],{"class":462},[448,3544,3545],{"class":450,"line":559},[448,3546,470],{"emptyLinePlaceholder":469},[448,3548,3549,3551,3554,3556,3558,3560,3562],{"class":450,"line":590},[448,3550,1866],{"class":476},[448,3552,3553],{"class":480}," storage ",[448,3555,484],{"class":462},[448,3557,503],{"class":487},[448,3559,506],{"class":480},[448,3561,1103],{"class":462},[448,3563,3564],{"class":512}," // remplacer par HiveStorageAdapter()\n",[448,3566,3567,3569,3571,3573,3575,3577],{"class":450,"line":604},[448,3568,1090],{"class":1063},[448,3570,1093],{"class":480},[448,3572,656],{"class":462},[448,3574,1098],{"class":676},[448,3576,506],{"class":480},[448,3578,463],{"class":462},[448,3580,3581],{"class":450,"line":612},[448,3582,470],{"emptyLinePlaceholder":469},[448,3584,3585,3587,3589,3591,3593],{"class":450,"line":774},[448,3586,1866],{"class":476},[448,3588,481],{"class":480},[448,3590,484],{"class":462},[448,3592,488],{"class":487},[448,3594,491],{"class":480},[448,3596,3597,3600,3602,3604],{"class":450,"line":796},[448,3598,3599],{"class":480},"    storage",[448,3601,500],{"class":462},[448,3603,1093],{"class":480},[448,3605,541],{"class":462},[448,3607,3608,3611,3613,3615,3617,3619,3621,3623,3625],{"class":450,"line":803},[448,3609,3610],{"class":480},"    persistDuration",[448,3612,500],{"class":462},[448,3614,524],{"class":476},[448,3616,527],{"class":487},[448,3618,530],{"class":480},[448,3620,500],{"class":462},[448,3622,535],{"class":454},[448,3624,538],{"class":480},[448,3626,541],{"class":462},[448,3628,3629,3632,3634,3636,3638],{"class":450,"line":847},[448,3630,3631],{"class":480},"    config",[448,3633,500],{"class":462},[448,3635,524],{"class":476},[448,3637,554],{"class":487},[448,3639,491],{"class":480},[448,3641,3642,3645,3647,3649,3651,3653,3655,3657,3659,3661,3663],{"class":450,"line":897},[448,3643,3644],{"class":480},"      defaultOptions",[448,3646,500],{"class":462},[448,3648,567],{"class":487},[448,3650,570],{"class":480},[448,3652,500],{"class":462},[448,3654,527],{"class":487},[448,3656,577],{"class":480},[448,3658,500],{"class":462},[448,3660,582],{"class":454},[448,3662,585],{"class":480},[448,3664,541],{"class":462},[448,3666,3667,3669],{"class":450,"line":904},[448,3668,1383],{"class":480},[448,3670,541],{"class":462},[448,3672,3673,3675],{"class":450,"line":916},[448,3674,607],{"class":480},[448,3676,463],{"class":462},[448,3678,3679],{"class":450,"line":923},[448,3680,470],{"emptyLinePlaceholder":469},[448,3682,3683,3685,3687,3689,3691,3693],{"class":450,"line":928},[448,3684,2947],{"class":480},[448,3686,656],{"class":462},[448,3688,677],{"class":676},[448,3690,680],{"class":480},[448,3692,683],{"class":487},[448,3694,686],{"class":480},[448,3696,3697,3700,3702,3704,3706,3708,3710,3712,3714,3716,3718,3720,3722,3724,3726,3728],{"class":450,"line":934},[448,3698,3699],{"class":487},"    QoraSerializer",[448,3701,955],{"class":480},[448,3703,500],{"class":462},[448,3705,2694],{"class":480},[448,3707,706],{"class":462},[448,3709,2699],{"class":480},[448,3711,656],{"class":462},[448,3713,714],{"class":676},[448,3715,506],{"class":480},[448,3717,509],{"class":462},[448,3719,970],{"class":480},[448,3721,500],{"class":462},[448,3723,728],{"class":487},[448,3725,656],{"class":462},[448,3727,2823],{"class":480},[448,3729,541],{"class":462},[448,3731,3732,3734,3736,3738],{"class":450,"line":950},[448,3733,1928],{"class":480},[448,3735,500],{"class":462},[448,3737,751],{"class":458},[448,3739,541],{"class":462},[448,3741,3742,3744],{"class":450,"line":991},[448,3743,607],{"class":480},[448,3745,463],{"class":462},[448,3747,3748,3750,3752,3754,3756,3758,3760,3762],{"class":450,"line":1003},[448,3749,2947],{"class":480},[448,3751,656],{"class":462},[448,3753,677],{"class":676},[448,3755,680],{"class":480},[448,3757,785],{"class":487},[448,3759,680],{"class":480},[448,3761,790],{"class":487},[448,3763,793],{"class":480},[448,3765,3767,3769],{"class":450,"line":3766},24,[448,3768,3699],{"class":487},[448,3770,491],{"class":480},[448,3772,3774,3777,3779,3781,3783,3785,3787,3789,3791,3793,3795,3797,3799,3801,3803,3805,3807],{"class":450,"line":3773},25,[448,3775,3776],{"class":480},"      toJson",[448,3778,500],{"class":462},[448,3780,810],{"class":480},[448,3782,706],{"class":462},[448,3784,815],{"class":480},[448,3786,656],{"class":462},[448,3788,820],{"class":676},[448,3790,823],{"class":480},[448,3792,706],{"class":462},[448,3794,828],{"class":480},[448,3796,656],{"class":462},[448,3798,714],{"class":676},[448,3800,835],{"class":480},[448,3802,656],{"class":462},[448,3804,840],{"class":676},[448,3806,506],{"class":480},[448,3808,541],{"class":462},[448,3810,3812,3815,3817,3820,3822,3824,3826,3828,3830,3832,3834,3836,3838,3840,3842,3844,3846,3848,3850,3852],{"class":450,"line":3811},26,[448,3813,3814],{"class":480},"      fromJson",[448,3816,500],{"class":462},[448,3818,3819],{"class":480}," (json)  ",[448,3821,706],{"class":462},[448,3823,859],{"class":480},[448,3825,862],{"class":462},[448,3827,865],{"class":487},[448,3829,538],{"class":480},[448,3831,656],{"class":462},[448,3833,820],{"class":676},[448,3835,874],{"class":480},[448,3837,706],{"class":462},[448,3839,879],{"class":487},[448,3841,656],{"class":462},[448,3843,733],{"class":676},[448,3845,886],{"class":480},[448,3847,656],{"class":462},[448,3849,840],{"class":676},[448,3851,506],{"class":480},[448,3853,541],{"class":462},[448,3855,3857,3859],{"class":450,"line":3856},27,[448,3858,1383],{"class":480},[448,3860,541],{"class":462},[448,3862,3864,3866,3868,3870],{"class":450,"line":3863},28,[448,3865,1928],{"class":480},[448,3867,500],{"class":462},[448,3869,911],{"class":458},[448,3871,541],{"class":462},[448,3873,3875,3877],{"class":450,"line":3874},29,[448,3876,607],{"class":480},[448,3878,463],{"class":462},[448,3880,3882],{"class":450,"line":3881},30,[448,3883,470],{"emptyLinePlaceholder":469},[448,3885,3887,3889,3891,3893,3895,3897,3899],{"class":450,"line":3886},31,[448,3888,1090],{"class":1063},[448,3890,1113],{"class":480},[448,3892,656],{"class":462},[448,3894,655],{"class":676},[448,3896,506],{"class":480},[448,3898,1103],{"class":462},[448,3900,3901],{"class":512}," // chauffer le cache depuis le disque\n",[448,3903,3905],{"class":450,"line":3904},32,[448,3906,470],{"emptyLinePlaceholder":469},[448,3908,3910,3912],{"class":450,"line":3909},33,[448,3911,1133],{"class":676},[448,3913,491],{"class":480},[448,3915,3917,3919],{"class":450,"line":3916},34,[448,3918,1221],{"class":487},[448,3920,491],{"class":480},[448,3922,3924,3926,3928,3930],{"class":450,"line":3923},35,[448,3925,1228],{"class":480},[448,3927,500],{"class":462},[448,3929,1113],{"class":480},[448,3931,541],{"class":462},[448,3933,3935,3938,3940,3943,3946,3948,3951],{"class":450,"line":3934},36,[448,3936,3937],{"class":480},"      lifecycleManager",[448,3939,500],{"class":462},[448,3941,3942],{"class":487}," FlutterLifecycleManager",[448,3944,3945],{"class":480},"(qoraClient",[448,3947,500],{"class":462},[448,3949,3950],{"class":480}," client)",[448,3952,541],{"class":462},[448,3954,3956,3958,3960,3962,3964,3966],{"class":450,"line":3955},37,[448,3957,1239],{"class":480},[448,3959,500],{"class":462},[448,3961,524],{"class":476},[448,3963,1157],{"class":487},[448,3965,506],{"class":480},[448,3967,541],{"class":462},[448,3969,3971,3973],{"class":450,"line":3970},38,[448,3972,1383],{"class":480},[448,3974,541],{"class":462},[448,3976,3978,3980],{"class":450,"line":3977},39,[448,3979,607],{"class":480},[448,3981,463],{"class":462},[448,3983,3985],{"class":450,"line":3984},40,[448,3986,1167],{"class":480},[438,3988,3991],{"className":440,"code":3989,"filename":3990,"language":443,"meta":444,"style":444},"class PostsScreen extends StatelessWidget {\n  const PostsScreen({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return QoraBuilder\u003CList\u003CPost>>(\n      queryKey: ['posts'],\n      fetcher: PostApi.fetchAll,\n      // Si les données hydratées existent : affichées instantanément, pas de spinner.\n      // Si périmées : affichées immédiatement pendant le refetch en arrière-plan.\n      builder: (context, state, fetchStatus) => switch (state) {\n        Initial() => const SizedBox.shrink(),\n        Loading(previousData: null) => const CircularProgressIndicator(),\n        Loading(:final previousData?) => PostList(previousData),\n        Success(:final data) => PostList(data),\n        Failure(:final error, previousData: null) => ErrorView('$error'),\n        Failure(:final error, :final previousData?) => Column(children: [\n            PostList(previousData),\n            ErrorBanner('Rafraîchissement échoué : $error'),\n          ]),\n      },\n    );\n  }\n}\n","screens/posts_screen.dart",[360,3992,3993,4008,4024,4028,4033,4049,4066,4084,4101,4106,4111,4138,4161,4188,4215,4238,4285,4322,4331,4351,4358,4365,4371,4375],{"__ignoreMap":444},[448,3994,3995,3997,4000,4003,4006],{"class":450,"line":451},[448,3996,2430],{"class":462},[448,3998,3999],{"class":487}," PostsScreen",[448,4001,4002],{"class":462}," extends",[448,4004,4005],{"class":487}," StatelessWidget",[448,4007,1067],{"class":480},[448,4009,4010,4012,4014,4017,4019,4022],{"class":450,"line":466},[448,4011,2464],{"class":476},[448,4013,3999],{"class":487},[448,4015,4016],{"class":480},"({super",[448,4018,656],{"class":462},[448,4020,4021],{"class":480},"key})",[448,4023,463],{"class":462},[448,4025,4026],{"class":450,"line":473},[448,4027,470],{"emptyLinePlaceholder":469},[448,4029,4030],{"class":450,"line":494},[448,4031,4032],{"class":476},"  @override\n",[448,4034,4035,4038,4041,4043,4046],{"class":450,"line":516},[448,4036,4037],{"class":487},"  Widget",[448,4039,4040],{"class":676}," build",[448,4042,1136],{"class":480},[448,4044,4045],{"class":487},"BuildContext",[448,4047,4048],{"class":480}," context) {\n",[448,4050,4051,4053,4056,4058,4060,4062,4064],{"class":450,"line":544},[448,4052,2547],{"class":1063},[448,4054,4055],{"class":487}," QoraBuilder",[448,4057,680],{"class":480},[448,4059,785],{"class":487},[448,4061,680],{"class":480},[448,4063,790],{"class":487},[448,4065,793],{"class":480},[448,4067,4068,4071,4073,4076,4079,4082],{"class":450,"line":559},[448,4069,4070],{"class":480},"      queryKey",[448,4072,500],{"class":462},[448,4074,4075],{"class":480}," [",[448,4077,4078],{"class":458},"'posts'",[448,4080,4081],{"class":480},"]",[448,4083,541],{"class":462},[448,4085,4086,4089,4091,4094,4096,4099],{"class":450,"line":590},[448,4087,4088],{"class":480},"      fetcher",[448,4090,500],{"class":462},[448,4092,4093],{"class":487}," PostApi",[448,4095,656],{"class":462},[448,4097,4098],{"class":480},"fetchAll",[448,4100,541],{"class":462},[448,4102,4103],{"class":450,"line":604},[448,4104,4105],{"class":512},"      // Si les données hydratées existent : affichées instantanément, pas de spinner.\n",[448,4107,4108],{"class":450,"line":612},[448,4109,4110],{"class":512},"      // Si périmées : affichées immédiatement pendant le refetch en arrière-plan.\n",[448,4112,4113,4116,4118,4120,4122,4125,4127,4130,4132,4135],{"class":450,"line":774},[448,4114,4115],{"class":480},"      builder",[448,4117,500],{"class":462},[448,4119,1295],{"class":480},[448,4121,509],{"class":462},[448,4123,4124],{"class":480}," state",[448,4126,509],{"class":462},[448,4128,4129],{"class":480}," fetchStatus) ",[448,4131,706],{"class":462},[448,4133,4134],{"class":1063}," switch",[448,4136,4137],{"class":480}," (state) {\n",[448,4139,4140,4143,4145,4147,4149,4152,4154,4157,4159],{"class":450,"line":796},[448,4141,4142],{"class":487},"        Initial",[448,4144,1060],{"class":480},[448,4146,706],{"class":462},[448,4148,524],{"class":476},[448,4150,4151],{"class":487}," SizedBox",[448,4153,656],{"class":462},[448,4155,4156],{"class":676},"shrink",[448,4158,506],{"class":480},[448,4160,541],{"class":462},[448,4162,4163,4166,4169,4171,4174,4177,4179,4181,4184,4186],{"class":450,"line":803},[448,4164,4165],{"class":487},"        Loading",[448,4167,4168],{"class":480},"(previousData",[448,4170,500],{"class":462},[448,4172,4173],{"class":462}," null",[448,4175,4176],{"class":480},") ",[448,4178,706],{"class":462},[448,4180,524],{"class":476},[448,4182,4183],{"class":487}," CircularProgressIndicator",[448,4185,506],{"class":480},[448,4187,541],{"class":462},[448,4189,4190,4192,4194,4196,4198,4201,4203,4205,4207,4210,4213],{"class":450,"line":847},[448,4191,4165],{"class":487},[448,4193,1136],{"class":480},[448,4195,500],{"class":462},[448,4197,477],{"class":476},[448,4199,4200],{"class":480}," previousData",[448,4202,1970],{"class":462},[448,4204,4176],{"class":480},[448,4206,706],{"class":462},[448,4208,4209],{"class":487}," PostList",[448,4211,4212],{"class":480},"(previousData)",[448,4214,541],{"class":462},[448,4216,4217,4220,4222,4224,4226,4229,4231,4233,4236],{"class":450,"line":897},[448,4218,4219],{"class":487},"        Success",[448,4221,1136],{"class":480},[448,4223,500],{"class":462},[448,4225,477],{"class":476},[448,4227,4228],{"class":480}," data) ",[448,4230,706],{"class":462},[448,4232,4209],{"class":487},[448,4234,4235],{"class":480},"(data)",[448,4237,541],{"class":462},[448,4239,4240,4243,4245,4247,4249,4252,4254,4256,4258,4260,4262,4264,4267,4269,4272,4275,4279,4281,4283],{"class":450,"line":904},[448,4241,4242],{"class":487},"        Failure",[448,4244,1136],{"class":480},[448,4246,500],{"class":462},[448,4248,477],{"class":476},[448,4250,4251],{"class":480}," error",[448,4253,509],{"class":462},[448,4255,4200],{"class":480},[448,4257,500],{"class":462},[448,4259,4173],{"class":462},[448,4261,4176],{"class":480},[448,4263,706],{"class":462},[448,4265,4266],{"class":487}," ErrorView",[448,4268,1136],{"class":480},[448,4270,4271],{"class":458},"'",[448,4273,4274],{"class":480},"$",[448,4276,4278],{"class":4277},"sHdIc","error",[448,4280,4271],{"class":458},[448,4282,538],{"class":480},[448,4284,541],{"class":462},[448,4286,4287,4289,4291,4293,4295,4297,4299,4301,4303,4305,4307,4309,4311,4314,4317,4319],{"class":450,"line":916},[448,4288,4242],{"class":487},[448,4290,1136],{"class":480},[448,4292,500],{"class":462},[448,4294,477],{"class":476},[448,4296,4251],{"class":480},[448,4298,509],{"class":462},[448,4300,363],{"class":462},[448,4302,477],{"class":476},[448,4304,4200],{"class":480},[448,4306,1970],{"class":462},[448,4308,4176],{"class":480},[448,4310,706],{"class":462},[448,4312,4313],{"class":487}," Column",[448,4315,4316],{"class":480},"(children",[448,4318,500],{"class":462},[448,4320,4321],{"class":480}," [\n",[448,4323,4324,4327,4329],{"class":450,"line":923},[448,4325,4326],{"class":487},"            PostList",[448,4328,4212],{"class":480},[448,4330,541],{"class":462},[448,4332,4333,4336,4338,4341,4343,4345,4347,4349],{"class":450,"line":928},[448,4334,4335],{"class":487},"            ErrorBanner",[448,4337,1136],{"class":480},[448,4339,4340],{"class":458},"'Rafraîchissement échoué : ",[448,4342,4274],{"class":480},[448,4344,4278],{"class":4277},[448,4346,4271],{"class":458},[448,4348,538],{"class":480},[448,4350,541],{"class":462},[448,4352,4353,4356],{"class":450,"line":934},[448,4354,4355],{"class":480},"          ])",[448,4357,541],{"class":462},[448,4359,4360,4363],{"class":450,"line":950},[448,4361,4362],{"class":480},"      }",[448,4364,541],{"class":462},[448,4366,4367,4369],{"class":450,"line":991},[448,4368,1383],{"class":480},[448,4370,463],{"class":462},[448,4372,4373],{"class":450,"line":1003},[448,4374,2589],{"class":480},[448,4376,4377],{"class":450,"line":3766},[448,4378,1167],{"class":480},[348,4380,4381,4382,4385],{},"Lors du premier lancement, ",[360,4383,4384],{},"PostsScreen"," affiche un spinner et récupère depuis le réseau. Lors de chaque lancement suivant, la liste de posts apparaît instantanément depuis le disque pendant que Qora revalide en arrière-plan si les données sont périmées.",[421,4387],{},[424,4389,4391],{"id":4390},"déconnexion-changement-de-compte","Déconnexion / changement de compte",[348,4393,4394],{},"Lorsque l'utilisateur se déconnecte, vous pouvez choisir de vider uniquement la mémoire (pour garder le stockage pour la session suivante) ou de vider les deux :",[438,4396,4399],{"className":440,"code":4397,"filename":4398,"language":443,"meta":444,"style":444},"Future\u003Cvoid> logout() async {\n  await api.logout();\n\n  // Vide le cache + le stockage de façon atomique (réinitialisation complète)\n  client.clear(); \n\n  // Ou bien, vider seulement la mémoire et conserver le stockage persistant\n  // client.clearCache();\n\n  Navigator.pushReplacementNamed(context, '/login');\n}\n","auth_service.dart",[360,4400,4401,4420,4435,4439,4444,4460,4464,4469,4474,4478,4500],{"__ignoreMap":444},[448,4402,4403,4405,4407,4409,4411,4414,4416,4418],{"class":450,"line":451},[448,4404,1046],{"class":487},[448,4406,680],{"class":480},[448,4408,1051],{"class":476},[448,4410,1054],{"class":480},[448,4412,4413],{"class":676},"logout",[448,4415,1060],{"class":480},[448,4417,1064],{"class":1063},[448,4419,1067],{"class":480},[448,4421,4422,4424,4427,4429,4431,4433],{"class":450,"line":466},[448,4423,1090],{"class":1063},[448,4425,4426],{"class":480}," api",[448,4428,656],{"class":462},[448,4430,4413],{"class":676},[448,4432,506],{"class":480},[448,4434,463],{"class":462},[448,4436,4437],{"class":450,"line":473},[448,4438,470],{"emptyLinePlaceholder":469},[448,4440,4441],{"class":450,"line":494},[448,4442,4443],{"class":512},"  // Vide le cache + le stockage de façon atomique (réinitialisation complète)\n",[448,4445,4446,4448,4450,4453,4455,4457],{"class":450,"line":516},[448,4447,2947],{"class":480},[448,4449,656],{"class":462},[448,4451,4452],{"class":676},"clear",[448,4454,506],{"class":480},[448,4456,1103],{"class":462},[448,4458,4459],{"class":480}," \n",[448,4461,4462],{"class":450,"line":544},[448,4463,470],{"emptyLinePlaceholder":469},[448,4465,4466],{"class":450,"line":559},[448,4467,4468],{"class":512},"  // Ou bien, vider seulement la mémoire et conserver le stockage persistant\n",[448,4470,4471],{"class":450,"line":590},[448,4472,4473],{"class":512},"  // client.clearCache();\n",[448,4475,4476],{"class":450,"line":604},[448,4477,470],{"emptyLinePlaceholder":469},[448,4479,4480,4483,4485,4488,4491,4493,4496,4498],{"class":450,"line":612},[448,4481,4482],{"class":487},"  Navigator",[448,4484,656],{"class":462},[448,4486,4487],{"class":676},"pushReplacementNamed",[448,4489,4490],{"class":480},"(context",[448,4492,509],{"class":462},[448,4494,4495],{"class":458}," '/login'",[448,4497,538],{"class":480},[448,4499,463],{"class":462},[448,4501,4502],{"class":450,"line":774},[448,4503,1167],{"class":480},[421,4505],{},[424,4507,4509],{"id":4508},"étapes-suivantes","Étapes suivantes",[1423,4511,4512,4519,4525],{},[1426,4513,4514,4518],{},[1015,4515,4517],{"href":4516},"../api-reference/persist-qora-client","API PersistQoraClient"," : référence complète des méthodes",[1426,4520,4521,4524],{},[1015,4522,148],{"href":4523},"./optimistic-updates"," : combiner la persistance avec une UI optimiste",[1426,4526,4527,4531],{},[1015,4528,4530],{"href":4529},"../api-reference/qora-client","API QoraClient"," : référence du client de base",[4533,4534,4535],"style",{},"html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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 .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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .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}",{"title":444,"searchDepth":466,"depth":466,"links":4537},[4538,4544,4549,4555,4556,4557,4565,4566,4567,4568,4569],{"id":426,"depth":466,"text":98,"children":4539},[4540,4542,4543],{"id":430,"depth":473,"text":4541},"1. Remplacer QoraClient par PersistQoraClient",{"id":641,"depth":473,"text":642},{"id":1024,"depth":473,"text":1025},{"id":1410,"depth":466,"text":1411,"children":4545},[4546,4547,4548],{"id":1414,"depth":473,"text":1415},{"id":1451,"depth":473,"text":1452},{"id":1495,"depth":473,"text":1496},{"id":1559,"depth":466,"text":1560,"children":4550},[4551,4552,4553,4554],{"id":1563,"depth":473,"text":1564},{"id":1654,"depth":473,"text":1655},{"id":1729,"depth":473,"text":1730},{"id":1802,"depth":473,"text":1803},{"id":2012,"depth":466,"text":2013},{"id":2106,"depth":466,"text":2107},{"id":2255,"depth":466,"text":1018,"children":4558},[4559,4560,4562,4563],{"id":2269,"depth":473,"text":2270},{"id":2394,"depth":473,"text":4561},"Recommandé : fichiers _serializer.dart satellites",{"id":2876,"depth":473,"text":2877},{"id":3113,"depth":473,"text":4564},"Alternative : static const sur la classe du modèle",{"id":3336,"depth":466,"text":3337},{"id":3400,"depth":466,"text":3401},{"id":3476,"depth":466,"text":3477},{"id":4390,"depth":466,"text":4391},{"id":4508,"depth":466,"text":4509},"Persister les résultats des requêtes sur disque et les restaurer au démarrage : donnant à l'application une couche de données instantanée qui fonctionne entièrement hors-ligne.","md",null,{},{"icon":156},{"title":153,"description":4570},"lGKRfPjR-jebU-H_5nLEcwrZryMPBIcAjx3YJRN3wx4",[4578,4580],{"title":148,"path":149,"stem":150,"description":4579,"icon":151,"children":-1},"Mettre à jour l'UI instantanément pendant que les mutations sont en cours, et effectuer un rollback sécurisé en cas d'échec : sans bibliothèques supplémentaires.",{"title":158,"path":159,"stem":160,"description":4581,"icon":161,"children":-1},"Gérer le mode hors-ligne, les requêtes en pause, la file de mutations hors-ligne et la relecture à la reconnexion avec la couche réseau de Qora.",1794040393923]