@diplodoc/cli-tests 0.0.0-rc-reusable-e2es-202504041505 → 4.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/bin.mjs +3 -7
  2. package/e2e/__snapshots__/load-custom-resources.spec.ts.snap +4 -4
  3. package/e2e/__snapshots__/metadata.spec.ts.snap +2 -2
  4. package/e2e/__snapshots__/regression.test.ts.snap +13 -13
  5. package/e2e/__snapshots__/rtl.spec.ts.snap +3 -3
  6. package/e2e/__snapshots__/translation.spec.ts.snap +426 -0
  7. package/e2e/generate-map.spec.ts +2 -5
  8. package/e2e/include-toc.test.ts +15 -18
  9. package/e2e/load-custom-resources.spec.ts +3 -6
  10. package/e2e/metadata.spec.ts +3 -6
  11. package/e2e/plugin-corner-cases.spec.ts +3 -6
  12. package/e2e/regression.test.ts +7 -10
  13. package/e2e/rtl.spec.ts +3 -6
  14. package/e2e/translation.spec.ts +55 -0
  15. package/fixtures/cliAdapter.ts +47 -7
  16. package/fixtures/runners/binary.ts +5 -19
  17. package/fixtures/runners/source.ts +4 -9
  18. package/fixtures/utils/file.ts +23 -16
  19. package/fixtures/utils/test.ts +1 -1
  20. package/mocks/translation/dir-files/input/ru/_includes/test.md +1 -0
  21. package/mocks/translation/dir-files/input/ru/_no-translate/exclude.md +1 -0
  22. package/mocks/translation/dir-files/input/ru/aboba.md +7 -0
  23. package/mocks/translation/dir-files/input/ru/index.md +3 -0
  24. package/mocks/translation/dir-files/input/ru/nested/index-yfm.md +2 -0
  25. package/mocks/translation/dir-files/input/ru/nested/index.yaml +24 -0
  26. package/mocks/translation/dir-files/input/ru/nested/not-in-toc.md +20 -0
  27. package/mocks/translation/dir-files/input/ru/nested/syntax/base.md +1 -0
  28. package/mocks/translation/dir-files/input/ru/nested/syntax/index.md +2 -0
  29. package/mocks/translation/dir-files/input/ru/nested/syntax/lists.md +1 -0
  30. package/mocks/translation/dir-files/input/ru/nested/toc.yaml +18 -0
  31. package/mocks/translation/dir-files/input/ru/not-in-toc.md +20 -0
  32. package/mocks/translation/dir-files/input/ru/toc.yaml +7 -0
  33. package/mocks/translation/yaml-scheme/input/ru/.yfm +1 -0
  34. package/mocks/translation/yaml-scheme/input/ru/index-direct.yaml +133 -0
  35. package/mocks/translation/yaml-scheme/input/ru/index.yaml +32 -0
  36. package/mocks/translation/yaml-scheme/input/ru/toc.yaml +19 -0
  37. package/package.json +7 -5
@@ -0,0 +1,55 @@
1
+ import {describe, test} from 'vitest';
2
+ import {TestAdapter, TranslateRunArgs, compareDirectories, getTestPaths} from '../fixtures';
3
+
4
+ const generateMapTestTemplate = (
5
+ testTitle: string,
6
+ testRootPath: string,
7
+ args: TranslateRunArgs,
8
+ ) => {
9
+ test(testTitle, async () => {
10
+ const {inputPath, outputPath} = getTestPaths(testRootPath);
11
+
12
+ await TestAdapter.testTranslatePass(inputPath, outputPath, args);
13
+
14
+ await compareDirectories(outputPath, true);
15
+ });
16
+ };
17
+
18
+ const generateFilesYamlTestTemplate = (
19
+ testTitle: string,
20
+ testRootPath: string,
21
+ args: TranslateRunArgs,
22
+ ) => {
23
+ test(testTitle, async () => {
24
+ const {inputPath, outputPath} = getTestPaths(testRootPath);
25
+
26
+ await TestAdapter.testTranslatePass(inputPath, outputPath, args);
27
+
28
+ await compareDirectories(outputPath);
29
+ });
30
+ };
31
+
32
+ describe('Translate command', () => {
33
+ generateMapTestTemplate('filter files on extract', 'mocks/translation/dir-files', {
34
+ subcommand: 'extract',
35
+ source: 'ru-RU',
36
+ target: 'es-ES',
37
+ });
38
+
39
+ generateMapTestTemplate(
40
+ 'filter files on extract with extra exclude option',
41
+ 'mocks/translation/dir-files',
42
+ {
43
+ subcommand: 'extract',
44
+ source: 'ru-RU',
45
+ target: 'es-ES',
46
+ additionalArgs: '--exclude ru/_no-translate/*.md',
47
+ },
48
+ );
49
+
50
+ generateFilesYamlTestTemplate('extract yaml scheme files', 'mocks/translation/yaml-scheme', {
51
+ subcommand: 'extract',
52
+ source: 'ru-RU',
53
+ target: 'en-US',
54
+ });
55
+ });
@@ -1,21 +1,28 @@
1
1
  import {Runner, createRunner} from './runners';
