Привет, друзья! Представляю вашему вниманию результаты небольшого исследования, посвященного очистке данных, хранящихся на стороне клиента по сигналу сервера. Речь идет об относительно новом HTTP-заголовке Clear-Site-Data. Также в этой статье мы немного поговорим про карту импортов (imports map). Статья состоит из двух частей: теоретической и практической. В теоретической части мы кратко рассмотрим карту импортов и более подробно Clear-Site-Data. В практической части мы поднимем два сервера — один будет запускаться локально и, помимо прочего, обслуживать статические файлы нашего приложения, другой мы развернем на Heroku. Сначала мы запросим данные (включая куки) от серверов, сохраним эти данные в браузере с помощью трех наиболее популярных механизмов (локальное хранилище, индексированная база данных и интерфейс кеширования), затем попробуем очистить их с помощью заголовков Clear-Site-Data. Для разрешения путей импортируемых в приложении модулей мы будем использовать карту импортов. Исходный код проекта находится здесь. О механизмах для хранения данных на стороне клиента, которые мы будем использовать в приложении, я рассказывать не буду. Вот ссылки на отличные ресурсы, посвященные каждому из них: Несмотря на то, что HTTP-кеширование — тема очень интересная, мы с ним работать не будем. Масштаб нашего приложения не позволит в должной мере оценить преимущества, предоставляемые данным механизмом. Кроме того, в конечном счете, даже при наличии соответствующих заголовков (Cache-Control, Etag и др.), браузер сам решает, что и как кешировать. При разработке приложения мы будем использовать несколько инструментов, основными из которых являются следующие: Карта импортов (imports map) позволяет использовать так называемые голые спецификаторы импорта (bare import specifiers) в инструкциях import и выражениях import() без участия сборщиков типа Webpack или других инструментов для разрешения путей импортируемых модулей во время выполнения кода. Предположим, что в нашем проекте используются библиотека lodash и утилита very-simple-fetch: Для того, чтобы импортировать эти модули без помощи "бандлера", необходимо указать полный путь к соответствующим файлам, хранящимся в директории node_modules: Карта импортов позволяет связать кастомные ключи — названия модулей — с их расположением. Для этого в теге <script> с типом importmap определяется объект с ключом imports и парами ключ / значение, где значение — это путь к модулю, а ключ — синоним (алиас) для этого пути: После определения карты импортов, у нас появляется возможность импортировать наши модули следующим образом: Карты импортов также предоставляют много других интересных возможностей. К сожалению, в настоящее время они поддерживаются только Chrome. Если вы пользуетесь другим агентом, при разработке приложения в практической части статьи импортируйте модули напрямую из node_modules. HTTP-заголовок Clear-Site-Data позволяет передавать браузеру инструкции по очистке хранящихся на стороне клиента данных. Данный заголовок принимает следующие директивы: Директивы могут указываться как по одной: так и через запятую: Последний пример аналогичен следующему: К сожалению, в настоящее время данный заголовок не поддерживается Safari (ох уж этот современный IE :)). Вот как это должно работать в теории. Скоро мы выясним, что на практике это работает немного по-другому, а кое-что и вовсе не работает. Создаем директорию для проекта, переходим в нее, инициализируем проект и устанавливаем зависимости: Создаем файл server.js для локального сервера и директорию public для статических файлов, а в ней файлы index.html, style.css и script.js: Не забудьте создать файл .gitignore с node_modules. Начнем с public/index.html. Создаем контейнер для UI и секцию с кнопками для взаимодействия с локальным сервером: Обратите внимание на атрибуты data-action кнопок. Это небольшая хитрость позволит нам сильно упростить и сократить код скрипта. А по классам, вы, наверное, догадались, какой CSS-фреймворк мы используем для стилизации. Добавляем карту импортов для модулей very-simple-fetch и idb: Честно говоря, поиск нужного файла в директории node_modules — занятие не из приятных. К тому же приходится искать не просто основной файл, но нужную версию файла. Например, ES-модуль idb хранится в директории esm. Подключаем наш скрипт с типом module: С вашего позволения файл со стилями style.css я пропущу. Переходим к public/script.js. Давайте подумаем, что должен делать наш скрипт. Вот мои идеи на этот счет: Приступим к реализации (// -> — означает сигнатуру): Обратите внимание на то, как мы формируем URL запроса. Мы добавляем к адресу сервера значение атрибута data-action кнопки. Это первая половина хитрости. Теперь займемся сервером (server.js). Что он должен делать? Я хочу, чтобы он делал следующее: Реализация: Мы передаем клиенту куки вместе с загружаемыми модулями. Получается, что мы делаем это дважды, но это не критично. Поскольку у нас один домен и названия куки совпадают, мы в итоге получим только одно куки. Обратите внимание на то, как мы извлекаем тип операции — директиву для Clear-Site-Data — из тела запроса. Мы разбиваем строку в массив по символу - и извлекаем второй элемент (элемент по индексу 1). Таким образом, если сервер получил clear-storage, то типом операции (директивой) будет storage. Также обратите внимание на то, что директива должна быть закавычена, причем кавычки обязательно должны быть двойными ("). В сообщении мы просто указываем, что данные определенного типа для localhost были удалены. Пришло время запустить сервер и убедиться в том, что все работает. Добавляем в файл package.json команду для запуска сервера для разработки: Выполняем эту команду в терминале: Данная команда запускает сервер для разработки и открывает вкладку браузера по адресу http://localhost:3000. Открываем инструменты разработчика, переходим в раздел Application (“Приложение”) и проверяем, что все наши данные успешно сохранены в браузере: Нажимаем кнопку Clear storages. Получаем сообщения Data for localhost has been removed from storage от сервера и Clear-Site-Data header on 'http://localhost:3000/clear-storage': Cleared data types: "storage". от браузера: Видим, что данные из локального хранилища, индексированной БД и локального кеша были успешно удалены. Нажатие кнопки Clear cookies приводит к удалению куки: Нажатие кнопки Clear HTTP cache, вероятно, приводит к удалению HTTP-кеша: Кажется, что все хорошо, однако нажатие кнопки Reload contexts приводит к возникновению ошибки: Текст ошибки говорит нам о том, что браузер не может распознать тип операции (тип данных для очистки). Дело в том, что директива "executionContexts" в настоящее время поддерживается только Samsung Internet, т. е. можно сказать, что не поддерживается. В сети можно найти информацию о том, что данная директива, скорее всего, будет удалена из спецификации. Дальше интересней: нажатие кнопки Clear all site data также приводит к ошибке: Хотя должно приводить к очистке данных всех типов. Здесь мы имеем дело с багом Chrome. Вот все, что мне удалось найти по данному багу. Кажется, в ближайшее время никто не собирается его фиксить. В Firefox это работает: Из всего этого можно сделать следующий вывод: сегодня в браузерах Chrome и Firefox можно безопасно использовать только директивы "storage", "cookies" и "cache". В принципе, для очистки данных всех типов в Chrome можно указать все названные директивы через запятую: это будет иметь такой же эффект, что и использование директивы *. Кажется, что эти директивы правильно интерпретируются браузером, т. е. приводят к очистке данных указанного типа. Но что насчет определения принадлежности данных к источнику ответа перед их очисткой? Для того, чтобы убедиться в том, что удаляются только такие данные необходимо поднять еще один сервер. Для чистоты эксперимента развернем этот сервер на Heroku. Начнем с самого сервера. Создаем для него директорию, переходим в нее, инициализируем проект и устанавливаем зависимости: Определяем команду для запуска сервера в package.json: Создаем файл index.js следующего содержания: Настройки cors Настройки куки Без этих настроек и еще одной на клиенте мы не сможем получить куки от "удаленного" сервера. Деплой сервера Для того, чтобы иметь возможность разворачивать приложения на Heroku, необходимо создать там аккаунт, а также глобально установить heroku-cli: Инициализируем репозиторий и добавляем в него файлы приложения: Создаем проект на Heroku: Проверяем, что наш проект привязан к Heroku-проекту, и отправляем файлы: Готово. Полную инструкцию по деплою приложения на Heroku можно найти здесь. Проверять работоспособность приложения по автоматически сгенерированной ссылке (например, в моем случае это https://hidden-sands-68187.herokuapp.com) особого смысла не имеет, но можете это сделать, если хотите: перейдите по ссылке, откройте инструменты разработчика, вставьте в консоль fetch('/clear-storage').then((response) => response.json()).then(({ message }) => console.log(message)) и нажмите Enter. Если после этого вы получили сообщение Data for heroku has been removed from storage, значит, вы все сделали правильно. Добавляем в public/index.html раздел с кнопками для взаимодействия с удаленным сервером: И вносим несколько изменений в public/script.js: Перезапускаем сервер для разработки и видим в консоли сообщение от удаленного сервера: В разделе Application находим кешированные данные и куки от heroku: Нажимаем на кнопку Clear storages, получаем сообщения об очистке хранилищ браузера, но данные из локального хранилища, индексированной БД и кеша (!) при этом не удаляются. С локальным хранилищем и БД все понятно, они принадлежат localhost, но данные в кеше, полученные от heroku, должны были удалиться: И это не баг Chrome (или баг не только Chrome), точно такой же результат мы получаем в Firefox: Директива * также не удаляет кешированные данные, полученные от heroku: К сожалению, по этой проблеме информации найти не удалось. Если вдруг вы знаете, в чем дело, пожалуйста, сообщите в комментариях. Нажимаем на кнопку Clear cookies, получаем сообщения об удалении куки, и куки от heroku благополучно удаляется: Итак, что мы имеем в сухом остатке? Карта импортов в настоящее время поддерживается только Chrome. Будет ли она поддерживаться другими браузерами, и, если будет, когда это произойдет, неизвестно. Поэтому, несмотря на интересные возможности, использовать ее при разработке реальных приложений пока нельзя. Что касается заголовка Clear-Site-Data, то, в целом, он неплохо справляется со своей задачей, однако тот факт, что он не поддерживается Safari, а также учитывая баг в Chrome и не очень понятное поведение браузеров по очистке кешированных данных, говорить о возможности его использования в продакшне также преждевременно. Пожалуй, это все, чем я хотел с вами сегодня поделиться. Благодарю за внимание и хорошего дня!  JavaScript Developer