@tskmgr/client 2.0.6 → 2.4.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 (42) hide show
  1. package/.babelrc +10 -0
  2. package/README.md +1 -1
  3. package/eslint.config.mjs +28 -0
  4. package/package.json +4 -7
  5. package/project.json +31 -0
  6. package/src/{index.d.ts → index.ts} +6 -5
  7. package/src/lib/client-factory.ts +17 -0
  8. package/src/lib/client-options.ts +12 -0
  9. package/src/lib/client.spec.ts +36 -0
  10. package/src/lib/client.ts +265 -0
  11. package/src/lib/run-tasks-result.ts +17 -0
  12. package/src/lib/task-result.ts +10 -0
  13. package/src/lib/utils.spec.ts +27 -0
  14. package/src/lib/utils.ts +71 -0
  15. package/tsconfig.json +13 -0
  16. package/tsconfig.lib.json +27 -0
  17. package/tsconfig.spec.json +22 -0
  18. package/vite.config.ts +31 -0
  19. package/LICENSE +0 -21
  20. package/src/index.js +0 -22
  21. package/src/index.js.map +0 -1
  22. package/src/lib/client-example.d.ts +0 -11
  23. package/src/lib/client-example.js +0 -120
  24. package/src/lib/client-example.js.map +0 -1
  25. package/src/lib/client-factory.d.ts +0 -6
  26. package/src/lib/client-factory.js +0 -14
  27. package/src/lib/client-factory.js.map +0 -1
  28. package/src/lib/client-options.d.ts +0 -12
  29. package/src/lib/client-options.js +0 -3
  30. package/src/lib/client-options.js.map +0 -1
  31. package/src/lib/client.d.ts +0 -24
  32. package/src/lib/client.js +0 -249
  33. package/src/lib/client.js.map +0 -1
  34. package/src/lib/run-tasks-result.d.ts +0 -9
  35. package/src/lib/run-tasks-result.js +0 -14
  36. package/src/lib/run-tasks-result.js.map +0 -1
  37. package/src/lib/task-result.d.ts +0 -10
  38. package/src/lib/task-result.js +0 -3
  39. package/src/lib/task-result.js.map +0 -1
  40. package/src/lib/utils.d.ts +0 -14
  41. package/src/lib/utils.js +0 -72
  42. package/src/lib/utils.js.map +0 -1
