qhttpx 1.9.1 → 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +79 -17
  3. package/dist/examples/api-server.d.ts +1 -0
  4. package/dist/examples/api-server.js +77 -0
  5. package/dist/examples/basic.d.ts +1 -0
  6. package/dist/examples/basic.js +10 -0
  7. package/dist/examples/compression.d.ts +1 -0
  8. package/dist/examples/compression.js +17 -0
  9. package/dist/examples/cors.d.ts +1 -0
  10. package/dist/examples/cors.js +19 -0
  11. package/dist/examples/errors.d.ts +1 -0
  12. package/dist/examples/errors.js +25 -0
  13. package/dist/examples/file-upload.d.ts +1 -0
  14. package/dist/examples/file-upload.js +24 -0
  15. package/dist/examples/fusion.d.ts +1 -0
  16. package/dist/examples/fusion.js +21 -0
  17. package/dist/examples/rate-limiting.d.ts +1 -0
  18. package/dist/examples/rate-limiting.js +17 -0
  19. package/dist/examples/validation.d.ts +1 -0
  20. package/dist/examples/validation.js +23 -0
  21. package/dist/examples/websockets.d.ts +1 -0
  22. package/dist/examples/websockets.js +20 -0
  23. package/dist/package.json +112 -0
  24. package/dist/src/benchmarks/compare-frameworks.js +119 -0
  25. package/dist/src/benchmarks/compare.d.ts +1 -0
  26. package/dist/src/benchmarks/compare.js +288 -0
  27. package/dist/src/benchmarks/quantam-users.d.ts +1 -0
  28. package/dist/src/benchmarks/quantam-users.js +56 -0
  29. package/dist/src/benchmarks/simple-json.d.ts +1 -0
  30. package/dist/src/benchmarks/simple-json.js +60 -0
  31. package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
  32. package/dist/src/benchmarks/ultra-mode.js +94 -0
  33. package/dist/src/buffer-pool.js +70 -0
  34. package/dist/src/cli/index.d.ts +2 -0
  35. package/dist/src/cli/index.js +222 -0
  36. package/dist/src/client/index.d.ts +17 -0
  37. package/dist/src/client/index.js +72 -0
  38. package/dist/src/config.js +50 -0
  39. package/dist/src/cookies.js +59 -0
  40. package/dist/src/core/batch.d.ts +24 -0
  41. package/dist/src/core/batch.js +97 -0
  42. package/dist/src/core/body-parser.d.ts +15 -0
  43. package/dist/src/core/body-parser.js +121 -0
  44. package/dist/src/core/buffer-pool.d.ts +41 -0
  45. package/dist/src/core/buffer-pool.js +70 -0
  46. package/dist/src/core/config.d.ts +7 -0
  47. package/dist/src/core/config.js +50 -0
  48. package/dist/src/core/errors.d.ts +34 -0
  49. package/dist/src/core/errors.js +70 -0
  50. package/dist/src/core/fusion.d.ts +20 -0
  51. package/dist/src/core/fusion.js +193 -0
  52. package/dist/src/core/logger.d.ts +22 -0
  53. package/dist/src/core/logger.js +49 -0
  54. package/dist/src/core/metrics.d.ts +48 -0
  55. package/dist/src/core/metrics.js +117 -0
  56. package/dist/src/core/native-adapter.d.ts +11 -0
  57. package/dist/src/core/native-adapter.js +211 -0
  58. package/dist/src/core/resources.d.ts +9 -0
  59. package/dist/src/core/resources.js +25 -0
  60. package/dist/src/core/scheduler.d.ts +34 -0
  61. package/dist/src/core/scheduler.js +85 -0
  62. package/dist/src/core/scope.d.ts +26 -0
  63. package/dist/src/core/scope.js +68 -0
  64. package/dist/src/core/serializer.d.ts +10 -0
  65. package/dist/src/core/serializer.js +44 -0
  66. package/dist/src/core/server.d.ts +138 -0
  67. package/dist/src/core/server.js +1082 -0
  68. package/dist/src/core/stream.d.ts +15 -0
  69. package/dist/src/core/stream.js +71 -0
  70. package/dist/src/core/tasks.d.ts +29 -0
  71. package/dist/src/core/tasks.js +87 -0
  72. package/dist/src/core/types.d.ts +173 -0
  73. package/dist/src/core/types.js +19 -0
  74. package/dist/src/core/websocket.d.ts +25 -0
  75. package/dist/src/core/websocket.js +86 -0
  76. package/dist/src/core/worker-queue.d.ts +41 -0
  77. package/dist/src/core/worker-queue.js +73 -0
  78. package/dist/src/cors.js +66 -0
  79. package/dist/src/database/adapters/memory.d.ts +21 -0
  80. package/dist/src/database/adapters/memory.js +90 -0
  81. package/dist/src/database/adapters/mongo.d.ts +11 -0
  82. package/dist/src/database/adapters/mongo.js +141 -0
  83. package/dist/src/database/adapters/postgres.d.ts +10 -0
  84. package/dist/src/database/adapters/postgres.js +111 -0
  85. package/dist/src/database/adapters/sqlite.d.ts +10 -0
  86. package/dist/src/database/adapters/sqlite.js +42 -0
  87. package/dist/src/database/coalescer.d.ts +14 -0
  88. package/dist/src/database/coalescer.js +134 -0
  89. package/dist/src/database/manager.d.ts +35 -0
  90. package/dist/src/database/manager.js +87 -0
  91. package/dist/src/database/types.d.ts +20 -0
  92. package/dist/src/database/types.js +2 -0
  93. package/dist/src/index.d.ts +50 -0
  94. package/dist/src/index.js +91 -0
  95. package/dist/src/logger.js +45 -0
  96. package/dist/src/metrics.js +111 -0
  97. package/dist/src/middleware/compression.d.ts +2 -0
  98. package/dist/src/middleware/compression.js +133 -0
  99. package/dist/src/middleware/cors.d.ts +2 -0
  100. package/dist/src/middleware/cors.js +66 -0
  101. package/dist/src/middleware/presets.d.ts +16 -0
  102. package/dist/src/middleware/presets.js +52 -0
  103. package/dist/src/middleware/rate-limit.d.ts +14 -0
  104. package/dist/src/middleware/rate-limit.js +83 -0
  105. package/dist/src/middleware/security.d.ts +21 -0
  106. package/dist/src/middleware/security.js +69 -0
  107. package/dist/src/middleware/static.d.ts +11 -0
  108. package/dist/src/middleware/static.js +191 -0
  109. package/dist/src/native/index.d.ts +32 -0
  110. package/dist/src/native/index.js +141 -0
  111. package/dist/src/openapi/generator.d.ts +19 -0
  112. package/dist/src/openapi/generator.js +149 -0
  113. package/dist/src/presets.js +33 -0
  114. package/dist/src/radix-router.js +89 -0
  115. package/dist/src/radix-tree.js +81 -0
  116. package/dist/src/resources.js +25 -0
  117. package/dist/src/router/radix-router.d.ts +18 -0
  118. package/dist/src/router/radix-router.js +89 -0
  119. package/dist/src/router/radix-tree.d.ts +18 -0
  120. package/dist/src/router/radix-tree.js +131 -0
  121. package/dist/src/router/router.d.ts +34 -0
  122. package/dist/src/router/router.js +186 -0
  123. package/dist/src/router.js +138 -0
  124. package/dist/src/scheduler.js +85 -0
  125. package/dist/src/security.js +69 -0
  126. package/dist/src/server.js +685 -0
  127. package/dist/src/signals.js +31 -0
  128. package/dist/src/static.js +107 -0
  129. package/dist/src/stream.js +71 -0
  130. package/dist/src/tasks.js +87 -0
  131. package/dist/src/testing/index.d.ts +25 -0
  132. package/dist/src/testing/index.js +84 -0
  133. package/dist/src/testing.js +40 -0
  134. package/dist/src/types.js +19 -0
  135. package/dist/src/utils/cookies.d.ts +3 -0
  136. package/dist/src/utils/cookies.js +59 -0
  137. package/dist/src/utils/logger.d.ts +12 -0
  138. package/dist/src/utils/logger.js +45 -0
  139. package/dist/src/utils/signals.d.ts +6 -0
  140. package/dist/src/utils/signals.js +31 -0
  141. package/dist/src/utils/sse.d.ts +6 -0
  142. package/dist/src/utils/sse.js +32 -0
  143. package/dist/src/utils/testing.js +40 -0
  144. package/dist/src/validation/index.d.ts +3 -0
  145. package/dist/src/validation/index.js +19 -0
  146. package/dist/src/validation/simple.d.ts +5 -0
  147. package/dist/src/validation/simple.js +102 -0
  148. package/dist/src/validation/types.d.ts +32 -0
  149. package/dist/src/validation/types.js +12 -0
  150. package/dist/src/validation/zod.d.ts +4 -0
  151. package/dist/src/validation/zod.js +18 -0
  152. package/dist/src/views/index.d.ts +1 -0
  153. package/dist/src/views/index.js +17 -0
  154. package/dist/src/views/types.d.ts +3 -0
  155. package/dist/src/views/types.js +2 -0
  156. package/dist/src/worker-queue.js +73 -0
  157. package/dist/tests/adapters.test.d.ts +1 -0
  158. package/dist/tests/adapters.test.js +106 -0
  159. package/dist/tests/batch.test.d.ts +1 -0
  160. package/dist/tests/batch.test.js +117 -0
  161. package/dist/tests/body-parser.test.d.ts +1 -0
  162. package/dist/tests/body-parser.test.js +52 -0
  163. package/dist/tests/compression-sse.test.d.ts +1 -0
  164. package/dist/tests/compression-sse.test.js +87 -0
  165. package/dist/tests/cookies.test.d.ts +1 -0
  166. package/dist/tests/cookies.test.js +63 -0
  167. package/dist/tests/cors.test.d.ts +1 -0
  168. package/dist/tests/cors.test.js +55 -0
  169. package/dist/tests/database.test.d.ts +1 -0
  170. package/dist/tests/database.test.js +80 -0
  171. package/dist/tests/dx.test.d.ts +1 -0
  172. package/dist/tests/dx.test.js +114 -0
  173. package/dist/tests/ecosystem.test.d.ts +1 -0
  174. package/dist/tests/ecosystem.test.js +133 -0
  175. package/dist/tests/features.test.d.ts +1 -0
  176. package/dist/tests/features.test.js +47 -0
  177. package/dist/tests/fusion.test.d.ts +1 -0
  178. package/dist/tests/fusion.test.js +92 -0
  179. package/dist/tests/http-basic.test.d.ts +1 -0
  180. package/dist/tests/http-basic.test.js +124 -0
  181. package/dist/tests/logger.test.d.ts +1 -0
  182. package/dist/tests/logger.test.js +33 -0
  183. package/dist/tests/middleware.test.d.ts +1 -0
  184. package/dist/tests/middleware.test.js +109 -0
  185. package/dist/tests/native-adapter.test.d.ts +1 -0
  186. package/dist/tests/native-adapter.test.js +71 -0
  187. package/dist/tests/observability.test.d.ts +1 -0
  188. package/dist/tests/observability.test.js +59 -0
  189. package/dist/tests/openapi.test.d.ts +1 -0
  190. package/dist/tests/openapi.test.js +64 -0
  191. package/dist/tests/plugin.test.d.ts +1 -0
  192. package/dist/tests/plugin.test.js +65 -0
  193. package/dist/tests/plugins.test.d.ts +1 -0
  194. package/dist/tests/plugins.test.js +71 -0
  195. package/dist/tests/rate-limit.test.d.ts +1 -0
  196. package/dist/tests/rate-limit.test.js +77 -0
  197. package/dist/tests/resources.test.d.ts +1 -0
  198. package/dist/tests/resources.test.js +47 -0
  199. package/dist/tests/scheduler.test.d.ts +1 -0
  200. package/dist/tests/scheduler.test.js +46 -0
  201. package/dist/tests/schema-routes.test.d.ts +1 -0
  202. package/dist/tests/schema-routes.test.js +77 -0
  203. package/dist/tests/security.test.d.ts +1 -0
  204. package/dist/tests/security.test.js +83 -0
  205. package/dist/tests/server-db.test.d.ts +1 -0
  206. package/dist/tests/server-db.test.js +72 -0
  207. package/dist/tests/smoke.test.d.ts +1 -0
  208. package/dist/tests/smoke.test.js +10 -0
  209. package/dist/tests/sqlite-fusion.test.d.ts +1 -0
  210. package/dist/tests/sqlite-fusion.test.js +92 -0
  211. package/dist/tests/static.test.d.ts +1 -0
  212. package/dist/tests/static.test.js +102 -0
  213. package/dist/tests/stream.test.d.ts +1 -0
  214. package/dist/tests/stream.test.js +44 -0
  215. package/dist/tests/task-metrics.test.d.ts +1 -0
  216. package/dist/tests/task-metrics.test.js +53 -0
  217. package/dist/tests/tasks.test.d.ts +1 -0
  218. package/dist/tests/tasks.test.js +62 -0
  219. package/dist/tests/testing.test.d.ts +1 -0
  220. package/dist/tests/testing.test.js +47 -0
  221. package/dist/tests/validation.test.d.ts +1 -0
  222. package/dist/tests/validation.test.js +107 -0
  223. package/dist/tests/websocket.test.d.ts +1 -0
  224. package/dist/tests/websocket.test.js +146 -0
  225. package/dist/vitest.config.d.ts +2 -0
  226. package/dist/vitest.config.js +9 -0
  227. package/docs/FUSION.md +19 -0
  228. package/package.json +4 -15
  229. package/prebuilds/darwin-arm64/qhttpx.node +0 -0
  230. package/prebuilds/linux-x64/qhttpx.node +0 -0
  231. package/prebuilds/win32-x64/qhttpx.node +0 -0
  232. package/scripts/install-native.js +26 -0
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Router = void 0;
4
+ const radix_tree_1 = require("./radix-tree");
5
+ const types_1 = require("../core/types");
6
+ class Router {
7
+ constructor() {
8
+ // Per-method route buckets
9
+ this.methodBuckets = new Map([
10
+ ['GET', []],
11
+ ['POST', []],
12
+ ['PUT', []],
13
+ ['DELETE', []],
14
+ ['PATCH', []],
15
+ ['HEAD', []],
16
+ ['OPTIONS', []],
17
+ ]);
18
+ // Derived structures (built at freeze time)
19
+ this.radixTrees = new Map();
20
+ // Fast lookup for static routes (no params)
21
+ this.staticRoutes = new Map([
22
+ ['GET', new Map()],
23
+ ['POST', new Map()],
24
+ ['PUT', new Map()],
25
+ ['DELETE', new Map()],
26
+ ['PATCH', new Map()],
27
+ ['HEAD', new Map()],
28
+ ['OPTIONS', new Map()],
29
+ ]);
30
+ // Freeze state
31
+ this.isFrozen = false;
32
+ }
33
+ register(method, path, handler, options) {
34
+ if (this.isFrozen) {
35
+ console.warn(`Router is frozen. Late route registration (${method} ${path}) may not be optimized.`);
36
+ }
37
+ const segments = this.normalize(path);
38
+ const bucket = this.methodBuckets.get(method);
39
+ if (bucket) {
40
+ bucket.push({
41
+ path,
42
+ segments,
43
+ handler,
44
+ priority: options?.priority ?? types_1.RoutePriority.STANDARD,
45
+ schema: options?.schema,
46
+ });
47
+ }
48
+ }
49
+ getRoutes() {
50
+ return this.methodBuckets;
51
+ }
52
+ match(method, path) {
53
+ // Fast path for frozen router
54
+ if (this.isFrozen) {
55
+ // 1. Try static match (O(1))
56
+ const staticMap = this.staticRoutes.get(method);
57
+ if (staticMap) {
58
+ const match = staticMap.get(path);
59
+ if (match) {
60
+ return match;
61
+ }
62
+ }
63
+ // 2. Try radix tree match
64
+ const tree = this.radixTrees.get(method);
65
+ if (tree) {
66
+ const match = tree.lookupPath(path);
67
+ if (match) {
68
+ return match;
69
+ }
70
+ }
71
+ return undefined;
72
+ }
73
+ // Slow path for unfrozen router (legacy behavior)
74
+ const segments = this.normalize(path);
75
+ const bucket = this.methodBuckets.get(method);
76
+ if (!bucket || bucket.length === 0) {
77
+ return undefined;
78
+ }
79
+ // Try to match against routes
80
+ for (const route of bucket) {
81
+ if (route.segments.length !== segments.length) {
82
+ continue;
83
+ }
84
+ const params = {};
85
+ let matched = true;
86
+ for (let i = 0; i < route.segments.length; i += 1) {
87
+ const pattern = route.segments[i];
88
+ const value = segments[i];
89
+ if (pattern.startsWith(':')) {
90
+ const key = pattern.slice(1);
91
+ params[key] = value;
92
+ }
93
+ else if (pattern !== value) {
94
+ matched = false;
95
+ break;
96
+ }
97
+ }
98
+ if (matched) {
99
+ return {
100
+ handler: route.handler,
101
+ params,
102
+ priority: route.priority,
103
+ };
104
+ }
105
+ }
106
+ return undefined;
107
+ }
108
+ getAllowedMethods(path) {
109
+ const segments = this.normalize(path);
110
+ const methods = [];
111
+ for (const [method, routes] of this.methodBuckets.entries()) {
112
+ for (const route of routes) {
113
+ if (route.segments.length !== segments.length) {
114
+ continue;
115
+ }
116
+ let matched = true;
117
+ for (let i = 0; i < route.segments.length; i += 1) {
118
+ const pattern = route.segments[i];
119
+ const value = segments[i];
120
+ if (pattern.startsWith(':')) {
121
+ continue;
122
+ }
123
+ if (pattern !== value) {
124
+ matched = false;
125
+ break;
126
+ }
127
+ }
128
+ if (matched) {
129
+ methods.push(method);
130
+ break;
131
+ }
132
+ }
133
+ }
134
+ return methods;
135
+ }
136
+ /**
137
+ * Freeze the router after server starts.
138
+ * Prevents further route registration and builds derived structures for optimized matching.
139
+ */
140
+ freeze() {
141
+ if (this.isFrozen) {
142
+ return;
143
+ }
144
+ this.isFrozen = true;
145
+ // Build derived structures for faster matching
146
+ for (const [method, routes] of this.methodBuckets.entries()) {
147
+ const tree = new radix_tree_1.RadixTree();
148
+ const staticMap = this.staticRoutes.get(method);
149
+ for (const route of routes) {
150
+ tree.insert(route.segments, route.handler, route.priority);
151
+ // Check if route is static (no params)
152
+ let isStatic = true;
153
+ for (const segment of route.segments) {
154
+ if (segment.startsWith(':')) {
155
+ isStatic = false;
156
+ break;
157
+ }
158
+ }
159
+ if (isStatic) {
160
+ // Optimization: Pre-allocate static match result
161
+ // Note: we use route.path directly as key
162
+ staticMap.set(route.path, {
163
+ handler: route.handler,
164
+ params: {},
165
+ priority: route.priority,
166
+ });
167
+ // Also handle trailing slash or no trailing slash?
168
+ // For strict matching, we use exact path.
169
+ // QHTTPX seems to normalize, so /json/ and /json might be different?
170
+ // route.path comes from user.
171
+ }
172
+ }
173
+ this.radixTrees.set(method, tree);
174
+ }
175
+ }
176
+ isFrozenRouter() {
177
+ return this.isFrozen;
178
+ }
179
+ normalize(path) {
180
+ if (!path || path === '/') {
181
+ return [];
182
+ }
183
+ return path.split('/').filter((segment) => segment.length > 0);
184
+ }
185
+ }
186
+ exports.Router = Router;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Router = void 0;
4
+ const radix_tree_1 = require("./radix-tree");
5
+ const types_1 = require("./types");
6
+ class Router {
7
+ constructor() {
8
+ // Per-method route buckets
9
+ this.methodBuckets = new Map([
10
+ ['GET', []],
11
+ ['POST', []],
12
+ ['PUT', []],
13
+ ['DELETE', []],
14
+ ]);
15
+ // Derived structures (built at freeze time)
16
+ this.radixTrees = new Map();
17
+ // Freeze state
18
+ this.isFrozen = false;
19
+ }
20
+ register(method, path, handler, options) {
21
+ if (this.isFrozen) {
22
+ console.warn(`Router is frozen. Late route registration (${method} ${path}) may not be optimized.`);
23
+ }
24
+ const segments = this.normalize(path);
25
+ const bucket = this.methodBuckets.get(method);
26
+ if (bucket) {
27
+ bucket.push({
28
+ segments,
29
+ handler,
30
+ priority: options?.priority ?? types_1.RoutePriority.STANDARD,
31
+ });
32
+ }
33
+ }
34
+ match(method, path) {
35
+ // Fast path for frozen router
36
+ if (this.isFrozen) {
37
+ const tree = this.radixTrees.get(method);
38
+ if (tree) {
39
+ const segments = this.normalize(path);
40
+ const match = tree.lookup(segments);
41
+ if (match) {
42
+ return match;
43
+ }
44
+ }
45
+ return undefined;
46
+ }
47
+ // Slow path for unfrozen router (legacy behavior)
48
+ const segments = this.normalize(path);
49
+ const bucket = this.methodBuckets.get(method);
50
+ if (!bucket || bucket.length === 0) {
51
+ return undefined;
52
+ }
53
+ // Try to match against routes
54
+ for (const route of bucket) {
55
+ if (route.segments.length !== segments.length) {
56
+ continue;
57
+ }
58
+ const params = {};
59
+ let matched = true;
60
+ for (let i = 0; i < route.segments.length; i += 1) {
61
+ const pattern = route.segments[i];
62
+ const value = segments[i];
63
+ if (pattern.startsWith(':')) {
64
+ const key = pattern.slice(1);
65
+ params[key] = value;
66
+ }
67
+ else if (pattern !== value) {
68
+ matched = false;
69
+ break;
70
+ }
71
+ }
72
+ if (matched) {
73
+ return {
74
+ handler: route.handler,
75
+ params,
76
+ priority: route.priority,
77
+ };
78
+ }
79
+ }
80
+ return undefined;
81
+ }
82
+ getAllowedMethods(path) {
83
+ const segments = this.normalize(path);
84
+ const methods = [];
85
+ for (const [method, routes] of this.methodBuckets.entries()) {
86
+ for (const route of routes) {
87
+ if (route.segments.length !== segments.length) {
88
+ continue;
89
+ }
90
+ let matched = true;
91
+ for (let i = 0; i < route.segments.length; i += 1) {
92
+ const pattern = route.segments[i];
93
+ const value = segments[i];
94
+ if (pattern.startsWith(':')) {
95
+ continue;
96
+ }
97
+ if (pattern !== value) {
98
+ matched = false;
99
+ break;
100
+ }
101
+ }
102
+ if (matched) {
103
+ methods.push(method);
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ return methods;
109
+ }
110
+ /**
111
+ * Freeze the router after server starts.
112
+ * Prevents further route registration and builds derived structures for optimized matching.
113
+ */
114
+ freeze() {
115
+ if (this.isFrozen) {
116
+ return;
117
+ }
118
+ this.isFrozen = true;
119
+ // Build derived structures for faster matching
120
+ for (const [method, routes] of this.methodBuckets.entries()) {
121
+ const tree = new radix_tree_1.RadixTree();
122
+ for (const route of routes) {
123
+ tree.insert(route.segments, route.handler, route.priority);
124
+ }
125
+ this.radixTrees.set(method, tree);
126
+ }
127
+ }
128
+ isFrozenRouter() {
129
+ return this.isFrozen;
130
+ }
131
+ normalize(path) {
132
+ if (!path || path === '/') {
133
+ return [];
134
+ }
135
+ return path.split('/').filter((segment) => segment.length > 0);
136
+ }
137
+ }
138
+ exports.Router = Router;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Scheduler = void 0;
4
+ const worker_queue_1 = require("./worker-queue");
5
+ const types_1 = require("./types");
6
+ class Scheduler {
7
+ constructor(options = {}) {
8
+ this.inFlight = 0;
9
+ this.nextWorkerIndex = 0;
10
+ const max = options.maxConcurrency ?? Infinity;
11
+ this.maxConcurrency = max > 0 ? max : Infinity;
12
+ // Initialize per-worker queues
13
+ this.workerCount = options.workers ?? 1;
14
+ this.perWorkerQueues = [];
15
+ for (let i = 0; i < this.workerCount; i += 1) {
16
+ this.perWorkerQueues.push(new worker_queue_1.WorkerQueue(1024));
17
+ }
18
+ }
19
+ getCurrentInFlight() {
20
+ return this.inFlight;
21
+ }
22
+ /**
23
+ * Get scheduler statistics (queued tasks per worker, etc.)
24
+ */
25
+ getStats() {
26
+ return {
27
+ inFlight: this.inFlight,
28
+ maxConcurrency: this.maxConcurrency,
29
+ workers: this.workerCount,
30
+ perWorkerStats: this.perWorkerQueues.map((q, i) => ({
31
+ workerId: i,
32
+ queued: q.getSize(),
33
+ })),
34
+ };
35
+ }
36
+ async run(task, options) {
37
+ const priority = options.priority ?? types_1.RoutePriority.STANDARD;
38
+ let threshold = this.maxConcurrency;
39
+ if (priority === types_1.RoutePriority.BEST_EFFORT) {
40
+ // Shed best-effort requests if we are above 80% capacity
41
+ threshold = Math.max(1, Math.floor(this.maxConcurrency * 0.8));
42
+ }
43
+ else if (priority === types_1.RoutePriority.STANDARD) {
44
+ // Shed standard requests if we are above 95% capacity
45
+ threshold = Math.max(1, Math.floor(this.maxConcurrency * 0.95));
46
+ }
47
+ // CRITICAL allows up to 100%
48
+ if (this.inFlight >= threshold) {
49
+ if (options.onOverloaded) {
50
+ options.onOverloaded();
51
+ }
52
+ return;
53
+ }
54
+ this.inFlight += 1;
55
+ let timeoutId;
56
+ try {
57
+ if (!options.timeoutMs || options.timeoutMs <= 0) {
58
+ const result = task();
59
+ if (result && typeof result.then === 'function') {
60
+ await result;
61
+ }
62
+ return;
63
+ }
64
+ const taskPromise = Promise.resolve(task()).then(() => 'ok');
65
+ const timeoutPromise = new Promise((resolve) => {
66
+ timeoutId = setTimeout(() => {
67
+ resolve('timeout');
68
+ }, options.timeoutMs);
69
+ });
70
+ const result = await Promise.race([taskPromise, timeoutPromise]);
71
+ if (result === 'timeout') {
72
+ if (options.onTimeout) {
73
+ options.onTimeout();
74
+ }
75
+ }
76
+ }
77
+ finally {
78
+ if (timeoutId) {
79
+ clearTimeout(timeoutId);
80
+ }
81
+ this.inFlight -= 1;
82
+ }
83
+ }
84
+ }
85
+ exports.Scheduler = Scheduler;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSecurityHeadersMiddleware = createSecurityHeadersMiddleware;
4
+ exports.createSecureDefaults = createSecureDefaults;
5
+ exports.createRateLimitMiddleware = createRateLimitMiddleware;
6
+ const cors_1 = require("./cors");
7
+ function createSecurityHeadersMiddleware(options = {}) {
8
+ const { contentSecurityPolicy = "default-src 'self'", referrerPolicy = 'no-referrer', xFrameOptions = 'SAMEORIGIN', xContentTypeOptions = 'nosniff', xXssProtection = '1; mode=block', strictTransportSecurity, } = options;
9
+ return async (ctx, next) => {
10
+ if (contentSecurityPolicy) {
11
+ ctx.res.setHeader('content-security-policy', contentSecurityPolicy);
12
+ }
13
+ if (referrerPolicy) {
14
+ ctx.res.setHeader('referrer-policy', referrerPolicy);
15
+ }
16
+ if (xFrameOptions) {
17
+ ctx.res.setHeader('x-frame-options', xFrameOptions);
18
+ }
19
+ if (xContentTypeOptions) {
20
+ ctx.res.setHeader('x-content-type-options', xContentTypeOptions);
21
+ }
22
+ if (xXssProtection) {
23
+ ctx.res.setHeader('x-xss-protection', xXssProtection);
24
+ }
25
+ if (strictTransportSecurity) {
26
+ ctx.res.setHeader('strict-transport-security', strictTransportSecurity);
27
+ }
28
+ await next();
29
+ };
30
+ }
31
+ function createSecureDefaults(options = {}) {
32
+ const middlewares = [];
33
+ middlewares.push((0, cors_1.createCorsMiddleware)(options.cors));
34
+ middlewares.push(createSecurityHeadersMiddleware(options.securityHeaders));
35
+ return middlewares;
36
+ }
37
+ function createRateLimitMiddleware(options) {
38
+ const maxRequests = options.maxRequests;
39
+ const windowMs = options.windowMs;
40
+ const keyGenerator = options.keyGenerator ??
41
+ ((ctx) => {
42
+ const addr = ctx.req.socket.remoteAddress;
43
+ return addr || 'anonymous';
44
+ });
45
+ const buckets = new Map();
46
+ return async (ctx, next) => {
47
+ const now = Date.now();
48
+ const key = keyGenerator(ctx);
49
+ const existing = buckets.get(key);
50
+ let bucket = existing;
51
+ if (!bucket || bucket.resetAt <= now) {
52
+ bucket = {
53
+ count: 0,
54
+ resetAt: now + windowMs,
55
+ };
56
+ buckets.set(key, bucket);
57
+ }
58
+ bucket.count += 1;
59
+ if (bucket.count > maxRequests) {
60
+ if (!ctx.res.headersSent) {
61
+ ctx.res.statusCode = 429;
62
+ ctx.res.setHeader('content-type', 'text/plain; charset=utf-8');
63
+ }
64
+ ctx.res.end('Too Many Requests');
65
+ return;
66
+ }
67
+ await next();
68
+ };
69
+ }