Local-first заметки и явная синхронизация
Создаём «Блокнот»: изменяемые записи в IndexedDB, атомарную локальную очередь и собственный серверный контракт. Учимся сохранять текст без доступной сети, повторять стабильное намерение, получать журнал по cursor и разрешать конфликт двух редакций вручную. IndexedDB здесь хранит данные; серверная синхронизация реализована приложением. Service worker, кеш оболочки и CRDT остаются темами других серий.
Нужны основы JavaScript и HTTP, Node.js 24 и современный браузер с IndexedDB, Web Locks и BroadcastChannel. Сервер слушает localhost:4174. Два независимых профиля моделируют устройства одного аккаунта; настоящее размещение для разных компьютеров требует отдельного HTTPS сервера и реальной аутентификации. JSON сервер предназначен для одного процесса, без гарантии аварийной durability; journal/ledger/tombstones не очищаются. Учебные токены не являются паролями. Новый офлайн-вход и кеш оболочки не реализованы.
| Урок | Новый результат |
|---|---|
| 1. Local-first приложение: локальная запись и границы синхронизации | Разделяем локальную заметку, серверную ревизию и ещё не подтверждённое изменение. |
| 2. Local-first приложение: схема IndexedDB для записей и очереди | Открываем три object store и связываем обновление схемы с жизненным циклом вкладок. |
| 3. Local-first приложение: атомарное сохранение заметки и outbox | Сохраняем форму и намерение отправки одной транзакцией, сообщая об успехе после complete. |
| 4. Local-first приложение: стабильный ID операции и неизменяемый снимок | Отделяем идентификатор заметки от идентификатора отправки и сохраняем снимок до подтверждения. |
| 5. Local-first приложение: серверный revision и журнал изменений | Реализуем push/pull контракт с проверкой исходной ревизии и сохранением результата операции. |
| 6. Local-first приложение: отправка outbox и подтверждение снимка | Применяем серверный ответ к отправленной операции, не теряя правку во время запроса. |
| 7. Local-first приложение: получение изменений и атомарный cursor | Сохраняем страницу серверного журнала и её cursor одной транзакцией. |
| 8. Local-first приложение: сравнение версий и ручное объединение | Разрешаем конфликт явным выбором, сохраняя серверную revision и новый ID намерения. |
| 9. Local-first приложение: удаление заметки и tombstone | Передаём удаление как версионированное состояние и удерживаем его для отключённых устройств. |
| 10. Local-first приложение: повторы, отключение сети и прогресс | Разделяем временный сетевой отказ, завершение сессии и прикладной конфликт, сохраняя очередь. |
| 11. Local-first приложение: несколько вкладок и несколько устройств | Координируем отправителей одного origin и защищаем сохранение устаревшей формы. |
| 12. Local-first приложение: квоты браузера и экспорт заметок | Обрабатываем ограниченное хранение и экспортируем согласованный диагностический снимок. |
| 13. Local-first приложение: аккаунт, локальная база и прекращение сессии | Разделяем записи разных владельцев и прекращаем отправку при отказе авторизации. |
| 14. Local-first приложение: передача Блокнота и сценарии восстановления | Собираем регламент локального хранения, синхронизации, конфликта и экспорта с явными границами. |
Скачать полный конечный пример. README указывает порядок самостоятельного прохода, файлы, совместимость и границы. Архив доступен для скачивания; он не содержит готового работающего окружения.
Все материалы опубликованы. Учебные программы, серверы, установки, Git операции лабораторий, сборки образов, Kubernetes, Argo CD, HTTP сценарии и тесты не запускались. Показанные состояния и выводы ожидаемые. Сборка статического сайта, если выполнена отдельно, не подтверждает выполнение учебного примера.