@fedify/cli 2.0.0-pr.479.1919 → 2.0.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.
Files changed (160) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/dist/cache.js +17 -3
  4. package/dist/config.js +105 -0
  5. package/dist/deno.js +18 -8
  6. package/dist/generate-vocab/action.js +1 -1
  7. package/dist/imagerenderer.js +1 -1
  8. package/dist/inbox/rendercode.js +11 -21
  9. package/dist/inbox.js +162 -132
  10. package/dist/init/mod.js +3 -3
  11. package/dist/log.js +35 -1
  12. package/dist/lookup.js +55 -23
  13. package/dist/mod.js +95 -18
  14. package/dist/nodeinfo.js +39 -22
  15. package/dist/options.js +84 -0
  16. package/dist/relay.js +136 -0
  17. package/dist/tempserver.js +15 -8
  18. package/dist/tunnel.js +6 -10
  19. package/dist/utils.js +19 -108
  20. package/dist/webfinger/action.js +1 -1
  21. package/dist/webfinger/command.js +17 -9
  22. package/dist/webfinger/lib.js +3 -3
  23. package/package.json +50 -28
  24. package/deno.json +0 -71
  25. package/dist/globals.js +0 -49
  26. package/dist/init/action/configs.js +0 -91
  27. package/dist/init/action/const.js +0 -10
  28. package/dist/init/action/deps.js +0 -50
  29. package/dist/init/action/dir.js +0 -16
  30. package/dist/init/action/env.js +0 -13
  31. package/dist/init/action/install.js +0 -20
  32. package/dist/init/action/mod.js +0 -39
  33. package/dist/init/action/notice.js +0 -55
  34. package/dist/init/action/patch.js +0 -147
  35. package/dist/init/action/precommand.js +0 -28
  36. package/dist/init/action/recommend.js +0 -24
  37. package/dist/init/action/set.js +0 -31
  38. package/dist/init/action/templates.js +0 -58
  39. package/dist/init/action/utils.js +0 -50
  40. package/dist/init/ask/dir.js +0 -82
  41. package/dist/init/ask/kv.js +0 -44
  42. package/dist/init/ask/mod.js +0 -16
  43. package/dist/init/ask/mq.js +0 -46
  44. package/dist/init/ask/pm.js +0 -49
  45. package/dist/init/ask/wf.js +0 -29
  46. package/dist/init/command.js +0 -50
  47. package/dist/init/const.js +0 -31
  48. package/dist/init/json/biome.js +0 -24
  49. package/dist/init/json/kv.js +0 -53
  50. package/dist/init/json/mq.js +0 -72
  51. package/dist/init/json/pm.js +0 -44
  52. package/dist/init/json/rt.js +0 -39
  53. package/dist/init/json/vscode-settings-for-deno.js +0 -53
  54. package/dist/init/json/vscode-settings.js +0 -49
  55. package/dist/init/lib.js +0 -136
  56. package/dist/init/templates/defaults/federation.ts.tpl +0 -23
  57. package/dist/init/templates/defaults/logging.ts.tpl +0 -23
  58. package/dist/init/templates/express/app.ts.tpl +0 -16
  59. package/dist/init/templates/express/index.ts.tpl +0 -6
  60. package/dist/init/templates/hono/app.tsx.tpl +0 -14
  61. package/dist/init/templates/hono/index/bun.ts.tpl +0 -10
  62. package/dist/init/templates/hono/index/deno.ts.tpl +0 -13
  63. package/dist/init/templates/hono/index/node.ts.tpl +0 -14
  64. package/dist/init/templates/next/middleware.ts.tpl +0 -45
  65. package/dist/init/templates/nitro/.env.test.tpl +0 -1
  66. package/dist/init/templates/nitro/nitro.config.ts.tpl +0 -14
  67. package/dist/init/templates/nitro/server/error.ts.tpl +0 -3
  68. package/dist/init/templates/nitro/server/middleware/federation.ts.tpl +0 -8
  69. package/dist/init/test/action.js +0 -17
  70. package/dist/init/test/create.js +0 -100
  71. package/dist/init/test/fill.js +0 -32
  72. package/dist/init/test/lookup.js +0 -190
  73. package/dist/init/test/run.js +0 -25
  74. package/dist/init/test/utils.js +0 -17
  75. package/dist/init/webframeworks.js +0 -136
  76. package/scripts/pack.ts +0 -71
  77. package/src/cache.ts +0 -17
  78. package/src/docloader.ts +0 -67
  79. package/src/generate-vocab/action.ts +0 -17
  80. package/src/generate-vocab/command.ts +0 -44
  81. package/src/generate-vocab/mod.ts +0 -2
  82. package/src/globals.ts +0 -43
  83. package/src/imagerenderer.ts +0 -149
  84. package/src/inbox/entry.ts +0 -10
  85. package/src/inbox/rendercode.ts +0 -68
  86. package/src/inbox/view.tsx +0 -598
  87. package/src/inbox.tsx +0 -536
  88. package/src/init/action/configs.ts +0 -133
  89. package/src/init/action/const.ts +0 -9
  90. package/src/init/action/deps.ts +0 -161
  91. package/src/init/action/dir.ts +0 -11
  92. package/src/init/action/env.ts +0 -14
  93. package/src/init/action/install.ts +0 -24
  94. package/src/init/action/mod.ts +0 -66
  95. package/src/init/action/notice.ts +0 -103
  96. package/src/init/action/patch.ts +0 -233
  97. package/src/init/action/precommand.ts +0 -29
  98. package/src/init/action/recommend.ts +0 -38
  99. package/src/init/action/set.ts +0 -65
  100. package/src/init/action/templates.ts +0 -96
  101. package/src/init/action/utils.ts +0 -64
  102. package/src/init/ask/dir.ts +0 -98
  103. package/src/init/ask/kv.ts +0 -82
  104. package/src/init/ask/mod.ts +0 -23
  105. package/src/init/ask/mq.ts +0 -86
  106. package/src/init/ask/pm.ts +0 -58
  107. package/src/init/ask/wf.ts +0 -27
  108. package/src/init/command.ts +0 -135
  109. package/src/init/const.ts +0 -4
  110. package/src/init/json/biome.json +0 -17
  111. package/src/init/json/kv.json +0 -39
  112. package/src/init/json/mq.json +0 -95
  113. package/src/init/json/pm.json +0 -47
  114. package/src/init/json/rt.json +0 -42
  115. package/src/init/json/vscode-settings-for-deno.json +0 -43
  116. package/src/init/json/vscode-settings.json +0 -41
  117. package/src/init/lib.ts +0 -223
  118. package/src/init/mod.ts +0 -3
  119. package/src/init/templates/defaults/federation.ts.tpl +0 -23
  120. package/src/init/templates/defaults/logging.ts.tpl +0 -23
  121. package/src/init/templates/express/app.ts.tpl +0 -16
  122. package/src/init/templates/express/index.ts.tpl +0 -6
  123. package/src/init/templates/hono/app.tsx.tpl +0 -14
  124. package/src/init/templates/hono/index/bun.ts.tpl +0 -10
  125. package/src/init/templates/hono/index/deno.ts.tpl +0 -13
  126. package/src/init/templates/hono/index/node.ts.tpl +0 -14
  127. package/src/init/templates/next/middleware.ts.tpl +0 -45
  128. package/src/init/templates/nitro/.env.test.tpl +0 -1
  129. package/src/init/templates/nitro/nitro.config.ts.tpl +0 -14
  130. package/src/init/templates/nitro/server/error.ts.tpl +0 -3
  131. package/src/init/templates/nitro/server/middleware/federation.ts.tpl +0 -8
  132. package/src/init/test/action.ts +0 -28
  133. package/src/init/test/create.ts +0 -137
  134. package/src/init/test/fill.ts +0 -67
  135. package/src/init/test/lookup.ts +0 -254
  136. package/src/init/test/run.ts +0 -39
  137. package/src/init/test/types.ts +0 -27
  138. package/src/init/test/utils.ts +0 -21
  139. package/src/init/types.ts +0 -89
  140. package/src/init/webframeworks.ts +0 -168
  141. package/src/kv.bun.ts +0 -12
  142. package/src/kv.node.ts +0 -11
  143. package/src/log.ts +0 -64
  144. package/src/lookup.test.ts +0 -182
  145. package/src/lookup.ts +0 -563
  146. package/src/mod.ts +0 -62
  147. package/src/nodeinfo.test.ts +0 -229
  148. package/src/nodeinfo.ts +0 -454
  149. package/src/table.ts +0 -17
  150. package/src/tempserver.ts +0 -87
  151. package/src/tunnel.test.ts +0 -157
  152. package/src/tunnel.ts +0 -94
  153. package/src/utils.ts +0 -254
  154. package/src/webfinger/action.ts +0 -50
  155. package/src/webfinger/command.ts +0 -64
  156. package/src/webfinger/error.ts +0 -47
  157. package/src/webfinger/lib.ts +0 -37
  158. package/src/webfinger/mod.test.ts +0 -79
  159. package/src/webfinger/mod.ts +0 -2
  160. package/tsdown.config.ts +0 -35
