AnnaSimferopol's Blog

Без названия

No Комментарии


🗂️ Реестры проекта: руководство для редакторов и разработчиков

Для кого эта документация: для контент-менеджеров (гуманитариев) и разработчиков, помогающих вести мемориальный проект. Экспертиза не требуется — сложные вещи объясняем простым языком.
Последняя сверка с кодом: src/data/authors.yaml, src/content.config.ts (Astro 6.x), ветка main-qwen3.

1. Введение: Философия реестров

1.1. Единый источник правды (Single Source of Truth)

Представьте: один и тот же автор — скажем, поисковый отряд «Ак-Монай» — упомянут в 30 статьях. Если данные об отряде хранятся внутри каждой статьи (во frontmatter), то при опечатке в названии или смене ссылки вам придется править 30 файлов. Забыли один — и на одном сайте висит устаревшая ссылка.
Мы поступаем иначе. Все «справочные» сущности — авторы, источники, войсковые соединения, кладбища — живут в одном YAML-файле-реестре. Статья же только ссылается на запись реестра по её короткому id. Исправили запись в одном месте — обновился весь сайт.

1.2. Три главных реестра проекта

Реестр
Файл
Что хранит
Статус
Авторы и источники
src/data/authors.yaml
Персоны, библиотеки, сообщества, организации, редакции, архивные источники
✅ работает
Войсковые соединения
src/data/units.yaml
Части, дивизии, полки, отряды, в которых служили павшие
✅ зарегистрирован, наполняется
Мемориальные кладбища
src/data/cemeteries.yaml
Локации захоронений, паспорта объектов культурного наследия, координаты, охранные статусы
✅ зарегистрирован, наполняется
Все три реестра пересекаются: карточка персоны сможет ссылаться на id из units.yaml (где служил) и cemeteries.yaml (где захоронен). Об этом — в разделе 5.

1.3. ⚠️ Главный принцип: неинвазивность (Non-destructive approach)

