@fedify/cli 2.0.0-pr.474.1879 → 2.0.0-pr.479.1900

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 (63) hide show
  1. package/deno.json +1 -1
  2. package/dist/deno.js +1 -1
  3. package/dist/imagerenderer.js +1 -1
  4. package/dist/inbox.js +2 -5
  5. package/dist/init/action/configs.js +22 -13
  6. package/dist/init/action/const.js +10 -0
  7. package/dist/init/action/deps.js +26 -28
  8. package/dist/init/action/install.js +11 -13
  9. package/dist/init/action/mod.js +1 -1
  10. package/dist/init/action/notice.js +12 -19
  11. package/dist/init/action/patch.js +33 -27
  12. package/dist/init/action/precommand.js +7 -2
  13. package/dist/init/action/recommend.js +1 -1
  14. package/dist/init/action/set.js +1 -1
  15. package/dist/init/action/templates.js +2 -1
  16. package/dist/init/ask/kv.js +24 -13
  17. package/dist/init/ask/mq.js +26 -13
  18. package/dist/init/command.js +20 -3
  19. package/dist/init/lib.js +20 -13
  20. package/dist/init/mod.js +2 -1
  21. package/dist/init/templates/nitro/.env.test.tpl +1 -0
  22. package/dist/init/templates/nitro/nitro.config.ts.tpl +12 -3
  23. package/dist/init/test/action.js +15 -0
  24. package/dist/init/test/create.js +100 -0
  25. package/dist/init/test/fill.js +26 -0
  26. package/dist/init/test/lookup.js +189 -0
  27. package/dist/init/test/run.js +26 -0
  28. package/dist/init/test/utils.js +17 -0
  29. package/dist/init/webframeworks.js +31 -28
  30. package/dist/mod.js +4 -2
  31. package/dist/nodeinfo.js +6 -6
  32. package/dist/utils.js +75 -8
  33. package/package.json +5 -5
  34. package/src/inbox.tsx +1 -15
  35. package/src/init/action/configs.ts +56 -13
  36. package/src/init/action/const.ts +9 -0
  37. package/src/init/action/deps.ts +98 -30
  38. package/src/init/action/install.ts +17 -52
  39. package/src/init/action/notice.ts +16 -14
  40. package/src/init/action/patch.ts +48 -27
  41. package/src/init/action/precommand.ts +9 -2
  42. package/src/init/action/set.ts +2 -15
  43. package/src/init/action/templates.ts +3 -2
  44. package/src/init/action/utils.ts +1 -1
  45. package/src/init/ask/kv.ts +64 -21
  46. package/src/init/ask/mq.ts +69 -20
  47. package/src/init/command.ts +39 -1
  48. package/src/init/lib.ts +31 -28
  49. package/src/init/mod.ts +2 -1
  50. package/src/init/templates/nitro/.env.test.tpl +1 -0
  51. package/src/init/templates/nitro/nitro.config.ts.tpl +12 -3
  52. package/src/init/test/action.ts +25 -0
  53. package/src/init/test/create.ts +137 -0
  54. package/src/init/test/fill.ts +61 -0
  55. package/src/init/test/lookup.ts +253 -0
  56. package/src/init/test/run.ts +42 -0
  57. package/src/init/test/types.ts +34 -0
  58. package/src/init/test/utils.ts +21 -0
  59. package/src/init/types.ts +4 -3
  60. package/src/init/webframeworks.ts +35 -18
  61. package/src/mod.ts +10 -1
  62. package/src/nodeinfo.ts +2 -1
  63. package/src/utils.ts +128 -10
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { debugOption } from "../globals.js";
5
5
  import { KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK } from "./const.js";
6
- import { argument, choice, command, constant, message, object, option, optionNames, optional } from "@optique/core";
6
+ import { argument, choice, command, constant, message, multiple, object, option, optionNames, optional } from "@optique/core";
7
7
  import { path } from "@optique/run";
8
8
 
9
9
  //#region src/init/command.ts