@@ -1,23 +0,0 @@
1
- import { createFederation, Person } from "@fedify/fedify";
2
- import { getLogger } from "@logtape/logtape";
3
- /* imports */
4
-
5
- const logger = getLogger(/* logger */);
6
-
7
- const federation = createFederation({
8
- kv: /* kv */,
9
- queue: /* queue */,
10
- });
11
-
12
- federation.setActorDispatcher(
13
- "/users/{identifier}",
14
- async (ctx, identifier) => {
15
- return new Person({
16
- id: ctx.getActorUri(identifier),
17
- preferredUsername: identifier,
18
- name: identifier,
19
- });
20
- },
21
- );
22
-
23
- export default federation;
@@ -1,23 +0,0 @@
1
- import { configure, getConsoleSink } from "@logtape/logtape";
2
- import { AsyncLocalStorage } from "node:async_hooks";
3
-
4
- await configure({
5
- contextLocalStorage: new AsyncLocalStorage(),
6
- sinks: {
7
- console: getConsoleSink(),
8
- },
9
- filters: {},
10
- loggers: [
11
- {
12
- category: /* project name */,
13
- lowestLevel: "debug",
14
- sinks: ["console"],
15
- },
16
- { category: "fedify", lowestLevel: "info", sinks: ["console"] },
17
- {
18
- category: ["logtape", "meta"],
19
- lowestLevel: "warning",
20
- sinks: ["console"],
21
- },
22
- ],
23
- });
@@ -1,16 +0,0 @@
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;
@@ -1,6 +0,0 @@
1
- import app from "./app.ts";
2
- import "./logging.ts";
3
-
4
- app.listen(8000, () => {
5
- console.log("Server started at http://localhost:8000");
6
- });
@@ -1,14 +0,0 @@
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;
@@ -1,10 +0,0 @@
1
- import { behindProxy } from "x-forwarded-fetch";
2
- import app from "./app.tsx";
3
- import "./logging.ts";
4
-
5
- const server = Bun.serve({
6
- port: 8000,
7
- fetch: behindProxy(app.fetch.bind(app)),
8
- });
9
-
10
- console.log("Server started at", server.url.href);
@@ -1,13 +0,0 @@
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
- );
@@ -1,14 +0,0 @@
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
- );
@@ -1,45 +0,0 @@
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
- };
@@ -1 +0,0 @@
1
- HOST=127.0.0.1
@@ -1,14 +0,0 @@
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
- });
@@ -1,3 +0,0 @@
1
- import { onError } from "@fedify/h3";
2
-
3
- export default onError;
@@ -1,8 +0,0 @@
1
-
2
- import { integrateFederation } from "@fedify/h3";
3
- import federation from "../federation"
4
-
5
- export default integrateFederation(
6
- federation,
7
- (event, request) => undefined,
8
- );
@@ -1,28 +0,0 @@
1
- import { pipe, tap, when } from "@fxts/core";
2
- import { set } from "../../utils.ts";
3
- import type { TestInitCommand } from "../command.ts";
4
- import { fillEmptyOptions } from "./fill.ts";
5
- import runTests from "./run.ts";
6
- import {
7
- emptyTestDir,
8
- genRunId,
9
- genTestDirPrefix,
10
- logTestDir,
11
- } from "./utils.ts";
12
-
13
- const runTestInit = (options: TestInitCommand) =>
14
- pipe(
15
- options,
16
- set("runId", genRunId),
17
- set("testDirPrefix", genTestDirPrefix),
18
- tap(emptyTestDir),
19
- fillEmptyOptions,
20
- tap(when(isDryRun, runTests(true))),
21
- tap(when(isHydRun, runTests(false))),
22
- tap(logTestDir),
23
- );
24
-
25
- const isDryRun = <T extends { dryRun: boolean }>({ dryRun }: T) => dryRun;
26
- const isHydRun = <T extends { hydRun: boolean }>({ hydRun }: T) => hydRun;
27
-
28
- export default runTestInit;
@@ -1,137 +0,0 @@
1
- import { filter, isEmpty, pipe, toArray } from "@fxts/core";
2
- import { values } from "@optique/core";
3
- import { appendFile, mkdir } from "node:fs/promises";
4
- import { join, sep } from "node:path";
5
- import process from "node:process";
6
- import {
7
- CommandError,
8
- type GeneratedType,
9
- printErrorMessage,
10
- printMessage,
11
- product,
12
- runSubCommand,
13
- } from "../../utils.ts";
14
- import packageManagers from "../json/pm.json" with { type: "json" };
15
- import { kvStores, messageQueues } from "../lib.ts";
16
- import type {
17
- KvStore,
18
- MessageQueue,
19
- PackageManager,
20
- WebFramework,
21
- } from "../types.ts";
22
- import webFrameworks from "../webframeworks.ts";
23
- import type { InitTestData, MultipleOption } from "./types.ts";
24
-
25
- const BANNED_PMS: PackageManager[] = ["bun", "yarn"];
26
-
27
- const createTestApp = (testDirPrefix: string, dry: boolean) =>
28
- async (
29
- options: GeneratedType<ReturnType<typeof generateTestCases>>,
30
- ): Promise<string> => {
31
- const testDir = join(testDirPrefix, ...options);
32
- const vals = values(testDir.split(sep).slice(-4));
33
- try {
34
- const result = await runSubCommand(
35
- toArray(genInitCommand(testDir, dry, options)),
36
- {
37
- cwd: join(import.meta.dirname!, "../../.."),
38
- stdio: ["ignore", "pipe", "pipe"],
39
- },
40
- );
41
-
42
- await saveOutputs(testDir, result);
43
- printMessage` Pass: ${vals}`;
44
- return testDir;
45
- } catch (error) {
46
- if (error instanceof CommandError) {
47
- await saveOutputs(testDir, {
48
- stdout: error.stdout,
49
- stderr: error.stderr,
50
- });
51
- } else {
52
- const errorMessage = error instanceof Error
53
- ? error.message
54
- : String(error);
55
- await saveOutputs(testDir, { stdout: "", stderr: errorMessage });
56
- }
57
- printMessage` Fail: ${vals}`;
58
- return "";
59
- }
60
- };
61
-
62
- export default createTestApp;
63
-
64
- function* genInitCommand(
65
- testDir: string,
66
- dry: boolean,
67
- [webFramework, packageManager, kvStore, messageQueue]: //
68
- GeneratedType<ReturnType<typeof generateTestCases>>,
69
- ) {
70
- yield "deno";
71
- yield "run";
72
- yield "-A";
73
- yield "src/mod.ts";
74
- yield "init";
75
- yield testDir;
76
- yield "-w";
77
- yield webFramework;
78
- yield "-p";
79
- yield packageManager;
80
- yield "-k";
81
- yield kvStore;
82
- yield "-m";
83
- yield messageQueue;
84
- yield "--test-mode";
85
- if (dry) yield "-d";
86
- }
87
-
88
- export const generateTestCases = <T extends Pick<InitTestData, MultipleOption>>(
89
- { webFramework, packageManager, kvStore, messageQueue }: T,
90
- ) => {
91
- const pms = filterPackageManager(packageManager);
92
- exitIfCasesEmpty([webFramework, pms, kvStore, messageQueue]);
93
-
94
- return product(webFramework, pms, kvStore, messageQueue);
95
- };
96
-
97
- const filterPackageManager = (pm: PackageManager[]) =>
98
- pipe(
99
- pm,
100
- filter(
101
- (pm) =>
102
- BANNED_PMS.includes(pm)
103
- ? printErrorMessage`${packageManagers[pm]["label"]} is not \
104
- supported in test mode yet because ${packageManagers[pm]["label"]} don't \
105
- support local file dependencies properly.`
106
- : true,
107
- ),
108
- toArray,
109
- );
110
-
111
- const exitIfCasesEmpty = (cases: string[][]): never | void => {
112
- if (cases.some(isEmpty)) {
113
- printErrorMessage`No test cases to run. Exiting.`;
114
- process.exit(1);
115
- }
116
- };
117
-
118
- const saveOutputs = async (
119
- dirPath: string,
120
- { stdout, stderr }: { stdout: string; stderr: string },
121
- ): Promise<void> => {
122
- await mkdir(dirPath, { recursive: true });
123
- if (stdout) await appendFile(join(dirPath, "out.txt"), stdout + "\n", "utf8");
124
- if (stderr) await appendFile(join(dirPath, "err.txt"), stderr + "\n", "utf8");
125
- };
126
-
127
- export function filterOptions(
128
- options: GeneratedType<ReturnType<typeof generateTestCases>>,
129
- ): boolean {
130
- const [wf, pm, kv, mq] = options as //
131
- [WebFramework, PackageManager, KvStore, MessageQueue];
132
- return [
133
- webFrameworks[wf].packageManagers,
134
- kvStores[kv].packageManagers,
135
- messageQueues[mq].packageManagers,
136
- ].every((pms) => pms.includes(pm));
137
- }
@@ -1,67 +0,0 @@
1
- import { isEmpty, omit, pipe } from "@fxts/core";
2
- import type { TestInitCommand } from "../command.ts";
3
- import {
4
- KV_STORE,
5
- MESSAGE_QUEUE,
6
- PACKAGE_MANAGER,
7
- WEB_FRAMEWORK,
8
- } from "../const.ts";
9
- import type {
10
- DefineAllOptions,
11
- DefineOption,
12
- MultipleOption,
13
- } from "./types.ts";
14
-
15
- export const fillEmptyOptions = <T extends TestInitCommand>(
16
- options: T,
17
- ): DefineAllOptions<T> =>
18
- pipe(
19
- options,
20
- fillWebFramework,
21
- fillPackageManager,
22
- fillKVStore,
23
- fillMessageQueue,
24
- fillRunMode,
25
- ) as DefineAllOptions<T>;
26
-
27
- const fillOption = <
28
- K extends MultipleOption,
29
- AllValues = TestInitCommand[K] extends readonly (infer U)[] ? (U & string)[]
30
- : never,
31
- >(
32
- key: K,
33
- allValues: AllValues,
34
- ) =>
35
- <T extends TestInitCommand>(
36
- options: T,
37
- ): T extends Awaited<DefineOption<T, infer J>> | DefineOption<T, infer J>
38
- ? DefineOption<T, J | K>
39
- : DefineOption<T, K> =>
40
- ({
41
- ...options,
42
- [key]: (isEmpty(options[key])
43
- ? allValues
44
- : (options[key].filter((i) =>
45
- (allValues as readonly unknown[]).includes(i)
46
- ) as T[K])),
47
- }) as T extends Awaited<DefineOption<T, infer J>> | DefineOption<T, infer J>
48
- ? DefineOption<T, J | K>
49
- : DefineOption<T, K>;
50
-
51
- const fillWebFramework = fillOption("webFramework", WEB_FRAMEWORK);
52
- const fillPackageManager = fillOption("packageManager", PACKAGE_MANAGER);
53
- const fillKVStore = fillOption("kvStore", KV_STORE);
54
- const fillMessageQueue = fillOption("messageQueue", MESSAGE_QUEUE);
55
-
56
- const fillRunMode = <T extends TestInitCommand>(
57
- options: T,
58
- ): DefineAllOptions<T> =>
59
- pipe(
60
- options,
61
- (options) => ("noHydRun" in options
62
- ? ({ ...omit(["noHydRun"], options), hydRun: !options.noHydRun })
63
- : ({ ...options, hydRun: true })),
64
- (options) => ("noDryRun" in options
65
- ? ({ ...omit(["noDryRun"], options), dryRun: !options.noDryRun })
66
- : ({ ...options, dryRun: true })),
67
- ) as DefineAllOptions<T>;
@@ -1,254 +0,0 @@
1
- import { isEmpty } from "@fxts/core/index.js";
2
- import { values } from "@optique/core";
3
- import type { ChildProcessByStdio } from "node:child_process";
4
- import { spawn } from "node:child_process";
5
- import { createWriteStream } from "node:fs";
6
- import { join, sep } from "node:path";
7
- import process from "node:process";
8
- import type Stream from "node:stream";
9
- import { printErrorMessage, printMessage, runSubCommand } from "../../utils.ts";
10
- import { getDevCommand } from "../lib.ts";
11
- import type {
12
- KvStore,
13
- MessageQueue,
14
- PackageManager,
15
- WebFramework,
16
- } from "../types.ts";
17
- import webFrameworks from "../webframeworks.ts";
18
-
19
- const HANDLE = "john";
20
- const STARTUP_TIMEOUT = 30000; // 30 seconds
21
- const CWD = process.cwd();
22
- const BANNED_WFS: WebFramework[] = ["next"];
23
-
24
- /**
25
- * Run servers for all generated apps and test them with the lookup command.
26
- *
27
- * @param dirs - Array of paths to generated app directories
28
- */
29
- export default async function runServerAndLookupUser(
30
- dirs: string[],
31
- ): Promise<void> {
32
- const valid = dirs.filter(Boolean);
33
- if (valid.length === 0) {
34
- printErrorMessage`\nNo directories to lookup test.`;
35
- return;
36
- }
37
- const filtered = filterWebFrameworks(valid);
38
-
39
- printMessage`\nLookup Test start for ${String(filtered.length)} app(s)!`;
40
-
41
- const results = await Array.fromAsync(filtered, testApp);
42
-
43
- const successCount = results.filter(Boolean).length;
44
- const failCount = results.length - successCount;
45
-
46
- printMessage`Lookup Test Results:
47
- Total: ${String(results.length)}
48
- Passed: ${String(successCount)}
49
- Failed: ${String(failCount)}\n\n`;
50
- }
51
-
52
- function filterWebFrameworks(
53
- dirs: string[],
54
- ): string[] {
55
- const wfs = new Set<WebFramework>(
56
- dirs.map((dir) => dir.split(sep).slice(-4, -3)[0] as WebFramework),
57
- );
58
- const hasBanned = BANNED_WFS.filter((wf) => wfs.has(wf));
59
- if (isEmpty(hasBanned)) {
60
- return dirs;
61
- }
62
- const bannedLabels = hasBanned.map((wf) => webFrameworks[wf]["label"]);
63
- printErrorMessage`\n${
64
- values(bannedLabels)
65
- } is not supported in lookup test yet.`;
66
- return dirs.filter((dir) =>
67
- !BANNED_WFS.includes(dir.split(sep).slice(-4, -3)[0] as WebFramework)
68
- );
69
- }
70
-
71
- /**
72
- * Run the dev server and test with lookup command.
73
- */
74
- async function testApp(dir: string): Promise<boolean> {
75
- const [wf, pm, kv, mq] = dir.split(sep).slice(-4) as //
76
- [WebFramework, PackageManager, KvStore, MessageQueue];
77
-
78
- printMessage` Testing ${values([wf, pm, kv, mq])}...`;
79
-
80
- const result = await serverClosure(
81
- dir,
82
- getDevCommand(pm),
83
- sendLookup,
84
- );
85
-
86
- printMessage` Lookup ${result ? "successful" : "failed"} for ${
87
- values([wf, pm, kv, mq])
88
- }!`;
89
- if (!result) {
90
- printMessage` Check out these files for more details:
91
- ${join(dir, "out.txt")} and
92
- ${join(dir, "err.txt")}\n`;
93
- }
94
- printMessage`\n`;
95
-
96
- return result;
97
- }
98
-
99
- const sendLookup = async (port: number) => {
100
- const serverUrl = `http://localhost:${port}`;
101
- const lookupTarget = `${serverUrl}/users/${HANDLE}`;
102
- // Wait for server to be ready
103
- printMessage` Waiting for server to start at ${serverUrl}...`;
104
-
105
- const isReady = await waitForServer(serverUrl, STARTUP_TIMEOUT);
106
-
107
- if (!isReady) {
108
- printErrorMessage`Server did not start within \
109
- ${String(STARTUP_TIMEOUT)}ms`;
110
- return false;
111
- }
112
-
113
- printMessage` Server is ready. Running lookup command...`;
114
-
115
- // Run lookup command from original directory
116
- try {
117
- await runSubCommand(
118
- ["deno", "task", "cli", "lookup", lookupTarget],
119
- { cwd: CWD },
120
- );
121
-
122
- return true;
123
- } catch (error) {
124
- if (error instanceof Error) {
125
- printErrorMessage`${error.message}`;
126
- }
127
- }
128
- return false;
129
- };
130
-
131
- /**
132
- * Wait for the server to be ready by checking if it responds to requests.
133
- */
134
- async function waitForServer(url: string, timeout: number): Promise<boolean> {
135
- const startTime = Date.now();
136
-
137
- while (Date.now() - startTime < timeout) {
138
- try {
139
- const response = await fetch(url, { signal: AbortSignal.timeout(1000) });
140
- if (response.ok) {
141
- return true;
142
- }
143
- } catch {
144
- // Server not ready yet, continue waiting
145
- }
146
-
147
- // Wait 500ms before next attempt
148
- await new Promise((resolve) => setTimeout(resolve, 500));
149
- }
150
-
151
- return false;
152
- }
153
-
154
- async function serverClosure<T>(
155
- dir: string,
156
- cmd: string,
157
- callback: (port: number) => Promise<T>,
158
- ): Promise<Awaited<T>> {
159
- // Start the dev server using Node.js spawn
160
- const devCommand = cmd.split(" ");
161
- const serverProcess = spawn(devCommand[0], devCommand.slice(1), {
162
- cwd: dir,
163
- stdio: ["ignore", "pipe", "pipe"],
164
- detached: true, // Create a new process group
165
- });
166
-
167
- // Append stdout and stderr to files
168
- const stdout = createWriteStream(join(dir, "out.txt"), { flags: "a" });
169
- const stderr = createWriteStream(join(dir, "err.txt"), { flags: "a" });
170
-
171
- serverProcess.stdout?.pipe(stdout);
172
- serverProcess.stderr?.pipe(stderr);
173
-
174
- try {
175
- const port = await determinePort(serverProcess);
176
- return await callback(port);
177
- } finally {
178
- try {
179
- process.kill(-serverProcess.pid!, "SIGKILL");
180
- } catch {
181
- serverProcess.kill("SIGKILL");
182
-
183
- // Close file streams
184
- stdout.end();
185
- stderr.end();
186
- }
187
- }
188
- }
189
-
190
- function determinePort(
191
- server: ChildProcessByStdio<null, Stream.Readable, Stream.Readable>,
192
- ): Promise<number> {
193
- return new Promise((resolve, reject) => {
194
- const timeout = setTimeout(() => {
195
- reject(
196
- new Error("Timeout: Could not determine port from server output"),
197
- );
198
- }, STARTUP_TIMEOUT);
199
-
200
- let stdoutData = "";
201
- let stderrData = "";
202
-
203
- // Common patterns for port detection
204
- const portPatterns = [
205
- /listening on.*:(\d+)/i,
206
- /server.*:(\d+)/i,
207
- /port\s*:?\s*(\d+)/i,
208
- /https?:\/\/localhost:(\d+)/i,
209
- /https?:\/\/0\.0\.0\.0:(\d+)/i,
210
- /https?:\/\/127\.0\.0\.1:(\d+)/i,
211
- /https?:\/\/[^:]+:(\d+)/i,
212
- ];
213
-
214
- const checkForPort = (data: string) => {
215
- for (const pattern of portPatterns) {
216
- const match = data.match(pattern);
217
- if (match && match[1]) {
218
- const port = Number.parseInt(match[1], 10);
219
- if (port > 0 && port < 65536) {
220
- clearTimeout(timeout);
221
- return port;
222
- }
223
- }
224
- }
225
- return null;
226
- };
227
-
228
- server.stdout.on("data", (chunk) => {
229
- stdoutData += chunk.toString();
230
- const port = checkForPort(stdoutData);
231
- if (port) resolve(port);
232
- });
233
-
234
- server.stderr.on("data", (chunk) => {
235
- stderrData += chunk.toString();
236
- const port = checkForPort(stderrData);
237
- if (port) resolve(port);
238
- });
239
-
240
- server.on("error", (err) => {
241
- clearTimeout(timeout);
242
- reject(err);
243
- });
244
-
245
- server.on("exit", (code) => {
246
- clearTimeout(timeout);
247
- reject(
248
- new Error(
249
- `Server exited with code ${code} before port could be determined`,
250
- ),
251
- );
252
- });
253
- });
254
- }