create-nexwire 4.2.0 → 4.2.1

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.
@@ -127,8 +127,16 @@ export function createRouter(): NexwireRouter {
127
127
  const nativeRoute = router.route.bind(router);
128
128
  states.set(router, { routes: [], children: [] });
129
129
 
130
+ // Group middlewares are kept here rather than registered with router.use(),
131
+ // because a catch-all use() also matches requests whose route lives in a
132
+ // sibling router or in the parent. They are merged into every .api() call
133
+ // instead, so they travel with the routes they were declared on.
134
+ const groupMiddlewares: RequestHandler[] = [];
135
+
130
136
  router.group = function (...middlewares: RequestHandler[]) {
131
- if (middlewares.length) router.use(...middlewares);
137
+ for (const middleware of middlewares) {
138
+ groupMiddlewares.push(middleware);
139
+ }
132
140
  return this;
133
141
  };
134
142
 
@@ -140,7 +148,13 @@ export function createRouter(): NexwireRouter {
140
148
  throw new Error("api(route, middlewares, handler) requires a handler function");
141
149
  }
142
150
 
143
- (router as any)[route.method](expressPath(route.path), validationMiddleware(route), ...middlewares, finalHandler);
151
+ (router as any)[route.method](
152
+ expressPath(route.path),
153
+ validationMiddleware(route),
154
+ ...groupMiddlewares,
155
+ ...middlewares,
156
+ finalHandler
157
+ );
144
158
  stateOf(router).routes.push(route);
145
159
  return this;
146
160
  };
@@ -13,6 +13,6 @@ export default async function {{ClassName}}Seeder() {
13
13
 
14
14
  for (const row of rows) await db.insert({{tableVariable}}).values(row);
15
15
 
16
- console.log("{{ClassName}}: " + rows.length + " row(s) prepared");
16
+ console.log(`{{ClassName}}: ${rows.length} row(s) prepared`);
17
17
  }
18
18
 
@@ -18,5 +18,5 @@ export default async function {{ClassName}}Seeder() {
18
18
 
19
19
  // for (const row of rows) await db.insert({{tableVariable}}).values(row);
20
20
 
21
- console.log("{{ClassName}}: " + rows.length + " row(s) ready");
21
+ console.log(`{{ClassName}}: ${rows.length} row(s) ready`);
22
22
  }
@@ -25,14 +25,14 @@ export default defineConfig({
25
25
  // framework's own tests under `src/framework/**/__tests__/` are not
26
26
  // published, because they test the framework rather than the application.
27
27
  // That measures 8.49% statements, so a floor calibrated for this repository
28
- // (14%) reports a failure for a developer who has done nothing wrong and
29
- // whose 56 tests all pass.
28
+ // reports a failure for a developer who has done nothing wrong and whose
29
+ // 56 tests all pass.
30
30
  //
31
- // The floor is a guard on this repository, so it is applied by CI, which
32
- // passes --coverage.thresholds.* on the command line. That keeps this file
33
- // byte-identical across both engines, keeps it free of a number that would
34
- // be meaningless in someone else's project, and still fails the build when
35
- // untested code is added here.
31
+ // The floor is a guard on this repository, not on yours, so it is passed on
32
+ // the command line when the framework's own suite is run - it is never read
33
+ // from this file. That keeps this file byte-identical across both engines,
34
+ // keeps it free of a number that would be meaningless in someone else's
35
+ // project, and still fails when untested code is added here.
36
36
  }
37
37
  }
38
38
  });
