sprint-es 0.0.54 → 0.0.57
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/dist/cjs/cli.cjs +2 -4
- package/dist/cjs/index.cjs +99 -16
- package/dist/cjs/modules/schemas/index.cjs +10 -16
- package/dist/esm/cli.js +2 -4
- package/dist/esm/index.js +77 -16
- package/dist/esm/modules/schemas/index.js +10 -16
- package/dist/types/modules/schemas/index.d.ts.map +1 -1
- package/dist/types/sprint.d.ts +2 -0
- package/dist/types/sprint.d.ts.map +1 -1
- package/dist/types/types.d.ts +8 -0
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +5 -1
package/dist/cjs/cli.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
const child_process = require("child_process");
|
|
4
3
|
const fs = require("fs");
|
|
5
4
|
const crypto = require("crypto");
|
|
6
5
|
const path = require("path");
|
|
6
|
+
const child_process = require("child_process");
|
|
7
7
|
function _interopNamespaceDefault(e) {
|
|
8
8
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
9
|
if (e) {
|
|
@@ -48,9 +48,7 @@ if (command === "--help" || command === "-h") {
|
|
|
48
48
|
function getProjectRoot() {
|
|
49
49
|
let dir = process.cwd();
|
|
50
50
|
while (dir !== path.resolve(dir, "..")) {
|
|
51
|
-
if (fs.existsSync(path.join(dir, "sprint.config.ts")) || fs.existsSync(path.join(dir, "sprint.config.js")))
|
|
52
|
-
return dir;
|
|
53
|
-
}
|
|
51
|
+
if (fs.existsSync(path.join(dir, "sprint.config.ts")) || fs.existsSync(path.join(dir, "sprint.config.js"))) return dir;
|
|
54
52
|
dir = path.resolve(dir, "..");
|
|
55
53
|
}
|
|
56
54
|
return process.cwd();
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
2
24
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
25
|
const express = require("express");
|
|
4
26
|
const fs = require("fs");
|
|
@@ -55,9 +77,7 @@ async function findProjectRoot(startDir) {
|
|
|
55
77
|
let currentDir = startDir;
|
|
56
78
|
while (currentDir !== path.parse(currentDir).root) {
|
|
57
79
|
const packageJsonPath = path.join(currentDir, "package.json");
|
|
58
|
-
if (fs.existsSync(packageJsonPath))
|
|
59
|
-
return currentDir;
|
|
60
|
-
}
|
|
80
|
+
if (fs.existsSync(packageJsonPath)) return currentDir;
|
|
61
81
|
currentDir = path.dirname(currentDir);
|
|
62
82
|
}
|
|
63
83
|
return null;
|
|
@@ -65,9 +85,7 @@ async function findProjectRoot(startDir) {
|
|
|
65
85
|
async function loadSprintConfig() {
|
|
66
86
|
const callerDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();
|
|
67
87
|
const projectRoot = await findProjectRoot(callerDir);
|
|
68
|
-
if (!projectRoot)
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
88
|
+
if (!projectRoot) return null;
|
|
71
89
|
const configFiles = ["sprint.config.ts", "sprint.config.js"];
|
|
72
90
|
for (const configFile of configFiles) {
|
|
73
91
|
const configPath = path.join(projectRoot, configFile);
|
|
@@ -93,7 +111,13 @@ class Sprint {
|
|
|
93
111
|
this.urlEncodedLimit = "50mb";
|
|
94
112
|
this.prefix = "";
|
|
95
113
|
this.loadedMiddlewares = [];
|
|
96
|
-
this.openapi = {
|
|
114
|
+
this.openapi = {
|
|
115
|
+
generateOnBuild: false,
|
|
116
|
+
swaggerUi: {
|
|
117
|
+
enabled: false
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
this.registeredRoutes = [];
|
|
97
121
|
this.app = express();
|
|
98
122
|
loadSprintConfig().then((config) => {
|
|
99
123
|
const defaults = {
|
|
@@ -106,7 +130,10 @@ class Sprint {
|
|
|
106
130
|
prefix: "",
|
|
107
131
|
autoListen: true,
|
|
108
132
|
openapi: {
|
|
109
|
-
generateOnBuild: false
|
|
133
|
+
generateOnBuild: false,
|
|
134
|
+
swaggerUi: {
|
|
135
|
+
enabled: false
|
|
136
|
+
}
|
|
110
137
|
}
|
|
111
138
|
};
|
|
112
139
|
const finalConfig = { ...defaults, ...config };
|
|
@@ -118,15 +145,29 @@ class Sprint {
|
|
|
118
145
|
this.urlEncodedLimit = finalConfig.urlEncodedLimit || "50mb";
|
|
119
146
|
this.prefix = finalConfig.prefix ? "/" + finalConfig.prefix.replace(/^\/+|\/+$/g, "") : "";
|
|
120
147
|
this.openapi = {
|
|
121
|
-
generateOnBuild: finalConfig.openapi?.generateOnBuild ?? false
|
|
148
|
+
generateOnBuild: finalConfig.openapi?.generateOnBuild ?? false,
|
|
149
|
+
swaggerUi: {
|
|
150
|
+
enabled: finalConfig.openapi?.swaggerUi?.enabled ?? false
|
|
151
|
+
}
|
|
122
152
|
};
|
|
123
|
-
if (this.openapi.generateOnBuild === true)
|
|
124
|
-
console.log(`[Sprint] ⚠️ openapi.generateOnBuild is enabled but this option makes nothing for now`);
|
|
125
|
-
}
|
|
126
|
-
this.server = http.createServer(this.app);
|
|
153
|
+
if (this.openapi.generateOnBuild === true) console.log(`[Sprint] ⚠️ openapi.generateOnBuild is enabled but this option makes nothing for now`);
|
|
127
154
|
this.loadDefaults();
|
|
128
155
|
this.loadHealthcheck();
|
|
129
156
|
this.routesLoaded = this.init();
|
|
157
|
+
if (this.openapi.generateOnBuild) {
|
|
158
|
+
this.app.get("/openapi.json", (_, res) => {
|
|
159
|
+
res.json(this.generateOpenAPISpec());
|
|
160
|
+
});
|
|
161
|
+
if (this.openapi.swaggerUi.enabled) {
|
|
162
|
+
import("swagger-ui-express").then((swaggerUi) => {
|
|
163
|
+
this.app.use("/swagger", swaggerUi.serve, swaggerUi.setup(void 0, {
|
|
164
|
+
swaggerUrl: "/openapi.json"
|
|
165
|
+
}));
|
|
166
|
+
}).catch(() => {
|
|
167
|
+
console.error(`[Sprint] ⚠️ swagger-ui-express is not installed. Run "npm install swagger-ui-express" to enable Swagger UI.`);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
130
171
|
if (finalConfig.autoListen) this.routesLoaded.then(() => this.listen());
|
|
131
172
|
});
|
|
132
173
|
}
|
|
@@ -277,6 +318,24 @@ class Sprint {
|
|
|
277
318
|
if (routePath.endsWith("/index")) routePath = routePath.slice(0, -6) || "/";
|
|
278
319
|
const fullRoute = this.prefix + (routePath === "/" ? "" : routePath);
|
|
279
320
|
const finalRoute = fullRoute || "/";
|
|
321
|
+
if (this.openapi.generateOnBuild) {
|
|
322
|
+
if (!this.registeredRoutes) this.registeredRoutes = [];
|
|
323
|
+
for (const layer of router.stack) {
|
|
324
|
+
if (!layer.route) continue;
|
|
325
|
+
const route = layer.route;
|
|
326
|
+
for (const routeLayer of route.stack) {
|
|
327
|
+
const schema = routeLayer.handle.__sprintRouteSchema;
|
|
328
|
+
if (!schema) continue;
|
|
329
|
+
const method = (routeLayer.method || "").toUpperCase();
|
|
330
|
+
if (!method) continue;
|
|
331
|
+
this.registeredRoutes.push({
|
|
332
|
+
method,
|
|
333
|
+
path: finalRoute + route.path,
|
|
334
|
+
schema
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
280
339
|
const routeMiddlewares = this.getMiddlewaresForRoute(routePath);
|
|
281
340
|
if (routeMiddlewares.length > 0) {
|
|
282
341
|
this.app.use(finalRoute, ...routeMiddlewares, router);
|
|
@@ -345,6 +404,29 @@ class Sprint {
|
|
|
345
404
|
if (!this.prefix) return routePath;
|
|
346
405
|
return this.prefix + (routePath.startsWith("/") ? routePath : "/" + routePath);
|
|
347
406
|
}
|
|
407
|
+
generateOpenAPISpec() {
|
|
408
|
+
const paths = {};
|
|
409
|
+
for (const route of this.registeredRoutes || []) {
|
|
410
|
+
const method = route.method.toLowerCase();
|
|
411
|
+
if (!paths[route.path]) paths[route.path] = {};
|
|
412
|
+
paths[route.path][method] = {
|
|
413
|
+
summary: "Auto generated by Sprint",
|
|
414
|
+
responses: {
|
|
415
|
+
"200": {
|
|
416
|
+
description: "Success"
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
openapi: "3.0.0",
|
|
423
|
+
info: {
|
|
424
|
+
title: "Sprint API",
|
|
425
|
+
version: "1.0.0"
|
|
426
|
+
},
|
|
427
|
+
paths
|
|
428
|
+
};
|
|
429
|
+
}
|
|
348
430
|
// HTTP Methods (prefix is applied automatically).
|
|
349
431
|
get(path2, handler) {
|
|
350
432
|
return this.app.get(this.applyPrefix(path2), handler);
|
|
@@ -362,9 +444,7 @@ class Sprint {
|
|
|
362
444
|
return this.app.patch(this.applyPrefix(path2), handler);
|
|
363
445
|
}
|
|
364
446
|
use(pathOrHandler, maybeHandler) {
|
|
365
|
-
if (typeof pathOrHandler === "string" && maybeHandler)
|
|
366
|
-
return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
|
|
367
|
-
}
|
|
447
|
+
if (typeof pathOrHandler === "string" && maybeHandler) return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
|
|
368
448
|
if (pathOrHandler && typeof pathOrHandler === "object" && "handler" in pathOrHandler) {
|
|
369
449
|
const config = pathOrHandler;
|
|
370
450
|
const handlers = Array.isArray(config.handler) ? config.handler : [config.handler];
|
|
@@ -379,6 +459,7 @@ class Sprint {
|
|
|
379
459
|
let serverStarted = false;
|
|
380
460
|
const tryListen = (port) => {
|
|
381
461
|
triedPorts.push(port);
|
|
462
|
+
this.server = http.createServer(this.app);
|
|
382
463
|
this.server.listen(port, () => {
|
|
383
464
|
serverStarted = true;
|
|
384
465
|
const prefixInfo = this.prefix ? this.prefix : "/";
|
|
@@ -392,6 +473,8 @@ class Sprint {
|
|
|
392
473
|
console.log(` ${dim}Local:${reset} http://localhost:${bold}${port}${reset}`);
|
|
393
474
|
console.log(` ${dim}Prefix:${reset} ${bold}${prefixInfo}${reset}`);
|
|
394
475
|
console.log(` ${dim}Healthcheck:${reset} http://localhost:${port}${this.prefix}/healthcheck`);
|
|
476
|
+
if (this.openapi.generateOnBuild) console.log(` ${dim}OpenAPI Spec:${reset} http://localhost:${port}${this.prefix}/openapi.json`);
|
|
477
|
+
if (this.openapi.generateOnBuild) console.log(` ${dim}Swagger UI:${reset} http://localhost:${port}${this.prefix}/swagger`);
|
|
395
478
|
console.log("");
|
|
396
479
|
console.log(` ${dim}Tip: Need stronger route protection? Learn more at${reset}`);
|
|
397
480
|
console.log(` ${dim}https://docs.tpeoficial.com/docs/dymo-api/private/request-verifier${reset}`);
|
|
@@ -15,31 +15,25 @@ function parseSchema(schema, data) {
|
|
|
15
15
|
return { success: true, data: result.data };
|
|
16
16
|
}
|
|
17
17
|
function defineRouteSchema(schema) {
|
|
18
|
-
|
|
18
|
+
const middleware = (req, res, next) => {
|
|
19
19
|
const errors = [];
|
|
20
|
-
if (schema.body
|
|
20
|
+
if (schema.body) {
|
|
21
21
|
const result = parseSchema(schema.body, req.body);
|
|
22
|
-
if (!result.success) {
|
|
23
|
-
errors.push(...result.errors.map((e) => ({ location: "body", ...e })));
|
|
24
|
-
}
|
|
22
|
+
if (!result.success) errors.push(...result.errors.map((e) => ({ location: "body", ...e })));
|
|
25
23
|
}
|
|
26
|
-
if (schema.queryParams
|
|
24
|
+
if (schema.queryParams) {
|
|
27
25
|
const result = parseSchema(schema.queryParams, req.query);
|
|
28
|
-
if (!result.success) {
|
|
29
|
-
errors.push(...result.errors.map((e) => ({ location: "queryParams", ...e })));
|
|
30
|
-
}
|
|
26
|
+
if (!result.success) errors.push(...result.errors.map((e) => ({ location: "queryParams", ...e })));
|
|
31
27
|
}
|
|
32
|
-
if (schema.params
|
|
28
|
+
if (schema.params) {
|
|
33
29
|
const result = parseSchema(schema.params, req.params);
|
|
34
|
-
if (!result.success) {
|
|
35
|
-
errors.push(...result.errors.map((e) => ({ location: "params", ...e })));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (errors.length > 0) {
|
|
39
|
-
return res.status(400).json({ error: "Validation failed", details: errors });
|
|
30
|
+
if (!result.success) errors.push(...result.errors.map((e) => ({ location: "params", ...e })));
|
|
40
31
|
}
|
|
32
|
+
if (errors.length > 0) return res.status(400).json({ error: "Validation failed", details: errors });
|
|
41
33
|
next();
|
|
42
34
|
};
|
|
35
|
+
middleware.__sprintRouteSchema = schema;
|
|
36
|
+
return middleware;
|
|
43
37
|
}
|
|
44
38
|
Object.defineProperty(exports, "z", {
|
|
45
39
|
enumerable: true,
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "child_process";
|
|
3
2
|
import { existsSync } from "fs";
|
|
4
3
|
import * as crypto from "crypto";
|
|
5
4
|
import { join, resolve } from "path";
|
|
5
|
+
import { spawn } from "child_process";
|
|
6
6
|
const args = process.argv.slice(2);
|
|
7
7
|
const command = args[0];
|
|
8
8
|
if (!command) {
|
|
@@ -30,9 +30,7 @@ if (command === "--help" || command === "-h") {
|
|
|
30
30
|
function getProjectRoot() {
|
|
31
31
|
let dir = process.cwd();
|
|
32
32
|
while (dir !== resolve(dir, "..")) {
|
|
33
|
-
if (existsSync(join(dir, "sprint.config.ts")) || existsSync(join(dir, "sprint.config.js")))
|
|
34
|
-
return dir;
|
|
35
|
-
}
|
|
33
|
+
if (existsSync(join(dir, "sprint.config.ts")) || existsSync(join(dir, "sprint.config.js"))) return dir;
|
|
36
34
|
dir = resolve(dir, "..");
|
|
37
35
|
}
|
|
38
36
|
return process.cwd();
|
package/dist/esm/index.js
CHANGED
|
@@ -52,9 +52,7 @@ async function findProjectRoot(startDir) {
|
|
|
52
52
|
let currentDir = startDir;
|
|
53
53
|
while (currentDir !== path.parse(currentDir).root) {
|
|
54
54
|
const packageJsonPath = path.join(currentDir, "package.json");
|
|
55
|
-
if (fs.existsSync(packageJsonPath))
|
|
56
|
-
return currentDir;
|
|
57
|
-
}
|
|
55
|
+
if (fs.existsSync(packageJsonPath)) return currentDir;
|
|
58
56
|
currentDir = path.dirname(currentDir);
|
|
59
57
|
}
|
|
60
58
|
return null;
|
|
@@ -62,9 +60,7 @@ async function findProjectRoot(startDir) {
|
|
|
62
60
|
async function loadSprintConfig() {
|
|
63
61
|
const callerDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();
|
|
64
62
|
const projectRoot = await findProjectRoot(callerDir);
|
|
65
|
-
if (!projectRoot)
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
63
|
+
if (!projectRoot) return null;
|
|
68
64
|
const configFiles = ["sprint.config.ts", "sprint.config.js"];
|
|
69
65
|
for (const configFile of configFiles) {
|
|
70
66
|
const configPath = path.join(projectRoot, configFile);
|
|
@@ -90,7 +86,13 @@ class Sprint {
|
|
|
90
86
|
this.urlEncodedLimit = "50mb";
|
|
91
87
|
this.prefix = "";
|
|
92
88
|
this.loadedMiddlewares = [];
|
|
93
|
-
this.openapi = {
|
|
89
|
+
this.openapi = {
|
|
90
|
+
generateOnBuild: false,
|
|
91
|
+
swaggerUi: {
|
|
92
|
+
enabled: false
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
this.registeredRoutes = [];
|
|
94
96
|
this.app = express();
|
|
95
97
|
loadSprintConfig().then((config) => {
|
|
96
98
|
const defaults = {
|
|
@@ -103,7 +105,10 @@ class Sprint {
|
|
|
103
105
|
prefix: "",
|
|
104
106
|
autoListen: true,
|
|
105
107
|
openapi: {
|
|
106
|
-
generateOnBuild: false
|
|
108
|
+
generateOnBuild: false,
|
|
109
|
+
swaggerUi: {
|
|
110
|
+
enabled: false
|
|
111
|
+
}
|
|
107
112
|
}
|
|
108
113
|
};
|
|
109
114
|
const finalConfig = { ...defaults, ...config };
|
|
@@ -115,15 +120,29 @@ class Sprint {
|
|
|
115
120
|
this.urlEncodedLimit = finalConfig.urlEncodedLimit || "50mb";
|
|
116
121
|
this.prefix = finalConfig.prefix ? "/" + finalConfig.prefix.replace(/^\/+|\/+$/g, "") : "";
|
|
117
122
|
this.openapi = {
|
|
118
|
-
generateOnBuild: finalConfig.openapi?.generateOnBuild ?? false
|
|
123
|
+
generateOnBuild: finalConfig.openapi?.generateOnBuild ?? false,
|
|
124
|
+
swaggerUi: {
|
|
125
|
+
enabled: finalConfig.openapi?.swaggerUi?.enabled ?? false
|
|
126
|
+
}
|
|
119
127
|
};
|
|
120
|
-
if (this.openapi.generateOnBuild === true)
|
|
121
|
-
console.log(`[Sprint] ⚠️ openapi.generateOnBuild is enabled but this option makes nothing for now`);
|
|
122
|
-
}
|
|
123
|
-
this.server = http.createServer(this.app);
|
|
128
|
+
if (this.openapi.generateOnBuild === true) console.log(`[Sprint] ⚠️ openapi.generateOnBuild is enabled but this option makes nothing for now`);
|
|
124
129
|
this.loadDefaults();
|
|
125
130
|
this.loadHealthcheck();
|
|
126
131
|
this.routesLoaded = this.init();
|
|
132
|
+
if (this.openapi.generateOnBuild) {
|
|
133
|
+
this.app.get("/openapi.json", (_, res) => {
|
|
134
|
+
res.json(this.generateOpenAPISpec());
|
|
135
|
+
});
|
|
136
|
+
if (this.openapi.swaggerUi.enabled) {
|
|
137
|
+
import("swagger-ui-express").then((swaggerUi) => {
|
|
138
|
+
this.app.use("/swagger", swaggerUi.serve, swaggerUi.setup(void 0, {
|
|
139
|
+
swaggerUrl: "/openapi.json"
|
|
140
|
+
}));
|
|
141
|
+
}).catch(() => {
|
|
142
|
+
console.error(`[Sprint] ⚠️ swagger-ui-express is not installed. Run "npm install swagger-ui-express" to enable Swagger UI.`);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
127
146
|
if (finalConfig.autoListen) this.routesLoaded.then(() => this.listen());
|
|
128
147
|
});
|
|
129
148
|
}
|
|
@@ -274,6 +293,24 @@ class Sprint {
|
|
|
274
293
|
if (routePath.endsWith("/index")) routePath = routePath.slice(0, -6) || "/";
|
|
275
294
|
const fullRoute = this.prefix + (routePath === "/" ? "" : routePath);
|
|
276
295
|
const finalRoute = fullRoute || "/";
|
|
296
|
+
if (this.openapi.generateOnBuild) {
|
|
297
|
+
if (!this.registeredRoutes) this.registeredRoutes = [];
|
|
298
|
+
for (const layer of router.stack) {
|
|
299
|
+
if (!layer.route) continue;
|
|
300
|
+
const route = layer.route;
|
|
301
|
+
for (const routeLayer of route.stack) {
|
|
302
|
+
const schema = routeLayer.handle.__sprintRouteSchema;
|
|
303
|
+
if (!schema) continue;
|
|
304
|
+
const method = (routeLayer.method || "").toUpperCase();
|
|
305
|
+
if (!method) continue;
|
|
306
|
+
this.registeredRoutes.push({
|
|
307
|
+
method,
|
|
308
|
+
path: finalRoute + route.path,
|
|
309
|
+
schema
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
277
314
|
const routeMiddlewares = this.getMiddlewaresForRoute(routePath);
|
|
278
315
|
if (routeMiddlewares.length > 0) {
|
|
279
316
|
this.app.use(finalRoute, ...routeMiddlewares, router);
|
|
@@ -342,6 +379,29 @@ class Sprint {
|
|
|
342
379
|
if (!this.prefix) return routePath;
|
|
343
380
|
return this.prefix + (routePath.startsWith("/") ? routePath : "/" + routePath);
|
|
344
381
|
}
|
|
382
|
+
generateOpenAPISpec() {
|
|
383
|
+
const paths = {};
|
|
384
|
+
for (const route of this.registeredRoutes || []) {
|
|
385
|
+
const method = route.method.toLowerCase();
|
|
386
|
+
if (!paths[route.path]) paths[route.path] = {};
|
|
387
|
+
paths[route.path][method] = {
|
|
388
|
+
summary: "Auto generated by Sprint",
|
|
389
|
+
responses: {
|
|
390
|
+
"200": {
|
|
391
|
+
description: "Success"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
openapi: "3.0.0",
|
|
398
|
+
info: {
|
|
399
|
+
title: "Sprint API",
|
|
400
|
+
version: "1.0.0"
|
|
401
|
+
},
|
|
402
|
+
paths
|
|
403
|
+
};
|
|
404
|
+
}
|
|
345
405
|
// HTTP Methods (prefix is applied automatically).
|
|
346
406
|
get(path2, handler) {
|
|
347
407
|
return this.app.get(this.applyPrefix(path2), handler);
|
|
@@ -359,9 +419,7 @@ class Sprint {
|
|
|
359
419
|
return this.app.patch(this.applyPrefix(path2), handler);
|
|
360
420
|
}
|
|
361
421
|
use(pathOrHandler, maybeHandler) {
|
|
362
|
-
if (typeof pathOrHandler === "string" && maybeHandler)
|
|
363
|
-
return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
|
|
364
|
-
}
|
|
422
|
+
if (typeof pathOrHandler === "string" && maybeHandler) return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
|
|
365
423
|
if (pathOrHandler && typeof pathOrHandler === "object" && "handler" in pathOrHandler) {
|
|
366
424
|
const config = pathOrHandler;
|
|
367
425
|
const handlers = Array.isArray(config.handler) ? config.handler : [config.handler];
|
|
@@ -376,6 +434,7 @@ class Sprint {
|
|
|
376
434
|
let serverStarted = false;
|
|
377
435
|
const tryListen = (port) => {
|
|
378
436
|
triedPorts.push(port);
|
|
437
|
+
this.server = http.createServer(this.app);
|
|
379
438
|
this.server.listen(port, () => {
|
|
380
439
|
serverStarted = true;
|
|
381
440
|
const prefixInfo = this.prefix ? this.prefix : "/";
|
|
@@ -389,6 +448,8 @@ class Sprint {
|
|
|
389
448
|
console.log(` ${dim}Local:${reset} http://localhost:${bold}${port}${reset}`);
|
|
390
449
|
console.log(` ${dim}Prefix:${reset} ${bold}${prefixInfo}${reset}`);
|
|
391
450
|
console.log(` ${dim}Healthcheck:${reset} http://localhost:${port}${this.prefix}/healthcheck`);
|
|
451
|
+
if (this.openapi.generateOnBuild) console.log(` ${dim}OpenAPI Spec:${reset} http://localhost:${port}${this.prefix}/openapi.json`);
|
|
452
|
+
if (this.openapi.generateOnBuild) console.log(` ${dim}Swagger UI:${reset} http://localhost:${port}${this.prefix}/swagger`);
|
|
392
453
|
console.log("");
|
|
393
454
|
console.log(` ${dim}Tip: Need stronger route protection? Learn more at${reset}`);
|
|
394
455
|
console.log(` ${dim}https://docs.tpeoficial.com/docs/dymo-api/private/request-verifier${reset}`);
|
|
@@ -13,31 +13,25 @@ function parseSchema(schema, data) {
|
|
|
13
13
|
return { success: true, data: result.data };
|
|
14
14
|
}
|
|
15
15
|
function defineRouteSchema(schema) {
|
|
16
|
-
|
|
16
|
+
const middleware = (req, res, next) => {
|
|
17
17
|
const errors = [];
|
|
18
|
-
if (schema.body
|
|
18
|
+
if (schema.body) {
|
|
19
19
|
const result = parseSchema(schema.body, req.body);
|
|
20
|
-
if (!result.success) {
|
|
21
|
-
errors.push(...result.errors.map((e) => ({ location: "body", ...e })));
|
|
22
|
-
}
|
|
20
|
+
if (!result.success) errors.push(...result.errors.map((e) => ({ location: "body", ...e })));
|
|
23
21
|
}
|
|
24
|
-
if (schema.queryParams
|
|
22
|
+
if (schema.queryParams) {
|
|
25
23
|
const result = parseSchema(schema.queryParams, req.query);
|
|
26
|
-
if (!result.success) {
|
|
27
|
-
errors.push(...result.errors.map((e) => ({ location: "queryParams", ...e })));
|
|
28
|
-
}
|
|
24
|
+
if (!result.success) errors.push(...result.errors.map((e) => ({ location: "queryParams", ...e })));
|
|
29
25
|
}
|
|
30
|
-
if (schema.params
|
|
26
|
+
if (schema.params) {
|
|
31
27
|
const result = parseSchema(schema.params, req.params);
|
|
32
|
-
if (!result.success) {
|
|
33
|
-
errors.push(...result.errors.map((e) => ({ location: "params", ...e })));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (errors.length > 0) {
|
|
37
|
-
return res.status(400).json({ error: "Validation failed", details: errors });
|
|
28
|
+
if (!result.success) errors.push(...result.errors.map((e) => ({ location: "params", ...e })));
|
|
38
29
|
}
|
|
30
|
+
if (errors.length > 0) return res.status(400).json({ error: "Validation failed", details: errors });
|
|
39
31
|
next();
|
|
40
32
|
};
|
|
33
|
+
middleware.__sprintRouteSchema = schema;
|
|
34
|
+
return middleware;
|
|
41
35
|
}
|
|
42
36
|
export {
|
|
43
37
|
defineRouteSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,SAAS,IAAI,aAAa,EAAE,UAAU,EAAY,MAAM,KAAK,CAAC;AAE1E,OAAO,EAAE,CAAC,EAAE,CAAC;AAEb,MAAM,WAAW,kBAAkB;IAC/B,IAAI,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;CAChD;AAqBD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,kBAAkB,EAAE,MAAM,EAAE,CAAC,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,SAAS,IAAI,aAAa,EAAE,UAAU,EAAY,MAAM,KAAK,CAAC;AAE1E,OAAO,EAAE,CAAC,EAAE,CAAC;AAEb,MAAM,WAAW,kBAAkB;IAC/B,IAAI,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;CAChD;AAqBD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,kBAAkB,EAAE,MAAM,EAAE,CAAC,GAAG,cAAc,CA4BzF;AAED,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/types/sprint.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare class Sprint {
|
|
|
15
15
|
private server;
|
|
16
16
|
private loadedMiddlewares;
|
|
17
17
|
private openapi;
|
|
18
|
+
private registeredRoutes;
|
|
18
19
|
constructor();
|
|
19
20
|
private init;
|
|
20
21
|
private loadDefaults;
|
|
@@ -32,6 +33,7 @@ export declare class Sprint {
|
|
|
32
33
|
private loadNotFound;
|
|
33
34
|
/** Applies prefix to a path */
|
|
34
35
|
private applyPrefix;
|
|
36
|
+
private generateOpenAPISpec;
|
|
35
37
|
get(path: string, handler: Handler): express.Application;
|
|
36
38
|
post(path: string, handler: Handler): express.Application;
|
|
37
39
|
put(path: string, handler: Handler): express.Application;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sprint.d.ts","sourceRoot":"","sources":["../../src/sprint.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,OAAO,EAA+B,gBAAgB,EAAoB,MAAM,SAAS,CAAC;AACnG,OAAO,OAAO,EAAE,EAAE,WAAW,EAAoD,MAAM,SAAS,CAAC;AAejG,eAAO,MAAM,aAAa,SAAQ,CAAC;AACnC,eAAO,MAAM,YAAY,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"sprint.d.ts","sourceRoot":"","sources":["../../src/sprint.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,OAAO,EAA+B,gBAAgB,EAAoB,MAAM,SAAS,CAAC;AACnG,OAAO,OAAO,EAAE,EAAE,WAAW,EAAoD,MAAM,SAAS,CAAC;AAejG,eAAO,MAAM,aAAa,SAAQ,CAAC;AACnC,eAAO,MAAM,YAAY,SAAS,CAAC;AAoCnC,qBAAa,MAAM;IACR,GAAG,EAAE,WAAW,CAAC;IACxB,OAAO,CAAC,IAAI,CAAwD;IACpE,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,OAAO,CAUb;IACF,OAAO,CAAC,gBAAgB,CAIhB;;YAkEM,IAAI;IA+BlB,OAAO,CAAC,YAAY;IAiDpB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA4B9B;;OAEG;YACW,eAAe;IAgC7B,OAAO,CAAC,eAAe;YAcT,UAAU;YA2EV,YAAY;IAmB1B,OAAO,CAAC,YAAY;IAgCpB,+BAA+B;IAC/B,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,mBAAmB;IA6BpB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAClC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IACnC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAClC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IACrC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IACpC,GAAG,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,GAAG,gBAAgB,EAAE,YAAY,CAAC,EAAE,OAAO;IAYlF,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI;CA0DzC"}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -68,7 +68,11 @@ export interface SprintOptions {
|
|
|
68
68
|
/** Auto-start the server. Default: true */
|
|
69
69
|
autoListen?: boolean;
|
|
70
70
|
openapi?: {
|
|
71
|
+
enabled?: boolean;
|
|
71
72
|
generateOnBuild?: boolean;
|
|
73
|
+
swaggerUi?: {
|
|
74
|
+
enabled?: boolean;
|
|
75
|
+
};
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
export interface SprintConfig {
|
|
@@ -81,7 +85,11 @@ export interface SprintConfig {
|
|
|
81
85
|
prefix?: string;
|
|
82
86
|
autoListen?: boolean;
|
|
83
87
|
openapi?: {
|
|
88
|
+
enabled?: boolean;
|
|
84
89
|
generateOnBuild?: boolean;
|
|
90
|
+
swaggerUi?: {
|
|
91
|
+
enabled?: boolean;
|
|
92
|
+
};
|
|
85
93
|
};
|
|
86
94
|
}
|
|
87
95
|
export type { NextFunction } from 'express';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE1E,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAEpG,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,GAAG,CAAC;AAE/E,MAAM,MAAM,mBAAmB,GACzB,SAAS,MAAM,EAAE,GACjB,WAAW,MAAM,EAAE,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC1B,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;CACnG;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEtC,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC;QACd,UAAU,OAAO;YACb,MAAM,EAAE,aAAa,CAAC;YAEtB,MAAM,EAAE,GAAG,CAAC;SACf;KACJ;CACJ;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,oFAAoF;IACpF,OAAO,EAAE,cAAc,GAAG,mBAAmB,GAAG,CAAC,cAAc,GAAG,mBAAmB,CAAC,EAAE,CAAC;IACzF;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,OAAO,CAAC,EAAE;QACN,eAAe,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE1E,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAEpG,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,GAAG,CAAC;AAE/E,MAAM,MAAM,mBAAmB,GACzB,SAAS,MAAM,EAAE,GACjB,WAAW,MAAM,EAAE,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC1B,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;CACnG;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEtC,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC;QACd,UAAU,OAAO;YACb,MAAM,EAAE,aAAa,CAAC;YAEtB,MAAM,EAAE,GAAG,CAAC;SACf;KACJ;CACJ;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,oFAAoF;IACpF,OAAO,EAAE,cAAc,GAAG,mBAAmB,GAAG,CAAC,cAAc,GAAG,mBAAmB,CAAC,EAAE,CAAC;IACzF;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,SAAS,CAAC,EAAE;YACR,OAAO,CAAC,EAAE,OAAO,CAAC;SACrB,CAAC;KACL,CAAC;CACL;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,SAAS,CAAC,EAAE;YACR,OAAO,CAAC,EAAE,OAAO,CAAC;SACrB,CAAC;KACL,CAAC;CACL;AAED,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sprint-es",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.57",
|
|
4
4
|
"description": "Sprint - Quickly API",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"@types/node": "^22.18.7",
|
|
104
104
|
"@types/node-cron": "^3.0.11",
|
|
105
105
|
"@types/serve-favicon": "^2.5.7",
|
|
106
|
+
"@types/swagger-ui-express": "^4.1.8",
|
|
106
107
|
"jest": "^30.1.3",
|
|
107
108
|
"rimraf": "^6.0.1",
|
|
108
109
|
"ts-jest": "^29.4.4",
|
|
@@ -111,6 +112,9 @@
|
|
|
111
112
|
"vite": "^6.4.1",
|
|
112
113
|
"vite-plugin-dts": "^4.5.4"
|
|
113
114
|
},
|
|
115
|
+
"optionalDependencies": {
|
|
116
|
+
"swagger-ui-express": "^5.0.1"
|
|
117
|
+
},
|
|
114
118
|
"prettier": {
|
|
115
119
|
"tabWidth": 4,
|
|
116
120
|
"useTabs": false
|