Проект построен на базе коммерческого шаблона. Мы не имеем права разрушать или глубоко переписывать его оригинальную архитектуру — иначе мы потеряем возможность получать обновления шаблона, а любая правка станет минным полем для будущих мержей.
Наши расширения — это «аддоны» (надстройки), которые работают параллельно с оригинальным кодом:
Что делаем
Как именно (изолированно)
Добавляем реестр
Новый файл src/data/*.yaml + отдельная коллекция в content.config.ts, оригинальные коллекции posts/about не трогаем
Связываем статью с реестром
Одно необязательное поле authorIds добавлено в схему posts; без него всё работает как раньше («мягкое» разрешение: пустой массив = нет автора)
Отображаем данные
Наши компоненты (PostAuthor.astro) и утилиты JSON-LD рендерятся условно — только если ссылки есть; шаблонный рендер постов не ломается
Пишем документацию
Отдельная папка docs/ — ничего общего с кодом шаблона
Правило большого пальца: если изменение требует переписать логику шаблона — остановитесь и спросите, нельзя ли сделать обёртку рядом. Почти всегда можно.

2. Подробный гайд по authors.yaml

2.1. Структура файла

Файл — это один YAML-список (массив) объектов. Каждая запись начинается с дефиса - на нулевом отступе:
yaml
Между записями принято ставить разделительный комментарий — это помогает глазу и поиску:
yaml

2.2. Поле type и зависимость набора полей

Каждая запись обязана иметь type — строго одно из шести значений:
type
Кого записываем
Пример
персона
Живой или исторический человек
исследователь, волонтёр, автор очерка
библиотека
Библиотека / читальня
научная библиотека имени …
сообщество
Отряд, клуб, движение
поисковый отряд «Ак-Монай»
организация
Юрлицо, музей, архив
общество краеведения
редакция
СМИ, интернет-издание
Bospor.com.ua
источник
Книга, архивный фонд, база данных
памятная книга, ЦАМО
Тип определяет, какие поля осмысленно заполнять (технически все поля кроме id, name, type необязательны, но логика такая):
Группа полей
Поля
персона
организации (библиотека, сообщество, организация, редакция, источник)
Внутренние (для сайта)
credentials, access, license_note, link, note
✅
✅
Schema.org — общие
url, sameAs, image
✅
✅
Schema.org — для персон
jobTitle, affiliation
✅
❌ оставить пустыми
Schema.org — для организаций
schema_type, legal_name, alternate_name, founding_date, dissolution_date, successor, predecessor, parent_organization, location
❌ оставить пустыми
✅
schema_type — техническое имя схемы Schema.org (Person / Organization). Он должен согласовываться с type: персона → Person, все остальные → обычно Organization.

2.3. Строгая уникальность id

id — это адрес записи. На него ссылаются статьи (authorIds), на нем строится JSON-LD, по нему люди ищут запись в файле. Поэтому:
  1. id не повторяется внутри файла. Две записи с одинаковым id = ошибка сборки (Duplicate entry id) — Astro не соберёт контент.
  2. id не повторяется между всеми файлами реестров. Когда появятся units.yaml и cemeteries.yaml, идентификаторы должны быть уникальны и между ними тоже. Если редакция совпадает с одноимённым сообществом — придумайте разные ключи: redaktsiya-lenino vs soobshchestvo-lenino.
  3. Формат id: строчные латинские буквы, цифры и дефисы (транслит): ak-monay, nevzorov-b-i, redaktsiya. Без пробелов, точек, кавычек и кириллицы.
  4. id неизменяем после публикации. Переименовали — порвали все ссылки. Если запись ошибочна — лучше создать новую с новым id, а старую пометить в note.
Поле name при этом может быть каким угодно (полное ФИО, название с кавычками) — оно для людей, id — для машины.

3. Технический нюанс: даты и «умный» парсинг YAML

3.1. Феномен smart parsing

YAML — язык слишком заботливый. Если написать дату без кавычек:
yaml
парсер YAML решит: «это же дата!» — и превратит её в объект JavaScript Date, а не в строку. Наша Zod-схема ожидает строку и на объект заругается:
(Да, объект. Для компьютера Date — объект, хотя человеку видно «просто дату».)

3.2. Наше решение: z.preprocess

Чтобы не заставлять редакторов помнить про тонкости парсеров, в src/content.config.ts для полей дат стоит автоматический «переводчик»:
ts
То есть: что бы YAML ни сконвертировал в Date, Zod сам вернёт его к каноничной строке YYYY-MM-DD. Данные не портятся, сборка не падает.

3.3. ✅ Правило: даты пишем строго БЕЗ кавычек

yaml
Почему так: наш линтер ESLint с плагином для YAML правилом yaml/plain-scalar подсказывает, что скаляр, который YAML может прочитать сам (число, дата, true), не нужно оборачивать в кавычки без необходимости. Даты без кавычек + preprocess в схеме = чистый линтер и чистая сборка. Два механизма страховки работают вместе.
Формат даты — только ISO: ГГГГ-ММ-ДД. Если известен только год — пишите строкой 1862 (Zod примет любую непустую строку; partial-дату вида 1862-07 тоже можно, но полный ISO предпочтительнее).

4. Правила заполнения пустых значений

Запись заполняет все поля из канона — даже те, что не нужны. Пустое — не значит «отсутствует», пустое значит «проверено, информации нет». Это делает записи однотипными и удобными для поиска.
Тип значения
Как пишем
Пример
Пустая строка
два апострофа ''
license_note: ''
Пустой массив
квадратные скобки []
sameAs: []
Неизвестно/нет данных
'нет данных' в note или ''
access: ''
Целая строка поля не нужна
можно пропустить строку (поле optional в Zod)
—
Категорически запрещено:
yaml
Лайфхак: скопируйте последнюю запись того же type, вставьте ниже, замените значения. Канон не сломается, опечаток в именах полей не будет.

5. Реестры units.yaml и cemeteries.yaml — зарегистрированы и работают

(раздел для разработчиков и продвинутых редакторов)
Архитектура скопирована с паттерна authors.yaml один в один, как и планировалось:
  1. YAML-файлы в src/data/ — тот же формат «массив записей с id».
  2. В content.config.ts — отдельные defineCollection (unitSchema, cemeterySchema) с изолированными loader'ами (glob({ pattern: 'units.yaml', base: './src/data' })). Оригинальные коллекции шаблона (posts/about) не тронуты — принцип неинвазивности соблюдён.
  3. Уникальность id — глобальная, через все три реестра (см. п. 2.3).
  4. Даты — общий helper isoDate() (z.preprocess: Date → строка YYYY-MM-DD).
  5. Документальные позиции не меняем. Поля, где источник даёт «строку, похожую на число» (passport_number с слэшем 82-289/2023, obd_document_id, buried_known/unknown), защищены z.preprocess от number→string; значения из CSV переносятся символ в символ.
  6. Внешние ссылки на сервисы МО РФ (obd-memorial.ru, memmo.ru): официальное предупреждение Минобороны требует российских сертификатов / корневого сертификата Минцифры. Поэтому: ссылка хранится как есть + метка [Минцифры] в note; автоматическая проверка доступности при сборке НЕ делается; карточка самодостаточна (данные CSV дублируются в полях).

5.1. Фактические поля units.yaml (схема unitSchema)

Поле
Тип
Смысл
id
string
префикс unit- + код карточки словаря: unit-276-00
name
string
развёрнутое имя из словаря warunits
type
enum
армия, фронт, корпус, дивизия, бригада, полк, батальон, рота, батарея, станция, прочее
alternate_names
array
все варианты написания («Написания» словаря)
short_name
string
краткое имя для подписей/таблиц
parent_unit
string
id вышестоящего соединения (или '')
front, army
string
подчиненность (фронт, армия)
commander
string
командир(ы), ФИО строкой
formation_date, disbandment_date
date-string
даты без кавычек, ISO
battles
array
массив сражений/операций
obd_source_doc
string
путь к документу-источнику в docs/
schema_type, url, sameAs, image, license_note, access, note
служебные
как в authors-реестре

5.2. Фактические поля cemeteries.yaml (схема cemeterySchema)

Поле
Тип
Смысл
id
string
география + объект: simferopolskiy-trudovoe, ниши — суффикс nisha-NN
name, type
string
учётное название; тип открытый (братская могила, некрополь, …)
country, region, location
string
привязка из CSV (current_burial)
coordinates
string
широта, долгота — пусто, пока нет данных
protection_status
string
охранный статус ОКН
passport_number
string
ВМС-номер учёта КАК ЕСТЬ (82-289/2023)
obd_document_id
string
якорь документа OBD для перепроверки
primary_burial_object, rebural_from
string
первичное захоронение / откуда перенесено (орфография OBD сохранена)
date_created, date_last_burial
date-string
даты без кавычек; «..1944» → только год + пояснение в note
burial_state
string
состояние захоронения
graves_count, buried_total
number
числа из CSV
buried_known, buried_unknown
string
известные/неизвестные (OBD даёт то числа, то пустые)
sponsor
string
шеф / балансодержатель
units, sources_authors
array
связи с units.yaml / authors.yaml
url, schema_type: Place, sameAs, image, license_note, access, note
служебные
ссылка OBD + метка [Минцифры] в note

5.3. Как контент-менеджер связывает сущности

Точно так же, как сейчас связывает статью с авторами — массивами ID:
yaml
Плюс внутри самих реестров перекрёстные ссылки уже предусмотрены: запись кладбища содержит поля units (кто здесь захоронен) и sources_authors (ид источников). ID во всех массивах — строки в кавычках (массив ID не является датой/числом, кавычки здесь обязательны и линтеру не мешают).
⚠️ Ссылки «мягкие» (soft resolve): несуществующий ID не должен ронять сборку — компонент просто проигнорирует его и выведет предупреждение. Так защищает нас от опечаток редактора.

6. Словарь типичных ошибок (перевод на человеческий)

Что написано в ошибке
Что это значит по-человечески
Что делать
InvalidContentEntryDataError: authors
«Запись в реестре не соответствует правилам» — где-то опечатка в имени поля, неверный type, или поле имеет не тот тип данных
Читайте строку ниже сообщения — там путь к проблемному полю (например, type: Expected one of "персона" | ...). Исправьте указанную запись
Expected type "string", received "object"
«Ждал текст, а получил структуру» — чаще всего это дата без кавычек стала Date-объектом, либо вместо строки случайно вставили список/вложенный блок
Проверьте поле из сообщения: строка должна оставаться строкой (для дат — см. п. 3: оставьте как есть, preprocess сам всё починит; если ошибка не проходит — вероятно, поле забыли добавить в preprocess)
Expected type "string", received "null" / undefined
«Поле оказалось совсем пустым» — поставили : без значения или null
Заполните '' (строка) или [] (массив). См. раздел 4
Duplicate entry id
«Два объекта называются одинаково для машины» — в списке два одинаковых id
Переименуйте id одного из них по правилам п. 2.3 (уникальность глобальная, через все реестры)
Expected type "string", received "number"
«YAML прочитал число вместо строки» — бывает с полями типа ВМС-номеров и ID документов OBD
Не кавычьте вручную: схемы passport_number, obd_document_id, buried_known/unknown уже содержат z.preprocess number→string. Если ошибка на новом поле — добавьте preprocess в схему
Invalid enum value у units.type
«Тип соединения не из списка» — например написали отряд, а enum его не содержит
Используйте значения enum: армия / фронт / корпус / дивизия / бригада / полк / батальон / рота / батарея / станция / прочее. Не хватает типа — обсудите расширение enum с разработчиком
Cannot parse YAML file / bad indentation
«Файл свёрстан не по правилам» — съехал отступ, дефис не с начала строки, табуляция вместо пробелов
Отступы — только пробелы (2 на уровень); каждая запись начинается с - на нулевом уровне; проверьте строку, названную в сообщении
Сборка прошла, но автор не виден в статье
ID из authorIds нет в реестре (опечатка) или массив не указан
Сверьте id символ в символ с реестром; регистр и дефисы важны
Универсальный порядок действий при любой ошибке:
  1. Прочитайте сообщение целиком — там указаны файл, запись и поле.
  2. Откройте указанный файл и найдите запись по id из сообщения.
  3. Сравните с соседней «рабочей» записью того же типа.
  4. После правки: npm run build (или npx astro check) — сборка должна пройти молча.

Приложение А. Готовая шапка-шпаргалка для authors.yaml

Скопируйте этот блок в самое начало файла (заменив текущую шапку) — он вечная инструкция прямо на рабочем месте:

Leave a Comment