@@ -11,47 +11,71 @@ export type NexwireRouter = OpenAPIHono & {
11
11
  * When: Building root app and module route files.
12
12
  * Where: HTTP app bootstrap and route stubs.
13
13
  * How: Wraps OpenAPIHono and adds group/api convenience APIs.
14
+ *
15
+ * group() records its middlewares here instead of calling this.use("*", mw).
16
+ * A use("*") is a route entry: mounting the router copies it and "*" rewrites
17
+ * to the mount prefix, so a limiter declared on one sub-router also ran for its
18
+ * siblings and for every route added after the mount. Recorded middlewares are
19
+ * merged into each .api() call instead, so they travel with the routes they
20
+ * were declared on and reach nothing else.
14
21
  */
15
22
  export function createRouter(): NexwireRouter {
16
23
  const router = new OpenAPIHono({ strict: false, defaultHook }) as NexwireRouter;
17
24
  const baseOpenapi = router.openapi.bind(router);
18
25
 
26
+ // Store group middlewares on the instance
27
+ const groupMiddlewares: any[] = [];
28
+
19
29
  router.group = function (...middlewares) {
30
+ // Accumulate group middlewares (supports multiple .group() calls)
20
31
  for (const middleware of middlewares) {
21
- this.use("*", middleware);
32
+ groupMiddlewares.push(middleware);
22
33
  }
23
-
24
34
  return this;
25
35
  };
26
36
 
27
37
  router.api = function (route: any, handlerOrMiddlewares: any, handler?: any) {
38
+ // Normalize arguments:
39
+ // .api(route, handler) → [groupMiddlewares], handler
40
+ // .api(route, [mw1, mw2], handler) → [groupMiddlewares, mw1, mw2], handler
41
+ let routeMiddlewares: any[] = [];
42
+ let finalHandler: any;
43
+
28
44
  if (!Array.isArray(handlerOrMiddlewares)) {
29
- baseOpenapi(route, handlerOrMiddlewares);
30
- return this;
45
+ finalHandler = handlerOrMiddlewares;
46
+ } else {
47
+ routeMiddlewares = handlerOrMiddlewares;
48
+ finalHandler = handler;
31
49
  }
32
50
 
33
- const middlewares = handlerOrMiddlewares;
34
- const finalHandler = handler;
51
+ // Combine: group middleware runs first, then route middleware
52
+ const allMiddlewares = [...groupMiddlewares, ...routeMiddlewares];
35
53
 
36
54
  if (typeof finalHandler !== "function") {
37
55
  throw new Error("api(route, middlewares, handler) requires a handler function");
38
56
  }
39
57
 
58
+ // No middleware at all → simple pass-through
59
+ if (allMiddlewares.length === 0) {
60
+ baseOpenapi(route, finalHandler);
61
+ return this;
62
+ }
63
+
64
+ // Wrap middleware chain into the handler — travels with the route,
65
+ // no matter where the router is mounted.
40
66
  const wrappedHandler = async (c: any) => {
41
67
  let index = 0;
42
68
 
43
69
  const next = async (): Promise<any> => {
44
- if (index < middlewares.length) {
45
- const middleware = middlewares[index++];
70
+ if (index < allMiddlewares.length) {
71
+ const middleware = allMiddlewares[index++];
46
72
  let downstream: any;
47
73
  const result = await middleware(c, async () => {
48
74
  downstream = await next();
49
75
  return downstream;
50
76
  });
51
-
52
77
  return result === undefined ? downstream : result;
53
78
  }
54
-
55
79
  return await finalHandler(c);
56
80
  };
57
81
 
@@ -66,10 +90,7 @@ export function createRouter(): NexwireRouter {
66
90
  }
67
91
 
68
92
  /**
69
- * Why: Shorthand for creating grouped router middleware chain.
70
- * When: Route files need shared middleware composition.
71
- * Where: Module route declarations.
72
- * How: Creates a router instance and applies all provided middlewares.
93
+ * Shorthand for creating a router with group middleware pre-applied.
73
94
  */
74
95
  export function group(...middlewares: any[]) {
75
96
  return createRouter().group(...middlewares);
@@ -13,6 +13,6 @@ export default async function {{ClassName}}Seeder() {
13
13
 
14
14
  for (const row of rows) await db.insert({{tableVariable}}).values(row);
15
15
 
16
- console.log("{{ClassName}}: " + rows.length + " row(s) prepared");
16
+ console.log(`{{ClassName}}: ${rows.length} row(s) prepared`);
17
17
  }
18
18
 
@@ -18,5 +18,5 @@ export default async function {{ClassName}}Seeder() {
18
18
 
19
19
  // for (const row of rows) await db.insert({{tableVariable}}).values(row);
20
20
 
21
- console.log("{{ClassName}}: " + rows.length + " row(s) ready");
21
+ console.log(`{{ClassName}}: ${rows.length} row(s) ready`);
22
22
  }
@@ -25,14 +25,14 @@ export default defineConfig({
25
25
  // framework's own tests under `src/framework/**/__tests__/` are not
26
26
  // published, because they test the framework rather than the application.
27
27
  // That measures 8.49% statements, so a floor calibrated for this repository
28
- // (14%) reports a failure for a developer who has done nothing wrong and
29
- // whose 56 tests all pass.
28
+ // reports a failure for a developer who has done nothing wrong and whose
29
+ // 56 tests all pass.
30
30
  //
31
- // The floor is a guard on this repository, so it is applied by CI, which
32
- // passes --coverage.thresholds.* on the command line. That keeps this file
33
- // byte-identical across both engines, keeps it free of a number that would
34
- // be meaningless in someone else's project, and still fails the build when
35
- // untested code is added here.
31
+ // The floor is a guard on this repository, not on yours, so it is passed on
32
+ // the command line when the framework's own suite is run - it is never read
33
+ // from this file. That keeps this file byte-identical across both engines,
34
+ // keeps it free of a number that would be meaningless in someone else's
35
+ // project, and still fails when untested code is added here.
36
36
  }
37
37
  }
38
38
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nexwire",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "type": "module",
5
5
  "description": "Scaffold a new nexwire project",
6
6
  "bin": {