@fedify/init 2.0.0-dev.0
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/LICENSE +20 -0
- package/README.md +93 -0
- package/dist/action/configs.mjs +89 -0
- package/dist/action/const.mjs +7 -0
- package/dist/action/deps.mjs +50 -0
- package/dist/action/dir.mjs +13 -0
- package/dist/action/env.mjs +10 -0
- package/dist/action/install.mjs +17 -0
- package/dist/action/mod.d.mts +23 -0
- package/dist/action/mod.mjs +38 -0
- package/dist/action/notice.mjs +52 -0
- package/dist/action/patch.mjs +143 -0
- package/dist/action/precommand.mjs +25 -0
- package/dist/action/recommend.mjs +21 -0
- package/dist/action/set.mjs +28 -0
- package/dist/action/templates.mjs +55 -0
- package/dist/action/utils.mjs +47 -0
- package/dist/ask/dir.mjs +79 -0
- package/dist/ask/kv.mjs +41 -0
- package/dist/ask/mod.mjs +13 -0
- package/dist/ask/mq.mjs +43 -0
- package/dist/ask/pm.mjs +49 -0
- package/dist/ask/wf.mjs +26 -0
- package/dist/command.d.mts +41 -0
- package/dist/command.mjs +44 -0
- package/dist/const.d.mts +4 -0
- package/dist/const.mjs +34 -0
- package/dist/deno.mjs +5 -0
- package/dist/json/biome.json +14 -0
- package/dist/json/biome.mjs +14 -0
- package/dist/json/db-to-check.json +17 -0
- package/dist/json/db-to-check.mjs +21 -0
- package/dist/json/kv.json +39 -0
- package/dist/json/kv.mjs +47 -0
- package/dist/json/mq.json +95 -0
- package/dist/json/mq.mjs +65 -0
- package/dist/json/pm.json +47 -0
- package/dist/json/pm.mjs +36 -0
- package/dist/json/rt.json +42 -0
- package/dist/json/rt.mjs +31 -0
- package/dist/json/vscode-settings-for-deno.json +43 -0
- package/dist/json/vscode-settings-for-deno.mjs +39 -0
- package/dist/json/vscode-settings.json +41 -0
- package/dist/json/vscode-settings.mjs +37 -0
- package/dist/lib.mjs +129 -0
- package/dist/mod.d.mts +3 -0
- package/dist/mod.mjs +4 -0
- package/dist/templates/defaults/eslint.config.ts.tpl +3 -0
- package/dist/templates/defaults/federation.ts.tpl +24 -0
- package/dist/templates/defaults/logging.ts.tpl +23 -0
- package/dist/templates/elysia/index/bun.ts.tpl +13 -0
- package/dist/templates/elysia/index/deno.ts.tpl +19 -0
- package/dist/templates/elysia/index/node.ts.tpl +14 -0
- package/dist/templates/express/app.ts.tpl +16 -0
- package/dist/templates/express/index.ts.tpl +6 -0
- package/dist/templates/hono/app.tsx.tpl +14 -0
- package/dist/templates/hono/index/bun.ts.tpl +10 -0
- package/dist/templates/hono/index/deno.ts.tpl +13 -0
- package/dist/templates/hono/index/node.ts.tpl +14 -0
- package/dist/templates/next/middleware.ts.tpl +45 -0
- package/dist/templates/nitro/.env.test.tpl +1 -0
- package/dist/templates/nitro/nitro.config.ts.tpl +14 -0
- package/dist/templates/nitro/server/error.ts.tpl +3 -0
- package/dist/templates/nitro/server/middleware/federation.ts.tpl +8 -0
- package/dist/test/action.mjs +15 -0
- package/dist/test/create.mjs +92 -0
- package/dist/test/db.mjs +42 -0
- package/dist/test/fill.mjs +29 -0
- package/dist/test/lookup.mjs +183 -0
- package/dist/test/mod.d.mts +1 -0
- package/dist/test/mod.mjs +16 -0
- package/dist/test/run.mjs +22 -0
- package/dist/test/utils.mjs +15 -0
- package/dist/types.d.mts +50 -0
- package/dist/utils.d.mts +9 -0
- package/dist/utils.mjs +102 -0
- package/dist/webframeworks.mjs +220 -0
- package/package.json +66 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { fedify } from "@fedify/elysia";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
import federation from "./federation.ts";
|
|
4
|
+
import "./logging.ts";
|
|
5
|
+
|
|
6
|
+
const app = new Elysia();
|
|
7
|
+
|
|
8
|
+
app
|
|
9
|
+
.use(fedify(federation, () => undefined))
|
|
10
|
+
.get("/", () => "Hello, Fedify!")
|
|
11
|
+
.listen(3000, () => {
|
|
12
|
+
console.log("Server started at http://localhost:3000");
|
|
13
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { fedify } from "@fedify/elysia";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
import federation from "./federation.ts";
|
|
4
|
+
import "./logging.ts";
|
|
5
|
+
|
|
6
|
+
const app = new Elysia();
|
|
7
|
+
|
|
8
|
+
app
|
|
9
|
+
.use(fedify(federation, () => undefined))
|
|
10
|
+
.get("/", () => "Hello, Fedify!")
|
|
11
|
+
|
|
12
|
+
Deno.serve(
|
|
13
|
+
{
|
|
14
|
+
port: 3000,
|
|
15
|
+
onListen: ({ port, hostname }) =>
|
|
16
|
+
console.log("Server started at http://" + hostname + ":" + port),
|
|
17
|
+
},
|
|
18
|
+
app.fetch,
|
|
19
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { fedify } from "@fedify/elysia";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
import federation from "./federation.ts";
|
|
4
|
+
import "./logging.ts";
|
|
5
|
+
import { node } from '@elysiajs/node'
|
|
6
|
+
|
|
7
|
+
const app = new Elysia({ adapter: node() });
|
|
8
|
+
|
|
9
|
+
app
|
|
10
|
+
.use(fedify(federation, () => undefined))
|
|
11
|
+
.get("/", () => "Hello, Fedify!")
|
|
12
|
+
.listen(3000, () => {
|
|
13
|
+
console.log("Server started at http://localhost:3000");
|
|
14
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { integrateFederation } from "@fedify/express";
|
|
2
|
+
import { getLogger } from "@logtape/logtape";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import federation from "./federation.ts";
|
|
5
|
+
|
|
6
|
+
const logger = getLogger("/* logger */");
|
|
7
|
+
|
|
8
|
+
export const app = express();
|
|
9
|
+
|
|
10
|
+
app.set("trust proxy", true);
|
|
11
|
+
|
|
12
|
+
app.use(integrateFederation(federation, () => undefined));
|
|
13
|
+
|
|
14
|
+
app.get("/", (_, res) => res.send("Hello, Fedify!"));
|
|
15
|
+
|
|
16
|
+
export default app;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @ts-nocheck this file is just a template
|
|
2
|
+
import { Hono } from "/* hono */";
|
|
3
|
+
import { federation } from "@fedify/hono";
|
|
4
|
+
import { getLogger } from "@logtape/logtape";
|
|
5
|
+
import fedi from "./federation.ts";
|
|
6
|
+
|
|
7
|
+
const logger = getLogger("/* logger */");
|
|
8
|
+
|
|
9
|
+
const app = new Hono();
|
|
10
|
+
app.use(federation(fedi, () => undefined));
|
|
11
|
+
|
|
12
|
+
app.get("/", (c) => c.text("Hello, Fedify!"));
|
|
13
|
+
|
|
14
|
+
export default app;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { behindProxy } from "@hongminhee/x-forwarded-fetch";
|
|
2
|
+
import "@std/dotenv/load";
|
|
3
|
+
import app from "./app.tsx";
|
|
4
|
+
import "./logging.ts";
|
|
5
|
+
|
|
6
|
+
Deno.serve(
|
|
7
|
+
{
|
|
8
|
+
port: 8000,
|
|
9
|
+
onListen: ({ port, hostname }) =>
|
|
10
|
+
console.log("Server started at http://" + hostname + ":" + port),
|
|
11
|
+
},
|
|
12
|
+
behindProxy(app.fetch.bind(app)),
|
|
13
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @ts-nocheck this file is just a template
|
|
2
|
+
import { serve } from "@hono/node-server";
|
|
3
|
+
import { behindProxy } from "x-forwarded-fetch";
|
|
4
|
+
import app from "./app.tsx";
|
|
5
|
+
import "./logging.ts";
|
|
6
|
+
|
|
7
|
+
serve(
|
|
8
|
+
{
|
|
9
|
+
port: 8000,
|
|
10
|
+
fetch: behindProxy(app.fetch.bind(app)),
|
|
11
|
+
},
|
|
12
|
+
(info) =>
|
|
13
|
+
console.log("Server started at http://" + info.address + ":" + info.port),
|
|
14
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { fedifyWith } from "@fedify/next";
|
|
2
|
+
import federation from "./federation";
|
|
3
|
+
|
|
4
|
+
export default fedifyWith(federation)(
|
|
5
|
+
/*
|
|
6
|
+
function (request: Request) {
|
|
7
|
+
// If you need to handle other requests besides federation
|
|
8
|
+
// requests in middleware, you can do it here.
|
|
9
|
+
// If you handle only federation requests in middleware,
|
|
10
|
+
// you don't need this function.
|
|
11
|
+
return NextResponse.next();
|
|
12
|
+
},
|
|
13
|
+
*/
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// This config needs because middleware process only requests with the
|
|
17
|
+
// "Accept" header matching the federation accept regex.
|
|
18
|
+
// More details: https://nextjs.org/docs/app/api-reference/file-conventions/middleware#config-object-optional
|
|
19
|
+
export const config = {
|
|
20
|
+
runtime: "nodejs",
|
|
21
|
+
matcher: [
|
|
22
|
+
{
|
|
23
|
+
source: "/:path*",
|
|
24
|
+
has: [
|
|
25
|
+
{
|
|
26
|
+
type: "header",
|
|
27
|
+
key: "Accept",
|
|
28
|
+
value: ".*application\\/((jrd|activity|ld)\\+json|xrd\\+xml).*",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
source: "/:path*",
|
|
34
|
+
has: [
|
|
35
|
+
{
|
|
36
|
+
type: "header",
|
|
37
|
+
key: "content-type",
|
|
38
|
+
value: ".*application\\/((jrd|activity|ld)\\+json|xrd\\+xml).*",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{ source: "/.well-known/nodeinfo" },
|
|
43
|
+
{ source: "/.well-known/x-nodeinfo2" },
|
|
44
|
+
],
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
HOST=127.0.0.1
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineNitroConfig } from "nitropack/config"
|
|
2
|
+
|
|
3
|
+
// https://nitro.build/config
|
|
4
|
+
export default defineNitroConfig({
|
|
5
|
+
errorHandler: "~/error",
|
|
6
|
+
esbuild: {
|
|
7
|
+
options: {
|
|
8
|
+
target: "esnext",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
compatibilityDate: "latest",
|
|
12
|
+
srcDir: "server",
|
|
13
|
+
imports: false
|
|
14
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { set } from "../utils.mjs";
|
|
2
|
+
import { checkRequiredDbs } from "./db.mjs";
|
|
3
|
+
import { fillEmptyOptions } from "./fill.mjs";
|
|
4
|
+
import run_default from "./run.mjs";
|
|
5
|
+
import { emptyTestDir, genRunId, genTestDirPrefix, logTestDir } from "./utils.mjs";
|
|
6
|
+
import { pipe, tap, when } from "@fxts/core";
|
|
7
|
+
|
|
8
|
+
//#region src/test/action.ts
|
|
9
|
+
const runTestInit = (options) => pipe(options, set("runId", genRunId), set("testDirPrefix", genTestDirPrefix), tap(emptyTestDir), fillEmptyOptions, tap(checkRequiredDbs), tap(logTestDir), tap(when(isDryRun, run_default(true))), tap(when(isHydRun, run_default(false))));
|
|
10
|
+
const isDryRun = ({ dryRun }) => dryRun;
|
|
11
|
+
const isHydRun = ({ hydRun }) => hydRun;
|
|
12
|
+
var action_default = runTestInit;
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { action_default as default };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { CommandError, printErrorMessage, printMessage, product, runSubCommand } from "../utils.mjs";
|
|
2
|
+
import pm_default from "../json/pm.mjs";
|
|
3
|
+
import { kvStores, messageQueues } from "../lib.mjs";
|
|
4
|
+
import webframeworks_default from "../webframeworks.mjs";
|
|
5
|
+
import { filter, isEmpty, pipe, toArray } from "@fxts/core";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { values } from "@optique/core";
|
|
8
|
+
import { appendFile, mkdir } from "node:fs/promises";
|
|
9
|
+
import { join as join$1, sep } from "node:path";
|
|
10
|
+
|
|
11
|
+
//#region src/test/create.ts
|
|
12
|
+
const BANNED_PMS = ["bun", "yarn"];
|
|
13
|
+
const createTestApp = (testDirPrefix, dry) => async (options) => {
|
|
14
|
+
const testDir = join$1(testDirPrefix, ...options);
|
|
15
|
+
const vals = values(testDir.split(sep).slice(-4));
|
|
16
|
+
try {
|
|
17
|
+
await saveOutputs(testDir, await runSubCommand(toArray(genInitCommand(testDir, dry, options)), {
|
|
18
|
+
cwd: join$1(import.meta.dirname, "../.."),
|
|
19
|
+
stdio: [
|
|
20
|
+
"ignore",
|
|
21
|
+
"pipe",
|
|
22
|
+
"pipe"
|
|
23
|
+
]
|
|
24
|
+
}));
|
|
25
|
+
printMessage` Pass: ${vals}`;
|
|
26
|
+
return testDir;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof CommandError) await saveOutputs(testDir, {
|
|
29
|
+
stdout: error.stdout,
|
|
30
|
+
stderr: error.stderr
|
|
31
|
+
});
|
|
32
|
+
else await saveOutputs(testDir, {
|
|
33
|
+
stdout: "",
|
|
34
|
+
stderr: error instanceof Error ? error.message : String(error)
|
|
35
|
+
});
|
|
36
|
+
printMessage` Fail: ${vals}`;
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var create_default = createTestApp;
|
|
41
|
+
function* genInitCommand(testDir, dry, [webFramework, packageManager, kvStore, messageQueue]) {
|
|
42
|
+
yield "deno";
|
|
43
|
+
yield "run";
|
|
44
|
+
yield "-A";
|
|
45
|
+
yield "src/test/execute.ts";
|
|
46
|
+
yield "init";
|
|
47
|
+
yield testDir;
|
|
48
|
+
yield "-w";
|
|
49
|
+
yield webFramework;
|
|
50
|
+
yield "-p";
|
|
51
|
+
yield packageManager;
|
|
52
|
+
yield "-k";
|
|
53
|
+
yield kvStore;
|
|
54
|
+
yield "-m";
|
|
55
|
+
yield messageQueue;
|
|
56
|
+
if (dry) yield "-d";
|
|
57
|
+
}
|
|
58
|
+
const generateTestCases = ({ webFramework, packageManager, kvStore, messageQueue }) => {
|
|
59
|
+
const pms = filterPackageManager(packageManager);
|
|
60
|
+
exitIfCasesEmpty([
|
|
61
|
+
webFramework,
|
|
62
|
+
pms,
|
|
63
|
+
kvStore,
|
|
64
|
+
messageQueue
|
|
65
|
+
]);
|
|
66
|
+
return product(webFramework, pms, kvStore, messageQueue);
|
|
67
|
+
};
|
|
68
|
+
const filterPackageManager = (pm) => pipe(pm, filter((pm$1) => BANNED_PMS.includes(pm$1) ? printErrorMessage`${pm_default[pm$1]["label"]} is not \
|
|
69
|
+
supported in test mode yet because ${pm_default[pm$1]["label"]} don't \
|
|
70
|
+
support local file dependencies properly.` : true), toArray);
|
|
71
|
+
const exitIfCasesEmpty = (cases) => {
|
|
72
|
+
if (cases.some(isEmpty)) {
|
|
73
|
+
printErrorMessage`No test cases to run. Exiting.`;
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const saveOutputs = async (dirPath, { stdout, stderr }) => {
|
|
78
|
+
await mkdir(dirPath, { recursive: true });
|
|
79
|
+
if (stdout) await appendFile(join$1(dirPath, "out.txt"), stdout + "\n", "utf8");
|
|
80
|
+
if (stderr) await appendFile(join$1(dirPath, "err.txt"), stderr + "\n", "utf8");
|
|
81
|
+
};
|
|
82
|
+
function filterOptions(options) {
|
|
83
|
+
const [wf, pm, kv, mq] = options;
|
|
84
|
+
return [
|
|
85
|
+
webframeworks_default[wf].packageManagers,
|
|
86
|
+
kvStores[kv].packageManagers,
|
|
87
|
+
messageQueues[mq].packageManagers
|
|
88
|
+
].every((pms) => pms.includes(pm));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
export { create_default as default, filterOptions, generateTestCases };
|
package/dist/test/db.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { printErrorMessage, printMessage } from "../utils.mjs";
|
|
2
|
+
import { DB_TO_CHECK } from "../const.mjs";
|
|
3
|
+
import db_to_check_default from "../json/db-to-check.mjs";
|
|
4
|
+
import { concat, filter, pipe, toArray, uniq } from "@fxts/core";
|
|
5
|
+
|
|
6
|
+
//#region src/test/db.ts
|
|
7
|
+
/**
|
|
8
|
+
* This function checks if a given port is open by attempting to fetch from
|
|
9
|
+
* localhost at that port. So, may give false positives if the service does not
|
|
10
|
+
* respond to HTTP requests.
|
|
11
|
+
* @param port The port number to check.
|
|
12
|
+
* @returns A promise that resolves to true if the port is open, else false.
|
|
13
|
+
*/
|
|
14
|
+
async function isPortOpen(port) {
|
|
15
|
+
try {
|
|
16
|
+
await fetch(`http://localhost:${port}`, { signal: AbortSignal.timeout(3e3) });
|
|
17
|
+
return true;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error instanceof TypeError) {
|
|
20
|
+
const msg = error.message.toLowerCase();
|
|
21
|
+
if (msg.includes("refused") || msg.includes("econnrefused")) return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (error instanceof DOMException && error.name === "TimeoutError") return false;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const getRequiredDbs = ({ kvStore, messageQueue }) => pipe(kvStore, concat(messageQueue), uniq, filter((db) => DB_TO_CHECK.includes(db)), toArray);
|
|
29
|
+
async function checkRequiredDbs(options) {
|
|
30
|
+
const dbs = Array.from(getRequiredDbs(options));
|
|
31
|
+
if (dbs.length === 0) return;
|
|
32
|
+
printMessage`Checking required databases...`;
|
|
33
|
+
for (const db of dbs) {
|
|
34
|
+
const info = db_to_check_default[db];
|
|
35
|
+
const port = String(info.defaultPort);
|
|
36
|
+
if (await isPortOpen(info.defaultPort)) printMessage` ${info.name} is running on port ${port}.`;
|
|
37
|
+
else printErrorMessage`${info.name} is not running on port ${port}. Tests requiring ${info.name} may fail. Install: ${info.documentation}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { checkRequiredDbs };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { KV_STORE, MESSAGE_QUEUE, PACKAGE_MANAGER, WEB_FRAMEWORK } from "../const.mjs";
|
|
2
|
+
import { isEmpty, omit, pipe } from "@fxts/core";
|
|
3
|
+
|
|
4
|
+
//#region src/test/fill.ts
|
|
5
|
+
const fillEmptyOptions = (options) => pipe(options, fillWebFramework, fillPackageManager, fillKVStore, fillMessageQueue, fillRunMode);
|
|
6
|
+
const fillOption = (key, allValues) => (options) => ({
|
|
7
|
+
...options,
|
|
8
|
+
[key]: isEmpty(options[key]) ? allValues : options[key].filter((i) => allValues.includes(i))
|
|
9
|
+
});
|
|
10
|
+
const fillWebFramework = fillOption("webFramework", WEB_FRAMEWORK);
|
|
11
|
+
const fillPackageManager = fillOption("packageManager", PACKAGE_MANAGER);
|
|
12
|
+
const fillKVStore = fillOption("kvStore", KV_STORE);
|
|
13
|
+
const fillMessageQueue = fillOption("messageQueue", MESSAGE_QUEUE);
|
|
14
|
+
const fillRunMode = (options) => pipe(options, (options$1) => "noHydRun" in options$1 ? {
|
|
15
|
+
...omit(["noHydRun"], options$1),
|
|
16
|
+
hydRun: !options$1.noHydRun
|
|
17
|
+
} : {
|
|
18
|
+
...options$1,
|
|
19
|
+
hydRun: true
|
|
20
|
+
}, (options$1) => "noDryRun" in options$1 ? {
|
|
21
|
+
...omit(["noDryRun"], options$1),
|
|
22
|
+
dryRun: !options$1.noDryRun
|
|
23
|
+
} : {
|
|
24
|
+
...options$1,
|
|
25
|
+
dryRun: true
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { fillEmptyOptions };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { printErrorMessage, printMessage, runSubCommand } from "../utils.mjs";
|
|
2
|
+
import { getDevCommand } from "../lib.mjs";
|
|
3
|
+
import webframeworks_default from "../webframeworks.mjs";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { values } from "@optique/core";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { createWriteStream } from "node:fs";
|
|
8
|
+
import { join, sep } from "node:path";
|
|
9
|
+
import { isEmpty } from "@fxts/core/index.js";
|
|
10
|
+
|
|
11
|
+
//#region src/test/lookup.ts
|
|
12
|
+
const HANDLE = "john";
|
|
13
|
+
const STARTUP_TIMEOUT = 3e4;
|
|
14
|
+
const CWD = process.cwd();
|
|
15
|
+
const BANNED_WFS = ["next"];
|
|
16
|
+
/**
|
|
17
|
+
* Run servers for all generated apps and test them with the lookup command.
|
|
18
|
+
*
|
|
19
|
+
* @param dirs - Array of paths to generated app directories
|
|
20
|
+
*/
|
|
21
|
+
async function runServerAndLookupUser(dirs) {
|
|
22
|
+
const valid = dirs.filter(Boolean);
|
|
23
|
+
if (valid.length === 0) {
|
|
24
|
+
printErrorMessage`\nNo directories to lookup test.`;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const filtered = filterWebFrameworks(valid);
|
|
28
|
+
printMessage`\nLookup Test start for ${String(filtered.length)} app(s)!`;
|
|
29
|
+
const results = await Array.fromAsync(filtered, testApp);
|
|
30
|
+
const successCount = results.filter(Boolean).length;
|
|
31
|
+
const failCount = results.length - successCount;
|
|
32
|
+
printMessage`Lookup Test Results:
|
|
33
|
+
Total: ${String(results.length)}
|
|
34
|
+
Passed: ${String(successCount)}
|
|
35
|
+
Failed: ${String(failCount)}\n\n`;
|
|
36
|
+
}
|
|
37
|
+
function filterWebFrameworks(dirs) {
|
|
38
|
+
const wfs = new Set(dirs.map((dir) => dir.split(sep).slice(-4, -3)[0]));
|
|
39
|
+
const hasBanned = BANNED_WFS.filter((wf) => wfs.has(wf));
|
|
40
|
+
if (isEmpty(hasBanned)) return dirs;
|
|
41
|
+
printErrorMessage`\n${values(hasBanned.map((wf) => webframeworks_default[wf]["label"]))} is not supported in lookup test yet.`;
|
|
42
|
+
return dirs.filter((dir) => !BANNED_WFS.includes(dir.split(sep).slice(-4, -3)[0]));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run the dev server and test with lookup command.
|
|
46
|
+
*/
|
|
47
|
+
async function testApp(dir) {
|
|
48
|
+
const [wf, pm, kv, mq] = dir.split(sep).slice(-4);
|
|
49
|
+
printMessage` Testing ${values([
|
|
50
|
+
wf,
|
|
51
|
+
pm,
|
|
52
|
+
kv,
|
|
53
|
+
mq
|
|
54
|
+
])}...`;
|
|
55
|
+
const result = await serverClosure(dir, getDevCommand(pm), sendLookup);
|
|
56
|
+
printMessage` Lookup ${result ? "successful" : "failed"} for ${values([
|
|
57
|
+
wf,
|
|
58
|
+
pm,
|
|
59
|
+
kv,
|
|
60
|
+
mq
|
|
61
|
+
])}!`;
|
|
62
|
+
if (!result) printMessage` Check out these files for more details:
|
|
63
|
+
${join(dir, "out.txt")} and
|
|
64
|
+
${join(dir, "err.txt")}\n`;
|
|
65
|
+
printMessage`\n`;
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
const sendLookup = async (port) => {
|
|
69
|
+
const serverUrl = `http://localhost:${port}`;
|
|
70
|
+
const lookupTarget = `${serverUrl}/users/${HANDLE}`;
|
|
71
|
+
printMessage` Waiting for server to start at ${serverUrl}...`;
|
|
72
|
+
if (!await waitForServer(serverUrl, STARTUP_TIMEOUT)) {
|
|
73
|
+
printErrorMessage`Server did not start within \
|
|
74
|
+
${String(STARTUP_TIMEOUT)}ms`;
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
printMessage` Server is ready. Running lookup command...`;
|
|
78
|
+
try {
|
|
79
|
+
await runSubCommand([
|
|
80
|
+
"deno",
|
|
81
|
+
"task",
|
|
82
|
+
"cli",
|
|
83
|
+
"lookup",
|
|
84
|
+
lookupTarget
|
|
85
|
+
], { cwd: CWD });
|
|
86
|
+
return true;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (error instanceof Error) printErrorMessage`${error.message}`;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Wait for the server to be ready by checking if it responds to requests.
|
|
94
|
+
*/
|
|
95
|
+
async function waitForServer(url, timeout) {
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
while (Date.now() - startTime < timeout) {
|
|
98
|
+
try {
|
|
99
|
+
if ((await fetch(url, { signal: AbortSignal.timeout(1e3) })).ok) return true;
|
|
100
|
+
} catch {}
|
|
101
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
async function serverClosure(dir, cmd, callback) {
|
|
106
|
+
const devCommand = cmd.split(" ");
|
|
107
|
+
const serverProcess = spawn(devCommand[0], devCommand.slice(1), {
|
|
108
|
+
cwd: dir,
|
|
109
|
+
stdio: [
|
|
110
|
+
"ignore",
|
|
111
|
+
"pipe",
|
|
112
|
+
"pipe"
|
|
113
|
+
],
|
|
114
|
+
detached: true
|
|
115
|
+
});
|
|
116
|
+
const stdout = createWriteStream(join(dir, "out.txt"), { flags: "a" });
|
|
117
|
+
const stderr = createWriteStream(join(dir, "err.txt"), { flags: "a" });
|
|
118
|
+
serverProcess.stdout?.pipe(stdout);
|
|
119
|
+
serverProcess.stderr?.pipe(stderr);
|
|
120
|
+
try {
|
|
121
|
+
return await callback(await determinePort(serverProcess));
|
|
122
|
+
} finally {
|
|
123
|
+
try {
|
|
124
|
+
process.kill(-serverProcess.pid, "SIGKILL");
|
|
125
|
+
} catch {
|
|
126
|
+
serverProcess.kill("SIGKILL");
|
|
127
|
+
stdout.end();
|
|
128
|
+
stderr.end();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function determinePort(server) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
const timeout = setTimeout(() => {
|
|
135
|
+
reject(/* @__PURE__ */ new Error("Timeout: Could not determine port from server output"));
|
|
136
|
+
}, STARTUP_TIMEOUT);
|
|
137
|
+
let stdoutData = "";
|
|
138
|
+
let stderrData = "";
|
|
139
|
+
const portPatterns = [
|
|
140
|
+
/listening on.*:(\d+)/i,
|
|
141
|
+
/server.*:(\d+)/i,
|
|
142
|
+
/port\s*:?\s*(\d+)/i,
|
|
143
|
+
/https?:\/\/localhost:(\d+)/i,
|
|
144
|
+
/https?:\/\/0\.0\.0\.0:(\d+)/i,
|
|
145
|
+
/https?:\/\/127\.0\.0\.1:(\d+)/i,
|
|
146
|
+
/https?:\/\/[^:]+:(\d+)/i
|
|
147
|
+
];
|
|
148
|
+
const checkForPort = (data) => {
|
|
149
|
+
for (const pattern of portPatterns) {
|
|
150
|
+
const match = data.match(pattern);
|
|
151
|
+
if (match && match[1]) {
|
|
152
|
+
const port = Number.parseInt(match[1], 10);
|
|
153
|
+
if (port > 0 && port < 65536) {
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
return port;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
};
|
|
161
|
+
server.stdout.on("data", (chunk) => {
|
|
162
|
+
stdoutData += chunk.toString();
|
|
163
|
+
const port = checkForPort(stdoutData);
|
|
164
|
+
if (port) resolve(port);
|
|
165
|
+
});
|
|
166
|
+
server.stderr.on("data", (chunk) => {
|
|
167
|
+
stderrData += chunk.toString();
|
|
168
|
+
const port = checkForPort(stderrData);
|
|
169
|
+
if (port) resolve(port);
|
|
170
|
+
});
|
|
171
|
+
server.on("error", (err) => {
|
|
172
|
+
clearTimeout(timeout);
|
|
173
|
+
reject(err);
|
|
174
|
+
});
|
|
175
|
+
server.on("exit", (code) => {
|
|
176
|
+
clearTimeout(timeout);
|
|
177
|
+
reject(/* @__PURE__ */ new Error(`Server exited with code ${code} before port could be determined`));
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
export { runServerAndLookupUser as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { testInitCommand } from "../command.mjs";
|
|
2
|
+
import action_default from "./action.mjs";
|
|
3
|
+
import { run } from "@optique/run";
|
|
4
|
+
|
|
5
|
+
//#region src/test/mod.ts
|
|
6
|
+
async function main() {
|
|
7
|
+
console.log("Running test-init command...");
|
|
8
|
+
await action_default(run(testInitCommand, {
|
|
9
|
+
programName: "fedify-test-init",
|
|
10
|
+
help: "both"
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
await main();
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { printMessage } from "../utils.mjs";
|
|
2
|
+
import create_default, { filterOptions, generateTestCases } from "./create.mjs";
|
|
3
|
+
import runServerAndLookupUser from "./lookup.mjs";
|
|
4
|
+
import { always, filter, map, pipe, tap, unless } from "@fxts/core";
|
|
5
|
+
import { optionNames } from "@optique/core";
|
|
6
|
+
import { join as join$1 } from "node:path";
|
|
7
|
+
|
|
8
|
+
//#region src/test/run.ts
|
|
9
|
+
const runTests = (dry) => ({ testDirPrefix, dryRun, hydRun, ...options }) => pipe(options, printStartMessage(dry), generateTestCases, filter(filterOptions), map(create_default(join$1(testDirPrefix, getMid(dryRun, hydRun, dry)), dry)), Array.fromAsync, unless(always(dry), runServerAndLookupUser));
|
|
10
|
+
var run_default = runTests;
|
|
11
|
+
const printStartMessage = (dry) => tap(() => printMessage`\n
|
|
12
|
+
Init ${dry ? "Dry" : "Hyd"} Test start!
|
|
13
|
+
Options: ${optionNames([
|
|
14
|
+
"Web Framework",
|
|
15
|
+
"Package Manager",
|
|
16
|
+
"KV Store",
|
|
17
|
+
"Message Queue"
|
|
18
|
+
])}`);
|
|
19
|
+
const getMid = (dryRun, hydRun, dry) => dryRun === hydRun ? dry ? "dry" : "hyd" : "";
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { run_default as default };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { printMessage } from "../utils.mjs";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
|
|
6
|
+
//#region src/test/utils.ts
|
|
7
|
+
const genRunId = () => `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
8
|
+
const genTestDirPrefix = ({ runId }) => join(tmpdir(), "fedify-init", runId);
|
|
9
|
+
const emptyTestDir = ({ testDirPrefix }) => rm(testDirPrefix, { recursive: true }).catch(() => {});
|
|
10
|
+
const logTestDir = ({ runId, testDirPrefix }) => printMessage`Test run completed.
|
|
11
|
+
Run ID: ${runId}
|
|
12
|
+
Path: ${testDirPrefix}`;
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { emptyTestDir, genRunId, genTestDirPrefix, logTestDir };
|