@@ -11,6 +11,7 @@ const webFramework = optional(option("-w", "--web-framework", choice(WEB_FRAMEWO
11
11
  const packageManager = optional(option("-p", "--package-manager", choice(PACKAGE_MANAGER, { metavar: "PACKAGE_MANAGER" }), { description: message`The package manager to use for installing dependencies.` }));
12
12
  const kvStore = optional(option("-k", "--kv-store", choice(KV_STORE, { metavar: "KV_STORE" }), { description: message`The key-value store to use for caching and some other features.` }));
13
13
  const messageQueue = optional(option("-m", "--message-queue", choice(MESSAGE_QUEUE, { metavar: "MESSAGE_QUEUE" }), { description: message`The message queue to use for background tasks.` }));
14
+ const testMode = option("--test-mode", { description: message`The test mode to use for testing purposes.` });
14
15
  const initCommand = command("init", object("Initialization options", {
15
16
  command: constant("init"),
16
17
  dir: optional(argument(path({ metavar: "DIR" }), { description: message`The project directory to initialize. If a specified directory does not exist, it will be created.` })),
@@ -19,7 +20,8 @@ const initCommand = command("init", object("Initialization options", {
19
20
  kvStore,
20
21
  messageQueue,
21
22
  dryRun: option("-d", "--dry-run", { description: message`Perform a trial run with no changes made.` }),
22
- debugOption
23
+ debugOption,
24
+ testMode
23
25
  }), {
24
26
  brief: message`Initialize a new Fedify project directory.`,
25
27
  description: message`Initialize a new Fedify project directory.
@@ -28,6 +30,21 @@ By default, it initializes the current directory. You can specify a different d
28
30
 
29
31
  Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${optionNames(["-p", "--package-manager"])}, ${optionNames(["-k", "--kv-store"])}, and ${optionNames(["-m", "--message-queue"])}), it will prompt you to select the options interactively.`
30
32
  });
33
+ const testInitCommand = command("test-init", object("Initialization options", {
34
+ command: constant("test-init"),
35
+ webFramework: multiple(webFramework),
36
+ packageManager: multiple(packageManager),
37
+ kvStore: multiple(kvStore),
38
+ messageQueue: multiple(messageQueue),
39
+ hydRun: option("-h", "--hyd-run", { description: message`Test with files creations and installations.` }),
40
+ dryRun: option("-d", "--dry-run", { description: message`Log outputs without creating files.` }),
41
+ debugOption
42
+ }), {
43
+ brief: message`Test an initializing command .`,
44
+ description: message`Test an initializing command on temporary directories.
45
+
46
+ Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${optionNames(["-p", "--package-manager"])}, ${optionNames(["-k", "--kv-store"])}, and ${optionNames(["-m", "--message-queue"])}), it will test all combinations of the options.`
47
+ });
31
48
 
32
49
  //#endregion
33
- export { initCommand };
50
+ export { initCommand, testInitCommand };
package/dist/init/lib.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
 
4
4
  import deno_default from "../deno.js";
5
- import { colors, isNotFoundError, runSubCommand } from "../utils.js";
5
+ import { isNotFoundError, runSubCommand } from "../utils.js";
6
6
  import kv_default from "./json/kv.js";
7
7
  import mq_default from "./json/mq.js";
8
8
  import pm_default from "./json/pm.js";
@@ -10,6 +10,7 @@ import rt_default from "./json/rt.js";
10
10
  import webframeworks_default from "./webframeworks.js";
11
11
  import { dirname, join } from "node:path";
12
12
  import { mkdir, readdir, writeFile } from "node:fs/promises";
13
+ import { commandLine, message } from "@optique/core/message";
13
14
  import process from "node:process";
14
15
  import { getLogger } from "@logtape/logtape";
15
16
  import { entries, evolve, fromEntries, isObject, map, negate, pipe, throwIf, when } from "@fxts/core";
@@ -44,17 +45,17 @@ async function isPackageManagerAvailable(pm) {
44
45
  return false;
45
46
  }
46
47
  const readTemplate = (templatePath) => readFileSync(join(import.meta.dirname, "templates", ...(templatePath + ".tpl").split("/")), "utf8");
47
- const getInstruction = (pm) => `
48
+ const getInstruction = (pm, port) => message`
48
49
  To start the server, run the following command:
49
50
 
50
- ${getDevCommand(pm)}
51
+ ${commandLine(getDevCommand(pm))}
51
52
 
52
53
  Then, try look up an actor from your server:
53
54
 
54
- ${colors.bold(colors.green("fedify lookup http://localhost:8000/users/john"))}
55
+ ${commandLine(`fedify lookup http://localhost:${port}/users/john`)}
55
56
 
56
57
  `;
57
- const getDevCommand = (pm) => colors.bold(colors.green(pm === "deno" ? "deno task dev" : pm === "bun" ? "bun dev" : `${pm} run dev`));
58
+ const getDevCommand = (pm) => pm === "deno" ? "deno task dev" : pm === "bun" ? "bun dev" : `${pm} run dev`;
58
59
  async function isCommandAvailable({ checkCommand, outputPattern }) {
59
60
  try {
60
61
  const { stdout } = await runSubCommand(checkCommand, { stdio: [
@@ -91,18 +92,20 @@ const isDirectoryEmpty = async (path$1) => {
91
92
  return true;
92
93
  }
93
94
  };
95
+ /**
96
+ * Converts a package manager to its corresponding runtime.
97
+ * @param pm - The package manager (deno, bun, npm, yarn, pnpm)
98
+ * @returns The runtime name (deno, bun, or node)
99
+ */
100
+ const packageManagerToRuntime = (pm) => pm === "deno" ? "deno" : pm === "bun" ? "bun" : "node";
94
101
  const getNextInitCommand = (pm) => [
95
102
  ...createNextAppCommand(pm),
96
103
  ".",
97
- "--ts",
98
- "--app",
99
- "--biome",
100
- "--skip-install"
104
+ "--yes"
101
105
  ];
102
106
  const createNextAppCommand = (pm) => pm === "deno" ? [
103
107
  "deno",
104
- "run",
105
- "-A",
108
+ "-Ar",
106
109
  "npm:create-next-app@latest"
107
110
  ] : pm === "bun" ? [
108
111
  "bun",
@@ -117,13 +120,17 @@ const getNitroInitCommand = (pm) => [
117
120
  ...createNitroAppCommand(pm),
118
121
  pm === "deno" ? "npm:giget@latest" : "giget@latest",
119
122
  "nitro",
120
- "."
123
+ ".",
124
+ "&&",
125
+ "rm",
126
+ "nitro.config.ts"
121
127
  ];
122
128
  const createNitroAppCommand = (pm) => pm === "deno" ? [
123
129
  "deno",
124
130
  "run",
125
131
  "-A"
126
132
  ] : pm === "bun" ? ["bunx"] : pm === "npm" ? ["npx"] : [pm, "dlx"];
133
+ const isTest = ({ testMode }) => testMode;
127
134
 
128
135
  //#endregion
129
- export { PACKAGE_VERSION, createFile, getInstallUrl, getInstruction, getLabel, getNextInitCommand, getNitroInitCommand, isDirectoryEmpty, isPackageManagerAvailable, kvStores, logger, messageQueues, readTemplate, throwUnlessNotExists };
136
+ export { PACKAGE_VERSION, createFile, getDevCommand, getInstallUrl, getInstruction, getLabel, getNextInitCommand, getNitroInitCommand, isDirectoryEmpty, isPackageManagerAvailable, isTest, kvStores, logger, messageQueues, packageManagerToRuntime, readTemplate, throwUnlessNotExists };
package/dist/init/mod.js CHANGED
@@ -2,4 +2,5 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
 
4
4
  import action_default from "./action/mod.js";
5
- import { initCommand } from "./command.js";
5
+ import { initCommand, testInitCommand } from "./command.js";
6
+ import action_default$1 from "./test/action.js";
@@ -0,0 +1 @@
1
+ HOST=127.0.0.1
@@ -1,5 +1,14 @@
1
- // https://nitro.unjs.io/config
1
+ import { defineNitroConfig } from "nitropack/config"
2
+
3
+ // https://nitro.build/config
2
4
  export default defineNitroConfig({
5
+ errorHandler: "~/error",
6
+ esbuild: {
7
+ options: {
8
+ target: "esnext",
9
+ },
10
+ },
11
+ compatibilityDate: "latest",
3
12
  srcDir: "server",
4
- errorHandler: "~/error"
5
- });
13
+ imports: false
14
+ });
@@ -0,0 +1,15 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+
4
+ import { set } from "../../utils.js";
5
+ import { fillEmptyOptions } from "./fill.js";
6
+ import { isDryRun, isHydRun, runTests } from "./run.js";
7
+ import { emptyTestDir, genRunId, genTestDirPrefix, logTestDir } from "./utils.js";
8
+ import { pipe, tap, when } from "@fxts/core";
9
+
10
+ //#region src/init/test/action.ts
11
+ const runTestInit = (options) => pipe(options, set("runId", genRunId), set("testDirPrefix", genTestDirPrefix), tap(emptyTestDir), fillEmptyOptions, tap(when(isHydRun, runTests(false))), tap(when(isDryRun, runTests(true))), tap(logTestDir));
12
+ var action_default = runTestInit;
13
+
14
+ //#endregion
15
+ export { action_default as default };
@@ -0,0 +1,100 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+
4
+ import { CommandError, printErrorMessage, printMessage, product, runSubCommand } from "../../utils.js";
5
+ import pm_default from "../json/pm.js";
6
+ import webframeworks_default from "../webframeworks.js";
7
+ import { kvStores, messageQueues } from "../lib.js";
8
+ import { join, sep } from "node:path";
9
+ import { appendFile, mkdir } from "node:fs/promises";
10
+ import { values } from "@optique/core";
11
+ import process from "node:process";
12
+ import { filter, isEmpty, pipe, toArray } from "@fxts/core";
13
+
14
+ //#region src/init/test/create.ts
15
+ const BANNED_PMS = ["bun", "yarn"];
16
+ const createTestApp = (testDirPrefix, dry) => async (options) => {
17
+ const testDir = join(testDirPrefix, ...options);
18
+ const vals = values(testDir.split(sep).slice(-4));
19
+ try {
20
+ const result = await runSubCommand(toArray(genInitCommand(testDir, dry, options)), {
21
+ cwd: join(import.meta.dirname, "../../.."),
22
+ stdio: [
23
+ "ignore",
24
+ "pipe",
25
+ "pipe"
26
+ ]
27
+ });
28
+ await saveOutputs(testDir, result);
29
+ printMessage` Pass: ${vals}`;
30
+ return testDir;
31
+ } catch (error) {
32
+ if (error instanceof CommandError) await saveOutputs(testDir, {
33
+ stdout: error.stdout,
34
+ stderr: error.stderr
35
+ });
36
+ else {
37
+ const errorMessage = error instanceof Error ? error.message : String(error);
38
+ await saveOutputs(testDir, {
39
+ stdout: "",
40
+ stderr: errorMessage
41
+ });
42
+ }
43
+ printMessage` Fail: ${vals}`;
44
+ return "";
45
+ }
46
+ };
47
+ var create_default = createTestApp;
48
+ function* genInitCommand(testDir, dry, [webFramework, packageManager, kvStore, messageQueue]) {
49
+ yield "deno";
50
+ yield "run";
51
+ yield "-A";
52
+ yield "src/mod.ts";
53
+ yield "init";
54
+ yield testDir;
55
+ yield "-w";
56
+ yield webFramework;
57
+ yield "-p";
58
+ yield packageManager;
59
+ yield "-k";
60
+ yield kvStore;
61
+ yield "-m";
62
+ yield messageQueue;
63
+ yield "--test-mode";
64
+ if (dry) yield "-d";
65
+ }
66
+ const generateTestCases = ({ webFramework, packageManager, kvStore, messageQueue }) => {
67
+ const pms = filterPackageManager(packageManager);
68
+ exitIfCasesEmpty([
69
+ webFramework,
70
+ pms,
71
+ kvStore,
72
+ messageQueue
73
+ ]);
74
+ return product(webFramework, pms, kvStore, messageQueue);
75
+ };
76
+ const filterPackageManager = (pm) => pipe(pm, filter((pm$1) => BANNED_PMS.includes(pm$1) ? printErrorMessage`${pm_default[pm$1]["label"]} is not \
77
+ supported in test mode yet because ${pm_default[pm$1]["label"]} don't \
78
+ support local file dependencies properly.` : true), toArray);
79
+ const exitIfCasesEmpty = (cases) => {
80
+ if (cases.some(isEmpty)) {
81
+ printErrorMessage`No test cases to run. Exiting.`;
82
+ process.exit(1);
83
+ }
84
+ };
85
+ const saveOutputs = async (dirPath, { stdout, stderr }) => {
86
+ await mkdir(dirPath, { recursive: true });
87
+ if (stdout) await appendFile(join(dirPath, "out.txt"), stdout + "\n", "utf8");
88
+ if (stderr) await appendFile(join(dirPath, "err.txt"), stderr + "\n", "utf8");
89
+ };
90
+ function filterOptions(options) {
91
+ const [wf, pm, kv, mq] = options;
92
+ return [
93
+ webframeworks_default[wf].packageManagers,
94
+ kvStores[kv].packageManagers,
95
+ messageQueues[mq].packageManagers
96
+ ].every((pms) => pms.includes(pm));
97
+ }
98
+
99
+ //#endregion
100
+ export { create_default as default, filterOptions, generateTestCases };
@@ -0,0 +1,26 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+
4
+ import { KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK } from "../const.js";
5
+ import { isEmpty, pipe } from "@fxts/core";
6
+
7
+ //#region src/init/test/fill.ts
8
+ const fillEmptyOptions = (options) => pipe(options, fillWebFramework, fillPackageManager, fillKVStore, fillMessageQueue, fillRunMode);
9
+ const fillOption = (key, allValues) => (options) => ({
10
+ ...options,
11
+ [key]: isEmpty(options[key]) ? allValues : options[key].filter((i) => allValues.includes(i))
12
+ });
13
+ const fillWebFramework = fillOption("webFramework", WEB_FRAMEWORK);
14
+ const fillPackageManager = fillOption("packageManager", PACKAGE_MANAGER);
15
+ const fillKVStore = fillOption("kvStore", KV_STORE);
16
+ const fillMessageQueue = fillOption("messageQueue", MESSAGE_QUEUE);
17
+ const fillRunMode = (options) => ({
18
+ ...options,
19
+ ...options.hydRun || options.dryRun ? {} : {
20
+ hydRun: true,
21
+ dryRun: true
22
+ }
23
+ });
24
+
25
+ //#endregion
26
+ export { fillEmptyOptions };
@@ -0,0 +1,189 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+
4
+ import { printErrorMessage, printMessage, runSubCommand } from "../../utils.js";
5
+ import webframeworks_default from "../webframeworks.js";
6
+ import { getDevCommand } from "../lib.js";
7
+ import { join, sep } from "node:path";
8
+ import { values } from "@optique/core";
9
+ import process from "node:process";
10
+ import { spawn } from "node:child_process";
11
+ import { createWriteStream } from "node:fs";
12
+
13
+ //#region src/init/test/lookup.ts
14
+ const HANDLE = "john";
15
+ const STARTUP_TIMEOUT = 3e4;
16
+ const CWD = process.cwd();
17
+ const BANNED_WFS = ["next"];
18
+ /**
19
+ * Run servers for all generated apps and test them with the lookup command.
20
+ *
21
+ * @param dirs - Array of paths to generated app directories
22
+ */
23
+ async function runServerAndReadUser(dirs) {
24
+ const valid = dirs.filter(Boolean);
25
+ if (valid.length === 0) {
26
+ printErrorMessage`\nNo directories to lookup test.`;
27
+ return;
28
+ }
29
+ const filtered = filterWebFrameworks(valid);
30
+ printMessage`\nLookup Test start for ${String(filtered.length)} app(s)!`;
31
+ const results = await Array.fromAsync(filtered, testApp);
32
+ const successCount = results.filter(Boolean).length;
33
+ const failCount = results.length - successCount;
34
+ printMessage`Lookup Test Results:
35
+ Total: ${String(results.length)}
36
+ Passed: ${String(successCount)}
37
+ Failed: ${String(failCount)}\n\n`;
38
+ }
39
+ function filterWebFrameworks(dirs) {
40
+ const wfs = new Set(dirs.map((dir) => dir.split(sep).slice(-4, -3)[0]));
41
+ const hasBanned = BANNED_WFS.filter((wf) => wfs.has(wf));
42
+ if (!hasBanned) return dirs;
43
+ const bannedLabels = hasBanned.map((wf) => webframeworks_default[wf]["label"]);
44
+ printErrorMessage`\n${values(bannedLabels)} is not supported in test mode yet.`;
45
+ return dirs.filter((dir) => !BANNED_WFS.includes(dir.split(sep).slice(-4, -3)[0]));
46
+ }
47
+ /**
48
+ * Run the dev server and test with lookup command.
49
+ */
50
+ async function testApp(dir) {
51
+ const [wf, pm, kv, mq] = dir.split(sep).slice(-4);
52
+ printMessage` Testing ${values([
53
+ wf,
54
+ pm,
55
+ kv,
56
+ mq
57
+ ])}...`;
58
+ const result = await serverClosure(dir, getDevCommand(pm), sendLookup);
59
+ printMessage` Lookup ${result ? "successful" : "failed"} for ${values([
60
+ wf,
61
+ pm,
62
+ kv,
63
+ mq
64
+ ])}!`;
65
+ if (!result) printMessage` Check out these files for more details:
66
+ ${join(dir, "out.txt")}
67
+ ${join(dir, "err.txt")}\n`;
68
+ printMessage`\n`;
69
+ return result;
70
+ }
71
+ const sendLookup = async (port) => {
72
+ const serverUrl = `http://localhost:${port}`;
73
+ const lookupTarget = `${serverUrl}/users/${HANDLE}`;
74
+ printMessage` Waiting for server to start at ${serverUrl}...`;
75
+ const isReady = await waitForServer(serverUrl, STARTUP_TIMEOUT);
76
+ if (!isReady) {
77
+ printErrorMessage`Server did not start within \
78
+ ${String(STARTUP_TIMEOUT)}ms`;
79
+ return false;
80
+ }
81
+ printMessage` Server is ready. Running lookup command...`;
82
+ try {
83
+ await runSubCommand([
84
+ "deno",
85
+ "task",
86
+ "cli",
87
+ "lookup",
88
+ lookupTarget
89
+ ], { cwd: CWD });
90
+ return true;
91
+ } catch (error) {
92
+ if (error instanceof Error) printErrorMessage`${error.message}`;
93
+ }
94
+ return false;
95
+ };
96
+ /**
97
+ * Wait for the server to be ready by checking if it responds to requests.
98
+ */
99
+ async function waitForServer(url, timeout) {
100
+ const startTime = Date.now();
101
+ while (Date.now() - startTime < timeout) {
102
+ try {
103
+ const response = await fetch(url, { signal: AbortSignal.timeout(1e3) });
104
+ if (response.ok) return true;
105
+ } catch {}
106
+ await new Promise((resolve) => setTimeout(resolve, 500));
107
+ }
108
+ return false;
109
+ }
110
+ async function serverClosure(dir, cmd, callback) {
111
+ const devCommand = cmd.split(" ");
112
+ const serverProcess = spawn(devCommand[0], devCommand.slice(1), {
113
+ cwd: dir,
114
+ stdio: [
115
+ "ignore",
116
+ "pipe",
117
+ "pipe"
118
+ ],
119
+ detached: true
120
+ });
121
+ const stdout = createWriteStream(join(dir, "out.txt"), { flags: "a" });
122
+ const stderr = createWriteStream(join(dir, "err.txt"), { flags: "a" });
123
+ serverProcess.stdout?.pipe(stdout);
124
+ serverProcess.stderr?.pipe(stderr);
125
+ try {
126
+ const port = await determinePort(serverProcess);
127
+ return await callback(port);
128
+ } finally {
129
+ try {
130
+ process.kill(-serverProcess.pid, "SIGKILL");
131
+ } catch {
132
+ serverProcess.kill("SIGKILL");
133
+ stdout.end();
134
+ stderr.end();
135
+ }
136
+ }
137
+ }
138
+ function determinePort(server) {
139
+ return new Promise((resolve, reject) => {
140
+ const timeout = setTimeout(() => {
141
+ reject(/* @__PURE__ */ new Error("Timeout: Could not determine port from server output"));
142
+ }, STARTUP_TIMEOUT);
143
+ let stdoutData = "";
144
+ let stderrData = "";
145
+ const portPatterns = [
146
+ /listening on.*:(\d+)/i,
147
+ /server.*:(\d+)/i,
148
+ /port\s*:?\s*(\d+)/i,
149
+ /localhost:(\d+)/i,
150
+ /0\.0\.0\.0:(\d+)/i,
151
+ /127\.0\.0\.1:(\d+)/i,
152
+ /https?:\/\/[^:]+:(\d+)/i
153
+ ];
154
+ const checkForPort = (data) => {
155
+ for (const pattern of portPatterns) {
156
+ const match = data.match(pattern);
157
+ if (match && match[1]) {
158
+ const port = Number.parseInt(match[1], 10);
159
+ if (port > 0 && port < 65536) {
160
+ clearTimeout(timeout);
161
+ return port;
162
+ }
163
+ }
164
+ }
165
+ return null;
166
+ };
167
+ server.stdout.on("data", (chunk) => {
168
+ stdoutData += chunk.toString();
169
+ const port = checkForPort(stdoutData);
170
+ if (port) resolve(port);
171
+ });
172
+ server.stderr.on("data", (chunk) => {
173
+ stderrData += chunk.toString();
174
+ const port = checkForPort(stderrData);
175
+ if (port) resolve(port);
176
+ });
177
+ server.on("error", (err) => {
178
+ clearTimeout(timeout);
179
+ reject(err);
180
+ });
181
+ server.on("exit", (code) => {
182
+ clearTimeout(timeout);
183
+ reject(/* @__PURE__ */ new Error(`Server exited with code ${code} before port could be determined`));
184
+ });
185
+ });
186
+ }
187
+
188
+ //#endregion
189
+ export { runServerAndReadUser };
@@ -0,0 +1,26 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+
4
+ import { printMessage } from "../../utils.js";
5
+ import create_default, { filterOptions, generateTestCases } from "./create.js";
6
+ import { runServerAndReadUser } from "./lookup.js";
7
+ import { join } from "node:path";
8
+ import { optionNames } from "@optique/core";
9
+ import { filter, map as map$1, pipe, tap } from "@fxts/core";
10
+
11
+ //#region src/init/test/run.ts
12
+ const isDryRun = ({ dryRun }) => dryRun;
13
+ const isHydRun = ({ hydRun }) => hydRun;
14
+ const runTests = (dry) => ({ testDirPrefix, dryRun, hydRun,...options }) => pipe(options, printStartMessage, generateTestCases, filter(filterOptions), map$1(create_default(join(testDirPrefix, getMid(dryRun, hydRun, dry)), dry)), Array.fromAsync, runServerAndReadUser);
15
+ const printStartMessage = tap(() => printMessage`\n
16
+ Init Test start!
17
+ Options: ${optionNames([
18
+ "Web Framework",
19
+ "Package Manager",
20
+ "KV Store",
21
+ "Message Queue"
22
+ ])}`);
23
+ const getMid = (dryRun, hydRun, dry) => dryRun === hydRun ? dry ? "dry" : "hyd" : "";
24
+
25
+ //#endregion
26
+ export { isDryRun, isHydRun, runTests };
@@ -0,0 +1,17 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+
4
+ import { printMessage } from "../../utils.js";
5
+ import { join } from "node:path";
6
+ import { tmpdir } from "node:os";
7
+
8
+ //#region src/init/test/utils.ts
9
+ const genRunId = () => `${Date.now()}-${Math.random().toString(36).slice(2)}`;
10
+ const genTestDirPrefix = ({ runId }) => join(tmpdir(), "fedify-init", runId);
11
+ const emptyTestDir = ({ testDirPrefix }) => Deno.remove(testDirPrefix, { recursive: true }).catch(() => {});
12
+ const logTestDir = ({ runId, testDirPrefix }) => printMessage`Test run completed.
13
+ Run ID: ${runId}
14
+ Path: ${testDirPrefix}`;
15
+
16
+ //#endregion
17
+ export { emptyTestDir, genRunId, genTestDirPrefix, logTestDir };
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { replace } from "../utils.js";
5
5
  import { PACKAGE_MANAGER } from "./const.js";
6
- import { PACKAGE_VERSION, getInstruction, getNextInitCommand, getNitroInitCommand, readTemplate } from "./lib.js";
6
+ import { PACKAGE_VERSION, getInstruction, getNextInitCommand, getNitroInitCommand, packageManagerToRuntime, readTemplate } from "./lib.js";
7
7
  import { pipe } from "@fxts/core";
8
8
 
9
9
  //#region src/init/webframeworks.ts
@@ -11,27 +11,30 @@ const webFrameworks = {
11
11
  hono: {
12
12
  label: "Hono",
13
13
  packageManagers: PACKAGE_MANAGER,
14
- init: (projectName, pm) => ({
14
+ init: ({ projectName, packageManager: pm }) => ({
15
15
  dependencies: pm === "deno" ? {
16
16
  "@std/dotenv": "^0.225.2",
17
17
  "@hono/hono": "^4.5.0",
18
- "@hongminhee/x-forwarded-fetch": "^0.2.0"
18
+ "@hongminhee/x-forwarded-fetch": "^0.2.0",
19
+ "@fedify/hono": PACKAGE_VERSION
19
20
  } : pm === "bun" ? {
20
21
  hono: "^4.5.0",
21
- "x-forwarded-fetch": "^0.2.0"
22
+ "x-forwarded-fetch": "^0.2.0",
23
+ "@fedify/hono": PACKAGE_VERSION
22
24
  } : {
23
25
  "@dotenvx/dotenvx": "^1.14.1",
24
26
  hono: "^4.5.0",
25
27
  "@hono/node-server": "^1.12.0",
26
28
  tsx: "^4.17.0",
27
- "x-forwarded-fetch": "^0.2.0"
29
+ "x-forwarded-fetch": "^0.2.0",
30
+ "@fedify/hono": PACKAGE_VERSION
28
31
  },
29
32
  devDependencies: pm === "bun" ? { "@types/bun": "^1.1.6" } : {},
30
33
  federationFile: "src/federation.ts",
31
34
  loggingFile: "src/logging.ts",
32
35
  files: {
33
- "src/app.tsx": pipe("hono/app.tsx", readTemplate, replace(/\/\* hono \*\//, pm === "deno" ? "@hono/hono" : "hono")).replace(/\/\* logger \*\//, projectName),
34
- "src/index.ts": readTemplate(`hono/index/${pm}.ts`)
36
+ "src/app.tsx": pipe("hono/app.tsx", readTemplate, replace(/\/\* hono \*\//, pm === "deno" ? "@hono/hono" : "hono"), replace(/\/\* logger \*\//, projectName)),
37
+ "src/index.ts": readTemplate(`hono/index/${packageManagerToRuntime(pm)}.ts`)
35
38
  },
36
39
  compilerOptions: pm === "deno" ? void 0 : {
37
40
  "lib": ["ESNext", "DOM"],
@@ -49,20 +52,16 @@ const webFrameworks = {
49
52
  "dev": pm === "deno" ? "deno run -A --watch ./src/index.ts" : pm === "bun" ? "bun run --hot ./src/index.ts" : "dotenvx run -- tsx watch ./src/index.ts",
50
53
  "prod": pm === "deno" ? "deno run -A ./src/index.ts" : pm === "bun" ? "bun run ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts"
51
54
  },
52
- instruction: getInstruction(pm)
53
- })
55
+ instruction: getInstruction(pm, 8e3)
56
+ }),
57
+ defaultPort: 8e3
54
58
  },
55
59
  express: {
56
60
  label: "Express",
57
- packageManagers: [
58
- "bun",
59
- "npm",
60
- "yarn",
61
- "pnpm"
62
- ],
63
- init: (projectName, pm) => ({
61
+ packageManagers: PACKAGE_MANAGER,
62
+ init: ({ projectName, packageManager: pm }) => ({
64
63
  dependencies: {
65
- express: "^4.19.2",
64
+ "npm:express": "^4.19.2",
66
65
  "@fedify/express": PACKAGE_VERSION,
67
66
  ...pm !== "deno" && pm !== "bun" ? {
68
67
  "@dotenvx/dotenvx": "^1.14.1",
@@ -90,16 +89,17 @@ const webFrameworks = {
90
89
  "strict": true
91
90
  },
92
91
  tasks: {
93
- "dev": pm === "bun" ? "bun run --hot ./src/index.ts" : "dotenvx run -- tsx watch ./src/index.ts",
94
- "prod": pm === "bun" ? "bun run ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts"
92
+ "dev": pm === "bun" ? "bun run --hot ./src/index.ts" : pm === "deno" ? "deno run --allow-net --allow-env --allow-sys --watch ./src/index.ts" : "dotenvx run -- tsx watch ./src/index.ts",
93
+ "prod": pm === "bun" ? "bun run ./src/index.ts" : pm === "deno" ? "deno run --allow-net --allow-env --allow-sys ./src/index.ts" : "dotenvx run -- node --import tsx ./src/index.ts"
95
94
  },
96
- instruction: getInstruction(pm)
97
- })
95
+ instruction: getInstruction(pm, 8e3)
96
+ }),
97
+ defaultPort: 8e3
98
98
  },
99
99
  nitro: {
100
100
  label: "Nitro",
101
101
  packageManagers: PACKAGE_MANAGER,
102
- init: (_, pm) => ({
102
+ init: ({ packageManager: pm, testMode }) => ({
103
103
  command: getNitroInitCommand(pm),
104
104
  dependencies: { "@fedify/h3": PACKAGE_VERSION },
105
105
  federationFile: "server/federation.ts",
@@ -107,15 +107,17 @@ const webFrameworks = {
107
107
  files: {
108
108
  "server/middleware/federation.ts": readTemplate("nitro/server/middleware/federation.ts"),
109
109
  "server/error.ts": readTemplate("nitro/server/error.ts"),
110
- "nitro.config.ts": readTemplate("nitro/nitro.config.ts")
110
+ "nitro.config.ts": readTemplate("nitro/nitro.config.ts"),
111
+ ...testMode ? { ".env": readTemplate("nitro/.env.test") } : {}
111
112
  },
112
- instruction: getInstruction(pm)
113
- })
113
+ instruction: getInstruction(pm, 3e3)
114
+ }),
115
+ defaultPort: 3e3
114
116
  },
115
117
  next: {
116
118
  label: "Next.js",
117
119
  packageManagers: PACKAGE_MANAGER,
118
- init: (_, pm) => ({
120
+ init: ({ packageManager: pm }) => ({
119
121
  label: "Next.js",
120
122
  command: getNextInitCommand(pm),
121
123
  dependencies: { "@fedify/next": PACKAGE_VERSION },
@@ -123,8 +125,9 @@ const webFrameworks = {
123
125
  federationFile: "federation/index.ts",
124
126
  loggingFile: "logging.ts",
125
127
  files: { "middleware.ts": readTemplate("next/middleware.ts") },
126
- instruction: getInstruction(pm)
127
- })
128
+ instruction: getInstruction(pm, 3e3)
129
+ }),
130
+ defaultPort: 3e3
128
131
  }
129
132
  };
130
133
  var webframeworks_default = webFrameworks;