@zudojs/http 1.2.0 → 1.4.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.
Files changed (127) hide show
  1. package/README.md +221 -0
  2. package/dist/httpAdapter/http.adapters.d.ts +24 -3
  3. package/dist/httpAdapter/http.adapters.js +20 -11
  4. package/dist/httpAdapter/node/httpNode.adapter.d.ts +2 -1
  5. package/dist/httpAdapter/node/httpNode.adapter.js +17 -2
  6. package/dist/httpAdapter/node/httpNode.request.js +7 -1
  7. package/dist/httpAdapter/node/httpNode.type.d.ts +14 -0
  8. package/dist/httpAgent/http.agent.d.ts +22 -3
  9. package/dist/httpAgent/http.agent.js +52 -14
  10. package/dist/httpCacheControl/httpCacheControl.freshness.d.ts +7 -1
  11. package/dist/httpCacheControl/httpCacheControl.freshness.js +30 -3
  12. package/dist/httpClient/httpClient.retry.d.ts +17 -12
  13. package/dist/httpClient/httpClient.retry.js +35 -10
  14. package/dist/httpClient/httpClient.type.d.ts +14 -0
  15. package/dist/httpErrors/httpError.base.js +2 -2
  16. package/dist/httpErrors/httpError.util.d.ts +8 -0
  17. package/dist/httpErrors/httpError.util.js +12 -0
  18. package/dist/httpFetchMount/httpFetchMount.core.d.ts +25 -0
  19. package/dist/httpFetchMount/httpFetchMount.core.js +84 -0
  20. package/dist/httpFetchMount/httpFetchMount.request.d.ts +21 -0
  21. package/dist/httpFetchMount/httpFetchMount.request.js +100 -0
  22. package/dist/httpFetchMount/httpFetchMount.type.d.ts +56 -0
  23. package/dist/httpFetchMount/httpFetchMount.type.js +5 -0
  24. package/dist/httpFetchMount/index.d.ts +11 -0
  25. package/dist/httpFetchMount/index.js +10 -0
  26. package/dist/httpKeepAlive/httpKeepAlive.core.js +14 -2
  27. package/dist/httpMiddleware/builtin/logging/httpMiddleware.logging.d.ts +18 -0
  28. package/dist/httpMiddleware/builtin/logging/httpMiddleware.logging.js +17 -1
  29. package/dist/httpMiddleware/builtin/rateLimit/httpMiddleware.rateLimit.d.ts +6 -3
  30. package/dist/httpMiddleware/builtin/rateLimit/httpMiddleware.rateLimit.js +34 -6
  31. package/dist/httpMiddleware/builtin/security/httpMiddleware.security.d.ts +11 -0
  32. package/dist/httpMiddleware/builtin/security/httpMiddleware.security.js +25 -12
  33. package/dist/httpMiddleware/httpMiddleware.type.d.ts +9 -1
  34. package/dist/httpMiddleware/pipeline/httpPipeline.execution.js +22 -45
  35. package/dist/httpMiddleware/pipeline/httpPipeline.guardResponse.d.ts +36 -0
  36. package/dist/httpMiddleware/pipeline/httpPipeline.guardResponse.js +57 -0
  37. package/dist/httpMiddleware/pipeline/httpPipeline.helper.d.ts +2 -1
  38. package/dist/httpMiddleware/pipeline/httpPipeline.helper.js +10 -0
  39. package/dist/httpMiddleware/pipeline/index.d.ts +1 -0
  40. package/dist/httpMiddleware/pipeline/index.js +1 -0
  41. package/dist/httpNegotiation/httpNegotiation.core.d.ts +13 -0
  42. package/dist/httpNegotiation/httpNegotiation.core.js +60 -8
  43. package/dist/httpOpenApi/httpOpenApi.document.d.ts +44 -0
  44. package/dist/httpOpenApi/httpOpenApi.document.js +61 -0
  45. package/dist/httpOpenApi/httpOpenApi.mount.d.ts +31 -0
  46. package/dist/httpOpenApi/httpOpenApi.mount.js +58 -0
  47. package/dist/httpOpenApi/httpOpenApi.type.d.ts +54 -0
  48. package/dist/httpOpenApi/httpOpenApi.type.js +5 -0
  49. package/dist/httpOpenApi/index.d.ts +13 -0
  50. package/dist/httpOpenApi/index.js +12 -0
  51. package/dist/httpOpenApi/routeTable/index.d.ts +11 -0
  52. package/dist/httpOpenApi/routeTable/index.js +11 -0
  53. package/dist/httpOpenApi/routeTable/routeTable.collect.d.ts +19 -0
  54. package/dist/httpOpenApi/routeTable/routeTable.collect.js +89 -0
  55. package/dist/httpOpenApi/routeTable/routeTable.merge.d.ts +15 -0
  56. package/dist/httpOpenApi/routeTable/routeTable.merge.js +37 -0
  57. package/dist/httpOpenApi/routeTable/routeTable.template.d.ts +30 -0
  58. package/dist/httpOpenApi/routeTable/routeTable.template.js +67 -0
  59. package/dist/httpProxy/http.proxy.d.ts +9 -0
  60. package/dist/httpProxy/http.proxy.js +35 -3
  61. package/dist/httpQuery/index.d.ts +11 -2
  62. package/dist/httpQuery/index.js +11 -2
  63. package/dist/httpQuery/queryParse/index.d.ts +10 -0
  64. package/dist/httpQuery/queryParse/index.js +10 -0
  65. package/dist/httpQuery/queryParse/queryParse.flat.d.ts +13 -0
  66. package/dist/httpQuery/queryParse/queryParse.flat.js +44 -0
  67. package/dist/httpQuery/queryParse/queryParse.nested.d.ts +25 -0
  68. package/dist/httpQuery/queryParse/queryParse.nested.js +112 -0
  69. package/dist/httpQuery/queryParse/queryParse.tokenizer.d.ts +37 -0
  70. package/dist/httpQuery/queryParse/queryParse.tokenizer.js +95 -0
  71. package/dist/httpQuery/queryRequest/index.d.ts +9 -0
  72. package/dist/httpQuery/queryRequest/index.js +9 -0
  73. package/dist/httpQuery/queryRequest/query.request.d.ts +43 -0
  74. package/dist/httpQuery/queryRequest/query.request.js +96 -0
  75. package/dist/httpQuery/querySerialize/index.d.ts +10 -0
  76. package/dist/httpQuery/querySerialize/index.js +10 -0
  77. package/dist/httpQuery/querySerialize/query.util.d.ts +21 -0
  78. package/dist/httpQuery/querySerialize/query.util.js +67 -0
  79. package/dist/httpQuery/querySerialize/querySerialize.core.d.ts +12 -0
  80. package/dist/httpQuery/querySerialize/querySerialize.core.js +97 -0
  81. package/dist/httpQuery/queryTypes/index.d.ts +11 -0
  82. package/dist/httpQuery/queryTypes/index.js +9 -0
  83. package/dist/httpQuery/queryTypes/query.container.d.ts +16 -0
  84. package/dist/httpQuery/queryTypes/query.container.js +51 -0
  85. package/dist/httpQuery/queryTypes/query.limit.d.ts +25 -0
  86. package/dist/httpQuery/queryTypes/query.limit.js +32 -0
  87. package/dist/httpQuery/queryTypes/query.type.d.ts +62 -0
  88. package/dist/httpQuery/queryTypes/query.type.js +2 -0
  89. package/dist/httpRedirect/http.redirect.d.ts +6 -0
  90. package/dist/httpRedirect/http.redirect.js +53 -2
  91. package/dist/httpRequest/http.request.d.ts +61 -2
  92. package/dist/httpRequest/http.request.js +86 -35
  93. package/dist/httpRequest/httpRequest.context.d.ts +8 -0
  94. package/dist/httpRequest/httpRequest.context.js +23 -18
  95. package/dist/httpRequest/index.d.ts +1 -0
  96. package/dist/httpRequest/index.js +1 -0
  97. package/dist/httpRequest/requestId/httpRequest.requestId.d.ts +25 -0
  98. package/dist/httpRequest/requestId/httpRequest.requestId.js +34 -0
  99. package/dist/httpRequest/requestId/index.d.ts +7 -0
  100. package/dist/httpRequest/requestId/index.js +7 -0
  101. package/dist/httpRequest/target/httpRequest.target.d.ts +2 -2
  102. package/dist/httpRequest/target/httpRequest.target.js +23 -5
  103. package/dist/httpResponse/httpResponse.writer.js +15 -0
  104. package/dist/httpRouter/core/factory/httpRoute.factory.base.d.ts +24 -4
  105. package/dist/httpRouter/core/factory/httpRoute.factory.base.js +73 -17
  106. package/dist/httpRouter/core/factory/httpRoute.factory.js +3 -3
  107. package/dist/httpRouter/core/group/httpRouterGroup.core.js +13 -0
  108. package/dist/httpRouter/core/register/httpRouter.register.js +19 -21
  109. package/dist/httpRouter/core/types/httpRouter.type.d.ts +37 -1
  110. package/dist/httpRouter/core/util/httpRoute.util.d.ts +55 -0
  111. package/dist/httpRouter/core/util/httpRoute.util.js +101 -4
  112. package/dist/httpRouter/dispatch/httpRoute.dispatcher.d.ts +11 -0
  113. package/dist/httpRouter/dispatch/httpRoute.dispatcher.js +25 -6
  114. package/dist/httpRouter/matching/httpRoute.matcher.core.js +51 -6
  115. package/dist/httpRouter/matching/httpRoute.matcher.d.ts +0 -1
  116. package/dist/httpRouter/matching/httpRoute.matcher.js +64 -26
  117. package/dist/httpRouter/pattern/httpRoute.pattern.parse.d.ts +16 -0
  118. package/dist/httpRouter/pattern/httpRoute.pattern.parse.js +40 -11
  119. package/dist/httpRouter/pattern/index.d.ts +1 -1
  120. package/dist/httpRouter/pattern/index.js +1 -1
  121. package/dist/httpSecurity/httpSecurity.config.js +4 -1
  122. package/dist/httpSecurity/httpSecurity.validator.js +16 -7
  123. package/dist/httpServer/factory/httpServer.factory.d.ts +10 -9
  124. package/dist/httpServer/factory/httpServer.factory.js +8 -0
  125. package/dist/index.d.ts +2 -0
  126. package/dist/index.js +2 -0
  127. package/package.json +11 -8
