fulmine.js 5.19.9 → 5.20.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fulmine.js",
3
- "version": "5.19.9",
4
- "description": "Drop-in Express 5 replacement on uWebSockets.js. Your existing middleware keeps working.",
3
+ "version": "5.20.0",
4
+ "description": "Drop-in Express 5 replacement on uWebSockets.js, up to 20x faster. Same API, your middleware and framework keep working.",
5
5
  "main": "src/index.js",
6
6
  "exports": {
7
7
  ".": {
@@ -64,16 +64,28 @@
64
64
  "keywords": [
65
65
  "express",
66
66
  "express5",
67
+ "express-alternative",
68
+ "drop-in replacement",
67
69
  "fast",
70
+ "fastest",
71
+ "performance",
72
+ "web framework",
68
73
  "http",
69
74
  "http server",
70
75
  "https",
71
76
  "https server",
77
+ "router",
78
+ "middleware",
79
+ "rest api",
72
80
  "uwebsockets",
73
81
  "uws",
74
82
  "websocket",
75
83
  "websockets",
76
- "performance"
84
+ "nestjs",
85
+ "angular ssr",
86
+ "nextjs",
87
+ "cluster",
88
+ "typescript"
77
89
  ],
78
90
  "types": "src/types.d.ts",
79
91
  "author": "Nigro Simone",
@@ -81,7 +93,7 @@
81
93
  "bugs": {
82
94
  "url": "https://github.com/nigrosimone/fulmine.js/issues"
83
95
  },
84
- "homepage": "https://github.com/nigrosimone/fulmine.js#readme",
96
+ "homepage": "https://fulmine.sndesign.it",
85
97
  "dependencies": {
86
98
  "@types/express": "^5.0.6",
87
99
  "accepts": "^2.0.0",
package/src/adopt.js CHANGED
@@ -14,17 +14,20 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- // The two commands that edit a config file rather than source: `npx fulmine.js override` and
18
- // `npx fulmine.js angular`.
17
+ // The commands that edit a config file rather than source: `npx fulmine.js override`,
18
+ // `npx fulmine.js angular` and `npx fulmine.js pnpm`.
19
19
  //
20
- // `migrate` rewrites `require("express")` in your own files. The two cases it cannot reach are
21
- // both a line in a JSON file:
20
+ // `migrate` rewrites `require("express")` in your own files. The cases it cannot reach are each
21
+ // a line in a config file:
22
22
  //
23
23
  // override A framework built on Express requires it in its own code, so there is no specifier
24
24
  // to rewrite. Every package manager can answer `express` with this package instead,
25
25
  // and each one spells it differently.
26
26
  // angular An Angular server bundle is built with esbuild, which inlines every dependency and
27
27
  // cannot load uWS's native binary. Two names in `externalDependencies` fix it.
28
+ // pnpm pnpm 10.26 and later refuse a git dependency of a dependency, and µWebSockets.js
29
+ // is one. A direct dependency is allowed, so the project takes it on itself and an
30
+ // override drops the copy this package asks for.
28
31
 
29
32
  "use strict";
30
33
 
@@ -37,6 +40,12 @@ const REPLACES = "express";
37
40
  /** The major this package tracks, which is the range an override should ask for. */
38
41
  const MAJOR = require("../package.json").version.split(".")[0];
39
42
 
43
+ const UWS = "uWebSockets.js";
44
+ /** The git spec this package pins, which is what a project taking uWS on itself has to pin too. */
45
+ const UWS_SPEC = /** @type {string} */ (require("../package.json").dependencies[UWS]);
46
+ /** The pnpm override that drops this package's own copy, so the project's direct one is the only one. */
47
+ const UWS_OVERRIDE = `${SELF}>${UWS}`;
48
+
40
49
  /** Where each manager keeps its substitutions, and what to call it when telling someone. */
41
50
  const MANAGERS = {
42
51
  npm: { keys: ["overrides"], reinstall: "npm install" },
@@ -315,4 +324,106 @@ function angular(argv) {
315
324
  return 0;
316
325
  }
317
326
 
318
- module.exports = { override, angular, detectManager, serverBuilds, indentOf };
327
+ /**
328
+ * The pnpm-workspace.yaml with the override in it. Written by hand rather than through a YAML
329
+ * library: the file is small, the block is two lines, and what is already there is kept as is.
330
+ *
331
+ * @param {string} source the file as it is, or "" when there is none
332
+ * @returns {string|undefined} the new file, or nothing when the override is already there
333
+ */
334
+ function withPnpmOverride(source) {
335
+ const line = ` "${UWS_OVERRIDE}": "-"`;
336
+ if (
337
+ new RegExp(
338
+ `^\\s*["']?${SELF.replace(".", "\\.")}>${UWS.replace(".", "\\.")}["']?:\\s*["']?-["']?\\s*$`,
339
+ "m"
340
+ ).test(source)
341
+ ) {
342
+ return undefined;
343
+ }
344
+ const lines = source.length ? source.replace(/\r\n/g, "\n").replace(/\n*$/, "").split("\n") : [];
345
+ const at = lines.findIndex((one) => /^overrides:\s*$/.test(one));
346
+ if (at === -1) {
347
+ return [...lines, ...(lines.length ? [""] : []), "overrides:", line, ""].join("\n");
348
+ }
349
+ lines.splice(at + 1, 0, line);
350
+ return lines.join("\n") + "\n";
351
+ }
352
+
353
+ /**
354
+ * npx fulmine.js pnpm [dir] [--dry-run]
355
+ *
356
+ * Makes a pnpm project install this package, which pnpm 10.26 and later otherwise refuse: the
357
+ * project takes µWebSockets.js as a direct dependency, at the spec this package pins, and an
358
+ * override in pnpm-workspace.yaml drops the copy this package asks for. pnpm 11 reads its
359
+ * settings from that file only, so package.json's `pnpm` field is not where this goes.
360
+ *
361
+ * @param {string[]} argv everything after the command name
362
+ * @returns {number} exit code
363
+ */
364
+ function pnpm(argv) {
365
+ const dryRun = argv.includes("--dry-run");
366
+ const dir = path.resolve(argv.find((arg) => !arg.startsWith("--")) ?? ".");
367
+ const file = path.join(dir, "package.json");
368
+ const workspaceFile = path.join(dir, "pnpm-workspace.yaml");
369
+
370
+ const read = readJson(file);
371
+ if ("error" in read) {
372
+ console.error(read.code === "ENOENT" ? `no package.json in ${dir}` : read.error);
373
+ return 1;
374
+ }
375
+ const { data: pkg, source } = read;
376
+
377
+ let changed = 0;
378
+ const existing = readPath(pkg, ["dependencies", UWS]);
379
+ if (existing === UWS_SPEC) {
380
+ console.log(`dependencies.${UWS} already says ${UWS_SPEC}`);
381
+ } else {
382
+ if (existing !== undefined) {
383
+ console.log(`dependencies.${UWS} says ${JSON.stringify(existing)}, and this package pins ${UWS_SPEC}:`);
384
+ console.log("the pin is what the code here was tested against, so it is the one written.");
385
+ }
386
+ writePath(pkg, ["dependencies", UWS], UWS_SPEC);
387
+ changed++;
388
+ console.log(`${dryRun ? "would add" : "added"} to package.json: "dependencies": { "${UWS}": "${UWS_SPEC}" }`);
389
+ if (!dryRun) fs.writeFileSync(file, JSON.stringify(pkg, null, indentOf(source)) + "\n");
390
+ }
391
+
392
+ let workspace = "";
393
+ try {
394
+ workspace = fs.readFileSync(workspaceFile, "utf8");
395
+ } catch {
396
+ // no workspace file yet, it is written below
397
+ }
398
+ const rewritten = withPnpmOverride(workspace);
399
+ if (rewritten === undefined) {
400
+ console.log(`pnpm-workspace.yaml already overrides ${UWS_OVERRIDE}`);
401
+ } else {
402
+ changed++;
403
+ console.log(`${dryRun ? "would add" : "added"} to pnpm-workspace.yaml: overrides: { "${UWS_OVERRIDE}": "-" }`);
404
+ if (!dryRun) fs.writeFileSync(workspaceFile, rewritten);
405
+ }
406
+
407
+ if (!changed) {
408
+ console.log("\nNothing to change.");
409
+ return 0;
410
+ }
411
+ console.log(`\nThen \`pnpm install\`. µWebSockets.js is now the project's own dependency, so it is fetched as a`);
412
+ console.log("direct one, which pnpm allows, and the copy this package asks for is dropped from the graph.");
413
+ console.log(
414
+ `When this package moves its pin, run this again: \`npx fulmine.js verify\` says when the two differ.\n`
415
+ );
416
+ return 0;
417
+ }
418
+
419
+ module.exports = {
420
+ override,
421
+ angular,
422
+ pnpm,
423
+ detectManager,
424
+ serverBuilds,
425
+ indentOf,
426
+ withPnpmOverride,
427
+ UWS_SPEC,
428
+ UWS_OVERRIDE
429
+ };
@@ -62,6 +62,9 @@ class FSWorker {
62
62
  this.worker = new Worker(path.join(__dirname, "worker.js"));
63
63
 
64
64
  this.worker.on("message", (message) => {
65
+ // node speaks on this channel too: under --watch a worker reports the files it loaded
66
+ // as {"watch:import": [...]}, which carries no key of ours
67
+ if (workerTasks[message.key] === undefined) return;
65
68
  this.busy = false;
66
69
  if (message.err) {
67
70
  workerTasks[message.key].reject(new Error(message.err));
package/src/cli.js CHANGED
@@ -37,6 +37,15 @@ limitations under the License.
37
37
  // The two things a project needs that are a line in a JSON file rather than a specifier in a
38
38
  // source file: the package manager substitution, for a framework that requires express in its own
39
39
  // code, and angular.json's externalDependencies. See src/adopt.js.
40
+ //
41
+ // npx fulmine create <dir>
42
+ //
43
+ // A new project for whoever has nothing to migrate: a server, a package.json and the Dockerfile
44
+ // that works. See src/create.js.
45
+ //
46
+ // npx fulmine pnpm [dir]
47
+ //
48
+ // The two lines a pnpm project needs before it will install this at all. See src/adopt.js.
40
49
 
41
50
  const fs = require("fs");
42
51
  const path = require("path");
@@ -44,7 +53,8 @@ const acorn = require("acorn");
44
53
  // the same walk express.testing asserts on, so the command and the assertions cannot drift
45
54
  const { collectRoutes } = require("./testing.js");
46
55
  const { verify } = require("./verify.js");
47
- const { override, angular } = require("./adopt.js");
56
+ const { override, angular, pnpm } = require("./adopt.js");
57
+ const { create } = require("./create.js");
48
58
 
49
59
  /** @typedef {import("./application.js").Application} Application */
50
60
  /** @typedef {import("./router-utils.js").RouteEntry} RouteEntry */
@@ -910,13 +920,23 @@ function main(argv) {
910
920
  if (command === "angular") {
911
921
  return angular(argv.slice(1));
912
922
  }
923
+ if (command === "create") {
924
+ return create(argv.slice(1));
925
+ }
926
+ if (command === "pnpm") {
927
+ return pnpm(argv.slice(1));
928
+ }
913
929
  if (command !== "migrate") {
914
930
  console.log(`Usage:
931
+ npx ${TO} create <dir> start a new project: a server, a package.json and a Dockerfile that
932
+ works, --ts for TypeScript, --pnpm for the two lines pnpm needs
915
933
  npx ${TO} migrate [dir] rewrite require("${FROM}") and import from "${FROM}" to "${TO}"
916
934
  npx ${TO} override [dir] answer ${FROM} with this package for the whole dependency tree, for
917
935
  when a framework requires ${FROM} in its own code and not in yours
918
936
  npx ${TO} angular [dir] declare this package external in angular.json's server build, which
919
937
  esbuild otherwise tries to inline a native binary into
938
+ npx ${TO} pnpm [dir] make a pnpm project install this: pnpm 10.26 and later refuse a git
939
+ dependency of a dependency, and µWebSockets.js is one
920
940
  npx ${TO} profile [entry] load an application without listening and print what compiling
921
941
  its routes decided, route by route
922
942
  npx ${TO} explain <route> what happens when a request for that route arrives
@@ -924,7 +944,7 @@ function main(argv) {
924
944
  npx ${TO} differences print what behaves differently, without changing anything
925
945
 
926
946
  Options:
927
- --dry-run migrate, override, angular: say what would change and change nothing`);
947
+ --dry-run migrate, override, angular, pnpm: say what would change and change nothing`);
928
948
  return command ? 1 : 0;
929
949
  }
930
950
 
package/src/create.js ADDED
@@ -0,0 +1,248 @@
1
+ /*
2
+ Copyright 2026 Nigro Simone
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ // npx fulmine.js create <dir> [--ts] [--pnpm]
18
+ //
19
+ // A new project, for whoever has no Express application to migrate. `migrate` and `override` start
20
+ // from somebody's code; this starts from nothing and writes the few files a first run needs: a
21
+ // server, a package.json, and the Dockerfile that works, since the base image is the one thing a
22
+ // Dockerfile written for Express gets wrong here. Nothing is installed, that is the user's call.
23
+
24
+ "use strict";
25
+
26
+ const fs = require("fs");
27
+ const path = require("path");
28
+ const { withPnpmOverride, UWS_SPEC } = require("./adopt.js");
29
+
30
+ const SELF = "fulmine.js";
31
+
32
+ /** The major this package tracks, which is the range a new project should ask for. */
33
+ const MAJOR = require("../package.json").version.split(".")[0];
34
+
35
+ /**
36
+ * The node major the Dockerfile names: the one running this when it has a µWS binary, which is the
37
+ * even lines, and the newest supported one otherwise.
38
+ *
39
+ * @returns {number}
40
+ */
41
+ function nodeMajor() {
42
+ const running = Number(process.versions.node.split(".")[0]);
43
+ return running >= 22 && running % 2 === 0 ? running : 26;
44
+ }
45
+
46
+ /**
47
+ * The server, in the flavour asked for. The same three routes either way: a page, a JSON answer
48
+ * with a parameter, and a body read by the built-in parser.
49
+ *
50
+ * @param {boolean} ts
51
+ * @returns {string}
52
+ */
53
+ function serverSource(ts) {
54
+ const types = ts ? 'import type { Request, Response } from "fulmine.js";\n' : "";
55
+ const params = ts ? "(req: Request, res: Response)" : "(req, res)";
56
+ return `import express from "${SELF}";
57
+ ${types}
58
+ const app = express();
59
+ const port = Number(process.env.PORT ?? 3000);
60
+
61
+ app.use(express.json());
62
+ app.use(express.static("public"));
63
+
64
+ app.get("/api/hello", ${params} => {
65
+ res.json({ hello: "world" });
66
+ });
67
+
68
+ app.get("/api/items/:id", ${params} => {
69
+ res.json({ id: req.params.id });
70
+ });
71
+
72
+ app.post("/api/items", ${params} => {
73
+ res.status(201).json(req.body);
74
+ });
75
+
76
+ app.listen(port, () => {
77
+ console.log(\`listening on http://localhost:\${port}\`);
78
+ });
79
+ `;
80
+ }
81
+
82
+ /**
83
+ * @param {string} name the package name, from the directory
84
+ * @param {boolean} ts
85
+ * @param {boolean} pnpm the project also owns µWebSockets.js, see `npx fulmine.js pnpm`
86
+ * @returns {string}
87
+ */
88
+ function packageSource(name, ts, pnpm) {
89
+ const pkg = {
90
+ name,
91
+ version: "0.1.0",
92
+ private: true,
93
+ type: "module",
94
+ scripts: ts
95
+ ? {
96
+ build: "tsc",
97
+ start: "node dist/server.js",
98
+ dev: "node --watch --experimental-strip-types src/server.ts"
99
+ }
100
+ : { start: "node server.js", dev: "node --watch server.js" },
101
+ dependencies: { [SELF]: `^${MAJOR}`, ...(pnpm ? { "uWebSockets.js": UWS_SPEC } : {}) },
102
+ ...(ts ? { devDependencies: { "@types/node": `^${nodeMajor()}`, typescript: "^5" } } : {}),
103
+ engines: { node: ">=22" }
104
+ };
105
+ return JSON.stringify(pkg, null, 4) + "\n";
106
+ }
107
+
108
+ const TSCONFIG = `{
109
+ "compilerOptions": {
110
+ "target": "es2022",
111
+ "module": "nodenext",
112
+ "outDir": "dist",
113
+ "rootDir": "src",
114
+ "strict": true,
115
+ "verbatimModuleSyntax": true,
116
+ "skipLibCheck": true,
117
+ "types": ["node"]
118
+ },
119
+ "include": ["src"]
120
+ }
121
+ `;
122
+
123
+ /**
124
+ * Install with the full image, which has git for the µWS fetch, run with the slim one. trixie or
125
+ * newer on both, since bookworm's glibc is too old for the binary and Alpine has no build at all.
126
+ *
127
+ * @param {boolean} ts
128
+ * @param {boolean} pnpm install with pnpm, whose lockfile and workspace file come along
129
+ * @returns {string}
130
+ */
131
+ function dockerfileSource(ts, pnpm) {
132
+ const major = nodeMajor();
133
+ const manifests = pnpm ? "package.json pnpm-lock.yaml pnpm-workspace.yaml" : "package*.json";
134
+ const install = pnpm ? "npm install -g pnpm@11 && pnpm install --frozen-lockfile" : "npm ci";
135
+ const build = ts
136
+ ? `COPY ${manifests} tsconfig.json ./
137
+ RUN ${install}
138
+ COPY src ./src
139
+ RUN ${pnpm ? "pnpm run build && pnpm prune --prod" : "npm run build && npm prune --omit=dev"}`
140
+ : `COPY ${manifests} ./
141
+ RUN ${install}${pnpm ? " --prod" : " --omit=dev"}`;
142
+ const run = ts
143
+ ? `COPY --from=build /app/node_modules ./node_modules
144
+ COPY --from=build /app/dist ./dist
145
+ COPY package.json ./
146
+ COPY public ./public
147
+ EXPOSE 3000
148
+ CMD ["node", "dist/server.js"]`
149
+ : `COPY --from=build /app/node_modules ./node_modules
150
+ COPY . .
151
+ EXPOSE 3000
152
+ CMD ["node", "server.js"]`;
153
+ return `FROM node:${major}-trixie AS build
154
+ WORKDIR /app
155
+ ${build}
156
+
157
+ FROM node:${major}-trixie-slim
158
+ WORKDIR /app
159
+ ENV NODE_ENV=production
160
+ ${run}
161
+ `;
162
+ }
163
+
164
+ const INDEX_HTML = `<!doctype html>
165
+ <meta charset="utf-8">
166
+ <title>fulmine.js</title>
167
+ <p>Served by <code>express.static()</code>. The API answers on <a href="/api/hello">/api/hello</a>.</p>
168
+ `;
169
+
170
+ /**
171
+ * The files a new project is made of, by path.
172
+ *
173
+ * @param {string} name
174
+ * @param {boolean} ts
175
+ * @param {boolean} pnpm
176
+ * @returns {Record<string, string>}
177
+ */
178
+ function projectFiles(name, ts, pnpm) {
179
+ return {
180
+ "package.json": packageSource(name, ts, pnpm),
181
+ [ts ? "src/server.ts" : "server.js"]: serverSource(ts),
182
+ ...(ts ? { "tsconfig.json": TSCONFIG } : {}),
183
+ // pnpm refuses a git dependency of a dependency, so the project owns µWebSockets.js itself
184
+ ...(pnpm ? { "pnpm-workspace.yaml": /** @type {string} */ (withPnpmOverride("")) } : {}),
185
+ "public/index.html": INDEX_HTML,
186
+ Dockerfile: dockerfileSource(ts, pnpm),
187
+ ".dockerignore": "node_modules\n.git\ndist\n",
188
+ ".gitignore": "node_modules\ndist\n"
189
+ };
190
+ }
191
+
192
+ /**
193
+ * `npx fulmine.js create <dir> [--ts] [--pnpm]`
194
+ *
195
+ * pnpm is also read from how this was started: `pnpm dlx fulmine.js create` says so in the user
196
+ * agent npm and pnpm both set, and a project started that way would otherwise fail its first install.
197
+ *
198
+ * @param {string[]} argv everything after the command
199
+ * @returns {number} exit code
200
+ */
201
+ function create(argv) {
202
+ const ts = argv.includes("--ts");
203
+ const pnpm = argv.includes("--pnpm") || /^pnpm\//.test(process.env.npm_config_user_agent ?? "");
204
+ const given = argv.find((arg) => !arg.startsWith("--"));
205
+ if (!given) {
206
+ console.error(`Usage: npx ${SELF} create <dir> [--ts] [--pnpm]`);
207
+ return 1;
208
+ }
209
+ const dir = path.resolve(given);
210
+ // a name npm accepts: what the directory is called, lowercased, anything else a dash
211
+ const name =
212
+ path
213
+ .basename(dir)
214
+ .toLowerCase()
215
+ .replace(/[^a-z0-9._-]+/g, "-") || "app";
216
+
217
+ if (fs.existsSync(dir) && fs.readdirSync(dir).length) {
218
+ console.error(`${dir} is not empty. Nothing was written: this only starts a project, it does not join one.`);
219
+ return 1;
220
+ }
221
+
222
+ const files = projectFiles(name, ts, pnpm);
223
+ for (const [file, content] of Object.entries(files)) {
224
+ const full = path.join(dir, file);
225
+ fs.mkdirSync(path.dirname(full), { recursive: true });
226
+ fs.writeFileSync(full, content);
227
+ console.log(`wrote ${path.join(given, file)}`);
228
+ }
229
+
230
+ const pm = pnpm ? "pnpm" : "npm";
231
+ console.log(`\nNext:\n`);
232
+ console.log(` cd ${given}`);
233
+ console.log(` ${pm} install`);
234
+ console.log(` ${pm} run dev${ts ? ` # or ${pm} run build && ${pm} start` : ""}\n`);
235
+ if (pnpm) {
236
+ console.log("pnpm refuses a git dependency of a dependency, and µWebSockets.js is one, so this project owns");
237
+ console.log(
238
+ "it: the pin in package.json and the override in pnpm-workspace.yaml. See docs/deployment.md#pnpm."
239
+ );
240
+ }
241
+ console.log(
242
+ `The Dockerfile uses node:${nodeMajor()}-trixie, since Alpine and bookworm cannot load µWebSockets.js.`
243
+ );
244
+ console.log(`\`npx ${SELF} verify\` says whether this machine can, before you install anything.\n`);
245
+ return 0;
246
+ }
247
+
248
+ module.exports = { create, projectFiles };
package/src/index.js CHANGED
@@ -21,6 +21,28 @@ limitations under the License.
21
21
  // package ships, so the module is read through a loose alias
22
22
  const uWS = require("uWebSockets.js");
23
23
  const uWSAny = /** @type {any} */ (uWS);
24
+
25
+ // A project on pnpm owns the uWebSockets.js dependency itself, see `npx fulmine.js pnpm`, so the
26
+ // one installed can drift from the one this package pins and was tested against. Said once, at
27
+ // require time, where it reaches every deployment rather than only the ones that run verify.
28
+ {
29
+ const pinned = /#v?([\d.]+)$/.exec(require("../package.json").dependencies["uWebSockets.js"])?.[1];
30
+ /** @type {string|undefined} */
31
+ let installed;
32
+ try {
33
+ // its exports map does not expose package.json, so it is read beside the entry point
34
+ const beside = require("path").join(require.resolve("uWebSockets.js"), "..", "package.json");
35
+ installed = JSON.parse(require("fs").readFileSync(beside, "utf8")).version;
36
+ } catch {
37
+ // nothing to compare against, which is not worth a warning of its own
38
+ }
39
+ if (pinned && installed && installed !== pinned) {
40
+ console.warn(
41
+ `fulmine.js: uWebSockets.js ${installed} is installed, this version was tested with ${pinned}.\n` +
42
+ " On pnpm the pin is the project's own: `npx fulmine.js pnpm` writes the tested one."
43
+ );
44
+ }
45
+ }
24
46
  const Application = require("./application.js");
25
47
  const Router = require("./router.js");
26
48
  const Route = require("./route.js");