@tskmgr/client 2.4.0 → 2.4.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Mathieu Paquette
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@tskmgr/client",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "license": "MIT",
5
5
  "dependencies": {
6
6
  "debug": "^4.3.4",
7
7
  "p-retry": "^7.1.0",
8
- "@tskmgr/common": "2.4.0"
9
- }
10
- }
8
+ "@tskmgr/common": "2.4.1"
9
+ },
10
+ "types": "./src/index.d.ts",
11
+ "main": "./src/index.js",
12
+ "type": "commonjs"
13
+ }
@@ -1,6 +1,5 @@
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';
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';
package/src/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./lib/client"), exports);
18
+ __exportStar(require("./lib/client-options"), exports);
19
+ __exportStar(require("./lib/client-factory"), exports);
20
+ __exportStar(require("./lib/run-tasks-result"), exports);
21
+ __exportStar(require("./lib/task-result"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/client/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,uDAAqC;AACrC,uDAAqC;AAErC,yDAAuC;AACvC,oDAAkC"}
@@ -0,0 +1,6 @@
1
+ import { Client } from './client';
2
+ import { ClientOptions } from './client-options';
3
+ export declare class ClientFactory {
4
+ static createNew(baseUrl: string, //
5
+ runnerId: string, options?: ClientOptions): Client;
6
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientFactory = void 0;
4
+ const client_1 = require("./client");
5
+ const common_1 = require("@tskmgr/common");
6
+ class ClientFactory {
7
+ static createNew(baseUrl, //
8
+ runnerId, options) {
9
+ return new client_1.Client(common_1.ApiUrl.create(baseUrl), //
10
+ runnerId, Object.assign(Object.assign({}, client_1.Client.DefaultOptions), options));
11
+ }
12
+ }
13
+ exports.ClientFactory = ClientFactory;
14
+ //# sourceMappingURL=client-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-factory.js","sourceRoot":"","sources":["../../../../../libs/client/src/lib/client-factory.ts"],"names":[],"mappings":";;;AAAA,qCAAkC;AAElC,2CAAwC;AAExC,MAAa,aAAa;IACjB,MAAM,CAAC,SAAS,CACrB,OAAe,EAAE,EAAE;IACnB,QAAgB,EAChB,OAAuB;QAEvB,OAAO,IAAI,eAAM,CACf,eAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE;QAC1B,QAAQ,kCACH,eAAM,CAAC,cAAc,GAAK,OAAO,EACvC,CAAC;IACJ,CAAC;CACF;AAZD,sCAYC"}
@@ -0,0 +1,11 @@
1
+ import { Task } from '@tskmgr/common';
2
+ import { SpawnOptionsWithoutStdio } from 'child_process';
3
+ export interface ClientOptions {
4
+ parallel?: number;
5
+ dataCallback?: (task: Task, data: string, cached: () => void) => void;
6
+ errorCallback?: (task: Task, data: string) => void;
7
+ pollingDelayMs?: number;
8
+ retryDelayMs?: number;
9
+ retryCount?: number;
10
+ spawnOptions?: SpawnOptionsWithoutStdio;
11
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=client-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-options.js","sourceRoot":"","sources":["../../../../../libs/client/src/lib/client-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ import { ApiUrl, Run, CompleteTaskDto, CreateRunRequestDto, CreateTasksDto, StartTaskDto, StartTaskResponseDto, Task, File as File_, SetLeaderResponseDto, CreateFileRequestDto } from '@tskmgr/common';
2
+ import { RunTasksResult } from './run-tasks-result';
3
+ import { ClientOptions } from './client-options';
4
+ export declare class Client {
5
+ private readonly apiUrl;
6
+ private readonly runnerId;
7
+ private readonly clientOptions;
8
+ static readonly DefaultOptions: ClientOptions;
9
+ constructor(apiUrl: ApiUrl, runnerId: string, clientOptions: ClientOptions);
10
+ createRun(params: CreateRunRequestDto): Promise<Run>;
11
+ closeRun(runId: number): Promise<Run>;
12
+ abortRun(runId: number): Promise<Run>;
13
+ failRun(runId: number): Promise<Run>;
14
+ setLeader(runId: number): Promise<SetLeaderResponseDto>;
15
+ createTasks(runId: number, params: CreateTasksDto): Promise<Task[]>;
16
+ startTask(runId: number, params: StartTaskDto): Promise<StartTaskResponseDto>;
17
+ runTasks(runId: number): Promise<RunTasksResult>;
18
+ completeTask(taskId: number, params: CompleteTaskDto): Promise<Task>;
19
+ failTask(taskId: number): Promise<Task>;
20
+ uploadRunFile(runId: number, path: string, params: CreateFileRequestDto): Promise<File_>;
21
+ uploadTaskFile(taskId: number, path: string, params: CreateFileRequestDto): Promise<File_>;
22
+ private uploadFile;
23
+ private defaultParallelTaskRunner;
24
+ }
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.Client = void 0;
16
+ const utils_1 = require("./utils");
17
+ const run_tasks_result_1 = require("./run-tasks-result");
18
+ const node_fs_1 = require("node:fs");
19
+ const debug_1 = __importDefault(require("debug"));
20
+ const promises_1 = require("node:fs/promises");
21
+ const debug = (0, debug_1.default)('tskmgr:client');
22
+ const headers = { 'Content-Type': 'application/json' };
23
+ class Client {
24
+ constructor(apiUrl, runnerId, clientOptions) {
25
+ this.apiUrl = apiUrl;
26
+ this.runnerId = runnerId;
27
+ this.clientOptions = clientOptions;
28
+ }
29
+ createRun(params) {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.createRunUrl(), {
32
+ headers,
33
+ method: 'POST',
34
+ body: JSON.stringify(params),
35
+ });
36
+ });
37
+ }
38
+ closeRun(runId) {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.closeRunUrl(runId), {
41
+ headers,
42
+ method: 'PUT',
43
+ });
44
+ });
45
+ }
46
+ abortRun(runId) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.abortRunUrl(runId), {
49
+ headers,
50
+ method: 'PUT',
51
+ });
52
+ });
53
+ }
54
+ failRun(runId) {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.failRunUrl(runId), {
57
+ headers,
58
+ method: 'PUT',
59
+ });
60
+ });
61
+ }
62
+ setLeader(runId) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ const params = { runnerId: this.runnerId };
65
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.setLeaderUrl(runId), {
66
+ headers,
67
+ method: 'PUT',
68
+ body: JSON.stringify(params),
69
+ });
70
+ });
71
+ }
72
+ createTasks(runId, params) {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.createTasksUrl(runId), {
75
+ headers,
76
+ method: 'POST',
77
+ body: JSON.stringify(params),
78
+ });
79
+ });
80
+ }
81
+ startTask(runId, params) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.startTaskUrl(runId), {
84
+ headers,
85
+ method: 'PUT',
86
+ body: JSON.stringify(params),
87
+ });
88
+ });
89
+ }
90
+ runTasks(runId) {
91
+ return __awaiter(this, void 0, void 0, function* () {
92
+ const taskRunners = [];
93
+ for (let i = 0; i < this.clientOptions.parallel; i++) {
94
+ taskRunners.push(this.defaultParallelTaskRunner(runId, i));
95
+ }
96
+ const taskResults = yield Promise.all(taskRunners);
97
+ return new run_tasks_result_1.RunTasksResult(taskResults.flatMap((x) => x));
98
+ });
99
+ }
100
+ completeTask(taskId, params) {
101
+ return __awaiter(this, void 0, void 0, function* () {
102
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.completeTaskUrl(taskId), {
103
+ headers,
104
+ method: 'PUT',
105
+ body: JSON.stringify(params),
106
+ });
107
+ });
108
+ }
109
+ failTask(taskId) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ return (0, utils_1.fetchWithRetry)(this.apiUrl.failTaskUrl(taskId), {
112
+ headers,
113
+ method: 'PUT',
114
+ });
115
+ });
116
+ }
117
+ uploadRunFile(runId, path, params) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ return this.uploadFile(this.apiUrl.createFileRunUrl(runId), path, params);
120
+ });
121
+ }
122
+ uploadTaskFile(taskId, path, params) {
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ return this.uploadFile(this.apiUrl.createFileTaskUrl(taskId), path, params);
125
+ });
126
+ }
127
+ uploadFile(url, path, params) {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ const formData = new FormData();
130
+ const blob = new Blob([yield (0, promises_1.readFile)(path)]);
131
+ formData.append('file', blob, path);
132
+ if (params.type) {
133
+ formData.append('type', params.type);
134
+ }
135
+ if (params.description) {
136
+ formData.append('description', params.description);
137
+ }
138
+ return (0, utils_1.fetchWithRetry)(url, {
139
+ method: 'POST',
140
+ body: formData,
141
+ });
142
+ });
143
+ }
144
+ defaultParallelTaskRunner(runId, parallelId) {
145
+ return __awaiter(this, void 0, void 0, function* () {
146
+ var _a, _b;
147
+ const logInfo = `[${this.runnerId}:${parallelId}]`;
148
+ const taskResults = [];
149
+ let _continue = true;
150
+ debug(`${logInfo} parallel task runner started`);
151
+ while (_continue) {
152
+ const res = yield this.startTask(runId, { runnerId: this.runnerId });
153
+ if (!res.continue) {
154
+ debug(`${logInfo} continue set to false`);
155
+ _continue = false;
156
+ }
157
+ if (!res.continue && !res.task) {
158
+ debug(`${logInfo} continue set to false and no task, exiting`);
159
+ continue;
160
+ }
161
+ if (!res.task) {
162
+ debug(`${logInfo} polling for new tasks in ${this.clientOptions.pollingDelayMs} ms`);
163
+ yield (0, utils_1.delay)(this.clientOptions.pollingDelayMs);
164
+ continue;
165
+ }
166
+ const { run, task } = res;
167
+ debug(`${logInfo} received task: ${task.id}`);
168
+ let cached = false;
169
+ let completed = false;
170
+ let writable = false;
171
+ let childProcess;
172
+ let error;
173
+ const taskLogFilename = (0, utils_1.getTaskLogFilename)(task.id);
174
+ const writeStream = (0, node_fs_1.createWriteStream)(taskLogFilename);
175
+ writeStream.on('open', () => (writable = true));
176
+ const dataHandler = (data) => {
177
+ this.clientOptions.dataCallback(task, data, () => (cached = true));
178
+ if (writable) {
179
+ writeStream.write(`[${new Date().toISOString()}] ${data}\n`);
180
+ }
181
+ };
182
+ const errorHandler = (data) => {
183
+ this.clientOptions.errorCallback(task, data);
184
+ if (writable) {
185
+ writeStream.write(`[${new Date().toISOString()}] ${data}\n`);
186
+ }
187
+ };
188
+ const spawnOptions = Object.assign(Object.assign(Object.assign({}, task.options), this.clientOptions.spawnOptions), { env: Object.assign(Object.assign(Object.assign(Object.assign({}, process.env), (_a = task.options) === null || _a === void 0 ? void 0 : _a.env), (_b = this.clientOptions.spawnOptions) === null || _b === void 0 ? void 0 : _b.env), { TSKMGR_PARALLEL_ID: parallelId.toString() }) });
189
+ debug(`${logInfo} starting task: ${task.id}`);
190
+ try {
191
+ childProcess = yield (0, utils_1.spawnAsync)(task.command, task.arguments, spawnOptions, {
192
+ dataHandler,
193
+ errorHandler,
194
+ });
195
+ completed = true;
196
+ debug(`${logInfo} completed task: ${task.id}`);
197
+ }
198
+ catch (err) {
199
+ console.error(`${logInfo} failed task: ${task.id} with error: ${err}`);
200
+ error = err;
201
+ if (run.failFast) {
202
+ // TODO: should abort all running tasks by current client
203
+ debug(`${logInfo} fail fast enabled, aborting`);
204
+ throw err;
205
+ }
206
+ }
207
+ finally {
208
+ taskResults.push({ run, task, childProcess, completed, error });
209
+ if (completed) {
210
+ yield this.completeTask(task.id, { cached });
211
+ debug(`${logInfo} completed status for task: ${task.id} sent`);
212
+ }
213
+ else {
214
+ yield this.failTask(task.id);
215
+ debug(`${logInfo} failed status for task: ${task.id} sent`);
216
+ }
217
+ writeStream.end();
218
+ writeStream.close();
219
+ yield this.uploadTaskFile(task.id, taskLogFilename, {
220
+ type: 'log',
221
+ description: `Log for task ${task.id}`,
222
+ });
223
+ debug(`${logInfo} uploaded file for task: ${task.id} sent`);
224
+ (0, node_fs_1.unlinkSync)(taskLogFilename);
225
+ }
226
+ }
227
+ debug(`${logInfo} parallel task runner completed`);
228
+ return taskResults;
229
+ });
230
+ }
231
+ }
232
+ exports.Client = Client;
233
+ Client.DefaultOptions = {
234
+ parallel: 1,
235
+ dataCallback: (task, data, cached) => {
236
+ // noop
237
+ },
238
+ errorCallback: (task, data) => {
239
+ // noop
240
+ },
241
+ pollingDelayMs: 5000,
242
+ retryDelayMs: 5000,
243
+ retryCount: 2,
244
+ };
245
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../../../libs/client/src/lib/client.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAeA,mCAAgF;AAChF,yDAAoD;AAGpD,qCAAwD;AACxD,kDAA0B;AAC1B,+CAA4C;AAG5C,MAAM,KAAK,GAAG,IAAA,eAAK,EAAC,eAAe,CAAC,CAAC;AACrC,MAAM,OAAO,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAEvD,MAAa,MAAM;IAcjB,YACmB,MAAc,EACd,QAAgB,EAChB,aAA4B;QAF5B,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAQ;QAChB,kBAAa,GAAb,aAAa,CAAe;IAC5C,CAAC;IAES,SAAS,CAAC,MAA2B;;YAChD,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE;gBAChD,OAAO;gBACP,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,QAAQ,CAAC,KAAa;;YACjC,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;gBACpD,OAAO;gBACP,MAAM,EAAE,KAAK;aACd,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,QAAQ,CAAC,KAAa;;YACjC,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;gBACpD,OAAO;gBACP,MAAM,EAAE,KAAK;aACd,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,OAAO,CAAC,KAAa;;YAChC,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;gBACnD,OAAO;gBACP,MAAM,EAAE,KAAK;aACd,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,SAAS,CAAC,KAAa;;YAClC,MAAM,MAAM,GAAwB,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChE,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;gBACrD,OAAO;gBACP,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,WAAW,CAAC,KAAa,EAAE,MAAsB;;YAC5D,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE;gBACvD,OAAO;gBACP,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,SAAS,CAAC,KAAa,EAAE,MAAoB;;YACxD,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;gBACrD,OAAO;gBACP,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,QAAQ,CAAC,KAAa;;YACjC,MAAM,WAAW,GAA4B,EAAE,CAAC;YAEhD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrD,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACnD,OAAO,IAAI,iCAAc,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC;KAAA;IAEY,YAAY,CAAC,MAAc,EAAE,MAAuB;;YAC/D,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE;gBACzD,OAAO;gBACP,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,QAAQ,CAAC,MAAc;;YAClC,OAAO,IAAA,sBAAc,EAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;gBACrD,OAAO;gBACP,MAAM,EAAE,KAAK;aACd,CAAC,CAAC;QACL,CAAC;KAAA;IAEY,aAAa,CAAC,KAAa,EAAE,IAAY,EAAE,MAA4B;;YAClF,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5E,CAAC;KAAA;IAEY,cAAc,CAAC,MAAc,EAAE,IAAY,EAAE,MAA4B;;YACpF,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC9E,CAAC;KAAA;IAEa,UAAU,CAAC,GAAW,EAAE,IAAY,EAAE,MAA4B;;YAC9E,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;YAEhC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,MAAM,IAAA,mBAAQ,EAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YAEpC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAChB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;YAED,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;gBACvB,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YACrD,CAAC;YAED,OAAO,IAAA,sBAAc,EAAC,GAAG,EAAE;gBACzB,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;QACL,CAAC;KAAA;IAEa,yBAAyB,CAAC,KAAa,EAAE,UAAkB;;;YACvE,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,QAAQ,IAAI,UAAU,GAAG,CAAC;YACnD,MAAM,WAAW,GAAiB,EAAE,CAAC;YACrC,IAAI,SAAS,GAAG,IAAI,CAAC;YAErB,KAAK,CAAC,GAAG,OAAO,+BAA+B,CAAC,CAAC;YAEjD,OAAO,SAAS,EAAE,CAAC;gBACjB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAErE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;oBAClB,KAAK,CAAC,GAAG,OAAO,wBAAwB,CAAC,CAAC;oBAC1C,SAAS,GAAG,KAAK,CAAC;gBACpB,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC/B,KAAK,CAAC,GAAG,OAAO,6CAA6C,CAAC,CAAC;oBAC/D,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;oBACd,KAAK,CAAC,GAAG,OAAO,6BAA6B,IAAI,CAAC,aAAa,CAAC,cAAc,KAAK,CAAC,CAAC;oBACrF,MAAM,IAAA,aAAK,EAAC,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;oBAC/C,SAAS;gBACX,CAAC;gBAED,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;gBAC1B,KAAK,CAAC,GAAG,OAAO,mBAAmB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;gBAE9C,IAAI,MAAM,GAAG,KAAK,CAAC;gBACnB,IAAI,SAAS,GAAG,KAAK,CAAC;gBACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;gBACrB,IAAI,YAA0B,CAAC;gBAC/B,IAAI,KAAY,CAAC;gBAEjB,MAAM,eAAe,GAAG,IAAA,0BAAkB,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACpD,MAAM,WAAW,GAAG,IAAA,2BAAiB,EAAC,eAAe,CAAC,CAAC;gBACvD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC;gBAEhD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAQ,EAAE;oBACzC,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;oBACnE,IAAI,QAAQ,EAAE,CAAC;wBACb,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC;oBAC/D,CAAC;gBACH,CAAC,CAAC;gBAEF,MAAM,YAAY,GAAG,CAAC,IAAY,EAAQ,EAAE;oBAC1C,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBAC7C,IAAI,QAAQ,EAAE,CAAC;wBACb,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC;oBAC/D,CAAC;gBACH,CAAC,CAAC;gBAEF,MAAM,YAAY,iDACZ,IAAI,CAAC,OAAoC,GAC1C,IAAI,CAAC,aAAa,CAAC,YAAY,KAClC,GAAG,8DACE,OAAO,CAAC,GAAG,GACX,MAAA,IAAI,CAAC,OAAO,0CAAE,GAAG,GACjB,MAAA,IAAI,CAAC,aAAa,CAAC,YAAY,0CAAE,GAAG,KACvC,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,MAE5C,CAAC;gBAEF,KAAK,CAAC,GAAG,OAAO,mBAAmB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9C,IAAI,CAAC;oBACH,YAAY,GAAG,MAAM,IAAA,kBAAU,EAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE;wBAC1E,WAAW;wBACX,YAAY;qBACb,CAAC,CAAC;oBACH,SAAS,GAAG,IAAI,CAAC;oBACjB,KAAK,CAAC,GAAG,OAAO,oBAAoB,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,iBAAiB,IAAI,CAAC,EAAE,gBAAgB,GAAG,EAAE,CAAC,CAAC;oBACvE,KAAK,GAAG,GAAG,CAAC;oBAEZ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;wBACjB,yDAAyD;wBACzD,KAAK,CAAC,GAAG,OAAO,8BAA8B,CAAC,CAAC;wBAChD,MAAM,GAAG,CAAC;oBACZ,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;oBAEhE,IAAI,SAAS,EAAE,CAAC;wBACd,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;wBAC7C,KAAK,CAAC,GAAG,OAAO,+BAA+B,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;oBACjE,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;wBAC7B,KAAK,CAAC,GAAG,OAAO,4BAA4B,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;oBAC9D,CAAC;oBAED,WAAW,CAAC,GAAG,EAAE,CAAC;oBAClB,WAAW,CAAC,KAAK,EAAE,CAAC;oBAEpB,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,eAAe,EAAE;wBAClD,IAAI,EAAE,KAAK;wBACX,WAAW,EAAE,gBAAgB,IAAI,CAAC,EAAE,EAAE;qBACvC,CAAC,CAAC;oBAEH,KAAK,CAAC,GAAG,OAAO,4BAA4B,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;oBAC5D,IAAA,oBAAU,EAAC,eAAe,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;YAED,KAAK,CAAC,GAAG,OAAO,iCAAiC,CAAC,CAAC;YAEnD,OAAO,WAAW,CAAC;QACrB,CAAC;KAAA;;AA5OH,wBA6OC;AA5OwB,qBAAc,GAAkB;IACrD,QAAQ,EAAE,CAAC;IACX,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QACnC,OAAO;IACT,CAAC;IACD,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC5B,OAAO;IACT,CAAC;IACD,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,IAAI;IAClB,UAAU,EAAE,CAAC;CACd,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { TaskResult } from './task-result';
2
+ export declare class RunTasksResult {
3
+ readonly tasks: TaskResult[];
4
+ readonly completedTasks: TaskResult[];
5
+ readonly failedTasks: TaskResult[];
6
+ readonly completed: boolean;
7
+ readonly failed: boolean;
8
+ constructor(tasks: TaskResult[]);
9
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RunTasksResult = void 0;
4
+ class RunTasksResult {
5
+ constructor(tasks) {
6
+ this.tasks = tasks;
7
+ this.completedTasks = tasks.filter((x) => x.completed);
8
+ this.failedTasks = tasks.filter((x) => !x.completed);
9
+ this.completed = this.failedTasks.length === 0;
10
+ this.failed = this.failedTasks.length > 0;
11
+ }
12
+ }
13
+ exports.RunTasksResult = RunTasksResult;
14
+ //# sourceMappingURL=run-tasks-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-tasks-result.js","sourceRoot":"","sources":["../../../../../libs/client/src/lib/run-tasks-result.ts"],"names":[],"mappings":";;;AAEA,MAAa,cAAc;IAOzB,YAA4B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;QAC7C,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAErD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5C,CAAC;CACF;AAdD,wCAcC"}
@@ -1,10 +1,9 @@
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
- }
1
+ import { Run, Task } from '@tskmgr/common';
2
+ import { ChildProcess } from 'child_process';
3
+ export interface TaskResult {
4
+ run: Run;
5
+ task: Task;
6
+ childProcess: ChildProcess;
7
+ completed: boolean;
8
+ error?: Error;
9
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=task-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-result.js","sourceRoot":"","sources":["../../../../../libs/client/src/lib/task-result.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import { ChildProcess, SpawnOptionsWithoutStdio } from 'node:child_process';
2
+ export interface SpawnAsyncLineHandlers {
3
+ dataHandler?: (line: string) => void;
4
+ errorHandler?: (line: string) => void;
5
+ }
6
+ export declare function spawnAsync(command: string, args?: ReadonlyArray<string>, options?: SpawnOptionsWithoutStdio, lineHandlers?: SpawnAsyncLineHandlers): Promise<ChildProcess>;
7
+ export declare function delay(ms: number): Promise<void>;
8
+ export declare function getTaskLogFilename(taskId: number): string;
9
+ export declare function fetchWithRetry<T>(input: string | URL | Request, init?: RequestInit): Promise<T>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.spawnAsync = spawnAsync;
16
+ exports.delay = delay;
17
+ exports.getTaskLogFilename = getTaskLogFilename;
18
+ exports.fetchWithRetry = fetchWithRetry;
19
+ const node_child_process_1 = require("node:child_process");
20
+ const node_readline_1 = require("node:readline");
21
+ const node_os_1 = require("node:os");
22
+ const node_path_1 = require("node:path");
23
+ const p_retry_1 = __importDefault(require("p-retry"));
24
+ const debug_1 = __importDefault(require("debug"));
25
+ const debug = (0, debug_1.default)('tskmgr:client');
26
+ function spawnAsync(command, args, options, lineHandlers) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ return new Promise((resolve, reject) => {
29
+ const childProcess = (0, node_child_process_1.spawn)(command, args, options);
30
+ if (lineHandlers === null || lineHandlers === void 0 ? void 0 : lineHandlers.dataHandler) {
31
+ const readlineStdout = (0, node_readline_1.createInterface)({ input: childProcess.stdout });
32
+ readlineStdout.on('line', lineHandlers.dataHandler);
33
+ }
34
+ if (lineHandlers === null || lineHandlers === void 0 ? void 0 : lineHandlers.errorHandler) {
35
+ const readlineStderr = (0, node_readline_1.createInterface)({ input: childProcess.stderr });
36
+ readlineStderr.on('line', lineHandlers.errorHandler);
37
+ }
38
+ childProcess.on('close', (code) => {
39
+ if (code === 0) {
40
+ resolve(childProcess);
41
+ }
42
+ else {
43
+ reject(childProcess);
44
+ }
45
+ });
46
+ childProcess.on('error', (err) => {
47
+ reject(childProcess); // TODO: try to return error message
48
+ });
49
+ });
50
+ });
51
+ }
52
+ function delay(ms) {
53
+ return new Promise((resolve) => {
54
+ setTimeout(resolve, ms);
55
+ });
56
+ }
57
+ function getTaskLogFilename(taskId) {
58
+ return (0, node_path_1.join)((0, node_os_1.tmpdir)(), `task-${taskId}.log`);
59
+ }
60
+ function fetchWithRetry(input, init) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ const run = () => __awaiter(this, void 0, void 0, function* () {
63
+ const response = yield fetch(input, init);
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP ${response.status} ${response.statusText}: ${input}`);
66
+ }
67
+ return response.json();
68
+ });
69
+ return (0, p_retry_1.default)(run, {
70
+ retries: 3,
71
+ onFailedAttempt: (error) => debug('Retry attempt failed:', error),
72
+ });
73
+ });
74
+ }
75
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../../libs/client/src/lib/utils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAcA,gCA+BC;AAED,sBAIC;AAED,gDAEC;AAED,wCAaC;AAtED,2DAAmF;AACnF,iDAAgD;AAChD,qCAAiC;AACjC,yCAAiC;AACjC,sDAA6B;AAC7B,kDAA0B;AAE1B,MAAM,KAAK,GAAG,IAAA,eAAK,EAAC,eAAe,CAAC,CAAC;AAOrC,SAAsB,UAAU,CAC9B,OAAe,EACf,IAA4B,EAC5B,OAAkC,EAClC,YAAqC;;QAErC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,YAAY,GAAG,IAAA,0BAAK,EAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAEnD,IAAI,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,WAAW,EAAE,CAAC;gBAC9B,MAAM,cAAc,GAAG,IAAA,+BAAe,EAAC,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;gBACvE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;YACtD,CAAC;YAED,IAAI,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,YAAY,EAAE,CAAC;gBAC/B,MAAM,cAAc,GAAG,IAAA,+BAAe,EAAC,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;gBACvE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC;YACvD,CAAC;YAED,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBAChC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,CAAC,YAAY,CAAC,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,YAAY,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBACtC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,oCAAoC;YAC5D,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CAAA;AAED,SAAgB,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,kBAAkB,CAAC,MAAc;IAC/C,OAAO,IAAA,gBAAI,EAAC,IAAA,gBAAM,GAAE,EAAE,QAAQ,MAAM,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAsB,cAAc,CAAI,KAA6B,EAAE,IAAkB;;QACvF,MAAM,GAAG,GAAG,GAAS,EAAE;YACrB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC,CAAA,CAAC;QAEF,OAAO,IAAA,iBAAM,EAAC,GAAG,EAAE;YACjB,OAAO,EAAE,CAAC;YACV,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC;SAClE,CAAC,CAAC;IACL,CAAC;CAAA"}
package/.babelrc DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "presets": [
3
- [
4
- "@nx/js/babel",
5
- {
6
- "useBuiltIns": "usage"
7
- }
8
- ]
9
- ]
10
- }
package/eslint.config.mjs DELETED
@@ -1,28 +0,0 @@
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/project.json DELETED
@@ -1,31 +0,0 @@
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,17 +0,0 @@
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
- }
@@ -1,12 +0,0 @@
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
- }
@@ -1,36 +0,0 @@
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
- });
package/src/lib/client.ts DELETED
@@ -1,265 +0,0 @@
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
- }
@@ -1,17 +0,0 @@
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
- }
@@ -1,27 +0,0 @@
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
- });
package/src/lib/utils.ts DELETED
@@ -1,71 +0,0 @@
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 DELETED
@@ -1,13 +0,0 @@
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
- }
package/tsconfig.lib.json DELETED
@@ -1,27 +0,0 @@
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
- }
@@ -1,22 +0,0 @@
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 DELETED
@@ -1,31 +0,0 @@
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
- }));