npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yonote/ssr-sync

v0.2.0

Published

Sync Yonote documents to local markdown + nav manifest for SSR and static docs sites

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-sync

Node.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 dev

Releases / CI

Версии публикуются через Gitea Release:

  1. Workflow делает npm stage publish (секрет NPM_STAGE_TOKEN, stage-only).
  2. Maintainer подтверждает staged-пакет: npm stage approve <id> (2FA) или UI на npmjs.com.

Прямой npm publish из CI со stage-токеном недоступен — так и задумано.

License

UNLICENSED (private distribution unless stated otherwise by Yonote).