diff --git a/.github/workflows/readme-translate.yml b/.github/workflows/readme-translate.yml new file mode 100644 index 0000000000..9f8ed95438 --- /dev/null +++ b/.github/workflows/readme-translate.yml @@ -0,0 +1,122 @@ +name: Sync README Translations + +on: + push: + branches: [dev] + paths: + - 'README.md' + pull_request: + branches: [dev] + paths: + - 'scripts/readme/**' + - '.github/workflows/readme-translate.yml' + workflow_dispatch: + inputs: + languages: + description: 'Comma-separated language codes' + default: 'zh,ru' + force: + description: 'Retranslate everything, ignoring the cache' + type: boolean + default: false + +concurrency: + # Pull request test runs get their own group so they never displace a pending translation run. + group: readme-translate${{ github.event_name == 'pull_request' && format('-pr-{0}', github.event.number) || '' }} + cancel-in-progress: false + +permissions: {} + +jobs: + test: + name: Test Translation Helpers + if: ${{ github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Set Up Node.js + uses: actions/setup-node@v5 + with: + node-version: '24.16.0' + + - name: Install Translation Dependencies + run: npm ci --prefix scripts/readme --ignore-scripts --no-audit --no-fund --registry=https://registry.npmjs.org/ + + - name: Test Translation Helpers + run: node --test scripts/readme/lib.test.mjs + + translate: + name: Translate README + # Unset on forks and until a maintainer picks a model, so the job skips instead of failing. + if: ${{ github.event_name != 'pull_request' && vars.README_TRANSLATE_MODEL != '' }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + with: + ref: dev + persist-credentials: false + + - name: Set Up Node.js + uses: actions/setup-node@v5 + with: + node-version: '24.16.0' + + - name: Install Translation Dependencies + run: npm ci --prefix scripts/readme --ignore-scripts --no-audit --no-fund --registry=https://registry.npmjs.org/ + + - name: Test Translation Helpers + run: node --test scripts/readme/lib.test.mjs + + - name: Translate Changed README Sections + env: + README_TRANSLATE_BASE_URL: ${{ secrets.README_TRANSLATE_BASE_URL }} + README_TRANSLATE_API_KEY: ${{ secrets.README_TRANSLATE_API_KEY }} + README_TRANSLATE_MODEL: ${{ vars.README_TRANSLATE_MODEL }} + LANGUAGES: ${{ inputs.languages || 'zh,ru' }} + FORCE: ${{ inputs.force && '--force' || '' }} + run: node scripts/readme/index.mjs "--langs=$LANGUAGES" $FORCE + + - name: Create Pull Request + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 + with: + token: ${{ github.token }} + add-paths: | + README.md + README.*.md + .github/readme-i18n.json + commit-message: 'i18n: Sync README Translations' + base: dev + branch: i18n/readme-translations + title: '🌐 i18n: Sync README Translations' + body: | + ## Summary + + `README.md` changed on `dev`, so the Chinese and Russian READMEs are regenerated from it. Only sections whose English text changed were retranslated; the rest comes from the cache in `.github/readme-i18n.json`. Each section is checked against its source for identical links, image paths, HTML tags and list structure before it is accepted. + + ## Type of change + + * [x] Translation + + ## Testing + + * `node --test scripts/readme/lib.test.mjs` passed in the workflow run. + * Every regenerated section kept its source links, image paths, HTML tags and list structure. + + ## Risk / compatibility + + None. Only README files and the translation cache change. + + ## Checklist + + * [x] The change does not introduce new warnings or errors + labels: '🌍 i18n' diff --git a/scripts/readme/index.mjs b/scripts/readme/index.mjs new file mode 100644 index 0000000000..da66c7cd21 --- /dev/null +++ b/scripts/readme/index.mjs @@ -0,0 +1,164 @@ +#!/usr/bin/env node + +import { readFile, writeFile } from 'node:fs/promises'; +import { + LANGUAGES, + buildMessages, + cleanOutput, + definitions, + hash, + isSwitcher, + localizeAnchors, + needsTranslation, + renderSwitcher, + reusable, + splitChunks, + validate, +} from './lib.mjs'; + +const SOURCE = 'README.md'; +const CACHE = '.github/readme-i18n.json'; +const ATTEMPTS = 3; +const CONCURRENCY = 4; + +const args = process.argv.slice(2); +const force = args.includes('--force'); +const langsArg = args.find((arg) => arg.startsWith('--langs='))?.slice('--langs='.length); +const langs = (langsArg || Object.keys(LANGUAGES).join(',')) + .split(',') + .map((code) => code.trim()) + .filter(Boolean); + +const baseUrl = process.env.README_TRANSLATE_BASE_URL?.replace(/\/+$/, ''); +const apiKey = process.env.README_TRANSLATE_API_KEY; +const model = process.env.README_TRANSLATE_MODEL; +const missing = Object.entries({ + README_TRANSLATE_BASE_URL: baseUrl, + README_TRANSLATE_API_KEY: apiKey, + README_TRANSLATE_MODEL: model, +}) + .filter(([, value]) => !value) + .map(([key]) => key); +if (missing.length > 0) { + console.error(`Missing environment variables: ${missing.join(', ')}`); + process.exit(2); +} +for (const code of langs) { + if (!LANGUAGES[code]) { + console.error(`Unsupported language: ${code}`); + process.exit(2); + } +} + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +async function complete(code, chunk) { + const response = await fetch(`${baseUrl}/chat/completions`, { + method: 'POST', + headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` }, + body: JSON.stringify({ model, messages: buildMessages(LANGUAGES[code].name, chunk) }), + signal: AbortSignal.timeout(120_000), + }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const data = await response.json(); + const content = data?.choices?.[0]?.message?.content; + if (typeof content !== 'string' || content.trim() === '') throw new Error('empty completion'); + return cleanOutput(content); +} + +async function translate(code, chunk, context) { + let reason = ''; + for (let attempt = 1; attempt <= ATTEMPTS; attempt += 1) { + try { + const output = await complete(code, chunk); + const problems = validate(chunk, output, code, context); + if (problems.length === 0) return output; + reason = problems.join('; '); + } catch (error) { + reason = error instanceof Error ? error.message : 'request failed'; + } + if (attempt < ATTEMPTS) await sleep(1000 * 2 ** (attempt - 1)); + } + const preview = chunk.slice(0, 80).replace(/\s+/g, ' '); + throw new Error(`${code} translation failed (${reason}) for: ${preview}`); +} + +async function pool(items, worker) { + const queue = [...items]; + await Promise.all( + Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => { + while (queue.length > 0) await worker(queue.shift()); + }), + ); +} + +const readIfExists = async (file) => readFile(file, 'utf8').catch(() => null); + +const source = await readFile(SOURCE, 'utf8'); +const units = splitChunks(source); +const chunks = units.map((unit) => unit.text); +const context = definitions(source); +const stored = JSON.parse((await readIfExists(CACHE)) ?? '{}'); +const cache = { version: 1 }; +const outputs = {}; +const available = await Promise.all( + Object.entries(LANGUAGES).map(async ([code, { file }]) => + langs.includes(code) || (await readIfExists(file)) !== null ? code : null, + ), +).then((codes) => codes.filter(Boolean)); + +for (const code of langs) { + const { file } = LANGUAGES[code]; + const previous = force ? {} : (stored[code] ?? {}); + const next = {}; + const pending = []; + for (const chunk of chunks) { + if (isSwitcher(chunk) || !needsTranslation(chunk, context)) continue; + const key = hash(chunk); + const cached = reusable(previous[key], chunk, code, context); + if (cached !== undefined) next[key] = cached; + else if (!pending.some((item) => item.key === key)) pending.push({ key, chunk }); + } + console.log( + `${code}: ${pending.length} chunk(s) to translate, ${Object.keys(next).length} cached`, + ); + await pool(pending, async ({ key, chunk }) => { + next[key] = await translate(code, chunk, context); + }); + const assembled = units + .map(({ text, joiner }, index) => { + const part = isSwitcher(text) ? renderSwitcher(code, available) : text; + const out = !isSwitcher(text) && needsTranslation(text, context) ? next[hash(text)] : part; + return index === 0 ? out : joiner + out; + }) + .join(''); + const body = localizeAnchors(source.replace(/\r\n/g, '\n'), assembled); + const notice = + ''; + outputs[file] = `${notice}\n\n${body}\n`; + cache[code] = Object.fromEntries( + chunks + .filter((chunk) => !isSwitcher(chunk) && needsTranslation(chunk, context)) + .map((chunk) => [hash(chunk), next[hash(chunk)]]), + ); +} + +for (const code of Object.keys(LANGUAGES)) { + if (!langs.includes(code) && stored[code]) cache[code] = stored[code]; +} + +// Languages not selected keep their body, but their switcher must list every available language. +for (const [code, { file }] of Object.entries(LANGUAGES)) { + const existing = langs.includes(code) ? null : await readIfExists(file); + const current = existing && splitChunks(existing).find((unit) => isSwitcher(unit.text)); + if (!current || current.text === renderSwitcher(code, available)) continue; + outputs[file] = existing.replace(current.text, () => renderSwitcher(code, available)); +} + +const switcher = chunks.find(isSwitcher); +if (switcher && switcher !== renderSwitcher('en', available)) { + outputs[SOURCE] = source.replace(switcher, () => renderSwitcher('en', available)); +} + +for (const [file, content] of Object.entries(outputs)) await writeFile(file, content); +await writeFile(CACHE, `${JSON.stringify(cache, null, 2)}\n`); diff --git a/scripts/readme/lib.mjs b/scripts/readme/lib.mjs new file mode 100644 index 0000000000..81d926c214 --- /dev/null +++ b/scripts/readme/lib.mjs @@ -0,0 +1,376 @@ +import { createHash } from 'node:crypto'; +import GithubSlugger from 'github-slugger'; +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { gfmFromMarkdown } from 'mdast-util-gfm'; +import { gfm } from 'micromark-extension-gfm'; + +export const LANGUAGES = { + zh: { + name: 'Simplified Chinese', + label: '中文', + file: 'README.zh.md', + script: /\p{Script=Han}/u, + }, + ru: { name: 'Russian', label: 'Русский', file: 'README.ru.md', script: /\p{Script=Cyrillic}/u }, +}; + +export const GLOSSARY = [ + 'LibreChat', + 'Agents', + 'MCP', + 'Artifacts', + 'Skills', + 'Subagents', + 'Code Interpreter', + 'OpenAI', + 'Anthropic', + 'Docker', + 'Helm', + 'Railway', + 'Zeabur', + 'Sealos', + 'API', + 'URL', + 'SDK', + 'OAuth', + 'JSON', + 'YAML', + 'CLI', + 'npm', + 'GitHub', + 'Discord', + 'YouTube', +]; + +export const hash = (text) => createHash('sha256').update(text).digest('hex').slice(0, 16); + +const GLOSSARY_PATTERN = new RegExp( + `\\b(?:${GLOSSARY.map((term) => term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})(?:['’]s|s)?\\b`, + 'gi', +); +const MIN_WORDS_FOR_CONTENT_CHECK = 3; +const MIN_LENGTH_RATIO = 0.2; +const MAX_UNTRANSLATED_SHARE = 0.6; +const TEXT_ATTRIBUTE = /(?]+)/g; +const HTML_PIECE = /|<\/?[a-zA-Z][^>]*>/g; + +const parse = (markdown) => + fromMarkdown(markdown, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] }); + +const unquote = (value) => value.replace(/^(["'])([\s\S]*)\1$/, '$2'); + +/** + * Splits Markdown into the units that are translated and cached: each top-level block, with list + * items taken one by one. `joiner` is the separator that precedes a unit when they are reassembled. + */ +export function splitChunks(markdown) { + const text = markdown.replace(/\r\n/g, '\n'); + const slice = (node) => text.slice(node.position.start.offset, node.position.end.offset); + const chunks = []; + for (const node of parse(text).children) { + if (node.type !== 'list') { + chunks.push({ text: slice(node), joiner: '\n\n' }); + continue; + } + node.children.forEach((item, index) => { + const joiner = index === 0 || node.spread ? '\n\n' : '\n'; + chunks.push({ text: slice(item), joiner }); + }); + } + return chunks; +} + +/** + * Every link reference and footnote definition in a document. A chunk is validated with these + * appended, so a reference-style link or footnote resolves the same way it does in the whole README. + */ +export function definitions(markdown) { + const text = markdown.replace(/\r\n/g, '\n'); + const found = []; + walk(parse(text), (node) => { + if (node.type === 'definition' || node.type === 'footnoteDefinition') { + found.push(text.slice(node.position.start.offset, node.position.end.offset)); + } + }); + return found.join('\n\n'); +} + +const SWITCHER_ITEM = '(?:[^<]+|[^<]+)'; +const SWITCHER = new RegExp(`^

