@plasius/api 1.0.5 → 1.0.7

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +5 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +15 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/middleware/index.d.ts +10 -0
  8. package/dist/middleware/index.d.ts.map +1 -0
  9. package/dist/middleware/index.js +26 -0
  10. package/dist/middleware/index.js.map +1 -0
  11. package/dist/middleware/withCSRF.d.ts +3 -0
  12. package/dist/middleware/withCSRF.d.ts.map +1 -0
  13. package/dist/middleware/withCSRF.js +52 -0
  14. package/dist/middleware/withCSRF.js.map +1 -0
  15. package/dist/middleware/withCors.d.ts +3 -0
  16. package/dist/middleware/withCors.d.ts.map +1 -0
  17. package/dist/middleware/withCors.js +41 -0
  18. package/dist/middleware/withCors.js.map +1 -0
  19. package/dist/middleware/withDefaultMiddleware.d.ts +3 -0
  20. package/dist/middleware/withDefaultMiddleware.d.ts.map +1 -0
  21. package/dist/middleware/withDefaultMiddleware.js +42 -0
  22. package/dist/middleware/withDefaultMiddleware.js.map +1 -0
  23. package/dist/middleware/withLogging.d.ts +3 -0
  24. package/dist/middleware/withLogging.d.ts.map +1 -0
  25. package/dist/middleware/withLogging.js +19 -0
  26. package/dist/middleware/withLogging.js.map +1 -0
  27. package/dist/middleware/withMCPHeader.d.ts +3 -0
  28. package/dist/middleware/withMCPHeader.d.ts.map +1 -0
  29. package/dist/middleware/withMCPHeader.js +17 -0
  30. package/dist/middleware/withMCPHeader.js.map +1 -0
  31. package/dist/middleware/withMiddleware.d.ts +5 -0
  32. package/dist/middleware/withMiddleware.d.ts.map +1 -0
  33. package/dist/middleware/withMiddleware.js +45 -0
  34. package/dist/middleware/withMiddleware.js.map +1 -0
  35. package/dist/middleware/withRateLimiting.d.ts +12 -0
  36. package/dist/middleware/withRateLimiting.d.ts.map +1 -0
  37. package/dist/middleware/withRateLimiting.js +131 -0
  38. package/dist/middleware/withRateLimiting.js.map +1 -0
  39. package/dist/middleware/withSecurity.d.ts +3 -0
  40. package/dist/middleware/withSecurity.d.ts.map +1 -0
  41. package/dist/middleware/withSecurity.js +25 -0
  42. package/dist/middleware/withSecurity.js.map +1 -0
  43. package/dist/middleware/withSession.d.ts +3 -0
  44. package/dist/middleware/withSession.d.ts.map +1 -0
  45. package/dist/middleware/withSession.js +16 -0
  46. package/dist/middleware/withSession.js.map +1 -0
  47. package/dist/utils/context.d.ts +6 -0
  48. package/dist/utils/context.d.ts.map +1 -0
  49. package/dist/utils/context.js +9 -0
  50. package/dist/utils/context.js.map +1 -0
  51. package/dist/utils/extract.ip.d.ts +3 -0
  52. package/dist/utils/extract.ip.d.ts.map +1 -0
  53. package/dist/utils/extract.ip.js +14 -0
  54. package/dist/utils/extract.ip.js.map +1 -0
  55. package/dist/utils/http.errors.d.ts +13 -0
  56. package/dist/utils/http.errors.d.ts.map +1 -0
  57. package/dist/utils/http.errors.js +48 -0
  58. package/dist/utils/http.errors.js.map +1 -0
  59. package/dist/utils/index.d.ts +10 -0
  60. package/dist/utils/index.d.ts.map +1 -0
  61. package/dist/utils/index.js +26 -0
  62. package/dist/utils/index.js.map +1 -0
  63. package/dist/utils/names.d.ts +6 -0
  64. package/dist/utils/names.d.ts.map +1 -0
  65. package/dist/utils/names.js +21 -0
  66. package/dist/utils/names.js.map +1 -0
  67. package/dist/utils/sanitize.table.key.d.ts +2 -0
  68. package/dist/utils/sanitize.table.key.d.ts.map +1 -0
  69. package/dist/utils/sanitize.table.key.js +17 -0
  70. package/dist/utils/sanitize.table.key.js.map +1 -0
  71. package/package.json +9 -1
package/CHANGELOG.md CHANGED
@@ -18,6 +18,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
18
18
  - **Security**
19
19
  - (placeholder)
20
20
 
21
+ ## [1.0.6] - 2026-03-01
22
+
23
+ - **Added**
24
+ - Public middleware export surface via package root and `@plasius/api/middleware` subpath.
25
+ - Middleware export-surface tests for root and middleware barrel coverage.
26
+
27
+ - **Changed**
28
+ - Root package now re-exports middleware primitives (`withMiddleware`, `withCors`, `withRateLimiting`, etc.).
29
+ - `withRateLimiting` now lazily initializes Redis clients to avoid import-time side effects.
30
+
31
+ - **Fixed**
32
+ - Removed middleware import-time Redis connection attempts that could emit runtime errors in consumer environments.
33
+
34
+ - **Security**
35
+ - (placeholder)
36
+
21
37
  ## [1.0.5] - 2026-03-01
22
38
 
23
39
  - **Added**
@@ -117,10 +133,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
117
133
 
118
134
  - Initial public release scaffold for `@plasius/api`.
119
135
 
120
- [Unreleased]: https://github.com/Plasius-LTD/api/compare/v1.0.5...HEAD
136
+ [Unreleased]: https://github.com/Plasius-LTD/api/compare/v1.0.6...HEAD
121
137
  [1.0.0]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.0
122
138
  [1.0.1]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.1
123
139
  [1.0.2]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.2
124
140
  [1.0.3]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.3
125
141
  [1.0.4]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.4
126
142
  [1.0.5]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.5
143
+ [1.0.6]: https://github.com/Plasius-LTD/api/releases/tag/v1.0.6
package/README.md CHANGED
@@ -51,6 +51,7 @@ npm install @plasius/api
51
51
  ## Entrypoints
52
52
 
53
53
  - Main module: `@plasius/api`
54
+ - Middleware module: `@plasius/api/middleware`
54
55
 
55
56
  ### Example
56
57
 
@@ -62,6 +63,10 @@ import {
62
63
  } from "@plasius/api";
63
64
  ```
64
65
 
66
+ ```ts
67
+ import { withCors, withRateLimiting, withMiddleware } from "@plasius/api/middleware";
68
+ ```
69
+
65
70
  ## Local development
66
71
 
67
72
  ```bash
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { applyBaselineSecurityHeaders, isHttpsRequest, isInsecureLocalRequest, shouldEnforceHttps, } from "./middleware/transportSecurity.js";
2
+ export * from "./middleware/index.js";
2
3
  export { decodeOAuthReturnToState, parseEncodedState, verifyState, } from "./utils/state.js";
