@synchjs/ewb 1.0.1 → 1.0.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.
@@ -9,8 +9,9 @@ import rateLimit from "express-rate-limit";
9
9
  import Ajv from "ajv";
10
10
  import addFormats from "ajv-formats";
11
11
  import { MetadataStorage } from "../Decorations";
12
- import * as jwt from "jsonwebtoken";
13
- import { AUTH_METADATA_KEY, PUBLIC_METADATA_KEY, } from "../Decorations/Authorized";
12
+ import { createServer } from "http";
13
+ import { Server as SocketServer } from "socket.io";
14
+ import { PUBLIC_METADATA_KEY, ROLES_METADATA_KEY, } from "../Decorations/Authorized";
14
15
  import { SECURITY_METADATA_KEY, } from "../Decorations/Security";
15
16
  import { TAILWIND_METADATA_KEY, } from "../Decorations/Tailwind";
16
17
  import { SERVE_HTML_METADATA_KEY } from "../Decorations/Serve";
@@ -21,6 +22,7 @@ export class Server {
21
22
  _app;
22
23
  _port;
23
24
  _controllersDir;
25
+ _viewsDir;
24
26
  _id;
25
27
  _enableSwagger;
26
28
  _swaggerPath;
@@ -29,13 +31,15 @@ export class Server {
29
31
  _ajv;
30
32
  _securityHandlers;
31
33
  _container;
34
+ _roleHandler;
35
+ _userHandler;
32
36
  _serverInstance;
37
+ _io;
33
38
  // Stats
34
39
  _controllerCount = 0;
35
40
  _routeCount = 0;
36
41
  _tailwindEnabled = false;
37
42
  _devMode = false;
38
- _sseClients = [];
39
43
  constructor(options) {
40
44
  this._port = options.port;
41
45
  this._app = express();
@@ -45,21 +49,44 @@ export class Server {
45
49
  this._enableLogging =
46
50
  options.logging === undefined ? true : options.logging;
47
51
  this._controllersDir = options.controllersDir || "controllers";
52
+ this._viewsDir = options.viewsDir;
48
53
  this._securityHandlers = options.securityHandlers || {};
49
54
  this._securitySchemes = options.securitySchemes;
50
55
  this._container = options.container;
51
- // Initialize AJV
52
- this._ajv = new Ajv({ allErrors: true, strict: false });
56
+ this._roleHandler = options.roleHandler;
57
+ // Initialize AJV with strict mode for better security
58
+ this._ajv = new Ajv({
59
+ allErrors: true,
60
+ strict: true,
61
+ removeAdditional: true, // Automatically remove properties not in schema
62
+ });
53
63
  addFormats(this._ajv);
54
64
  // Security Middleware
55
- this._app.use(helmet(options.helmetOptions));
65
+ const helmetOptions = options.helmetOptions || {};
66
+ if (process.env.NODE_ENV !== "production") {
67
+ // Relax CSP for HMR in development
68
+ helmetOptions.contentSecurityPolicy = {
69
+ directives: {
70
+ defaultSrc: ["'self'"],
71
+ scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'", "blob:"],
72
+ connectSrc: ["'self'", "ws:", "wss:", "http:", "https:"],
73
+ styleSrc: ["'self'", "'unsafe-inline'"],
74
+ imgSrc: ["'self'", "data:", "blob:"],
75
+ frameSrc: ["'self'"],
76
+ },
77
+ };
78
+ }
79
+ this._app.use(helmet(helmetOptions));
56
80
  this._app.use(cors(options.corsOptions));
57
81
  this._app.use(rateLimit(options.rateLimitOptions || {
58
82
  windowMs: 15 * 60 * 1000,
59
83
  max: 100,
84
+ skip: (req) => ServeMemoryStore.instance.getAsset(req.path) !== undefined, // Don't rate limit static assets
60
85
  }));
61
- this._app.use(express.json());
62
- this._devMode = options.devMode ?? process.env.NODE_ENV === "development";
86
+ this._app.use(express.json({ limit: "1mb" })); // Protection against large payloads
87
+ // Clear cache on startup
88
+ ServeMemoryStore.instance.clearCache();
89
+ this._devMode = process.env.NODE_ENV !== "production";
63
90
  if (this._devMode) {
64
91
  this.setupHmr();
65
92
  }
@@ -67,22 +94,16 @@ export class Server {
67
94
  }
68
95
  setupHmr() {
69
96
  ServeMemoryStore.instance.setDevMode(true);
70
- ServeMemoryStore.instance.onRebuild(() => {
71
- this.log(`[${this._id}] Sending reload signal to ${this._sseClients.length} clients`);
72
- this._sseClients.forEach((res) => {
73
- res.write("data: reload\n\n");
74
- });
75
- });
76
- this._app.get("/ebw-hmr", (req, res) => {
77
- res.setHeader("Content-Type", "text/event-stream");
78
- res.setHeader("Cache-Control", "no-cache");
79
- res.setHeader("Connection", "keep-alive");
80
- res.flushHeaders();
81
- this._sseClients.push(res);
82
- req.on("close", () => {
83
- this._sseClients = this._sseClients.filter((c) => c !== res);
84
- });
97
+ ServeMemoryStore.instance.onRebuild((data) => {
98
+ if (data && data.html) {
99
+ this._io?.emit("rebuild", { html: data.html });
100
+ }
101
+ else {
102
+ this._io?.emit("reload");
103
+ }
85
104
  });
105
+ // No redundant global watcher here, we rely on ServeMemoryStore's specific watchers
106
+ // which rebuild before notifying.
86
107
  }
87
108
  log(message) {
88
109
  if (this._enableLogging) {
@@ -90,6 +111,9 @@ export class Server {
90
111
  }
91
112
  }
92
113
  async init() {
114
+ if (this._userHandler) {
115
+ this.setupAuthRoutes();
116
+ }
93
117
  await this.loadControllers();
94
118
  if (this._enableSwagger) {
95
119
  this.setupSwagger();
@@ -100,6 +124,11 @@ export class Server {
100
124
  return next();
101
125
  const asset = ServeMemoryStore.instance.getAsset(req.path);
102
126
  if (asset) {
127
+ if (this._devMode) {
128
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
129
+ res.setHeader("Pragma", "no-cache");
130
+ res.setHeader("Expires", "0");
131
+ }
103
132
  res.type(asset.type).send(Buffer.from(asset.content));
104
133
  return;
105
134
  }
@@ -107,13 +136,31 @@ export class Server {
107
136
  });
108
137
  // Global Error Handler
109
138
  this._app.use((err, req, res, next) => {
110
- console.error(`[${this._id}] Error:`, err);
139
+ const isProd = process.env.NODE_ENV === "production";
140
+ if (!isProd) {
141
+ console.error(`[${this._id}] Error:`, err);
142
+ }
111
143
  res.status(err.status || 500).json({
112
144
  error: "Internal Server Error",
113
- message: "An unexpected error occurred",
145
+ message: isProd
146
+ ? "An unexpected error occurred"
147
+ : err.message || "An unexpected error occurred",
148
+ // Mask stack trace in production
149
+ stack: isProd ? undefined : err.stack,
114
150
  });
115
151
  });
116
- this._serverInstance = this._app.listen(this._port, () => {
152
+ this._serverInstance = createServer(this._app);
153
+ if (this._devMode) {
154
+ this._io = new SocketServer(this._serverInstance, {
155
+ cors: {
156
+ origin: [/localhost/, /127\.0\.0\.1/], // Restrict HMR to local development origin
157
+ },
158
+ });
159
+ this._io.on("connection", (socket) => {
160
+ // Disconnected client log
161
+ });
162
+ }
163
+ this._serverInstance.listen(this._port, () => {
117
164
  if (this._enableLogging) {
118
165
  this.printStartupMessage();
119
166
  }
@@ -131,6 +178,9 @@ export class Server {
131
178
  `${pc.bold(pad("Tailwind:"))}${this._tailwindEnabled ? pc.blue("Enabled") : pc.dim("Disabled")}`,
132
179
  `${pc.bold(pad("HMR:"))}${this._devMode ? pc.cyan("Active") : pc.dim("Inactive")}`,
133
180
  ];
181
+ if (this._viewsDir) {
182
+ lines.push(`${pc.bold(pad("Views:"))}${this._viewsDir}`);
183
+ }
134
184
  if (this._enableSwagger) {
135
185
  lines.push(`${pc.bold(pad("Swagger:"))}http://localhost:${this._port}${this._swaggerPath}`);
136
186
  }
@@ -151,6 +201,40 @@ export class Server {
151
201
  }
152
202
  global.servers.delete(this._id);
153
203
  }
204
+ setUserHandler(handler) {
205
+ this._userHandler = handler;
206
+ }
207
+ setupAuthRoutes() {
208
+ if (!this._userHandler)
209
+ return;
210
+ this._app.post("/auth/signin", async (req, res, next) => {
211
+ try {
212
+ const result = await this._userHandler.signin(req, res);
213
+ res.json(result);
214
+ }
215
+ catch (err) {
216
+ next(err);
217
+ }
218
+ });
219
+ this._app.post("/auth/signup", async (req, res, next) => {
220
+ try {
221
+ const result = await this._userHandler.signup(req, res);
222
+ res.json(result);
223
+ }
224
+ catch (err) {
225
+ next(err);
226
+ }
227
+ });
228
+ this._app.post("/auth/logout", async (req, res, next) => {
229
+ try {
230
+ const result = await this._userHandler.logout(req, res);
231
+ res.json(result);
232
+ }
233
+ catch (err) {
234
+ next(err);
235
+ }
236
+ });
237
+ }
154
238
  async loadControllers() {
155
239
  const absoluteControllersPath = path.resolve(process.cwd(), this._controllersDir);
156
240
  if (!fs.existsSync(absoluteControllersPath)) {
@@ -178,28 +262,24 @@ export class Server {
178
262
  const instance = this._container?.get
179
263
  ? this._container.get(controller.target)
180
264
  : new controller.target();
181
- // Check class level auth
182
- const classAuthInfo = Reflect.getMetadata(AUTH_METADATA_KEY, controller.target);
265
+ const router = express.Router({ mergeParams: true });
266
+ if (controller.middlewares) {
267
+ router.use(...controller.middlewares);
268
+ }
183
269
  for (const route of controller.routes) {
184
- const fullPath = (controller.path + "/" + route.path).replace(/\/+/g, "/");
270
+ // Used only for Swagger and internal path logic if needed,
271
+ // but actual routing uses route.path directly which supports RegExp
272
+ const routePathString = route.path instanceof RegExp ? route.path.source : route.path;
185
273
  const middlewares = [];
186
- // Check method level metadata
187
- const methodAuthInfo = Reflect.getMetadata(AUTH_METADATA_KEY, controller.target.prototype, route.handlerName);
274
+ // Check ROLES metadata
275
+ const classRoles = Reflect.getMetadata(ROLES_METADATA_KEY, controller.target);
276
+ const methodRoles = Reflect.getMetadata(ROLES_METADATA_KEY, controller.target.prototype, route.handlerName);
277
+ const requiredRoles = methodRoles || classRoles;
188
278
  const isPublic = Reflect.getMetadata(PUBLIC_METADATA_KEY, controller.target.prototype, route.handlerName);
189
- // Determine if auth is required and which secret to use
190
- let authSecret;
191
- if (isPublic) {
192
- authSecret = undefined;
193
- }
194
- else if (methodAuthInfo) {
195
- authSecret = methodAuthInfo.secret || process.env.JWT_SECRET;
196
- }
197
- else if (classAuthInfo) {
198
- authSecret = classAuthInfo.secret || process.env.JWT_SECRET;
199
- }
200
- // 1. Auth Middleware (New Decorator Logic)
201
- if (authSecret) {
202
- middlewares.push(this.createJwtMiddleware(authSecret));
279
+ // 1. Auth & Role Middlewares
280
+ if (!isPublic) {
281
+ // If not public, we always run the role check (which might include authentication via UserHandler)
282
+ middlewares.push(this.createRoleMiddleware(requiredRoles || []));
203
283
  // Inject Swagger security definition automatically
204
284
  if (!route.swagger) {
205
285
  route.swagger = {
@@ -219,7 +299,8 @@ export class Server {
219
299
  route.swagger.security.push({ bearerAuth: [] });
220
300
  }
221
301
  }
222
- // 2. Generic Security Middleware (@Security, @OAuth, etc)
302
+ // 1.1 Role Middlewares - now handled above for non-public routes
303
+ // 2. Generic Security Middleware (@Security, @ApiKey, etc)
223
304
  const classGenericSecurity = Reflect.getMetadata(SECURITY_METADATA_KEY, controller.target) || [];
224
305
  const methodGenericSecurity = Reflect.getMetadata(SECURITY_METADATA_KEY, controller.target.prototype, route.handlerName) || [];
225
306
  let genericRequirements = [];
@@ -293,17 +374,26 @@ export class Server {
293
374
  }
294
375
  // 3. Custom Middlewares
295
376
  // Controller Level
296
- if (controller.middlewares) {
297
- middlewares.push(...controller.middlewares);
298
- }
377
+ // if (controller.middlewares) {
378
+ // middlewares.push(...controller.middlewares);
379
+ // }
380
+ // Controller middlewares are now mounted on the router level.
299
381
  // Route Level
300
382
  if (route.middlewares) {
301
383
  middlewares.push(...route.middlewares);
302
384
  }
303
385
  // 3. Route Handler
304
- const handler = (req, res, next) => {
386
+ const handler = async (req, res, next) => {
305
387
  try {
306
- const result = instance[route.handlerName](req, res, next);
388
+ const user = this._userHandler
389
+ ? await this._userHandler.authenticate(req, res)
390
+ : req.user;
391
+ const result = instance[route.handlerName]({
392
+ req,
393
+ res,
394
+ user,
395
+ next,
396
+ });
307
397
  const handleResult = (val) => {
308
398
  if (val !== undefined && !res.headersSent) {
309
399
  if (typeof val === "string") {
@@ -325,33 +415,61 @@ export class Server {
325
415
  next(error);
326
416
  }
327
417
  };
328
- this._app[route.method](fullPath, ...middlewares, handler);
418
+ // Register route on the controller router
419
+ router[route.method](route.path, ...middlewares, handler);
329
420
  this._routeCount++;
330
421
  // Pre-build if @Serve is used
331
422
  const htmlPath = Reflect.getMetadata(SERVE_HTML_METADATA_KEY, controller.target.prototype, route.handlerName);
332
423
  if (htmlPath) {
333
- this.log(`[${this._id}] Pre-building HTML: ${htmlPath}`);
424
+ // Silent pre-build
334
425
  await ServeMemoryStore.instance.buildAndCache(htmlPath, tailwindOptions);
335
426
  }
336
427
  }
428
+ this._app.use(controller.path, router);
337
429
  }
338
430
  }
339
- createJwtMiddleware(secret) {
340
- return (req, res, next) => {
341
- const authHeader = req.headers.authorization;
342
- if (!authHeader || !authHeader.startsWith("Bearer ")) {
343
- return res
344
- .status(401)
345
- .json({ error: "Unauthorized: Missing Bearer token" });
431
+ createRoleMiddleware(roles) {
432
+ return async (req, res, next) => {
433
+ const user = this._userHandler
434
+ ? await this._userHandler.authenticate(req, res)
435
+ : req.user;
436
+ if (!user) {
437
+ return res.status(401).json({ error: "Unauthorized: No user found" });
438
+ }
439
+ // If no specific roles required, but it's not public, just authentication is enough
440
+ if (roles.length === 0) {
441
+ return next();
442
+ }
443
+ if (!this._roleHandler) {
444
+ // Default role check behavior
445
+ const userRoles = Array.isArray(user.roles)
446
+ ? user.roles
447
+ : user.role
448
+ ? [user.role]
449
+ : [];
450
+ const hasRole = roles.some((role) => userRoles.includes(role));
451
+ if (!hasRole) {
452
+ return res.status(403).json({
453
+ error: "Forbidden: You do not have the required permissions",
454
+ });
455
+ }
456
+ return next();
346
457
  }
347
- const token = authHeader.split(" ")[1];
348
458
  try {
349
- const decoded = jwt.verify(token, secret);
350
- req.user = decoded; // Attach user to request
351
- next();
459
+ const result = await this._roleHandler(req, roles);
460
+ if (result) {
461
+ // Assuming 'result' is a boolean indicating success
462
+ next();
463
+ }
464
+ else {
465
+ // Applying the original logic for role failure.
466
+ res.status(403).json({
467
+ error: "Forbidden: You do not have the required permissions",
468
+ });
469
+ }
352
470
  }
353
471
  catch (err) {
354
- return res.status(403).json({ error: "Forbidden: Invalid token" });
472
+ next(err);
355
473
  }
356
474
  };
357
475
  }
@@ -420,7 +538,8 @@ export class Server {
420
538
  const paths = {};
421
539
  for (const controller of controllers) {
422
540
  for (const route of controller.routes) {
423
- const fullPath = (controller.path + "/" + route.path)
541
+ const routePathString = route.path instanceof RegExp ? route.path.source : route.path;
542
+ const fullPath = (controller.path + "/" + routePathString)
424
543
  .replace(/\/+/g, "/")
425
544
  .replace(/:([^/]+)/g, "{$1}");
426
545
  if (!paths[fullPath]) {
@@ -0,0 +1,21 @@
1
+ import { Request, Response } from "express";
2
+ export declare abstract class UserHandler {
3
+ /**
4
+ * Optional: Logic to authenticate the request and return the user object.
5
+ * If this is not implemented, 'user' will be undefined in route parameters.
6
+ */
7
+ authenticate(req: Request, res: Response): Promise<any | null>;
8
+ /**
9
+ * Sign in logic. Usually returns a token or user info.
10
+ */
11
+ abstract signin(req: Request, res: Response): Promise<any> | any;
12
+ /**
13
+ * Sign up logic.
14
+ */
15
+ abstract signup(req: Request, res: Response): Promise<any> | any;
16
+ /**
17
+ * Logout logic.
18
+ */
19
+ abstract logout(req: Request, res: Response): Promise<any> | any;
20
+ }
21
+ //# sourceMappingURL=UserHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserHandler.d.ts","sourceRoot":"","sources":["../../src/Components/UserHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAgB,MAAM,SAAS,CAAC;AAE1D,8BAAsB,WAAW;IAC/B;;;OAGG;IACU,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAI3E;;OAEG;aACa,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;IAEvE;;OAEG;aACa,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;IAEvE;;OAEG;aACa,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG;CACxE"}
@@ -0,0 +1,9 @@
1
+ export class UserHandler {
2
+ /**
3
+ * Optional: Logic to authenticate the request and return the user object.
4
+ * If this is not implemented, 'user' will be undefined in route parameters.
5
+ */
6
+ async authenticate(req, res) {
7
+ return req.user;
8
+ }
9
+ }
@@ -1,8 +1,5 @@
1
- export declare const AUTH_METADATA_KEY = "auth:info";
2
1
  export declare const PUBLIC_METADATA_KEY = "auth:public";
3
- export interface AuthInfo {
4
- secret?: string;
5
- }
6
- export declare function BearerAuth(secret?: string): MethodDecorator & ClassDecorator;
2
+ export declare const ROLES_METADATA_KEY = "auth:roles";
3
+ export declare function Authorized(roles?: string[]): MethodDecorator & ClassDecorator;
7
4
  export declare function Public(): MethodDecorator;
8
5
  //# sourceMappingURL=Authorized.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Authorized.d.ts","sourceRoot":"","sources":["../../src/Decorations/Authorized.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAEjD,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,cAAc,CAmB5E;AAED,wBAAgB,MAAM,IAAI,eAAe,CAQxC"}
1
+ {"version":3,"file":"Authorized.d.ts","sourceRoot":"","sources":["../../src/Decorations/Authorized.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,wBAAgB,UAAU,CACxB,KAAK,GAAE,MAAM,EAAO,GACnB,eAAe,GAAG,cAAc,CAclC;AAED,wBAAgB,MAAM,IAAI,eAAe,CAQxC"}
@@ -1,15 +1,15 @@
1
1
  // Key to store auth metadata
2
- export const AUTH_METADATA_KEY = "auth:info";
3
2
  export const PUBLIC_METADATA_KEY = "auth:public";
4
- export function BearerAuth(secret) {
3
+ export const ROLES_METADATA_KEY = "auth:roles";
4
+ export function Authorized(roles = []) {
5
5
  return function (target, propertyKey, descriptor) {
6
- // If used on a method
7
6
  if (propertyKey) {
8
- Reflect.defineMetadata(AUTH_METADATA_KEY, { secret }, target, propertyKey);
7
+ Reflect.defineMetadata(ROLES_METADATA_KEY, roles, target, propertyKey);
8
+ // Explicitly mark as not public to prevent bypass if @Public() is also used
9
+ Reflect.defineMetadata(PUBLIC_METADATA_KEY, false, target, propertyKey);
9
10
  }
10
11
  else {
11
- // If used on a class
12
- Reflect.defineMetadata(AUTH_METADATA_KEY, { secret }, target);
12
+ Reflect.defineMetadata(ROLES_METADATA_KEY, roles, target);
13
13
  }
14
14
  };
15
15
  }
@@ -3,7 +3,7 @@ import type { RequestHandler } from "express";
3
3
  import { OpenAPIV3 } from "openapi-types";
4
4
  export interface RouteDefinition {
5
5
  method: "get" | "post" | "put" | "delete" | "patch";
6
- path: string;
6
+ path: string | RegExp;
7
7
  handlerName: string;
8
8
  swagger?: OpenAPIV3.OperationObject;
9
9
  middlewares?: RequestHandler[];
@@ -1 +1 @@
1
- {"version":3,"file":"Metadata.d.ts","sourceRoot":"","sources":["../../src/Decorations/Metadata.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC;IACpC,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC,yBAAyB,EAAE,CAAC;IACjD,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAkB;IAC1C,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO;IAEP,WAAkB,QAAQ,IAAI,eAAe,CAK5C;IAEM,aAAa,CAAC,UAAU,EAAE,oBAAoB;IAI9C,cAAc,IAAI,oBAAoB,EAAE;IAIxC,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe;IAc5C,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,eAAe,EAAE;CAGjD"}
1
+ {"version":3,"file":"Metadata.d.ts","sourceRoot":"","sources":["../../src/Decorations/Metadata.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpD,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC;IACpC,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC,yBAAyB,EAAE,CAAC;IACjD,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAkB;IAC1C,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO;IAEP,WAAkB,QAAQ,IAAI,eAAe,CAK5C;IAEM,aAAa,CAAC,UAAU,EAAE,oBAAoB;IAI9C,cAAc,IAAI,oBAAoB,EAAE;IAIxC,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe;IAc5C,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,eAAe,EAAE;CAGjD"}
@@ -1,8 +1,8 @@
1
1
  import "reflect-metadata";
2
2
  import { OpenAPIV3 } from "openapi-types";
3
- export declare function Get(path: string, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
4
- export declare function Post(path: string, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
5
- export declare function Put(path: string, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
6
- export declare function Delete(path: string, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
7
- export declare function Patch(path: string, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
3
+ export declare function Get(path: string | RegExp, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
4
+ export declare function Post(path: string | RegExp, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
5
+ export declare function Put(path: string | RegExp, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
6
+ export declare function Delete(path: string | RegExp, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
7
+ export declare function Patch(path: string | RegExp, swagger?: OpenAPIV3.OperationObject): MethodDecorator;
8
8
  //# sourceMappingURL=Methods.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Methods.d.ts","sourceRoot":"","sources":["../../src/Decorations/Methods.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,wBAAgB,GAAG,CACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,IAAI,CAClB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,GAAG,CACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB"}
1
+ {"version":3,"file":"Methods.d.ts","sourceRoot":"","sources":["../../src/Decorations/Methods.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,wBAAgB,GAAG,CACjB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,IAAI,CAClB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,GAAG,CACjB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB;AAED,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,CAAC,EAAE,SAAS,CAAC,eAAe,GAClC,eAAe,CAajB"}
@@ -4,6 +4,5 @@ export interface SecurityRequirement {
4
4
  [name: string]: string[];
5
5
  }
6
6
  export declare function Security(name: string, scopes?: string[]): MethodDecorator & ClassDecorator;
7
- export declare function OAuth(scopes?: string[]): MethodDecorator & ClassDecorator;
8
7
  export declare function ApiKey(name: string): MethodDecorator & ClassDecorator;
9
8
  //# sourceMappingURL=Security.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../src/Decorations/Security.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,eAAO,MAAM,qBAAqB,eAAqB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAM,EAAO,GACpB,eAAe,GAAG,cAAc,CAyBlC;AAED,wBAAgB,KAAK,CAAC,MAAM,GAAE,MAAM,EAAO,GAAG,eAAe,GAAG,cAAc,CAE7E;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,cAAc,CAErE"}
1
+ {"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../src/Decorations/Security.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,eAAO,MAAM,qBAAqB,eAAqB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAM,EAAO,GACpB,eAAe,GAAG,cAAc,CAyBlC;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,cAAc,CAErE"}
@@ -16,9 +16,6 @@ export function Security(name, scopes = []) {
16
16
  }
17
17
  };
18
18
  }
19
- export function OAuth(scopes = []) {
20
- return Security("oauth2", scopes);
21
- }
22
19
  export function ApiKey(name) {
23
20
  return Security(name, []);
24
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Serve.d.ts","sourceRoot":"","sources":["../../src/Decorations/Serve.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAmDvD"}
1
+ {"version":3,"file":"Serve.d.ts","sourceRoot":"","sources":["../../src/Decorations/Serve.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAwEvD"}
@@ -6,10 +6,12 @@ export function Serve(htmlPath) {
6
6
  // Store the HTML path in metadata for pre-building
7
7
  Reflect.defineMetadata(SERVE_HTML_METADATA_KEY, htmlPath, target, propertyKey);
8
8
  const originalMethod = descriptor.value;
9
- descriptor.value = async function (req, res, next) {
9
+ descriptor.value = async function ({ req, res, user, next, }) {
10
10
  try {
11
11
  // Execute the original method
12
- const result = await originalMethod.apply(this, [req, res, next]);
12
+ const result = await originalMethod.apply(this, [
13
+ { req, res, user, next },
14
+ ]);
13
15
  // Check if response has been sent or if result is not undefined/void
14
16
  if (res.headersSent || result !== undefined) {
15
17
  return result;
@@ -19,14 +21,25 @@ export function Serve(htmlPath) {
19
21
  // If no response, build and serve the HTML
20
22
  const html = await ServeMemoryStore.instance.buildAndCache(htmlPath, tailwindOptions);
21
23
  if (html) {
24
+ const devMode = process.env.NODE_ENV === "development";
25
+ if (devMode) {
26
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
27
+ res.setHeader("Pragma", "no-cache");
28
+ res.setHeader("Expires", "0");
29
+ }
22
30
  res.type("html").send(html);
23
31
  }
24
- else {
32
+ else if (next) {
25
33
  next(); // No HTML found?
26
34
  }
27
35
  }
28
36
  catch (error) {
29
- next(error);
37
+ if (next) {
38
+ next(error);
39
+ }
40
+ else {
41
+ throw error;
42
+ }
30
43
  }
31
44
  };
32
45
  return descriptor;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import "./globals";
2
2
  export * from "./Components/Server";
3
3
  export * from "./Components/ServeMemoryStore";
4
+ export * from "./Components/UserHandler";
4
5
  export * from "./Decorations";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,CAAC;AACnB,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -2,4 +2,5 @@ global.servers = new Map();
2
2
  import "./globals";
3
3
  export * from "./Components/Server";
4
4
  export * from "./Components/ServeMemoryStore";
5
+ export * from "./Components/UserHandler";
5
6
  export * from "./Decorations";