\\s*(?:${SWITCHER_ITEM}\\s*·?\\s*)+

$`); + +/** True for the language switcher block in any README, whichever language is current. */ +export const isSwitcher = (chunk) => SWITCHER.test(chunk.trim()); + +/** `available` lists the language codes whose README exists or is being generated. */ +export function renderSwitcher(current, available = Object.keys(LANGUAGES)) { + const entries = [ + { code: 'en', label: 'English', file: 'README.md' }, + ...Object.entries(LANGUAGES) + .filter(([code]) => available.includes(code)) + .map(([code, { label, file }]) => ({ code, label, file })), + ]; + const items = entries.map((entry) => + entry.code === current + ? `${entry.label}` + : `${entry.label}`, + ); + return `

\n ${items.join(' ·\n ')}\n

`; +} + +/** Change in ``/`
` nesting that an HTML fragment causes. */
+const rawCodeDelta = (html) =>
+  (html.match(/<(?:code|pre)[\s>]/gi) ?? []).length -
+  (html.match(/<\/(?:code|pre)>/gi) ?? []).length;
+
+function walk(node, visit) {
+  visit(node);
+  for (const child of node.children ?? []) walk(child, visit);
+}
+
+/** Nodes whose alt text or title a reader sees. */
+const LABELED_NODES = new Set(['image', 'imageReference', 'link', 'definition']);
+
+/**
+ * Words a reader sees: text, image alt, and alt, title and aria-label values, minus glossary terms.
+ * `context` resolves reference links without contributing words of its own.
+ */
+function proseWords(markdown, context = '', withAttributes = true) {
+  const parts = [];
+  let rawCode = 0;
+  walk(parse(context ? `${markdown}\n\n${context}` : markdown), (node) => {
+    if (node.position && node.position.start.offset >= markdown.length) return;
+    if (node.type === 'html') rawCode = Math.max(rawCode + rawCodeDelta(node.value), 0);
+    if (node.type === 'text' && rawCode === 0) parts.push(node.value);
+    else if (withAttributes && LABELED_NODES.has(node.type)) {
+      if (node.alt) parts.push(node.alt);
+      if (node.title) parts.push(node.title);
+    } else if (node.type === 'html') {
+      if (withAttributes) {
+        for (const match of node.value.matchAll(TEXT_ATTRIBUTE)) parts.push(unquote(match[2]));
+      }
+      const visible = node.value.replace(/<(code|pre)[\s>][\s\S]*?<\/\1>/gi, ' ');
+      parts.push(visible.replace(HTML_PIECE, ' '));
+    }
+  });
+  const text = parts
+    .join(' ')
+    .replace(/https?:\/\/\S+/g, ' ')
+    .replace(GLOSSARY_PATTERN, ' ');
+  return text.match(/\p{L}[\p{L}'’-]*/gu) ?? [];
+}
+
+/** True when a chunk holds prose worth sending to the model. */
+export const needsTranslation = (markdown, context = '') =>
+  proseWords(markdown, context).length > 0;
+
+const normalizeTag = (tag) =>
+  tag.startsWith('\n\n## Heading text here';
+  assert.ok(validate(source, '\n\n## Заголовок здесь', 'ru').length > 0);
+  assert.deepEqual(validate(source, '\n\n## Заголовок здесь', 'ru'), []);
+});
+
+test('validate resolves reference links against the document definitions', () => {
+  const source = 'Read the [setup guide][setup] before you start.';
+  const context = definitions(`${source}\n\n[setup]: https://x.y`);
+  assert.equal(context, '[setup]: https://x.y');
+  const renamed = 'Прочитайте [руководство][документация] перед началом.';
+  assert.ok(validate(source, renamed, 'ru').length === 0, 'without context the reference is text');
+  assert.ok(validate(source, renamed, 'ru', context).length > 0);
+  assert.deepEqual(
+    validate(source, 'Прочитайте [руководство][setup] перед началом.', 'ru', context),
+    [],
+  );
+});
+
+test('reusable keeps a valid cached translation and drops a stale or malformed one', () => {
+  const source = 'Read the setup guide before you start.';
+  const good = 'Прочитайте руководство по установке перед началом.';
+  assert.equal(reusable(good, source, 'ru'), good);
+  assert.equal(reusable(source, source, 'ru'), undefined);
+  assert.equal(reusable({ text: good }, source, 'ru'), undefined);
+  assert.equal(reusable(undefined, source, 'ru'), undefined);
+});
+
+test('validate keeps code inside a raw HTML block verbatim', () => {
+  const block = '
Install the app here: npm install
'; + assert.ok( + validate(block, '
Установите приложение здесь: npm uninstall
', 'ru') + .length > 0, + ); + assert.deepEqual( + validate(block, '
Установите приложение здесь: npm install
', 'ru'), + [], + ); +}); + +test('validate accepts a translated shortcut reference written in full form', () => { + const context = '[Installation]: https://x.y'; + const source = 'Read the [Installation] guide first.'; + assert.deepEqual( + validate(source, 'Сначала прочитайте руководство [Установка][Installation].', 'ru', context), + [], + ); + assert.ok( + validate(source, 'Сначала прочитайте руководство [Установка].', 'ru', context).length > 0, + ); +}); + +test('needsTranslation and validate read the alt text of a reference-style image', () => { + const context = '[badge]: https://x.y/badge.svg'; + const source = '![Build status][badge]'; + assert.equal(needsTranslation(`${source}\n\n${context}`), true); + assert.deepEqual(validate(source, '![Статус сборки][badge]', 'ru', context), []); + assert.ok(validate(source, '![Статус сборки][значок]', 'ru', context).length > 0); +}); + +test('reference images keep alt presence and definition titles are translated', () => { + const context = '[badge]: https://x.y/badge.svg'; + const source = 'See the ![Build status][badge] here.'; + assert.ok(validate(source, 'Смотрите ![][badge] здесь.', 'ru', context).length > 0); + const definition = '[docs]: https://x.y "Read the documentation"'; + assert.equal(needsTranslation(definition), true); + assert.deepEqual(validate(definition, '[docs]: https://x.y "Прочитать документацию"', 'ru'), []); + assert.ok(validate(definition, '[docs]: https://x.y', 'ru').length > 0); +}); + +test('a reference identifier resolved by the document definitions is not prose', () => { + const context = '[docs]: https://x.y'; + assert.equal(needsTranslation('[API][docs]'), true); + assert.equal(needsTranslation('[API][docs]', context), false); + const source = 'Read the [API][docs] reference first.'; + assert.deepEqual( + validate(source, 'Сначала прочитайте справочник [API][docs].', 'ru', context), + [], + ); + assert.ok(validate(source, source, 'ru', context).length > 0); +}); + +test('validate resolves footnote references against the document footnotes', () => { + const context = definitions('Read this[^note] first.\n\n[^note]: An explanation.'); + assert.equal(context, '[^note]: An explanation.'); + const source = 'Read this[^note] before you start.'; + assert.deepEqual(validate(source, 'Прочитайте это[^note] перед началом.', 'ru', context), []); + assert.ok(validate(source, 'Прочитайте это[^другое] перед началом.', 'ru', context).length > 0); +}); + +test('localizeAnchors points fragment links at the translated heading anchors', () => { + const source = + '# ✨ Features\n\nSee [Features](#-features), [setup][s] and setup.\n\n## Setup\n\n## Setup\n\n[s]: #setup-1'; + const translated = + '# ✨ 功能\n\n参见 [功能](#-features)、[设置][s] 和 设置。\n\n## 设置\n\n## 设置\n\n[s]: #setup-1'; + assert.equal( + localizeAnchors(source, translated), + '# ✨ 功能\n\n参见 [功能](#-功能)、[设置][s] 和 设置。\n\n## 设置\n\n## 设置\n\n[s]: #设置-1', + ); +}); + +test('localizeAnchors leaves external links and unknown fragments alone', () => { + const source = '## Setup\n\n[Docs](https://example.com/#setup) and [Top](#top)'; + const translated = '## 设置\n\n[文档](https://example.com/#setup) 和 [顶部](#top)'; + assert.equal(localizeAnchors(source, translated), translated); +}); diff --git a/scripts/readme/package-lock.json b/scripts/readme/package-lock.json new file mode 100644 index 0000000000..5320af0c48 --- /dev/null +++ b/scripts/readme/package-lock.json @@ -0,0 +1,1002 @@ +{ + "name": "readme-translate", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "readme-translate", + "dependencies": { + "github-slugger": "2.0.0", + "mdast-util-from-markdown": "2.0.1", + "mdast-util-gfm": "3.0.0", + "micromark-extension-gfm": "3.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.3.tgz", + "integrity": "sha512-xPgpDNl0/OXHsI7TlaIs22lWnH3KlpvZdXG1ET/m/YT2Hhdkx8lJV4kLphE6l9simyM4KPDFBwas08BgEob6Jw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.1.tgz", + "integrity": "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.2.0.tgz", + "integrity": "sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", + "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz", + "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.1.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.3" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.2.tgz", + "integrity": "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", + "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-edit-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz", + "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", + "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/scripts/readme/package.json b/scripts/readme/package.json new file mode 100644 index 0000000000..e7c717421a --- /dev/null +++ b/scripts/readme/package.json @@ -0,0 +1,12 @@ +{ + "name": "readme-translate", + "private": true, + "type": "module", + "description": "Dependencies for the README translation sync workflow; installed with npm ci in CI.", + "dependencies": { + "github-slugger": "2.0.0", + "mdast-util-from-markdown": "2.0.1", + "mdast-util-gfm": "3.0.0", + "micromark-extension-gfm": "3.0.0" + } +}