# Локальный поиск по каталогу на JavaScript Исследовательский прототип к статье «Поиск по миллиону товаров на JavaScript: когда сжатый индекс проигрывает массиву». ## Запуск Распакуйте архив, перейдите в `catalog-search-lab/lab`. Нужен Node.js 24+. У ядра, сервера, тестов и измерительных скриптов нет runtime-зависимостей. ```sh node --test test/*.test.mjs node scripts/serve.mjs ``` Сервер выведет адрес демо и будет слушать только 127.0.0.1. Открывайте этот адрес в браузере; `file://` для Worker не подходит. Порт выбирается свободный: при новом порте браузер создаёт отдельное хранилище IndexedDB. Статья доступна по `/ARTICLE.html`. ## Повторить измерения ```sh node scripts/matrix.mjs node scripts/lifecycle.mjs ``` Матрица: 44 свежих процесса, 7 104 измерения. Для реального корпуса, 100 тысяч и миллиона синтетических документов (seed 2752026) — по три процесса на каждую из четырёх реализаций. Ещё два seed на 100 тысячах — по одному процессу. В каждом процессе два прогревочных и три измеряемых прохода; порядок вариантов и запросов перемешан. Проверка полного количества и хеша выдачи линейным эталоном выполняется после остановки таймера. В results/matrix-summary.json сохранены порядок запусков, группы и определение квантилей; отдельные JSON содержат все наблюдения. Lifecycle: по три процесса на режим открытия файла, три процесса для delta и компактации. Первый запуск создаст `results/input-million.csi` размером около 137 МиБ; этот промежуточный файл в архив не включён. Миллион генерируется локально. Полный прогон занимает несколько минут и требует нескольких гигабайт свободной RAM. Среда исходного опыта: Node 24.15.0, Windows, Intel Core Ultra 7 155H, 15,5 ГиБ RAM. Фоновая нагрузка не контролировалась. Приведённые диапазоны — разброс опыта, а не гарантии производительности. ## Ядро и приложение `src/engine.mjs` — измерительное ядро. Его SHA-256: `30cf0f9dbe116f9d81e686fa5348ea7cbded4f02c763ccda3c539682084fe2f3`. В нём намеренно доступны внутренние структуры для экспериментов: нельзя передавать их стороннему коду и затем считать снимок защищённым от мутации. Для приложения используйте `openCatalog(bytes, head)` из `src/catalog.mjs`. Фасад сам открывает бинарную базу, держит ядро в замыкании, копирует выдаваемые документы и состояние. `prepare` и `compact` возвращают следующий защищённый снимок. Методы: `search`, `documents`, `head`, `stats`, `serializeBase`; свойство `generation`. Worker использует именно этот API. Сборка первоначального бинарного файла — в `scripts/prepare-data.mjs`. Исходные времена поиска, подготовки delta и компактации измеряют ядро. Копирование данных фасадом, передача через Worker, сохранение в IndexedDB и отрисовка в эти времена не входят. Новая регрессия UI немедленно инвалидирует предыдущий запрос при вводе, подавляет старые ответы и ошибки, отменяет debounce при ручной отправке. Unit-тесты включают независимый поиск-эталон, повреждение формата, мутацию входного буфера, изоляцию снимков и детерминированные гонки UI. Для автоматического браузерного набора нужны отдельно установленный Chrome и `playwright-core@1.62.1`: ```sh npm install --no-save playwright-core@1.62.1 node scripts/browser-runner.mjs ``` ## Данные 32 772 реальных товара — сохранённое подмножество Open Food Facts от 5 августа 2026. Полный использованный корпус включён в `data/catalog.json`, бинарный индекс — в `data/catalog.csi`. Хеши в `data/manifest.json`. База сохраняет условия ODbL, содержимое записей — DbCL. См. `data/SOURCE.txt`. Реальный корпус и синтетический миллион независимы. Шрифт Onest: SIL Open Font License 1.1, авторы и полный текст в `web/OFL-Onest.txt`. ## Границы Точный AND по словам, приоритет полного ID. Нет морфологии, исправления опечаток, ранжирования, автоматического планировщика или автоматической компактации. Интерфейс показывает 30 карточек, но ядро получает полный ответ. Миллион проверен в Node, не на телефоне. Компактация занимает единственный Worker и задерживает последующий поиск. После загрузки запросы локальны, однако повторное открытие оболочки требует доступного локального сервера: это не offline-PWA. Проверки IndexedDB включают намеренный abort, сохранение после reload и конфликт вкладок. Отключение питания не испытывалось. С готовыми поисковыми библиотеками сравнение не проводилось.