@fedify/init 2.5.0-dev.2516 → 2.5.0-dev.2541
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/README.md +17 -7
- package/dist/_virtual/_rolldown/runtime.js +26 -0
- package/dist/action/configs.js +23 -11
- package/dist/ask/kv.js +10 -10
- package/dist/ask/mq.js +10 -10
- package/dist/ask/pm.js +8 -5
- package/dist/command.d.ts +21 -1
- package/dist/json/kv.js +1 -0
- package/dist/json/kv.json +1 -0
- package/dist/json/mq.js +1 -0
- package/dist/json/mq.json +1 -0
- package/dist/lib.js +2 -2
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.js +29 -0
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.js +42 -0
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js +67 -0
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.js +23 -0
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js +22 -0
- package/dist/node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js +36 -0
- package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js +47 -0
- package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js +33 -0
- package/dist/node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js +33 -0
- package/dist/node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js +14 -0
- package/dist/node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js +17 -0
- package/dist/node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js +8 -0
- package/dist/node_modules/.pnpm/which@2.0.2/node_modules/which/which.js +77 -0
- package/dist/{deno.js → packages/init/deno.js} +1 -1
- package/dist/scripts/tooling_main.js +17 -0
- package/dist/test/action.d.ts +6 -0
- package/dist/test/action.js +15 -5
- package/dist/test/cancel.js +14 -0
- package/dist/test/create.js +10 -5
- package/dist/test/lookup.js +17 -7
- package/dist/test/mod.d.ts +6 -1
- package/dist/test/mod.js +13 -6
- package/dist/test/node.js +6 -0
- package/dist/test/port.js +34 -13
- package/dist/test/run.js +15 -9
- package/dist/test/server.js +136 -108
- package/dist/types.d.ts +4 -0
- package/package.json +4 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { __commonJSMin } from "../../../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { require_shebang_regex } from "../../../shebang-regex@3.0.0/node_modules/shebang-regex/index.js";
|
|
3
|
+
//#region ../../node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js
|
|
4
|
+
var require_shebang_command = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
5
|
+
const shebangRegex = require_shebang_regex();
|
|
6
|
+
module.exports = (string = "") => {
|
|
7
|
+
const match = string.match(shebangRegex);
|
|
8
|
+
if (!match) return null;
|
|
9
|
+
const [path, argument] = match[0].replace(/#! ?/, "").split(" ");
|
|
10
|
+
const binary = path.split("/").pop();
|
|
11
|
+
if (binary === "env") return argument;
|
|
12
|
+
return argument ? `${binary} ${argument}` : binary;
|
|
13
|
+
};
|
|
14
|
+
}));
|
|
15
|
+
//#endregion
|
|
16
|
+
export default require_shebang_command();
|
|
17
|
+
export { require_shebang_command };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { __commonJSMin } from "../../../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region ../../node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js
|
|
3
|
+
var require_shebang_regex = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
4
|
+
module.exports = /^#!(.*)/;
|
|
5
|
+
}));
|
|
6
|
+
//#endregion
|
|
7
|
+
export default require_shebang_regex();
|
|
8
|
+
export { require_shebang_regex };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { __commonJSMin, __require } from "../../../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { require_isexe } from "../../../isexe@2.0.0/node_modules/isexe/index.js";
|
|
3
|
+
//#region ../../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js
|
|
4
|
+
var require_which = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
5
|
+
const isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
|
|
6
|
+
const path = __require("path");
|
|
7
|
+
const COLON = isWindows ? ";" : ":";
|
|
8
|
+
const isexe = require_isexe();
|
|
9
|
+
const getNotFoundError = (cmd) => Object.assign(/* @__PURE__ */ new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
10
|
+
const getPathInfo = (cmd, opt) => {
|
|
11
|
+
const colon = opt.colon || COLON;
|
|
12
|
+
const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [...isWindows ? [process.cwd()] : [], ...(opt.path || process.env.PATH || "").split(colon)];
|
|
13
|
+
const pathExtExe = isWindows ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : "";
|
|
14
|
+
const pathExt = isWindows ? pathExtExe.split(colon) : [""];
|
|
15
|
+
if (isWindows) {
|
|
16
|
+
if (cmd.indexOf(".") !== -1 && pathExt[0] !== "") pathExt.unshift("");
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
pathEnv,
|
|
20
|
+
pathExt,
|
|
21
|
+
pathExtExe
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
const which = (cmd, opt, cb) => {
|
|
25
|
+
if (typeof opt === "function") {
|
|
26
|
+
cb = opt;
|
|
27
|
+
opt = {};
|
|
28
|
+
}
|
|
29
|
+
if (!opt) opt = {};
|
|
30
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
31
|
+
const found = [];
|
|
32
|
+
const step = (i) => new Promise((resolve, reject) => {
|
|
33
|
+
if (i === pathEnv.length) return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
|
|
34
|
+
const ppRaw = pathEnv[i];
|
|
35
|
+
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
36
|
+
const pCmd = path.join(pathPart, cmd);
|
|
37
|
+
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
38
|
+
resolve(subStep(p, i, 0));
|
|
39
|
+
});
|
|
40
|
+
const subStep = (p, i, ii) => new Promise((resolve, reject) => {
|
|
41
|
+
if (ii === pathExt.length) return resolve(step(i + 1));
|
|
42
|
+
const ext = pathExt[ii];
|
|
43
|
+
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
44
|
+
if (!er && is) if (opt.all) found.push(p + ext);
|
|
45
|
+
else return resolve(p + ext);
|
|
46
|
+
return resolve(subStep(p, i, ii + 1));
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
50
|
+
};
|
|
51
|
+
const whichSync = (cmd, opt) => {
|
|
52
|
+
opt = opt || {};
|
|
53
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
54
|
+
const found = [];
|
|
55
|
+
for (let i = 0; i < pathEnv.length; i++) {
|
|
56
|
+
const ppRaw = pathEnv[i];
|
|
57
|
+
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
58
|
+
const pCmd = path.join(pathPart, cmd);
|
|
59
|
+
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
60
|
+
for (let j = 0; j < pathExt.length; j++) {
|
|
61
|
+
const cur = p + pathExt[j];
|
|
62
|
+
try {
|
|
63
|
+
if (isexe.sync(cur, { pathExt: pathExtExe })) if (opt.all) found.push(cur);
|
|
64
|
+
else return cur;
|
|
65
|
+
} catch (ex) {}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (opt.all && found.length) return found;
|
|
69
|
+
if (opt.nothrow) return null;
|
|
70
|
+
throw getNotFoundError(cmd);
|
|
71
|
+
};
|
|
72
|
+
module.exports = which;
|
|
73
|
+
which.sync = whichSync;
|
|
74
|
+
}));
|
|
75
|
+
//#endregion
|
|
76
|
+
export default require_which();
|
|
77
|
+
export { require_which };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
//#region ../../scripts/tooling_main.ts
|
|
6
|
+
/** Whether Node.js launched this module as its entry point. */
|
|
7
|
+
function isMain(url) {
|
|
8
|
+
const entry = process.argv[1];
|
|
9
|
+
if (entry == null) return false;
|
|
10
|
+
try {
|
|
11
|
+
return realpathSync(resolve(entry)) === realpathSync(fileURLToPath(url));
|
|
12
|
+
} catch {
|
|
13
|
+
return pathToFileURL(resolve(entry)).href === url;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { isMain };
|
package/dist/test/action.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import { set } from "../utils.js";
|
|
2
1
|
import { checkRequiredDbs } from "./db.js";
|
|
3
2
|
import { fillEmptyOptions } from "./fill.js";
|
|
4
3
|
import runTests from "./run.js";
|
|
5
4
|
import { emptyTestDir, genRunId, genTestDirPrefix, logTestDir } from "./utils.js";
|
|
6
|
-
import { pipe, tap, when } from "@fxts/core";
|
|
7
5
|
//#region src/test/action.ts
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
6
|
+
async function runTestInit(options) {
|
|
7
|
+
const runId = genRunId();
|
|
8
|
+
const data = fillEmptyOptions({
|
|
9
|
+
...options,
|
|
10
|
+
runId,
|
|
11
|
+
testDirPrefix: genTestDirPrefix({ runId })
|
|
12
|
+
});
|
|
13
|
+
await emptyTestDir(data);
|
|
14
|
+
await checkRequiredDbs(data);
|
|
15
|
+
logTestDir(data);
|
|
16
|
+
let success = true;
|
|
17
|
+
if (data.dryRun) success = await runTests(true)(data) && success;
|
|
18
|
+
if (data.hydRun) success = await runTests(false)(data) && success;
|
|
19
|
+
return success;
|
|
20
|
+
}
|
|
11
21
|
//#endregion
|
|
12
22
|
export { runTestInit as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/test/cancel.ts
|
|
2
|
+
/** An OS request to stop the initializer matrix after cleaning up its server. */
|
|
3
|
+
var TestInterruptedError = class extends Error {
|
|
4
|
+
signal;
|
|
5
|
+
exitCode;
|
|
6
|
+
constructor(signal) {
|
|
7
|
+
super(`Initializer tests interrupted by ${signal}`);
|
|
8
|
+
this.signal = signal;
|
|
9
|
+
this.name = "TestInterruptedError";
|
|
10
|
+
this.exitCode = signal === "SIGINT" ? 130 : 143;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { TestInterruptedError };
|
package/dist/test/create.js
CHANGED
|
@@ -2,6 +2,7 @@ import pm_default from "../json/pm.js";
|
|
|
2
2
|
import { printErrorMessage, printMessage, product } from "../utils.js";
|
|
3
3
|
import { getBuildCommand, kvStores, messageQueues } from "../lib.js";
|
|
4
4
|
import webFrameworks from "../webframeworks/mod.js";
|
|
5
|
+
import { nodeExecutable } from "./node.js";
|
|
5
6
|
import { filter, isEmpty, pipe, toArray } from "@fxts/core";
|
|
6
7
|
import process from "node:process";
|
|
7
8
|
import $ from "@david/dax";
|
|
@@ -11,9 +12,10 @@ import { values } from "@optique/core";
|
|
|
11
12
|
//#region src/test/create.ts
|
|
12
13
|
const BANNED_PMS = ["bun", "yarn"];
|
|
13
14
|
const createTestApp = (testDirPrefix, dry) => async (options) => {
|
|
15
|
+
if (!dry && options[1] === "deno") await ensureDenoWorkspaceCache();
|
|
14
16
|
const testDir = join$1(testDirPrefix, ...options);
|
|
15
17
|
const vals = values(testDir.split(sep).slice(-4));
|
|
16
|
-
const result = await $`${toArray(genInitCommand(testDir, dry, options))}`.cwd(join$1(import.meta.dirname, "..", "..")).stdin("null").stdout("piped").stderr("piped").noThrow().spawn();
|
|
18
|
+
const result = await $`${toArray(genInitCommand(testDir, dry, options))}`.cwd(join$1(import.meta.dirname, "..", "..")).env("FEDIFY_TEST_MODE", "true").stdin("null").stdout("piped").stderr("piped").noThrow().spawn();
|
|
17
19
|
await saveOutputs(testDir, result);
|
|
18
20
|
if (result.code === 0) {
|
|
19
21
|
if (!dry && (!await validateDevToolScripts(testDir, options) || !await validateFrameworkBuild(testDir, options))) {
|
|
@@ -33,9 +35,8 @@ ${join$1(testDir, "err.txt")}\n`;
|
|
|
33
35
|
return "";
|
|
34
36
|
};
|
|
35
37
|
function* genInitCommand(testDir, dry, [webFramework, packageManager, kvStore, messageQueue]) {
|
|
36
|
-
yield
|
|
37
|
-
yield "
|
|
38
|
-
yield "-A";
|
|
38
|
+
yield nodeExecutable;
|
|
39
|
+
yield "--experimental-transform-types";
|
|
39
40
|
yield "src/test/execute.ts";
|
|
40
41
|
yield "init";
|
|
41
42
|
yield testDir;
|
|
@@ -128,5 +129,9 @@ function filterOptions(options) {
|
|
|
128
129
|
messageQueues[mq].packageManagers
|
|
129
130
|
].every((pms) => pms.includes(pm));
|
|
130
131
|
}
|
|
132
|
+
let denoWorkspaceCache;
|
|
133
|
+
function ensureDenoWorkspaceCache() {
|
|
134
|
+
return denoWorkspaceCache ??= $`mise run deno:cache`.cwd(join$1(import.meta.dirname, "..", "..", "..", "..")).stdin("null").spawn();
|
|
135
|
+
}
|
|
131
136
|
//#endregion
|
|
132
|
-
export { createTestApp as default, filterOptions, generateTestCases };
|
|
137
|
+
export { createTestApp as default, filterOptions, genInitCommand, generateTestCases };
|
package/dist/test/lookup.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { printErrorMessage, printMessage } from "../utils.js";
|
|
2
2
|
import { getDevCommand, kvStores, messageQueues, packageManagers } from "../lib.js";
|
|
3
3
|
import webFrameworks from "../webframeworks/mod.js";
|
|
4
|
+
import { nodeExecutable } from "./node.js";
|
|
5
|
+
import { TestInterruptedError } from "./cancel.js";
|
|
4
6
|
import { replacePortInApp, reservePort } from "./port.js";
|
|
5
7
|
import { STARTUP_TIMEOUT, serverClosure, waitForServer } from "./server.js";
|
|
6
8
|
import { join } from "@fxts/core";
|
|
7
9
|
import $ from "@david/dax";
|
|
8
10
|
import { join as join$1, sep } from "node:path";
|
|
9
11
|
import { values } from "@optique/core";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
10
13
|
//#region src/test/lookup.ts
|
|
11
14
|
const HANDLE = "john";
|
|
12
15
|
const BANNED_LOOKUP_REASONS = {
|
|
@@ -22,13 +25,13 @@ const BANNED_LOOKUP_CASES = Object.keys(BANNED_LOOKUP_REASONS).map((key) => key.
|
|
|
22
25
|
*
|
|
23
26
|
* @param dirs - Array of paths to generated app directories
|
|
24
27
|
*/
|
|
25
|
-
async function runServerAndLookupUser(dirs) {
|
|
28
|
+
async function runServerAndLookupUser(dirs, test = testApp) {
|
|
26
29
|
const valid = dirs.filter(Boolean).filter(isTestable);
|
|
27
30
|
printSkippedCases(dirs);
|
|
28
31
|
if (valid.length === 0) printErrorMessage`\nNo directories to lookup test.`;
|
|
29
32
|
printMessage``;
|
|
30
33
|
printMessage`Lookup Test start for ${String(valid.length)} app(s)!`;
|
|
31
|
-
const results = await Array.fromAsync(valid,
|
|
34
|
+
const results = await Array.fromAsync(valid, test);
|
|
32
35
|
const successCount = results.filter(Boolean).length;
|
|
33
36
|
const failCount = results.length - successCount;
|
|
34
37
|
printMessage`Lookup Test Results:
|
|
@@ -36,6 +39,7 @@ async function runServerAndLookupUser(dirs) {
|
|
|
36
39
|
Passed: ${String(successCount)}
|
|
37
40
|
Failed: ${String(failCount)}\n\n`;
|
|
38
41
|
printFailedCases(valid, results);
|
|
42
|
+
return valid.length > 0 && results.every(Boolean);
|
|
39
43
|
}
|
|
40
44
|
const parseLookupCase = (dir) => dir.split(sep).slice(-4);
|
|
41
45
|
const matchesLookupCasePattern = (target) => (pattern) => pattern.every((value, index) => value === "*" || value === target[index]);
|
|
@@ -80,7 +84,10 @@ async function testApp(dir) {
|
|
|
80
84
|
const { port, release } = await reservePort();
|
|
81
85
|
await replacePortInApp(dir, wf, defaultPort, port);
|
|
82
86
|
printMessage` Using port ${String(port)}`;
|
|
83
|
-
const result = await serverClosure(dir, getDevCommand(pm), port, sendLookup, release).catch(() =>
|
|
87
|
+
const result = await serverClosure(dir, getDevCommand(pm), port, (port, signal) => sendLookup(port, void 0, signal), release).catch((error) => {
|
|
88
|
+
if (error instanceof TestInterruptedError) throw error;
|
|
89
|
+
return false;
|
|
90
|
+
});
|
|
84
91
|
printMessage` Lookup ${result ? "successful" : "failed"} for ${values([
|
|
85
92
|
wf,
|
|
86
93
|
pm,
|
|
@@ -93,21 +100,24 @@ ${join$1(dir, "err.txt")}\n`;
|
|
|
93
100
|
printMessage`\n`;
|
|
94
101
|
return result;
|
|
95
102
|
}
|
|
96
|
-
async function sendLookup(port) {
|
|
103
|
+
async function sendLookup(port, cliEntry = fileURLToPath(new URL("../../../cli/dist/mod.js", import.meta.url)), signal) {
|
|
97
104
|
const serverUrl = `http://localhost:${port}`;
|
|
98
105
|
const lookupTarget = `${serverUrl}/users/${HANDLE}`;
|
|
99
106
|
printMessage` Waiting for server to start at ${serverUrl}...`;
|
|
100
|
-
if (!await waitForServer(serverUrl)) {
|
|
107
|
+
if (!await waitForServer(serverUrl, 1e4, signal)) {
|
|
101
108
|
printErrorMessage`Server did not start within ${String(STARTUP_TIMEOUT)}ms`;
|
|
102
109
|
return false;
|
|
103
110
|
}
|
|
104
111
|
printMessage` Server is ready. Running lookup command...`;
|
|
105
112
|
try {
|
|
106
|
-
|
|
113
|
+
let command = $`${nodeExecutable} ${cliEntry} lookup ${lookupTarget} -p`.stdin("null").stdout("piped").stderr("piped").noThrow();
|
|
114
|
+
if (signal != null) command = command.signal(signal);
|
|
115
|
+
const res = await command.spawn();
|
|
116
|
+
return res.code === 0 && res.stdout.includes(`id: URL '${lookupTarget}',`);
|
|
107
117
|
} catch (error) {
|
|
108
118
|
if (error instanceof Error) printErrorMessage`${error.message}`;
|
|
109
119
|
}
|
|
110
120
|
return false;
|
|
111
121
|
}
|
|
112
122
|
//#endregion
|
|
113
|
-
export { runServerAndLookupUser as default, isTestable, matchesLookupCasePattern, parseLookupCase };
|
|
123
|
+
export { runServerAndLookupUser as default, isTestable, matchesLookupCasePattern, parseLookupCase, sendLookup };
|
package/dist/test/mod.d.ts
CHANGED
package/dist/test/mod.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { testInitCommand } from "../command.js";
|
|
2
|
+
import { isMain } from "../scripts/tooling_main.js";
|
|
3
|
+
import { TestInterruptedError } from "./cancel.js";
|
|
2
4
|
import runTestInit from "./action.js";
|
|
3
5
|
import process from "node:process";
|
|
4
6
|
import { run } from "@optique/run";
|
|
5
7
|
//#region src/test/mod.ts
|
|
6
|
-
async function main() {
|
|
7
|
-
|
|
8
|
+
async function main(action = runTestInit) {
|
|
9
|
+
const result = run(testInitCommand, {
|
|
8
10
|
programName: "fedify-test-init",
|
|
9
11
|
help: "both"
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
+
});
|
|
13
|
+
try {
|
|
14
|
+
process.exitCode = await action(result) ? 0 : 1;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (!(error instanceof TestInterruptedError)) throw error;
|
|
17
|
+
process.exitCode = error.exitCode;
|
|
18
|
+
}
|
|
12
19
|
}
|
|
13
|
-
await main();
|
|
20
|
+
if (isMain(import.meta.url)) await main();
|
|
14
21
|
//#endregion
|
|
15
|
-
export {};
|
|
22
|
+
export { main };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
//#region src/test/node.ts
|
|
3
|
+
/** Select Node.js even when a compatibility test imports the harness in Deno. */
|
|
4
|
+
const nodeExecutable = process.versions.deno != null || process.versions.bun != null ? "node" : process.execPath;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { nodeExecutable };
|
package/dist/test/port.js
CHANGED
|
@@ -41,26 +41,47 @@ async function waitForPortRelease(port, timeout = 5e3) {
|
|
|
41
41
|
}
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Attempt to kill any process listening on the given port using lsof.
|
|
46
|
-
*/
|
|
44
|
+
/** Attempt to stop remaining listeners using the platform's process tools. */
|
|
47
45
|
async function killProcessOnPort(port) {
|
|
46
|
+
const windows = process.platform === "win32";
|
|
48
47
|
try {
|
|
49
|
-
const
|
|
50
|
-
execFile("lsof", [
|
|
48
|
+
const output = await new Promise((resolve, reject) => {
|
|
49
|
+
execFile(windows ? "netstat" : "lsof", windows ? [
|
|
50
|
+
"-ano",
|
|
51
|
+
"-p",
|
|
52
|
+
"tcp"
|
|
53
|
+
] : [
|
|
51
54
|
"-t",
|
|
52
55
|
`-i:${port}`,
|
|
53
56
|
"-sTCP:LISTEN"
|
|
54
|
-
],
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
57
|
+
], {
|
|
58
|
+
timeout: 1e3,
|
|
59
|
+
windowsHide: true
|
|
60
|
+
}, (error, stdout) => error ? reject(error) : resolve(stdout));
|
|
58
61
|
});
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
const pids = windows ? parseWindowsListeners(output, port) : output.trim().split(/\s+/).filter((pid) => /^\d+$/.test(pid));
|
|
63
|
+
for (const pid of new Set(pids)) if (windows) await new Promise((resolve) => {
|
|
64
|
+
execFile("taskkill", [
|
|
65
|
+
"/pid",
|
|
66
|
+
pid,
|
|
67
|
+
"/t",
|
|
68
|
+
"/f"
|
|
69
|
+
], {
|
|
70
|
+
timeout: 1e3,
|
|
71
|
+
windowsHide: true
|
|
72
|
+
}, () => resolve());
|
|
73
|
+
});
|
|
74
|
+
else try {
|
|
75
|
+
process.kill(Number(pid), "SIGKILL");
|
|
61
76
|
} catch {}
|
|
62
77
|
} catch {}
|
|
63
78
|
}
|
|
79
|
+
function parseWindowsListeners(output, port) {
|
|
80
|
+
return output.split(/\r?\n/).flatMap((line) => {
|
|
81
|
+
const [protocol, localAddress, , state, pid] = line.trim().split(/\s+/);
|
|
82
|
+
return protocol === "TCP" && localAddress?.endsWith(`:${port}`) && state === "LISTENING" && /^\d+$/.test(pid) ? [pid] : [];
|
|
83
|
+
});
|
|
84
|
+
}
|
|
64
85
|
/**
|
|
65
86
|
* Reserve a free port by binding to port 0 and letting the OS assign one.
|
|
66
87
|
* The socket is held until the returned `release` function is called,
|
|
@@ -123,8 +144,8 @@ async function ensurePortReleased(port) {
|
|
|
123
144
|
if (!await waitForPortRelease(port, 5e3)) {
|
|
124
145
|
printMessage` Port ${String(port)} still in use—force-killing...`;
|
|
125
146
|
await killProcessOnPort(port);
|
|
126
|
-
await waitForPortRelease(port, 3e3);
|
|
147
|
+
if (!await waitForPortRelease(port, 3e3)) throw new Error(`Port ${port} is still in use after server cleanup`);
|
|
127
148
|
}
|
|
128
149
|
}
|
|
129
150
|
//#endregion
|
|
130
|
-
export { ensurePortReleased, isPortInUse, killProcessOnPort, replacePortInApp, reservePort, waitForPortRelease };
|
|
151
|
+
export { ensurePortReleased, isPortInUse, killProcessOnPort, parseWindowsListeners, replacePortInApp, reservePort, waitForPortRelease };
|
package/dist/test/run.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { printMessage } from "../utils.js";
|
|
2
2
|
import createTestApp, { filterOptions, generateTestCases } from "./create.js";
|
|
3
3
|
import runServerAndLookupUser from "./lookup.js";
|
|
4
|
-
import {
|
|
5
|
-
import { join as join$1 } from "node:path";
|
|
4
|
+
import { join } from "node:path";
|
|
6
5
|
import { optionNames } from "@optique/core";
|
|
7
6
|
//#region src/test/run.ts
|
|
8
|
-
const runTests = (dry) => ({ testDirPrefix, dryRun, hydRun, ...options }) =>
|
|
9
|
-
|
|
7
|
+
const runTests = (dry, runners) => async ({ testDirPrefix, dryRun, hydRun, ...options }) => {
|
|
8
|
+
printMessage`\n
|
|
10
9
|
Init ${dry ? "Dry" : "Hyd"} Test start!
|
|
11
10
|
Options: ${optionNames([
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
])}
|
|
11
|
+
"Web Framework",
|
|
12
|
+
"Package Manager",
|
|
13
|
+
"KV Store",
|
|
14
|
+
"Message Queue"
|
|
15
|
+
])}`;
|
|
16
|
+
const create = runners?.create ?? createTestApp(join(testDirPrefix, getMid(dryRun, hydRun, dry)), dry);
|
|
17
|
+
const dirs = [];
|
|
18
|
+
for (const testCase of generateTestCases(options)) if (filterOptions(testCase)) dirs.push(await create(testCase));
|
|
19
|
+
const generated = dirs.length > 0 && dirs.every(Boolean);
|
|
20
|
+
const lookup = dry ? true : await (runners?.lookup ?? runServerAndLookupUser)(dirs);
|
|
21
|
+
return generated && lookup;
|
|
22
|
+
};
|
|
17
23
|
const getMid = (dryRun, hydRun, dry) => dryRun === hydRun ? dry ? "dry" : "hyd" : "";
|
|
18
24
|
//#endregion
|
|
19
25
|
export { runTests as default };
|