Архитектура
Pulse состоит из трёх независимых частей: среды разработки презентаций, переносимого формата .pulse и PWA-плеера.
┌──────────────────────────┐
│ Проект презентации │
│ HTML · CSS · entry.js │
└────────────┬─────────────┘
│ npm run pack
▼
┌──────────────────────────┐
│ product-1.2.0.pulse │
│ manifest · hashes · files│
└────────────┬─────────────┘
│ ручной импорт маркетологом
▼
┌──────────────────────────┐ ┌─────────────────────────┐
│ PWA на устройстве │ │ Analytics backend │
│ Cache Storage + IndexedDB│──────▶│ и кабинет (след. этап) │
│ Viewer без iframe │ сеть └─────────────────────────┘
└──────────────────────────┘Центральный каталог не участвует в доставке. Сервер не решает, какие презентации видны на устройстве: набор пакетов формирует сам маркетолог.
Исходники презентации
presentations/<id>/
├── deck.json
├── entry.js
├── styles.css
├── slides/
└── assets/deck.jsonзадаёт стабильный ID, версию, название и настройки движка;entry.jsсодержит код конкретной презентации и собирается как ES-модуль;styles.cssприменяется только к установленной презентации;slides/содержит упорядоченные HTML-файлы;assets/содержит все ресурсы, необходимые офлайн.
Сборщик
scripts/pack-presentation.mjs создаёт воспроизводимый ZIP-архив с расширением .pulse. Точка входа собирается через esbuild, а каждый файл фиксируется в manifest.json хешем SHA-256.
Импорт и локальное хранение
Импортёр проверяет:
- формат, версию схемы и runtime API;
- семантическую версию презентации;
- отсутствие абсолютных путей и
..(защита от ZIP Slip); - лимиты архива, количества и размера файлов;
- наличие только заявленных файлов и совпадение SHA-256.
После проверки файлы атомарно записываются в Cache Storage, а manifest и сведения об установленной версии — в IndexedDB. Service Worker отдаёт их по виртуальным URL вида:
/_pulse/packages/product-demo/1.2.0/assets/cover.webpViewer
player.html загружает точную версию пакета из активной сессии, подключает его CSS и JavaScript, переписывает относительные URL ассетов на виртуальные локальные URL и запускает существующий движок Pulse. iframe не используется.
Поскольку entry.js выполняется как обычный JavaScript, импортировать следует только доверенные пакеты.
Аналитика
Во время показа движок собирает просмотры, переходы, клики и пользовательские события. После завершения они сохраняются в IndexedDB вместе с названием клиента, пользователем, packageId и packageVersion.
В текущей версии готова локальная outbox-очередь. Backend, вход по личному ключу и интерфейс аналитики проектируются отдельным следующим слоем.
Структура приложения
src/
├── engine/ # навигация, слайды, анимации, аналитика
├── packages/ # manifest, импорт, Cache Storage, IndexedDB, SW routing
├── player/ # запуск установленного пакета
├── app/ # PWA: список пакетов, клиент, активный показ, outbox
├── components/ # общие Web Components
└── sw.js # offline shell и виртуальные URL пакетов