qhttpx 1.8.12 → 1.9.1

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 (206) hide show
  1. package/package.json +26 -4
  2. package/prebuilds/darwin-arm64/qhttpx.node +0 -0
  3. package/prebuilds/linux-x64/qhttpx.node +0 -0
  4. package/prebuilds/win32-x64/qhttpx.node +0 -0
  5. package/src/native/index.ts +104 -24
  6. package/src/native/picohttpparser.h +5 -0
  7. package/src/native/server.cc +2 -0
  8. package/dist/examples/api-server.d.ts +0 -1
  9. package/dist/examples/api-server.js +0 -77
  10. package/dist/examples/basic.d.ts +0 -1
  11. package/dist/examples/basic.js +0 -10
  12. package/dist/examples/compression.d.ts +0 -1
  13. package/dist/examples/compression.js +0 -17
  14. package/dist/examples/cors.d.ts +0 -1
  15. package/dist/examples/cors.js +0 -19
  16. package/dist/examples/errors.d.ts +0 -1
  17. package/dist/examples/errors.js +0 -25
  18. package/dist/examples/file-upload.d.ts +0 -1
  19. package/dist/examples/file-upload.js +0 -24
  20. package/dist/examples/fusion.d.ts +0 -1
  21. package/dist/examples/fusion.js +0 -21
  22. package/dist/examples/rate-limiting.d.ts +0 -1
  23. package/dist/examples/rate-limiting.js +0 -17
  24. package/dist/examples/validation.d.ts +0 -1
  25. package/dist/examples/validation.js +0 -23
  26. package/dist/examples/websockets.d.ts +0 -1
  27. package/dist/examples/websockets.js +0 -20
  28. package/dist/package.json +0 -101
  29. package/dist/src/benchmarks/quantam-users.d.ts +0 -1
  30. package/dist/src/benchmarks/quantam-users.js +0 -56
  31. package/dist/src/benchmarks/simple-json.d.ts +0 -1
  32. package/dist/src/benchmarks/simple-json.js +0 -60
  33. package/dist/src/benchmarks/ultra-mode.d.ts +0 -1
  34. package/dist/src/benchmarks/ultra-mode.js +0 -94
  35. package/dist/src/cli/index.d.ts +0 -2
  36. package/dist/src/cli/index.js +0 -222
  37. package/dist/src/client/index.d.ts +0 -17
  38. package/dist/src/client/index.js +0 -72
  39. package/dist/src/core/batch.d.ts +0 -24
  40. package/dist/src/core/batch.js +0 -97
  41. package/dist/src/core/body-parser.d.ts +0 -15
  42. package/dist/src/core/body-parser.js +0 -121
  43. package/dist/src/core/buffer-pool.d.ts +0 -41
  44. package/dist/src/core/buffer-pool.js +0 -70
  45. package/dist/src/core/config.d.ts +0 -7
  46. package/dist/src/core/config.js +0 -50
  47. package/dist/src/core/errors.d.ts +0 -34
  48. package/dist/src/core/errors.js +0 -70
  49. package/dist/src/core/fusion.d.ts +0 -14
  50. package/dist/src/core/fusion.js +0 -183
  51. package/dist/src/core/logger.d.ts +0 -22
  52. package/dist/src/core/logger.js +0 -49
  53. package/dist/src/core/metrics.d.ts +0 -45
  54. package/dist/src/core/metrics.js +0 -111
  55. package/dist/src/core/native-adapter.d.ts +0 -11
  56. package/dist/src/core/native-adapter.js +0 -211
  57. package/dist/src/core/resources.d.ts +0 -9
  58. package/dist/src/core/resources.js +0 -25
  59. package/dist/src/core/scheduler.d.ts +0 -34
  60. package/dist/src/core/scheduler.js +0 -85
  61. package/dist/src/core/scope.d.ts +0 -26
  62. package/dist/src/core/scope.js +0 -68
  63. package/dist/src/core/serializer.d.ts +0 -10
  64. package/dist/src/core/serializer.js +0 -44
  65. package/dist/src/core/server.d.ts +0 -138
  66. package/dist/src/core/server.js +0 -1082
  67. package/dist/src/core/stream.d.ts +0 -15
  68. package/dist/src/core/stream.js +0 -71
  69. package/dist/src/core/tasks.d.ts +0 -29
  70. package/dist/src/core/tasks.js +0 -87
  71. package/dist/src/core/types.d.ts +0 -173
  72. package/dist/src/core/types.js +0 -19
  73. package/dist/src/core/websocket.d.ts +0 -25
  74. package/dist/src/core/websocket.js +0 -86
  75. package/dist/src/core/worker-queue.d.ts +0 -41
  76. package/dist/src/core/worker-queue.js +0 -73
  77. package/dist/src/database/adapters/memory.d.ts +0 -21
  78. package/dist/src/database/adapters/memory.js +0 -90
  79. package/dist/src/database/adapters/mongo.d.ts +0 -11
  80. package/dist/src/database/adapters/mongo.js +0 -141
  81. package/dist/src/database/adapters/postgres.d.ts +0 -10
  82. package/dist/src/database/adapters/postgres.js +0 -111
  83. package/dist/src/database/adapters/sqlite.d.ts +0 -10
  84. package/dist/src/database/adapters/sqlite.js +0 -42
  85. package/dist/src/database/coalescer.d.ts +0 -14
  86. package/dist/src/database/coalescer.js +0 -134
  87. package/dist/src/database/manager.d.ts +0 -35
  88. package/dist/src/database/manager.js +0 -87
  89. package/dist/src/database/types.d.ts +0 -20
  90. package/dist/src/database/types.js +0 -2
  91. package/dist/src/index.d.ts +0 -50
  92. package/dist/src/index.js +0 -91
  93. package/dist/src/middleware/compression.d.ts +0 -2
  94. package/dist/src/middleware/compression.js +0 -133
  95. package/dist/src/middleware/cors.d.ts +0 -2
  96. package/dist/src/middleware/cors.js +0 -66
  97. package/dist/src/middleware/presets.d.ts +0 -16
  98. package/dist/src/middleware/presets.js +0 -52
  99. package/dist/src/middleware/rate-limit.d.ts +0 -14
  100. package/dist/src/middleware/rate-limit.js +0 -83
  101. package/dist/src/middleware/security.d.ts +0 -21
  102. package/dist/src/middleware/security.js +0 -69
  103. package/dist/src/middleware/static.d.ts +0 -11
  104. package/dist/src/middleware/static.js +0 -191
  105. package/dist/src/native/index.d.ts +0 -29
  106. package/dist/src/native/index.js +0 -64
  107. package/dist/src/openapi/generator.d.ts +0 -19
  108. package/dist/src/openapi/generator.js +0 -149
  109. package/dist/src/router/radix-router.d.ts +0 -18
  110. package/dist/src/router/radix-router.js +0 -89
  111. package/dist/src/router/radix-tree.d.ts +0 -18
  112. package/dist/src/router/radix-tree.js +0 -131
  113. package/dist/src/router/router.d.ts +0 -34
  114. package/dist/src/router/router.js +0 -186
  115. package/dist/src/testing/index.d.ts +0 -25
  116. package/dist/src/testing/index.js +0 -84
  117. package/dist/src/utils/cookies.d.ts +0 -3
  118. package/dist/src/utils/cookies.js +0 -59
  119. package/dist/src/utils/logger.d.ts +0 -12
  120. package/dist/src/utils/logger.js +0 -45
  121. package/dist/src/utils/signals.d.ts +0 -6
  122. package/dist/src/utils/signals.js +0 -31
  123. package/dist/src/utils/sse.d.ts +0 -6
  124. package/dist/src/utils/sse.js +0 -32
  125. package/dist/src/validation/index.d.ts +0 -3
  126. package/dist/src/validation/index.js +0 -19
  127. package/dist/src/validation/simple.d.ts +0 -5
  128. package/dist/src/validation/simple.js +0 -102
  129. package/dist/src/validation/types.d.ts +0 -32
  130. package/dist/src/validation/types.js +0 -12
  131. package/dist/src/validation/zod.d.ts +0 -4
  132. package/dist/src/validation/zod.js +0 -18
  133. package/dist/src/views/index.d.ts +0 -1
  134. package/dist/src/views/index.js +0 -17
  135. package/dist/src/views/types.d.ts +0 -3
  136. package/dist/src/views/types.js +0 -2
  137. package/dist/tests/adapters.test.d.ts +0 -1
  138. package/dist/tests/adapters.test.js +0 -106
  139. package/dist/tests/batch.test.d.ts +0 -1
  140. package/dist/tests/batch.test.js +0 -117
  141. package/dist/tests/body-parser.test.d.ts +0 -1
  142. package/dist/tests/body-parser.test.js +0 -52
  143. package/dist/tests/compression-sse.test.d.ts +0 -1
  144. package/dist/tests/compression-sse.test.js +0 -87
  145. package/dist/tests/cookies.test.d.ts +0 -1
  146. package/dist/tests/cookies.test.js +0 -63
  147. package/dist/tests/cors.test.d.ts +0 -1
  148. package/dist/tests/cors.test.js +0 -55
  149. package/dist/tests/database.test.d.ts +0 -1
  150. package/dist/tests/database.test.js +0 -80
  151. package/dist/tests/dx.test.d.ts +0 -1
  152. package/dist/tests/dx.test.js +0 -114
  153. package/dist/tests/ecosystem.test.d.ts +0 -1
  154. package/dist/tests/ecosystem.test.js +0 -133
  155. package/dist/tests/features.test.d.ts +0 -1
  156. package/dist/tests/features.test.js +0 -47
  157. package/dist/tests/fusion.test.d.ts +0 -1
  158. package/dist/tests/fusion.test.js +0 -92
  159. package/dist/tests/http-basic.test.d.ts +0 -1
  160. package/dist/tests/http-basic.test.js +0 -124
  161. package/dist/tests/logger.test.d.ts +0 -1
  162. package/dist/tests/logger.test.js +0 -33
  163. package/dist/tests/middleware.test.d.ts +0 -1
  164. package/dist/tests/middleware.test.js +0 -109
  165. package/dist/tests/native-adapter.test.d.ts +0 -1
  166. package/dist/tests/native-adapter.test.js +0 -71
  167. package/dist/tests/observability.test.d.ts +0 -1
  168. package/dist/tests/observability.test.js +0 -59
  169. package/dist/tests/openapi.test.d.ts +0 -1
  170. package/dist/tests/openapi.test.js +0 -64
  171. package/dist/tests/plugin.test.d.ts +0 -1
  172. package/dist/tests/plugin.test.js +0 -65
  173. package/dist/tests/plugins.test.d.ts +0 -1
  174. package/dist/tests/plugins.test.js +0 -71
  175. package/dist/tests/rate-limit.test.d.ts +0 -1
  176. package/dist/tests/rate-limit.test.js +0 -77
  177. package/dist/tests/resources.test.d.ts +0 -1
  178. package/dist/tests/resources.test.js +0 -47
  179. package/dist/tests/scheduler.test.d.ts +0 -1
  180. package/dist/tests/scheduler.test.js +0 -46
  181. package/dist/tests/schema-routes.test.d.ts +0 -1
  182. package/dist/tests/schema-routes.test.js +0 -77
  183. package/dist/tests/security.test.d.ts +0 -1
  184. package/dist/tests/security.test.js +0 -83
  185. package/dist/tests/server-db.test.d.ts +0 -1
  186. package/dist/tests/server-db.test.js +0 -72
  187. package/dist/tests/smoke.test.d.ts +0 -1
  188. package/dist/tests/smoke.test.js +0 -10
  189. package/dist/tests/sqlite-fusion.test.d.ts +0 -1
  190. package/dist/tests/sqlite-fusion.test.js +0 -92
  191. package/dist/tests/static.test.d.ts +0 -1
  192. package/dist/tests/static.test.js +0 -102
  193. package/dist/tests/stream.test.d.ts +0 -1
  194. package/dist/tests/stream.test.js +0 -44
  195. package/dist/tests/task-metrics.test.d.ts +0 -1
  196. package/dist/tests/task-metrics.test.js +0 -53
  197. package/dist/tests/tasks.test.d.ts +0 -1
  198. package/dist/tests/tasks.test.js +0 -62
  199. package/dist/tests/testing.test.d.ts +0 -1
  200. package/dist/tests/testing.test.js +0 -47
  201. package/dist/tests/validation.test.d.ts +0 -1
  202. package/dist/tests/validation.test.js +0 -107
  203. package/dist/tests/websocket.test.d.ts +0 -1
  204. package/dist/tests/websocket.test.js +0 -146
  205. package/dist/vitest.config.d.ts +0 -2
  206. package/dist/vitest.config.js +0 -9
