maxserver 0.1.7 → 0.1.8

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/README.md CHANGED
@@ -229,11 +229,8 @@ Rule of thumb: make the message something you would want to see at 03:00 in logs
229
229
  ## 🛠️ Tips & Tools
230
230
 
231
231
  ### 🔌 Scalar API Client (Live Testing)
232
- - Download the great new api client
233
- - It autosyncs well with the server
234
- - Add Item → Import from OpenAPI
235
- - Paste: `http://localhost:3000/openapi.json`
236
- - Enable watch mode for live updates
232
+ - Open: `http://localhost:3000/docs`
233
+
237
234
 
238
235
  ### ⚡ VS Code Auto-Start
239
236
  In `.vscode/tasks.json`, enable the task with:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maxserver",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Node server setup based fastify",
5
5
  "author": "Max Matinpalo",
6
6
  "type": "module",
package/src/setupDocs.js CHANGED
@@ -2,6 +2,42 @@ 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 for this API in JSON format.",
9
+ tags: ["Docs"],
10
+ response: {
11
+ 200: {
12
+ type: "object",
13
+ additionalProperties: true,
14
+ properties: {
15
+ openapi: {
16
+ type: "string",
17
+ example: "3.0.3"
18
+ },
19
+ info: {
20
+ type: "object",
21
+ properties: {
22
+ title: { type: "string", example: "maxserver API" },
23
+ version: { type: "string", example: "1.0.0" }
24
+ },
25
+ required: ["title", "version"]
26
+ },
27
+ paths: {
28
+ type: "object",
29
+ example: {}
30
+ }
31
+ },
32
+ required: ["openapi", "info", "paths"]
33
+ }
34
+ }
35
+ };
36
+
37
+
38
+
39
+
40
+
5
41
  export async function setupDocs(app) {
6
42
 
7
43
  const info = app.maxserver.openapiInfo || {
@@ -20,15 +56,11 @@ export async function setupDocs(app) {
20
56
  cookieAuth: { type: "apiKey", in: "cookie", name: "token" },
21
57
  },
22
58
  },
23
-
24
- // This replaces your manual app.get line
25
- exposeRoute: true,
26
- routePrefix: "/openapi.json"
27
59
  },
28
60
  });
29
61
 
30
62
 
31
- //app.get("/openapi.json", {}, () => app.swagger());
63
+ app.get("/openapi.json", { schema }, () => app.swagger());
32
64
 
33
65
  if (app.maxserver.docs !== false)
34
66
  await app.register(apiReference, { routePrefix: "/docs", openapi: true });
