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 +1 -1
- package/src/setupDocs.js +1 -32
- package/src/setupRoutes.js +67 -27
- package/templates/ai/BASE +2 -0
- package/templates/package.json +1 -16
package/package.json
CHANGED
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 šØāš»",
|
package/src/setupRoutes.js
CHANGED
|
@@ -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
|
|
61
|
+
// 1. Pass One: Register Named Exports Globally
|
|
62
|
+
const globals = new Map();
|
|
69
63
|
for (const file of files) {
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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:
|
|
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
|
-
//
|
|
88
|
-
const
|
|
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
|
|
96
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
}
|
package/templates/package.json
CHANGED
|
@@ -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
|
}
|