testeranto 0.47.11 → 0.47.13

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 (52) hide show
  1. package/dist/common/Features.js +85 -0
  2. package/dist/common/Node.js +82 -0
  3. package/dist/common/NodeWriter.js +56 -0
  4. package/dist/common/Project.js +648 -0
  5. package/dist/common/Types.js +2 -0
  6. package/dist/common/Web.js +70 -0
  7. package/dist/common/core.js +392 -0
  8. package/dist/common/electron.js +40 -0
  9. package/dist/common/preload.js +8 -0
  10. package/dist/common/subPackages/react/component.js +2 -0
  11. package/dist/common/subPackages/react/node.js +57 -0
  12. package/dist/common/subPackages/react/web.js +57 -0
  13. package/dist/common/subPackages/react-test-render/component.js +2 -0
  14. package/dist/common/subPackages/react-test-render/node.js +46 -0
  15. package/dist/common/subPackages/react-test-render/web.js +46 -0
  16. package/dist/common/tsconfig.common.tsbuildinfo +1 -0
  17. package/dist/module/Features.js +74 -0
  18. package/dist/module/Node.js +77 -0
  19. package/dist/module/NodeWriter.js +50 -0
  20. package/dist/module/Project.js +618 -0
  21. package/dist/module/Report.js +186 -0
  22. package/dist/module/Types.js +1 -0
  23. package/dist/module/Web.js +65 -0
  24. package/dist/module/core.js +383 -0
  25. package/dist/module/electron.js +35 -0
  26. package/dist/module/preload.js +6 -0
  27. package/dist/module/subPackages/react/component.js +1 -0
  28. package/dist/module/subPackages/react/node.js +52 -0
  29. package/dist/module/subPackages/react/web.js +52 -0
  30. package/dist/module/subPackages/react-test-render/component.js +1 -0
  31. package/dist/module/subPackages/react-test-render/node.js +16 -0
  32. package/dist/module/subPackages/react-test-render/web.js +16 -0
  33. package/dist/module/tsconfig.module.tsbuildinfo +1 -0
  34. package/dist/types/Features.d.ts +68 -0
  35. package/dist/types/Node.d.ts +12 -0
  36. package/dist/types/NodeWriter.d.ts +2 -0
  37. package/dist/types/Project.d.ts +32 -0
  38. package/dist/types/Types.d.ts +17 -0
  39. package/dist/types/Web.d.ts +12 -0
  40. package/dist/types/core.d.ts +219 -0
  41. package/dist/types/electron.d.ts +1 -0
  42. package/dist/types/preload.d.ts +1 -0
  43. package/dist/types/subPackages/react/component.d.ts +17 -0
  44. package/dist/types/subPackages/react/node.d.ts +4 -0
  45. package/dist/types/subPackages/react/web.d.ts +4 -0
  46. package/dist/types/subPackages/react-test-render/component.d.ts +17 -0
  47. package/dist/types/subPackages/react-test-render/node.d.ts +4 -0
  48. package/dist/types/subPackages/react-test-render/web.d.ts +4 -0
  49. package/dist/types/tsconfig.types.tsbuildinfo +1 -0
  50. package/package.json +9 -5
  51. package/src/subPackages/react/component.ts +1 -0
  52. package/src/subPackages/react-test-render/component.ts +21 -0
