13 KiB
Аудит архитектуры (2026-10)
Статус: draft. Сырые находки read-only аудита на оценку разработчика. Подтверждённые пункты переезжают в
docs/backlog.mdили тематический документ, отклонённые удаляются; после разбора файл — вdocs/archive/.Примечание:
$effectсgoto()вSchemaToolView.svelte:529-546намеренно исключён — бесшовная смена маршрута при структурном изменении цепочки (docs/architecture.md, раздел 1) является требованием, не дефектом.
Проверено и чисто (не является находкой):
- Слоистость соблюдена: обратных импортов
core→registry/components,registry→components/routes/executorнет. - Прямых вызовов
tool.runв обходexecutor.executeнет. - DOM-API в
core/**только в санкционированныхio.ts/domText.ts. - Устаревшего Svelte 4 синтаксиса нет (
export let,$:,on:,<slot>,$store,createEventDispatcher— 0 совпадений). console.errorв lib/routes отсутствует; worker-протокол кодирует ошибки; скачивание/encode — единый источникcore/io.ts.
High
H1. SchemaToolView.svelte — god-component — СДЕЛАНО (2026-10)
Компонент уменьшен со 1115 до ~660 строк (оркестратор: derived-связки, init, handleFiles, эффекты, workspace-разметка). Декомпозиция:
- H1a: aligned-раскладка вынесена в
SchemaAlignedLayout.svelte(view-only: разметка + scoped-стили, сниппетstepCardпередаётся пропсом). - H1b: runner вынесен в
schema-tool-runner.svelte.ts(createSchemaToolRunner(ctx)); идиома «$state-объект + модульные функции» зафиксирована вdocs/architecture.md, раздел 9. - H1c: стейт шагов вынесен в
schema-tool-state.svelte.ts(createSchemaToolState(ctx): steps, touchedByStep/lastAxis, все мутации, DnD;toolSchemaOf— модульный экспорт).
H2. Race condition превью — СДЕЛАНО (2026-10)
SchemaToolView.svelte: добавлен generation-counter runGen; устаревший прогон
runChain после await выходит без записи результатов, ошибок и сброса
running (finally сбрасывает только актуальный); init() инкрементом
инвалидирует прогоны прошлого хоста.
H3. registry-schema.ts — 5 ответственностей — СДЕЛАНО (2026-10)
Файл (769 строк) разрезан на директорию web/src/lib/registry-schema/:
specs.ts (типы полей, fieldSpecs, ToolSchema), field.ts (builder DSL +
toolSchema), layout.ts (resolveLayoutGroups), aspect.ts (effectiveMax,
clampSourceAwareMaxes, withAspectLock), values.ts (дефолты и санитизация);
index.ts — баррель, пути импортов не менялись. docs/architecture.md (разделы
1, 4) обновлён. JSDoc в specs.ts сжат до однострочников — лимит плотности
комментариев (conventions/comment-format, 15%).
Med
M1. Маски через $effect вместо $derived.by — СДЕЛАНО (2026-10)
SchemaToolView.svelte: эффект вычисления maskResults заменён на
$derived.by; ручной сброс maskResults = {} в init() удалён — derived
пересчитывается сам по смене steps/source/maskOnKeys.
M2. init() не сбрасывал часть состояния — СДЕЛАНО (2026-10)
SchemaToolView.svelte: init() теперь очищает touchedByStep и lastAxis —
метки от ключей шагов прошлого хоста больше не протекают в новую цепочку.
Переработка самого паттерна $effect(() => init()) — часть H1, отдельно не
делалась.
M3. SchemaPreview.svelte — интерфейс из ~40 пропсов — СДЕЛАНО (2026-10)
Пропсы сгруппированы в 4 view-model объекта по внутреннему потребителю: head
(SchemaActions), source (SchemaSourceTile), steps (промежуточные тайлы),
result (SchemaResultTile). Интерфейсы — в schema-preview-model.ts
(PreviewHeadModel и др.); имена полей зеркалят пропсы потребителей, поэтому
внутри работает спред (<SchemaActions {...head}>). Плоскими остались 7
идентифицирующих пропсов (inputMode, resultKind, toolId, running, error,
aligned, ontogglealign).
M4. runMask обходил санитизацию параметров — СДЕЛАНО (2026-10)
SchemaToolView.svelte (derived maskResults): параметры шага теперь проходят
sanitizeSchemaParams(stepSchema, step.params, { source: input }) — тот же
контракт, что в executor/executor.ts (вход шага как source-контекст).
M5. UI-метаданные дропдауна скачивания в core-слое — СДЕЛАНО (2026-10)
OutputFormatOption/OUTPUT_FORMATS/outputFormatByMime вынесены из
core/io.ts в web/src/lib/output-formats.ts (UI-слой: label и
settings.quality дропдауна кнопки Download). В core остался только
mime-контракт OutputMime; сам io.ts таблицу не использовал. Потребители (4
файла) переведены на новый путь.
M6. Доменная политика сжатия в IO-модуле — СДЕЛАНО (2026-10)
fitWithinBytes перенесён из core/io.ts в core/compress.ts (дом стратегий
сжатия). Сигнатура с инъекцией:
fitWithinBytes(img, mime, targetBytes, quality, encodeFn) — вызывающий
(schema-tool-runner) передаёт io.encode, поэтому compress.ts остаётся
DOM-free и node-тестируемым (попутно fitWithinBytes теперь можно
юнит-тестировать с фейковым encode). io.ts вернулся к чистому IO.
M7. clamp продублирован 5 раз — СДЕЛАНО (2026-10)
Создан web/src/lib/core/math.ts (clamp, clampInt, clamp01, clampByte —
round-семантика clampByte и trunc-семантика clampInt сохранены раздельно);
локальные копии удалены из core/alpha.ts, core/background.ts,
core/color.ts, core/convolution.ts, core/geometry.ts, core/palette.ts,
core/pixel-fx.ts. Вариант с опциональными границами в registry-schema.ts
переведён на общий clamp через ?? -Infinity / ?? Infinity. Копия в
palette.test.ts оставлена — тесты независимы.
M8. Два параллельных hex-парсера
parseHex (core/alpha.ts:147, tuple, бросает ToolError) и hexToRgb
(core/palette.ts:8, объект {r,g,b}). Половина core-модулей использует первый
(affine.ts:31, geometry.ts:20, morphology.ts:123,150), другая — второй
(masks.ts:25,98, pixel-fx.ts:195, registry/tools/generate.ts:36).
M9. Гард localStorage продублирован 4 раза — СДЕЛАНО (2026-10)
Создан web/src/lib/storage.ts (storage() + safeSetItem); локальные гарды
удалены из chains.svelte.ts, favorites.svelte.ts, i18n/locale.svelte.ts,
theme.svelte.ts.
M10. Логика темы дублируется между app.html и theme.svelte.ts — ОТКЛОНЕНО
web/src/app.html:10-16 повторяет systemTheme() (theme.svelte.ts:11-16) и
захардкоживает ключ "theme" (theme.svelte.ts:3).
Решение (2026-10): оставить как есть. Inline-скрипт обязан выполниться синхронно
до парсинга контента, чтобы тема не мигала, — импортировать theme.svelte.ts из
него нельзя в принципе (модули async, app.html не проходит сборку TS).
Дублирование неизбежно.
Остаточный риск drift'а (принят, без фикса): ключ "theme", значения
"dark"/"light" и атрибут dataset.theme продублированы; при exception inline
ставит "light", а initTheme() при невалидном attr берёт systemTheme() (на
практике безразлично — inline всегда выставляет attr). Если риск когда-то
материализуется, дешёвая защита — unit-тест (~20 строк), читающий app.html и
пиннющий контракт: ключ "theme", dataset.theme, значения dark/light.
Low
L1. Флаг running общий для run() и download()
SchemaToolView.svelte:124,433,469: параллельный auto-run и клик по download
гоняют один $state; индикация и disabled ведут себя некорректно.
L2. Дублирование dismiss-логики поповеров
SchemaDownload.svelte:62-76 и ToolPickerButton.svelte:25-39 — побуквенно
одинаковые ~15 строк: pointerdown-outside + Escape + подписка на window в
$effect. Кандидат на shared action (clickOutside/popoverDismiss). Сами
эффекты корректны.
L3. Несогласованный API Toggle
SchemaToolView.svelte:811 — bind:checked={aligned};
SchemaPreview.svelte:114-118 тот же компонент — checked + onchange.
Компонент (ui/Toggle.svelte:7) поддерживает оба; стоит выбрать один паттерн.
L4. Непроверяемые as-касты схемы
SchemaToolView.svelte:97,211
(tool.schema as ToolSchema<Record<string, unknown>>) и паттерн spec as XSpec
в 14+ field-компонентах (CheckboxControl.svelte:13, ColorControl.svelte:14,
GradientControl.svelte:16 и др.); двойной каст as unknown as WorkerLike в
executor/executor.ts:35. Касты отключают проверку соответствия spec kind'у.
L5. Хардкод slug-ов вне registry
tool-icons.ts:54-142 — карта slug→icon без типизации по slug-union
(переименование slug ломает иконки молча); kit/+page.svelte:40 — захардкожены
["linear-gradient-png","resize-png","quantize-png"] (kit — витрина, не
production).
L6. Дублирование CSS-значения превью-тайла
clamp(var(--space-brand), 30vh, 60vh) в PreviewTile.svelte:96 и
SchemaResultTile.svelte:129; магические 30vh/60vh не в токенах.
L7. localStorage.setItem без try/catch вне app.html — СДЕЛАНО (2026-10)
Все setItem в theme.svelte.ts, chains.svelte.ts, favorites.svelte.ts,
locale.svelte.ts переведены на safeSetItem из web/src/lib/storage.ts
(best-effort persistence: бросок в privacy-режиме проглатывается).
L8. Переросшие файлы групп реестра и core
registry/tools/generate.ts — 969 строк (~21 инструмент), geometry.ts — 753
(~18), alpha.ts — 681 (~20): конвенция «один файл на категорию», но навигация
страдает. core/geometry.ts — 436 строк, смешаны поддомены (тайлинг, ресайн,
contentBounds, ratio). core/palette.ts — 23 экспорта, смешаны color math и
генерация PixelImage (renderSwatches/renderWheel/ renderBlend).