qhttpx 2.1.0 → 2.3.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/LICENSE +201 -21
- package/README.md +117 -221
- package/dist/chunk-QW72SEAS.mjs +98 -0
- package/dist/{src/cli/index.d.ts → cli.d.mts} +0 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +287 -0
- package/dist/cli.mjs +209 -0
- package/dist/index.d.mts +433 -0
- package/dist/index.d.ts +433 -0
- package/dist/index.js +1955 -0
- package/dist/index.mjs +1863 -0
- package/dist/qhttpx-core-new.linux-x64-gnu.node +0 -0
- package/dist/qhttpx-core-new.node +0 -0
- package/dist/qhttpx-core-new.win32-x64-msvc.node +0 -0
- package/examples/benchmark.ts +67 -0
- package/examples/body_demo.ts +32 -0
- package/examples/chat_client.ts +36 -0
- package/examples/chat_demo.ts +41 -0
- package/examples/cluster_demo.ts +26 -0
- package/examples/cors_demo.ts +25 -0
- package/examples/db_auth_demo.ts +66 -0
- package/examples/demo.ts +52 -0
- package/examples/headers_demo.ts +24 -0
- package/examples/hello.ts +7 -0
- package/examples/http2_demo.ts +52 -0
- package/examples/magic-dev.ts +15 -0
- package/examples/middleware_demo.ts +34 -0
- package/examples/mongo_demo.ts +40 -0
- package/examples/native_policy_demo.ts +33 -0
- package/examples/observability_demo.ts +24 -0
- package/examples/public/1mb.dat +1 -0
- package/examples/query_demo.ts +29 -0
- package/examples/response_demo.ts +21 -0
- package/examples/routing_demo.ts +24 -0
- package/examples/server.ts +51 -0
- package/examples/static_demo.ts +29 -0
- package/examples/test_middleware_client.ts +33 -0
- package/examples/test_query_client.ts +34 -0
- package/examples/test_response_client.ts +30 -0
- package/examples/tls_demo.ts +32 -0
- package/examples/upload_demo.ts +43 -0
- package/examples/uploads/test.txt +1 -0
- package/examples/verify_upload.ts +69 -0
- package/examples/ws_client.ts +26 -0
- package/examples/ws_demo.ts +21 -0
- package/package.json +65 -81
- package/CHANGELOG.md +0 -285
- package/dist/examples/api-server.d.ts +0 -1
- package/dist/examples/api-server.js +0 -80
- package/dist/examples/basic.d.ts +0 -1
- package/dist/examples/basic.js +0 -9
- package/dist/examples/compression.d.ts +0 -1
- package/dist/examples/compression.js +0 -15
- package/dist/examples/cors.d.ts +0 -1
- package/dist/examples/cors.js +0 -18
- package/dist/examples/errors.d.ts +0 -1
- package/dist/examples/errors.js +0 -26
- package/dist/examples/file-upload.d.ts +0 -1
- package/dist/examples/file-upload.js +0 -22
- 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 -22
- package/dist/examples/websockets.d.ts +0 -1
- package/dist/examples/websockets.js +0 -19
- package/dist/package.json +0 -107
- package/dist/src/benchmarks/quantam-users.d.ts +0 -1
- package/dist/src/benchmarks/quantam-users.js +0 -56
- package/dist/src/benchmarks/quick-bench.d.ts +0 -1
- package/dist/src/benchmarks/quick-bench.js +0 -57
- package/dist/src/benchmarks/simple-json.d.ts +0 -1
- package/dist/src/benchmarks/simple-json.js +0 -171
- package/dist/src/benchmarks/ultra-mode.d.ts +0 -1
- package/dist/src/benchmarks/ultra-mode.js +0 -64
- 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/context-pool.d.ts +0 -12
- package/dist/src/core/context-pool.js +0 -34
- 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 -20
- package/dist/src/core/fusion.js +0 -191
- 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 -50
- package/dist/src/core/metrics.js +0 -123
- 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 -38
- package/dist/src/core/scheduler.js +0 -126
- package/dist/src/core/scope.d.ts +0 -41
- package/dist/src/core/scope.js +0 -107
- package/dist/src/core/serializer.d.ts +0 -10
- package/dist/src/core/serializer.js +0 -82
- package/dist/src/core/server.d.ts +0 -179
- package/dist/src/core/server.js +0 -1511
- 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/timer.d.ts +0 -11
- package/dist/src/core/timer.js +0 -29
- package/dist/src/core/types.d.ts +0 -225
- 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 -52
- package/dist/src/index.js +0 -92
- 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 -15
- 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 -10
- package/dist/src/middleware/security.js +0 -74
- package/dist/src/middleware/static.d.ts +0 -11
- package/dist/src/middleware/static.js +0 -191
- 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 -21
- package/dist/src/router/radix-tree.js +0 -175
- package/dist/src/router/router.d.ts +0 -37
- package/dist/src/router/router.js +0 -203
- 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 -2
- 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/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 -79
- 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/docs/AEGIS.md +0 -91
- package/docs/API_REFERENCE.md +0 -749
- package/docs/BENCHMARKS.md +0 -39
- package/docs/CAPABILITIES.md +0 -70
- package/docs/CLI.md +0 -43
- package/docs/DATABASE.md +0 -142
- package/docs/ECOSYSTEM.md +0 -146
- package/docs/ERRORS.md +0 -112
- package/docs/FUSION.md +0 -87
- package/docs/MIDDLEWARE.md +0 -65
- package/docs/MIGRATION_1.9_TO_2.0.md +0 -495
- package/docs/NEXT_STEPS.md +0 -99
- package/docs/OPENAPI.md +0 -99
- package/docs/PLUGINS.md +0 -59
- package/docs/PRODUCTION_DEPLOYMENT.md +0 -798
- package/docs/REAL_WORLD_EXAMPLES.md +0 -109
- package/docs/ROADMAP.md +0 -366
- package/docs/ROUTING.md +0 -78
- package/docs/SECURITY.md +0 -876
- package/docs/STATIC.md +0 -61
- package/docs/VALIDATION.md +0 -114
- package/docs/WEBSOCKETS.md +0 -76
package/dist/src/utils/sse.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createSSE = createSSE;
|
|
4
|
-
function createSSE(ctx) {
|
|
5
|
-
const res = ctx.res;
|
|
6
|
-
// Disable auto-end so we can keep the connection open
|
|
7
|
-
ctx.disableAutoEnd = true;
|
|
8
|
-
// Only write headers if not already sent
|
|
9
|
-
if (!res.headersSent) {
|
|
10
|
-
res.setHeader('Content-Type', 'text/event-stream');
|
|
11
|
-
res.setHeader('Cache-Control', 'no-cache');
|
|
12
|
-
res.setHeader('Connection', 'keep-alive');
|
|
13
|
-
res.setHeader('X-Accel-Buffering', 'no'); // For Nginx
|
|
14
|
-
// Send initial ping/comment to flush headers and establish connection
|
|
15
|
-
res.write(': connected\n\n');
|
|
16
|
-
}
|
|
17
|
-
const send = (data, event, id) => {
|
|
18
|
-
if (id)
|
|
19
|
-
res.write(`id: ${id}\n`);
|
|
20
|
-
if (event)
|
|
21
|
-
res.write(`event: ${event}\n`);
|
|
22
|
-
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
|
23
|
-
res.write(`data: ${payload}\n\n`);
|
|
24
|
-
};
|
|
25
|
-
const close = () => {
|
|
26
|
-
res.end();
|
|
27
|
-
};
|
|
28
|
-
ctx.req.on('close', () => {
|
|
29
|
-
// console.log('SSE client disconnected');
|
|
30
|
-
});
|
|
31
|
-
return { send, close };
|
|
32
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./types"), exports);
|
|
18
|
-
__exportStar(require("./simple"), exports);
|
|
19
|
-
__exportStar(require("./zod"), exports);
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SimpleValidator = void 0;
|
|
4
|
-
const types_1 = require("./types");
|
|
5
|
-
class SimpleValidator {
|
|
6
|
-
validate(schema, data) {
|
|
7
|
-
try {
|
|
8
|
-
// If schema is not a SimpleSchema object, we can't validate it with this validator
|
|
9
|
-
// But for simplicity, we assume the user passes a valid SimpleSchema if they use this validator.
|
|
10
|
-
const validData = this.check(schema, data);
|
|
11
|
-
return { success: true, data: validData };
|
|
12
|
-
}
|
|
13
|
-
catch (err) {
|
|
14
|
-
return {
|
|
15
|
-
success: false,
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
-
error: new types_1.ValidationError(err.message || 'Validation failed')
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
check(schema, data, path = '') {
|
|
23
|
-
if (schema.required !== false && (data === undefined || data === null)) {
|
|
24
|
-
throw new Error(`Field '${path}' is required`);
|
|
25
|
-
}
|
|
26
|
-
if (data === undefined || data === null) {
|
|
27
|
-
return data;
|
|
28
|
-
}
|
|
29
|
-
switch (schema.type) {
|
|
30
|
-
case 'string':
|
|
31
|
-
if (typeof data !== 'string')
|
|
32
|
-
throw new Error(`Field '${path}' must be a string`);
|
|
33
|
-
if (schema.min !== undefined && data.length < schema.min)
|
|
34
|
-
throw new Error(`Field '${path}' too short (min ${schema.min})`);
|
|
35
|
-
if (schema.max !== undefined && data.length > schema.max)
|
|
36
|
-
throw new Error(`Field '${path}' too long (max ${schema.max})`);
|
|
37
|
-
if (schema.pattern && !new RegExp(schema.pattern).test(data))
|
|
38
|
-
throw new Error(`Field '${path}' format invalid`);
|
|
39
|
-
if (schema.enum && !schema.enum.includes(data))
|
|
40
|
-
throw new Error(`Field '${path}' must be one of [${schema.enum.join(', ')}]`);
|
|
41
|
-
return data;
|
|
42
|
-
case 'number':
|
|
43
|
-
// Try to coerce if it's a string looking like a number (useful for query params)
|
|
44
|
-
let num = data;
|
|
45
|
-
if (typeof data === 'string' && !isNaN(Number(data))) {
|
|
46
|
-
num = Number(data);
|
|
47
|
-
}
|
|
48
|
-
if (typeof num !== 'number' || isNaN(num))
|
|
49
|
-
throw new Error(`Field '${path}' must be a number`);
|
|
50
|
-
if (schema.min !== undefined && num < schema.min)
|
|
51
|
-
throw new Error(`Field '${path}' too small (min ${schema.min})`);
|
|
52
|
-
if (schema.max !== undefined && num > schema.max)
|
|
53
|
-
throw new Error(`Field '${path}' too large (max ${schema.max})`);
|
|
54
|
-
if (schema.enum && !schema.enum.includes(num))
|
|
55
|
-
throw new Error(`Field '${path}' must be one of [${schema.enum.join(', ')}]`);
|
|
56
|
-
return num;
|
|
57
|
-
case 'boolean':
|
|
58
|
-
if (typeof data === 'boolean')
|
|
59
|
-
return data;
|
|
60
|
-
if (data === 'true')
|
|
61
|
-
return true;
|
|
62
|
-
if (data === 'false')
|
|
63
|
-
return false;
|
|
64
|
-
throw new Error(`Field '${path}' must be a boolean`);
|
|
65
|
-
case 'array':
|
|
66
|
-
if (!Array.isArray(data))
|
|
67
|
-
throw new Error(`Field '${path}' must be an array`);
|
|
68
|
-
if (schema.min !== undefined && data.length < schema.min)
|
|
69
|
-
throw new Error(`Field '${path}' too few items (min ${schema.min})`);
|
|
70
|
-
if (schema.max !== undefined && data.length > schema.max)
|
|
71
|
-
throw new Error(`Field '${path}' too many items (max ${schema.max})`);
|
|
72
|
-
if (schema.items) {
|
|
73
|
-
return data.map((item, i) => this.check(schema.items, item, `${path}[${i}]`));
|
|
74
|
-
}
|
|
75
|
-
return data;
|
|
76
|
-
case 'object':
|
|
77
|
-
if (typeof data !== 'object' || Array.isArray(data))
|
|
78
|
-
throw new Error(`Field '${path}' must be an object`);
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
-
const result = {};
|
|
81
|
-
const props = schema.properties || {};
|
|
82
|
-
// Check known properties
|
|
83
|
-
for (const key in props) {
|
|
84
|
-
const propSchema = props[key];
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
-
const propValue = data[key];
|
|
87
|
-
result[key] = this.check(propSchema, propValue, path ? `${path}.${key}` : key);
|
|
88
|
-
}
|
|
89
|
-
// Pass through unknown properties?
|
|
90
|
-
// For strictness, maybe we should strip them?
|
|
91
|
-
// Let's keep unknown properties for now to be safe, or make it configurable.
|
|
92
|
-
// For now: Only keep validated properties if properties are defined.
|
|
93
|
-
if (Object.keys(props).length > 0) {
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
return data;
|
|
97
|
-
default:
|
|
98
|
-
return data;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
exports.SimpleValidator = SimpleValidator;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export type ValidationResult<T = any> = {
|
|
2
|
-
success: true;
|
|
3
|
-
data: T;
|
|
4
|
-
} | {
|
|
5
|
-
success: false;
|
|
6
|
-
error: ValidationError;
|
|
7
|
-
};
|
|
8
|
-
export declare class ValidationError extends Error {
|
|
9
|
-
details: any;
|
|
10
|
-
constructor(details: any);
|
|
11
|
-
}
|
|
12
|
-
export interface Validator {
|
|
13
|
-
validate(schema: unknown, data: unknown): Promise<ValidationResult> | ValidationResult;
|
|
14
|
-
}
|
|
15
|
-
export type RouteSchema = {
|
|
16
|
-
body?: unknown;
|
|
17
|
-
query?: unknown;
|
|
18
|
-
params?: unknown;
|
|
19
|
-
headers?: unknown;
|
|
20
|
-
response?: unknown;
|
|
21
|
-
};
|
|
22
|
-
export type SimpleType = 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
23
|
-
export interface SimpleSchema {
|
|
24
|
-
type: SimpleType;
|
|
25
|
-
required?: boolean;
|
|
26
|
-
properties?: Record<string, SimpleSchema>;
|
|
27
|
-
items?: SimpleSchema;
|
|
28
|
-
min?: number;
|
|
29
|
-
max?: number;
|
|
30
|
-
pattern?: string;
|
|
31
|
-
enum?: (string | number)[];
|
|
32
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ValidationError = void 0;
|
|
4
|
-
class ValidationError extends Error {
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
-
constructor(details) {
|
|
7
|
-
super('Validation Error');
|
|
8
|
-
this.details = details;
|
|
9
|
-
this.name = 'ValidationError';
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
exports.ValidationError = ValidationError;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ZodValidator = void 0;
|
|
4
|
-
const types_1 = require("./types");
|
|
5
|
-
class ZodValidator {
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
-
async validate(schema, data) {
|
|
8
|
-
try {
|
|
9
|
-
const zodSchema = schema;
|
|
10
|
-
const result = await zodSchema.parseAsync(data);
|
|
11
|
-
return { success: true, data: result };
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
return { success: false, error: new types_1.ValidationError(error) };
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
exports.ZodValidator = ZodValidator;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './types';
|
package/dist/src/views/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./types"), exports);
|
package/dist/src/views/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const postgres_1 = require("../src/database/adapters/postgres");
|
|
5
|
-
const mongo_1 = require("../src/database/adapters/mongo");
|
|
6
|
-
// Define mocks
|
|
7
|
-
const pgPoolQuery = vitest_1.vi.fn(() => Promise.resolve({ rows: [], command: 'SELECT' }));
|
|
8
|
-
const pgConnect = vitest_1.vi.fn(() => Promise.resolve({ release: vitest_1.vi.fn() }));
|
|
9
|
-
const pgEnd = vitest_1.vi.fn();
|
|
10
|
-
vitest_1.vi.mock('pg', () => {
|
|
11
|
-
const Pool = function () {
|
|
12
|
-
return {
|
|
13
|
-
connect: pgConnect,
|
|
14
|
-
query: pgPoolQuery,
|
|
15
|
-
end: pgEnd,
|
|
16
|
-
on: vitest_1.vi.fn(),
|
|
17
|
-
ended: false
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
return {
|
|
21
|
-
default: { Pool },
|
|
22
|
-
Pool
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
const mongoFind = vitest_1.vi.fn(() => ({
|
|
26
|
-
toArray: vitest_1.vi.fn(() => Promise.resolve([])),
|
|
27
|
-
limit: vitest_1.vi.fn(),
|
|
28
|
-
skip: vitest_1.vi.fn()
|
|
29
|
-
}));
|
|
30
|
-
const mongoConnect = vitest_1.vi.fn(() => Promise.resolve());
|
|
31
|
-
const mongoClose = vitest_1.vi.fn();
|
|
32
|
-
vitest_1.vi.mock('mongodb', () => {
|
|
33
|
-
const MongoClient = function () {
|
|
34
|
-
return {
|
|
35
|
-
connect: mongoConnect,
|
|
36
|
-
db: vitest_1.vi.fn(() => ({
|
|
37
|
-
collection: vitest_1.vi.fn(() => ({
|
|
38
|
-
find: mongoFind,
|
|
39
|
-
findOne: vitest_1.vi.fn(),
|
|
40
|
-
insertOne: vitest_1.vi.fn(),
|
|
41
|
-
// add other methods as needed
|
|
42
|
-
updateOne: vitest_1.vi.fn(),
|
|
43
|
-
updateMany: vitest_1.vi.fn(),
|
|
44
|
-
deleteOne: vitest_1.vi.fn(),
|
|
45
|
-
deleteMany: vitest_1.vi.fn(),
|
|
46
|
-
aggregate: vitest_1.vi.fn(() => ({ toArray: vitest_1.vi.fn(() => Promise.resolve([])) }))
|
|
47
|
-
}))
|
|
48
|
-
})),
|
|
49
|
-
close: mongoClose
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
return {
|
|
53
|
-
default: { MongoClient },
|
|
54
|
-
MongoClient
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
(0, vitest_1.describe)('Database Adapters', () => {
|
|
58
|
-
(0, vitest_1.describe)('PostgresAdapter', () => {
|
|
59
|
-
let adapter;
|
|
60
|
-
(0, vitest_1.beforeEach)(() => {
|
|
61
|
-
adapter = new postgres_1.PostgresAdapter({ type: 'postgres', database: 'test' });
|
|
62
|
-
vitest_1.vi.clearAllMocks();
|
|
63
|
-
});
|
|
64
|
-
(0, vitest_1.it)('should connect and disconnect', async () => {
|
|
65
|
-
await adapter.connect();
|
|
66
|
-
(0, vitest_1.expect)(pgConnect).toHaveBeenCalled();
|
|
67
|
-
(0, vitest_1.expect)(adapter.isConnected()).toBe(true);
|
|
68
|
-
await adapter.disconnect();
|
|
69
|
-
(0, vitest_1.expect)(pgEnd).toHaveBeenCalled();
|
|
70
|
-
// Since our mock object is static in the factory, we can't easily check 'pool' null state
|
|
71
|
-
// via the mock, but we can check the adapter state if it relies on the pool property.
|
|
72
|
-
// The adapter sets this.pool = null.
|
|
73
|
-
(0, vitest_1.expect)(adapter.isConnected()).toBe(false);
|
|
74
|
-
});
|
|
75
|
-
(0, vitest_1.it)('should execute query', async () => {
|
|
76
|
-
await adapter.connect();
|
|
77
|
-
await adapter.query('SELECT * FROM users');
|
|
78
|
-
(0, vitest_1.expect)(pgPoolQuery).toHaveBeenCalledWith('SELECT * FROM users', undefined);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
(0, vitest_1.describe)('MongoAdapter', () => {
|
|
82
|
-
let adapter;
|
|
83
|
-
(0, vitest_1.beforeEach)(() => {
|
|
84
|
-
adapter = new mongo_1.MongoAdapter({ type: 'mongo', url: 'mongodb://localhost' });
|
|
85
|
-
vitest_1.vi.clearAllMocks();
|
|
86
|
-
});
|
|
87
|
-
(0, vitest_1.it)('should connect and disconnect', async () => {
|
|
88
|
-
await adapter.connect();
|
|
89
|
-
(0, vitest_1.expect)(mongoConnect).toHaveBeenCalled();
|
|
90
|
-
(0, vitest_1.expect)(adapter.isConnected()).toBe(true);
|
|
91
|
-
await adapter.disconnect();
|
|
92
|
-
(0, vitest_1.expect)(mongoClose).toHaveBeenCalled();
|
|
93
|
-
(0, vitest_1.expect)(adapter.isConnected()).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
(0, vitest_1.it)('should execute find query via object syntax', async () => {
|
|
96
|
-
await adapter.connect();
|
|
97
|
-
await adapter.query({ collection: 'users', action: 'find', filter: { id: 1 } });
|
|
98
|
-
(0, vitest_1.expect)(mongoFind).toHaveBeenCalledWith({ id: 1 });
|
|
99
|
-
});
|
|
100
|
-
(0, vitest_1.it)('should execute find query via string syntax', async () => {
|
|
101
|
-
await adapter.connect();
|
|
102
|
-
await adapter.query('users.find', [{ id: 1 }]);
|
|
103
|
-
(0, vitest_1.expect)(mongoFind).toHaveBeenCalledWith({ id: 1 });
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/tests/batch.test.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const server_1 = require("../src/core/server");
|
|
5
|
-
const manager_1 = require("../src/database/manager");
|
|
6
|
-
const memory_1 = require("../src/database/adapters/memory");
|
|
7
|
-
(0, vitest_1.describe)('Batch Engine & Query Fusion', () => {
|
|
8
|
-
let app;
|
|
9
|
-
let dbManager;
|
|
10
|
-
(0, vitest_1.beforeEach)(async () => {
|
|
11
|
-
manager_1.DatabaseManager.registerAdapter('memory', memory_1.MemoryAdapter);
|
|
12
|
-
dbManager = new manager_1.DatabaseManager({
|
|
13
|
-
default: 'main',
|
|
14
|
-
connections: {
|
|
15
|
-
main: { type: 'memory', database: 'test_db' }
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
await dbManager.connect();
|
|
19
|
-
app = new server_1.QHTTPX({
|
|
20
|
-
database: dbManager,
|
|
21
|
-
enableBatching: true
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
(0, vitest_1.afterEach)(async () => {
|
|
25
|
-
await app.close();
|
|
26
|
-
await dbManager.disconnect();
|
|
27
|
-
});
|
|
28
|
-
(0, vitest_1.it)('should execute multiple operations in a single batch request', async () => {
|
|
29
|
-
// Define ops
|
|
30
|
-
app.op('math.add', async (params) => {
|
|
31
|
-
return params.a + params.b;
|
|
32
|
-
});
|
|
33
|
-
app.op('math.multiply', async (params) => {
|
|
34
|
-
return params.a * params.b;
|
|
35
|
-
});
|
|
36
|
-
const { port } = await app.listen(0);
|
|
37
|
-
const batch = {
|
|
38
|
-
batch: [
|
|
39
|
-
{ op: 'math.add', params: { a: 5, b: 3 }, id: 1 },
|
|
40
|
-
{ op: 'math.multiply', params: { a: 4, b: 2 }, id: 2 }
|
|
41
|
-
]
|
|
42
|
-
};
|
|
43
|
-
const response = await fetch(`http://localhost:${port}/qhttpx`, {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
headers: { 'Content-Type': 'application/json' },
|
|
46
|
-
body: JSON.stringify(batch)
|
|
47
|
-
});
|
|
48
|
-
const data = await response.json();
|
|
49
|
-
(0, vitest_1.expect)(data.results).toHaveLength(2);
|
|
50
|
-
(0, vitest_1.expect)(data.results[0]).toEqual({ result: 8, id: 1 });
|
|
51
|
-
(0, vitest_1.expect)(data.results[1]).toEqual({ result: 8, id: 2 });
|
|
52
|
-
});
|
|
53
|
-
(0, vitest_1.it)('should fuse database queries', async () => {
|
|
54
|
-
// Setup spy on the adapter's query method to verify fusion
|
|
55
|
-
const adapter = dbManager.get('main');
|
|
56
|
-
const querySpy = vitest_1.vi.spyOn(adapter, 'query');
|
|
57
|
-
// Define an op that uses DB
|
|
58
|
-
app.op('getUser', async (params, ctx) => {
|
|
59
|
-
// Simulate simple query that matches our coalescer pattern
|
|
60
|
-
// "SELECT * FROM users WHERE id = ?"
|
|
61
|
-
if (!ctx.db)
|
|
62
|
-
throw new Error('No DB');
|
|
63
|
-
const result = await ctx.db.get().query('SELECT * FROM users WHERE id = ?', [params.id]);
|
|
64
|
-
return result;
|
|
65
|
-
});
|
|
66
|
-
const { port } = await app.listen(0);
|
|
67
|
-
// Prepare batch with 3 requests
|
|
68
|
-
const batch = {
|
|
69
|
-
batch: [
|
|
70
|
-
{ op: 'getUser', params: { id: 1 }, id: 'req1' },
|
|
71
|
-
{ op: 'getUser', params: { id: 2 }, id: 'req2' },
|
|
72
|
-
{ op: 'getUser', params: { id: 3 }, id: 'req3' }
|
|
73
|
-
]
|
|
74
|
-
};
|
|
75
|
-
// Mock DB response for the FUSED query
|
|
76
|
-
// The coalescer will generate: "SELECT * FROM users WHERE id IN (?, ?, ?)"
|
|
77
|
-
// We need to mock the implementation to return data that the coalescer can map back.
|
|
78
|
-
// The memory adapter handles basic queries, but does it handle "IN"?
|
|
79
|
-
// The current MemoryAdapter likely DOES NOT support SQL parsing.
|
|
80
|
-
// So we need to mock the query method to return expected data for the fused query.
|
|
81
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
-
querySpy.mockImplementation(async (query) => {
|
|
83
|
-
if (typeof query === 'string' && query.includes('IN')) {
|
|
84
|
-
// This is the fused query
|
|
85
|
-
// Return dummy users
|
|
86
|
-
return [
|
|
87
|
-
{ id: 1, name: 'User 1' },
|
|
88
|
-
{ id: 2, name: 'User 2' },
|
|
89
|
-
{ id: 3, name: 'User 3' }
|
|
90
|
-
];
|
|
91
|
-
}
|
|
92
|
-
return [];
|
|
93
|
-
});
|
|
94
|
-
const response = await fetch(`http://localhost:${port}/qhttpx`, {
|
|
95
|
-
method: 'POST',
|
|
96
|
-
headers: { 'Content-Type': 'application/json' },
|
|
97
|
-
body: JSON.stringify(batch)
|
|
98
|
-
});
|
|
99
|
-
const data = await response.json();
|
|
100
|
-
// Verification
|
|
101
|
-
// 1. Check results
|
|
102
|
-
(0, vitest_1.expect)(data.results).toHaveLength(3);
|
|
103
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
|
-
(0, vitest_1.expect)(data.results.find((r) => r.id === 'req1').result).toEqual([{ id: 1, name: 'User 1' }]);
|
|
105
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
-
(0, vitest_1.expect)(data.results.find((r) => r.id === 'req2').result).toEqual([{ id: 2, name: 'User 2' }]);
|
|
107
|
-
// 2. Check that query was called ONCE with fused SQL
|
|
108
|
-
// The spy calls should show 1 call with "IN" and maybe others if coalescer failed?
|
|
109
|
-
// Wait, the coalescer calls the ADAPTER.
|
|
110
|
-
// Filter calls that look like the fused one
|
|
111
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
112
|
-
const fusedCalls = querySpy.mock.calls.filter((args) => args[0].includes('IN'));
|
|
113
|
-
(0, vitest_1.expect)(fusedCalls.length).toBe(1);
|
|
114
|
-
const individualCalls = querySpy.mock.calls.filter(args => args[0].includes('= ?'));
|
|
115
|
-
(0, vitest_1.expect)(individualCalls.length).toBe(0); // Should be 0 if fusion worked
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const http_1 = require("http");
|
|
5
|
-
const net_1 = require("net");
|
|
6
|
-
const body_parser_1 = require("../src/core/body-parser");
|
|
7
|
-
// Mock IncomingMessage
|
|
8
|
-
class MockRequest extends http_1.IncomingMessage {
|
|
9
|
-
constructor(method, headers, body) {
|
|
10
|
-
super(new net_1.Socket());
|
|
11
|
-
this.method = method;
|
|
12
|
-
this.headers = headers;
|
|
13
|
-
if (body) {
|
|
14
|
-
this.push(body);
|
|
15
|
-
this.push(null);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
this.push(null);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
(0, vitest_1.describe)('BodyParser', () => {
|
|
23
|
-
(0, vitest_1.it)('parses JSON body', async () => {
|
|
24
|
-
const req = new MockRequest('POST', { 'content-type': 'application/json' }, JSON.stringify({ foo: 'bar' }));
|
|
25
|
-
const parsed = await body_parser_1.BodyParser.parse(req);
|
|
26
|
-
(0, vitest_1.expect)(parsed.body).toEqual({ foo: 'bar' });
|
|
27
|
-
});
|
|
28
|
-
(0, vitest_1.it)('parses urlencoded body', async () => {
|
|
29
|
-
const req = new MockRequest('POST', { 'content-type': 'application/x-www-form-urlencoded' }, 'foo=bar&baz=123');
|
|
30
|
-
const parsed = await body_parser_1.BodyParser.parse(req);
|
|
31
|
-
(0, vitest_1.expect)(parsed.body).toEqual({ foo: 'bar', baz: '123' });
|
|
32
|
-
});
|
|
33
|
-
(0, vitest_1.it)('returns buffer for unknown content type', async () => {
|
|
34
|
-
const req = new MockRequest('POST', { 'content-type': 'application/octet-stream' }, 'hello world');
|
|
35
|
-
const parsed = await body_parser_1.BodyParser.parse(req);
|
|
36
|
-
(0, vitest_1.expect)(parsed.body).toBeInstanceOf(Buffer);
|
|
37
|
-
(0, vitest_1.expect)(parsed.body.toString()).toBe('hello world');
|
|
38
|
-
});
|
|
39
|
-
(0, vitest_1.it)('throws error when body too large', async () => {
|
|
40
|
-
const req = new MockRequest('POST', { 'content-type': 'text/plain' }, '1234567890');
|
|
41
|
-
await (0, vitest_1.expect)(body_parser_1.BodyParser.parse(req, { maxBodyBytes: 5 })).rejects.toThrow('QHTTPX_BODY_TOO_LARGE');
|
|
42
|
-
});
|
|
43
|
-
(0, vitest_1.it)('throws error for invalid JSON', async () => {
|
|
44
|
-
const req = new MockRequest('POST', { 'content-type': 'application/json' }, '{ invalid json');
|
|
45
|
-
await (0, vitest_1.expect)(body_parser_1.BodyParser.parse(req)).rejects.toThrow('QHTTPX_INVALID_JSON');
|
|
46
|
-
});
|
|
47
|
-
(0, vitest_1.it)('ignores GET requests', async () => {
|
|
48
|
-
const req = new MockRequest('GET', {});
|
|
49
|
-
const parsed = await body_parser_1.BodyParser.parse(req);
|
|
50
|
-
(0, vitest_1.expect)(parsed.body).toBeUndefined();
|
|
51
|
-
});
|
|
52
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,87 +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
|
-
const vitest_1 = require("vitest");
|
|
7
|
-
const src_1 = require("../src");
|
|
8
|
-
const zlib_1 = __importDefault(require("zlib"));
|
|
9
|
-
const http_1 = __importDefault(require("http"));
|
|
10
|
-
(0, vitest_1.describe)('Compression Middleware', () => {
|
|
11
|
-
(0, vitest_1.it)('should compress JSON response with gzip', async () => {
|
|
12
|
-
const app = new src_1.QHTTPX();
|
|
13
|
-
app.use((0, src_1.createCompressionMiddleware)());
|
|
14
|
-
const bigData = { data: 'a'.repeat(10000) };
|
|
15
|
-
app.get('/gzip', (ctx) => ctx.json(bigData));
|
|
16
|
-
const { port } = await app.listen(0, '127.0.0.1');
|
|
17
|
-
// Use http.request to avoid automatic decompression by fetch
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
-
const response = await new Promise((resolve, reject) => {
|
|
20
|
-
const req = http_1.default.request(`http://127.0.0.1:${port}/gzip`, {
|
|
21
|
-
headers: { 'Accept-Encoding': 'gzip' }
|
|
22
|
-
}, (res) => {
|
|
23
|
-
const chunks = [];
|
|
24
|
-
res.on('data', (chunk) => chunks.push(chunk));
|
|
25
|
-
res.on('end', () => resolve({ headers: res.headers, data: Buffer.concat(chunks) }));
|
|
26
|
-
res.on('error', reject);
|
|
27
|
-
});
|
|
28
|
-
req.end();
|
|
29
|
-
});
|
|
30
|
-
(0, vitest_1.expect)(response.headers['content-encoding']).toBe('gzip');
|
|
31
|
-
(0, vitest_1.expect)(response.headers['vary']).toContain('Accept-Encoding');
|
|
32
|
-
const decompressed = zlib_1.default.gunzipSync(response.data);
|
|
33
|
-
const json = JSON.parse(decompressed.toString());
|
|
34
|
-
(0, vitest_1.expect)(json).toEqual(bigData);
|
|
35
|
-
await app.close();
|
|
36
|
-
});
|
|
37
|
-
(0, vitest_1.it)('should not compress small response if threshold is high', async () => {
|
|
38
|
-
const app = new src_1.QHTTPX();
|
|
39
|
-
app.use((0, src_1.createCompressionMiddleware)({ threshold: 1000 }));
|
|
40
|
-
app.get('/small', (ctx) => {
|
|
41
|
-
ctx.json({ data: 'small' });
|
|
42
|
-
});
|
|
43
|
-
const { port } = await app.listen(0, '127.0.0.1');
|
|
44
|
-
const response = await fetch(`http://127.0.0.1:${port}/small`, {
|
|
45
|
-
headers: { 'Accept-Encoding': 'gzip' },
|
|
46
|
-
});
|
|
47
|
-
(0, vitest_1.expect)(response.status).toBe(200);
|
|
48
|
-
// Should NOT have content-encoding because it's too small
|
|
49
|
-
(0, vitest_1.expect)(response.headers.get('content-encoding')).toBeNull();
|
|
50
|
-
await app.close();
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
(0, vitest_1.describe)('Server-Sent Events (SSE)', () => {
|
|
54
|
-
(0, vitest_1.it)('should stream events', async () => {
|
|
55
|
-
const app = new src_1.QHTTPX();
|
|
56
|
-
// app.use(createCompressionMiddleware()); // Disable compression for SSE test to debug
|
|
57
|
-
app.get('/sse', (ctx) => {
|
|
58
|
-
const stream = (0, src_1.createSSE)(ctx);
|
|
59
|
-
let count = 0;
|
|
60
|
-
const interval = setInterval(() => {
|
|
61
|
-
count++;
|
|
62
|
-
stream.send({ count }, 'ping');
|
|
63
|
-
if (count >= 3) {
|
|
64
|
-
clearInterval(interval);
|
|
65
|
-
stream.close();
|
|
66
|
-
}
|
|
67
|
-
}, 50);
|
|
68
|
-
});
|
|
69
|
-
const { port } = await app.listen(0, '127.0.0.1');
|
|
70
|
-
const response = await new Promise((resolve, reject) => {
|
|
71
|
-
const req = http_1.default.request(`http://127.0.0.1:${port}/sse`, (res) => {
|
|
72
|
-
let data = '';
|
|
73
|
-
res.on('data', (chunk) => {
|
|
74
|
-
data += chunk.toString();
|
|
75
|
-
});
|
|
76
|
-
res.on('end', () => resolve(data));
|
|
77
|
-
res.on('error', reject);
|
|
78
|
-
});
|
|
79
|
-
req.end();
|
|
80
|
-
});
|
|
81
|
-
(0, vitest_1.expect)(response).toContain('event: ping');
|
|
82
|
-
(0, vitest_1.expect)(response).toContain('data: {"count":1}');
|
|
83
|
-
(0, vitest_1.expect)(response).toContain('data: {"count":2}');
|
|
84
|
-
(0, vitest_1.expect)(response).toContain('data: {"count":3}');
|
|
85
|
-
await app.close();
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|