@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
package/dist/test/server.js
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
|
+
import { __toESM } from "../_virtual/_rolldown/runtime.js";
|
|
1
2
|
import { printErrorMessage } from "../utils.js";
|
|
2
|
-
import {
|
|
3
|
+
import { TestInterruptedError } from "./cancel.js";
|
|
4
|
+
import { ensurePortReleased } from "./port.js";
|
|
5
|
+
import { require_cross_spawn } from "../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.js";
|
|
3
6
|
import process from "node:process";
|
|
4
7
|
import { createWriteStream } from "node:fs";
|
|
5
8
|
import { join } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
import { finished } from "node:stream/promises";
|
|
11
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
8
12
|
//#region src/test/server.ts
|
|
13
|
+
var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
|
|
9
14
|
const STARTUP_TIMEOUT = 1e4;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
|
-
async function waitForServer(url, timeout = STARTUP_TIMEOUT) {
|
|
15
|
+
/** Wait for the generated application to respond successfully. */
|
|
16
|
+
async function waitForServer(url, timeout = STARTUP_TIMEOUT, signal) {
|
|
14
17
|
const startTime = Date.now();
|
|
15
18
|
while (Date.now() - startTime < timeout) {
|
|
19
|
+
signal?.throwIfAborted();
|
|
16
20
|
try {
|
|
17
|
-
const
|
|
21
|
+
const timeoutSignal = AbortSignal.timeout(1e3);
|
|
22
|
+
const response = await fetch(url, { signal: signal == null ? timeoutSignal : AbortSignal.any([signal, timeoutSignal]) });
|
|
18
23
|
const ok = response.ok;
|
|
19
24
|
await response.body?.cancel();
|
|
20
25
|
if (ok) return true;
|
|
21
26
|
} catch {}
|
|
22
|
-
await
|
|
27
|
+
await setTimeout$1(500, void 0, { signal });
|
|
23
28
|
}
|
|
24
29
|
return false;
|
|
25
30
|
}
|
|
26
|
-
|
|
31
|
+
/** Run a generated application's server and always stop its process tree. */
|
|
32
|
+
async function serverClosure(dir, cmd, defaultPort, callback, releasePort, signal) {
|
|
27
33
|
await releasePort?.();
|
|
28
|
-
|
|
29
|
-
const
|
|
34
|
+
signal?.throwIfAborted();
|
|
35
|
+
const command = typeof cmd === "string" ? cmd.split(" ") : cmd;
|
|
36
|
+
const child = (0, import_cross_spawn.default)(command[0], [...command.slice(1)], {
|
|
30
37
|
cwd: dir,
|
|
31
38
|
env: {
|
|
32
39
|
...process.env,
|
|
@@ -37,121 +44,142 @@ async function serverClosure(dir, cmd, defaultPort, callback, releasePort) {
|
|
|
37
44
|
"pipe",
|
|
38
45
|
"pipe"
|
|
39
46
|
],
|
|
40
|
-
detached:
|
|
47
|
+
detached: process.platform !== "win32"
|
|
41
48
|
});
|
|
42
|
-
child.on("error", () => {});
|
|
43
|
-
const stdoutWeb = Readable.toWeb(child.stdout);
|
|
44
|
-
const stderrWeb = Readable.toWeb(child.stderr);
|
|
45
|
-
const [stdoutForFile, stdoutForPort] = stdoutWeb.tee();
|
|
46
|
-
const [stderrForFile, stderrForPort] = stderrWeb.tee();
|
|
47
|
-
const cleanup = new AbortController();
|
|
48
49
|
const outFile = createWriteStream(join(dir, "out.txt"), { flags: "a" });
|
|
49
50
|
const errFile = createWriteStream(join(dir, "err.txt"), { flags: "a" });
|
|
50
|
-
const
|
|
51
|
-
|
|
51
|
+
const filesDone = Promise.all([finished(outFile), finished(errFile)]);
|
|
52
|
+
filesDone.catch(() => {});
|
|
53
|
+
child.stdout.pipe(outFile, { end: false });
|
|
54
|
+
child.stderr.pipe(errFile, { end: false });
|
|
55
|
+
let rejectFailure;
|
|
56
|
+
const failure = new Promise((_, reject) => rejectFailure = reject);
|
|
57
|
+
failure.catch(() => {});
|
|
58
|
+
const closed = new Promise((resolve) => child.once("close", () => resolve()));
|
|
59
|
+
child.once("error", (error) => rejectFailure(error));
|
|
60
|
+
child.once("exit", (code, exitSignal) => {
|
|
61
|
+
rejectFailure(/* @__PURE__ */ new Error(`Server exited (${exitSignal ?? code})`));
|
|
62
|
+
});
|
|
63
|
+
outFile.once("error", (error) => rejectFailure(error));
|
|
64
|
+
errFile.once("error", (error) => rejectFailure(error));
|
|
65
|
+
const onAbort = () => rejectFailure(signal?.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Server test aborted"));
|
|
66
|
+
const onInterrupt = () => rejectFailure(new TestInterruptedError("SIGINT"));
|
|
67
|
+
const onTerminate = () => rejectFailure(new TestInterruptedError("SIGTERM"));
|
|
68
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
69
|
+
process.once("SIGINT", onInterrupt);
|
|
70
|
+
process.once("SIGTERM", onTerminate);
|
|
71
|
+
if (signal?.aborted) onAbort();
|
|
52
72
|
let port = defaultPort;
|
|
73
|
+
const detection = detectPort(child);
|
|
74
|
+
let operationError;
|
|
75
|
+
let failed = false;
|
|
76
|
+
let result;
|
|
77
|
+
const shutdown = new AbortController();
|
|
53
78
|
try {
|
|
54
|
-
port = await
|
|
55
|
-
printErrorMessage`Failed to determine server port: ${
|
|
79
|
+
port = await Promise.race([detection.result.catch((error) => {
|
|
80
|
+
printErrorMessage`Failed to determine server port: ${error.message}`;
|
|
56
81
|
printErrorMessage`Use default port ${String(defaultPort)} for lookup.`;
|
|
57
82
|
return defaultPort;
|
|
58
|
-
});
|
|
59
|
-
|
|
83
|
+
}), failure]);
|
|
84
|
+
result = await Promise.race([callback(port, shutdown.signal), failure]);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
operationError = error;
|
|
87
|
+
failed = true;
|
|
60
88
|
} finally {
|
|
89
|
+
shutdown.abort(/* @__PURE__ */ new Error("Server test finished"));
|
|
90
|
+
detection.stop();
|
|
91
|
+
signal?.removeEventListener("abort", onAbort);
|
|
92
|
+
process.removeListener("SIGINT", onInterrupt);
|
|
93
|
+
process.removeListener("SIGTERM", onTerminate);
|
|
61
94
|
try {
|
|
62
|
-
|
|
63
|
-
} catch {
|
|
95
|
+
await stopProcessTree(child, closed);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (!failed) operationError = error;
|
|
98
|
+
failed = true;
|
|
99
|
+
printErrorMessage`Failed to stop server: ${String(error)}`;
|
|
100
|
+
} finally {
|
|
101
|
+
child.stdout.destroy();
|
|
102
|
+
child.stderr.destroy();
|
|
103
|
+
outFile.end();
|
|
104
|
+
errFile.end();
|
|
105
|
+
}
|
|
64
106
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
await ensurePortReleased(port);
|
|
107
|
+
await filesDone;
|
|
108
|
+
if (child.pid != null) await ensurePortReleased(port);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (!failed) operationError = error;
|
|
111
|
+
failed = true;
|
|
112
|
+
printErrorMessage`Failed to clean up server: ${String(error)}`;
|
|
113
|
+
}
|
|
73
114
|
}
|
|
115
|
+
if (failed) throw operationError;
|
|
116
|
+
return result;
|
|
74
117
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
function detectPort(child) {
|
|
119
|
+
let output = "";
|
|
120
|
+
let timer;
|
|
121
|
+
let onData;
|
|
122
|
+
const stop = () => {
|
|
123
|
+
clearTimeout(timer);
|
|
124
|
+
child.stdout.removeListener("data", onData);
|
|
125
|
+
child.stderr.removeListener("data", onData);
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
result: new Promise((resolve, reject) => {
|
|
129
|
+
timer = setTimeout(() => {
|
|
130
|
+
stop();
|
|
131
|
+
reject(/* @__PURE__ */ new Error("Timeout: Could not determine port from server output"));
|
|
132
|
+
}, STARTUP_TIMEOUT);
|
|
133
|
+
onData = (chunk) => {
|
|
134
|
+
output += chunk.toString("utf8");
|
|
135
|
+
for (const pattern of [
|
|
136
|
+
/listening on.*:(\d+)/i,
|
|
137
|
+
/server.*:(\d+)/i,
|
|
138
|
+
/port\s*:?\s*(\d+)/i,
|
|
139
|
+
/https?:\/\/[^:]+:(\d+)/i
|
|
140
|
+
]) {
|
|
141
|
+
const match = pattern.exec(output);
|
|
142
|
+
const port = Number(match?.[1]);
|
|
97
143
|
if (port > 0 && port < 65536) {
|
|
98
|
-
|
|
99
|
-
|
|
144
|
+
stop();
|
|
145
|
+
resolve(port);
|
|
146
|
+
break;
|
|
100
147
|
}
|
|
101
148
|
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
149
|
+
};
|
|
150
|
+
child.stdout.on("data", onData);
|
|
151
|
+
child.stderr.on("data", onData);
|
|
152
|
+
}),
|
|
153
|
+
stop
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function stopProcessTree(child, closed) {
|
|
157
|
+
if (child.pid == null) return;
|
|
158
|
+
if (process.platform === "win32") await new Promise((resolve) => {
|
|
159
|
+
execFile("taskkill", [
|
|
160
|
+
"/pid",
|
|
161
|
+
String(child.pid),
|
|
162
|
+
"/t",
|
|
163
|
+
"/f"
|
|
164
|
+
], {
|
|
165
|
+
timeout: 1e3,
|
|
166
|
+
windowsHide: true
|
|
167
|
+
}, () => resolve());
|
|
168
|
+
});
|
|
169
|
+
else {
|
|
170
|
+
const kill = (signal) => {
|
|
117
171
|
try {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
onData(decoder.decode(value, { stream: true }));
|
|
122
|
-
}
|
|
123
|
-
} catch {} finally {
|
|
124
|
-
signal?.removeEventListener("abort", onAbort);
|
|
125
|
-
reader.releaseLock();
|
|
126
|
-
onStreamEnd();
|
|
172
|
+
process.kill(-child.pid, signal);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error.code !== "ESRCH") throw error;
|
|
127
175
|
}
|
|
128
176
|
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (port) resolve(port);
|
|
133
|
-
});
|
|
134
|
-
readStream(stderr, (chunk) => {
|
|
135
|
-
stderrData += chunk;
|
|
136
|
-
const port = checkForPort(stderrData);
|
|
137
|
-
if (port) resolve(port);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
async function pipeStream(readable, writable, signal) {
|
|
142
|
-
const reader = readable.getReader();
|
|
143
|
-
const onAbort = () => void reader.cancel().catch(() => {});
|
|
144
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
145
|
-
try {
|
|
146
|
-
while (true) {
|
|
147
|
-
const { done, value } = await reader.read();
|
|
148
|
-
if (done) break;
|
|
149
|
-
writable.write(value);
|
|
150
|
-
}
|
|
151
|
-
} catch {} finally {
|
|
152
|
-
signal?.removeEventListener("abort", onAbort);
|
|
153
|
-
reader.releaseLock();
|
|
177
|
+
kill("SIGTERM");
|
|
178
|
+
await Promise.race([closed, setTimeout$1(1e3)]);
|
|
179
|
+
kill("SIGKILL");
|
|
154
180
|
}
|
|
181
|
+
child.kill("SIGKILL");
|
|
182
|
+
await Promise.race([closed, setTimeout$1(1e3)]);
|
|
155
183
|
}
|
|
156
184
|
//#endregion
|
|
157
185
|
export { STARTUP_TIMEOUT, serverClosure, waitForServer };
|
package/dist/types.d.ts
CHANGED
|
@@ -77,6 +77,8 @@ interface MessageQueueDescription {
|
|
|
77
77
|
object: string;
|
|
78
78
|
/** Deno unstable feature flags required (e.g., `["kv"]`). */
|
|
79
79
|
denoUnstable?: string[];
|
|
80
|
+
/** Whether this message queue is deprecated but still selectable. */
|
|
81
|
+
deprecated?: boolean;
|
|
80
82
|
/** Environment variables required, keyed by name to default value. */
|
|
81
83
|
env?: Record<string, string>;
|
|
82
84
|
}
|
|
@@ -99,6 +101,8 @@ interface KvStoreDescription {
|
|
|
99
101
|
object: string;
|
|
100
102
|
/** Deno unstable feature flags required (e.g., `["kv"]`). */
|
|
101
103
|
denoUnstable?: string[];
|
|
104
|
+
/** Whether this key-value store is deprecated but still selectable. */
|
|
105
|
+
deprecated?: boolean;
|
|
102
106
|
/** Environment variables required, keyed by name to default value. */
|
|
103
107
|
env?: Record<string, string>;
|
|
104
108
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/init",
|
|
3
|
-
"version": "2.5.0-dev.
|
|
3
|
+
"version": "2.5.0-dev.2541+d39a2437",
|
|
4
4
|
"description": "Project initializer for Fedify",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fedify",
|
|
@@ -57,7 +57,9 @@
|
|
|
57
57
|
"jsonc-parser": "^3.3.1"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
+
"@types/cross-spawn": "^6.0.6",
|
|
60
61
|
"@types/node": "^24.12.2",
|
|
62
|
+
"cross-spawn": "^7.0.6",
|
|
61
63
|
"tsdown": "^0.22.0",
|
|
62
64
|
"typescript": "^6.0.0"
|
|
63
65
|
},
|
|
@@ -66,7 +68,6 @@
|
|
|
66
68
|
"build": "pnpm --filter @fedify/init... run build:self",
|
|
67
69
|
"prepublish": "pnpm build",
|
|
68
70
|
"pretest": "pnpm build",
|
|
69
|
-
"test": "node --test --experimental-transform-types 'src/**/*.test.ts'"
|
|
70
|
-
"test-init": "deno task test-init"
|
|
71
|
+
"test": "node --test --experimental-transform-types 'src/**/*.test.ts'"
|
|
71
72
|
}
|
|
72
73
|
}
|