route-scoped-cors 0.1.0

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.
@@ -0,0 +1,30 @@
1
+ import type { RouteScopedCorsOptions, ExpressMiddlewareFn } from '../types';
2
+ /**
3
+ * Returns an Express middleware function that overrides
4
+ * `Access-Control-Allow-Methods` with the exact set of HTTP methods
5
+ * registered for the matched route.
6
+ *
7
+ * The route map is built lazily on the first request so all routes are
8
+ * guaranteed to be registered in the Express router before inspection.
9
+ *
10
+ * @example
11
+ * // Express
12
+ * app.use(routeScopedCors());
13
+ *
14
+ * // Express with options
15
+ * app.use(routeScopedCors({ fallbackMethods: ['GET', 'POST', 'OPTIONS'] }));
16
+ */
17
+ export declare function routeScopedCors(options?: RouteScopedCorsOptions): ExpressMiddlewareFn;
18
+ /**
19
+ * Installs `routeScopedCors` onto an Express (or Connect-compatible) app.
20
+ * Equivalent to calling `app.use(routeScopedCors(options))`.
21
+ *
22
+ * @example
23
+ * import express from 'express';
24
+ * import { install } from 'route-scoped-cors';
25
+ *
26
+ * const app = express();
27
+ * install(app);
28
+ */
29
+ export declare function install(app: any, options?: RouteScopedCorsOptions): void;
30
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/core/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAM5E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,mBAAmB,CAmCzF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAOxE"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.routeScopedCors = routeScopedCors;
4
+ exports.install = install;
5
+ const route_map_1 = require("./route-map");
6
+ const path_matcher_1 = require("./path-matcher");
7
+ const DEFAULT_FALLBACK_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
8
+ /**
9
+ * Returns an Express middleware function that overrides
10
+ * `Access-Control-Allow-Methods` with the exact set of HTTP methods
11
+ * registered for the matched route.
12
+ *
13
+ * The route map is built lazily on the first request so all routes are
14
+ * guaranteed to be registered in the Express router before inspection.
15
+ *
16
+ * @example
17
+ * // Express
18
+ * app.use(routeScopedCors());
19
+ *
20
+ * // Express with options
21
+ * app.use(routeScopedCors({ fallbackMethods: ['GET', 'POST', 'OPTIONS'] }));
22
+ */
23
+ function routeScopedCors(options = {}) {
24
+ var _a;
25
+ const fallbackMethods = (_a = options.fallbackMethods) !== null && _a !== void 0 ? _a : DEFAULT_FALLBACK_METHODS;
26
+ let routeMap = null;
27
+ return function routeScopedCorsMiddleware(req, res, next) {
28
+ if (!routeMap) {
29
+ routeMap = (0, route_map_1.buildRouteMap)(req.app);
30
+ }
31
+ const allowedMethods = (0, path_matcher_1.matchAllowedMethods)(routeMap, req.path, fallbackMethods);
32
+ res.setHeader('Access-Control-Allow-Methods', allowedMethods.join(', '));
33
+ if (req.method === 'OPTIONS') {
34
+ res.status(204).end();
35
+ return;
36
+ }
37
+ if (!allowedMethods.includes(req.method.toUpperCase())) {
38
+ const actualMethods = allowedMethods.filter((m) => m !== 'OPTIONS');
39
+ res.setHeader('Allow', actualMethods.join(', '));
40
+ res.status(405).json({
41
+ statusCode: 405,
42
+ message: 'Method Not Allowed',
43
+ error: `${req.method} is not supported on this resource. Allowed: ${actualMethods.join(', ')}`,
44
+ });
45
+ return;
46
+ }
47
+ next();
48
+ };
49
+ }
50
+ /**
51
+ * Installs `routeScopedCors` onto an Express (or Connect-compatible) app.
52
+ * Equivalent to calling `app.use(routeScopedCors(options))`.
53
+ *
54
+ * @example
55
+ * import express from 'express';
56
+ * import { install } from 'route-scoped-cors';
57
+ *
58
+ * const app = express();
59
+ * install(app);
60
+ */
61
+ function install(app, options) {
62
+ if (typeof (app === null || app === void 0 ? void 0 : app.use) !== 'function') {
63
+ throw new Error('[route-scoped-cors] install() requires an Express or Connect-compatible application instance.');
64
+ }
65
+ app.use(routeScopedCors(options));
66
+ }
67
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/core/middleware.ts"],"names":[],"mappings":";;AAsBA,0CAmCC;AAaD,0BAOC;AA3ED,2CAA4C;AAC5C,iDAAqD;AAErD,MAAM,wBAAwB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEtF;;;;;;;;;;;;;;GAcG;AACH,SAAgB,eAAe,CAAC,UAAkC,EAAE;;IAClE,MAAM,eAAe,GAAG,MAAA,OAAO,CAAC,eAAe,mCAAI,wBAAwB,CAAC;IAC5E,IAAI,QAAQ,GAAoC,IAAI,CAAC;IAErD,OAAO,SAAS,yBAAyB,CACvC,GAA4B,EAC5B,GAAa,EACb,IAAkB;QAElB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,QAAQ,GAAG,IAAA,yBAAa,EAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,cAAc,GAAG,IAAA,kCAAmB,EAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAEhF,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAEzE,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACvD,MAAM,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;YACpE,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACjD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,UAAU,EAAE,GAAG;gBACf,OAAO,EAAE,oBAAoB;gBAC7B,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,gDAAgD,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aAC/F,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,OAAO,CAAC,GAAQ,EAAE,OAAgC;IAChE,IAAI,OAAO,CAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,GAAG,CAAA,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,+FAA+F,CAChG,CAAC;IACJ,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Finds the allowed methods for `requestPath` by matching against the
3
+ * stored route map. Express path params (`:id`) are treated as wildcards.
4
+ * Returns `fallbackMethods` when no pattern matches.
5
+ */
6
+ export declare function matchAllowedMethods(routeMap: Map<string, Set<string>>, requestPath: string, fallbackMethods: string[]): string[];
7
+ //# sourceMappingURL=path-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-matcher.d.ts","sourceRoot":"","sources":["../../src/core/path-matcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,EAClC,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EAAE,GACxB,MAAM,EAAE,CAkBV"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchAllowedMethods = matchAllowedMethods;
4
+ /**
5
+ * Finds the allowed methods for `requestPath` by matching against the
6
+ * stored route map. Express path params (`:id`) are treated as wildcards.
7
+ * Returns `fallbackMethods` when no pattern matches.
8
+ */
9
+ function matchAllowedMethods(routeMap, requestPath, fallbackMethods) {
10
+ for (const [pattern, methods] of routeMap.entries()) {
11
+ const regexStr = '^' +
12
+ pattern
13
+ .replace(/\//g, '\\/')
14
+ .replace(/:[^/]+/g, '[^/]+') // :param → any non-slash segment
15
+ .replace(/\*/g, '.*') + // wildcard support
16
+ '$';
17
+ try {
18
+ if (new RegExp(regexStr).test(requestPath)) {
19
+ return [...methods];
20
+ }
21
+ }
22
+ catch {
23
+ // Malformed pattern — skip rather than crash
24
+ }
25
+ }
26
+ return fallbackMethods;
27
+ }
28
+ //# sourceMappingURL=path-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-matcher.js","sourceRoot":"","sources":["../../src/core/path-matcher.ts"],"names":[],"mappings":";;AAKA,kDAsBC;AA3BD;;;;GAIG;AACH,SAAgB,mBAAmB,CACjC,QAAkC,EAClC,WAAmB,EACnB,eAAyB;IAEzB,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACpD,MAAM,QAAQ,GACZ,GAAG;YACH,OAAO;iBACJ,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;iBACrB,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,iCAAiC;iBAC7D,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAS,mBAAmB;YACnD,GAAG,CAAC;QACN,IAAI,CAAC;YACH,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC3C,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Walks the Express _router.stack recursively to build a map of
3
+ * route path-pattern → Set of uppercase HTTP methods.
4
+ *
5
+ * Built lazily on the first request so that all routes are guaranteed
6
+ * to be registered before inspection.
7
+ */
8
+ export declare function buildRouteMap(expressApp: any): Map<string, Set<string>>;
9
+ //# sourceMappingURL=route-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-map.d.ts","sourceRoot":"","sources":["../../src/core/route-map.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAmCvE"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildRouteMap = buildRouteMap;
4
+ /**
5
+ * Walks the Express _router.stack recursively to build a map of
6
+ * route path-pattern → Set of uppercase HTTP methods.
7
+ *
8
+ * Built lazily on the first request so that all routes are guaranteed
9
+ * to be registered before inspection.
10
+ */
11
+ function buildRouteMap(expressApp) {
12
+ var _a, _b;
13
+ const map = new Map();
14
+ const walkLayer = (layer, prefix = '') => {
15
+ var _a, _b, _c;
16
+ if (layer.route) {
17
+ const path = (prefix + layer.route.path).replace(/\/+/g, '/');
18
+ const methods = new Set(Object.keys(layer.route.methods)
19
+ .filter((m) => m !== '_all' && layer.route.methods[m])
20
+ .map((m) => m.toUpperCase()));
21
+ // OPTIONS is always required for CORS preflight
22
+ methods.add('OPTIONS');
23
+ const existing = map.get(path);
24
+ if (existing) {
25
+ methods.forEach((m) => existing.add(m));
26
+ }
27
+ else {
28
+ map.set(path, methods);
29
+ }
30
+ }
31
+ else if ((_a = layer.handle) === null || _a === void 0 ? void 0 : _a.stack) {
32
+ // Nested router: extract the path segment from the compiled regexp.
33
+ // Express compiles router paths into regexps like: ^\/segment\/?(?=\/|$)
34
+ const src = (_c = (_b = layer.regexp) === null || _b === void 0 ? void 0 : _b.source) !== null && _c !== void 0 ? _c : '';
35
+ const match = src.match(/^\^\\\/([^\\?$/]*)/);
36
+ const segment = match ? `/${match[1].replace(/\\\//g, '/')}` : '';
37
+ layer.handle.stack.forEach((child) => walkLayer(child, prefix + segment));
38
+ }
39
+ };
40
+ const stack = (_b = (_a = expressApp === null || expressApp === void 0 ? void 0 : expressApp._router) === null || _a === void 0 ? void 0 : _a.stack) !== null && _b !== void 0 ? _b : [];
41
+ stack.forEach((layer) => walkLayer(layer));
42
+ return map;
43
+ }
44
+ //# sourceMappingURL=route-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-map.js","sourceRoot":"","sources":["../../src/core/route-map.ts"],"names":[],"mappings":";;AAOA,sCAmCC;AA1CD;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,UAAe;;IAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE3C,MAAM,SAAS,GAAG,CAAC,KAAU,EAAE,MAAM,GAAG,EAAE,EAAQ,EAAE;;QAClD,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAI,KAAK,CAAC,KAAK,CAAC,IAAe,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,IAAI,GAAG,CACpB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAc;iBAC3C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;iBACrD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC;YACF,gDAAgD;YAChD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAEvB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;aAAM,IAAI,MAAA,KAAK,CAAC,MAAM,0CAAE,KAAK,EAAE,CAAC;YAC/B,oEAAoE;YACpE,yEAAyE;YACzE,MAAM,GAAG,GAAW,MAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,MAAM,mCAAI,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,MAAM,CAAC,KAAe,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9C,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CACnC,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,KAAK,GAAU,MAAA,MAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,OAAO,0CAAE,KAAK,mCAAI,EAAE,CAAC;IACtD,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { routeScopedCors, install } from './core/middleware';
2
+ export type { RouteScopedCorsOptions, ExpressMiddlewareFn } from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.install = exports.routeScopedCors = void 0;
4
+ var middleware_1 = require("./core/middleware");
5
+ Object.defineProperty(exports, "routeScopedCors", { enumerable: true, get: function () { return middleware_1.routeScopedCors; } });
6
+ Object.defineProperty(exports, "install", { enumerable: true, get: function () { return middleware_1.install; } });
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,gDAA6D;AAApD,6GAAA,eAAe,OAAA;AAAE,qGAAA,OAAO,OAAA"}
@@ -0,0 +1,5 @@
1
+ export { RouteScopedCorsModule } from './module';
2
+ export { RouteScopedCorsMiddleware } from './middleware';
3
+ export { ROUTE_SCOPED_CORS_OPTIONS } from './tokens';
4
+ export type { RouteScopedCorsOptions } from '../types';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ROUTE_SCOPED_CORS_OPTIONS = exports.RouteScopedCorsMiddleware = exports.RouteScopedCorsModule = void 0;
4
+ var module_1 = require("./module");
5
+ Object.defineProperty(exports, "RouteScopedCorsModule", { enumerable: true, get: function () { return module_1.RouteScopedCorsModule; } });
6
+ var middleware_1 = require("./middleware");
7
+ Object.defineProperty(exports, "RouteScopedCorsMiddleware", { enumerable: true, get: function () { return middleware_1.RouteScopedCorsMiddleware; } });
8
+ var tokens_1 = require("./tokens");
9
+ Object.defineProperty(exports, "ROUTE_SCOPED_CORS_OPTIONS", { enumerable: true, get: function () { return tokens_1.ROUTE_SCOPED_CORS_OPTIONS; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":";;;AAAA,mCAAiD;AAAxC,+GAAA,qBAAqB,OAAA;AAC9B,2CAAyD;AAAhD,uHAAA,yBAAyB,OAAA;AAClC,mCAAqD;AAA5C,mHAAA,yBAAyB,OAAA"}
@@ -0,0 +1,9 @@
1
+ import { NestMiddleware } from '@nestjs/common';
2
+ import type { Request, Response, NextFunction } from 'express';
3
+ import type { RouteScopedCorsOptions } from '../types';
4
+ export declare class RouteScopedCorsMiddleware implements NestMiddleware {
5
+ private readonly handler;
6
+ constructor(options?: RouteScopedCorsOptions);
7
+ use(req: Request, res: Response, next: NextFunction): void;
8
+ }
9
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/nestjs/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,cAAc,EAAY,MAAM,gBAAgB,CAAC;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAuB,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAG5E,qBACa,yBAA0B,YAAW,cAAc;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;gBAGG,OAAO,CAAC,EAAE,sBAAsB;IAKjF,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;CAG3D"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RouteScopedCorsMiddleware = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const middleware_1 = require("../core/middleware");
18
+ const tokens_1 = require("./tokens");
19
+ let RouteScopedCorsMiddleware = class RouteScopedCorsMiddleware {
20
+ constructor(options) {
21
+ this.handler = (0, middleware_1.routeScopedCors)(options !== null && options !== void 0 ? options : {});
22
+ }
23
+ use(req, res, next) {
24
+ this.handler(req, res, next);
25
+ }
26
+ };
27
+ exports.RouteScopedCorsMiddleware = RouteScopedCorsMiddleware;
28
+ exports.RouteScopedCorsMiddleware = RouteScopedCorsMiddleware = __decorate([
29
+ (0, common_1.Injectable)(),
30
+ __param(0, (0, common_1.Optional)()),
31
+ __param(0, (0, common_1.Inject)(tokens_1.ROUTE_SCOPED_CORS_OPTIONS)),
32
+ __metadata("design:paramtypes", [Object])
33
+ ], RouteScopedCorsMiddleware);
34
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/nestjs/middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA8E;AAE9E,mDAAqD;AAErD,qCAAqD;AAG9C,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IAGpC,YACiD,OAAgC;QAE/E,IAAI,CAAC,OAAO,GAAG,IAAA,4BAAe,EAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;QACjD,IAAI,CAAC,OAAO,CAAC,GAAU,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;CACF,CAAA;AAZY,8DAAyB;oCAAzB,yBAAyB;IADrC,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,iBAAQ,GAAE,CAAA;IAAE,WAAA,IAAA,eAAM,EAAC,kCAAyB,CAAC,CAAA;;GAJrC,yBAAyB,CAYrC"}
@@ -0,0 +1,23 @@
1
+ import { DynamicModule, MiddlewareConsumer, NestModule } from '@nestjs/common';
2
+ import type { RouteScopedCorsOptions } from '../types';
3
+ /**
4
+ * Drop-in NestJS module that automatically applies per-route CORS method
5
+ * scoping to every route in the application.
6
+ *
7
+ * @example
8
+ * // app.module.ts
9
+ * import { RouteScopedCorsModule } from 'route-scoped-cors/nestjs';
10
+ *
11
+ * @Module({
12
+ * imports: [RouteScopedCorsModule.forRoot()],
13
+ * })
14
+ * export class AppModule {}
15
+ *
16
+ * // With options:
17
+ * RouteScopedCorsModule.forRoot({ fallbackMethods: ['GET', 'POST', 'OPTIONS'] })
18
+ */
19
+ export declare class RouteScopedCorsModule implements NestModule {
20
+ static forRoot(options?: RouteScopedCorsOptions): DynamicModule;
21
+ configure(consumer: MiddlewareConsumer): void;
22
+ }
23
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/nestjs/module.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,kBAAkB,EAElB,UAAU,EAEX,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAIvD;;;;;;;;;;;;;;;GAeG;AACH,qBACa,qBAAsB,YAAW,UAAU;IACtD,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,sBAA2B,GAAG,aAAa;IAYnE,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;CAK9C"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var RouteScopedCorsModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.RouteScopedCorsModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const middleware_1 = require("./middleware");
13
+ const tokens_1 = require("./tokens");
14
+ /**
15
+ * Drop-in NestJS module that automatically applies per-route CORS method
16
+ * scoping to every route in the application.
17
+ *
18
+ * @example
19
+ * // app.module.ts
20
+ * import { RouteScopedCorsModule } from 'route-scoped-cors/nestjs';
21
+ *
22
+ * @Module({
23
+ * imports: [RouteScopedCorsModule.forRoot()],
24
+ * })
25
+ * export class AppModule {}
26
+ *
27
+ * // With options:
28
+ * RouteScopedCorsModule.forRoot({ fallbackMethods: ['GET', 'POST', 'OPTIONS'] })
29
+ */
30
+ let RouteScopedCorsModule = RouteScopedCorsModule_1 = class RouteScopedCorsModule {
31
+ static forRoot(options = {}) {
32
+ return {
33
+ module: RouteScopedCorsModule_1,
34
+ global: true,
35
+ providers: [
36
+ { provide: tokens_1.ROUTE_SCOPED_CORS_OPTIONS, useValue: options },
37
+ middleware_1.RouteScopedCorsMiddleware,
38
+ ],
39
+ exports: [middleware_1.RouteScopedCorsMiddleware],
40
+ };
41
+ }
42
+ configure(consumer) {
43
+ consumer
44
+ .apply(middleware_1.RouteScopedCorsMiddleware)
45
+ .forRoutes({ path: '*', method: common_1.RequestMethod.ALL });
46
+ }
47
+ };
48
+ exports.RouteScopedCorsModule = RouteScopedCorsModule;
49
+ exports.RouteScopedCorsModule = RouteScopedCorsModule = RouteScopedCorsModule_1 = __decorate([
50
+ (0, common_1.Module)({})
51
+ ], RouteScopedCorsModule);
52
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/nestjs/module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAMwB;AAExB,6CAAyD;AACzD,qCAAqD;AAErD;;;;;;;;;;;;;;;GAeG;AAEI,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,OAAO,CAAC,UAAkC,EAAE;QACjD,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,kCAAyB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBACzD,sCAAyB;aAC1B;YACD,OAAO,EAAE,CAAC,sCAAyB,CAAC;SACrC,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,QAA4B;QACpC,QAAQ;aACL,KAAK,CAAC,sCAAyB,CAAC;aAChC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAE,CAAC,CAAC;IACzD,CAAC;CACF,CAAA;AAlBY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,qBAAqB,CAkBjC"}
@@ -0,0 +1,2 @@
1
+ export declare const ROUTE_SCOPED_CORS_OPTIONS = "ROUTE_SCOPED_CORS_OPTIONS";
2
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/nestjs/tokens.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,8BAA8B,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ROUTE_SCOPED_CORS_OPTIONS = void 0;
4
+ exports.ROUTE_SCOPED_CORS_OPTIONS = 'ROUTE_SCOPED_CORS_OPTIONS';
5
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/nestjs/tokens.ts"],"names":[],"mappings":";;;AAAa,QAAA,yBAAyB,GAAG,2BAA2B,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Request, Response, NextFunction } from 'express';
2
+ export interface RouteScopedCorsOptions {
3
+ /**
4
+ * Methods returned for unmatched paths and preflight requests on unknown
5
+ * routes. Defaults to the standard set: GET POST PUT PATCH DELETE OPTIONS.
6
+ */
7
+ fallbackMethods?: string[];
8
+ }
9
+ export type ExpressMiddlewareFn = (req: Request, res: Response, next: NextFunction) => void;
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,CAChC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,KACf,IAAI,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "route-scoped-cors",
3
+ "version": "0.1.0",
4
+ "description": "Per-route CORS method scoping middleware for Express and NestJS — prevents HTTP verb tampering by advertising only the methods each route actually handles.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "exports": {
11
+ ".": {
12
+ "require": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ },
15
+ "./nestjs": {
16
+ "require": "./dist/nestjs/index.js",
17
+ "types": "./dist/nestjs/index.d.ts"
18
+ }
19
+ },
20
+ "typesVersions": {
21
+ "*": {
22
+ "nestjs": [
23
+ "dist/nestjs/index.d.ts"
24
+ ]
25
+ }
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.build.json",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "keywords": [
32
+ "cors",
33
+ "express",
34
+ "nestjs",
35
+ "middleware",
36
+ "route-scoped",
37
+ "http-methods",
38
+ "verb-tampering"
39
+ ],
40
+ "license": "MIT",
41
+ "peerDependencies": {
42
+ "@nestjs/common": ">=9.0.0",
43
+ "@nestjs/core": ">=9.0.0",
44
+ "express": ">=4.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@nestjs/common": {
48
+ "optional": true
49
+ },
50
+ "@nestjs/core": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@nestjs/common": "^10.0.0",
56
+ "@nestjs/core": "^10.0.0",
57
+ "@types/express": "^4.17.21",
58
+ "@types/node": "^20.0.0",
59
+ "reflect-metadata": "^0.1.13",
60
+ "typescript": "^5.0.0"
61
+ }
62
+ }