[{"data":1,"prerenderedAt":20046},["ShallowReactive",2],{"navigation_docs":3,"-react-native-version2-networking-and-app-lifecycle":3728,"-react-native-version2-networking-and-app-lifecycle-surround":20041},[4,1707,1912,2366,2547,2673,2880,3002,3052,3109,3143,3269,3346,3360,3495,3512,3525,3671,3724],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"Робота з файлами: C-стиль (stdio.h)","\u002Fcpp\u002Fc-style-files","02.cpp\u002F47.c-style-files",{"title":1905,"path":1906,"stem":1907},"Робота з файлами: C++-стиль (fstream)","\u002Fcpp\u002Fcpp-style-files","02.cpp\u002F48.cpp-style-files",{"title":1909,"path":1910,"stem":1911},"План навчання: Курс C++ — Продовження (Статті 29–60+)","\u002Fcpp\u002Fcurriculum-plan","02.cpp\u002Fcurriculum-plan",{"title":1913,"icon":1914,"path":1915,"stem":1916,"children":1917,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[1918,1944,1998,2020,2324,2362],{"title":1919,"icon":1920,"path":1921,"stem":1922,"children":1923,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[1924,1928,1932,1936,1940],{"title":1925,"path":1926,"stem":1927},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":1929,"path":1930,"stem":1931},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":1933,"path":1934,"stem":1935},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":1937,"path":1938,"stem":1939},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":1941,"path":1942,"stem":1943},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":1945,"icon":1946,"path":1947,"stem":1948,"children":1949,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[1950,1954,1958,1962,1966,1970,1974,1978,1982,1986,1990,1994],{"title":1951,"path":1952,"stem":1953},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":1955,"path":1956,"stem":1957},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":1959,"path":1960,"stem":1961},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":1963,"path":1964,"stem":1965},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":1967,"path":1968,"stem":1969},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":1971,"path":1972,"stem":1973},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":1975,"path":1976,"stem":1977},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":1979,"path":1980,"stem":1981},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":1983,"path":1984,"stem":1985},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":1987,"path":1988,"stem":1989},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":1991,"path":1992,"stem":1993},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":1995,"path":1996,"stem":1997},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":1999,"icon":2000,"path":2001,"stem":2002,"children":2003,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[2004,2008,2012,2016],{"title":2005,"path":2006,"stem":2007},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2009,"path":2010,"stem":2011},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2013,"path":2014,"stem":2015},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2017,"path":2018,"stem":2019},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2021,"icon":2022,"path":2023,"stem":2024,"children":2025},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2026,2027,2031,2035,2039,2043,2106,2141,2293],{"title":2021,"path":2023,"stem":2024},{"title":2028,"path":2029,"stem":2030},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2032,"path":2033,"stem":2034},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2036,"path":2037,"stem":2038},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2040,"path":2041,"stem":2042},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2044,"icon":132,"path":2045,"stem":2046,"children":2047},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2048,2050,2054,2058,2062,2066,2070,2074,2078,2082,2086,2090,2094,2098,2102],{"title":2049,"path":2045,"stem":2046},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2051,"path":2052,"stem":2053},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2055,"path":2056,"stem":2057},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2059,"path":2060,"stem":2061},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2063,"path":2064,"stem":2065},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2067,"path":2068,"stem":2069},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2071,"path":2072,"stem":2073},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2075,"path":2076,"stem":2077},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2079,"path":2080,"stem":2081},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2083,"path":2084,"stem":2085},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2087,"path":2088,"stem":2089},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2091,"path":2092,"stem":2093},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2095,"path":2096,"stem":2097},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2099,"path":2100,"stem":2101},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2103,"path":2104,"stem":2105},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2107,"icon":2022,"path":2108,"stem":2109,"children":2110},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2111,2113,2117,2121,2125,2129,2133,2137],{"title":2112,"path":2108,"stem":2109},"React Router: Навігаційна система сучасного вебу",{"title":2114,"path":2115,"stem":2116},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2118,"path":2119,"stem":2120},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2122,"path":2123,"stem":2124},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2126,"path":2127,"stem":2128},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2130,"path":2131,"stem":2132},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2134,"path":2135,"stem":2136},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2138,"path":2139,"stem":2140},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2142,"icon":132,"path":2143,"stem":2144,"children":2145},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2146,2148,2164,2193,2202,2223,2239,2268],{"title":2147,"path":2143,"stem":2144},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2149,"stem":2150,"children":2151,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2152,2156,2160],{"title":2153,"path":2154,"stem":2155},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2157,"path":2158,"stem":2159},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2161,"path":2162,"stem":2163},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2165,"icon":132,"path":2166,"stem":2167,"children":2168,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2169,2173,2177,2181,2185,2189],{"title":2170,"path":2171,"stem":2172},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2174,"path":2175,"stem":2176},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2178,"path":2179,"stem":2180},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2182,"path":2183,"stem":2184},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2186,"path":2187,"stem":2188},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2190,"path":2191,"stem":2192},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2194,"icon":132,"path":2195,"stem":2196,"children":2197,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2198],{"title":2199,"path":2200,"stem":2201},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2203,"icon":132,"path":2204,"stem":2205,"children":2206,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2207,2211,2215,2219],{"title":2208,"path":2209,"stem":2210},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2212,"path":2213,"stem":2214},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2216,"path":2217,"stem":2218},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2220,"path":2221,"stem":2222},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2224,"icon":92,"path":2225,"stem":2226,"children":2227,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2228,2232,2236],{"title":2229,"path":2230,"stem":2231},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2233,"path":2234,"stem":2235},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2083,"path":2237,"stem":2238},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2240,"icon":132,"path":2241,"stem":2242,"children":2243,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2244,2248,2252,2256,2260,2264],{"title":2245,"path":2246,"stem":2247},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2249,"path":2250,"stem":2251},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2253,"path":2254,"stem":2255},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2257,"path":2258,"stem":2259},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2261,"path":2262,"stem":2263},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2265,"path":2266,"stem":2267},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2269,"icon":132,"path":2270,"stem":2271,"children":2272,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2273,2277,2281,2285,2289],{"title":2274,"path":2275,"stem":2276},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2278,"path":2279,"stem":2280},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2282,"path":2283,"stem":2284},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2286,"path":2287,"stem":2288},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2290,"path":2291,"stem":2292},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2294,"icon":132,"path":2295,"stem":2296,"children":2297},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2298,2300,2304,2308,2312,2316,2320],{"title":2299,"path":2295,"stem":2296},"UI Бібліотеки в React",{"title":2301,"path":2302,"stem":2303},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2305,"path":2306,"stem":2307},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2309,"path":2310,"stem":2311},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2313,"path":2314,"stem":2315},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2317,"path":2318,"stem":2319},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2321,"path":2322,"stem":2323},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2325,"icon":2326,"path":2327,"stem":2328,"children":2329,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2330,2334,2338,2342,2346,2350,2354,2358],{"title":2331,"path":2332,"stem":2333},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2335,"path":2336,"stem":2337},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2339,"path":2340,"stem":2341},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2343,"path":2344,"stem":2345},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2347,"path":2348,"stem":2349},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2351,"path":2352,"stem":2353},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2355,"path":2356,"stem":2357},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2359,"path":2360,"stem":2361},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2363,"path":2364,"stem":2365},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2367,"icon":2368,"path":2369,"stem":2370,"children":2371,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2372,2375,2378,2382,2386,2390,2394],{"title":162,"path":2373,"stem":2374},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2376,"stem":2377},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2379,"path":2380,"stem":2381},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2383,"path":2384,"stem":2385},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2387,"path":2388,"stem":2389},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2391,"path":2392,"stem":2393},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2395,"path":2396,"stem":2397,"children":2398,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2399,2403,2407,2411,2415,2419,2423,2427,2431,2435,2439,2443,2447,2451,2455,2459,2463,2467,2471,2475,2479,2483,2487,2491,2495,2499,2503,2507,2511,2515,2519,2523,2527,2531,2535,2539,2543],{"title":2400,"path":2401,"stem":2402},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2404,"path":2405,"stem":2406},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2408,"path":2409,"stem":2410},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2412,"path":2413,"stem":2414},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2416,"path":2417,"stem":2418},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2420,"path":2421,"stem":2422},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2424,"path":2425,"stem":2426},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2428,"path":2429,"stem":2430},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2432,"path":2433,"stem":2434},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2436,"path":2437,"stem":2438},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2440,"path":2441,"stem":2442},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2444,"path":2445,"stem":2446},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2448,"path":2449,"stem":2450},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2452,"path":2453,"stem":2454},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2456,"path":2457,"stem":2458},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2460,"path":2461,"stem":2462},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2464,"path":2465,"stem":2466},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2468,"path":2469,"stem":2470},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2472,"path":2473,"stem":2474},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2476,"path":2477,"stem":2478},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2480,"path":2481,"stem":2482},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2484,"path":2485,"stem":2486},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2488,"path":2489,"stem":2490},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2492,"path":2493,"stem":2494},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2496,"path":2497,"stem":2498},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2500,"path":2501,"stem":2502},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2504,"path":2505,"stem":2506},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2508,"path":2509,"stem":2510},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2512,"path":2513,"stem":2514},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2516,"path":2517,"stem":2518},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2520,"path":2521,"stem":2522},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2524,"path":2525,"stem":2526},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2528,"path":2529,"stem":2530},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2532,"path":2533,"stem":2534},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2536,"path":2537,"stem":2538},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2540,"path":2541,"stem":2542},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2544,"path":2545,"stem":2546},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2548,"icon":2549,"path":2550,"stem":2551,"children":2552,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2553,2557,2560,2564,2568,2572,2576,2580,2584,2588,2592,2596,2600,2604,2608,2669],{"title":2554,"path":2555,"stem":2556},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2558,"stem":2559},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2561,"path":2562,"stem":2563},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2565,"path":2566,"stem":2567},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2569,"path":2570,"stem":2571},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2573,"path":2574,"stem":2575},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2577,"path":2578,"stem":2579},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2581,"path":2582,"stem":2583},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2585,"path":2586,"stem":2587},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2589,"path":2590,"stem":2591},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2593,"path":2594,"stem":2595},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2597,"path":2598,"stem":2599},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2601,"path":2602,"stem":2603},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2605,"path":2606,"stem":2607},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2609,"icon":92,"path":2610,"stem":2611,"children":2612,"page":59},"FastAPI","\u002Fpython\u002Ffastapi","05.python\u002Ffastapi",[2613,2617,2621,2625,2629,2633,2637,2641,2645,2649,2653,2657,2661,2665],{"title":2614,"path":2615,"stem":2616},"Глибокий Typing та Pydantic v2 — від анотацій до валідації","\u002Fpython\u002Ffastapi\u002Ftyping-pydantic","05.python\u002Ffastapi\u002F15.typing-pydantic",{"title":2618,"path":2619,"stem":2620},"WSGI, ASGI та Python Web-екосистема","\u002Fpython\u002Ffastapi\u002Fwsgi-asgi-ecosystem","05.python\u002Ffastapi\u002F16.wsgi-asgi-ecosystem",{"title":2622,"path":2623,"stem":2624},"FastAPI: Перший додаток, Uvicorn та OpenAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-intro","05.python\u002Ffastapi\u002F17.fastapi-intro",{"title":2626,"path":2627,"stem":2628},"Маршрутизація, параметри запитів та APIRouter","\u002Fpython\u002Ffastapi\u002Ffastapi-routing-params","05.python\u002Ffastapi\u002F18.fastapi-routing-params",{"title":2630,"path":2631,"stem":2632},"Pydantic v2 у FastAPI — схеми, валідація та серіалізація","\u002Fpython\u002Ffastapi\u002Ffastapi-pydantic-schemas","05.python\u002Ffastapi\u002F19.fastapi-pydantic-schemas",{"title":2634,"path":2635,"stem":2636},"Dependency Injection — серце архітектури FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-dependency-injection","05.python\u002Ffastapi\u002F20.fastapi-dependency-injection",{"title":2638,"path":2639,"stem":2640},"Обробка помилок, Middleware та CORS у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-errors-middleware","05.python\u002Ffastapi\u002F21.fastapi-errors-middleware",{"title":2642,"path":2643,"stem":2644},"SQLAlchemy 2.0 — ORM, Core та Async Engine","\u002Fpython\u002Ffastapi\u002Fsqlalchemy-orm","05.python\u002Ffastapi\u002F22.sqlalchemy-orm",{"title":2646,"path":2647,"stem":2648},"Alembic — міграції бази даних","\u002Fpython\u002Ffastapi\u002Falembic-migrations","05.python\u002Ffastapi\u002F23.alembic-migrations",{"title":2650,"path":2651,"stem":2652},"Repository Pattern та Clean Architecture у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-repository-clean-arch","05.python\u002Ffastapi\u002F24.fastapi-repository-clean-arch",{"title":2654,"path":2655,"stem":2656},"Автентифікація та авторизація — JWT, OAuth2, RBAC","\u002Fpython\u002Ffastapi\u002Ffastapi-auth-security","05.python\u002Ffastapi\u002F25.fastapi-auth-security",{"title":2658,"path":2659,"stem":2660},"Тестування FastAPI — pytest, httpx, fixtures","\u002Fpython\u002Ffastapi\u002Ffastapi-testing","05.python\u002Ffastapi\u002F26.fastapi-testing",{"title":2662,"path":2663,"stem":2664},"Async на повну — Background Tasks, WebSockets, SSE та Celery","\u002Fpython\u002Ffastapi\u002Ffastapi-async-advanced","05.python\u002Ffastapi\u002F27.fastapi-async-advanced",{"title":2666,"path":2667,"stem":2668},"Deployment — Docker, CI\u002FCD, Monitoring","\u002Fpython\u002Ffastapi\u002Ffastapi-deployment","05.python\u002Ffastapi\u002F28.fastapi-deployment",{"title":2670,"path":2671,"stem":2672},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2674,"icon":2675,"path":2676,"stem":2677,"children":2678,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2679,2709,2732,2769,2798,2816,2850,2862,2871],{"title":2680,"icon":2681,"path":2682,"stem":2683,"children":2684,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2685,2689,2693,2697,2701,2705],{"title":2686,"path":2687,"stem":2688},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2690,"path":2691,"stem":2692},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2694,"path":2695,"stem":2696},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2698,"path":2699,"stem":2700},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2702,"path":2703,"stem":2704},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2706,"path":2707,"stem":2708},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2710,"icon":2675,"path":2711,"stem":2712,"children":2713,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2714,2718,2724,2728],{"title":2715,"path":2716,"stem":2717},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2719,"path":2720,"stem":2721,"children":2722},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2723],{"title":2719,"path":2720,"stem":2721},{"title":2725,"path":2726,"stem":2727},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2729,"path":2730,"stem":2731},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2733,"icon":2675,"path":2734,"stem":2735,"children":2736,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2737,2741,2745,2749,2753,2757,2761,2765],{"title":2738,"path":2739,"stem":2740},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2742,"path":2743,"stem":2744},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2746,"path":2747,"stem":2748},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2750,"path":2751,"stem":2752},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2754,"path":2755,"stem":2756},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2758,"path":2759,"stem":2760},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2762,"path":2763,"stem":2764},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2766,"path":2767,"stem":2768},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2770,"icon":2675,"path":2771,"stem":2772,"children":2773,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2774,2778,2782,2786,2790,2794],{"title":2775,"path":2776,"stem":2777},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2779,"path":2780,"stem":2781},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2783,"path":2784,"stem":2785},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2787,"path":2788,"stem":2789},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2791,"path":2792,"stem":2793},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2795,"path":2796,"stem":2797},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2799,"icon":2800,"path":2801,"stem":2802,"children":2803,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2804,2808,2812],{"title":2805,"path":2806,"stem":2807},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2809,"path":2810,"stem":2811},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2813,"path":2814,"stem":2815},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2817,"icon":2818,"path":2819,"stem":2820,"children":2821,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2822,2826,2830,2834,2838,2842,2846],{"title":2823,"path":2824,"stem":2825},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2827,"path":2828,"stem":2829},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2831,"path":2832,"stem":2833},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2835,"path":2836,"stem":2837},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2839,"path":2840,"stem":2841},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2843,"path":2844,"stem":2845},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2847,"path":2848,"stem":2849},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2847,"icon":793,"path":2851,"stem":2852,"children":2853,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[2854,2858],{"title":2855,"path":2856,"stem":2857},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":2859,"path":2860,"stem":2861},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":2863,"icon":2864,"path":2865,"stem":2866,"children":2867,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[2868],{"title":2863,"path":2869,"stem":2870},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":2872,"icon":2873,"path":2874,"stem":2875,"children":2876,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[2877],{"title":2872,"path":2878,"stem":2879},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":2881,"icon":2882,"path":2883,"stem":2884,"children":2885,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[2886,2962],{"title":2887,"icon":2888,"path":2889,"stem":2890,"children":2891},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[2892,2894,2898,2902,2906,2910,2914,2918,2922,2926,2930,2934,2938,2942,2946,2950,2954,2958],{"title":2893,"path":2889,"stem":2890},"Docker: від нуля до production",{"title":2895,"path":2896,"stem":2897},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":2899,"path":2900,"stem":2901},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":2903,"path":2904,"stem":2905},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":2907,"path":2908,"stem":2909},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":2911,"path":2912,"stem":2913},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":2915,"path":2916,"stem":2917},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":2919,"path":2920,"stem":2921},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":2923,"path":2924,"stem":2925},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":2927,"path":2928,"stem":2929},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":2931,"path":2932,"stem":2933},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":2935,"path":2936,"stem":2937},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":2939,"path":2940,"stem":2941},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":2943,"path":2944,"stem":2945},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":2947,"path":2948,"stem":2949},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":2951,"path":2952,"stem":2953},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":2955,"path":2956,"stem":2957},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":2959,"path":2960,"stem":2961},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":2963,"icon":2964,"path":2965,"stem":2966,"children":2967},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[2968,2970,2974,2978,2982,2986,2990,2994,2998],{"title":2969,"path":2965,"stem":2966},"Kubernetes: від розробки до production",{"title":2971,"path":2972,"stem":2973},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":2975,"path":2976,"stem":2977},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":2979,"path":2980,"stem":2981},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":2983,"path":2984,"stem":2985},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":2987,"path":2988,"stem":2989},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":2991,"path":2992,"stem":2993},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":2995,"path":2996,"stem":2997},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":2999,"path":3000,"stem":3001},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":3003,"icon":3004,"path":3005,"stem":3006,"children":3007,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[3008,3012,3016,3020,3024,3028,3032,3036,3040,3044,3048],{"title":3009,"path":3010,"stem":3011},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":3013,"path":3014,"stem":3015},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":3017,"path":3018,"stem":3019},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":3021,"path":3022,"stem":3023},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":3025,"path":3026,"stem":3027},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":3029,"path":3030,"stem":3031},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":3033,"path":3034,"stem":3035},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":3037,"path":3038,"stem":3039},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":3041,"path":3042,"stem":3043},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":3045,"path":3046,"stem":3047},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":3049,"path":3050,"stem":3051},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":3053,"icon":943,"path":3054,"stem":3055,"children":3056,"page":59},"DDD","\u002Fddd","10.ddd",[3057,3061,3065,3069,3073,3077,3081,3085,3089,3093,3097,3101,3105],{"title":3058,"path":3059,"stem":3060},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":3062,"path":3063,"stem":3064},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3066,"path":3067,"stem":3068},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3070,"path":3071,"stem":3072},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3074,"path":3075,"stem":3076},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3078,"path":3079,"stem":3080},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3082,"path":3083,"stem":3084},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3086,"path":3087,"stem":3088},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3090,"path":3091,"stem":3092},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3094,"path":3095,"stem":3096},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3098,"path":3099,"stem":3100},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3102,"path":3103,"stem":3104},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3106,"path":3107,"stem":3108},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3110,"icon":3111,"path":3112,"stem":3113,"children":3114,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3115,3119,3123,3127,3131,3135,3139],{"title":3116,"path":3117,"stem":3118},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3120,"path":3121,"stem":3122},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3124,"path":3125,"stem":3126},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3128,"path":3129,"stem":3130},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3132,"path":3133,"stem":3134},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3136,"path":3137,"stem":3138},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3140,"path":3141,"stem":3142},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3144,"icon":3145,"path":3146,"stem":3147,"children":3148,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3149,3153,3157,3161,3165,3169,3173,3177,3181,3185,3189,3193,3197,3201,3205,3209,3213,3217,3221,3225,3229,3233,3237,3241,3245,3249,3253,3257,3261,3265],{"title":3150,"path":3151,"stem":3152},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3154,"path":3155,"stem":3156},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3158,"path":3159,"stem":3160},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3162,"path":3163,"stem":3164},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3166,"path":3167,"stem":3168},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3170,"path":3171,"stem":3172},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3174,"path":3175,"stem":3176},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3178,"path":3179,"stem":3180},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3182,"path":3183,"stem":3184},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3186,"path":3187,"stem":3188},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3190,"path":3191,"stem":3192},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3194,"path":3195,"stem":3196},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3198,"path":3199,"stem":3200},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3202,"path":3203,"stem":3204},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3206,"path":3207,"stem":3208},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3210,"path":3211,"stem":3212},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3214,"path":3215,"stem":3216},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3218,"path":3219,"stem":3220},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3222,"path":3223,"stem":3224},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3226,"path":3227,"stem":3228},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3230,"path":3231,"stem":3232},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3234,"path":3235,"stem":3236},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3238,"path":3239,"stem":3240},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3242,"path":3243,"stem":3244},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3246,"path":3247,"stem":3248},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3250,"path":3251,"stem":3252},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3254,"path":3255,"stem":3256},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3258,"path":3259,"stem":3260},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3262,"path":3263,"stem":3264},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3266,"path":3267,"stem":3268},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3270,"path":3271,"stem":3272,"children":3273,"page":59},"AWS","\u002Faws","13.aws",[3274,3278,3282,3286,3290,3294,3298,3302,3306,3310,3314,3318,3322,3326,3330,3334,3338,3342],{"title":3275,"path":3276,"stem":3277},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3279,"path":3280,"stem":3281},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3283,"path":3284,"stem":3285},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3287,"path":3288,"stem":3289},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3291,"path":3292,"stem":3293},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3295,"path":3296,"stem":3297},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3299,"path":3300,"stem":3301},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3303,"path":3304,"stem":3305},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3307,"path":3308,"stem":3309},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3311,"path":3312,"stem":3313},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3315,"path":3316,"stem":3317},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3319,"path":3320,"stem":3321},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3323,"path":3324,"stem":3325},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3327,"path":3328,"stem":3329},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3331,"path":3332,"stem":3333},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3335,"path":3336,"stem":3337},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3339,"path":3340,"stem":3341},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3343,"path":3344,"stem":3345},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3347,"path":3348,"stem":3349,"children":3350,"page":59},"Ai Development","\u002Fai-development","14.ai-development",[3351],{"title":3352,"path":3353,"stem":3354,"children":3355,"page":59},"Foundations","\u002Fai-development\u002Ffoundations","14.ai-development\u002F00.foundations",[3356],{"title":3357,"path":3358,"stem":3359},"Модуль 0.1. Що таке Large Language Model (LLM)","\u002Fai-development\u002Ffoundations\u002Fwhat-is-llm","14.ai-development\u002F00.foundations\u002F01.what-is-llm",{"title":3361,"path":3362,"stem":3363,"children":3364,"page":59},"React Native","\u002Freact-native","15.react-native",[3365,3386],{"title":3366,"path":3367,"stem":3368,"children":3369,"page":59},"Version1","\u002Freact-native\u002Fversion1","15.react-native\u002Fversion1",[3370,3374,3378,3382],{"title":3371,"path":3372,"stem":3373},"Що таке React Native","\u002Freact-native\u002Fversion1\u002Fwhat-is-react-native","15.react-native\u002Fversion1\u002F01.what-is-react-native",{"title":3375,"path":3376,"stem":3377},"Як влаштований React Native «під капотом»","\u002Freact-native\u002Fversion1\u002Freact-native-architecture","15.react-native\u002Fversion1\u002F02.react-native-architecture",{"title":3379,"path":3380,"stem":3381},"Від React до React Native: ключові відмінності","\u002Freact-native\u002Fversion1\u002Freact-native-vs-react","15.react-native\u002Fversion1\u002F03.react-native-vs-react",{"title":3383,"path":3384,"stem":3385},"View та Text — фундаментальні будівельні блоки","\u002Freact-native\u002Fversion1\u002Fview-and-text","15.react-native\u002Fversion1\u002F04.view-and-text",{"title":3387,"path":3388,"stem":3389,"children":3390,"page":59},"Version2","\u002Freact-native\u002Fversion2","15.react-native\u002Fversion2",[3391,3395,3399,3403,3407,3411,3415,3419,3423,3427,3431,3435,3439,3443,3447,3451,3455,3459,3463,3467,3471,3475,3479,3483,3487,3491],{"title":3392,"path":3393,"stem":3394},"React Native — навіщо він потрібен","\u002Freact-native\u002Fversion2\u002Fwhy-react-native","15.react-native\u002Fversion2\u002F01.why-react-native",{"title":3396,"path":3397,"stem":3398},"Налаштування середовища розробки","\u002Freact-native\u002Fversion2\u002Fenvironment-setup","15.react-native\u002Fversion2\u002F02.environment-setup",{"title":3400,"path":3401,"stem":3402},"Expo і класичний React Native — два шляхи одного фреймворку","\u002Freact-native\u002Fversion2\u002Fexpo-vs-cli-landscape","15.react-native\u002Fversion2\u002F03.expo-vs-cli-landscape",{"title":3404,"path":3405,"stem":3406},"Середовище Expo — перший запуск","\u002Freact-native\u002Fversion2\u002Fexpo-setup-and-tooling","15.react-native\u002Fversion2\u002F04.expo-setup-and-tooling",{"title":3408,"path":3409,"stem":3410},"Структура проєкту та перші UI-примітиви","\u002Freact-native\u002Fversion2\u002Fproject-structure-and-conventions","15.react-native\u002Fversion2\u002F05.project-structure-and-conventions",{"title":3412,"path":3413,"stem":3414},"Базові компоненти React Native","\u002Freact-native\u002Fversion2\u002Fcore-components","15.react-native\u002Fversion2\u002F06.core-components",{"title":3416,"path":3417,"stem":3418},"Flexbox і layout у React Native","\u002Freact-native\u002Fversion2\u002Fflexbox-and-layout","15.react-native\u002Fversion2\u002F07.flexbox-and-layout",{"title":3420,"path":3421,"stem":3422},"StyleSheet і теми (light \u002F dark)","\u002Freact-native\u002Fversion2\u002Fstylesheet-and-theming","15.react-native\u002Fversion2\u002F08.stylesheet-and-theming",{"title":3424,"path":3425,"stem":3426},"Списки та віртуалізація","\u002Freact-native\u002Fversion2\u002Flists-and-virtualization","15.react-native\u002Fversion2\u002F09.lists-and-virtualization",{"title":3428,"path":3429,"stem":3430},"Форми, ввід і валідація","\u002Freact-native\u002Fversion2\u002Fforms-input-validation","15.react-native\u002Fversion2\u002F10.forms-input-validation",{"title":3432,"path":3433,"stem":3434},"Основи Expo Router","\u002Freact-native\u002Fversion2\u002Fexpo-router-basics","15.react-native\u002Fversion2\u002F11.expo-router-basics",{"title":3436,"path":3437,"stem":3438},"Вкладені навігатори та модалки","\u002Freact-native\u002Fversion2\u002Fnested-navigators-and-modals","15.react-native\u002Fversion2\u002F12.nested-navigators-and-modals",{"title":3440,"path":3441,"stem":3442},"Deep linking і параметри маршрутів","\u002Freact-native\u002Fversion2\u002Fdeep-linking-and-params","15.react-native\u002Fversion2\u002F13.deep-linking-and-params",{"title":3444,"path":3445,"stem":3446},"Робота з мережею та життєвий цикл застосунку","\u002Freact-native\u002Fversion2\u002Fnetworking-and-app-lifecycle","15.react-native\u002Fversion2\u002F14.networking-and-app-lifecycle",{"title":3448,"path":3449,"stem":3450},"Redux Toolkit на мобільних платформах","\u002Freact-native\u002Fversion2\u002Fredux-toolkit-on-mobile","15.react-native\u002Fversion2\u002F15.redux-toolkit-on-mobile",{"title":3452,"path":3453,"stem":3454},"RTK Query на мобільних платформах","\u002Freact-native\u002Fversion2\u002Frtk-query-mobile","15.react-native\u002Fversion2\u002F16.rtk-query-mobile",{"title":3456,"path":3457,"stem":3458},"Автентифікація та Безпечне Зберігання","\u002Freact-native\u002Fversion2\u002Fauth-secure-storage","15.react-native\u002Fversion2\u002F18.auth-secure-storage",{"title":3460,"path":3461,"stem":3462},"Локальне сховище — AsyncStorage, MMKV, SQLite","\u002Freact-native\u002Fversion2\u002Flocal-storage-mmkv-sqlite","15.react-native\u002Fversion2\u002F19.local-storage-mmkv-sqlite",{"title":3464,"path":3465,"stem":3466},"Офлайн-First і синхронізація","\u002Freact-native\u002Fversion2\u002Foffline-first-sync","15.react-native\u002Fversion2\u002F20.offline-first-sync",{"title":3468,"path":3469,"stem":3470},"Камера, медіа та файлова система","\u002Freact-native\u002Fversion2\u002Fcamera-media-filesystem","15.react-native\u002Fversion2\u002F21.camera-media-filesystem",{"title":3472,"path":3473,"stem":3474},"Карти та геолокація","\u002Freact-native\u002Fversion2\u002Fmaps-and-location","15.react-native\u002Fversion2\u002F22.maps-and-location",{"title":3476,"path":3477,"stem":3478},"Жести та дотики","\u002Freact-native\u002Fversion2\u002Fgestures-and-touch","15.react-native\u002Fversion2\u002F23.gestures-and-touch",{"title":3480,"path":3481,"stem":3482},"Анімації з Reanimated","\u002Freact-native\u002Fversion2\u002Freanimated-animations","15.react-native\u002Fversion2\u002F24.reanimated-animations",{"title":3484,"path":3485,"stem":3486},"Push і локальні сповіщення","\u002Freact-native\u002Fversion2\u002Fpush-and-local-notifications","15.react-native\u002Fversion2\u002F25.push-and-local-notifications",{"title":3488,"path":3489,"stem":3490},"Доступність та інтернаціоналізація","\u002Freact-native\u002Fversion2\u002Faccessibility-and-i18n","15.react-native\u002Fversion2\u002F26.accessibility-and-i18n",{"title":3492,"path":3493,"stem":3494},"EAS Build та профілі збірки","\u002Freact-native\u002Fversion2\u002Feas-build-and-profiles","15.react-native\u002Fversion2\u002F29.eas-build-and-profiles",{"title":3496,"path":3497,"stem":3498,"children":3499,"page":59},"Ai Python","\u002Fai-python","16.ai-python",[3500,3504,3508],{"title":3501,"path":3502,"stem":3503},"Вступ до штучного інтелекту та машинного навчання","\u002Fai-python\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai",{"title":3505,"path":3506,"stem":3507},"Знайомство з Jupyter Notebook та JupyterLab","\u002Fai-python\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook",{"title":3509,"path":3510,"stem":3511},"Робота з даними з використанням NumPy і pandas","\u002Fai-python\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas",{"title":3513,"icon":658,"path":3514,"stem":3515,"children":3516,"page":59},"Клієнт-серверна архітектура","\u002Fclient-server-architecture","17.client-server-architecture",[3517,3521],{"title":3518,"path":3519,"stem":3520},"Архітектура клієнт-серверних систем та основи Go","\u002Fclient-server-architecture\u002Fclient-server-architecture-and-go","17.client-server-architecture\u002F01.client-server-architecture-and-go",{"title":3522,"path":3523,"stem":3524},"Система типів та модель пам’яті мови Go","\u002Fclient-server-architecture\u002Ftype-system-and-memory-model","17.client-server-architecture\u002F02.type-system-and-memory-model",{"title":3526,"icon":1946,"path":3527,"stem":3528,"children":3529,"page":59},"Розробка WEB-застосувань","\u002Fweb-development","18.web-development",[3530,3575,3613,3642],{"title":3531,"icon":658,"path":3532,"stem":3533,"children":3534,"page":59},"Архітектура комп'ютерних мереж та мережеві протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols","18.web-development\u002F01.network-architecture-and-protocols",[3535,3539,3543,3547,3551,3555,3559,3563,3567,3571],{"title":3536,"path":3537,"stem":3538},"Еволюція веброзробки та модель клієнт-сервер","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fweb-evolution-client-server","18.web-development\u002F01.network-architecture-and-protocols\u002F01.web-evolution-client-server",{"title":3540,"path":3541,"stem":3542},"Еталонна модель OSI","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fosi-model","18.web-development\u002F01.network-architecture-and-protocols\u002F02.osi-model",{"title":3544,"path":3545,"stem":3546},"Стек протоколів TCP\u002FIP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-ip-stack","18.web-development\u002F01.network-architecture-and-protocols\u002F03.tcp-ip-stack",{"title":3548,"path":3549,"stem":3550},"IP-адресація, порти та сокети","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fip-addressing-ports-sockets","18.web-development\u002F01.network-architecture-and-protocols\u002F04.ip-addressing-ports-sockets",{"title":3552,"path":3553,"stem":3554},"Протоколи TCP та UDP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-udp-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F05.tcp-udp-protocols",{"title":3556,"path":3557,"stem":3558},"Система доменних імен DNS","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fdns-system","18.web-development\u002F01.network-architecture-and-protocols\u002F06.dns-system",{"title":3560,"path":3561,"stem":3562},"Протокол SMTP та поштові протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fsmtp-email-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F07.smtp-email-protocols",{"title":3564,"path":3565,"stem":3566},"Протокол WebSocket","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fwebsocket-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F08.websocket-protocol",{"title":3568,"path":3569,"stem":3570},"Протокол SSH","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fssh-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F09.ssh-protocol",{"title":3572,"path":3573,"stem":3574},"Огляд сучасних серверних технологій","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fmodern-backend-technologies","18.web-development\u002F01.network-architecture-and-protocols\u002F10.modern-backend-technologies",{"title":3576,"icon":3577,"path":3578,"stem":3579,"children":3580,"page":59},"Протокол HTTP та захищена передача даних HTTPS","i-lucide-lock","\u002Fweb-development\u002Fhttp-and-https","18.web-development\u002F02.http-and-https",[3581,3585,3589,3593,3597,3601,3605,3609],{"title":3582,"path":3583,"stem":3584},"Вступ до протоколу HTTP та еволюція веб-технологій","\u002Fweb-development\u002Fhttp-and-https\u002Fintroduction-and-evolution","18.web-development\u002F02.http-and-https\u002F01.introduction-and-evolution",{"title":3586,"path":3587,"stem":3588},"Анатомія HTTP-запиту","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-request-structure","18.web-development\u002F02.http-and-https\u002F02.http-request-structure",{"title":3590,"path":3591,"stem":3592},"Анатомія HTTP-відповіді","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-response-structure","18.web-development\u002F02.http-and-https\u002F03.http-response-structure",{"title":3594,"path":3595,"stem":3596},"Методи HTTP та їхня семантика","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-methods","18.web-development\u002F02.http-and-https\u002F04.http-methods",{"title":3598,"path":3599,"stem":3600},"Коди статусу HTTP та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-status-codes","18.web-development\u002F02.http-and-https\u002F05.http-status-codes",{"title":3602,"path":3603,"stem":3604},"HTTP-заголовки та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-headers","18.web-development\u002F02.http-and-https\u002F06.http-headers",{"title":3606,"path":3607,"stem":3608},"CORS та механізми безпеки HTTP","\u002Fweb-development\u002Fhttp-and-https\u002Fcors-and-security","18.web-development\u002F02.http-and-https\u002F07.cors-and-security",{"title":3610,"path":3611,"stem":3612},"HTTPS та криптографічні основи TLS","\u002Fweb-development\u002Fhttp-and-https\u002Fhttps-and-tls","18.web-development\u002F02.http-and-https\u002F08.https-and-tls",{"title":3614,"icon":2326,"path":3615,"stem":3616,"children":3617,"page":59},"Система типів мови TypeScript","\u002Fweb-development\u002Ftypescript-type-system","18.web-development\u002F03.typescript-type-system",[3618,3622,3626,3630,3634,3638],{"title":3619,"path":3620,"stem":3621},"Знайомство з TypeScript Compiler","\u002Fweb-development\u002Ftypescript-type-system\u002Fintroduction-compiler-config","18.web-development\u002F03.typescript-type-system\u002F01.introduction-compiler-config",{"title":3623,"path":3624,"stem":3625},"Примітивні типи даних: string, number, boolean","\u002Fweb-development\u002Ftypescript-type-system\u002Fprimitive-special-types","18.web-development\u002F03.typescript-type-system\u002F02.primitive-special-types",{"title":3627,"path":3628,"stem":3629},"Масиви, кортежі та об","\u002Fweb-development\u002Ftypescript-type-system\u002Farrays-tuples-objects","18.web-development\u002F03.typescript-type-system\u002F03.arrays-tuples-objects",{"title":3631,"path":3632,"stem":3633},"Union типи, Intersection типи та літеральні типи","\u002Fweb-development\u002Ftypescript-type-system\u002Funion-intersection-literals","18.web-development\u002F03.typescript-type-system\u002F04.union-intersection-literals",{"title":3635,"path":3636,"stem":3637},"Type Narrowing (звуження типів)","\u002Fweb-development\u002Ftypescript-type-system\u002Ftype-narrowing","18.web-development\u002F03.typescript-type-system\u002F05.type-narrowing",{"title":3639,"path":3640,"stem":3641},"Типізація функцій","\u002Fweb-development\u002Ftypescript-type-system\u002Ffunctions-typing","18.web-development\u002F03.typescript-type-system\u002F06.functions-typing",{"title":3643,"icon":2326,"path":3644,"stem":3645,"children":3646,"page":59},"Розширені засоби мови TypeScript","\u002Fweb-development\u002Ftypescript-advanced","18.web-development\u002F04.typescript-advanced",[3647,3651,3655,3659,3663,3667],{"title":3648,"path":3649,"stem":3650},"Interfaces та Classes","\u002Fweb-development\u002Ftypescript-advanced\u002Finterfaces-classes","18.web-development\u002F04.typescript-advanced\u002F01.interfaces-classes",{"title":3652,"path":3653,"stem":3654},"Основи Generic типів","\u002Fweb-development\u002Ftypescript-advanced\u002Fgenerics-fundamentals","18.web-development\u002F04.typescript-advanced\u002F02.generics-fundamentals",{"title":3656,"path":3657,"stem":3658},"Enums та Decorators","\u002Fweb-development\u002Ftypescript-advanced\u002Fenums-decorators","18.web-development\u002F04.typescript-advanced\u002F03.enums-decorators",{"title":3660,"path":3661,"stem":3662},"Utility Types","\u002Fweb-development\u002Ftypescript-advanced\u002Futility-types","18.web-development\u002F04.typescript-advanced\u002F04.utility-types",{"title":3664,"path":3665,"stem":3666},"Mapped та Conditional Types","\u002Fweb-development\u002Ftypescript-advanced\u002Fmapped-conditional-types","18.web-development\u002F04.typescript-advanced\u002F05.mapped-conditional-types",{"title":3668,"path":3669,"stem":3670},"Модулі та Strict Mode","\u002Fweb-development\u002Ftypescript-advanced\u002Fmodules-strict-mode","18.web-development\u002F04.typescript-advanced\u002F06.modules-strict-mode",{"title":3672,"path":3673,"stem":3674,"children":3675,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[3676,3680,3684,3688,3692,3696,3700,3704,3708,3712,3716,3720],{"title":3677,"path":3678,"stem":3679},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":3681,"path":3682,"stem":3683},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":3685,"path":3686,"stem":3687},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":3689,"path":3690,"stem":3691},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":3693,"path":3694,"stem":3695},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":3697,"path":3698,"stem":3699},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":3701,"path":3702,"stem":3703},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":3705,"path":3706,"stem":3707},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":3709,"path":3710,"stem":3711},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":3713,"path":3714,"stem":3715},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":3717,"path":3718,"stem":3719},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":3721,"path":3722,"stem":3723},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":3725,"path":3726,"stem":3727},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":3729,"title":3444,"body":3730,"description":20035,"extension":20036,"links":20037,"meta":20038,"navigation":4059,"path":3445,"seo":20039,"stem":3446,"__hash__":20040},"docs\u002F15.react-native\u002Fversion2\u002F14.networking-and-app-lifecycle.md",{"type":3731,"value":3732,"toc":19953},"minimark",[3733,3737,3742,3746,3762,3773,3778,3952,3963,3966,3970,3973,3977,3999,4010,4280,4284,4287,4362,4372,4374,4378,4388,4392,4398,4481,4488,4491,4518,4529,4540,4542,4550,4553,4643,4653,4657,4664,4711,4715,4718,4724,4732,4861,4865,4868,6470,6472,6481,6490,6497,6513,6589,6595,6605,7190,7196,7202,7251,7255,7382,7384,7390,7393,7401,7412,7452,7456,7469,7475,7632,7635,9158,9160,9164,9167,9171,9174,9292,9296,9303,9310,9317,9320,9323,9355,9479,9483,9489,10051,10053,10059,10070,11301,11303,11309,11312,11320,11326,12127,12174,12180,12186,12223,12227,12771,12773,12777,12780,13545,13547,13551,13554,13585,13589,13691,13695,17610,17612,17616,17629,17635,17645,17822,17858,17862,18197,18207,19438,19448,19685,19687,19691,19747,19749,19753,19844,19846,19850,19949],[3734,3735,3444],"h1",{"id":3736},"робота-з-мережею-та-життєвий-цикл-застосунку",[3738,3739,3741],"h2",{"id":3740},"вступ-особливості-мобільної-мережі-та-життєвий-цикл-застосунку","Вступ: Особливості мобільної мережі та життєвий цикл застосунку",[3743,3744,3745],"p",{},"У класичній веб-розробці клієнтський застосунок функціонує в середовищі умовної стабільності: браузер запущений на стаціонарному комп'ютері або лептопі з постійним підключенням до мережі Ethernet чи стаціонарного Wi-Fi. Якщо зв'язок обривається, браузер бере на себе візуалізацію збою або користувач просто перезавантажує сторінку. Крім того, вкладка веб-переглядача або активна, або закрита; концепція призупинення процесів операційною системою у вебі здебільшого прихована від інженера.",[3743,3747,3748,3749,3752,3753,3756,3757,3761],{},"У мобільній розробці на ",[3750,3751,3361],"strong",{}," середовище є ",[3750,3754,3755],{},"нестабільним, непередбачуваним та обмеженим у ресурсах",". Смартфон постійно переміщується у фізичному просторі, перемикається між стільниковими вежами різних стандартів (2G, 3G, LTE, 5G) та публічними точками доступу Wi-Fi. Під час цього процесу змінюються IP-адреси, виникають зони радіотіні (ліфти, підземні паркінги, тунелі) та спостерігається суттєва втрата мережевих пакетів (",[3758,3759,3760],"em",{},"packet loss",").",[3743,3763,3764,3765,3768,3769,3772],{},"Одночасно з мережевою нестабільністю мобільна операційна система (iOS чи Android) веде жорсткий моніторинг енергоспоживання та використання оперативної пам'яті (RAM). Застосунок може бути щомиті переведений у фоновий режим, «заморожений» (",[3758,3766,3767],{},"suspended",") або примусово завершений системним диспетчером (",[3758,3770,3771],{},"Out-Of-Memory Killer",") без виклику фінальних обробників чи очищення пам'яті в JavaScript.",[3774,3775,3777],"h3",{"id":3776},"навчальні-цілі-розділу","Навчальні цілі розділу:",[3779,3780,3781,3785,3809,3816,3837,3862,3873,3880,3890,3899,3916,3939,3946],"ul",{},[3782,3783,3784],"li",{},"Зрозуміти різницю між роботою мережі у вебі та мобільних додатках: відсутність браузерного пісочника CORS, пряма взаємодія з нативними сокетами ОС.",[3782,3786,3787,3788,3791,3792,3795,3796,3802,3803,3808],{},"Простежити шлях HTTP-запиту: від виклику в рушії ",[3750,3789,3790],{},"Hermes"," через ",[3750,3793,3794],{},"JSI \u002F TurboModules"," до системних стеків ",[3750,3797,3798],{},[3799,3800,3801],"code",{},"NSURLSession"," (iOS) та ",[3750,3804,3805],{},[3799,3806,3807],{},"OkHttpClient"," (Android).",[3782,3810,3811,3812,3815],{},"Розібратися, як працює мобільний модем (стани ",[3750,3813,3814],{},"RRC",") і чому неконтрольовані запити швидко розряджають акумулятор.",[3782,3817,3818,3819,3830,3831,3836],{},"Опанувати архітектуру ",[3750,3820,3821,3822,3825,3826,3829],{},"нормалізованого клієнтського стану (Normalized State: ",[3799,3823,3824],{},"byId"," + ",[3799,3827,3828],{},"allIds",")",": чому збереження масивів об'єктів з API є антипатерном для мобільних додатків і як досягти складності вибірки та оновлення ",[3832,3833],"math-formula",{":inline":3834,"tex":3835},"true","O(1)",".",[3782,3838,3839,3840,3845,3846,3851,3852,3855,3856,3861],{},"Порівняти стандартний поліфіл ",[3750,3841,3842],{},[3799,3843,3844],{},"fetch"," та бібліотеку ",[3750,3847,3848],{},[3799,3849,3850],{},"axios",": керування таймаутами, перехоплювачі (",[3758,3853,3854],{},"interceptors","), обробка скасування через ",[3750,3857,3858],{},[3799,3859,3860],{},"AbortController"," та безпечна десеріалізація відповідей.",[3782,3863,3864,3865,3868,3869,3872],{},"Створити надійну ",[3750,3866,3867],{},"чергу оновлення токенів (Mutex Token Refresh Queue)"," на базі Axios Interceptors для усунення стану гонитви (",[3758,3870,3871],{},"Race Condition",") при паралельних запитах з простроченим токеном.",[3782,3874,3875,3876,3879],{},"Розібрати ",[3750,3877,3878],{},"класифікацію та типізацію мережевих помилок",": розмежування транспортних збоїв, серверних відмов, бізнес-помилок авторизації та валідації схем (Zod).",[3782,3881,3882,3883,3886,3887,3761],{},"Засвоїти формулу та логіку алгоритму ",[3750,3884,3885],{},"Exponential Backoff із Jitter",", щоб захистити сервер від перевантаження при одночасних повторних запитах (",[3758,3888,3889],{},"Thundering Herd",[3782,3891,3892,3893,3898],{},"Створити типізований клас помилок ",[3750,3894,3895],{},[3799,3896,3897],{},"AppError"," для уніфікації збоїв з різних джерел.",[3782,3900,3901,3902,3907,3908,3911,3912,3915],{},"Дослідити бібліотеку ",[3750,3903,3904],{},[3799,3905,3906],{},"@react-native-community\u002Fnetinfo",": різниця між фізичним з'єднанням (",[3799,3909,3910],{},"isConnected",") та верифікованою доступністю Інтернету (",[3799,3913,3914],{},"isInternetReachable","), обробка Captive Portals.",[3782,3917,3918,3919,3924,3925,3928,3929,3928,3932,3935,3936,3761],{},"Вивчити стани життєвого циклу ",[3750,3920,3921],{},[3799,3922,3923],{},"AppState"," (",[3799,3926,3927],{},"active",", ",[3799,3930,3931],{},"inactive",[3799,3933,3934],{},"background","), їхню кореляцію з нативними подіями iOS\u002FAndroid та сценарії захисту конфіденційних даних (",[3758,3937,3938],{},"Privacy Shield",[3782,3940,3941,3942,3945],{},"Реалізувати розумне автооновлення даних (",[3750,3943,3944],{},"Smart Refetch on Focus & Reconnect",") при поверненні в додаток або відновленні зв'язку.",[3782,3947,3948,3949,3836],{},"Побудувати повноцінний міні-проєкт «Курси валют» та інтегрувати нормалізований мережевий шар з обробкою життєвого циклу в наскрізний проєкт ",[3750,3950,3951],{},"Nomad",[3953,3954,3955,3958,3959,3962],"note",{},[3750,3956,3957],{},"Головне правило мобільного розробника:"," проєктуйте клієнт із розрахунку на те, що мережі ",[3750,3960,3961],{},"немає за замовчуванням",", будь-який запит може завершитися за таймаутом або обірватися на середині передачі байтів, а процес програми може бути знищений операційною системою будь-якої секунди після переходу у фоновий стан.",[3964,3965],"hr",{},[3738,3967,3969],{"id":3968},"як-працює-мережа-від-js-коду-до-нативного-стека-ос","Як працює мережа: від JS-коду до нативного стека ОС",[3743,3971,3972],{},"Щоб усвідомити, як функціонує мережа в React Native, необхідно простежити шлях HTTP-запиту крізь шари абстракції платформи.",[3774,3974,3976],{"id":3975},"чому-в-react-native-відсутній-cors","Чому в React Native відсутній CORS?",[3743,3978,3979,3980,3983,3984,3987,3988,3991,3992,3995,3996,3761],{},"У веб-розробці головним джерелом складнощів при роботі з API є політика однакового джерела — ",[3750,3981,3982],{},"Same-Origin Policy (SOP)"," та механізм ",[3750,3985,3986],{},"Cross-Origin Resource Sharing (CORS)",". CORS — це ",[3750,3989,3990],{},"виключно браузерний механізм безпеки",", розроблений для захисту користувача від міжсайтових атак (наприклад, коли шкідливий скрипт на ",[3799,3993,3994],{},"evil.com"," намагається через браузер виконати запит з кукі-файлами до ",[3799,3997,3998],{},"bank.com",[3743,4000,4001,4002,4005,4006,4009],{},"Мобільний застосунок не є веб-сторінкою, завантаженою з віддаленого хоста. Це скомпільований бінарний пакет, встановлений локально в ізольованій пісочниці (",[3758,4003,4004],{},"Sandbox",") операційної системи. Нативний код мобільного пристрою здійснює HTTP-запити через системні сокети TCP\u002FTLS так само, як утиліта ",[3799,4007,4008],{},"curl",", клієнт Postman або бекенд-сервіс на Node.js чи Go.",[4011,4012,4013],"plant-uml",{},[4014,4015,4020],"pre",{"className":4016,"code":4017,"language":4018,"meta":4019,"style":4019},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam defaultFontName \"Helvetica\"\nskinparam defaultFontSize 12\n\npackage \"JavaScript Runtime (Hermes \u002F V8)\" #f8fafc {\n    [React Native Component] as UI #e2e8f0\n    [JS Fetch \u002F Axios API] as JSNet #e2e8f0\n    UI -> JSNet : 1. fetch(url, options)\n}\n\npackage \"C++ Bridge \u002F JSI Layer\" #f1f5f9 {\n    [TurboModule \u002F NativeModule\\n(NetworkingModule.cpp)] as Bridge #cbd5e1\n    JSNet -> Bridge : 2. JSI C++ Host Object Call\\n(Serialization of URL, Headers, Body)\n}\n\npackage \"Platform Native OS Layer\" #f8fafc {\n    node \"iOS Runtime\" #f1f5f9 {\n        [NSURLSession \u002F NSURLSessionTask] as iOSNet #bfdbfe\n    }\n    node \"Android Runtime\" #f1f5f9 {\n        [OkHttpClient \u002F OkHttp Call] as DroidNet #bbf7d0\n    }\n    Bridge --> iOSNet : 3a. Native iOS Dispatch\n    Bridge --> DroidNet : 3b. Native Android JNI\n}\n\ncloud \"Remote Infrastructure\" #f8fafc {\n    [TLS 1.3 \u002F TCP Socket Connection] as Socket #fed7aa\n    [Backend REST \u002F GraphQL API] as Server #fecaca\n    iOSNet --> Socket : 4a. Native Socket I\u002FO\n    DroidNet --> Socket : 4b. Native Socket I\u002FO\n    Socket \u003C--> Server : 5. HTTP\u002F1.1 or HTTP\u002F2 or HTTP\u002F3\n}\n\nnote bottom of Bridge\n  **Ключові відмінності від Web:**\n  1. CORS не застосовується (запити йдуть від імені нативного клієнта).\n  2. Запити виконуються в окремому нативному пулі потоків (не блокують JS Event Loop).\n  3. Підтримка системних кешів (NSURLCache) та пулів сокетів OkHttp.\nend note\n\n@enduml\n","plantuml","",[3799,4021,4022,4030,4036,4042,4048,4054,4061,4067,4073,4079,4085,4091,4096,4102,4108,4114,4119,4124,4130,4136,4142,4148,4154,4160,4165,4171,4177,4182,4187,4193,4199,4205,4211,4217,4223,4228,4233,4239,4245,4251,4257,4263,4269,4274],{"__ignoreMap":4019},[4023,4024,4027],"span",{"class":4025,"line":4026},"line",1,[4023,4028,4029],{},"@startuml\n",[4023,4031,4033],{"class":4025,"line":4032},2,[4023,4034,4035],{},"skinparam style plain\n",[4023,4037,4039],{"class":4025,"line":4038},3,[4023,4040,4041],{},"skinparam backgroundColor #ffffff\n",[4023,4043,4045],{"class":4025,"line":4044},4,[4023,4046,4047],{},"skinparam defaultFontName \"Helvetica\"\n",[4023,4049,4051],{"class":4025,"line":4050},5,[4023,4052,4053],{},"skinparam defaultFontSize 12\n",[4023,4055,4057],{"class":4025,"line":4056},6,[4023,4058,4060],{"emptyLinePlaceholder":4059},true,"\n",[4023,4062,4064],{"class":4025,"line":4063},7,[4023,4065,4066],{},"package \"JavaScript Runtime (Hermes \u002F V8)\" #f8fafc {\n",[4023,4068,4070],{"class":4025,"line":4069},8,[4023,4071,4072],{},"    [React Native Component] as UI #e2e8f0\n",[4023,4074,4076],{"class":4025,"line":4075},9,[4023,4077,4078],{},"    [JS Fetch \u002F Axios API] as JSNet #e2e8f0\n",[4023,4080,4082],{"class":4025,"line":4081},10,[4023,4083,4084],{},"    UI -> JSNet : 1. fetch(url, options)\n",[4023,4086,4088],{"class":4025,"line":4087},11,[4023,4089,4090],{},"}\n",[4023,4092,4094],{"class":4025,"line":4093},12,[4023,4095,4060],{"emptyLinePlaceholder":4059},[4023,4097,4099],{"class":4025,"line":4098},13,[4023,4100,4101],{},"package \"C++ Bridge \u002F JSI Layer\" #f1f5f9 {\n",[4023,4103,4105],{"class":4025,"line":4104},14,[4023,4106,4107],{},"    [TurboModule \u002F NativeModule\\n(NetworkingModule.cpp)] as Bridge #cbd5e1\n",[4023,4109,4111],{"class":4025,"line":4110},15,[4023,4112,4113],{},"    JSNet -> Bridge : 2. JSI C++ Host Object Call\\n(Serialization of URL, Headers, Body)\n",[4023,4115,4117],{"class":4025,"line":4116},16,[4023,4118,4090],{},[4023,4120,4122],{"class":4025,"line":4121},17,[4023,4123,4060],{"emptyLinePlaceholder":4059},[4023,4125,4127],{"class":4025,"line":4126},18,[4023,4128,4129],{},"package \"Platform Native OS Layer\" #f8fafc {\n",[4023,4131,4133],{"class":4025,"line":4132},19,[4023,4134,4135],{},"    node \"iOS Runtime\" #f1f5f9 {\n",[4023,4137,4139],{"class":4025,"line":4138},20,[4023,4140,4141],{},"        [NSURLSession \u002F NSURLSessionTask] as iOSNet #bfdbfe\n",[4023,4143,4145],{"class":4025,"line":4144},21,[4023,4146,4147],{},"    }\n",[4023,4149,4151],{"class":4025,"line":4150},22,[4023,4152,4153],{},"    node \"Android Runtime\" #f1f5f9 {\n",[4023,4155,4157],{"class":4025,"line":4156},23,[4023,4158,4159],{},"        [OkHttpClient \u002F OkHttp Call] as DroidNet #bbf7d0\n",[4023,4161,4163],{"class":4025,"line":4162},24,[4023,4164,4147],{},[4023,4166,4168],{"class":4025,"line":4167},25,[4023,4169,4170],{},"    Bridge --> iOSNet : 3a. Native iOS Dispatch\n",[4023,4172,4174],{"class":4025,"line":4173},26,[4023,4175,4176],{},"    Bridge --> DroidNet : 3b. Native Android JNI\n",[4023,4178,4180],{"class":4025,"line":4179},27,[4023,4181,4090],{},[4023,4183,4185],{"class":4025,"line":4184},28,[4023,4186,4060],{"emptyLinePlaceholder":4059},[4023,4188,4190],{"class":4025,"line":4189},29,[4023,4191,4192],{},"cloud \"Remote Infrastructure\" #f8fafc {\n",[4023,4194,4196],{"class":4025,"line":4195},30,[4023,4197,4198],{},"    [TLS 1.3 \u002F TCP Socket Connection] as Socket #fed7aa\n",[4023,4200,4202],{"class":4025,"line":4201},31,[4023,4203,4204],{},"    [Backend REST \u002F GraphQL API] as Server #fecaca\n",[4023,4206,4208],{"class":4025,"line":4207},32,[4023,4209,4210],{},"    iOSNet --> Socket : 4a. Native Socket I\u002FO\n",[4023,4212,4214],{"class":4025,"line":4213},33,[4023,4215,4216],{},"    DroidNet --> Socket : 4b. Native Socket I\u002FO\n",[4023,4218,4220],{"class":4025,"line":4219},34,[4023,4221,4222],{},"    Socket \u003C--> Server : 5. HTTP\u002F1.1 or HTTP\u002F2 or HTTP\u002F3\n",[4023,4224,4226],{"class":4025,"line":4225},35,[4023,4227,4090],{},[4023,4229,4231],{"class":4025,"line":4230},36,[4023,4232,4060],{"emptyLinePlaceholder":4059},[4023,4234,4236],{"class":4025,"line":4235},37,[4023,4237,4238],{},"note bottom of Bridge\n",[4023,4240,4242],{"class":4025,"line":4241},38,[4023,4243,4244],{},"  **Ключові відмінності від Web:**\n",[4023,4246,4248],{"class":4025,"line":4247},39,[4023,4249,4250],{},"  1. CORS не застосовується (запити йдуть від імені нативного клієнта).\n",[4023,4252,4254],{"class":4025,"line":4253},40,[4023,4255,4256],{},"  2. Запити виконуються в окремому нативному пулі потоків (не блокують JS Event Loop).\n",[4023,4258,4260],{"class":4025,"line":4259},41,[4023,4261,4262],{},"  3. Підтримка системних кешів (NSURLCache) та пулів сокетів OkHttp.\n",[4023,4264,4266],{"class":4025,"line":4265},42,[4023,4267,4268],{},"end note\n",[4023,4270,4272],{"class":4025,"line":4271},43,[4023,4273,4060],{"emptyLinePlaceholder":4059},[4023,4275,4277],{"class":4025,"line":4276},44,[4023,4278,4279],{},"@enduml\n",[3774,4281,4283],{"id":4282},"механізм-виконання-запиту-крізь-шари-платформи","Механізм виконання запиту крізь шари платформи",[3743,4285,4286],{},"Розглянемо послідовні фази проходження мережевого виклику:",[4288,4289,4290,4303,4313,4350,4356],"ol",{},[3782,4291,4292,4295,4296,4299,4300,3836],{},[3750,4293,4294],{},"Ініціалізація в JS-шарі:"," Код застосунку викликає глобальний метод ",[3799,4297,4298],{},"fetch()"," або метод екземпляра ",[3799,4301,4302],{},"axios.get()",[3782,4304,4305,4308,4309,4312],{},[3750,4306,4307],{},"Маршрутизація через JSI (JavaScript Interface):"," Замість застарілого асинхронного JSON-моста (",[3758,4310,4311],{},"Legacy Bridge",") сучасна архітектура React Native (New Architecture) використовує C++ TurboModules. Запит серіалізується у структури пам'яті C++ безпосередньо через JSI.",[3782,4314,4315,4318],{},[3750,4316,4317],{},"Делегування нативній підсистемі:",[3779,4319,4320,4335],{},[3782,4321,4322,4323,4326,4327,4331,4332,3836],{},"В ",[3750,4324,4325],{},"iOS"," запит передається в середовище Foundation до класу ",[3750,4328,4329],{},[3799,4330,3801],{},". Він керує пулом системних потоків, підтримкою HTTP\u002F2 та HTTP\u002F3 (QUIC), сертифікатами безпеки та нативним дисковим кешем ",[3799,4333,4334],{},"NSURLCache",[3782,4336,4322,4337,4340,4341,4345,4346,4349],{},[3750,4338,4339],{},"Android"," запит делегується бібліотеці ",[3750,4342,4343],{},[3799,4344,3807],{},". Вона оптимізує повторне використання відкритих TCP-з'єднань (",[3758,4347,4348],{},"Connection Pooling","), прозоро стискає трафік за алгоритмом GZIP\u002FBrotli та взаємодіє з Android Network Security Configuration.",[3782,4351,4352,4355],{},[3750,4353,4354],{},"Транспортний рівень:"," Нативна підсистема встановлює TLS-хендшейк та здійснює обмін байтами через мережевий адаптер пристрою.",[3782,4357,4358,4361],{},[3750,4359,4360],{},"Зворотне сповіщення:"," Отримані байти збираються в буфер пам'яті нативної частини, перетворюються на відповідні типи C++\u002FJS і повертають Promise у JavaScript Event Loop як завершену операцію.",[4363,4364,4365,4368,4369,3836],"tip",{},[3750,4366,4367],{},"Пул потоків та швидкодія."," Виконання мережевих запитів у нативному шарі означає, що важкі операції шифрування TLS, парсингу заголовків і завантаження великих бінарних тіл (наприклад, фотографій) відбуваються в окремих нативних системних потоках і ",[3750,4370,4371],{},"жодним чином не блокують головний потік рендерингу UI або потік виконання JavaScript",[3964,4373],{},[3738,4375,4377],{"id":4376},"як-мобільний-модем-витрачає-батарею-стани-rrc-та-енергоспоживання","Як мобільний модем витрачає батарею: стани RRC та енергоспоживання",[3743,4379,4380,4381,4384,4385,3836],{},"Одна з найбільших помилок інженера, який прийшов з веб-розробки, — сприймати передачу даних мобільним пристроєм як «безкоштовну» операцію, де єдиним ресурсом є пропускна здатність (",[3758,4382,4383],{},"bandwidth","). У мобільних пристроях головним обмеженням є ",[3750,4386,4387],{},"енерговитрати радіомодуля (Baseband Radio Processor)",[3774,4389,4391],{"id":4390},"модель-станів-rrc-radio-resource-control","Модель станів RRC (Radio Resource Control)",[3743,4393,4394,4395,4397],{},"Радіомодуль стільникового зв'язку (LTE\u002F5G) не може постійно працювати на повній потужності, оскільки акумулятор смартфона розрядився б за 2–3 години. Для економії енергії операційна система та модемний чип перемикають радіомодуль між кількома станами ",[3750,4396,3814],{},":",[4011,4399,4400],{},[4014,4401,4403],{"className":4016,"code":4402,"language":4018,"meta":4019,"style":4019},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam defaultFontName \"Helvetica\"\nskinparam defaultFontSize 12\n\nstate \"RRC IDLE\\n(Сплячий режим)\" as Idle #e2e8f0 : Мінімальне енергоспоживання (~0-10 мВт)\\nРадіоканал не виділено.\nstate \"RRC CONNECTED (High Power)\\n(Повна потужність)\" as Connected #fecaca : Максимальне енергоспоживання (~1000-2000 мВт)\\nВисока швидкість передачі даних.\nstate \"RRC INACTIVE \u002F SHORT DRX\\n(Проміжне очікування \u002F Tail State)\" as Tail #fef08a : Середнє споживання (~400-600 мВт)\\nОчікування нових пакетів перед засинанням (10-20 сек).\n\nIdle --> Connected : 1. Мережевий запит (Latency spike ~200-500мс для підйому радіо)\nConnected --> Connected : 2. Неперервна передача даних\nConnected --> Tail : 3. Завершення передачі (Таймаут неактивності)\nTail --> Connected : 4. Новий запит перехоплює радіо до засинання\nTail --> Idle : 5. Таймаут хвоста вичерпано (Перехід у сон)\n\n@enduml\n",[3799,4404,4405,4409,4413,4417,4421,4425,4429,4434,4439,4444,4448,4453,4458,4463,4468,4473,4477],{"__ignoreMap":4019},[4023,4406,4407],{"class":4025,"line":4026},[4023,4408,4029],{},[4023,4410,4411],{"class":4025,"line":4032},[4023,4412,4035],{},[4023,4414,4415],{"class":4025,"line":4038},[4023,4416,4041],{},[4023,4418,4419],{"class":4025,"line":4044},[4023,4420,4047],{},[4023,4422,4423],{"class":4025,"line":4050},[4023,4424,4053],{},[4023,4426,4427],{"class":4025,"line":4056},[4023,4428,4060],{"emptyLinePlaceholder":4059},[4023,4430,4431],{"class":4025,"line":4063},[4023,4432,4433],{},"state \"RRC IDLE\\n(Сплячий режим)\" as Idle #e2e8f0 : Мінімальне енергоспоживання (~0-10 мВт)\\nРадіоканал не виділено.\n",[4023,4435,4436],{"class":4025,"line":4069},[4023,4437,4438],{},"state \"RRC CONNECTED (High Power)\\n(Повна потужність)\" as Connected #fecaca : Максимальне енергоспоживання (~1000-2000 мВт)\\nВисока швидкість передачі даних.\n",[4023,4440,4441],{"class":4025,"line":4075},[4023,4442,4443],{},"state \"RRC INACTIVE \u002F SHORT DRX\\n(Проміжне очікування \u002F Tail State)\" as Tail #fef08a : Середнє споживання (~400-600 мВт)\\nОчікування нових пакетів перед засинанням (10-20 сек).\n",[4023,4445,4446],{"class":4025,"line":4081},[4023,4447,4060],{"emptyLinePlaceholder":4059},[4023,4449,4450],{"class":4025,"line":4087},[4023,4451,4452],{},"Idle --> Connected : 1. Мережевий запит (Latency spike ~200-500мс для підйому радіо)\n",[4023,4454,4455],{"class":4025,"line":4093},[4023,4456,4457],{},"Connected --> Connected : 2. Неперервна передача даних\n",[4023,4459,4460],{"class":4025,"line":4098},[4023,4461,4462],{},"Connected --> Tail : 3. Завершення передачі (Таймаут неактивності)\n",[4023,4464,4465],{"class":4025,"line":4104},[4023,4466,4467],{},"Tail --> Connected : 4. Новий запит перехоплює радіо до засинання\n",[4023,4469,4470],{"class":4025,"line":4110},[4023,4471,4472],{},"Tail --> Idle : 5. Таймаут хвоста вичерпано (Перехід у сон)\n",[4023,4474,4475],{"class":4025,"line":4116},[4023,4476,4060],{"emptyLinePlaceholder":4059},[4023,4478,4479],{"class":4025,"line":4121},[4023,4480,4279],{},[3774,4482,4484,4485,3829],{"id":4483},"чому-частий-polling-вбиває-батарею-ефект-хвоста-tail-energy","Чому частий Polling вбиває батарею: «Ефект хвоста» (",[3758,4486,4487],{},"Tail Energy",[3743,4489,4490],{},"Коли застосунок відправляє хоча б один крихітний пакет розміром 50 байт:",[4288,4492,4493,4504,4507],{},[3782,4494,4495,4496,4499,4500,4503],{},"Модем переходить зі стану ",[3799,4497,4498],{},"IDLE"," у стан ",[3799,4501,4502],{},"CONNECTED",". Цей перехід займає від 100 до 500 мс і супроводжується різким піком енергоспоживання.",[3782,4505,4506],{},"Пакет передається за кілька мілісекунд.",[3782,4508,4509,4510,4513,4514,4517],{},"Радіомодуль ",[3750,4511,4512],{},"не засинає миттєво",". Він залишається в проміжному стані (",[3758,4515,4516],{},"Tail State",") протягом 10–20 секунд, очікуючи на ймовірні наступні пакети від оператора.",[3743,4519,4520,4521,4524,4525,4528],{},"Якщо ваш застосунок виконує наївний polling (наприклад, опитування сервера через ",[3799,4522,4523],{},"setInterval"," кожні 15 секунд), ",[3750,4526,4527],{},"радіомодуль смартфона ніколи не перейде в режим сну (IDLE)",". Пристрій постійно спалює енергію акумулятора у фоновому режимі, що викликає нагрівання телефону та невдоволення користувачів.",[4530,4531,4532,4535,4536,4539],"warning",{},[3750,4533,4534],{},"Правило мобільного мережевого дизайну:"," Замість частих дрібних запитів об'єднуйте дані в пакети (",[3758,4537,4538],{},"Batching",") або використовуйте двонаправлені протоколи реального часу з оптимізованим серцебиттям (WebSocket, Server-Sent Events або Push-сповіщення Apple APNs \u002F Google FCM).",[3964,4541],{},[3738,4543,4545,4546,3825,4548,3829],{"id":4544},"організація-даних-клієнта-нормалізація-серверного-стану-byid-allids","Організація даних клієнта: Нормалізація серверного стану (",[3799,4547,3824],{},[3799,4549,3828],{},[3743,4551,4552],{},"Більшість REST API повертають колекції сутностей у вигляді простих масивів JSON-об'єктів:",[4014,4554,4558],{"className":4555,"code":4556,"language":4557,"meta":4019,"style":4019},"language-json shiki shiki-themes light-plus dark-plus dark-plus","[\n  { \"id\": \"t1\", \"title\": \"Карпати\", \"likes\": 12 },\n  { \"id\": \"t2\", \"title\": \"Львів\", \"likes\": 45 }\n]\n","json",[3799,4559,4560,4566,4606,4638],{"__ignoreMap":4019},[4023,4561,4562],{"class":4025,"line":4026},[4023,4563,4565],{"class":4564},"sHH4Y","[\n",[4023,4567,4568,4571,4575,4578,4582,4584,4587,4589,4592,4594,4597,4599,4603],{"class":4025,"line":4032},[4023,4569,4570],{"class":4564},"  { ",[4023,4572,4574],{"class":4573},"sLwNe","\"id\"",[4023,4576,4577],{"class":4564},": ",[4023,4579,4581],{"class":4580},"sbdoH","\"t1\"",[4023,4583,3928],{"class":4564},[4023,4585,4586],{"class":4573},"\"title\"",[4023,4588,4577],{"class":4564},[4023,4590,4591],{"class":4580},"\"Карпати\"",[4023,4593,3928],{"class":4564},[4023,4595,4596],{"class":4573},"\"likes\"",[4023,4598,4577],{"class":4564},[4023,4600,4602],{"class":4601},"sJj4R","12",[4023,4604,4605],{"class":4564}," },\n",[4023,4607,4608,4610,4612,4614,4617,4619,4621,4623,4626,4628,4630,4632,4635],{"class":4025,"line":4038},[4023,4609,4570],{"class":4564},[4023,4611,4574],{"class":4573},[4023,4613,4577],{"class":4564},[4023,4615,4616],{"class":4580},"\"t2\"",[4023,4618,3928],{"class":4564},[4023,4620,4586],{"class":4573},[4023,4622,4577],{"class":4564},[4023,4624,4625],{"class":4580},"\"Львів\"",[4023,4627,3928],{"class":4564},[4023,4629,4596],{"class":4573},[4023,4631,4577],{"class":4564},[4023,4633,4634],{"class":4601},"45",[4023,4636,4637],{"class":4564}," }\n",[4023,4639,4640],{"class":4025,"line":4044},[4023,4641,4642],{"class":4564},"]\n",[3743,4644,4645,4646,4649,4650,3836],{},"У веб-проєктах розробники часто зберігають такий масив безпосередньо в ",[3799,4647,4648],{},"useState\u003CTrip[]>([])",". Проте в мобільних застосунках із насиченими списками, деталізацією, модальними вікнами та оптимістичними оновленнями збереження звичайного масиву ",[3750,4651,4652],{},"суттєво сповільнює роботу інтерфейсу",[3774,4654,4656],{"id":4655},"чому-масиви-обєктів-це-антипатерн-для-мобільного-сховища","Чому масиви об'єктів — це антипатерн для мобільного сховища",[3743,4658,4659,4660,4663],{},"Розглянемо алгоритмічну складність типових операцій над масивом ",[3832,4661],{":inline":3834,"tex":4662},"N"," елементів:",[4288,4665,4666,4684,4695,4705],{},[3782,4667,4668,4675,4676,4679,4680,4683],{},[3750,4669,4670,4671,4674],{},"Пошук елемента за ідентифікатором (",[3799,4672,4673],{},"find","):"," Складність ",[3832,4677],{":inline":3834,"tex":4678},"O(N)",". При відкритті екрана деталей поїздки ",[3799,4681,4682],{},"\u002Ftrips\u002Ft2"," доводиться виконувати лінійний перебір усього масиву.",[3782,4685,4686,4675,4689,4691,4692,3836],{},[3750,4687,4688],{},"Оновлення одиничного елемента (наприклад, лайк або перейменування):",[3832,4690],{":inline":3834,"tex":4678},". Необхідно створити новий масив через ",[3799,4693,4694],{},"items.map(item => item.id === id ? { ...item, ...patch } : item)",[3782,4696,4697,4675,4700,3791,4702,3836],{},[3750,4698,4699],{},"Видалення сутності зі списку:",[3832,4701],{":inline":3834,"tex":4678},[3799,4703,4704],{},"items.filter(item => item.id !== id)",[3782,4706,4707,4710],{},[3750,4708,4709],{},"Розсинхронізація дубльованого стану:"," Якщо один і той самий об'єкт відображається у стрічці поїздок, у списку обраного та на екрані деталей, зміна одного екземпляра в масиві призводить до розбіжності даних на інших екранах.",[3774,4712,4714],{"id":4713},"структура-нормалізованого-стану","Структура нормалізованого стану",[3743,4716,4717],{},"Нормалізація (за аналогією з реляційними базами даних) полягає у розділенні сутностей на таблицю за ідентифікаторами та впорядкований масив ключів:",[3832,4719,4720,4721,4723],{},"\\text{NormalizedState} = { \\text{byId}: \\text{Record}\\langle\\text{ID}, \\text{Entity}\\rangle, ; \\text{allIds}: \\text{ID}",[4023,4722],{}," }",[4014,4725,4730],{"className":4726,"code":4728,"language":4729},[4727],"language-text","         СЕРВЕРНА ВІДПОВІДЬ (ARRAY)                   НОРМАЛІЗОВАНИЙ СТЕЙТ КЛІЄНТА\n  +-------------------------------------+       +-----------------------------------------+\n  | [                                   |       | {                                       |\n  |   { \"id\": \"t1\", \"title\": \"Карпати\" },|  ==> |   byId: {                               |\n  |   { \"id\": \"t2\", \"title\": \"Львів\" }   |       |     \"t1\": { \"id\": \"t1\", title: \"...\" }, |\n  | ]                                   |       |     \"t2\": { \"id\": \"t2\", title: \"...\" }  |\n  +-------------------------------------+       |   },                                    |\n                                                |   allIds: [\"t1\", \"t2\"]                  |\n                                                | }                                       |\n                                                +-----------------------------------------+\n","text",[3799,4731,4728],{"__ignoreMap":4019},[4733,4734,4735,4759],"table",{},[4736,4737,4738],"thead",{},[4739,4740,4741,4746,4752],"tr",{},[4742,4743,4745],"th",{"align":4744},"left","Операція",[4742,4747,4748,4749,3829],{"align":4744},"Масив (",[3799,4750,4751],{},"Entity[]",[4742,4753,4754,4755,3825,4757,3829],{"align":4744},"Нормалізований стан (",[3799,4756,3824],{},[3799,4758,3828],{},[4760,4761,4762,4788,4812,4837],"tbody",{},[4739,4763,4764,4773,4781],{},[4765,4766,4767],"td",{"align":4744},[3750,4768,4769,4770,3829],{},"Вибірка за ID (",[3799,4771,4772],{},"getById",[4765,4774,4775,4777,4778],{"align":4744},[3832,4776],{":inline":3834,"tex":4678}," — ",[3799,4779,4780],{},"list.find()",[4765,4782,4783,4777,4785],{"align":4744},[3832,4784],{":inline":3834,"tex":3835},[3799,4786,4787],{},"state.byId[id]",[4739,4789,4790,4798,4805],{},[4765,4791,4792],{"align":4744},[3750,4793,4794,4795,3829],{},"Оновлення сутності (",[3799,4796,4797],{},"patch",[4765,4799,4800,4777,4802],{"align":4744},[3832,4801],{":inline":3834,"tex":4678},[3799,4803,4804],{},"list.map()",[4765,4806,4807,4777,4809],{"align":4744},[3832,4808],{":inline":3834,"tex":3835},[3799,4810,4811],{},"state.byId[id] = { ...item, ...patch }",[4739,4813,4814,4822,4829],{},[4765,4815,4816],{"align":4744},[3750,4817,4818,4819,3829],{},"Видалення сутності (",[3799,4820,4821],{},"delete",[4765,4823,4824,4777,4826],{"align":4744},[3832,4825],{":inline":3834,"tex":4678},[3799,4827,4828],{},"list.filter()",[4765,4830,4831,4833,4834,4836],{"align":4744},[3832,4832],{":inline":3834,"tex":3835}," у словнику + ",[3832,4835],{":inline":3834,"tex":4678}," у масиві ID",[4739,4838,4839,4847,4854],{},[4765,4840,4841],{"align":4744},[3750,4842,4843,4844,3829],{},"Перевірка наявності (",[3799,4845,4846],{},"has",[4765,4848,4849,4777,4851],{"align":4744},[3832,4850],{":inline":3834,"tex":4678},[3799,4852,4853],{},"list.some()",[4765,4855,4856,4777,4858],{"align":4744},[3832,4857],{":inline":3834,"tex":3835},[3799,4859,4860],{},"Boolean(state.byId[id])",[3774,4862,4864],{"id":4863},"практична-реалізація-утиліти-нормалізації-та-типізований-менеджер-стану","Практична реалізація: Утиліти нормалізації та типізований менеджер стану",[3743,4866,4867],{},"Розглянемо повний цикл нормалізації та денормалізації на TypeScript:",[4869,4870,4871,5717],"tabs",{},[4872,4873,4875],"tabs-item",{"label":4874},"src\u002Futils\u002Fnormalize.ts",[4014,4876,4880],{"className":4877,"code":4878,"language":4879,"meta":4019,"style":4019},"language-typescript shiki shiki-themes light-plus dark-plus dark-plus","export interface NormalizedEntities\u003CT extends { id: string }> {\n    byId: Record\u003Cstring, T>\n    allIds: string[]\n}\n\n\u002F**\n * Перетворює масив серверних сутностей у нормалізовану структуру\n *\u002F\nexport function normalizeEntities\u003CT extends { id: string }>(items: T[]): NormalizedEntities\u003CT> {\n    const byId: Record\u003Cstring, T> = {}\n    const allIds: string[] = []\n\n    for (const item of items) {\n        byId[item.id] = item\n        allIds.push(item.id)\n    }\n\n    return { byId, allIds }\n}\n\n\u002F**\n * Відновлює впорядкований масив сутностей для передачі у FlatList\n *\u002F\nexport function denormalizeEntities\u003CT extends { id: string }>(normalized: NormalizedEntities\u003CT>): T[] {\n    return normalized.allIds.map((id) => normalized.byId[id]).filter((item): item is T => Boolean(item))\n}\n\n\u002F**\n * Оновлює одну сутність зі складністю O(1)\n *\u002F\nexport function updateEntity\u003CT extends { id: string }>(\n    state: NormalizedEntities\u003CT>,\n    id: string,\n    patch: Partial\u003CT>,\n): NormalizedEntities\u003CT> {\n    const existing = state.byId[id]\n    if (!existing) return state\n\n    return {\n        ...state,\n        byId: {\n            ...state.byId,\n            [id]: { ...existing, ...patch },\n        },\n    }\n}\n\n\u002F**\n * Додає або оновлює нову сутність\n *\u002F\nexport function upsertEntity\u003CT extends { id: string }>(\n    state: NormalizedEntities\u003CT>,\n    entity: T,\n): NormalizedEntities\u003CT> {\n    const isNew = !state.byId[entity.id]\n\n    return {\n        byId: {\n            ...state.byId,\n            [entity.id]: entity,\n        },\n        allIds: isNew ? [entity.id, ...state.allIds] : state.allIds,\n    }\n}\n","typescript",[3799,4881,4882,4920,4941,4953,4957,4961,4967,4972,4977,5025,5049,5063,5067,5089,5110,5132,5136,5140,5155,5159,5163,5167,5172,5176,5220,5291,5295,5299,5303,5308,5312,5338,5354,5366,5382,5394,5417,5436,5440,5447,5456,5463,5476,5493,5498,5503,5508,5513,5518,5524,5529,5555,5570,5582,5595,5623,5628,5635,5642,5655,5666,5671,5707,5712],{"__ignoreMap":4019},[4023,4883,4884,4888,4892,4896,4899,4902,4905,4908,4912,4914,4917],{"class":4025,"line":4026},[4023,4885,4887],{"class":4886},"s8xlr","export",[4023,4889,4891],{"class":4890},"su1O8"," interface",[4023,4893,4895],{"class":4894},"sN1BT"," NormalizedEntities",[4023,4897,4898],{"class":4564},"\u003C",[4023,4900,4901],{"class":4894},"T",[4023,4903,4904],{"class":4890}," extends",[4023,4906,4907],{"class":4564}," { ",[4023,4909,4911],{"class":4910},"siwwj","id",[4023,4913,4577],{"class":4564},[4023,4915,4916],{"class":4894},"string",[4023,4918,4919],{"class":4564}," }> {\n",[4023,4921,4922,4925,4927,4930,4932,4934,4936,4938],{"class":4025,"line":4032},[4023,4923,4924],{"class":4910},"    byId",[4023,4926,4577],{"class":4564},[4023,4928,4929],{"class":4894},"Record",[4023,4931,4898],{"class":4564},[4023,4933,4916],{"class":4894},[4023,4935,3928],{"class":4564},[4023,4937,4901],{"class":4894},[4023,4939,4940],{"class":4564},">\n",[4023,4942,4943,4946,4948,4950],{"class":4025,"line":4038},[4023,4944,4945],{"class":4910},"    allIds",[4023,4947,4577],{"class":4564},[4023,4949,4916],{"class":4894},[4023,4951,4952],{"class":4564},"[]\n",[4023,4954,4955],{"class":4025,"line":4044},[4023,4956,4090],{"class":4564},[4023,4958,4959],{"class":4025,"line":4050},[4023,4960,4060],{"emptyLinePlaceholder":4059},[4023,4962,4963],{"class":4025,"line":4056},[4023,4964,4966],{"class":4965},"spJ8K","\u002F**\n",[4023,4968,4969],{"class":4025,"line":4063},[4023,4970,4971],{"class":4965}," * Перетворює масив серверних сутностей у нормалізовану структуру\n",[4023,4973,4974],{"class":4025,"line":4069},[4023,4975,4976],{"class":4965}," *\u002F\n",[4023,4978,4979,4981,4984,4988,4990,4992,4994,4996,4998,5000,5002,5005,5008,5010,5012,5015,5018,5020,5022],{"class":4025,"line":4075},[4023,4980,4887],{"class":4886},[4023,4982,4983],{"class":4890}," function",[4023,4985,4987],{"class":4986},"s8Opu"," normalizeEntities",[4023,4989,4898],{"class":4564},[4023,4991,4901],{"class":4894},[4023,4993,4904],{"class":4890},[4023,4995,4907],{"class":4564},[4023,4997,4911],{"class":4910},[4023,4999,4577],{"class":4564},[4023,5001,4916],{"class":4894},[4023,5003,5004],{"class":4564}," }>(",[4023,5006,5007],{"class":4910},"items",[4023,5009,4577],{"class":4564},[4023,5011,4901],{"class":4894},[4023,5013,5014],{"class":4564},"[]): ",[4023,5016,5017],{"class":4894},"NormalizedEntities",[4023,5019,4898],{"class":4564},[4023,5021,4901],{"class":4894},[4023,5023,5024],{"class":4564},"> {\n",[4023,5026,5027,5030,5034,5036,5038,5040,5042,5044,5046],{"class":4025,"line":4081},[4023,5028,5029],{"class":4890},"    const",[4023,5031,5033],{"class":5032},"s-QsJ"," byId",[4023,5035,4577],{"class":4564},[4023,5037,4929],{"class":4894},[4023,5039,4898],{"class":4564},[4023,5041,4916],{"class":4894},[4023,5043,3928],{"class":4564},[4023,5045,4901],{"class":4894},[4023,5047,5048],{"class":4564},"> = {}\n",[4023,5050,5051,5053,5056,5058,5060],{"class":4025,"line":4087},[4023,5052,5029],{"class":4890},[4023,5054,5055],{"class":5032}," allIds",[4023,5057,4577],{"class":4564},[4023,5059,4916],{"class":4894},[4023,5061,5062],{"class":4564},"[] = []\n",[4023,5064,5065],{"class":4025,"line":4093},[4023,5066,4060],{"emptyLinePlaceholder":4059},[4023,5068,5069,5072,5074,5077,5080,5083,5086],{"class":4025,"line":4098},[4023,5070,5071],{"class":4886},"    for",[4023,5073,3924],{"class":4564},[4023,5075,5076],{"class":4890},"const",[4023,5078,5079],{"class":5032}," item",[4023,5081,5082],{"class":4890}," of",[4023,5084,5085],{"class":4910}," items",[4023,5087,5088],{"class":4564},") {\n",[4023,5090,5091,5094,5097,5100,5102,5104,5107],{"class":4025,"line":4104},[4023,5092,5093],{"class":4910},"        byId",[4023,5095,5096],{"class":4564},"[",[4023,5098,5099],{"class":4910},"item",[4023,5101,3836],{"class":4564},[4023,5103,4911],{"class":4910},[4023,5105,5106],{"class":4564},"] = ",[4023,5108,5109],{"class":4910},"item\n",[4023,5111,5112,5115,5117,5120,5123,5125,5127,5129],{"class":4025,"line":4110},[4023,5113,5114],{"class":4910},"        allIds",[4023,5116,3836],{"class":4564},[4023,5118,5119],{"class":4986},"push",[4023,5121,5122],{"class":4564},"(",[4023,5124,5099],{"class":4910},[4023,5126,3836],{"class":4564},[4023,5128,4911],{"class":4910},[4023,5130,5131],{"class":4564},")\n",[4023,5133,5134],{"class":4025,"line":4116},[4023,5135,4147],{"class":4564},[4023,5137,5138],{"class":4025,"line":4121},[4023,5139,4060],{"emptyLinePlaceholder":4059},[4023,5141,5142,5145,5147,5149,5151,5153],{"class":4025,"line":4126},[4023,5143,5144],{"class":4886},"    return",[4023,5146,4907],{"class":4564},[4023,5148,3824],{"class":4910},[4023,5150,3928],{"class":4564},[4023,5152,3828],{"class":4910},[4023,5154,4637],{"class":4564},[4023,5156,5157],{"class":4025,"line":4132},[4023,5158,4090],{"class":4564},[4023,5160,5161],{"class":4025,"line":4138},[4023,5162,4060],{"emptyLinePlaceholder":4059},[4023,5164,5165],{"class":4025,"line":4144},[4023,5166,4966],{"class":4965},[4023,5168,5169],{"class":4025,"line":4150},[4023,5170,5171],{"class":4965}," * Відновлює впорядкований масив сутностей для передачі у FlatList\n",[4023,5173,5174],{"class":4025,"line":4156},[4023,5175,4976],{"class":4965},[4023,5177,5178,5180,5182,5185,5187,5189,5191,5193,5195,5197,5199,5201,5204,5206,5208,5210,5212,5215,5217],{"class":4025,"line":4162},[4023,5179,4887],{"class":4886},[4023,5181,4983],{"class":4890},[4023,5183,5184],{"class":4986}," denormalizeEntities",[4023,5186,4898],{"class":4564},[4023,5188,4901],{"class":4894},[4023,5190,4904],{"class":4890},[4023,5192,4907],{"class":4564},[4023,5194,4911],{"class":4910},[4023,5196,4577],{"class":4564},[4023,5198,4916],{"class":4894},[4023,5200,5004],{"class":4564},[4023,5202,5203],{"class":4910},"normalized",[4023,5205,4577],{"class":4564},[4023,5207,5017],{"class":4894},[4023,5209,4898],{"class":4564},[4023,5211,4901],{"class":4894},[4023,5213,5214],{"class":4564},">): ",[4023,5216,4901],{"class":4894},[4023,5218,5219],{"class":4564},"[] {\n",[4023,5221,5222,5224,5227,5229,5231,5233,5236,5239,5241,5244,5247,5249,5251,5253,5255,5257,5260,5263,5265,5267,5270,5272,5275,5278,5281,5284,5286,5288],{"class":4025,"line":4167},[4023,5223,5144],{"class":4886},[4023,5225,5226],{"class":4910}," normalized",[4023,5228,3836],{"class":4564},[4023,5230,3828],{"class":4910},[4023,5232,3836],{"class":4564},[4023,5234,5235],{"class":4986},"map",[4023,5237,5238],{"class":4564},"((",[4023,5240,4911],{"class":4910},[4023,5242,5243],{"class":4564},") ",[4023,5245,5246],{"class":4890},"=>",[4023,5248,5226],{"class":4910},[4023,5250,3836],{"class":4564},[4023,5252,3824],{"class":4910},[4023,5254,5096],{"class":4564},[4023,5256,4911],{"class":4910},[4023,5258,5259],{"class":4564},"]).",[4023,5261,5262],{"class":4986},"filter",[4023,5264,5238],{"class":4564},[4023,5266,5099],{"class":4910},[4023,5268,5269],{"class":4564},"): ",[4023,5271,5099],{"class":4910},[4023,5273,5274],{"class":4890}," is",[4023,5276,5277],{"class":4894}," T",[4023,5279,5280],{"class":4890}," =>",[4023,5282,5283],{"class":4986}," Boolean",[4023,5285,5122],{"class":4564},[4023,5287,5099],{"class":4910},[4023,5289,5290],{"class":4564},"))\n",[4023,5292,5293],{"class":4025,"line":4173},[4023,5294,4090],{"class":4564},[4023,5296,5297],{"class":4025,"line":4179},[4023,5298,4060],{"emptyLinePlaceholder":4059},[4023,5300,5301],{"class":4025,"line":4184},[4023,5302,4966],{"class":4965},[4023,5304,5305],{"class":4025,"line":4189},[4023,5306,5307],{"class":4965}," * Оновлює одну сутність зі складністю O(1)\n",[4023,5309,5310],{"class":4025,"line":4195},[4023,5311,4976],{"class":4965},[4023,5313,5314,5316,5318,5321,5323,5325,5327,5329,5331,5333,5335],{"class":4025,"line":4201},[4023,5315,4887],{"class":4886},[4023,5317,4983],{"class":4890},[4023,5319,5320],{"class":4986}," updateEntity",[4023,5322,4898],{"class":4564},[4023,5324,4901],{"class":4894},[4023,5326,4904],{"class":4890},[4023,5328,4907],{"class":4564},[4023,5330,4911],{"class":4910},[4023,5332,4577],{"class":4564},[4023,5334,4916],{"class":4894},[4023,5336,5337],{"class":4564}," }>(\n",[4023,5339,5340,5343,5345,5347,5349,5351],{"class":4025,"line":4207},[4023,5341,5342],{"class":4910},"    state",[4023,5344,4577],{"class":4564},[4023,5346,5017],{"class":4894},[4023,5348,4898],{"class":4564},[4023,5350,4901],{"class":4894},[4023,5352,5353],{"class":4564},">,\n",[4023,5355,5356,5359,5361,5363],{"class":4025,"line":4213},[4023,5357,5358],{"class":4910},"    id",[4023,5360,4577],{"class":4564},[4023,5362,4916],{"class":4894},[4023,5364,5365],{"class":4564},",\n",[4023,5367,5368,5371,5373,5376,5378,5380],{"class":4025,"line":4219},[4023,5369,5370],{"class":4910},"    patch",[4023,5372,4577],{"class":4564},[4023,5374,5375],{"class":4894},"Partial",[4023,5377,4898],{"class":4564},[4023,5379,4901],{"class":4894},[4023,5381,5353],{"class":4564},[4023,5383,5384,5386,5388,5390,5392],{"class":4025,"line":4225},[4023,5385,5269],{"class":4564},[4023,5387,5017],{"class":4894},[4023,5389,4898],{"class":4564},[4023,5391,4901],{"class":4894},[4023,5393,5024],{"class":4564},[4023,5395,5396,5398,5401,5404,5407,5409,5411,5413,5415],{"class":4025,"line":4230},[4023,5397,5029],{"class":4890},[4023,5399,5400],{"class":5032}," existing",[4023,5402,5403],{"class":4564}," = ",[4023,5405,5406],{"class":4910},"state",[4023,5408,3836],{"class":4564},[4023,5410,3824],{"class":4910},[4023,5412,5096],{"class":4564},[4023,5414,4911],{"class":4910},[4023,5416,4642],{"class":4564},[4023,5418,5419,5422,5425,5428,5430,5433],{"class":4025,"line":4235},[4023,5420,5421],{"class":4886},"    if",[4023,5423,5424],{"class":4564}," (!",[4023,5426,5427],{"class":4910},"existing",[4023,5429,5243],{"class":4564},[4023,5431,5432],{"class":4886},"return",[4023,5434,5435],{"class":4910}," state\n",[4023,5437,5438],{"class":4025,"line":4241},[4023,5439,4060],{"emptyLinePlaceholder":4059},[4023,5441,5442,5444],{"class":4025,"line":4247},[4023,5443,5144],{"class":4886},[4023,5445,5446],{"class":4564}," {\n",[4023,5448,5449,5452,5454],{"class":4025,"line":4253},[4023,5450,5451],{"class":4564},"        ...",[4023,5453,5406],{"class":4910},[4023,5455,5365],{"class":4564},[4023,5457,5458,5461],{"class":4025,"line":4259},[4023,5459,5460],{"class":4910},"        byId:",[4023,5462,5446],{"class":4564},[4023,5464,5465,5468,5470,5472,5474],{"class":4025,"line":4265},[4023,5466,5467],{"class":4564},"            ...",[4023,5469,5406],{"class":4910},[4023,5471,3836],{"class":4564},[4023,5473,3824],{"class":4910},[4023,5475,5365],{"class":4564},[4023,5477,5478,5481,5484,5486,5489,5491],{"class":4025,"line":4271},[4023,5479,5480],{"class":4910},"            [id]:",[4023,5482,5483],{"class":4564}," { ...",[4023,5485,5427],{"class":4910},[4023,5487,5488],{"class":4564},", ...",[4023,5490,4797],{"class":4910},[4023,5492,4605],{"class":4564},[4023,5494,5495],{"class":4025,"line":4276},[4023,5496,5497],{"class":4564},"        },\n",[4023,5499,5501],{"class":4025,"line":5500},45,[4023,5502,4147],{"class":4564},[4023,5504,5506],{"class":4025,"line":5505},46,[4023,5507,4090],{"class":4564},[4023,5509,5511],{"class":4025,"line":5510},47,[4023,5512,4060],{"emptyLinePlaceholder":4059},[4023,5514,5516],{"class":4025,"line":5515},48,[4023,5517,4966],{"class":4965},[4023,5519,5521],{"class":4025,"line":5520},49,[4023,5522,5523],{"class":4965}," * Додає або оновлює нову сутність\n",[4023,5525,5527],{"class":4025,"line":5526},50,[4023,5528,4976],{"class":4965},[4023,5530,5532,5534,5536,5539,5541,5543,5545,5547,5549,5551,5553],{"class":4025,"line":5531},51,[4023,5533,4887],{"class":4886},[4023,5535,4983],{"class":4890},[4023,5537,5538],{"class":4986}," upsertEntity",[4023,5540,4898],{"class":4564},[4023,5542,4901],{"class":4894},[4023,5544,4904],{"class":4890},[4023,5546,4907],{"class":4564},[4023,5548,4911],{"class":4910},[4023,5550,4577],{"class":4564},[4023,5552,4916],{"class":4894},[4023,5554,5337],{"class":4564},[4023,5556,5558,5560,5562,5564,5566,5568],{"class":4025,"line":5557},52,[4023,5559,5342],{"class":4910},[4023,5561,4577],{"class":4564},[4023,5563,5017],{"class":4894},[4023,5565,4898],{"class":4564},[4023,5567,4901],{"class":4894},[4023,5569,5353],{"class":4564},[4023,5571,5573,5576,5578,5580],{"class":4025,"line":5572},53,[4023,5574,5575],{"class":4910},"    entity",[4023,5577,4577],{"class":4564},[4023,5579,4901],{"class":4894},[4023,5581,5365],{"class":4564},[4023,5583,5585,5587,5589,5591,5593],{"class":4025,"line":5584},54,[4023,5586,5269],{"class":4564},[4023,5588,5017],{"class":4894},[4023,5590,4898],{"class":4564},[4023,5592,4901],{"class":4894},[4023,5594,5024],{"class":4564},[4023,5596,5598,5600,5603,5606,5608,5610,5612,5614,5617,5619,5621],{"class":4025,"line":5597},55,[4023,5599,5029],{"class":4890},[4023,5601,5602],{"class":5032}," isNew",[4023,5604,5605],{"class":4564}," = !",[4023,5607,5406],{"class":4910},[4023,5609,3836],{"class":4564},[4023,5611,3824],{"class":4910},[4023,5613,5096],{"class":4564},[4023,5615,5616],{"class":4910},"entity",[4023,5618,3836],{"class":4564},[4023,5620,4911],{"class":4910},[4023,5622,4642],{"class":4564},[4023,5624,5626],{"class":4025,"line":5625},56,[4023,5627,4060],{"emptyLinePlaceholder":4059},[4023,5629,5631,5633],{"class":4025,"line":5630},57,[4023,5632,5144],{"class":4886},[4023,5634,5446],{"class":4564},[4023,5636,5638,5640],{"class":4025,"line":5637},58,[4023,5639,5460],{"class":4910},[4023,5641,5446],{"class":4564},[4023,5643,5645,5647,5649,5651,5653],{"class":4025,"line":5644},59,[4023,5646,5467],{"class":4564},[4023,5648,5406],{"class":4910},[4023,5650,3836],{"class":4564},[4023,5652,3824],{"class":4910},[4023,5654,5365],{"class":4564},[4023,5656,5658,5661,5664],{"class":4025,"line":5657},60,[4023,5659,5660],{"class":4910},"            [entity.id]:",[4023,5662,5663],{"class":4910}," entity",[4023,5665,5365],{"class":4564},[4023,5667,5669],{"class":4025,"line":5668},61,[4023,5670,5497],{"class":4564},[4023,5672,5674,5677,5679,5682,5684,5686,5688,5690,5692,5694,5696,5699,5701,5703,5705],{"class":4025,"line":5673},62,[4023,5675,5676],{"class":4910},"        allIds:",[4023,5678,5602],{"class":4910},[4023,5680,5681],{"class":4564}," ? [",[4023,5683,5616],{"class":4910},[4023,5685,3836],{"class":4564},[4023,5687,4911],{"class":4910},[4023,5689,5488],{"class":4564},[4023,5691,5406],{"class":4910},[4023,5693,3836],{"class":4564},[4023,5695,3828],{"class":4910},[4023,5697,5698],{"class":4564},"] : ",[4023,5700,5406],{"class":4910},[4023,5702,3836],{"class":4564},[4023,5704,3828],{"class":4910},[4023,5706,5365],{"class":4564},[4023,5708,5710],{"class":4025,"line":5709},63,[4023,5711,4147],{"class":4564},[4023,5713,5715],{"class":4025,"line":5714},64,[4023,5716,4090],{"class":4564},[4872,5718,5720],{"label":5719},"Приклад використання в компоненті",[4014,5721,5725],{"className":5722,"code":5723,"language":5724,"meta":4019,"style":4019},"language-tsx shiki shiki-themes light-plus dark-plus dark-plus","import React, { useState } from 'react'\nimport { View, Text, Button } from 'react-native'\nimport { normalizeEntities, updateEntity, denormalizeEntities, NormalizedEntities } from '.\u002Fnormalize'\n\ninterface Trip {\n    id: string\n    title: string\n    likes: number\n}\n\nexport function TripManager() {\n    const rawApiData: Trip[] = [\n        { id: '1', title: 'Карпати', likes: 10 },\n        { id: '2', title: 'Львів', likes: 25 },\n    ]\n\n    \u002F\u002F Ініціалізуємо стан у нормалізованій формі\n    const [tripsState, setTripsState] = useState\u003CNormalizedEntities\u003CTrip>>(() => normalizeEntities(rawApiData))\n\n    \u002F\u002F Швидка вибірка O(1) для будь-якого екрана чи модалки\n    const selectedTrip = tripsState.byId['2']\n\n    \u002F\u002F Оптимістичне миттєве оновлення O(1)\n    const handleLike = (tripId: string) => {\n        setTripsState((prevState) => {\n            const current = prevState.byId[tripId]\n            if (!current) return prevState\n            return updateEntity(prevState, tripId, { likes: current.likes + 1 })\n        })\n    }\n\n    \u002F\u002F Денормалізація лише для рендерингу FlatList\n    const listForRender = denormalizeEntities(tripsState)\n\n    return (\n        \u003CView style={{ padding: 16 }}>\n            \u003CText style={{ fontSize: 18, fontWeight: 'bold' }}>Вибрана поїздка: {selectedTrip?.title}\u003C\u002FText>\n            {listForRender.map((trip) => (\n                \u003CView key={trip.id} style={{ marginVertical: 8 }}>\n                    \u003CText>\n                        {trip.title} — Лайків: {trip.likes}\n                    \u003C\u002FText>\n                    \u003CButton title=\"👍 Лайкнути\" onPress={() => handleLike(trip.id)} \u002F>\n                \u003C\u002FView>\n            ))}\n        \u003C\u002FView>\n    )\n}\n","tsx",[3799,5726,5727,5750,5776,5806,5810,5820,5829,5838,5848,5852,5856,5868,5883,5912,5937,5942,5946,5951,5992,5996,6001,6023,6027,6032,6055,6071,6093,6110,6144,6149,6153,6157,6162,6179,6183,6190,6225,6284,6307,6349,6358,6384,6393,6436,6445,6452,6461,6466],{"__ignoreMap":4019},[4023,5728,5729,5732,5735,5738,5741,5744,5747],{"class":4025,"line":4026},[4023,5730,5731],{"class":4886},"import",[4023,5733,5734],{"class":4910}," React",[4023,5736,5737],{"class":4564},", { ",[4023,5739,5740],{"class":4910},"useState",[4023,5742,5743],{"class":4564}," } ",[4023,5745,5746],{"class":4886},"from",[4023,5748,5749],{"class":4580}," 'react'\n",[4023,5751,5752,5754,5756,5759,5761,5764,5766,5769,5771,5773],{"class":4025,"line":4032},[4023,5753,5731],{"class":4886},[4023,5755,4907],{"class":4564},[4023,5757,5758],{"class":4910},"View",[4023,5760,3928],{"class":4564},[4023,5762,5763],{"class":4910},"Text",[4023,5765,3928],{"class":4564},[4023,5767,5768],{"class":4910},"Button",[4023,5770,5743],{"class":4564},[4023,5772,5746],{"class":4886},[4023,5774,5775],{"class":4580}," 'react-native'\n",[4023,5777,5778,5780,5782,5785,5787,5790,5792,5795,5797,5799,5801,5803],{"class":4025,"line":4038},[4023,5779,5731],{"class":4886},[4023,5781,4907],{"class":4564},[4023,5783,5784],{"class":4910},"normalizeEntities",[4023,5786,3928],{"class":4564},[4023,5788,5789],{"class":4910},"updateEntity",[4023,5791,3928],{"class":4564},[4023,5793,5794],{"class":4910},"denormalizeEntities",[4023,5796,3928],{"class":4564},[4023,5798,5017],{"class":4910},[4023,5800,5743],{"class":4564},[4023,5802,5746],{"class":4886},[4023,5804,5805],{"class":4580}," '.\u002Fnormalize'\n",[4023,5807,5808],{"class":4025,"line":4044},[4023,5809,4060],{"emptyLinePlaceholder":4059},[4023,5811,5812,5815,5818],{"class":4025,"line":4050},[4023,5813,5814],{"class":4890},"interface",[4023,5816,5817],{"class":4894}," Trip",[4023,5819,5446],{"class":4564},[4023,5821,5822,5824,5826],{"class":4025,"line":4056},[4023,5823,5358],{"class":4910},[4023,5825,4577],{"class":4564},[4023,5827,5828],{"class":4894},"string\n",[4023,5830,5831,5834,5836],{"class":4025,"line":4063},[4023,5832,5833],{"class":4910},"    title",[4023,5835,4577],{"class":4564},[4023,5837,5828],{"class":4894},[4023,5839,5840,5843,5845],{"class":4025,"line":4069},[4023,5841,5842],{"class":4910},"    likes",[4023,5844,4577],{"class":4564},[4023,5846,5847],{"class":4894},"number\n",[4023,5849,5850],{"class":4025,"line":4075},[4023,5851,4090],{"class":4564},[4023,5853,5854],{"class":4025,"line":4081},[4023,5855,4060],{"emptyLinePlaceholder":4059},[4023,5857,5858,5860,5862,5865],{"class":4025,"line":4087},[4023,5859,4887],{"class":4886},[4023,5861,4983],{"class":4890},[4023,5863,5864],{"class":4986}," TripManager",[4023,5866,5867],{"class":4564},"() {\n",[4023,5869,5870,5872,5875,5877,5880],{"class":4025,"line":4093},[4023,5871,5029],{"class":4890},[4023,5873,5874],{"class":5032}," rawApiData",[4023,5876,4577],{"class":4564},[4023,5878,5879],{"class":4894},"Trip",[4023,5881,5882],{"class":4564},"[] = [\n",[4023,5884,5885,5888,5891,5894,5896,5899,5902,5904,5907,5910],{"class":4025,"line":4098},[4023,5886,5887],{"class":4564},"        { ",[4023,5889,5890],{"class":4910},"id:",[4023,5892,5893],{"class":4580}," '1'",[4023,5895,3928],{"class":4564},[4023,5897,5898],{"class":4910},"title:",[4023,5900,5901],{"class":4580}," 'Карпати'",[4023,5903,3928],{"class":4564},[4023,5905,5906],{"class":4910},"likes:",[4023,5908,5909],{"class":4601}," 10",[4023,5911,4605],{"class":4564},[4023,5913,5914,5916,5918,5921,5923,5925,5928,5930,5932,5935],{"class":4025,"line":4104},[4023,5915,5887],{"class":4564},[4023,5917,5890],{"class":4910},[4023,5919,5920],{"class":4580}," '2'",[4023,5922,3928],{"class":4564},[4023,5924,5898],{"class":4910},[4023,5926,5927],{"class":4580}," 'Львів'",[4023,5929,3928],{"class":4564},[4023,5931,5906],{"class":4910},[4023,5933,5934],{"class":4601}," 25",[4023,5936,4605],{"class":4564},[4023,5938,5939],{"class":4025,"line":4110},[4023,5940,5941],{"class":4564},"    ]\n",[4023,5943,5944],{"class":4025,"line":4116},[4023,5945,4060],{"emptyLinePlaceholder":4059},[4023,5947,5948],{"class":4025,"line":4121},[4023,5949,5950],{"class":4965},"    \u002F\u002F Ініціалізуємо стан у нормалізованій формі\n",[4023,5952,5953,5955,5958,5961,5963,5966,5968,5970,5972,5974,5976,5978,5981,5983,5985,5987,5990],{"class":4025,"line":4126},[4023,5954,5029],{"class":4890},[4023,5956,5957],{"class":4564}," [",[4023,5959,5960],{"class":5032},"tripsState",[4023,5962,3928],{"class":4564},[4023,5964,5965],{"class":5032},"setTripsState",[4023,5967,5106],{"class":4564},[4023,5969,5740],{"class":4986},[4023,5971,4898],{"class":4564},[4023,5973,5017],{"class":4894},[4023,5975,4898],{"class":4564},[4023,5977,5879],{"class":4894},[4023,5979,5980],{"class":4564},">>(() ",[4023,5982,5246],{"class":4890},[4023,5984,4987],{"class":4986},[4023,5986,5122],{"class":4564},[4023,5988,5989],{"class":4910},"rawApiData",[4023,5991,5290],{"class":4564},[4023,5993,5994],{"class":4025,"line":4132},[4023,5995,4060],{"emptyLinePlaceholder":4059},[4023,5997,5998],{"class":4025,"line":4138},[4023,5999,6000],{"class":4965},"    \u002F\u002F Швидка вибірка O(1) для будь-якого екрана чи модалки\n",[4023,6002,6003,6005,6008,6010,6012,6014,6016,6018,6021],{"class":4025,"line":4144},[4023,6004,5029],{"class":4890},[4023,6006,6007],{"class":5032}," selectedTrip",[4023,6009,5403],{"class":4564},[4023,6011,5960],{"class":4910},[4023,6013,3836],{"class":4564},[4023,6015,3824],{"class":4910},[4023,6017,5096],{"class":4564},[4023,6019,6020],{"class":4580},"'2'",[4023,6022,4642],{"class":4564},[4023,6024,6025],{"class":4025,"line":4150},[4023,6026,4060],{"emptyLinePlaceholder":4059},[4023,6028,6029],{"class":4025,"line":4156},[4023,6030,6031],{"class":4965},"    \u002F\u002F Оптимістичне миттєве оновлення O(1)\n",[4023,6033,6034,6036,6039,6042,6045,6047,6049,6051,6053],{"class":4025,"line":4162},[4023,6035,5029],{"class":4890},[4023,6037,6038],{"class":4986}," handleLike",[4023,6040,6041],{"class":4564}," = (",[4023,6043,6044],{"class":4910},"tripId",[4023,6046,4577],{"class":4564},[4023,6048,4916],{"class":4894},[4023,6050,5243],{"class":4564},[4023,6052,5246],{"class":4890},[4023,6054,5446],{"class":4564},[4023,6056,6057,6060,6062,6065,6067,6069],{"class":4025,"line":4167},[4023,6058,6059],{"class":4986},"        setTripsState",[4023,6061,5238],{"class":4564},[4023,6063,6064],{"class":4910},"prevState",[4023,6066,5243],{"class":4564},[4023,6068,5246],{"class":4890},[4023,6070,5446],{"class":4564},[4023,6072,6073,6076,6079,6081,6083,6085,6087,6089,6091],{"class":4025,"line":4173},[4023,6074,6075],{"class":4890},"            const",[4023,6077,6078],{"class":5032}," current",[4023,6080,5403],{"class":4564},[4023,6082,6064],{"class":4910},[4023,6084,3836],{"class":4564},[4023,6086,3824],{"class":4910},[4023,6088,5096],{"class":4564},[4023,6090,6044],{"class":4910},[4023,6092,4642],{"class":4564},[4023,6094,6095,6098,6100,6103,6105,6107],{"class":4025,"line":4179},[4023,6096,6097],{"class":4886},"            if",[4023,6099,5424],{"class":4564},[4023,6101,6102],{"class":4910},"current",[4023,6104,5243],{"class":4564},[4023,6106,5432],{"class":4886},[4023,6108,6109],{"class":4910}," prevState\n",[4023,6111,6112,6115,6117,6119,6121,6123,6125,6127,6129,6131,6133,6136,6138,6141],{"class":4025,"line":4184},[4023,6113,6114],{"class":4886},"            return",[4023,6116,5320],{"class":4986},[4023,6118,5122],{"class":4564},[4023,6120,6064],{"class":4910},[4023,6122,3928],{"class":4564},[4023,6124,6044],{"class":4910},[4023,6126,5737],{"class":4564},[4023,6128,5906],{"class":4910},[4023,6130,6078],{"class":4910},[4023,6132,3836],{"class":4564},[4023,6134,6135],{"class":4910},"likes",[4023,6137,3825],{"class":4564},[4023,6139,6140],{"class":4601},"1",[4023,6142,6143],{"class":4564}," })\n",[4023,6145,6146],{"class":4025,"line":4189},[4023,6147,6148],{"class":4564},"        })\n",[4023,6150,6151],{"class":4025,"line":4195},[4023,6152,4147],{"class":4564},[4023,6154,6155],{"class":4025,"line":4201},[4023,6156,4060],{"emptyLinePlaceholder":4059},[4023,6158,6159],{"class":4025,"line":4207},[4023,6160,6161],{"class":4965},"    \u002F\u002F Денормалізація лише для рендерингу FlatList\n",[4023,6163,6164,6166,6169,6171,6173,6175,6177],{"class":4025,"line":4213},[4023,6165,5029],{"class":4890},[4023,6167,6168],{"class":5032}," listForRender",[4023,6170,5403],{"class":4564},[4023,6172,5794],{"class":4986},[4023,6174,5122],{"class":4564},[4023,6176,5960],{"class":4910},[4023,6178,5131],{"class":4564},[4023,6180,6181],{"class":4025,"line":4219},[4023,6182,4060],{"emptyLinePlaceholder":4059},[4023,6184,6185,6187],{"class":4025,"line":4225},[4023,6186,5144],{"class":4886},[4023,6188,6189],{"class":4564}," (\n",[4023,6191,6192,6196,6198,6202,6205,6208,6212,6215,6218,6220,6223],{"class":4025,"line":4230},[4023,6193,6195],{"class":6194},"s0P7L","        \u003C",[4023,6197,5758],{"class":4894},[4023,6199,6201],{"class":6200},"sa4r_"," style",[4023,6203,6204],{"class":4564},"=",[4023,6206,6207],{"class":4890},"{",[4023,6209,6211],{"class":6210},"sD7JJ","{ ",[4023,6213,6214],{"class":4910},"padding:",[4023,6216,6217],{"class":4601}," 16",[4023,6219,4723],{"class":6210},[4023,6221,6222],{"class":4890},"}",[4023,6224,4940],{"class":6194},[4023,6226,6227,6230,6232,6234,6236,6238,6240,6243,6246,6248,6251,6254,6256,6258,6261,6264,6266,6269,6272,6275,6277,6280,6282],{"class":4025,"line":4235},[4023,6228,6229],{"class":6194},"            \u003C",[4023,6231,5763],{"class":4894},[4023,6233,6201],{"class":6200},[4023,6235,6204],{"class":4564},[4023,6237,6207],{"class":4890},[4023,6239,6211],{"class":6210},[4023,6241,6242],{"class":4910},"fontSize:",[4023,6244,6245],{"class":4601}," 18",[4023,6247,3928],{"class":6210},[4023,6249,6250],{"class":4910},"fontWeight:",[4023,6252,6253],{"class":4580}," 'bold'",[4023,6255,4723],{"class":6210},[4023,6257,6222],{"class":4890},[4023,6259,6260],{"class":6194},">",[4023,6262,6263],{"class":4564},"Вибрана поїздка: ",[4023,6265,6207],{"class":4890},[4023,6267,6268],{"class":4910},"selectedTrip",[4023,6270,6271],{"class":6210},"?.",[4023,6273,6274],{"class":4910},"title",[4023,6276,6222],{"class":4890},[4023,6278,6279],{"class":6194},"\u003C\u002F",[4023,6281,5763],{"class":4894},[4023,6283,4940],{"class":6194},[4023,6285,6286,6289,6292,6294,6296,6298,6301,6303,6305],{"class":4025,"line":4241},[4023,6287,6288],{"class":4890},"            {",[4023,6290,6291],{"class":4910},"listForRender",[4023,6293,3836],{"class":6210},[4023,6295,5235],{"class":4986},[4023,6297,5238],{"class":6210},[4023,6299,6300],{"class":4910},"trip",[4023,6302,5243],{"class":6210},[4023,6304,5246],{"class":4890},[4023,6306,6189],{"class":6210},[4023,6308,6309,6312,6314,6317,6319,6321,6323,6325,6327,6329,6331,6333,6335,6337,6340,6343,6345,6347],{"class":4025,"line":4247},[4023,6310,6311],{"class":6194},"                \u003C",[4023,6313,5758],{"class":4894},[4023,6315,6316],{"class":6200}," key",[4023,6318,6204],{"class":4564},[4023,6320,6207],{"class":4890},[4023,6322,6300],{"class":4910},[4023,6324,3836],{"class":6210},[4023,6326,4911],{"class":4910},[4023,6328,6222],{"class":4890},[4023,6330,6201],{"class":6200},[4023,6332,6204],{"class":4564},[4023,6334,6207],{"class":4890},[4023,6336,6211],{"class":6210},[4023,6338,6339],{"class":4910},"marginVertical:",[4023,6341,6342],{"class":4601}," 8",[4023,6344,4723],{"class":6210},[4023,6346,6222],{"class":4890},[4023,6348,4940],{"class":6194},[4023,6350,6351,6354,6356],{"class":4025,"line":4253},[4023,6352,6353],{"class":6194},"                    \u003C",[4023,6355,5763],{"class":4894},[4023,6357,4940],{"class":6194},[4023,6359,6360,6363,6365,6367,6369,6371,6374,6376,6378,6380,6382],{"class":4025,"line":4259},[4023,6361,6362],{"class":4890},"                        {",[4023,6364,6300],{"class":4910},[4023,6366,3836],{"class":6210},[4023,6368,6274],{"class":4910},[4023,6370,6222],{"class":4890},[4023,6372,6373],{"class":6210}," — Лайків: ",[4023,6375,6207],{"class":4890},[4023,6377,6300],{"class":4910},[4023,6379,3836],{"class":6210},[4023,6381,6135],{"class":4910},[4023,6383,4090],{"class":4890},[4023,6385,6386,6389,6391],{"class":4025,"line":4265},[4023,6387,6388],{"class":6194},"                    \u003C\u002F",[4023,6390,5763],{"class":4894},[4023,6392,4940],{"class":6194},[4023,6394,6395,6397,6399,6402,6404,6407,6410,6412,6414,6417,6419,6421,6423,6425,6427,6429,6431,6433],{"class":4025,"line":4271},[4023,6396,6353],{"class":6194},[4023,6398,5768],{"class":4894},[4023,6400,6401],{"class":6200}," title",[4023,6403,6204],{"class":4564},[4023,6405,6406],{"class":4580},"\"👍 Лайкнути\"",[4023,6408,6409],{"class":6200}," onPress",[4023,6411,6204],{"class":4564},[4023,6413,6207],{"class":4890},[4023,6415,6416],{"class":6210},"() ",[4023,6418,5246],{"class":4890},[4023,6420,6038],{"class":4986},[4023,6422,5122],{"class":6210},[4023,6424,6300],{"class":4910},[4023,6426,3836],{"class":6210},[4023,6428,4911],{"class":4910},[4023,6430,3829],{"class":6210},[4023,6432,6222],{"class":4890},[4023,6434,6435],{"class":6194}," \u002F>\n",[4023,6437,6438,6441,6443],{"class":4025,"line":4276},[4023,6439,6440],{"class":6194},"                \u003C\u002F",[4023,6442,5758],{"class":4894},[4023,6444,4940],{"class":6194},[4023,6446,6447,6450],{"class":4025,"line":5500},[4023,6448,6449],{"class":6210},"            ))",[4023,6451,4090],{"class":4890},[4023,6453,6454,6457,6459],{"class":4025,"line":5505},[4023,6455,6456],{"class":6194},"        \u003C\u002F",[4023,6458,5758],{"class":4894},[4023,6460,4940],{"class":6194},[4023,6462,6463],{"class":4025,"line":5510},[4023,6464,6465],{"class":4564},"    )\n",[4023,6467,6468],{"class":4025,"line":5515},[4023,6469,4090],{"class":4564},[3964,6471],{},[3738,6473,6475,6476,6478,6479],{"id":6474},"http-клієнти-fetch-проти-axios","HTTP-клієнти: ",[3799,6477,3844],{}," проти ",[3799,6480,3850],{},[3743,6482,6483,6484,6486,6487,6489],{},"У React Native стандартною є наявність глобальної функції ",[3799,6485,3844],{},", що відповідає стандарту W3C\u002FWHATWG Fetch API. Проте в реальних проєктах частіше використовують перевірені бібліотеки, такі як ",[3799,6488,3850],{},". Розглянемо ключову різницю між ними.",[3774,6491,6493,6494,6496],{"id":6492},"реалізація-fetch-у-react-native-та-підводні-камені","Реалізація ",[3799,6495,3844],{}," у React Native та підводні камені",[3743,6498,6499,6500,6502,6503,3924,6506,6509,6510,6512],{},"Поліфіл ",[3799,6501,3844],{}," у React Native побудований поверх внутрішньої нативної реалізації ",[3799,6504,6505],{},"XMLHttpRequest",[3799,6507,6508],{},"RCTNetworking","). Незважаючи на стандартизований інтерфейс, пряме використання ",[3799,6511,3844],{}," у мобільних додатках створює низку практичних проблем:",[6514,6515,6516,6543,6574],"field-group",{},[6517,6518,6521,6522,6524,6525,6528,6529,6532,6533,6535,6536,5243,6539,6542],"field",{"name":6519,"type":6520},"1. Відсутність таймауту за замовчуванням","Проблема \u002F Обмеження","Специфікація ",[3799,6523,3844],{}," не містить параметра ",[3799,6526,6527],{},"timeout",". Якщо смартфон увійшов у тунель метро, де TCP-пакети губляться без явного скидання з'єднання (",[3758,6530,6531],{},"TCP Silent Drop","), Promise запиту ",[3799,6534,3844],{}," перебуватиме у стані очікування (",[3758,6537,6538],{},"Pending",[3750,6540,6541],{},"невизначено довго"," (іноді до 10–15 хвилин, поки операційна система не розірве сокет).",[6517,6544,6546,6548,6549,5243,6552,6555,6556,3928,6559,6562,6563,6566,6567,6570,6571,3836],{"name":6545,"type":6520},"2. Помилкова семантика HTTP-статусів",[3799,6547,3844],{}," переходить у стан відхилення (",[3758,6550,6551],{},"Rejected",[3750,6553,6554],{},"лише при фізичній помилці мережі"," (відсутній DNS, немає з'єднання). Якщо сервер повернув відповідь ",[3799,6557,6558],{},"401 Unauthorized",[3799,6560,6561],{},"404 Not Found"," або ",[3799,6564,6565],{},"500 Internal Server Error",", Promise переходить у стан ",[3799,6568,6569],{},"Resolved",". Розробник зобов'язаний вручну перевіряти властивість ",[3799,6572,6573],{},"response.ok",[6517,6575,6577,6578,6581,6582,6585,6586,3836],{"name":6576,"type":6520},"3. Ручна двокрокова десеріалізація","Для отримання JSON-структури потрібно викликати ",[3799,6579,6580],{},"await response.json()",". Якщо сервер через аварію проксі-сервера (наприклад, Nginx) повернув HTML-сторінку з помилкою замість JSON, виклик ",[3799,6583,6584],{},".json()"," впаде з неінформативною синтаксичною помилкою ",[3799,6587,6588],{},"SyntaxError: Unexpected token \u003C in JSON at position 0",[3774,6590,6592,6593],{"id":6591},"керування-таймаутом-і-життєвим-циклом-запиту-через-abortcontroller","Керування таймаутом і життєвим циклом запиту через ",[3799,6594,3860],{},[3743,6596,6597,6598,6600,6601,4397],{},"Для безпечного використання ",[3799,6599,3844],{}," у мобільному застосунку необхідно вручну зв'язувати його з екземпляром ",[3750,6602,6603],{},[3799,6604,3860],{},[4014,6606,6608],{"className":4877,"code":6607,"language":4879,"meta":4019,"style":4019},"\u002F**\n * Виконує безпечний HTTP-запит з гарантованим таймаутом та обробкою HTTP-помилок\n *\u002F\nasync function executeFetchWithTimeout\u003CT>(\n    url: string,\n    options: RequestInit = {},\n    timeoutMs: number = 8000,\n): Promise\u003CT> {\n    const controller = new AbortController()\n    const { signal } = controller\n\n    \u002F\u002F Реєструємо таймер примусового скасування запиту\n    const timerId = setTimeout(() => {\n        controller.abort()\n    }, timeoutMs)\n\n    try {\n        const response = await fetch(url, {\n            ...options,\n            signal,\n            headers: {\n                Accept: 'application\u002Fjson',\n                'Content-Type': 'application\u002Fjson',\n                ...options.headers,\n            },\n        })\n\n        clearTimeout(timerId)\n\n        \u002F\u002F Валідація діапазону успішних статусів (200-299)\n        if (!response.ok) {\n            throw new Error(`HTTP_${response.status}: ${response.statusText}`)\n        }\n\n        const contentType = response.headers.get('content-type')\n        if (!contentType || !contentType.includes('application\u002Fjson')) {\n            throw new Error('INVALID_CONTENT_TYPE: Очікувався application\u002Fjson')\n        }\n\n        return (await response.json()) as T\n    } catch (error: unknown) {\n        clearTimeout(timerId)\n\n        if (error instanceof Error && error.name === 'AbortError') {\n            throw new Error(`TIMEOUT: Запит перевищив ліміт очікування у ${timeoutMs} мс`)\n        }\n\n        throw error\n    }\n}\n",[3799,6609,6610,6614,6619,6623,6640,6651,6664,6681,6694,6712,6727,6731,6736,6755,6767,6777,6781,6788,6812,6821,6828,6835,6845,6856,6870,6875,6879,6883,6895,6899,6904,6921,6967,6972,6976,7003,7030,7045,7049,7053,7077,7097,7107,7111,7142,7166,7170,7174,7182,7186],{"__ignoreMap":4019},[4023,6611,6612],{"class":4025,"line":4026},[4023,6613,4966],{"class":4965},[4023,6615,6616],{"class":4025,"line":4032},[4023,6617,6618],{"class":4965}," * Виконує безпечний HTTP-запит з гарантованим таймаутом та обробкою HTTP-помилок\n",[4023,6620,6621],{"class":4025,"line":4038},[4023,6622,4976],{"class":4965},[4023,6624,6625,6628,6630,6633,6635,6637],{"class":4025,"line":4044},[4023,6626,6627],{"class":4890},"async",[4023,6629,4983],{"class":4890},[4023,6631,6632],{"class":4986}," executeFetchWithTimeout",[4023,6634,4898],{"class":4564},[4023,6636,4901],{"class":4894},[4023,6638,6639],{"class":4564},">(\n",[4023,6641,6642,6645,6647,6649],{"class":4025,"line":4050},[4023,6643,6644],{"class":4910},"    url",[4023,6646,4577],{"class":4564},[4023,6648,4916],{"class":4894},[4023,6650,5365],{"class":4564},[4023,6652,6653,6656,6658,6661],{"class":4025,"line":4056},[4023,6654,6655],{"class":4910},"    options",[4023,6657,4577],{"class":4564},[4023,6659,6660],{"class":4894},"RequestInit",[4023,6662,6663],{"class":4564}," = {},\n",[4023,6665,6666,6669,6671,6674,6676,6679],{"class":4025,"line":4063},[4023,6667,6668],{"class":4910},"    timeoutMs",[4023,6670,4577],{"class":4564},[4023,6672,6673],{"class":4894},"number",[4023,6675,5403],{"class":4564},[4023,6677,6678],{"class":4601},"8000",[4023,6680,5365],{"class":4564},[4023,6682,6683,6685,6688,6690,6692],{"class":4025,"line":4069},[4023,6684,5269],{"class":4564},[4023,6686,6687],{"class":4894},"Promise",[4023,6689,4898],{"class":4564},[4023,6691,4901],{"class":4894},[4023,6693,5024],{"class":4564},[4023,6695,6696,6698,6701,6703,6706,6709],{"class":4025,"line":4075},[4023,6697,5029],{"class":4890},[4023,6699,6700],{"class":5032}," controller",[4023,6702,5403],{"class":4564},[4023,6704,6705],{"class":4890},"new",[4023,6707,6708],{"class":4986}," AbortController",[4023,6710,6711],{"class":4564},"()\n",[4023,6713,6714,6716,6718,6721,6724],{"class":4025,"line":4081},[4023,6715,5029],{"class":4890},[4023,6717,4907],{"class":4564},[4023,6719,6720],{"class":5032},"signal",[4023,6722,6723],{"class":4564}," } = ",[4023,6725,6726],{"class":4910},"controller\n",[4023,6728,6729],{"class":4025,"line":4087},[4023,6730,4060],{"emptyLinePlaceholder":4059},[4023,6732,6733],{"class":4025,"line":4093},[4023,6734,6735],{"class":4965},"    \u002F\u002F Реєструємо таймер примусового скасування запиту\n",[4023,6737,6738,6740,6743,6745,6748,6751,6753],{"class":4025,"line":4098},[4023,6739,5029],{"class":4890},[4023,6741,6742],{"class":5032}," timerId",[4023,6744,5403],{"class":4564},[4023,6746,6747],{"class":4986},"setTimeout",[4023,6749,6750],{"class":4564},"(() ",[4023,6752,5246],{"class":4890},[4023,6754,5446],{"class":4564},[4023,6756,6757,6760,6762,6765],{"class":4025,"line":4104},[4023,6758,6759],{"class":4910},"        controller",[4023,6761,3836],{"class":4564},[4023,6763,6764],{"class":4986},"abort",[4023,6766,6711],{"class":4564},[4023,6768,6769,6772,6775],{"class":4025,"line":4110},[4023,6770,6771],{"class":4564},"    }, ",[4023,6773,6774],{"class":4910},"timeoutMs",[4023,6776,5131],{"class":4564},[4023,6778,6779],{"class":4025,"line":4116},[4023,6780,4060],{"emptyLinePlaceholder":4059},[4023,6782,6783,6786],{"class":4025,"line":4121},[4023,6784,6785],{"class":4886},"    try",[4023,6787,5446],{"class":4564},[4023,6789,6790,6793,6796,6798,6801,6804,6806,6809],{"class":4025,"line":4126},[4023,6791,6792],{"class":4890},"        const",[4023,6794,6795],{"class":5032}," response",[4023,6797,5403],{"class":4564},[4023,6799,6800],{"class":4886},"await",[4023,6802,6803],{"class":4986}," fetch",[4023,6805,5122],{"class":4564},[4023,6807,6808],{"class":4910},"url",[4023,6810,6811],{"class":4564},", {\n",[4023,6813,6814,6816,6819],{"class":4025,"line":4132},[4023,6815,5467],{"class":4564},[4023,6817,6818],{"class":4910},"options",[4023,6820,5365],{"class":4564},[4023,6822,6823,6826],{"class":4025,"line":4138},[4023,6824,6825],{"class":4910},"            signal",[4023,6827,5365],{"class":4564},[4023,6829,6830,6833],{"class":4025,"line":4144},[4023,6831,6832],{"class":4910},"            headers:",[4023,6834,5446],{"class":4564},[4023,6836,6837,6840,6843],{"class":4025,"line":4150},[4023,6838,6839],{"class":4910},"                Accept:",[4023,6841,6842],{"class":4580}," 'application\u002Fjson'",[4023,6844,5365],{"class":4564},[4023,6846,6847,6850,6852,6854],{"class":4025,"line":4156},[4023,6848,6849],{"class":4580},"                'Content-Type'",[4023,6851,4397],{"class":4910},[4023,6853,6842],{"class":4580},[4023,6855,5365],{"class":4564},[4023,6857,6858,6861,6863,6865,6868],{"class":4025,"line":4162},[4023,6859,6860],{"class":4564},"                ...",[4023,6862,6818],{"class":4910},[4023,6864,3836],{"class":4564},[4023,6866,6867],{"class":4910},"headers",[4023,6869,5365],{"class":4564},[4023,6871,6872],{"class":4025,"line":4167},[4023,6873,6874],{"class":4564},"            },\n",[4023,6876,6877],{"class":4025,"line":4173},[4023,6878,6148],{"class":4564},[4023,6880,6881],{"class":4025,"line":4179},[4023,6882,4060],{"emptyLinePlaceholder":4059},[4023,6884,6885,6888,6890,6893],{"class":4025,"line":4184},[4023,6886,6887],{"class":4986},"        clearTimeout",[4023,6889,5122],{"class":4564},[4023,6891,6892],{"class":4910},"timerId",[4023,6894,5131],{"class":4564},[4023,6896,6897],{"class":4025,"line":4189},[4023,6898,4060],{"emptyLinePlaceholder":4059},[4023,6900,6901],{"class":4025,"line":4195},[4023,6902,6903],{"class":4965},"        \u002F\u002F Валідація діапазону успішних статусів (200-299)\n",[4023,6905,6906,6909,6911,6914,6916,6919],{"class":4025,"line":4201},[4023,6907,6908],{"class":4886},"        if",[4023,6910,5424],{"class":4564},[4023,6912,6913],{"class":4910},"response",[4023,6915,3836],{"class":4564},[4023,6917,6918],{"class":4910},"ok",[4023,6920,5088],{"class":4564},[4023,6922,6923,6926,6929,6932,6934,6937,6940,6942,6944,6947,6949,6951,6953,6955,6957,6960,6962,6965],{"class":4025,"line":4207},[4023,6924,6925],{"class":4886},"            throw",[4023,6927,6928],{"class":4890}," new",[4023,6930,6931],{"class":4986}," Error",[4023,6933,5122],{"class":4564},[4023,6935,6936],{"class":4580},"`HTTP_",[4023,6938,6939],{"class":4890},"${",[4023,6941,6913],{"class":4910},[4023,6943,3836],{"class":6210},[4023,6945,6946],{"class":4910},"status",[4023,6948,6222],{"class":4890},[4023,6950,4577],{"class":4580},[4023,6952,6939],{"class":4890},[4023,6954,6913],{"class":4910},[4023,6956,3836],{"class":6210},[4023,6958,6959],{"class":4910},"statusText",[4023,6961,6222],{"class":4890},[4023,6963,6964],{"class":4580},"`",[4023,6966,5131],{"class":4564},[4023,6968,6969],{"class":4025,"line":4213},[4023,6970,6971],{"class":4564},"        }\n",[4023,6973,6974],{"class":4025,"line":4219},[4023,6975,4060],{"emptyLinePlaceholder":4059},[4023,6977,6978,6980,6983,6985,6987,6989,6991,6993,6996,6998,7001],{"class":4025,"line":4225},[4023,6979,6792],{"class":4890},[4023,6981,6982],{"class":5032}," contentType",[4023,6984,5403],{"class":4564},[4023,6986,6913],{"class":4910},[4023,6988,3836],{"class":4564},[4023,6990,6867],{"class":4910},[4023,6992,3836],{"class":4564},[4023,6994,6995],{"class":4986},"get",[4023,6997,5122],{"class":4564},[4023,6999,7000],{"class":4580},"'content-type'",[4023,7002,5131],{"class":4564},[4023,7004,7005,7007,7009,7012,7015,7017,7019,7022,7024,7027],{"class":4025,"line":4230},[4023,7006,6908],{"class":4886},[4023,7008,5424],{"class":4564},[4023,7010,7011],{"class":4910},"contentType",[4023,7013,7014],{"class":4564}," || !",[4023,7016,7011],{"class":4910},[4023,7018,3836],{"class":4564},[4023,7020,7021],{"class":4986},"includes",[4023,7023,5122],{"class":4564},[4023,7025,7026],{"class":4580},"'application\u002Fjson'",[4023,7028,7029],{"class":4564},")) {\n",[4023,7031,7032,7034,7036,7038,7040,7043],{"class":4025,"line":4235},[4023,7033,6925],{"class":4886},[4023,7035,6928],{"class":4890},[4023,7037,6931],{"class":4986},[4023,7039,5122],{"class":4564},[4023,7041,7042],{"class":4580},"'INVALID_CONTENT_TYPE: Очікувався application\u002Fjson'",[4023,7044,5131],{"class":4564},[4023,7046,7047],{"class":4025,"line":4241},[4023,7048,6971],{"class":4564},[4023,7050,7051],{"class":4025,"line":4247},[4023,7052,4060],{"emptyLinePlaceholder":4059},[4023,7054,7055,7058,7060,7062,7064,7066,7068,7071,7074],{"class":4025,"line":4253},[4023,7056,7057],{"class":4886},"        return",[4023,7059,3924],{"class":4564},[4023,7061,6800],{"class":4886},[4023,7063,6795],{"class":4910},[4023,7065,3836],{"class":4564},[4023,7067,4557],{"class":4986},[4023,7069,7070],{"class":4564},"()) ",[4023,7072,7073],{"class":4886},"as",[4023,7075,7076],{"class":4894}," T\n",[4023,7078,7079,7082,7085,7087,7090,7092,7095],{"class":4025,"line":4259},[4023,7080,7081],{"class":4564},"    } ",[4023,7083,7084],{"class":4886},"catch",[4023,7086,3924],{"class":4564},[4023,7088,7089],{"class":4910},"error",[4023,7091,4577],{"class":4564},[4023,7093,7094],{"class":4894},"unknown",[4023,7096,5088],{"class":4564},[4023,7098,7099,7101,7103,7105],{"class":4025,"line":4265},[4023,7100,6887],{"class":4986},[4023,7102,5122],{"class":4564},[4023,7104,6892],{"class":4910},[4023,7106,5131],{"class":4564},[4023,7108,7109],{"class":4025,"line":4271},[4023,7110,4060],{"emptyLinePlaceholder":4059},[4023,7112,7113,7115,7117,7119,7122,7124,7127,7129,7131,7134,7137,7140],{"class":4025,"line":4276},[4023,7114,6908],{"class":4886},[4023,7116,3924],{"class":4564},[4023,7118,7089],{"class":4910},[4023,7120,7121],{"class":4890}," instanceof",[4023,7123,6931],{"class":4894},[4023,7125,7126],{"class":4564}," && ",[4023,7128,7089],{"class":4910},[4023,7130,3836],{"class":4564},[4023,7132,7133],{"class":4910},"name",[4023,7135,7136],{"class":4564}," === ",[4023,7138,7139],{"class":4580},"'AbortError'",[4023,7141,5088],{"class":4564},[4023,7143,7144,7146,7148,7150,7152,7155,7157,7159,7161,7164],{"class":4025,"line":5500},[4023,7145,6925],{"class":4886},[4023,7147,6928],{"class":4890},[4023,7149,6931],{"class":4986},[4023,7151,5122],{"class":4564},[4023,7153,7154],{"class":4580},"`TIMEOUT: Запит перевищив ліміт очікування у ",[4023,7156,6939],{"class":4890},[4023,7158,6774],{"class":4910},[4023,7160,6222],{"class":4890},[4023,7162,7163],{"class":4580}," мс`",[4023,7165,5131],{"class":4564},[4023,7167,7168],{"class":4025,"line":5505},[4023,7169,6971],{"class":4564},[4023,7171,7172],{"class":4025,"line":5510},[4023,7173,4060],{"emptyLinePlaceholder":4059},[4023,7175,7176,7179],{"class":4025,"line":5515},[4023,7177,7178],{"class":4886},"        throw",[4023,7180,7181],{"class":4910}," error\n",[4023,7183,7184],{"class":4025,"line":5520},[4023,7185,4147],{"class":4564},[4023,7187,7188],{"class":4025,"line":5526},[4023,7189,4090],{"class":4564},[3774,7191,7193,7194],{"id":7192},"чому-на-практиці-обирають-axios","Чому на практиці обирають ",[3799,7195,3850],{},[3743,7197,7198,7199,7201],{},"Бібліотека ",[3799,7200,3850],{}," вирішує ці проблеми з коробки завдяки зручному API:",[3779,7203,7204,7217,7229,7243],{},[3782,7205,7206,7209,7210,7213,7214,3836],{},[3750,7207,7208],{},"Декларативні таймаути:"," Властивість ",[3799,7211,7212],{},"timeout: 8000"," на рівні інстансу або запиту автоматично скасовує операцію за допомогою коду помилки ",[3799,7215,7216],{},"ECONNABORTED",[3782,7218,7219,7222,7223,7225,7226,3836],{},[3750,7220,7221],{},"Автоматична десеріалізація та трансформація:"," ",[3799,7224,3850],{}," самостійно парсить JSON і надає дані у властивості ",[3799,7227,7228],{},"response.data",[3782,7230,7231,7234,7235,7238,7239,7242],{},[3750,7232,7233],{},"Єдина модель винятків:"," Будь-який HTTP-статус за межами діапазону ",[3799,7236,7237],{},"2xx"," автоматично генерує об'єкт ",[3799,7240,7241],{},"AxiosError",", який містить повний контекст (запит, статус, відповідь).",[3782,7244,7245,7248,7249,3761],{},[3750,7246,7247],{},"Конвеєр перехоплювачів (Interceptors Pipeline):"," Дозволяє модифікувати конфігурацію запитів перед відправкою (додавання Bearer токенів) та перехоплювати відповіді перед передачею в компоненти (централізована обробка викликів ",[3799,7250,6558],{},[3774,7252,7254],{"id":7253},"порівняльний-аналіз-можливостей","Порівняльний аналіз можливостей",[4733,7256,7257,7273],{},[4736,7258,7259],{},[4739,7260,7261,7264,7269],{},[4742,7262,7263],{"align":4744},"Критерій порівняння",[4742,7265,7266,7267],{"align":4744},"Нативний ",[3799,7268,3844],{},[4742,7270,7198,7271],{"align":4744},[3799,7272,3850],{},[4760,7274,7275,7288,7306,7326,7342,7361],{},[4739,7276,7277,7282,7285],{},[4765,7278,7279],{"align":4744},[3750,7280,7281],{},"Розмір бандла",[4765,7283,7284],{"align":4744},"0 КБ (вбудований у рантайм)",[4765,7286,7287],{"align":4744},"~12–15 КБ (незначно для мобільного бандла)",[4739,7289,7290,7295,7300],{},[4765,7291,7292],{"align":4744},[3750,7293,7294],{},"Керування таймаутом",[4765,7296,7297,7298],{"align":4744},"Лише через ручний ",[3799,7299,3860],{},[4765,7301,7302,7303,7305],{"align":4744},"Вбудоване поле ",[3799,7304,6527],{}," у конфігурації",[4739,7307,7308,7313,7321],{},[4765,7309,7310],{"align":4744},[3750,7311,7312],{},"Поведінка при HTTP 4xx\u002F5xx",[4765,7314,7315,7316,3928,7318,3829],{"align":4744},"Вважає успіхом (",[3799,7317,6569],{},[3799,7319,7320],{},"ok: false",[4765,7322,7323,7324,3829],{"align":4744},"Генерує виняток (",[3799,7325,6551],{},[4739,7327,7328,7333,7336],{},[4765,7329,7330],{"align":4744},[3750,7331,7332],{},"Глобальні інтерцептори",[4765,7334,7335],{"align":4744},"Потребує власної обгортки-монкипатчингу",[4765,7337,7338,7339],{"align":4744},"Нативна підтримка ",[3799,7340,7341],{},"interceptors.request \u002F response",[4739,7343,7344,7349,7358],{},[4765,7345,7346],{"align":4744},[3750,7347,7348],{},"Трансформація тіла (Body)",[4765,7350,7351,7352,7355,7356],{"align":4744},"Вимагає ручного ",[3799,7353,7354],{},"JSON.stringify()"," \u002F ",[3799,7357,6584],{},[4765,7359,7360],{"align":4744},"Автоматична серіалізація\u002Fдесеріалізація JSON",[4739,7362,7363,7367,7373],{},[4765,7364,7365],{"align":4744},[3750,7366,1959],{},[4765,7368,7369,7370,3829],{"align":4744},"Складно (через ",[3799,7371,7372],{},"ReadableStream",[4765,7374,7375,7376,3928,7379],{"align":4744},"Вбудовані колбеки ",[3799,7377,7378],{},"onUploadProgress",[3799,7380,7381],{},"onDownloadProgress",[3964,7383],{},[3738,7385,7387,7388],{"id":7386},"налаштування-мережевого-клієнта-на-базі-axios","Налаштування мережевого клієнта на базі ",[3799,7389,3850],{},[3743,7391,7392],{},"Побудуємо надійний клієнт, що враховує специфіку мобільних операційних систем, середовища емуляторів, автентифікації та черги оновлення токенів.",[3774,7394,7396,7397,7400],{"id":7395},"проблема-мережевої-адресації-localhost-на-мобільних-пристроях","Проблема мережевої адресації: ",[3799,7398,7399],{},"localhost"," на мобільних пристроях",[3743,7402,7403,7404,7407,7408,7411],{},"Початківці часто стикаються з помилкою ",[3799,7405,7406],{},"Network Error"," при спробі звернутися за адресою ",[3799,7409,7410],{},"http:\u002F\u002Flocalhost:3000",". Необхідно розуміти мережеву топологію середовищ:",[3779,7413,7414,7423,7438],{},[3782,7415,7416,7419,7420,7422],{},[3750,7417,7418],{},"iOS Simulator:"," Працює як звичайний процес у macOS і ділить з хостом єдиний мережевий стек. ",[3799,7421,7410],{}," працює коректно.",[3782,7424,7425,7428,7429,7432,7433,3836],{},[3750,7426,7427],{},"Android Emulator:"," Запущений всередині віртуальної машини QEMU з власним ізольованим мережевим маршрутизатором. Для нього ",[3799,7430,7431],{},"127.0.0.1"," — це внутрішній інтерфейс віртуального Android-пристрою. Звернення до хост-машини розробника здійснюється за зарезервованою IP-адресою ",[3750,7434,7435],{},[3799,7436,7437],{},"http:\u002F\u002F10.0.2.2:3000",[3782,7439,7440,7443,7444,7447,7448,7451],{},[3750,7441,7442],{},"Фізичний пристрій (iOS\u002FAndroid):"," Знаходиться в локальній мережі Wi-Fi. Для доступу до комп'ютера розробника потрібна явна IP-адреса хоста в локальній мережі (наприклад, ",[3799,7445,7446],{},"http:\u002F\u002F192.168.1.150:3000","). Пакет ",[3799,7449,7450],{},"expo-constants"," дозволяє отримати її динамічно з маніфесту сервера Metro.",[3774,7453,7455],{"id":7454},"черга-оновлення-токенів-refresh-token-mutex-queue","Черга оновлення токенів (Refresh Token Mutex Queue)",[3743,7457,7458,7459,7461,7462,7468],{},"Коли термін дії Access Token закінчується, клієнт отримує помилку ",[3799,7460,6558],{},". Якщо на екрані одночасно ініціалізується 5 паралельних запитів (наприклад, профіль, список поїздок, сповіщення, баланс і налаштування), без належної координації клієнт надішле ",[3750,7463,7464,7465],{},"5 одночасних запитів ",[3799,7466,7467],{},"\u002Fauth\u002Frefresh",". Це призведе до інвалідації сесії на бекенді через десинхронізацію Refresh Token.",[3743,7470,7471,7472,4397],{},"Для розв'язання цієї проблеми створюється ",[3750,7473,7474],{},"черга на базі промісів (Promise-based Mutex Queue)",[4011,7476,7477],{},[4014,7478,7480],{"className":4016,"code":7479,"language":4018,"meta":4019,"style":4019},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam defaultFontName \"Helvetica\"\nskinparam defaultFontSize 12\n\nactor \"UI Components\" as UI #bfdbfe\nparticipant \"Axios Interceptor\" as Interceptor #fef08a\nparticipant \"Auth Token Service\" as Auth #fed7aa\nparticipant \"Backend API\" as API #fecaca\n\nUI -> Interceptor : 1. Request A & Request B (Expired Token)\nInterceptor -> API : 2. GET \u002Ftrips & GET \u002Fprofile\nAPI --> Interceptor : 3. 401 Unauthorized (Обидва запити)\n\nnote over Interceptor\n  **Запит A стає ініціатором оновлення (isRefreshing = true).**\n  **Запит B стає в чергу очікування (subscribeTokenRefresh).**\nend note\n\nInterceptor -> Auth : 4. POST \u002Fauth\u002Frefresh (Один єдиний запит!)\nAuth --> Interceptor : 5. 200 OK (New Access Token)\n\nnote over Interceptor\n  **Розблокування черги:**\n  Всі накопичені запити отримують новий токен\n  і повторюються автоматично!\nend note\n\nInterceptor -> API : 6. Re-execute Request A with New Token\nInterceptor -> API : 7. Re-execute Request B with New Token\nAPI --> UI : 8. Успішні дані на UI екрани\n@enduml\n",[3799,7481,7482,7486,7490,7494,7498,7502,7506,7511,7516,7521,7526,7530,7535,7540,7545,7549,7554,7559,7564,7568,7572,7577,7582,7586,7590,7595,7600,7605,7609,7613,7618,7623,7628],{"__ignoreMap":4019},[4023,7483,7484],{"class":4025,"line":4026},[4023,7485,4029],{},[4023,7487,7488],{"class":4025,"line":4032},[4023,7489,4035],{},[4023,7491,7492],{"class":4025,"line":4038},[4023,7493,4041],{},[4023,7495,7496],{"class":4025,"line":4044},[4023,7497,4047],{},[4023,7499,7500],{"class":4025,"line":4050},[4023,7501,4053],{},[4023,7503,7504],{"class":4025,"line":4056},[4023,7505,4060],{"emptyLinePlaceholder":4059},[4023,7507,7508],{"class":4025,"line":4063},[4023,7509,7510],{},"actor \"UI Components\" as UI #bfdbfe\n",[4023,7512,7513],{"class":4025,"line":4069},[4023,7514,7515],{},"participant \"Axios Interceptor\" as Interceptor #fef08a\n",[4023,7517,7518],{"class":4025,"line":4075},[4023,7519,7520],{},"participant \"Auth Token Service\" as Auth #fed7aa\n",[4023,7522,7523],{"class":4025,"line":4081},[4023,7524,7525],{},"participant \"Backend API\" as API #fecaca\n",[4023,7527,7528],{"class":4025,"line":4087},[4023,7529,4060],{"emptyLinePlaceholder":4059},[4023,7531,7532],{"class":4025,"line":4093},[4023,7533,7534],{},"UI -> Interceptor : 1. Request A & Request B (Expired Token)\n",[4023,7536,7537],{"class":4025,"line":4098},[4023,7538,7539],{},"Interceptor -> API : 2. GET \u002Ftrips & GET \u002Fprofile\n",[4023,7541,7542],{"class":4025,"line":4104},[4023,7543,7544],{},"API --> Interceptor : 3. 401 Unauthorized (Обидва запити)\n",[4023,7546,7547],{"class":4025,"line":4110},[4023,7548,4060],{"emptyLinePlaceholder":4059},[4023,7550,7551],{"class":4025,"line":4116},[4023,7552,7553],{},"note over Interceptor\n",[4023,7555,7556],{"class":4025,"line":4121},[4023,7557,7558],{},"  **Запит A стає ініціатором оновлення (isRefreshing = true).**\n",[4023,7560,7561],{"class":4025,"line":4126},[4023,7562,7563],{},"  **Запит B стає в чергу очікування (subscribeTokenRefresh).**\n",[4023,7565,7566],{"class":4025,"line":4132},[4023,7567,4268],{},[4023,7569,7570],{"class":4025,"line":4138},[4023,7571,4060],{"emptyLinePlaceholder":4059},[4023,7573,7574],{"class":4025,"line":4144},[4023,7575,7576],{},"Interceptor -> Auth : 4. POST \u002Fauth\u002Frefresh (Один єдиний запит!)\n",[4023,7578,7579],{"class":4025,"line":4150},[4023,7580,7581],{},"Auth --> Interceptor : 5. 200 OK (New Access Token)\n",[4023,7583,7584],{"class":4025,"line":4156},[4023,7585,4060],{"emptyLinePlaceholder":4059},[4023,7587,7588],{"class":4025,"line":4162},[4023,7589,7553],{},[4023,7591,7592],{"class":4025,"line":4167},[4023,7593,7594],{},"  **Розблокування черги:**\n",[4023,7596,7597],{"class":4025,"line":4173},[4023,7598,7599],{},"  Всі накопичені запити отримують новий токен\n",[4023,7601,7602],{"class":4025,"line":4179},[4023,7603,7604],{},"  і повторюються автоматично!\n",[4023,7606,7607],{"class":4025,"line":4184},[4023,7608,4268],{},[4023,7610,7611],{"class":4025,"line":4189},[4023,7612,4060],{"emptyLinePlaceholder":4059},[4023,7614,7615],{"class":4025,"line":4195},[4023,7616,7617],{},"Interceptor -> API : 6. Re-execute Request A with New Token\n",[4023,7619,7620],{"class":4025,"line":4201},[4023,7621,7622],{},"Interceptor -> API : 7. Re-execute Request B with New Token\n",[4023,7624,7625],{"class":4025,"line":4207},[4023,7626,7627],{},"API --> UI : 8. Успішні дані на UI екрани\n",[4023,7629,7630],{"class":4025,"line":4213},[4023,7631,4279],{},[3743,7633,7634],{},"Розглянемо повну реалізацію мережевого клієнта з чергою токенів:",[4869,7636,7637,9119],{},[4872,7638,7640],{"label":7639},"src\u002Fapi\u002Fclient.ts",[4014,7641,7643],{"className":4877,"code":7642,"language":4879,"meta":4019,"style":4019},"import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios'\nimport { Platform } from 'react-native'\nimport Constants from 'expo-constants'\n\nfunction resolveBaseUrl(): string {\n    if (Platform.OS === 'android' && !Constants.isDevice) {\n        return 'http:\u002F\u002F10.0.2.2:3000\u002Fapi\u002Fv1'\n    }\n\n    const hostUri = Constants.expoConfig?.hostUri\n    if (hostUri) {\n        const hostIp = hostUri.split(':')[0]\n        return `http:\u002F\u002F${hostIp}:3000\u002Fapi\u002Fv1`\n    }\n\n    return 'http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fv1'\n}\n\nexport const apiClient: AxiosInstance = axios.create({\n    baseURL: resolveBaseUrl(),\n    timeout: 8000,\n    headers: {\n        Accept: 'application\u002Fjson',\n        'Content-Type': 'application\u002Fjson',\n        'X-Client-Platform': Platform.OS,\n        'X-Client-Version': Constants.expoConfig?.version ?? '1.0.0',\n    },\n})\n\n\u002F\u002F Змінні стану оновлення сесії\nlet isRefreshing = false\nlet failedQueue: Array\u003C{\n    resolve: (token: string) => void\n    reject: (error: unknown) => void\n}> = []\n\nconst processQueue = (error: unknown, token: string | null = null) => {\n    failedQueue.forEach((prom) => {\n        if (error) {\n            prom.reject(error)\n        } else if (token) {\n            prom.resolve(token)\n        }\n    })\n    failedQueue = []\n}\n\n\u002F\u002F 1. Інтерцептор запитів: Додавання токена авторизації\napiClient.interceptors.request.use(\n    async (config: InternalAxiosRequestConfig) => {\n        const token: string | null = 'mock-access-token' \u002F\u002F У реальному додатку: await SecureStore.getItemAsync('token')\n        if (token && config.headers) {\n            config.headers.Authorization = `Bearer ${token}`\n        }\n        return config\n    },\n    (error) => Promise.reject(error),\n)\n\n\u002F\u002F 2. Інтерцептор відповідей: Черга оновлення токена при 401\napiClient.interceptors.response.use(\n    (response) => response,\n    async (error: AxiosError) => {\n        const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }\n\n        \u002F\u002F Якщо помилка не 401 або запит уже був повторений - повертаємо виняток\n        if (error.response?.status !== 401 || !originalRequest || originalRequest._retry) {\n            return Promise.reject(error)\n        }\n\n        \u002F\u002F Якщо оновлення вже відбувається іншим запитом, ставимо цей запит у чергу\n        if (isRefreshing) {\n            return new Promise((resolve, reject) => {\n                failedQueue.push({\n                    resolve: (newToken: string) => {\n                        if (originalRequest.headers) {\n                            originalRequest.headers.Authorization = `Bearer ${newToken}`\n                        }\n                        resolve(apiClient(originalRequest))\n                    },\n                    reject: (err: unknown) => {\n                        reject(err)\n                    },\n                })\n            })\n        }\n\n        originalRequest._retry = true\n        isRefreshing = true\n\n        try {\n            \u002F\u002F Виконуємо запит на оновлення токена\n            \u002F\u002F Важливо: використовуємо базовий axios, щоб оминути інтерцептори клієнта!\n            const refreshResponse = await axios.post\u003C{ accessToken: string }>(\n                `${resolveBaseUrl()}\u002Fauth\u002Frefresh`,\n                { refreshToken: 'mock-refresh-token' },\n                { timeout: 5000 },\n            )\n\n            const newAccessToken = refreshResponse.data.accessToken\n\n            \u002F\u002F Зберігаємо новий токен у безпечне сховище\n            \u002F\u002F await SecureStore.setItemAsync('token', newAccessToken)\n\n            processQueue(null, newAccessToken)\n\n            if (originalRequest.headers) {\n                originalRequest.headers.Authorization = `Bearer ${newAccessToken}`\n            }\n\n            return apiClient(originalRequest)\n        } catch (refreshError) {\n            processQueue(refreshError, null)\n            \u002F\u002F Тут ініціюється вихід із системи (Logout)\n            return Promise.reject(refreshError)\n        } finally {\n            isRefreshing = false\n        }\n    },\n)\n",[3799,7644,7645,7673,7688,7701,7705,7720,7751,7758,7762,7766,7787,7798,7827,7844,7848,7852,7859,7863,7867,7893,7903,7913,7920,7929,7940,7956,7982,7987,7992,7996,8001,8014,8029,8051,8070,8075,8079,8118,8139,8149,8165,8182,8197,8201,8206,8213,8217,8221,8226,8248,8268,8291,8309,8337,8341,8348,8352,8377,8381,8385,8390,8408,8422,8440,8475,8480,8486,8525,8542,8547,8552,8558,8570,8593,8605,8628,8644,8670,8676,8692,8698,8721,8733,8738,8744,8750,8755,8760,8775,8785,8790,8798,8804,8810,8841,8862,8876,8889,8895,8900,8923,8928,8934,8940,8945,8962,8967,8982,9008,9014,9019,9032,9046,9061,9067,9084,9094,9104,9109,9114],{"__ignoreMap":4019},[4023,7646,7647,7649,7652,7654,7657,7659,7661,7663,7666,7668,7670],{"class":4025,"line":4026},[4023,7648,5731],{"class":4886},[4023,7650,7651],{"class":4910}," axios",[4023,7653,5737],{"class":4564},[4023,7655,7656],{"class":4910},"AxiosInstance",[4023,7658,3928],{"class":4564},[4023,7660,7241],{"class":4910},[4023,7662,3928],{"class":4564},[4023,7664,7665],{"class":4910},"InternalAxiosRequestConfig",[4023,7667,5743],{"class":4564},[4023,7669,5746],{"class":4886},[4023,7671,7672],{"class":4580}," 'axios'\n",[4023,7674,7675,7677,7679,7682,7684,7686],{"class":4025,"line":4032},[4023,7676,5731],{"class":4886},[4023,7678,4907],{"class":4564},[4023,7680,7681],{"class":4910},"Platform",[4023,7683,5743],{"class":4564},[4023,7685,5746],{"class":4886},[4023,7687,5775],{"class":4580},[4023,7689,7690,7692,7695,7698],{"class":4025,"line":4038},[4023,7691,5731],{"class":4886},[4023,7693,7694],{"class":4910}," Constants",[4023,7696,7697],{"class":4886}," from",[4023,7699,7700],{"class":4580}," 'expo-constants'\n",[4023,7702,7703],{"class":4025,"line":4044},[4023,7704,4060],{"emptyLinePlaceholder":4059},[4023,7706,7707,7710,7713,7716,7718],{"class":4025,"line":4050},[4023,7708,7709],{"class":4890},"function",[4023,7711,7712],{"class":4986}," resolveBaseUrl",[4023,7714,7715],{"class":4564},"(): ",[4023,7717,4916],{"class":4894},[4023,7719,5446],{"class":4564},[4023,7721,7722,7724,7726,7728,7730,7733,7735,7738,7741,7744,7746,7749],{"class":4025,"line":4056},[4023,7723,5421],{"class":4886},[4023,7725,3924],{"class":4564},[4023,7727,7681],{"class":4910},[4023,7729,3836],{"class":4564},[4023,7731,7732],{"class":5032},"OS",[4023,7734,7136],{"class":4564},[4023,7736,7737],{"class":4580},"'android'",[4023,7739,7740],{"class":4564}," && !",[4023,7742,7743],{"class":4910},"Constants",[4023,7745,3836],{"class":4564},[4023,7747,7748],{"class":4910},"isDevice",[4023,7750,5088],{"class":4564},[4023,7752,7753,7755],{"class":4025,"line":4063},[4023,7754,7057],{"class":4886},[4023,7756,7757],{"class":4580}," 'http:\u002F\u002F10.0.2.2:3000\u002Fapi\u002Fv1'\n",[4023,7759,7760],{"class":4025,"line":4069},[4023,7761,4147],{"class":4564},[4023,7763,7764],{"class":4025,"line":4075},[4023,7765,4060],{"emptyLinePlaceholder":4059},[4023,7767,7768,7770,7773,7775,7777,7779,7782,7784],{"class":4025,"line":4081},[4023,7769,5029],{"class":4890},[4023,7771,7772],{"class":5032}," hostUri",[4023,7774,5403],{"class":4564},[4023,7776,7743],{"class":4910},[4023,7778,3836],{"class":4564},[4023,7780,7781],{"class":4910},"expoConfig",[4023,7783,6271],{"class":4564},[4023,7785,7786],{"class":4910},"hostUri\n",[4023,7788,7789,7791,7793,7796],{"class":4025,"line":4087},[4023,7790,5421],{"class":4886},[4023,7792,3924],{"class":4564},[4023,7794,7795],{"class":4910},"hostUri",[4023,7797,5088],{"class":4564},[4023,7799,7800,7802,7805,7807,7809,7811,7814,7816,7819,7822,7825],{"class":4025,"line":4093},[4023,7801,6792],{"class":4890},[4023,7803,7804],{"class":5032}," hostIp",[4023,7806,5403],{"class":4564},[4023,7808,7795],{"class":4910},[4023,7810,3836],{"class":4564},[4023,7812,7813],{"class":4986},"split",[4023,7815,5122],{"class":4564},[4023,7817,7818],{"class":4580},"':'",[4023,7820,7821],{"class":4564},")[",[4023,7823,7824],{"class":4601},"0",[4023,7826,4642],{"class":4564},[4023,7828,7829,7831,7834,7836,7839,7841],{"class":4025,"line":4098},[4023,7830,7057],{"class":4886},[4023,7832,7833],{"class":4580}," `http:\u002F\u002F",[4023,7835,6939],{"class":4890},[4023,7837,7838],{"class":4910},"hostIp",[4023,7840,6222],{"class":4890},[4023,7842,7843],{"class":4580},":3000\u002Fapi\u002Fv1`\n",[4023,7845,7846],{"class":4025,"line":4104},[4023,7847,4147],{"class":4564},[4023,7849,7850],{"class":4025,"line":4110},[4023,7851,4060],{"emptyLinePlaceholder":4059},[4023,7853,7854,7856],{"class":4025,"line":4116},[4023,7855,5144],{"class":4886},[4023,7857,7858],{"class":4580}," 'http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fv1'\n",[4023,7860,7861],{"class":4025,"line":4121},[4023,7862,4090],{"class":4564},[4023,7864,7865],{"class":4025,"line":4126},[4023,7866,4060],{"emptyLinePlaceholder":4059},[4023,7868,7869,7871,7874,7877,7879,7881,7883,7885,7887,7890],{"class":4025,"line":4132},[4023,7870,4887],{"class":4886},[4023,7872,7873],{"class":4890}," const",[4023,7875,7876],{"class":5032}," apiClient",[4023,7878,4577],{"class":4564},[4023,7880,7656],{"class":4894},[4023,7882,5403],{"class":4564},[4023,7884,3850],{"class":4910},[4023,7886,3836],{"class":4564},[4023,7888,7889],{"class":4986},"create",[4023,7891,7892],{"class":4564},"({\n",[4023,7894,7895,7898,7900],{"class":4025,"line":4138},[4023,7896,7897],{"class":4910},"    baseURL:",[4023,7899,7712],{"class":4986},[4023,7901,7902],{"class":4564},"(),\n",[4023,7904,7905,7908,7911],{"class":4025,"line":4144},[4023,7906,7907],{"class":4910},"    timeout:",[4023,7909,7910],{"class":4601}," 8000",[4023,7912,5365],{"class":4564},[4023,7914,7915,7918],{"class":4025,"line":4150},[4023,7916,7917],{"class":4910},"    headers:",[4023,7919,5446],{"class":4564},[4023,7921,7922,7925,7927],{"class":4025,"line":4156},[4023,7923,7924],{"class":4910},"        Accept:",[4023,7926,6842],{"class":4580},[4023,7928,5365],{"class":4564},[4023,7930,7931,7934,7936,7938],{"class":4025,"line":4162},[4023,7932,7933],{"class":4580},"        'Content-Type'",[4023,7935,4397],{"class":4910},[4023,7937,6842],{"class":4580},[4023,7939,5365],{"class":4564},[4023,7941,7942,7945,7947,7950,7952,7954],{"class":4025,"line":4167},[4023,7943,7944],{"class":4580},"        'X-Client-Platform'",[4023,7946,4397],{"class":4910},[4023,7948,7949],{"class":4910}," Platform",[4023,7951,3836],{"class":4564},[4023,7953,7732],{"class":5032},[4023,7955,5365],{"class":4564},[4023,7957,7958,7961,7963,7965,7967,7969,7971,7974,7977,7980],{"class":4025,"line":4173},[4023,7959,7960],{"class":4580},"        'X-Client-Version'",[4023,7962,4397],{"class":4910},[4023,7964,7694],{"class":4910},[4023,7966,3836],{"class":4564},[4023,7968,7781],{"class":4910},[4023,7970,6271],{"class":4564},[4023,7972,7973],{"class":4910},"version",[4023,7975,7976],{"class":4564}," ?? ",[4023,7978,7979],{"class":4580},"'1.0.0'",[4023,7981,5365],{"class":4564},[4023,7983,7984],{"class":4025,"line":4179},[4023,7985,7986],{"class":4564},"    },\n",[4023,7988,7989],{"class":4025,"line":4184},[4023,7990,7991],{"class":4564},"})\n",[4023,7993,7994],{"class":4025,"line":4189},[4023,7995,4060],{"emptyLinePlaceholder":4059},[4023,7997,7998],{"class":4025,"line":4195},[4023,7999,8000],{"class":4965},"\u002F\u002F Змінні стану оновлення сесії\n",[4023,8002,8003,8006,8009,8011],{"class":4025,"line":4201},[4023,8004,8005],{"class":4890},"let",[4023,8007,8008],{"class":4910}," isRefreshing",[4023,8010,5403],{"class":4564},[4023,8012,8013],{"class":4890},"false\n",[4023,8015,8016,8018,8021,8023,8026],{"class":4025,"line":4207},[4023,8017,8005],{"class":4890},[4023,8019,8020],{"class":4910}," failedQueue",[4023,8022,4577],{"class":4564},[4023,8024,8025],{"class":4894},"Array",[4023,8027,8028],{"class":4564},"\u003C{\n",[4023,8030,8031,8034,8037,8040,8042,8044,8046,8048],{"class":4025,"line":4213},[4023,8032,8033],{"class":4986},"    resolve",[4023,8035,8036],{"class":4564},": (",[4023,8038,8039],{"class":4910},"token",[4023,8041,4577],{"class":4564},[4023,8043,4916],{"class":4894},[4023,8045,5243],{"class":4564},[4023,8047,5246],{"class":4890},[4023,8049,8050],{"class":4894}," void\n",[4023,8052,8053,8056,8058,8060,8062,8064,8066,8068],{"class":4025,"line":4219},[4023,8054,8055],{"class":4986},"    reject",[4023,8057,8036],{"class":4564},[4023,8059,7089],{"class":4910},[4023,8061,4577],{"class":4564},[4023,8063,7094],{"class":4894},[4023,8065,5243],{"class":4564},[4023,8067,5246],{"class":4890},[4023,8069,8050],{"class":4894},[4023,8071,8072],{"class":4025,"line":4225},[4023,8073,8074],{"class":4564},"}> = []\n",[4023,8076,8077],{"class":4025,"line":4230},[4023,8078,4060],{"emptyLinePlaceholder":4059},[4023,8080,8081,8083,8086,8088,8090,8092,8094,8096,8098,8100,8102,8105,8108,8110,8112,8114,8116],{"class":4025,"line":4235},[4023,8082,5076],{"class":4890},[4023,8084,8085],{"class":4986}," processQueue",[4023,8087,6041],{"class":4564},[4023,8089,7089],{"class":4910},[4023,8091,4577],{"class":4564},[4023,8093,7094],{"class":4894},[4023,8095,3928],{"class":4564},[4023,8097,8039],{"class":4910},[4023,8099,4577],{"class":4564},[4023,8101,4916],{"class":4894},[4023,8103,8104],{"class":4564}," | ",[4023,8106,8107],{"class":4894},"null",[4023,8109,5403],{"class":4564},[4023,8111,8107],{"class":4890},[4023,8113,5243],{"class":4564},[4023,8115,5246],{"class":4890},[4023,8117,5446],{"class":4564},[4023,8119,8120,8123,8125,8128,8130,8133,8135,8137],{"class":4025,"line":4241},[4023,8121,8122],{"class":4910},"    failedQueue",[4023,8124,3836],{"class":4564},[4023,8126,8127],{"class":4986},"forEach",[4023,8129,5238],{"class":4564},[4023,8131,8132],{"class":4910},"prom",[4023,8134,5243],{"class":4564},[4023,8136,5246],{"class":4890},[4023,8138,5446],{"class":4564},[4023,8140,8141,8143,8145,8147],{"class":4025,"line":4247},[4023,8142,6908],{"class":4886},[4023,8144,3924],{"class":4564},[4023,8146,7089],{"class":4910},[4023,8148,5088],{"class":4564},[4023,8150,8151,8154,8156,8159,8161,8163],{"class":4025,"line":4253},[4023,8152,8153],{"class":4910},"            prom",[4023,8155,3836],{"class":4564},[4023,8157,8158],{"class":4986},"reject",[4023,8160,5122],{"class":4564},[4023,8162,7089],{"class":4910},[4023,8164,5131],{"class":4564},[4023,8166,8167,8170,8173,8176,8178,8180],{"class":4025,"line":4259},[4023,8168,8169],{"class":4564},"        } ",[4023,8171,8172],{"class":4886},"else",[4023,8174,8175],{"class":4886}," if",[4023,8177,3924],{"class":4564},[4023,8179,8039],{"class":4910},[4023,8181,5088],{"class":4564},[4023,8183,8184,8186,8188,8191,8193,8195],{"class":4025,"line":4265},[4023,8185,8153],{"class":4910},[4023,8187,3836],{"class":4564},[4023,8189,8190],{"class":4986},"resolve",[4023,8192,5122],{"class":4564},[4023,8194,8039],{"class":4910},[4023,8196,5131],{"class":4564},[4023,8198,8199],{"class":4025,"line":4271},[4023,8200,6971],{"class":4564},[4023,8202,8203],{"class":4025,"line":4276},[4023,8204,8205],{"class":4564},"    })\n",[4023,8207,8208,8210],{"class":4025,"line":5500},[4023,8209,8122],{"class":4910},[4023,8211,8212],{"class":4564}," = []\n",[4023,8214,8215],{"class":4025,"line":5505},[4023,8216,4090],{"class":4564},[4023,8218,8219],{"class":4025,"line":5510},[4023,8220,4060],{"emptyLinePlaceholder":4059},[4023,8222,8223],{"class":4025,"line":5515},[4023,8224,8225],{"class":4965},"\u002F\u002F 1. Інтерцептор запитів: Додавання токена авторизації\n",[4023,8227,8228,8231,8233,8235,8237,8240,8242,8245],{"class":4025,"line":5520},[4023,8229,8230],{"class":4910},"apiClient",[4023,8232,3836],{"class":4564},[4023,8234,3854],{"class":4910},[4023,8236,3836],{"class":4564},[4023,8238,8239],{"class":4910},"request",[4023,8241,3836],{"class":4564},[4023,8243,8244],{"class":4986},"use",[4023,8246,8247],{"class":4564},"(\n",[4023,8249,8250,8253,8255,8258,8260,8262,8264,8266],{"class":4025,"line":5526},[4023,8251,8252],{"class":4890},"    async",[4023,8254,3924],{"class":4564},[4023,8256,8257],{"class":4910},"config",[4023,8259,4577],{"class":4564},[4023,8261,7665],{"class":4894},[4023,8263,5243],{"class":4564},[4023,8265,5246],{"class":4890},[4023,8267,5446],{"class":4564},[4023,8269,8270,8272,8275,8277,8279,8281,8283,8285,8288],{"class":4025,"line":5531},[4023,8271,6792],{"class":4890},[4023,8273,8274],{"class":5032}," token",[4023,8276,4577],{"class":4564},[4023,8278,4916],{"class":4894},[4023,8280,8104],{"class":4564},[4023,8282,8107],{"class":4894},[4023,8284,5403],{"class":4564},[4023,8286,8287],{"class":4580},"'mock-access-token'",[4023,8289,8290],{"class":4965}," \u002F\u002F У реальному додатку: await SecureStore.getItemAsync('token')\n",[4023,8292,8293,8295,8297,8299,8301,8303,8305,8307],{"class":4025,"line":5557},[4023,8294,6908],{"class":4886},[4023,8296,3924],{"class":4564},[4023,8298,8039],{"class":4910},[4023,8300,7126],{"class":4564},[4023,8302,8257],{"class":4910},[4023,8304,3836],{"class":4564},[4023,8306,6867],{"class":4910},[4023,8308,5088],{"class":4564},[4023,8310,8311,8314,8316,8318,8320,8323,8325,8328,8330,8332,8334],{"class":4025,"line":5572},[4023,8312,8313],{"class":4910},"            config",[4023,8315,3836],{"class":4564},[4023,8317,6867],{"class":4910},[4023,8319,3836],{"class":4564},[4023,8321,8322],{"class":4910},"Authorization",[4023,8324,5403],{"class":4564},[4023,8326,8327],{"class":4580},"`Bearer ",[4023,8329,6939],{"class":4890},[4023,8331,8039],{"class":4910},[4023,8333,6222],{"class":4890},[4023,8335,8336],{"class":4580},"`\n",[4023,8338,8339],{"class":4025,"line":5584},[4023,8340,6971],{"class":4564},[4023,8342,8343,8345],{"class":4025,"line":5597},[4023,8344,7057],{"class":4886},[4023,8346,8347],{"class":4910}," config\n",[4023,8349,8350],{"class":4025,"line":5625},[4023,8351,7986],{"class":4564},[4023,8353,8354,8357,8359,8361,8363,8366,8368,8370,8372,8374],{"class":4025,"line":5630},[4023,8355,8356],{"class":4564},"    (",[4023,8358,7089],{"class":4910},[4023,8360,5243],{"class":4564},[4023,8362,5246],{"class":4890},[4023,8364,8365],{"class":4894}," Promise",[4023,8367,3836],{"class":4564},[4023,8369,8158],{"class":4986},[4023,8371,5122],{"class":4564},[4023,8373,7089],{"class":4910},[4023,8375,8376],{"class":4564},"),\n",[4023,8378,8379],{"class":4025,"line":5637},[4023,8380,5131],{"class":4564},[4023,8382,8383],{"class":4025,"line":5644},[4023,8384,4060],{"emptyLinePlaceholder":4059},[4023,8386,8387],{"class":4025,"line":5657},[4023,8388,8389],{"class":4965},"\u002F\u002F 2. Інтерцептор відповідей: Черга оновлення токена при 401\n",[4023,8391,8392,8394,8396,8398,8400,8402,8404,8406],{"class":4025,"line":5668},[4023,8393,8230],{"class":4910},[4023,8395,3836],{"class":4564},[4023,8397,3854],{"class":4910},[4023,8399,3836],{"class":4564},[4023,8401,6913],{"class":4910},[4023,8403,3836],{"class":4564},[4023,8405,8244],{"class":4986},[4023,8407,8247],{"class":4564},[4023,8409,8410,8412,8414,8416,8418,8420],{"class":4025,"line":5673},[4023,8411,8356],{"class":4564},[4023,8413,6913],{"class":4910},[4023,8415,5243],{"class":4564},[4023,8417,5246],{"class":4890},[4023,8419,6795],{"class":4910},[4023,8421,5365],{"class":4564},[4023,8423,8424,8426,8428,8430,8432,8434,8436,8438],{"class":4025,"line":5709},[4023,8425,8252],{"class":4890},[4023,8427,3924],{"class":4564},[4023,8429,7089],{"class":4910},[4023,8431,4577],{"class":4564},[4023,8433,7241],{"class":4894},[4023,8435,5243],{"class":4564},[4023,8437,5246],{"class":4890},[4023,8439,5446],{"class":4564},[4023,8441,8442,8444,8447,8449,8451,8453,8455,8458,8461,8464,8467,8470,8473],{"class":4025,"line":5714},[4023,8443,6792],{"class":4890},[4023,8445,8446],{"class":5032}," originalRequest",[4023,8448,5403],{"class":4564},[4023,8450,7089],{"class":4910},[4023,8452,3836],{"class":4564},[4023,8454,8257],{"class":4910},[4023,8456,8457],{"class":4886}," as",[4023,8459,8460],{"class":4894}," InternalAxiosRequestConfig",[4023,8462,8463],{"class":4564}," & { ",[4023,8465,8466],{"class":4910},"_retry",[4023,8468,8469],{"class":4564},"?: ",[4023,8471,8472],{"class":4894},"boolean",[4023,8474,4637],{"class":4564},[4023,8476,8478],{"class":4025,"line":8477},65,[4023,8479,4060],{"emptyLinePlaceholder":4059},[4023,8481,8483],{"class":4025,"line":8482},66,[4023,8484,8485],{"class":4965},"        \u002F\u002F Якщо помилка не 401 або запит уже був повторений - повертаємо виняток\n",[4023,8487,8489,8491,8493,8495,8497,8499,8501,8503,8506,8509,8511,8514,8517,8519,8521,8523],{"class":4025,"line":8488},67,[4023,8490,6908],{"class":4886},[4023,8492,3924],{"class":4564},[4023,8494,7089],{"class":4910},[4023,8496,3836],{"class":4564},[4023,8498,6913],{"class":4910},[4023,8500,6271],{"class":4564},[4023,8502,6946],{"class":4910},[4023,8504,8505],{"class":4564}," !== ",[4023,8507,8508],{"class":4601},"401",[4023,8510,7014],{"class":4564},[4023,8512,8513],{"class":4910},"originalRequest",[4023,8515,8516],{"class":4564}," || ",[4023,8518,8513],{"class":4910},[4023,8520,3836],{"class":4564},[4023,8522,8466],{"class":4910},[4023,8524,5088],{"class":4564},[4023,8526,8528,8530,8532,8534,8536,8538,8540],{"class":4025,"line":8527},68,[4023,8529,6114],{"class":4886},[4023,8531,8365],{"class":4894},[4023,8533,3836],{"class":4564},[4023,8535,8158],{"class":4986},[4023,8537,5122],{"class":4564},[4023,8539,7089],{"class":4910},[4023,8541,5131],{"class":4564},[4023,8543,8545],{"class":4025,"line":8544},69,[4023,8546,6971],{"class":4564},[4023,8548,8550],{"class":4025,"line":8549},70,[4023,8551,4060],{"emptyLinePlaceholder":4059},[4023,8553,8555],{"class":4025,"line":8554},71,[4023,8556,8557],{"class":4965},"        \u002F\u002F Якщо оновлення вже відбувається іншим запитом, ставимо цей запит у чергу\n",[4023,8559,8561,8563,8565,8568],{"class":4025,"line":8560},72,[4023,8562,6908],{"class":4886},[4023,8564,3924],{"class":4564},[4023,8566,8567],{"class":4910},"isRefreshing",[4023,8569,5088],{"class":4564},[4023,8571,8573,8575,8577,8579,8581,8583,8585,8587,8589,8591],{"class":4025,"line":8572},73,[4023,8574,6114],{"class":4886},[4023,8576,6928],{"class":4890},[4023,8578,8365],{"class":4894},[4023,8580,5238],{"class":4564},[4023,8582,8190],{"class":4910},[4023,8584,3928],{"class":4564},[4023,8586,8158],{"class":4910},[4023,8588,5243],{"class":4564},[4023,8590,5246],{"class":4890},[4023,8592,5446],{"class":4564},[4023,8594,8596,8599,8601,8603],{"class":4025,"line":8595},74,[4023,8597,8598],{"class":4910},"                failedQueue",[4023,8600,3836],{"class":4564},[4023,8602,5119],{"class":4986},[4023,8604,7892],{"class":4564},[4023,8606,8608,8611,8613,8615,8618,8620,8622,8624,8626],{"class":4025,"line":8607},75,[4023,8609,8610],{"class":4986},"                    resolve",[4023,8612,4397],{"class":4910},[4023,8614,3924],{"class":4564},[4023,8616,8617],{"class":4910},"newToken",[4023,8619,4577],{"class":4564},[4023,8621,4916],{"class":4894},[4023,8623,5243],{"class":4564},[4023,8625,5246],{"class":4890},[4023,8627,5446],{"class":4564},[4023,8629,8631,8634,8636,8638,8640,8642],{"class":4025,"line":8630},76,[4023,8632,8633],{"class":4886},"                        if",[4023,8635,3924],{"class":4564},[4023,8637,8513],{"class":4910},[4023,8639,3836],{"class":4564},[4023,8641,6867],{"class":4910},[4023,8643,5088],{"class":4564},[4023,8645,8647,8650,8652,8654,8656,8658,8660,8662,8664,8666,8668],{"class":4025,"line":8646},77,[4023,8648,8649],{"class":4910},"                            originalRequest",[4023,8651,3836],{"class":4564},[4023,8653,6867],{"class":4910},[4023,8655,3836],{"class":4564},[4023,8657,8322],{"class":4910},[4023,8659,5403],{"class":4564},[4023,8661,8327],{"class":4580},[4023,8663,6939],{"class":4890},[4023,8665,8617],{"class":4910},[4023,8667,6222],{"class":4890},[4023,8669,8336],{"class":4580},[4023,8671,8673],{"class":4025,"line":8672},78,[4023,8674,8675],{"class":4564},"                        }\n",[4023,8677,8679,8682,8684,8686,8688,8690],{"class":4025,"line":8678},79,[4023,8680,8681],{"class":4986},"                        resolve",[4023,8683,5122],{"class":4564},[4023,8685,8230],{"class":4986},[4023,8687,5122],{"class":4564},[4023,8689,8513],{"class":4910},[4023,8691,5290],{"class":4564},[4023,8693,8695],{"class":4025,"line":8694},80,[4023,8696,8697],{"class":4564},"                    },\n",[4023,8699,8701,8704,8706,8708,8711,8713,8715,8717,8719],{"class":4025,"line":8700},81,[4023,8702,8703],{"class":4986},"                    reject",[4023,8705,4397],{"class":4910},[4023,8707,3924],{"class":4564},[4023,8709,8710],{"class":4910},"err",[4023,8712,4577],{"class":4564},[4023,8714,7094],{"class":4894},[4023,8716,5243],{"class":4564},[4023,8718,5246],{"class":4890},[4023,8720,5446],{"class":4564},[4023,8722,8724,8727,8729,8731],{"class":4025,"line":8723},82,[4023,8725,8726],{"class":4986},"                        reject",[4023,8728,5122],{"class":4564},[4023,8730,8710],{"class":4910},[4023,8732,5131],{"class":4564},[4023,8734,8736],{"class":4025,"line":8735},83,[4023,8737,8697],{"class":4564},[4023,8739,8741],{"class":4025,"line":8740},84,[4023,8742,8743],{"class":4564},"                })\n",[4023,8745,8747],{"class":4025,"line":8746},85,[4023,8748,8749],{"class":4564},"            })\n",[4023,8751,8753],{"class":4025,"line":8752},86,[4023,8754,6971],{"class":4564},[4023,8756,8758],{"class":4025,"line":8757},87,[4023,8759,4060],{"emptyLinePlaceholder":4059},[4023,8761,8763,8766,8768,8770,8772],{"class":4025,"line":8762},88,[4023,8764,8765],{"class":4910},"        originalRequest",[4023,8767,3836],{"class":4564},[4023,8769,8466],{"class":4910},[4023,8771,5403],{"class":4564},[4023,8773,8774],{"class":4890},"true\n",[4023,8776,8778,8781,8783],{"class":4025,"line":8777},89,[4023,8779,8780],{"class":4910},"        isRefreshing",[4023,8782,5403],{"class":4564},[4023,8784,8774],{"class":4890},[4023,8786,8788],{"class":4025,"line":8787},90,[4023,8789,4060],{"emptyLinePlaceholder":4059},[4023,8791,8793,8796],{"class":4025,"line":8792},91,[4023,8794,8795],{"class":4886},"        try",[4023,8797,5446],{"class":4564},[4023,8799,8801],{"class":4025,"line":8800},92,[4023,8802,8803],{"class":4965},"            \u002F\u002F Виконуємо запит на оновлення токена\n",[4023,8805,8807],{"class":4025,"line":8806},93,[4023,8808,8809],{"class":4965},"            \u002F\u002F Важливо: використовуємо базовий axios, щоб оминути інтерцептори клієнта!\n",[4023,8811,8813,8815,8818,8820,8822,8824,8826,8829,8832,8835,8837,8839],{"class":4025,"line":8812},94,[4023,8814,6075],{"class":4890},[4023,8816,8817],{"class":5032}," refreshResponse",[4023,8819,5403],{"class":4564},[4023,8821,6800],{"class":4886},[4023,8823,7651],{"class":4910},[4023,8825,3836],{"class":4564},[4023,8827,8828],{"class":4986},"post",[4023,8830,8831],{"class":4564},"\u003C{ ",[4023,8833,8834],{"class":4910},"accessToken",[4023,8836,4577],{"class":4564},[4023,8838,4916],{"class":4894},[4023,8840,5337],{"class":4564},[4023,8842,8844,8847,8849,8852,8855,8857,8860],{"class":4025,"line":8843},95,[4023,8845,8846],{"class":4580},"                `",[4023,8848,6939],{"class":4890},[4023,8850,8851],{"class":4986},"resolveBaseUrl",[4023,8853,8854],{"class":6210},"()",[4023,8856,6222],{"class":4890},[4023,8858,8859],{"class":4580},"\u002Fauth\u002Frefresh`",[4023,8861,5365],{"class":4564},[4023,8863,8865,8868,8871,8874],{"class":4025,"line":8864},96,[4023,8866,8867],{"class":4564},"                { ",[4023,8869,8870],{"class":4910},"refreshToken:",[4023,8872,8873],{"class":4580}," 'mock-refresh-token'",[4023,8875,4605],{"class":4564},[4023,8877,8879,8881,8884,8887],{"class":4025,"line":8878},97,[4023,8880,8867],{"class":4564},[4023,8882,8883],{"class":4910},"timeout:",[4023,8885,8886],{"class":4601}," 5000",[4023,8888,4605],{"class":4564},[4023,8890,8892],{"class":4025,"line":8891},98,[4023,8893,8894],{"class":4564},"            )\n",[4023,8896,8898],{"class":4025,"line":8897},99,[4023,8899,4060],{"emptyLinePlaceholder":4059},[4023,8901,8903,8905,8908,8910,8913,8915,8918,8920],{"class":4025,"line":8902},100,[4023,8904,6075],{"class":4890},[4023,8906,8907],{"class":5032}," newAccessToken",[4023,8909,5403],{"class":4564},[4023,8911,8912],{"class":4910},"refreshResponse",[4023,8914,3836],{"class":4564},[4023,8916,8917],{"class":4910},"data",[4023,8919,3836],{"class":4564},[4023,8921,8922],{"class":4910},"accessToken\n",[4023,8924,8926],{"class":4025,"line":8925},101,[4023,8927,4060],{"emptyLinePlaceholder":4059},[4023,8929,8931],{"class":4025,"line":8930},102,[4023,8932,8933],{"class":4965},"            \u002F\u002F Зберігаємо новий токен у безпечне сховище\n",[4023,8935,8937],{"class":4025,"line":8936},103,[4023,8938,8939],{"class":4965},"            \u002F\u002F await SecureStore.setItemAsync('token', newAccessToken)\n",[4023,8941,8943],{"class":4025,"line":8942},104,[4023,8944,4060],{"emptyLinePlaceholder":4059},[4023,8946,8948,8951,8953,8955,8957,8960],{"class":4025,"line":8947},105,[4023,8949,8950],{"class":4986},"            processQueue",[4023,8952,5122],{"class":4564},[4023,8954,8107],{"class":4890},[4023,8956,3928],{"class":4564},[4023,8958,8959],{"class":4910},"newAccessToken",[4023,8961,5131],{"class":4564},[4023,8963,8965],{"class":4025,"line":8964},106,[4023,8966,4060],{"emptyLinePlaceholder":4059},[4023,8968,8970,8972,8974,8976,8978,8980],{"class":4025,"line":8969},107,[4023,8971,6097],{"class":4886},[4023,8973,3924],{"class":4564},[4023,8975,8513],{"class":4910},[4023,8977,3836],{"class":4564},[4023,8979,6867],{"class":4910},[4023,8981,5088],{"class":4564},[4023,8983,8985,8988,8990,8992,8994,8996,8998,9000,9002,9004,9006],{"class":4025,"line":8984},108,[4023,8986,8987],{"class":4910},"                originalRequest",[4023,8989,3836],{"class":4564},[4023,8991,6867],{"class":4910},[4023,8993,3836],{"class":4564},[4023,8995,8322],{"class":4910},[4023,8997,5403],{"class":4564},[4023,8999,8327],{"class":4580},[4023,9001,6939],{"class":4890},[4023,9003,8959],{"class":4910},[4023,9005,6222],{"class":4890},[4023,9007,8336],{"class":4580},[4023,9009,9011],{"class":4025,"line":9010},109,[4023,9012,9013],{"class":4564},"            }\n",[4023,9015,9017],{"class":4025,"line":9016},110,[4023,9018,4060],{"emptyLinePlaceholder":4059},[4023,9020,9022,9024,9026,9028,9030],{"class":4025,"line":9021},111,[4023,9023,6114],{"class":4886},[4023,9025,7876],{"class":4986},[4023,9027,5122],{"class":4564},[4023,9029,8513],{"class":4910},[4023,9031,5131],{"class":4564},[4023,9033,9035,9037,9039,9041,9044],{"class":4025,"line":9034},112,[4023,9036,8169],{"class":4564},[4023,9038,7084],{"class":4886},[4023,9040,3924],{"class":4564},[4023,9042,9043],{"class":4910},"refreshError",[4023,9045,5088],{"class":4564},[4023,9047,9049,9051,9053,9055,9057,9059],{"class":4025,"line":9048},113,[4023,9050,8950],{"class":4986},[4023,9052,5122],{"class":4564},[4023,9054,9043],{"class":4910},[4023,9056,3928],{"class":4564},[4023,9058,8107],{"class":4890},[4023,9060,5131],{"class":4564},[4023,9062,9064],{"class":4025,"line":9063},114,[4023,9065,9066],{"class":4965},"            \u002F\u002F Тут ініціюється вихід із системи (Logout)\n",[4023,9068,9070,9072,9074,9076,9078,9080,9082],{"class":4025,"line":9069},115,[4023,9071,6114],{"class":4886},[4023,9073,8365],{"class":4894},[4023,9075,3836],{"class":4564},[4023,9077,8158],{"class":4986},[4023,9079,5122],{"class":4564},[4023,9081,9043],{"class":4910},[4023,9083,5131],{"class":4564},[4023,9085,9087,9089,9092],{"class":4025,"line":9086},116,[4023,9088,8169],{"class":4564},[4023,9090,9091],{"class":4886},"finally",[4023,9093,5446],{"class":4564},[4023,9095,9097,9100,9102],{"class":4025,"line":9096},117,[4023,9098,9099],{"class":4910},"            isRefreshing",[4023,9101,5403],{"class":4564},[4023,9103,8013],{"class":4890},[4023,9105,9107],{"class":4025,"line":9106},118,[4023,9108,6971],{"class":4564},[4023,9110,9112],{"class":4025,"line":9111},119,[4023,9113,7986],{"class":4564},[4023,9115,9117],{"class":4025,"line":9116},120,[4023,9118,5131],{"class":4564},[4872,9120,9122],{"label":9121},"Встановлення залежностей",[4014,9123,9127],{"className":9124,"code":9125,"language":9126,"meta":4019,"style":4019},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# Встановлення необхідних модулів у проєкт\nnpx expo install expo-constants\nnpm install axios\n","bash",[3799,9128,9129,9134,9148],{"__ignoreMap":4019},[4023,9130,9131],{"class":4025,"line":4026},[4023,9132,9133],{"class":4965},"# Встановлення необхідних модулів у проєкт\n",[4023,9135,9136,9139,9142,9145],{"class":4025,"line":4032},[4023,9137,9138],{"class":4986},"npx",[4023,9140,9141],{"class":4580}," expo",[4023,9143,9144],{"class":4580}," install",[4023,9146,9147],{"class":4580}," expo-constants\n",[4023,9149,9150,9153,9155],{"class":4025,"line":4038},[4023,9151,9152],{"class":4986},"npm",[4023,9154,9144],{"class":4580},[4023,9156,9157],{"class":4580}," axios\n",[3964,9159],{},[3738,9161,9163],{"id":9162},"класифікація-мережевих-помилок-та-стійкість-до-збоїв","Класифікація мережевих помилок та стійкість до збоїв",[3743,9165,9166],{},"Стійкість мобільного застосунку визначається тим, наскільки коректно він ізолює збої та інтерпретує помилки для користувача.",[3774,9168,9170],{"id":9169},"класифікація-помилок-за-рівнями-абстракції","Класифікація помилок за рівнями абстракції",[3743,9172,9173],{},"Мережеві винятки поділяються на 4 взаємовиключні рівні:",[6514,9175,9176,9192,9218,9267],{},[6517,9177,9180,9183,9184,9187,9188,9191],{"name":9178,"type":9179},"1. Транспортний \u002F Мережевий рівень (Network Layer)","Клас помилки",[3750,9181,9182],{},"Симптоми:"," Немає сигналу, обрив сокета, збій дозволу DNS-імені, спрацювання клієнтського таймауту.\n",[3750,9185,9186],{},"Характеристика:"," Запит або не вийшов з пристрою, або відповідь не досягла клієнта за відведений ліміт часу.\n",[3750,9189,9190],{},"Стратегія відновлення:"," Перевірка стану підключення, показ Offline-індикатора, очікування відновлення мережі або повтор запиту з ручної ініціативи.",[6517,9193,9195,7222,9197,3928,9199,3928,9202,3928,9205,9208,9209,9211,9212,9214,9215,3836],{"name":9194,"type":9179},"2. Серверний інфраструктурний рівень (HTTP 5xx)",[3750,9196,9182],{},[3799,9198,6565],{},[3799,9200,9201],{},"502 Bad Gateway",[3799,9203,9204],{},"503 Service Unavailable",[3799,9206,9207],{},"504 Gateway Timeout",".\n",[3750,9210,9186],{}," Сервер перевантажений, аварійно завершив роботу або виконує розгортання нової версії.\n",[3750,9213,9190],{}," Автоматичний повтор запитів за алгоритмом ",[3750,9216,9217],{},"Exponential Backoff із випадковим зсувом (Jitter)",[6517,9219,9221,9243],{"name":9220,"type":9179},"3. Авторизаційний та бізнес-рівень (HTTP 4xx)",[3743,9222,9223,7222,9225,3928,9228,3928,9230,3928,9233,3928,9235,9208,9238,9240,9241],{},[3750,9224,9182],{},[3799,9226,9227],{},"400 Bad Request",[3799,9229,6558],{},[3799,9231,9232],{},"403 Forbidden",[3799,9234,6561],{},[3799,9236,9237],{},"422 Unprocessable Entity",[3750,9239,9186],{}," Помилка в даних клієнта або недійсний сесійний токен.\n",[3750,9242,9190],{},[3779,9244,9245,9255,9261],{},[3782,9246,9247,9248,9250,9251,9254],{},"При ",[3799,9249,8508],{},": Запуск процедури оновлення токена (",[3758,9252,9253],{},"Token Refresh Flow",") або вихід у стан деавтентифікації.",[3782,9256,9247,9257,9260],{},[3799,9258,9259],{},"422",": Відображення тексту помилок біля полів вводу.",[3782,9262,9263,9266],{},[3750,9264,9265],{},"Категорична заборона автоматичного Retry"," (повтор того самого некоректного тіла запиту завжди повертатиме ту саму помилку 4xx).",[6517,9268,9270,9272,9273,9276,9277,9279,9280,9282,9283,9279,9286,9288,9289,3761],{"name":9269,"type":9179},"4. Рівень узгодження контрактів (Schema \u002F Deserialization)",[3750,9271,9182],{}," Відповідь успішна (",[3799,9274,9275],{},"200 OK","), але структура JSON змінилася або містить неочікуваний тип (наприклад, замість масиву об'єктів прийшов ",[3799,9278,8107],{},").\n",[3750,9281,9186],{}," Розсинхронізація версій клієнта та сервера (",[3758,9284,9285],{},"API Contract Break",[3750,9287,9190],{}," Логування критичної події в систему телеметрії (Sentry, Datadog), відображення безпечного аварійного екрана (",[3758,9290,9291],{},"Fallback UI",[3774,9293,9295],{"id":9294},"алгоритм-та-формула-exponential-backoff-із-jitter","Алгоритм та формула: Exponential Backoff із Jitter",[3743,9297,9298,9299,9302],{},"Коли сотні тисяч мобільних пристроїв стикаються з падінням сервера (HTTP 503) і починають одночасно повторювати запити через фіксований інтервал (наприклад, щосекунди), виникає явище ",[3750,9300,9301],{},"«шторму запитів» (Thundering Herd Problem)",". Відновлений сервер миттєво падає знову під піковим навантаженням.",[3743,9304,9305,9306,9309],{},"Для вирішення цієї проблеми використовують алгоритм ",[3750,9307,9308],{},"Full Jitter Exponential Backoff",", запропонований дослідниками інфраструктури AWS:",[3743,9311,9312,9313,9316],{},"Інтервал очікування перед ",[3832,9314],{":inline":3834,"tex":9315},"n","-ю спробою розраховується за формулою:",[3832,9318,9319],{},"t_{\\text{sleep}} = \\text{random}(0, \\min(t_{\\text{max}}, t_{\\text{base}} \\cdot 2^{n}))",[3743,9321,9322],{},"де:",[3779,9324,9325,9331,9340,9346],{},[3782,9326,9327,9330],{},[3832,9328],{":inline":3834,"tex":9329},"t_{\\text{base}}"," — базовий інтервал затримки (наприклад, 500 мс);",[3782,9332,9333,9335,9336,9339],{},[3832,9334],{":inline":3834,"tex":9315}," — порядковий номер поточної спроби повтору (",[3832,9337],{":inline":3834,"tex":9338},"n \\in [0, N-1]",");",[3782,9341,9342,9345],{},[3832,9343],{":inline":3834,"tex":9344},"t_{\\text{max}}"," — граничний ліміт інтервалу затримки (наприклад, 10 000 мс);",[3782,9347,9348,9351,9352,3836],{},[3832,9349],{":inline":3834,"tex":9350},"\\text{random}(0, x)"," — функція рівномірного розподілу випадкової величини на відрізку ",[3832,9353],{":inline":3834,"tex":9354},"[0, x]",[4011,9356,9357],{},[4014,9358,9360],{"className":4016,"code":9359,"language":4018,"meta":4019,"style":4019},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam defaultFontName \"Helvetica\"\nskinparam defaultFontSize 12\n\nactor \"Mobile Client\" as App #bfdbfe\nparticipant \"API Gateway\" as Server #fecaca\n\nApp -> Server : 1. GET \u002Ftrips (Attempt 0)\nServer --> App : 2. 503 Service Unavailable\nnote over App : Розрахунок затримки:\\nt_sleep = random(0, 500 * 2^0) = 320ms\n\n... Пауза 320мс ...\n\nApp -> Server : 3. GET \u002Ftrips (Attempt 1)\nServer --> App : 4. 503 Service Unavailable\nnote over App : Розрахунок затримки:\\nt_sleep = random(0, 500 * 2^1) = 780ms\n\n... Пауза 780мс ...\n\nApp -> Server : 5. GET \u002Ftrips (Attempt 2)\nServer --> App : 6. 200 OK [Payload Data]\nnote over App : Успіх! Оновлення стейту екрана.\n\n@enduml\n",[3799,9361,9362,9366,9370,9374,9378,9382,9386,9391,9396,9400,9405,9410,9415,9419,9424,9428,9433,9438,9443,9447,9452,9456,9461,9466,9471,9475],{"__ignoreMap":4019},[4023,9363,9364],{"class":4025,"line":4026},[4023,9365,4029],{},[4023,9367,9368],{"class":4025,"line":4032},[4023,9369,4035],{},[4023,9371,9372],{"class":4025,"line":4038},[4023,9373,4041],{},[4023,9375,9376],{"class":4025,"line":4044},[4023,9377,4047],{},[4023,9379,9380],{"class":4025,"line":4050},[4023,9381,4053],{},[4023,9383,9384],{"class":4025,"line":4056},[4023,9385,4060],{"emptyLinePlaceholder":4059},[4023,9387,9388],{"class":4025,"line":4063},[4023,9389,9390],{},"actor \"Mobile Client\" as App #bfdbfe\n",[4023,9392,9393],{"class":4025,"line":4069},[4023,9394,9395],{},"participant \"API Gateway\" as Server #fecaca\n",[4023,9397,9398],{"class":4025,"line":4075},[4023,9399,4060],{"emptyLinePlaceholder":4059},[4023,9401,9402],{"class":4025,"line":4081},[4023,9403,9404],{},"App -> Server : 1. GET \u002Ftrips (Attempt 0)\n",[4023,9406,9407],{"class":4025,"line":4087},[4023,9408,9409],{},"Server --> App : 2. 503 Service Unavailable\n",[4023,9411,9412],{"class":4025,"line":4093},[4023,9413,9414],{},"note over App : Розрахунок затримки:\\nt_sleep = random(0, 500 * 2^0) = 320ms\n",[4023,9416,9417],{"class":4025,"line":4098},[4023,9418,4060],{"emptyLinePlaceholder":4059},[4023,9420,9421],{"class":4025,"line":4104},[4023,9422,9423],{},"... Пауза 320мс ...\n",[4023,9425,9426],{"class":4025,"line":4110},[4023,9427,4060],{"emptyLinePlaceholder":4059},[4023,9429,9430],{"class":4025,"line":4116},[4023,9431,9432],{},"App -> Server : 3. GET \u002Ftrips (Attempt 1)\n",[4023,9434,9435],{"class":4025,"line":4121},[4023,9436,9437],{},"Server --> App : 4. 503 Service Unavailable\n",[4023,9439,9440],{"class":4025,"line":4126},[4023,9441,9442],{},"note over App : Розрахунок затримки:\\nt_sleep = random(0, 500 * 2^1) = 780ms\n",[4023,9444,9445],{"class":4025,"line":4132},[4023,9446,4060],{"emptyLinePlaceholder":4059},[4023,9448,9449],{"class":4025,"line":4138},[4023,9450,9451],{},"... Пауза 780мс ...\n",[4023,9453,9454],{"class":4025,"line":4144},[4023,9455,4060],{"emptyLinePlaceholder":4059},[4023,9457,9458],{"class":4025,"line":4150},[4023,9459,9460],{},"App -> Server : 5. GET \u002Ftrips (Attempt 2)\n",[4023,9462,9463],{"class":4025,"line":4156},[4023,9464,9465],{},"Server --> App : 6. 200 OK [Payload Data]\n",[4023,9467,9468],{"class":4025,"line":4162},[4023,9469,9470],{},"note over App : Успіх! Оновлення стейту екрана.\n",[4023,9472,9473],{"class":4025,"line":4167},[4023,9474,4060],{"emptyLinePlaceholder":4059},[4023,9476,9477],{"class":4025,"line":4173},[4023,9478,4279],{},[3774,9480,9482],{"id":9481},"програмна-реалізація-алгоритму-повторних-спроб","Програмна реалізація алгоритму повторних спроб",[3743,9484,9485,9486,4397],{},"Створимо універсальну утиліту ",[3799,9487,9488],{},"retryWithBackoff",[4014,9490,9492],{"className":4877,"code":9491,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Futils\u002FretryWithBackoff.ts\nexport interface RetryOptions {\n    maxRetries?: number\n    baseDelayMs?: number\n    maxDelayMs?: number\n    shouldRetry?: (error: unknown) => boolean\n}\n\n\u002F**\n * Виконує асинхронну операцію з алгоритмом Full Jitter Exponential Backoff\n *\u002F\nexport async function retryWithBackoff\u003CT>(operation: () => Promise\u003CT>, options: RetryOptions = {}): Promise\u003CT> {\n    const {\n        maxRetries = 3,\n        baseDelayMs = 500,\n        maxDelayMs = 8000,\n        shouldRetry = (err: any) => {\n            \u002F\u002F За замовчуванням повторюємо лише мережеві збої та 5xx статуси\n            if (err?.response?.status && err.response.status >= 400 && err.response.status \u003C 500) {\n                return false \u002F\u002F Ніколи не повторюємо 4xx\n            }\n            return true\n        },\n    } = options\n\n    let attempt = 0\n\n    while (attempt \u003C maxRetries) {\n        try {\n            return await operation()\n        } catch (error) {\n            attempt++\n\n            if (attempt >= maxRetries || !shouldRetry(error)) {\n                throw error\n            }\n\n            \u002F\u002F Розрахунок експоненційного діапазону: base * 2^attempt\n            const calculatedCap = Math.min(maxDelayMs, baseDelayMs * Math.pow(2, attempt))\n            \u002F\u002F Full Jitter: рівномірний випадковий вибір від 0 до calculatedCap\n            const jitterDelay = Math.random() * calculatedCap\n\n            await new Promise((resolve) => setTimeout(resolve, jitterDelay))\n        }\n    }\n\n    throw new Error('RETRY_EXHAUSTED')\n}\n",[3799,9493,9494,9499,9510,9519,9528,9537,9558,9562,9566,9570,9575,9579,9633,9639,9651,9663,9674,9694,9699,9752,9763,9767,9774,9778,9786,9790,9803,9807,9824,9830,9842,9854,9862,9866,9889,9896,9900,9904,9909,9957,9962,9984,9988,10019,10023,10027,10031,10047],{"__ignoreMap":4019},[4023,9495,9496],{"class":4025,"line":4026},[4023,9497,9498],{"class":4965},"\u002F\u002F src\u002Futils\u002FretryWithBackoff.ts\n",[4023,9500,9501,9503,9505,9508],{"class":4025,"line":4032},[4023,9502,4887],{"class":4886},[4023,9504,4891],{"class":4890},[4023,9506,9507],{"class":4894}," RetryOptions",[4023,9509,5446],{"class":4564},[4023,9511,9512,9515,9517],{"class":4025,"line":4038},[4023,9513,9514],{"class":4910},"    maxRetries",[4023,9516,8469],{"class":4564},[4023,9518,5847],{"class":4894},[4023,9520,9521,9524,9526],{"class":4025,"line":4044},[4023,9522,9523],{"class":4910},"    baseDelayMs",[4023,9525,8469],{"class":4564},[4023,9527,5847],{"class":4894},[4023,9529,9530,9533,9535],{"class":4025,"line":4050},[4023,9531,9532],{"class":4910},"    maxDelayMs",[4023,9534,8469],{"class":4564},[4023,9536,5847],{"class":4894},[4023,9538,9539,9542,9545,9547,9549,9551,9553,9555],{"class":4025,"line":4056},[4023,9540,9541],{"class":4986},"    shouldRetry",[4023,9543,9544],{"class":4564},"?: (",[4023,9546,7089],{"class":4910},[4023,9548,4577],{"class":4564},[4023,9550,7094],{"class":4894},[4023,9552,5243],{"class":4564},[4023,9554,5246],{"class":4890},[4023,9556,9557],{"class":4894}," boolean\n",[4023,9559,9560],{"class":4025,"line":4063},[4023,9561,4090],{"class":4564},[4023,9563,9564],{"class":4025,"line":4069},[4023,9565,4060],{"emptyLinePlaceholder":4059},[4023,9567,9568],{"class":4025,"line":4075},[4023,9569,4966],{"class":4965},[4023,9571,9572],{"class":4025,"line":4081},[4023,9573,9574],{"class":4965}," * Виконує асинхронну операцію з алгоритмом Full Jitter Exponential Backoff\n",[4023,9576,9577],{"class":4025,"line":4087},[4023,9578,4976],{"class":4965},[4023,9580,9581,9583,9586,9588,9591,9593,9595,9598,9601,9604,9606,9608,9610,9612,9615,9617,9619,9622,9625,9627,9629,9631],{"class":4025,"line":4093},[4023,9582,4887],{"class":4886},[4023,9584,9585],{"class":4890}," async",[4023,9587,4983],{"class":4890},[4023,9589,9590],{"class":4986}," retryWithBackoff",[4023,9592,4898],{"class":4564},[4023,9594,4901],{"class":4894},[4023,9596,9597],{"class":4564},">(",[4023,9599,9600],{"class":4986},"operation",[4023,9602,9603],{"class":4564},": () ",[4023,9605,5246],{"class":4890},[4023,9607,8365],{"class":4894},[4023,9609,4898],{"class":4564},[4023,9611,4901],{"class":4894},[4023,9613,9614],{"class":4564},">, ",[4023,9616,6818],{"class":4910},[4023,9618,4577],{"class":4564},[4023,9620,9621],{"class":4894},"RetryOptions",[4023,9623,9624],{"class":4564}," = {}): ",[4023,9626,6687],{"class":4894},[4023,9628,4898],{"class":4564},[4023,9630,4901],{"class":4894},[4023,9632,5024],{"class":4564},[4023,9634,9635,9637],{"class":4025,"line":4098},[4023,9636,5029],{"class":4890},[4023,9638,5446],{"class":4564},[4023,9640,9641,9644,9646,9649],{"class":4025,"line":4104},[4023,9642,9643],{"class":5032},"        maxRetries",[4023,9645,5403],{"class":4564},[4023,9647,9648],{"class":4601},"3",[4023,9650,5365],{"class":4564},[4023,9652,9653,9656,9658,9661],{"class":4025,"line":4110},[4023,9654,9655],{"class":5032},"        baseDelayMs",[4023,9657,5403],{"class":4564},[4023,9659,9660],{"class":4601},"500",[4023,9662,5365],{"class":4564},[4023,9664,9665,9668,9670,9672],{"class":4025,"line":4116},[4023,9666,9667],{"class":5032},"        maxDelayMs",[4023,9669,5403],{"class":4564},[4023,9671,6678],{"class":4601},[4023,9673,5365],{"class":4564},[4023,9675,9676,9679,9681,9683,9685,9688,9690,9692],{"class":4025,"line":4121},[4023,9677,9678],{"class":5032},"        shouldRetry",[4023,9680,6041],{"class":4564},[4023,9682,8710],{"class":4910},[4023,9684,4577],{"class":4564},[4023,9686,9687],{"class":4894},"any",[4023,9689,5243],{"class":4564},[4023,9691,5246],{"class":4890},[4023,9693,5446],{"class":4564},[4023,9695,9696],{"class":4025,"line":4126},[4023,9697,9698],{"class":4965},"            \u002F\u002F За замовчуванням повторюємо лише мережеві збої та 5xx статуси\n",[4023,9700,9701,9703,9705,9707,9709,9711,9713,9715,9717,9719,9721,9723,9725,9727,9730,9733,9735,9737,9739,9741,9743,9745,9748,9750],{"class":4025,"line":4132},[4023,9702,6097],{"class":4886},[4023,9704,3924],{"class":4564},[4023,9706,8710],{"class":4910},[4023,9708,6271],{"class":4564},[4023,9710,6913],{"class":4910},[4023,9712,6271],{"class":4564},[4023,9714,6946],{"class":4910},[4023,9716,7126],{"class":4564},[4023,9718,8710],{"class":4910},[4023,9720,3836],{"class":4564},[4023,9722,6913],{"class":4910},[4023,9724,3836],{"class":4564},[4023,9726,6946],{"class":4910},[4023,9728,9729],{"class":4564}," >= ",[4023,9731,9732],{"class":4601},"400",[4023,9734,7126],{"class":4564},[4023,9736,8710],{"class":4910},[4023,9738,3836],{"class":4564},[4023,9740,6913],{"class":4910},[4023,9742,3836],{"class":4564},[4023,9744,6946],{"class":4910},[4023,9746,9747],{"class":4564}," \u003C ",[4023,9749,9660],{"class":4601},[4023,9751,5088],{"class":4564},[4023,9753,9754,9757,9760],{"class":4025,"line":4138},[4023,9755,9756],{"class":4886},"                return",[4023,9758,9759],{"class":4890}," false",[4023,9761,9762],{"class":4965}," \u002F\u002F Ніколи не повторюємо 4xx\n",[4023,9764,9765],{"class":4025,"line":4144},[4023,9766,9013],{"class":4564},[4023,9768,9769,9771],{"class":4025,"line":4150},[4023,9770,6114],{"class":4886},[4023,9772,9773],{"class":4890}," true\n",[4023,9775,9776],{"class":4025,"line":4156},[4023,9777,5497],{"class":4564},[4023,9779,9780,9783],{"class":4025,"line":4162},[4023,9781,9782],{"class":4564},"    } = ",[4023,9784,9785],{"class":4910},"options\n",[4023,9787,9788],{"class":4025,"line":4167},[4023,9789,4060],{"emptyLinePlaceholder":4059},[4023,9791,9792,9795,9798,9800],{"class":4025,"line":4173},[4023,9793,9794],{"class":4890},"    let",[4023,9796,9797],{"class":4910}," attempt",[4023,9799,5403],{"class":4564},[4023,9801,9802],{"class":4601},"0\n",[4023,9804,9805],{"class":4025,"line":4179},[4023,9806,4060],{"emptyLinePlaceholder":4059},[4023,9808,9809,9812,9814,9817,9819,9822],{"class":4025,"line":4184},[4023,9810,9811],{"class":4886},"    while",[4023,9813,3924],{"class":4564},[4023,9815,9816],{"class":4910},"attempt",[4023,9818,9747],{"class":4564},[4023,9820,9821],{"class":4910},"maxRetries",[4023,9823,5088],{"class":4564},[4023,9825,9826,9828],{"class":4025,"line":4189},[4023,9827,8795],{"class":4886},[4023,9829,5446],{"class":4564},[4023,9831,9832,9834,9837,9840],{"class":4025,"line":4195},[4023,9833,6114],{"class":4886},[4023,9835,9836],{"class":4886}," await",[4023,9838,9839],{"class":4986}," operation",[4023,9841,6711],{"class":4564},[4023,9843,9844,9846,9848,9850,9852],{"class":4025,"line":4201},[4023,9845,8169],{"class":4564},[4023,9847,7084],{"class":4886},[4023,9849,3924],{"class":4564},[4023,9851,7089],{"class":4910},[4023,9853,5088],{"class":4564},[4023,9855,9856,9859],{"class":4025,"line":4207},[4023,9857,9858],{"class":4910},"            attempt",[4023,9860,9861],{"class":4564},"++\n",[4023,9863,9864],{"class":4025,"line":4213},[4023,9865,4060],{"emptyLinePlaceholder":4059},[4023,9867,9868,9870,9872,9874,9876,9878,9880,9883,9885,9887],{"class":4025,"line":4219},[4023,9869,6097],{"class":4886},[4023,9871,3924],{"class":4564},[4023,9873,9816],{"class":4910},[4023,9875,9729],{"class":4564},[4023,9877,9821],{"class":4910},[4023,9879,7014],{"class":4564},[4023,9881,9882],{"class":4986},"shouldRetry",[4023,9884,5122],{"class":4564},[4023,9886,7089],{"class":4910},[4023,9888,7029],{"class":4564},[4023,9890,9891,9894],{"class":4025,"line":4225},[4023,9892,9893],{"class":4886},"                throw",[4023,9895,7181],{"class":4910},[4023,9897,9898],{"class":4025,"line":4230},[4023,9899,9013],{"class":4564},[4023,9901,9902],{"class":4025,"line":4235},[4023,9903,4060],{"emptyLinePlaceholder":4059},[4023,9905,9906],{"class":4025,"line":4241},[4023,9907,9908],{"class":4965},"            \u002F\u002F Розрахунок експоненційного діапазону: base * 2^attempt\n",[4023,9910,9911,9913,9916,9918,9921,9923,9926,9928,9931,9933,9936,9939,9941,9943,9946,9948,9951,9953,9955],{"class":4025,"line":4247},[4023,9912,6075],{"class":4890},[4023,9914,9915],{"class":5032}," calculatedCap",[4023,9917,5403],{"class":4564},[4023,9919,9920],{"class":4910},"Math",[4023,9922,3836],{"class":4564},[4023,9924,9925],{"class":4986},"min",[4023,9927,5122],{"class":4564},[4023,9929,9930],{"class":4910},"maxDelayMs",[4023,9932,3928],{"class":4564},[4023,9934,9935],{"class":4910},"baseDelayMs",[4023,9937,9938],{"class":4564}," * ",[4023,9940,9920],{"class":4910},[4023,9942,3836],{"class":4564},[4023,9944,9945],{"class":4986},"pow",[4023,9947,5122],{"class":4564},[4023,9949,9950],{"class":4601},"2",[4023,9952,3928],{"class":4564},[4023,9954,9816],{"class":4910},[4023,9956,5290],{"class":4564},[4023,9958,9959],{"class":4025,"line":4253},[4023,9960,9961],{"class":4965},"            \u002F\u002F Full Jitter: рівномірний випадковий вибір від 0 до calculatedCap\n",[4023,9963,9964,9966,9969,9971,9973,9975,9978,9981],{"class":4025,"line":4259},[4023,9965,6075],{"class":4890},[4023,9967,9968],{"class":5032}," jitterDelay",[4023,9970,5403],{"class":4564},[4023,9972,9920],{"class":4910},[4023,9974,3836],{"class":4564},[4023,9976,9977],{"class":4986},"random",[4023,9979,9980],{"class":4564},"() * ",[4023,9982,9983],{"class":4910},"calculatedCap\n",[4023,9985,9986],{"class":4025,"line":4265},[4023,9987,4060],{"emptyLinePlaceholder":4059},[4023,9989,9990,9993,9995,9997,9999,10001,10003,10005,10008,10010,10012,10014,10017],{"class":4025,"line":4271},[4023,9991,9992],{"class":4886},"            await",[4023,9994,6928],{"class":4890},[4023,9996,8365],{"class":4894},[4023,9998,5238],{"class":4564},[4023,10000,8190],{"class":4910},[4023,10002,5243],{"class":4564},[4023,10004,5246],{"class":4890},[4023,10006,10007],{"class":4986}," setTimeout",[4023,10009,5122],{"class":4564},[4023,10011,8190],{"class":4910},[4023,10013,3928],{"class":4564},[4023,10015,10016],{"class":4910},"jitterDelay",[4023,10018,5290],{"class":4564},[4023,10020,10021],{"class":4025,"line":4276},[4023,10022,6971],{"class":4564},[4023,10024,10025],{"class":4025,"line":5500},[4023,10026,4147],{"class":4564},[4023,10028,10029],{"class":4025,"line":5505},[4023,10030,4060],{"emptyLinePlaceholder":4059},[4023,10032,10033,10036,10038,10040,10042,10045],{"class":4025,"line":5510},[4023,10034,10035],{"class":4886},"    throw",[4023,10037,6928],{"class":4890},[4023,10039,6931],{"class":4986},[4023,10041,5122],{"class":4564},[4023,10043,10044],{"class":4580},"'RETRY_EXHAUSTED'",[4023,10046,5131],{"class":4564},[4023,10048,10049],{"class":4025,"line":5515},[4023,10050,4090],{"class":4564},[3964,10052],{},[3738,10054,10056,10057],{"id":10055},"уніфікована-обробка-помилок-клас-apperror","Уніфікована обробка помилок: клас ",[3799,10058,3897],{},[3743,10060,10061,10062,10064,10065,3928,10067,10069],{},"Створимо клас ",[3799,10063,3897],{},", який зводить різнорідні помилки від ",[3799,10066,3850],{},[3799,10068,3844],{}," та рантайму JavaScript до єдиного типізованого формату:",[4014,10071,10073],{"className":4877,"code":10072,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fapi\u002FAppError.ts\nimport axios, { AxiosError } from 'axios'\n\nexport enum ErrorKind {\n    Network = 'NETWORK',\n    Timeout = 'TIMEOUT',\n    Server = 'SERVER',\n    Unauthorized = 'UNAUTHORIZED',\n    Forbidden = 'FORBIDDEN',\n    NotFound = 'NOT_FOUND',\n    Validation = 'VALIDATION',\n    SchemaMismatch = 'SCHEMA_MISMATCH',\n    Unknown = 'UNKNOWN',\n}\n\nexport class AppError extends Error {\n    public readonly kind: ErrorKind\n    public readonly userMessage: string\n    public readonly statusCode?: number\n    public readonly originalError?: unknown\n\n    constructor(params: { kind: ErrorKind; userMessage: string; statusCode?: number; originalError?: unknown }) {\n        super(params.userMessage)\n        this.name = 'AppError'\n        this.kind = params.kind\n        this.userMessage = params.userMessage\n        this.statusCode = params.statusCode\n        this.originalError = params.originalError\n\n        \u002F\u002F Зберігаємо коректний прототип для перевірки через instanceof\n        Object.setPrototypeOf(this, AppError.prototype)\n    }\n\n    \u002F**\n     * Фабричний метод для приведення будь-якого невідомого винятку до стандарту AppError\n     *\u002F\n    public static from(error: unknown): AppError {\n        if (error instanceof AppError) {\n            return error\n        }\n\n        if (axios.isAxiosError(error)) {\n            const axiosError = error as AxiosError\u003C{ message?: string; error?: string }>\n\n            \u002F\u002F 1. Помилка таймауту\n            if (axiosError.code === 'ECONNABORTED' || axiosError.message.includes('timeout')) {\n                return new AppError({\n                    kind: ErrorKind.Timeout,\n                    userMessage: 'Час очікування відповіді вичерпано. Перевірте швидкість інтернету.',\n                    originalError: error,\n                })\n            }\n\n            \u002F\u002F 2. Відсутність фізичного з'єднання або DNS збій\n            if (!axiosError.response) {\n                return new AppError({\n                    kind: ErrorKind.Network,\n                    userMessage: \"Зв'язок із сервером відсутній. Перевірте мережеве з'єднання.\",\n                    originalError: error,\n                })\n            }\n\n            const status = axiosError.response.status\n            const serverMessage = axiosError.response.data?.message\n\n            \u002F\u002F 3. Авторизаційні помилки\n            if (status === 401) {\n                return new AppError({\n                    kind: ErrorKind.Unauthorized,\n                    userMessage: 'Сесія завершилася. Будь ласка, виконайте вхід повторно.',\n                    statusCode: status,\n                    originalError: error,\n                })\n            }\n\n            if (status === 403) {\n                return new AppError({\n                    kind: ErrorKind.Forbidden,\n                    userMessage: 'У вас немає прав для виконання цієї дії.',\n                    statusCode: status,\n                    originalError: error,\n                })\n            }\n\n            if (status === 404) {\n                return new AppError({\n                    kind: ErrorKind.NotFound,\n                    userMessage: serverMessage ?? 'Запитуваний ресурс не знайдено на сервері.',\n                    statusCode: status,\n                    originalError: error,\n                })\n            }\n\n            if (status === 422 || status === 400) {\n                return new AppError({\n                    kind: ErrorKind.Validation,\n                    userMessage: serverMessage ?? 'Надіслані дані містять помилки валідації.',\n                    statusCode: status,\n                    originalError: error,\n                })\n            }\n\n            \u002F\u002F 4. Серверні помилки 5xx\n            if (status >= 500) {\n                return new AppError({\n                    kind: ErrorKind.Server,\n                    userMessage: 'На сервері сталася технічна помилка. Ми вже працюємо над її усуненням.',\n                    statusCode: status,\n                    originalError: error,\n                })\n            }\n        }\n\n        \u002F\u002F 5. Нерозпізнані або синтаксичні винятки\n        return new AppError({\n            kind: ErrorKind.Unknown,\n            userMessage: 'Сталася непередбачена помилка. Спробуйте пізніше.',\n            originalError: error,\n        })\n    }\n}\n",[3799,10074,10075,10080,10096,10100,10112,10124,10136,10148,10160,10172,10184,10196,10208,10220,10224,10228,10244,10260,10273,10286,10300,10304,10356,10371,10385,10402,10419,10436,10453,10457,10462,10488,10492,10496,10501,10506,10511,10534,10548,10554,10558,10562,10581,10617,10621,10626,10663,10673,10687,10697,10707,10711,10715,10719,10724,10738,10748,10760,10769,10777,10781,10785,10789,10809,10833,10837,10842,10856,10866,10879,10888,10897,10905,10909,10913,10917,10932,10942,10955,10964,10972,10980,10984,10988,10992,11007,11017,11030,11043,11051,11059,11063,11067,11071,11093,11103,11116,11129,11137,11145,11149,11153,11157,11162,11176,11186,11199,11208,11216,11224,11228,11232,11236,11240,11245,11255,11269,11279,11288,11292,11296],{"__ignoreMap":4019},[4023,10076,10077],{"class":4025,"line":4026},[4023,10078,10079],{"class":4965},"\u002F\u002F src\u002Fapi\u002FAppError.ts\n",[4023,10081,10082,10084,10086,10088,10090,10092,10094],{"class":4025,"line":4032},[4023,10083,5731],{"class":4886},[4023,10085,7651],{"class":4910},[4023,10087,5737],{"class":4564},[4023,10089,7241],{"class":4910},[4023,10091,5743],{"class":4564},[4023,10093,5746],{"class":4886},[4023,10095,7672],{"class":4580},[4023,10097,10098],{"class":4025,"line":4038},[4023,10099,4060],{"emptyLinePlaceholder":4059},[4023,10101,10102,10104,10107,10110],{"class":4025,"line":4044},[4023,10103,4887],{"class":4886},[4023,10105,10106],{"class":4890}," enum",[4023,10108,10109],{"class":4894}," ErrorKind",[4023,10111,5446],{"class":4564},[4023,10113,10114,10117,10119,10122],{"class":4025,"line":4050},[4023,10115,10116],{"class":5032},"    Network",[4023,10118,5403],{"class":4564},[4023,10120,10121],{"class":4580},"'NETWORK'",[4023,10123,5365],{"class":4564},[4023,10125,10126,10129,10131,10134],{"class":4025,"line":4056},[4023,10127,10128],{"class":5032},"    Timeout",[4023,10130,5403],{"class":4564},[4023,10132,10133],{"class":4580},"'TIMEOUT'",[4023,10135,5365],{"class":4564},[4023,10137,10138,10141,10143,10146],{"class":4025,"line":4063},[4023,10139,10140],{"class":5032},"    Server",[4023,10142,5403],{"class":4564},[4023,10144,10145],{"class":4580},"'SERVER'",[4023,10147,5365],{"class":4564},[4023,10149,10150,10153,10155,10158],{"class":4025,"line":4069},[4023,10151,10152],{"class":5032},"    Unauthorized",[4023,10154,5403],{"class":4564},[4023,10156,10157],{"class":4580},"'UNAUTHORIZED'",[4023,10159,5365],{"class":4564},[4023,10161,10162,10165,10167,10170],{"class":4025,"line":4075},[4023,10163,10164],{"class":5032},"    Forbidden",[4023,10166,5403],{"class":4564},[4023,10168,10169],{"class":4580},"'FORBIDDEN'",[4023,10171,5365],{"class":4564},[4023,10173,10174,10177,10179,10182],{"class":4025,"line":4081},[4023,10175,10176],{"class":5032},"    NotFound",[4023,10178,5403],{"class":4564},[4023,10180,10181],{"class":4580},"'NOT_FOUND'",[4023,10183,5365],{"class":4564},[4023,10185,10186,10189,10191,10194],{"class":4025,"line":4087},[4023,10187,10188],{"class":5032},"    Validation",[4023,10190,5403],{"class":4564},[4023,10192,10193],{"class":4580},"'VALIDATION'",[4023,10195,5365],{"class":4564},[4023,10197,10198,10201,10203,10206],{"class":4025,"line":4093},[4023,10199,10200],{"class":5032},"    SchemaMismatch",[4023,10202,5403],{"class":4564},[4023,10204,10205],{"class":4580},"'SCHEMA_MISMATCH'",[4023,10207,5365],{"class":4564},[4023,10209,10210,10213,10215,10218],{"class":4025,"line":4098},[4023,10211,10212],{"class":5032},"    Unknown",[4023,10214,5403],{"class":4564},[4023,10216,10217],{"class":4580},"'UNKNOWN'",[4023,10219,5365],{"class":4564},[4023,10221,10222],{"class":4025,"line":4104},[4023,10223,4090],{"class":4564},[4023,10225,10226],{"class":4025,"line":4110},[4023,10227,4060],{"emptyLinePlaceholder":4059},[4023,10229,10230,10232,10235,10238,10240,10242],{"class":4025,"line":4116},[4023,10231,4887],{"class":4886},[4023,10233,10234],{"class":4890}," class",[4023,10236,10237],{"class":4894}," AppError",[4023,10239,4904],{"class":4890},[4023,10241,6931],{"class":4894},[4023,10243,5446],{"class":4564},[4023,10245,10246,10249,10252,10255,10257],{"class":4025,"line":4121},[4023,10247,10248],{"class":4890},"    public",[4023,10250,10251],{"class":4890}," readonly",[4023,10253,10254],{"class":4910}," kind",[4023,10256,4577],{"class":4564},[4023,10258,10259],{"class":4894},"ErrorKind\n",[4023,10261,10262,10264,10266,10269,10271],{"class":4025,"line":4126},[4023,10263,10248],{"class":4890},[4023,10265,10251],{"class":4890},[4023,10267,10268],{"class":4910}," userMessage",[4023,10270,4577],{"class":4564},[4023,10272,5828],{"class":4894},[4023,10274,10275,10277,10279,10282,10284],{"class":4025,"line":4132},[4023,10276,10248],{"class":4890},[4023,10278,10251],{"class":4890},[4023,10280,10281],{"class":4910}," statusCode",[4023,10283,8469],{"class":4564},[4023,10285,5847],{"class":4894},[4023,10287,10288,10290,10292,10295,10297],{"class":4025,"line":4138},[4023,10289,10248],{"class":4890},[4023,10291,10251],{"class":4890},[4023,10293,10294],{"class":4910}," originalError",[4023,10296,8469],{"class":4564},[4023,10298,10299],{"class":4894},"unknown\n",[4023,10301,10302],{"class":4025,"line":4144},[4023,10303,4060],{"emptyLinePlaceholder":4059},[4023,10305,10306,10309,10311,10314,10317,10320,10322,10325,10328,10331,10333,10335,10337,10340,10342,10344,10346,10349,10351,10353],{"class":4025,"line":4150},[4023,10307,10308],{"class":4890},"    constructor",[4023,10310,5122],{"class":4564},[4023,10312,10313],{"class":4910},"params",[4023,10315,10316],{"class":4564},": { ",[4023,10318,10319],{"class":4910},"kind",[4023,10321,4577],{"class":4564},[4023,10323,10324],{"class":4894},"ErrorKind",[4023,10326,10327],{"class":4564},"; ",[4023,10329,10330],{"class":4910},"userMessage",[4023,10332,4577],{"class":4564},[4023,10334,4916],{"class":4894},[4023,10336,10327],{"class":4564},[4023,10338,10339],{"class":4910},"statusCode",[4023,10341,8469],{"class":4564},[4023,10343,6673],{"class":4894},[4023,10345,10327],{"class":4564},[4023,10347,10348],{"class":4910},"originalError",[4023,10350,8469],{"class":4564},[4023,10352,7094],{"class":4894},[4023,10354,10355],{"class":4564}," }) {\n",[4023,10357,10358,10361,10363,10365,10367,10369],{"class":4025,"line":4156},[4023,10359,10360],{"class":4890},"        super",[4023,10362,5122],{"class":4564},[4023,10364,10313],{"class":4910},[4023,10366,3836],{"class":4564},[4023,10368,10330],{"class":4910},[4023,10370,5131],{"class":4564},[4023,10372,10373,10376,10378,10380,10382],{"class":4025,"line":4162},[4023,10374,10375],{"class":4890},"        this",[4023,10377,3836],{"class":4564},[4023,10379,7133],{"class":4910},[4023,10381,5403],{"class":4564},[4023,10383,10384],{"class":4580},"'AppError'\n",[4023,10386,10387,10389,10391,10393,10395,10397,10399],{"class":4025,"line":4167},[4023,10388,10375],{"class":4890},[4023,10390,3836],{"class":4564},[4023,10392,10319],{"class":4910},[4023,10394,5403],{"class":4564},[4023,10396,10313],{"class":4910},[4023,10398,3836],{"class":4564},[4023,10400,10401],{"class":4910},"kind\n",[4023,10403,10404,10406,10408,10410,10412,10414,10416],{"class":4025,"line":4173},[4023,10405,10375],{"class":4890},[4023,10407,3836],{"class":4564},[4023,10409,10330],{"class":4910},[4023,10411,5403],{"class":4564},[4023,10413,10313],{"class":4910},[4023,10415,3836],{"class":4564},[4023,10417,10418],{"class":4910},"userMessage\n",[4023,10420,10421,10423,10425,10427,10429,10431,10433],{"class":4025,"line":4179},[4023,10422,10375],{"class":4890},[4023,10424,3836],{"class":4564},[4023,10426,10339],{"class":4910},[4023,10428,5403],{"class":4564},[4023,10430,10313],{"class":4910},[4023,10432,3836],{"class":4564},[4023,10434,10435],{"class":4910},"statusCode\n",[4023,10437,10438,10440,10442,10444,10446,10448,10450],{"class":4025,"line":4184},[4023,10439,10375],{"class":4890},[4023,10441,3836],{"class":4564},[4023,10443,10348],{"class":4910},[4023,10445,5403],{"class":4564},[4023,10447,10313],{"class":4910},[4023,10449,3836],{"class":4564},[4023,10451,10452],{"class":4910},"originalError\n",[4023,10454,10455],{"class":4025,"line":4189},[4023,10456,4060],{"emptyLinePlaceholder":4059},[4023,10458,10459],{"class":4025,"line":4195},[4023,10460,10461],{"class":4965},"        \u002F\u002F Зберігаємо коректний прототип для перевірки через instanceof\n",[4023,10463,10464,10467,10469,10472,10474,10477,10479,10481,10483,10486],{"class":4025,"line":4201},[4023,10465,10466],{"class":4910},"        Object",[4023,10468,3836],{"class":4564},[4023,10470,10471],{"class":4986},"setPrototypeOf",[4023,10473,5122],{"class":4564},[4023,10475,10476],{"class":4890},"this",[4023,10478,3928],{"class":4564},[4023,10480,3897],{"class":4894},[4023,10482,3836],{"class":4564},[4023,10484,10485],{"class":4910},"prototype",[4023,10487,5131],{"class":4564},[4023,10489,10490],{"class":4025,"line":4207},[4023,10491,4147],{"class":4564},[4023,10493,10494],{"class":4025,"line":4213},[4023,10495,4060],{"emptyLinePlaceholder":4059},[4023,10497,10498],{"class":4025,"line":4219},[4023,10499,10500],{"class":4965},"    \u002F**\n",[4023,10502,10503],{"class":4025,"line":4225},[4023,10504,10505],{"class":4965},"     * Фабричний метод для приведення будь-якого невідомого винятку до стандарту AppError\n",[4023,10507,10508],{"class":4025,"line":4230},[4023,10509,10510],{"class":4965},"     *\u002F\n",[4023,10512,10513,10515,10518,10520,10522,10524,10526,10528,10530,10532],{"class":4025,"line":4235},[4023,10514,10248],{"class":4890},[4023,10516,10517],{"class":4890}," static",[4023,10519,7697],{"class":4986},[4023,10521,5122],{"class":4564},[4023,10523,7089],{"class":4910},[4023,10525,4577],{"class":4564},[4023,10527,7094],{"class":4894},[4023,10529,5269],{"class":4564},[4023,10531,3897],{"class":4894},[4023,10533,5446],{"class":4564},[4023,10535,10536,10538,10540,10542,10544,10546],{"class":4025,"line":4241},[4023,10537,6908],{"class":4886},[4023,10539,3924],{"class":4564},[4023,10541,7089],{"class":4910},[4023,10543,7121],{"class":4890},[4023,10545,10237],{"class":4894},[4023,10547,5088],{"class":4564},[4023,10549,10550,10552],{"class":4025,"line":4247},[4023,10551,6114],{"class":4886},[4023,10553,7181],{"class":4910},[4023,10555,10556],{"class":4025,"line":4253},[4023,10557,6971],{"class":4564},[4023,10559,10560],{"class":4025,"line":4259},[4023,10561,4060],{"emptyLinePlaceholder":4059},[4023,10563,10564,10566,10568,10570,10572,10575,10577,10579],{"class":4025,"line":4265},[4023,10565,6908],{"class":4886},[4023,10567,3924],{"class":4564},[4023,10569,3850],{"class":4910},[4023,10571,3836],{"class":4564},[4023,10573,10574],{"class":4986},"isAxiosError",[4023,10576,5122],{"class":4564},[4023,10578,7089],{"class":4910},[4023,10580,7029],{"class":4564},[4023,10582,10583,10585,10588,10590,10592,10594,10597,10599,10602,10604,10606,10608,10610,10612,10614],{"class":4025,"line":4271},[4023,10584,6075],{"class":4890},[4023,10586,10587],{"class":5032}," axiosError",[4023,10589,5403],{"class":4564},[4023,10591,7089],{"class":4910},[4023,10593,8457],{"class":4886},[4023,10595,10596],{"class":4894}," AxiosError",[4023,10598,8831],{"class":4564},[4023,10600,10601],{"class":4910},"message",[4023,10603,8469],{"class":4564},[4023,10605,4916],{"class":4894},[4023,10607,10327],{"class":4564},[4023,10609,7089],{"class":4910},[4023,10611,8469],{"class":4564},[4023,10613,4916],{"class":4894},[4023,10615,10616],{"class":4564}," }>\n",[4023,10618,10619],{"class":4025,"line":4276},[4023,10620,4060],{"emptyLinePlaceholder":4059},[4023,10622,10623],{"class":4025,"line":5500},[4023,10624,10625],{"class":4965},"            \u002F\u002F 1. Помилка таймауту\n",[4023,10627,10628,10630,10632,10635,10637,10639,10641,10644,10646,10648,10650,10652,10654,10656,10658,10661],{"class":4025,"line":5505},[4023,10629,6097],{"class":4886},[4023,10631,3924],{"class":4564},[4023,10633,10634],{"class":4910},"axiosError",[4023,10636,3836],{"class":4564},[4023,10638,3799],{"class":4910},[4023,10640,7136],{"class":4564},[4023,10642,10643],{"class":4580},"'ECONNABORTED'",[4023,10645,8516],{"class":4564},[4023,10647,10634],{"class":4910},[4023,10649,3836],{"class":4564},[4023,10651,10601],{"class":4910},[4023,10653,3836],{"class":4564},[4023,10655,7021],{"class":4986},[4023,10657,5122],{"class":4564},[4023,10659,10660],{"class":4580},"'timeout'",[4023,10662,7029],{"class":4564},[4023,10664,10665,10667,10669,10671],{"class":4025,"line":5510},[4023,10666,9756],{"class":4886},[4023,10668,6928],{"class":4890},[4023,10670,10237],{"class":4986},[4023,10672,7892],{"class":4564},[4023,10674,10675,10678,10680,10682,10685],{"class":4025,"line":5515},[4023,10676,10677],{"class":4910},"                    kind:",[4023,10679,10109],{"class":4910},[4023,10681,3836],{"class":4564},[4023,10683,10684],{"class":4910},"Timeout",[4023,10686,5365],{"class":4564},[4023,10688,10689,10692,10695],{"class":4025,"line":5520},[4023,10690,10691],{"class":4910},"                    userMessage:",[4023,10693,10694],{"class":4580}," 'Час очікування відповіді вичерпано. Перевірте швидкість інтернету.'",[4023,10696,5365],{"class":4564},[4023,10698,10699,10702,10705],{"class":4025,"line":5526},[4023,10700,10701],{"class":4910},"                    originalError:",[4023,10703,10704],{"class":4910}," error",[4023,10706,5365],{"class":4564},[4023,10708,10709],{"class":4025,"line":5531},[4023,10710,8743],{"class":4564},[4023,10712,10713],{"class":4025,"line":5557},[4023,10714,9013],{"class":4564},[4023,10716,10717],{"class":4025,"line":5572},[4023,10718,4060],{"emptyLinePlaceholder":4059},[4023,10720,10721],{"class":4025,"line":5584},[4023,10722,10723],{"class":4965},"            \u002F\u002F 2. Відсутність фізичного з'єднання або DNS збій\n",[4023,10725,10726,10728,10730,10732,10734,10736],{"class":4025,"line":5597},[4023,10727,6097],{"class":4886},[4023,10729,5424],{"class":4564},[4023,10731,10634],{"class":4910},[4023,10733,3836],{"class":4564},[4023,10735,6913],{"class":4910},[4023,10737,5088],{"class":4564},[4023,10739,10740,10742,10744,10746],{"class":4025,"line":5625},[4023,10741,9756],{"class":4886},[4023,10743,6928],{"class":4890},[4023,10745,10237],{"class":4986},[4023,10747,7892],{"class":4564},[4023,10749,10750,10752,10754,10756,10758],{"class":4025,"line":5630},[4023,10751,10677],{"class":4910},[4023,10753,10109],{"class":4910},[4023,10755,3836],{"class":4564},[4023,10757,1945],{"class":4910},[4023,10759,5365],{"class":4564},[4023,10761,10762,10764,10767],{"class":4025,"line":5637},[4023,10763,10691],{"class":4910},[4023,10765,10766],{"class":4580}," \"Зв'язок із сервером відсутній. Перевірте мережеве з'єднання.\"",[4023,10768,5365],{"class":4564},[4023,10770,10771,10773,10775],{"class":4025,"line":5644},[4023,10772,10701],{"class":4910},[4023,10774,10704],{"class":4910},[4023,10776,5365],{"class":4564},[4023,10778,10779],{"class":4025,"line":5657},[4023,10780,8743],{"class":4564},[4023,10782,10783],{"class":4025,"line":5668},[4023,10784,9013],{"class":4564},[4023,10786,10787],{"class":4025,"line":5673},[4023,10788,4060],{"emptyLinePlaceholder":4059},[4023,10790,10791,10793,10796,10798,10800,10802,10804,10806],{"class":4025,"line":5709},[4023,10792,6075],{"class":4890},[4023,10794,10795],{"class":5032}," status",[4023,10797,5403],{"class":4564},[4023,10799,10634],{"class":4910},[4023,10801,3836],{"class":4564},[4023,10803,6913],{"class":4910},[4023,10805,3836],{"class":4564},[4023,10807,10808],{"class":4910},"status\n",[4023,10810,10811,10813,10816,10818,10820,10822,10824,10826,10828,10830],{"class":4025,"line":5714},[4023,10812,6075],{"class":4890},[4023,10814,10815],{"class":5032}," serverMessage",[4023,10817,5403],{"class":4564},[4023,10819,10634],{"class":4910},[4023,10821,3836],{"class":4564},[4023,10823,6913],{"class":4910},[4023,10825,3836],{"class":4564},[4023,10827,8917],{"class":4910},[4023,10829,6271],{"class":4564},[4023,10831,10832],{"class":4910},"message\n",[4023,10834,10835],{"class":4025,"line":8477},[4023,10836,4060],{"emptyLinePlaceholder":4059},[4023,10838,10839],{"class":4025,"line":8482},[4023,10840,10841],{"class":4965},"            \u002F\u002F 3. Авторизаційні помилки\n",[4023,10843,10844,10846,10848,10850,10852,10854],{"class":4025,"line":8488},[4023,10845,6097],{"class":4886},[4023,10847,3924],{"class":4564},[4023,10849,6946],{"class":4910},[4023,10851,7136],{"class":4564},[4023,10853,8508],{"class":4601},[4023,10855,5088],{"class":4564},[4023,10857,10858,10860,10862,10864],{"class":4025,"line":8527},[4023,10859,9756],{"class":4886},[4023,10861,6928],{"class":4890},[4023,10863,10237],{"class":4986},[4023,10865,7892],{"class":4564},[4023,10867,10868,10870,10872,10874,10877],{"class":4025,"line":8544},[4023,10869,10677],{"class":4910},[4023,10871,10109],{"class":4910},[4023,10873,3836],{"class":4564},[4023,10875,10876],{"class":4910},"Unauthorized",[4023,10878,5365],{"class":4564},[4023,10880,10881,10883,10886],{"class":4025,"line":8549},[4023,10882,10691],{"class":4910},[4023,10884,10885],{"class":4580}," 'Сесія завершилася. Будь ласка, виконайте вхід повторно.'",[4023,10887,5365],{"class":4564},[4023,10889,10890,10893,10895],{"class":4025,"line":8554},[4023,10891,10892],{"class":4910},"                    statusCode:",[4023,10894,10795],{"class":4910},[4023,10896,5365],{"class":4564},[4023,10898,10899,10901,10903],{"class":4025,"line":8560},[4023,10900,10701],{"class":4910},[4023,10902,10704],{"class":4910},[4023,10904,5365],{"class":4564},[4023,10906,10907],{"class":4025,"line":8572},[4023,10908,8743],{"class":4564},[4023,10910,10911],{"class":4025,"line":8595},[4023,10912,9013],{"class":4564},[4023,10914,10915],{"class":4025,"line":8607},[4023,10916,4060],{"emptyLinePlaceholder":4059},[4023,10918,10919,10921,10923,10925,10927,10930],{"class":4025,"line":8630},[4023,10920,6097],{"class":4886},[4023,10922,3924],{"class":4564},[4023,10924,6946],{"class":4910},[4023,10926,7136],{"class":4564},[4023,10928,10929],{"class":4601},"403",[4023,10931,5088],{"class":4564},[4023,10933,10934,10936,10938,10940],{"class":4025,"line":8646},[4023,10935,9756],{"class":4886},[4023,10937,6928],{"class":4890},[4023,10939,10237],{"class":4986},[4023,10941,7892],{"class":4564},[4023,10943,10944,10946,10948,10950,10953],{"class":4025,"line":8672},[4023,10945,10677],{"class":4910},[4023,10947,10109],{"class":4910},[4023,10949,3836],{"class":4564},[4023,10951,10952],{"class":4910},"Forbidden",[4023,10954,5365],{"class":4564},[4023,10956,10957,10959,10962],{"class":4025,"line":8678},[4023,10958,10691],{"class":4910},[4023,10960,10961],{"class":4580}," 'У вас немає прав для виконання цієї дії.'",[4023,10963,5365],{"class":4564},[4023,10965,10966,10968,10970],{"class":4025,"line":8694},[4023,10967,10892],{"class":4910},[4023,10969,10795],{"class":4910},[4023,10971,5365],{"class":4564},[4023,10973,10974,10976,10978],{"class":4025,"line":8700},[4023,10975,10701],{"class":4910},[4023,10977,10704],{"class":4910},[4023,10979,5365],{"class":4564},[4023,10981,10982],{"class":4025,"line":8723},[4023,10983,8743],{"class":4564},[4023,10985,10986],{"class":4025,"line":8735},[4023,10987,9013],{"class":4564},[4023,10989,10990],{"class":4025,"line":8740},[4023,10991,4060],{"emptyLinePlaceholder":4059},[4023,10993,10994,10996,10998,11000,11002,11005],{"class":4025,"line":8746},[4023,10995,6097],{"class":4886},[4023,10997,3924],{"class":4564},[4023,10999,6946],{"class":4910},[4023,11001,7136],{"class":4564},[4023,11003,11004],{"class":4601},"404",[4023,11006,5088],{"class":4564},[4023,11008,11009,11011,11013,11015],{"class":4025,"line":8752},[4023,11010,9756],{"class":4886},[4023,11012,6928],{"class":4890},[4023,11014,10237],{"class":4986},[4023,11016,7892],{"class":4564},[4023,11018,11019,11021,11023,11025,11028],{"class":4025,"line":8757},[4023,11020,10677],{"class":4910},[4023,11022,10109],{"class":4910},[4023,11024,3836],{"class":4564},[4023,11026,11027],{"class":4910},"NotFound",[4023,11029,5365],{"class":4564},[4023,11031,11032,11034,11036,11038,11041],{"class":4025,"line":8762},[4023,11033,10691],{"class":4910},[4023,11035,10815],{"class":4910},[4023,11037,7976],{"class":4564},[4023,11039,11040],{"class":4580},"'Запитуваний ресурс не знайдено на сервері.'",[4023,11042,5365],{"class":4564},[4023,11044,11045,11047,11049],{"class":4025,"line":8777},[4023,11046,10892],{"class":4910},[4023,11048,10795],{"class":4910},[4023,11050,5365],{"class":4564},[4023,11052,11053,11055,11057],{"class":4025,"line":8787},[4023,11054,10701],{"class":4910},[4023,11056,10704],{"class":4910},[4023,11058,5365],{"class":4564},[4023,11060,11061],{"class":4025,"line":8792},[4023,11062,8743],{"class":4564},[4023,11064,11065],{"class":4025,"line":8800},[4023,11066,9013],{"class":4564},[4023,11068,11069],{"class":4025,"line":8806},[4023,11070,4060],{"emptyLinePlaceholder":4059},[4023,11072,11073,11075,11077,11079,11081,11083,11085,11087,11089,11091],{"class":4025,"line":8812},[4023,11074,6097],{"class":4886},[4023,11076,3924],{"class":4564},[4023,11078,6946],{"class":4910},[4023,11080,7136],{"class":4564},[4023,11082,9259],{"class":4601},[4023,11084,8516],{"class":4564},[4023,11086,6946],{"class":4910},[4023,11088,7136],{"class":4564},[4023,11090,9732],{"class":4601},[4023,11092,5088],{"class":4564},[4023,11094,11095,11097,11099,11101],{"class":4025,"line":8843},[4023,11096,9756],{"class":4886},[4023,11098,6928],{"class":4890},[4023,11100,10237],{"class":4986},[4023,11102,7892],{"class":4564},[4023,11104,11105,11107,11109,11111,11114],{"class":4025,"line":8864},[4023,11106,10677],{"class":4910},[4023,11108,10109],{"class":4910},[4023,11110,3836],{"class":4564},[4023,11112,11113],{"class":4910},"Validation",[4023,11115,5365],{"class":4564},[4023,11117,11118,11120,11122,11124,11127],{"class":4025,"line":8878},[4023,11119,10691],{"class":4910},[4023,11121,10815],{"class":4910},[4023,11123,7976],{"class":4564},[4023,11125,11126],{"class":4580},"'Надіслані дані містять помилки валідації.'",[4023,11128,5365],{"class":4564},[4023,11130,11131,11133,11135],{"class":4025,"line":8891},[4023,11132,10892],{"class":4910},[4023,11134,10795],{"class":4910},[4023,11136,5365],{"class":4564},[4023,11138,11139,11141,11143],{"class":4025,"line":8897},[4023,11140,10701],{"class":4910},[4023,11142,10704],{"class":4910},[4023,11144,5365],{"class":4564},[4023,11146,11147],{"class":4025,"line":8902},[4023,11148,8743],{"class":4564},[4023,11150,11151],{"class":4025,"line":8925},[4023,11152,9013],{"class":4564},[4023,11154,11155],{"class":4025,"line":8930},[4023,11156,4060],{"emptyLinePlaceholder":4059},[4023,11158,11159],{"class":4025,"line":8936},[4023,11160,11161],{"class":4965},"            \u002F\u002F 4. Серверні помилки 5xx\n",[4023,11163,11164,11166,11168,11170,11172,11174],{"class":4025,"line":8942},[4023,11165,6097],{"class":4886},[4023,11167,3924],{"class":4564},[4023,11169,6946],{"class":4910},[4023,11171,9729],{"class":4564},[4023,11173,9660],{"class":4601},[4023,11175,5088],{"class":4564},[4023,11177,11178,11180,11182,11184],{"class":4025,"line":8947},[4023,11179,9756],{"class":4886},[4023,11181,6928],{"class":4890},[4023,11183,10237],{"class":4986},[4023,11185,7892],{"class":4564},[4023,11187,11188,11190,11192,11194,11197],{"class":4025,"line":8964},[4023,11189,10677],{"class":4910},[4023,11191,10109],{"class":4910},[4023,11193,3836],{"class":4564},[4023,11195,11196],{"class":4910},"Server",[4023,11198,5365],{"class":4564},[4023,11200,11201,11203,11206],{"class":4025,"line":8969},[4023,11202,10691],{"class":4910},[4023,11204,11205],{"class":4580}," 'На сервері сталася технічна помилка. Ми вже працюємо над її усуненням.'",[4023,11207,5365],{"class":4564},[4023,11209,11210,11212,11214],{"class":4025,"line":8984},[4023,11211,10892],{"class":4910},[4023,11213,10795],{"class":4910},[4023,11215,5365],{"class":4564},[4023,11217,11218,11220,11222],{"class":4025,"line":9010},[4023,11219,10701],{"class":4910},[4023,11221,10704],{"class":4910},[4023,11223,5365],{"class":4564},[4023,11225,11226],{"class":4025,"line":9016},[4023,11227,8743],{"class":4564},[4023,11229,11230],{"class":4025,"line":9021},[4023,11231,9013],{"class":4564},[4023,11233,11234],{"class":4025,"line":9034},[4023,11235,6971],{"class":4564},[4023,11237,11238],{"class":4025,"line":9048},[4023,11239,4060],{"emptyLinePlaceholder":4059},[4023,11241,11242],{"class":4025,"line":9063},[4023,11243,11244],{"class":4965},"        \u002F\u002F 5. Нерозпізнані або синтаксичні винятки\n",[4023,11246,11247,11249,11251,11253],{"class":4025,"line":9069},[4023,11248,7057],{"class":4886},[4023,11250,6928],{"class":4890},[4023,11252,10237],{"class":4986},[4023,11254,7892],{"class":4564},[4023,11256,11257,11260,11262,11264,11267],{"class":4025,"line":9086},[4023,11258,11259],{"class":4910},"            kind:",[4023,11261,10109],{"class":4910},[4023,11263,3836],{"class":4564},[4023,11265,11266],{"class":4910},"Unknown",[4023,11268,5365],{"class":4564},[4023,11270,11271,11274,11277],{"class":4025,"line":9096},[4023,11272,11273],{"class":4910},"            userMessage:",[4023,11275,11276],{"class":4580}," 'Сталася непередбачена помилка. Спробуйте пізніше.'",[4023,11278,5365],{"class":4564},[4023,11280,11281,11284,11286],{"class":4025,"line":9106},[4023,11282,11283],{"class":4910},"            originalError:",[4023,11285,10704],{"class":4910},[4023,11287,5365],{"class":4564},[4023,11289,11290],{"class":4025,"line":9111},[4023,11291,6148],{"class":4564},[4023,11293,11294],{"class":4025,"line":9116},[4023,11295,4147],{"class":4564},[4023,11297,11299],{"class":4025,"line":11298},121,[4023,11300,4090],{"class":4564},[3964,11302],{},[3738,11304,11306,11307],{"id":11305},"моніторинг-стану-звязку-в-реальному-часі-react-native-communitynetinfo","Моніторинг стану зв'язку в реальному часі: ",[3799,11308,3906],{},[3743,11310,11311],{},"У мобільних застосунках недостатньо реагувати на помилку вже після її виникнення. Інтерфейс повинен проактивно адаптуватися до відсутності зв'язку: блокувати відправку важких форм, показувати плашку режиму офлайн та перемикатися на локальний кеш.",[3774,11313,11315,11316,6478,11318],{"id":11314},"різниця-між-прапорцями-isconnected-проти-isinternetreachable","Різниця між прапорцями: ",[3799,11317,3910],{},[3799,11319,3914],{},[3743,11321,11322,11323,11325],{},"Модуль ",[3799,11324,3906],{}," надає детальний знімок стану мережі пристрою. Найважливішим аспектом є розрізнення двох логічних властивостей:",[6514,11327,11328,11351,11389,11415,11422,11425,11992,11994,12000,12003,12009,12020,12118,12122],{},[6517,11329,11331,11334,11335,7222,11338,11341,11342,11345,11346,11348,11349,3836],{"name":3910,"type":11330},"boolean | null",[3750,11332,11333],{},"Фізичний лінк."," Показує, чи підключений пристрій до будь-якого локального мережевого інтерфейсу (Wi-Fi роутер, мобільна вежа стільникового зв'язку, Bluetooth PAN чи USB Ethernet).\n",[3799,11336,11337],{},"isConnected === true",[3750,11339,11340],{},"не гарантує наявності Інтернету",". Якщо ви підключилися до публічного Wi-Fi у кафе, який вимагає авторизації (",[3758,11343,11344],{},"Captive Portal","), або домашній роутер втратив зв'язок із провайдером, ",[3799,11347,3910],{}," буде дорівнювати ",[3799,11350,3834],{},[6517,11352,11353,11371],{"name":3914,"type":11330},[3743,11354,11355,11358,11359,11362,11363,11366,11367,11370],{},[3750,11356,11357],{},"Фактичний вихід у глобальну мережу."," Показує, чи зміг нативний мережевий стек успішно виконати фоновий зондувальний запит (",[3758,11360,11361],{},"Probe Request",") до контрольного сервера перевірки доступності Інтернету (наприклад, ",[3799,11364,11365],{},"http:\u002F\u002Fclients3.google.com\u002Fgenerate_204"," на Android або ",[3799,11368,11369],{},"http:\u002F\u002Fcaptive.apple.com\u002Fhotspot-detect.html"," на iOS).",[3779,11372,11373,11378,11384],{},[3782,11374,11375,11377],{},[3799,11376,8107],{}," — стан доступності ще визначається (початковий стан при холодному старті).",[3782,11379,11380,11383],{},[3799,11381,11382],{},"false"," — фізичний канал є, але Інтернет недоступний (Captive Portal або аварія провайдера).",[3782,11385,11386,11388],{},[3799,11387,3834],{}," — Інтернет функціонує повноцінно.\n::",[4530,11390,11391,11394,11395,11398,11399,11401,11402,11404,11405,3836],{},[3750,11392,11393],{},"Типова критична помилка:"," Перевірка ",[3799,11396,11397],{},"if (!state.isConnected) return showOfflineBanner();",". Якщо користувач знаходиться в мережі без Інтернету, прапорець ",[3799,11400,3910],{}," дорівнює ",[3799,11403,3834],{},", банер не показується, а всі реальні запити застосунку аварійно падають за таймаутом. ",[3750,11406,11407,11408,11411,11412],{},"Завжди використовуйте перевірку ",[3799,11409,11410],{},"isInternetReachable === false"," або комбіновану умову ",[3799,11413,11414],{},"!isConnected || isInternetReachable === false",[3774,11416,11418,11419],{"id":11417},"проєктування-хука-usenetworkstatus","Проєктування хука ",[3799,11420,11421],{},"useNetworkStatus",[3743,11423,11424],{},"Створимо реактивний хук, що ізолює підписку на системні події та нормалізує стан:",[4014,11426,11428],{"className":4877,"code":11427,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fhooks\u002FuseNetworkStatus.ts\nimport { useEffect, useState } from 'react'\nimport NetInfo, { NetInfoState, NetInfoStateType } from '@react-native-community\u002Fnetinfo'\n\nexport interface NetworkState {\n    isConnected: boolean\n    isInternetReachable: boolean\n    isOffline: boolean\n    connectionType: NetInfoStateType\n    isExpensive: boolean\n}\n\nexport function useNetworkStatus(): NetworkState {\n    const [networkState, setNetworkState] = useState\u003CNetworkState>({\n        isConnected: true,\n        isInternetReachable: true,\n        isOffline: false,\n        connectionType: NetInfoStateType.unknown,\n        isExpensive: false,\n    })\n\n    useEffect(() => {\n        \u002F\u002F 1. Отримуємо початковий стан з'єднання\n        NetInfo.fetch().then((state: NetInfoState) => {\n            updateState(state)\n        })\n\n        \u002F\u002F 2. Підписуємося на динамічні зміни радіоінтерфейсів\n        const unsubscribe = NetInfo.addEventListener((state: NetInfoState) => {\n            updateState(state)\n        })\n\n        function updateState(state: NetInfoState) {\n            const isConnected = state.isConnected ?? false\n            \u002F\u002F Якщо стан доступності ще не визначено (null), оптимістично вважаємо його доступним\n            \u002F\u002F за умови наявності фізичного з'єднання\n            const isInternetReachable = state.isInternetReachable ?? isConnected\n            const isOffline = !isConnected || !isInternetReachable\n\n            setNetworkState({\n                isConnected,\n                isInternetReachable,\n                isOffline,\n                connectionType: state.type,\n                isExpensive: state.details ? ((state.details as any).isConnectionExpensive ?? false) : false,\n            })\n        }\n\n        return () => {\n            unsubscribe()\n        }\n    }, [])\n\n    return networkState\n}\n",[3799,11429,11430,11435,11454,11478,11482,11493,11503,11512,11521,11531,11540,11544,11548,11564,11589,11599,11608,11617,11631,11640,11644,11648,11659,11664,11693,11704,11708,11712,11717,11748,11758,11762,11766,11784,11803,11808,11813,11833,11849,11853,11860,11867,11874,11881,11896,11938,11942,11946,11950,11961,11968,11972,11977,11981,11988],{"__ignoreMap":4019},[4023,11431,11432],{"class":4025,"line":4026},[4023,11433,11434],{"class":4965},"\u002F\u002F src\u002Fhooks\u002FuseNetworkStatus.ts\n",[4023,11436,11437,11439,11441,11444,11446,11448,11450,11452],{"class":4025,"line":4032},[4023,11438,5731],{"class":4886},[4023,11440,4907],{"class":4564},[4023,11442,11443],{"class":4910},"useEffect",[4023,11445,3928],{"class":4564},[4023,11447,5740],{"class":4910},[4023,11449,5743],{"class":4564},[4023,11451,5746],{"class":4886},[4023,11453,5749],{"class":4580},[4023,11455,11456,11458,11461,11463,11466,11468,11471,11473,11475],{"class":4025,"line":4038},[4023,11457,5731],{"class":4886},[4023,11459,11460],{"class":4910}," NetInfo",[4023,11462,5737],{"class":4564},[4023,11464,11465],{"class":4910},"NetInfoState",[4023,11467,3928],{"class":4564},[4023,11469,11470],{"class":4910},"NetInfoStateType",[4023,11472,5743],{"class":4564},[4023,11474,5746],{"class":4886},[4023,11476,11477],{"class":4580}," '@react-native-community\u002Fnetinfo'\n",[4023,11479,11480],{"class":4025,"line":4044},[4023,11481,4060],{"emptyLinePlaceholder":4059},[4023,11483,11484,11486,11488,11491],{"class":4025,"line":4050},[4023,11485,4887],{"class":4886},[4023,11487,4891],{"class":4890},[4023,11489,11490],{"class":4894}," NetworkState",[4023,11492,5446],{"class":4564},[4023,11494,11495,11498,11500],{"class":4025,"line":4056},[4023,11496,11497],{"class":4910},"    isConnected",[4023,11499,4577],{"class":4564},[4023,11501,11502],{"class":4894},"boolean\n",[4023,11504,11505,11508,11510],{"class":4025,"line":4063},[4023,11506,11507],{"class":4910},"    isInternetReachable",[4023,11509,4577],{"class":4564},[4023,11511,11502],{"class":4894},[4023,11513,11514,11517,11519],{"class":4025,"line":4069},[4023,11515,11516],{"class":4910},"    isOffline",[4023,11518,4577],{"class":4564},[4023,11520,11502],{"class":4894},[4023,11522,11523,11526,11528],{"class":4025,"line":4075},[4023,11524,11525],{"class":4910},"    connectionType",[4023,11527,4577],{"class":4564},[4023,11529,11530],{"class":4894},"NetInfoStateType\n",[4023,11532,11533,11536,11538],{"class":4025,"line":4081},[4023,11534,11535],{"class":4910},"    isExpensive",[4023,11537,4577],{"class":4564},[4023,11539,11502],{"class":4894},[4023,11541,11542],{"class":4025,"line":4087},[4023,11543,4090],{"class":4564},[4023,11545,11546],{"class":4025,"line":4093},[4023,11547,4060],{"emptyLinePlaceholder":4059},[4023,11549,11550,11552,11554,11557,11559,11562],{"class":4025,"line":4098},[4023,11551,4887],{"class":4886},[4023,11553,4983],{"class":4890},[4023,11555,11556],{"class":4986}," useNetworkStatus",[4023,11558,7715],{"class":4564},[4023,11560,11561],{"class":4894},"NetworkState",[4023,11563,5446],{"class":4564},[4023,11565,11566,11568,11570,11573,11575,11578,11580,11582,11584,11586],{"class":4025,"line":4104},[4023,11567,5029],{"class":4890},[4023,11569,5957],{"class":4564},[4023,11571,11572],{"class":5032},"networkState",[4023,11574,3928],{"class":4564},[4023,11576,11577],{"class":5032},"setNetworkState",[4023,11579,5106],{"class":4564},[4023,11581,5740],{"class":4986},[4023,11583,4898],{"class":4564},[4023,11585,11561],{"class":4894},[4023,11587,11588],{"class":4564},">({\n",[4023,11590,11591,11594,11597],{"class":4025,"line":4110},[4023,11592,11593],{"class":4910},"        isConnected:",[4023,11595,11596],{"class":4890}," true",[4023,11598,5365],{"class":4564},[4023,11600,11601,11604,11606],{"class":4025,"line":4116},[4023,11602,11603],{"class":4910},"        isInternetReachable:",[4023,11605,11596],{"class":4890},[4023,11607,5365],{"class":4564},[4023,11609,11610,11613,11615],{"class":4025,"line":4121},[4023,11611,11612],{"class":4910},"        isOffline:",[4023,11614,9759],{"class":4890},[4023,11616,5365],{"class":4564},[4023,11618,11619,11622,11625,11627,11629],{"class":4025,"line":4126},[4023,11620,11621],{"class":4910},"        connectionType:",[4023,11623,11624],{"class":4910}," NetInfoStateType",[4023,11626,3836],{"class":4564},[4023,11628,7094],{"class":4910},[4023,11630,5365],{"class":4564},[4023,11632,11633,11636,11638],{"class":4025,"line":4132},[4023,11634,11635],{"class":4910},"        isExpensive:",[4023,11637,9759],{"class":4890},[4023,11639,5365],{"class":4564},[4023,11641,11642],{"class":4025,"line":4138},[4023,11643,8205],{"class":4564},[4023,11645,11646],{"class":4025,"line":4144},[4023,11647,4060],{"emptyLinePlaceholder":4059},[4023,11649,11650,11653,11655,11657],{"class":4025,"line":4150},[4023,11651,11652],{"class":4986},"    useEffect",[4023,11654,6750],{"class":4564},[4023,11656,5246],{"class":4890},[4023,11658,5446],{"class":4564},[4023,11660,11661],{"class":4025,"line":4156},[4023,11662,11663],{"class":4965},"        \u002F\u002F 1. Отримуємо початковий стан з'єднання\n",[4023,11665,11666,11669,11671,11673,11676,11679,11681,11683,11685,11687,11689,11691],{"class":4025,"line":4162},[4023,11667,11668],{"class":4910},"        NetInfo",[4023,11670,3836],{"class":4564},[4023,11672,3844],{"class":4986},[4023,11674,11675],{"class":4564},"().",[4023,11677,11678],{"class":4986},"then",[4023,11680,5238],{"class":4564},[4023,11682,5406],{"class":4910},[4023,11684,4577],{"class":4564},[4023,11686,11465],{"class":4894},[4023,11688,5243],{"class":4564},[4023,11690,5246],{"class":4890},[4023,11692,5446],{"class":4564},[4023,11694,11695,11698,11700,11702],{"class":4025,"line":4167},[4023,11696,11697],{"class":4986},"            updateState",[4023,11699,5122],{"class":4564},[4023,11701,5406],{"class":4910},[4023,11703,5131],{"class":4564},[4023,11705,11706],{"class":4025,"line":4173},[4023,11707,6148],{"class":4564},[4023,11709,11710],{"class":4025,"line":4179},[4023,11711,4060],{"emptyLinePlaceholder":4059},[4023,11713,11714],{"class":4025,"line":4184},[4023,11715,11716],{"class":4965},"        \u002F\u002F 2. Підписуємося на динамічні зміни радіоінтерфейсів\n",[4023,11718,11719,11721,11724,11726,11729,11731,11734,11736,11738,11740,11742,11744,11746],{"class":4025,"line":4189},[4023,11720,6792],{"class":4890},[4023,11722,11723],{"class":5032}," unsubscribe",[4023,11725,5403],{"class":4564},[4023,11727,11728],{"class":4910},"NetInfo",[4023,11730,3836],{"class":4564},[4023,11732,11733],{"class":4986},"addEventListener",[4023,11735,5238],{"class":4564},[4023,11737,5406],{"class":4910},[4023,11739,4577],{"class":4564},[4023,11741,11465],{"class":4894},[4023,11743,5243],{"class":4564},[4023,11745,5246],{"class":4890},[4023,11747,5446],{"class":4564},[4023,11749,11750,11752,11754,11756],{"class":4025,"line":4195},[4023,11751,11697],{"class":4986},[4023,11753,5122],{"class":4564},[4023,11755,5406],{"class":4910},[4023,11757,5131],{"class":4564},[4023,11759,11760],{"class":4025,"line":4201},[4023,11761,6148],{"class":4564},[4023,11763,11764],{"class":4025,"line":4207},[4023,11765,4060],{"emptyLinePlaceholder":4059},[4023,11767,11768,11771,11774,11776,11778,11780,11782],{"class":4025,"line":4213},[4023,11769,11770],{"class":4890},"        function",[4023,11772,11773],{"class":4986}," updateState",[4023,11775,5122],{"class":4564},[4023,11777,5406],{"class":4910},[4023,11779,4577],{"class":4564},[4023,11781,11465],{"class":4894},[4023,11783,5088],{"class":4564},[4023,11785,11786,11788,11791,11793,11795,11797,11799,11801],{"class":4025,"line":4219},[4023,11787,6075],{"class":4890},[4023,11789,11790],{"class":5032}," isConnected",[4023,11792,5403],{"class":4564},[4023,11794,5406],{"class":4910},[4023,11796,3836],{"class":4564},[4023,11798,3910],{"class":4910},[4023,11800,7976],{"class":4564},[4023,11802,8013],{"class":4890},[4023,11804,11805],{"class":4025,"line":4225},[4023,11806,11807],{"class":4965},"            \u002F\u002F Якщо стан доступності ще не визначено (null), оптимістично вважаємо його доступним\n",[4023,11809,11810],{"class":4025,"line":4230},[4023,11811,11812],{"class":4965},"            \u002F\u002F за умови наявності фізичного з'єднання\n",[4023,11814,11815,11817,11820,11822,11824,11826,11828,11830],{"class":4025,"line":4235},[4023,11816,6075],{"class":4890},[4023,11818,11819],{"class":5032}," isInternetReachable",[4023,11821,5403],{"class":4564},[4023,11823,5406],{"class":4910},[4023,11825,3836],{"class":4564},[4023,11827,3914],{"class":4910},[4023,11829,7976],{"class":4564},[4023,11831,11832],{"class":4910},"isConnected\n",[4023,11834,11835,11837,11840,11842,11844,11846],{"class":4025,"line":4241},[4023,11836,6075],{"class":4890},[4023,11838,11839],{"class":5032}," isOffline",[4023,11841,5605],{"class":4564},[4023,11843,3910],{"class":4910},[4023,11845,7014],{"class":4564},[4023,11847,11848],{"class":4910},"isInternetReachable\n",[4023,11850,11851],{"class":4025,"line":4247},[4023,11852,4060],{"emptyLinePlaceholder":4059},[4023,11854,11855,11858],{"class":4025,"line":4253},[4023,11856,11857],{"class":4986},"            setNetworkState",[4023,11859,7892],{"class":4564},[4023,11861,11862,11865],{"class":4025,"line":4259},[4023,11863,11864],{"class":4910},"                isConnected",[4023,11866,5365],{"class":4564},[4023,11868,11869,11872],{"class":4025,"line":4265},[4023,11870,11871],{"class":4910},"                isInternetReachable",[4023,11873,5365],{"class":4564},[4023,11875,11876,11879],{"class":4025,"line":4271},[4023,11877,11878],{"class":4910},"                isOffline",[4023,11880,5365],{"class":4564},[4023,11882,11883,11886,11889,11891,11894],{"class":4025,"line":4276},[4023,11884,11885],{"class":4910},"                connectionType:",[4023,11887,11888],{"class":4910}," state",[4023,11890,3836],{"class":4564},[4023,11892,11893],{"class":4910},"type",[4023,11895,5365],{"class":4564},[4023,11897,11898,11901,11903,11905,11908,11911,11913,11915,11917,11919,11922,11924,11927,11929,11931,11934,11936],{"class":4025,"line":5500},[4023,11899,11900],{"class":4910},"                isExpensive:",[4023,11902,11888],{"class":4910},[4023,11904,3836],{"class":4564},[4023,11906,11907],{"class":4910},"details",[4023,11909,11910],{"class":4564}," ? ((",[4023,11912,5406],{"class":4910},[4023,11914,3836],{"class":4564},[4023,11916,11907],{"class":4910},[4023,11918,8457],{"class":4886},[4023,11920,11921],{"class":4894}," any",[4023,11923,3761],{"class":4564},[4023,11925,11926],{"class":4910},"isConnectionExpensive",[4023,11928,7976],{"class":4564},[4023,11930,11382],{"class":4890},[4023,11932,11933],{"class":4564},") : ",[4023,11935,11382],{"class":4890},[4023,11937,5365],{"class":4564},[4023,11939,11940],{"class":4025,"line":5505},[4023,11941,8749],{"class":4564},[4023,11943,11944],{"class":4025,"line":5510},[4023,11945,6971],{"class":4564},[4023,11947,11948],{"class":4025,"line":5515},[4023,11949,4060],{"emptyLinePlaceholder":4059},[4023,11951,11952,11954,11957,11959],{"class":4025,"line":5520},[4023,11953,7057],{"class":4886},[4023,11955,11956],{"class":4564}," () ",[4023,11958,5246],{"class":4890},[4023,11960,5446],{"class":4564},[4023,11962,11963,11966],{"class":4025,"line":5526},[4023,11964,11965],{"class":4986},"            unsubscribe",[4023,11967,6711],{"class":4564},[4023,11969,11970],{"class":4025,"line":5531},[4023,11971,6971],{"class":4564},[4023,11973,11974],{"class":4025,"line":5557},[4023,11975,11976],{"class":4564},"    }, [])\n",[4023,11978,11979],{"class":4025,"line":5572},[4023,11980,4060],{"emptyLinePlaceholder":4059},[4023,11982,11983,11985],{"class":4025,"line":5584},[4023,11984,5144],{"class":4886},[4023,11986,11987],{"class":4910}," networkState\n",[4023,11989,11990],{"class":4025,"line":5597},[4023,11991,4090],{"class":4564},[3964,11993],{},[3738,11995,11997,11998],{"id":11996},"життєвий-цикл-мобільного-застосунку-стани-appstate","Життєвий цикл мобільного застосунку: стани ",[3799,11999,3923],{},[3743,12001,12002],{},"На відміну від традиційного веб-сайту, мобільний застосунок є керованим клієнтом у багатозадачному середовищі операційної системи.",[3774,12004,12006,12007],{"id":12005},"схема-станів-appstate","Схема станів ",[3799,12008,3923],{},[3743,12010,12011,12012,12015,12016,12019],{},"React Native транслює нативні стани життєвого циклу iOS (",[3799,12013,12014],{},"UIApplicationState",") та Android (",[3799,12017,12018],{},"Activity Lifecycle",") у три стандартизовані рядкові стани:",[4011,12021,12022],{},[4014,12023,12025],{"className":4016,"code":12024,"language":4018,"meta":4019,"style":4019},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam defaultFontName \"Helvetica\"\nskinparam defaultFontSize 12\n\nstate \"active\" as Active #bbf7d0 : Застосунок на передньому плані (Foreground).\\nІнтерфейс відображається, користувач взаємодіє з екраном.\nstate \"inactive\" as Inactive #fef08a : Перехідний стан (iOS).\\nІнтерфейс видимий, але не отримує подій вводу.\\n(Шторка сповіщень, App Switcher, FaceID діалог, дзвінок).\nstate \"background\" as Background #fed7aa : Застосунок згорнуто (Background).\\nJS-потік призупинено або суворо обмежено за квотою часу.\nstate \"Suspended \u002F Terminated\" as Terminated #fecaca : Процес вивантажено з оперативної пам'яті (OOM Killer).\\nJS-середовище повністю знищено.\n\n[*] --> Active : Холодний старт (Cold Start)\nActive --> Inactive : Опускання системної шторки \u002F Вхідний виклик\nInactive --> Active : Повернення фокусу\nInactive --> Background : Згортання на головний екран\nBackground --> Inactive : Відкриття з панелі багатозадачності\nBackground --> Terminated : Нестача RAM в системі (Silent Kill)\nTerminated --> [*]\n\n@enduml\n",[3799,12026,12027,12031,12035,12039,12043,12047,12051,12056,12061,12066,12071,12075,12080,12085,12090,12095,12100,12105,12110,12114],{"__ignoreMap":4019},[4023,12028,12029],{"class":4025,"line":4026},[4023,12030,4029],{},[4023,12032,12033],{"class":4025,"line":4032},[4023,12034,4035],{},[4023,12036,12037],{"class":4025,"line":4038},[4023,12038,4041],{},[4023,12040,12041],{"class":4025,"line":4044},[4023,12042,4047],{},[4023,12044,12045],{"class":4025,"line":4050},[4023,12046,4053],{},[4023,12048,12049],{"class":4025,"line":4056},[4023,12050,4060],{"emptyLinePlaceholder":4059},[4023,12052,12053],{"class":4025,"line":4063},[4023,12054,12055],{},"state \"active\" as Active #bbf7d0 : Застосунок на передньому плані (Foreground).\\nІнтерфейс відображається, користувач взаємодіє з екраном.\n",[4023,12057,12058],{"class":4025,"line":4069},[4023,12059,12060],{},"state \"inactive\" as Inactive #fef08a : Перехідний стан (iOS).\\nІнтерфейс видимий, але не отримує подій вводу.\\n(Шторка сповіщень, App Switcher, FaceID діалог, дзвінок).\n",[4023,12062,12063],{"class":4025,"line":4075},[4023,12064,12065],{},"state \"background\" as Background #fed7aa : Застосунок згорнуто (Background).\\nJS-потік призупинено або суворо обмежено за квотою часу.\n",[4023,12067,12068],{"class":4025,"line":4081},[4023,12069,12070],{},"state \"Suspended \u002F Terminated\" as Terminated #fecaca : Процес вивантажено з оперативної пам'яті (OOM Killer).\\nJS-середовище повністю знищено.\n",[4023,12072,12073],{"class":4025,"line":4087},[4023,12074,4060],{"emptyLinePlaceholder":4059},[4023,12076,12077],{"class":4025,"line":4093},[4023,12078,12079],{},"[*] --> Active : Холодний старт (Cold Start)\n",[4023,12081,12082],{"class":4025,"line":4098},[4023,12083,12084],{},"Active --> Inactive : Опускання системної шторки \u002F Вхідний виклик\n",[4023,12086,12087],{"class":4025,"line":4104},[4023,12088,12089],{},"Inactive --> Active : Повернення фокусу\n",[4023,12091,12092],{"class":4025,"line":4110},[4023,12093,12094],{},"Inactive --> Background : Згортання на головний екран\n",[4023,12096,12097],{"class":4025,"line":4116},[4023,12098,12099],{},"Background --> Inactive : Відкриття з панелі багатозадачності\n",[4023,12101,12102],{"class":4025,"line":4121},[4023,12103,12104],{},"Background --> Terminated : Нестача RAM в системі (Silent Kill)\n",[4023,12106,12107],{"class":4025,"line":4126},[4023,12108,12109],{},"Terminated --> [*]\n",[4023,12111,12112],{"class":4025,"line":4132},[4023,12113,4060],{"emptyLinePlaceholder":4059},[4023,12115,12116],{"class":4025,"line":4138},[4023,12117,4279],{},[3774,12119,12121],{"id":12120},"деталізація-станів","Деталізація станів",[6514,12123,12124],{},[6517,12125,12126],{"name":3927,"type":3923},"Застосунок виконується на передньому плані, має повний доступ до ресурсів графічного прискорювача та процесора, обробляє жести користувача та анімації з частотою 60\u002F120 FPS.",[6517,12128,12129,12135,12160],{"name":3931,"type":3923},[3743,12130,12131,12132],{},"Специфічний для iOS перехідний стан. Застосунок все ще рендериться на екрані, але операційна система перехопила фокус вводу.\n",[3750,12133,12134],{},"Типові тригери:",[3779,12136,12137,12147,12150,12153],{},[3782,12138,12139,12140,12143,12144,9339],{},"Відкриття Центру сповіщень (",[3758,12141,12142],{},"Notification Center",") або Пункту керування (",[3758,12145,12146],{},"Control Center",[3782,12148,12149],{},"Активація системного біометричного діалогу (Face ID \u002F Touch ID);",[3782,12151,12152],{},"Вхідний телефонний виклик або спрацювання системного будильника;",[3782,12154,12155,12156,12159],{},"Жест переходу в панель перемикання застосунків (",[3758,12157,12158],{},"App Switcher",").\n::",[6517,12161,12162,12163,12166,12167,12170,12171,3836],{"name":3934,"type":3923},"Застосунок повністю прихований від користувача. Операційна система надає JS-потоку від 5 до 30 секунд для завершення поточних операцій, після чого повністю заморожує виконання потоків (",[3758,12164,12165],{},"Suspend","). Якщо системі знадобиться пам'ять для іншої програми, процес буде безшумно знищено (",[3758,12168,12169],{},"Killed",") без виклику будь-яких React-хуків чи ",[3799,12172,12173],{},"componentWillUnmount",[3774,12175,12177,12178],{"id":12176},"що-потрібно-робити-при-зміні-appstate","Що потрібно робити при зміні ",[3799,12179,3923],{},[3743,12181,12182,12183,12185],{},"Коректна обробка ",[3799,12184,3923],{}," необхідна для:",[4288,12187,12188,12198,12207],{},[3782,12189,12190,12193,12194,12197],{},[3750,12191,12192],{},"Ресинхронізації застарілого кешу (Stale Cache Invalidation):"," Якщо користувач відкрив застосунок через 4 години після згортання, дані на екрані втратили актуальність. Перехід ",[3799,12195,12196],{},"background -> active"," повинен викликати тихе фонове оновлення.",[3782,12199,12200,12203,12204,12206],{},[3750,12201,12202],{},"Зупинки енергоємних процесів:"," При переході в ",[3799,12205,3934],{}," необхідно зупиняти таймери, скасовувати активні анімації, закривати постійні з'єднання WebSocket та зупиняти фоновий Polling.",[3782,12208,12209,12212,12213,12216,12217,12219,12220,3761],{},[3750,12210,12211],{},"Безпеки та приватності (Privacy Screen Overlay):"," Банківські та медичні застосунки зобов'язані накладати непрозорий екран-заглушку (",[3758,12214,12215],{},"Blur Mask",") у стані ",[3799,12218,3931],{},", щоб конфіденційні дані не зберігалися у знімках панелі багатозадачності ОС (",[3758,12221,12222],{},"App Switcher Snapshots",[3774,12224,12226],{"id":12225},"приклад-компонента-privacyshield","Приклад компонента PrivacyShield",[4014,12228,12230],{"className":5722,"code":12229,"language":5724,"meta":4019,"style":4019},"\u002F\u002F src\u002Fcomponents\u002FPrivacyShield.tsx\nimport React, { useEffect, useState } from 'react'\nimport { StyleSheet, View, Text, AppState, AppStateStatus } from 'react-native'\n\n\u002F**\n * Накладає захисну маску, коли застосунок згортається у фон або переходить в App Switcher\n *\u002F\nexport const PrivacyShield: React.FC\u003C{ children: React.ReactNode }> = ({ children }) => {\n    const [isShieldVisible, setIsShieldVisible] = useState(false)\n\n    useEffect(() => {\n        const subscription = AppState.addEventListener('change', (nextState: AppStateStatus) => {\n            \u002F\u002F На iOS при відкритті App Switcher стан стає 'inactive'\n            \u002F\u002F На Android при згортанні стан стає 'background'\n            const shouldHideData = nextState === 'inactive' || nextState === 'background'\n            setIsShieldVisible(shouldHideData)\n        })\n\n        return () => subscription.remove()\n    }, [])\n\n    return (\n        \u003CView style={styles.container}>\n            {children}\n            {isShieldVisible && (\n                \u003CView style={styles.overlay}>\n                    \u003CText style={styles.shieldText}>🔒 Дані захищено\u003C\u002FText>\n                \u003C\u002FView>\n            )}\n        \u003C\u002FView>\n    )\n}\n\nconst styles = StyleSheet.create({\n    container: { flex: 1 },\n    overlay: {\n        ...StyleSheet.absoluteFillObject,\n        backgroundColor: '#0f172a',\n        justifyContent: 'center',\n        alignItems: 'center',\n        zIndex: 99999,\n    },\n    shieldText: { color: '#ffffff', fontSize: 20, fontWeight: '700' },\n})\n",[3799,12231,12232,12237,12257,12289,12293,12297,12302,12306,12350,12374,12378,12388,12424,12429,12434,12459,12471,12475,12479,12496,12500,12504,12510,12534,12542,12553,12576,12608,12616,12623,12631,12635,12639,12643,12660,12675,12682,12695,12705,12715,12724,12734,12738,12767],{"__ignoreMap":4019},[4023,12233,12234],{"class":4025,"line":4026},[4023,12235,12236],{"class":4965},"\u002F\u002F src\u002Fcomponents\u002FPrivacyShield.tsx\n",[4023,12238,12239,12241,12243,12245,12247,12249,12251,12253,12255],{"class":4025,"line":4032},[4023,12240,5731],{"class":4886},[4023,12242,5734],{"class":4910},[4023,12244,5737],{"class":4564},[4023,12246,11443],{"class":4910},[4023,12248,3928],{"class":4564},[4023,12250,5740],{"class":4910},[4023,12252,5743],{"class":4564},[4023,12254,5746],{"class":4886},[4023,12256,5749],{"class":4580},[4023,12258,12259,12261,12263,12266,12268,12270,12272,12274,12276,12278,12280,12283,12285,12287],{"class":4025,"line":4038},[4023,12260,5731],{"class":4886},[4023,12262,4907],{"class":4564},[4023,12264,12265],{"class":4910},"StyleSheet",[4023,12267,3928],{"class":4564},[4023,12269,5758],{"class":4910},[4023,12271,3928],{"class":4564},[4023,12273,5763],{"class":4910},[4023,12275,3928],{"class":4564},[4023,12277,3923],{"class":4910},[4023,12279,3928],{"class":4564},[4023,12281,12282],{"class":4910},"AppStateStatus",[4023,12284,5743],{"class":4564},[4023,12286,5746],{"class":4886},[4023,12288,5775],{"class":4580},[4023,12290,12291],{"class":4025,"line":4044},[4023,12292,4060],{"emptyLinePlaceholder":4059},[4023,12294,12295],{"class":4025,"line":4050},[4023,12296,4966],{"class":4965},[4023,12298,12299],{"class":4025,"line":4056},[4023,12300,12301],{"class":4965}," * Накладає захисну маску, коли застосунок згортається у фон або переходить в App Switcher\n",[4023,12303,12304],{"class":4025,"line":4063},[4023,12305,4976],{"class":4965},[4023,12307,12308,12310,12312,12315,12317,12319,12321,12324,12326,12329,12331,12333,12335,12338,12341,12343,12346,12348],{"class":4025,"line":4069},[4023,12309,4887],{"class":4886},[4023,12311,7873],{"class":4890},[4023,12313,12314],{"class":4986}," PrivacyShield",[4023,12316,4577],{"class":4564},[4023,12318,2021],{"class":4894},[4023,12320,3836],{"class":4564},[4023,12322,12323],{"class":4894},"FC",[4023,12325,8831],{"class":4564},[4023,12327,12328],{"class":4910},"children",[4023,12330,4577],{"class":4564},[4023,12332,2021],{"class":4894},[4023,12334,3836],{"class":4564},[4023,12336,12337],{"class":4894},"ReactNode",[4023,12339,12340],{"class":4564}," }> = ({ ",[4023,12342,12328],{"class":4910},[4023,12344,12345],{"class":4564}," }) ",[4023,12347,5246],{"class":4890},[4023,12349,5446],{"class":4564},[4023,12351,12352,12354,12356,12359,12361,12364,12366,12368,12370,12372],{"class":4025,"line":4075},[4023,12353,5029],{"class":4890},[4023,12355,5957],{"class":4564},[4023,12357,12358],{"class":5032},"isShieldVisible",[4023,12360,3928],{"class":4564},[4023,12362,12363],{"class":5032},"setIsShieldVisible",[4023,12365,5106],{"class":4564},[4023,12367,5740],{"class":4986},[4023,12369,5122],{"class":4564},[4023,12371,11382],{"class":4890},[4023,12373,5131],{"class":4564},[4023,12375,12376],{"class":4025,"line":4081},[4023,12377,4060],{"emptyLinePlaceholder":4059},[4023,12379,12380,12382,12384,12386],{"class":4025,"line":4087},[4023,12381,11652],{"class":4986},[4023,12383,6750],{"class":4564},[4023,12385,5246],{"class":4890},[4023,12387,5446],{"class":4564},[4023,12389,12390,12392,12395,12397,12399,12401,12403,12405,12408,12411,12414,12416,12418,12420,12422],{"class":4025,"line":4093},[4023,12391,6792],{"class":4890},[4023,12393,12394],{"class":5032}," subscription",[4023,12396,5403],{"class":4564},[4023,12398,3923],{"class":4910},[4023,12400,3836],{"class":4564},[4023,12402,11733],{"class":4986},[4023,12404,5122],{"class":4564},[4023,12406,12407],{"class":4580},"'change'",[4023,12409,12410],{"class":4564},", (",[4023,12412,12413],{"class":4910},"nextState",[4023,12415,4577],{"class":4564},[4023,12417,12282],{"class":4894},[4023,12419,5243],{"class":4564},[4023,12421,5246],{"class":4890},[4023,12423,5446],{"class":4564},[4023,12425,12426],{"class":4025,"line":4098},[4023,12427,12428],{"class":4965},"            \u002F\u002F На iOS при відкритті App Switcher стан стає 'inactive'\n",[4023,12430,12431],{"class":4025,"line":4104},[4023,12432,12433],{"class":4965},"            \u002F\u002F На Android при згортанні стан стає 'background'\n",[4023,12435,12436,12438,12441,12443,12445,12447,12450,12452,12454,12456],{"class":4025,"line":4110},[4023,12437,6075],{"class":4890},[4023,12439,12440],{"class":5032}," shouldHideData",[4023,12442,5403],{"class":4564},[4023,12444,12413],{"class":4910},[4023,12446,7136],{"class":4564},[4023,12448,12449],{"class":4580},"'inactive'",[4023,12451,8516],{"class":4564},[4023,12453,12413],{"class":4910},[4023,12455,7136],{"class":4564},[4023,12457,12458],{"class":4580},"'background'\n",[4023,12460,12461,12464,12466,12469],{"class":4025,"line":4116},[4023,12462,12463],{"class":4986},"            setIsShieldVisible",[4023,12465,5122],{"class":4564},[4023,12467,12468],{"class":4910},"shouldHideData",[4023,12470,5131],{"class":4564},[4023,12472,12473],{"class":4025,"line":4121},[4023,12474,6148],{"class":4564},[4023,12476,12477],{"class":4025,"line":4126},[4023,12478,4060],{"emptyLinePlaceholder":4059},[4023,12480,12481,12483,12485,12487,12489,12491,12494],{"class":4025,"line":4132},[4023,12482,7057],{"class":4886},[4023,12484,11956],{"class":4564},[4023,12486,5246],{"class":4890},[4023,12488,12394],{"class":4910},[4023,12490,3836],{"class":4564},[4023,12492,12493],{"class":4986},"remove",[4023,12495,6711],{"class":4564},[4023,12497,12498],{"class":4025,"line":4138},[4023,12499,11976],{"class":4564},[4023,12501,12502],{"class":4025,"line":4144},[4023,12503,4060],{"emptyLinePlaceholder":4059},[4023,12505,12506,12508],{"class":4025,"line":4150},[4023,12507,5144],{"class":4886},[4023,12509,6189],{"class":4564},[4023,12511,12512,12514,12516,12518,12520,12522,12525,12527,12530,12532],{"class":4025,"line":4156},[4023,12513,6195],{"class":6194},[4023,12515,5758],{"class":4894},[4023,12517,6201],{"class":6200},[4023,12519,6204],{"class":4564},[4023,12521,6207],{"class":4890},[4023,12523,12524],{"class":4910},"styles",[4023,12526,3836],{"class":6210},[4023,12528,12529],{"class":4910},"container",[4023,12531,6222],{"class":4890},[4023,12533,4940],{"class":6194},[4023,12535,12536,12538,12540],{"class":4025,"line":4162},[4023,12537,6288],{"class":4890},[4023,12539,12328],{"class":4910},[4023,12541,4090],{"class":4890},[4023,12543,12544,12546,12548,12551],{"class":4025,"line":4167},[4023,12545,6288],{"class":4890},[4023,12547,12358],{"class":4910},[4023,12549,12550],{"class":4564}," &&",[4023,12552,6189],{"class":6210},[4023,12554,12555,12557,12559,12561,12563,12565,12567,12569,12572,12574],{"class":4025,"line":4173},[4023,12556,6311],{"class":6194},[4023,12558,5758],{"class":4894},[4023,12560,6201],{"class":6200},[4023,12562,6204],{"class":4564},[4023,12564,6207],{"class":4890},[4023,12566,12524],{"class":4910},[4023,12568,3836],{"class":6210},[4023,12570,12571],{"class":4910},"overlay",[4023,12573,6222],{"class":4890},[4023,12575,4940],{"class":6194},[4023,12577,12578,12580,12582,12584,12586,12588,12590,12592,12595,12597,12599,12602,12604,12606],{"class":4025,"line":4179},[4023,12579,6353],{"class":6194},[4023,12581,5763],{"class":4894},[4023,12583,6201],{"class":6200},[4023,12585,6204],{"class":4564},[4023,12587,6207],{"class":4890},[4023,12589,12524],{"class":4910},[4023,12591,3836],{"class":6210},[4023,12593,12594],{"class":4910},"shieldText",[4023,12596,6222],{"class":4890},[4023,12598,6260],{"class":6194},[4023,12600,12601],{"class":6210},"🔒 Дані захищено",[4023,12603,6279],{"class":6194},[4023,12605,5763],{"class":4894},[4023,12607,4940],{"class":6194},[4023,12609,12610,12612,12614],{"class":4025,"line":4184},[4023,12611,6440],{"class":6194},[4023,12613,5758],{"class":4894},[4023,12615,4940],{"class":6194},[4023,12617,12618,12621],{"class":4025,"line":4189},[4023,12619,12620],{"class":6210},"            )",[4023,12622,4090],{"class":4890},[4023,12624,12625,12627,12629],{"class":4025,"line":4195},[4023,12626,6456],{"class":6194},[4023,12628,5758],{"class":4894},[4023,12630,4940],{"class":6194},[4023,12632,12633],{"class":4025,"line":4201},[4023,12634,6465],{"class":4564},[4023,12636,12637],{"class":4025,"line":4207},[4023,12638,4090],{"class":4564},[4023,12640,12641],{"class":4025,"line":4213},[4023,12642,4060],{"emptyLinePlaceholder":4059},[4023,12644,12645,12647,12650,12652,12654,12656,12658],{"class":4025,"line":4219},[4023,12646,5076],{"class":4890},[4023,12648,12649],{"class":5032}," styles",[4023,12651,5403],{"class":4564},[4023,12653,12265],{"class":4910},[4023,12655,3836],{"class":4564},[4023,12657,7889],{"class":4986},[4023,12659,7892],{"class":4564},[4023,12661,12662,12665,12667,12670,12673],{"class":4025,"line":4225},[4023,12663,12664],{"class":4910},"    container:",[4023,12666,4907],{"class":4564},[4023,12668,12669],{"class":4910},"flex:",[4023,12671,12672],{"class":4601}," 1",[4023,12674,4605],{"class":4564},[4023,12676,12677,12680],{"class":4025,"line":4230},[4023,12678,12679],{"class":4910},"    overlay:",[4023,12681,5446],{"class":4564},[4023,12683,12684,12686,12688,12690,12693],{"class":4025,"line":4235},[4023,12685,5451],{"class":4564},[4023,12687,12265],{"class":4910},[4023,12689,3836],{"class":4564},[4023,12691,12692],{"class":4910},"absoluteFillObject",[4023,12694,5365],{"class":4564},[4023,12696,12697,12700,12703],{"class":4025,"line":4241},[4023,12698,12699],{"class":4910},"        backgroundColor:",[4023,12701,12702],{"class":4580}," '#0f172a'",[4023,12704,5365],{"class":4564},[4023,12706,12707,12710,12713],{"class":4025,"line":4247},[4023,12708,12709],{"class":4910},"        justifyContent:",[4023,12711,12712],{"class":4580}," 'center'",[4023,12714,5365],{"class":4564},[4023,12716,12717,12720,12722],{"class":4025,"line":4253},[4023,12718,12719],{"class":4910},"        alignItems:",[4023,12721,12712],{"class":4580},[4023,12723,5365],{"class":4564},[4023,12725,12726,12729,12732],{"class":4025,"line":4259},[4023,12727,12728],{"class":4910},"        zIndex:",[4023,12730,12731],{"class":4601}," 99999",[4023,12733,5365],{"class":4564},[4023,12735,12736],{"class":4025,"line":4265},[4023,12737,7986],{"class":4564},[4023,12739,12740,12743,12745,12748,12751,12753,12755,12758,12760,12762,12765],{"class":4025,"line":4271},[4023,12741,12742],{"class":4910},"    shieldText:",[4023,12744,4907],{"class":4564},[4023,12746,12747],{"class":4910},"color:",[4023,12749,12750],{"class":4580}," '#ffffff'",[4023,12752,3928],{"class":4564},[4023,12754,6242],{"class":4910},[4023,12756,12757],{"class":4601}," 20",[4023,12759,3928],{"class":4564},[4023,12761,6250],{"class":4910},[4023,12763,12764],{"class":4580}," '700'",[4023,12766,4605],{"class":4564},[4023,12768,12769],{"class":4025,"line":4276},[4023,12770,7991],{"class":4564},[3964,12772],{},[3738,12774,12776],{"id":12775},"поєднання-підсистем-патерн-smart-refetch-on-focus-network","Поєднання підсистем: Патерн Smart Refetch on Focus & Network",[3743,12778,12779],{},"Об'єднаємо відстеження життєвого циклу застосунку та моніторинг мережі в єдиний комплексний хук реактивної ресинхронізації даних:",[4014,12781,12783],{"className":4877,"code":12782,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fhooks\u002FuseRefetchOnFocusAndNetwork.ts\nimport { useEffect, useRef } from 'react'\nimport { AppState, AppStateStatus } from 'react-native'\nimport { useNetworkStatus } from '.\u002FuseNetworkStatus'\n\ninterface RefetchOptions {\n    \u002F**\n     * Мінімальний інтервал між повторними запитами в мілісекундах (захист від Throttling)\n     * @default 5000\n     *\u002F\n    throttleIntervalMs?: number\n}\n\n\u002F**\n * Автоматично викликає колбек оновлення даних при:\n * 1. Поверненні застосунку з фону в активний стан (background -> active);\n * 2. Відновленні доступу до Інтернету після офлайну.\n *\u002F\nexport function useRefetchOnFocusAndNetwork(onRefetch: () => void | Promise\u003Cvoid>, options: RefetchOptions = {}): void {\n    const { throttleIntervalMs = 5000 } = options\n    const { isOffline } = useNetworkStatus()\n\n    const prevAppStateRef = useRef\u003CAppStateStatus>(AppState.currentState)\n    const prevOfflineRef = useRef\u003Cboolean>(isOffline)\n    const lastRefetchTimestampRef = useRef\u003Cnumber>(Date.now())\n    const onRefetchRef = useRef(onRefetch)\n\n    \u002F\u002F Оновлюємо актуальне посилання на функцію без перезапуску ефекту\n    useEffect(() => {\n        onRefetchRef.current = onRefetch\n    }, [onRefetch])\n\n    useEffect(() => {\n        function executeThrottledRefetch(reason: string) {\n            const now = Date.now()\n            if (now - lastRefetchTimestampRef.current \u003C throttleIntervalMs) {\n                return \u002F\u002F Запобігаємо надто частим запитам\n            }\n\n            if (!isOffline) {\n                lastRefetchTimestampRef.current = now\n                onRefetchRef.current()\n            }\n        }\n\n        \u002F\u002F 1. Слухач життєвого циклу застосунку\n        const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => {\n            const isComingToForeground =\n                prevAppStateRef.current.match(\u002Finactive|background\u002F) && nextAppState === 'active'\n\n            if (isComingToForeground) {\n                executeThrottledRefetch('APP_ENTERED_FOREGROUND')\n            }\n\n            prevAppStateRef.current = nextAppState\n        })\n\n        \u002F\u002F 2. Реакція на відновлення мережі\n        const wasOffline = prevOfflineRef.current\n        const isNowOnline = !isOffline\n\n        if (wasOffline && isNowOnline) {\n            executeThrottledRefetch('NETWORK_RECONNECTED')\n        }\n\n        prevOfflineRef.current = isOffline\n\n        return () => {\n            subscription.remove()\n        }\n    }, [isOffline, throttleIntervalMs])\n}\n",[3799,12784,12785,12790,12809,12827,12842,12846,12855,12859,12864,12875,12879,12888,12892,12896,12900,12905,12910,12915,12919,12964,12982,12997,13001,13027,13048,13076,13093,13097,13102,13112,13126,13136,13140,13150,13168,13185,13209,13216,13220,13224,13234,13248,13259,13263,13267,13271,13276,13309,13319,13356,13360,13371,13383,13387,13391,13405,13409,13413,13418,13435,13447,13451,13467,13479,13483,13487,13500,13504,13514,13525,13529,13541],{"__ignoreMap":4019},[4023,12786,12787],{"class":4025,"line":4026},[4023,12788,12789],{"class":4965},"\u002F\u002F src\u002Fhooks\u002FuseRefetchOnFocusAndNetwork.ts\n",[4023,12791,12792,12794,12796,12798,12800,12803,12805,12807],{"class":4025,"line":4032},[4023,12793,5731],{"class":4886},[4023,12795,4907],{"class":4564},[4023,12797,11443],{"class":4910},[4023,12799,3928],{"class":4564},[4023,12801,12802],{"class":4910},"useRef",[4023,12804,5743],{"class":4564},[4023,12806,5746],{"class":4886},[4023,12808,5749],{"class":4580},[4023,12810,12811,12813,12815,12817,12819,12821,12823,12825],{"class":4025,"line":4038},[4023,12812,5731],{"class":4886},[4023,12814,4907],{"class":4564},[4023,12816,3923],{"class":4910},[4023,12818,3928],{"class":4564},[4023,12820,12282],{"class":4910},[4023,12822,5743],{"class":4564},[4023,12824,5746],{"class":4886},[4023,12826,5775],{"class":4580},[4023,12828,12829,12831,12833,12835,12837,12839],{"class":4025,"line":4044},[4023,12830,5731],{"class":4886},[4023,12832,4907],{"class":4564},[4023,12834,11421],{"class":4910},[4023,12836,5743],{"class":4564},[4023,12838,5746],{"class":4886},[4023,12840,12841],{"class":4580}," '.\u002FuseNetworkStatus'\n",[4023,12843,12844],{"class":4025,"line":4050},[4023,12845,4060],{"emptyLinePlaceholder":4059},[4023,12847,12848,12850,12853],{"class":4025,"line":4056},[4023,12849,5814],{"class":4890},[4023,12851,12852],{"class":4894}," RefetchOptions",[4023,12854,5446],{"class":4564},[4023,12856,12857],{"class":4025,"line":4063},[4023,12858,10500],{"class":4965},[4023,12860,12861],{"class":4025,"line":4069},[4023,12862,12863],{"class":4965},"     * Мінімальний інтервал між повторними запитами в мілісекундах (захист від Throttling)\n",[4023,12865,12866,12869,12872],{"class":4025,"line":4075},[4023,12867,12868],{"class":4965},"     * ",[4023,12870,12871],{"class":4890},"@default",[4023,12873,12874],{"class":4910}," 5000\n",[4023,12876,12877],{"class":4025,"line":4081},[4023,12878,10510],{"class":4965},[4023,12880,12881,12884,12886],{"class":4025,"line":4087},[4023,12882,12883],{"class":4910},"    throttleIntervalMs",[4023,12885,8469],{"class":4564},[4023,12887,5847],{"class":4894},[4023,12889,12890],{"class":4025,"line":4093},[4023,12891,4090],{"class":4564},[4023,12893,12894],{"class":4025,"line":4098},[4023,12895,4060],{"emptyLinePlaceholder":4059},[4023,12897,12898],{"class":4025,"line":4104},[4023,12899,4966],{"class":4965},[4023,12901,12902],{"class":4025,"line":4110},[4023,12903,12904],{"class":4965}," * Автоматично викликає колбек оновлення даних при:\n",[4023,12906,12907],{"class":4025,"line":4116},[4023,12908,12909],{"class":4965}," * 1. Поверненні застосунку з фону в активний стан (background -> active);\n",[4023,12911,12912],{"class":4025,"line":4121},[4023,12913,12914],{"class":4965}," * 2. Відновленні доступу до Інтернету після офлайну.\n",[4023,12916,12917],{"class":4025,"line":4126},[4023,12918,4976],{"class":4965},[4023,12920,12921,12923,12925,12928,12930,12933,12935,12937,12940,12942,12944,12946,12949,12951,12953,12955,12958,12960,12962],{"class":4025,"line":4132},[4023,12922,4887],{"class":4886},[4023,12924,4983],{"class":4890},[4023,12926,12927],{"class":4986}," useRefetchOnFocusAndNetwork",[4023,12929,5122],{"class":4564},[4023,12931,12932],{"class":4986},"onRefetch",[4023,12934,9603],{"class":4564},[4023,12936,5246],{"class":4890},[4023,12938,12939],{"class":4894}," void",[4023,12941,8104],{"class":4564},[4023,12943,6687],{"class":4894},[4023,12945,4898],{"class":4564},[4023,12947,12948],{"class":4894},"void",[4023,12950,9614],{"class":4564},[4023,12952,6818],{"class":4910},[4023,12954,4577],{"class":4564},[4023,12956,12957],{"class":4894},"RefetchOptions",[4023,12959,9624],{"class":4564},[4023,12961,12948],{"class":4894},[4023,12963,5446],{"class":4564},[4023,12965,12966,12968,12970,12973,12975,12978,12980],{"class":4025,"line":4138},[4023,12967,5029],{"class":4890},[4023,12969,4907],{"class":4564},[4023,12971,12972],{"class":5032},"throttleIntervalMs",[4023,12974,5403],{"class":4564},[4023,12976,12977],{"class":4601},"5000",[4023,12979,6723],{"class":4564},[4023,12981,9785],{"class":4910},[4023,12983,12984,12986,12988,12991,12993,12995],{"class":4025,"line":4144},[4023,12985,5029],{"class":4890},[4023,12987,4907],{"class":4564},[4023,12989,12990],{"class":5032},"isOffline",[4023,12992,6723],{"class":4564},[4023,12994,11421],{"class":4986},[4023,12996,6711],{"class":4564},[4023,12998,12999],{"class":4025,"line":4150},[4023,13000,4060],{"emptyLinePlaceholder":4059},[4023,13002,13003,13005,13008,13010,13012,13014,13016,13018,13020,13022,13025],{"class":4025,"line":4156},[4023,13004,5029],{"class":4890},[4023,13006,13007],{"class":5032}," prevAppStateRef",[4023,13009,5403],{"class":4564},[4023,13011,12802],{"class":4986},[4023,13013,4898],{"class":4564},[4023,13015,12282],{"class":4894},[4023,13017,9597],{"class":4564},[4023,13019,3923],{"class":4910},[4023,13021,3836],{"class":4564},[4023,13023,13024],{"class":4910},"currentState",[4023,13026,5131],{"class":4564},[4023,13028,13029,13031,13034,13036,13038,13040,13042,13044,13046],{"class":4025,"line":4162},[4023,13030,5029],{"class":4890},[4023,13032,13033],{"class":5032}," prevOfflineRef",[4023,13035,5403],{"class":4564},[4023,13037,12802],{"class":4986},[4023,13039,4898],{"class":4564},[4023,13041,8472],{"class":4894},[4023,13043,9597],{"class":4564},[4023,13045,12990],{"class":4910},[4023,13047,5131],{"class":4564},[4023,13049,13050,13052,13055,13057,13059,13061,13063,13065,13068,13070,13073],{"class":4025,"line":4167},[4023,13051,5029],{"class":4890},[4023,13053,13054],{"class":5032}," lastRefetchTimestampRef",[4023,13056,5403],{"class":4564},[4023,13058,12802],{"class":4986},[4023,13060,4898],{"class":4564},[4023,13062,6673],{"class":4894},[4023,13064,9597],{"class":4564},[4023,13066,13067],{"class":4910},"Date",[4023,13069,3836],{"class":4564},[4023,13071,13072],{"class":4986},"now",[4023,13074,13075],{"class":4564},"())\n",[4023,13077,13078,13080,13083,13085,13087,13089,13091],{"class":4025,"line":4173},[4023,13079,5029],{"class":4890},[4023,13081,13082],{"class":5032}," onRefetchRef",[4023,13084,5403],{"class":4564},[4023,13086,12802],{"class":4986},[4023,13088,5122],{"class":4564},[4023,13090,12932],{"class":4910},[4023,13092,5131],{"class":4564},[4023,13094,13095],{"class":4025,"line":4179},[4023,13096,4060],{"emptyLinePlaceholder":4059},[4023,13098,13099],{"class":4025,"line":4184},[4023,13100,13101],{"class":4965},"    \u002F\u002F Оновлюємо актуальне посилання на функцію без перезапуску ефекту\n",[4023,13103,13104,13106,13108,13110],{"class":4025,"line":4189},[4023,13105,11652],{"class":4986},[4023,13107,6750],{"class":4564},[4023,13109,5246],{"class":4890},[4023,13111,5446],{"class":4564},[4023,13113,13114,13117,13119,13121,13123],{"class":4025,"line":4195},[4023,13115,13116],{"class":4910},"        onRefetchRef",[4023,13118,3836],{"class":4564},[4023,13120,6102],{"class":4910},[4023,13122,5403],{"class":4564},[4023,13124,13125],{"class":4910},"onRefetch\n",[4023,13127,13128,13131,13133],{"class":4025,"line":4201},[4023,13129,13130],{"class":4564},"    }, [",[4023,13132,12932],{"class":4910},[4023,13134,13135],{"class":4564},"])\n",[4023,13137,13138],{"class":4025,"line":4207},[4023,13139,4060],{"emptyLinePlaceholder":4059},[4023,13141,13142,13144,13146,13148],{"class":4025,"line":4213},[4023,13143,11652],{"class":4986},[4023,13145,6750],{"class":4564},[4023,13147,5246],{"class":4890},[4023,13149,5446],{"class":4564},[4023,13151,13152,13154,13157,13159,13162,13164,13166],{"class":4025,"line":4219},[4023,13153,11770],{"class":4890},[4023,13155,13156],{"class":4986}," executeThrottledRefetch",[4023,13158,5122],{"class":4564},[4023,13160,13161],{"class":4910},"reason",[4023,13163,4577],{"class":4564},[4023,13165,4916],{"class":4894},[4023,13167,5088],{"class":4564},[4023,13169,13170,13172,13175,13177,13179,13181,13183],{"class":4025,"line":4225},[4023,13171,6075],{"class":4890},[4023,13173,13174],{"class":5032}," now",[4023,13176,5403],{"class":4564},[4023,13178,13067],{"class":4910},[4023,13180,3836],{"class":4564},[4023,13182,13072],{"class":4986},[4023,13184,6711],{"class":4564},[4023,13186,13187,13189,13191,13193,13196,13199,13201,13203,13205,13207],{"class":4025,"line":4230},[4023,13188,6097],{"class":4886},[4023,13190,3924],{"class":4564},[4023,13192,13072],{"class":4910},[4023,13194,13195],{"class":4564}," - ",[4023,13197,13198],{"class":4910},"lastRefetchTimestampRef",[4023,13200,3836],{"class":4564},[4023,13202,6102],{"class":4910},[4023,13204,9747],{"class":4564},[4023,13206,12972],{"class":4910},[4023,13208,5088],{"class":4564},[4023,13210,13211,13213],{"class":4025,"line":4235},[4023,13212,9756],{"class":4886},[4023,13214,13215],{"class":4965}," \u002F\u002F Запобігаємо надто частим запитам\n",[4023,13217,13218],{"class":4025,"line":4241},[4023,13219,9013],{"class":4564},[4023,13221,13222],{"class":4025,"line":4247},[4023,13223,4060],{"emptyLinePlaceholder":4059},[4023,13225,13226,13228,13230,13232],{"class":4025,"line":4253},[4023,13227,6097],{"class":4886},[4023,13229,5424],{"class":4564},[4023,13231,12990],{"class":4910},[4023,13233,5088],{"class":4564},[4023,13235,13236,13239,13241,13243,13245],{"class":4025,"line":4259},[4023,13237,13238],{"class":4910},"                lastRefetchTimestampRef",[4023,13240,3836],{"class":4564},[4023,13242,6102],{"class":4910},[4023,13244,5403],{"class":4564},[4023,13246,13247],{"class":4910},"now\n",[4023,13249,13250,13253,13255,13257],{"class":4025,"line":4265},[4023,13251,13252],{"class":4910},"                onRefetchRef",[4023,13254,3836],{"class":4564},[4023,13256,6102],{"class":4986},[4023,13258,6711],{"class":4564},[4023,13260,13261],{"class":4025,"line":4271},[4023,13262,9013],{"class":4564},[4023,13264,13265],{"class":4025,"line":4276},[4023,13266,6971],{"class":4564},[4023,13268,13269],{"class":4025,"line":5500},[4023,13270,4060],{"emptyLinePlaceholder":4059},[4023,13272,13273],{"class":4025,"line":5505},[4023,13274,13275],{"class":4965},"        \u002F\u002F 1. Слухач життєвого циклу застосунку\n",[4023,13277,13278,13280,13282,13284,13286,13288,13290,13292,13294,13296,13299,13301,13303,13305,13307],{"class":4025,"line":5510},[4023,13279,6792],{"class":4890},[4023,13281,12394],{"class":5032},[4023,13283,5403],{"class":4564},[4023,13285,3923],{"class":4910},[4023,13287,3836],{"class":4564},[4023,13289,11733],{"class":4986},[4023,13291,5122],{"class":4564},[4023,13293,12407],{"class":4580},[4023,13295,12410],{"class":4564},[4023,13297,13298],{"class":4910},"nextAppState",[4023,13300,4577],{"class":4564},[4023,13302,12282],{"class":4894},[4023,13304,5243],{"class":4564},[4023,13306,5246],{"class":4890},[4023,13308,5446],{"class":4564},[4023,13310,13311,13313,13316],{"class":4025,"line":5515},[4023,13312,6075],{"class":4890},[4023,13314,13315],{"class":5032}," isComingToForeground",[4023,13317,13318],{"class":4564}," =\n",[4023,13320,13321,13324,13326,13328,13330,13333,13335,13339,13343,13346,13349,13351,13353],{"class":4025,"line":5520},[4023,13322,13323],{"class":4910},"                prevAppStateRef",[4023,13325,3836],{"class":4564},[4023,13327,6102],{"class":4910},[4023,13329,3836],{"class":4564},[4023,13331,13332],{"class":4986},"match",[4023,13334,5122],{"class":4564},[4023,13336,13338],{"class":13337},"shsrj","\u002Finactive",[4023,13340,13342],{"class":13341},"siofk","|",[4023,13344,13345],{"class":13337},"background\u002F",[4023,13347,13348],{"class":4564},") && ",[4023,13350,13298],{"class":4910},[4023,13352,7136],{"class":4564},[4023,13354,13355],{"class":4580},"'active'\n",[4023,13357,13358],{"class":4025,"line":5526},[4023,13359,4060],{"emptyLinePlaceholder":4059},[4023,13361,13362,13364,13366,13369],{"class":4025,"line":5531},[4023,13363,6097],{"class":4886},[4023,13365,3924],{"class":4564},[4023,13367,13368],{"class":4910},"isComingToForeground",[4023,13370,5088],{"class":4564},[4023,13372,13373,13376,13378,13381],{"class":4025,"line":5557},[4023,13374,13375],{"class":4986},"                executeThrottledRefetch",[4023,13377,5122],{"class":4564},[4023,13379,13380],{"class":4580},"'APP_ENTERED_FOREGROUND'",[4023,13382,5131],{"class":4564},[4023,13384,13385],{"class":4025,"line":5572},[4023,13386,9013],{"class":4564},[4023,13388,13389],{"class":4025,"line":5584},[4023,13390,4060],{"emptyLinePlaceholder":4059},[4023,13392,13393,13396,13398,13400,13402],{"class":4025,"line":5597},[4023,13394,13395],{"class":4910},"            prevAppStateRef",[4023,13397,3836],{"class":4564},[4023,13399,6102],{"class":4910},[4023,13401,5403],{"class":4564},[4023,13403,13404],{"class":4910},"nextAppState\n",[4023,13406,13407],{"class":4025,"line":5625},[4023,13408,6148],{"class":4564},[4023,13410,13411],{"class":4025,"line":5630},[4023,13412,4060],{"emptyLinePlaceholder":4059},[4023,13414,13415],{"class":4025,"line":5637},[4023,13416,13417],{"class":4965},"        \u002F\u002F 2. Реакція на відновлення мережі\n",[4023,13419,13420,13422,13425,13427,13430,13432],{"class":4025,"line":5644},[4023,13421,6792],{"class":4890},[4023,13423,13424],{"class":5032}," wasOffline",[4023,13426,5403],{"class":4564},[4023,13428,13429],{"class":4910},"prevOfflineRef",[4023,13431,3836],{"class":4564},[4023,13433,13434],{"class":4910},"current\n",[4023,13436,13437,13439,13442,13444],{"class":4025,"line":5657},[4023,13438,6792],{"class":4890},[4023,13440,13441],{"class":5032}," isNowOnline",[4023,13443,5605],{"class":4564},[4023,13445,13446],{"class":4910},"isOffline\n",[4023,13448,13449],{"class":4025,"line":5668},[4023,13450,4060],{"emptyLinePlaceholder":4059},[4023,13452,13453,13455,13457,13460,13462,13465],{"class":4025,"line":5673},[4023,13454,6908],{"class":4886},[4023,13456,3924],{"class":4564},[4023,13458,13459],{"class":4910},"wasOffline",[4023,13461,7126],{"class":4564},[4023,13463,13464],{"class":4910},"isNowOnline",[4023,13466,5088],{"class":4564},[4023,13468,13469,13472,13474,13477],{"class":4025,"line":5709},[4023,13470,13471],{"class":4986},"            executeThrottledRefetch",[4023,13473,5122],{"class":4564},[4023,13475,13476],{"class":4580},"'NETWORK_RECONNECTED'",[4023,13478,5131],{"class":4564},[4023,13480,13481],{"class":4025,"line":5714},[4023,13482,6971],{"class":4564},[4023,13484,13485],{"class":4025,"line":8477},[4023,13486,4060],{"emptyLinePlaceholder":4059},[4023,13488,13489,13492,13494,13496,13498],{"class":4025,"line":8482},[4023,13490,13491],{"class":4910},"        prevOfflineRef",[4023,13493,3836],{"class":4564},[4023,13495,6102],{"class":4910},[4023,13497,5403],{"class":4564},[4023,13499,13446],{"class":4910},[4023,13501,13502],{"class":4025,"line":8488},[4023,13503,4060],{"emptyLinePlaceholder":4059},[4023,13505,13506,13508,13510,13512],{"class":4025,"line":8527},[4023,13507,7057],{"class":4886},[4023,13509,11956],{"class":4564},[4023,13511,5246],{"class":4890},[4023,13513,5446],{"class":4564},[4023,13515,13516,13519,13521,13523],{"class":4025,"line":8544},[4023,13517,13518],{"class":4910},"            subscription",[4023,13520,3836],{"class":4564},[4023,13522,12493],{"class":4986},[4023,13524,6711],{"class":4564},[4023,13526,13527],{"class":4025,"line":8549},[4023,13528,6971],{"class":4564},[4023,13530,13531,13533,13535,13537,13539],{"class":4025,"line":8554},[4023,13532,13130],{"class":4564},[4023,13534,12990],{"class":4910},[4023,13536,3928],{"class":4564},[4023,13538,12972],{"class":4910},[4023,13540,13135],{"class":4564},[4023,13542,13543],{"class":4025,"line":8560},[4023,13544,4090],{"class":4564},[3964,13546],{},[3738,13548,13550],{"id":13549},"міні-проєкт-монітор-курсів-валют-нбу-з-нормалізованим-сховищем","Міні-проєкт: «Монітор курсів валют НБУ» з нормалізованим сховищем",[3743,13552,13553],{},"Створимо завершений міні-застосунок для моніторингу офіційних курсів валют, що демонструє всі досліджені концепції:",[3779,13555,13556,13559,13570,13576,13582],{},[3782,13557,13558],{},"Отримання даних з реального публічного REST API Національного банку України;",[3782,13560,13561,13562,3825,13564,13566,13567,13569],{},"Нормалізація отриманих даних (",[3799,13563,3824],{},[3799,13565,3828],{},") для оптимізації ",[3832,13568],{":inline":3834,"tex":3835},";",[3782,13571,13572,13573,9339],{},"Керування станами завантаження, помилок та ручного оновлення (",[3758,13574,13575],{},"Pull-to-Refresh",[3782,13577,13578,13579,13569],{},"Індикація відсутності мережі через ",[3799,13580,13581],{},"OfflineBanner",[3782,13583,13584],{},"Автоматичне фонове оновлення даних при поверненні в додаток та відновленні зв'язку.",[3774,13586,13588],{"id":13587},"структура-файлової-системи-міні-проєкту","Структура файлової системи міні-проєкту",[13590,13591,13592,13602,13611,13621,13631,13641,13651,13661,13671,13681],"code-tree",{},[4014,13593,13596],{"className":4877,"code":13594,"filename":13595,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Інтерфейси DTO моделі даних Національного банку України\n","src\u002Fapi\u002Ftypes.ts",[3799,13597,13598],{"__ignoreMap":4019},[4023,13599,13600],{"class":4025,"line":4026},[4023,13601,13594],{"class":4965},[4014,13603,13605],{"className":4877,"code":13604,"filename":4874,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Утиліти нормалізації та денормалізації стану (byId + allIds)\n",[3799,13606,13607],{"__ignoreMap":4019},[4023,13608,13609],{"class":4025,"line":4026},[4023,13610,13604],{"class":4965},[4014,13612,13615],{"className":4877,"code":13613,"filename":13614,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Мережевий сервіс axios з таймаутом та обробкою помилок\n","src\u002Fapi\u002FcurrencyService.ts",[3799,13616,13617],{"__ignoreMap":4019},[4023,13618,13619],{"class":4025,"line":4026},[4023,13620,13613],{"class":4965},[4014,13622,13625],{"className":4877,"code":13623,"filename":13624,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Класифікатор помилок AppError та перелік ErrorKind\n","src\u002Fapi\u002FAppError.ts",[3799,13626,13627],{"__ignoreMap":4019},[4023,13628,13629],{"class":4025,"line":4026},[4023,13630,13623],{"class":4965},[4014,13632,13635],{"className":4877,"code":13633,"filename":13634,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Реактивний хук стану з'єднання NetInfo\n","src\u002Fhooks\u002FuseNetworkStatus.ts",[3799,13636,13637],{"__ignoreMap":4019},[4023,13638,13639],{"class":4025,"line":4026},[4023,13640,13633],{"class":4965},[4014,13642,13645],{"className":4877,"code":13643,"filename":13644,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Хук синхронізації життєвого циклу та мережі\n","src\u002Fhooks\u002FuseRefetchOnFocusAndNetwork.ts",[3799,13646,13647],{"__ignoreMap":4019},[4023,13648,13649],{"class":4025,"line":4026},[4023,13650,13643],{"class":4965},[4014,13652,13655],{"className":4877,"code":13653,"filename":13654,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Компонент верхньої панелі статусу офлайн\n","src\u002Fcomponents\u002FOfflineBanner.tsx",[3799,13656,13657],{"__ignoreMap":4019},[4023,13658,13659],{"class":4025,"line":4026},[4023,13660,13653],{"class":4965},[4014,13662,13665],{"className":4877,"code":13663,"filename":13664,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Компонент стану відмови з кнопкою повтору\n","src\u002Fcomponents\u002FErrorView.tsx",[3799,13666,13667],{"__ignoreMap":4019},[4023,13668,13669],{"class":4025,"line":4026},[4023,13670,13663],{"class":4965},[4014,13672,13675],{"className":4877,"code":13673,"filename":13674,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Головний екран відображення курсів з FlatList\n","src\u002Fscreens\u002FCurrencyRatesScreen.tsx",[3799,13676,13677],{"__ignoreMap":4019},[4023,13678,13679],{"class":4025,"line":4026},[4023,13680,13673],{"class":4965},[4014,13682,13685],{"className":4877,"code":13683,"filename":13684,"language":4879,"meta":4019,"style":4019},"\u002F\u002F Точка входу в застосунок\n","App.tsx",[3799,13686,13687],{"__ignoreMap":4019},[4023,13688,13689],{"class":4025,"line":4026},[4023,13690,13683],{"class":4965},[3774,13692,13694],{"id":13693},"покрокова-інженерна-реалізація","Покрокова інженерна реалізація",[13696,13697,13698,13702,13849,13853,14261,14265,14585,15185,15189,17173,17179],"steps",{},[3774,13699,13701],{"id":13700},"крок-1-опис-типів-даних-та-моделі-api","Крок 1. Опис типів даних та моделі API",[4014,13703,13705],{"className":4877,"code":13704,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fapi\u002Ftypes.ts\nexport interface NbuCurrencyDto {\n    r030: number \u002F\u002F Цифровий код валюти (наприклад, 840)\n    txt: string \u002F\u002F Назва валюти українською (наприклад, \"Долар США\")\n    rate: number \u002F\u002F Офіційний курс відносно гривні\n    cc: string \u002F\u002F Літерний код валюти за ISO 4217 (USD, EUR)\n    exchangedate: string \u002F\u002F Дата встановлення курсу\n}\n\nexport interface CurrencyItem {\n    id: string\n    code: string\n    name: string\n    rate: string\n    date: string\n}\n",[3799,13706,13707,13712,13723,13735,13747,13759,13771,13783,13787,13791,13802,13810,13819,13828,13836,13845],{"__ignoreMap":4019},[4023,13708,13709],{"class":4025,"line":4026},[4023,13710,13711],{"class":4965},"\u002F\u002F src\u002Fapi\u002Ftypes.ts\n",[4023,13713,13714,13716,13718,13721],{"class":4025,"line":4032},[4023,13715,4887],{"class":4886},[4023,13717,4891],{"class":4890},[4023,13719,13720],{"class":4894}," NbuCurrencyDto",[4023,13722,5446],{"class":4564},[4023,13724,13725,13728,13730,13732],{"class":4025,"line":4038},[4023,13726,13727],{"class":4910},"    r030",[4023,13729,4577],{"class":4564},[4023,13731,6673],{"class":4894},[4023,13733,13734],{"class":4965}," \u002F\u002F Цифровий код валюти (наприклад, 840)\n",[4023,13736,13737,13740,13742,13744],{"class":4025,"line":4044},[4023,13738,13739],{"class":4910},"    txt",[4023,13741,4577],{"class":4564},[4023,13743,4916],{"class":4894},[4023,13745,13746],{"class":4965}," \u002F\u002F Назва валюти українською (наприклад, \"Долар США\")\n",[4023,13748,13749,13752,13754,13756],{"class":4025,"line":4050},[4023,13750,13751],{"class":4910},"    rate",[4023,13753,4577],{"class":4564},[4023,13755,6673],{"class":4894},[4023,13757,13758],{"class":4965}," \u002F\u002F Офіційний курс відносно гривні\n",[4023,13760,13761,13764,13766,13768],{"class":4025,"line":4056},[4023,13762,13763],{"class":4910},"    cc",[4023,13765,4577],{"class":4564},[4023,13767,4916],{"class":4894},[4023,13769,13770],{"class":4965}," \u002F\u002F Літерний код валюти за ISO 4217 (USD, EUR)\n",[4023,13772,13773,13776,13778,13780],{"class":4025,"line":4063},[4023,13774,13775],{"class":4910},"    exchangedate",[4023,13777,4577],{"class":4564},[4023,13779,4916],{"class":4894},[4023,13781,13782],{"class":4965}," \u002F\u002F Дата встановлення курсу\n",[4023,13784,13785],{"class":4025,"line":4069},[4023,13786,4090],{"class":4564},[4023,13788,13789],{"class":4025,"line":4075},[4023,13790,4060],{"emptyLinePlaceholder":4059},[4023,13792,13793,13795,13797,13800],{"class":4025,"line":4081},[4023,13794,4887],{"class":4886},[4023,13796,4891],{"class":4890},[4023,13798,13799],{"class":4894}," CurrencyItem",[4023,13801,5446],{"class":4564},[4023,13803,13804,13806,13808],{"class":4025,"line":4087},[4023,13805,5358],{"class":4910},[4023,13807,4577],{"class":4564},[4023,13809,5828],{"class":4894},[4023,13811,13812,13815,13817],{"class":4025,"line":4093},[4023,13813,13814],{"class":4910},"    code",[4023,13816,4577],{"class":4564},[4023,13818,5828],{"class":4894},[4023,13820,13821,13824,13826],{"class":4025,"line":4098},[4023,13822,13823],{"class":4910},"    name",[4023,13825,4577],{"class":4564},[4023,13827,5828],{"class":4894},[4023,13829,13830,13832,13834],{"class":4025,"line":4104},[4023,13831,13751],{"class":4910},[4023,13833,4577],{"class":4564},[4023,13835,5828],{"class":4894},[4023,13837,13838,13841,13843],{"class":4025,"line":4110},[4023,13839,13840],{"class":4910},"    date",[4023,13842,4577],{"class":4564},[4023,13844,5828],{"class":4894},[4023,13846,13847],{"class":4025,"line":4116},[4023,13848,4090],{"class":4564},[3774,13850,13852],{"id":13851},"крок-2-побудова-мережевого-сервісу-валют","Крок 2. Побудова мережевого сервісу валют",[4014,13854,13856],{"className":4877,"code":13855,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fapi\u002FcurrencyService.ts\nimport axios from 'axios'\nimport { NbuCurrencyDto, CurrencyItem } from '.\u002Ftypes'\nimport { AppError } from '.\u002FAppError'\n\nconst nbuClient = axios.create({\n    baseURL: 'https:\u002F\u002Fbank.gov.ua\u002FNBUStatService\u002Fv1\u002Fstatdirectory',\n    timeout: 8000,\n    headers: {\n        Accept: 'application\u002Fjson',\n    },\n})\n\nconst TARGET_CURRENCIES = ['USD', 'EUR', 'GBP', 'PLN', 'CHF', 'CAD']\n\nexport async function fetchNbuRates(): Promise\u003CCurrencyItem[]> {\n    try {\n        const response = await nbuClient.get\u003CNbuCurrencyDto[]>('\u002Fexchange?json')\n\n        return response.data\n            .filter((item) => TARGET_CURRENCIES.includes(item.cc))\n            .map((item) => ({\n                id: String(item.r030),\n                code: item.cc,\n                name: item.txt,\n                rate: item.rate.toFixed(2),\n                date: item.exchangedate,\n            }))\n    } catch (error) {\n        throw AppError.from(error)\n    }\n}\n",[3799,13857,13858,13863,13873,13894,13909,13913,13930,13939,13947,13953,13961,13965,13969,13973,14013,14017,14039,14045,14073,14077,14088,14120,14137,14156,14169,14183,14206,14220,14225,14237,14253,14257],{"__ignoreMap":4019},[4023,13859,13860],{"class":4025,"line":4026},[4023,13861,13862],{"class":4965},"\u002F\u002F src\u002Fapi\u002FcurrencyService.ts\n",[4023,13864,13865,13867,13869,13871],{"class":4025,"line":4032},[4023,13866,5731],{"class":4886},[4023,13868,7651],{"class":4910},[4023,13870,7697],{"class":4886},[4023,13872,7672],{"class":4580},[4023,13874,13875,13877,13879,13882,13884,13887,13889,13891],{"class":4025,"line":4038},[4023,13876,5731],{"class":4886},[4023,13878,4907],{"class":4564},[4023,13880,13881],{"class":4910},"NbuCurrencyDto",[4023,13883,3928],{"class":4564},[4023,13885,13886],{"class":4910},"CurrencyItem",[4023,13888,5743],{"class":4564},[4023,13890,5746],{"class":4886},[4023,13892,13893],{"class":4580}," '.\u002Ftypes'\n",[4023,13895,13896,13898,13900,13902,13904,13906],{"class":4025,"line":4044},[4023,13897,5731],{"class":4886},[4023,13899,4907],{"class":4564},[4023,13901,3897],{"class":4910},[4023,13903,5743],{"class":4564},[4023,13905,5746],{"class":4886},[4023,13907,13908],{"class":4580}," '.\u002FAppError'\n",[4023,13910,13911],{"class":4025,"line":4050},[4023,13912,4060],{"emptyLinePlaceholder":4059},[4023,13914,13915,13917,13920,13922,13924,13926,13928],{"class":4025,"line":4056},[4023,13916,5076],{"class":4890},[4023,13918,13919],{"class":5032}," nbuClient",[4023,13921,5403],{"class":4564},[4023,13923,3850],{"class":4910},[4023,13925,3836],{"class":4564},[4023,13927,7889],{"class":4986},[4023,13929,7892],{"class":4564},[4023,13931,13932,13934,13937],{"class":4025,"line":4063},[4023,13933,7897],{"class":4910},[4023,13935,13936],{"class":4580}," 'https:\u002F\u002Fbank.gov.ua\u002FNBUStatService\u002Fv1\u002Fstatdirectory'",[4023,13938,5365],{"class":4564},[4023,13940,13941,13943,13945],{"class":4025,"line":4069},[4023,13942,7907],{"class":4910},[4023,13944,7910],{"class":4601},[4023,13946,5365],{"class":4564},[4023,13948,13949,13951],{"class":4025,"line":4075},[4023,13950,7917],{"class":4910},[4023,13952,5446],{"class":4564},[4023,13954,13955,13957,13959],{"class":4025,"line":4081},[4023,13956,7924],{"class":4910},[4023,13958,6842],{"class":4580},[4023,13960,5365],{"class":4564},[4023,13962,13963],{"class":4025,"line":4087},[4023,13964,7986],{"class":4564},[4023,13966,13967],{"class":4025,"line":4093},[4023,13968,7991],{"class":4564},[4023,13970,13971],{"class":4025,"line":4098},[4023,13972,4060],{"emptyLinePlaceholder":4059},[4023,13974,13975,13977,13980,13983,13986,13988,13991,13993,13996,13998,14001,14003,14006,14008,14011],{"class":4025,"line":4104},[4023,13976,5076],{"class":4890},[4023,13978,13979],{"class":5032}," TARGET_CURRENCIES",[4023,13981,13982],{"class":4564}," = [",[4023,13984,13985],{"class":4580},"'USD'",[4023,13987,3928],{"class":4564},[4023,13989,13990],{"class":4580},"'EUR'",[4023,13992,3928],{"class":4564},[4023,13994,13995],{"class":4580},"'GBP'",[4023,13997,3928],{"class":4564},[4023,13999,14000],{"class":4580},"'PLN'",[4023,14002,3928],{"class":4564},[4023,14004,14005],{"class":4580},"'CHF'",[4023,14007,3928],{"class":4564},[4023,14009,14010],{"class":4580},"'CAD'",[4023,14012,4642],{"class":4564},[4023,14014,14015],{"class":4025,"line":4110},[4023,14016,4060],{"emptyLinePlaceholder":4059},[4023,14018,14019,14021,14023,14025,14028,14030,14032,14034,14036],{"class":4025,"line":4116},[4023,14020,4887],{"class":4886},[4023,14022,9585],{"class":4890},[4023,14024,4983],{"class":4890},[4023,14026,14027],{"class":4986}," fetchNbuRates",[4023,14029,7715],{"class":4564},[4023,14031,6687],{"class":4894},[4023,14033,4898],{"class":4564},[4023,14035,13886],{"class":4894},[4023,14037,14038],{"class":4564},"[]> {\n",[4023,14040,14041,14043],{"class":4025,"line":4121},[4023,14042,6785],{"class":4886},[4023,14044,5446],{"class":4564},[4023,14046,14047,14049,14051,14053,14055,14057,14059,14061,14063,14065,14068,14071],{"class":4025,"line":4126},[4023,14048,6792],{"class":4890},[4023,14050,6795],{"class":5032},[4023,14052,5403],{"class":4564},[4023,14054,6800],{"class":4886},[4023,14056,13919],{"class":4910},[4023,14058,3836],{"class":4564},[4023,14060,6995],{"class":4986},[4023,14062,4898],{"class":4564},[4023,14064,13881],{"class":4894},[4023,14066,14067],{"class":4564},"[]>(",[4023,14069,14070],{"class":4580},"'\u002Fexchange?json'",[4023,14072,5131],{"class":4564},[4023,14074,14075],{"class":4025,"line":4132},[4023,14076,4060],{"emptyLinePlaceholder":4059},[4023,14078,14079,14081,14083,14085],{"class":4025,"line":4138},[4023,14080,7057],{"class":4886},[4023,14082,6795],{"class":4910},[4023,14084,3836],{"class":4564},[4023,14086,14087],{"class":4910},"data\n",[4023,14089,14090,14093,14095,14097,14099,14101,14103,14105,14107,14109,14111,14113,14115,14118],{"class":4025,"line":4144},[4023,14091,14092],{"class":4564},"            .",[4023,14094,5262],{"class":4986},[4023,14096,5238],{"class":4564},[4023,14098,5099],{"class":4910},[4023,14100,5243],{"class":4564},[4023,14102,5246],{"class":4890},[4023,14104,13979],{"class":5032},[4023,14106,3836],{"class":4564},[4023,14108,7021],{"class":4986},[4023,14110,5122],{"class":4564},[4023,14112,5099],{"class":4910},[4023,14114,3836],{"class":4564},[4023,14116,14117],{"class":4910},"cc",[4023,14119,5290],{"class":4564},[4023,14121,14122,14124,14126,14128,14130,14132,14134],{"class":4025,"line":4150},[4023,14123,14092],{"class":4564},[4023,14125,5235],{"class":4986},[4023,14127,5238],{"class":4564},[4023,14129,5099],{"class":4910},[4023,14131,5243],{"class":4564},[4023,14133,5246],{"class":4890},[4023,14135,14136],{"class":4564}," ({\n",[4023,14138,14139,14142,14145,14147,14149,14151,14154],{"class":4025,"line":4156},[4023,14140,14141],{"class":4910},"                id:",[4023,14143,14144],{"class":4986}," String",[4023,14146,5122],{"class":4564},[4023,14148,5099],{"class":4910},[4023,14150,3836],{"class":4564},[4023,14152,14153],{"class":4910},"r030",[4023,14155,8376],{"class":4564},[4023,14157,14158,14161,14163,14165,14167],{"class":4025,"line":4162},[4023,14159,14160],{"class":4910},"                code:",[4023,14162,5079],{"class":4910},[4023,14164,3836],{"class":4564},[4023,14166,14117],{"class":4910},[4023,14168,5365],{"class":4564},[4023,14170,14171,14174,14176,14178,14181],{"class":4025,"line":4167},[4023,14172,14173],{"class":4910},"                name:",[4023,14175,5079],{"class":4910},[4023,14177,3836],{"class":4564},[4023,14179,14180],{"class":4910},"txt",[4023,14182,5365],{"class":4564},[4023,14184,14185,14188,14190,14192,14195,14197,14200,14202,14204],{"class":4025,"line":4173},[4023,14186,14187],{"class":4910},"                rate:",[4023,14189,5079],{"class":4910},[4023,14191,3836],{"class":4564},[4023,14193,14194],{"class":4910},"rate",[4023,14196,3836],{"class":4564},[4023,14198,14199],{"class":4986},"toFixed",[4023,14201,5122],{"class":4564},[4023,14203,9950],{"class":4601},[4023,14205,8376],{"class":4564},[4023,14207,14208,14211,14213,14215,14218],{"class":4025,"line":4179},[4023,14209,14210],{"class":4910},"                date:",[4023,14212,5079],{"class":4910},[4023,14214,3836],{"class":4564},[4023,14216,14217],{"class":4910},"exchangedate",[4023,14219,5365],{"class":4564},[4023,14221,14222],{"class":4025,"line":4184},[4023,14223,14224],{"class":4564},"            }))\n",[4023,14226,14227,14229,14231,14233,14235],{"class":4025,"line":4189},[4023,14228,7081],{"class":4564},[4023,14230,7084],{"class":4886},[4023,14232,3924],{"class":4564},[4023,14234,7089],{"class":4910},[4023,14236,5088],{"class":4564},[4023,14238,14239,14241,14243,14245,14247,14249,14251],{"class":4025,"line":4195},[4023,14240,7178],{"class":4886},[4023,14242,10237],{"class":4910},[4023,14244,3836],{"class":4564},[4023,14246,5746],{"class":4986},[4023,14248,5122],{"class":4564},[4023,14250,7089],{"class":4910},[4023,14252,5131],{"class":4564},[4023,14254,14255],{"class":4025,"line":4201},[4023,14256,4147],{"class":4564},[4023,14258,14259],{"class":4025,"line":4207},[4023,14260,4090],{"class":4564},[3774,14262,14264],{"id":14263},"крок-3-реалізація-візуальних-компонентів-стану-offlinebanner-та-errorview","Крок 3. Реалізація візуальних компонентів стану (OfflineBanner та ErrorView)",[4014,14266,14268],{"className":4877,"code":14267,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fcomponents\u002FOfflineBanner.tsx\nimport React from 'react'\nimport { View, Text, StyleSheet } from 'react-native'\nimport { useNetworkStatus } from '..\u002Fhooks\u002FuseNetworkStatus'\n\nexport const OfflineBanner: React.FC = () => {\n    const { isOffline } = useNetworkStatus()\n\n    if (!isOffline) return null\n\n    return (\n        \u003CView style={styles.container}>\n            \u003CText style={styles.text}>⚠️ Відсутнє підключення до Інтернету\u003C\u002FText>\n        \u003C\u002FView>\n    )\n}\n\nconst styles = StyleSheet.create({\n    container: {\n        backgroundColor: '#b91c1c',\n        paddingVertical: 6,\n        paddingHorizontal: 16,\n        alignItems: 'center',\n        justifyContent: 'center',\n    },\n    text: {\n        color: '#ffffff',\n        fontSize: 12,\n        fontWeight: '700',\n        letterSpacing: 0.2,\n    },\n})\n",[3799,14269,14270,14275,14285,14307,14322,14326,14350,14364,14368,14383,14387,14393,14409,14442,14450,14454,14458,14462,14478,14484,14493,14503,14512,14520,14528,14532,14539,14548,14558,14567,14577,14581],{"__ignoreMap":4019},[4023,14271,14272],{"class":4025,"line":4026},[4023,14273,14274],{"class":4965},"\u002F\u002F src\u002Fcomponents\u002FOfflineBanner.tsx\n",[4023,14276,14277,14279,14281,14283],{"class":4025,"line":4032},[4023,14278,5731],{"class":4886},[4023,14280,5734],{"class":4910},[4023,14282,7697],{"class":4886},[4023,14284,5749],{"class":4580},[4023,14286,14287,14289,14291,14293,14295,14297,14299,14301,14303,14305],{"class":4025,"line":4038},[4023,14288,5731],{"class":4886},[4023,14290,4907],{"class":4564},[4023,14292,5758],{"class":4910},[4023,14294,3928],{"class":4564},[4023,14296,5763],{"class":4910},[4023,14298,3928],{"class":4564},[4023,14300,12265],{"class":4910},[4023,14302,5743],{"class":4564},[4023,14304,5746],{"class":4886},[4023,14306,5775],{"class":4580},[4023,14308,14309,14311,14313,14315,14317,14319],{"class":4025,"line":4044},[4023,14310,5731],{"class":4886},[4023,14312,4907],{"class":4564},[4023,14314,11421],{"class":4910},[4023,14316,5743],{"class":4564},[4023,14318,5746],{"class":4886},[4023,14320,14321],{"class":4580}," '..\u002Fhooks\u002FuseNetworkStatus'\n",[4023,14323,14324],{"class":4025,"line":4050},[4023,14325,4060],{"emptyLinePlaceholder":4059},[4023,14327,14328,14330,14332,14335,14337,14339,14341,14343,14346,14348],{"class":4025,"line":4056},[4023,14329,4887],{"class":4886},[4023,14331,7873],{"class":4890},[4023,14333,14334],{"class":4986}," OfflineBanner",[4023,14336,4577],{"class":4564},[4023,14338,2021],{"class":4894},[4023,14340,3836],{"class":4564},[4023,14342,12323],{"class":4894},[4023,14344,14345],{"class":4564}," = () ",[4023,14347,5246],{"class":4890},[4023,14349,5446],{"class":4564},[4023,14351,14352,14354,14356,14358,14360,14362],{"class":4025,"line":4063},[4023,14353,5029],{"class":4890},[4023,14355,4907],{"class":4564},[4023,14357,12990],{"class":5032},[4023,14359,6723],{"class":4564},[4023,14361,11421],{"class":4986},[4023,14363,6711],{"class":4564},[4023,14365,14366],{"class":4025,"line":4069},[4023,14367,4060],{"emptyLinePlaceholder":4059},[4023,14369,14370,14372,14374,14376,14378,14380],{"class":4025,"line":4075},[4023,14371,5421],{"class":4886},[4023,14373,5424],{"class":4564},[4023,14375,12990],{"class":4910},[4023,14377,5243],{"class":4564},[4023,14379,5432],{"class":4886},[4023,14381,14382],{"class":4890}," null\n",[4023,14384,14385],{"class":4025,"line":4081},[4023,14386,4060],{"emptyLinePlaceholder":4059},[4023,14388,14389,14391],{"class":4025,"line":4087},[4023,14390,5144],{"class":4886},[4023,14392,6189],{"class":4564},[4023,14394,14395,14397,14399,14401,14404,14406],{"class":4025,"line":4093},[4023,14396,6195],{"class":4564},[4023,14398,5758],{"class":4910},[4023,14400,6201],{"class":4910},[4023,14402,14403],{"class":4564},"={styles.",[4023,14405,12529],{"class":4910},[4023,14407,14408],{"class":4564},"}>\n",[4023,14410,14411,14413,14415,14417,14419,14421,14424,14427,14430,14433,14436,14438,14440],{"class":4025,"line":4098},[4023,14412,6229],{"class":4564},[4023,14414,5763],{"class":4910},[4023,14416,6201],{"class":4910},[4023,14418,14403],{"class":4564},[4023,14420,4729],{"class":4910},[4023,14422,14423],{"class":4564},"}>⚠️ ",[4023,14425,14426],{"class":4910},"Відсутнє",[4023,14428,14429],{"class":4910}," підключення",[4023,14431,14432],{"class":4910}," до",[4023,14434,14435],{"class":4910}," Інтернету",[4023,14437,6279],{"class":4564},[4023,14439,5763],{"class":4910},[4023,14441,4940],{"class":4564},[4023,14443,14444,14446,14448],{"class":4025,"line":4104},[4023,14445,6456],{"class":4564},[4023,14447,5758],{"class":4910},[4023,14449,4940],{"class":4564},[4023,14451,14452],{"class":4025,"line":4110},[4023,14453,6465],{"class":4564},[4023,14455,14456],{"class":4025,"line":4116},[4023,14457,4090],{"class":4564},[4023,14459,14460],{"class":4025,"line":4121},[4023,14461,4060],{"emptyLinePlaceholder":4059},[4023,14463,14464,14466,14468,14470,14472,14474,14476],{"class":4025,"line":4126},[4023,14465,5076],{"class":4890},[4023,14467,12649],{"class":5032},[4023,14469,5403],{"class":4564},[4023,14471,12265],{"class":4910},[4023,14473,3836],{"class":4564},[4023,14475,7889],{"class":4986},[4023,14477,7892],{"class":4564},[4023,14479,14480,14482],{"class":4025,"line":4132},[4023,14481,12664],{"class":4910},[4023,14483,5446],{"class":4564},[4023,14485,14486,14488,14491],{"class":4025,"line":4138},[4023,14487,12699],{"class":4910},[4023,14489,14490],{"class":4580}," '#b91c1c'",[4023,14492,5365],{"class":4564},[4023,14494,14495,14498,14501],{"class":4025,"line":4144},[4023,14496,14497],{"class":4910},"        paddingVertical:",[4023,14499,14500],{"class":4601}," 6",[4023,14502,5365],{"class":4564},[4023,14504,14505,14508,14510],{"class":4025,"line":4150},[4023,14506,14507],{"class":4910},"        paddingHorizontal:",[4023,14509,6217],{"class":4601},[4023,14511,5365],{"class":4564},[4023,14513,14514,14516,14518],{"class":4025,"line":4156},[4023,14515,12719],{"class":4910},[4023,14517,12712],{"class":4580},[4023,14519,5365],{"class":4564},[4023,14521,14522,14524,14526],{"class":4025,"line":4162},[4023,14523,12709],{"class":4910},[4023,14525,12712],{"class":4580},[4023,14527,5365],{"class":4564},[4023,14529,14530],{"class":4025,"line":4167},[4023,14531,7986],{"class":4564},[4023,14533,14534,14537],{"class":4025,"line":4173},[4023,14535,14536],{"class":4910},"    text:",[4023,14538,5446],{"class":4564},[4023,14540,14541,14544,14546],{"class":4025,"line":4179},[4023,14542,14543],{"class":4910},"        color:",[4023,14545,12750],{"class":4580},[4023,14547,5365],{"class":4564},[4023,14549,14550,14553,14556],{"class":4025,"line":4184},[4023,14551,14552],{"class":4910},"        fontSize:",[4023,14554,14555],{"class":4601}," 12",[4023,14557,5365],{"class":4564},[4023,14559,14560,14563,14565],{"class":4025,"line":4189},[4023,14561,14562],{"class":4910},"        fontWeight:",[4023,14564,12764],{"class":4580},[4023,14566,5365],{"class":4564},[4023,14568,14569,14572,14575],{"class":4025,"line":4195},[4023,14570,14571],{"class":4910},"        letterSpacing:",[4023,14573,14574],{"class":4601}," 0.2",[4023,14576,5365],{"class":4564},[4023,14578,14579],{"class":4025,"line":4201},[4023,14580,7986],{"class":4564},[4023,14582,14583],{"class":4025,"line":4207},[4023,14584,7991],{"class":4564},[4014,14586,14588],{"className":4877,"code":14587,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fcomponents\u002FErrorView.tsx\nimport React from 'react'\nimport { View, Text, Pressable, StyleSheet } from 'react-native'\nimport { AppError } from '..\u002Fapi\u002FAppError'\n\ninterface ErrorViewProps {\n    error: AppError\n    onRetry: () => void\n}\n\nexport const ErrorView: React.FC\u003CErrorViewProps> = ({ error, onRetry }) => {\n    return (\n        \u003CView style={styles.container}>\n            \u003CText style={styles.icon}>📡\u003C\u002FText>\n            \u003CText style={styles.title}>Помилка завантаження даних\u003C\u002FText>\n            \u003CText style={styles.message}>{error.userMessage}\u003C\u002FText>\n            \u003CPressable style={styles.button} onPress={onRetry}>\n                \u003CText style={styles.buttonText}>Повторити спробу\u003C\u002FText>\n            \u003C\u002FPressable>\n        \u003C\u002FView>\n    )\n}\n\nconst styles = StyleSheet.create({\n    container: {\n        flex: 1,\n        justifyContent: 'center',\n        alignItems: 'center',\n        padding: 24,\n    },\n    icon: {\n        fontSize: 48,\n        marginBottom: 16,\n    },\n    title: {\n        fontSize: 18,\n        fontWeight: '700',\n        color: '#0f172a',\n        marginBottom: 8,\n        textAlign: 'center',\n    },\n    message: {\n        fontSize: 14,\n        color: '#64748b',\n        textAlign: 'center',\n        marginBottom: 20,\n        lineHeight: 20,\n    },\n    button: {\n        backgroundColor: '#2563eb',\n        paddingHorizontal: 20,\n        paddingVertical: 12,\n        borderRadius: 8,\n    },\n    buttonText: {\n        color: '#ffffff',\n        fontSize: 15,\n        fontWeight: '600',\n    },\n})\n",[3799,14589,14590,14595,14605,14632,14647,14651,14660,14670,14681,14685,14689,14727,14733,14747,14767,14797,14821,14847,14874,14883,14891,14895,14899,14903,14919,14925,14934,14942,14950,14960,14964,14971,14980,14989,14993,15000,15008,15016,15024,15032,15041,15045,15052,15061,15070,15078,15086,15095,15099,15106,15115,15123,15131,15140,15144,15151,15159,15168,15177,15181],{"__ignoreMap":4019},[4023,14591,14592],{"class":4025,"line":4026},[4023,14593,14594],{"class":4965},"\u002F\u002F src\u002Fcomponents\u002FErrorView.tsx\n",[4023,14596,14597,14599,14601,14603],{"class":4025,"line":4032},[4023,14598,5731],{"class":4886},[4023,14600,5734],{"class":4910},[4023,14602,7697],{"class":4886},[4023,14604,5749],{"class":4580},[4023,14606,14607,14609,14611,14613,14615,14617,14619,14622,14624,14626,14628,14630],{"class":4025,"line":4038},[4023,14608,5731],{"class":4886},[4023,14610,4907],{"class":4564},[4023,14612,5758],{"class":4910},[4023,14614,3928],{"class":4564},[4023,14616,5763],{"class":4910},[4023,14618,3928],{"class":4564},[4023,14620,14621],{"class":4910},"Pressable",[4023,14623,3928],{"class":4564},[4023,14625,12265],{"class":4910},[4023,14627,5743],{"class":4564},[4023,14629,5746],{"class":4886},[4023,14631,5775],{"class":4580},[4023,14633,14634,14636,14638,14640,14642,14644],{"class":4025,"line":4044},[4023,14635,5731],{"class":4886},[4023,14637,4907],{"class":4564},[4023,14639,3897],{"class":4910},[4023,14641,5743],{"class":4564},[4023,14643,5746],{"class":4886},[4023,14645,14646],{"class":4580}," '..\u002Fapi\u002FAppError'\n",[4023,14648,14649],{"class":4025,"line":4050},[4023,14650,4060],{"emptyLinePlaceholder":4059},[4023,14652,14653,14655,14658],{"class":4025,"line":4056},[4023,14654,5814],{"class":4890},[4023,14656,14657],{"class":4894}," ErrorViewProps",[4023,14659,5446],{"class":4564},[4023,14661,14662,14665,14667],{"class":4025,"line":4063},[4023,14663,14664],{"class":4910},"    error",[4023,14666,4577],{"class":4564},[4023,14668,14669],{"class":4894},"AppError\n",[4023,14671,14672,14675,14677,14679],{"class":4025,"line":4069},[4023,14673,14674],{"class":4986},"    onRetry",[4023,14676,9603],{"class":4564},[4023,14678,5246],{"class":4890},[4023,14680,8050],{"class":4894},[4023,14682,14683],{"class":4025,"line":4075},[4023,14684,4090],{"class":4564},[4023,14686,14687],{"class":4025,"line":4081},[4023,14688,4060],{"emptyLinePlaceholder":4059},[4023,14690,14691,14693,14695,14698,14700,14702,14704,14706,14708,14711,14714,14716,14718,14721,14723,14725],{"class":4025,"line":4087},[4023,14692,4887],{"class":4886},[4023,14694,7873],{"class":4890},[4023,14696,14697],{"class":4986}," ErrorView",[4023,14699,4577],{"class":4564},[4023,14701,2021],{"class":4894},[4023,14703,3836],{"class":4564},[4023,14705,12323],{"class":4894},[4023,14707,4898],{"class":4564},[4023,14709,14710],{"class":4894},"ErrorViewProps",[4023,14712,14713],{"class":4564},"> = ({ ",[4023,14715,7089],{"class":4910},[4023,14717,3928],{"class":4564},[4023,14719,14720],{"class":4910},"onRetry",[4023,14722,12345],{"class":4564},[4023,14724,5246],{"class":4890},[4023,14726,5446],{"class":4564},[4023,14728,14729,14731],{"class":4025,"line":4093},[4023,14730,5144],{"class":4886},[4023,14732,6189],{"class":4564},[4023,14734,14735,14737,14739,14741,14743,14745],{"class":4025,"line":4098},[4023,14736,6195],{"class":4564},[4023,14738,5758],{"class":4910},[4023,14740,6201],{"class":4910},[4023,14742,14403],{"class":4564},[4023,14744,12529],{"class":4910},[4023,14746,14408],{"class":4564},[4023,14748,14749,14751,14753,14755,14757,14760,14763,14765],{"class":4025,"line":4104},[4023,14750,6229],{"class":4564},[4023,14752,5763],{"class":4910},[4023,14754,6201],{"class":4910},[4023,14756,14403],{"class":4564},[4023,14758,14759],{"class":4910},"icon",[4023,14761,14762],{"class":4564},"}>📡\u003C\u002F",[4023,14764,5763],{"class":4910},[4023,14766,4940],{"class":4564},[4023,14768,14769,14771,14773,14775,14777,14779,14782,14785,14788,14791,14793,14795],{"class":4025,"line":4110},[4023,14770,6229],{"class":4564},[4023,14772,5763],{"class":4910},[4023,14774,6201],{"class":4910},[4023,14776,14403],{"class":4564},[4023,14778,6274],{"class":4910},[4023,14780,14781],{"class":4564},"}>",[4023,14783,14784],{"class":4910},"Помилка",[4023,14786,14787],{"class":4910}," завантаження",[4023,14789,14790],{"class":4910}," даних",[4023,14792,6279],{"class":4564},[4023,14794,5763],{"class":4910},[4023,14796,4940],{"class":4564},[4023,14798,14799,14801,14803,14805,14807,14809,14812,14814,14817,14819],{"class":4025,"line":4116},[4023,14800,6229],{"class":4564},[4023,14802,5763],{"class":4910},[4023,14804,6201],{"class":4910},[4023,14806,14403],{"class":4564},[4023,14808,10601],{"class":4910},[4023,14810,14811],{"class":4564},"}>{error.",[4023,14813,10330],{"class":4910},[4023,14815,14816],{"class":4564},"}\u003C\u002F",[4023,14818,5763],{"class":4910},[4023,14820,4940],{"class":4564},[4023,14822,14823,14825,14827,14829,14831,14834,14837,14840,14843,14845],{"class":4025,"line":4121},[4023,14824,6229],{"class":4564},[4023,14826,14621],{"class":4910},[4023,14828,6201],{"class":4910},[4023,14830,14403],{"class":4564},[4023,14832,14833],{"class":4910},"button",[4023,14835,14836],{"class":4564},"} ",[4023,14838,14839],{"class":4910},"onPress",[4023,14841,14842],{"class":4564},"={",[4023,14844,14720],{"class":4910},[4023,14846,14408],{"class":4564},[4023,14848,14849,14851,14853,14855,14857,14860,14862,14865,14868,14870,14872],{"class":4025,"line":4126},[4023,14850,6311],{"class":4564},[4023,14852,5763],{"class":4910},[4023,14854,6201],{"class":4910},[4023,14856,14403],{"class":4564},[4023,14858,14859],{"class":4910},"buttonText",[4023,14861,14781],{"class":4564},[4023,14863,14864],{"class":4910},"Повторити",[4023,14866,14867],{"class":4910}," спробу",[4023,14869,6279],{"class":4564},[4023,14871,5763],{"class":4910},[4023,14873,4940],{"class":4564},[4023,14875,14876,14879,14881],{"class":4025,"line":4132},[4023,14877,14878],{"class":4564},"            \u003C\u002F",[4023,14880,14621],{"class":4910},[4023,14882,4940],{"class":4564},[4023,14884,14885,14887,14889],{"class":4025,"line":4138},[4023,14886,6456],{"class":4564},[4023,14888,5758],{"class":4910},[4023,14890,4940],{"class":4564},[4023,14892,14893],{"class":4025,"line":4144},[4023,14894,6465],{"class":4564},[4023,14896,14897],{"class":4025,"line":4150},[4023,14898,4090],{"class":4564},[4023,14900,14901],{"class":4025,"line":4156},[4023,14902,4060],{"emptyLinePlaceholder":4059},[4023,14904,14905,14907,14909,14911,14913,14915,14917],{"class":4025,"line":4162},[4023,14906,5076],{"class":4890},[4023,14908,12649],{"class":5032},[4023,14910,5403],{"class":4564},[4023,14912,12265],{"class":4910},[4023,14914,3836],{"class":4564},[4023,14916,7889],{"class":4986},[4023,14918,7892],{"class":4564},[4023,14920,14921,14923],{"class":4025,"line":4167},[4023,14922,12664],{"class":4910},[4023,14924,5446],{"class":4564},[4023,14926,14927,14930,14932],{"class":4025,"line":4173},[4023,14928,14929],{"class":4910},"        flex:",[4023,14931,12672],{"class":4601},[4023,14933,5365],{"class":4564},[4023,14935,14936,14938,14940],{"class":4025,"line":4179},[4023,14937,12709],{"class":4910},[4023,14939,12712],{"class":4580},[4023,14941,5365],{"class":4564},[4023,14943,14944,14946,14948],{"class":4025,"line":4184},[4023,14945,12719],{"class":4910},[4023,14947,12712],{"class":4580},[4023,14949,5365],{"class":4564},[4023,14951,14952,14955,14958],{"class":4025,"line":4189},[4023,14953,14954],{"class":4910},"        padding:",[4023,14956,14957],{"class":4601}," 24",[4023,14959,5365],{"class":4564},[4023,14961,14962],{"class":4025,"line":4195},[4023,14963,7986],{"class":4564},[4023,14965,14966,14969],{"class":4025,"line":4201},[4023,14967,14968],{"class":4910},"    icon:",[4023,14970,5446],{"class":4564},[4023,14972,14973,14975,14978],{"class":4025,"line":4207},[4023,14974,14552],{"class":4910},[4023,14976,14977],{"class":4601}," 48",[4023,14979,5365],{"class":4564},[4023,14981,14982,14985,14987],{"class":4025,"line":4213},[4023,14983,14984],{"class":4910},"        marginBottom:",[4023,14986,6217],{"class":4601},[4023,14988,5365],{"class":4564},[4023,14990,14991],{"class":4025,"line":4219},[4023,14992,7986],{"class":4564},[4023,14994,14995,14998],{"class":4025,"line":4225},[4023,14996,14997],{"class":4910},"    title:",[4023,14999,5446],{"class":4564},[4023,15001,15002,15004,15006],{"class":4025,"line":4230},[4023,15003,14552],{"class":4910},[4023,15005,6245],{"class":4601},[4023,15007,5365],{"class":4564},[4023,15009,15010,15012,15014],{"class":4025,"line":4235},[4023,15011,14562],{"class":4910},[4023,15013,12764],{"class":4580},[4023,15015,5365],{"class":4564},[4023,15017,15018,15020,15022],{"class":4025,"line":4241},[4023,15019,14543],{"class":4910},[4023,15021,12702],{"class":4580},[4023,15023,5365],{"class":4564},[4023,15025,15026,15028,15030],{"class":4025,"line":4247},[4023,15027,14984],{"class":4910},[4023,15029,6342],{"class":4601},[4023,15031,5365],{"class":4564},[4023,15033,15034,15037,15039],{"class":4025,"line":4253},[4023,15035,15036],{"class":4910},"        textAlign:",[4023,15038,12712],{"class":4580},[4023,15040,5365],{"class":4564},[4023,15042,15043],{"class":4025,"line":4259},[4023,15044,7986],{"class":4564},[4023,15046,15047,15050],{"class":4025,"line":4265},[4023,15048,15049],{"class":4910},"    message:",[4023,15051,5446],{"class":4564},[4023,15053,15054,15056,15059],{"class":4025,"line":4271},[4023,15055,14552],{"class":4910},[4023,15057,15058],{"class":4601}," 14",[4023,15060,5365],{"class":4564},[4023,15062,15063,15065,15068],{"class":4025,"line":4276},[4023,15064,14543],{"class":4910},[4023,15066,15067],{"class":4580}," '#64748b'",[4023,15069,5365],{"class":4564},[4023,15071,15072,15074,15076],{"class":4025,"line":5500},[4023,15073,15036],{"class":4910},[4023,15075,12712],{"class":4580},[4023,15077,5365],{"class":4564},[4023,15079,15080,15082,15084],{"class":4025,"line":5505},[4023,15081,14984],{"class":4910},[4023,15083,12757],{"class":4601},[4023,15085,5365],{"class":4564},[4023,15087,15088,15091,15093],{"class":4025,"line":5510},[4023,15089,15090],{"class":4910},"        lineHeight:",[4023,15092,12757],{"class":4601},[4023,15094,5365],{"class":4564},[4023,15096,15097],{"class":4025,"line":5515},[4023,15098,7986],{"class":4564},[4023,15100,15101,15104],{"class":4025,"line":5520},[4023,15102,15103],{"class":4910},"    button:",[4023,15105,5446],{"class":4564},[4023,15107,15108,15110,15113],{"class":4025,"line":5526},[4023,15109,12699],{"class":4910},[4023,15111,15112],{"class":4580}," '#2563eb'",[4023,15114,5365],{"class":4564},[4023,15116,15117,15119,15121],{"class":4025,"line":5531},[4023,15118,14507],{"class":4910},[4023,15120,12757],{"class":4601},[4023,15122,5365],{"class":4564},[4023,15124,15125,15127,15129],{"class":4025,"line":5557},[4023,15126,14497],{"class":4910},[4023,15128,14555],{"class":4601},[4023,15130,5365],{"class":4564},[4023,15132,15133,15136,15138],{"class":4025,"line":5572},[4023,15134,15135],{"class":4910},"        borderRadius:",[4023,15137,6342],{"class":4601},[4023,15139,5365],{"class":4564},[4023,15141,15142],{"class":4025,"line":5584},[4023,15143,7986],{"class":4564},[4023,15145,15146,15149],{"class":4025,"line":5597},[4023,15147,15148],{"class":4910},"    buttonText:",[4023,15150,5446],{"class":4564},[4023,15152,15153,15155,15157],{"class":4025,"line":5625},[4023,15154,14543],{"class":4910},[4023,15156,12750],{"class":4580},[4023,15158,5365],{"class":4564},[4023,15160,15161,15163,15166],{"class":4025,"line":5630},[4023,15162,14552],{"class":4910},[4023,15164,15165],{"class":4601}," 15",[4023,15167,5365],{"class":4564},[4023,15169,15170,15172,15175],{"class":4025,"line":5637},[4023,15171,14562],{"class":4910},[4023,15173,15174],{"class":4580}," '600'",[4023,15176,5365],{"class":4564},[4023,15178,15179],{"class":4025,"line":5644},[4023,15180,7986],{"class":4564},[4023,15182,15183],{"class":4025,"line":5657},[4023,15184,7991],{"class":4564},[3774,15186,15188],{"id":15187},"крок-4-головний-екран-із-нормалізованим-сховищем-та-фільтрацією","Крок 4. Головний екран із нормалізованим сховищем та фільтрацією",[4014,15190,15192],{"className":4877,"code":15191,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fscreens\u002FCurrencyRatesScreen.tsx\nimport React, { useState, useEffect, useCallback, useMemo } from 'react'\nimport {\n    View,\n    Text,\n    TextInput,\n    FlatList,\n    ActivityIndicator,\n    RefreshControl,\n    StyleSheet,\n} from 'react-native'\nimport { fetchNbuRates } from '..\u002Fapi\u002FcurrencyService'\nimport { CurrencyItem } from '..\u002Fapi\u002Ftypes'\nimport { AppError } from '..\u002Fapi\u002FAppError'\nimport { ErrorView } from '..\u002Fcomponents\u002FErrorView'\nimport { normalizeEntities, denormalizeEntities, NormalizedEntities } from '..\u002Futils\u002Fnormalize'\nimport { useRefetchOnFocusAndNetwork } from '..\u002Fhooks\u002FuseRefetchOnFocusAndNetwork'\n\nexport const CurrencyRatesScreen: React.FC = () => {\n    const [ratesState, setRatesState] = useState\u003CNormalizedEntities\u003CCurrencyItem>>({\n        byId: {},\n        allIds: [],\n    })\n    const [searchQuery, setSearchQuery] = useState\u003Cstring>('')\n    const [isLoading, setIsLoading] = useState\u003Cboolean>(true)\n    const [isRefreshing, setIsRefreshing] = useState\u003Cboolean>(false)\n    const [error, setError] = useState\u003CAppError | null>(null)\n\n    const loadData = useCallback(async (isManualRefresh = false) => {\n        if (isManualRefresh) {\n            setIsRefreshing(true)\n        } else {\n            setIsLoading(true)\n        }\n        setError(null)\n\n        try {\n            const data = await fetchNbuRates()\n            \u002F\u002F Зберігаємо у нормалізованій формі\n            setRatesState(normalizeEntities(data))\n        } catch (err) {\n            setError(AppError.from(err))\n        } finally {\n            setIsLoading(false)\n            setIsRefreshing(false)\n        }\n    }, [])\n\n    useEffect(() => {\n        loadData()\n    }, [loadData])\n\n    \u002F\u002F Автоматична ресинхронізація при поверненні в додаток або відновленні мережі\n    useRefetchOnFocusAndNetwork(() => {\n        loadData(true)\n    })\n\n    \u002F\u002F Фільтрація денормалізованого списку\n    const filteredRates = useMemo(() => {\n        const fullList = denormalizeEntities(ratesState)\n        if (!searchQuery.trim()) return fullList\n\n        const query = searchQuery.toLowerCase()\n        return fullList.filter(\n            (item) => item.code.toLowerCase().includes(query) || item.name.toLowerCase().includes(query),\n        )\n    }, [ratesState, searchQuery])\n\n    if (isLoading && !isRefreshing) {\n        return (\n            \u003CView style={styles.centerContainer}>\n                \u003CActivityIndicator size=\"large\" color=\"#2563eb\" \u002F>\n                \u003CText style={styles.loadingText}>Отримання офіційних курсів НБУ...\u003C\u002FText>\n            \u003C\u002FView>\n        )\n    }\n\n    if (error && ratesState.allIds.length === 0) {\n        return \u003CErrorView error={error} onRetry={() => loadData()} \u002F>\n    }\n\n    return (\n        \u003CView style={styles.container}>\n            \u003CView style={styles.searchContainer}>\n                \u003CTextInput\n                    style={styles.searchInput}\n                    placeholder=\"Пошук валюти (USD, EUR, гривня...)\"\n                    placeholderTextColor=\"#94a3b8\"\n                    value={searchQuery}\n                    onChangeText={setSearchQuery}\n                    clearButtonMode=\"while-editing\"\n                \u002F>\n            \u003C\u002FView>\n\n            \u003CFlatList\n                data={filteredRates}\n                keyExtractor={(item) => item.id}\n                contentContainerStyle={styles.listContainer}\n                refreshControl={\n                    \u003CRefreshControl\n                        refreshing={isRefreshing}\n                        onRefresh={() => loadData(true)}\n                        colors={['#2563eb']}\n                        tintColor=\"#2563eb\"\n                    \u002F>\n                }\n                renderItem={({ item }) => (\n                    \u003CView style={styles.card}>\n                        \u003CView style={styles.codeBadge}>\n                            \u003CText style={styles.codeText}>{item.code}\u003C\u002FText>\n                        \u003C\u002FView>\n                        \u003CView style={styles.infoContainer}>\n                            \u003CText style={styles.currencyName}>{item.name}\u003C\u002FText>\n                            \u003CText style={styles.dateText}>на {item.date}\u003C\u002FText>\n                        \u003C\u002FView>\n                        \u003CText style={styles.rateValue}>{item.rate} ₴\u003C\u002FText>\n                    \u003C\u002FView>\n                )}\n            \u002F>\n        \u003C\u002FView>\n    )\n}\n\nconst styles = StyleSheet.create({\n    container: { flex: 1, backgroundColor: '#f8fafc' },\n    centerContainer: {\n        flex: 1,\n        justifyContent: 'center',\n        alignItems: 'center',\n        backgroundColor: '#f8fafc',\n    },\n    loadingText: {\n        marginTop: 12,\n        fontSize: 14,\n        color: '#64748b',\n    },\n    searchContainer: {\n        paddingHorizontal: 16,\n        paddingTop: 12,\n        paddingBottom: 4,\n    },\n    searchInput: {\n        backgroundColor: '#ffffff',\n        borderWidth: 1,\n        borderColor: '#e2e8f0',\n        borderRadius: 10,\n        paddingHorizontal: 14,\n        paddingVertical: 10,\n        fontSize: 14,\n        color: '#0f172a',\n    },\n    listContainer: {\n        padding: 16,\n    },\n    card: {\n        flexDirection: 'row',\n        alignItems: 'center',\n        backgroundColor: '#ffffff',\n        padding: 16,\n        borderRadius: 12,\n        marginBottom: 12,\n        shadowColor: '#000',\n        shadowOffset: { width: 0, height: 1 },\n        shadowOpacity: 0.05,\n        shadowRadius: 3,\n        elevation: 2,\n    },\n    codeBadge: {\n        backgroundColor: '#eff6ff',\n        paddingVertical: 8,\n        paddingHorizontal: 12,\n        borderRadius: 8,\n        marginRight: 12,\n    },\n    codeText: {\n        fontSize: 16,\n        fontWeight: '800',\n        color: '#1d4ed8',\n    },\n    infoContainer: {\n        flex: 1,\n    },\n    currencyName: {\n        fontSize: 15,\n        fontWeight: '600',\n        color: '#0f172a',\n    },\n    dateText: {\n        fontSize: 12,\n        color: '#94a3b8',\n        marginTop: 2,\n    },\n    rateValue: {\n        fontSize: 18,\n        fontWeight: '800',\n        color: '#047857',\n    },\n})\n",[3799,15193,15194,15199,15229,15235,15242,15249,15256,15263,15270,15277,15284,15292,15308,15323,15337,15353,15376,15392,15396,15419,15448,15455,15462,15466,15495,15523,15550,15581,15585,15615,15625,15636,15644,15655,15659,15670,15674,15680,15695,15700,15715,15727,15746,15754,15764,15774,15778,15782,15786,15796,15803,15812,15816,15821,15832,15842,15846,15850,15855,15872,15889,15909,15913,15931,15943,15996,16001,16013,16017,16031,16037,16052,16077,16111,16119,16123,16127,16131,16158,16187,16191,16195,16200,16214,16229,16236,16248,16258,16268,16279,16290,16300,16305,16313,16317,16324,16336,16348,16360,16368,16375,16386,16403,16418,16423,16428,16433,16438,16443,16448,16453,16458,16463,16468,16473,16477,16482,16487,16492,16497,16502,16506,16511,16516,16522,16543,16551,16560,16569,16578,16587,16592,16600,16610,16619,16628,16633,16641,16650,16660,16671,16676,16684,16693,16703,16714,16723,16732,16741,16750,16759,16764,16772,16781,16786,16794,16805,16814,16823,16832,16841,16850,16861,16884,16895,16906,16917,16922,16930,16940,16949,16958,16967,16977,16982,16990,16999,17009,17019,17024,17032,17041,17046,17054,17063,17072,17081,17086,17094,17103,17113,17122,17127,17135,17144,17153,17163,17168],{"__ignoreMap":4019},[4023,15195,15196],{"class":4025,"line":4026},[4023,15197,15198],{"class":4965},"\u002F\u002F src\u002Fscreens\u002FCurrencyRatesScreen.tsx\n",[4023,15200,15201,15203,15205,15207,15209,15211,15213,15215,15218,15220,15223,15225,15227],{"class":4025,"line":4032},[4023,15202,5731],{"class":4886},[4023,15204,5734],{"class":4910},[4023,15206,5737],{"class":4564},[4023,15208,5740],{"class":4910},[4023,15210,3928],{"class":4564},[4023,15212,11443],{"class":4910},[4023,15214,3928],{"class":4564},[4023,15216,15217],{"class":4910},"useCallback",[4023,15219,3928],{"class":4564},[4023,15221,15222],{"class":4910},"useMemo",[4023,15224,5743],{"class":4564},[4023,15226,5746],{"class":4886},[4023,15228,5749],{"class":4580},[4023,15230,15231,15233],{"class":4025,"line":4038},[4023,15232,5731],{"class":4886},[4023,15234,5446],{"class":4564},[4023,15236,15237,15240],{"class":4025,"line":4044},[4023,15238,15239],{"class":4910},"    View",[4023,15241,5365],{"class":4564},[4023,15243,15244,15247],{"class":4025,"line":4050},[4023,15245,15246],{"class":4910},"    Text",[4023,15248,5365],{"class":4564},[4023,15250,15251,15254],{"class":4025,"line":4056},[4023,15252,15253],{"class":4910},"    TextInput",[4023,15255,5365],{"class":4564},[4023,15257,15258,15261],{"class":4025,"line":4063},[4023,15259,15260],{"class":4910},"    FlatList",[4023,15262,5365],{"class":4564},[4023,15264,15265,15268],{"class":4025,"line":4069},[4023,15266,15267],{"class":4910},"    ActivityIndicator",[4023,15269,5365],{"class":4564},[4023,15271,15272,15275],{"class":4025,"line":4075},[4023,15273,15274],{"class":4910},"    RefreshControl",[4023,15276,5365],{"class":4564},[4023,15278,15279,15282],{"class":4025,"line":4081},[4023,15280,15281],{"class":4910},"    StyleSheet",[4023,15283,5365],{"class":4564},[4023,15285,15286,15288,15290],{"class":4025,"line":4087},[4023,15287,14836],{"class":4564},[4023,15289,5746],{"class":4886},[4023,15291,5775],{"class":4580},[4023,15293,15294,15296,15298,15301,15303,15305],{"class":4025,"line":4093},[4023,15295,5731],{"class":4886},[4023,15297,4907],{"class":4564},[4023,15299,15300],{"class":4910},"fetchNbuRates",[4023,15302,5743],{"class":4564},[4023,15304,5746],{"class":4886},[4023,15306,15307],{"class":4580}," '..\u002Fapi\u002FcurrencyService'\n",[4023,15309,15310,15312,15314,15316,15318,15320],{"class":4025,"line":4098},[4023,15311,5731],{"class":4886},[4023,15313,4907],{"class":4564},[4023,15315,13886],{"class":4910},[4023,15317,5743],{"class":4564},[4023,15319,5746],{"class":4886},[4023,15321,15322],{"class":4580}," '..\u002Fapi\u002Ftypes'\n",[4023,15324,15325,15327,15329,15331,15333,15335],{"class":4025,"line":4104},[4023,15326,5731],{"class":4886},[4023,15328,4907],{"class":4564},[4023,15330,3897],{"class":4910},[4023,15332,5743],{"class":4564},[4023,15334,5746],{"class":4886},[4023,15336,14646],{"class":4580},[4023,15338,15339,15341,15343,15346,15348,15350],{"class":4025,"line":4110},[4023,15340,5731],{"class":4886},[4023,15342,4907],{"class":4564},[4023,15344,15345],{"class":4910},"ErrorView",[4023,15347,5743],{"class":4564},[4023,15349,5746],{"class":4886},[4023,15351,15352],{"class":4580}," '..\u002Fcomponents\u002FErrorView'\n",[4023,15354,15355,15357,15359,15361,15363,15365,15367,15369,15371,15373],{"class":4025,"line":4116},[4023,15356,5731],{"class":4886},[4023,15358,4907],{"class":4564},[4023,15360,5784],{"class":4910},[4023,15362,3928],{"class":4564},[4023,15364,5794],{"class":4910},[4023,15366,3928],{"class":4564},[4023,15368,5017],{"class":4910},[4023,15370,5743],{"class":4564},[4023,15372,5746],{"class":4886},[4023,15374,15375],{"class":4580}," '..\u002Futils\u002Fnormalize'\n",[4023,15377,15378,15380,15382,15385,15387,15389],{"class":4025,"line":4121},[4023,15379,5731],{"class":4886},[4023,15381,4907],{"class":4564},[4023,15383,15384],{"class":4910},"useRefetchOnFocusAndNetwork",[4023,15386,5743],{"class":4564},[4023,15388,5746],{"class":4886},[4023,15390,15391],{"class":4580}," '..\u002Fhooks\u002FuseRefetchOnFocusAndNetwork'\n",[4023,15393,15394],{"class":4025,"line":4126},[4023,15395,4060],{"emptyLinePlaceholder":4059},[4023,15397,15398,15400,15402,15405,15407,15409,15411,15413,15415,15417],{"class":4025,"line":4132},[4023,15399,4887],{"class":4886},[4023,15401,7873],{"class":4890},[4023,15403,15404],{"class":4986}," CurrencyRatesScreen",[4023,15406,4577],{"class":4564},[4023,15408,2021],{"class":4894},[4023,15410,3836],{"class":4564},[4023,15412,12323],{"class":4894},[4023,15414,14345],{"class":4564},[4023,15416,5246],{"class":4890},[4023,15418,5446],{"class":4564},[4023,15420,15421,15423,15425,15428,15430,15433,15435,15437,15439,15441,15443,15445],{"class":4025,"line":4138},[4023,15422,5029],{"class":4890},[4023,15424,5957],{"class":4564},[4023,15426,15427],{"class":5032},"ratesState",[4023,15429,3928],{"class":4564},[4023,15431,15432],{"class":5032},"setRatesState",[4023,15434,5106],{"class":4564},[4023,15436,5740],{"class":4986},[4023,15438,4898],{"class":4564},[4023,15440,5017],{"class":4894},[4023,15442,4898],{"class":4564},[4023,15444,13886],{"class":4894},[4023,15446,15447],{"class":4564},">>({\n",[4023,15449,15450,15452],{"class":4025,"line":4144},[4023,15451,5460],{"class":4910},[4023,15453,15454],{"class":4564}," {},\n",[4023,15456,15457,15459],{"class":4025,"line":4150},[4023,15458,5676],{"class":4910},[4023,15460,15461],{"class":4564}," [],\n",[4023,15463,15464],{"class":4025,"line":4156},[4023,15465,8205],{"class":4564},[4023,15467,15468,15470,15472,15475,15477,15480,15482,15484,15486,15488,15490,15493],{"class":4025,"line":4162},[4023,15469,5029],{"class":4890},[4023,15471,5957],{"class":4564},[4023,15473,15474],{"class":5032},"searchQuery",[4023,15476,3928],{"class":4564},[4023,15478,15479],{"class":5032},"setSearchQuery",[4023,15481,5106],{"class":4564},[4023,15483,5740],{"class":4986},[4023,15485,4898],{"class":4564},[4023,15487,4916],{"class":4894},[4023,15489,9597],{"class":4564},[4023,15491,15492],{"class":4580},"''",[4023,15494,5131],{"class":4564},[4023,15496,15497,15499,15501,15504,15506,15509,15511,15513,15515,15517,15519,15521],{"class":4025,"line":4167},[4023,15498,5029],{"class":4890},[4023,15500,5957],{"class":4564},[4023,15502,15503],{"class":5032},"isLoading",[4023,15505,3928],{"class":4564},[4023,15507,15508],{"class":5032},"setIsLoading",[4023,15510,5106],{"class":4564},[4023,15512,5740],{"class":4986},[4023,15514,4898],{"class":4564},[4023,15516,8472],{"class":4894},[4023,15518,9597],{"class":4564},[4023,15520,3834],{"class":4890},[4023,15522,5131],{"class":4564},[4023,15524,15525,15527,15529,15531,15533,15536,15538,15540,15542,15544,15546,15548],{"class":4025,"line":4173},[4023,15526,5029],{"class":4890},[4023,15528,5957],{"class":4564},[4023,15530,8567],{"class":5032},[4023,15532,3928],{"class":4564},[4023,15534,15535],{"class":5032},"setIsRefreshing",[4023,15537,5106],{"class":4564},[4023,15539,5740],{"class":4986},[4023,15541,4898],{"class":4564},[4023,15543,8472],{"class":4894},[4023,15545,9597],{"class":4564},[4023,15547,11382],{"class":4890},[4023,15549,5131],{"class":4564},[4023,15551,15552,15554,15556,15558,15560,15563,15565,15567,15569,15571,15573,15575,15577,15579],{"class":4025,"line":4179},[4023,15553,5029],{"class":4890},[4023,15555,5957],{"class":4564},[4023,15557,7089],{"class":5032},[4023,15559,3928],{"class":4564},[4023,15561,15562],{"class":5032},"setError",[4023,15564,5106],{"class":4564},[4023,15566,5740],{"class":4986},[4023,15568,4898],{"class":4564},[4023,15570,3897],{"class":4894},[4023,15572,8104],{"class":4564},[4023,15574,8107],{"class":4894},[4023,15576,9597],{"class":4564},[4023,15578,8107],{"class":4890},[4023,15580,5131],{"class":4564},[4023,15582,15583],{"class":4025,"line":4184},[4023,15584,4060],{"emptyLinePlaceholder":4059},[4023,15586,15587,15589,15592,15594,15596,15598,15600,15602,15605,15607,15609,15611,15613],{"class":4025,"line":4189},[4023,15588,5029],{"class":4890},[4023,15590,15591],{"class":5032}," loadData",[4023,15593,5403],{"class":4564},[4023,15595,15217],{"class":4986},[4023,15597,5122],{"class":4564},[4023,15599,6627],{"class":4890},[4023,15601,3924],{"class":4564},[4023,15603,15604],{"class":4910},"isManualRefresh",[4023,15606,5403],{"class":4564},[4023,15608,11382],{"class":4890},[4023,15610,5243],{"class":4564},[4023,15612,5246],{"class":4890},[4023,15614,5446],{"class":4564},[4023,15616,15617,15619,15621,15623],{"class":4025,"line":4195},[4023,15618,6908],{"class":4886},[4023,15620,3924],{"class":4564},[4023,15622,15604],{"class":4910},[4023,15624,5088],{"class":4564},[4023,15626,15627,15630,15632,15634],{"class":4025,"line":4201},[4023,15628,15629],{"class":4986},"            setIsRefreshing",[4023,15631,5122],{"class":4564},[4023,15633,3834],{"class":4890},[4023,15635,5131],{"class":4564},[4023,15637,15638,15640,15642],{"class":4025,"line":4207},[4023,15639,8169],{"class":4564},[4023,15641,8172],{"class":4886},[4023,15643,5446],{"class":4564},[4023,15645,15646,15649,15651,15653],{"class":4025,"line":4213},[4023,15647,15648],{"class":4986},"            setIsLoading",[4023,15650,5122],{"class":4564},[4023,15652,3834],{"class":4890},[4023,15654,5131],{"class":4564},[4023,15656,15657],{"class":4025,"line":4219},[4023,15658,6971],{"class":4564},[4023,15660,15661,15664,15666,15668],{"class":4025,"line":4225},[4023,15662,15663],{"class":4986},"        setError",[4023,15665,5122],{"class":4564},[4023,15667,8107],{"class":4890},[4023,15669,5131],{"class":4564},[4023,15671,15672],{"class":4025,"line":4230},[4023,15673,4060],{"emptyLinePlaceholder":4059},[4023,15675,15676,15678],{"class":4025,"line":4235},[4023,15677,8795],{"class":4886},[4023,15679,5446],{"class":4564},[4023,15681,15682,15684,15687,15689,15691,15693],{"class":4025,"line":4241},[4023,15683,6075],{"class":4890},[4023,15685,15686],{"class":5032}," data",[4023,15688,5403],{"class":4564},[4023,15690,6800],{"class":4886},[4023,15692,14027],{"class":4986},[4023,15694,6711],{"class":4564},[4023,15696,15697],{"class":4025,"line":4247},[4023,15698,15699],{"class":4965},"            \u002F\u002F Зберігаємо у нормалізованій формі\n",[4023,15701,15702,15705,15707,15709,15711,15713],{"class":4025,"line":4253},[4023,15703,15704],{"class":4986},"            setRatesState",[4023,15706,5122],{"class":4564},[4023,15708,5784],{"class":4986},[4023,15710,5122],{"class":4564},[4023,15712,8917],{"class":4910},[4023,15714,5290],{"class":4564},[4023,15716,15717,15719,15721,15723,15725],{"class":4025,"line":4259},[4023,15718,8169],{"class":4564},[4023,15720,7084],{"class":4886},[4023,15722,3924],{"class":4564},[4023,15724,8710],{"class":4910},[4023,15726,5088],{"class":4564},[4023,15728,15729,15732,15734,15736,15738,15740,15742,15744],{"class":4025,"line":4265},[4023,15730,15731],{"class":4986},"            setError",[4023,15733,5122],{"class":4564},[4023,15735,3897],{"class":4910},[4023,15737,3836],{"class":4564},[4023,15739,5746],{"class":4986},[4023,15741,5122],{"class":4564},[4023,15743,8710],{"class":4910},[4023,15745,5290],{"class":4564},[4023,15747,15748,15750,15752],{"class":4025,"line":4271},[4023,15749,8169],{"class":4564},[4023,15751,9091],{"class":4886},[4023,15753,5446],{"class":4564},[4023,15755,15756,15758,15760,15762],{"class":4025,"line":4276},[4023,15757,15648],{"class":4986},[4023,15759,5122],{"class":4564},[4023,15761,11382],{"class":4890},[4023,15763,5131],{"class":4564},[4023,15765,15766,15768,15770,15772],{"class":4025,"line":5500},[4023,15767,15629],{"class":4986},[4023,15769,5122],{"class":4564},[4023,15771,11382],{"class":4890},[4023,15773,5131],{"class":4564},[4023,15775,15776],{"class":4025,"line":5505},[4023,15777,6971],{"class":4564},[4023,15779,15780],{"class":4025,"line":5510},[4023,15781,11976],{"class":4564},[4023,15783,15784],{"class":4025,"line":5515},[4023,15785,4060],{"emptyLinePlaceholder":4059},[4023,15787,15788,15790,15792,15794],{"class":4025,"line":5520},[4023,15789,11652],{"class":4986},[4023,15791,6750],{"class":4564},[4023,15793,5246],{"class":4890},[4023,15795,5446],{"class":4564},[4023,15797,15798,15801],{"class":4025,"line":5526},[4023,15799,15800],{"class":4986},"        loadData",[4023,15802,6711],{"class":4564},[4023,15804,15805,15807,15810],{"class":4025,"line":5531},[4023,15806,13130],{"class":4564},[4023,15808,15809],{"class":4910},"loadData",[4023,15811,13135],{"class":4564},[4023,15813,15814],{"class":4025,"line":5557},[4023,15815,4060],{"emptyLinePlaceholder":4059},[4023,15817,15818],{"class":4025,"line":5572},[4023,15819,15820],{"class":4965},"    \u002F\u002F Автоматична ресинхронізація при поверненні в додаток або відновленні мережі\n",[4023,15822,15823,15826,15828,15830],{"class":4025,"line":5584},[4023,15824,15825],{"class":4986},"    useRefetchOnFocusAndNetwork",[4023,15827,6750],{"class":4564},[4023,15829,5246],{"class":4890},[4023,15831,5446],{"class":4564},[4023,15833,15834,15836,15838,15840],{"class":4025,"line":5597},[4023,15835,15800],{"class":4986},[4023,15837,5122],{"class":4564},[4023,15839,3834],{"class":4890},[4023,15841,5131],{"class":4564},[4023,15843,15844],{"class":4025,"line":5625},[4023,15845,8205],{"class":4564},[4023,15847,15848],{"class":4025,"line":5630},[4023,15849,4060],{"emptyLinePlaceholder":4059},[4023,15851,15852],{"class":4025,"line":5637},[4023,15853,15854],{"class":4965},"    \u002F\u002F Фільтрація денормалізованого списку\n",[4023,15856,15857,15859,15862,15864,15866,15868,15870],{"class":4025,"line":5644},[4023,15858,5029],{"class":4890},[4023,15860,15861],{"class":5032}," filteredRates",[4023,15863,5403],{"class":4564},[4023,15865,15222],{"class":4986},[4023,15867,6750],{"class":4564},[4023,15869,5246],{"class":4890},[4023,15871,5446],{"class":4564},[4023,15873,15874,15876,15879,15881,15883,15885,15887],{"class":4025,"line":5657},[4023,15875,6792],{"class":4890},[4023,15877,15878],{"class":5032}," fullList",[4023,15880,5403],{"class":4564},[4023,15882,5794],{"class":4986},[4023,15884,5122],{"class":4564},[4023,15886,15427],{"class":4910},[4023,15888,5131],{"class":4564},[4023,15890,15891,15893,15895,15897,15899,15902,15904,15906],{"class":4025,"line":5668},[4023,15892,6908],{"class":4886},[4023,15894,5424],{"class":4564},[4023,15896,15474],{"class":4910},[4023,15898,3836],{"class":4564},[4023,15900,15901],{"class":4986},"trim",[4023,15903,7070],{"class":4564},[4023,15905,5432],{"class":4886},[4023,15907,15908],{"class":4910}," fullList\n",[4023,15910,15911],{"class":4025,"line":5673},[4023,15912,4060],{"emptyLinePlaceholder":4059},[4023,15914,15915,15917,15920,15922,15924,15926,15929],{"class":4025,"line":5709},[4023,15916,6792],{"class":4890},[4023,15918,15919],{"class":5032}," query",[4023,15921,5403],{"class":4564},[4023,15923,15474],{"class":4910},[4023,15925,3836],{"class":4564},[4023,15927,15928],{"class":4986},"toLowerCase",[4023,15930,6711],{"class":4564},[4023,15932,15933,15935,15937,15939,15941],{"class":4025,"line":5714},[4023,15934,7057],{"class":4886},[4023,15936,15878],{"class":4910},[4023,15938,3836],{"class":4564},[4023,15940,5262],{"class":4986},[4023,15942,8247],{"class":4564},[4023,15944,15945,15948,15950,15952,15954,15956,15958,15960,15962,15964,15966,15968,15970,15973,15976,15978,15980,15982,15984,15986,15988,15990,15992,15994],{"class":4025,"line":8477},[4023,15946,15947],{"class":4564},"            (",[4023,15949,5099],{"class":4910},[4023,15951,5243],{"class":4564},[4023,15953,5246],{"class":4890},[4023,15955,5079],{"class":4910},[4023,15957,3836],{"class":4564},[4023,15959,3799],{"class":4910},[4023,15961,3836],{"class":4564},[4023,15963,15928],{"class":4986},[4023,15965,11675],{"class":4564},[4023,15967,7021],{"class":4986},[4023,15969,5122],{"class":4564},[4023,15971,15972],{"class":4910},"query",[4023,15974,15975],{"class":4564},") || ",[4023,15977,5099],{"class":4910},[4023,15979,3836],{"class":4564},[4023,15981,7133],{"class":4910},[4023,15983,3836],{"class":4564},[4023,15985,15928],{"class":4986},[4023,15987,11675],{"class":4564},[4023,15989,7021],{"class":4986},[4023,15991,5122],{"class":4564},[4023,15993,15972],{"class":4910},[4023,15995,8376],{"class":4564},[4023,15997,15998],{"class":4025,"line":8482},[4023,15999,16000],{"class":4564},"        )\n",[4023,16002,16003,16005,16007,16009,16011],{"class":4025,"line":8488},[4023,16004,13130],{"class":4564},[4023,16006,15427],{"class":4910},[4023,16008,3928],{"class":4564},[4023,16010,15474],{"class":4910},[4023,16012,13135],{"class":4564},[4023,16014,16015],{"class":4025,"line":8527},[4023,16016,4060],{"emptyLinePlaceholder":4059},[4023,16018,16019,16021,16023,16025,16027,16029],{"class":4025,"line":8544},[4023,16020,5421],{"class":4886},[4023,16022,3924],{"class":4564},[4023,16024,15503],{"class":4910},[4023,16026,7740],{"class":4564},[4023,16028,8567],{"class":4910},[4023,16030,5088],{"class":4564},[4023,16032,16033,16035],{"class":4025,"line":8549},[4023,16034,7057],{"class":4886},[4023,16036,6189],{"class":4564},[4023,16038,16039,16041,16043,16045,16047,16050],{"class":4025,"line":8554},[4023,16040,6229],{"class":4564},[4023,16042,5758],{"class":4910},[4023,16044,6201],{"class":4910},[4023,16046,14403],{"class":4564},[4023,16048,16049],{"class":4910},"centerContainer",[4023,16051,14408],{"class":4564},[4023,16053,16054,16056,16059,16062,16064,16067,16070,16072,16075],{"class":4025,"line":8560},[4023,16055,6311],{"class":4564},[4023,16057,16058],{"class":4910},"ActivityIndicator",[4023,16060,16061],{"class":4910}," size",[4023,16063,6204],{"class":4564},[4023,16065,16066],{"class":4580},"\"large\"",[4023,16068,16069],{"class":4910}," color",[4023,16071,6204],{"class":4564},[4023,16073,16074],{"class":4580},"\"#2563eb\"",[4023,16076,6435],{"class":4564},[4023,16078,16079,16081,16083,16085,16087,16090,16092,16095,16098,16101,16104,16107,16109],{"class":4025,"line":8572},[4023,16080,6311],{"class":4564},[4023,16082,5763],{"class":4910},[4023,16084,6201],{"class":4910},[4023,16086,14403],{"class":4564},[4023,16088,16089],{"class":4910},"loadingText",[4023,16091,14781],{"class":4564},[4023,16093,16094],{"class":4910},"Отримання",[4023,16096,16097],{"class":4910}," офіційних",[4023,16099,16100],{"class":4910}," курсів",[4023,16102,16103],{"class":5032}," НБУ",[4023,16105,16106],{"class":4564},"...\u003C\u002F",[4023,16108,5763],{"class":4910},[4023,16110,4940],{"class":4564},[4023,16112,16113,16115,16117],{"class":4025,"line":8595},[4023,16114,14878],{"class":4564},[4023,16116,5758],{"class":4910},[4023,16118,4940],{"class":4564},[4023,16120,16121],{"class":4025,"line":8607},[4023,16122,16000],{"class":4564},[4023,16124,16125],{"class":4025,"line":8630},[4023,16126,4147],{"class":4564},[4023,16128,16129],{"class":4025,"line":8646},[4023,16130,4060],{"emptyLinePlaceholder":4059},[4023,16132,16133,16135,16137,16139,16141,16143,16145,16147,16149,16152,16154,16156],{"class":4025,"line":8672},[4023,16134,5421],{"class":4886},[4023,16136,3924],{"class":4564},[4023,16138,7089],{"class":4910},[4023,16140,7126],{"class":4564},[4023,16142,15427],{"class":4910},[4023,16144,3836],{"class":4564},[4023,16146,3828],{"class":4910},[4023,16148,3836],{"class":4564},[4023,16150,16151],{"class":4910},"length",[4023,16153,7136],{"class":4564},[4023,16155,7824],{"class":4601},[4023,16157,5088],{"class":4564},[4023,16159,16160,16162,16165,16167,16169,16171,16173,16175,16177,16180,16182,16184],{"class":4025,"line":8678},[4023,16161,7057],{"class":4886},[4023,16163,16164],{"class":4564}," \u003C",[4023,16166,15345],{"class":4894},[4023,16168,10704],{"class":4894},[4023,16170,14842],{"class":4564},[4023,16172,7089],{"class":4910},[4023,16174,14836],{"class":4564},[4023,16176,14720],{"class":4894},[4023,16178,16179],{"class":4564},"={() ",[4023,16181,5246],{"class":4890},[4023,16183,15591],{"class":4894},[4023,16185,16186],{"class":4564},"()} \u002F>\n",[4023,16188,16189],{"class":4025,"line":8694},[4023,16190,4147],{"class":4564},[4023,16192,16193],{"class":4025,"line":8700},[4023,16194,4060],{"emptyLinePlaceholder":4059},[4023,16196,16197],{"class":4025,"line":8723},[4023,16198,16199],{"class":4564},"    return (\n",[4023,16201,16202,16204,16206,16208,16210,16212],{"class":4025,"line":8735},[4023,16203,6195],{"class":4564},[4023,16205,5758],{"class":4910},[4023,16207,6201],{"class":4910},[4023,16209,14403],{"class":4564},[4023,16211,12529],{"class":4910},[4023,16213,14408],{"class":4564},[4023,16215,16216,16218,16220,16222,16224,16227],{"class":4025,"line":8740},[4023,16217,6229],{"class":4564},[4023,16219,5758],{"class":4910},[4023,16221,6201],{"class":4910},[4023,16223,14403],{"class":4564},[4023,16225,16226],{"class":4910},"searchContainer",[4023,16228,14408],{"class":4564},[4023,16230,16231,16233],{"class":4025,"line":8746},[4023,16232,6311],{"class":4564},[4023,16234,16235],{"class":4910},"TextInput\n",[4023,16237,16238,16241,16243,16246],{"class":4025,"line":8752},[4023,16239,16240],{"class":4910},"                    style",[4023,16242,14403],{"class":4564},[4023,16244,16245],{"class":4910},"searchInput",[4023,16247,4090],{"class":4564},[4023,16249,16250,16253,16255],{"class":4025,"line":8757},[4023,16251,16252],{"class":4910},"                    placeholder",[4023,16254,6204],{"class":4564},[4023,16256,16257],{"class":4580},"\"Пошук валюти (USD, EUR, гривня...)\"\n",[4023,16259,16260,16263,16265],{"class":4025,"line":8762},[4023,16261,16262],{"class":4910},"                    placeholderTextColor",[4023,16264,6204],{"class":4564},[4023,16266,16267],{"class":4580},"\"#94a3b8\"\n",[4023,16269,16270,16273,16275,16277],{"class":4025,"line":8777},[4023,16271,16272],{"class":4910},"                    value",[4023,16274,14842],{"class":4564},[4023,16276,15474],{"class":4910},[4023,16278,4090],{"class":4564},[4023,16280,16281,16284,16286,16288],{"class":4025,"line":8787},[4023,16282,16283],{"class":4910},"                    onChangeText",[4023,16285,14842],{"class":4564},[4023,16287,15479],{"class":4910},[4023,16289,4090],{"class":4564},[4023,16291,16292,16295,16297],{"class":4025,"line":8792},[4023,16293,16294],{"class":4910},"                    clearButtonMode",[4023,16296,6204],{"class":4564},[4023,16298,16299],{"class":4580},"\"while-editing\"\n",[4023,16301,16302],{"class":4025,"line":8800},[4023,16303,16304],{"class":4564},"                \u002F>\n",[4023,16306,16307,16309,16311],{"class":4025,"line":8806},[4023,16308,14878],{"class":4564},[4023,16310,5758],{"class":4910},[4023,16312,4940],{"class":4564},[4023,16314,16315],{"class":4025,"line":8812},[4023,16316,4060],{"emptyLinePlaceholder":4059},[4023,16318,16319,16321],{"class":4025,"line":8843},[4023,16320,6229],{"class":4564},[4023,16322,16323],{"class":4910},"FlatList\n",[4023,16325,16326,16329,16331,16334],{"class":4025,"line":8864},[4023,16327,16328],{"class":4910},"                data",[4023,16330,14842],{"class":4564},[4023,16332,16333],{"class":4910},"filteredRates",[4023,16335,4090],{"class":4564},[4023,16337,16338,16341,16344,16346],{"class":4025,"line":8878},[4023,16339,16340],{"class":4910},"                keyExtractor",[4023,16342,16343],{"class":4564},"={(item) => item.",[4023,16345,4911],{"class":4910},[4023,16347,4090],{"class":4564},[4023,16349,16350,16353,16355,16358],{"class":4025,"line":8891},[4023,16351,16352],{"class":4910},"                contentContainerStyle",[4023,16354,14403],{"class":4564},[4023,16356,16357],{"class":4910},"listContainer",[4023,16359,4090],{"class":4564},[4023,16361,16362,16365],{"class":4025,"line":8897},[4023,16363,16364],{"class":4910},"                refreshControl",[4023,16366,16367],{"class":4564},"={\n",[4023,16369,16370,16372],{"class":4025,"line":8902},[4023,16371,6353],{"class":4564},[4023,16373,16374],{"class":4910},"RefreshControl\n",[4023,16376,16377,16380,16382,16384],{"class":4025,"line":8925},[4023,16378,16379],{"class":4910},"                        refreshing",[4023,16381,14842],{"class":4564},[4023,16383,8567],{"class":4910},[4023,16385,4090],{"class":4564},[4023,16387,16388,16391,16394,16396,16398,16400],{"class":4025,"line":8930},[4023,16389,16390],{"class":4910},"                        onRefresh",[4023,16392,16393],{"class":4564},"={() => ",[4023,16395,15809],{"class":4986},[4023,16397,5122],{"class":4564},[4023,16399,3834],{"class":4910},[4023,16401,16402],{"class":4564},")}\n",[4023,16404,16405,16408,16410,16412,16415],{"class":4025,"line":8936},[4023,16406,16407],{"class":4910},"                        colors",[4023,16409,14842],{"class":4564},[4023,16411,5096],{"class":4910},[4023,16413,16414],{"class":4580},"'#2563eb'",[4023,16416,16417],{"class":4910},"]}\n",[4023,16419,16420],{"class":4025,"line":8942},[4023,16421,16422],{"class":4910},"                        tintColor=\"#2563eb\"\n",[4023,16424,16425],{"class":4025,"line":8947},[4023,16426,16427],{"class":4910},"                    \u002F>\n",[4023,16429,16430],{"class":4025,"line":8964},[4023,16431,16432],{"class":4910},"                }\n",[4023,16434,16435],{"class":4025,"line":8969},[4023,16436,16437],{"class":4910},"                renderItem={({ item }) => (\n",[4023,16439,16440],{"class":4025,"line":8984},[4023,16441,16442],{"class":4910},"                    \u003CView style={styles.card}>\n",[4023,16444,16445],{"class":4025,"line":9010},[4023,16446,16447],{"class":4910},"                        \u003CView style={styles.codeBadge}>\n",[4023,16449,16450],{"class":4025,"line":9016},[4023,16451,16452],{"class":4910},"                            \u003CText style={styles.codeText}>{item.code}\u003C\u002FText>\n",[4023,16454,16455],{"class":4025,"line":9021},[4023,16456,16457],{"class":4910},"                        \u003C\u002FView>\n",[4023,16459,16460],{"class":4025,"line":9034},[4023,16461,16462],{"class":4910},"                        \u003CView style={styles.infoContainer}>\n",[4023,16464,16465],{"class":4025,"line":9048},[4023,16466,16467],{"class":4910},"                            \u003CText style={styles.currencyName}>{item.name}\u003C\u002FText>\n",[4023,16469,16470],{"class":4025,"line":9063},[4023,16471,16472],{"class":4910},"                            \u003CText style={styles.dateText}>на {item.date}\u003C\u002FText>\n",[4023,16474,16475],{"class":4025,"line":9069},[4023,16476,16457],{"class":4910},[4023,16478,16479],{"class":4025,"line":9086},[4023,16480,16481],{"class":4910},"                        \u003CText style={styles.rateValue}>{item.rate} ₴\u003C\u002FText>\n",[4023,16483,16484],{"class":4025,"line":9096},[4023,16485,16486],{"class":4910},"                    \u003C\u002FView>\n",[4023,16488,16489],{"class":4025,"line":9106},[4023,16490,16491],{"class":4910},"                )}\n",[4023,16493,16494],{"class":4025,"line":9111},[4023,16495,16496],{"class":4910},"            \u002F>\n",[4023,16498,16499],{"class":4025,"line":9116},[4023,16500,16501],{"class":4910},"        \u003C\u002FView>\n",[4023,16503,16504],{"class":4025,"line":11298},[4023,16505,6465],{"class":4910},[4023,16507,16509],{"class":4025,"line":16508},122,[4023,16510,4090],{"class":4910},[4023,16512,16514],{"class":4025,"line":16513},123,[4023,16515,4060],{"emptyLinePlaceholder":4059},[4023,16517,16519],{"class":4025,"line":16518},124,[4023,16520,16521],{"class":4910},"const styles = StyleSheet.create({\n",[4023,16523,16525,16527,16529,16531,16533,16535,16538,16541],{"class":4025,"line":16524},125,[4023,16526,12664],{"class":4910},[4023,16528,4907],{"class":4564},[4023,16530,12669],{"class":4910},[4023,16532,12672],{"class":4601},[4023,16534,3928],{"class":4564},[4023,16536,16537],{"class":4910},"backgroundColor:",[4023,16539,16540],{"class":4580}," '#f8fafc'",[4023,16542,4605],{"class":4564},[4023,16544,16546,16549],{"class":4025,"line":16545},126,[4023,16547,16548],{"class":4910},"    centerContainer:",[4023,16550,5446],{"class":4564},[4023,16552,16554,16556,16558],{"class":4025,"line":16553},127,[4023,16555,14929],{"class":4910},[4023,16557,12672],{"class":4601},[4023,16559,5365],{"class":4564},[4023,16561,16563,16565,16567],{"class":4025,"line":16562},128,[4023,16564,12709],{"class":4910},[4023,16566,12712],{"class":4580},[4023,16568,5365],{"class":4564},[4023,16570,16572,16574,16576],{"class":4025,"line":16571},129,[4023,16573,12719],{"class":4910},[4023,16575,12712],{"class":4580},[4023,16577,5365],{"class":4564},[4023,16579,16581,16583,16585],{"class":4025,"line":16580},130,[4023,16582,12699],{"class":4910},[4023,16584,16540],{"class":4580},[4023,16586,5365],{"class":4564},[4023,16588,16590],{"class":4025,"line":16589},131,[4023,16591,7986],{"class":4564},[4023,16593,16595,16598],{"class":4025,"line":16594},132,[4023,16596,16597],{"class":4910},"    loadingText:",[4023,16599,5446],{"class":4564},[4023,16601,16603,16606,16608],{"class":4025,"line":16602},133,[4023,16604,16605],{"class":4910},"        marginTop:",[4023,16607,14555],{"class":4601},[4023,16609,5365],{"class":4564},[4023,16611,16613,16615,16617],{"class":4025,"line":16612},134,[4023,16614,14552],{"class":4910},[4023,16616,15058],{"class":4601},[4023,16618,5365],{"class":4564},[4023,16620,16622,16624,16626],{"class":4025,"line":16621},135,[4023,16623,14543],{"class":4910},[4023,16625,15067],{"class":4580},[4023,16627,5365],{"class":4564},[4023,16629,16631],{"class":4025,"line":16630},136,[4023,16632,7986],{"class":4564},[4023,16634,16636,16639],{"class":4025,"line":16635},137,[4023,16637,16638],{"class":4910},"    searchContainer:",[4023,16640,5446],{"class":4564},[4023,16642,16644,16646,16648],{"class":4025,"line":16643},138,[4023,16645,14507],{"class":4910},[4023,16647,6217],{"class":4601},[4023,16649,5365],{"class":4564},[4023,16651,16653,16656,16658],{"class":4025,"line":16652},139,[4023,16654,16655],{"class":4910},"        paddingTop:",[4023,16657,14555],{"class":4601},[4023,16659,5365],{"class":4564},[4023,16661,16663,16666,16669],{"class":4025,"line":16662},140,[4023,16664,16665],{"class":4910},"        paddingBottom:",[4023,16667,16668],{"class":4601}," 4",[4023,16670,5365],{"class":4564},[4023,16672,16674],{"class":4025,"line":16673},141,[4023,16675,7986],{"class":4564},[4023,16677,16679,16682],{"class":4025,"line":16678},142,[4023,16680,16681],{"class":4910},"    searchInput:",[4023,16683,5446],{"class":4564},[4023,16685,16687,16689,16691],{"class":4025,"line":16686},143,[4023,16688,12699],{"class":4910},[4023,16690,12750],{"class":4580},[4023,16692,5365],{"class":4564},[4023,16694,16696,16699,16701],{"class":4025,"line":16695},144,[4023,16697,16698],{"class":4910},"        borderWidth:",[4023,16700,12672],{"class":4601},[4023,16702,5365],{"class":4564},[4023,16704,16706,16709,16712],{"class":4025,"line":16705},145,[4023,16707,16708],{"class":4910},"        borderColor:",[4023,16710,16711],{"class":4580}," '#e2e8f0'",[4023,16713,5365],{"class":4564},[4023,16715,16717,16719,16721],{"class":4025,"line":16716},146,[4023,16718,15135],{"class":4910},[4023,16720,5909],{"class":4601},[4023,16722,5365],{"class":4564},[4023,16724,16726,16728,16730],{"class":4025,"line":16725},147,[4023,16727,14507],{"class":4910},[4023,16729,15058],{"class":4601},[4023,16731,5365],{"class":4564},[4023,16733,16735,16737,16739],{"class":4025,"line":16734},148,[4023,16736,14497],{"class":4910},[4023,16738,5909],{"class":4601},[4023,16740,5365],{"class":4564},[4023,16742,16744,16746,16748],{"class":4025,"line":16743},149,[4023,16745,14552],{"class":4910},[4023,16747,15058],{"class":4601},[4023,16749,5365],{"class":4564},[4023,16751,16753,16755,16757],{"class":4025,"line":16752},150,[4023,16754,14543],{"class":4910},[4023,16756,12702],{"class":4580},[4023,16758,5365],{"class":4564},[4023,16760,16762],{"class":4025,"line":16761},151,[4023,16763,7986],{"class":4564},[4023,16765,16767,16770],{"class":4025,"line":16766},152,[4023,16768,16769],{"class":4910},"    listContainer:",[4023,16771,5446],{"class":4564},[4023,16773,16775,16777,16779],{"class":4025,"line":16774},153,[4023,16776,14954],{"class":4910},[4023,16778,6217],{"class":4601},[4023,16780,5365],{"class":4564},[4023,16782,16784],{"class":4025,"line":16783},154,[4023,16785,7986],{"class":4564},[4023,16787,16789,16792],{"class":4025,"line":16788},155,[4023,16790,16791],{"class":4910},"    card:",[4023,16793,5446],{"class":4564},[4023,16795,16797,16800,16803],{"class":4025,"line":16796},156,[4023,16798,16799],{"class":4910},"        flexDirection:",[4023,16801,16802],{"class":4580}," 'row'",[4023,16804,5365],{"class":4564},[4023,16806,16808,16810,16812],{"class":4025,"line":16807},157,[4023,16809,12719],{"class":4910},[4023,16811,12712],{"class":4580},[4023,16813,5365],{"class":4564},[4023,16815,16817,16819,16821],{"class":4025,"line":16816},158,[4023,16818,12699],{"class":4910},[4023,16820,12750],{"class":4580},[4023,16822,5365],{"class":4564},[4023,16824,16826,16828,16830],{"class":4025,"line":16825},159,[4023,16827,14954],{"class":4910},[4023,16829,6217],{"class":4601},[4023,16831,5365],{"class":4564},[4023,16833,16835,16837,16839],{"class":4025,"line":16834},160,[4023,16836,15135],{"class":4910},[4023,16838,14555],{"class":4601},[4023,16840,5365],{"class":4564},[4023,16842,16844,16846,16848],{"class":4025,"line":16843},161,[4023,16845,14984],{"class":4910},[4023,16847,14555],{"class":4601},[4023,16849,5365],{"class":4564},[4023,16851,16853,16856,16859],{"class":4025,"line":16852},162,[4023,16854,16855],{"class":4910},"        shadowColor:",[4023,16857,16858],{"class":4580}," '#000'",[4023,16860,5365],{"class":4564},[4023,16862,16864,16867,16869,16872,16875,16877,16880,16882],{"class":4025,"line":16863},163,[4023,16865,16866],{"class":4910},"        shadowOffset:",[4023,16868,4907],{"class":4564},[4023,16870,16871],{"class":4910},"width:",[4023,16873,16874],{"class":4601}," 0",[4023,16876,3928],{"class":4564},[4023,16878,16879],{"class":4910},"height:",[4023,16881,12672],{"class":4601},[4023,16883,4605],{"class":4564},[4023,16885,16887,16890,16893],{"class":4025,"line":16886},164,[4023,16888,16889],{"class":4910},"        shadowOpacity:",[4023,16891,16892],{"class":4601}," 0.05",[4023,16894,5365],{"class":4564},[4023,16896,16898,16901,16904],{"class":4025,"line":16897},165,[4023,16899,16900],{"class":4910},"        shadowRadius:",[4023,16902,16903],{"class":4601}," 3",[4023,16905,5365],{"class":4564},[4023,16907,16909,16912,16915],{"class":4025,"line":16908},166,[4023,16910,16911],{"class":4910},"        elevation:",[4023,16913,16914],{"class":4601}," 2",[4023,16916,5365],{"class":4564},[4023,16918,16920],{"class":4025,"line":16919},167,[4023,16921,7986],{"class":4564},[4023,16923,16925,16928],{"class":4025,"line":16924},168,[4023,16926,16927],{"class":4910},"    codeBadge:",[4023,16929,5446],{"class":4564},[4023,16931,16933,16935,16938],{"class":4025,"line":16932},169,[4023,16934,12699],{"class":4910},[4023,16936,16937],{"class":4580}," '#eff6ff'",[4023,16939,5365],{"class":4564},[4023,16941,16943,16945,16947],{"class":4025,"line":16942},170,[4023,16944,14497],{"class":4910},[4023,16946,6342],{"class":4601},[4023,16948,5365],{"class":4564},[4023,16950,16952,16954,16956],{"class":4025,"line":16951},171,[4023,16953,14507],{"class":4910},[4023,16955,14555],{"class":4601},[4023,16957,5365],{"class":4564},[4023,16959,16961,16963,16965],{"class":4025,"line":16960},172,[4023,16962,15135],{"class":4910},[4023,16964,6342],{"class":4601},[4023,16966,5365],{"class":4564},[4023,16968,16970,16973,16975],{"class":4025,"line":16969},173,[4023,16971,16972],{"class":4910},"        marginRight:",[4023,16974,14555],{"class":4601},[4023,16976,5365],{"class":4564},[4023,16978,16980],{"class":4025,"line":16979},174,[4023,16981,7986],{"class":4564},[4023,16983,16985,16988],{"class":4025,"line":16984},175,[4023,16986,16987],{"class":4910},"    codeText:",[4023,16989,5446],{"class":4564},[4023,16991,16993,16995,16997],{"class":4025,"line":16992},176,[4023,16994,14552],{"class":4910},[4023,16996,6217],{"class":4601},[4023,16998,5365],{"class":4564},[4023,17000,17002,17004,17007],{"class":4025,"line":17001},177,[4023,17003,14562],{"class":4910},[4023,17005,17006],{"class":4580}," '800'",[4023,17008,5365],{"class":4564},[4023,17010,17012,17014,17017],{"class":4025,"line":17011},178,[4023,17013,14543],{"class":4910},[4023,17015,17016],{"class":4580}," '#1d4ed8'",[4023,17018,5365],{"class":4564},[4023,17020,17022],{"class":4025,"line":17021},179,[4023,17023,7986],{"class":4564},[4023,17025,17027,17030],{"class":4025,"line":17026},180,[4023,17028,17029],{"class":4910},"    infoContainer:",[4023,17031,5446],{"class":4564},[4023,17033,17035,17037,17039],{"class":4025,"line":17034},181,[4023,17036,14929],{"class":4910},[4023,17038,12672],{"class":4601},[4023,17040,5365],{"class":4564},[4023,17042,17044],{"class":4025,"line":17043},182,[4023,17045,7986],{"class":4564},[4023,17047,17049,17052],{"class":4025,"line":17048},183,[4023,17050,17051],{"class":4910},"    currencyName:",[4023,17053,5446],{"class":4564},[4023,17055,17057,17059,17061],{"class":4025,"line":17056},184,[4023,17058,14552],{"class":4910},[4023,17060,15165],{"class":4601},[4023,17062,5365],{"class":4564},[4023,17064,17066,17068,17070],{"class":4025,"line":17065},185,[4023,17067,14562],{"class":4910},[4023,17069,15174],{"class":4580},[4023,17071,5365],{"class":4564},[4023,17073,17075,17077,17079],{"class":4025,"line":17074},186,[4023,17076,14543],{"class":4910},[4023,17078,12702],{"class":4580},[4023,17080,5365],{"class":4564},[4023,17082,17084],{"class":4025,"line":17083},187,[4023,17085,7986],{"class":4564},[4023,17087,17089,17092],{"class":4025,"line":17088},188,[4023,17090,17091],{"class":4910},"    dateText:",[4023,17093,5446],{"class":4564},[4023,17095,17097,17099,17101],{"class":4025,"line":17096},189,[4023,17098,14552],{"class":4910},[4023,17100,14555],{"class":4601},[4023,17102,5365],{"class":4564},[4023,17104,17106,17108,17111],{"class":4025,"line":17105},190,[4023,17107,14543],{"class":4910},[4023,17109,17110],{"class":4580}," '#94a3b8'",[4023,17112,5365],{"class":4564},[4023,17114,17116,17118,17120],{"class":4025,"line":17115},191,[4023,17117,16605],{"class":4910},[4023,17119,16914],{"class":4601},[4023,17121,5365],{"class":4564},[4023,17123,17125],{"class":4025,"line":17124},192,[4023,17126,7986],{"class":4564},[4023,17128,17130,17133],{"class":4025,"line":17129},193,[4023,17131,17132],{"class":4910},"    rateValue:",[4023,17134,5446],{"class":4564},[4023,17136,17138,17140,17142],{"class":4025,"line":17137},194,[4023,17139,14552],{"class":4910},[4023,17141,6245],{"class":4601},[4023,17143,5365],{"class":4564},[4023,17145,17147,17149,17151],{"class":4025,"line":17146},195,[4023,17148,14562],{"class":4910},[4023,17150,17006],{"class":4580},[4023,17152,5365],{"class":4564},[4023,17154,17156,17158,17161],{"class":4025,"line":17155},196,[4023,17157,14543],{"class":4910},[4023,17159,17160],{"class":4580}," '#047857'",[4023,17162,5365],{"class":4564},[4023,17164,17166],{"class":4025,"line":17165},197,[4023,17167,7986],{"class":4564},[4023,17169,17171],{"class":4025,"line":17170},198,[4023,17172,7991],{"class":4564},[3774,17174,17176,17177],{"id":17175},"крок-5-збирання-кореневого-компонента-apptsx","Крок 5. Збирання кореневого компонента ",[3799,17178,13684],{},[4014,17180,17182],{"className":4877,"code":17181,"language":4879,"meta":4019,"style":4019},"\u002F\u002F App.tsx\nimport React from 'react'\nimport { SafeAreaView, View, Text, StyleSheet, StatusBar } from 'react-native'\nimport { OfflineBanner } from '.\u002Fsrc\u002Fcomponents\u002FOfflineBanner'\nimport { CurrencyRatesScreen } from '.\u002Fsrc\u002Fscreens\u002FCurrencyRatesScreen'\n\nexport default function App() {\n    return (\n        \u003CSafeAreaView style={styles.safeArea}>\n            \u003CStatusBar barStyle=\"dark-content\" backgroundColor=\"#ffffff\" \u002F>\n            \u003COfflineBanner \u002F>\n            \u003CView style={styles.header}>\n                \u003CText style={styles.headerTitle}>Курси валют НБУ\u003C\u002FText>\n                \u003CText style={styles.headerSubtitle}>Нормалізоване сховище та авторесинхронізація\u003C\u002FText>\n            \u003C\u002FView>\n            \u003CCurrencyRatesScreen \u002F>\n        \u003C\u002FSafeAreaView>\n    )\n}\n\nconst styles = StyleSheet.create({\n    safeArea: {\n        flex: 1,\n        backgroundColor: '#ffffff',\n    },\n    header: {\n        padding: 16,\n        borderBottomWidth: 1,\n        borderBottomColor: '#e2e8f0',\n        backgroundColor: '#ffffff',\n    },\n    headerTitle: {\n        fontSize: 22,\n        fontWeight: '800',\n        color: '#0f172a',\n    },\n    headerSubtitle: {\n        fontSize: 13,\n        color: '#64748b',\n        marginTop: 2,\n    },\n})\n",[3799,17183,17184,17189,17199,17231,17246,17262,17266,17280,17286,17301,17325,17333,17348,17377,17410,17418,17426,17434,17438,17442,17446,17462,17469,17477,17485,17489,17496,17504,17513,17522,17530,17534,17541,17550,17558,17566,17570,17577,17586,17594,17602,17606],{"__ignoreMap":4019},[4023,17185,17186],{"class":4025,"line":4026},[4023,17187,17188],{"class":4965},"\u002F\u002F App.tsx\n",[4023,17190,17191,17193,17195,17197],{"class":4025,"line":4032},[4023,17192,5731],{"class":4886},[4023,17194,5734],{"class":4910},[4023,17196,7697],{"class":4886},[4023,17198,5749],{"class":4580},[4023,17200,17201,17203,17205,17208,17210,17212,17214,17216,17218,17220,17222,17225,17227,17229],{"class":4025,"line":4038},[4023,17202,5731],{"class":4886},[4023,17204,4907],{"class":4564},[4023,17206,17207],{"class":4910},"SafeAreaView",[4023,17209,3928],{"class":4564},[4023,17211,5758],{"class":4910},[4023,17213,3928],{"class":4564},[4023,17215,5763],{"class":4910},[4023,17217,3928],{"class":4564},[4023,17219,12265],{"class":4910},[4023,17221,3928],{"class":4564},[4023,17223,17224],{"class":4910},"StatusBar",[4023,17226,5743],{"class":4564},[4023,17228,5746],{"class":4886},[4023,17230,5775],{"class":4580},[4023,17232,17233,17235,17237,17239,17241,17243],{"class":4025,"line":4044},[4023,17234,5731],{"class":4886},[4023,17236,4907],{"class":4564},[4023,17238,13581],{"class":4910},[4023,17240,5743],{"class":4564},[4023,17242,5746],{"class":4886},[4023,17244,17245],{"class":4580}," '.\u002Fsrc\u002Fcomponents\u002FOfflineBanner'\n",[4023,17247,17248,17250,17252,17255,17257,17259],{"class":4025,"line":4050},[4023,17249,5731],{"class":4886},[4023,17251,4907],{"class":4564},[4023,17253,17254],{"class":4910},"CurrencyRatesScreen",[4023,17256,5743],{"class":4564},[4023,17258,5746],{"class":4886},[4023,17260,17261],{"class":4580}," '.\u002Fsrc\u002Fscreens\u002FCurrencyRatesScreen'\n",[4023,17263,17264],{"class":4025,"line":4056},[4023,17265,4060],{"emptyLinePlaceholder":4059},[4023,17267,17268,17270,17273,17275,17278],{"class":4025,"line":4063},[4023,17269,4887],{"class":4886},[4023,17271,17272],{"class":4886}," default",[4023,17274,4983],{"class":4890},[4023,17276,17277],{"class":4986}," App",[4023,17279,5867],{"class":4564},[4023,17281,17282,17284],{"class":4025,"line":4069},[4023,17283,5144],{"class":4886},[4023,17285,6189],{"class":4564},[4023,17287,17288,17290,17292,17294,17296,17299],{"class":4025,"line":4075},[4023,17289,6195],{"class":4564},[4023,17291,17207],{"class":4910},[4023,17293,6201],{"class":4910},[4023,17295,14403],{"class":4564},[4023,17297,17298],{"class":4910},"safeArea",[4023,17300,14408],{"class":4564},[4023,17302,17303,17305,17307,17310,17312,17315,17318,17320,17323],{"class":4025,"line":4081},[4023,17304,6229],{"class":4564},[4023,17306,17224],{"class":4910},[4023,17308,17309],{"class":4910}," barStyle",[4023,17311,6204],{"class":4564},[4023,17313,17314],{"class":4580},"\"dark-content\"",[4023,17316,17317],{"class":4910}," backgroundColor",[4023,17319,6204],{"class":4564},[4023,17321,17322],{"class":4580},"\"#ffffff\"",[4023,17324,6435],{"class":4564},[4023,17326,17327,17329,17331],{"class":4025,"line":4087},[4023,17328,6229],{"class":4564},[4023,17330,13581],{"class":4910},[4023,17332,6435],{"class":4564},[4023,17334,17335,17337,17339,17341,17343,17346],{"class":4025,"line":4093},[4023,17336,6229],{"class":4564},[4023,17338,5758],{"class":4910},[4023,17340,6201],{"class":4910},[4023,17342,14403],{"class":4564},[4023,17344,17345],{"class":4910},"header",[4023,17347,14408],{"class":4564},[4023,17349,17350,17352,17354,17356,17358,17361,17363,17366,17369,17371,17373,17375],{"class":4025,"line":4098},[4023,17351,6311],{"class":4564},[4023,17353,5763],{"class":4910},[4023,17355,6201],{"class":4910},[4023,17357,14403],{"class":4564},[4023,17359,17360],{"class":4910},"headerTitle",[4023,17362,14781],{"class":4564},[4023,17364,17365],{"class":4910},"Курси",[4023,17367,17368],{"class":4910}," валют",[4023,17370,16103],{"class":5032},[4023,17372,6279],{"class":4564},[4023,17374,5763],{"class":4910},[4023,17376,4940],{"class":4564},[4023,17378,17379,17381,17383,17385,17387,17390,17392,17395,17398,17401,17404,17406,17408],{"class":4025,"line":4104},[4023,17380,6311],{"class":4564},[4023,17382,5763],{"class":4910},[4023,17384,6201],{"class":4910},[4023,17386,14403],{"class":4564},[4023,17388,17389],{"class":4910},"headerSubtitle",[4023,17391,14781],{"class":4564},[4023,17393,17394],{"class":4910},"Нормалізоване",[4023,17396,17397],{"class":4910}," сховище",[4023,17399,17400],{"class":4910}," та",[4023,17402,17403],{"class":4910}," авторесинхронізація",[4023,17405,6279],{"class":4564},[4023,17407,5763],{"class":4910},[4023,17409,4940],{"class":4564},[4023,17411,17412,17414,17416],{"class":4025,"line":4110},[4023,17413,14878],{"class":4564},[4023,17415,5758],{"class":4910},[4023,17417,4940],{"class":4564},[4023,17419,17420,17422,17424],{"class":4025,"line":4116},[4023,17421,6229],{"class":4564},[4023,17423,17254],{"class":4910},[4023,17425,6435],{"class":4564},[4023,17427,17428,17430,17432],{"class":4025,"line":4121},[4023,17429,6456],{"class":4564},[4023,17431,17207],{"class":4910},[4023,17433,4940],{"class":4564},[4023,17435,17436],{"class":4025,"line":4126},[4023,17437,6465],{"class":4564},[4023,17439,17440],{"class":4025,"line":4132},[4023,17441,4090],{"class":4564},[4023,17443,17444],{"class":4025,"line":4138},[4023,17445,4060],{"emptyLinePlaceholder":4059},[4023,17447,17448,17450,17452,17454,17456,17458,17460],{"class":4025,"line":4144},[4023,17449,5076],{"class":4890},[4023,17451,12649],{"class":5032},[4023,17453,5403],{"class":4564},[4023,17455,12265],{"class":4910},[4023,17457,3836],{"class":4564},[4023,17459,7889],{"class":4986},[4023,17461,7892],{"class":4564},[4023,17463,17464,17467],{"class":4025,"line":4150},[4023,17465,17466],{"class":4910},"    safeArea:",[4023,17468,5446],{"class":4564},[4023,17470,17471,17473,17475],{"class":4025,"line":4156},[4023,17472,14929],{"class":4910},[4023,17474,12672],{"class":4601},[4023,17476,5365],{"class":4564},[4023,17478,17479,17481,17483],{"class":4025,"line":4162},[4023,17480,12699],{"class":4910},[4023,17482,12750],{"class":4580},[4023,17484,5365],{"class":4564},[4023,17486,17487],{"class":4025,"line":4167},[4023,17488,7986],{"class":4564},[4023,17490,17491,17494],{"class":4025,"line":4173},[4023,17492,17493],{"class":4910},"    header:",[4023,17495,5446],{"class":4564},[4023,17497,17498,17500,17502],{"class":4025,"line":4179},[4023,17499,14954],{"class":4910},[4023,17501,6217],{"class":4601},[4023,17503,5365],{"class":4564},[4023,17505,17506,17509,17511],{"class":4025,"line":4184},[4023,17507,17508],{"class":4910},"        borderBottomWidth:",[4023,17510,12672],{"class":4601},[4023,17512,5365],{"class":4564},[4023,17514,17515,17518,17520],{"class":4025,"line":4189},[4023,17516,17517],{"class":4910},"        borderBottomColor:",[4023,17519,16711],{"class":4580},[4023,17521,5365],{"class":4564},[4023,17523,17524,17526,17528],{"class":4025,"line":4195},[4023,17525,12699],{"class":4910},[4023,17527,12750],{"class":4580},[4023,17529,5365],{"class":4564},[4023,17531,17532],{"class":4025,"line":4201},[4023,17533,7986],{"class":4564},[4023,17535,17536,17539],{"class":4025,"line":4207},[4023,17537,17538],{"class":4910},"    headerTitle:",[4023,17540,5446],{"class":4564},[4023,17542,17543,17545,17548],{"class":4025,"line":4213},[4023,17544,14552],{"class":4910},[4023,17546,17547],{"class":4601}," 22",[4023,17549,5365],{"class":4564},[4023,17551,17552,17554,17556],{"class":4025,"line":4219},[4023,17553,14562],{"class":4910},[4023,17555,17006],{"class":4580},[4023,17557,5365],{"class":4564},[4023,17559,17560,17562,17564],{"class":4025,"line":4225},[4023,17561,14543],{"class":4910},[4023,17563,12702],{"class":4580},[4023,17565,5365],{"class":4564},[4023,17567,17568],{"class":4025,"line":4230},[4023,17569,7986],{"class":4564},[4023,17571,17572,17575],{"class":4025,"line":4235},[4023,17573,17574],{"class":4910},"    headerSubtitle:",[4023,17576,5446],{"class":4564},[4023,17578,17579,17581,17584],{"class":4025,"line":4241},[4023,17580,14552],{"class":4910},[4023,17582,17583],{"class":4601}," 13",[4023,17585,5365],{"class":4564},[4023,17587,17588,17590,17592],{"class":4025,"line":4247},[4023,17589,14543],{"class":4910},[4023,17591,15067],{"class":4580},[4023,17593,5365],{"class":4564},[4023,17595,17596,17598,17600],{"class":4025,"line":4253},[4023,17597,16605],{"class":4910},[4023,17599,16914],{"class":4601},[4023,17601,5365],{"class":4564},[4023,17603,17604],{"class":4025,"line":4259},[4023,17605,7986],{"class":4564},[4023,17607,17608],{"class":4025,"line":4265},[4023,17609,7991],{"class":4564},[3964,17611],{},[3738,17613,17615],{"id":17614},"інтеграція-в-наскрізний-проєкт-nomad-нормалізований-tripsprovider","Інтеграція в наскрізний проєкт Nomad: Нормалізований TripsProvider",[3743,17617,17618,17619,17621,17622,17625,17626,17628],{},"У щоденнику подорожей ",[3750,17620,3951],{}," ми замінюємо статичний стан списку поїздок на динамічне завантаження з мок-сервера ",[3799,17623,17624],{},"json-server",", зберігаємо поїздки в нормалізованому словнику ",[3799,17627,3824],{},", додаємо відстеження мережі та обробку життєвого циклу.",[3774,17630,17632,17633],{"id":17631},"_1-конфігурація-мок-сервера-json-server","1. Конфігурація мок-сервера ",[3799,17634,17624],{},[3743,17636,17637,17638,17641,17642,4674],{},"У корені репозиторію Nomad створюємо файл даних ",[3799,17639,17640],{},"db.json"," та додаємо команду запуску з прив'язкою до всіх мережевих інтерфейсів (",[3799,17643,17644],{},"--host 0.0.0.0",[4014,17646,17648],{"className":4555,"code":17647,"filename":17640,"language":4557,"meta":4019,"style":4019},"{\n    \"trips\": [\n        {\n            \"id\": \"1\",\n            \"title\": \"Карпати: Сходження на Говерлу\",\n            \"dateLabel\": \"12–15 травня 2026\",\n            \"description\": \"Триденний похід Чорногірським хребтом з ночівлею в колибах.\",\n            \"coverUri\": \"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1464822759023-fed622ff2c3b?w=800&q=80\",\n            \"region\": \"Карпати\"\n        },\n        {\n            \"id\": \"2\",\n            \"title\": \"Вікенд у старому Львові\",\n            \"dateLabel\": \"20–22 червня 2026\",\n            \"description\": \"Кавові мануфактури, дворики та вулична музика.\",\n            \"coverUri\": \"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555881400-74d7acaacd8b?w=800&q=80\",\n            \"region\": \"Захід\"\n        }\n    ]\n}\n",[3799,17649,17650,17655,17663,17668,17680,17692,17704,17716,17728,17738,17742,17746,17757,17768,17779,17790,17801,17810,17814,17818],{"__ignoreMap":4019},[4023,17651,17652],{"class":4025,"line":4026},[4023,17653,17654],{"class":4564},"{\n",[4023,17656,17657,17660],{"class":4025,"line":4032},[4023,17658,17659],{"class":4573},"    \"trips\"",[4023,17661,17662],{"class":4564},": [\n",[4023,17664,17665],{"class":4025,"line":4038},[4023,17666,17667],{"class":4564},"        {\n",[4023,17669,17670,17673,17675,17678],{"class":4025,"line":4044},[4023,17671,17672],{"class":4573},"            \"id\"",[4023,17674,4577],{"class":4564},[4023,17676,17677],{"class":4580},"\"1\"",[4023,17679,5365],{"class":4564},[4023,17681,17682,17685,17687,17690],{"class":4025,"line":4050},[4023,17683,17684],{"class":4573},"            \"title\"",[4023,17686,4577],{"class":4564},[4023,17688,17689],{"class":4580},"\"Карпати: Сходження на Говерлу\"",[4023,17691,5365],{"class":4564},[4023,17693,17694,17697,17699,17702],{"class":4025,"line":4056},[4023,17695,17696],{"class":4573},"            \"dateLabel\"",[4023,17698,4577],{"class":4564},[4023,17700,17701],{"class":4580},"\"12–15 травня 2026\"",[4023,17703,5365],{"class":4564},[4023,17705,17706,17709,17711,17714],{"class":4025,"line":4063},[4023,17707,17708],{"class":4573},"            \"description\"",[4023,17710,4577],{"class":4564},[4023,17712,17713],{"class":4580},"\"Триденний похід Чорногірським хребтом з ночівлею в колибах.\"",[4023,17715,5365],{"class":4564},[4023,17717,17718,17721,17723,17726],{"class":4025,"line":4069},[4023,17719,17720],{"class":4573},"            \"coverUri\"",[4023,17722,4577],{"class":4564},[4023,17724,17725],{"class":4580},"\"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1464822759023-fed622ff2c3b?w=800&q=80\"",[4023,17727,5365],{"class":4564},[4023,17729,17730,17733,17735],{"class":4025,"line":4075},[4023,17731,17732],{"class":4573},"            \"region\"",[4023,17734,4577],{"class":4564},[4023,17736,17737],{"class":4580},"\"Карпати\"\n",[4023,17739,17740],{"class":4025,"line":4081},[4023,17741,5497],{"class":4564},[4023,17743,17744],{"class":4025,"line":4087},[4023,17745,17667],{"class":4564},[4023,17747,17748,17750,17752,17755],{"class":4025,"line":4093},[4023,17749,17672],{"class":4573},[4023,17751,4577],{"class":4564},[4023,17753,17754],{"class":4580},"\"2\"",[4023,17756,5365],{"class":4564},[4023,17758,17759,17761,17763,17766],{"class":4025,"line":4098},[4023,17760,17684],{"class":4573},[4023,17762,4577],{"class":4564},[4023,17764,17765],{"class":4580},"\"Вікенд у старому Львові\"",[4023,17767,5365],{"class":4564},[4023,17769,17770,17772,17774,17777],{"class":4025,"line":4104},[4023,17771,17696],{"class":4573},[4023,17773,4577],{"class":4564},[4023,17775,17776],{"class":4580},"\"20–22 червня 2026\"",[4023,17778,5365],{"class":4564},[4023,17780,17781,17783,17785,17788],{"class":4025,"line":4110},[4023,17782,17708],{"class":4573},[4023,17784,4577],{"class":4564},[4023,17786,17787],{"class":4580},"\"Кавові мануфактури, дворики та вулична музика.\"",[4023,17789,5365],{"class":4564},[4023,17791,17792,17794,17796,17799],{"class":4025,"line":4116},[4023,17793,17720],{"class":4573},[4023,17795,4577],{"class":4564},[4023,17797,17798],{"class":4580},"\"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555881400-74d7acaacd8b?w=800&q=80\"",[4023,17800,5365],{"class":4564},[4023,17802,17803,17805,17807],{"class":4025,"line":4121},[4023,17804,17732],{"class":4573},[4023,17806,4577],{"class":4564},[4023,17808,17809],{"class":4580},"\"Захід\"\n",[4023,17811,17812],{"class":4025,"line":4126},[4023,17813,6971],{"class":4564},[4023,17815,17816],{"class":4025,"line":4132},[4023,17817,5941],{"class":4564},[4023,17819,17820],{"class":4025,"line":4138},[4023,17821,4090],{"class":4564},[4014,17823,17826],{"className":4555,"code":17824,"filename":17825,"language":4557,"meta":4019,"style":4019},"{\n    \"scripts\": {\n        \"api\": \"json-server --watch db.json --port 3000 --host 0.0.0.0\"\n    }\n}\n","package.json",[3799,17827,17828,17832,17840,17850,17854],{"__ignoreMap":4019},[4023,17829,17830],{"class":4025,"line":4026},[4023,17831,17654],{"class":4564},[4023,17833,17834,17837],{"class":4025,"line":4032},[4023,17835,17836],{"class":4573},"    \"scripts\"",[4023,17838,17839],{"class":4564},": {\n",[4023,17841,17842,17845,17847],{"class":4025,"line":4038},[4023,17843,17844],{"class":4573},"        \"api\"",[4023,17846,4577],{"class":4564},[4023,17848,17849],{"class":4580},"\"json-server --watch db.json --port 3000 --host 0.0.0.0\"\n",[4023,17851,17852],{"class":4025,"line":4044},[4023,17853,4147],{"class":4564},[4023,17855,17856],{"class":4025,"line":4050},[4023,17857,4090],{"class":4564},[3774,17859,17861],{"id":17860},"_2-створення-api-клієнта-поїздок","2. Створення API-клієнта поїздок",[4014,17863,17865],{"className":4877,"code":17864,"language":4879,"meta":4019,"style":4019},"\u002F\u002F src\u002Fapi\u002FtripsApi.ts\nimport { apiClient } from '.\u002Fclient'\nimport { AppError } from '.\u002FAppError'\n\nexport interface TripDto {\n    id: string\n    title: string\n    dateLabel: string\n    description: string\n    coverUri: string\n    region: string\n}\n\nexport async function getTrips(): Promise\u003CTripDto[]> {\n    try {\n        const response = await apiClient.get\u003CTripDto[]>('\u002Ftrips')\n        return response.data\n    } catch (error) {\n        throw AppError.from(error)\n    }\n}\n\nexport async function getTripById(id: string): Promise\u003CTripDto> {\n    try {\n        const response = await apiClient.get\u003CTripDto>(`\u002Ftrips\u002F${id}`)\n        return response.data\n    } catch (error) {\n        throw AppError.from(error)\n    }\n}\n",[3799,17866,17867,17872,17887,17901,17905,17916,17924,17932,17941,17950,17959,17968,17972,17976,17998,18004,18031,18041,18053,18069,18073,18077,18081,18110,18116,18151,18161,18173,18189,18193],{"__ignoreMap":4019},[4023,17868,17869],{"class":4025,"line":4026},[4023,17870,17871],{"class":4965},"\u002F\u002F src\u002Fapi\u002FtripsApi.ts\n",[4023,17873,17874,17876,17878,17880,17882,17884],{"class":4025,"line":4032},[4023,17875,5731],{"class":4886},[4023,17877,4907],{"class":4564},[4023,17879,8230],{"class":4910},[4023,17881,5743],{"class":4564},[4023,17883,5746],{"class":4886},[4023,17885,17886],{"class":4580}," '.\u002Fclient'\n",[4023,17888,17889,17891,17893,17895,17897,17899],{"class":4025,"line":4038},[4023,17890,5731],{"class":4886},[4023,17892,4907],{"class":4564},[4023,17894,3897],{"class":4910},[4023,17896,5743],{"class":4564},[4023,17898,5746],{"class":4886},[4023,17900,13908],{"class":4580},[4023,17902,17903],{"class":4025,"line":4044},[4023,17904,4060],{"emptyLinePlaceholder":4059},[4023,17906,17907,17909,17911,17914],{"class":4025,"line":4050},[4023,17908,4887],{"class":4886},[4023,17910,4891],{"class":4890},[4023,17912,17913],{"class":4894}," TripDto",[4023,17915,5446],{"class":4564},[4023,17917,17918,17920,17922],{"class":4025,"line":4056},[4023,17919,5358],{"class":4910},[4023,17921,4577],{"class":4564},[4023,17923,5828],{"class":4894},[4023,17925,17926,17928,17930],{"class":4025,"line":4063},[4023,17927,5833],{"class":4910},[4023,17929,4577],{"class":4564},[4023,17931,5828],{"class":4894},[4023,17933,17934,17937,17939],{"class":4025,"line":4069},[4023,17935,17936],{"class":4910},"    dateLabel",[4023,17938,4577],{"class":4564},[4023,17940,5828],{"class":4894},[4023,17942,17943,17946,17948],{"class":4025,"line":4075},[4023,17944,17945],{"class":4910},"    description",[4023,17947,4577],{"class":4564},[4023,17949,5828],{"class":4894},[4023,17951,17952,17955,17957],{"class":4025,"line":4081},[4023,17953,17954],{"class":4910},"    coverUri",[4023,17956,4577],{"class":4564},[4023,17958,5828],{"class":4894},[4023,17960,17961,17964,17966],{"class":4025,"line":4087},[4023,17962,17963],{"class":4910},"    region",[4023,17965,4577],{"class":4564},[4023,17967,5828],{"class":4894},[4023,17969,17970],{"class":4025,"line":4093},[4023,17971,4090],{"class":4564},[4023,17973,17974],{"class":4025,"line":4098},[4023,17975,4060],{"emptyLinePlaceholder":4059},[4023,17977,17978,17980,17982,17984,17987,17989,17991,17993,17996],{"class":4025,"line":4104},[4023,17979,4887],{"class":4886},[4023,17981,9585],{"class":4890},[4023,17983,4983],{"class":4890},[4023,17985,17986],{"class":4986}," getTrips",[4023,17988,7715],{"class":4564},[4023,17990,6687],{"class":4894},[4023,17992,4898],{"class":4564},[4023,17994,17995],{"class":4894},"TripDto",[4023,17997,14038],{"class":4564},[4023,17999,18000,18002],{"class":4025,"line":4110},[4023,18001,6785],{"class":4886},[4023,18003,5446],{"class":4564},[4023,18005,18006,18008,18010,18012,18014,18016,18018,18020,18022,18024,18026,18029],{"class":4025,"line":4116},[4023,18007,6792],{"class":4890},[4023,18009,6795],{"class":5032},[4023,18011,5403],{"class":4564},[4023,18013,6800],{"class":4886},[4023,18015,7876],{"class":4910},[4023,18017,3836],{"class":4564},[4023,18019,6995],{"class":4986},[4023,18021,4898],{"class":4564},[4023,18023,17995],{"class":4894},[4023,18025,14067],{"class":4564},[4023,18027,18028],{"class":4580},"'\u002Ftrips'",[4023,18030,5131],{"class":4564},[4023,18032,18033,18035,18037,18039],{"class":4025,"line":4121},[4023,18034,7057],{"class":4886},[4023,18036,6795],{"class":4910},[4023,18038,3836],{"class":4564},[4023,18040,14087],{"class":4910},[4023,18042,18043,18045,18047,18049,18051],{"class":4025,"line":4126},[4023,18044,7081],{"class":4564},[4023,18046,7084],{"class":4886},[4023,18048,3924],{"class":4564},[4023,18050,7089],{"class":4910},[4023,18052,5088],{"class":4564},[4023,18054,18055,18057,18059,18061,18063,18065,18067],{"class":4025,"line":4132},[4023,18056,7178],{"class":4886},[4023,18058,10237],{"class":4910},[4023,18060,3836],{"class":4564},[4023,18062,5746],{"class":4986},[4023,18064,5122],{"class":4564},[4023,18066,7089],{"class":4910},[4023,18068,5131],{"class":4564},[4023,18070,18071],{"class":4025,"line":4138},[4023,18072,4147],{"class":4564},[4023,18074,18075],{"class":4025,"line":4144},[4023,18076,4090],{"class":4564},[4023,18078,18079],{"class":4025,"line":4150},[4023,18080,4060],{"emptyLinePlaceholder":4059},[4023,18082,18083,18085,18087,18089,18092,18094,18096,18098,18100,18102,18104,18106,18108],{"class":4025,"line":4156},[4023,18084,4887],{"class":4886},[4023,18086,9585],{"class":4890},[4023,18088,4983],{"class":4890},[4023,18090,18091],{"class":4986}," getTripById",[4023,18093,5122],{"class":4564},[4023,18095,4911],{"class":4910},[4023,18097,4577],{"class":4564},[4023,18099,4916],{"class":4894},[4023,18101,5269],{"class":4564},[4023,18103,6687],{"class":4894},[4023,18105,4898],{"class":4564},[4023,18107,17995],{"class":4894},[4023,18109,5024],{"class":4564},[4023,18111,18112,18114],{"class":4025,"line":4162},[4023,18113,6785],{"class":4886},[4023,18115,5446],{"class":4564},[4023,18117,18118,18120,18122,18124,18126,18128,18130,18132,18134,18136,18138,18141,18143,18145,18147,18149],{"class":4025,"line":4167},[4023,18119,6792],{"class":4890},[4023,18121,6795],{"class":5032},[4023,18123,5403],{"class":4564},[4023,18125,6800],{"class":4886},[4023,18127,7876],{"class":4910},[4023,18129,3836],{"class":4564},[4023,18131,6995],{"class":4986},[4023,18133,4898],{"class":4564},[4023,18135,17995],{"class":4894},[4023,18137,9597],{"class":4564},[4023,18139,18140],{"class":4580},"`\u002Ftrips\u002F",[4023,18142,6939],{"class":4890},[4023,18144,4911],{"class":4910},[4023,18146,6222],{"class":4890},[4023,18148,6964],{"class":4580},[4023,18150,5131],{"class":4564},[4023,18152,18153,18155,18157,18159],{"class":4025,"line":4173},[4023,18154,7057],{"class":4886},[4023,18156,6795],{"class":4910},[4023,18158,3836],{"class":4564},[4023,18160,14087],{"class":4910},[4023,18162,18163,18165,18167,18169,18171],{"class":4025,"line":4179},[4023,18164,7081],{"class":4564},[4023,18166,7084],{"class":4886},[4023,18168,3924],{"class":4564},[4023,18170,7089],{"class":4910},[4023,18172,5088],{"class":4564},[4023,18174,18175,18177,18179,18181,18183,18185,18187],{"class":4025,"line":4184},[4023,18176,7178],{"class":4886},[4023,18178,10237],{"class":4910},[4023,18180,3836],{"class":4564},[4023,18182,5746],{"class":4986},[4023,18184,5122],{"class":4564},[4023,18186,7089],{"class":4910},[4023,18188,5131],{"class":4564},[4023,18190,18191],{"class":4025,"line":4189},[4023,18192,4147],{"class":4564},[4023,18194,18195],{"class":4025,"line":4195},[4023,18196,4090],{"class":4564},[3774,18198,18200,18201,18204,18205],{"id":18199},"_3-нормалізований-tripsprovider-зі-швидким-доступом","3. Нормалізований ",[3799,18202,18203],{},"TripsProvider"," зі швидким доступом ",[3832,18206],{":inline":3834,"tex":3835},[4014,18208,18210],{"className":5722,"code":18209,"language":5724,"meta":4019,"style":4019},"\u002F\u002F src\u002Ffeatures\u002Ftrips\u002Fmodel\u002FTripsProvider.tsx\nimport React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react'\nimport { getTrips, TripDto } from '..\u002F..\u002F..\u002Fapi\u002FtripsApi'\nimport { AppError } from '..\u002F..\u002F..\u002Fapi\u002FAppError'\nimport {\n    normalizeEntities,\n    denormalizeEntities,\n    updateEntity,\n    upsertEntity,\n    NormalizedEntities,\n} from '..\u002F..\u002F..\u002Futils\u002Fnormalize'\nimport { useRefetchOnFocusAndNetwork } from '..\u002F..\u002F..\u002Fhooks\u002FuseRefetchOnFocusAndNetwork'\n\ninterface TripsContextType {\n    trips: TripDto[]\n    tripsById: Record\u003Cstring, TripDto>\n    isLoading: boolean\n    isRefreshing: boolean\n    error: AppError | null\n    getTrip: (id: string) => TripDto | undefined\n    patchTrip: (id: string, patch: Partial\u003CTripDto>) => void\n    addTrip: (trip: TripDto) => void\n    refetch: (isManual?: boolean) => Promise\u003Cvoid>\n}\n\nconst TripsContext = createContext\u003CTripsContextType | undefined>(undefined)\n\nexport const TripsProvider: React.FC\u003C{ children: React.ReactNode }> = ({ children }) => {\n    const [tripsState, setTripsState] = useState\u003CNormalizedEntities\u003CTripDto>>({\n        byId: {},\n        allIds: [],\n    })\n    const [isLoading, setIsLoading] = useState\u003Cboolean>(true)\n    const [isRefreshing, setIsRefreshing] = useState\u003Cboolean>(false)\n    const [error, setError] = useState\u003CAppError | null>(null)\n\n    const fetchTripsData = useCallback(async (isManual = false) => {\n        if (isManual) {\n            setIsRefreshing(true)\n        } else {\n            setIsLoading(true)\n        }\n        setError(null)\n\n        try {\n            const data = await getTrips()\n            \u002F\u002F Зберігаємо нормалізовану структуру\n            setTripsState(normalizeEntities(data))\n        } catch (err) {\n            setError(AppError.from(err))\n        } finally {\n            setIsLoading(false)\n            setIsRefreshing(false)\n        }\n    }, [])\n\n    useEffect(() => {\n        fetchTripsData()\n    }, [fetchTripsData])\n\n    \u002F\u002F Автоматична ресинхронізація\n    useRefetchOnFocusAndNetwork(() => {\n        fetchTripsData(true)\n    })\n\n    \u002F\u002F Швидка вибірка O(1) за ID\n    const getTrip = useCallback((id: string) => tripsState.byId[id], [tripsState.byId])\n\n    \u002F\u002F Миттєве оновлення O(1)\n    const patchTrip = useCallback((id: string, patch: Partial\u003CTripDto>) => {\n        setTripsState((prev) => updateEntity(prev, id, patch))\n    }, [])\n\n    \u002F\u002F Додавання нової поїздки O(1)\n    const addTrip = useCallback((trip: TripDto) => {\n        setTripsState((prev) => upsertEntity(prev, trip))\n    }, [])\n\n    \u002F\u002F Мемоізований список для компонентів-списків\n    const tripsArray = useMemo(() => denormalizeEntities(tripsState), [tripsState])\n\n    return (\n        \u003CTripsContext.Provider\n            value={{\n                trips: tripsArray,\n                tripsById: tripsState.byId,\n                isLoading,\n                isRefreshing,\n                error,\n                getTrip,\n                patchTrip,\n                addTrip,\n                refetch: fetchTripsData,\n            }}\n        >\n            {children}\n        \u003C\u002FTripsContext.Provider>\n    )\n}\n\nexport function useTrips(): TripsContextType {\n    const context = useContext(TripsContext)\n    if (!context) {\n        throw new Error('useTrips must be used within a TripsProvider')\n    }\n    return context\n}\n",[3799,18211,18212,18217,18255,18275,18290,18296,18303,18310,18317,18324,18331,18340,18355,18359,18368,18379,18398,18407,18416,18429,18453,18485,18504,18530,18534,18538,18565,18569,18608,18634,18640,18646,18650,18676,18702,18732,18736,18765,18775,18785,18793,18803,18807,18817,18821,18827,18841,18846,18861,18873,18891,18899,18909,18919,18923,18927,18931,18941,18948,18957,18961,18966,18976,18986,18990,18994,18999,19044,19048,19053,19090,19119,19123,19127,19132,19157,19181,19185,19189,19194,19222,19226,19232,19239,19250,19259,19272,19279,19286,19293,19300,19307,19314,19323,19330,19335,19343,19352,19356,19360,19364,19379,19397,19408,19423,19427,19434],{"__ignoreMap":4019},[4023,18213,18214],{"class":4025,"line":4026},[4023,18215,18216],{"class":4965},"\u002F\u002F src\u002Ffeatures\u002Ftrips\u002Fmodel\u002FTripsProvider.tsx\n",[4023,18218,18219,18221,18223,18225,18228,18230,18233,18235,18237,18239,18241,18243,18245,18247,18249,18251,18253],{"class":4025,"line":4032},[4023,18220,5731],{"class":4886},[4023,18222,5734],{"class":4910},[4023,18224,5737],{"class":4564},[4023,18226,18227],{"class":4910},"createContext",[4023,18229,3928],{"class":4564},[4023,18231,18232],{"class":4910},"useContext",[4023,18234,3928],{"class":4564},[4023,18236,5740],{"class":4910},[4023,18238,3928],{"class":4564},[4023,18240,11443],{"class":4910},[4023,18242,3928],{"class":4564},[4023,18244,15217],{"class":4910},[4023,18246,3928],{"class":4564},[4023,18248,15222],{"class":4910},[4023,18250,5743],{"class":4564},[4023,18252,5746],{"class":4886},[4023,18254,5749],{"class":4580},[4023,18256,18257,18259,18261,18264,18266,18268,18270,18272],{"class":4025,"line":4038},[4023,18258,5731],{"class":4886},[4023,18260,4907],{"class":4564},[4023,18262,18263],{"class":4910},"getTrips",[4023,18265,3928],{"class":4564},[4023,18267,17995],{"class":4910},[4023,18269,5743],{"class":4564},[4023,18271,5746],{"class":4886},[4023,18273,18274],{"class":4580}," '..\u002F..\u002F..\u002Fapi\u002FtripsApi'\n",[4023,18276,18277,18279,18281,18283,18285,18287],{"class":4025,"line":4044},[4023,18278,5731],{"class":4886},[4023,18280,4907],{"class":4564},[4023,18282,3897],{"class":4910},[4023,18284,5743],{"class":4564},[4023,18286,5746],{"class":4886},[4023,18288,18289],{"class":4580}," '..\u002F..\u002F..\u002Fapi\u002FAppError'\n",[4023,18291,18292,18294],{"class":4025,"line":4050},[4023,18293,5731],{"class":4886},[4023,18295,5446],{"class":4564},[4023,18297,18298,18301],{"class":4025,"line":4056},[4023,18299,18300],{"class":4910},"    normalizeEntities",[4023,18302,5365],{"class":4564},[4023,18304,18305,18308],{"class":4025,"line":4063},[4023,18306,18307],{"class":4910},"    denormalizeEntities",[4023,18309,5365],{"class":4564},[4023,18311,18312,18315],{"class":4025,"line":4069},[4023,18313,18314],{"class":4910},"    updateEntity",[4023,18316,5365],{"class":4564},[4023,18318,18319,18322],{"class":4025,"line":4075},[4023,18320,18321],{"class":4910},"    upsertEntity",[4023,18323,5365],{"class":4564},[4023,18325,18326,18329],{"class":4025,"line":4081},[4023,18327,18328],{"class":4910},"    NormalizedEntities",[4023,18330,5365],{"class":4564},[4023,18332,18333,18335,18337],{"class":4025,"line":4087},[4023,18334,14836],{"class":4564},[4023,18336,5746],{"class":4886},[4023,18338,18339],{"class":4580}," '..\u002F..\u002F..\u002Futils\u002Fnormalize'\n",[4023,18341,18342,18344,18346,18348,18350,18352],{"class":4025,"line":4093},[4023,18343,5731],{"class":4886},[4023,18345,4907],{"class":4564},[4023,18347,15384],{"class":4910},[4023,18349,5743],{"class":4564},[4023,18351,5746],{"class":4886},[4023,18353,18354],{"class":4580}," '..\u002F..\u002F..\u002Fhooks\u002FuseRefetchOnFocusAndNetwork'\n",[4023,18356,18357],{"class":4025,"line":4098},[4023,18358,4060],{"emptyLinePlaceholder":4059},[4023,18360,18361,18363,18366],{"class":4025,"line":4104},[4023,18362,5814],{"class":4890},[4023,18364,18365],{"class":4894}," TripsContextType",[4023,18367,5446],{"class":4564},[4023,18369,18370,18373,18375,18377],{"class":4025,"line":4110},[4023,18371,18372],{"class":4910},"    trips",[4023,18374,4577],{"class":4564},[4023,18376,17995],{"class":4894},[4023,18378,4952],{"class":4564},[4023,18380,18381,18384,18386,18388,18390,18392,18394,18396],{"class":4025,"line":4116},[4023,18382,18383],{"class":4910},"    tripsById",[4023,18385,4577],{"class":4564},[4023,18387,4929],{"class":4894},[4023,18389,4898],{"class":4564},[4023,18391,4916],{"class":4894},[4023,18393,3928],{"class":4564},[4023,18395,17995],{"class":4894},[4023,18397,4940],{"class":4564},[4023,18399,18400,18403,18405],{"class":4025,"line":4121},[4023,18401,18402],{"class":4910},"    isLoading",[4023,18404,4577],{"class":4564},[4023,18406,11502],{"class":4894},[4023,18408,18409,18412,18414],{"class":4025,"line":4126},[4023,18410,18411],{"class":4910},"    isRefreshing",[4023,18413,4577],{"class":4564},[4023,18415,11502],{"class":4894},[4023,18417,18418,18420,18422,18424,18426],{"class":4025,"line":4132},[4023,18419,14664],{"class":4910},[4023,18421,4577],{"class":4564},[4023,18423,3897],{"class":4894},[4023,18425,8104],{"class":4564},[4023,18427,18428],{"class":4894},"null\n",[4023,18430,18431,18434,18436,18438,18440,18442,18444,18446,18448,18450],{"class":4025,"line":4138},[4023,18432,18433],{"class":4986},"    getTrip",[4023,18435,8036],{"class":4564},[4023,18437,4911],{"class":4910},[4023,18439,4577],{"class":4564},[4023,18441,4916],{"class":4894},[4023,18443,5243],{"class":4564},[4023,18445,5246],{"class":4890},[4023,18447,17913],{"class":4894},[4023,18449,8104],{"class":4564},[4023,18451,18452],{"class":4894},"undefined\n",[4023,18454,18455,18458,18460,18462,18464,18466,18468,18470,18472,18474,18476,18478,18481,18483],{"class":4025,"line":4144},[4023,18456,18457],{"class":4986},"    patchTrip",[4023,18459,8036],{"class":4564},[4023,18461,4911],{"class":4910},[4023,18463,4577],{"class":4564},[4023,18465,4916],{"class":4894},[4023,18467,3928],{"class":4564},[4023,18469,4797],{"class":4910},[4023,18471,4577],{"class":4564},[4023,18473,5375],{"class":4894},[4023,18475,4898],{"class":4564},[4023,18477,17995],{"class":4894},[4023,18479,18480],{"class":4564},">) ",[4023,18482,5246],{"class":4890},[4023,18484,8050],{"class":4894},[4023,18486,18487,18490,18492,18494,18496,18498,18500,18502],{"class":4025,"line":4150},[4023,18488,18489],{"class":4986},"    addTrip",[4023,18491,8036],{"class":4564},[4023,18493,6300],{"class":4910},[4023,18495,4577],{"class":4564},[4023,18497,17995],{"class":4894},[4023,18499,5243],{"class":4564},[4023,18501,5246],{"class":4890},[4023,18503,8050],{"class":4894},[4023,18505,18506,18509,18511,18514,18516,18518,18520,18522,18524,18526,18528],{"class":4025,"line":4156},[4023,18507,18508],{"class":4986},"    refetch",[4023,18510,8036],{"class":4564},[4023,18512,18513],{"class":4910},"isManual",[4023,18515,8469],{"class":4564},[4023,18517,8472],{"class":4894},[4023,18519,5243],{"class":4564},[4023,18521,5246],{"class":4890},[4023,18523,8365],{"class":4894},[4023,18525,4898],{"class":4564},[4023,18527,12948],{"class":4894},[4023,18529,4940],{"class":4564},[4023,18531,18532],{"class":4025,"line":4162},[4023,18533,4090],{"class":4564},[4023,18535,18536],{"class":4025,"line":4167},[4023,18537,4060],{"emptyLinePlaceholder":4059},[4023,18539,18540,18542,18545,18547,18549,18551,18554,18556,18559,18561,18563],{"class":4025,"line":4173},[4023,18541,5076],{"class":4890},[4023,18543,18544],{"class":5032}," TripsContext",[4023,18546,5403],{"class":4564},[4023,18548,18227],{"class":4986},[4023,18550,4898],{"class":4564},[4023,18552,18553],{"class":4894},"TripsContextType",[4023,18555,8104],{"class":4564},[4023,18557,18558],{"class":4894},"undefined",[4023,18560,9597],{"class":4564},[4023,18562,18558],{"class":4890},[4023,18564,5131],{"class":4564},[4023,18566,18567],{"class":4025,"line":4179},[4023,18568,4060],{"emptyLinePlaceholder":4059},[4023,18570,18571,18573,18575,18578,18580,18582,18584,18586,18588,18590,18592,18594,18596,18598,18600,18602,18604,18606],{"class":4025,"line":4184},[4023,18572,4887],{"class":4886},[4023,18574,7873],{"class":4890},[4023,18576,18577],{"class":4986}," TripsProvider",[4023,18579,4577],{"class":4564},[4023,18581,2021],{"class":4894},[4023,18583,3836],{"class":4564},[4023,18585,12323],{"class":4894},[4023,18587,8831],{"class":4564},[4023,18589,12328],{"class":4910},[4023,18591,4577],{"class":4564},[4023,18593,2021],{"class":4894},[4023,18595,3836],{"class":4564},[4023,18597,12337],{"class":4894},[4023,18599,12340],{"class":4564},[4023,18601,12328],{"class":4910},[4023,18603,12345],{"class":4564},[4023,18605,5246],{"class":4890},[4023,18607,5446],{"class":4564},[4023,18609,18610,18612,18614,18616,18618,18620,18622,18624,18626,18628,18630,18632],{"class":4025,"line":4189},[4023,18611,5029],{"class":4890},[4023,18613,5957],{"class":4564},[4023,18615,5960],{"class":5032},[4023,18617,3928],{"class":4564},[4023,18619,5965],{"class":5032},[4023,18621,5106],{"class":4564},[4023,18623,5740],{"class":4986},[4023,18625,4898],{"class":4564},[4023,18627,5017],{"class":4894},[4023,18629,4898],{"class":4564},[4023,18631,17995],{"class":4894},[4023,18633,15447],{"class":4564},[4023,18635,18636,18638],{"class":4025,"line":4195},[4023,18637,5460],{"class":4910},[4023,18639,15454],{"class":4564},[4023,18641,18642,18644],{"class":4025,"line":4201},[4023,18643,5676],{"class":4910},[4023,18645,15461],{"class":4564},[4023,18647,18648],{"class":4025,"line":4207},[4023,18649,8205],{"class":4564},[4023,18651,18652,18654,18656,18658,18660,18662,18664,18666,18668,18670,18672,18674],{"class":4025,"line":4213},[4023,18653,5029],{"class":4890},[4023,18655,5957],{"class":4564},[4023,18657,15503],{"class":5032},[4023,18659,3928],{"class":4564},[4023,18661,15508],{"class":5032},[4023,18663,5106],{"class":4564},[4023,18665,5740],{"class":4986},[4023,18667,4898],{"class":4564},[4023,18669,8472],{"class":4894},[4023,18671,9597],{"class":4564},[4023,18673,3834],{"class":4890},[4023,18675,5131],{"class":4564},[4023,18677,18678,18680,18682,18684,18686,18688,18690,18692,18694,18696,18698,18700],{"class":4025,"line":4219},[4023,18679,5029],{"class":4890},[4023,18681,5957],{"class":4564},[4023,18683,8567],{"class":5032},[4023,18685,3928],{"class":4564},[4023,18687,15535],{"class":5032},[4023,18689,5106],{"class":4564},[4023,18691,5740],{"class":4986},[4023,18693,4898],{"class":4564},[4023,18695,8472],{"class":4894},[4023,18697,9597],{"class":4564},[4023,18699,11382],{"class":4890},[4023,18701,5131],{"class":4564},[4023,18703,18704,18706,18708,18710,18712,18714,18716,18718,18720,18722,18724,18726,18728,18730],{"class":4025,"line":4225},[4023,18705,5029],{"class":4890},[4023,18707,5957],{"class":4564},[4023,18709,7089],{"class":5032},[4023,18711,3928],{"class":4564},[4023,18713,15562],{"class":5032},[4023,18715,5106],{"class":4564},[4023,18717,5740],{"class":4986},[4023,18719,4898],{"class":4564},[4023,18721,3897],{"class":4894},[4023,18723,8104],{"class":4564},[4023,18725,8107],{"class":4894},[4023,18727,9597],{"class":4564},[4023,18729,8107],{"class":4890},[4023,18731,5131],{"class":4564},[4023,18733,18734],{"class":4025,"line":4230},[4023,18735,4060],{"emptyLinePlaceholder":4059},[4023,18737,18738,18740,18743,18745,18747,18749,18751,18753,18755,18757,18759,18761,18763],{"class":4025,"line":4235},[4023,18739,5029],{"class":4890},[4023,18741,18742],{"class":5032}," fetchTripsData",[4023,18744,5403],{"class":4564},[4023,18746,15217],{"class":4986},[4023,18748,5122],{"class":4564},[4023,18750,6627],{"class":4890},[4023,18752,3924],{"class":4564},[4023,18754,18513],{"class":4910},[4023,18756,5403],{"class":4564},[4023,18758,11382],{"class":4890},[4023,18760,5243],{"class":4564},[4023,18762,5246],{"class":4890},[4023,18764,5446],{"class":4564},[4023,18766,18767,18769,18771,18773],{"class":4025,"line":4241},[4023,18768,6908],{"class":4886},[4023,18770,3924],{"class":4564},[4023,18772,18513],{"class":4910},[4023,18774,5088],{"class":4564},[4023,18776,18777,18779,18781,18783],{"class":4025,"line":4247},[4023,18778,15629],{"class":4986},[4023,18780,5122],{"class":4564},[4023,18782,3834],{"class":4890},[4023,18784,5131],{"class":4564},[4023,18786,18787,18789,18791],{"class":4025,"line":4253},[4023,18788,8169],{"class":4564},[4023,18790,8172],{"class":4886},[4023,18792,5446],{"class":4564},[4023,18794,18795,18797,18799,18801],{"class":4025,"line":4259},[4023,18796,15648],{"class":4986},[4023,18798,5122],{"class":4564},[4023,18800,3834],{"class":4890},[4023,18802,5131],{"class":4564},[4023,18804,18805],{"class":4025,"line":4265},[4023,18806,6971],{"class":4564},[4023,18808,18809,18811,18813,18815],{"class":4025,"line":4271},[4023,18810,15663],{"class":4986},[4023,18812,5122],{"class":4564},[4023,18814,8107],{"class":4890},[4023,18816,5131],{"class":4564},[4023,18818,18819],{"class":4025,"line":4276},[4023,18820,4060],{"emptyLinePlaceholder":4059},[4023,18822,18823,18825],{"class":4025,"line":5500},[4023,18824,8795],{"class":4886},[4023,18826,5446],{"class":4564},[4023,18828,18829,18831,18833,18835,18837,18839],{"class":4025,"line":5505},[4023,18830,6075],{"class":4890},[4023,18832,15686],{"class":5032},[4023,18834,5403],{"class":4564},[4023,18836,6800],{"class":4886},[4023,18838,17986],{"class":4986},[4023,18840,6711],{"class":4564},[4023,18842,18843],{"class":4025,"line":5510},[4023,18844,18845],{"class":4965},"            \u002F\u002F Зберігаємо нормалізовану структуру\n",[4023,18847,18848,18851,18853,18855,18857,18859],{"class":4025,"line":5515},[4023,18849,18850],{"class":4986},"            setTripsState",[4023,18852,5122],{"class":4564},[4023,18854,5784],{"class":4986},[4023,18856,5122],{"class":4564},[4023,18858,8917],{"class":4910},[4023,18860,5290],{"class":4564},[4023,18862,18863,18865,18867,18869,18871],{"class":4025,"line":5520},[4023,18864,8169],{"class":4564},[4023,18866,7084],{"class":4886},[4023,18868,3924],{"class":4564},[4023,18870,8710],{"class":4910},[4023,18872,5088],{"class":4564},[4023,18874,18875,18877,18879,18881,18883,18885,18887,18889],{"class":4025,"line":5526},[4023,18876,15731],{"class":4986},[4023,18878,5122],{"class":4564},[4023,18880,3897],{"class":4910},[4023,18882,3836],{"class":4564},[4023,18884,5746],{"class":4986},[4023,18886,5122],{"class":4564},[4023,18888,8710],{"class":4910},[4023,18890,5290],{"class":4564},[4023,18892,18893,18895,18897],{"class":4025,"line":5531},[4023,18894,8169],{"class":4564},[4023,18896,9091],{"class":4886},[4023,18898,5446],{"class":4564},[4023,18900,18901,18903,18905,18907],{"class":4025,"line":5557},[4023,18902,15648],{"class":4986},[4023,18904,5122],{"class":4564},[4023,18906,11382],{"class":4890},[4023,18908,5131],{"class":4564},[4023,18910,18911,18913,18915,18917],{"class":4025,"line":5572},[4023,18912,15629],{"class":4986},[4023,18914,5122],{"class":4564},[4023,18916,11382],{"class":4890},[4023,18918,5131],{"class":4564},[4023,18920,18921],{"class":4025,"line":5584},[4023,18922,6971],{"class":4564},[4023,18924,18925],{"class":4025,"line":5597},[4023,18926,11976],{"class":4564},[4023,18928,18929],{"class":4025,"line":5625},[4023,18930,4060],{"emptyLinePlaceholder":4059},[4023,18932,18933,18935,18937,18939],{"class":4025,"line":5630},[4023,18934,11652],{"class":4986},[4023,18936,6750],{"class":4564},[4023,18938,5246],{"class":4890},[4023,18940,5446],{"class":4564},[4023,18942,18943,18946],{"class":4025,"line":5637},[4023,18944,18945],{"class":4986},"        fetchTripsData",[4023,18947,6711],{"class":4564},[4023,18949,18950,18952,18955],{"class":4025,"line":5644},[4023,18951,13130],{"class":4564},[4023,18953,18954],{"class":4910},"fetchTripsData",[4023,18956,13135],{"class":4564},[4023,18958,18959],{"class":4025,"line":5657},[4023,18960,4060],{"emptyLinePlaceholder":4059},[4023,18962,18963],{"class":4025,"line":5668},[4023,18964,18965],{"class":4965},"    \u002F\u002F Автоматична ресинхронізація\n",[4023,18967,18968,18970,18972,18974],{"class":4025,"line":5673},[4023,18969,15825],{"class":4986},[4023,18971,6750],{"class":4564},[4023,18973,5246],{"class":4890},[4023,18975,5446],{"class":4564},[4023,18977,18978,18980,18982,18984],{"class":4025,"line":5709},[4023,18979,18945],{"class":4986},[4023,18981,5122],{"class":4564},[4023,18983,3834],{"class":4890},[4023,18985,5131],{"class":4564},[4023,18987,18988],{"class":4025,"line":5714},[4023,18989,8205],{"class":4564},[4023,18991,18992],{"class":4025,"line":8477},[4023,18993,4060],{"emptyLinePlaceholder":4059},[4023,18995,18996],{"class":4025,"line":8482},[4023,18997,18998],{"class":4965},"    \u002F\u002F Швидка вибірка O(1) за ID\n",[4023,19000,19001,19003,19006,19008,19010,19012,19014,19016,19018,19020,19022,19025,19027,19029,19031,19033,19036,19038,19040,19042],{"class":4025,"line":8488},[4023,19002,5029],{"class":4890},[4023,19004,19005],{"class":5032}," getTrip",[4023,19007,5403],{"class":4564},[4023,19009,15217],{"class":4986},[4023,19011,5238],{"class":4564},[4023,19013,4911],{"class":4910},[4023,19015,4577],{"class":4564},[4023,19017,4916],{"class":4894},[4023,19019,5243],{"class":4564},[4023,19021,5246],{"class":4890},[4023,19023,19024],{"class":4910}," tripsState",[4023,19026,3836],{"class":4564},[4023,19028,3824],{"class":4910},[4023,19030,5096],{"class":4564},[4023,19032,4911],{"class":4910},[4023,19034,19035],{"class":4564},"], [",[4023,19037,5960],{"class":4910},[4023,19039,3836],{"class":4564},[4023,19041,3824],{"class":4910},[4023,19043,13135],{"class":4564},[4023,19045,19046],{"class":4025,"line":8527},[4023,19047,4060],{"emptyLinePlaceholder":4059},[4023,19049,19050],{"class":4025,"line":8544},[4023,19051,19052],{"class":4965},"    \u002F\u002F Миттєве оновлення O(1)\n",[4023,19054,19055,19057,19060,19062,19064,19066,19068,19070,19072,19074,19076,19078,19080,19082,19084,19086,19088],{"class":4025,"line":8549},[4023,19056,5029],{"class":4890},[4023,19058,19059],{"class":5032}," patchTrip",[4023,19061,5403],{"class":4564},[4023,19063,15217],{"class":4986},[4023,19065,5238],{"class":4564},[4023,19067,4911],{"class":4910},[4023,19069,4577],{"class":4564},[4023,19071,4916],{"class":4894},[4023,19073,3928],{"class":4564},[4023,19075,4797],{"class":4910},[4023,19077,4577],{"class":4564},[4023,19079,5375],{"class":4894},[4023,19081,4898],{"class":4564},[4023,19083,17995],{"class":4894},[4023,19085,18480],{"class":4564},[4023,19087,5246],{"class":4890},[4023,19089,5446],{"class":4564},[4023,19091,19092,19094,19096,19099,19101,19103,19105,19107,19109,19111,19113,19115,19117],{"class":4025,"line":8554},[4023,19093,6059],{"class":4986},[4023,19095,5238],{"class":4564},[4023,19097,19098],{"class":4910},"prev",[4023,19100,5243],{"class":4564},[4023,19102,5246],{"class":4890},[4023,19104,5320],{"class":4986},[4023,19106,5122],{"class":4564},[4023,19108,19098],{"class":4910},[4023,19110,3928],{"class":4564},[4023,19112,4911],{"class":4910},[4023,19114,3928],{"class":4564},[4023,19116,4797],{"class":4910},[4023,19118,5290],{"class":4564},[4023,19120,19121],{"class":4025,"line":8560},[4023,19122,11976],{"class":4564},[4023,19124,19125],{"class":4025,"line":8572},[4023,19126,4060],{"emptyLinePlaceholder":4059},[4023,19128,19129],{"class":4025,"line":8595},[4023,19130,19131],{"class":4965},"    \u002F\u002F Додавання нової поїздки O(1)\n",[4023,19133,19134,19136,19139,19141,19143,19145,19147,19149,19151,19153,19155],{"class":4025,"line":8607},[4023,19135,5029],{"class":4890},[4023,19137,19138],{"class":5032}," addTrip",[4023,19140,5403],{"class":4564},[4023,19142,15217],{"class":4986},[4023,19144,5238],{"class":4564},[4023,19146,6300],{"class":4910},[4023,19148,4577],{"class":4564},[4023,19150,17995],{"class":4894},[4023,19152,5243],{"class":4564},[4023,19154,5246],{"class":4890},[4023,19156,5446],{"class":4564},[4023,19158,19159,19161,19163,19165,19167,19169,19171,19173,19175,19177,19179],{"class":4025,"line":8630},[4023,19160,6059],{"class":4986},[4023,19162,5238],{"class":4564},[4023,19164,19098],{"class":4910},[4023,19166,5243],{"class":4564},[4023,19168,5246],{"class":4890},[4023,19170,5538],{"class":4986},[4023,19172,5122],{"class":4564},[4023,19174,19098],{"class":4910},[4023,19176,3928],{"class":4564},[4023,19178,6300],{"class":4910},[4023,19180,5290],{"class":4564},[4023,19182,19183],{"class":4025,"line":8646},[4023,19184,11976],{"class":4564},[4023,19186,19187],{"class":4025,"line":8672},[4023,19188,4060],{"emptyLinePlaceholder":4059},[4023,19190,19191],{"class":4025,"line":8678},[4023,19192,19193],{"class":4965},"    \u002F\u002F Мемоізований список для компонентів-списків\n",[4023,19195,19196,19198,19201,19203,19205,19207,19209,19211,19213,19215,19218,19220],{"class":4025,"line":8694},[4023,19197,5029],{"class":4890},[4023,19199,19200],{"class":5032}," tripsArray",[4023,19202,5403],{"class":4564},[4023,19204,15222],{"class":4986},[4023,19206,6750],{"class":4564},[4023,19208,5246],{"class":4890},[4023,19210,5184],{"class":4986},[4023,19212,5122],{"class":4564},[4023,19214,5960],{"class":4910},[4023,19216,19217],{"class":4564},"), [",[4023,19219,5960],{"class":4910},[4023,19221,13135],{"class":4564},[4023,19223,19224],{"class":4025,"line":8700},[4023,19225,4060],{"emptyLinePlaceholder":4059},[4023,19227,19228,19230],{"class":4025,"line":8723},[4023,19229,5144],{"class":4886},[4023,19231,6189],{"class":4564},[4023,19233,19234,19236],{"class":4025,"line":8735},[4023,19235,6195],{"class":6194},[4023,19237,19238],{"class":4894},"TripsContext.Provider\n",[4023,19240,19241,19244,19246,19248],{"class":4025,"line":8740},[4023,19242,19243],{"class":6200},"            value",[4023,19245,6204],{"class":4564},[4023,19247,6207],{"class":4890},[4023,19249,17654],{"class":6210},[4023,19251,19252,19255,19257],{"class":4025,"line":8746},[4023,19253,19254],{"class":4910},"                trips:",[4023,19256,19200],{"class":4910},[4023,19258,5365],{"class":6210},[4023,19260,19261,19264,19266,19268,19270],{"class":4025,"line":8752},[4023,19262,19263],{"class":4910},"                tripsById:",[4023,19265,19024],{"class":4910},[4023,19267,3836],{"class":6210},[4023,19269,3824],{"class":4910},[4023,19271,5365],{"class":6210},[4023,19273,19274,19277],{"class":4025,"line":8757},[4023,19275,19276],{"class":4910},"                isLoading",[4023,19278,5365],{"class":6210},[4023,19280,19281,19284],{"class":4025,"line":8762},[4023,19282,19283],{"class":4910},"                isRefreshing",[4023,19285,5365],{"class":6210},[4023,19287,19288,19291],{"class":4025,"line":8777},[4023,19289,19290],{"class":4910},"                error",[4023,19292,5365],{"class":6210},[4023,19294,19295,19298],{"class":4025,"line":8787},[4023,19296,19297],{"class":4910},"                getTrip",[4023,19299,5365],{"class":6210},[4023,19301,19302,19305],{"class":4025,"line":8792},[4023,19303,19304],{"class":4910},"                patchTrip",[4023,19306,5365],{"class":6210},[4023,19308,19309,19312],{"class":4025,"line":8800},[4023,19310,19311],{"class":4910},"                addTrip",[4023,19313,5365],{"class":6210},[4023,19315,19316,19319,19321],{"class":4025,"line":8806},[4023,19317,19318],{"class":4910},"                refetch:",[4023,19320,18742],{"class":4910},[4023,19322,5365],{"class":6210},[4023,19324,19325,19328],{"class":4025,"line":8812},[4023,19326,19327],{"class":6210},"            }",[4023,19329,4090],{"class":4890},[4023,19331,19332],{"class":4025,"line":8843},[4023,19333,19334],{"class":6194},"        >\n",[4023,19336,19337,19339,19341],{"class":4025,"line":8864},[4023,19338,6288],{"class":4890},[4023,19340,12328],{"class":4910},[4023,19342,4090],{"class":4890},[4023,19344,19345,19347,19350],{"class":4025,"line":8878},[4023,19346,6456],{"class":6194},[4023,19348,19349],{"class":4894},"TripsContext.Provider",[4023,19351,4940],{"class":6194},[4023,19353,19354],{"class":4025,"line":8891},[4023,19355,6465],{"class":4564},[4023,19357,19358],{"class":4025,"line":8897},[4023,19359,4090],{"class":4564},[4023,19361,19362],{"class":4025,"line":8902},[4023,19363,4060],{"emptyLinePlaceholder":4059},[4023,19365,19366,19368,19370,19373,19375,19377],{"class":4025,"line":8925},[4023,19367,4887],{"class":4886},[4023,19369,4983],{"class":4890},[4023,19371,19372],{"class":4986}," useTrips",[4023,19374,7715],{"class":4564},[4023,19376,18553],{"class":4894},[4023,19378,5446],{"class":4564},[4023,19380,19381,19383,19386,19388,19390,19392,19395],{"class":4025,"line":8930},[4023,19382,5029],{"class":4890},[4023,19384,19385],{"class":5032}," context",[4023,19387,5403],{"class":4564},[4023,19389,18232],{"class":4986},[4023,19391,5122],{"class":4564},[4023,19393,19394],{"class":4910},"TripsContext",[4023,19396,5131],{"class":4564},[4023,19398,19399,19401,19403,19406],{"class":4025,"line":8936},[4023,19400,5421],{"class":4886},[4023,19402,5424],{"class":4564},[4023,19404,19405],{"class":4910},"context",[4023,19407,5088],{"class":4564},[4023,19409,19410,19412,19414,19416,19418,19421],{"class":4025,"line":8942},[4023,19411,7178],{"class":4886},[4023,19413,6928],{"class":4890},[4023,19415,6931],{"class":4986},[4023,19417,5122],{"class":4564},[4023,19419,19420],{"class":4580},"'useTrips must be used within a TripsProvider'",[4023,19422,5131],{"class":4564},[4023,19424,19425],{"class":4025,"line":8947},[4023,19426,4147],{"class":4564},[4023,19428,19429,19431],{"class":4025,"line":8964},[4023,19430,5144],{"class":4886},[4023,19432,19433],{"class":4910}," context\n",[4023,19435,19436],{"class":4025,"line":8969},[4023,19437,4090],{"class":4564},[3774,19439,19441,19442,19444,19445],{"id":19440},"_4-інтеграція-offlinebanner-у-кореневий-макет-_layouttsx","4. Інтеграція ",[3799,19443,13581],{}," у кореневий макет ",[3799,19446,19447],{},"_layout.tsx",[4014,19449,19451],{"className":5722,"code":19450,"language":5724,"meta":4019,"style":4019},"\u002F\u002F app\u002F_layout.tsx\nimport { Stack } from 'expo-router'\nimport { TripsProvider } from '..\u002Fsrc\u002Ffeatures\u002Ftrips\u002Fmodel\u002FTripsProvider'\nimport { OfflineBanner } from '..\u002Fsrc\u002Fcomponents\u002FOfflineBanner'\nimport { SafeAreaView, StyleSheet } from 'react-native'\n\nexport default function RootLayout() {\n    return (\n        \u003CSafeAreaView style={styles.root}>\n            \u003COfflineBanner \u002F>\n            \u003CTripsProvider>\n                \u003CStack screenOptions={{ headerShown: false }} \u002F>\n            \u003C\u002FTripsProvider>\n        \u003C\u002FSafeAreaView>\n    )\n}\n\nconst styles = StyleSheet.create({\n    root: {\n        flex: 1,\n        backgroundColor: '#0f172a',\n    },\n})\n",[3799,19452,19453,19458,19474,19489,19504,19522,19526,19539,19545,19568,19576,19584,19610,19618,19626,19630,19634,19638,19654,19661,19669,19677,19681],{"__ignoreMap":4019},[4023,19454,19455],{"class":4025,"line":4026},[4023,19456,19457],{"class":4965},"\u002F\u002F app\u002F_layout.tsx\n",[4023,19459,19460,19462,19464,19467,19469,19471],{"class":4025,"line":4032},[4023,19461,5731],{"class":4886},[4023,19463,4907],{"class":4564},[4023,19465,19466],{"class":4910},"Stack",[4023,19468,5743],{"class":4564},[4023,19470,5746],{"class":4886},[4023,19472,19473],{"class":4580}," 'expo-router'\n",[4023,19475,19476,19478,19480,19482,19484,19486],{"class":4025,"line":4038},[4023,19477,5731],{"class":4886},[4023,19479,4907],{"class":4564},[4023,19481,18203],{"class":4910},[4023,19483,5743],{"class":4564},[4023,19485,5746],{"class":4886},[4023,19487,19488],{"class":4580}," '..\u002Fsrc\u002Ffeatures\u002Ftrips\u002Fmodel\u002FTripsProvider'\n",[4023,19490,19491,19493,19495,19497,19499,19501],{"class":4025,"line":4044},[4023,19492,5731],{"class":4886},[4023,19494,4907],{"class":4564},[4023,19496,13581],{"class":4910},[4023,19498,5743],{"class":4564},[4023,19500,5746],{"class":4886},[4023,19502,19503],{"class":4580}," '..\u002Fsrc\u002Fcomponents\u002FOfflineBanner'\n",[4023,19505,19506,19508,19510,19512,19514,19516,19518,19520],{"class":4025,"line":4050},[4023,19507,5731],{"class":4886},[4023,19509,4907],{"class":4564},[4023,19511,17207],{"class":4910},[4023,19513,3928],{"class":4564},[4023,19515,12265],{"class":4910},[4023,19517,5743],{"class":4564},[4023,19519,5746],{"class":4886},[4023,19521,5775],{"class":4580},[4023,19523,19524],{"class":4025,"line":4056},[4023,19525,4060],{"emptyLinePlaceholder":4059},[4023,19527,19528,19530,19532,19534,19537],{"class":4025,"line":4063},[4023,19529,4887],{"class":4886},[4023,19531,17272],{"class":4886},[4023,19533,4983],{"class":4890},[4023,19535,19536],{"class":4986}," RootLayout",[4023,19538,5867],{"class":4564},[4023,19540,19541,19543],{"class":4025,"line":4069},[4023,19542,5144],{"class":4886},[4023,19544,6189],{"class":4564},[4023,19546,19547,19549,19551,19553,19555,19557,19559,19561,19564,19566],{"class":4025,"line":4075},[4023,19548,6195],{"class":6194},[4023,19550,17207],{"class":4894},[4023,19552,6201],{"class":6200},[4023,19554,6204],{"class":4564},[4023,19556,6207],{"class":4890},[4023,19558,12524],{"class":4910},[4023,19560,3836],{"class":6210},[4023,19562,19563],{"class":4910},"root",[4023,19565,6222],{"class":4890},[4023,19567,4940],{"class":6194},[4023,19569,19570,19572,19574],{"class":4025,"line":4081},[4023,19571,6229],{"class":6194},[4023,19573,13581],{"class":4894},[4023,19575,6435],{"class":6194},[4023,19577,19578,19580,19582],{"class":4025,"line":4087},[4023,19579,6229],{"class":6194},[4023,19581,18203],{"class":4894},[4023,19583,4940],{"class":6194},[4023,19585,19586,19588,19590,19593,19595,19597,19599,19602,19604,19606,19608],{"class":4025,"line":4093},[4023,19587,6311],{"class":6194},[4023,19589,19466],{"class":4894},[4023,19591,19592],{"class":6200}," screenOptions",[4023,19594,6204],{"class":4564},[4023,19596,6207],{"class":4890},[4023,19598,6211],{"class":6210},[4023,19600,19601],{"class":4910},"headerShown:",[4023,19603,9759],{"class":4890},[4023,19605,4723],{"class":6210},[4023,19607,6222],{"class":4890},[4023,19609,6435],{"class":6194},[4023,19611,19612,19614,19616],{"class":4025,"line":4098},[4023,19613,14878],{"class":6194},[4023,19615,18203],{"class":4894},[4023,19617,4940],{"class":6194},[4023,19619,19620,19622,19624],{"class":4025,"line":4104},[4023,19621,6456],{"class":6194},[4023,19623,17207],{"class":4894},[4023,19625,4940],{"class":6194},[4023,19627,19628],{"class":4025,"line":4110},[4023,19629,6465],{"class":4564},[4023,19631,19632],{"class":4025,"line":4116},[4023,19633,4090],{"class":4564},[4023,19635,19636],{"class":4025,"line":4121},[4023,19637,4060],{"emptyLinePlaceholder":4059},[4023,19639,19640,19642,19644,19646,19648,19650,19652],{"class":4025,"line":4126},[4023,19641,5076],{"class":4890},[4023,19643,12649],{"class":5032},[4023,19645,5403],{"class":4564},[4023,19647,12265],{"class":4910},[4023,19649,3836],{"class":4564},[4023,19651,7889],{"class":4986},[4023,19653,7892],{"class":4564},[4023,19655,19656,19659],{"class":4025,"line":4132},[4023,19657,19658],{"class":4910},"    root:",[4023,19660,5446],{"class":4564},[4023,19662,19663,19665,19667],{"class":4025,"line":4138},[4023,19664,14929],{"class":4910},[4023,19666,12672],{"class":4601},[4023,19668,5365],{"class":4564},[4023,19670,19671,19673,19675],{"class":4025,"line":4144},[4023,19672,12699],{"class":4910},[4023,19674,12702],{"class":4580},[4023,19676,5365],{"class":4564},[4023,19678,19679],{"class":4025,"line":4150},[4023,19680,7986],{"class":4564},[4023,19682,19683],{"class":4025,"line":4156},[4023,19684,7991],{"class":4564},[3964,19686],{},[3738,19688,19690],{"id":19689},"резюме-розділу","Резюме розділу",[19692,19693,19694,19705,19719,19724,19729],"card-group",{},[19695,19696,19698,19699,19701,19702,19704],"card",{"icon":658,"title":19697},"🌐 Мережевий стек React Native","HTTP-запити виконуються нативними рушіями ОС (",[3799,19700,3801],{}," на iOS, ",[3799,19703,3807],{}," на Android) через JSI\u002FTurboModules. Політика CORS відсутня на рівні платформи.",[19695,19706,19708,19709,19711,19712,3825,19714,19716,19717,3836],{"icon":2675,"title":19707},"📦 Нормалізація стану","Збереження масивів з API сповільнює додаток до ",[3832,19710],{":inline":3834,"tex":4678},". Нормалізуйте дані в структуру ",[3799,19713,3824],{},[3799,19715,3828],{}," для забезпечення вибірки та оновлення зі складністю ",[3832,19718],{":inline":3834,"tex":3835},[19695,19720,19723],{"icon":19721,"title":19722},"i-lucide-key-round","🔒 Черга оновлення токенів","Запобігайте стану гонитви при паралельних 401 помилках за допомогою Mutex-черги в Axios Response Interceptor.",[19695,19725,19728],{"icon":19726,"title":19727},"i-lucide-shield-alert","📊 Класифікація помилок та Jitter","Розрізняйте транспортні (Network\u002FTimeout), серверні (5xx) та клієнтські (4xx) помилки. Повторюйте збої 5xx через Full Jitter Exponential Backoff і ніколи не робіть автоповтор для 4xx.",[19695,19730,19733,19734,3924,19736,19738,19739,3924,19741,19743,19744,19746],{"icon":19731,"title":19732},"i-lucide-refresh-cw","🔋 Життєвий цикл і зв'язок","Використовуйте ",[3799,19735,11728],{},[3799,19737,3914],{},") та ",[3799,19740,3923],{},[3799,19742,3927],{},"\u002F",[3799,19745,3934],{},") для побудови патерну Smart Refetch, що підтримує інтерфейс актуальним без зайвих навантажень на батарею.",[3964,19748],{},[3738,19750,19752],{"id":19751},"практичні-завдання","Практичні завдання",[4733,19754,19755,19768],{},[4736,19756,19757],{},[4739,19758,19759,19762,19765],{},[4742,19760,19761],{"align":4744},"Рівень складності",[4742,19763,19764],{"align":4744},"Назва завдання",[4742,19766,19767],{"align":4744},"Формулювання вимог",[4760,19769,19770,19790,19820],{},[4739,19771,19772,19777,19780],{},[4765,19773,19774],{"align":4744},[3750,19775,19776],{},"Базовий (Basic)",[4765,19778,19779],{"align":4744},"Пошук та фільтрація валют",[4765,19781,19782,19783,19786,19787,3836],{"align":4744},"Додати у міні-проєкт поле ",[3799,19784,19785],{},"TextInput"," для динамічної фільтрації списку валют за назвою або ISO-кодом. Реалізувати скидання фільтра при оновленні списку через ",[3799,19788,19789],{},"RefreshControl",[4739,19791,19792,19797,19803],{},[4765,19793,19794],{"align":4744},[3750,19795,19796],{},"Середній (Intermediate)",[4765,19798,19799,19800],{"align":4744},"Кастомний хук ",[3799,19801,19802],{},"useFetchWithAbort",[4765,19804,19805,19806,19809,19810,19812,19813,19816,19817,3836],{"align":4744},"Спроєктувати типізований хук ",[3799,19807,19808],{},"useFetchWithAbort\u003CT>(url: string)",", який автоматично створює екземпляр ",[3799,19811,3860],{},", скасовує активний мережевий запит при розмонтуванні компонента (",[3799,19814,19815],{},"unmount",") та повертає об'єкт ",[3799,19818,19819],{},"{ data, isLoading, error, refetch }",[4739,19821,19822,19827,19830],{},[4765,19823,19824],{"align":4744},[3750,19825,19826],{},"Просунутий (Advanced)",[4765,19828,19829],{"align":4744},"Офлайн-черга мутацій (Offline Mutation Queue)",[4765,19831,19832,19833,19836,19837,19840,19841,3836],{"align":4744},"Реалізувати чергу відкладених дій: якщо користувач створює поїздку в режимі офлайн, запис зберігається в локальному сховищі (AsyncStorage \u002F MMKV) з прапорцем ",[3799,19834,19835],{},"isPending: true",". При відновленні мережі (",[3799,19838,19839],{},"isInternetReachable === true",") черга автоматично синхронізується з сервером через метод ",[3799,19842,19843],{},"POST \u002Ftrips",[3964,19845],{},[3738,19847,19849],{"id":19848},"поширені-запитання-faq","Поширені запитання (FAQ)",[19851,19852,19853,19866,19888,19916,19937],"accordion",{},[19854,19855,19858,19859,19861,19862,3836],"accordion-item",{"icon":19856,"title":19857},"i-lucide-help-circle","Чому Android-емулятор повертає Network Error при зверненні до localhost:3000?","Android-емулятор є повноцінною віртуальною машиною з власною віртуальною мережею. Для нього адреса ",[3799,19860,7431],{}," вказує на сам віртуальний телефон. Щоб звернутися до сервера розробки, запущеного на вашому комп'ютері, необхідно використовувати зарезервовану IP-адресу моста маршрутизатора Android QEMU: ",[3750,19863,19864],{},[3799,19865,7437],{},[19854,19867,19869,19870,19873,19874,19877,19878,19880,19881,19884,19885,19887],{"icon":2675,"title":19868},"Чому нормалізація стану краща за збереження масиву?","При збереженні звичайного масиву ",[3799,19871,19872],{},"items.find()"," та ",[3799,19875,19876],{},"items.map()"," виконуються зі складністю ",[3832,19879],{":inline":3834,"tex":4678}," при кожному перегляді або лайку. У нормалізованому стані ",[3799,19882,19883],{},"{ byId, allIds }"," будь-яка сутність вилучається та оновлюється миттєво за ",[3832,19886],{":inline":3834,"tex":3835},", а зміни відображаються узгоджено на всіх екранах.",[19854,19889,19892,19893,7355,19896,19899,19900,19873,19902,19904,19905,19908,19909,19912,19913,3836],{"icon":19890,"title":19891},"i-lucide-cookie","Чи працюють cookie-файли у React Native так само, як у браузері?","Ні. У браузері рушій автоматично зберігає та надсилає заголовок ",[3799,19894,19895],{},"Set-Cookie",[3799,19897,19898],{},"Cookie",". У React Native нативний шар (",[3799,19901,3801],{},[3799,19903,3807],{},") має власне сховище ",[3799,19906,19907],{},"CookieJar",", але поведінка збереження між перезапусками застосунку різниться на платформах. Для надійної автентифікації в мобільних додатках загальноприйнятим стандартом є використання заголовка ",[3799,19910,19911],{},"Authorization: Bearer \u003CJWT>"," у поєднанні з безпечним сховищем ",[3799,19914,19915],{},"expo-secure-store",[19854,19917,19920,19921,19924,19925,3928,19928,19931,19932,19934,19935,3836],{"icon":19918,"title":19919},"i-lucide-smartphone","Що відбувається з активним запитом, коли користувач блокує екран або згортає додаток?","Нативний сокет операційної системи може завершити передачу байтів, якщо це відбудеться протягом кількох секунд після згортання. Проте, коли ОС переводить процес у стан ",[3799,19922,19923],{},"Suspended",", потік виконання JavaScript зупиняється. Обробники ",[3799,19926,19927],{},"then()",[3799,19929,19930],{},"catch()"," або продовження після ",[3799,19933,6800],{}," виконаються лише тоді, коли користувач поверне застосунок у стан ",[3799,19936,3927],{},[19854,19938,19941,19942,19944,19945,3836],{"icon":19939,"title":19940},"i-lucide-wifi-off","Чому не можна покладатися лише на прапорець NetInfo.isConnected?","Прапорець ",[3799,19943,3910],{}," лише сигналізує про наявність фізичного каналу зв'язку з локальною точкою доступу або вежею (наприклад, телефон підключений до Wi-Fi в літаку або до готельного Captive Portal). Він не гарантує маршрутизації до глобальної мережі Інтернет. Завжди перевіряйте значення ",[3750,19946,19947],{},[3799,19948,19839],{},[19950,19951,19952],"style",{},"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 .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .s-QsJ, html code.shiki .s-QsJ{--shiki-light:#0070C1;--shiki-default:#4FC1FF;--shiki-dark:#4FC1FF}html pre.shiki code .s0P7L, html code.shiki .s0P7L{--shiki-light:#800000;--shiki-default:#808080;--shiki-dark:#808080}html pre.shiki code .sa4r_, html code.shiki .sa4r_{--shiki-light:#E50000;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .shsrj, html code.shiki .shsrj{--shiki-light:#811F3F;--shiki-default:#D16969;--shiki-dark:#D16969}html pre.shiki code .siofk, html code.shiki .siofk{--shiki-light:#EE0000;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}",{"title":4019,"searchDepth":4032,"depth":4032,"links":19954},[19955,19958,19962,19967,19973,19983,19989,19994,19996,20003,20012,20013,20023,20032,20033,20034],{"id":3740,"depth":4032,"text":3741,"children":19956},[19957],{"id":3776,"depth":4038,"text":3777},{"id":3968,"depth":4032,"text":3969,"children":19959},[19960,19961],{"id":3975,"depth":4038,"text":3976},{"id":4282,"depth":4038,"text":4283},{"id":4376,"depth":4032,"text":4377,"children":19963},[19964,19965],{"id":4390,"depth":4038,"text":4391},{"id":4483,"depth":4038,"text":19966},"Чому частий Polling вбиває батарею: «Ефект хвоста» (Tail Energy)",{"id":4544,"depth":4032,"text":19968,"children":19969},"Організація даних клієнта: Нормалізація серверного стану (byId + allIds)",[19970,19971,19972],{"id":4655,"depth":4038,"text":4656},{"id":4713,"depth":4038,"text":4714},{"id":4863,"depth":4038,"text":4864},{"id":6474,"depth":4032,"text":19974,"children":19975},"HTTP-клієнти: fetch проти axios",[19976,19978,19980,19982],{"id":6492,"depth":4038,"text":19977},"Реалізація fetch у React Native та підводні камені",{"id":6591,"depth":4038,"text":19979},"Керування таймаутом і життєвим циклом запиту через AbortController",{"id":7192,"depth":4038,"text":19981},"Чому на практиці обирають axios",{"id":7253,"depth":4038,"text":7254},{"id":7386,"depth":4032,"text":19984,"children":19985},"Налаштування мережевого клієнта на базі axios",[19986,19988],{"id":7395,"depth":4038,"text":19987},"Проблема мережевої адресації: localhost на мобільних пристроях",{"id":7454,"depth":4038,"text":7455},{"id":9162,"depth":4032,"text":9163,"children":19990},[19991,19992,19993],{"id":9169,"depth":4038,"text":9170},{"id":9294,"depth":4038,"text":9295},{"id":9481,"depth":4038,"text":9482},{"id":10055,"depth":4032,"text":19995},"Уніфікована обробка помилок: клас AppError",{"id":11305,"depth":4032,"text":19997,"children":19998},"Моніторинг стану зв'язку в реальному часі: @react-native-community\u002Fnetinfo",[19999,20001],{"id":11314,"depth":4038,"text":20000},"Різниця між прапорцями: isConnected проти isInternetReachable",{"id":11417,"depth":4038,"text":20002},"Проєктування хука useNetworkStatus",{"id":11996,"depth":4032,"text":20004,"children":20005},"Життєвий цикл мобільного застосунку: стани AppState",[20006,20008,20009,20011],{"id":12005,"depth":4038,"text":20007},"Схема станів AppState",{"id":12120,"depth":4038,"text":12121},{"id":12176,"depth":4038,"text":20010},"Що потрібно робити при зміні AppState",{"id":12225,"depth":4038,"text":12226},{"id":12775,"depth":4032,"text":12776},{"id":13549,"depth":4032,"text":13550,"children":20014},[20015,20016,20017,20018,20019,20020,20021],{"id":13587,"depth":4038,"text":13588},{"id":13693,"depth":4038,"text":13694},{"id":13700,"depth":4038,"text":13701},{"id":13851,"depth":4038,"text":13852},{"id":14263,"depth":4038,"text":14264},{"id":15187,"depth":4038,"text":15188},{"id":17175,"depth":4038,"text":20022},"Крок 5. Збирання кореневого компонента App.tsx",{"id":17614,"depth":4032,"text":17615,"children":20024},[20025,20027,20028,20030],{"id":17631,"depth":4038,"text":20026},"1. Конфігурація мок-сервера json-server",{"id":17860,"depth":4038,"text":17861},{"id":18199,"depth":4038,"text":20029},"3. Нормалізований TripsProvider зі швидким доступом ",{"id":19440,"depth":4038,"text":20031},"4. Інтеграція OfflineBanner у кореневий макет _layout.tsx",{"id":19689,"depth":4032,"text":19690},{"id":19751,"depth":4032,"text":19752},{"id":19848,"depth":4032,"text":19849},"Глибокий практичний розбір мережевого стека React Native (fetch, axios, нативні стеки NSURLSession та OkHttpClient), нормалізація серверного стану, класифікація та обробка помилок, експоненційний backoff з jitter, черги оновлення токенів, моніторинг з'єднання через NetInfo та керування життєвим циклом застосунку через AppState.","md",null,{},{"title":3444,"description":20035},"tRy717ykvMOnNEdZTDb2XGEi1TKwwas1ZJYkHZssS4M",[20042,20044],{"title":3440,"path":3441,"stem":3442,"description":20043,"children":-1},"Архітектура параметризації маршрутів у Expo Router — динамічні сегменти [id], query-параметри, хуки useLocalSearchParams та useGlobalSearchParams, реєстрація Custom URL Scheme, робота з модулем expo-linking, холодний та теплий старт (Cold\u002FWarm Start), Universal Links і Android App Links, міні-проєкт «Купон за лінком» та навігація за посиланням у Nomad",{"title":3448,"path":3449,"stem":3450,"description":20045,"children":-1},"Повний практичний посібник з Redux Toolkit у React Native — будова Store, Slice, Reducer, Action, createListenerMiddleware, персистентність з MMKV, оптимістичні оновлення, скасування thunk через AbortController, createEntityAdapter, Reselect мемоїзація та тестування логіки.",1788069962972]