@yonote/ssr-sync
v0.2.0
Published
Sync Yonote documents to local markdown + nav manifest for SSR and static docs sites
Maintainers
Readme
@yonote/ssr-sync
Синхронизирует документы из Yonote в локальные markdown-файлы и manifest.json — чтобы собрать документацию на Next.js / любом SSR или статическом сайте.
Пакет умеет:
- тянуть дерево документов по API-токену или share URL
- брать markdown из
documents.infoили собирать его из Yjs-состояния документа (markdownSource: 'state') - сохранять markdown на диск, скачивать картинки, вложения и обложки
- пропускать документы, которые не менялись с прошлого синка
- строить навигационный манифест (
NavNode/Manifest) с иконкой, обложкой иfullWidthдокумента - переписывать ссылки
/doc/...и share-ссылки на документы дерева на пути вашего сайта - рендерить Yonote-markdown в HTML: notice, таблицы, чекбоксы, toggle, колонки, код с подсветкой, формулы KaTeX, цвета текста, emoji, embed, содержание
React-оболочка и хелперы Next.js живут в этом же monorepo (
@wilix/yonote-ssr-react,@wilix/yonote-ssr-next) и пока публикуются отдельно / из исходников workspace.
Install
npm install @yonote/ssr-sync
# or: yarn add @yonote/ssr-syncNode.js ≥ 18.
Quick start
import { sync, renderMarkdown } from '@yonote/ssr-sync';
const result = await sync({
baseURL: 'https://yo.yonote.ru',
auth: {
// token wins over shareUrl if both are set
shareUrl: 'https://yo.yonote.ru/share/manuals',
// token: process.env.YONOTE_API_TOKEN,
},
outDir: './content/yonote',
mode: 'tree',
roots: [
{
id: 'docs',
documentUrl: 'f5c4c7fa-2635-4bc6-8d4d-b171bc66ab5b', // id or /doc/... url
basePath: '/docs',
},
],
});
console.log(`Synced ${result.manifest.roots.length} root(s)`);
console.log(renderMarkdown('# Hello\n\n:::info\nNote\n:::\n'));После sync на диске:
content/yonote/
manifest.json
roots/docs/
index.md
…
_assets/ # скачанные изображенияAuth
| Способ | Как |
| --- | --- |
| API token | auth: { token: '…' } → Authorization: Bearer … |
| Публичный share | auth: { shareUrl: 'https://…/share/…' } |
Если заданы оба, используется token.
API (главное)
| Export | Назначение |
| --- | --- |
| sync / syncWithClient | Одноразовая синхронизация roots → файлы + manifest |
| startBackgroundSync | Периодический sync (intervalMs > 0) |
| renderMarkdown / createMarkdownIt | Markdown Yonote → HTML |
| renderMarkdownWithHeadings | То же, плюс список заголовков и hasToc, если в документе есть блок «Содержание» |
| stateToMarkdown / prosemirrorJSONToMarkdown | Yjs-состояние или ProseMirror JSON документа → markdown |
| YonoteClient | Низкоуровневый HTTP-клиент (info, state, structure, resolveShare) |
| finalizeNestedDocumentsMarkdown / finalizeTreeMarkdown | Маркер вложенных документов и перезапись ссылок, обычно вызываются внутри sync |
| resolveShareLinks | Сопоставляет share-ссылки продукта с документами дерева |
Типы: SyncConfig, SyncRoot, Manifest, NavNode, …
SyncConfig (кратко)
type SyncConfig = {
baseURL: string;
auth: { token?: string; shareUrl?: string };
outDir: string;
mode: 'tree'; // zip — позже
roots: Array<{ id: string; documentUrl: string; basePath: string }>;
intervalMs?: number;
revalidateSeconds?: number;
strict?: boolean;
markdownSource?: 'text' | 'state'; // по умолчанию 'text'
concurrency?: number; // по умолчанию 6
};basePath у root должен начинаться с / (например /docs).
concurrency ограничивает, сколько документов и файлов синк качает одновременно.
Источник markdown
markdownSource: 'text' берёт поле text из documents.info, как в 0.1.x. У части документов там приходит текст без разметки.
markdownSource: 'state' запрашивает documents.state, разворачивает Yjs в ProseMirror JSON и собирает markdown своим сериализатором. Узлы, которых нет в обычном markdown (toggle, колонки, цвета, embed, содержание), пишутся в виде, который понимает renderMarkdown этого же пакета. Если state получить или разобрать не удалось, синк пишет предупреждение и берёт text.
Повторный синк
В NavNode сохраняется updatedAt. При следующем запуске синк читает прошлый manifest.json из outDir и не перекачивает документ, если updatedAt не изменился и файл на месте. Первый синк после обновления с 0.1.x качает всё заново, в старом манифесте updatedAt нет.
Nested documents и ссылки
Блок :::nested-documents после sync заменяется маркером. Список дочерних документов рисует сайт по свежему манифесту и передаёт готовый HTML в renderMarkdown(md, { nestedDocumentsHtml }). Без этой опции блок рендерится пустым.
Ссылки [Title](/doc/…) переписываются на локальные пути по sourceUrl, id и urlId документа, так что старый slug тоже находится. Share-ссылки продукта синк открывает анонимным documents.info и, если документ есть в дереве, тоже ведёт на сайт. Ссылки на документы вне дерева становятся абсолютными ссылками в Yonote и печатаются в консоль списком.
Изменения в 0.2.0
finalizeNestedDocumentsMarkdownбольше не принимаетchildrenи не вставляет список в markdown, вместо него маркер иnestedDocumentsHtmlпри рендере- пустой документ больше не заменяется списком дочерних
renderMarkdownпо умолчанию превращает embed известных продукту сервисов в iframe.skipEmbeds: trueубирает все embed,skipEmbeds: falseоставляет незнакомые обычными ссылками- вложение, которое отдало 404, в markdown заменяется адресом
yonote-missing:, рендер показывает на его месте «файл не найден»
Example app
В репозитории apps/example — Next.js 15 demo:
yarn install
yarn workspace example sync:manuals # нужен .env.manuals
yarn workspace example devReleases / CI
Версии публикуются через Gitea Release:
- Workflow делает
npm stage publish(секретNPM_STAGE_TOKEN, stage-only). - Maintainer подтверждает staged-пакет:
npm stage approve <id>(2FA) или UI на npmjs.com.
Прямой npm publish из CI со stage-токеном недоступен — так и задумано.
License
UNLICENSED (private distribution unless stated otherwise by Yonote).
