From ef493ae64a772a84ca0bb69579e78f6027f19822 Mon Sep 17 00:00:00 2001 From: Babayaga Date: Sun, 5 Apr 2026 14:09:39 +0200 Subject: [PATCH] deepl-mark --- packages/deepl-mark/.gitignore | 5 + packages/deepl-mark/.npmignore | 4 + packages/deepl-mark/LICENSE | 21 + packages/deepl-mark/README.md | 119 + packages/deepl-mark/build.js | 44 + packages/deepl-mark/dist/ast/estree.d.ts | 115 + packages/deepl-mark/dist/ast/estree.js | 24 + packages/deepl-mark/dist/ast/eswalk.d.ts | 18 + packages/deepl-mark/dist/ast/eswalk.js | 75 + packages/deepl-mark/dist/ast/mdast.d.ts | 24 + packages/deepl-mark/dist/ast/mdast.js | 48 + packages/deepl-mark/dist/ast/unist.d.ts | 13 + packages/deepl-mark/dist/ast/unist.js | 6 + packages/deepl-mark/dist/ast/unwalk.d.ts | 5 + packages/deepl-mark/dist/ast/unwalk.js | 24 + packages/deepl-mark/dist/config.d.ts | 215 + packages/deepl-mark/dist/config.js | 244 + packages/deepl-mark/dist/extract.d.ts | 11 + packages/deepl-mark/dist/extract.js | 196 + packages/deepl-mark/dist/format.d.ts | 1 + packages/deepl-mark/dist/format.js | 33 + packages/deepl-mark/dist/index.d.ts | 32 + packages/deepl-mark/dist/index.js | 25 + packages/deepl-mark/dist/lint.d.ts | 1 + packages/deepl-mark/dist/lint.js | 0 packages/deepl-mark/dist/replace.d.ts | 13 + packages/deepl-mark/dist/replace.js | 205 + packages/deepl-mark/dist/translate.d.ts | 6 + packages/deepl-mark/dist/translate.js | 41 + packages/deepl-mark/dist/utils.d.ts | 7 + packages/deepl-mark/dist/utils.js | 30 + .../dist/vendor/mdast-util-html-comment.d.ts | 4 + .../dist/vendor/mdast-util-html-comment.js | 37 + .../micromark-extension-html-comment.d.ts | 3 + .../micromark-extension-html-comment.js | 107 + packages/deepl-mark/experiments/test-fix.mjs | 38 + .../experiments/test-prettier-list.mjs | 51 + .../deepl-mark/experiments/test-spread.mjs | 50 + packages/deepl-mark/package-lock.json | 4335 +++++++++++++++++ packages/deepl-mark/package.json | 56 + .../src/__test__/__samples__/config/base.mjs | 11 + .../__test__/__samples__/config/hybrid.mjs | 12 + .../__test__/__samples__/config/offline.mjs | 9 + .../__test__/__samples__/config/online.mjs | 12 + .../__test__/__samples__/frontmatter/empty.md | 2 + .../__test__/__samples__/frontmatter/index.md | 6 + .../src/__test__/__samples__/json/navbar.json | 18 + .../__test__/__samples__/jsx/code-and-pre.mdx | 5 + .../__test__/__samples__/jsx/jsx-in-prop.mdx | 14 + .../src/__test__/__samples__/jsx/nested.mdx | 4 + .../src/__test__/__samples__/yaml/authors.yml | 17 + .../__snapshots__/extract.test.ts.snap | 52 + .../deepl-mark/src/__test__/config.test.ts | 62 + packages/deepl-mark/src/__test__/e2e.test.ts | 23 + .../deepl-mark/src/__test__/extract.test.ts | 152 + packages/deepl-mark/src/__test__/table.de.md | 14 + packages/deepl-mark/src/__test__/table.md | 15 + packages/deepl-mark/src/ast/estree.ts | 352 ++ packages/deepl-mark/src/ast/eswalk.ts | 109 + packages/deepl-mark/src/ast/mdast.ts | 257 + packages/deepl-mark/src/ast/unist.ts | 19 + packages/deepl-mark/src/ast/unwalk.ts | 40 + packages/deepl-mark/src/config.ts | 586 +++ packages/deepl-mark/src/extract.ts | 267 + packages/deepl-mark/src/format.ts | 42 + packages/deepl-mark/src/index.ts | 69 + packages/deepl-mark/src/lint.ts | 0 packages/deepl-mark/src/replace.ts | 296 ++ packages/deepl-mark/src/translate.ts | 62 + packages/deepl-mark/src/utils.ts | 27 + .../src/vendor/mdast-util-html-comment.ts | 40 + .../micromark-extension-html-comment.ts | 128 + packages/deepl-mark/tsconfig.json | 27 + packages/deepl-mark/vite.config.js | 23 + packages/i18n/dist/commands/glossary.js | 2 +- packages/i18n/dist/commands/translate.js | 2 +- packages/i18n/dist/lib/translate_document.js | 32 +- packages/i18n/dist/zod_schema_cli.js | 2 +- packages/i18n/package-lock.json | 40 + packages/i18n/package.json | 4 +- packages/i18n/salamand.json | 6 +- packages/i18n/src/commands/glossary.ts | 2 +- packages/i18n/src/commands/translate.ts | 2 +- packages/i18n/src/lib/translate_document.ts | 56 +- packages/i18n/src/zod_schema_cli.ts | 2 +- packages/i18n/tests/document.e2e.test.ts | 24 +- packages/i18n/tests/documents/test_de.pdf | Bin 0 -> 146107 bytes packages/i18n/tests/documents/test_de.xlsx | Bin 0 -> 5846 bytes packages/kbot/dev-kbot.code-workspace | 6 + 89 files changed, 9192 insertions(+), 46 deletions(-) create mode 100644 packages/deepl-mark/.gitignore create mode 100644 packages/deepl-mark/.npmignore create mode 100644 packages/deepl-mark/LICENSE create mode 100644 packages/deepl-mark/README.md create mode 100644 packages/deepl-mark/build.js create mode 100644 packages/deepl-mark/dist/ast/estree.d.ts create mode 100644 packages/deepl-mark/dist/ast/estree.js create mode 100644 packages/deepl-mark/dist/ast/eswalk.d.ts create mode 100644 packages/deepl-mark/dist/ast/eswalk.js create mode 100644 packages/deepl-mark/dist/ast/mdast.d.ts create mode 100644 packages/deepl-mark/dist/ast/mdast.js create mode 100644 packages/deepl-mark/dist/ast/unist.d.ts create mode 100644 packages/deepl-mark/dist/ast/unist.js create mode 100644 packages/deepl-mark/dist/ast/unwalk.d.ts create mode 100644 packages/deepl-mark/dist/ast/unwalk.js create mode 100644 packages/deepl-mark/dist/config.d.ts create mode 100644 packages/deepl-mark/dist/config.js create mode 100644 packages/deepl-mark/dist/extract.d.ts create mode 100644 packages/deepl-mark/dist/extract.js create mode 100644 packages/deepl-mark/dist/format.d.ts create mode 100644 packages/deepl-mark/dist/format.js create mode 100644 packages/deepl-mark/dist/index.d.ts create mode 100644 packages/deepl-mark/dist/index.js create mode 100644 packages/deepl-mark/dist/lint.d.ts create mode 100644 packages/deepl-mark/dist/lint.js create mode 100644 packages/deepl-mark/dist/replace.d.ts create mode 100644 packages/deepl-mark/dist/replace.js create mode 100644 packages/deepl-mark/dist/translate.d.ts create mode 100644 packages/deepl-mark/dist/translate.js create mode 100644 packages/deepl-mark/dist/utils.d.ts create mode 100644 packages/deepl-mark/dist/utils.js create mode 100644 packages/deepl-mark/dist/vendor/mdast-util-html-comment.d.ts create mode 100644 packages/deepl-mark/dist/vendor/mdast-util-html-comment.js create mode 100644 packages/deepl-mark/dist/vendor/micromark-extension-html-comment.d.ts create mode 100644 packages/deepl-mark/dist/vendor/micromark-extension-html-comment.js create mode 100644 packages/deepl-mark/experiments/test-fix.mjs create mode 100644 packages/deepl-mark/experiments/test-prettier-list.mjs create mode 100644 packages/deepl-mark/experiments/test-spread.mjs create mode 100644 packages/deepl-mark/package-lock.json create mode 100644 packages/deepl-mark/package.json create mode 100644 packages/deepl-mark/src/__test__/__samples__/config/base.mjs create mode 100644 packages/deepl-mark/src/__test__/__samples__/config/hybrid.mjs create mode 100644 packages/deepl-mark/src/__test__/__samples__/config/offline.mjs create mode 100644 packages/deepl-mark/src/__test__/__samples__/config/online.mjs create mode 100644 packages/deepl-mark/src/__test__/__samples__/frontmatter/empty.md create mode 100644 packages/deepl-mark/src/__test__/__samples__/frontmatter/index.md create mode 100644 packages/deepl-mark/src/__test__/__samples__/json/navbar.json create mode 100644 packages/deepl-mark/src/__test__/__samples__/jsx/code-and-pre.mdx create mode 100644 packages/deepl-mark/src/__test__/__samples__/jsx/jsx-in-prop.mdx create mode 100644 packages/deepl-mark/src/__test__/__samples__/jsx/nested.mdx create mode 100644 packages/deepl-mark/src/__test__/__samples__/yaml/authors.yml create mode 100644 packages/deepl-mark/src/__test__/__snapshots__/extract.test.ts.snap create mode 100644 packages/deepl-mark/src/__test__/config.test.ts create mode 100644 packages/deepl-mark/src/__test__/e2e.test.ts create mode 100644 packages/deepl-mark/src/__test__/extract.test.ts create mode 100644 packages/deepl-mark/src/__test__/table.de.md create mode 100644 packages/deepl-mark/src/__test__/table.md create mode 100644 packages/deepl-mark/src/ast/estree.ts create mode 100644 packages/deepl-mark/src/ast/eswalk.ts create mode 100644 packages/deepl-mark/src/ast/mdast.ts create mode 100644 packages/deepl-mark/src/ast/unist.ts create mode 100644 packages/deepl-mark/src/ast/unwalk.ts create mode 100644 packages/deepl-mark/src/config.ts create mode 100644 packages/deepl-mark/src/extract.ts create mode 100644 packages/deepl-mark/src/format.ts create mode 100644 packages/deepl-mark/src/index.ts create mode 100644 packages/deepl-mark/src/lint.ts create mode 100644 packages/deepl-mark/src/replace.ts create mode 100644 packages/deepl-mark/src/translate.ts create mode 100644 packages/deepl-mark/src/utils.ts create mode 100644 packages/deepl-mark/src/vendor/mdast-util-html-comment.ts create mode 100644 packages/deepl-mark/src/vendor/micromark-extension-html-comment.ts create mode 100644 packages/deepl-mark/tsconfig.json create mode 100644 packages/deepl-mark/vite.config.js create mode 100644 packages/i18n/tests/documents/test_de.pdf create mode 100644 packages/i18n/tests/documents/test_de.xlsx diff --git a/packages/deepl-mark/.gitignore b/packages/deepl-mark/.gitignore new file mode 100644 index 00000000..23b4e574 --- /dev/null +++ b/packages/deepl-mark/.gitignore @@ -0,0 +1,5 @@ +/node_modules +/coverage +*.log +.DS_Store +.env diff --git a/packages/deepl-mark/.npmignore b/packages/deepl-mark/.npmignore new file mode 100644 index 00000000..4c9addac --- /dev/null +++ b/packages/deepl-mark/.npmignore @@ -0,0 +1,4 @@ +./docs +./scripts +./tests +./incoming \ No newline at end of file diff --git a/packages/deepl-mark/LICENSE b/packages/deepl-mark/LICENSE new file mode 100644 index 00000000..5a1ad2d4 --- /dev/null +++ b/packages/deepl-mark/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Izzuddin Natsir + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/deepl-mark/README.md b/packages/deepl-mark/README.md new file mode 100644 index 00000000..38266873 --- /dev/null +++ b/packages/deepl-mark/README.md @@ -0,0 +1,119 @@ +# @polymech/deepl-mark + +Translate markdown and MDX content using [DeepL](https://www.deepl.com/), powered by `mdast`. + +Correctly handles headings, paragraphs, lists, tables (GFM), links, JSX components, frontmatter, and inline formatting — preserving structure while translating only the text. + +## Install + +```bash +npm install @polymech/deepl-mark +``` + +## Usage + +```ts +import { translate } from '@polymech/deepl-mark'; + +const markdown = '# Hello World\n\nThis is a paragraph.'; +const result = await translate(markdown, 'en', 'de'); + +console.log(result); +// # Hallo Welt +// +// Dies ist ein Absatz. +``` + +### Authentication + +Provide your DeepL API key via **options** or **environment variable**: + +```ts +// Option 1: pass directly +await translate(md, 'en', 'de', { apiKey: 'your-deepl-key' }); + +// Option 2: environment variable +// Set DEEPL_AUTH_KEY=your-deepl-key +await translate(md, 'en', 'de'); +``` + +### Options + +The optional 4th argument accepts a `TranslateOptions` object: + +```ts +await translate(content, 'en', 'de', { + // DeepL API key (falls back to DEEPL_AUTH_KEY env var) + apiKey: '...', + + // DeepL translation options (tagHandling, splitSentences, formality, glossaryId, etc.) + deeplOptions: { + formality: 'more', + glossaryId: '...', + }, + + // Frontmatter fields to include/exclude + frontmatterFields: { + include: ['title', 'description'], + exclude: ['slug'], + }, + + // Markdown node types to include/exclude (defaults: exclude 'code') + markdownNodes: { + exclude: ['code'], + }, + + // HTML elements to include/exclude + htmlElements: { + exclude: ['pre', 'code'], + }, + + // JSX components to include/exclude (with attribute-level control) + jsxComponents: { + include: { + Card: { children: true, attributes: ['header'] }, + }, + }, +}); +``` + +#### DeepL defaults + +The following DeepL options are applied by default and can be overridden via `deeplOptions`: + +| Option | Default | +|------------------|----------------| +| `tagHandling` | `'html'` | +| `splitSentences` | `'nonewlines'` | + +### Supported content + +- **Markdown** (`.md`) — headings, paragraphs, lists, blockquotes, tables (GFM), links, images +- **MDX** (`.mdx`) — JSX components and expressions +- **Frontmatter** — YAML frontmatter fields +- **HTML** — inline HTML elements and attributes + +## API + +### `translate(content, sourceLang, targetLang, options?)` + +| Parameter | Type | Description | +|--------------|------------------------|-----------------------------------------------| +| `content` | `string` | Markdown or MDX string to translate | +| `sourceLang` | `SourceLanguageCode` | Source language (e.g. `'en'`, `'de'`, `'fr'`) | +| `targetLang` | `TargetLanguageCode` | Target language (e.g. `'de'`, `'en-US'`) | +| `options` | `TranslateOptions` | Optional config (see above) | + +Returns `Promise` — the translated markdown. + +## Scripts + +```bash +npm test # run all tests +npm run test:tables # run table translation e2e test +npm run build # build for distribution +``` + +## License + +MIT diff --git a/packages/deepl-mark/build.js b/packages/deepl-mark/build.js new file mode 100644 index 00000000..bd7d0027 --- /dev/null +++ b/packages/deepl-mark/build.js @@ -0,0 +1,44 @@ +import { transform } from 'esbuild'; +import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises'; +import { join, dirname, relative } from 'node:path'; + +const SRC = 'src'; +const DIST = 'dist'; + +async function getFiles(dir) { + const entries = await readdir(dir, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === '__test__' || entry.name === 'types') continue; + files.push(...(await getFiles(full))); + } else if (entry.name.endsWith('.ts')) { + files.push(full); + } + } + return files; +} + +async function main() { + const files = await getFiles(SRC); + + for (const file of files) { + const input = await readFile(file, 'utf-8'); + const { code } = await transform(input, { + format: 'esm', + loader: 'ts', + target: 'es2021' + }); + + const rel = relative(SRC, file).replace(/\.ts$/, '.js'); + const outPath = join(DIST, rel); + + await mkdir(dirname(outPath), { recursive: true }); + await writeFile(outPath, code); + } + + console.log(`Built ${files.length} files to ${DIST}/`); +} + +main(); diff --git a/packages/deepl-mark/dist/ast/estree.d.ts b/packages/deepl-mark/dist/ast/estree.d.ts new file mode 100644 index 00000000..fe314c20 --- /dev/null +++ b/packages/deepl-mark/dist/ast/estree.d.ts @@ -0,0 +1,115 @@ +import type { BaseNode as EsBaseNode, Identifier as EsIdentifier, Program as EsProgram, SwitchCase as EsSwitchCase, CatchClause as EsCatchClause, VariableDeclarator as EsVariableDeclarator, ExpressionStatement as EsExpressionStatement, BlockStatement as EsBlockStatement, EmptyStatement as EsEmptyStatement, DebuggerStatement as EsDebuggerStatement, WithStatement as EsWithStatement, ReturnStatement as EsReturnStatement, LabeledStatement as EsLabeledStatement, BreakStatement as EsBreakStatement, ContinueStatement as EsContinueStatement, IfStatement as EsIfStatement, SwitchStatement as EsSwitchStatement, ThrowStatement as EsThrowStatement, TryStatement as EsTryStatement, WhileStatement as EsWhileStatement, DoWhileStatement as EsDoWhileStatement, ForStatement as EsForStatement, ForInStatement as EsForInStatement, ForOfStatement as EsForOfStatement, ClassDeclaration as EsClassDeclaration, FunctionDeclaration as EsFunctionDeclaration, VariableDeclaration as EsVariableDeclaration, ModuleDeclaration as EsModuleDeclaration, ImportDeclaration as EsImportDeclaration, ExportDefaultDeclaration as EsExportDefaultDeclaration, ExportNamedDeclaration as EsExportNamedDeclaration, ExportAllDeclaration as EsExportAllDeclaration, ThisExpression as EsThisExpression, ArrayExpression as EsArrayExpression, ObjectExpression as EsObjectExpression, FunctionExpression as EsFunctionExpression, ArrowFunctionExpression as EsArrowFunctionExpression, YieldExpression as EsYieldExpression, UnaryExpression as EsUnaryExpression, UpdateExpression as EsUpdateExpression, BinaryExpression as EsBinaryExpression, AssignmentExpression as EsAssignmentExpression, LogicalExpression as EsLogicalExpression, MemberExpression as EsMemberExpression, ConditionalExpression as EsConditionalExpression, CallExpression as EsCallExpression, NewExpression as EsNewExpression, SequenceExpression as EsSequenceExpression, TaggedTemplateExpression as EsTaggedTemplateExpression, ClassExpression as EsClassExpression, AwaitExpression as EsAwaitExpression, ImportExpression as EsImportExpression, ChainExpression as EsChainExpression, SimpleLiteral as EsSimpleLiteral, RegExpLiteral as EsRegExpLiteral, BigIntLiteral as EsBigIntLiteral, TemplateLiteral as EsTemplateLiteral, PrivateIdentifier as EsPrivateIdentifier, Property as EsProperty, MetaProperty as EsMetaProperty, PropertyDefinition as EsPropertyDefinition, AssignmentProperty as EsAssignmentProperty, Super as EsSuper, TemplateElement as EsTemplateElement, SpreadElement as EsSpreadElement, ObjectPattern as EsObjectPattern, ArrayPattern as EsArrayPattern, RestElement as EsRestElement, AssignmentPattern as EsAssignmentPattern, Class as EsClass, ClassBody as EsClassBody, StaticBlock as EsStaticBlock, MethodDefinition as EsMethodDefinition, ModuleSpecifier as EsModuleSpecifier, ImportSpecifier as EsImportSpecifier, ImportNamespaceSpecifier as EsImportNamespaceSpecifier, ImportDefaultSpecifier as EsImportDefaultSpecifier, ExportSpecifier as EsExportSpecifier } from 'estree'; +import type { JSXAttribute as EsJsxAttribute, JSXClosingElement as EsJsxClosingElement, JSXClosingFragment as EsJsxClosingFragment, JSXElement as EsJsxElement, JSXEmptyExpression as EsJsxEmptyExpression, JSXExpressionContainer as EsJsxExpressionContainer, JSXFragment as EsJsxFragment, JSXIdentifier as EsJsxIdentifier, JSXMemberExpression as EsJsxMemberExpression, JSXNamespacedName as EsJsxNamespacedName, JSXOpeningElement as EsJsxOpeningElement, JSXOpeningFragment as EsJsxOpeningFragment, JSXSpreadAttribute as EsJsxSpreadAttribute, JSXSpreadChild as EsJsxSpreadChild, JSXText as EsJsxText } from 'estree-jsx'; +export declare function esNodeIs(node: EsNode, type: T): node is EsNodeMap[T]; +export declare function resolveEstreePropertyPath(node: EsProperty, parents: EsNode[], attributeName: string): string | undefined; +/** + * ============================================================ + */ +export type { EsBaseNode, EsIdentifier, EsProgram, EsSwitchCase, EsCatchClause, EsVariableDeclarator, EsExpressionStatement, EsBlockStatement, EsEmptyStatement, EsDebuggerStatement, EsWithStatement, EsReturnStatement, EsLabeledStatement, EsBreakStatement, EsContinueStatement, EsIfStatement, EsSwitchStatement, EsThrowStatement, EsTryStatement, EsWhileStatement, EsDoWhileStatement, EsForStatement, EsForInStatement, EsForOfStatement, EsClassDeclaration, EsFunctionDeclaration, EsVariableDeclaration, EsModuleDeclaration, EsImportDeclaration, EsExportDefaultDeclaration, EsExportNamedDeclaration, EsExportAllDeclaration, EsThisExpression, EsArrayExpression, EsObjectExpression, EsFunctionExpression, EsArrowFunctionExpression, EsYieldExpression, EsUnaryExpression, EsUpdateExpression, EsBinaryExpression, EsAssignmentExpression, EsLogicalExpression, EsMemberExpression, EsConditionalExpression, EsCallExpression, EsNewExpression, EsSequenceExpression, EsTaggedTemplateExpression, EsClassExpression, EsAwaitExpression, EsImportExpression, EsChainExpression, EsSimpleLiteral, EsRegExpLiteral, EsBigIntLiteral, EsTemplateLiteral, EsPrivateIdentifier, EsProperty, EsMetaProperty, EsPropertyDefinition, EsAssignmentProperty, EsSuper, EsTemplateElement, EsSpreadElement, EsObjectPattern, EsArrayPattern, EsRestElement, EsAssignmentPattern, EsClass, EsClassBody, EsStaticBlock, EsMethodDefinition, EsModuleSpecifier, EsImportSpecifier, EsImportNamespaceSpecifier, EsImportDefaultSpecifier, EsExportSpecifier }; +export type { EsJsxAttribute, EsJsxClosingElement, EsJsxClosingFragment, EsJsxElement, EsJsxEmptyExpression, EsJsxExpressionContainer, EsJsxFragment, EsJsxIdentifier, EsJsxMemberExpression, EsJsxNamespacedName, EsJsxOpeningElement, EsJsxOpeningFragment, EsJsxSpreadAttribute, EsJsxSpreadChild, EsJsxText }; +export type EsNode = EsNodeMap[keyof EsNodeMap]; +export type EsNodeMap = EsExpressionMap & EsLiteralMap & EsFunctionMap & EsPatternMap & EsStatementMap & EsJsxMap & { + CatchClause: EsCatchClause; + Class: EsClass; + ClassBody: EsClassBody; + MethodDefinition: EsMethodDefinition; + ModuleDeclaration: EsModuleDeclaration; + ModuleSpecifier: EsModuleSpecifier; + PrivateIdentifier: EsPrivateIdentifier; + Program: EsProgram; + Property: EsProperty; + PropertyDefinition: EsPropertyDefinition; + SpreadElement: EsSpreadElement; + Super: EsSuper; + SwitchCase: EsSwitchCase; + TemplateElement: EsTemplateElement; + VariableDeclarator: EsVariableDeclarator; +}; +export type EsExpressionMap = EsLiteralMap & { + ArrayExpression: EsArrayExpression; + ArrowFunctionExpression: EsArrowFunctionExpression; + AssignmentExpression: EsAssignmentExpression; + AwaitExpression: EsAwaitExpression; + BinaryExpression: EsBinaryExpression; + CallExpression: EsCallExpression; + ChainExpression: EsChainExpression; + ClassExpression: EsClassExpression; + ConditionalExpression: EsConditionalExpression; + FunctionExpression: EsFunctionExpression; + Identifier: EsIdentifier; + ImportExpression: EsImportExpression; + LogicalExpression: EsLogicalExpression; + MemberExpression: EsMemberExpression; + MetaProperty: EsMetaProperty; + NewExpression: EsNewExpression; + ObjectExpression: EsObjectExpression; + SequenceExpression: EsSequenceExpression; + TaggedTemplateExpression: EsTaggedTemplateExpression; + TemplateLiteral: EsTemplateLiteral; + ThisExpression: EsThisExpression; + UnaryExpression: EsUnaryExpression; + UpdateExpression: EsUpdateExpression; + YieldExpression: EsYieldExpression; +}; +export interface EsLiteralMap { + Literal: EsSimpleLiteral | EsRegExpLiteral | EsBigIntLiteral; + SimpleLiteral: EsSimpleLiteral; + RegExpLiteral: EsRegExpLiteral; + BigIntLiteral: EsBigIntLiteral; +} +export interface EsFunctionMap { + FunctionDeclaration: EsFunctionDeclaration; + FunctionExpression: EsFunctionExpression; + ArrowFunctionExpression: EsArrowFunctionExpression; +} +export interface EsPatternMap { + Identifier: EsIdentifier; + ObjectPattern: EsObjectPattern; + ArrayPattern: EsArrayPattern; + RestElement: EsRestElement; + AssignmentPattern: EsAssignmentPattern; + MemberExpression: EsMemberExpression; +} +export type EsStatementMap = EsDeclarationMap & { + ExpressionStatement: EsExpressionStatement; + BlockStatement: EsBlockStatement; + StaticBlock: EsStaticBlock; + EmptyStatement: EsEmptyStatement; + DebuggerStatement: EsDebuggerStatement; + WithStatement: EsWithStatement; + ReturnStatement: EsReturnStatement; + LabeledStatement: EsLabeledStatement; + BreakStatement: EsBreakStatement; + ContinueStatement: EsContinueStatement; + IfStatement: EsIfStatement; + SwitchStatement: EsSwitchStatement; + ThrowStatement: EsThrowStatement; + TryStatement: EsTryStatement; + WhileStatement: EsWhileStatement; + DoWhileStatement: EsDoWhileStatement; + ForStatement: EsForStatement; + ForInStatement: EsForInStatement; + ForOfStatement: EsForOfStatement; +}; +export interface EsDeclarationMap { + FunctionDeclaration: EsFunctionDeclaration; + VariableDeclaration: EsVariableDeclaration; + ClassDeclaration: EsClassDeclaration; +} +export interface EsJsxMap { + JSXAttribute: EsJsxAttribute; + JSXClosingElement: EsJsxClosingElement; + JSXClosingFragment: EsJsxClosingFragment; + JSXElement: EsJsxElement; + JSXEmptyExpression: EsJsxEmptyExpression; + JSXExpressionContainer: EsJsxExpressionContainer; + JSXFragment: EsJsxFragment; + JSXIdentifier: EsJsxIdentifier; + JSXMemberExpression: EsJsxMemberExpression; + JSXNamespacedName: EsJsxNamespacedName; + JSXOpeningElement: EsJsxOpeningElement; + JSXOpeningFragment: EsJsxOpeningFragment; + JSXSpreadAttribute: EsJsxSpreadAttribute; + JSXSpreadChild: EsJsxSpreadChild; + JSXText: EsJsxText; +} diff --git a/packages/deepl-mark/dist/ast/estree.js b/packages/deepl-mark/dist/ast/estree.js new file mode 100644 index 00000000..39eb8b14 --- /dev/null +++ b/packages/deepl-mark/dist/ast/estree.js @@ -0,0 +1,24 @@ +function esNodeIs(node, type) { + return node ? node.type === type : false; +} +function resolveEstreePropertyPath(node, parents, attributeName) { + if (!esNodeIs(parents[2], "ArrayExpression") && !esNodeIs(parents[2], "ObjectExpression")) return; + if (!esNodeIs(node.key, "Identifier")) return; + const names = [node.key.name]; + for (let i = parents.length - 1; i > 1; i--) { + const parent = parents[i]; + if (esNodeIs(parent, "ArrayExpression") || esNodeIs(parent, "ObjectExpression")) continue; + if (esNodeIs(parent, "Property")) { + if (!esNodeIs(parent.key, "Identifier")) return; + names.push(parent.key.name); + continue; + } + return; + } + names.push(attributeName); + return names.reverse().join("."); +} +export { + esNodeIs, + resolveEstreePropertyPath +}; diff --git a/packages/deepl-mark/dist/ast/eswalk.d.ts b/packages/deepl-mark/dist/ast/eswalk.d.ts new file mode 100644 index 00000000..470b3da8 --- /dev/null +++ b/packages/deepl-mark/dist/ast/eswalk.d.ts @@ -0,0 +1,18 @@ +import { EsNode, EsNodeMap, EsProgram } from './estree.js'; +export declare const DEFAULT_ESWALKERS: EsWalkers; +export declare function eswalk(ast: EsProgram, visitors: EsVisitors, walkers?: EsWalkers): void; +export interface EsProcessor { + (node: EsNode | null, parents: EsNode[]): void; +} +export interface EsVisitor { + (node: EsNodeMap[NodeType], parents: EsNode[]): boolean | void; +} +export type EsVisitors = { + [NodeType in keyof EsNodeMap]?: EsVisitor; +}; +export interface EsWalker { + (node: EsNodeMap[NodeType], parents: EsNode[], process: EsProcessor): void; +} +export type EsWalkers = { + [NodeType in keyof Partial]: EsWalker; +}; diff --git a/packages/deepl-mark/dist/ast/eswalk.js b/packages/deepl-mark/dist/ast/eswalk.js new file mode 100644 index 00000000..638f6d7b --- /dev/null +++ b/packages/deepl-mark/dist/ast/eswalk.js @@ -0,0 +1,75 @@ +import { isRegExp } from "node:util/types"; +import { esNodeIs } from "./estree.js"; +const DEFAULT_ESWALKERS = { + Program(node, parents, process) { + parents.push(node); + for (const statement of node.body) { + process(statement, parents); + } + parents.pop(); + }, + ExpressionStatement(node, parents, process) { + parents.push(node); + process(node.expression, parents); + parents.pop(); + }, + ArrayExpression(node, parents, process) { + parents.push(node); + for (const element of node.elements) { + process(element, parents); + } + parents.pop(); + }, + ObjectExpression(node, parents, process) { + parents.push(node); + for (const property of node.properties) { + process(property, parents); + } + parents.pop(); + }, + Property(node, parents, process) { + parents.push(node); + process(node.key, parents); + process(node.value, parents); + parents.pop(); + }, + JSXElement(node, parents, process) { + parents.push(node); + for (const child of node.children) { + process(child, parents); + } + for (const attribute of node.openingElement.attributes) { + process(attribute, parents); + } + parents.pop(); + }, + JSXAttribute(node, parents, process) { + parents.push(node); + if (node.value) { + process(node.value, parents); + } + parents.pop(); + } +}; +function eswalk(ast, visitors, walkers = DEFAULT_ESWALKERS) { + const process = (node, parents) => { + if (!node) return; + let type = node.type; + if (esNodeIs(node, "Literal")) { + type = typeof node.value === "bigint" ? "BigIntLiteral" : isRegExp(node.value) ? "RegExpLiteral" : "SimpleLiteral"; + } + const visit = visitors[type]; + const walk = walkers[type]; + let keepWalking = true; + if (visit !== void 0) { + const signal = visit(node, parents); + keepWalking = signal === false ? false : true; + } + if (keepWalking && walk) walk(node, parents, process); + }; + process(ast, []); +} +export { + DEFAULT_ESWALKERS, + eswalk +}; diff --git a/packages/deepl-mark/dist/ast/mdast.d.ts b/packages/deepl-mark/dist/ast/mdast.d.ts new file mode 100644 index 00000000..5c8f32ce --- /dev/null +++ b/packages/deepl-mark/dist/ast/mdast.d.ts @@ -0,0 +1,24 @@ +import type { Root as MdRoot, Blockquote as MdBlockquote, Break as MdBreak, Code as MdCode, Definition as MdDefinition, Delete as MdDelete, Emphasis as MdEmphasis, Footnote as MdFootnote, FootnoteDefinition as MdFootnoteDefinition, FootnoteReference as MdFootnoteReference, HTML as MdHTML, Heading as MdHeading, Image as MdImage, ImageReference as MdImageReference, InlineCode as MdInlineCode, Link as MdLink, LinkReference as MdLinkReference, List as MdList, ListItem as MdListItem, Paragraph as MdParagraph, Strong as MdStrong, Table as MdTable, TableCell as MdTableCell, TableRow as MdTableRow, Text as MdText, ThematicBreak as MdThematicBreak, YAML as MdYaml } from 'mdast'; +import type { MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm } from 'mdast-util-mdx'; +import type { UnNode } from './unist.js'; +declare module 'mdast' { + interface PhrasingContentMap extends StaticPhrasingContentMap { + mdxJsxFlowElement: MdxJsxFlowElement; + mdxJsxTextElement: MdxJsxTextElement; + mdxFlowExpression: MdxFlowExpression; + mdxTextExpression: MdxTextExpression; + } +} +export declare function mdNodeIs(node: UnNode | undefined, type: T): node is T extends MdRoot['type'] ? MdRoot : T extends MdBlockquote['type'] ? MdBlockquote : T extends MdBreak['type'] ? MdBreak : T extends MdCode['type'] ? MdCode : T extends MdDefinition['type'] ? MdDefinition : T extends MdDelete['type'] ? MdDelete : T extends MdEmphasis['type'] ? MdEmphasis : T extends MdFootnote['type'] ? MdFootnote : T extends MdFootnoteDefinition['type'] ? MdFootnoteDefinition : T extends MdFootnoteReference['type'] ? MdFootnoteReference : T extends MdHTML['type'] ? MdHTML : T extends MdHeading['type'] ? MdHeading : T extends MdImage['type'] ? MdImage : T extends MdImageReference['type'] ? MdImageReference : T extends MdInlineCode['type'] ? MdInlineCode : T extends MdLink['type'] ? MdLink : T extends MdLinkReference['type'] ? MdLinkReference : T extends MdList['type'] ? MdList : T extends MdListItem['type'] ? MdListItem : T extends MdParagraph['type'] ? MdParagraph : T extends MdStrong['type'] ? MdStrong : T extends MdTable['type'] ? MdTable : T extends MdTableCell['type'] ? MdTableCell : T extends MdTableRow['type'] ? MdTableRow : T extends MdText['type'] ? MdText : T extends MdThematicBreak['type'] ? MdThematicBreak : T extends MdYaml ? MdYaml : T extends MdxFlowExpression['type'] ? MdxFlowExpression : T extends MdxJsxAttribute['type'] ? MdxJsxAttribute : T extends MdxJsxAttributeValueExpression['type'] ? MdxJsxAttributeValueExpression : T extends MdxJsxExpressionAttribute['type'] ? MdxJsxExpressionAttribute : T extends MdxJsxFlowElement['type'] ? MdxJsxFlowElement : T extends MdxJsxTextElement['type'] ? MdxJsxTextElement : T extends MdxTextExpression['type'] ? MdxTextExpression : MdxjsEsm; +export declare function mdNodeIsJsxElement(node: UnNode): node is MdxJsxFlowElement | MdxJsxTextElement; +/** + * Get MDX flavored `mdast`. + */ +export declare function getMdast(markdown: string): MdRoot; +export declare function getMarkdown(mdast: MdRoot): string; +/** + * ============================================================ + */ +export type MdNodeType = MdRoot['type'] | MdBlockquote['type'] | MdBreak['type'] | MdCode['type'] | MdDefinition['type'] | MdDelete['type'] | MdEmphasis['type'] | MdFootnote['type'] | MdFootnoteDefinition['type'] | MdFootnoteReference['type'] | MdHTML['type'] | MdHeading['type'] | MdImage['type'] | MdImageReference['type'] | MdInlineCode['type'] | MdLink['type'] | MdLinkReference['type'] | MdList['type'] | MdListItem['type'] | MdParagraph['type'] | MdStrong['type'] | MdTable['type'] | MdTableCell['type'] | MdTableRow['type'] | MdText['type'] | MdThematicBreak['type'] | MdYaml['type'] | MdxFlowExpression['type'] | MdxJsxAttribute['type'] | MdxJsxAttributeValueExpression['type'] | MdxJsxExpressionAttribute['type'] | MdxJsxFlowElement['type'] | MdxJsxTextElement['type'] | MdxTextExpression['type'] | MdxjsEsm['type']; +export type { MdRoot, MdBlockquote, MdBreak, MdCode, MdDefinition, MdDelete, MdEmphasis, MdFootnote, MdFootnoteDefinition, MdFootnoteReference, MdHTML, MdHeading, MdImage, MdImageReference, MdInlineCode, MdLink, MdLinkReference, MdList, MdListItem, MdParagraph, MdStrong, MdTable, MdTableCell, MdTableRow, MdText, MdThematicBreak, MdYaml }; +export type { MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm }; diff --git a/packages/deepl-mark/dist/ast/mdast.js b/packages/deepl-mark/dist/ast/mdast.js new file mode 100644 index 00000000..29ec7718 --- /dev/null +++ b/packages/deepl-mark/dist/ast/mdast.js @@ -0,0 +1,48 @@ +import { fromMarkdown } from "mdast-util-from-markdown"; +import { frontmatterFromMarkdown, frontmatterToMarkdown } from "mdast-util-frontmatter"; +import { gfmTableFromMarkdown, gfmTableToMarkdown } from "mdast-util-gfm-table"; +import { htmlCommentFromMarkdown, htmlCommentToMarkdown } from "../vendor/mdast-util-html-comment.js"; +import { mdxFromMarkdown, mdxToMarkdown } from "mdast-util-mdx"; +import { toMarkdown } from "mdast-util-to-markdown"; +import { frontmatter } from "micromark-extension-frontmatter"; +import { gfmTable } from "micromark-extension-gfm-table"; +import { htmlComment } from "../vendor/micromark-extension-html-comment.js"; +import { mdxjs } from "micromark-extension-mdxjs"; +function mdNodeIs(node, type) { + return node ? node.type === type : false; +} +function mdNodeIsJsxElement(node) { + return mdNodeIs(node, "mdxJsxFlowElement") || mdNodeIs(node, "mdxJsxTextElement"); +} +function getMdast(markdown) { + return fromMarkdown(markdown, { + extensions: [frontmatter("yaml"), mdxjs(), gfmTable, htmlComment()], + mdastExtensions: [frontmatterFromMarkdown("yaml"), mdxFromMarkdown(), gfmTableFromMarkdown, htmlCommentFromMarkdown()] + }); +} +function getMarkdown(mdast) { + return toMarkdown(mdast, { + extensions: [frontmatterToMarkdown("yaml"), mdxToMarkdown(), gfmTableToMarkdown(), htmlCommentToMarkdown()], + listItemIndent: "one", + join: [ + (__, _, parent) => { + if (mdNodeIsJsxElement(parent)) { + return 0; + } + if (mdNodeIs(parent, "list")) { + return 0; + } + if (mdNodeIs(parent, "listItem")) { + return 0; + } + return 1; + } + ] + }); +} +export { + getMarkdown, + getMdast, + mdNodeIs, + mdNodeIsJsxElement +}; diff --git a/packages/deepl-mark/dist/ast/unist.d.ts b/packages/deepl-mark/dist/ast/unist.d.ts new file mode 100644 index 00000000..568370b1 --- /dev/null +++ b/packages/deepl-mark/dist/ast/unist.d.ts @@ -0,0 +1,13 @@ +import type { Position as UnPosition } from 'unist'; +export declare function unNodeIsParent(node: UnNode): node is UnParent; +/** + * ============================================================ + */ +export interface UnNode { + type: string; + position?: UnPosition; + data?: unknown; +} +export interface UnParent extends UnNode { + children: (UnNode | UnParent)[]; +} diff --git a/packages/deepl-mark/dist/ast/unist.js b/packages/deepl-mark/dist/ast/unist.js new file mode 100644 index 00000000..a7d081ad --- /dev/null +++ b/packages/deepl-mark/dist/ast/unist.js @@ -0,0 +1,6 @@ +function unNodeIsParent(node) { + return "children" in node; +} +export { + unNodeIsParent +}; diff --git a/packages/deepl-mark/dist/ast/unwalk.d.ts b/packages/deepl-mark/dist/ast/unwalk.d.ts new file mode 100644 index 00000000..4b22db9b --- /dev/null +++ b/packages/deepl-mark/dist/ast/unwalk.d.ts @@ -0,0 +1,5 @@ +import { type UnNode, type UnParent } from './unist.js'; +export declare function unwalk(node: UnNode, visit: UnVisitor, filter?: (node: UnNode, parent: UnParent | undefined) => boolean): void; +export interface UnVisitor { + (node: UnNode | UnParent, parent: UnParent | undefined, index: number | undefined): boolean | void; +} diff --git a/packages/deepl-mark/dist/ast/unwalk.js b/packages/deepl-mark/dist/ast/unwalk.js new file mode 100644 index 00000000..0266bc73 --- /dev/null +++ b/packages/deepl-mark/dist/ast/unwalk.js @@ -0,0 +1,24 @@ +import { unNodeIsParent } from "./unist.js"; +const NEXT = true; +const STOP = false; +function unwalk(node, visit, filter) { + let next = true; + function step(node2, parent, index) { + if (filter && !filter(node2, parent)) return; + if (unNodeIsParent(node2)) { + for (let i = 0; i < node2.children.length; i++) { + if (!next) break; + const child = node2.children[i]; + step(child, node2, i); + } + node2.children = node2.children.filter((child) => child); + } + if (!next) return; + const signal = visit(node2, parent, index); + next = signal === void 0 || NEXT ? NEXT : STOP; + } + step(node, void 0, void 0); +} +export { + unwalk +}; diff --git a/packages/deepl-mark/dist/config.d.ts b/packages/deepl-mark/dist/config.d.ts new file mode 100644 index 00000000..47779ee3 --- /dev/null +++ b/packages/deepl-mark/dist/config.d.ts @@ -0,0 +1,215 @@ +import type { SourceLanguageCode, TargetLanguageCode } from 'deepl-node'; +import type { MdNodeType } from './ast/mdast.js'; +export interface ConfigBase { + /** + * Source's language code. Based on DeepL supported languages. + */ + sourceLanguage: SourceLanguageCode; + /** + * Output's languages code. Based on DeepL supported languages. + */ + outputLanguages: TargetLanguageCode[]; + /** + * Sources and ouputs directories pairs. $langcode$ variable + * is provided to dynamically define directory. + * + * e.g. [ ["docs", "i18n/$langcode$/docs"], ["blog", "i18n/$langcode$/blog"] ] + */ + directories: [string, string][]; +} +export interface Config extends ConfigBase { + /** + * Override current working directory, defaults to `process.cwd()`. + */ + cwd: string; + /** + * By default, all .md, .mdx, .json, and .yaml|.yml files inside + * source directories will be included. + * + * Define glob patterns to filter what files to include or exclude. + * But, the end result is still restricted by file types (.md, .mdx, .json). + */ + files: { + include?: string[]; + exclude: string[]; + }; + /** + * Frontmatter fields. + */ + frontmatterFields: { + include: string[]; + exclude: string[]; + }; + /** + * Markdown node types to include or exclude based on MDAST. Defaults to exclude `code` and `link`. + */ + markdownNodes: { + default: boolean; + include: MdNodeType[]; + exclude: MdNodeType[]; + }; + /** + * HTML elements to include and exlcude, down to the level of attributes + * and children. Include all HTML elements text content + * and some global attributes such as title and placeholder. + */ + htmlElements: { + include: Partial<{ + [Tag in HtmlTag]: { + children: boolean; + attributes: string[]; + }; + }>; + exclude: HtmlTag[]; + }; + /** + * JSX components to include and exclude, down to the level of attributes + * and children. Include all JSX components text children + * and exclude all attributes by default. + * + * Support array, object, and jsx attribute value. For object and array value, + * you can specify the access path starting with the attribute name + * e.g. `items.description` to translate `items={[{description: "..."}]}. + */ + jsxComponents: { + default: boolean; + include: { + [Name: string]: { + children: boolean; + attributes: string[]; + }; + }; + exclude: string[]; + }; + /** + * JSON or YAML file properties to include and exclude. + * Exclude all properties by default. + */ + jsonOrYamlProperties: { + include: (string | number | symbol)[]; + exclude: (string | number | symbol)[]; + }; +} +export interface UserConfig extends ConfigBase { + /** + * Override current working directory, defaults to `process.cwd()`. + */ + cwd?: string; + /** + * By default, all .md, .mdx, .json, and .yaml|.yml files inside + * source directories will be included. + * + * Define glob patterns to filter what files to include or exclude. + * But, the end result is still restricted by file types (.md, .mdx, .json). + */ + files?: { + include?: string[]; + exclude?: string[]; + }; + /** + * Frontmatter fields. + */ + frontmatterFields?: { + include?: string[]; + exclude?: string[]; + }; + /** + * Markdown node types to include or exclude based on MDAST. Defaults to exclude `code` and `link`. + */ + markdownNodes?: { + default?: boolean; + include?: MdNodeType[]; + exclude?: MdNodeType[]; + }; + /** + * HTML elements to include and exlcude, down to the level of attributes + * and children. Include all HTML elements text content + * and some global attributes such as title and placeholder. + */ + htmlElements?: { + default?: boolean; + include?: Partial<{ + [Tag in HtmlTag]: { + children: boolean; + attributes: string[]; + }; + }>; + exclude?: HtmlTag[]; + }; + /** + * JSX components to include and exclude, down to the level of attributes + * and children. Include all JSX components text children + * and exclude all attributes by default. + * + * Support array, object, and jsx attribute value. For object and array value, + * you can specify the access path starting with the attribute name + * e.g. `items.description` to translate `items={[{description: "..."}]}. + */ + jsxComponents?: { + default?: boolean; + include?: { + [Name: string]: { + children: boolean; + attributes: string[]; + }; + }; + exclude?: string[]; + }; + /** + * JSON or YAML file properties to include and exclude. + * Exclude all properties by default. + */ + jsonOrYamlProperties?: { + include?: string[]; + exclude?: string[]; + }; +} +export type HtmlElementsConfig = { + [Tag in HtmlTag]: { + children: boolean; + attributes: string[]; + }; +}; +export declare const HTML_ELEMENTS_CONFIG: HtmlElementsConfig; +export declare const HTML_TAGS: HtmlTag[]; +export declare function isHtmlTag(name: string): name is HtmlTag; +export declare function resolveConfig({ sourceLanguage, outputLanguages, directories, cwd, files, markdownNodes, frontmatterFields, htmlElements, jsxComponents, jsonOrYamlProperties }: UserConfig): Config; +export declare function isFrontmatterFieldIncluded({ field, config }: { + field: string; + config: Config; +}): boolean; +export declare function isMarkdownNodeIncluded({ type, config }: { + type: MdNodeType; + config: Config; +}): boolean; +export declare function isHtmlElementIncluded({ tag, config }: { + tag: HtmlTag; + config: Config; +}): boolean; +export declare function isHtmlElementAttributeIncluded({ tag, attribute, config }: { + tag: HtmlTag; + attribute: string; + config: Config; +}): boolean; +export declare function isHtmlElementChildrenIncluded({ tag, config }: { + tag: HtmlTag; + config: Config; +}): boolean; +export declare function isJsxComponentIncluded({ name, config }: { + name: string; + config: Config; +}): boolean; +export declare function isJsxComponentAttributeIncluded({ name, attribute, config }: { + name: string; + attribute: string; + config: Config; +}): boolean; +export declare function isJsxComponentChildrenIncluded({ name, config }: { + name: string; + config: Config; +}): boolean; +export declare function isJsonOrYamlPropertyIncluded({ property, config }: { + config: Config; + property: string | number | symbol; +}): boolean; +export type HtmlTag = 'a' | 'abbr' | 'address' | 'article' | 'aside' | 'audio' | 'b' | 'bdi' | 'bdo' | 'blockquote' | 'body' | 'button' | 'canvas' | 'caption' | 'cite' | 'col' | 'colgroup' | 'data' | 'datalist' | 'dd' | 'del' | 'details' | 'dfn' | 'dialog' | 'div' | 'dl' | 'dt' | 'em' | 'fieldset' | 'figcaption' | 'figure' | 'footer' | 'form' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'header' | 'html' | 'i' | 'input' | 'ins' | 'label' | 'legend' | 'li' | 'main' | 'mark' | 'meter' | 'nav' | 'ol' | 'optgroup' | 'output' | 'p' | 'progress' | 'q' | 'rp' | 's' | 'samp' | 'section' | 'select' | 'small' | 'span' | 'strong' | 'sub' | 'summary' | 'sup' | 'table' | 'tbody' | 'td' | 'template' | 'text-area' | 'tfoot' | 'th' | 'thead' | 'time' | 'title' | 'tr' | 'track' | 'u' | 'ul' | 'area' | 'base' | 'br' | 'code' | 'embed' | 'head' | 'hr' | 'iframe' | 'img' | 'kbd' | 'link' | 'meta' | 'noscript' | 'object' | 'param' | 'picture' | 'pre' | 'rt' | 'ruby' | 'script' | 'source' | 'style' | 'svg' | 'var' | 'video' | 'qbr'; diff --git a/packages/deepl-mark/dist/config.js b/packages/deepl-mark/dist/config.js new file mode 100644 index 00000000..799e02b7 --- /dev/null +++ b/packages/deepl-mark/dist/config.js @@ -0,0 +1,244 @@ +import { isBoolean } from "./utils.js"; +const HTML_ELEMENTS_CONFIG = getHtmlElementsConfig(); +function getHtmlElementsConfig() { + const includeChildren = [ + "a", + "abbr", + "address", + "article", + "aside", + "audio", + "b", + "bdi", + "bdo", + "blockquote", + "body", + "button", + "canvas", + "caption", + "cite", + "col", + "colgroup", + "data", + "datalist", + "dd", + "del", + "details", + "dfn", + "dialog", + "div", + "dl", + "dt", + "em", + "fieldset", + "figcaption", + "figure", + "footer", + "form", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "html", + "i", + "input", + "ins", + "label", + "legend", + "li", + "main", + "mark", + "meter", + "nav", + "ol", + "optgroup", + "output", + "p", + "progress", + "q", + "rp", + "s", + "samp", + "section", + "select", + "small", + "span", + "strong", + "sub", + "summary", + "sup", + "table", + "tbody", + "td", + "template", + "text-area", + "tfoot", + "th", + "thead", + "time", + "title", + "tr", + "track", + "u", + "ul" + ]; + const excludeChildren = [ + "area", + "base", + "br", + "code", + "embed", + "head", + "hr", + "iframe", + "img", + "kbd", + "link", + "meta", + "noscript", + "object", + "param", + "picture", + "pre", + "rt", + "ruby", + "script", + "source", + "style", + "svg", + "var", + "video", + "qbr" + ]; + const config = {}; + for (const tag of includeChildren) { + config[tag] = { + children: true, + attributes: ["title"] + }; + } + for (const tag of excludeChildren) { + config[tag] = { + children: false, + attributes: ["title"] + }; + } + return config; +} +const HTML_TAGS = Object.keys(HTML_ELEMENTS_CONFIG); +function isHtmlTag(name) { + return HTML_TAGS.includes(name); +} +function resolveConfig({ + sourceLanguage, + outputLanguages, + directories, + cwd, + files, + markdownNodes, + frontmatterFields, + htmlElements, + jsxComponents, + jsonOrYamlProperties +}) { + return { + sourceLanguage, + outputLanguages, + directories, + cwd: cwd ?? "", + files: files ? { + include: files.include, + exclude: files.exclude ?? [] + } : { exclude: [] }, + markdownNodes: markdownNodes ? { + default: isBoolean(markdownNodes.default) ? markdownNodes.default : true, + include: markdownNodes.include ?? [], + exclude: markdownNodes.exclude ?? ["code"] + } : { default: true, include: [], exclude: ["code"] }, + frontmatterFields: frontmatterFields ? { + include: frontmatterFields.include ?? [], + exclude: frontmatterFields.exclude ?? [] + } : { include: [], exclude: [] }, + htmlElements: htmlElements ? { + include: htmlElements.include ? isBoolean(htmlElements.default) && htmlElements.default || htmlElements.default === void 0 ? { ...HTML_ELEMENTS_CONFIG, ...htmlElements.include } : htmlElements.include : isBoolean(htmlElements.default) && !htmlElements.default ? {} : HTML_ELEMENTS_CONFIG, + exclude: htmlElements.exclude ?? [] + } : { include: HTML_ELEMENTS_CONFIG, exclude: [] }, + jsxComponents: jsxComponents ? { + default: isBoolean(jsxComponents.default) ? jsxComponents.default : true, + include: jsxComponents.include ?? {}, + exclude: jsxComponents.exclude ?? [] + } : { default: true, include: {}, exclude: [] }, + jsonOrYamlProperties: jsonOrYamlProperties ? { include: jsonOrYamlProperties.include ?? [], exclude: jsonOrYamlProperties.exclude ?? [] } : { include: [], exclude: [] } + }; +} +function isFrontmatterFieldIncluded({ + field, + config +}) { + return !config.frontmatterFields.exclude.includes(field) && config.frontmatterFields.include.includes(field); +} +function isMarkdownNodeIncluded({ + type, + config +}) { + return !config.markdownNodes.exclude.includes(type) && (config.markdownNodes.default || config.markdownNodes.include.includes(type)); +} +function isHtmlElementIncluded({ tag, config }) { + return !config.htmlElements.exclude.includes(tag) && Object.keys(config.htmlElements.include).includes(tag); +} +function isHtmlElementAttributeIncluded({ + tag, + attribute, + config +}) { + return isHtmlElementIncluded({ tag, config }) && config.htmlElements.include[tag].attributes.includes(attribute); +} +function isHtmlElementChildrenIncluded({ + tag, + config +}) { + return isHtmlElementIncluded({ tag, config }) && config.htmlElements.include[tag].children; +} +function isJsxComponentIncluded({ + name, + config +}) { + return !config.jsxComponents.exclude.includes(name) && (config.jsxComponents.default || Object.keys(config.jsxComponents.include).includes(name)); +} +function isJsxComponentAttributeIncluded({ + name, + attribute, + config +}) { + return !config.jsxComponents.exclude.includes(name) && Object.keys(config.jsxComponents.include).includes(name) && config.jsxComponents.include[name].attributes.includes(attribute); +} +function isJsxComponentChildrenIncluded({ + name, + config +}) { + return !config.jsxComponents.exclude.includes(name) && (Object.keys(config.jsxComponents.include).includes(name) && config.jsxComponents.include[name].children || !Object.keys(config.jsxComponents.include).includes(name) && config.jsxComponents.default); +} +function isJsonOrYamlPropertyIncluded({ + property, + config +}) { + return !config.jsonOrYamlProperties.exclude.includes(property) && config.jsonOrYamlProperties.include.includes(property); +} +export { + HTML_ELEMENTS_CONFIG, + HTML_TAGS, + isFrontmatterFieldIncluded, + isHtmlElementAttributeIncluded, + isHtmlElementChildrenIncluded, + isHtmlElementIncluded, + isHtmlTag, + isJsonOrYamlPropertyIncluded, + isJsxComponentAttributeIncluded, + isJsxComponentChildrenIncluded, + isJsxComponentIncluded, + isMarkdownNodeIncluded, + resolveConfig +}; diff --git a/packages/deepl-mark/dist/extract.d.ts b/packages/deepl-mark/dist/extract.d.ts new file mode 100644 index 00000000..7dd3d234 --- /dev/null +++ b/packages/deepl-mark/dist/extract.d.ts @@ -0,0 +1,11 @@ +import type { UnNode } from './ast/unist.js'; +import { type Config } from './config.js'; +export declare function extractMdastStrings({ mdast, config }: { + mdast: UnNode; + config: Config; +}): string[]; +export declare function extractJsonOrYamlStrings({ source, type, config }: { + source: string; + type?: 'json' | 'yaml'; + config: Config; +}): string[]; diff --git a/packages/deepl-mark/dist/extract.js b/packages/deepl-mark/dist/extract.js new file mode 100644 index 00000000..83f2f87c --- /dev/null +++ b/packages/deepl-mark/dist/extract.js @@ -0,0 +1,196 @@ +import { parse as parseYaml } from "yaml"; +import { + esNodeIs, + resolveEstreePropertyPath +} from "./ast/estree.js"; +import { eswalk } from "./ast/eswalk.js"; +import { mdNodeIs, mdNodeIsJsxElement } from "./ast/mdast.js"; +import { unwalk } from "./ast/unwalk.js"; +import { + isHtmlTag, + isFrontmatterFieldIncluded, + isHtmlElementIncluded, + isHtmlElementAttributeIncluded, + isJsonOrYamlPropertyIncluded, + isJsxComponentIncluded, + isJsxComponentAttributeIncluded, + isMarkdownNodeIncluded, + isHtmlElementChildrenIncluded, + isJsxComponentChildrenIncluded +} from "./config.js"; +import { isArray, isEmptyArray, isEmptyString, isObject, isString } from "./utils.js"; +function extractMdastStrings({ + mdast, + config +}) { + const strings = []; + unwalk( + mdast, + (node, __, _) => { + if (mdNodeIs(node, "text")) { + pushTidyString({ array: strings, string: node.value }); + return; + } + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, "mdxJsxAttribute")) continue; + if (!isHtmlElementAttributeIncluded({ tag: node.name, attribute: attribute.name, config })) + continue; + if (isString(attribute.value)) { + strings.push(attribute.value.trim()); + } else if (attribute.value?.data?.estree) { + const estree = attribute.value.data.estree; + eswalk(estree, { + SimpleLiteral(esnode, _2) { + if (isString(esnode.value)) + pushTidyString({ array: strings, string: esnode.value }); + } + }); + } + } + } else { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, "mdxJsxAttribute")) continue; + const componentName = node.name; + const isAttributeIncluded = isJsxComponentAttributeIncluded({ + name: componentName, + attribute: attribute.name, + config + }); + if (isString(attribute.value)) { + if (!isAttributeIncluded) continue; + strings.push(attribute.value.trim()); + } else if (attribute.value?.data?.estree) { + if (!config.jsxComponents.include[componentName] || !config.jsxComponents.include[componentName].attributes.some( + (attrName) => attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + )) + continue; + const estree = attribute.value.data.estree; + eswalk(estree, { + SimpleLiteral(esnode, _2) { + if (isString(esnode.value)) + pushTidyString({ array: strings, string: esnode.value }); + if (esnode.value === "aye") console.log("passed"); + }, + JSXElement(esnode, _2) { + const name = esnode.openingElement.name.name; + if (isHtmlTag(name)) { + if (!isHtmlElementIncluded({ tag: name, config }) || !isHtmlElementChildrenIncluded({ tag: name, config })) + return false; + } else if (!isJsxComponentIncluded({ name, config }) || !isJsxComponentChildrenIncluded({ name, config })) + return false; + }, + JSXAttribute(esnode, parents) { + const name = typeof esnode.name.name === "string" ? esnode.name.name : esnode.name.name.name; + const parentName = parents[parents.length - 1].openingElement.name.name; + if (isHtmlTag(parentName)) { + if (!isHtmlElementAttributeIncluded({ tag: parentName, attribute: name, config })) + return false; + } else if (!config.jsxComponents.include[name] || !config.jsxComponents.include[name].attributes.some( + (attrName) => attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + )) { + return false; + } + }, + JSXText(esnode, _2) { + pushTidyString({ array: strings, string: esnode.value }); + }, + Property(esnode, parents) { + if (!esNodeIs(esnode, "Identifier")) return false; + const propertyPath = resolveEstreePropertyPath(esnode, parents, attribute.name); + if (!propertyPath || !isJsxComponentAttributeIncluded({ + name: componentName, + attribute: propertyPath, + config + })) + return false; + } + }); + } + } + } + } + if (mdNodeIs(node, "yaml")) { + if (isEmptyArray(config.frontmatterFields.include)) return; + if (isEmptyString(node.value)) return; + const object = parseYaml(node.value); + for (const field in object) { + if (!isFrontmatterFieldIncluded({ field, config })) continue; + const value = object[field]; + if (isString(value)) { + strings.push(value); + continue; + } + if (isArray(value)) { + for (const item of value) { + if (!isString(item)) continue; + strings.push(item); + } + } + } + return; + } + }, + (node, parent) => { + if (!isMarkdownNodeIncluded({ type: node.type, config })) return false; + if (parent && mdNodeIsJsxElement(parent) && parent.name) { + if (isHtmlTag(parent.name)) { + if (!isHtmlElementChildrenIncluded({ tag: parent.name, config })) return false; + } else { + if (!isJsxComponentChildrenIncluded({ name: parent.name, config })) return false; + } + return true; + } + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + if (!isHtmlElementIncluded({ tag: node.name, config })) return false; + } else { + if (!isJsxComponentIncluded({ name: node.name, config })) return false; + } + return true; + } + return true; + } + ); + return strings; +} +function extractJsonOrYamlStrings({ + source, + type = "json", + config +}) { + const strings = []; + if (isEmptyArray(config.jsonOrYamlProperties.include)) return strings; + const parsed = type === "json" ? JSON.parse(source) : parseYaml(source); + process(parsed); + function process(value, property) { + if (typeof value === "string") { + if (property && isJsonOrYamlPropertyIncluded({ property, config })) strings.push(value); + return; + } + if (isArray(value)) { + for (const item of value) { + process(item); + } + return; + } + if (isObject(value)) { + for (const property2 in value) { + const item = value[property2]; + process(item, property2); + } + return; + } + } + return strings; +} +function pushTidyString({ array, string }) { + if (!/^\s*$/.test(string)) { + array.push(string.replace(/(^\n|\r|\t|\v)+\s*/, "").replace(/\s+$/, " ")); + } +} +export { + extractJsonOrYamlStrings, + extractMdastStrings +}; diff --git a/packages/deepl-mark/dist/format.d.ts b/packages/deepl-mark/dist/format.d.ts new file mode 100644 index 00000000..73449111 --- /dev/null +++ b/packages/deepl-mark/dist/format.d.ts @@ -0,0 +1 @@ +export declare function format(markdown: string): Promise; diff --git a/packages/deepl-mark/dist/format.js b/packages/deepl-mark/dist/format.js new file mode 100644 index 00000000..8c34bc83 --- /dev/null +++ b/packages/deepl-mark/dist/format.js @@ -0,0 +1,33 @@ +import prettier from "prettier"; +import { getMarkdown, getMdast, mdNodeIs } from "./ast/mdast.js"; +import { unwalk } from "./ast/unwalk.js"; +async function format(markdown) { + const mdast = getMdast( + await prettier.format(markdown, { + parser: "mdx", + printWidth: Infinity, + proseWrap: "never", + useTabs: true + }) + ); + unwalk( + mdast, + (node, parent, index) => { + if (mdNodeIs(node, "mdxFlowExpression") && expressionIsEmpty(node.value)) { + parent.children[index] = void 0; + } + }, + (node, parent) => { + delete node.position; + return mdNodeIs(parent, "root"); + } + ); + return getMarkdown(mdast); +} +function expressionIsEmpty(text) { + const regex = /^('|")\s*('|")$/; + return regex.test(text); +} +export { + format +}; diff --git a/packages/deepl-mark/dist/index.d.ts b/packages/deepl-mark/dist/index.d.ts new file mode 100644 index 00000000..b1277ee7 --- /dev/null +++ b/packages/deepl-mark/dist/index.d.ts @@ -0,0 +1,32 @@ +import type { SourceLanguageCode, TargetLanguageCode, TranslateTextOptions } from 'deepl-node'; +import type { UserConfig } from './config.js'; +/** + * Options to control which parts of the markdown are translated. + */ +export type TranslateOptions = Omit & { + /** DeepL API key. Falls back to `DEEPL_AUTH_KEY` env var if not provided. */ + apiKey?: string; + /** DeepL translation options (tagHandling, splitSentences, formality, glossaryId, etc.) */ + deeplOptions?: TranslateTextOptions; +}; +/** + * Translate markdown/MDX content from one language to another using DeepL. + * + * Requires `DEEPL_AUTH_KEY` environment variable to be set. + * + * @param content - Markdown or MDX string to translate + * @param sourceLang - Source language code (e.g. 'en', 'de', 'fr') + * @param targetLang - Target language code (e.g. 'de', 'en-US', 'fr') + * @param options - Optional config to control extraction (frontmatter, jsx, html, etc.) + * @returns Translated markdown string + * + * @example + * ```ts + * import { translate } from 'deepmark'; + * + * const result = await translate('# Hello World', 'en', 'de'); + * console.log(result); // '# Hallo Welt' + * ``` + */ +export declare function translate(content: string, sourceLang: SourceLanguageCode, targetLang: TargetLanguageCode, options?: TranslateOptions): Promise; +export type { SourceLanguageCode, TargetLanguageCode, TranslateTextOptions } from 'deepl-node'; diff --git a/packages/deepl-mark/dist/index.js b/packages/deepl-mark/dist/index.js new file mode 100644 index 00000000..9f933f02 --- /dev/null +++ b/packages/deepl-mark/dist/index.js @@ -0,0 +1,25 @@ +import { getMarkdown, getMdast } from "./ast/mdast.js"; +import { resolveConfig } from "./config.js"; +import { extractMdastStrings } from "./extract.js"; +import { format } from "./format.js"; +import { replaceMdastStrings } from "./replace.js"; +import { translateStrings } from "./translate.js"; +async function translate(content, sourceLang, targetLang, options) { + const { apiKey, deeplOptions, ...configOptions } = options ?? {}; + const config = resolveConfig({ + sourceLanguage: sourceLang, + outputLanguages: [targetLang], + directories: [["", ""]], + ...configOptions + }); + const formatted = await format(content); + const mdast = getMdast(formatted); + const strings = extractMdastStrings({ mdast, config }); + if (strings.length === 0) return content; + const translated = await translateStrings(strings, sourceLang, targetLang, apiKey, deeplOptions); + const result = replaceMdastStrings({ mdast, strings: translated, config }); + return getMarkdown(result); +} +export { + translate +}; diff --git a/packages/deepl-mark/dist/lint.d.ts b/packages/deepl-mark/dist/lint.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/packages/deepl-mark/dist/lint.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/deepl-mark/dist/lint.js b/packages/deepl-mark/dist/lint.js new file mode 100644 index 00000000..e69de29b diff --git a/packages/deepl-mark/dist/replace.d.ts b/packages/deepl-mark/dist/replace.d.ts new file mode 100644 index 00000000..f664e40e --- /dev/null +++ b/packages/deepl-mark/dist/replace.d.ts @@ -0,0 +1,13 @@ +import type { MdRoot } from './ast/mdast.js'; +import { type Config } from './config.js'; +export declare function replaceMdastStrings({ mdast, config, strings }: { + mdast: MdRoot; + strings: string[]; + config: Config; +}): MdRoot; +export declare function replaceJsonOrYamlStrings({ source, type, strings, config }: { + source: string; + type?: 'json' | 'yaml'; + strings: string[]; + config: Config; +}): string; diff --git a/packages/deepl-mark/dist/replace.js b/packages/deepl-mark/dist/replace.js new file mode 100644 index 00000000..eb41d750 --- /dev/null +++ b/packages/deepl-mark/dist/replace.js @@ -0,0 +1,205 @@ +import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import { + esNodeIs, + resolveEstreePropertyPath +} from "./ast/estree.js"; +import { eswalk } from "./ast/eswalk.js"; +import { mdNodeIs, mdNodeIsJsxElement } from "./ast/mdast.js"; +import { unwalk } from "./ast/unwalk.js"; +import { + isHtmlTag, + isFrontmatterFieldIncluded, + isHtmlElementIncluded, + isHtmlElementAttributeIncluded, + isJsonOrYamlPropertyIncluded, + isJsxComponentIncluded, + isJsxComponentAttributeIncluded, + isMarkdownNodeIncluded, + isHtmlElementChildrenIncluded, + isJsxComponentChildrenIncluded +} from "./config.js"; +import { isArray, isEmptyArray, isEmptyString, isObject, isString } from "./utils.js"; +function replaceMdastStrings({ + mdast, + config, + strings +}) { + strings = strings.reverse(); + unwalk( + mdast, + (node, __, _) => { + if (mdNodeIs(node, "text")) { + node.value = strings.pop(); + return; + } + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, "mdxJsxAttribute")) continue; + if (!isHtmlElementAttributeIncluded({ tag: node.name, attribute: attribute.name, config })) + continue; + if (isString(attribute.value)) { + attribute.value = strings.pop(); + } else if (attribute.value?.data?.estree) { + const estree = attribute.value.data.estree; + eswalk(estree, { + SimpleLiteral(esnode, _2) { + if (isString(esnode.value)) esnode.value = strings.pop(); + } + }); + } + } + } else { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, "mdxJsxAttribute")) continue; + const componentName = node.name; + const isAttributeIncluded = isJsxComponentAttributeIncluded({ + name: componentName, + attribute: attribute.name, + config + }); + if (isString(attribute.value)) { + if (!isAttributeIncluded) continue; + attribute.value = strings.pop(); + } else if (attribute.value?.data?.estree) { + if (!config.jsxComponents.include[componentName] || !config.jsxComponents.include[componentName].attributes.some( + (attrName) => attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + )) + continue; + const estree = attribute.value.data.estree; + eswalk(estree, { + SimpleLiteral(esnode, _2) { + if (isString(esnode.value)) esnode.value = strings.pop(); + }, + JSXElement(esnode, _2) { + const name = esnode.openingElement.name.name; + if (isHtmlTag(name)) { + if (!isHtmlElementIncluded({ tag: name, config }) || !isHtmlElementChildrenIncluded({ tag: name, config })) + return false; + } else if (!isJsxComponentIncluded({ name, config }) || !isJsxComponentChildrenIncluded({ name, config })) + return false; + }, + JSXAttribute(esnode, parents) { + const name = typeof esnode.name.name === "string" ? esnode.name.name : esnode.name.name.name; + const parentName = parents[parents.length - 1].openingElement.name.name; + if (isHtmlTag(parentName)) { + if (!isHtmlElementAttributeIncluded({ tag: parentName, attribute: name, config })) + return false; + } else if (!config.jsxComponents.include[name] || !config.jsxComponents.include[name].attributes.some( + (attrName) => attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + )) { + return false; + } + }, + JSXText(esnode, _2) { + esnode.value = strings.pop(); + }, + Property(esnode, parents) { + if (!esNodeIs(esnode, "Identifier")) return false; + const propertyPath = resolveEstreePropertyPath(esnode, parents, attribute.name); + if (!propertyPath || !isJsxComponentAttributeIncluded({ + name: componentName, + attribute: propertyPath, + config + })) + return false; + } + }); + } + } + } + } + if (mdNodeIs(node, "yaml")) { + if (isEmptyArray(config.frontmatterFields.include)) return; + if (isEmptyString(node.value)) return; + const object = parseYaml(node.value); + for (const field in object) { + if (!isFrontmatterFieldIncluded({ field, config })) continue; + const value = object[field]; + if (isString(value)) { + object[field] = strings.pop(); + continue; + } + if (isArray(value)) { + for (const [index, item] of value.entries()) { + if (!isString(item)) continue; + value[index] = strings.pop(); + } + } + } + return; + } + }, + (node, parent) => { + if (!isMarkdownNodeIncluded({ type: node.type, config })) return false; + if (parent && mdNodeIsJsxElement(parent) && parent.name) { + if (isHtmlTag(parent.name)) { + if (!isHtmlElementChildrenIncluded({ tag: parent.name, config })) return false; + } else { + if (!isJsxComponentChildrenIncluded({ name: parent.name, config })) return false; + } + return true; + } + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + if (!isHtmlElementIncluded({ tag: node.name, config })) return false; + } else { + if (!isJsxComponentIncluded({ name: node.name, config })) return false; + } + return true; + } + return true; + } + ); + return mdast; +} +function replaceJsonOrYamlStrings({ + source, + type = "json", + strings, + config +}) { + if (isEmptyArray(config.jsonOrYamlProperties.include)) return source; + strings = strings.reverse(); + const parsed = type === "json" ? JSON.parse(source) : parseYaml(source); + process({ value: parsed }); + function process({ + value, + parent, + property, + index + }) { + if (isArray(value)) { + for (const [index2, item] of value.entries()) { + process({ value: item, parent: value, property, index: index2 }); + } + return; + } + if (isObject(value)) { + for (const property2 in value) { + const item = value[property2]; + process({ value: item, parent: value, property: property2 }); + } + return; + } + if (typeof value === "string") { + if (property && isJsonOrYamlPropertyIncluded({ property, config })) { + if (isArray(parent) && index) { + parent[index] = strings.pop(); + return; + } + if (isObject(parent)) { + parent[property] = strings.pop(); + return; + } + } + return; + } + } + if (type === "json") return JSON.stringify(parsed); + return stringifyYaml(parsed); +} +export { + replaceJsonOrYamlStrings, + replaceMdastStrings +}; diff --git a/packages/deepl-mark/dist/translate.d.ts b/packages/deepl-mark/dist/translate.d.ts new file mode 100644 index 00000000..fa43d581 --- /dev/null +++ b/packages/deepl-mark/dist/translate.d.ts @@ -0,0 +1,6 @@ +import type { SourceLanguageCode, TargetLanguageCode, TranslateTextOptions } from 'deepl-node'; +/** + * Translate an array of strings from sourceLang to targetLang using DeepL. + * Batches requests and retries on rate-limit (429) or server (5xx) errors. + */ +export declare function translateStrings(strings: string[], sourceLang: SourceLanguageCode, targetLang: TargetLanguageCode, apiKey?: string, deeplOptions?: TranslateTextOptions, batchSize?: number): Promise; diff --git a/packages/deepl-mark/dist/translate.js b/packages/deepl-mark/dist/translate.js new file mode 100644 index 00000000..cbff58f1 --- /dev/null +++ b/packages/deepl-mark/dist/translate.js @@ -0,0 +1,41 @@ +import { Translator } from "deepl-node"; +const DEFAULT_BATCH_SIZE = 50; +const MAX_RETRIES = 3; +async function translateStrings(strings, sourceLang, targetLang, apiKey, deeplOptions, batchSize = DEFAULT_BATCH_SIZE) { + if (strings.length === 0) return []; + const key = apiKey ?? process.env.DEEPL_AUTH_KEY; + if (!key) throw new Error("DeepL API key must be provided via options.apiKey or DEEPL_AUTH_KEY environment variable"); + const deepl = new Translator(key); + const translations = new Array(strings.length).fill(""); + const textOptions = { + tagHandling: "html", + splitSentences: "nonewlines", + ...deeplOptions + }; + for (let i = 0; i < strings.length; i += batchSize) { + const batch = strings.slice(i, i + batchSize); + const results = await retry( + () => deepl.translateText(batch, sourceLang, targetLang, textOptions) + ); + for (let j = 0; j < batch.length; j++) { + translations[i + j] = results[j].text; + } + } + return translations; +} +async function retry(fn, retries = MAX_RETRIES) { + for (let attempt = 0; ; attempt++) { + try { + return await fn(); + } catch (err) { + const status = err?.statusCode ?? err?.status; + const retryable = status === 429 || status === 456 || status >= 500 && status < 600; + if (!retryable || attempt >= retries) throw err; + const delay = Math.min(1e3 * 2 ** attempt, 1e4); + await new Promise((r) => setTimeout(r, delay)); + } + } +} +export { + translateStrings +}; diff --git a/packages/deepl-mark/dist/utils.d.ts b/packages/deepl-mark/dist/utils.d.ts new file mode 100644 index 00000000..5df7aa2d --- /dev/null +++ b/packages/deepl-mark/dist/utils.d.ts @@ -0,0 +1,7 @@ +export declare function isArray(value: unknown): value is any[]; +export declare function isBoolean(value: unknown): value is boolean; +export declare function isEmptyArray(array: any[]): boolean; +export declare function isEmptyObject(object: Object): boolean; +export declare function isEmptyString(string: string): boolean; +export declare function isObject(value: unknown): value is Record; +export declare function isString(value: unknown): value is string; diff --git a/packages/deepl-mark/dist/utils.js b/packages/deepl-mark/dist/utils.js new file mode 100644 index 00000000..9b4035e6 --- /dev/null +++ b/packages/deepl-mark/dist/utils.js @@ -0,0 +1,30 @@ +function isArray(value) { + return Array.isArray(value); +} +function isBoolean(value) { + return typeof value === "boolean"; +} +function isEmptyArray(array) { + return array.length === 0; +} +function isEmptyObject(object) { + return Object.keys(object).length === 0; +} +function isEmptyString(string) { + return string.length === 0; +} +function isObject(value) { + return isArray(value) ? false : typeof value == "object" ? true : false; +} +function isString(value) { + return typeof value === "string"; +} +export { + isArray, + isBoolean, + isEmptyArray, + isEmptyObject, + isEmptyString, + isObject, + isString +}; diff --git a/packages/deepl-mark/dist/vendor/mdast-util-html-comment.d.ts b/packages/deepl-mark/dist/vendor/mdast-util-html-comment.d.ts new file mode 100644 index 00000000..aedb0bc5 --- /dev/null +++ b/packages/deepl-mark/dist/vendor/mdast-util-html-comment.d.ts @@ -0,0 +1,4 @@ +import type { Extension } from 'mdast-util-from-markdown'; +import type { Options } from 'mdast-util-to-markdown'; +export declare function htmlCommentFromMarkdown(): Extension; +export declare function htmlCommentToMarkdown(): Options; diff --git a/packages/deepl-mark/dist/vendor/mdast-util-html-comment.js b/packages/deepl-mark/dist/vendor/mdast-util-html-comment.js new file mode 100644 index 00000000..12cadc85 --- /dev/null +++ b/packages/deepl-mark/dist/vendor/mdast-util-html-comment.js @@ -0,0 +1,37 @@ +function htmlCommentFromMarkdown() { + return { + canContainEols: ["htmlComment"], + enter: { + htmlComment() { + this.buffer(); + } + }, + exit: { + htmlComment(token) { + const string = this.resume(); + this.enter( + { + // @ts-ignore + type: "htmlComment", + value: string.slice(0, -3) + }, + token + ); + this.exit(token); + } + } + }; +} +function htmlCommentToMarkdown() { + return { + handlers: { + htmlComment(node) { + return ``; + } + } + }; +} +export { + htmlCommentFromMarkdown, + htmlCommentToMarkdown +}; diff --git a/packages/deepl-mark/dist/vendor/micromark-extension-html-comment.d.ts b/packages/deepl-mark/dist/vendor/micromark-extension-html-comment.d.ts new file mode 100644 index 00000000..77fb0cb5 --- /dev/null +++ b/packages/deepl-mark/dist/vendor/micromark-extension-html-comment.d.ts @@ -0,0 +1,3 @@ +import type { Extension, HtmlExtension } from 'micromark-util-types'; +export declare function htmlComment(): Extension; +export declare function htmlCommentToHtml(): HtmlExtension; diff --git a/packages/deepl-mark/dist/vendor/micromark-extension-html-comment.js b/packages/deepl-mark/dist/vendor/micromark-extension-html-comment.js new file mode 100644 index 00000000..75788665 --- /dev/null +++ b/packages/deepl-mark/dist/vendor/micromark-extension-html-comment.js @@ -0,0 +1,107 @@ +import { factorySpace } from "micromark-factory-space"; +import { markdownLineEnding } from "micromark-util-character"; +import { codes } from "micromark-util-symbol/codes.js"; +import { types } from "micromark-util-symbol/types.js"; +function htmlComment() { + return { + flow: { + [codes.lessThan]: { tokenize, concrete: true } + }, + text: { + [codes.lessThan]: { tokenize } + } + }; +} +function htmlCommentToHtml() { + return { + enter: { + htmlComment() { + this.buffer(); + } + }, + exit: { + htmlComment() { + this.resume(); + } + } + }; +} +const tokenize = (effects, ok, nok) => { + let value = ""; + return start; + function start(code) { + effects.enter("htmlComment"); + effects.enter("htmlCommentMarker"); + effects.consume(code); + value += "<"; + return open; + } + function open(code) { + if (value === "<" && code === codes.exclamationMark) { + effects.consume(code); + value += "!"; + return open; + } + if (code === codes.dash) { + if (value === "= 6 && value.slice(-2) === "--") { + effects.consume(code); + effects.exit(types.data); + effects.exit("htmlCommentString"); + effects.exit("htmlComment"); + value += ">"; + return ok; + } + return nok(code); + } +}; +export { + htmlComment, + htmlCommentToHtml +}; diff --git a/packages/deepl-mark/experiments/test-fix.mjs b/packages/deepl-mark/experiments/test-fix.mjs new file mode 100644 index 00000000..3cefe6ba --- /dev/null +++ b/packages/deepl-mark/experiments/test-fix.mjs @@ -0,0 +1,38 @@ +import prettier from 'prettier'; +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { toMarkdown } from 'mdast-util-to-markdown'; + +const md = `The panel includes the following settings: + +* ON/OFF Toggle: A main switch. +* Min Heating Time (1-60s): Minimum duration. +* Mode: Selects the sequential heating algorithm: + * 0 - All: Cycles through all devices with time-based control. + * 1 - SP: Cycles through devices in groups. + * 2 - SP Any: Heats any devices that need heating. +* Post-Heatup Mode: Mode to switch to after initial heatup phase. +* Current Status: Display field showing the current state.`; + +const prettified = await prettier.format(md, { + parser: 'mdx', + printWidth: Infinity, + proseWrap: 'never', + useTabs: true +}); + +const tree = fromMarkdown(prettified); + +// FIX: also handle listItem parent in the join rule +const result = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + if (parent?.type === 'listItem') return 0; + return 1; + } + ] +}); + +console.log('=== FIXED output ==='); +console.log(result); diff --git a/packages/deepl-mark/experiments/test-prettier-list.mjs b/packages/deepl-mark/experiments/test-prettier-list.mjs new file mode 100644 index 00000000..75a8ada2 --- /dev/null +++ b/packages/deepl-mark/experiments/test-prettier-list.mjs @@ -0,0 +1,51 @@ +import prettier from 'prettier'; +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { toMarkdown } from 'mdast-util-to-markdown'; + +const md = `The panel includes the following settings: + +* ON/OFF Toggle: A main switch. +* Min Heating Time (1-60s): Minimum duration. +* Mode: Selects the sequential heating algorithm: + * 0 - All: Cycles through all devices with time-based control. + * 1 - SP: Cycles through devices in groups. + * 2 - SP Any: Heats any devices that need heating. +* Post-Heatup Mode: Mode to switch to after initial heatup phase. +* Current Status: Display field showing the current state.`; + +console.log('=== Input ==='); +console.log(md); + +const prettified = await prettier.format(md, { + parser: 'mdx', + printWidth: Infinity, + proseWrap: 'never', + useTabs: true +}); + +console.log('\n=== After Prettier ==='); +console.log(JSON.stringify(prettified)); +console.log(prettified); + +const tree = fromMarkdown(prettified); +const list = tree.children[1]; // after the paragraph + +console.log('=== AST Analysis of list ==='); +console.log('list spread:', list.spread); +for (const [i, item] of list.children.entries()) { + console.log(`item[${i}] spread:`, item.spread, 'children:', item.children.length, item.children.map(c => c.type)); +} + +// Now serialize with the join rule: +const result = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + return 1; + } + ] +}); + +console.log('\n=== Serialized output ==='); +console.log(result); diff --git a/packages/deepl-mark/experiments/test-spread.mjs b/packages/deepl-mark/experiments/test-spread.mjs new file mode 100644 index 00000000..08160378 --- /dev/null +++ b/packages/deepl-mark/experiments/test-spread.mjs @@ -0,0 +1,50 @@ +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { toMarkdown } from 'mdast-util-to-markdown'; + +const md = `* Mode: Selects the algorithm: + * 0 - All: Cycles through all devices. + * 1 - SP: Cycles through devices in groups. +* Post-Mode: blah`; + +const tree = fromMarkdown(md); +const list = tree.children[0]; + +console.log('=== AST Analysis ==='); +console.log('list spread:', list.spread); +for (const [i, item] of list.children.entries()) { + console.log(`item[${i}] spread:`, item.spread, 'children count:', item.children.length, 'children types:', item.children.map(c => c.type)); +} + +console.log('\n=== Default toMarkdown ==='); +console.log(JSON.stringify(toMarkdown(tree))); + +console.log('\n=== With join rule ==='); +const result = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + if (parent?.type === 'listItem') return 0; + return 1; + } + ] +}); +console.log(JSON.stringify(result)); + +console.log('\n=== With join rule + forced spread=false ==='); +function clearSpread(node) { + if (node.spread !== undefined) node.spread = false; + if (node.children) node.children.forEach(clearSpread); +} +clearSpread(tree); +const result2 = toMarkdown(tree, { + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (parent?.type === 'list') return 0; + if (parent?.type === 'listItem') return 0; + return 1; + } + ] +}); +console.log(JSON.stringify(result2)); diff --git a/packages/deepl-mark/package-lock.json b/packages/deepl-mark/package-lock.json new file mode 100644 index 00000000..fbd42bc7 --- /dev/null +++ b/packages/deepl-mark/package-lock.json @@ -0,0 +1,4335 @@ +{ + "name": "@polymech/deepl-mark", + "version": "0.3.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@polymech/deepl-mark", + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "acorn": "^8.8.2", + "acorn-jsx": "^5.3.2", + "astring": "^1.8.4", + "deepl-node": "^1.24.0", + "mdast-util-from-markdown": "^1.3.0", + "mdast-util-frontmatter": "^1.0.1", + "mdast-util-gfm-table": "^1.0.7", + "mdast-util-mdx": "^2.0.1", + "mdast-util-to-markdown": "^1.5.0", + "micromark-extension-frontmatter": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.7", + "micromark-extension-mdxjs": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1", + "micromark-util-types": "^1.0.2", + "prettier": "^2.8.3", + "yaml": "^2.2.1" + }, + "devDependencies": { + "@types/estree": "^1.0.0", + "@types/mdast": "^3.0.10", + "@types/node": "^25.3.3", + "@types/prettier": "^2.7.2", + "@types/unist": "^2.0.6", + "esbuild": "^0.25.0", + "typescript": "^5.9.3", + "vitest": "^3.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "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/node": { + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deepl-node": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/deepl-node/-/deepl-node-1.24.0.tgz", + "integrity": "sha512-vZ9jUpzJRvFamgVOfm1LDy3YYJ7k8FhxtAX9whR92EFshLIP9JlYS0HFwXL5yYsfqzXdb/wssGRSWvR48t7nSg==", + "license": "MIT", + "dependencies": { + "@types/node": ">=12.0", + "adm-zip": "^0.5.16", + "axios": "^1.7.4", + "form-data": "^3.0.0", + "loglevel": ">=1.6.2", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "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/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", + "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-1.2.1.tgz", + "integrity": "sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "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/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.1.tgz", + "integrity": "sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-extension-frontmatter": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz", + "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", + "integrity": "sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdx-jsx": "^2.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz", + "integrity": "sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.4.tgz", + "integrity": "sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "mdast-util-from-markdown": "^1.1.0", + "mdast-util-to-markdown": "^1.3.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^4.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz", + "integrity": "sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "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", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "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-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.1.1.tgz", + "integrity": "sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==", + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz", + "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==", + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.8.tgz", + "integrity": "sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz", + "integrity": "sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==", + "license": "MIT", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz", + "integrity": "sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz", + "integrity": "sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^1.0.0", + "micromark-extension-mdx-jsx": "^1.0.0", + "micromark-extension-mdx-md": "^1.0.0", + "micromark-extension-mdxjs-esm": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz", + "integrity": "sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-core-commonmark": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.1.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.9.tgz", + "integrity": "sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "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": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz", + "integrity": "sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^2.0.0", + "estree-util-visit": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "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": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "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": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", + "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", + "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "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/packages/deepl-mark/package.json b/packages/deepl-mark/package.json new file mode 100644 index 00000000..b48bfa69 --- /dev/null +++ b/packages/deepl-mark/package.json @@ -0,0 +1,56 @@ +{ + "name": "@polymech/deepl-mark", + "description": "Translate markdown files correctly with `mdast` and DeepL.", + "version": "0.3.0", + "license": "MIT", + "author": "Izzuddin Natsir | Polymech", + "type": "module", + "files": [ + "dist/*" + ], + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc && node build.js", + "dev": "tsc -w & node --watch build.js", + "test": "vitest run --reporter verbose", + "test:watch": "vitest watch --reporter verbose", + "test:tables": "vitest run src/__test__/e2e.test.ts --reporter verbose" + }, + "dependencies": { + "acorn": "^8.8.2", + "acorn-jsx": "^5.3.2", + "astring": "^1.8.4", + "deepl-node": "^1.24.0", + "mdast-util-from-markdown": "^1.3.0", + "mdast-util-frontmatter": "^1.0.1", + "mdast-util-gfm-table": "^1.0.7", + "mdast-util-mdx": "^2.0.1", + "mdast-util-to-markdown": "^1.5.0", + "micromark-extension-frontmatter": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.7", + "micromark-extension-mdxjs": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1", + "micromark-util-types": "^1.0.2", + "prettier": "^2.8.3", + "yaml": "^2.2.1" + }, + "devDependencies": { + "@types/node": "^25.3.3", + "@types/estree": "^1.0.0", + "@types/mdast": "^3.0.10", + "@types/prettier": "^2.7.2", + "@types/unist": "^2.0.6", + "esbuild": "^0.25.0", + "typescript": "^5.9.3", + "vitest": "^3.0.0" + } +} \ No newline at end of file diff --git a/packages/deepl-mark/src/__test__/__samples__/config/base.mjs b/packages/deepl-mark/src/__test__/__samples__/config/base.mjs new file mode 100644 index 00000000..95b5b491 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/config/base.mjs @@ -0,0 +1,11 @@ +/** @type {import("../../../config").UserConfig} */ +export default { + sourceLanguage: 'en', + outputLanguages: ['zh', 'ja'], + directories: [ + ['i18n/$langcode$', 'i18n/$langcode$'], + ['docs', 'i18n/$langcode$/docs'], + ['blog', 'i18n/$langcode$/blog'] + ], + cwd: '../../example' +}; diff --git a/packages/deepl-mark/src/__test__/__samples__/config/hybrid.mjs b/packages/deepl-mark/src/__test__/__samples__/config/hybrid.mjs new file mode 100644 index 00000000..44ff554b --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/config/hybrid.mjs @@ -0,0 +1,12 @@ +import base from './base.mjs'; + +/** @type {import("../../../config").UserConfig} */ +export default { + ...base, + files: { + include: ['docs/intro.md', 'docs/tutorial-basics/markdown-features.mdx', 'i18n/en/code.json'] + }, + jsonOrYamlProperties: { + include: ['message', 'description'] + } +}; diff --git a/packages/deepl-mark/src/__test__/__samples__/config/offline.mjs b/packages/deepl-mark/src/__test__/__samples__/config/offline.mjs new file mode 100644 index 00000000..d824a898 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/config/offline.mjs @@ -0,0 +1,9 @@ +import base from './base.mjs'; + +/** @type {import("../../../config").UserConfig} */ +export default { + ...base, + jsonOrYamlProperties: { + include: ['message', 'description'] + } +}; diff --git a/packages/deepl-mark/src/__test__/__samples__/config/online.mjs b/packages/deepl-mark/src/__test__/__samples__/config/online.mjs new file mode 100644 index 00000000..145890de --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/config/online.mjs @@ -0,0 +1,12 @@ +import base from './base.mjs'; + +/** @type {import("../../../config").UserConfig} */ +export default { + ...base, + files: { + include: ['docs/tutorial-basics/markdown-features.mdx', 'i18n/en/code.json'] + }, + jsonOrYamlProperties: { + include: ['message', 'description'] + } +}; diff --git a/packages/deepl-mark/src/__test__/__samples__/frontmatter/empty.md b/packages/deepl-mark/src/__test__/__samples__/frontmatter/empty.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/frontmatter/empty.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/deepl-mark/src/__test__/__samples__/frontmatter/index.md b/packages/deepl-mark/src/__test__/__samples__/frontmatter/index.md new file mode 100644 index 00000000..096a5586 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/frontmatter/index.md @@ -0,0 +1,6 @@ +--- +author: Izzuddin Natsir +title: A Short Title +tags: [tagone, tagtwo] +description: A short description. +--- diff --git a/packages/deepl-mark/src/__test__/__samples__/json/navbar.json b/packages/deepl-mark/src/__test__/__samples__/json/navbar.json new file mode 100644 index 00000000..a430cded --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/json/navbar.json @@ -0,0 +1,18 @@ +{ + "title": { + "message": "My Site", + "description": "The title in the navbar" + }, + "item.label.Tutorial": { + "message": "Tutorial", + "description": "Navbar item with label Tutorial" + }, + "item.label.Blog": { + "message": "Blog", + "description": "Navbar item with label Blog" + }, + "item.label.GitHub": { + "message": "GitHub", + "description": "Navbar item with label GitHub" + } +} diff --git a/packages/deepl-mark/src/__test__/__samples__/jsx/code-and-pre.mdx b/packages/deepl-mark/src/__test__/__samples__/jsx/code-and-pre.mdx new file mode 100644 index 00000000..06a78590 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/jsx/code-and-pre.mdx @@ -0,0 +1,5 @@ +

