maxserver 0.8.2 → 0.8.3

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,6 +1,6 @@
1
1
  {
2
2
  "name": "maxserver",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Node server setup based fastify",
5
5
  "author": "Max Matinpalo",
6
6
  "type": "module",
package/src/setupDocs.js CHANGED
@@ -2,37 +2,6 @@ import swagger from "@fastify/swagger";
2
2
  import apiReference from "@scalar/fastify-api-reference";
3
3
 
4
4
 
5
- /*
6
- const schema = {
7
- summary: "OpenAPI Specification",
8
- description: "Returns the full OpenAPI 3.0 specification.",
9
- tags: ["Docs"],
10
- response: {
11
- 200: {
12
- type: "object",
13
- additionalProperties: true,
14
- required: ["openapi", "info", "paths"],
15
- properties: {
16
- openapi: { type: "string", example: "3.0.3" },
17
- info: {
18
- type: "object",
19
- required: ["title", "version"],
20
- properties: {
21
- title: { type: "string", example: "API" },
22
- version: { type: "string", example: "1.0.0" }
23
- }
24
- },
25
- paths: { type: "object", example: {} }
26
- }
27
- }
28
- }
29
- };
30
- */
31
-
32
-
33
-
34
-
35
-
36
5
  export async function setupDocs(app) {
37
6
 
38
7
  const info = app.maxserver.openapiInfo || {
@@ -76,7 +45,7 @@ export async function setupDocs(app) {
76
45
  persistAuth: true,
77
46
  showDeveloperTools: "never",
78
47
  //"expandAllModelSections": true,
79
- operationsSorter: "alpha",
48
+ // operationsSorter: "alpha",
80
49
  orderSchemaPropertiesBy: "preserve",
81
50
  metaData: {
82
51
  title: "API Docs šŸ‘Øā€šŸ’»",
@@ -54,64 +54,77 @@ function getRoute(file) {
54
54
  };
55
55
  }
56
56
 
57
- /**
58
- * Safe dynamic import for ESM.
59
- */
60
- async function importDefault(file) {
61
- return (await import(pathToFileURL(file).href)).default;
62
- }
63
-
64
57
  export async function setupRoutes(app) {
65
58
  const root = path.resolve(app.maxserver.routesDir || "src");
66
59
  const files = walk(root);
67
60
 
68
- // 1. Pass One: Register Global Schemas (Lonely .schema.js files)
61
+ // 1. Pass One: Register Named Exports Globally
62
+ const globals = new Map();
69
63
  for (const file of files) {
70
- // Only process if no matching handler file exists
71
- if (file.endsWith(".schema.js") && !fs.existsSync(file.replace(".schema.js", ".js"))) {
72
- const mod = await import(pathToFileURL(file).href);
64
+ if (file.endsWith(".schema.js")) continue;
73
65
 
74
- // Register default export + all named exports if they have an $id
75
- for (const schema of Object.values(mod))
76
- if (schema?.$id) app.addSchema(schema);
66
+ const mod = await import(pathToFileURL(file).href);
67
+ for (const [key, val] of Object.entries(mod)) {
68
+ if (key === "default" || key.startsWith("autoregister_")) continue;
69
+
70
+ if (globals.has(key)) {
71
+ console.error("\nāŒ Global Identifier Conflict!");
72
+ console.error(`The export "${key}" is defined in multiple files:`);
73
+ console.error(` -> ${globals.get(key)}`);
74
+ console.error(` -> ${file}\n`);
75
+ throw new Error(`Duplicate global identifier "${key}"`);
76
+ }
77
+
78
+ globals.set(key, file);
79
+ global[key] = val;
77
80
  }
78
81
  }
79
82
 
80
- // 2. Pass Two: Auto-register hooks
83
+ // 2. Pass Two: Register Global Schemas (Lonely .schema.js files)
81
84
  for (const file of files) {
85
+ const isLonely = file.endsWith(".schema.js") &&
86
+ !fs.existsSync(file.replace(".schema.js", ".js"));
87
+
88
+ if (!isLonely) continue;
89
+
90
+ const mod = await import(pathToFileURL(file).href);
91
+ for (const schema of Object.values(mod))
92
+ if (schema?.$id) app.addSchema(schema);
93
+ }
94
+
95
+ // 3. Pass Three: Auto-register hooks
96
+ for (const file of files) {
97
+ if (file.endsWith(".schema.js")) continue;
98
+
82
99
  const mod = await import(pathToFileURL(file).href);
83
100
  for (const [key, fn] of Object.entries(mod))
84
101
  if (key.startsWith("autoregister_") && typeof fn === "function") await fn(app);
85
102
  }
86
103
 
87
- // 3. Pass Three: Register Routes
88
- const seen = new Map();
104
+ // 4. Pass Four: Collect and Group Routes by Directory
105
+ const groups = new Map();
89
106
  for (const file of files) {
90
107
  if (file.endsWith(".schema.js")) continue;
91
108
 
92
109
  const info = getRoute(file);
93
110
  if (!info) continue;
94
111
 
95
- const key = `${info.method} ${info.url}`;
96
- if (seen.has(key)) throw new Error(`Duplicate route "${key}" detected.`);
97
- seen.set(key, file);
98
-
99
- const handler = await importDefault(file);
112
+ const mod = await import(pathToFileURL(file).href);
113
+ const handler = mod.default;
100
114
  if (typeof handler !== "function") {
101
- throw new Error(`Route "${key}" in "${file}" must export a default function.`);
115
+ throw new Error(`Route in "${file}" must export a default function.`);
102
116
  }
103
117
 
104
118
  const schemaFile = file.replace(/\.js$/, ".schema.js");
105
119
  let raw = {};
106
120
 
107
121
  if (fs.existsSync(schemaFile)) {
108
- const loaded = await importDefault(schemaFile);
122
+ const loaded = (await import(pathToFileURL(schemaFile).href)).default;
109
123
  if (loaded && typeof loaded === "object") raw = loaded;
110
124
  }
111
125
 
112
- let { auth, routeOptions = {}, ...schema } = raw;
126
+ let { auth, order = 999, routeOptions = {}, ...schema } = raw;
113
127
 
114
- // Inject 'auth' config for the global authentication hook
115
128
  if (auth !== undefined) {
116
129
  routeOptions = {
117
130
  ...routeOptions,
@@ -119,6 +132,33 @@ export async function setupRoutes(app) {
119
132
  };
120
133
  }
121
134
 
122
- app[info.method](info.url, { ...routeOptions, schema }, handler);
135
+ const dir = path.dirname(file);
136
+ if (!groups.has(dir)) groups.set(dir, []);
137
+
138
+ groups.get(dir).push({
139
+ method: info.method,
140
+ url: info.url,
141
+ options: { ...routeOptions, schema },
142
+ handler,
143
+ order,
144
+ file
145
+ });
146
+ }
147
+
148
+ // Sort routes locally within each directory
149
+ const routes = [];
150
+ for (const list of groups.values()) {
151
+ list.sort((a, b) => a.order - b.order);
152
+ routes.push(...list);
153
+ }
154
+
155
+ // 5. Pass Five: Register Sorted Routes
156
+ const seen = new Map();
157
+ for (const r of routes) {
158
+ const key = `${r.method} ${r.url}`;
159
+ if (seen.has(key)) throw new Error(`Duplicate route "${key}" detected.`);
160
+ seen.set(key, r.file);
161
+
162
+ app[r.method](r.url, r.options, r.handler);
123
163
  }
124
164
  }
@@ -0,0 +1,2 @@
1
+ @node
2
+ @js
@@ -9,20 +9,5 @@
9
9
  },
10
10
  "type": "module",
11
11
  "private": "true",
12
- "dependencies": {},
13
- "keywords": [
14
- "fastify",
15
- "backend",
16
- "framework",
17
- "node-server",
18
- "api-server",
19
- "auto-routes",
20
- "automatic-docs",
21
- "scalar",
22
- "openapi",
23
- "jwt-auth",
24
- "mongodb",
25
- "minimalist",
26
- "zero-boilerplate"
27
- ]
12
+ "dependencies": {}
28
13
  }