2
2
  import {cleanupDirectory} from './utils/file';
3
3
 
4
- export interface TestRunArgs {
4
+ export interface BuildRunArgs {
5
5
  md2md?: boolean;
6
6
  md2html?: boolean;
7
7
  args?: string;
8
8
  }
9
9
 
10
- export class CliTestAdapter {
10
+ export interface TranslateRunArgs {
11
+ subcommand: 'extract';
12
+ source: string;
13
+ target: string;
14
+ additionalArgs?: string;
15
+ }
16
+
17
+ class CliTestAdapter {
11
18
  private readonly runner: Runner = createRunner();
12
19
 
13
- async testPass(
20
+ async testBuildPass(
14
21
  inputPath: string,
15
22
  outputPath: string,
16
- {md2md = true, md2html = true, args = ''}: TestRunArgs = {},
23
+ {md2md = true, md2html = true, args = ''}: BuildRunArgs = {},
17
24
  ): Promise<void> {
18
- cleanupDirectory(outputPath);
25
+ await cleanupDirectory(outputPath);
19
26
 
20
27
  const baseArgs = [
21
28
  '--input',
@@ -26,14 +33,47 @@ export class CliTestAdapter {
26
33
  ];
27
34
 
28
35
  if (md2md && md2html) {
29
- cleanupDirectory(`${outputPath}-html`);
36
+ await cleanupDirectory(`${outputPath}-html`);
30
37
 
31
38
  await this.runner.runYfmDocs([...baseArgs, '--output', outputPath, '-f', 'md']);
32
- await this.runner.runYfmDocs([...baseArgs, '--output', `${outputPath}-html`, '-f', 'html']);
39
+ await this.runner.runYfmDocs([
40
+ ...baseArgs,
41
+ '--output',
42
+ `${outputPath}-html`,
43
+ '-f',
44
+ 'html',
45
+ ]);
33
46
  } else if (md2md) {
34
47
  await this.runner.runYfmDocs([...baseArgs, '--output', outputPath, '-f', 'md']);
35
48
  } else {
36
49
  await this.runner.runYfmDocs([...baseArgs, '--output', outputPath]);
37
50
  }
38
51
  }
52
+
53
+ async testTranslatePass(
54
+ inputPath: string,
55
+ outputPath: string,
56
+ {subcommand, source, target, additionalArgs = ''}: TranslateRunArgs,
57
+ ): Promise<void> {
58
+ await cleanupDirectory(outputPath);
59
+
60
+ const baseArgs = [
61
+ 'translate',
62
+ subcommand,
63
+ '--quiet',
64
+ '--input',
65
+ inputPath,
66
+ '--output',
67
+ outputPath,
68
+ '--source',
69
+ source,
70
+ '--target',
71
+ target,
72
+ ...additionalArgs.split(' ').filter(Boolean),
73
+ ];
74
+
75
+ await this.runner.runYfmDocs(baseArgs);
76
+ }
39
77
  }
78
+
79
+ export const TestAdapter = new CliTestAdapter();
@@ -1,5 +1,5 @@
1
- import {spawn} from 'child_process';
2
1
  import {Runner} from './types';
2
+ import {execa} from 'execa';
3
3
 
4
4
  export class BinaryRunner implements Runner {
5
5
  private readonly binaryPath: string;
@@ -8,24 +8,10 @@ export class BinaryRunner implements Runner {
8
8
  this.binaryPath = binaryPath;
9
9
  }
10
10
 
11
- runYfmDocs(argv: string[]): Promise<void> {
12
- return this.spawnAsync(argv);
13
- }
14
-
15
- private spawnAsync(argv: string[]): Promise<void> {
16
- return new Promise((resolve, reject) => {
17
- const child = spawn(this.binaryPath, argv, {
18
- stdio: 'inherit',
19
- });
11
+ async runYfmDocs(argv: string[]) {
12
+ const {all} = await execa(this.binaryPath, argv, {all: true});
20
13
 
21
- child.on('error', reject);
22
- child.on('exit', (code) => {
23
- if (code === 0) {
24
- resolve();
25
- } else {
26
- reject(new Error(`Process exited with code ${code}`));
27
- }
28
- });
29
- });
14
+ // eslint-disable-next-line no-console
15
+ console.log(all);
30
16
  }
31
17
  }
@@ -1,15 +1,10 @@
1
- export class SourceRunner {
2
- private readonly cliPath: string;
3
-
4
- constructor() {
5
- // Allow overriding the CLI path via environment variable, otherwise use local build
6
- this.cliPath = process.env.DIPLODOC_CLI_BUILD_PATH || require.resolve('../../../build');
7
- }
1
+ const MODULE_PATH = require.resolve('../../../');
8
2
 
3
+ export class SourceRunner {
9
4
  async runYfmDocs(argv: string[]): Promise<void> {
10
- const {run} = await import(this.cliPath);
5
+ const {run} = await import(MODULE_PATH);
11
6
 
12
- const baseArgs = ['node', this.cliPath, ...argv];
7
+ const baseArgs = ['node', MODULE_PATH, ...argv];
13
8
 
14
9
  const exitCode = await run(baseArgs);
15
10
 
@@ -1,9 +1,9 @@
1
- import {readFileSync} from 'fs';
2
- import {join, resolve} from 'path';
3
- import walkSync from 'walk-sync';
1
+ import {readFileSync} from 'node:fs';
2
+ import {join, resolve} from 'node:path';
3
+ import {glob} from 'glob';
4
4
  import {bundleless, platformless} from './test';
5
5
  import {expect} from 'vitest';
6
- import shell from 'shelljs';
6
+ import {$} from 'execa';
7
7
 
8
8
  export function getFileContent(filePath: string) {
9
9
  return bundleless(platformless(readFileSync(filePath, 'utf8')));
@@ -12,24 +12,31 @@ export function getFileContent(filePath: string) {
12
12
  const uselessFile = (file: string) =>
13
13
  !['_bundle/', '_assets/', '_search/'].some((part) => file.includes(part));
14
14
 
15
- export function compareDirectories(outputPath: string): void {
16
- const filesFromOutput = walkSync(outputPath, {
17
- directories: false,
18
- includeBasePath: false,
19
- }).sort();
15
+ export async function compareDirectories(outputPath: string, ignoreFileContent = false) {
16
+ const filesFromOutput = (
17
+ await glob(`**/*`, {
18
+ cwd: outputPath,
19
+ dot: true,
20
+ follow: true,
21
+ nodir: true,
22
+ posix: true,
23
+ })
24
+ ).sort();
20
25
 
21
26
  expect(bundleless(JSON.stringify(filesFromOutput, null, 2))).toMatchSnapshot('filelist');
22
27
 
23
- filesFromOutput.filter(uselessFile).forEach((filePath) => {
24
- const content = getFileContent(resolve(outputPath, filePath));
25
- expect(content).toMatchSnapshot(filePath);
26
- });
28
+ if (!ignoreFileContent) {
29
+ filesFromOutput.filter(uselessFile).forEach((filePath) => {
30
+ const content = getFileContent(resolve(outputPath, filePath));
31
+ expect(content).toMatchSnapshot(filePath);
32
+ });
33
+ }
27
34
  }
28
35
 
29
36
  type TestPaths = {
30
37
  inputPath: string;
31
38
  outputPath: string;
32
- }
39
+ };
33
40
 
34
41
  export function getTestPaths(testRootPath: string): TestPaths {
35
42
  return {
@@ -38,6 +45,6 @@ export function getTestPaths(testRootPath: string): TestPaths {
38
45
  };
39
46
  }
40
47
 
41
- export function cleanupDirectory(path: string): void {
42
- shell.rm('-rf', path);
48
+ export function cleanupDirectory(path: string) {
49
+ return $`rm -rf ${path}`;
43
50
  }
@@ -1,4 +1,4 @@
1
- import {readFileSync} from 'fs';
1
+ import {readFileSync} from 'node:fs';
2
2
 
3
3
  export function platformless(text: string): string {
4
4
  return text
@@ -0,0 +1 @@
1
+ ## Заголовок include
@@ -0,0 +1 @@
1
+ ## Не переводить
@@ -0,0 +1,7 @@
1
+ ## Заголовок include
2
+
3
+ [ссылка на файл не в toc.yaml](./aboba-not-in-toc.md)
4
+
5
+ {% include [Описание](./_includes/test.md) %}
6
+
7
+
@@ -0,0 +1,3 @@
1
+ ## Заголовок index
2
+
3
+ lorem
@@ -0,0 +1,2 @@
1
+ # Yandex Flavored Markdown
2
+
@@ -0,0 +1,24 @@
1
+ title: Diplodoc
2
+ description: Diplodoc - платформа работы с документацией в парадигме Documentation as a Code.
3
+ meta:
4
+ title: Метаданные
5
+ noIndex: true
6
+ links:
7
+ - title: API Yandex Cloud
8
+ description: Архитектура API Yandex Cloud (урезанная)
9
+ href: api-design-guide
10
+ - title: API Yandex Cloud Абсолют
11
+ description: Архитектура API Yandex Cloud (урезанная) с абсолютным путем
12
+ href: /api-design-guide
13
+ - title: Обзор системы
14
+ description: Описание платформы, ее основные возможности, особенности и преимущества.
15
+ href: about.md
16
+ - title: Как попробовать
17
+ description: С чего начать работу
18
+ href: how-it-work.md
19
+ - title: Yandex Flavored Markdown
20
+ description: Описание языка Yandex Flavored Markdown.
21
+ href: index-yfm.md
22
+
23
+
24
+
@@ -0,0 +1,20 @@
1
+ ---
2
+ keywords: яндекс станция макс
3
+ voter: no
4
+ title: Справочник голосовых команд для Яндекс Станции — Справка
5
+ ---
6
+
7
+
8
+ aboba
9
+
10
+ Общение в issues и pull requests в GitHub на русском и преимущественно английском языке.- Презентация/продвижение продукта на разных **![i](./complex/_images/camera.svg) [конференциях](*conference)**{.photo} и **![i](./complex/_images/camera.svg) [митапах](*meetup)**{.photo}.
11
+
12
+ абоба тест
13
+
14
+ {% include [Описание](./_includes/test.md) %}
15
+
16
+ [*meetup]:
17
+ ![i](./_images/highload.png)
18
+
19
+ [*conference]:
20
+ ![i](./_images/highload.png)
@@ -0,0 +1 @@
1
+ # Базовая разметка
@@ -0,0 +1,2 @@
1
+ # Синтаксис
2
+
@@ -0,0 +1,18 @@
1
+ title: Diplodoc
2
+ href: index.yaml
3
+ items:
4
+ - name: API Yandex Cloud
5
+ href: index.yaml
6
+ - name: Yandex Flavored Markdown
7
+ href: index-yfm.md
8
+ items:
9
+ - name: Синтаксис
10
+ items:
11
+ - name: Обзор
12
+ href: syntax/index.md
13
+ when: test == "nested"
14
+ - name: Базовая разметка
15
+ href: syntax/base.md
16
+ - name: Списки
17
+ href: syntax/lists.md
18
+ - name: Таблицы
@@ -0,0 +1,20 @@
1
+ ---
2
+ keywords: яндекс станция макс
3
+ voter: no
4
+ title: Справочник голосовых команд для Яндекс Станции — Справка
5
+ ---
6
+
7
+
8
+ aboba
9
+
10
+ Общение в issues и pull requests в GitHub на русском и преимущественно английском языке.- Презентация/продвижение продукта на разных **![i](./complex/_images/camera.svg) [конференциях](*conference)**{.photo} и **![i](./complex/_images/camera.svg) [митапах](*meetup)**{.photo}.
11
+
12
+ абоба тест
13
+
14
+ {% include [Описание](./_includes/test.md) %}
15
+
16
+ [*meetup]:
17
+ ![i](./_images/highload.png)
18
+
19
+ [*conference]:
20
+ ![i](./_images/highload.png)
@@ -0,0 +1,7 @@
1
+ title: Test123
2
+ href: index.md
3
+ items:
4
+ - name: Aboba
5
+ href: aboba.md
6
+ - name: Не переводить
7
+ href: _no-translate/exclude.md
@@ -0,0 +1 @@
1
+ langs: ['ru']
@@ -0,0 +1,133 @@
1
+ meta:
2
+ title: Справка Сервиса
3
+ description: В Справке Сервиса собрали всю необходимую информацию.
4
+
5
+ fullScreen: true
6
+ blocks:
7
+ - type: header-block
8
+ title: <div class="u-breadcrumbs"></div>
9
+ breadcrumbs:
10
+ items:
11
+ - text: Справка
12
+ url: 'https://site.ru/support/'
13
+ - text: Реклама
14
+ url: 'https://site.ru/support/adv/'
15
+ - type: 'filter-block'
16
+ centered: false
17
+ title:
18
+ text: 'Сервис'
19
+ tags:
20
+ - id: 'one'
21
+ label: 'Для специалистов'
22
+ - id: 'two'
23
+ label: 'Для предпринимателей'
24
+ colSizes:
25
+ all: 12
26
+ xl: 4
27
+ sm: 6
28
+ md: 4
29
+
30
+ items:
31
+ - tags:
32
+ - 'one'
33
+ card:
34
+ type: 'image-card'
35
+ image: 'https://site.ru/img/orig'
36
+ title: 'Единая перфоманс-кампания'
37
+ text: '<div>Комплексно решайте любые перфоманс-задачи в рамках одной кампании</div><div class="u-card__more">Подробнее</>'
38
+ margins: 'm'
39
+ border: shadow
40
+ url: 'unified-performance-campaign/about.md'
41
+ urlTitle: 'Подробнее'
42
+
43
+ - tags:
44
+ - 'one'
45
+ card:
46
+ type: 'image-card'
47
+ image: 'https://site.ru/img2/orig'
48
+ title: 'Товарная кампания'
49
+ text: '<div>Используйте простой инструмент для продвижения товаров интернете</div><div class="u-card__more">Подробнее</>'
50
+ margins: 'm'
51
+ border: shadow
52
+ url: 'campaign-master/product-campaign.md'
53
+ urlTitle: 'Подробнее'
54
+
55
+ - tags:
56
+ - 'one'
57
+ card:
58
+ type: 'image-card'
59
+ image: 'https://site.ru/img2/orig'
60
+ title: 'Реклама в Telegram'
61
+ text: '<div>Покажите релевантную рекламу в телеграм-каналах партнеров РСЯ</div><div class="u-card__more">Подробнее</>'
62
+ margins: 'm'
63
+ border: shadow
64
+ url: 'efficiency/telegram-ads.md'
65
+ urlTitle: 'Подробнее'
66
+
67
+ - tags:
68
+ - 'one'
69
+ card:
70
+ type: 'background-card'
71
+ title: Часто ищут
72
+ text: ''
73
+ backgroundColor: '#F3F6FC'
74
+ border: line
75
+ links:
76
+ - text: Статистика
77
+ url: 'statistics.md'
78
+ arrow: true
79
+ theme: 'normal'
80
+ - text: Управление фидами
81
+ url: 'feeds/about.md'
82
+ arrow: true
83
+ theme: 'normal'
84
+
85
+ - tags:
86
+ - 'one'
87
+ card:
88
+ type: background-card
89
+ title: Сервисные функции
90
+ text: ''
91
+ backgroundColor: '#F3F6FC'
92
+ border: line
93
+ links:
94
+ - text: Правила и модерация
95
+ url: 'moderation/adv-rules.md'
96
+ arrow: true
97
+ theme: 'normal'
98
+ - text: Технологии и сервисы
99
+ url: 'technologies-and-services/technologies-and-services.md'
100
+ arrow: true
101
+ theme: 'normal'
102
+ - text: Поддержка 24/7
103
+ url: 'troubleshooting/other.md'
104
+ arrow: true
105
+ theme: 'normal'
106
+
107
+ - tags:
108
+ - 'one'
109
+ card:
110
+ type: background-card
111
+ title: Полезные ссылки
112
+ text: ''
113
+ backgroundColor: '#F3F6FC'
114
+ border: line
115
+ links:
116
+ - text: Глоссарий
117
+ url: 'glossary.md'
118
+ arrow: true
119
+ theme: 'normal'
120
+
121
+ - tags:
122
+ - 'two'
123
+ card:
124
+ type: 'image-card'
125
+ image: 'https://site.ru/img4/orig'
126
+ title: 'Простой старт'
127
+ text: '<div>Минимум ручных настроек: достаточно указать ссылку на сайт</div><div class="u-card__more">Подробнее</>'
128
+ margins: 'm'
129
+ border: shadow
130
+ url: 'products-automatic/about.md'
131
+ urlTitle: 'Подробнее'
132
+
133
+ animated: false
@@ -0,0 +1,32 @@
1
+ blocks:
2
+ - type: 'content-layout-block'
3
+ textWidth: 'l'
4
+ textContent:
5
+ title: 'Введение'
6
+ text: "
7
+ API сервиса:
8
+ - Управлять счетчиками, их настройками и правами доступа, не используя веб-интерфейс.\n
9
+ - Получать информацию о посещаемости сайта и другие данные.\n
10
+ - Формировать отчеты, в том числе с помощью сегментации и параметризации.\n
11
+ "
12
+ - type: 'card-layout-block'
13
+ colSizes:
14
+ all: 12
15
+ lg: 4
16
+ md: 6
17
+ sm: 12
18
+ xl: 4
19
+ indent:
20
+ bottom: 'xs'
21
+ title: 'Виды API'
22
+ animated: false
23
+ children:
24
+ - type: 'basic-card'
25
+ title: 'API управления'
26
+ text: "Управление счетчиками, целями, фильтрами и другими объектами Сервиса (например, создать счетчик, отредактировать его настройки, создать цель, выдать права доступа)."
27
+ urlTitle: 'API управления'
28
+ border: 'shadow'
29
+ controlPosition: 'content'
30
+ url: 'management/index'
31
+ target: ''
32
+ animated: false
@@ -0,0 +1,19 @@
1
+ title: Заголовок
2
+ href: index.yaml
3
+ items:
4
+ - name: Дочерняя страница
5
+ - href: index-direct.yaml
6
+ navigation:
7
+ header:
8
+ leftItems:
9
+ - text: 'Инструменты'
10
+ type: 'dropdown'
11
+ items:
12
+ - text: 'Пункт меню'
13
+ type: 'link'
14
+ href: 'menu/item'
15
+ logo:
16
+ light:
17
+ icon: 'https://icon.link/logo.svg'
18
+ url: 'https://site.ru'
19
+ urlTitle : 'На сайт сервиса'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diplodoc/cli-tests",
3
- "version": "0.0.0-rc-reusable-e2es-202504041505",
3
+ "version": "4.59.0",
4
4
  "bin": {
5
5
  "diplodoc-cli-test": "bin.mjs"
6
6
  },
@@ -22,19 +22,21 @@
22
22
  "publishConfig": {
23
23
  "access": "public"
24
24
  },
25
+ "devDependencies": {
26
+ "@diplodoc/cli": "file:.."
27
+ },
25
28
  "dependencies": {
26
29
  "@diplodoc/client": "^3.3.1",
27
30
  "@types/node": "18.x",
28
- "@types/shelljs": "^0.8.15",
29
31
  "@vitest/coverage-istanbul": "^3.1.1",
30
32
  "@vitest/coverage-v8": "^3.1.1",
31
33
  "commander": "^13.1.0",
34
+ "execa": "^9.5.3",
35
+ "glob": "^11.0.2",
32
36
  "js-yaml": "^4.1.0",
33
- "shelljs": "^0.9.1",
34
37
  "ts-dedent": "^2.2.0",
35
38
  "ts-node": "^10.4.0",
36
- "typescript": "^5.3.3",
37
- "walk-sync": "^3.0.0"
39
+ "typescript": "^5.3.3"
38
40
  },
39
41
  "peerDependencies": {
40
42
  "vitest": "^3.1.1"