@@ -0,0 +1,618 @@
1
+ import { WebSocketServer } from 'ws';
2
+ import esbuild from "esbuild";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import fsExists from "fs.promises.exists";
6
+ import pm2 from "pm2";
7
+ import readline from 'readline';
8
+ readline.emitKeypressEvents(process.stdin);
9
+ if (process.stdin.isTTY)
10
+ process.stdin.setRawMode(true);
11
+ const TIMEOUT = 2000;
12
+ const OPEN_PORT = "";
13
+ let webSocketServer;
14
+ const getRunnables = (tests, payload = [new Set(), new Set()]) => {
15
+ return tests.reduce((pt, cv, cndx, cry) => {
16
+ if (cv[1] === "node") {
17
+ pt[0].add(cv[0]);
18
+ }
19
+ else if (cv[1] === "web") {
20
+ pt[1].add(cv[0]);
21
+ }
22
+ if (cv[2].length) {
23
+ getRunnables(cv[2], payload);
24
+ }
25
+ return pt;
26
+ }, payload);
27
+ };
28
+ export class ITProject {
29
+ constructor(config) {
30
+ this.exitCodes = {};
31
+ this.mode = `up`;
32
+ this.ports = {};
33
+ this.websockets = {};
34
+ this.resourceQueue = [];
35
+ this.spinCycle = 0;
36
+ this.spinAnimation = "←↖↑↗→↘↓↙";
37
+ this.mainLoop = async () => {
38
+ if (this.clearScreen) {
39
+ console.clear();
40
+ }
41
+ const procsTable = [];
42
+ pm2.list((err, procs) => {
43
+ procs.forEach((proc) => {
44
+ var _a, _b;
45
+ procsTable.push({
46
+ name: proc.name,
47
+ pid: proc.pid,
48
+ pm_id: proc.pm_id,
49
+ mem: (_a = proc.monit) === null || _a === void 0 ? void 0 : _a.memory,
50
+ cpu: (_b = proc.monit) === null || _b === void 0 ? void 0 : _b.cpu,
51
+ "exit code": this.exitCodes[proc.name]
52
+ });
53
+ });
54
+ console.table(procsTable);
55
+ console.table(this.resourceQueue);
56
+ // console.log("webSocketServer.clients", webSocketServer.clients.size);
57
+ // console.log("resourceQueue", this.resourceQueue);
58
+ const resourceRequest = this.resourceQueue.pop();
59
+ if (!resourceRequest) {
60
+ if (!this.devMode && this.mode === "up") {
61
+ this.initiateShutdown("resource request queue is empty");
62
+ }
63
+ if (this.mode === "down" && procsTable.every((p) => p.pid === 0 || p.pid === undefined)) {
64
+ this.shutdown();
65
+ }
66
+ }
67
+ else {
68
+ console.log("handling", resourceRequest);
69
+ if (resourceRequest.protocol === "ipc") {
70
+ this.allocateViaIpc(resourceRequest);
71
+ }
72
+ else if (resourceRequest.protocol === "ws") {
73
+ this.allocateViaWs(resourceRequest);
74
+ }
75
+ }
76
+ if (this.devMode) {
77
+ if (this.mode === "up") {
78
+ console.log(this.spinner(), "Running tests while watching for changes. Use 'q' to initiate shutdown");
79
+ }
80
+ else {
81
+ console.log(this.spinner(), "Shutdown is in progress. Please wait.");
82
+ }
83
+ }
84
+ else {
85
+ if (this.mode === "up") {
86
+ console.log(this.spinner(), "Running tests without watching for changes. Use 'q' to initiate shutdown");
87
+ }
88
+ else {
89
+ console.log(this.spinner(), "Shutdown is in progress. Please wait.");
90
+ }
91
+ }
92
+ // console.log(this.spinner());
93
+ // console.log(
94
+ // this.spinner(),
95
+ // this.mode === `up`
96
+ // ? `press "q" to initiate graceful shutdown`
97
+ // : `please wait while testeranto shuts down gracefully...`
98
+ // );
99
+ });
100
+ };
101
+ this.clearScreen = config.clearScreen;
102
+ this.devMode = config.devMode;
103
+ Object.values(config.ports).forEach((port) => {
104
+ this.ports[port] = OPEN_PORT;
105
+ });
106
+ const testPath = `${process.cwd()}/${config.tests}`;
107
+ const featurePath = `${process.cwd()}/${config.features}`;
108
+ process.on('SIGINT', () => this.initiateShutdown("CTRL+C"));
109
+ process.on('SIGQUIT', () => this.initiateShutdown("Keyboard quit"));
110
+ process.on('SIGTERM', () => this.initiateShutdown("'kill' command"));
111
+ process.stdin.on('keypress', (str, key) => {
112
+ if (key.name === 'q') {
113
+ this.initiateShutdown("'q' command");
114
+ }
115
+ });
116
+ import(testPath).then((tests) => {
117
+ this.tests = tests.default;
118
+ import(featurePath).then((features) => {
119
+ this.features = features.default;
120
+ Promise.resolve(Promise.all([
121
+ ...this.getSecondaryEndpointsPoints("web")
122
+ ]
123
+ .map(async (sourceFilePath) => {
124
+ const sourceFileSplit = sourceFilePath.split("/");
125
+ const sourceDir = sourceFileSplit.slice(0, -1);
126
+ const sourceFileName = sourceFileSplit[sourceFileSplit.length - 1];
127
+ const sourceFileNameMinusJs = sourceFileName.split(".").slice(0, -1).join(".");
128
+ const htmlFilePath = path.normalize(`${process.cwd()}/${config.outdir}/${sourceDir.join("/")}/${sourceFileNameMinusJs}.html`);
129
+ const jsfilePath = `./${sourceFileNameMinusJs}.mjs`;
130
+ return fs.promises.mkdir(path.dirname(htmlFilePath), { recursive: true }).then(x => fs.writeFileSync(htmlFilePath, `
131
+ <!DOCTYPE html>
132
+ <html lang="en">
133
+ <head>
134
+ <script type="module" src="${jsfilePath}"></script>
135
+ </head>
136
+
137
+ <body>
138
+ <h1>${htmlFilePath}</h1>
139
+ <div id="root">
140
+
141
+ </div>
142
+ </body>
143
+
144
+ <footer></footer>
145
+
146
+ </html>
147
+ `));
148
+ })));
149
+ const [nodeEntryPoints, webEntryPoints] = getRunnables(this.tests);
150
+ const esbuildConfigNode = {
151
+ define: {
152
+ "process.env.FLUENTFFMPEG_COV": "0"
153
+ },
154
+ absWorkingDir: process.cwd(),
155
+ banner: {
156
+ js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
157
+ },
158
+ target: "esnext",
159
+ // packages: "external",
160
+ format: "esm",
161
+ splitting: true,
162
+ outExtension: { '.js': '.mjs' },
163
+ platform: "node",
164
+ external: ["tests.test.js", "features.test.js", "react"],
165
+ outbase: config.outbase,
166
+ outdir: config.outdir,
167
+ jsx: 'transform',
168
+ entryPoints: [...nodeEntryPoints],
169
+ bundle: true,
170
+ minify: config.minify === true,
171
+ write: true,
172
+ loader: {
173
+ '.js': 'jsx',
174
+ '.png': 'binary',
175
+ '.jpg': 'binary',
176
+ },
177
+ plugins: [
178
+ ...(config.plugins || []),
179
+ {
180
+ name: 'rebuild-notify',
181
+ setup(build) {
182
+ build.onEnd(result => {
183
+ console.log(`node build ended with ${result.errors.length} errors`);
184
+ result.errors.length !== 0 && process.exit(-1);
185
+ // HERE: somehow restart the server from here, e.g., by sending a signal that you trap and react to inside the server.
186
+ });
187
+ }
188
+ },
189
+ ],
190
+ };
191
+ const esbuildConfigWeb = {
192
+ // packages: "external",
193
+ target: "esnext",
194
+ format: "esm",
195
+ splitting: true,
196
+ outExtension: { '.js': '.mjs' },
197
+ alias: {
198
+ react: path.resolve("./node_modules/react")
199
+ },
200
+ external: [
201
+ // "url",
202
+ "electron",
203
+ "path",
204
+ "fs",
205
+ // "react",
206
+ "stream",
207
+ "tests.test.js", "features.test.js"
208
+ ],
209
+ platform: "browser",
210
+ outbase: config.outbase,
211
+ outdir: config.outdir,
212
+ jsx: 'transform',
213
+ entryPoints: [
214
+ ...webEntryPoints,
215
+ testPath,
216
+ featurePath,
217
+ ],
218
+ bundle: true,
219
+ minify: config.minify === true,
220
+ write: true,
221
+ loader: {
222
+ '.js': 'jsx',
223
+ '.png': 'binary',
224
+ '.jpg': 'binary',
225
+ },
226
+ plugins: [
227
+ ...(config.plugins || []),
228
+ {
229
+ name: 'rebuild-notify',
230
+ setup(build) {
231
+ build.onEnd(result => {
232
+ console.log(`web build ended with ${result.errors.length} errors`);
233
+ result.errors.length !== 0 && process.exit(-1);
234
+ // HERE: somehow restart the server from here, e.g., by sending a signal that you trap and react to inside the server.
235
+ });
236
+ }
237
+ },
238
+ ],
239
+ };
240
+ esbuild.build({
241
+ bundle: true,
242
+ entryPoints: ["./node_modules/testeranto/dist/module/Report.js"],
243
+ minify: config.minify === true,
244
+ outbase: config.outbase,
245
+ write: true,
246
+ outfile: `${config.outdir}/Report.js`,
247
+ external: ["tests.test.js", "features.test.js"]
248
+ });
249
+ fs.writeFileSync(`${config.outdir}/report.html`, `
250
+ <!DOCTYPE html>
251
+ <html lang="en">
252
+
253
+ <head>
254
+ <meta name="description" content="Webpage description goes here" />
255
+ <meta charset="utf-8" />
256
+ <title>kokomoBay - testeranto</title>
257
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
258
+ <meta name="author" content="" />
259
+ <link rel="stylesheet" href="./Report.css" />
260
+
261
+ <script type="importmap">
262
+ {
263
+ "imports": {
264
+ "tests.test.js": "./tests.test.js",
265
+ "features.test.js": "./features.test.js"
266
+ }
267
+ }
268
+ </script>
269
+
270
+
271
+ <script src="./Report.js"></script>
272
+ </head>
273
+
274
+ <body>
275
+ <div id="root">
276
+ react is loading
277
+ </div>
278
+ </body>
279
+
280
+ </html>
281
+ `);
282
+ Promise.all([
283
+ esbuild.context(esbuildConfigNode).then(async (nodeContext) => {
284
+ await nodeContext.watch();
285
+ }),
286
+ esbuild.context(esbuildConfigWeb).then(async (esbuildWeb) => {
287
+ await esbuildWeb.watch();
288
+ })
289
+ ]);
290
+ pm2.connect(async (err) => {
291
+ if (err) {
292
+ console.error(err);
293
+ process.exit(-1);
294
+ }
295
+ else {
296
+ console.log(`pm2 is connected`);
297
+ }
298
+ // run a websocket as an alternative to node IPC
299
+ webSocketServer = new WebSocketServer({
300
+ port: 8080,
301
+ host: "localhost",
302
+ });
303
+ webSocketServer.on('open', () => {
304
+ console.log('open');
305
+ // process.exit()
306
+ });
307
+ webSocketServer.on('close', (data) => {
308
+ console.log('webSocketServer close: %s', data);
309
+ // process.exit()
310
+ });
311
+ webSocketServer.on('listening', () => {
312
+ console.log("webSocketServer listening", webSocketServer.address());
313
+ // process.exit()
314
+ });
315
+ webSocketServer.on('connection', (webSocket) => {
316
+ console.log('webSocketServer connection');
317
+ webSocket.on('message', (webSocketData) => {
318
+ console.log('webSocket message: %s', webSocketData);
319
+ const payload = webSocketData.valueOf();
320
+ const name = payload.data.name;
321
+ const messageType = payload.type;
322
+ const requestedResources = payload.data;
323
+ this.websockets[name] = webSocket;
324
+ console.log('connected: ' + name + ' in ' + Object.getOwnPropertyNames(this.websockets));
325
+ if (messageType === "testeranto:hola") {
326
+ console.log("hola WS", requestedResources);
327
+ this.requestResource(requestedResources, 'ws');
328
+ }
329
+ else if (messageType === "testeranto:adios") {
330
+ console.log("adios WS", name);
331
+ this.releaseTestResources(payload);
332
+ }
333
+ });
334
+ });
335
+ const makePath = (fPath, rt) => {
336
+ return path.resolve("./" + config.outdir + "/" + fPath.replace(path.extname(fPath), "") + ".mjs");
337
+ };
338
+ const bootInterval = setInterval(async () => {
339
+ const filesToLookup = this.tests
340
+ .map(([p, rt]) => {
341
+ const filepath = makePath(p, rt);
342
+ return {
343
+ filepath,
344
+ exists: fsExists(filepath),
345
+ };
346
+ });
347
+ const allFilesExist = (await Promise.all(filesToLookup.map((f) => f.exists))).every((b) => b);
348
+ if (!allFilesExist) {
349
+ console.log(this.spinner(), "waiting for files to build...");
350
+ filesToLookup.forEach((f) => {
351
+ console.log(f.exists, "\t", f.filepath);
352
+ });
353
+ }
354
+ else {
355
+ clearInterval(bootInterval);
356
+ pm2.launchBus((err, pm2_bus) => {
357
+ pm2_bus.on("testeranto:hola", (packet) => {
358
+ console.log("hola IPC", packet);
359
+ this.requestResource(packet.data.requirement, 'ipc');
360
+ });
361
+ pm2_bus.on("testeranto:adios", (payload) => {
362
+ console.log("adios IPC", payload);
363
+ this.releaseTestResources(payload.data);
364
+ });
365
+ });
366
+ this
367
+ .tests
368
+ .reduce((m, [inputFilePath, runtime]) => {
369
+ const script = makePath(inputFilePath, runtime);
370
+ const partialTestResourceByCommandLineArg = `${script} '${JSON.stringify({
371
+ name: inputFilePath,
372
+ ports: [],
373
+ fs: path.resolve(process.cwd(), config.outdir, inputFilePath),
374
+ })}'`;
375
+ if (runtime === "web") {
376
+ const fileAsList = inputFilePath.split("/");
377
+ const fileListHead = fileAsList.slice(0, -1);
378
+ const fname = fileAsList[fileAsList.length - 1];
379
+ const fnameOnly = fname.split(".").slice(0, -1).join(".");
380
+ const htmlFile = [config.outdir, ...fileListHead, `${fnameOnly}.html`].join("/");
381
+ const jsFile = htmlFile.split(".html")[0] + ".mjs";
382
+ console.log("watching", jsFile);
383
+ pm2.start({
384
+ script: `yarn electron node_modules/testeranto/dist/common/electron.js ${htmlFile} '${JSON.stringify({
385
+ name: inputFilePath,
386
+ ports: [],
387
+ fs: path.resolve(process.cwd(), config.outdir, inputFilePath),
388
+ })}'`,
389
+ name: inputFilePath,
390
+ autorestart: false,
391
+ args: partialTestResourceByCommandLineArg,
392
+ watch: [jsFile],
393
+ }, (err, proc) => {
394
+ if (err) {
395
+ console.error(err);
396
+ return pm2.disconnect();
397
+ }
398
+ });
399
+ }
400
+ else if (runtime === "node") {
401
+ pm2.start({
402
+ name: inputFilePath,
403
+ script: `node ${script} '${JSON.stringify({
404
+ name: inputFilePath,
405
+ ports: [],
406
+ fs: path.resolve(process.cwd(), config.outdir, inputFilePath),
407
+ })}'`,
408
+ autorestart: false,
409
+ watch: [script],
410
+ args: partialTestResourceByCommandLineArg
411
+ }, (err, proc) => {
412
+ if (err) {
413
+ console.error(err);
414
+ return pm2.disconnect();
415
+ }
416
+ });
417
+ }
418
+ this.exitCodes[inputFilePath] = null;
419
+ return [inputFilePath, ...m];
420
+ }, []);
421
+ setInterval(this.mainLoop, TIMEOUT).unref();
422
+ }
423
+ }, TIMEOUT).unref();
424
+ });
425
+ });
426
+ });
427
+ }
428
+ requestResource(requirement, protocol) {
429
+ this.resourceQueue.push({ requirement, protocol });
430
+ }
431
+ getSecondaryEndpointsPoints(runtime) {
432
+ if (runtime) {
433
+ return this.tests
434
+ .filter((t) => {
435
+ return (t[1] === runtime);
436
+ })
437
+ .map((tc) => tc[0]);
438
+ }
439
+ return this.tests
440
+ .map((tc) => tc[0]);
441
+ }
442
+ initiateShutdown(reason) {
443
+ console.log("Shutdown initiated because", reason);
444
+ this.mode = "down";
445
+ }
446
+ shutdown() {
447
+ console.log("Stopping PM2");
448
+ pm2.stop("all", (e) => console.error(e));
449
+ pm2.killDaemon((e) => console.error(e));
450
+ pm2.disconnect();
451
+ process.exit();
452
+ }
453
+ spinner() {
454
+ this.spinCycle = (this.spinCycle + 1) % this.spinAnimation.length;
455
+ return this.spinAnimation[this.spinCycle];
456
+ }
457
+ async releaseTestResources(payload) {
458
+ const name = payload.testResourceConfiguration.name;
459
+ const failed = payload.failed;
460
+ console.log("releaseTestResources", name, failed);
461
+ // reset ports
462
+ pm2.list((err, processes) => {
463
+ processes.forEach((proc) => {
464
+ if (proc.name === name) {
465
+ Object.keys(this.ports).forEach((port) => {
466
+ if (this.ports[port] === name) {
467
+ this.ports[port] = OPEN_PORT;
468
+ }
469
+ });
470
+ }
471
+ });
472
+ });
473
+ this.exitCodes[name] = failed;
474
+ }
475
+ allocateViaWs(resourceRequest) {
476
+ const pName = resourceRequest.requirement.name;
477
+ const testResourceRequirement = resourceRequest.requirement;
478
+ pm2.list((err, processes) => {
479
+ console.error(err);
480
+ processes.forEach((p) => {
481
+ if (p.name === pName && p.pid) {
482
+ const message = {
483
+ // these fields must be present
484
+ id: p.pid,
485
+ topic: "some topic",
486
+ type: "process:msg",
487
+ // Data to be sent
488
+ data: {
489
+ testResourceConfiguration: {
490
+ ports: [],
491
+ // fs: fPath,
492
+ },
493
+ id: p.pm_id,
494
+ },
495
+ };
496
+ if ((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) === 0) {
497
+ pm2.sendDataToProcessId(p.pid, message, function (err, res) {
498
+ // console.log("sendDataToProcessId", err, res, message);
499
+ });
500
+ }
501
+ if (((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) || 0) > 0) {
502
+ // clear any port-slots associated with this job
503
+ Object.values(this.ports).forEach((jobMaybe, portNumber) => {
504
+ if (jobMaybe && jobMaybe === pName) {
505
+ this.ports[portNumber] = OPEN_PORT;
506
+ }
507
+ });
508
+ // find a list of open ports
509
+ const foundOpenPorts = Object.keys(this.ports).filter((p) => this.ports[p] === OPEN_PORT);
510
+ // if there are enough open port-slots...
511
+ if (foundOpenPorts.length >= testResourceRequirement.ports) {
512
+ const selectionOfPorts = foundOpenPorts.slice(0, testResourceRequirement.ports);
513
+ const message = {
514
+ // these fields must be present
515
+ id: p.pid,
516
+ topic: "some topic",
517
+ // process:msg will be send as 'message' on target process
518
+ type: "process:msg",
519
+ // Data to be sent
520
+ data: {
521
+ testResourceConfiguration: {
522
+ // fs: fPath,
523
+ ports: selectionOfPorts,
524
+ },
525
+ id: p.pid,
526
+ },
527
+ };
528
+ pm2.sendDataToProcessId(p.pid, message, function (err, res) {
529
+ // no-op
530
+ });
531
+ // mark the selected ports as occupied
532
+ for (const foundOpenPort of selectionOfPorts) {
533
+ this.ports[foundOpenPort] = p.pid.toString();
534
+ }
535
+ }
536
+ else {
537
+ console.log(`no port was open so send the ${p.pid} job to the back of the resourceQueue`);
538
+ this.resourceQueue.push(resourceRequest);
539
+ }
540
+ }
541
+ }
542
+ });
543
+ });
544
+ }
545
+ allocateViaIpc(resourceRequest) {
546
+ console.log("allocateViaIpc", resourceRequest);
547
+ const pName = resourceRequest.requirement.name;
548
+ const testResourceRequirement = resourceRequest.requirement;
549
+ pm2.list((err, processes) => {
550
+ console.error(err);
551
+ processes.forEach((p) => {
552
+ console.log("p.pid, p.name, p.pm_id", p.pid, p.name, p.pm_id);
553
+ if (p.name === pName && p.pid) {
554
+ const message = {
555
+ // these fields must be present
556
+ id: p.pid,
557
+ topic: "some topic",
558
+ type: "process:msg",
559
+ // Data to be sent
560
+ data: {
561
+ testResourceConfiguration: {
562
+ ports: [],
563
+ // fs: fPath,
564
+ },
565
+ id: p.pm_id,
566
+ },
567
+ };
568
+ console.log("message", message);
569
+ if ((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) === 0) {
570
+ pm2.sendDataToProcessId(p.pm_id, message, function (err, res) {
571
+ // console.log("sendDataToProcessId", err, res, message);
572
+ });
573
+ }
574
+ if (((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) || 0) > 0) {
575
+ // clear any port-slots associated with this job
576
+ Object.values(this.ports).forEach((jobMaybe, portNumber) => {
577
+ if (jobMaybe && jobMaybe === pName) {
578
+ this.ports[portNumber] = OPEN_PORT;
579
+ }
580
+ });
581
+ // find a list of open ports
582
+ const foundOpenPorts = Object.keys(this.ports).filter((p) => this.ports[p] === OPEN_PORT);
583
+ // if there are enough open port-slots...
584
+ if (foundOpenPorts.length >= testResourceRequirement.ports) {
585
+ const selectionOfPorts = foundOpenPorts.slice(0, testResourceRequirement.ports);
586
+ const message = {
587
+ // these fields must be present
588
+ id: p.pid,
589
+ topic: "some topic",
590
+ // process:msg will be send as 'message' on target process
591
+ type: "process:msg",
592
+ // Data to be sent
593
+ data: {
594
+ testResourceConfiguration: {
595
+ // fs: fPath,
596
+ ports: selectionOfPorts,
597
+ },
598
+ id: p.pid,
599
+ },
600
+ };
601
+ pm2.sendDataToProcessId(p.pm_id, message, function (err, res) {
602
+ // no-op
603
+ });
604
+ // mark the selected ports as occupied
605
+ for (const foundOpenPort of selectionOfPorts) {
606
+ this.ports[foundOpenPort] = p.pid.toString();
607
+ }
608
+ }
609
+ else {
610
+ console.log(`no port was open so send the ${p.pid} job to the back of the resourceQueue`);
611
+ this.resourceQueue.push(resourceRequest);
612
+ }
613
+ }
614
+ }
615
+ });
616
+ });
617
+ }
618
+ }