@@ -1,183 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RequestFusion = void 0;
4
- const crypto_1 = require("crypto");
5
- class RequestFusion {
6
- constructor(options = {}) {
7
- this.inflight = new Map();
8
- this.cache = new Map();
9
- this.options = typeof options === 'boolean' ? {} : options;
10
- if (!this.options.vary) {
11
- this.options.vary = ['authorization', 'cookie'];
12
- }
13
- }
14
- async coalesce(ctx, next) {
15
- const key = this.getKey(ctx);
16
- // 1. Check Cache (Short-lived "Micro-TTL")
17
- if (this.options.windowMs && this.options.windowMs > 0) {
18
- const cached = this.cache.get(key);
19
- if (cached) {
20
- if (Date.now() < cached.expires) {
21
- this.applyResult(ctx, cached.result);
22
- return;
23
- }
24
- else {
25
- this.cache.delete(key);
26
- }
27
- }
28
- }
29
- // 2. Check In-Flight
30
- if (this.inflight.has(key)) {
31
- try {
32
- const result = await this.inflight.get(key);
33
- this.applyResult(ctx, result);
34
- return;
35
- }
36
- catch (err) {
37
- // If leader failed, we should probably fail too or retry?
38
- // For now, let's propagate the error.
39
- throw err;
40
- }
41
- }
42
- // 3. Be the Leader
43
- let resolve;
44
- let reject;
45
- const promise = new Promise((res, rej) => {
46
- resolve = res;
47
- reject = rej;
48
- });
49
- this.inflight.set(key, promise);
50
- // Hijack context methods to capture result
51
- // Cast to any to allow overwriting readonly methods
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- const mutableCtx = ctx;
54
- const originalJson = mutableCtx.json;
55
- const originalSend = mutableCtx.send;
56
- const originalHtml = mutableCtx.html;
57
- const originalRedirect = mutableCtx.redirect;
58
- let captured = false;
59
- // Helper to cleanup and resolve
60
- const finish = (result) => {
61
- if (captured)
62
- return;
63
- captured = true;
64
- resolve(result);
65
- // Cache if needed
66
- if (this.options.windowMs && this.options.windowMs > 0) {
67
- this.cache.set(key, {
68
- result,
69
- expires: Date.now() + this.options.windowMs
70
- });
71
- // Cleanup cache later
72
- setTimeout(() => this.cache.delete(key), this.options.windowMs);
73
- }
74
- // Remove from inflight immediately (unless we rely on cache for window)
75
- // Actually, if we have windowMs, we rely on cache.
76
- // If windowMs=0, we remove immediately.
77
- this.inflight.delete(key);
78
- };
79
- mutableCtx.json = (payload, status = 200) => {
80
- finish({ type: 'json', payload, status });
81
- return originalJson.call(ctx, payload, status);
82
- };
83
- mutableCtx.send = (payload, status = 200) => {
84
- finish({ type: 'send', payload, status });
85
- return originalSend.call(ctx, payload, status);
86
- };
87
- mutableCtx.html = (payload, status = 200) => {
88
- finish({ type: 'html', payload, status });
89
- return originalHtml.call(ctx, payload, status);
90
- };
91
- mutableCtx.redirect = (url, status = 302) => {
92
- finish({ type: 'redirect', payload: url, status });
93
- return originalRedirect.call(ctx, url, status);
94
- };
95
- try {
96
- await next(ctx);
97
- // If next() completes but no response method was called,
98
- // it implies the handler might be async and forgot to await,
99
- // or it's a 404/middleware issue.
100
- // If not captured yet, we can't resolve the followers properly.
101
- // They will hang unless we reject or resolve with something.
102
- // However, typical QHTTPX usage implies ctx.json/send is called.
103
- }
104
- catch (err) {
105
- reject(err);
106
- this.inflight.delete(key);
107
- // Restore methods
108
- mutableCtx.json = originalJson;
109
- mutableCtx.send = originalSend;
110
- mutableCtx.html = originalHtml;
111
- mutableCtx.redirect = originalRedirect;
112
- throw err;
113
- }
114
- }
115
- applyResult(ctx, result) {
116
- switch (result.type) {
117
- case 'json':
118
- ctx.json(result.payload, result.status);
119
- break;
120
- case 'send': {
121
- if (typeof result.payload === 'string' || Buffer.isBuffer(result.payload)) {
122
- ctx.send(result.payload, result.status);
123
- }
124
- else {
125
- ctx.send(String(result.payload), result.status);
126
- }
127
- break;
128
- }
129
- case 'html': {
130
- if (typeof result.payload === 'string') {
131
- ctx.html(result.payload, result.status);
132
- }
133
- else {
134
- ctx.html(String(result.payload), result.status);
135
- }
136
- break;
137
- }
138
- case 'redirect': {
139
- if (typeof result.payload === 'string') {
140
- ctx.redirect(result.payload, result.status);
141
- }
142
- else {
143
- ctx.redirect(String(result.payload), result.status);
144
- }
145
- break;
146
- }
147
- }
148
- }
149
- getKey(ctx) {
150
- // Base: Method + Path
151
- let data = `${ctx.req.method}|${ctx.url?.pathname}|`;
152
- // Query
153
- if (ctx.query) {
154
- // Deterministic sort
155
- const keys = Object.keys(ctx.query).sort();
156
- for (const k of keys) {
157
- data += `${k}=${ctx.query[k]}|`;
158
- }
159
- }
160
- // Body (if parsed)
161
- if (ctx.body) {
162
- // We assume body is simple JSON.
163
- // For objects, order matters for hashing.
164
- // fast-json-stringify or JSON.stringify isn't always deterministic key order.
165
- // But for performance, JSON.stringify is often "good enough" if keys aren't shuffled.
166
- // For strict correctness, we should use a canonical stringify, but that's slow.
167
- // Let's use JSON.stringify for now.
168
- data += JSON.stringify(ctx.body);
169
- }
170
- // Vary Headers
171
- if (this.options.vary) {
172
- for (const header of this.options.vary) {
173
- const val = ctx.req.headers[header.toLowerCase()];
174
- if (val) {
175
- data += `|${header}:${val}`;
176
- }
177
- }
178
- }
179
- // We hash it to keep the key size manageable
180
- return (0, crypto_1.createHash)('sha1').update(data).digest('hex');
181
- }
182
- }
183
- exports.RequestFusion = RequestFusion;
@@ -1,22 +0,0 @@
1
- import pino from 'pino';
2
- export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
3
- export interface LoggerOptions {
4
- level?: LogLevel;
5
- pretty?: boolean;
6
- name?: string;
7
- }
8
- export declare class Logger {
9
- private pino;
10
- constructor(options?: LoggerOptions);
11
- info(msg: string, ...args: any[]): void;
12
- info(obj: object, msg?: string, ...args: any[]): void;
13
- error(msg: string, ...args: any[]): void;
14
- error(obj: object, msg?: string, ...args: any[]): void;
15
- warn(msg: string, ...args: any[]): void;
16
- warn(obj: object, msg?: string, ...args: any[]): void;
17
- debug(msg: string, ...args: any[]): void;
18
- debug(obj: object, msg?: string, ...args: any[]): void;
19
- fatal(msg: string, ...args: any[]): void;
20
- fatal(obj: object, msg?: string, ...args: any[]): void;
21
- child(bindings: pino.Bindings): Logger;
22
- }
@@ -1,49 +0,0 @@
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.Logger = void 0;
7
- /* eslint-disable @typescript-eslint/no-explicit-any */
8
- const pino_1 = __importDefault(require("pino"));
9
- class Logger {
10
- constructor(options = {}) {
11
- const isDev = process.env.NODE_ENV !== 'production';
12
- const pretty = options.pretty ?? isDev;
13
- this.pino = (0, pino_1.default)({
14
- name: options.name || 'qhttpx',
15
- level: options.level || 'info',
16
- transport: pretty
17
- ? {
18
- target: 'pino-pretty',
19
- options: {
20
- colorize: true,
21
- translateTime: 'HH:MM:ss Z',
22
- ignore: 'pid,hostname',
23
- },
24
- }
25
- : undefined,
26
- });
27
- }
28
- info(arg1, ...args) {
29
- this.pino.info(arg1, ...args);
30
- }
31
- error(arg1, ...args) {
32
- this.pino.error(arg1, ...args);
33
- }
34
- warn(arg1, ...args) {
35
- this.pino.warn(arg1, ...args);
36
- }
37
- debug(arg1, ...args) {
38
- this.pino.debug(arg1, ...args);
39
- }
40
- fatal(arg1, ...args) {
41
- this.pino.fatal(arg1, ...args);
42
- }
43
- child(bindings) {
44
- const child = new Logger();
45
- child.pino = this.pino.child(bindings);
46
- return child;
47
- }
48
- }
49
- exports.Logger = Logger;
@@ -1,45 +0,0 @@
1
- import type { TaskEngine, TaskMetrics } from './tasks';
2
- import { Scheduler } from './scheduler';
3
- export type LatencySnapshot = {
4
- p50: number | null;
5
- p95: number | null;
6
- p99: number | null;
7
- };
8
- export type MetricsSnapshot = {
9
- totalRequests: number;
10
- inFlightRequests: number;
11
- totalErrors: number;
12
- totalTimeouts: number;
13
- requestsPerSecond: number;
14
- latency: LatencySnapshot;
15
- scheduler: {
16
- inFlight: number;
17
- };
18
- tasks?: TaskMetrics;
19
- memory: {
20
- rssBytes: number;
21
- heapUsedBytes: number;
22
- };
23
- };
24
- export declare class Metrics {
25
- private readonly scheduler;
26
- private readonly taskEngine?;
27
- private readonly latencies;
28
- private readonly maxLatencies;
29
- private readonly enabled;
30
- private totalRequests;
31
- private inFlightRequests;
32
- private totalErrors;
33
- private totalTimeouts;
34
- constructor(scheduler: Scheduler, options?: {
35
- maxLatencies?: number;
36
- enabled?: boolean;
37
- }, taskEngine?: TaskEngine);
38
- onRequestStart(): void;
39
- onRequestEnd(durationMs: number, statusCode: number): void;
40
- onTimeout(): void;
41
- snapshot(): MetricsSnapshot;
42
- private recordLatency;
43
- private latencySnapshot;
44
- private percentile;
45
- }
@@ -1,111 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Metrics = void 0;
4
- class Metrics {
5
- constructor(scheduler, options = {}, taskEngine) {
6
- this.latencies = [];
7
- this.totalRequests = 0;
8
- this.inFlightRequests = 0;
9
- this.totalErrors = 0;
10
- this.totalTimeouts = 0;
11
- this.scheduler = scheduler;
12
- this.taskEngine = taskEngine;
13
- this.maxLatencies = options.maxLatencies ?? 1000;
14
- this.enabled = options.enabled ?? true;
15
- }
16
- onRequestStart() {
17
- if (!this.enabled) {
18
- return;
19
- }
20
- this.inFlightRequests += 1;
21
- }
22
- onRequestEnd(durationMs, statusCode) {
23
- if (!this.enabled) {
24
- return;
25
- }
26
- this.totalRequests += 1;
27
- if (this.inFlightRequests > 0) {
28
- this.inFlightRequests -= 1;
29
- }
30
- if (statusCode >= 500) {
31
- this.totalErrors += 1;
32
- }
33
- this.recordLatency(durationMs);
34
- }
35
- onTimeout() {
36
- if (!this.enabled) {
37
- return;
38
- }
39
- this.totalTimeouts += 1;
40
- }
41
- snapshot() {
42
- const uptime = process.uptime();
43
- const requestsPerSecond = uptime > 0 ? this.totalRequests / uptime : this.totalRequests;
44
- const latency = this.latencySnapshot();
45
- const memoryUsage = process.memoryUsage();
46
- let tasks;
47
- if (this.taskEngine) {
48
- tasks = this.taskEngine.getMetrics();
49
- }
50
- return {
51
- totalRequests: this.totalRequests,
52
- inFlightRequests: this.inFlightRequests,
53
- totalErrors: this.totalErrors,
54
- totalTimeouts: this.totalTimeouts,
55
- requestsPerSecond,
56
- latency,
57
- scheduler: {
58
- inFlight: this.scheduler.getCurrentInFlight(),
59
- },
60
- tasks,
61
- memory: {
62
- rssBytes: memoryUsage.rss,
63
- heapUsedBytes: memoryUsage.heapUsed,
64
- },
65
- };
66
- }
67
- recordLatency(durationMs) {
68
- if (!this.enabled) {
69
- return;
70
- }
71
- if (!Number.isFinite(durationMs) || durationMs < 0) {
72
- return;
73
- }
74
- this.latencies.push(durationMs);
75
- if (this.latencies.length > this.maxLatencies) {
76
- this.latencies.shift();
77
- }
78
- }
79
- latencySnapshot() {
80
- if (this.latencies.length === 0) {
81
- return {
82
- p50: null,
83
- p95: null,
84
- p99: null,
85
- };
86
- }
87
- const sorted = [...this.latencies].sort((a, b) => a - b);
88
- const p50 = this.percentile(sorted, 0.5);
89
- const p95 = this.percentile(sorted, 0.95);
90
- const p99 = this.percentile(sorted, 0.99);
91
- return {
92
- p50,
93
- p95,
94
- p99,
95
- };
96
- }
97
- percentile(sorted, p) {
98
- if (sorted.length === 0) {
99
- return 0;
100
- }
101
- if (p <= 0) {
102
- return sorted[0];
103
- }
104
- if (p >= 1) {
105
- return sorted[sorted.length - 1];
106
- }
107
- const index = Math.floor(p * (sorted.length - 1));
108
- return sorted[index];
109
- }
110
- }
111
- exports.Metrics = Metrics;
@@ -1,11 +0,0 @@
1
- import net from 'net';
2
- import { QHTTPX } from './server';
3
- export declare class NativeAdapter {
4
- private app;
5
- private nativeServer;
6
- private responsePool;
7
- constructor(app: QHTTPX);
8
- listen(port: number, cb?: () => void): net.Server | Promise<{
9
- port: number;
10
- }>;
11
- }
@@ -1,211 +0,0 @@
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.NativeAdapter = void 0;
7
- const net_1 = __importDefault(require("net"));
8
- const stream_1 = require("stream");
9
- const native_1 = require("../native");
10
- class MockIncomingMessage extends stream_1.Readable {
11
- constructor(socket, method, url, headers) {
12
- super();
13
- this.httpVersion = '1.1';
14
- this.socket = socket;
15
- this.method = method;
16
- this.url = url;
17
- this.headers = headers;
18
- }
19
- _read() {
20
- // No-op, data is pushed manually
21
- }
22
- }
23
- class MockServerResponse extends stream_1.Writable {
24
- constructor(socket, nativeServer) {
25
- super();
26
- this.statusCode = 200;
27
- this.headersSent = false;
28
- this.headers = {};
29
- this.socket = socket;
30
- this.nativeServer = nativeServer;
31
- }
32
- setHeader(name, value) {
33
- this.headers[name.toLowerCase()] = value;
34
- return this;
35
- }
36
- getHeader(name) {
37
- return this.headers[name.toLowerCase()];
38
- }
39
- getHeaders() {
40
- return { ...this.headers };
41
- }
42
- hasHeader(name) {
43
- return Object.prototype.hasOwnProperty.call(this.headers, name.toLowerCase());
44
- }
45
- removeHeader(name) {
46
- delete this.headers[name.toLowerCase()];
47
- }
48
- writeHead(statusCode, headers) {
49
- this.statusCode = statusCode;
50
- if (headers) {
51
- for (const key in headers) {
52
- this.setHeader(key, headers[key]);
53
- }
54
- }
55
- return this;
56
- }
57
- reset(socket) {
58
- this.socket = socket;
59
- this.statusCode = 200;
60
- this.headersSent = false;
61
- this.headers = {};
62
- this.removeAllListeners();
63
- }
64
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
- _write(chunk, encoding, callback) {
66
- if (!this.headersSent) {
67
- this.sendHeaders();
68
- }
69
- this.socket.write(chunk, encoding, callback);
70
- }
71
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
- end(arg1, arg2, arg3) {
73
- if (arg1 && typeof arg1 !== 'function') {
74
- if (!this.headersSent) {
75
- // Optimization: Use native createResponse / createJSONResponse if possible
76
- try {
77
- let respBuffer;
78
- // Check for JSON object (not buffer/string)
79
- if (typeof arg1 === 'object' && !Buffer.isBuffer(arg1)) {
80
- respBuffer = this.nativeServer.createJSONResponse(arg1);
81
- }
82
- else {
83
- const body = arg1;
84
- const headers = {};
85
- for (const k in this.headers) {
86
- const v = this.headers[k];
87
- headers[k] = Array.isArray(v) ? v.join(', ') : String(v);
88
- }
89
- respBuffer = this.nativeServer.createResponse(this.statusCode, headers, body);
90
- }
91
- // Try direct writeResponse if FD is available
92
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
- const fd = this.socket._handle?.fd;
94
- if (typeof fd === 'number' && fd >= 0) {
95
- this.nativeServer.writeResponse(fd, [respBuffer]);
96
- }
97
- else {
98
- this.socket.write(respBuffer);
99
- }
100
- this.headersSent = true;
101
- if (arg2 && typeof arg2 === 'function')
102
- arg2();
103
- else if (arg3 && typeof arg3 === 'function')
104
- arg3();
105
- return this;
106
- }
107
- catch {
108
- // Fallback
109
- }
110
- }
111
- this.write(arg1, arg2);
112
- }
113
- if (!this.headersSent) {
114
- this.sendHeaders();
115
- }
116
- super.end();
117
- if (arg1 && typeof arg1 === 'function')
118
- arg1();
119
- else if (arg2 && typeof arg2 === 'function')
120
- arg2();
121
- else if (arg3 && typeof arg3 === 'function')
122
- arg3();
123
- return this;
124
- }
125
- sendHeaders() {
126
- let head = `HTTP/1.1 ${this.statusCode} OK\r\n`; // Status msg todo
127
- for (const key in this.headers) {
128
- const val = this.headers[key];
129
- if (Array.isArray(val)) {
130
- for (const v of val) {
131
- head += `${key}: ${v}\r\n`;
132
- }
133
- }
134
- else {
135
- head += `${key}: ${val}\r\n`;
136
- }
137
- }
138
- head += '\r\n';
139
- this.socket.write(head);
140
- this.headersSent = true;
141
- }
142
- }
143
- class MockResponsePool {
144
- constructor(nativeServer) {
145
- this.pool = [];
146
- this.nativeServer = nativeServer;
147
- }
148
- acquire(socket) {
149
- const res = this.pool.pop();
150
- if (res) {
151
- res.reset(socket);
152
- return res;
153
- }
154
- return new MockServerResponse(socket, this.nativeServer);
155
- }
156
- release(res) {
157
- // Simple cap to prevent memory leak if something goes wrong
158
- if (this.pool.length < 2000) {
159
- this.pool.push(res);
160
- }
161
- }
162
- }
163
- class NativeAdapter {
164
- constructor(app) {
165
- this.app = app;
166
- this.nativeServer = new native_1.NativeServer();
167
- this.responsePool = new MockResponsePool(this.nativeServer);
168
- }
169
- listen(port, cb) {
170
- if (!this.nativeServer.isAvailable) {
171
- console.warn('Native server not available, falling back to Node.js HTTP');
172
- return this.app.listen(port, cb);
173
- }
174
- const server = net_1.default.createServer((socket) => {
175
- let buffer = Buffer.alloc(0);
176
- socket.on('data', (chunk) => {
177
- buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);
178
- // Try parsing
179
- const res = this.nativeServer.parse(buffer);
180
- if (res) {
181
- // Parsed successfully
182
- const req = new MockIncomingMessage(socket, res.method, res.path, res.headers);
183
- const response = this.responsePool.acquire(socket);
184
- response.on('finish', () => {
185
- this.responsePool.release(response);
186
- });
187
- // Push body if any
188
- if (res.bodyOffset < buffer.length) {
189
- req.push(buffer.slice(res.bodyOffset));
190
- }
191
- req.push(null); // End of body (assuming single packet for now or content-length handling needed for real impl)
192
- // Reset buffer for pipelining support (not implemented here, assuming connection: close or one req per packet for simplicity)
193
- buffer = Buffer.alloc(0);
194
- // Dispatch to QHTTPX
195
- this.app.handleRequest(req, response);
196
- }
197
- else if (res === null) {
198
- // Error
199
- socket.destroy();
200
- }
201
- // else undefined -> partial, wait for more data
202
- });
203
- socket.on('error', () => {
204
- // console.error(err);
205
- });
206
- });
207
- server.listen(port, cb);
208
- return server;
209
- }
210
- }
211
- exports.NativeAdapter = NativeAdapter;
@@ -1,9 +0,0 @@
1
- export type WorkerSetting = 'auto' | number;
2
- export declare function calculateWorkerCount(setting: WorkerSetting): number;
3
- export type ResourceThresholds = {
4
- maxRssBytes?: number;
5
- };
6
- export type ResourceSample = {
7
- rssBytes: number;
8
- };
9
- export declare function isResourceOverloaded(sample: ResourceSample, thresholds: ResourceThresholds): boolean;
@@ -1,25 +0,0 @@
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.calculateWorkerCount = calculateWorkerCount;
7
- exports.isResourceOverloaded = isResourceOverloaded;
8
- const os_1 = __importDefault(require("os"));
9
- function calculateWorkerCount(setting) {
10
- if (typeof setting === 'number') {
11
- if (!Number.isFinite(setting) || setting <= 0) {
12
- return 1;
13
- }
14
- return Math.floor(setting);
15
- }
16
- const cpuCount = os_1.default.cpus().length || 1;
17
- return Math.max(1, cpuCount);
18
- }
19
- function isResourceOverloaded(sample, thresholds) {
20
- if (thresholds.maxRssBytes !== undefined &&
21
- sample.rssBytes > thresholds.maxRssBytes) {
22
- return true;
23
- }
24
- return false;
25
- }