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.
- package/package.json +26 -4
- package/prebuilds/darwin-arm64/qhttpx.node +0 -0
- package/prebuilds/linux-x64/qhttpx.node +0 -0
- package/prebuilds/win32-x64/qhttpx.node +0 -0
- package/src/native/index.ts +104 -24
- package/src/native/picohttpparser.h +5 -0
- package/src/native/server.cc +2 -0
- package/dist/examples/api-server.d.ts +0 -1
- package/dist/examples/api-server.js +0 -77
- package/dist/examples/basic.d.ts +0 -1
- package/dist/examples/basic.js +0 -10
- package/dist/examples/compression.d.ts +0 -1
- package/dist/examples/compression.js +0 -17
- package/dist/examples/cors.d.ts +0 -1
- package/dist/examples/cors.js +0 -19
- package/dist/examples/errors.d.ts +0 -1
- package/dist/examples/errors.js +0 -25
- package/dist/examples/file-upload.d.ts +0 -1
- package/dist/examples/file-upload.js +0 -24
- package/dist/examples/fusion.d.ts +0 -1
- package/dist/examples/fusion.js +0 -21
- package/dist/examples/rate-limiting.d.ts +0 -1
- package/dist/examples/rate-limiting.js +0 -17
- package/dist/examples/validation.d.ts +0 -1
- package/dist/examples/validation.js +0 -23
- package/dist/examples/websockets.d.ts +0 -1
- package/dist/examples/websockets.js +0 -20
- package/dist/package.json +0 -101
- package/dist/src/benchmarks/quantam-users.d.ts +0 -1
- package/dist/src/benchmarks/quantam-users.js +0 -56
- package/dist/src/benchmarks/simple-json.d.ts +0 -1
- package/dist/src/benchmarks/simple-json.js +0 -60
- package/dist/src/benchmarks/ultra-mode.d.ts +0 -1
- package/dist/src/benchmarks/ultra-mode.js +0 -94
- package/dist/src/cli/index.d.ts +0 -2
- package/dist/src/cli/index.js +0 -222
- package/dist/src/client/index.d.ts +0 -17
- package/dist/src/client/index.js +0 -72
- package/dist/src/core/batch.d.ts +0 -24
- package/dist/src/core/batch.js +0 -97
- package/dist/src/core/body-parser.d.ts +0 -15
- package/dist/src/core/body-parser.js +0 -121
- package/dist/src/core/buffer-pool.d.ts +0 -41
- package/dist/src/core/buffer-pool.js +0 -70
- package/dist/src/core/config.d.ts +0 -7
- package/dist/src/core/config.js +0 -50
- package/dist/src/core/errors.d.ts +0 -34
- package/dist/src/core/errors.js +0 -70
- package/dist/src/core/fusion.d.ts +0 -14
- package/dist/src/core/fusion.js +0 -183
- package/dist/src/core/logger.d.ts +0 -22
- package/dist/src/core/logger.js +0 -49
- package/dist/src/core/metrics.d.ts +0 -45
- package/dist/src/core/metrics.js +0 -111
- package/dist/src/core/native-adapter.d.ts +0 -11
- package/dist/src/core/native-adapter.js +0 -211
- package/dist/src/core/resources.d.ts +0 -9
- package/dist/src/core/resources.js +0 -25
- package/dist/src/core/scheduler.d.ts +0 -34
- package/dist/src/core/scheduler.js +0 -85
- package/dist/src/core/scope.d.ts +0 -26
- package/dist/src/core/scope.js +0 -68
- package/dist/src/core/serializer.d.ts +0 -10
- package/dist/src/core/serializer.js +0 -44
- package/dist/src/core/server.d.ts +0 -138
- package/dist/src/core/server.js +0 -1082
- package/dist/src/core/stream.d.ts +0 -15
- package/dist/src/core/stream.js +0 -71
- package/dist/src/core/tasks.d.ts +0 -29
- package/dist/src/core/tasks.js +0 -87
- package/dist/src/core/types.d.ts +0 -173
- package/dist/src/core/types.js +0 -19
- package/dist/src/core/websocket.d.ts +0 -25
- package/dist/src/core/websocket.js +0 -86
- package/dist/src/core/worker-queue.d.ts +0 -41
- package/dist/src/core/worker-queue.js +0 -73
- package/dist/src/database/adapters/memory.d.ts +0 -21
- package/dist/src/database/adapters/memory.js +0 -90
- package/dist/src/database/adapters/mongo.d.ts +0 -11
- package/dist/src/database/adapters/mongo.js +0 -141
- package/dist/src/database/adapters/postgres.d.ts +0 -10
- package/dist/src/database/adapters/postgres.js +0 -111
- package/dist/src/database/adapters/sqlite.d.ts +0 -10
- package/dist/src/database/adapters/sqlite.js +0 -42
- package/dist/src/database/coalescer.d.ts +0 -14
- package/dist/src/database/coalescer.js +0 -134
- package/dist/src/database/manager.d.ts +0 -35
- package/dist/src/database/manager.js +0 -87
- package/dist/src/database/types.d.ts +0 -20
- package/dist/src/database/types.js +0 -2
- package/dist/src/index.d.ts +0 -50
- package/dist/src/index.js +0 -91
- package/dist/src/middleware/compression.d.ts +0 -2
- package/dist/src/middleware/compression.js +0 -133
- package/dist/src/middleware/cors.d.ts +0 -2
- package/dist/src/middleware/cors.js +0 -66
- package/dist/src/middleware/presets.d.ts +0 -16
- package/dist/src/middleware/presets.js +0 -52
- package/dist/src/middleware/rate-limit.d.ts +0 -14
- package/dist/src/middleware/rate-limit.js +0 -83
- package/dist/src/middleware/security.d.ts +0 -21
- package/dist/src/middleware/security.js +0 -69
- package/dist/src/middleware/static.d.ts +0 -11
- package/dist/src/middleware/static.js +0 -191
- package/dist/src/native/index.d.ts +0 -29
- package/dist/src/native/index.js +0 -64
- package/dist/src/openapi/generator.d.ts +0 -19
- package/dist/src/openapi/generator.js +0 -149
- package/dist/src/router/radix-router.d.ts +0 -18
- package/dist/src/router/radix-router.js +0 -89
- package/dist/src/router/radix-tree.d.ts +0 -18
- package/dist/src/router/radix-tree.js +0 -131
- package/dist/src/router/router.d.ts +0 -34
- package/dist/src/router/router.js +0 -186
- package/dist/src/testing/index.d.ts +0 -25
- package/dist/src/testing/index.js +0 -84
- package/dist/src/utils/cookies.d.ts +0 -3
- package/dist/src/utils/cookies.js +0 -59
- package/dist/src/utils/logger.d.ts +0 -12
- package/dist/src/utils/logger.js +0 -45
- package/dist/src/utils/signals.d.ts +0 -6
- package/dist/src/utils/signals.js +0 -31
- package/dist/src/utils/sse.d.ts +0 -6
- package/dist/src/utils/sse.js +0 -32
- package/dist/src/validation/index.d.ts +0 -3
- package/dist/src/validation/index.js +0 -19
- package/dist/src/validation/simple.d.ts +0 -5
- package/dist/src/validation/simple.js +0 -102
- package/dist/src/validation/types.d.ts +0 -32
- package/dist/src/validation/types.js +0 -12
- package/dist/src/validation/zod.d.ts +0 -4
- package/dist/src/validation/zod.js +0 -18
- package/dist/src/views/index.d.ts +0 -1
- package/dist/src/views/index.js +0 -17
- package/dist/src/views/types.d.ts +0 -3
- package/dist/src/views/types.js +0 -2
- package/dist/tests/adapters.test.d.ts +0 -1
- package/dist/tests/adapters.test.js +0 -106
- package/dist/tests/batch.test.d.ts +0 -1
- package/dist/tests/batch.test.js +0 -117
- package/dist/tests/body-parser.test.d.ts +0 -1
- package/dist/tests/body-parser.test.js +0 -52
- package/dist/tests/compression-sse.test.d.ts +0 -1
- package/dist/tests/compression-sse.test.js +0 -87
- package/dist/tests/cookies.test.d.ts +0 -1
- package/dist/tests/cookies.test.js +0 -63
- package/dist/tests/cors.test.d.ts +0 -1
- package/dist/tests/cors.test.js +0 -55
- package/dist/tests/database.test.d.ts +0 -1
- package/dist/tests/database.test.js +0 -80
- package/dist/tests/dx.test.d.ts +0 -1
- package/dist/tests/dx.test.js +0 -114
- package/dist/tests/ecosystem.test.d.ts +0 -1
- package/dist/tests/ecosystem.test.js +0 -133
- package/dist/tests/features.test.d.ts +0 -1
- package/dist/tests/features.test.js +0 -47
- package/dist/tests/fusion.test.d.ts +0 -1
- package/dist/tests/fusion.test.js +0 -92
- package/dist/tests/http-basic.test.d.ts +0 -1
- package/dist/tests/http-basic.test.js +0 -124
- package/dist/tests/logger.test.d.ts +0 -1
- package/dist/tests/logger.test.js +0 -33
- package/dist/tests/middleware.test.d.ts +0 -1
- package/dist/tests/middleware.test.js +0 -109
- package/dist/tests/native-adapter.test.d.ts +0 -1
- package/dist/tests/native-adapter.test.js +0 -71
- package/dist/tests/observability.test.d.ts +0 -1
- package/dist/tests/observability.test.js +0 -59
- package/dist/tests/openapi.test.d.ts +0 -1
- package/dist/tests/openapi.test.js +0 -64
- package/dist/tests/plugin.test.d.ts +0 -1
- package/dist/tests/plugin.test.js +0 -65
- package/dist/tests/plugins.test.d.ts +0 -1
- package/dist/tests/plugins.test.js +0 -71
- package/dist/tests/rate-limit.test.d.ts +0 -1
- package/dist/tests/rate-limit.test.js +0 -77
- package/dist/tests/resources.test.d.ts +0 -1
- package/dist/tests/resources.test.js +0 -47
- package/dist/tests/scheduler.test.d.ts +0 -1
- package/dist/tests/scheduler.test.js +0 -46
- package/dist/tests/schema-routes.test.d.ts +0 -1
- package/dist/tests/schema-routes.test.js +0 -77
- package/dist/tests/security.test.d.ts +0 -1
- package/dist/tests/security.test.js +0 -83
- package/dist/tests/server-db.test.d.ts +0 -1
- package/dist/tests/server-db.test.js +0 -72
- package/dist/tests/smoke.test.d.ts +0 -1
- package/dist/tests/smoke.test.js +0 -10
- package/dist/tests/sqlite-fusion.test.d.ts +0 -1
- package/dist/tests/sqlite-fusion.test.js +0 -92
- package/dist/tests/static.test.d.ts +0 -1
- package/dist/tests/static.test.js +0 -102
- package/dist/tests/stream.test.d.ts +0 -1
- package/dist/tests/stream.test.js +0 -44
- package/dist/tests/task-metrics.test.d.ts +0 -1
- package/dist/tests/task-metrics.test.js +0 -53
- package/dist/tests/tasks.test.d.ts +0 -1
- package/dist/tests/tasks.test.js +0 -62
- package/dist/tests/testing.test.d.ts +0 -1
- package/dist/tests/testing.test.js +0 -47
- package/dist/tests/validation.test.d.ts +0 -1
- package/dist/tests/validation.test.js +0 -107
- package/dist/tests/websocket.test.d.ts +0 -1
- package/dist/tests/websocket.test.js +0 -146
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -9
package/dist/src/core/fusion.js
DELETED
|
@@ -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
|
-
}
|
package/dist/src/core/logger.js
DELETED
|
@@ -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
|
-
}
|
package/dist/src/core/metrics.js
DELETED
|
@@ -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
|
-
}
|