Подготовка редактора к выпуску
Мы построили учебный редактор «Черновик» из отдельных слоёв: общий текст, привязка ввода, двоичный обмен, присутствие, reconnect, браузерный кеш, серверное хранение, права и ревизии. Перед выпуском важно определить, какое поведение действительно реализовано, а какие обещания потребуют дальнейшей работы. В этой главе рассмотрим финальное состояние проекта, очистку ресурсов и контракт передачи, не превращая наличие исходников в заявление об успешном запуске.
Каталог lesson-12 является полной самостоятельной поставкой. Он сохраняет комнату draft-42, исходный план встречи и роли Анны и Бориса. Все предыдущие каталоги остаются отдельными состояниями для обучения. Архив содержит исходные файлы и описание зависимостей; зависимые пакеты не установлены, browser bundle не собран, пример не запускался по условию локального авторства.
Завершение редактора
У каждого слоя свой ресурс: текстовая привязка держит DOM-обработчики, provider держит сокет и таймер reconnect, awareness поддерживает временное состояние, IndexedDB-адаптер подписан на документ, UndoManager наблюдает общие типы. Уничтожение только Y.Doc не выражает всю эту последовательность. В функции dispose сначала прекращается транспорт, затем зависимые подписки, после чего освобождается документ.
Ниже показан точный фрагмент финального src/app.js. Переменные принадлежат одному вызову openEditor; функция сохраняется для кнопки закрытия, повторного входа и pagehide. Вызывать её повторно должно быть безопасно для страницы. Обработчики кнопок тоже относятся к этому состоянию: после закрытия поле недоступно, а новое открытие заменяет действия свежими объектами.
dispose=()=>{
if(disposed)return;disposed=true;
provider.destroy();
awareness.off('change',roster);awareness.destroy();
persistence.destroy();
undo.destroy();
unbind();
doc.destroy();$('editor').disabled=true;};
Явное закрытие provider удаляет таймер повторного соединения. Это означает, что пользовательское завершение не должно восприниматься как очередной сетевой сбой. Если закрытая вкладка спустя несколько секунд снова подключается к комнате, проблема лежит в жизненном цикле транспорта. Наличие CRDT или IndexedDB не исправляет неубранный таймер; такой отказ следует искать в ресурсах интерфейса.
После самостоятельного закрытия редактора ожидается прекращение участия данной реплики и запрет поля ввода. Общий документ на сервере остаётся существовать, а локальный кеш не удаляется автоматически. Эти последствия различаются: выход из редактора освобождает активные ресурсы, удаление локальных данных требует отдельной команды, удаление общего документа требует серверного разрешения и другого пользовательского действия.
Контракт сообщений
В поставке используется собственный JSON-протокол, а не стандартный provider готового сервиса. hello сообщает идентификатор реплики и вектор; sync возвращает недостающую часть и серверный вектор; update передаёт двоичные операции; awareness переносит эфемерное состояние; ack подтверждает запись; revision создаёт серверный снимок. Получатель проверяет допустимость типа сообщения в текущем контексте.
Следующим практическим улучшением станет явная версия протокола. Сейчас схема закреплена одним комплектом клиента и сервера. Если новый клиент изменит поля или формат кодирования, прежний сервер не должен пытаться угадать намерение. Лучше возвращать понятный отказ несовместимости до принятия изменений. Для длительно открытых вкладок политика обновления особенно важна: пользователю нужна возможность сохранить или экспортировать локальные правки.
Числа seq не являются глобальным порядком правок, clientID не является аккаунтом, label ревизии не является уникальным ключом. Эти три различия стоит сохранить в документации передачи. Ошибка значения поля часто ведёт к неверной аналитике или небезопасному решению о доступе. Серверные идентификаторы и время назначаются там, где можно связать их с проверенной сессией.
Данные после остановки
Серверный файл содержит текущую CRDT-структуру и до пятидесяти ревизий. IndexedDB хранит отдельную браузерную копию. У этих хранилищ разные области отказа, но ни одно само по себе не доказывает наличие независимого backup. Перед эксплуатацией нужно определить копирование серверного состояния, срок хранения, ответственность за восстановление и проверку, что восстановленная копия читается поддерживаемой версией приложения.
Также требуется политика удаления. Удалить видимые символы из Y.Text, удалить файл комнаты, очистить локальную базу и убрать backup — разные операции. Пользователь может ожидать одну из них, а код выполнить другую. Для портала компании важно сформулировать точный срок сохранения ревизий и локальных данных. Учебная кнопка закрытия не представляет собой обещание удаления содержимого.
В однопроцессном хранении каждый update приводит к полной записи состояния. Это ограничивает размер и нагрузку. Более серьёзный сервис должен выбрать журнал updates, механизм уплотнения и хранилище с согласованными подтверждениями. Пока такое решение не реализовано, нельзя объявлять учебный файловый сервер готовым к множеству организаций и больших документов только потому, что два клиента могут использовать один формат.
Границы редакторского интерфейса
Textarea-привязка вычисляет изменённый диапазон по строкам и сохраняет выделение только числовым ограничением. Она не решает полноценное положение курсора при удалённых правках, сложный composition и богатую модель Markdown-редактора. Список awareness показывает подтверждённые имена, но не рисует курсоры. Предварительный просмотр выводит текст, а не безопасно преобразованный Markdown. Это реализованные границы финального примера.
Для дальнейшего развития выбирайте binding под конкретный редактор и сохраняйте существующие транспортные границы. Не переносите права в UI и не заменяйте updates полной строкой ради новой панели инструментов. Изменение интерфейса может оставаться отдельным слоем, если схема общего документа, origin, provider и серверный протокол имеют понятные договорённости. Тогда новая функция не требует случайно переписывать весь механизм синхронизации.
Критерии передачи
До реального выпуска потребуется самостоятельно наблюдать параллельные вставки и удаления, composition, перезагрузку с кешем, истечение сессии, отказ записи и возвращение после обрыва. Статья перечисляет ожидаемые сценарии, но не содержит результатов таких наблюдений. Здесь нет выдуманных скриншотов, сетевых задержек или утверждения о прохождении тестов. Ограничение выполнения сохранено во всех метаданных серии.
Владелец поставки также должен заменить локальную модель входа полноценной политикой аккаунтов, настроить HTTPS и параметры cookie, ограничить ресурсы комнаты и добавить надёжное обнаружение потерянных WebSocket-соединений. Это конкретные недостающие части выпуска, а не причины скрывать уже подготовленный исходный код. Учебный результат завершён: каждый слой имеет своё назначение, проверяемый ожидаемый эффект и предел ответственности.
Оглавление серии · Исходные состояния проекта.
API сверены с первичной документацией 11 октября 2026 года. Пример имеет статус not_run: фактический запуск и наблюдение результата читателю предстоит выполнить самостоятельно.