3
4
  export { generatePkceCodeChallenge, generatePkceCodeVerifier, generatePkceCookieId, getPkceCookieName, isValidPkceCodeVerifier, isValidPkceCookieId, } from "./utils/oauth-pkce.js";
4
5
  export { DEFAULT_SESSION_COOKIE_NAME, createSessionCookie, ensureSession, getSessionIdFromRequest, } from "./utils/session.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,cAAc,EACd,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,aAAa,EACb,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,cAAc,EACd,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,aAAa,EACb,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,18 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.getSessionIdFromRequest = exports.ensureSession = exports.createSessionCookie = exports.DEFAULT_SESSION_COOKIE_NAME = exports.isValidPkceCookieId = exports.isValidPkceCodeVerifier = exports.getPkceCookieName = exports.generatePkceCookieId = exports.generatePkceCodeVerifier = exports.generatePkceCodeChallenge = exports.verifyState = exports.parseEncodedState = exports.decodeOAuthReturnToState = exports.shouldEnforceHttps = exports.isInsecureLocalRequest = exports.isHttpsRequest = exports.applyBaselineSecurityHeaders = void 0;
4
18
  var transportSecurity_js_1 = require("./middleware/transportSecurity.js");
@@ -6,6 +20,7 @@ Object.defineProperty(exports, "applyBaselineSecurityHeaders", { enumerable: tru
6
20
  Object.defineProperty(exports, "isHttpsRequest", { enumerable: true, get: function () { return transportSecurity_js_1.isHttpsRequest; } });
7
21
  Object.defineProperty(exports, "isInsecureLocalRequest", { enumerable: true, get: function () { return transportSecurity_js_1.isInsecureLocalRequest; } });
8
22
  Object.defineProperty(exports, "shouldEnforceHttps", { enumerable: true, get: function () { return transportSecurity_js_1.shouldEnforceHttps; } });
23
+ __exportStar(require("./middleware/index.js"), exports);
9
24
  var state_js_1 = require("./utils/state.js");
10
25
  Object.defineProperty(exports, "decodeOAuthReturnToState", { enumerable: true, get: function () { return state_js_1.decodeOAuthReturnToState; } });
11
26
  Object.defineProperty(exports, "parseEncodedState", { enumerable: true, get: function () { return state_js_1.parseEncodedState; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,0EAK2C;AAJzC,oIAAA,4BAA4B,OAAA;AAC5B,sHAAA,cAAc,OAAA;AACd,8HAAA,sBAAsB,OAAA;AACtB,0HAAA,kBAAkB,OAAA;AAGpB,6CAI0B;AAHxB,oHAAA,wBAAwB,OAAA;AACxB,6GAAA,iBAAiB,OAAA;AACjB,uGAAA,WAAW,OAAA;AAGb,uDAO+B;AAN7B,0HAAA,yBAAyB,OAAA;AACzB,yHAAA,wBAAwB,OAAA;AACxB,qHAAA,oBAAoB,OAAA;AACpB,kHAAA,iBAAiB,OAAA;AACjB,wHAAA,uBAAuB,OAAA;AACvB,oHAAA,mBAAmB,OAAA;AAGrB,iDAK4B;AAJ1B,yHAAA,2BAA2B,OAAA;AAC3B,iHAAA,mBAAmB,OAAA;AACnB,2GAAA,aAAa,OAAA;AACb,qHAAA,uBAAuB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,0EAK2C;AAJzC,oIAAA,4BAA4B,OAAA;AAC5B,sHAAA,cAAc,OAAA;AACd,8HAAA,sBAAsB,OAAA;AACtB,0HAAA,kBAAkB,OAAA;AAEpB,wDAAsC;AAEtC,6CAI0B;AAHxB,oHAAA,wBAAwB,OAAA;AACxB,6GAAA,iBAAiB,OAAA;AACjB,uGAAA,WAAW,OAAA;AAGb,uDAO+B;AAN7B,0HAAA,yBAAyB,OAAA;AACzB,yHAAA,wBAAwB,OAAA;AACxB,qHAAA,oBAAoB,OAAA;AACpB,kHAAA,iBAAiB,OAAA;AACjB,wHAAA,uBAAuB,OAAA;AACvB,oHAAA,mBAAmB,OAAA;AAGrB,iDAK4B;AAJ1B,yHAAA,2BAA2B,OAAA;AAC3B,iHAAA,mBAAmB,OAAA;AACnB,2GAAA,aAAa,OAAA;AACb,qHAAA,uBAAuB,OAAA"}
@@ -0,0 +1,10 @@
1
+ export * from "./withMiddleware.js";
2
+ export * from "./withMCPHeader.js";
3
+ export * from "./withCors.js";
4
+ export * from "./withRateLimiting.js";
5
+ export * from "./withLogging.js";
6
+ export * from "./withSession.js";
7
+ export * from "./withCSRF.js";
8
+ export * from "./withSecurity.js";
9
+ export * from "./withDefaultMiddleware.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./withMiddleware.js"), exports);
18
+ __exportStar(require("./withMCPHeader.js"), exports);
19
+ __exportStar(require("./withCors.js"), exports);
20
+ __exportStar(require("./withRateLimiting.js"), exports);
21
+ __exportStar(require("./withLogging.js"), exports);
22
+ __exportStar(require("./withSession.js"), exports);
23
+ __exportStar(require("./withCSRF.js"), exports);
24
+ __exportStar(require("./withSecurity.js"), exports);
25
+ __exportStar(require("./withDefaultMiddleware.js"), exports);
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,qDAAmC;AACnC,gDAA8B;AAC9B,wDAAsC;AACtC,mDAAiC;AACjC,mDAAiC;AACjC,gDAA8B;AAC9B,oDAAkC;AAClC,6DAA2C"}
@@ -0,0 +1,3 @@
1
+ import type { Middleware } from "./withMiddleware";
2
+ export declare const withCSRF: () => Middleware;
3
+ //# sourceMappingURL=withCSRF.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withCSRF.d.ts","sourceRoot":"","sources":["../../src/middleware/withCSRF.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,eAAO,MAAM,QAAQ,QAAO,UAoD3B,CAAC"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withCSRF = void 0;
4
+ const utils_1 = require("../utils");
5
+ const crypto_1 = require("crypto");
6
+ const index_js_1 = require("../utils/index.js");
7
+ const CSRF_HEADER_NAME = "x-csrf-token";
8
+ const CSRF_COOKIE_NAME = "csrf-token";
9
+ const withCSRF = () => {
10
+ return async (request, context) => {
11
+ const logger = context.extraInputs.get("logger");
12
+ const { headers, cookies } = (0, index_js_1.getExtraOutputs)(context);
13
+ const method = request.method?.toUpperCase();
14
+ const isReadOnly = method === "GET" || method === "HEAD" || method === "OPTIONS";
15
+ // Read token from header and cookie
16
+ const headerToken = request.headers?.get(CSRF_HEADER_NAME);
17
+ const cookieToken = (0, utils_1.getCookie)(request, CSRF_COOKIE_NAME);
18
+ // ✅ If GET and no CSRF cookie set, generate one
19
+ if (isReadOnly && !cookieToken) {
20
+ const newToken = (0, crypto_1.randomUUID)();
21
+ const newCookies = [
22
+ ...cookies,
23
+ {
24
+ name: CSRF_COOKIE_NAME,
25
+ value: newToken,
26
+ secure: true,
27
+ sameSite: "None",
28
+ path: "/",
29
+ maxAge: 10 * 60, // 10 minutes
30
+ },
31
+ ];
32
+ context.extraOutputs.set("cookies", newCookies);
33
+ logger.log("CSRF token set on GET request");
34
+ }
35
+ // Only validate CSRF token on non-readonly methods
36
+ if (!isReadOnly) {
37
+ if (!headerToken || !cookieToken || headerToken !== cookieToken) {
38
+ logger.warn("CSRF token validation failed.");
39
+ context.extraOutputs.set("http", {
40
+ status: 403,
41
+ headers,
42
+ cookies,
43
+ body: "Invalid CSRF token.",
44
+ });
45
+ return false;
46
+ }
47
+ }
48
+ return true;
49
+ };
50
+ };
51
+ exports.withCSRF = withCSRF;
52
+ //# sourceMappingURL=withCSRF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withCSRF.js","sourceRoot":"","sources":["../../src/middleware/withCSRF.ts"],"names":[],"mappings":";;;AAEA,oCAAqC;AACrC,mCAAoC;AACpC,gDAAoD;AAEpD,MAAM,gBAAgB,GAAG,cAAc,CAAC;AACxC,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE/B,MAAM,QAAQ,GAAG,GAAe,EAAE;IACvC,OAAO,KAAK,EAAE,OAAoB,EAAE,OAA0B,EAAE,EAAE;QAChE,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAI9C,CAAC;QAEF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAA,0BAAe,EAAC,OAAO,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;QAC7C,MAAM,UAAU,GACd,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,SAAS,CAAC;QAEhE,oCAAoC;QACpC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,IAAA,iBAAS,EAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAEzD,gDAAgD;QAChD,IAAI,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,IAAA,mBAAU,GAAE,CAAC;YAC9B,MAAM,UAAU,GAAG;gBACjB,GAAG,OAAO;gBACV;oBACE,IAAI,EAAE,gBAAgB;oBACtB,KAAK,EAAE,QAAQ;oBACf,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,GAAG;oBACT,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,aAAa;iBAC/B;aACF,CAAC;YAEF,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YAChD,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC9C,CAAC;QAED,mDAAmD;QACnD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;gBAChE,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;gBAC7C,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE;oBAC/B,MAAM,EAAE,GAAG;oBACX,OAAO;oBACP,OAAO;oBACP,IAAI,EAAE,qBAAqB;iBAC5B,CAAC,CAAC;gBACH,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC,CAAC;AApDW,QAAA,QAAQ,YAoDnB"}
@@ -0,0 +1,3 @@
1
+ import { Middleware } from "./withMiddleware.js";
2
+ export declare function withCors(allowedOrigins?: string[], allowedMethods?: string[], allowedHeaders?: string[]): Middleware;
3
+ //# sourceMappingURL=withCors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withCors.d.ts","sourceRoot":"","sources":["../../src/middleware/withCors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,wBAAgB,QAAQ,CACtB,cAAc,GAAE,MAAM,EAAU,EAChC,cAAc,GAAE,MAAM,EAQrB,EACD,cAAc,GAAE,MAAM,EAAsC,GAC3D,UAAU,CA0CZ"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withCors = withCors;
4
+ const index_js_1 = require("../utils/index.js");
5
+ function withCors(allowedOrigins = ["*"], allowedMethods = [
6
+ "GET",
7
+ "POST",
8
+ "PUT",
9
+ "PATCH",
10
+ "DELETE",
11
+ "OPTIONS",
12
+ "HEAD",
13
+ ], allowedHeaders = ["Content-Type", "Authorization"]) {
14
+ return async (req, context) => {
15
+ const logger = context.extraInputs.get("logger");
16
+ const { headers, cookies } = (0, index_js_1.getExtraOutputs)(context);
17
+ logger?.log(`[withCors] Executing middleware for ${req.method} ${req.url}`);
18
+ const origin = req.headers.get("origin") ?? "*";
19
+ const isOriginAllowed = allowedOrigins.includes("*") || allowedOrigins.includes(origin);
20
+ if (!isOriginAllowed) {
21
+ logger?.warn(`CORS blocked request from origin: ${origin}`);
22
+ }
23
+ headers.set("Access-Control-Allow-Origin", isOriginAllowed ? origin : "null");
24
+ headers.set("Access-Control-Allow-Methods", allowedMethods.join(", "));
25
+ headers.set("Access-Control-Allow-Headers", allowedHeaders.join(", "));
26
+ headers.set("Access-Control-Allow-Credentials", "true");
27
+ headers.set("Referrer-Policy", "origin-when-cross-origin");
28
+ context.extraOutputs.set("headers", headers);
29
+ if (req.method === "OPTIONS") {
30
+ logger?.log("CORS preflight request received. Returning early with 204.");
31
+ context.extraOutputs.set("http", {
32
+ status: 204,
33
+ headers,
34
+ cookies,
35
+ });
36
+ return false;
37
+ }
38
+ return true;
39
+ };
40
+ }
41
+ //# sourceMappingURL=withCors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withCors.js","sourceRoot":"","sources":["../../src/middleware/withCors.ts"],"names":[],"mappings":";;AAIA,4BAsDC;AAxDD,gDAAoD;AAEpD,SAAgB,QAAQ,CACtB,iBAA2B,CAAC,GAAG,CAAC,EAChC,iBAA2B;IACzB,KAAK;IACL,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,SAAS;IACT,MAAM;CACP,EACD,iBAA2B,CAAC,cAAc,EAAE,eAAe,CAAC;IAE5D,OAAO,KAAK,EAAE,GAAgB,EAAE,OAA0B,EAAE,EAAE;QAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAI9C,CAAC;QAEF,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAA,0BAAe,EAAC,OAAO,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,CAAC,uCAAuC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAE5E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;QAChD,MAAM,eAAe,GACnB,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAElE,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,EAAE,IAAI,CAAC,qCAAqC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,CAAC,GAAG,CACT,6BAA6B,EAC7B,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAClC,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,0BAA0B,CAAC,CAAC;QAE3D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE7C,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,EAAE,GAAG,CAAC,4DAA4D,CAAC,CAAC;YAC1E,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE;gBAC/B,MAAM,EAAE,GAAG;gBACX,OAAO;gBACP,OAAO;aACR,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Middleware } from "./withMiddleware.js";
2
+ export declare function withDefaultMiddleware(): Middleware[];
3
+ //# sourceMappingURL=withDefaultMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withDefaultMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/withDefaultMiddleware.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,wBAAgB,qBAAqB,IAAI,UAAU,EAAE,CAmCpD"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ // backend/src/middleware/defaultMiddleware.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.withDefaultMiddleware = withDefaultMiddleware;
5
+ const withCors_1 = require("./withCors");
6
+ const withSecurity_1 = require("./withSecurity");
7
+ const withRateLimiting_1 = require("./withRateLimiting");
8
+ const withCSRF_1 = require("./withCSRF");
9
+ const withSession_1 = require("./withSession");
10
+ function withDefaultMiddleware() {
11
+ return [
12
+ (0, withCSRF_1.withCSRF)(),
13
+ (0, withSecurity_1.withSecurity)(),
14
+ (0, withCors_1.withCors)([
15
+ "*",
16
+ ], ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], [
17
+ "Content-Type",
18
+ "Authorization",
19
+ "x-csrf-token",
20
+ "x-requested-with",
21
+ "Accept",
22
+ "Cookie",
23
+ "Set-Cookie",
24
+ "Origin",
25
+ "Referer",
26
+ "X-Forwarded-For",
27
+ "X-Real-IP",
28
+ "X-Session-Id",
29
+ "Cache-Control",
30
+ "Pragma",
31
+ "If-None-Match",
32
+ "ETag",
33
+ ]),
34
+ (0, withRateLimiting_1.withRateLimiting)({
35
+ global: { limit: 1000, windowMs: 60 * 1000 }, // 1000 requests per minute
36
+ perUser: { limit: 10, windowMs: 60 * 1000 }, // 100 requests per user per minute
37
+ perApi: { limit: 500, windowMs: 60 * 1000 }, // 500 requests per API endpoint per minute
38
+ }),
39
+ withSession_1.withSession,
40
+ ];
41
+ }
42
+ //# sourceMappingURL=withDefaultMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withDefaultMiddleware.js","sourceRoot":"","sources":["../../src/middleware/withDefaultMiddleware.ts"],"names":[],"mappings":";AAAA,8CAA8C;;AAS9C,sDAmCC;AA1CD,yCAAsC;AACtC,iDAA8C;AAC9C,yDAAsD;AAEtD,yCAAsC;AACtC,+CAA4C;AAE5C,SAAgB,qBAAqB;IACnC,OAAO;QACL,IAAA,mBAAQ,GAAE;QACV,IAAA,2BAAY,GAAE;QACd,IAAA,mBAAQ,EACN;YACE,GAAG;SACJ,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,EACpD;YACE,cAAc;YACd,eAAe;YACf,cAAc;YACd,kBAAkB;YAClB,QAAQ;YACR,QAAQ;YACR,YAAY;YACZ,QAAQ;YACR,SAAS;YACT,iBAAiB;YACjB,WAAW;YACX,cAAc;YACd,eAAe;YACf,QAAQ;YACR,eAAe;YACf,MAAM;SACP,CACF;QACD,IAAA,mCAAgB,EAAC;YACf,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,2BAA2B;YACzE,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,mCAAmC;YAChF,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,2CAA2C;SACzF,CAAC;QACF,yBAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Middleware } from "./withMiddleware.js";
2
+ export declare const withLogging: Middleware;
3
+ //# sourceMappingURL=withLogging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withLogging.d.ts","sourceRoot":"","sources":["../../src/middleware/withLogging.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,eAAO,MAAM,WAAW,EAAE,UA6BzB,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withLogging = void 0;
4
+ const withLogging = async (req, context) => {
5
+ const method = req.method;
6
+ const path = req.url;
7
+ const prefix = `${method} ${path} `;
8
+ function makeLogger(context) {
9
+ return {
10
+ log: (...args) => context.log(...args.map((arg) => (typeof arg === "string" ? prefix + arg : arg))),
11
+ warn: (...args) => context.warn(...args.map((arg) => (typeof arg === "string" ? prefix + arg : arg))),
12
+ error: (...args) => context.error(...args.map((arg) => (typeof arg === "string" ? prefix + arg : arg))),
13
+ };
14
+ }
15
+ context.extraInputs.set("logger", makeLogger(context));
16
+ return true;
17
+ };
18
+ exports.withLogging = withLogging;
19
+ //# sourceMappingURL=withLogging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withLogging.js","sourceRoot":"","sources":["../../src/middleware/withLogging.ts"],"names":[],"mappings":";;;AAGO,MAAM,WAAW,GAAe,KAAK,EAC1C,GAAgB,EAChB,OAA0B,EAC1B,EAAE;IACF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC;IAErB,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC;IAEpC,SAAS,UAAU,CAAC,OAA0B;QAC5C,OAAO;YACL,GAAG,EAAE,CAAC,GAAG,IAAW,EAAE,EAAE,CACtB,OAAO,CAAC,GAAG,CACT,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CACrE;YACH,IAAI,EAAE,CAAC,GAAG,IAAW,EAAE,EAAE,CACvB,OAAO,CAAC,IAAI,CACV,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CACrE;YACH,KAAK,EAAE,CAAC,GAAG,IAAW,EAAE,EAAE,CACxB,OAAO,CAAC,KAAK,CACX,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CACrE;SACJ,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAEvD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AA7BW,QAAA,WAAW,eA6BtB"}
@@ -0,0 +1,3 @@
1
+ import { Middleware } from "./withMiddleware.js";
2
+ export declare const withMCPHeader: Middleware;
3
+ //# sourceMappingURL=withMCPHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withMCPHeader.d.ts","sourceRoot":"","sources":["../../src/middleware/withMCPHeader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,eAAO,MAAM,aAAa,EAAE,UAc3B,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withMCPHeader = void 0;
4
+ const withMCPHeader = async (req, context) => {
5
+ const modelHeader = req.headers.get("x-mcp-model");
6
+ if (!modelHeader) {
7
+ context.extraOutputs.set("response", {
8
+ status: 400,
9
+ body: { error: "Missing x-mcp-model header" },
10
+ });
11
+ return false;
12
+ }
13
+ // Could add more validation or parsing here
14
+ return true;
15
+ };
16
+ exports.withMCPHeader = withMCPHeader;
17
+ //# sourceMappingURL=withMCPHeader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withMCPHeader.js","sourceRoot":"","sources":["../../src/middleware/withMCPHeader.ts"],"names":[],"mappings":";;;AAGO,MAAM,aAAa,GAAe,KAAK,EAC5C,GAAgB,EAChB,OAA0B,EAC1B,EAAE;IACF,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACnD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE;YACnC,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,KAAK,EAAE,4BAA4B,EAAE;SAC9C,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC;IACD,4CAA4C;IAC5C,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAdW,QAAA,aAAa,iBAcxB"}
@@ -0,0 +1,5 @@
1
+ import type { HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
2
+ export type AzureFunction = (req: HttpRequest, context: InvocationContext) => Promise<HttpResponseInit>;
3
+ export type Middleware = (req: HttpRequest, context: InvocationContext) => Promise<boolean>;
4
+ export declare function withMiddleware(handler: AzureFunction, middlewares: Middleware[]): AzureFunction;
5
+ //# sourceMappingURL=withMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/withMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AASzF,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,iBAAiB,KACvB,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE/B,MAAM,MAAM,UAAU,GAAG,CACvB,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,iBAAiB,KACvB,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,UAAU,EAAE,GACxB,aAAa,CAuDf"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withMiddleware = withMiddleware;
4
+ const withLogging_1 = require("./withLogging");
5
+ const index_js_1 = require("../utils/index.js");
6
+ const transportSecurity_js_1 = require("./transportSecurity.js");
7
+ function withMiddleware(handler, middlewares) {
8
+ return async function (req, context) {
9
+ context.extraInputs.set("fetchData", handler);
10
+ const start = Date.now();
11
+ const ip = (0, index_js_1.extractAndHashClientIp)(req);
12
+ const userAgent = req.headers.get("user-agent") ?? "unknown";
13
+ const method = req.method;
14
+ const path = req.url;
15
+ // Add additional logging context to all context.log/.warn/.error messages
16
+ await (0, withLogging_1.withLogging)(req, context);
17
+ const { headers, cookies } = (0, index_js_1.getExtraOutputs)(context);
18
+ (0, transportSecurity_js_1.applyBaselineSecurityHeaders)(headers);
19
+ context.extraOutputs.set("headers", headers);
20
+ if ((0, transportSecurity_js_1.shouldEnforceHttps)() &&
21
+ !(0, transportSecurity_js_1.isHttpsRequest)(req) &&
22
+ !(0, transportSecurity_js_1.isInsecureLocalRequest)(req)) {
23
+ context.log("request rejected: insecure transport in https-enforced mode");
24
+ return {
25
+ status: 426,
26
+ headers,
27
+ cookies,
28
+ body: "HTTPS is required.",
29
+ };
30
+ }
31
+ for (const mw of middlewares) {
32
+ const continueProcessing = await mw(req, context);
33
+ if (!continueProcessing) {
34
+ // Middleware handled response or aborted
35
+ return context.extraOutputs.get("http");
36
+ }
37
+ }
38
+ const value = await handler(req, context);
39
+ const end = Date.now();
40
+ const duration = end - start;
41
+ context.log(`Handled ${method} ${path} | IP: ${ip} | Agent: ${userAgent} | Duration: ${duration}ms`);
42
+ return value;
43
+ };
44
+ }
45
+ //# sourceMappingURL=withMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withMiddleware.js","sourceRoot":"","sources":["../../src/middleware/withMiddleware.ts"],"names":[],"mappings":";;AAmBA,wCA0DC;AA5ED,+CAA4C;AAC5C,gDAA4E;AAC5E,iEAKgC;AAWhC,SAAgB,cAAc,CAC5B,OAAsB,EACtB,WAAyB;IAEzB,OAAO,KAAK,WACV,GAAgB,EAChB,OAA0B;QAE1B,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAE9C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,MAAM,EAAE,GAAG,IAAA,iCAAsB,EAAC,GAAG,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC;QAC7D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC;QAErB,0EAA0E;QAC1E,MAAM,IAAA,yBAAW,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAEhC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAA,0BAAe,EAAC,OAAO,CAAC,CAAC;QACtD,IAAA,mDAA4B,EAAC,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE7C,IACE,IAAA,yCAAkB,GAAE;YACpB,CAAC,IAAA,qCAAc,EAAC,GAAG,CAAC;YACpB,CAAC,IAAA,6CAAsB,EAAC,GAAG,CAAC,EAC5B,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;YAC3E,OAAO;gBACL,MAAM,EAAE,GAAG;gBACX,OAAO;gBACP,OAAO;gBACP,IAAI,EAAE,oBAAoB;aAC3B,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;YAC7B,MAAM,kBAAkB,GAAG,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,yCAAyC;gBAEzC,OAAO,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAqB,CAAC;YAC9D,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,GAAG,GAAG,KAAK,CAAC;QAE7B,OAAO,CAAC,GAAG,CACT,WAAW,MAAM,IAAI,IAAI,UAAU,EAAE,aAAa,SAAS,gBAAgB,QAAQ,IAAI,CACxF,CAAC;QAEF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Middleware } from "./withMiddleware.js";
2
+ export interface RateLimitConfig {
3
+ limit: number;
4
+ windowMs: number;
5
+ }
6
+ export interface ApiRateLimiterConfig {
7
+ global?: RateLimitConfig;
8
+ perUser?: RateLimitConfig;
9
+ perApi?: RateLimitConfig;
10
+ }
11
+ export declare function withRateLimiting(config: ApiRateLimiterConfig): Middleware;
12
+ //# sourceMappingURL=withRateLimiting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withRateLimiting.d.ts","sourceRoot":"","sources":["../../src/middleware/withRateLimiting.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAuFD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,UAAU,CA+EzE"}
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withRateLimiting = withRateLimiting;
7
+ const ioredis_1 = __importDefault(require("ioredis"));
8
+ const redisUrl = process.env.REDIS_URL?.trim();
9
+ const redisHost = process.env.REDIS_HOST?.trim();
10
+ const redisEnabled = (process.env.REDIS_ENABLED ??
11
+ ((redisUrl || redisHost) ? "true" : "false")).toLowerCase() === "true";
12
+ let redisClient;
13
+ function getRedisClient() {
14
+ if (!redisEnabled) {
15
+ return null;
16
+ }
17
+ if (redisClient !== undefined) {
18
+ return redisClient;
19
+ }
20
+ redisClient = redisUrl
21
+ ? new ioredis_1.default(redisUrl, { lazyConnect: true })
22
+ : new ioredis_1.default({
23
+ host: redisHost ?? "redis",
24
+ port: Number(process.env.REDIS_PORT ?? 6379),
25
+ password: process.env.REDIS_PASSWORD,
26
+ lazyConnect: true,
27
+ });
28
+ return redisClient;
29
+ }
30
+ async function checkRateLimit(key, config, context) {
31
+ const logger = context.extraInputs.get("logger");
32
+ const now = Date.now();
33
+ const windowKey = `${key}:${Math.floor(now / config.windowMs)}`;
34
+ const redis = getRedisClient();
35
+ if (!redis) {
36
+ logger?.log(`Rate limiting bypassed for ${key} (Redis disabled)`);
37
+ return {
38
+ allowed: true,
39
+ remaining: config.limit,
40
+ reset: Math.ceil((now + config.windowMs) / 1000),
41
+ };
42
+ }
43
+ try {
44
+ const count = await redis.incr(windowKey);
45
+ if (count === 1) {
46
+ await redis.pexpire(windowKey, config.windowMs);
47
+ }
48
+ const ttl = await redis.pttl(windowKey);
49
+ const retryAfter = count > config.limit ? Math.ceil(ttl / 1000) : undefined;
50
+ const remaining = Math.max(config.limit - count, 0);
51
+ const reset = Math.ceil((now + ttl) / 1000); // Unix timestamp when reset will happen
52
+ if (count > config.limit) {
53
+ logger?.warn(`Rate limit exceeded for ${key}. Retry after ${retryAfter}s.`);
54
+ return { allowed: false, remaining: 0, retryAfter, reset };
55
+ }
56
+ return { allowed: true, remaining, reset };
57
+ }
58
+ catch (error) {
59
+ logger?.warn(`Rate limit backend unavailable for ${key}; allowing request.`);
60
+ logger?.warn(error);
61
+ return {
62
+ allowed: true,
63
+ remaining: config.limit,
64
+ reset: Math.ceil((now + config.windowMs) / 1000),
65
+ };
66
+ }
67
+ }
68
+ function withRateLimiting(config) {
69
+ return async (req, context) => {
70
+ const path = new URL(req.url).pathname;
71
+ // ----- GLOBAL -----
72
+ if (config.global) {
73
+ const result = await checkRateLimit("rate:global", config.global, context);
74
+ if (!result.allowed) {
75
+ context.extraOutputs.set("http", {
76
+ status: 429,
77
+ body: "Server is busy (global limit). Please retry later.",
78
+ headers: {
79
+ "Retry-After": result.retryAfter?.toString(),
80
+ "X-RateLimit-Limit": config.global.limit.toString(),
81
+ "X-RateLimit-Remaining": result.remaining.toString(),
82
+ "X-RateLimit-Reset": result.reset.toString(),
83
+ },
84
+ });
85
+ return false;
86
+ }
87
+ }
88
+ // ----- PER USER -----
89
+ if (config.perUser) {
90
+ const userId = req.headers.get("x-user-id") ||
91
+ req.headers.get("authorization") ||
92
+ req.headers.get("x-forwarded-for") ||
93
+ "anonymous";
94
+ const result = await checkRateLimit(`rate:user:${userId}`, config.perUser, context);
95
+ if (!result.allowed) {
96
+ context.extraOutputs.set("http", {
97
+ status: 429,
98
+ body: "You are sending requests too fast (user limit). Please retry later.",
99
+ headers: {
100
+ "Retry-After": result.retryAfter?.toString(),
101
+ "X-RateLimit-Limit": config.perUser.limit.toString(),
102
+ "X-RateLimit-Remaining": result.remaining.toString(),
103
+ "X-RateLimit-Reset": result.reset.toString(),
104
+ },
105
+ });
106
+ return false;
107
+ }
108
+ }
109
+ // ----- PER API -----
110
+ if (config.perApi) {
111
+ const apiKey = req.method + ":" + path;
112
+ const result = await checkRateLimit(`rate:api:${apiKey}`, config.perApi, context);
113
+ if (!result.allowed) {
114
+ context.extraOutputs.set("http", {
115
+ status: 429,
116
+ body: "This API is receiving too many requests. Please retry later.",
117
+ headers: {
118
+ "Retry-After": result.retryAfter?.toString(),
119
+ "X-RateLimit-Limit": config.perApi.limit.toString(),
120
+ "X-RateLimit-Remaining": result.remaining.toString(),
121
+ "X-RateLimit-Reset": result.reset.toString(),
122
+ },
123
+ });
124
+ return false;
125
+ }
126
+ }
127
+ // All passed
128
+ return true;
129
+ };
130
+ }
131
+ //# sourceMappingURL=withRateLimiting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withRateLimiting.js","sourceRoot":"","sources":["../../src/middleware/withRateLimiting.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Middleware } from "./withMiddleware";
2
+ export declare function withSecurity(): Middleware;
3
+ //# sourceMappingURL=withSecurity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withSecurity.d.ts","sourceRoot":"","sources":["../../src/middleware/withSecurity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,wBAAgB,YAAY,IAAI,UAAU,CAwBzC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withSecurity = withSecurity;
4
+ const index_js_1 = require("../utils/index.js");
5
+ const transportSecurity_js_1 = require("./transportSecurity.js");
6
+ function withSecurity() {
7
+ return async (request, context) => {
8
+ const { headers, cookies } = (0, index_js_1.getExtraOutputs)(context);
9
+ (0, transportSecurity_js_1.applyBaselineSecurityHeaders)(headers);
10
+ context.extraOutputs.set("headers", headers);
11
+ if ((0, transportSecurity_js_1.shouldEnforceHttps)() &&
12
+ !(0, transportSecurity_js_1.isHttpsRequest)(request) &&
13
+ !(0, transportSecurity_js_1.isInsecureLocalRequest)(request)) {
14
+ context.extraOutputs.set("http", {
15
+ status: 426,
16
+ headers,
17
+ cookies,
18
+ body: "HTTPS is required.",
19
+ });
20
+ return Promise.resolve(false);
21
+ }
22
+ return Promise.resolve(true);
23
+ };
24
+ }
25
+ //# sourceMappingURL=withSecurity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withSecurity.js","sourceRoot":"","sources":["../../src/middleware/withSecurity.ts"],"names":[],"mappings":";;AASA,oCAwBC;AA/BD,gDAAoD;AACpD,iEAKgC;AAChC,SAAgB,YAAY;IAC1B,OAAO,KAAK,EAAE,OAAoB,EAAE,OAA0B,EAAE,EAAE;QAChE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAA,0BAAe,EAAC,OAAO,CAAC,CAAC;QAEtD,IAAA,mDAA4B,EAAC,OAAO,CAAC,CAAC;QAEtC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE7C,IACE,IAAA,yCAAkB,GAAE;YACpB,CAAC,IAAA,qCAAc,EAAC,OAAO,CAAC;YACxB,CAAC,IAAA,6CAAsB,EAAC,OAAO,CAAC,EAChC,CAAC;YACD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE;gBAC/B,MAAM,EAAE,GAAG;gBACX,OAAO;gBACP,OAAO;gBACP,IAAI,EAAE,oBAAoB;aAC3B,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Middleware } from "./withMiddleware.js";
2
+ export declare const withSession: Middleware;
3
+ //# sourceMappingURL=withSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withSession.d.ts","sourceRoot":"","sources":["../../src/middleware/withSession.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAIjD,eAAO,MAAM,WAAW,EAAE,UAezB,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withSession = void 0;
4
+ const index_js_1 = require("../utils/index.js");
5
+ const withSession = async (req, context) => {
6
+ const { cookies } = (0, index_js_1.getExtraOutputs)(context);
7
+ const session = (0, index_js_1.ensureSession)(req);
8
+ if (session.isNew && session.cookie) {
9
+ const newCookies = [...cookies, session.cookie];
10
+ context.extraOutputs.set("cookies", newCookies);
11
+ }
12
+ context.extraInputs.set("sessionId", session.sessionId);
13
+ return true;
14
+ };
15
+ exports.withSession = withSession;
16
+ //# sourceMappingURL=withSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withSession.js","sourceRoot":"","sources":["../../src/middleware/withSession.ts"],"names":[],"mappings":";;;AAGA,gDAAmE;AAE5D,MAAM,WAAW,GAAe,KAAK,EAC1C,GAAgB,EAChB,OAA0B,EAC1B,EAAE;IACF,MAAM,EAAE,OAAO,EAAE,GAAG,IAAA,0BAAe,EAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAC;IAEnC,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAExD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAfW,QAAA,WAAW,eAetB"}
@@ -0,0 +1,6 @@
1
+ import { Cookie, InvocationContext } from "@azure/functions";
2
+ export declare function getExtraOutputs(context: InvocationContext): {
3
+ headers: Headers;
4
+ cookies: Cookie[];
5
+ };
6
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/utils/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAKA"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getExtraOutputs = getExtraOutputs;
4
+ function getExtraOutputs(context) {
5
+ const headers = context.extraOutputs.get("headers") || new Headers();
6
+ const cookies = context.extraOutputs.get("cookies") || [];
7
+ return { headers, cookies };
8
+ }
9
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/utils/context.ts"],"names":[],"mappings":";;AAEA,0CAQC;AARD,SAAgB,eAAe,CAAC,OAA0B;IAIxD,MAAM,OAAO,GACV,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAa,IAAI,IAAI,OAAO,EAAE,CAAC;IACpE,MAAM,OAAO,GAAI,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAc,IAAI,EAAE,CAAC;IACxE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { HttpRequest } from "@azure/functions";
2
+ export declare function extractAndHashClientIp(req: HttpRequest): string;
3
+ //# sourceMappingURL=extract.ip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.ip.d.ts","sourceRoot":"","sources":["../../src/utils/extract.ip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM/C,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAQ/D"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractAndHashClientIp = extractAndHashClientIp;
4
+ const crypto_1 = require("crypto");
5
+ const HMAC_SECRET = process.env.HMAC_SECRET || "OuYDwS9zpItZ9d84mIuZ+rzU6c9abFkzDWzXAPk4elg="; // Replace for production
6
+ function extractAndHashClientIp(req) {
7
+ const rawIp = req.headers.get("x-forwarded-for")?.split(",")[0].trim() ||
8
+ req.headers.get("x-client-ip")?.trim() ||
9
+ req.headers.get("host")?.trim() ||
10
+ "unknown";
11
+ const ip = (0, crypto_1.createHmac)("sha256", HMAC_SECRET).update(rawIp).digest("hex");
12
+ return ip;
13
+ }
14
+ //# sourceMappingURL=extract.ip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.ip.js","sourceRoot":"","sources":["../../src/utils/extract.ip.ts"],"names":[],"mappings":";;AAMA,wDAQC;AAbD,mCAAoC;AAEpC,MAAM,WAAW,GACf,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,8CAA8C,CAAC,CAAC,yBAAyB;AAEtG,SAAgB,sBAAsB,CAAC,GAAgB;IACrD,MAAM,KAAK,GACT,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QACxD,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE;QACtC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE;QAC/B,SAAS,CAAC;IACZ,MAAM,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzE,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { HttpResponseInit } from "@azure/functions";
2
+ export declare const badRequestResponse: HttpResponseInit;
3
+ export declare const unauthorizedResponse: HttpResponseInit;
4
+ export declare const forbiddenResponse: HttpResponseInit;
5
+ export declare const notFoundResponse: HttpResponseInit;
6
+ export declare const requestTimeoutResponse: HttpResponseInit;
7
+ export declare const tooManyRequestsResponse: HttpResponseInit;
8
+ export declare const internalServerErrorResponse: HttpResponseInit;
9
+ export declare const notImplementedResponse: HttpResponseInit;
10
+ export declare const badGatewayResponse: HttpResponseInit;
11
+ export declare const serviceUnavailableResponse: HttpResponseInit;
12
+ export declare const gatewayTimeoutResponse: HttpResponseInit;
13
+ //# sourceMappingURL=http.errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.errors.d.ts","sourceRoot":"","sources":["../../src/utils/http.errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAEnD,eAAO,MAAM,kBAAkB,EAAE,gBAGhC,CAAA;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAGlC,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAE,gBAG/B,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,gBAG9B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,gBAGpC,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAGrC,CAAA;AAED,eAAO,MAAM,2BAA2B,EAAE,gBAGzC,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,gBAGpC,CAAA;AAED,eAAO,MAAM,kBAAkB,EAAE,gBAGhC,CAAA;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAGxC,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,gBAGpC,CAAA"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gatewayTimeoutResponse = exports.serviceUnavailableResponse = exports.badGatewayResponse = exports.notImplementedResponse = exports.internalServerErrorResponse = exports.tooManyRequestsResponse = exports.requestTimeoutResponse = exports.notFoundResponse = exports.forbiddenResponse = exports.unauthorizedResponse = exports.badRequestResponse = void 0;
4
+ exports.badRequestResponse = {
5
+ status: 400,
6
+ body: JSON.stringify({ error: 'Bad Request' }),
7
+ };
8
+ exports.unauthorizedResponse = {
9
+ status: 401,
10
+ body: JSON.stringify({ error: 'Unauthorized' }),
11
+ };
12
+ exports.forbiddenResponse = {
13
+ status: 403,
14
+ body: JSON.stringify({ error: 'Forbidden' }),
15
+ };
16
+ exports.notFoundResponse = {
17
+ status: 404,
18
+ body: JSON.stringify({ error: 'Not Found' }),
19
+ };
20
+ exports.requestTimeoutResponse = {
21
+ status: 408,
22
+ body: JSON.stringify({ error: 'Request Timeout' }),
23
+ };
24
+ exports.tooManyRequestsResponse = {
25
+ status: 429,
26
+ body: JSON.stringify({ error: 'Too Many Requests' }),
27
+ };
28
+ exports.internalServerErrorResponse = {
29
+ status: 500,
30
+ body: JSON.stringify({ error: 'Internal Server Error' }),
31
+ };
32
+ exports.notImplementedResponse = {
33
+ status: 501,
34
+ body: JSON.stringify({ error: 'Not Implemented' }),
35
+ };
36
+ exports.badGatewayResponse = {
37
+ status: 502,
38
+ body: JSON.stringify({ error: 'Bad Gateway' }),
39
+ };
40
+ exports.serviceUnavailableResponse = {
41
+ status: 503,
42
+ body: JSON.stringify({ error: 'Service Unavailable' }),
43
+ };
44
+ exports.gatewayTimeoutResponse = {
45
+ status: 504,
46
+ body: JSON.stringify({ error: 'Gateway Timeout' }),
47
+ };
48
+ //# sourceMappingURL=http.errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.errors.js","sourceRoot":"","sources":["../../src/utils/http.errors.ts"],"names":[],"mappings":";;;AAEa,QAAA,kBAAkB,GAAqB;IAChD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;CACjD,CAAA;AAEY,QAAA,oBAAoB,GAAqB;IAClD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;CAClD,CAAA;AAEY,QAAA,iBAAiB,GAAqB;IAC/C,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;CAC/C,CAAA;AAEY,QAAA,gBAAgB,GAAqB;IAC9C,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;CAC/C,CAAA;AAEY,QAAA,sBAAsB,GAAqB;IACpD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;CACrD,CAAA;AAEY,QAAA,uBAAuB,GAAqB;IACrD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;CACvD,CAAA;AAEY,QAAA,2BAA2B,GAAqB;IACzD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;CAC3D,CAAA;AAEY,QAAA,sBAAsB,GAAqB;IACpD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;CACrD,CAAA;AAEY,QAAA,kBAAkB,GAAqB;IAChD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;CACjD,CAAA;AAEY,QAAA,0BAA0B,GAAqB;IACxD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;CACzD,CAAA;AAEY,QAAA,sBAAsB,GAAqB;IACpD,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;CACrD,CAAA"}
@@ -0,0 +1,10 @@
1
+ export * from "./cookies.js";
2
+ export * from "./state.js";
3
+ export * from "./http.errors.js";
4
+ export * from "./extract.ip.js";
5
+ export * from "./names.js";
6
+ export * from "./context.js";
7
+ export * from "./sanitize.table.key.js";
8
+ export * from "./oauth-pkce.js";
9
+ export * from "./session.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./cookies.js"), exports);
18
+ __exportStar(require("./state.js"), exports);
19
+ __exportStar(require("./http.errors.js"), exports);
20
+ __exportStar(require("./extract.ip.js"), exports);
21
+ __exportStar(require("./names.js"), exports);
22
+ __exportStar(require("./context.js"), exports);
23
+ __exportStar(require("./sanitize.table.key.js"), exports);
24
+ __exportStar(require("./oauth-pkce.js"), exports);
25
+ __exportStar(require("./session.js"), exports);
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,6CAA2B;AAC3B,mDAAiC;AACjC,kDAAgC;AAChC,6CAA2B;AAC3B,+CAA6B;AAC7B,0DAAwC;AACxC,kDAAgC;AAChC,+CAA6B"}
@@ -0,0 +1,6 @@
1
+ export declare function splitDisplayName(displayName: string): {
2
+ firstName: string;
3
+ middleName: string;
4
+ lastName: string;
5
+ };
6
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../src/utils/names.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAqBA"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.splitDisplayName = splitDisplayName;
4
+ function splitDisplayName(displayName) {
5
+ const parts = displayName.trim().split(/\s+/);
6
+ if (parts.length === 0) {
7
+ return { firstName: "", middleName: "", lastName: "" };
8
+ }
9
+ if (parts.length === 1) {
10
+ return { firstName: parts[0], middleName: "", lastName: "" };
11
+ }
12
+ if (parts.length === 2) {
13
+ return { firstName: parts[0], middleName: "", lastName: parts[1] };
14
+ }
15
+ // 3 or more parts
16
+ const firstName = parts[0];
17
+ const lastName = parts[parts.length - 1];
18
+ const middleName = parts.slice(1, parts.length - 1).join(" ");
19
+ return { firstName, middleName, lastName };
20
+ }
21
+ //# sourceMappingURL=names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../../src/utils/names.ts"],"names":[],"mappings":";;AAAA,4CAyBC;AAzBD,SAAgB,gBAAgB,CAAC,WAAmB;IAKlD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACzD,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC/D,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,CAAC;IAED,kBAAkB;IAClB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE9D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function sanitizeUrlForPartitionKey(rawUrl: string): string;
2
+ //# sourceMappingURL=sanitize.table.key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.table.key.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.table.key.ts"],"names":[],"mappings":"AAAA,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWjE"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sanitizeUrlForPartitionKey = sanitizeUrlForPartitionKey;
4
+ function sanitizeUrlForPartitionKey(rawUrl) {
5
+ try {
6
+ const { hostname } = new URL(rawUrl);
7
+ const sanitized = hostname
8
+ .toLowerCase()
9
+ .replace(/[^a-z0-9]/g, "-") // convert all non-alphanumeric characters to hyphens
10
+ .replace(/^-+|-+$/g, ""); // remove leading/trailing hyphens
11
+ return sanitized;
12
+ }
13
+ catch {
14
+ throw new Error(`Invalid URL passed for sanitization: "${rawUrl}"`);
15
+ }
16
+ }
17
+ //# sourceMappingURL=sanitize.table.key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.table.key.js","sourceRoot":"","sources":["../../src/utils/sanitize.table.key.ts"],"names":[],"mappings":";;AAAA,gEAWC;AAXD,SAAgB,0BAA0B,CAAC,MAAc;IACvD,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,QAAQ;aACvB,WAAW,EAAE;aACb,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,qDAAqD;aAChF,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,kCAAkC;QAC9D,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yCAAyC,MAAM,GAAG,CAAC,CAAC;IACtE,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/api",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Generic public API security and middleware helpers.",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",
@@ -21,6 +21,14 @@
21
21
  "types": "./dist/index.d.ts",
22
22
  "default": "./dist/index.js"
23
23
  },
24
+ "./middleware": {
25
+ "types": "./dist/middleware/index.d.ts",
26
+ "default": "./dist/middleware/index.js"
27
+ },
28
+ "./middleware/*": {
29
+ "types": "./dist/middleware/*.d.ts",
30
+ "default": "./dist/middleware/*.js"
31
+ },
24
32
  "./package.json": "./package.json"
25
33
  },
26
34
  "scripts": {