@@ -13,7 +13,7 @@
13
13
  * - `/assets/*path` trailing wildcard
14
14
  */
15
15
  import { InvalidRoutePatternError } from "../core/error/httpRouter.error.js";
16
- import { splitPath, validateParameterName, } from "../core/util/httpRoute.util.js";
16
+ import { hasTrailingSlash, splitRoutePattern, validateParameterName, } from "../core/util/httpRoute.util.js";
17
17
  const SEGMENT_SCORE_LITERAL = 3;
18
18
  const SEGMENT_SCORE_PARAMETER = 2;
19
19
  const SEGMENT_SCORE_WILDCARD = 1;
@@ -22,7 +22,7 @@ const SEGMENT_SCORE_WILDCARD = 1;
22
22
  */
23
23
  export function compileRouteSegments(path) {
24
24
  const segments = [];
25
- const parts = splitPath(path);
25
+ const parts = splitRoutePattern(path);
26
26
  for (const part of parts) {
27
27
  if (part.startsWith(":")) {
28
28
  segments.push(parseParameter(part, path));
@@ -56,6 +56,7 @@ export function compileRoute(path, strictTrailingSlash) {
56
56
  segments,
57
57
  score: scoreSegments(segments),
58
58
  strictTrailingSlash,
59
+ expectsTrailingSlash: hasTrailingSlash(path),
59
60
  });
60
61
  }
61
62
  /**
@@ -64,18 +65,46 @@ export function compileRoute(path, strictTrailingSlash) {
64
65
  export function scoreSegments(segments) {
65
66
  let score = 0;
66
67
  for (const segment of segments) {
67
- if (segment.type === "literal") {
68
- score += SEGMENT_SCORE_LITERAL;
69
- }
70
- else if (segment.type === "parameter") {
71
- score += SEGMENT_SCORE_PARAMETER;
72
- }
73
- else {
74
- score += SEGMENT_SCORE_WILDCARD;
75
- }
68
+ score += segmentScore(segment);
76
69
  }
77
70
  return score;
78
71
  }
72
+ function segmentScore(segment) {
73
+ if (segment === undefined) {
74
+ return 0;
75
+ }
76
+ if (segment.type === "literal") {
77
+ return SEGMENT_SCORE_LITERAL;
78
+ }
79
+ if (segment.type === "parameter") {
80
+ return SEGMENT_SCORE_PARAMETER;
81
+ }
82
+ return SEGMENT_SCORE_WILDCARD;
83
+ }
84
+ /**
85
+ * Compares two compiled patterns by specificity, most specific first.
86
+ *
87
+ * Segments are compared left to right by kind (literal, then parameter, then
88
+ * wildcard), which is how a router is expected to rank patterns. Summing the
89
+ * kinds into one scalar — as this used to — let a longer but entirely
90
+ * parameterised pattern such as `/:p/:q/:r/:s` outrank a literal-anchored
91
+ * `/admin/*rest`, so a request to `/admin/a/b/c` bypassed the admin route and
92
+ * every guard registered on it.
93
+ *
94
+ * @param left - The first pattern's segments.
95
+ * @param right - The second pattern's segments.
96
+ * @returns A negative number when `left` is more specific.
97
+ */
98
+ export function compareSegmentSpecificity(left, right) {
99
+ const length = Math.max(left.length, right.length);
100
+ for (let index = 0; index < length; index += 1) {
101
+ const difference = segmentScore(right[index]) - segmentScore(left[index]);
102
+ if (difference !== 0) {
103
+ return difference;
104
+ }
105
+ }
106
+ return 0;
107
+ }
79
108
  function parseParameter(segment, path) {
80
109
  let value = segment.slice(1);
81
110
  let optional = false;
@@ -14,6 +14,6 @@ export { parseSegments } from "./core/httpPattern.segmentParsing.js";
14
14
  export { buildRegex } from "./core/httpPattern.regexBuilding.js";
15
15
  export { testRoutePattern, matchRoutePattern, matchRoutePatterns, testRoutePatterns, } from "./core/httpPattern.matching.js";
16
16
  export { createRoutePattern, createStrictRoutePattern, createCaseInsensitiveRoutePattern, } from "./httpPattern.factory.js";
17
- export { compileRoute, compileRouteSegments, scoreSegments, } from "./httpRoute.pattern.parse.js";
17
+ export { compileRoute, compileRouteSegments, scoreSegments, compareSegmentSpecificity, } from "./httpRoute.pattern.parse.js";
18
18
  export type { CompiledRoutePath } from "./httpRoute.pattern.parse.js";
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -13,5 +13,5 @@ export { parseSegments } from "./core/httpPattern.segmentParsing.js";
13
13
  export { buildRegex } from "./core/httpPattern.regexBuilding.js";
14
14
  export { testRoutePattern, matchRoutePattern, matchRoutePatterns, testRoutePatterns, } from "./core/httpPattern.matching.js";
15
15
  export { createRoutePattern, createStrictRoutePattern, createCaseInsensitiveRoutePattern, } from "./httpPattern.factory.js";
16
- export { compileRoute, compileRouteSegments, scoreSegments, } from "./httpRoute.pattern.parse.js";
16
+ export { compileRoute, compileRouteSegments, scoreSegments, compareSegmentSpecificity, } from "./httpRoute.pattern.parse.js";
17
17
  //# sourceMappingURL=index.js.map
@@ -12,6 +12,7 @@
12
12
  * adapter has its own, separately named timeout options, and those are the
13
13
  * ones that take effect.
14
14
  */
15
+ import { INCOMING_REQUEST_ID_PATTERN } from "../httpRequest/requestId/httpRequest.requestId.js";
15
16
  /** Default security configuration. */
16
17
  export const DEFAULT_SECURITY_CONFIG = Object.freeze({
17
18
  maxBodySize: 1_048_576, // 1MB
@@ -23,7 +24,9 @@ export const DEFAULT_SECURITY_CONFIG = Object.freeze({
23
24
  requireHost: true,
24
25
  trustProxy: false,
25
26
  maxRequestIdLength: 128,
26
- requestIdPattern: /^[a-zA-Z0-9_-]+$/,
27
+ // Same rule the adapter uses to reuse an incoming x-request-id, so an id
28
+ // it would accept (trace ids like `svc.a:123`) is not refused here first.
29
+ requestIdPattern: INCOMING_REQUEST_ID_PATTERN,
27
30
  enableCrlfProtection: true,
28
31
  enableSmugglingProtection: true,
29
32
  });
@@ -20,14 +20,23 @@ export function validateHeaders(headers, config) {
20
20
  if (entries.length > cfg.maxHeaders) {
21
21
  errors.push(`Too many headers: ${entries.length} > ${cfg.maxHeaders}`);
22
22
  }
23
+ /*
24
+ * `IncomingMessage.headers` returns an array for `set-cookie` and for any
25
+ * header a non-Node caller supplies as a list. Checking only `typeof value
26
+ * === "string"` let every one of those values past the size cap and the
27
+ * CRLF filter while the guard still reported `allowed: true`.
28
+ */
23
29
  for (const [key, value] of entries) {
24
- if (typeof value === "string" && value.length > cfg.maxHeaderValueSize) {
25
- errors.push(`Header "${key}" value too large: ${value.length} > ${cfg.maxHeaderValueSize}`);
26
- }
27
- if (cfg.enableCrlfProtection &&
28
- typeof value === "string" &&
29
- /[\r\n]/.test(value)) {
30
- errors.push(`Header "${key}" contains CRLF characters`);
30
+ for (const element of Array.isArray(value) ? value : [value]) {
31
+ if (typeof element !== "string") {
32
+ continue;
33
+ }
34
+ if (element.length > cfg.maxHeaderValueSize) {
35
+ errors.push(`Header "${key}" value too large: ${element.length} > ${cfg.maxHeaderValueSize}`);
36
+ }
37
+ if (cfg.enableCrlfProtection && /[\r\n]/.test(element)) {
38
+ errors.push(`Header "${key}" contains CRLF characters`);
39
+ }
31
40
  }
32
41
  }
33
42
  return { valid: errors.length === 0, errors };
@@ -4,15 +4,16 @@
4
4
  * @module httpServer/factory
5
5
  */
6
6
  import { HttpServer } from "../core/httpServer.core.js";
7
- import type { HttpServerState, HttpServerAddress } from "../types/httpServer.type.js";
8
- export declare function createHttpServer(options: {
9
- readonly name?: string;
10
- readonly adapter: unknown;
11
- readonly handler?: unknown;
12
- readonly errorHandler?: unknown;
13
- readonly gracefulShutdownTimeout?: number;
14
- readonly metadata?: Readonly<Record<string, unknown>>;
15
- }): HttpServer;
7
+ import type { HttpServerState, HttpServerAddress, HttpServerOptions } from "../types/httpServer.type.js";
8
+ /**
9
+ * Creates an HTTP server around an adapter.
10
+ *
11
+ * The options are `HttpServerOptions`, so `handler: async (request) => ...`
12
+ * infers `request` as `HttpRequestContext` (it used to be typed `unknown`,
13
+ * which made the quick start fail under `strict` with TS7006), and
14
+ * `errorHandler` receives the thrown error and the request.
15
+ */
16
+ export declare function createHttpServer(options: HttpServerOptions): HttpServer;
16
17
  export declare function startServer(server: HttpServer): Promise<HttpServer>;
17
18
  export declare function stopServer(server: HttpServer): Promise<HttpServer>;
18
19
  export declare function restartServer(server: HttpServer): Promise<HttpServer>;
@@ -5,6 +5,14 @@
5
5
  */
6
6
  import { HttpServerLifecycleError } from "@zudojs/errors";
7
7
  import { HttpServer } from "../core/httpServer.core.js";
8
+ /**
9
+ * Creates an HTTP server around an adapter.
10
+ *
11
+ * The options are `HttpServerOptions`, so `handler: async (request) => ...`
12
+ * infers `request` as `HttpRequestContext` (it used to be typed `unknown`,
13
+ * which made the quick start fail under `strict` with TS7006), and
14
+ * `errorHandler` receives the thrown error and the request.
15
+ */
8
16
  export function createHttpServer(options) {
9
17
  return new HttpServer(options);
10
18
  }
package/dist/index.d.ts CHANGED
@@ -49,6 +49,8 @@ export * from "./httpAgent/index.js";
49
49
  export * from "./httpProxy/index.js";
50
50
  export * from "./httpSecurity/index.js";
51
51
  export * from "./httpRouter/index.js";
52
+ export * from "./httpOpenApi/index.js";
53
+ export * from "./httpFetchMount/index.js";
52
54
  /**
53
55
  * Several sub-modules export the same name. A star export alone would make
54
56
  * those names unavailable (TS2308), so ownership is declared explicitly here.
package/dist/index.js CHANGED
@@ -49,6 +49,8 @@ export * from "./httpAgent/index.js";
49
49
  export * from "./httpProxy/index.js";
50
50
  export * from "./httpSecurity/index.js";
51
51
  export * from "./httpRouter/index.js";
52
+ export * from "./httpOpenApi/index.js";
53
+ export * from "./httpFetchMount/index.js";
52
54
  /* -------------------------------------------------------------------------- */
53
55
  /* Ambiguous re-exports */
54
56
  /* -------------------------------------------------------------------------- */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zudojs/http",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "HTTP primitives, request handling, routing, middleware, and server infrastructure for Zudojs applications.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -24,17 +24,20 @@
24
24
  "!dist/.tsbuildinfo"
25
25
  ],
26
26
  "dependencies": {
27
- "@zudojs/crypto": "1.2.0",
28
- "@zudojs/errors": "1.1.0",
29
- "@zudojs/logger": "1.2.0",
30
- "@zudojs/security": "1.1.0"
27
+ "@zudojs/crypto": "1.3.1",
28
+ "@zudojs/errors": "1.3.0",
29
+ "@zudojs/logger": "1.4.0",
30
+ "@zudojs/middleware": "1.1.0",
31
+ "@zudojs/openapi": "1.5.0",
32
+ "@zudojs/security": "1.3.0"
31
33
  },
32
34
  "devDependencies": {
33
- "@types/node": "^26.4.1",
35
+ "@types/node": "^26.6.2",
36
+ "@zudojs/schema": "1.2.0",
34
37
  "fluent-ffmpeg": "^2.1.3",
35
38
  "sharp": "^0.35.4",
36
39
  "typescript": "7.0.2",
37
- "vitest": "^4.1.11"
40
+ "vitest": "^5.0.1"
38
41
  },
39
42
  "engines": {
40
43
  "node": ">=24.0.0"
@@ -49,7 +52,7 @@
49
52
  "middleware",
50
53
  "routing"
51
54
  ],
52
- "homepage": "https://github.com/oyinlola-tech/zudo#readme",
55
+ "homepage": "https://zudojs.oyinlola.site/docs/packages-http",
53
56
  "bugs": {
54
57
  "url": "https://github.com/oyinlola-tech/zudo/issues"
55
58
  },