sprint-es 0.0.52 → 0.0.56

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 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();
@@ -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");
@@ -51,31 +73,103 @@ const limiter = new rateLimit.RateLimiter({
51
73
  });
52
74
  const isDevelopment = isDev;
53
75
  const isProduction = isProd;
76
+ async function findProjectRoot(startDir) {
77
+ let currentDir = startDir;
78
+ while (currentDir !== path.parse(currentDir).root) {
79
+ const packageJsonPath = path.join(currentDir, "package.json");
80
+ if (fs.existsSync(packageJsonPath)) return currentDir;
81
+ currentDir = path.dirname(currentDir);
82
+ }
83
+ return null;
84
+ }
85
+ async function loadSprintConfig() {
86
+ const callerDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();
87
+ const projectRoot = await findProjectRoot(callerDir);
88
+ if (!projectRoot) return null;
89
+ const configFiles = ["sprint.config.ts", "sprint.config.js"];
90
+ for (const configFile of configFiles) {
91
+ const configPath = path.join(projectRoot, configFile);
92
+ if (fs.existsSync(configPath)) {
93
+ try {
94
+ const moduleUrl = url.pathToFileURL(configPath).href;
95
+ const config = await import(moduleUrl);
96
+ return config.default || config;
97
+ } catch (err) {
98
+ console.warn(`[Sprint] Failed to load config from ${configPath}:`, err);
99
+ }
100
+ }
101
+ }
102
+ return null;
103
+ }
54
104
  class Sprint {
55
- constructor({
56
- port = process.env.PORT,
57
- routesPath = "./routes",
58
- middlewaresPath = "./middlewares",
59
- cronjobsPath = "./cronjobs",
60
- jsonLimit = "50mb",
61
- urlEncodedLimit = "50mb",
62
- prefix = "",
63
- autoListen = true
64
- } = {}) {
105
+ constructor() {
106
+ this.port = process.env.PORT;
107
+ this.routesPath = "./routes";
108
+ this.middlewaresPath = "./middlewares";
109
+ this.cronjobsPath = "./cronjobs";
110
+ this.jsonLimit = "50mb";
111
+ this.urlEncodedLimit = "50mb";
112
+ this.prefix = "";
65
113
  this.loadedMiddlewares = [];
114
+ this.openapi = {
115
+ generateOnBuild: false,
116
+ swaggerUi: {
117
+ enabled: false
118
+ }
119
+ };
120
+ this.registeredRoutes = [];
66
121
  this.app = express();
67
- this.port = port;
68
- this.routesPath = routesPath;
69
- this.middlewaresPath = middlewaresPath;
70
- this.cronjobsPath = cronjobsPath;
71
- this.jsonLimit = jsonLimit;
72
- this.urlEncodedLimit = urlEncodedLimit;
73
- this.prefix = prefix ? "/" + prefix.replace(/^\/+|\/+$/g, "") : "";
74
- this.server = http.createServer(this.app);
75
- this.loadDefaults();
76
- this.loadHealthcheck();
77
- this.routesLoaded = this.init();
78
- if (autoListen) this.routesLoaded.then(() => this.listen());
122
+ loadSprintConfig().then((config) => {
123
+ const defaults = {
124
+ port: process.env.PORT,
125
+ routesPath: "./routes",
126
+ middlewaresPath: "./middlewares",
127
+ cronjobsPath: "./cronjobs",
128
+ jsonLimit: "50mb",
129
+ urlEncodedLimit: "50mb",
130
+ prefix: "",
131
+ autoListen: true,
132
+ openapi: {
133
+ generateOnBuild: false,
134
+ swaggerUi: {
135
+ enabled: false
136
+ }
137
+ }
138
+ };
139
+ const finalConfig = { ...defaults, ...config };
140
+ this.port = finalConfig.port;
141
+ this.routesPath = finalConfig.routesPath || "./routes";
142
+ this.middlewaresPath = finalConfig.middlewaresPath || "./middlewares";
143
+ this.cronjobsPath = finalConfig.cronjobsPath || "./cronjobs";
144
+ this.jsonLimit = finalConfig.jsonLimit || "50mb";
145
+ this.urlEncodedLimit = finalConfig.urlEncodedLimit || "50mb";
146
+ this.prefix = finalConfig.prefix ? "/" + finalConfig.prefix.replace(/^\/+|\/+$/g, "") : "";
147
+ this.openapi = {
148
+ generateOnBuild: finalConfig.openapi?.generateOnBuild ?? false,
149
+ swaggerUi: {
150
+ enabled: finalConfig.openapi?.swaggerUi?.enabled ?? false
151
+ }
152
+ };
153
+ if (this.openapi.generateOnBuild === true) console.log(`[Sprint] ⚠️ openapi.generateOnBuild is enabled but this option makes nothing for now`);
154
+ this.loadDefaults();
155
+ this.loadHealthcheck();
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 (finalConfig.openapi?.swaggerUi?.enabled) {
162
+ import("swagger-ui-express").then((swaggerUi) => {
163
+ this.app.use("/docs", 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
+ }
171
+ if (finalConfig.autoListen) this.routesLoaded.then(() => this.listen());
172
+ });
79
173
  }
80
174
  async init() {
81
175
  const callerDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();
@@ -224,6 +318,24 @@ class Sprint {
224
318
  if (routePath.endsWith("/index")) routePath = routePath.slice(0, -6) || "/";
225
319
  const fullRoute = this.prefix + (routePath === "/" ? "" : routePath);
226
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
+ }
227
339
  const routeMiddlewares = this.getMiddlewaresForRoute(routePath);
228
340
  if (routeMiddlewares.length > 0) {
229
341
  this.app.use(finalRoute, ...routeMiddlewares, router);
@@ -292,6 +404,29 @@ class Sprint {
292
404
  if (!this.prefix) return routePath;
293
405
  return this.prefix + (routePath.startsWith("/") ? routePath : "/" + routePath);
294
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
+ }
295
430
  // HTTP Methods (prefix is applied automatically).
296
431
  get(path2, handler) {
297
432
  return this.app.get(this.applyPrefix(path2), handler);
@@ -309,9 +444,7 @@ class Sprint {
309
444
  return this.app.patch(this.applyPrefix(path2), handler);
310
445
  }
311
446
  use(pathOrHandler, maybeHandler) {
312
- if (typeof pathOrHandler === "string" && maybeHandler) {
313
- return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
314
- }
447
+ if (typeof pathOrHandler === "string" && maybeHandler) return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
315
448
  if (pathOrHandler && typeof pathOrHandler === "object" && "handler" in pathOrHandler) {
316
449
  const config = pathOrHandler;
317
450
  const handlers = Array.isArray(config.handler) ? config.handler : [config.handler];
@@ -326,6 +459,7 @@ class Sprint {
326
459
  let serverStarted = false;
327
460
  const tryListen = (port) => {
328
461
  triedPorts.push(port);
462
+ this.server = http.createServer(this.app);
329
463
  this.server.listen(port, () => {
330
464
  serverStarted = true;
331
465
  const prefixInfo = this.prefix ? this.prefix : "/";
@@ -15,31 +15,25 @@ function parseSchema(schema, data) {
15
15
  return { success: true, data: result.data };
16
16
  }
17
17
  function defineRouteSchema(schema) {
18
- return (req, res, next) => {
18
+ const middleware = (req, res, next) => {
19
19
  const errors = [];
20
- if (schema.body && req.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 && req.query) {
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 && req.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
@@ -48,31 +48,103 @@ const limiter = new RateLimiter({
48
48
  });
49
49
  const isDevelopment = isDev;
50
50
  const isProduction = isProd;
51
+ async function findProjectRoot(startDir) {
52
+ let currentDir = startDir;
53
+ while (currentDir !== path.parse(currentDir).root) {
54
+ const packageJsonPath = path.join(currentDir, "package.json");
55
+ if (fs.existsSync(packageJsonPath)) return currentDir;
56
+ currentDir = path.dirname(currentDir);
57
+ }
58
+ return null;
59
+ }
60
+ async function loadSprintConfig() {
61
+ const callerDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();
62
+ const projectRoot = await findProjectRoot(callerDir);
63
+ if (!projectRoot) return null;
64
+ const configFiles = ["sprint.config.ts", "sprint.config.js"];
65
+ for (const configFile of configFiles) {
66
+ const configPath = path.join(projectRoot, configFile);
67
+ if (fs.existsSync(configPath)) {
68
+ try {
69
+ const moduleUrl = pathToFileURL(configPath).href;
70
+ const config = await import(moduleUrl);
71
+ return config.default || config;
72
+ } catch (err) {
73
+ console.warn(`[Sprint] Failed to load config from ${configPath}:`, err);
74
+ }
75
+ }
76
+ }
77
+ return null;
78
+ }
51
79
  class Sprint {
52
- constructor({
53
- port = process.env.PORT,
54
- routesPath = "./routes",
55
- middlewaresPath = "./middlewares",
56
- cronjobsPath = "./cronjobs",
57
- jsonLimit = "50mb",
58
- urlEncodedLimit = "50mb",
59
- prefix = "",
60
- autoListen = true
61
- } = {}) {
80
+ constructor() {
81
+ this.port = process.env.PORT;
82
+ this.routesPath = "./routes";
83
+ this.middlewaresPath = "./middlewares";
84
+ this.cronjobsPath = "./cronjobs";
85
+ this.jsonLimit = "50mb";
86
+ this.urlEncodedLimit = "50mb";
87
+ this.prefix = "";
62
88
  this.loadedMiddlewares = [];
89
+ this.openapi = {
90
+ generateOnBuild: false,
91
+ swaggerUi: {
92
+ enabled: false
93
+ }
94
+ };
95
+ this.registeredRoutes = [];
63
96
  this.app = express();
64
- this.port = port;
65
- this.routesPath = routesPath;
66
- this.middlewaresPath = middlewaresPath;
67
- this.cronjobsPath = cronjobsPath;
68
- this.jsonLimit = jsonLimit;
69
- this.urlEncodedLimit = urlEncodedLimit;
70
- this.prefix = prefix ? "/" + prefix.replace(/^\/+|\/+$/g, "") : "";
71
- this.server = http.createServer(this.app);
72
- this.loadDefaults();
73
- this.loadHealthcheck();
74
- this.routesLoaded = this.init();
75
- if (autoListen) this.routesLoaded.then(() => this.listen());
97
+ loadSprintConfig().then((config) => {
98
+ const defaults = {
99
+ port: process.env.PORT,
100
+ routesPath: "./routes",
101
+ middlewaresPath: "./middlewares",
102
+ cronjobsPath: "./cronjobs",
103
+ jsonLimit: "50mb",
104
+ urlEncodedLimit: "50mb",
105
+ prefix: "",
106
+ autoListen: true,
107
+ openapi: {
108
+ generateOnBuild: false,
109
+ swaggerUi: {
110
+ enabled: false
111
+ }
112
+ }
113
+ };
114
+ const finalConfig = { ...defaults, ...config };
115
+ this.port = finalConfig.port;
116
+ this.routesPath = finalConfig.routesPath || "./routes";
117
+ this.middlewaresPath = finalConfig.middlewaresPath || "./middlewares";
118
+ this.cronjobsPath = finalConfig.cronjobsPath || "./cronjobs";
119
+ this.jsonLimit = finalConfig.jsonLimit || "50mb";
120
+ this.urlEncodedLimit = finalConfig.urlEncodedLimit || "50mb";
121
+ this.prefix = finalConfig.prefix ? "/" + finalConfig.prefix.replace(/^\/+|\/+$/g, "") : "";
122
+ this.openapi = {
123
+ generateOnBuild: finalConfig.openapi?.generateOnBuild ?? false,
124
+ swaggerUi: {
125
+ enabled: finalConfig.openapi?.swaggerUi?.enabled ?? false
126
+ }
127
+ };
128
+ if (this.openapi.generateOnBuild === true) console.log(`[Sprint] ⚠️ openapi.generateOnBuild is enabled but this option makes nothing for now`);
129
+ this.loadDefaults();
130
+ this.loadHealthcheck();
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 (finalConfig.openapi?.swaggerUi?.enabled) {
137
+ import("swagger-ui-express").then((swaggerUi) => {
138
+ this.app.use("/docs", 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
+ }
146
+ if (finalConfig.autoListen) this.routesLoaded.then(() => this.listen());
147
+ });
76
148
  }
77
149
  async init() {
78
150
  const callerDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();
@@ -221,6 +293,24 @@ class Sprint {
221
293
  if (routePath.endsWith("/index")) routePath = routePath.slice(0, -6) || "/";
222
294
  const fullRoute = this.prefix + (routePath === "/" ? "" : routePath);
223
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
+ }
224
314
  const routeMiddlewares = this.getMiddlewaresForRoute(routePath);
225
315
  if (routeMiddlewares.length > 0) {
226
316
  this.app.use(finalRoute, ...routeMiddlewares, router);
@@ -289,6 +379,29 @@ class Sprint {
289
379
  if (!this.prefix) return routePath;
290
380
  return this.prefix + (routePath.startsWith("/") ? routePath : "/" + routePath);
291
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
+ }
292
405
  // HTTP Methods (prefix is applied automatically).
293
406
  get(path2, handler) {
294
407
  return this.app.get(this.applyPrefix(path2), handler);
@@ -306,9 +419,7 @@ class Sprint {
306
419
  return this.app.patch(this.applyPrefix(path2), handler);
307
420
  }
308
421
  use(pathOrHandler, maybeHandler) {
309
- if (typeof pathOrHandler === "string" && maybeHandler) {
310
- return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
311
- }
422
+ if (typeof pathOrHandler === "string" && maybeHandler) return this.app.use(this.applyPrefix(pathOrHandler), maybeHandler);
312
423
  if (pathOrHandler && typeof pathOrHandler === "object" && "handler" in pathOrHandler) {
313
424
  const config = pathOrHandler;
314
425
  const handlers = Array.isArray(config.handler) ? config.handler : [config.handler];
@@ -323,6 +434,7 @@ class Sprint {
323
434
  let serverStarted = false;
324
435
  const tryListen = (port) => {
325
436
  triedPorts.push(port);
437
+ this.server = http.createServer(this.app);
326
438
  this.server.listen(port, () => {
327
439
  serverStarted = true;
328
440
  const prefixInfo = this.prefix ? this.prefix : "/";
@@ -13,31 +13,25 @@ function parseSchema(schema, data) {
13
13
  return { success: true, data: result.data };
14
14
  }
15
15
  function defineRouteSchema(schema) {
16
- return (req, res, next) => {
16
+ const middleware = (req, res, next) => {
17
17
  const errors = [];
18
- if (schema.body && req.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 && req.query) {
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 && req.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,CA+BzF;AAED,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,SAAS,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"}
@@ -1,4 +1,4 @@
1
- import { Handler, SprintOptions, MiddlewareConfig } from './types';
1
+ import { Handler, MiddlewareConfig } from './types';
2
2
  import { default as express, Application } from 'express';
3
3
  export declare const isDevelopment: boolean;
4
4
  export declare const isProduction: boolean;
@@ -14,7 +14,9 @@ export declare class Sprint {
14
14
  private routesLoaded;
15
15
  private server;
16
16
  private loadedMiddlewares;
17
- constructor({ port, routesPath, middlewaresPath, cronjobsPath, jsonLimit, urlEncodedLimit, prefix, autoListen }?: SprintOptions);
17
+ private openapi;
18
+ private registeredRoutes;
19
+ constructor();
18
20
  private init;
19
21
  private loadDefaults;
20
22
  /**
@@ -31,6 +33,7 @@ export declare class Sprint {
31
33
  private loadNotFound;
32
34
  /** Applies prefix to a path */
33
35
  private applyPrefix;
36
+ private generateOpenAPISpec;
34
37
  get(path: string, handler: Handler): express.Application;
35
38
  post(path: string, handler: Handler): express.Application;
36
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,EAAE,aAAa,EAAE,gBAAgB,EAAoB,MAAM,SAAS,CAAC;AACrF,OAAO,OAAO,EAAE,EAAE,WAAW,EAAoD,MAAM,SAAS,CAAC;AAejG,eAAO,MAAM,aAAa,SAAQ,CAAC;AACnC,eAAO,MAAM,YAAY,SAAS,CAAC;AAEnC,qBAAa,MAAM;IACR,GAAG,EAAE,WAAW,CAAC;IACxB,OAAO,CAAC,IAAI,CAAqC;IACjD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,iBAAiB,CAA0B;gBAEvC,EACR,IAAuB,EACvB,UAAuB,EACvB,eAAiC,EACjC,YAA2B,EAC3B,SAAkB,EAClB,eAAwB,EACxB,MAAW,EACX,UAAiB,EACpB,GAAE,aAAkB;YAkBP,IAAI;IA+BlB,OAAO,CAAC,YAAY;IAiDpB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA4B9B;;OAEG;YACW,eAAe;IAgC7B,OAAO,CAAC,eAAe;YAcT,UAAU;YAgDV,YAAY;IAmB1B,OAAO,CAAC,YAAY;IAgCpB,+BAA+B;IAC/B,OAAO,CAAC,WAAW;IAMZ,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;IAclF,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI;CAsDzC"}
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;CAwDzC"}
@@ -67,6 +67,30 @@ export interface SprintOptions {
67
67
  prefix?: string;
68
68
  /** Auto-start the server. Default: true */
69
69
  autoListen?: boolean;
70
+ openapi?: {
71
+ enabled?: boolean;
72
+ generateOnBuild?: boolean;
73
+ swaggerUi?: {
74
+ enabled?: boolean;
75
+ };
76
+ };
77
+ }
78
+ export interface SprintConfig {
79
+ port?: string | number | null;
80
+ routesPath?: string;
81
+ middlewaresPath?: string;
82
+ cronjobsPath?: string;
83
+ jsonLimit?: string;
84
+ urlEncodedLimit?: string;
85
+ prefix?: string;
86
+ autoListen?: boolean;
87
+ openapi?: {
88
+ enabled?: boolean;
89
+ generateOnBuild?: boolean;
90
+ swaggerUi?: {
91
+ enabled?: boolean;
92
+ };
93
+ };
70
94
  }
71
95
  export type { NextFunction } from 'express';
72
96
  //# sourceMappingURL=types.d.ts.map
@@ -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;CACxB;AAED,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprint-es",
3
- "version": "0.0.52",
3
+ "version": "0.0.56",
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