@@ -44,42 +76,3 @@ export async function setupDocs(app) {
44
76
 
45
77
 
46
78
 
47
- export default {
48
- summary: "OpenAPI Documentation",
49
- description: "Returns the OpenAPI 3.0.0 specification for the entire API.",
50
- tags: ["Documentation"],
51
- response: {
52
- 200: {
53
- type: "object",
54
- properties: {
55
- openapi: {
56
- type: "string",
57
- example: "3.0.0"
58
- },
59
- info: {
60
- type: "object",
61
- properties: {
62
- title: {
63
- type: "string",
64
- example: "maxserver API"
65
- },
66
- version: {
67
- type: "string",
68
- example: "1.0.0"
69
- }
70
- },
71
- required: ["title", "version"]
72
- },
73
- paths: {
74
- type: "object",
75
- example: {}
76
- },
77
- components: {
78
- type: "object",
79
- example: {}
80
- }
81
- },
82
- required: ["openapi", "info", "paths"]
83
- }
84
- }
85
- };
@@ -1,132 +0,0 @@
1
-
2
- /**
3
- * 🚀 AUTO-LOADER
4
- * Scans src/ for files with "// METHOD /url" comments.
5
- * Automatically registers them as Fastify routes.
6
- */
7
-
8
-
9
- import fs from "fs";
10
- import path from "path";
11
- import { pathToFileURL } from "url";
12
-
13
-
14
- // Matches lines like: // GET /api/v1/users
15
- const ROUTE_REGEX = /^\/\/\s*(GET|POST|PUT|PATCH|DELETE)\s+(.+)$/gm;
16
-
17
-
18
- /**
19
- * Recursively finds all .js files in a directory.
20
- * Skips node_modules and dotfiles.
21
- */
22
- function walk(dir, out = []) {
23
- if (!fs.existsSync(dir)) return out;
24
-
25
- for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
26
- if (e.name === "node_modules") continue;
27
- if (e.name.startsWith(".")) continue;
28
-
29
- const full = path.join(dir, e.name);
30
- if (e.isDirectory()) {
31
- walk(full, out);
32
- continue;
33
- }
34
-
35
- if (!e.name.endsWith(".js")) continue;
36
- out.push(full);
37
- }
38
-
39
- return out;
40
- }
41
-
42
-
43
- /**
44
- * Extracts method and URL from the file's "magic comment".
45
- * Enforces strict "One Route Per File" policy.
46
- */
47
- function getRoute(file) {
48
- const text = fs.readFileSync(file, "utf8");
49
- const matches = [...text.matchAll(ROUTE_REGEX)];
50
-
51
- if (matches.length === 0) return null;
52
-
53
- // Warn if user accidentally defines multiple routes in one file
54
- if (matches.length > 1) {
55
- console.warn(
56
- `⚠️ Ignored "${file}": Found ${matches.length} route comments. ` +
57
- `Only 1 allowed per file.`
58
- );
59
- return null;
60
- }
61
-
62
- const m = matches[0];
63
- const method = m[1].toLowerCase();
64
- // Normalize URL: Remove all leading slashes, then add exactly one
65
- const url = "/" + m[2].trim().replace(/^\/+/, "");
66
-
67
- return { method, url };
68
- }
69
-
70
-
71
- /**
72
- * Safe dynamic import that handles Windows paths correctly.
73
- */
74
- async function importDefault(file) {
75
- return (await import(pathToFileURL(file).href)).default;
76
- }
77
-
78
-
79
- export async function setupRoutes(app) {
80
- const seen = new Map();
81
- const root = path.resolve(app.maxserver.routesDir || "src");
82
-
83
- for (const file of walk(root)) {
84
- // Skip schema files; they are loaded alongside their route file
85
- if (file.endsWith(".schema.js")) continue;
86
-
87
- const info = getRoute(file);
88
- if (!info) continue;
89
-
90
- // 🛡️ Collision Detection: Ensure no two files claim the same route
91
- const key = info.method + " " + info.url;
92
- if (seen.has(key)) {
93
- throw new Error(
94
- `Duplicate route "${key}" detected:\n` +
95
- `1. ${seen.get(key)}\n` +
96
- `2. ${file}`
97
- );
98
- }
99
- seen.set(key, file);
100
-
101
- // Import the route handler
102
- const handler = await importDefault(file);
103
- if (typeof handler !== "function") {
104
- throw new Error(
105
- `Route "${key}" in "${file}" must export a default function.`
106
- );
107
- }
108
-
109
- // 🤝 Schema Loading: Look for sibling .schema.js file
110
- const schemaFile = file.replace(/\.js$/, ".schema.js");
111
- let raw = {};
112
-
113
- if (fs.existsSync(schemaFile)) {
114
- const loaded = await importDefault(schemaFile);
115
- // 🛡️ Guard: Ensure export is a valid object before using it
116
- if (loaded && typeof loaded === "object") raw = loaded;
117
- }
118
-
119
- // ✨ Magic: Extract 'auth' and 'routeOptions' specifically
120
- let { auth, routeOptions = {}, ...schema } = raw;
121
-
122
- // Inject 'auth' into config if present (Syntactic Sugar)
123
- if (auth !== undefined) {
124
- routeOptions = {
125
- ...routeOptions,
126
- config: { ...(routeOptions.config || {}), auth: !!auth },
127
- };
128
- }
129
-
130
- app[info.method](info.url, { ...routeOptions, schema }, handler);
131
- }
132
- }