@devbro/pashmak 0.1.38 → 0.1.39

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.
@@ -782,7 +782,7 @@ async function dbTransaction(req, res, next) {
782
782
  await db().beginTransaction();
783
783
  await next();
784
784
  await db().commit();
785
- } finally {
785
+ } catch {
786
786
  await db().rollback();
787
787
  }
788
788
  }
@@ -29,7 +29,7 @@ async function dbTransaction(req, res, next) {
29
29
  await db().beginTransaction();
30
30
  await next();
31
31
  await db().commit();
32
- } finally {
32
+ } catch {
33
33
  await db().rollback();
34
34
  }
35
35
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middlewares.mts"],"sourcesContent":["import { Middleware, Request, Response } from \"@devbro/neko-router\";\nimport { logger, db, cache } from \"./facades.mjs\";\nimport { HttpTooManyRequestsError } from \"@devbro/neko-http\";\n\nexport function cors(\n options: { allowedOrigins?: (string | RegExp)[] } = {},\n): (req: Request, res: Response, next: () => Promise<void>) => Promise<void> {\n return async (\n req: Request,\n res: Response,\n next: () => Promise<void>,\n ): Promise<void> => {\n const allowedOrigins = options.allowedOrigins || [\"*\"];\n const origin = req.headers.origin || \"*\";\n\n for (const allowedOrigin of allowedOrigins) {\n if (typeof allowedOrigin === \"string\" && allowedOrigin === origin) {\n res.setHeader(\"Access-Control-Allow-Origin\", allowedOrigin);\n break;\n } else if (\n allowedOrigin instanceof RegExp &&\n allowedOrigin.test(origin)\n ) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n break;\n } else if (allowedOrigin === \"*\") {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n break;\n }\n }\n\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n await next();\n };\n}\n\nexport async function dbTransaction(\n req: Request,\n res: Response,\n next: () => Promise<void>,\n): Promise<void> {\n try {\n await db().beginTransaction();\n await next();\n await db().commit();\n } finally {\n await db().rollback();\n }\n}\n\nexport type RateLimiterMiddlewareParams = {\n generateIdentifier: (req: Request) => string;\n maxRequests: number;\n windowTimeSize: number;\n windowCount: number;\n};\n\n/**\n * Rate limiter middleware using a sliding window algorithm.\n *\n * This middleware tracks request counts across multiple time windows to prevent abuse.\n * It uses atomic cache increments to ensure accurate counting in concurrent environments.\n *\n * @example\n * ```typescript\n * // Basic usage with defaults (200 requests per 2 minutes)\n * authnedRouter.addGlobalMiddleware(RateLimiterMiddleware);\n *\n * // Custom configuration\n * authnedRouter.addGlobalMiddleware(RateLimiterMiddleware.getInstance({\n * generateIdentifier: (req) => req.headers['x-api-key'] || req.socket.remoteAddress,\n * maxRequests: 200, // Allow 200 requests\n * windowTimeSize: 30, // in 30 second windows\n * windowCount: 4 // looking back 4 windows (total: 120 seconds)\n * }));\n * ```\n *\n * @param params.generateIdentifier - Function to generate unique identifier for rate limiting (default: IP address)\n * @param params.maxRequests - Maximum number of requests allowed across all windows (default: 200)\n * @param params.windowTimeSize - Size of each time window in seconds (default: 30)\n * @param params.windowCount - Number of previous windows to check (default: 4)\n *\n * The total time period checked is `windowTimeSize * windowCount` seconds.\n * Default configuration: 200 requests per 120 seconds (30s × 4 windows).\n *\n * @throws {HttpTooManyRequestsError} When rate limit is exceeded\n */\nexport class RateLimiterMiddleware extends Middleware {\n static singletonInstance: Middleware | undefined = undefined;\n static getInstance(\n params: Partial<RateLimiterMiddlewareParams> = {},\n ): Middleware {\n if (RateLimiterMiddleware.singletonInstance) {\n return RateLimiterMiddleware.singletonInstance;\n }\n let default_params: RateLimiterMiddlewareParams = {\n generateIdentifier: (req: Request) =>\n req.socket.remoteAddress || \"unknown\",\n maxRequests: 200,\n windowTimeSize: 30,\n windowCount: 4,\n };\n const merged_params = { ...default_params, ...params };\n return (RateLimiterMiddleware.singletonInstance = new RateLimiterMiddleware(\n merged_params,\n ));\n }\n\n constructor(private params: RateLimiterMiddlewareParams) {\n super();\n }\n\n async call(\n req: Request,\n res: Response,\n next: () => Promise<void>,\n ): Promise<void> {\n let window = parseInt(\n (Date.now() / 1000 / this.params.windowTimeSize).toString(),\n );\n let key = `rate_limiter:${this.params.generateIdentifier(req)}:${window}`;\n let count: number = (await cache().get(key)) || 0;\n if (!count) {\n await cache().put(\n key,\n 1,\n this.params.windowTimeSize * (this.params.windowCount + 1),\n );\n count = 1;\n } else {\n count = await cache().increment(key, 1);\n }\n\n for (let i = 1; i < this.params.windowCount; i++) {\n count += parseInt(\n (await cache().get(\n `rate_limiter:${this.params.generateIdentifier(req)}:${window - i}`,\n )) || \"0\",\n );\n }\n\n if (count > this.params.maxRequests) {\n throw new HttpTooManyRequestsError(\n \"Too many requests. Please try again later.\",\n );\n }\n\n await next();\n return;\n }\n}\n"],"mappings":";;AAAA,SAAS,kBAAqC;AAC9C,SAAiB,IAAI,aAAa;AAClC,SAAS,gCAAgC;AAElC,SAAS,KACd,UAAoD,CAAC,GACsB;AAC3E,SAAO,OACL,KACA,KACA,SACkB;AAClB,UAAM,iBAAiB,QAAQ,kBAAkB,CAAC,GAAG;AACrD,UAAM,SAAS,IAAI,QAAQ,UAAU;AAErC,eAAW,iBAAiB,gBAAgB;AAC1C,UAAI,OAAO,kBAAkB,YAAY,kBAAkB,QAAQ;AACjE,YAAI,UAAU,+BAA+B,aAAa;AAC1D;AAAA,MACF,WACE,yBAAyB,UACzB,cAAc,KAAK,MAAM,GACzB;AACA,YAAI,UAAU,+BAA+B,MAAM;AACnD;AAAA,MACF,WAAW,kBAAkB,KAAK;AAChC,YAAI,UAAU,+BAA+B,GAAG;AAChD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,gCAAgC,GAAG;AACjD,UAAM,KAAK;AAAA,EACb;AACF;AA9BgB;AAgChB,eAAsB,cACpB,KACA,KACA,MACe;AACf,MAAI;AACF,UAAM,GAAG,EAAE,iBAAiB;AAC5B,UAAM,KAAK;AACX,UAAM,GAAG,EAAE,OAAO;AAAA,EACpB,UAAE;AACA,UAAM,GAAG,EAAE,SAAS;AAAA,EACtB;AACF;AAZsB;AAmDf,MAAM,8BAA8B,WAAW;AAAA,EAqBpD,YAAoB,QAAqC;AACvD,UAAM;AADY;AAAA,EAEpB;AAAA,EA9GF,OAuFsD;AAAA;AAAA;AAAA,EACpD,OAAO,oBAA4C;AAAA,EACnD,OAAO,YACL,SAA+C,CAAC,GACpC;AACZ,QAAI,sBAAsB,mBAAmB;AAC3C,aAAO,sBAAsB;AAAA,IAC/B;AACA,QAAI,iBAA8C;AAAA,MAChD,oBAAoB,wBAAC,QACnB,IAAI,OAAO,iBAAiB,WADV;AAAA,MAEpB,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,aAAa;AAAA,IACf;AACA,UAAM,gBAAgB,EAAE,GAAG,gBAAgB,GAAG,OAAO;AACrD,WAAQ,sBAAsB,oBAAoB,IAAI;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA,EAMA,MAAM,KACJ,KACA,KACA,MACe;AACf,QAAI,SAAS;AAAA,OACV,KAAK,IAAI,IAAI,MAAO,KAAK,OAAO,gBAAgB,SAAS;AAAA,IAC5D;AACA,QAAI,MAAM,gBAAgB,KAAK,OAAO,mBAAmB,GAAG,CAAC,IAAI,MAAM;AACvE,QAAI,QAAiB,MAAM,MAAM,EAAE,IAAI,GAAG,KAAM;AAChD,QAAI,CAAC,OAAO;AACV,YAAM,MAAM,EAAE;AAAA,QACZ;AAAA,QACA;AAAA,QACA,KAAK,OAAO,kBAAkB,KAAK,OAAO,cAAc;AAAA,MAC1D;AACA,cAAQ;AAAA,IACV,OAAO;AACL,cAAQ,MAAM,MAAM,EAAE,UAAU,KAAK,CAAC;AAAA,IACxC;AAEA,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,aAAa,KAAK;AAChD,eAAS;AAAA,QACN,MAAM,MAAM,EAAE;AAAA,UACb,gBAAgB,KAAK,OAAO,mBAAmB,GAAG,CAAC,IAAI,SAAS,CAAC;AAAA,QACnE,KAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI,QAAQ,KAAK,OAAO,aAAa;AACnC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK;AACX;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/middlewares.mts"],"sourcesContent":["import { Middleware, Request, Response } from \"@devbro/neko-router\";\nimport { logger, db, cache } from \"./facades.mjs\";\nimport { HttpTooManyRequestsError } from \"@devbro/neko-http\";\n\nexport function cors(\n options: { allowedOrigins?: (string | RegExp)[] } = {},\n): (req: Request, res: Response, next: () => Promise<void>) => Promise<void> {\n return async (\n req: Request,\n res: Response,\n next: () => Promise<void>,\n ): Promise<void> => {\n const allowedOrigins = options.allowedOrigins || [\"*\"];\n const origin = req.headers.origin || \"*\";\n\n for (const allowedOrigin of allowedOrigins) {\n if (typeof allowedOrigin === \"string\" && allowedOrigin === origin) {\n res.setHeader(\"Access-Control-Allow-Origin\", allowedOrigin);\n break;\n } else if (\n allowedOrigin instanceof RegExp &&\n allowedOrigin.test(origin)\n ) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n break;\n } else if (allowedOrigin === \"*\") {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n break;\n }\n }\n\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n await next();\n };\n}\n\nexport async function dbTransaction(\n req: Request,\n res: Response,\n next: () => Promise<void>,\n): Promise<void> {\n try {\n await db().beginTransaction();\n await next();\n await db().commit();\n } catch {\n await db().rollback();\n }\n}\n\nexport type RateLimiterMiddlewareParams = {\n generateIdentifier: (req: Request) => string;\n maxRequests: number;\n windowTimeSize: number;\n windowCount: number;\n};\n\n/**\n * Rate limiter middleware using a sliding window algorithm.\n *\n * This middleware tracks request counts across multiple time windows to prevent abuse.\n * It uses atomic cache increments to ensure accurate counting in concurrent environments.\n *\n * @example\n * ```typescript\n * // Basic usage with defaults (200 requests per 2 minutes)\n * authnedRouter.addGlobalMiddleware(RateLimiterMiddleware);\n *\n * // Custom configuration\n * authnedRouter.addGlobalMiddleware(RateLimiterMiddleware.getInstance({\n * generateIdentifier: (req) => req.headers['x-api-key'] || req.socket.remoteAddress,\n * maxRequests: 200, // Allow 200 requests\n * windowTimeSize: 30, // in 30 second windows\n * windowCount: 4 // looking back 4 windows (total: 120 seconds)\n * }));\n * ```\n *\n * @param params.generateIdentifier - Function to generate unique identifier for rate limiting (default: IP address)\n * @param params.maxRequests - Maximum number of requests allowed across all windows (default: 200)\n * @param params.windowTimeSize - Size of each time window in seconds (default: 30)\n * @param params.windowCount - Number of previous windows to check (default: 4)\n *\n * The total time period checked is `windowTimeSize * windowCount` seconds.\n * Default configuration: 200 requests per 120 seconds (30s × 4 windows).\n *\n * @throws {HttpTooManyRequestsError} When rate limit is exceeded\n */\nexport class RateLimiterMiddleware extends Middleware {\n static singletonInstance: Middleware | undefined = undefined;\n static getInstance(\n params: Partial<RateLimiterMiddlewareParams> = {},\n ): Middleware {\n if (RateLimiterMiddleware.singletonInstance) {\n return RateLimiterMiddleware.singletonInstance;\n }\n let default_params: RateLimiterMiddlewareParams = {\n generateIdentifier: (req: Request) =>\n req.socket.remoteAddress || \"unknown\",\n maxRequests: 200,\n windowTimeSize: 30,\n windowCount: 4,\n };\n const merged_params = { ...default_params, ...params };\n return (RateLimiterMiddleware.singletonInstance = new RateLimiterMiddleware(\n merged_params,\n ));\n }\n\n constructor(private params: RateLimiterMiddlewareParams) {\n super();\n }\n\n async call(\n req: Request,\n res: Response,\n next: () => Promise<void>,\n ): Promise<void> {\n let window = parseInt(\n (Date.now() / 1000 / this.params.windowTimeSize).toString(),\n );\n let key = `rate_limiter:${this.params.generateIdentifier(req)}:${window}`;\n let count: number = (await cache().get(key)) || 0;\n if (!count) {\n await cache().put(\n key,\n 1,\n this.params.windowTimeSize * (this.params.windowCount + 1),\n );\n count = 1;\n } else {\n count = await cache().increment(key, 1);\n }\n\n for (let i = 1; i < this.params.windowCount; i++) {\n count += parseInt(\n (await cache().get(\n `rate_limiter:${this.params.generateIdentifier(req)}:${window - i}`,\n )) || \"0\",\n );\n }\n\n if (count > this.params.maxRequests) {\n throw new HttpTooManyRequestsError(\n \"Too many requests. Please try again later.\",\n );\n }\n\n await next();\n return;\n }\n}\n"],"mappings":";;AAAA,SAAS,kBAAqC;AAC9C,SAAiB,IAAI,aAAa;AAClC,SAAS,gCAAgC;AAElC,SAAS,KACd,UAAoD,CAAC,GACsB;AAC3E,SAAO,OACL,KACA,KACA,SACkB;AAClB,UAAM,iBAAiB,QAAQ,kBAAkB,CAAC,GAAG;AACrD,UAAM,SAAS,IAAI,QAAQ,UAAU;AAErC,eAAW,iBAAiB,gBAAgB;AAC1C,UAAI,OAAO,kBAAkB,YAAY,kBAAkB,QAAQ;AACjE,YAAI,UAAU,+BAA+B,aAAa;AAC1D;AAAA,MACF,WACE,yBAAyB,UACzB,cAAc,KAAK,MAAM,GACzB;AACA,YAAI,UAAU,+BAA+B,MAAM;AACnD;AAAA,MACF,WAAW,kBAAkB,KAAK;AAChC,YAAI,UAAU,+BAA+B,GAAG;AAChD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,gCAAgC,GAAG;AACjD,UAAM,KAAK;AAAA,EACb;AACF;AA9BgB;AAgChB,eAAsB,cACpB,KACA,KACA,MACe;AACf,MAAI;AACF,UAAM,GAAG,EAAE,iBAAiB;AAC5B,UAAM,KAAK;AACX,UAAM,GAAG,EAAE,OAAO;AAAA,EACpB,QAAQ;AACN,UAAM,GAAG,EAAE,SAAS;AAAA,EACtB;AACF;AAZsB;AAmDf,MAAM,8BAA8B,WAAW;AAAA,EAqBpD,YAAoB,QAAqC;AACvD,UAAM;AADY;AAAA,EAEpB;AAAA,EA9GF,OAuFsD;AAAA;AAAA;AAAA,EACpD,OAAO,oBAA4C;AAAA,EACnD,OAAO,YACL,SAA+C,CAAC,GACpC;AACZ,QAAI,sBAAsB,mBAAmB;AAC3C,aAAO,sBAAsB;AAAA,IAC/B;AACA,QAAI,iBAA8C;AAAA,MAChD,oBAAoB,wBAAC,QACnB,IAAI,OAAO,iBAAiB,WADV;AAAA,MAEpB,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,aAAa;AAAA,IACf;AACA,UAAM,gBAAgB,EAAE,GAAG,gBAAgB,GAAG,OAAO;AACrD,WAAQ,sBAAsB,oBAAoB,IAAI;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA,EAMA,MAAM,KACJ,KACA,KACA,MACe;AACf,QAAI,SAAS;AAAA,OACV,KAAK,IAAI,IAAI,MAAO,KAAK,OAAO,gBAAgB,SAAS;AAAA,IAC5D;AACA,QAAI,MAAM,gBAAgB,KAAK,OAAO,mBAAmB,GAAG,CAAC,IAAI,MAAM;AACvE,QAAI,QAAiB,MAAM,MAAM,EAAE,IAAI,GAAG,KAAM;AAChD,QAAI,CAAC,OAAO;AACV,YAAM,MAAM,EAAE;AAAA,QACZ;AAAA,QACA;AAAA,QACA,KAAK,OAAO,kBAAkB,KAAK,OAAO,cAAc;AAAA,MAC1D;AACA,cAAQ;AAAA,IACV,OAAO;AACL,cAAQ,MAAM,MAAM,EAAE,UAAU,KAAK,CAAC;AAAA,IACxC;AAEA,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,aAAa,KAAK;AAChD,eAAS;AAAA,QACN,MAAM,MAAM,EAAE;AAAA,UACb,gBAAgB,KAAK,OAAO,mBAAmB,GAAG,CAAC,IAAI,SAAS,CAAC;AAAA,QACnE,KAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI,QAAQ,KAAK,OAAO,aAAa;AACnC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK;AACX;AAAA,EACF;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devbro/pashmak",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "testing application for the entire repo",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",