+ This is a text. + function +

preformatted
+

diff --git a/packages/deepl-mark/src/__test__/__samples__/jsx/jsx-in-prop.mdx b/packages/deepl-mark/src/__test__/__samples__/jsx/jsx-in-prop.mdx new file mode 100644 index 00000000..88902fc4 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/jsx/jsx-in-prop.mdx @@ -0,0 +1,14 @@ +This is a text inside a jsx prop.}> + This is a text inside a custom component. + + + + This is the text of jsx item one. This the nested text of jsx item one. + , +
+ This is the text of jsx item two. This the nested text of jsx item two. +
+ ]} +>
diff --git a/packages/deepl-mark/src/__test__/__samples__/jsx/nested.mdx b/packages/deepl-mark/src/__test__/__samples__/jsx/nested.mdx new file mode 100644 index 00000000..1d26b026 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/jsx/nested.mdx @@ -0,0 +1,4 @@ +

+ This is a paragraph.This is a span. + This is a text inside a custom component. +

diff --git a/packages/deepl-mark/src/__test__/__samples__/yaml/authors.yml b/packages/deepl-mark/src/__test__/__samples__/yaml/authors.yml new file mode 100644 index 00000000..bcb29915 --- /dev/null +++ b/packages/deepl-mark/src/__test__/__samples__/yaml/authors.yml @@ -0,0 +1,17 @@ +endi: + name: Endilie Yacop Sucipto + title: Maintainer of Docusaurus + url: https://github.com/endiliey + image_url: https://github.com/endiliey.png + +yangshun: + name: Yangshun Tay + title: Front End Engineer @ Facebook + url: https://github.com/yangshun + image_url: https://github.com/yangshun.png + +slorber: + name: Sébastien Lorber + title: Docusaurus maintainer + url: https://sebastienlorber.com + image_url: https://github.com/slorber.png diff --git a/packages/deepl-mark/src/__test__/__snapshots__/extract.test.ts.snap b/packages/deepl-mark/src/__test__/__snapshots__/extract.test.ts.snap new file mode 100644 index 00000000..1b2a934b --- /dev/null +++ b/packages/deepl-mark/src/__test__/__snapshots__/extract.test.ts.snap @@ -0,0 +1,52 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`extract frontmatter field string values > filter frontmatter fields based on configuration 1`] = ` +[ + "A Short Title", + "tagone", + "tagtwo", +] +`; + +exports[`extract jsx children and attribute string values > ignore some HTML elements by default 1`] = ` +[ + "This is a text. ", +] +`; + +exports[`extract jsx children and attribute string values > recursively extract strings from html elements and jsx components inside attributes 1`] = ` +[ + "This is a text inside a custom component.", + "This is a text inside a jsx prop.", + "This is the text of jsx item one. ", + "This the nested text of jsx item one.", + "A short title inside title attribute inside HTML element inside an attribute.", + "This is the text of jsx item two. ", + "This the nested text of jsx item two.", +] +`; + +exports[`extract jsx children and attribute string values > recursively extract strings from nested jsx components 1`] = ` +[ + "This is a paragraph.", + "This is a span.", + "This is a text inside a custom component.", +] +`; + +exports[`extract strings from JSON based on configuration > filter properties based on the config 1`] = ` +[ + "The title in the navbar", + "Navbar item with label Tutorial", + "Navbar item with label Blog", + "Navbar item with label GitHub", +] +`; + +exports[`extract strings from yaml based on configuration > filter properties based on the config 1`] = ` +[ + "Maintainer of Docusaurus", + "Front End Engineer @ Facebook", + "Docusaurus maintainer", +] +`; diff --git a/packages/deepl-mark/src/__test__/config.test.ts b/packages/deepl-mark/src/__test__/config.test.ts new file mode 100644 index 00000000..db806ab7 --- /dev/null +++ b/packages/deepl-mark/src/__test__/config.test.ts @@ -0,0 +1,62 @@ +import { describe } from 'vitest'; +import type { Config, UserConfig } from '../config'; +import { + isFrontmatterFieldIncluded, + isHtmlElementIncluded, + isHtmlElementAttributeIncluded, + isJsonOrYamlPropertyIncluded, + isJsxComponentIncluded, + isJsxComponentAttributeIncluded, + isMarkdownNodeIncluded, + resolveConfig +} from '../config'; + +const baseConfig: UserConfig = { + sourceLanguage: 'en', + outputLanguages: ['zh'], + directories: [['', '']] +}; + +describe('default configurations', (test) => { + const config: Config = resolveConfig(baseConfig); + + test('frontmatter fields', ({ expect }) => { + expect(isFrontmatterFieldIncluded({ field: 'title', config })).toBe(false); + expect(isFrontmatterFieldIncluded({ field: 'description', config })).toBe(false); + }); + + test('markdown nodes', ({ expect }) => { + expect(isMarkdownNodeIncluded({ type: 'code', config })).toBe(false); + expect(isMarkdownNodeIncluded({ type: 'blockquote', config })).toBe(true); + expect(isMarkdownNodeIncluded({ type: 'heading', config })).toBe(true); + }); + + test('html elements', ({ expect }) => { + expect(isHtmlElementIncluded({ tag: 'a', config })).toBe(true); + expect(isHtmlElementIncluded({ tag: 'div', config })).toBe(true); + }); + + test('html element attributes', ({ expect }) => { + expect(isHtmlElementAttributeIncluded({ tag: 'div', attribute: 'title', config })).toBe(true); + expect(isHtmlElementAttributeIncluded({ tag: 'div', attribute: 'id', config })).toBe(false); + }); + + test('jsx components', ({ expect }) => { + expect(isJsxComponentIncluded({ name: 'Card', config })).toBe(true); + expect(isJsxComponentIncluded({ name: 'Warning', config })).toBe(true); + }); + + test('jsx component attributes', ({ expect }) => { + expect(isJsxComponentAttributeIncluded({ name: 'Card', attribute: 'icon', config })).toBe( + false + ); + expect(isJsxComponentAttributeIncluded({ name: 'Warning', attribute: 'title', config })).toBe( + false + ); + }); + + test('json or yaml properties', ({ expect }) => { + expect(isJsonOrYamlPropertyIncluded({ property: 'author', config })).toBe(false); + expect(isJsonOrYamlPropertyIncluded({ property: 'title', config })).toBe(false); + }); +}); diff --git a/packages/deepl-mark/src/__test__/e2e.test.ts b/packages/deepl-mark/src/__test__/e2e.test.ts new file mode 100644 index 00000000..3a644573 --- /dev/null +++ b/packages/deepl-mark/src/__test__/e2e.test.ts @@ -0,0 +1,23 @@ +import { readFile, writeFile } from 'node:fs/promises'; +import np from 'node:path'; +import { describe, test, expect } from 'vitest'; +import { translate } from '../index'; + +const samplesDir = np.resolve('src/__test__'); + +describe('e2e', () => { + test( + 'MarkdownTables', + async () => { + const source = await readFile(np.join(samplesDir, 'table.md'), 'utf-8'); + const result = await translate(source, 'en', 'de'); + + const outPath = np.join(samplesDir, 'table.de.md'); + await writeFile(outPath, result, 'utf-8'); + + const written = await readFile(outPath, 'utf-8'); + expect(written).toBe(result); + }, + { timeout: 30_000 } + ); +}); diff --git a/packages/deepl-mark/src/__test__/extract.test.ts b/packages/deepl-mark/src/__test__/extract.test.ts new file mode 100644 index 00000000..1d1882a8 --- /dev/null +++ b/packages/deepl-mark/src/__test__/extract.test.ts @@ -0,0 +1,152 @@ +import { readFile } from 'node:fs/promises'; +import np from 'node:path'; +import { describe } from 'vitest'; +import { getMdast } from '../ast/mdast'; +import { Config, resolveConfig, UserConfig } from '../config'; +import { extractJsonOrYamlStrings, extractMdastStrings } from '../extract'; +import { format } from '../format'; + +const baseConfig: UserConfig = { + sourceLanguage: 'en', + outputLanguages: ['zh'], + directories: [['', '']], + cwd: 'src/__test__/__samples__' +}; + +async function extract( + path: string, + config: Config, + from: 'markdown' | 'json' | 'yaml' = 'markdown' +) { + const resolvedPath = np.resolve(config.cwd, path); + const source = await readFile(resolvedPath, { encoding: 'utf-8' }); + + if (from === 'markdown') + return extractMdastStrings({ mdast: getMdast(await format(source)), config }); + + return extractJsonOrYamlStrings({ source, type: from, config }); +} + +describe('extract frontmatter field string values', (test) => { + test('ignore empty frontmatter', async ({ expect }) => { + const strings = await extract('frontmatter/empty.md', resolveConfig(baseConfig)); + + expect(strings.length).toBe(0); + }); + + test('filter frontmatter fields based on configuration', async ({ expect }) => { + const strings = await extract( + 'frontmatter/index.md', + resolveConfig({ + ...baseConfig, + frontmatterFields: { + include: ['title', 'tags', 'description'], + exclude: ['description'] + } + }) + ); + + expect(strings).toMatchSnapshot(); + }); +}); + +describe('extract jsx children and attribute string values', (test) => { + test('recursively extract strings from nested jsx components', async ({ expect }) => { + const strings = await extract( + 'jsx/nested.mdx', + resolveConfig({ + ...baseConfig, + jsxComponents: { + include: { + Block: { children: true, attributes: [] } + } + } + }) + ); + + expect(strings).toMatchSnapshot(); + }); + + test('recursively extract strings from html elements and jsx components inside attributes', async ({ + expect + }) => { + const strings = await extract( + 'jsx/jsx-in-prop.mdx', + resolveConfig({ + ...baseConfig, + jsxComponents: { + include: { + Card: { + children: true, + attributes: ['header'] + }, + List: { + children: false, + attributes: ['items'] + } + } + } + }) + ); + + expect(strings).toMatchSnapshot(); + }); + + test('ignore some HTML elements by default', async ({ expect }) => { + const strings = await extract('jsx/code-and-pre.mdx', resolveConfig(baseConfig)); + + expect(strings).toMatchSnapshot(); + }); +}); + +describe('extract strings from JSON based on configuration', (test) => { + test('do not extract any string if no property name is included in the config', async ({ + expect + }) => { + const strings = await extract('json/navbar.json', resolveConfig(baseConfig), 'json'); + + expect(strings.length).toBe(0); + }); + + test('filter properties based on the config', async ({ expect }) => { + const strings = await extract( + 'json/navbar.json', + resolveConfig({ + ...baseConfig, + jsonOrYamlProperties: { + include: ['message', 'description'], + exclude: ['message'] + } + }), + 'json' + ); + + expect(strings).toMatchSnapshot(); + }); +}); + +describe('extract strings from yaml based on configuration', (test) => { + test('do not extract any string if no property name is included in the config', async ({ + expect + }) => { + const strings = await extract('yaml/authors.yml', resolveConfig(baseConfig), 'yaml'); + + expect(strings.length).toBe(0); + }); + + test('filter properties based on the config', async ({ expect }) => { + const strings = await extract( + 'yaml/authors.yml', + resolveConfig({ + ...baseConfig, + jsonOrYamlProperties: { + include: ['name', 'title'], + exclude: ['name'] + } + }), + 'yaml' + ); + + expect(strings).toMatchSnapshot(); + }); +}); diff --git a/packages/deepl-mark/src/__test__/table.de.md b/packages/deepl-mark/src/__test__/table.de.md new file mode 100644 index 00000000..d34928af --- /dev/null +++ b/packages/deepl-mark/src/__test__/table.de.md @@ -0,0 +1,14 @@ +| Name | Kassandra - EDC | +| --------------------- | -------------------------------------------------------- | +| Druck | 20T - Hydraulischer Wagenheber mit pneumatischem Antrieb | +| Presse - Plattengröße | 1150mm \& 1150mm x 2300mm verzahnt | +| Presse - Platten | 2-3 | +| Optionen | 2 aktive Kühlplatten \| 3 Heizplatten | +| Blattgröße | 60cm / 5-60mm dick | +| Elektrizität | 380V | +| Strom | 22kW | +| Gewicht | 710 kg | +| Größe | 1350 × 1350 × 1400 mm | +| Status | Ausgereift | +| Version | V1.0 (Revision A) | +| Lizenz | [CERN OHL v2](https://ohwr.org/cern_ohl_s_v2.txt) | diff --git a/packages/deepl-mark/src/__test__/table.md b/packages/deepl-mark/src/__test__/table.md new file mode 100644 index 00000000..04d87225 --- /dev/null +++ b/packages/deepl-mark/src/__test__/table.md @@ -0,0 +1,15 @@ +| Name | Cassandra – EDC | +|------|------------------| +| Pressure | 20T – Hydraulic Jack with Pneumatic Drive | +| Press – Plate Size | 1150mm & 1150mm x 2300mm interlocked | +| Press – Plates | 2–3 | +| Options | 2 Active Cooling Plates \| 3 Heating Plates | +| Sheet Size | 60cm / 5–60mm thick | +| Electricity | 380V | +| Power | 22kW | +| Weight | 710 Kg | +| Size | 1350 × 1350 × 1400 mm | +| Status | Mature | +| Version | V1.0 (Revision A) | +| License | [CERN OHL v2](https://ohwr.org/cern_ohl_s_v2.txt) | + diff --git a/packages/deepl-mark/src/ast/estree.ts b/packages/deepl-mark/src/ast/estree.ts new file mode 100644 index 00000000..5acf1b4c --- /dev/null +++ b/packages/deepl-mark/src/ast/estree.ts @@ -0,0 +1,352 @@ +import type { + BaseNode as EsBaseNode, + Identifier as EsIdentifier, + Program as EsProgram, + SwitchCase as EsSwitchCase, + CatchClause as EsCatchClause, + VariableDeclarator as EsVariableDeclarator, + ExpressionStatement as EsExpressionStatement, + BlockStatement as EsBlockStatement, + EmptyStatement as EsEmptyStatement, + DebuggerStatement as EsDebuggerStatement, + WithStatement as EsWithStatement, + ReturnStatement as EsReturnStatement, + LabeledStatement as EsLabeledStatement, + BreakStatement as EsBreakStatement, + ContinueStatement as EsContinueStatement, + IfStatement as EsIfStatement, + SwitchStatement as EsSwitchStatement, + ThrowStatement as EsThrowStatement, + TryStatement as EsTryStatement, + WhileStatement as EsWhileStatement, + DoWhileStatement as EsDoWhileStatement, + ForStatement as EsForStatement, + ForInStatement as EsForInStatement, + ForOfStatement as EsForOfStatement, + ClassDeclaration as EsClassDeclaration, + FunctionDeclaration as EsFunctionDeclaration, + VariableDeclaration as EsVariableDeclaration, + ModuleDeclaration as EsModuleDeclaration, + ImportDeclaration as EsImportDeclaration, + ExportDefaultDeclaration as EsExportDefaultDeclaration, + ExportNamedDeclaration as EsExportNamedDeclaration, + ExportAllDeclaration as EsExportAllDeclaration, + ThisExpression as EsThisExpression, + ArrayExpression as EsArrayExpression, + ObjectExpression as EsObjectExpression, + FunctionExpression as EsFunctionExpression, + ArrowFunctionExpression as EsArrowFunctionExpression, + YieldExpression as EsYieldExpression, + UnaryExpression as EsUnaryExpression, + UpdateExpression as EsUpdateExpression, + BinaryExpression as EsBinaryExpression, + AssignmentExpression as EsAssignmentExpression, + LogicalExpression as EsLogicalExpression, + MemberExpression as EsMemberExpression, + ConditionalExpression as EsConditionalExpression, + CallExpression as EsCallExpression, + NewExpression as EsNewExpression, + SequenceExpression as EsSequenceExpression, + TaggedTemplateExpression as EsTaggedTemplateExpression, + ClassExpression as EsClassExpression, + AwaitExpression as EsAwaitExpression, + ImportExpression as EsImportExpression, + ChainExpression as EsChainExpression, + SimpleLiteral as EsSimpleLiteral, + RegExpLiteral as EsRegExpLiteral, + BigIntLiteral as EsBigIntLiteral, + TemplateLiteral as EsTemplateLiteral, + PrivateIdentifier as EsPrivateIdentifier, + Property as EsProperty, + MetaProperty as EsMetaProperty, + PropertyDefinition as EsPropertyDefinition, + AssignmentProperty as EsAssignmentProperty, + Super as EsSuper, + TemplateElement as EsTemplateElement, + SpreadElement as EsSpreadElement, + ObjectPattern as EsObjectPattern, + ArrayPattern as EsArrayPattern, + RestElement as EsRestElement, + AssignmentPattern as EsAssignmentPattern, + Class as EsClass, + ClassBody as EsClassBody, + StaticBlock as EsStaticBlock, + MethodDefinition as EsMethodDefinition, + ModuleSpecifier as EsModuleSpecifier, + ImportSpecifier as EsImportSpecifier, + ImportNamespaceSpecifier as EsImportNamespaceSpecifier, + ImportDefaultSpecifier as EsImportDefaultSpecifier, + ExportSpecifier as EsExportSpecifier +} from 'estree'; + +import type { + JSXAttribute as EsJsxAttribute, + JSXClosingElement as EsJsxClosingElement, + JSXClosingFragment as EsJsxClosingFragment, + JSXElement as EsJsxElement, + JSXEmptyExpression as EsJsxEmptyExpression, + JSXExpressionContainer as EsJsxExpressionContainer, + JSXFragment as EsJsxFragment, + JSXIdentifier as EsJsxIdentifier, + JSXMemberExpression as EsJsxMemberExpression, + JSXNamespacedName as EsJsxNamespacedName, + JSXOpeningElement as EsJsxOpeningElement, + JSXOpeningFragment as EsJsxOpeningFragment, + JSXSpreadAttribute as EsJsxSpreadAttribute, + JSXSpreadChild as EsJsxSpreadChild, + JSXText as EsJsxText +} from 'estree-jsx'; + +export function esNodeIs(node: EsNode, type: T): node is EsNodeMap[T] { + return node ? node.type === type : false; +} + +export function resolveEstreePropertyPath( + node: EsProperty, + parents: EsNode[], + attributeName: string +): string | undefined { + if (!esNodeIs(parents[2], 'ArrayExpression') && !esNodeIs(parents[2], 'ObjectExpression')) return; + if (!esNodeIs(node.key, 'Identifier')) return; + + const names = [node.key.name]; + + for (let i = parents.length - 1; i > 1; i--) { + const parent = parents[i]; + if (esNodeIs(parent, 'ArrayExpression') || esNodeIs(parent, 'ObjectExpression')) continue; + if (esNodeIs(parent, 'Property')) { + if (!esNodeIs(parent.key, 'Identifier')) return; + names.push(parent.key.name); + continue; + } + + return; + } + + names.push(attributeName); + + return names.reverse().join('.'); +} + +/** + * ============================================================ + */ + +export type { + EsBaseNode, + EsIdentifier, + EsProgram, + EsSwitchCase, + EsCatchClause, + EsVariableDeclarator, + EsExpressionStatement, + EsBlockStatement, + EsEmptyStatement, + EsDebuggerStatement, + EsWithStatement, + EsReturnStatement, + EsLabeledStatement, + EsBreakStatement, + EsContinueStatement, + EsIfStatement, + EsSwitchStatement, + EsThrowStatement, + EsTryStatement, + EsWhileStatement, + EsDoWhileStatement, + EsForStatement, + EsForInStatement, + EsForOfStatement, + EsClassDeclaration, + EsFunctionDeclaration, + EsVariableDeclaration, + EsModuleDeclaration, + EsImportDeclaration, + EsExportDefaultDeclaration, + EsExportNamedDeclaration, + EsExportAllDeclaration, + EsThisExpression, + EsArrayExpression, + EsObjectExpression, + EsFunctionExpression, + EsArrowFunctionExpression, + EsYieldExpression, + EsUnaryExpression, + EsUpdateExpression, + EsBinaryExpression, + EsAssignmentExpression, + EsLogicalExpression, + EsMemberExpression, + EsConditionalExpression, + EsCallExpression, + EsNewExpression, + EsSequenceExpression, + EsTaggedTemplateExpression, + EsClassExpression, + EsAwaitExpression, + EsImportExpression, + EsChainExpression, + EsSimpleLiteral, + EsRegExpLiteral, + EsBigIntLiteral, + EsTemplateLiteral, + EsPrivateIdentifier, + EsProperty, + EsMetaProperty, + EsPropertyDefinition, + EsAssignmentProperty, + EsSuper, + EsTemplateElement, + EsSpreadElement, + EsObjectPattern, + EsArrayPattern, + EsRestElement, + EsAssignmentPattern, + EsClass, + EsClassBody, + EsStaticBlock, + EsMethodDefinition, + EsModuleSpecifier, + EsImportSpecifier, + EsImportNamespaceSpecifier, + EsImportDefaultSpecifier, + EsExportSpecifier +}; + +export type { + EsJsxAttribute, + EsJsxClosingElement, + EsJsxClosingFragment, + EsJsxElement, + EsJsxEmptyExpression, + EsJsxExpressionContainer, + EsJsxFragment, + EsJsxIdentifier, + EsJsxMemberExpression, + EsJsxNamespacedName, + EsJsxOpeningElement, + EsJsxOpeningFragment, + EsJsxSpreadAttribute, + EsJsxSpreadChild, + EsJsxText +}; + +export type EsNode = EsNodeMap[keyof EsNodeMap]; + +export type EsNodeMap = EsExpressionMap & + EsLiteralMap & + EsFunctionMap & + EsPatternMap & + EsStatementMap & + EsJsxMap & { + CatchClause: EsCatchClause; + Class: EsClass; + ClassBody: EsClassBody; + MethodDefinition: EsMethodDefinition; + ModuleDeclaration: EsModuleDeclaration; + ModuleSpecifier: EsModuleSpecifier; + PrivateIdentifier: EsPrivateIdentifier; + Program: EsProgram; + Property: EsProperty; + PropertyDefinition: EsPropertyDefinition; + SpreadElement: EsSpreadElement; + Super: EsSuper; + SwitchCase: EsSwitchCase; + TemplateElement: EsTemplateElement; + VariableDeclarator: EsVariableDeclarator; + }; + +export type EsExpressionMap = EsLiteralMap & { + ArrayExpression: EsArrayExpression; + ArrowFunctionExpression: EsArrowFunctionExpression; + AssignmentExpression: EsAssignmentExpression; + AwaitExpression: EsAwaitExpression; + BinaryExpression: EsBinaryExpression; + CallExpression: EsCallExpression; + ChainExpression: EsChainExpression; + ClassExpression: EsClassExpression; + ConditionalExpression: EsConditionalExpression; + FunctionExpression: EsFunctionExpression; + Identifier: EsIdentifier; + ImportExpression: EsImportExpression; + LogicalExpression: EsLogicalExpression; + MemberExpression: EsMemberExpression; + MetaProperty: EsMetaProperty; + NewExpression: EsNewExpression; + ObjectExpression: EsObjectExpression; + SequenceExpression: EsSequenceExpression; + TaggedTemplateExpression: EsTaggedTemplateExpression; + TemplateLiteral: EsTemplateLiteral; + ThisExpression: EsThisExpression; + UnaryExpression: EsUnaryExpression; + UpdateExpression: EsUpdateExpression; + YieldExpression: EsYieldExpression; +}; + +export interface EsLiteralMap { + Literal: EsSimpleLiteral | EsRegExpLiteral | EsBigIntLiteral; + SimpleLiteral: EsSimpleLiteral; + RegExpLiteral: EsRegExpLiteral; + BigIntLiteral: EsBigIntLiteral; +} + +export interface EsFunctionMap { + FunctionDeclaration: EsFunctionDeclaration; + FunctionExpression: EsFunctionExpression; + ArrowFunctionExpression: EsArrowFunctionExpression; +} + +export interface EsPatternMap { + Identifier: EsIdentifier; + ObjectPattern: EsObjectPattern; + ArrayPattern: EsArrayPattern; + RestElement: EsRestElement; + AssignmentPattern: EsAssignmentPattern; + MemberExpression: EsMemberExpression; +} + +export type EsStatementMap = EsDeclarationMap & { + ExpressionStatement: EsExpressionStatement; + BlockStatement: EsBlockStatement; + StaticBlock: EsStaticBlock; + EmptyStatement: EsEmptyStatement; + DebuggerStatement: EsDebuggerStatement; + WithStatement: EsWithStatement; + ReturnStatement: EsReturnStatement; + LabeledStatement: EsLabeledStatement; + BreakStatement: EsBreakStatement; + ContinueStatement: EsContinueStatement; + IfStatement: EsIfStatement; + SwitchStatement: EsSwitchStatement; + ThrowStatement: EsThrowStatement; + TryStatement: EsTryStatement; + WhileStatement: EsWhileStatement; + DoWhileStatement: EsDoWhileStatement; + ForStatement: EsForStatement; + ForInStatement: EsForInStatement; + ForOfStatement: EsForOfStatement; +}; + +export interface EsDeclarationMap { + FunctionDeclaration: EsFunctionDeclaration; + VariableDeclaration: EsVariableDeclaration; + ClassDeclaration: EsClassDeclaration; +} + +export interface EsJsxMap { + JSXAttribute: EsJsxAttribute; + JSXClosingElement: EsJsxClosingElement; + JSXClosingFragment: EsJsxClosingFragment; + JSXElement: EsJsxElement; + JSXEmptyExpression: EsJsxEmptyExpression; + JSXExpressionContainer: EsJsxExpressionContainer; + JSXFragment: EsJsxFragment; + JSXIdentifier: EsJsxIdentifier; + JSXMemberExpression: EsJsxMemberExpression; + JSXNamespacedName: EsJsxNamespacedName; + JSXOpeningElement: EsJsxOpeningElement; + JSXOpeningFragment: EsJsxOpeningFragment; + JSXSpreadAttribute: EsJsxSpreadAttribute; + JSXSpreadChild: EsJsxSpreadChild; + JSXText: EsJsxText; +} diff --git a/packages/deepl-mark/src/ast/eswalk.ts b/packages/deepl-mark/src/ast/eswalk.ts new file mode 100644 index 00000000..62c3cff1 --- /dev/null +++ b/packages/deepl-mark/src/ast/eswalk.ts @@ -0,0 +1,109 @@ +import { isRegExp } from 'node:util/types'; +import { EsNode, esNodeIs, EsNodeMap, EsProgram } from './estree.js'; + +export const DEFAULT_ESWALKERS: EsWalkers = { + Program(node, parents, process) { + parents.push(node); + for (const statement of node.body) { + process(statement, parents); + } + parents.pop(); + }, + ExpressionStatement(node, parents, process) { + parents.push(node); + process(node.expression, parents); + parents.pop(); + }, + ArrayExpression(node, parents, process) { + parents.push(node); + for (const element of node.elements) { + process(element, parents); + } + parents.pop(); + }, + ObjectExpression(node, parents, process) { + parents.push(node); + for (const property of node.properties) { + process(property, parents); + } + parents.pop(); + }, + Property(node, parents, process) { + parents.push(node); + process(node.key, parents); + process(node.value, parents); + parents.pop(); + }, + JSXElement(node, parents, process) { + parents.push(node); + for (const child of node.children) { + process(child, parents); + } + for (const attribute of node.openingElement.attributes) { + process(attribute, parents); + } + parents.pop(); + }, + JSXAttribute(node, parents, process) { + parents.push(node); + if (node.value) { + process(node.value, parents); + } + parents.pop(); + } +}; + +export function eswalk( + ast: EsProgram, + visitors: EsVisitors, + walkers: EsWalkers = DEFAULT_ESWALKERS +) { + const process: EsProcessor = (node, parents) => { + if (!node) return; + + let type = node.type as keyof EsNodeMap; + + if (esNodeIs(node, 'Literal')) { + type = + typeof node.value === 'bigint' + ? 'BigIntLiteral' + : isRegExp(node.value) + ? 'RegExpLiteral' + : 'SimpleLiteral'; + } + + const visit = visitors[type] as EsVisitor; + const walk = walkers[type] as EsWalker; + + let keepWalking = true; + + if (visit !== undefined) { + const signal = visit(node, parents); + keepWalking = signal === false ? false : true; + } + + if (keepWalking && walk) walk(node, parents, process); + }; + + process(ast, []); +} + +export interface EsProcessor { + (node: EsNode | null, parents: EsNode[]): void; +} + +export interface EsVisitor { + (node: EsNodeMap[NodeType], parents: EsNode[]): boolean | void; +} + +export type EsVisitors = { + [NodeType in keyof EsNodeMap]?: EsVisitor; +}; + +export interface EsWalker { + (node: EsNodeMap[NodeType], parents: EsNode[], process: EsProcessor): void; +} + +export type EsWalkers = { + [NodeType in keyof Partial]: EsWalker; +}; diff --git a/packages/deepl-mark/src/ast/mdast.ts b/packages/deepl-mark/src/ast/mdast.ts new file mode 100644 index 00000000..6a2178cf --- /dev/null +++ b/packages/deepl-mark/src/ast/mdast.ts @@ -0,0 +1,257 @@ +import type { + Root as MdRoot, + Blockquote as MdBlockquote, + Break as MdBreak, + Code as MdCode, + Definition as MdDefinition, + Delete as MdDelete, + Emphasis as MdEmphasis, + Footnote as MdFootnote, + FootnoteDefinition as MdFootnoteDefinition, + FootnoteReference as MdFootnoteReference, + HTML as MdHTML, + Heading as MdHeading, + Image as MdImage, + ImageReference as MdImageReference, + InlineCode as MdInlineCode, + Link as MdLink, + LinkReference as MdLinkReference, + List as MdList, + ListItem as MdListItem, + Paragraph as MdParagraph, + Strong as MdStrong, + Table as MdTable, + TableCell as MdTableCell, + TableRow as MdTableRow, + Text as MdText, + ThematicBreak as MdThematicBreak, + YAML as MdYaml +} from 'mdast'; + +import type { + MdxFlowExpression, + MdxJsxAttribute, + MdxJsxAttributeValueExpression, + MdxJsxExpressionAttribute, + MdxJsxFlowElement, + MdxJsxTextElement, + MdxTextExpression, + MdxjsEsm +} from 'mdast-util-mdx'; + +import type { UnNode } from './unist.js'; + +import { fromMarkdown } from 'mdast-util-from-markdown'; +import { frontmatterFromMarkdown, frontmatterToMarkdown } from 'mdast-util-frontmatter'; +import { gfmTableFromMarkdown, gfmTableToMarkdown } from 'mdast-util-gfm-table'; +import { htmlCommentFromMarkdown, htmlCommentToMarkdown } from '../vendor/mdast-util-html-comment.js'; +import { mdxFromMarkdown, mdxToMarkdown } from 'mdast-util-mdx'; +import { toMarkdown } from 'mdast-util-to-markdown'; +import { frontmatter } from 'micromark-extension-frontmatter'; +import { gfmTable } from 'micromark-extension-gfm-table'; +import { htmlComment } from '../vendor/micromark-extension-html-comment.js'; +import { mdxjs } from 'micromark-extension-mdxjs'; + +declare module 'mdast' { + export interface PhrasingContentMap extends StaticPhrasingContentMap { + mdxJsxFlowElement: MdxJsxFlowElement; + mdxJsxTextElement: MdxJsxTextElement; + mdxFlowExpression: MdxFlowExpression; + mdxTextExpression: MdxTextExpression; + } +} + +export function mdNodeIs( + node: UnNode | undefined, + type: T +): node is T extends MdRoot['type'] +? MdRoot +: T extends MdBlockquote['type'] +? MdBlockquote +: T extends MdBreak['type'] +? MdBreak +: T extends MdCode['type'] +? MdCode +: T extends MdDefinition['type'] +? MdDefinition +: T extends MdDelete['type'] +? MdDelete +: T extends MdEmphasis['type'] +? MdEmphasis +: T extends MdFootnote['type'] +? MdFootnote +: T extends MdFootnoteDefinition['type'] +? MdFootnoteDefinition +: T extends MdFootnoteReference['type'] +? MdFootnoteReference +: T extends MdHTML['type'] +? MdHTML +: T extends MdHeading['type'] +? MdHeading +: T extends MdImage['type'] +? MdImage +: T extends MdImageReference['type'] +? MdImageReference +: T extends MdInlineCode['type'] +? MdInlineCode +: T extends MdLink['type'] +? MdLink +: T extends MdLinkReference['type'] +? MdLinkReference +: T extends MdList['type'] +? MdList +: T extends MdListItem['type'] +? MdListItem +: T extends MdParagraph['type'] +? MdParagraph +: T extends MdStrong['type'] +? MdStrong +: T extends MdTable['type'] +? MdTable +: T extends MdTableCell['type'] +? MdTableCell +: T extends MdTableRow['type'] +? MdTableRow +: T extends MdText['type'] +? MdText +: T extends MdThematicBreak['type'] +? MdThematicBreak +: T extends MdYaml +? MdYaml +: T extends MdxFlowExpression['type'] +? MdxFlowExpression +: T extends MdxJsxAttribute['type'] +? MdxJsxAttribute +: T extends MdxJsxAttributeValueExpression['type'] +? MdxJsxAttributeValueExpression +: T extends MdxJsxExpressionAttribute['type'] +? MdxJsxExpressionAttribute +: T extends MdxJsxFlowElement['type'] +? MdxJsxFlowElement +: T extends MdxJsxTextElement['type'] +? MdxJsxTextElement +: T extends MdxTextExpression['type'] +? MdxTextExpression +: MdxjsEsm { + return node ? node.type === type : false; +} + +export function mdNodeIsJsxElement(node: UnNode): node is MdxJsxFlowElement | MdxJsxTextElement { + return mdNodeIs(node, 'mdxJsxFlowElement') || mdNodeIs(node, 'mdxJsxTextElement'); +} + +/** + * Get MDX flavored `mdast`. + */ +export function getMdast(markdown: string): MdRoot { + return fromMarkdown(markdown, { + extensions: [frontmatter('yaml'), mdxjs(), gfmTable, htmlComment()], + mdastExtensions: [frontmatterFromMarkdown('yaml'), mdxFromMarkdown(), gfmTableFromMarkdown, htmlCommentFromMarkdown()] + }); +} + +export function getMarkdown(mdast: MdRoot): string { + return toMarkdown(mdast, { + extensions: [frontmatterToMarkdown('yaml'), mdxToMarkdown(), gfmTableToMarkdown(), htmlCommentToMarkdown()], + listItemIndent: 'one', + join: [ + (__, _, parent) => { + if (mdNodeIsJsxElement(parent)) { + return 0; + } + + // Keep list items tight (no blank lines between them) + if (mdNodeIs(parent, 'list')) { + return 0; + } + // Keep content within a list item tight (e.g. paragraph + nested sub-list) + if (mdNodeIs(parent, 'listItem')) { + return 0; + } + return 1; + } + ] + }); +} + +/** + * ============================================================ + */ + +export type MdNodeType = + | MdRoot['type'] + | MdBlockquote['type'] + | MdBreak['type'] + | MdCode['type'] + | MdDefinition['type'] + | MdDelete['type'] + | MdEmphasis['type'] + | MdFootnote['type'] + | MdFootnoteDefinition['type'] + | MdFootnoteReference['type'] + | MdHTML['type'] + | MdHeading['type'] + | MdImage['type'] + | MdImageReference['type'] + | MdInlineCode['type'] + | MdLink['type'] + | MdLinkReference['type'] + | MdList['type'] + | MdListItem['type'] + | MdParagraph['type'] + | MdStrong['type'] + | MdTable['type'] + | MdTableCell['type'] + | MdTableRow['type'] + | MdText['type'] + | MdThematicBreak['type'] + | MdYaml['type'] + | MdxFlowExpression['type'] + | MdxJsxAttribute['type'] + | MdxJsxAttributeValueExpression['type'] + | MdxJsxExpressionAttribute['type'] + | MdxJsxFlowElement['type'] + | MdxJsxTextElement['type'] + | MdxTextExpression['type'] + | MdxjsEsm['type']; + +export type { + MdRoot, + MdBlockquote, + MdBreak, + MdCode, + MdDefinition, + MdDelete, + MdEmphasis, + MdFootnote, + MdFootnoteDefinition, + MdFootnoteReference, + MdHTML, + MdHeading, + MdImage, + MdImageReference, + MdInlineCode, + MdLink, + MdLinkReference, + MdList, + MdListItem, + MdParagraph, + MdStrong, + MdTable, + MdTableCell, + MdTableRow, + MdText, + MdThematicBreak, + MdYaml +}; + +export type { + MdxFlowExpression, + MdxJsxAttribute, + MdxJsxAttributeValueExpression, + MdxJsxExpressionAttribute, + MdxJsxFlowElement, + MdxJsxTextElement, + MdxTextExpression, + MdxjsEsm +}; diff --git a/packages/deepl-mark/src/ast/unist.ts b/packages/deepl-mark/src/ast/unist.ts new file mode 100644 index 00000000..b578e00b --- /dev/null +++ b/packages/deepl-mark/src/ast/unist.ts @@ -0,0 +1,19 @@ +import type { Position as UnPosition } from 'unist'; + +export function unNodeIsParent(node: UnNode): node is UnParent { + return 'children' in node; +} + +/** + * ============================================================ + */ + +export interface UnNode { + type: string; + position?: UnPosition; + data?: unknown; +} + +export interface UnParent extends UnNode { + children: (UnNode | UnParent)[]; +} diff --git a/packages/deepl-mark/src/ast/unwalk.ts b/packages/deepl-mark/src/ast/unwalk.ts new file mode 100644 index 00000000..57cfbf22 --- /dev/null +++ b/packages/deepl-mark/src/ast/unwalk.ts @@ -0,0 +1,40 @@ +import { type UnNode, type UnParent, unNodeIsParent } from './unist.js'; + +const NEXT = true; +const STOP = false; + +export function unwalk( + node: UnNode, + visit: UnVisitor, + filter?: (node: UnNode, parent: UnParent | undefined) => boolean +) { + let next = true; + + function step(node: UnNode, parent: UnParent | undefined, index: number | undefined) { + if (filter && !filter(node, parent)) return; + + if (unNodeIsParent(node)) { + for (let i = 0; i < node.children.length; i++) { + if (!next) break; + + const child = node.children[i]; + step(child, node, i); + } + + node.children = node.children.filter((child) => child); + } + + if (!next) return; + + const signal = visit(node, parent, index); + next = signal === undefined || NEXT ? NEXT : STOP; + } + + step(node, undefined, undefined); +} + +export interface UnVisitor { + (node: UnNode | UnParent, parent: UnParent | undefined, index: number | undefined): + | boolean + | void; +} diff --git a/packages/deepl-mark/src/config.ts b/packages/deepl-mark/src/config.ts new file mode 100644 index 00000000..6c10bbe1 --- /dev/null +++ b/packages/deepl-mark/src/config.ts @@ -0,0 +1,586 @@ +import type { SourceLanguageCode, TargetLanguageCode } from 'deepl-node'; +import type { MdNodeType } from './ast/mdast.js'; +import { isBoolean } from './utils.js'; + +export interface ConfigBase { + /** + * Source's language code. Based on DeepL supported languages. + */ + sourceLanguage: SourceLanguageCode; + /** + * Output's languages code. Based on DeepL supported languages. + */ + outputLanguages: TargetLanguageCode[]; + /** + * Sources and ouputs directories pairs. $langcode$ variable + * is provided to dynamically define directory. + * + * e.g. [ ["docs", "i18n/$langcode$/docs"], ["blog", "i18n/$langcode$/blog"] ] + */ + directories: [string, string][]; +} + +export interface Config extends ConfigBase { + /** + * Override current working directory, defaults to `process.cwd()`. + */ + cwd: string; + /** + * By default, all .md, .mdx, .json, and .yaml|.yml files inside + * source directories will be included. + * + * Define glob patterns to filter what files to include or exclude. + * But, the end result is still restricted by file types (.md, .mdx, .json). + */ + files: { + include?: string[]; + exclude: string[]; + }; + /** + * Frontmatter fields. + */ + frontmatterFields: { + include: string[]; + exclude: string[]; + }; + /** + * Markdown node types to include or exclude based on MDAST. Defaults to exclude `code` and `link`. + */ + markdownNodes: { + default: boolean; + include: MdNodeType[]; + exclude: MdNodeType[]; + }; + /** + * HTML elements to include and exlcude, down to the level of attributes + * and children. Include all HTML elements text content + * and some global attributes such as title and placeholder. + */ + htmlElements: { + include: Partial<{ [Tag in HtmlTag]: { children: boolean; attributes: string[] } }>; + exclude: HtmlTag[]; + }; + /** + * JSX components to include and exclude, down to the level of attributes + * and children. Include all JSX components text children + * and exclude all attributes by default. + * + * Support array, object, and jsx attribute value. For object and array value, + * you can specify the access path starting with the attribute name + * e.g. `items.description` to translate `items={[{description: "..."}]}. + */ + jsxComponents: { + default: boolean; + include: { [Name: string]: { children: boolean; attributes: string[] } }; + exclude: string[]; + }; + /** + * JSON or YAML file properties to include and exclude. + * Exclude all properties by default. + */ + jsonOrYamlProperties: { + include: (string | number | symbol)[]; + exclude: (string | number | symbol)[]; + }; +} + +export interface UserConfig extends ConfigBase { + /** + * Override current working directory, defaults to `process.cwd()`. + */ + cwd?: string; + /** + * By default, all .md, .mdx, .json, and .yaml|.yml files inside + * source directories will be included. + * + * Define glob patterns to filter what files to include or exclude. + * But, the end result is still restricted by file types (.md, .mdx, .json). + */ + files?: { + include?: string[]; + exclude?: string[]; + }; + /** + * Frontmatter fields. + */ + frontmatterFields?: { + include?: string[]; + exclude?: string[]; + }; + /** + * Markdown node types to include or exclude based on MDAST. Defaults to exclude `code` and `link`. + */ + markdownNodes?: { + default?: boolean; + include?: MdNodeType[]; + exclude?: MdNodeType[]; + }; + /** + * HTML elements to include and exlcude, down to the level of attributes + * and children. Include all HTML elements text content + * and some global attributes such as title and placeholder. + */ + htmlElements?: { + default?: boolean; + include?: Partial<{ [Tag in HtmlTag]: { children: boolean; attributes: string[] } }>; + exclude?: HtmlTag[]; + }; + /** + * JSX components to include and exclude, down to the level of attributes + * and children. Include all JSX components text children + * and exclude all attributes by default. + * + * Support array, object, and jsx attribute value. For object and array value, + * you can specify the access path starting with the attribute name + * e.g. `items.description` to translate `items={[{description: "..."}]}. + */ + jsxComponents?: { + default?: boolean; + include?: { [Name: string]: { children: boolean; attributes: string[] } }; + exclude?: string[]; + }; + /** + * JSON or YAML file properties to include and exclude. + * Exclude all properties by default. + */ + jsonOrYamlProperties?: { + include?: string[]; + exclude?: string[]; + }; +} + + + +export type HtmlElementsConfig = { [Tag in HtmlTag]: { children: boolean; attributes: string[] } }; + +export const HTML_ELEMENTS_CONFIG: HtmlElementsConfig = getHtmlElementsConfig(); + +function getHtmlElementsConfig(): HtmlElementsConfig { + const includeChildren: HtmlTag[] = [ + 'a', + 'abbr', + 'address', + 'article', + 'aside', + 'audio', + 'b', + 'bdi', + 'bdo', + 'blockquote', + 'body', + 'button', + 'canvas', + 'caption', + 'cite', + 'col', + 'colgroup', + 'data', + 'datalist', + 'dd', + 'del', + 'details', + 'dfn', + 'dialog', + 'div', + 'dl', + 'dt', + 'em', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'header', + 'html', + 'i', + 'input', + 'ins', + 'label', + 'legend', + 'li', + 'main', + 'mark', + 'meter', + 'nav', + 'ol', + 'optgroup', + 'output', + 'p', + 'progress', + 'q', + 'rp', + 's', + 'samp', + 'section', + 'select', + 'small', + 'span', + 'strong', + 'sub', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'template', + 'text-area', + 'tfoot', + 'th', + 'thead', + 'time', + 'title', + 'tr', + 'track', + 'u', + 'ul' + ]; + + const excludeChildren: HtmlTag[] = [ + 'area', + 'base', + 'br', + 'code', + 'embed', + 'head', + 'hr', + 'iframe', + 'img', + 'kbd', + 'link', + 'meta', + 'noscript', + 'object', + 'param', + 'picture', + 'pre', + 'rt', + 'ruby', + 'script', + 'source', + 'style', + 'svg', + 'var', + 'video', + 'qbr' + ]; + + const config: Partial = {}; + + for (const tag of includeChildren) { + config[tag] = { + children: true, + attributes: ['title'] + }; + } + + for (const tag of excludeChildren) { + config[tag] = { + children: false, + attributes: ['title'] + }; + } + + return config as HtmlElementsConfig; +} + +export const HTML_TAGS = Object.keys(HTML_ELEMENTS_CONFIG) as HtmlTag[]; + +export function isHtmlTag(name: string): name is HtmlTag { + return HTML_TAGS.includes(name as HtmlTag); +} + +export function resolveConfig({ + sourceLanguage, + outputLanguages, + directories, + cwd, + files, + markdownNodes, + frontmatterFields, + htmlElements, + jsxComponents, + jsonOrYamlProperties +}: UserConfig): Config { + return { + sourceLanguage, + outputLanguages, + directories, + cwd: cwd ?? '', + files: files + ? { + include: files.include, + exclude: files.exclude ?? [] + } + : { exclude: [] }, + markdownNodes: markdownNodes + ? { + default: isBoolean(markdownNodes.default) ? markdownNodes.default : true, + include: markdownNodes.include ?? [], + exclude: markdownNodes.exclude ?? ['code'] + } + : { default: true, include: [], exclude: ['code'] }, + frontmatterFields: frontmatterFields + ? { + include: frontmatterFields.include ?? [], + exclude: frontmatterFields.exclude ?? [] + } + : { include: [], exclude: [] }, + + htmlElements: htmlElements + ? { + include: htmlElements.include + ? (isBoolean(htmlElements.default) && htmlElements.default) || + htmlElements.default === undefined + ? { ...HTML_ELEMENTS_CONFIG, ...htmlElements.include } + : htmlElements.include + : isBoolean(htmlElements.default) && !htmlElements.default + ? {} + : HTML_ELEMENTS_CONFIG, + exclude: htmlElements.exclude ?? [] + } + : { include: HTML_ELEMENTS_CONFIG, exclude: [] }, + jsxComponents: jsxComponents + ? { + default: isBoolean(jsxComponents.default) ? jsxComponents.default : true, + include: jsxComponents.include ?? {}, + exclude: jsxComponents.exclude ?? [] + } + : { default: true, include: {}, exclude: [] }, + jsonOrYamlProperties: jsonOrYamlProperties + ? { include: jsonOrYamlProperties.include ?? [], exclude: jsonOrYamlProperties.exclude ?? [] } + : { include: [], exclude: [] } + }; +} + + + +export function isFrontmatterFieldIncluded({ + field, + config +}: { + field: string; + config: Config; +}): boolean { + return ( + !config.frontmatterFields.exclude.includes(field) && + config.frontmatterFields.include.includes(field) + ); +} + +export function isMarkdownNodeIncluded({ + type, + config +}: { + type: MdNodeType; + config: Config; +}): boolean { + return ( + !config.markdownNodes.exclude.includes(type) && + (config.markdownNodes.default || config.markdownNodes.include.includes(type)) + ); +} + +export function isHtmlElementIncluded({ tag, config }: { tag: HtmlTag; config: Config }): boolean { + return ( + !config.htmlElements.exclude.includes(tag) && + Object.keys(config.htmlElements.include).includes(tag) + ); +} + +export function isHtmlElementAttributeIncluded({ + tag, + attribute, + config +}: { + tag: HtmlTag; + attribute: string; + config: Config; +}): boolean { + return ( + isHtmlElementIncluded({ tag, config }) && + config.htmlElements.include[tag]!.attributes.includes(attribute) + ); +} + +export function isHtmlElementChildrenIncluded({ + tag, + config +}: { + tag: HtmlTag; + config: Config; +}): boolean { + return isHtmlElementIncluded({ tag, config }) && config.htmlElements.include[tag]!.children; +} + +export function isJsxComponentIncluded({ + name, + config +}: { + name: string; + config: Config; +}): boolean { + return ( + !config.jsxComponents.exclude.includes(name) && + (config.jsxComponents.default || Object.keys(config.jsxComponents.include).includes(name)) + ); +} + +export function isJsxComponentAttributeIncluded({ + name, + attribute, + config +}: { + name: string; + attribute: string; + config: Config; +}): boolean { + return ( + !config.jsxComponents.exclude.includes(name) && + Object.keys(config.jsxComponents.include).includes(name) && + config.jsxComponents.include[name].attributes.includes(attribute) + ); +} + +export function isJsxComponentChildrenIncluded({ + name, + config +}: { + name: string; + config: Config; +}): boolean { + return ( + !config.jsxComponents.exclude.includes(name) && + ((Object.keys(config.jsxComponents.include).includes(name) && + config.jsxComponents.include[name].children) || + (!Object.keys(config.jsxComponents.include).includes(name) && config.jsxComponents.default)) + ); +} + +export function isJsonOrYamlPropertyIncluded({ + property, + config +}: { + config: Config; + property: string | number | symbol; +}): boolean { + return ( + !config.jsonOrYamlProperties.exclude.includes(property) && + config.jsonOrYamlProperties.include.includes(property) + ); +} + +export type HtmlTag = + | 'a' + | 'abbr' + | 'address' + | 'article' + | 'aside' + | 'audio' + | 'b' + | 'bdi' + | 'bdo' + | 'blockquote' + | 'body' + | 'button' + | 'canvas' + | 'caption' + | 'cite' + | 'col' + | 'colgroup' + | 'data' + | 'datalist' + | 'dd' + | 'del' + | 'details' + | 'dfn' + | 'dialog' + | 'div' + | 'dl' + | 'dt' + | 'em' + | 'fieldset' + | 'figcaption' + | 'figure' + | 'footer' + | 'form' + | 'h1' + | 'h2' + | 'h3' + | 'h4' + | 'h5' + | 'h6' + | 'header' + | 'html' + | 'i' + | 'input' + | 'ins' + | 'label' + | 'legend' + | 'li' + | 'main' + | 'mark' + | 'meter' + | 'nav' + | 'ol' + | 'optgroup' + | 'output' + | 'p' + | 'progress' + | 'q' + | 'rp' + | 's' + | 'samp' + | 'section' + | 'select' + | 'small' + | 'span' + | 'strong' + | 'sub' + | 'summary' + | 'sup' + | 'table' + | 'tbody' + | 'td' + | 'template' + | 'text-area' + | 'tfoot' + | 'th' + | 'thead' + | 'time' + | 'title' + | 'tr' + | 'track' + | 'u' + | 'ul' + | 'area' + | 'base' + | 'br' + | 'code' + | 'embed' + | 'head' + | 'hr' + | 'iframe' + | 'img' + | 'kbd' + | 'link' + | 'meta' + | 'noscript' + | 'object' + | 'param' + | 'picture' + | 'pre' + | 'rt' + | 'ruby' + | 'script' + | 'source' + | 'style' + | 'svg' + | 'var' + | 'video' + | 'qbr'; diff --git a/packages/deepl-mark/src/extract.ts b/packages/deepl-mark/src/extract.ts new file mode 100644 index 00000000..d856b60d --- /dev/null +++ b/packages/deepl-mark/src/extract.ts @@ -0,0 +1,267 @@ +import { parse as parseYaml } from 'yaml'; +import { + EsJsxElement, + EsJsxIdentifier, + esNodeIs, + resolveEstreePropertyPath +} from './ast/estree.js'; +import { eswalk } from './ast/eswalk.js'; +import { mdNodeIs, mdNodeIsJsxElement, MdNodeType } from './ast/mdast.js'; +import type { UnNode } from './ast/unist.js'; +import { unwalk } from './ast/unwalk.js'; +import { + type Config, + isHtmlTag, + isFrontmatterFieldIncluded, + isHtmlElementIncluded, + isHtmlElementAttributeIncluded, + isJsonOrYamlPropertyIncluded, + isJsxComponentIncluded, + isJsxComponentAttributeIncluded, + isMarkdownNodeIncluded, + isHtmlElementChildrenIncluded, + isJsxComponentChildrenIncluded +} from './config.js'; +import { isArray, isEmptyArray, isEmptyString, isObject, isString } from './utils.js'; + +export function extractMdastStrings({ + mdast, + config +}: { + mdast: UnNode; + config: Config; +}): string[] { + const strings: string[] = []; + + unwalk( + mdast, + (node, __, _) => { + if (mdNodeIs(node, 'text')) { + pushTidyString({ array: strings, string: node.value }); + return; + } + + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, 'mdxJsxAttribute')) continue; + + if ( + !isHtmlElementAttributeIncluded({ tag: node.name, attribute: attribute.name, config }) + ) + continue; + + if (isString(attribute.value)) { + strings.push(attribute.value.trim()); + } else if (attribute.value?.data?.estree) { + const estree = attribute.value.data.estree; + + eswalk(estree, { + SimpleLiteral(esnode, _) { + if (isString(esnode.value)) + pushTidyString({ array: strings, string: esnode.value }); + } + }); + } + } + } else { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, 'mdxJsxAttribute')) continue; + + const componentName: string = node.name; + + const isAttributeIncluded = isJsxComponentAttributeIncluded({ + name: componentName, + attribute: attribute.name, + config + }); + + if (isString(attribute.value)) { + if (!isAttributeIncluded) continue; + + strings.push(attribute.value.trim()); + } else if (attribute.value?.data?.estree) { + if ( + !config.jsxComponents.include[componentName] || + !config.jsxComponents.include[componentName].attributes.some( + (attrName) => + attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + ) + ) + continue; + + const estree = attribute.value.data.estree; + + eswalk(estree, { + SimpleLiteral(esnode, _) { + if (isString(esnode.value)) + pushTidyString({ array: strings, string: esnode.value }); + + if (esnode.value === 'aye') console.log('passed'); + }, + JSXElement(esnode, _) { + const name = (esnode.openingElement.name as EsJsxIdentifier).name; + + if (isHtmlTag(name)) { + if ( + !isHtmlElementIncluded({ tag: name, config }) || + !isHtmlElementChildrenIncluded({ tag: name, config }) + ) + return false; + } else if ( + !isJsxComponentIncluded({ name: name, config }) || + !isJsxComponentChildrenIncluded({ name: name, config }) + ) + return false; + }, + JSXAttribute(esnode, parents) { + const name = + typeof esnode.name.name === 'string' ? esnode.name.name : esnode.name.name.name; + const parentName = ( + (parents[parents.length - 1] as EsJsxElement).openingElement + .name as EsJsxIdentifier + ).name; + + if (isHtmlTag(parentName)) { + if ( + !isHtmlElementAttributeIncluded({ tag: parentName, attribute: name, config }) + ) + return false; + } else if ( + !config.jsxComponents.include[name] || + !config.jsxComponents.include[name].attributes.some( + (attrName) => + attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + ) + ) { + return false; + } + }, + JSXText(esnode, _) { + pushTidyString({ array: strings, string: esnode.value }); + }, + Property(esnode, parents) { + if (!esNodeIs(esnode, 'Identifier')) return false; + + const propertyPath = resolveEstreePropertyPath(esnode, parents, attribute.name); + + if ( + !propertyPath || + !isJsxComponentAttributeIncluded({ + name: componentName, + attribute: propertyPath, + config + }) + ) + return false; + } + }); + } + } + } + } + + if (mdNodeIs(node, 'yaml')) { + if (isEmptyArray(config.frontmatterFields.include)) return; + if (isEmptyString(node.value)) return; + + const object: Record = parseYaml(node.value); + + for (const field in object) { + if (!isFrontmatterFieldIncluded({ field, config })) continue; + + const value = object[field]; + + if (isString(value)) { + strings.push(value); + continue; + } + + if (isArray(value)) { + for (const item of value) { + if (!isString(item)) continue; + strings.push(item); + } + } + } + + return; + } + }, + (node, parent) => { + if (!isMarkdownNodeIncluded({ type: node.type as MdNodeType, config })) return false; + + if (parent && mdNodeIsJsxElement(parent) && parent.name) { + if (isHtmlTag(parent.name)) { + if (!isHtmlElementChildrenIncluded({ tag: parent.name, config })) return false; + } else { + if (!isJsxComponentChildrenIncluded({ name: parent.name, config })) return false; + } + + return true; + } + + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + if (!isHtmlElementIncluded({ tag: node.name, config })) return false; + } else { + if (!isJsxComponentIncluded({ name: node.name, config })) return false; + } + + return true; + } + + return true; + } + ); + + return strings; +} + +export function extractJsonOrYamlStrings({ + source, + type = 'json', + config +}: { + source: string; + type?: 'json' | 'yaml'; + config: Config; +}): string[] { + const strings: string[] = []; + + if (isEmptyArray(config.jsonOrYamlProperties.include)) return strings; + + const parsed = type === 'json' ? JSON.parse(source) : parseYaml(source); + + process(parsed); + + function process(value: unknown, property?: string) { + if (typeof value === 'string') { + if (property && isJsonOrYamlPropertyIncluded({ property, config })) strings.push(value); + return; + } + + if (isArray(value)) { + for (const item of value) { + process(item); + } + return; + } + + if (isObject(value)) { + for (const property in value) { + const item = (value as Record)[property]; + process(item, property); + } + return; + } + } + + return strings; +} + +function pushTidyString({ array, string }: { array: string[]; string: string }) { + if (!/^\s*$/.test(string)) { + array.push(string.replace(/(^\n|\r|\t|\v)+\s*/, '').replace(/\s+$/, ' ')); + } +} diff --git a/packages/deepl-mark/src/format.ts b/packages/deepl-mark/src/format.ts new file mode 100644 index 00000000..edfca687 --- /dev/null +++ b/packages/deepl-mark/src/format.ts @@ -0,0 +1,42 @@ +import prettier from 'prettier'; +import { getMarkdown, getMdast, mdNodeIs } from './ast/mdast.js'; +import { unwalk } from './ast/unwalk.js'; + +export async function format(markdown: string) { + /** + * `printWidth` is set to Infinity and `proseWrap` is set to never + * to avoid unnecessary linebreaks that break translation result + */ + const mdast = getMdast( + await prettier.format(markdown, { + parser: 'mdx', + printWidth: Infinity, + proseWrap: 'never', + useTabs: true + }) + ); + + /** + * remove empty surface flow expression nodes that sometimes + * are produced by prettier + */ + unwalk( + mdast, + (node, parent, index) => { + if (mdNodeIs(node, 'mdxFlowExpression') && expressionIsEmpty(node.value)) { + (parent!.children[index!] as unknown) = undefined; + } + }, + (node, parent) => { + delete node.position; + return mdNodeIs(parent, 'root'); + } + ); + + return getMarkdown(mdast); +} + +function expressionIsEmpty(text: string): boolean { + const regex = /^('|")\s*('|")$/; + return regex.test(text); +} diff --git a/packages/deepl-mark/src/index.ts b/packages/deepl-mark/src/index.ts new file mode 100644 index 00000000..81261f83 --- /dev/null +++ b/packages/deepl-mark/src/index.ts @@ -0,0 +1,69 @@ +import type { SourceLanguageCode, TargetLanguageCode, TranslateTextOptions } from 'deepl-node'; +import { getMarkdown, getMdast } from './ast/mdast.js'; +import type { UserConfig } from './config.js'; +import { resolveConfig } from './config.js'; +import { extractMdastStrings } from './extract.js'; +import { format } from './format.js'; +import { replaceMdastStrings } from './replace.js'; +import { translateStrings } from './translate.js'; + +/** + * Options to control which parts of the markdown are translated. + */ +export type TranslateOptions = Omit & { + /** DeepL API key. Falls back to `DEEPL_AUTH_KEY` env var if not provided. */ + apiKey?: string; + /** DeepL translation options (tagHandling, splitSentences, formality, glossaryId, etc.) */ + deeplOptions?: TranslateTextOptions; +}; + +/** + * Translate markdown/MDX content from one language to another using DeepL. + * + * Requires `DEEPL_AUTH_KEY` environment variable to be set. + * + * @param content - Markdown or MDX string to translate + * @param sourceLang - Source language code (e.g. 'en', 'de', 'fr') + * @param targetLang - Target language code (e.g. 'de', 'en-US', 'fr') + * @param options - Optional config to control extraction (frontmatter, jsx, html, etc.) + * @returns Translated markdown string + * + * @example + * ```ts + * import { translate } from 'deepmark'; + * + * const result = await translate('# Hello World', 'en', 'de'); + * console.log(result); // '# Hallo Welt' + * ``` + */ +export async function translate( + content: string, + sourceLang: SourceLanguageCode, + targetLang: TargetLanguageCode, + options?: TranslateOptions +): Promise { + const { apiKey, deeplOptions, ...configOptions } = options ?? {}; + + const config = resolveConfig({ + sourceLanguage: sourceLang, + outputLanguages: [targetLang], + directories: [['', '']], + ...configOptions + }); + + // Format, parse, extract translatable strings + const formatted = await format(content); + const mdast = getMdast(formatted); + const strings = extractMdastStrings({ mdast, config }); + + if (strings.length === 0) return content; + + // Translate via DeepL + const translated = await translateStrings(strings, sourceLang, targetLang, apiKey, deeplOptions); + + // Replace strings in the AST and serialize back to markdown + const result = replaceMdastStrings({ mdast, strings: translated, config }); + return getMarkdown(result); +} + +export type { SourceLanguageCode, TargetLanguageCode, TranslateTextOptions } from 'deepl-node'; diff --git a/packages/deepl-mark/src/lint.ts b/packages/deepl-mark/src/lint.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/deepl-mark/src/replace.ts b/packages/deepl-mark/src/replace.ts new file mode 100644 index 00000000..76a12b30 --- /dev/null +++ b/packages/deepl-mark/src/replace.ts @@ -0,0 +1,296 @@ +import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'; +import { + EsJsxElement, + EsJsxIdentifier, + esNodeIs, + resolveEstreePropertyPath +} from './ast/estree.js'; +import { eswalk } from './ast/eswalk.js'; +import type { MdNodeType, MdRoot } from './ast/mdast.js'; +import { mdNodeIs, mdNodeIsJsxElement } from './ast/mdast.js'; +import { unwalk } from './ast/unwalk.js'; +import { + type Config, + isHtmlTag, + isFrontmatterFieldIncluded, + isHtmlElementIncluded, + isHtmlElementAttributeIncluded, + isJsonOrYamlPropertyIncluded, + isJsxComponentIncluded, + isJsxComponentAttributeIncluded, + isMarkdownNodeIncluded, + isHtmlElementChildrenIncluded, + isJsxComponentChildrenIncluded +} from './config.js'; +import { isArray, isEmptyArray, isEmptyString, isObject, isString } from './utils.js'; + +export function replaceMdastStrings({ + mdast, + config, + strings +}: { + mdast: MdRoot; + strings: string[]; + config: Config; +}): MdRoot { + strings = strings.reverse(); + + unwalk( + mdast, + (node, __, _) => { + if (mdNodeIs(node, 'text')) { + node.value = strings.pop()!; + return; + } + + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, 'mdxJsxAttribute')) continue; + + if ( + !isHtmlElementAttributeIncluded({ tag: node.name, attribute: attribute.name, config }) + ) + continue; + + if (isString(attribute.value)) { + attribute.value = strings.pop(); + } else if (attribute.value?.data?.estree) { + const estree = attribute.value.data.estree; + + eswalk(estree, { + SimpleLiteral(esnode, _) { + if (isString(esnode.value)) esnode.value = strings.pop()!; + } + }); + } + } + } else { + for (const attribute of node.attributes) { + if (!mdNodeIs(attribute, 'mdxJsxAttribute')) continue; + + const componentName: string = node.name; + + const isAttributeIncluded = isJsxComponentAttributeIncluded({ + name: componentName, + attribute: attribute.name, + config + }); + + if (isString(attribute.value)) { + if (!isAttributeIncluded) continue; + + attribute.value = strings.pop(); + } else if (attribute.value?.data?.estree) { + if ( + !config.jsxComponents.include[componentName] || + !config.jsxComponents.include[componentName].attributes.some( + (attrName) => + attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + ) + ) + continue; + + const estree = attribute.value.data.estree; + + eswalk(estree, { + SimpleLiteral(esnode, _) { + if (isString(esnode.value)) esnode.value = strings.pop()!; + }, + JSXElement(esnode, _) { + const name = (esnode.openingElement.name as EsJsxIdentifier).name; + + if (isHtmlTag(name)) { + if ( + !isHtmlElementIncluded({ tag: name, config }) || + !isHtmlElementChildrenIncluded({ tag: name, config }) + ) + return false; + } else if ( + !isJsxComponentIncluded({ name: name, config }) || + !isJsxComponentChildrenIncluded({ name: name, config }) + ) + return false; + }, + JSXAttribute(esnode, parents) { + const name = + typeof esnode.name.name === 'string' ? esnode.name.name : esnode.name.name.name; + const parentName = ( + (parents[parents.length - 1] as EsJsxElement).openingElement + .name as EsJsxIdentifier + ).name; + + if (isHtmlTag(parentName)) { + if ( + !isHtmlElementAttributeIncluded({ tag: parentName, attribute: name, config }) + ) + return false; + } else if ( + !config.jsxComponents.include[name] || + !config.jsxComponents.include[name].attributes.some( + (attrName) => + attrName === attribute.name || attrName.startsWith(`${attribute.name}.`) + ) + ) { + return false; + } + }, + JSXText(esnode, _) { + esnode.value = strings.pop()!; + }, + Property(esnode, parents) { + if (!esNodeIs(esnode, 'Identifier')) return false; + + const propertyPath = resolveEstreePropertyPath(esnode, parents, attribute.name); + + if ( + !propertyPath || + !isJsxComponentAttributeIncluded({ + name: componentName, + attribute: propertyPath, + config + }) + ) + return false; + } + }); + } + } + } + } + + if (mdNodeIs(node, 'yaml')) { + if (isEmptyArray(config.frontmatterFields.include)) return; + if (isEmptyString(node.value)) return; + + const object: Record = parseYaml(node.value); + + for (const field in object) { + if (!isFrontmatterFieldIncluded({ field, config })) continue; + + const value = object[field]; + + if (isString(value)) { + object[field] = strings.pop(); + continue; + } + + if (isArray(value)) { + for (const [index, item] of value.entries()) { + if (!isString(item)) continue; + value[index] = strings.pop(); + } + } + } + + return; + } + }, + (node, parent) => { + if (!isMarkdownNodeIncluded({ type: node.type as MdNodeType, config })) return false; + + if (parent && mdNodeIsJsxElement(parent) && parent.name) { + if (isHtmlTag(parent.name)) { + if (!isHtmlElementChildrenIncluded({ tag: parent.name, config })) return false; + } else { + if (!isJsxComponentChildrenIncluded({ name: parent.name, config })) return false; + } + + return true; + } + + if (mdNodeIsJsxElement(node) && node.name) { + if (isHtmlTag(node.name)) { + if (!isHtmlElementIncluded({ tag: node.name, config })) return false; + } else { + if (!isJsxComponentIncluded({ name: node.name, config })) return false; + } + + return true; + } + + return true; + } + ); + + return mdast; +} + +export function replaceJsonOrYamlStrings({ + source, + type = 'json', + strings, + config +}: { + source: string; + type?: 'json' | 'yaml'; + strings: string[]; + config: Config; +}): string { + if (isEmptyArray(config.jsonOrYamlProperties.include)) return source; + + strings = strings.reverse(); + const parsed = type === 'json' ? JSON.parse(source) : parseYaml(source); + + process({ value: parsed }); + + function process(args: { value: unknown; parent?: never; property?: never; index?: never }): void; + function process(args: { + value: unknown; + parent: unknown[]; + property?: string | number | symbol; + index: number; + }): void; + function process(args: { + value: unknown; + parent: Record; + property: string | number | symbol; + index?: never; + }): void; + function process({ + value, + parent, + property, + index + }: { + value: unknown; + parent?: unknown[] | Record; + property?: string | number | symbol; + index?: number; + }) { + if (isArray(value)) { + for (const [index, item] of value.entries()) { + process({ value: item, parent: value, property, index }); + } + return; + } + + if (isObject(value)) { + for (const property in value) { + const item = (value as Record)[property]; + process({ value: item, parent: value, property }); + } + return; + } + + if (typeof value === 'string') { + if (property && isJsonOrYamlPropertyIncluded({ property, config })) { + if (isArray(parent) && index) { + parent[index] = strings.pop(); + + return; + } + + if (isObject(parent)) { + parent[property] = strings.pop(); + + return; + } + } + return; + } + } + + if (type === 'json') return JSON.stringify(parsed); + return stringifyYaml(parsed); +} diff --git a/packages/deepl-mark/src/translate.ts b/packages/deepl-mark/src/translate.ts new file mode 100644 index 00000000..d222f7f0 --- /dev/null +++ b/packages/deepl-mark/src/translate.ts @@ -0,0 +1,62 @@ +import type { SourceLanguageCode, TargetLanguageCode, TranslateTextOptions } from 'deepl-node'; +import { Translator } from 'deepl-node'; + +const DEFAULT_BATCH_SIZE = 50; +const MAX_RETRIES = 3; + +/** + * Translate an array of strings from sourceLang to targetLang using DeepL. + * Batches requests and retries on rate-limit (429) or server (5xx) errors. + */ +export async function translateStrings( + strings: string[], + sourceLang: SourceLanguageCode, + targetLang: TargetLanguageCode, + apiKey?: string, + deeplOptions?: TranslateTextOptions, + batchSize: number = DEFAULT_BATCH_SIZE +): Promise { + if (strings.length === 0) return []; + + const key = apiKey ?? process.env.DEEPL_AUTH_KEY; + if (!key) throw new Error('DeepL API key must be provided via options.apiKey or DEEPL_AUTH_KEY environment variable'); + + const deepl = new Translator(key); + const translations: string[] = new Array(strings.length).fill(''); + + const textOptions: TranslateTextOptions = { + tagHandling: 'html', + splitSentences: 'nonewlines', + ...deeplOptions + }; + + for (let i = 0; i < strings.length; i += batchSize) { + const batch = strings.slice(i, i + batchSize); + + const results = await retry(() => + deepl.translateText(batch, sourceLang, targetLang, textOptions) + ); + + for (let j = 0; j < batch.length; j++) { + translations[i + j] = results[j].text; + } + } + + return translations; +} + +async function retry(fn: () => Promise, retries = MAX_RETRIES): Promise { + for (let attempt = 0; ; attempt++) { + try { + return await fn(); + } catch (err: any) { + const status = err?.statusCode ?? err?.status; + const retryable = status === 429 || status === 456 || (status >= 500 && status < 600); + + if (!retryable || attempt >= retries) throw err; + + const delay = Math.min(1000 * 2 ** attempt, 10_000); + await new Promise((r) => setTimeout(r, delay)); + } + } +} diff --git a/packages/deepl-mark/src/utils.ts b/packages/deepl-mark/src/utils.ts new file mode 100644 index 00000000..48908a76 --- /dev/null +++ b/packages/deepl-mark/src/utils.ts @@ -0,0 +1,27 @@ +export function isArray(value: unknown): value is any[] { + return Array.isArray(value); +} + +export function isBoolean(value: unknown): value is boolean { + return typeof value === 'boolean'; +} + +export function isEmptyArray(array: any[]): boolean { + return array.length === 0; +} + +export function isEmptyObject(object: Object): boolean { + return Object.keys(object).length === 0; +} + +export function isEmptyString(string: string): boolean { + return string.length === 0; +} + +export function isObject(value: unknown): value is Record { + return isArray(value) ? false : typeof value == 'object' ? true : false; +} + +export function isString(value: unknown): value is string { + return typeof value === 'string'; +} diff --git a/packages/deepl-mark/src/vendor/mdast-util-html-comment.ts b/packages/deepl-mark/src/vendor/mdast-util-html-comment.ts new file mode 100644 index 00000000..dbe45c57 --- /dev/null +++ b/packages/deepl-mark/src/vendor/mdast-util-html-comment.ts @@ -0,0 +1,40 @@ +// @ts-nocheck — vendored from mdast-util-html-comment, uses custom handler types +import type { Extension } from 'mdast-util-from-markdown'; +import type { Options } from 'mdast-util-to-markdown'; + +export function htmlCommentFromMarkdown(): Extension { + return { + canContainEols: ['htmlComment'], + enter: { + htmlComment() { + this.buffer(); + } + }, + exit: { + htmlComment(token) { + const string = this.resume(); + + this.enter( + { + // @ts-ignore + type: 'htmlComment', + value: string.slice(0, -3) + }, + token + ); + + this.exit(token); + } + } + }; +} + +export function htmlCommentToMarkdown(): Options { + return { + handlers: { + htmlComment(node) { + return ``; + } + } + }; +} diff --git a/packages/deepl-mark/src/vendor/micromark-extension-html-comment.ts b/packages/deepl-mark/src/vendor/micromark-extension-html-comment.ts new file mode 100644 index 00000000..d0b25669 --- /dev/null +++ b/packages/deepl-mark/src/vendor/micromark-extension-html-comment.ts @@ -0,0 +1,128 @@ +// @ts-nocheck — vendored from micromark-extension-html-comment, uses custom token types +import { factorySpace } from 'micromark-factory-space'; +import { markdownLineEnding } from 'micromark-util-character'; +import { codes } from 'micromark-util-symbol/codes.js'; +import { types } from 'micromark-util-symbol/types.js'; +import type { Code, Extension, HtmlExtension, State, Tokenizer } from 'micromark-util-types'; + +export function htmlComment(): Extension { + return { + flow: { + [codes.lessThan]: { tokenize, concrete: true } + }, + text: { + [codes.lessThan]: { tokenize } + } + }; +} + +export function htmlCommentToHtml(): HtmlExtension { + return { + enter: { + htmlComment() { + this.buffer(); + } + }, + exit: { + htmlComment() { + this.resume(); + } + } + }; +} + +const tokenize: Tokenizer = (effects, ok, nok) => { + let value: string = ''; + + return start; + + function start(code: Code): State | void { + effects.enter('htmlComment'); + effects.enter('htmlCommentMarker'); + effects.consume(code); + value += '<'; + + return open; + } + + function open(code: Code): State | void { + if (value === '<' && code === codes.exclamationMark) { + effects.consume(code); + value += '!'; + return open; + } + + if (code === codes.dash) { + if (value === '= 6 && value.slice(-2) === '--') { + effects.consume(code); + effects.exit(types.data); + effects.exit('htmlCommentString'); + effects.exit('htmlComment'); + value += '>'; + + return ok; + } + + return nok(code); + } +}; diff --git a/packages/deepl-mark/tsconfig.json b/packages/deepl-mark/tsconfig.json new file mode 100644 index 00000000..0052f00d --- /dev/null +++ b/packages/deepl-mark/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "forceConsistentCasingInFileNames": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "emitDeclarationOnly": true, + "lib": [ + "ESNext" + ], + "noEmit": false, + "outDir": "./dist", + "rootDir": "src", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ESNext" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/__test__/**" + ] +} \ No newline at end of file diff --git a/packages/deepl-mark/vite.config.js b/packages/deepl-mark/vite.config.js new file mode 100644 index 00000000..4121637d --- /dev/null +++ b/packages/deepl-mark/vite.config.js @@ -0,0 +1,23 @@ +import { readFileSync } from 'fs'; +import np from 'path'; + +const CWD = process.cwd(); + +// Load .env into process.env for tests +try { + const env = readFileSync(np.resolve(CWD, '.env'), 'utf-8'); + for (const line of env.split('\n')) { + const match = line.match(/^\s*([\w.-]+)\s*=\s*"?([^"]*)"?\s*$/); + if (match) process.env[match[1]] ??= match[2]; + } +} catch { } + +/** @type { import('vite').UserConfig } */ +export default { + resolve: { + alias: { + $types: np.resolve(CWD, './src/types/index.js'), + $utils: np.resolve(CWD, './src/utils/index.js') + } + } +}; diff --git a/packages/i18n/dist/commands/glossary.js b/packages/i18n/dist/commands/glossary.js index f36d05f2..7efa11d5 100644 --- a/packages/i18n/dist/commands/glossary.js +++ b/packages/i18n/dist/commands/glossary.js @@ -17,7 +17,7 @@ const defaultOptions = (yargs) => { describe: 'Target file', }).option('dst', { describe: 'Path to the output file(s). Glob patters are supported', - default: '${SRC_DIR}/${DST_LANG}/${SRC_NAME}${SRC_EXT}' + default: '${SRC_DIR}/${SRC_NAME}_${DST_LANG}${SRC_EXT}' }).option('srcLang', { describe: 'Source language. Please run `osr-i18n info to see all supported languages`', default: '' diff --git a/packages/i18n/dist/commands/translate.js b/packages/i18n/dist/commands/translate.js index cd1862e4..b06cbd49 100644 --- a/packages/i18n/dist/commands/translate.js +++ b/packages/i18n/dist/commands/translate.js @@ -18,7 +18,7 @@ export const options = (yargs) => { describe: 'Path to the input file(s). Glob patters are supported', }).option('dst', { describe: 'Path to the output file(s). Glob patters are supported', - default: '${SRC_DIR}/${DST_LANG}/${SRC_NAME}${SRC_EXT}' + default: '${SRC_DIR}/${SRC_NAME}_${DST_LANG}${SRC_EXT}' }).option('formality', { describe: 'Formality: default|more|less', default: 'default' diff --git a/packages/i18n/dist/lib/translate_document.js b/packages/i18n/dist/lib/translate_document.js index dba25f74..d531e3d7 100644 --- a/packages/i18n/dist/lib/translate_document.js +++ b/packages/i18n/dist/lib/translate_document.js @@ -1,5 +1,7 @@ import { upload_document, check_document_status, download_document } from './deepl.js'; import * as fs from 'fs'; +import * as path from 'path'; +import { logger } from '../index.js'; const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); export const translateDocument = async (src, dst, options) => { const api_key = options.config?.deepl?.auth_key || options.api_key; @@ -10,6 +12,10 @@ export const translateDocument = async (src, dst, options) => { if (!src) { throw new Error('Source file missing for document translation.'); } + if (options.cache === false && fs.existsSync(dst)) { + logger.info(`Destination document ${dst} already exists. Skipping DeepL translation based on cache=false.`); + return fs.readFileSync(dst); + } const targetLangMatch = (options.dstLang || '').toUpperCase(); if (!targetLangMatch) { throw new Error('Target language missing for document translation.'); @@ -28,33 +34,37 @@ export const translateDocument = async (src, dst, options) => { deepLOptions.formality = options.formality; } try { - console.log(`Uploading document for translation: ${src}`); + logger.info(`Uploading document for translation: ${src}`); const { document_id, document_key } = await upload_document(deepLOptions, src); let isDone = false; - let pollInterval = 3000; + let pollInterval = 1500; + const startTime = Date.now(); + const timeoutMs = 300000; // 5 minutes while (!isDone) { - console.log(`Checking status for document_id: ${document_id}`); + if (Date.now() - startTime > timeoutMs) { + throw new Error(`Document translation timed out after ${timeoutMs / 60000} minutes.`); + } + logger.info(`Checking status for document_id: ${document_id}`); const statusRes = await check_document_status(api_key, document_id, document_key, is_free); if (statusRes.status === 'done') { isDone = true; - console.log(`Translation finished. Billed characters: ${statusRes.billed_characters}`); + logger.info(`Translation finished. Billed characters: ${statusRes.billed_characters}`); } else if (statusRes.status === 'error') { throw new Error(`DeepL Document Translation failed: ${statusRes.message}`); } else { - const remaining = statusRes.seconds_remaining || 5; - const waitTime = Math.max(pollInterval, remaining * 1000); - console.log(`Status: ${statusRes.status}, waiting ${waitTime / 1000}s...`); + const waitTime = Math.round(pollInterval); + logger.info(`Status: ${statusRes.status}, waiting ${waitTime / 1000}s...`); await delay(waitTime); - // Backoff slightly to ensure we don't spam if seconds_remaining is inaccurate + // Backoff slightly to ensure we don't spam pollInterval = Math.min(pollInterval * 1.5, 10000); } } - console.log(`Downloading translated document...`); + logger.info(`Downloading translated document...`); const fileBuffer = await download_document(api_key, document_id, document_key, is_free); + fs.mkdirSync(path.dirname(dst), { recursive: true }); fs.writeFileSync(dst, Buffer.from(fileBuffer)); - console.log(`Saved translated document to ${dst}`); return fileBuffer; } catch (err) { @@ -62,4 +72,4 @@ export const translateDocument = async (src, dst, options) => { throw err; } }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNsYXRlX2RvY3VtZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpYi90cmFuc2xhdGVfZG9jdW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUNILGVBQWUsRUFDZixxQkFBcUIsRUFDckIsaUJBQWlCLEVBRXBCLE1BQU0sWUFBWSxDQUFBO0FBQ25CLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFBO0FBRXhCLE1BQU0sS0FBSyxHQUFHLENBQUMsRUFBVSxFQUFFLEVBQUUsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUU5RSxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxLQUFLLEVBQ2xDLEdBQVcsRUFDWCxHQUFXLEVBQ1gsT0FBaUIsRUFDbkIsRUFBRTtJQUNBLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDO0lBQ25FLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsSUFBSSxLQUFLLENBQUM7SUFFekQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ1gsTUFBTSxJQUFJLEtBQUssQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFRCxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDUCxNQUFNLElBQUksS0FBSyxDQUFDLCtDQUErQyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUNELE1BQU0sZUFBZSxHQUFHLENBQUMsT0FBTyxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUM5RCxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDbEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxD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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNsYXRlX2RvY3VtZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpYi90cmFuc2xhdGVfZG9jdW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUNILGVBQWUsRUFDZixxQkFBcUIsRUFDckIsaUJBQWlCLEVBRXBCLE1BQU0sWUFBWSxDQUFBO0FBQ25CLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFBO0FBQ3hCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBQzVCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFFcEMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxFQUFVLEVBQUUsRUFBRSxDQUFDLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRTlFLE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLEtBQUssRUFDbEMsR0FBVyxFQUNYLEdBQVcsRUFDWCxPQUFpQixFQUNuQixFQUFFO0lBQ0EsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsUUFBUSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUM7SUFDbkUsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsUUFBUSxJQUFJLEtBQUssQ0FBQztJQUV6RCxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDWCxNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVELElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNQLE1BQU0sSUFBSSxLQUFLLENBQUMsK0NBQStDLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRUQsSUFBSSxPQUFPLENBQUMsS0FBSyxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FB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 \ No newline at end of file diff --git a/packages/i18n/dist/zod_schema_cli.js b/packages/i18n/dist/zod_schema_cli.js index 63344513..ed33dce9 100644 --- a/packages/i18n/dist/zod_schema_cli.js +++ b/packages/i18n/dist/zod_schema_cli.js @@ -6,7 +6,7 @@ export const TranslateOptionsSchema = z.object({ dry: z.boolean().default(false), env_key: z.string().default('OSR-CONFIG'), src: z.string().optional(), - dst: z.string().default('${SRC_DIR}/${DST_LANG}/${SRC_NAME}${SRC_EXT}'), + dst: z.string().default('${SRC_DIR}/${SRC_NAME}_${DST_LANG}${SRC_EXT}'), formality: z.enum(formalityLevels).default('default'), srcLang: z.string().default(''), cache: z.boolean().default(false), diff --git a/packages/i18n/package-lock.json b/packages/i18n/package-lock.json index 6b5c349f..851e71a2 100644 --- a/packages/i18n/package-lock.json +++ b/packages/i18n/package-lock.json @@ -12,6 +12,7 @@ "@polymech/cache": "file:../cache", "@polymech/commons": "file:../commons", "@polymech/core": "file:../core", + "@polymech/deepl-mark": "file:../deepl-mark", "@polymech/fs": "file:../fs", "@polymech/log": "file:../log", "@types/html-minifier-terser": "^7.0.2", @@ -122,6 +123,41 @@ "typescript": "^5.7.3" } }, + "../deepl-mark": { + "name": "@polymech/deepl-mark", + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "acorn": "^8.8.2", + "acorn-jsx": "^5.3.2", + "astring": "^1.8.4", + "deepl-node": "^1.24.0", + "mdast-util-from-markdown": "^1.3.0", + "mdast-util-frontmatter": "^1.0.1", + "mdast-util-gfm-table": "^1.0.7", + "mdast-util-mdx": "^2.0.1", + "mdast-util-to-markdown": "^1.5.0", + "micromark-extension-frontmatter": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.7", + "micromark-extension-mdxjs": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1", + "micromark-util-types": "^1.0.2", + "prettier": "^2.8.3", + "yaml": "^2.2.1" + }, + "devDependencies": { + "@types/estree": "^1.0.0", + "@types/mdast": "^3.0.10", + "@types/node": "^25.3.3", + "@types/prettier": "^2.7.2", + "@types/unist": "^2.0.6", + "esbuild": "^0.25.0", + "typescript": "^5.9.3", + "vitest": "^3.0.0" + } + }, "../fs": { "name": "@polymech/fs", "version": "0.13.41", @@ -332,6 +368,10 @@ "resolved": "../core", "link": true }, + "node_modules/@polymech/deepl-mark": { + "resolved": "../deepl-mark", + "link": true + }, "node_modules/@polymech/fs": { "resolved": "../fs", "link": true diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 222c8a9a..f0b3cb42 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -43,6 +43,7 @@ "@polymech/cache": "file:../cache", "@polymech/commons": "file:../commons", "@polymech/core": "file:../core", + "@polymech/deepl-mark": "file:../deepl-mark", "@polymech/fs": "file:../fs", "@polymech/log": "file:../log", "@types/html-minifier-terser": "^7.0.2", @@ -75,6 +76,7 @@ }, "scripts": { "test": "vitest run", + "test:document": "npm run test -- tests/document.e2e.test.ts", "test-with-coverage": "vitest run --coverage", "dev-test-watch": "vitest", "lint": "tslint --project=./tsconfig.json", @@ -102,4 +104,4 @@ "vitest": "^4.1.2", "webpack-cli": "^7.0.2" } -} +} \ No newline at end of file diff --git a/packages/i18n/salamand.json b/packages/i18n/salamand.json index edcf8ebc..9d70de1e 100644 --- a/packages/i18n/salamand.json +++ b/packages/i18n/salamand.json @@ -1,8 +1,8 @@ { "translate-es": { - "name": "Translate to EU Languages", + "name": "Translate to Spanish - Internal", "command": "pm-i18n", - "args": "translate --alt=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_&{DST_LANG}.md\" --srcLang='en' --dstLang='es,de,it,fr'", + "args": "translate --alt=true --src=\"$(FullName)\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_&{DST_LANG}.md\" --srcLang='en' --dstLang='es'", "description": "Translate to EU" } -} +} \ No newline at end of file diff --git a/packages/i18n/src/commands/glossary.ts b/packages/i18n/src/commands/glossary.ts index 212f68a7..6bba5cb9 100644 --- a/packages/i18n/src/commands/glossary.ts +++ b/packages/i18n/src/commands/glossary.ts @@ -20,7 +20,7 @@ const defaultOptions = (yargs: CLI.Argv) => { describe: 'Target file', }).option('dst', { describe: 'Path to the output file(s). Glob patters are supported', - default: '${SRC_DIR}/${DST_LANG}/${SRC_NAME}${SRC_EXT}' + default: '${SRC_DIR}/${SRC_NAME}_${DST_LANG}${SRC_EXT}' }).option('srcLang', { describe: 'Source language. Please run `osr-i18n info to see all supported languages`', default: '' diff --git a/packages/i18n/src/commands/translate.ts b/packages/i18n/src/commands/translate.ts index 52f9888e..fab92cf6 100644 --- a/packages/i18n/src/commands/translate.ts +++ b/packages/i18n/src/commands/translate.ts @@ -22,7 +22,7 @@ export const options = (yargs: CLI.Argv) => { describe: 'Path to the input file(s). Glob patters are supported', }).option('dst', { describe: 'Path to the output file(s). Glob patters are supported', - default: '${SRC_DIR}/${DST_LANG}/${SRC_NAME}${SRC_EXT}' + default: '${SRC_DIR}/${SRC_NAME}_${DST_LANG}${SRC_EXT}' }).option('formality', { describe: 'Formality: default|more|less', default: 'default' diff --git a/packages/i18n/src/lib/translate_document.ts b/packages/i18n/src/lib/translate_document.ts index 7587c5ed..c4de90d7 100644 --- a/packages/i18n/src/lib/translate_document.ts +++ b/packages/i18n/src/lib/translate_document.ts @@ -6,6 +6,8 @@ import { IDeepLOptions } from './deepl.js' import * as fs from 'fs' +import * as path from 'path' +import { logger } from '../index.js' const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); @@ -16,19 +18,25 @@ export const translateDocument = async ( ) => { const api_key = options.config?.deepl?.auth_key || options.api_key; const is_free = options.config?.deepl?.free_api || false; - + if (!api_key) { throw new Error('DeepL auth_key is missing.'); } - + if (!src) { throw new Error('Source file missing for document translation.'); } + + if (options.cache === false && fs.existsSync(dst)) { + logger.info(`Destination document ${dst} already exists. Skipping DeepL translation based on cache=false.`); + return fs.readFileSync(dst); + } + const targetLangMatch = (options.dstLang || '').toUpperCase(); if (!targetLangMatch) { - throw new Error('Target language missing for document translation.'); + throw new Error('Target language missing for document translation.'); } - + // Structure DeepL Options const deepLOptions: IDeepLOptions = { free_api: is_free, @@ -36,7 +44,7 @@ export const translateDocument = async ( target_lang: targetLangMatch as any, text: '' }; - + if (options.srcLang) { deepLOptions.source_lang = options.srcLang.toUpperCase() as any; } @@ -45,39 +53,41 @@ export const translateDocument = async ( } try { - console.log(`Uploading document for translation: ${src}`); + logger.info(`Uploading document for translation: ${src}`); const { document_id, document_key } = await upload_document(deepLOptions, src); - + let isDone = false; - let pollInterval = 3000; - + let pollInterval = 1500; + const startTime = Date.now(); + const timeoutMs = 300000; // 5 minutes + while (!isDone) { - console.log(`Checking status for document_id: ${document_id}`); + if (Date.now() - startTime > timeoutMs) { + throw new Error(`Document translation timed out after ${timeoutMs / 60000} minutes.`); + } + + logger.info(`Checking status for document_id: ${document_id}`); const statusRes = await check_document_status(api_key, document_id, document_key, is_free); - + if (statusRes.status === 'done') { isDone = true; - console.log(`Translation finished. Billed characters: ${statusRes.billed_characters}`); + logger.info(`Translation finished. Billed characters: ${statusRes.billed_characters}`); } else if (statusRes.status === 'error') { throw new Error(`DeepL Document Translation failed: ${statusRes.message}`); } else { - const remaining = statusRes.seconds_remaining || 5; - const waitTime = Math.max(pollInterval, remaining * 1000); - console.log(`Status: ${statusRes.status}, waiting ${waitTime / 1000}s...`); + const waitTime = Math.round(pollInterval); + logger.info(`Status: ${statusRes.status}, waiting ${waitTime / 1000}s...`); await delay(waitTime); - // Backoff slightly to ensure we don't spam if seconds_remaining is inaccurate - pollInterval = Math.min(pollInterval * 1.5, 10000); + // Backoff slightly to ensure we don't spam + pollInterval = Math.min(pollInterval * 1.5, 10000); } } - - console.log(`Downloading translated document...`); + logger.info(`Downloading translated document...`); const fileBuffer = await download_document(api_key, document_id, document_key, is_free); - + fs.mkdirSync(path.dirname(dst), { recursive: true }); fs.writeFileSync(dst, Buffer.from(fileBuffer)); - console.log(`Saved translated document to ${dst}`); - return fileBuffer; - + } catch (err: any) { console.error(`Error during document translation: ${err.message}`); throw err; diff --git a/packages/i18n/src/zod_schema_cli.ts b/packages/i18n/src/zod_schema_cli.ts index 7e71e2bb..7e4d098d 100644 --- a/packages/i18n/src/zod_schema_cli.ts +++ b/packages/i18n/src/zod_schema_cli.ts @@ -8,7 +8,7 @@ export const TranslateOptionsSchema = z.object({ dry: z.boolean().default(false), env_key: z.string().default('OSR-CONFIG'), src: z.string().optional(), - dst: z.string().default('${SRC_DIR}/${DST_LANG}/${SRC_NAME}${SRC_EXT}'), + dst: z.string().default('${SRC_DIR}/${SRC_NAME}_${DST_LANG}${SRC_EXT}'), formality: z.enum(formalityLevels).default('default'), srcLang: z.string().default(''), cache: z.boolean().default(false), diff --git a/packages/i18n/tests/document.e2e.test.ts b/packages/i18n/tests/document.e2e.test.ts index cd7642bb..0ccd290e 100644 --- a/packages/i18n/tests/document.e2e.test.ts +++ b/packages/i18n/tests/document.e2e.test.ts @@ -12,27 +12,47 @@ describe('DeepL Document API Translation (Live)', () => { it('should translate test.pdf natively as a document', async () => { const srcPath = path.resolve(process.cwd(), 'tests/documents/test.pdf'); + const dstPath = path.resolve(process.cwd(), 'tests/documents/test_de.pdf'); // Assert the test file exists before running expect(fs.existsSync(srcPath)).toBe(true); + // Clean up any old output file + if (fs.existsSync(dstPath)) { + fs.unlinkSync(dstPath); + } + const { stdout, stderr } = await exec(`node ${CLI_PATH} translate --src="${srcPath}" --srcLang="en" --dstLang="de"`); console.log(stdout, stderr); // We verify the translation loop succeeds without errors. expect(stderr).not.toMatch(/error/i); - }, 60000); // 60s timeout for real API call + + // Verify that the output document got downloaded correctly + expect(fs.existsSync(dstPath)).toBe(true); + expect(fs.statSync(dstPath).size).toBeGreaterThan(0); + }, 120000); // 120s timeout for real API call it('should translate test.xlsx as a document specifically requesting engine=document', async () => { const srcPath = path.resolve(process.cwd(), 'tests/documents/test.xlsx'); + const dstPath = path.resolve(process.cwd(), 'tests/documents/test_de.xlsx'); // Assert the test file exists before running expect(fs.existsSync(srcPath)).toBe(true); + // Clean up any old output file + if (fs.existsSync(dstPath)) { + fs.unlinkSync(dstPath); + } + const { stdout, stderr } = await exec(`node ${CLI_PATH} translate --src="${srcPath}" --srcLang="en" --dstLang="de" --engine="document"`); console.log(stdout, stderr); // We verify the translation loop succeeds without errors. expect(stderr).not.toMatch(/error/i); - }, 60000); // 60s timeout for real API call + + // Verify that the output document got downloaded correctly + expect(fs.existsSync(dstPath)).toBe(true); + expect(fs.statSync(dstPath).size).toBeGreaterThan(0); + }, 120000); // 120s timeout for real API call }) diff --git a/packages/i18n/tests/documents/test_de.pdf b/packages/i18n/tests/documents/test_de.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3d6b66c34a3da184d313ffc30960d7b1cbd158f1 GIT binary patch literal 146107 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̗^UZ#19q#{+qs$t1ctI(F)lT04s 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 B3;zHB literal 0 HcmV?d00001 diff --git a/packages/i18n/tests/documents/test_de.xlsx b/packages/i18n/tests/documents/test_de.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..026ea2ba487acd37d7ec252d6f15963b9c2503ca GIT binary patch literal 5846 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