package/.babelrc ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@nx/js/babel",
5
+ {
6
+ "useBuiltIns": "usage"
7
+ }
8
+ ]
9
+ ]
10
+ }
package/README.md CHANGED
@@ -4,4 +4,4 @@ This library was generated with [Nx](https://nx.dev).
4
4
 
5
5
  ## Running unit tests
6
6
 
7
- Run `nx test client` to execute the unit tests via [Jest](https://jestjs.io).
7
+ Run `nx test client` to execute the unit tests via [Vitest](https://vitest.dev).
@@ -0,0 +1,28 @@
1
+ import baseConfig from '../../eslint.config.mjs';
2
+
3
+ export default [
4
+ {
5
+ ignores: ['**/dist'],
6
+ },
7
+ ...baseConfig,
8
+ {
9
+ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
10
+ // Override or add rules here
11
+ rules: {},
12
+ },
13
+ {
14
+ files: ['**/*.ts', '**/*.tsx'],
15
+ // Override or add rules here
16
+ rules: {},
17
+ languageOptions: {
18
+ parserOptions: {
19
+ project: ['libs/client/tsconfig.*?.json'],
20
+ },
21
+ },
22
+ },
23
+ {
24
+ files: ['**/*.js', '**/*.jsx'],
25
+ // Override or add rules here
26
+ rules: {},
27
+ },
28
+ ];
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@tskmgr/client",
3
- "version": "2.0.6",
3
+ "version": "2.4.0",
4
4
  "license": "MIT",
5
5
  "dependencies": {
6
6
  "debug": "^4.3.4",
7
- "@tskmgr/common": "2.0.6",
8
- "uuid": "^9.0.1",
9
- "@nx/devkit": "18.0.7"
10
- },
11
- "main": "./src/index.js",
12
- "type": "commonjs"
7
+ "p-retry": "^7.1.0",
8
+ "@tskmgr/common": "2.4.0"
9
+ }
13
10
  }
package/project.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "client",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/client/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "lint": {
9
+ "executor": "@nx/eslint:lint",
10
+ "outputs": ["{options.outputFile}"]
11
+ },
12
+ "test": {
13
+ "executor": "@nx/vite:test",
14
+ "outputs": ["{options.reportsDirectory}"],
15
+ "options": {
16
+ "reportsDirectory": "../../coverage/libs/client"
17
+ }
18
+ },
19
+ "build": {
20
+ "executor": "@nx/js:tsc",
21
+ "outputs": ["{options.outputPath}"],
22
+ "options": {
23
+ "outputPath": "dist/libs/client",
24
+ "tsConfig": "libs/client/tsconfig.lib.json",
25
+ "packageJson": "libs/client/package.json",
26
+ "main": "libs/client/src/index.ts",
27
+ "assets": ["libs/client/*.md", "LICENSE"]
28
+ }
29
+ }
30
+ }
31
+ }
@@ -1,5 +1,6 @@
1
- export * from './lib/client';
2
- export * from './lib/client-options';
3
- export * from './lib/client-factory';
4
- export * from './lib/run-tasks-result';
5
- export * from './lib/task-result';
1
+ export * from './lib/client';
2
+ export * from './lib/client-options';
3
+ export * from './lib/client-factory';
4
+
5
+ export * from './lib/run-tasks-result';
6
+ export * from './lib/task-result';
@@ -0,0 +1,17 @@
1
+ import { Client } from './client';
2
+ import { ClientOptions } from './client-options';
3
+ import { ApiUrl } from '@tskmgr/common';
4
+
5
+ export class ClientFactory {
6
+ public static createNew(
7
+ baseUrl: string, //
8
+ runnerId: string,
9
+ options?: ClientOptions
10
+ ): Client {
11
+ return new Client(
12
+ ApiUrl.create(baseUrl), //
13
+ runnerId,
14
+ { ...Client.DefaultOptions, ...options }
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,12 @@
1
+ import { Task } from '@tskmgr/common';
2
+ import { SpawnOptionsWithoutStdio } from 'child_process';
3
+
4
+ export interface ClientOptions {
5
+ parallel?: number;
6
+ dataCallback?: (task: Task, data: string, cached: () => void) => void;
7
+ errorCallback?: (task: Task, data: string) => void;
8
+ pollingDelayMs?: number;
9
+ retryDelayMs?: number;
10
+ retryCount?: number;
11
+ spawnOptions?: SpawnOptionsWithoutStdio;
12
+ }
@@ -0,0 +1,36 @@
1
+ import { Client } from './client';
2
+ import { ClientFactory } from './client-factory';
3
+
4
+ describe('client', () => {
5
+ let client: Client;
6
+
7
+ beforeEach(() => {
8
+ client = ClientFactory.createNew('http://localhost:3000', 'runner-1');
9
+ });
10
+
11
+ afterEach(() => {
12
+ vi.restoreAllMocks();
13
+ });
14
+
15
+ it('should retry when rejected promise', async () => {
16
+ // arrange
17
+ vi.spyOn(global, 'fetch')
18
+ .mockRejectedValueOnce(new Error())
19
+ .mockResolvedValue({ ok: true, json: async () => ({}) } as Response);
20
+ // act
21
+ await client.uploadRunFile(1, './README.md', {});
22
+ // assert
23
+ expect(global.fetch).toHaveBeenCalledTimes(2);
24
+ });
25
+
26
+ it('should retry when fetch response not ok', async () => {
27
+ // arrange
28
+ vi.spyOn(global, 'fetch')
29
+ .mockResolvedValueOnce({ ok: false, statusText: 'Internal Server Error' } as Response)
30
+ .mockResolvedValue({ ok: true, json: async () => ({}) } as Response);
31
+ // act
32
+ await client.uploadRunFile(1, './README.md', {});
33
+ // assert
34
+ expect(global.fetch).toHaveBeenCalledTimes(2);
35
+ });
36
+ });
@@ -0,0 +1,265 @@
1
+ import { ChildProcess } from 'node:child_process';
2
+ import {
3
+ ApiUrl,
4
+ Run,
5
+ CompleteTaskDto,
6
+ CreateRunRequestDto,
7
+ CreateTasksDto,
8
+ StartTaskDto,
9
+ StartTaskResponseDto,
10
+ Task,
11
+ File as File_,
12
+ SetLeaderRequestDto,
13
+ SetLeaderResponseDto,
14
+ CreateFileRequestDto,
15
+ } from '@tskmgr/common';
16
+ import { delay, fetchWithRetry, getTaskLogFilename, spawnAsync } from './utils';
17
+ import { RunTasksResult } from './run-tasks-result';
18
+ import { TaskResult } from './task-result';
19
+ import { ClientOptions } from './client-options';
20
+ import { createWriteStream, unlinkSync } from 'node:fs';
21
+ import Debug from 'debug';
22
+ import { readFile } from 'node:fs/promises';
23
+ import { SpawnOptionsWithoutStdio } from 'node:child_process';
24
+
25
+ const debug = Debug('tskmgr:client');
26
+ const headers = { 'Content-Type': 'application/json' };
27
+
28
+ export class Client {
29
+ public static readonly DefaultOptions: ClientOptions = {
30
+ parallel: 1,
31
+ dataCallback: (task, data, cached) => {
32
+ // noop
33
+ },
34
+ errorCallback: (task, data) => {
35
+ // noop
36
+ },
37
+ pollingDelayMs: 5000,
38
+ retryDelayMs: 5000,
39
+ retryCount: 2,
40
+ };
41
+
42
+ constructor(
43
+ private readonly apiUrl: ApiUrl,
44
+ private readonly runnerId: string,
45
+ private readonly clientOptions: ClientOptions,
46
+ ) {}
47
+
48
+ public async createRun(params: CreateRunRequestDto): Promise<Run> {
49
+ return fetchWithRetry(this.apiUrl.createRunUrl(), {
50
+ headers,
51
+ method: 'POST',
52
+ body: JSON.stringify(params),
53
+ });
54
+ }
55
+
56
+ public async closeRun(runId: number): Promise<Run> {
57
+ return fetchWithRetry(this.apiUrl.closeRunUrl(runId), {
58
+ headers,
59
+ method: 'PUT',
60
+ });
61
+ }
62
+
63
+ public async abortRun(runId: number): Promise<Run> {
64
+ return fetchWithRetry(this.apiUrl.abortRunUrl(runId), {
65
+ headers,
66
+ method: 'PUT',
67
+ });
68
+ }
69
+
70
+ public async failRun(runId: number): Promise<Run> {
71
+ return fetchWithRetry(this.apiUrl.failRunUrl(runId), {
72
+ headers,
73
+ method: 'PUT',
74
+ });
75
+ }
76
+
77
+ public async setLeader(runId: number): Promise<SetLeaderResponseDto> {
78
+ const params: SetLeaderRequestDto = { runnerId: this.runnerId };
79
+ return fetchWithRetry(this.apiUrl.setLeaderUrl(runId), {
80
+ headers,
81
+ method: 'PUT',
82
+ body: JSON.stringify(params),
83
+ });
84
+ }
85
+
86
+ public async createTasks(runId: number, params: CreateTasksDto): Promise<Task[]> {
87
+ return fetchWithRetry(this.apiUrl.createTasksUrl(runId), {
88
+ headers,
89
+ method: 'POST',
90
+ body: JSON.stringify(params),
91
+ });
92
+ }
93
+
94
+ public async startTask(runId: number, params: StartTaskDto): Promise<StartTaskResponseDto> {
95
+ return fetchWithRetry(this.apiUrl.startTaskUrl(runId), {
96
+ headers,
97
+ method: 'PUT',
98
+ body: JSON.stringify(params),
99
+ });
100
+ }
101
+
102
+ public async runTasks(runId: number): Promise<RunTasksResult> {
103
+ const taskRunners: Promise<TaskResult[]>[] = [];
104
+
105
+ for (let i = 0; i < this.clientOptions.parallel; i++) {
106
+ taskRunners.push(this.defaultParallelTaskRunner(runId, i));
107
+ }
108
+
109
+ const taskResults = await Promise.all(taskRunners);
110
+ return new RunTasksResult(taskResults.flatMap((x) => x));
111
+ }
112
+
113
+ public async completeTask(taskId: number, params: CompleteTaskDto): Promise<Task> {
114
+ return fetchWithRetry(this.apiUrl.completeTaskUrl(taskId), {
115
+ headers,
116
+ method: 'PUT',
117
+ body: JSON.stringify(params),
118
+ });
119
+ }
120
+
121
+ public async failTask(taskId: number): Promise<Task> {
122
+ return fetchWithRetry(this.apiUrl.failTaskUrl(taskId), {
123
+ headers,
124
+ method: 'PUT',
125
+ });
126
+ }
127
+
128
+ public async uploadRunFile(runId: number, path: string, params: CreateFileRequestDto): Promise<File_> {
129
+ return this.uploadFile(this.apiUrl.createFileRunUrl(runId), path, params);
130
+ }
131
+
132
+ public async uploadTaskFile(taskId: number, path: string, params: CreateFileRequestDto): Promise<File_> {
133
+ return this.uploadFile(this.apiUrl.createFileTaskUrl(taskId), path, params);
134
+ }
135
+
136
+ private async uploadFile(url: string, path: string, params: CreateFileRequestDto): Promise<File_> {
137
+ const formData = new FormData();
138
+
139
+ const blob = new Blob([await readFile(path)]);
140
+ formData.append('file', blob, path);
141
+
142
+ if (params.type) {
143
+ formData.append('type', params.type);
144
+ }
145
+
146
+ if (params.description) {
147
+ formData.append('description', params.description);
148
+ }
149
+
150
+ return fetchWithRetry(url, {
151
+ method: 'POST',
152
+ body: formData,
153
+ });
154
+ }
155
+
156
+ private async defaultParallelTaskRunner(runId: number, parallelId: number): Promise<TaskResult[]> {
157
+ const logInfo = `[${this.runnerId}:${parallelId}]`;
158
+ const taskResults: TaskResult[] = [];
159
+ let _continue = true;
160
+
161
+ debug(`${logInfo} parallel task runner started`);
162
+
163
+ while (_continue) {
164
+ const res = await this.startTask(runId, { runnerId: this.runnerId });
165
+
166
+ if (!res.continue) {
167
+ debug(`${logInfo} continue set to false`);
168
+ _continue = false;
169
+ }
170
+
171
+ if (!res.continue && !res.task) {
172
+ debug(`${logInfo} continue set to false and no task, exiting`);
173
+ continue;
174
+ }
175
+
176
+ if (!res.task) {
177
+ debug(`${logInfo} polling for new tasks in ${this.clientOptions.pollingDelayMs} ms`);
178
+ await delay(this.clientOptions.pollingDelayMs);
179
+ continue;
180
+ }
181
+
182
+ const { run, task } = res;
183
+ debug(`${logInfo} received task: ${task.id}`);
184
+
185
+ let cached = false;
186
+ let completed = false;
187
+ let writable = false;
188
+ let childProcess: ChildProcess;
189
+ let error: Error;
190
+
191
+ const taskLogFilename = getTaskLogFilename(task.id);
192
+ const writeStream = createWriteStream(taskLogFilename);
193
+ writeStream.on('open', () => (writable = true));
194
+
195
+ const dataHandler = (data: string): void => {
196
+ this.clientOptions.dataCallback(task, data, () => (cached = true));
197
+ if (writable) {
198
+ writeStream.write(`[${new Date().toISOString()}] ${data}\n`);
199
+ }
200
+ };
201
+
202
+ const errorHandler = (data: string): void => {
203
+ this.clientOptions.errorCallback(task, data);
204
+ if (writable) {
205
+ writeStream.write(`[${new Date().toISOString()}] ${data}\n`);
206
+ }
207
+ };
208
+
209
+ const spawnOptions: SpawnOptionsWithoutStdio = {
210
+ ...(task.options as SpawnOptionsWithoutStdio),
211
+ ...this.clientOptions.spawnOptions,
212
+ env: {
213
+ ...process.env,
214
+ ...task.options?.env,
215
+ ...this.clientOptions.spawnOptions?.env,
216
+ TSKMGR_PARALLEL_ID: parallelId.toString(),
217
+ },
218
+ };
219
+
220
+ debug(`${logInfo} starting task: ${task.id}`);
221
+ try {
222
+ childProcess = await spawnAsync(task.command, task.arguments, spawnOptions, {
223
+ dataHandler,
224
+ errorHandler,
225
+ });
226
+ completed = true;
227
+ debug(`${logInfo} completed task: ${task.id}`);
228
+ } catch (err) {
229
+ console.error(`${logInfo} failed task: ${task.id} with error: ${err}`);
230
+ error = err;
231
+
232
+ if (run.failFast) {
233
+ // TODO: should abort all running tasks by current client
234
+ debug(`${logInfo} fail fast enabled, aborting`);
235
+ throw err;
236
+ }
237
+ } finally {
238
+ taskResults.push({ run, task, childProcess, completed, error });
239
+
240
+ if (completed) {
241
+ await this.completeTask(task.id, { cached });
242
+ debug(`${logInfo} completed status for task: ${task.id} sent`);
243
+ } else {
244
+ await this.failTask(task.id);
245
+ debug(`${logInfo} failed status for task: ${task.id} sent`);
246
+ }
247
+
248
+ writeStream.end();
249
+ writeStream.close();
250
+
251
+ await this.uploadTaskFile(task.id, taskLogFilename, {
252
+ type: 'log',
253
+ description: `Log for task ${task.id}`,
254
+ });
255
+
256
+ debug(`${logInfo} uploaded file for task: ${task.id} sent`);
257
+ unlinkSync(taskLogFilename);
258
+ }
259
+ }
260
+
261
+ debug(`${logInfo} parallel task runner completed`);
262
+
263
+ return taskResults;
264
+ }
265
+ }
@@ -0,0 +1,17 @@
1
+ import { TaskResult } from './task-result';
2
+
3
+ export class RunTasksResult {
4
+ public readonly completedTasks: TaskResult[];
5
+ public readonly failedTasks: TaskResult[];
6
+
7
+ public readonly completed: boolean;
8
+ public readonly failed: boolean;
9
+
10
+ constructor(public readonly tasks: TaskResult[]) {
11
+ this.completedTasks = tasks.filter((x) => x.completed);
12
+ this.failedTasks = tasks.filter((x) => !x.completed);
13
+
14
+ this.completed = this.failedTasks.length === 0;
15
+ this.failed = this.failedTasks.length > 0;
16
+ }
17
+ }
@@ -0,0 +1,10 @@
1
+ import { Run, Task } from '@tskmgr/common';
2
+ import { ChildProcess } from 'child_process';
3
+
4
+ export interface TaskResult {
5
+ run: Run;
6
+ task: Task;
7
+ childProcess: ChildProcess;
8
+ completed: boolean;
9
+ error?: Error;
10
+ }
@@ -0,0 +1,27 @@
1
+ import { spawnAsync } from './utils';
2
+
3
+ describe('Utils', () => {
4
+ it('should call dataHandler', async () => {
5
+ // arrange
6
+ const dataHandler = vi.fn();
7
+ const errorHandler = vi.fn();
8
+ // act
9
+ const command = `node -e 'console.log("some data"); process.exit(0)'`;
10
+ await expect(spawnAsync(command, [], { shell: true }, { dataHandler, errorHandler })).resolves.toBeDefined();
11
+ // assert
12
+ expect(dataHandler).toHaveBeenCalledWith('some data');
13
+ expect(errorHandler).not.toHaveBeenCalled();
14
+ });
15
+
16
+ it('should call errorHandler', async () => {
17
+ // arrange
18
+ const dataHandler = vi.fn();
19
+ const errorHandler = vi.fn();
20
+ // act
21
+ const command = `node -e 'console.error("some error"); process.exit(1)'`;
22
+ await expect(spawnAsync(command, [], { shell: true }, { dataHandler, errorHandler })).rejects.toBeDefined();
23
+ // assert
24
+ expect(dataHandler).not.toHaveBeenCalled();
25
+ expect(errorHandler).toHaveBeenCalledWith('some error');
26
+ });
27
+ });
@@ -0,0 +1,71 @@
1
+ import { ChildProcess, spawn, SpawnOptionsWithoutStdio } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import pRetry from 'p-retry';
6
+ import Debug from 'debug';
7
+
8
+ const debug = Debug('tskmgr:client');
9
+
10
+ export interface SpawnAsyncLineHandlers {
11
+ dataHandler?: (line: string) => void;
12
+ errorHandler?: (line: string) => void;
13
+ }
14
+
15
+ export async function spawnAsync(
16
+ command: string,
17
+ args?: ReadonlyArray<string>,
18
+ options?: SpawnOptionsWithoutStdio,
19
+ lineHandlers?: SpawnAsyncLineHandlers,
20
+ ): Promise<ChildProcess> {
21
+ return new Promise((resolve, reject) => {
22
+ const childProcess = spawn(command, args, options);
23
+
24
+ if (lineHandlers?.dataHandler) {
25
+ const readlineStdout = createInterface({ input: childProcess.stdout });
26
+ readlineStdout.on('line', lineHandlers.dataHandler);
27
+ }
28
+
29
+ if (lineHandlers?.errorHandler) {
30
+ const readlineStderr = createInterface({ input: childProcess.stderr });
31
+ readlineStderr.on('line', lineHandlers.errorHandler);
32
+ }
33
+
34
+ childProcess.on('close', (code) => {
35
+ if (code === 0) {
36
+ resolve(childProcess);
37
+ } else {
38
+ reject(childProcess);
39
+ }
40
+ });
41
+
42
+ childProcess.on('error', (err: Error) => {
43
+ reject(childProcess); // TODO: try to return error message
44
+ });
45
+ });
46
+ }
47
+
48
+ export function delay(ms: number): Promise<void> {
49
+ return new Promise((resolve) => {
50
+ setTimeout(resolve, ms);
51
+ });
52
+ }
53
+
54
+ export function getTaskLogFilename(taskId: number): string {
55
+ return join(tmpdir(), `task-${taskId}.log`);
56
+ }
57
+
58
+ export async function fetchWithRetry<T>(input: string | URL | Request, init?: RequestInit): Promise<T> {
59
+ const run = async () => {
60
+ const response = await fetch(input, init);
61
+ if (!response.ok) {
62
+ throw new Error(`HTTP ${response.status} ${response.statusText}: ${input}`);
63
+ }
64
+ return response.json();
65
+ };
66
+
67
+ return pRetry(run, {
68
+ retries: 3,
69
+ onFailedAttempt: (error) => debug('Retry attempt failed:', error),
70
+ });
71
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "files": [],
4
+ "include": [],
5
+ "references": [
6
+ {
7
+ "path": "./tsconfig.lib.json"
8
+ },
9
+ {
10
+ "path": "./tsconfig.spec.json"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "module": "commonjs",
5
+ "outDir": "../../dist/out-tsc",
6
+ "declaration": true,
7
+ "types": ["node"],
8
+ "importHelpers": false
9
+ },
10
+ "exclude": [
11
+ "**/*.spec.ts",
12
+ "**/*.test.ts",
13
+ "vite.config.ts",
14
+ "vite.config.mts",
15
+ "vitest.config.ts",
16
+ "vitest.config.mts",
17
+ "src/**/*.test.ts",
18
+ "src/**/*.spec.ts",
19
+ "src/**/*.test.tsx",
20
+ "src/**/*.spec.tsx",
21
+ "src/**/*.test.js",
22
+ "src/**/*.spec.js",
23
+ "src/**/*.test.jsx",
24
+ "src/**/*.spec.jsx"
25
+ ],
26
+ "include": ["**/*.ts"]
27
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest"]
6
+ },
7
+ "include": [
8
+ "vite.config.ts",
9
+ "vite.config.mts",
10
+ "vitest.config.ts",
11
+ "vitest.config.mts",
12
+ "src/**/*.test.ts",
13
+ "src/**/*.spec.ts",
14
+ "src/**/*.test.tsx",
15
+ "src/**/*.spec.tsx",
16
+ "src/**/*.test.js",
17
+ "src/**/*.spec.js",
18
+ "src/**/*.test.jsx",
19
+ "src/**/*.spec.jsx",
20
+ "src/**/*.d.ts"
21
+ ]
22
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,31 @@
1
+ /// <reference types='vitest' />
2
+ import { defineConfig } from 'vite';
3
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
4
+ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
5
+
6
+ export default defineConfig(() => ({
7
+ root: __dirname,
8
+ cacheDir: '../../node_modules/.vite/libs/client',
9
+ plugins: [nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
10
+ // Uncomment this if you are using workers.
11
+ // worker: {
12
+ // plugins: [ nxViteTsPaths() ],
13
+ // },
14
+ test: {
15
+ name: 'client',
16
+ watch: false,
17
+ globals: true,
18
+ environment: 'jsdom',
19
+ include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
20
+ reporters: ['default'],
21
+ coverage: {
22
+ reportsDirectory: '../../coverage/libs/client',
23
+ provider: 'v8' as const,
24
+ },
25
+ typecheck: {
26
+ enabled: true,
27
+ tsconfig: 'tsconfig.spec.json',
28
+ include: ['{src,tests}/**/*.{test,spec}.{ts,mts,tsx}'],
29
+ },
30
+ },
31
+ }));