poku 1.10.0 → 1.10.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.
@@ -16,7 +16,7 @@ type BackgroundProcessOptions = {
16
16
  */
17
17
  startAfter?: string | number;
18
18
  /**
19
- * Stops the service after:
19
+ * Stops the service for neither success nor failure after:
20
20
  * @default 60000
21
21
  */
22
22
  timeout?: number;
@@ -43,4 +43,5 @@ export type StartServiceOptions = {
43
43
  */
44
44
  readonly platform?: Configs['platform'];
45
45
  } & BackgroundProcessOptions;
46
+ export type End = (port?: number) => Promise<void>;
46
47
  export {};
@@ -1,4 +1,4 @@
1
- import { StartScriptOptions, StartServiceOptions } from '../@types/background-process.js';
1
+ import { End, StartScriptOptions, StartServiceOptions } from '../@types/background-process.js';
2
2
  /**
3
3
  *
4
4
  * Starts a file in a background process
@@ -6,7 +6,7 @@ import { StartScriptOptions, StartServiceOptions } from '../@types/background-pr
6
6
  * Useful for servers, APIs, etc.
7
7
  */
8
8
  export declare const startService: (file: string, options?: StartServiceOptions) => Promise<{
9
- end: () => void;
9
+ end: End;
10
10
  }>;
11
11
  /**
12
12
  *
@@ -18,10 +18,8 @@ export declare const startService: (file: string, options?: StartServiceOptions)
18
18
  *
19
19
  * ---
20
20
  *
21
- * `startScript` currently doesn't works for **Bun** and **Deno**.
22
- *
23
- * - See: https://github.com/wellwelwel/poku/issues/143
21
+ * For **Bun**, please see https://github.com/oven-sh/bun/issues/11055
24
22
  */
25
23
  export declare const startScript: (script: string, options?: StartScriptOptions) => Promise<{
26
- end: () => void;
24
+ end: End;
27
25
  }>;
@@ -18,97 +18,137 @@ const node_child_process_1 = require("child_process");
18
18
  const runner_js_1 = require("../helpers/runner.js");
19
19
  const node_path_1 = __importDefault(require("path"));
20
20
  const list_files_js_1 = require("./list-files.js");
21
- const node_os_1 = require("os");
22
- const format_js_1 = require("../helpers/format.js");
21
+ const pid_js_1 = require("../services/pid.js");
22
+ const runningProcesses = new Map();
23
23
  /* c8 ignore start */
24
- const runningProcesses = {};
25
- const secureEnds = () => Object.values(runningProcesses).forEach((end) => end());
26
- const killWindowsProcess = (PID) => (0, node_child_process_1.spawn)('taskkill', ['/F', '/T', '/PID', PID.toString()]);
27
- node_process_1.default.once('SIGINT', () => {
28
- secureEnds();
29
- });
24
+ node_process_1.default.once('SIGINT', () => __awaiter(void 0, void 0, void 0, function* () {
25
+ for (const { end, port } of runningProcesses.values()) {
26
+ yield end(port);
27
+ }
28
+ }));
30
29
  /* c8 ignore stop */
31
30
  const backgroundProcess = (runtime, args, file, options) => new Promise((resolve, reject) => {
32
- let isResolved = false;
33
- const service = (0, node_child_process_1.spawn)(runtime, args, {
34
- stdio: ['inherit', 'pipe', 'pipe'],
35
- /* c8 ignore next */
36
- shell: runner_js_1.isWindows,
37
- cwd: (options === null || options === void 0 ? void 0 : options.cwd) ? (0, list_files_js_1.sanitizePath)(node_path_1.default.normalize(options.cwd)) : undefined,
38
- env: node_process_1.default.env,
39
- /* c8 ignore next */
40
- detached: !runner_js_1.isWindows,
41
- /* c8 ignore next */
42
- windowsHide: runner_js_1.isWindows,
43
- timeout: options === null || options === void 0 ? void 0 : options.timeout,
44
- });
45
- const PID = service.pid;
46
- /* c8 ignore start */
47
- const end = () => {
48
- delete runningProcesses[PID];
49
- if (runner_js_1.isWindows) {
50
- killWindowsProcess(PID);
51
- return;
52
- }
53
- if (['bun', 'deno'].includes(runtime) ||
54
- ['bun', 'deno'].includes(String(options === null || options === void 0 ? void 0 : options.runner))) {
55
- node_process_1.default.kill(PID);
56
- return;
57
- }
58
- node_process_1.default.kill(-PID, 'SIGKILL');
59
- return;
60
- };
61
- runningProcesses[PID] = end;
62
- /* c8 ignore stop */
63
- service.stdout.on('data', (data) => {
64
- if (!isResolved && typeof (options === null || options === void 0 ? void 0 : options.startAfter) !== 'number') {
65
- const stringData = JSON.stringify(String(data));
66
- if (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'undefined' ||
67
- (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'string' &&
68
- stringData.includes(options === null || options === void 0 ? void 0 : options.startAfter))) {
69
- resolve({ end });
70
- clearTimeout(timeout);
71
- isResolved = true;
31
+ try {
32
+ let isResolved = false;
33
+ const service = (0, node_child_process_1.spawn)(runtime, args, {
34
+ stdio: ['inherit', 'pipe', 'pipe'],
35
+ /* c8 ignore next */
36
+ shell: runner_js_1.isWindows,
37
+ cwd: (options === null || options === void 0 ? void 0 : options.cwd)
38
+ ? (0, list_files_js_1.sanitizePath)(node_path_1.default.normalize(options.cwd))
39
+ : undefined,
40
+ env: node_process_1.default.env,
41
+ /* c8 ignore next */
42
+ detached: !runner_js_1.isWindows,
43
+ /* c8 ignore next */
44
+ windowsHide: runner_js_1.isWindows,
45
+ timeout: options === null || options === void 0 ? void 0 : options.timeout,
46
+ });
47
+ const PID = service.pid;
48
+ let portBackup;
49
+ /* c8 ignore start */
50
+ const end = (port) => new Promise((resolve) => {
51
+ try {
52
+ runningProcesses.delete(PID);
53
+ if (runner_js_1.isWindows) {
54
+ pid_js_1.killPID.windows(PID);
55
+ return;
56
+ }
57
+ if (['bun', 'deno'].includes(runtime) ||
58
+ ['bun', 'deno'].includes(String(options === null || options === void 0 ? void 0 : options.runner))) {
59
+ node_process_1.default.kill(PID);
60
+ }
61
+ else
62
+ node_process_1.default.kill(-PID, 'SIGKILL');
63
+ if (port && ['bun', 'deno'].includes(runtime)) {
64
+ setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {
65
+ const PIDs = runner_js_1.isWindows
66
+ ? yield pid_js_1.findPID.windows(port)
67
+ : yield pid_js_1.findPID.unix(port);
68
+ for (const subPID of PIDs) {
69
+ if (!subPID)
70
+ continue;
71
+ runner_js_1.isWindows
72
+ ? yield pid_js_1.killPID.windows(subPID)
73
+ : yield pid_js_1.killPID.unix(subPID);
74
+ }
75
+ resolve(undefined);
76
+ return;
77
+ }));
78
+ }
79
+ else {
80
+ resolve(undefined);
81
+ return;
82
+ }
72
83
  }
73
- }
74
- (options === null || options === void 0 ? void 0 : options.verbose) && console.log(String(data));
75
- });
76
- service.stderr.on('data', (data) => {
77
- if (!isResolved && typeof (options === null || options === void 0 ? void 0 : options.startAfter) !== 'number') {
78
- const stringData = JSON.stringify(String(data));
79
- if (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'undefined' ||
80
- (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'string' &&
81
- stringData.includes(options === null || options === void 0 ? void 0 : options.startAfter))) {
82
- resolve({ end });
83
- clearTimeout(timeout);
84
- isResolved = true;
84
+ catch (_a) {
85
+ {
86
+ resolve(undefined);
87
+ return;
88
+ }
85
89
  }
86
- }
87
- (options === null || options === void 0 ? void 0 : options.verbose) && console.log(String(data));
88
- });
89
- service.on('error', (err) => {
90
- secureEnds();
91
- reject(`Service failed to start: ${err}`);
92
- });
93
- service.on('close', (code) => {
94
- if (code !== 0)
95
- reject(`Service exited with code ${code}`);
96
- });
97
- const timeout = setTimeout(() => {
98
- if (!isResolved) {
99
- secureEnds();
100
- reject(`createService: Timeout\nFile: ${file}`);
101
- }
102
- }, (options === null || options === void 0 ? void 0 : options.timeout) || 10000);
103
- if (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'number') {
104
- setTimeout(() => {
90
+ });
91
+ runningProcesses.set(PID, { end, port: portBackup });
92
+ /* c8 ignore stop */
93
+ /* c8 ignore start */
94
+ service.stdout.on('data', (data) => {
95
+ if (!isResolved && typeof (options === null || options === void 0 ? void 0 : options.startAfter) !== 'number') {
96
+ const stringData = JSON.stringify(String(data));
97
+ if (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'undefined' ||
98
+ (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'string' &&
99
+ stringData.includes(options === null || options === void 0 ? void 0 : options.startAfter))) {
100
+ resolve({ end });
101
+ clearTimeout(timeout);
102
+ isResolved = true;
103
+ }
104
+ }
105
+ (options === null || options === void 0 ? void 0 : options.verbose) && console.log(String(data));
106
+ });
107
+ /* c8 ignore stop */
108
+ /* c8 ignore start */
109
+ service.stderr.on('data', (data) => {
110
+ if (!isResolved && typeof (options === null || options === void 0 ? void 0 : options.startAfter) !== 'number') {
111
+ const stringData = JSON.stringify(String(data));
112
+ if (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'undefined' ||
113
+ (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'string' &&
114
+ stringData.includes(options === null || options === void 0 ? void 0 : options.startAfter))) {
115
+ resolve({ end });
116
+ clearTimeout(timeout);
117
+ isResolved = true;
118
+ }
119
+ }
120
+ (options === null || options === void 0 ? void 0 : options.verbose) && console.log(String(data));
121
+ });
122
+ /* c8 ignore stop */
123
+ /* c8 ignore stop */
124
+ service.on('error', (err) => {
125
+ end(portBackup);
126
+ reject(`Service failed to start: ${err}`);
127
+ });
128
+ /* c8 ignore stop */
129
+ /* c8 ignore start */
130
+ service.on('close', (code) => {
131
+ if (code !== 0)
132
+ reject(`Service exited with code ${code}`);
133
+ });
134
+ /* c8 ignore stop */
135
+ const timeout = setTimeout(() => {
105
136
  if (!isResolved) {
106
- resolve({ end });
107
- clearTimeout(timeout);
108
- isResolved = true;
137
+ end(portBackup);
138
+ reject(`createService: Timeout\nFile: ${file}`);
109
139
  }
110
- }, options.startAfter);
140
+ }, (options === null || options === void 0 ? void 0 : options.timeout) || 60000);
141
+ if (typeof (options === null || options === void 0 ? void 0 : options.startAfter) === 'number') {
142
+ setTimeout(() => {
143
+ if (!isResolved) {
144
+ resolve({ end });
145
+ clearTimeout(timeout);
146
+ isResolved = true;
147
+ }
148
+ }, options.startAfter);
149
+ }
111
150
  }
151
+ catch (_a) { }
112
152
  });
113
153
  /**
114
154
  *
@@ -133,19 +173,13 @@ exports.startService = startService;
133
173
  *
134
174
  * ---
135
175
  *
136
- * `startScript` currently doesn't works for **Bun** and **Deno**.
137
- *
138
- * - See: https://github.com/wellwelwel/poku/issues/143
176
+ * For **Bun**, please see https://github.com/oven-sh/bun/issues/11055
139
177
  */
140
178
  const startScript = (script, options) => __awaiter(void 0, void 0, void 0, function* () {
141
179
  const runner = (options === null || options === void 0 ? void 0 : options.runner) || 'npm';
142
180
  const runtimeOptions = (0, runner_js_1.scriptRunner)(runner);
143
181
  const runtime = runtimeOptions.shift();
144
182
  const runtimeArgs = [...runtimeOptions, script];
145
- /* c8 ignore start */
146
- if (['bun', 'deno'].includes(runner))
147
- throw new Error(`${format_js_1.format.bold('startScript')} currently doesn't works for Bun and Deno.${node_os_1.EOL}See: https://github.com/wellwelwel/poku/issues/143`);
148
- /* c8 ignore stop */
149
- return yield backgroundProcess(runtime, runtimeArgs, script, Object.assign(Object.assign({}, options), { isScript: true, runner }));
183
+ return yield backgroundProcess(runtime, runtimeArgs, script, Object.assign(Object.assign({}, options), { runner }));
150
184
  });
151
185
  exports.startScript = startScript;
@@ -0,0 +1,8 @@
1
+ export declare const killPID: {
2
+ unix: (PID: number) => Promise<void>;
3
+ windows: (PID: number) => Promise<void>;
4
+ };
5
+ export declare const findPID: {
6
+ unix: (port: number) => Promise<number[]>;
7
+ windows: (port: number) => Promise<number[]>;
8
+ };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /* c8 ignore start */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.findPID = exports.killPID = void 0;
5
+ const node_child_process_1 = require("child_process");
6
+ const node_os_1 = require("os");
7
+ exports.killPID = {
8
+ unix: (PID) => new Promise((resolve) => {
9
+ try {
10
+ const service = (0, node_child_process_1.spawn)('kill', ['-9', String(PID)]);
11
+ service.on('close', () => {
12
+ resolve(undefined);
13
+ });
14
+ }
15
+ catch (_a) {
16
+ resolve(undefined);
17
+ }
18
+ }),
19
+ windows: (PID) => new Promise((resolve) => {
20
+ try {
21
+ const service = (0, node_child_process_1.spawn)('taskkill', ['/F', '/T', '/PID', String(PID)]);
22
+ service.on('close', () => {
23
+ resolve(undefined);
24
+ });
25
+ }
26
+ catch (_a) {
27
+ resolve(undefined);
28
+ }
29
+ }),
30
+ };
31
+ exports.findPID = {
32
+ unix: (port) => new Promise((resolve) => {
33
+ try {
34
+ const PIDs = new Set();
35
+ const service = (0, node_child_process_1.spawn)('lsof', ['-t', '-i', `:${Number(port)}`]);
36
+ service.stdout.on('data', (data) => {
37
+ const output = data.toString().trim().split(node_os_1.EOL);
38
+ output.forEach((pid) => {
39
+ if (pid)
40
+ PIDs.add(Number(pid));
41
+ });
42
+ service.on('close', () => {
43
+ resolve(Array.from(PIDs));
44
+ });
45
+ });
46
+ }
47
+ catch (_a) { }
48
+ }),
49
+ windows: (port) => new Promise((resolve) => {
50
+ try {
51
+ const PIDs = new Set();
52
+ const service = (0, node_child_process_1.spawn)('cmd.exe', [
53
+ '/c',
54
+ `netstat -aon | findstr :${Number(port)}`,
55
+ ]);
56
+ service.stdout.on('data', (data) => {
57
+ const output = data.toString().trim();
58
+ const lines = output.trim().split(node_os_1.EOL);
59
+ lines.map((line) => {
60
+ const tokens = line.trim().split(/\s+/);
61
+ PIDs.add(Number(tokens[4]));
62
+ });
63
+ });
64
+ service.on('close', () => {
65
+ resolve(Array.from(PIDs));
66
+ });
67
+ service.stderr.on('data', (data) => {
68
+ console.error(`Erro: ${data}`);
69
+ });
70
+ }
71
+ catch (_a) { }
72
+ }),
73
+ };
74
+ /* c8 ignore stop */
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "poku",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "🐷 Poku makes testing easy for Node.js, Bun & Deno at the same time.",
5
5
  "main": "./lib/index.js",
6
6
  "scripts": {
7
7
  "test": "tsx src/bin/index.ts --parallel --debug --include=\"test/unit,test/integration,test/e2e\"",
8
+ "test:bun": "bun src/bin/index.ts --parallel --platform=bun --debug --include=\"test/unit,test/integration,test/e2e\"",
8
9
  "test:c8": "c8 npm run test",
9
10
  "test:ci": "tsx ./test/ci.test.ts",
10
- "test:node": "FILTER='node-' npm run test:ci",
11
- "test:deno": "FILTER='deno-' npm run test:ci",
12
- "test:bun": "FILTER='bun-' npm run test:ci",
11
+ "test:ci:node": "FILTER='node-' npm run test:ci",
12
+ "test:ci:bun": "FILTER='bun-' npm run test:ci",
13
+ "test:ci:deno": "FILTER='deno-' npm run test:ci",
13
14
  "predocker:deno": "docker compose -f ./test/docker/playground/deno/docker-compose.yml down",
14
15
  "docker:deno": "docker compose -f ./test/docker/playground/deno/docker-compose.yml up --build",
15
16
  "clear": "shx rm -rf ./lib ./ci ./coverage",