qhttpx 1.9.1 → 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +79 -17
  3. package/dist/examples/api-server.d.ts +1 -0
  4. package/dist/examples/api-server.js +77 -0
  5. package/dist/examples/basic.d.ts +1 -0
  6. package/dist/examples/basic.js +10 -0
  7. package/dist/examples/compression.d.ts +1 -0
  8. package/dist/examples/compression.js +17 -0
  9. package/dist/examples/cors.d.ts +1 -0
  10. package/dist/examples/cors.js +19 -0
  11. package/dist/examples/errors.d.ts +1 -0
  12. package/dist/examples/errors.js +25 -0
  13. package/dist/examples/file-upload.d.ts +1 -0
  14. package/dist/examples/file-upload.js +24 -0
  15. package/dist/examples/fusion.d.ts +1 -0
  16. package/dist/examples/fusion.js +21 -0
  17. package/dist/examples/rate-limiting.d.ts +1 -0
  18. package/dist/examples/rate-limiting.js +17 -0
  19. package/dist/examples/validation.d.ts +1 -0
  20. package/dist/examples/validation.js +23 -0
  21. package/dist/examples/websockets.d.ts +1 -0
  22. package/dist/examples/websockets.js +20 -0
  23. package/dist/package.json +112 -0
  24. package/dist/src/benchmarks/compare-frameworks.js +119 -0
  25. package/dist/src/benchmarks/compare.d.ts +1 -0
  26. package/dist/src/benchmarks/compare.js +288 -0
  27. package/dist/src/benchmarks/quantam-users.d.ts +1 -0
  28. package/dist/src/benchmarks/quantam-users.js +56 -0
  29. package/dist/src/benchmarks/simple-json.d.ts +1 -0
  30. package/dist/src/benchmarks/simple-json.js +60 -0
  31. package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
  32. package/dist/src/benchmarks/ultra-mode.js +94 -0
  33. package/dist/src/buffer-pool.js +70 -0
  34. package/dist/src/cli/index.d.ts +2 -0
  35. package/dist/src/cli/index.js +222 -0
  36. package/dist/src/client/index.d.ts +17 -0
  37. package/dist/src/client/index.js +72 -0
  38. package/dist/src/config.js +50 -0
  39. package/dist/src/cookies.js +59 -0
  40. package/dist/src/core/batch.d.ts +24 -0
  41. package/dist/src/core/batch.js +97 -0
  42. package/dist/src/core/body-parser.d.ts +15 -0
  43. package/dist/src/core/body-parser.js +121 -0
  44. package/dist/src/core/buffer-pool.d.ts +41 -0
  45. package/dist/src/core/buffer-pool.js +70 -0
  46. package/dist/src/core/config.d.ts +7 -0
  47. package/dist/src/core/config.js +50 -0
  48. package/dist/src/core/errors.d.ts +34 -0
  49. package/dist/src/core/errors.js +70 -0
  50. package/dist/src/core/fusion.d.ts +20 -0
  51. package/dist/src/core/fusion.js +193 -0
  52. package/dist/src/core/logger.d.ts +22 -0
  53. package/dist/src/core/logger.js +49 -0
  54. package/dist/src/core/metrics.d.ts +48 -0
  55. package/dist/src/core/metrics.js +117 -0
  56. package/dist/src/core/native-adapter.d.ts +11 -0
  57. package/dist/src/core/native-adapter.js +211 -0
  58. package/dist/src/core/resources.d.ts +9 -0
  59. package/dist/src/core/resources.js +25 -0
  60. package/dist/src/core/scheduler.d.ts +34 -0
  61. package/dist/src/core/scheduler.js +85 -0
  62. package/dist/src/core/scope.d.ts +26 -0
  63. package/dist/src/core/scope.js +68 -0
  64. package/dist/src/core/serializer.d.ts +10 -0
  65. package/dist/src/core/serializer.js +44 -0
  66. package/dist/src/core/server.d.ts +138 -0
  67. package/dist/src/core/server.js +1082 -0
  68. package/dist/src/core/stream.d.ts +15 -0
  69. package/dist/src/core/stream.js +71 -0
  70. package/dist/src/core/tasks.d.ts +29 -0
  71. package/dist/src/core/tasks.js +87 -0
  72. package/dist/src/core/types.d.ts +173 -0
  73. package/dist/src/core/types.js +19 -0
  74. package/dist/src/core/websocket.d.ts +25 -0
  75. package/dist/src/core/websocket.js +86 -0
  76. package/dist/src/core/worker-queue.d.ts +41 -0
  77. package/dist/src/core/worker-queue.js +73 -0
  78. package/dist/src/cors.js +66 -0
  79. package/dist/src/database/adapters/memory.d.ts +21 -0
  80. package/dist/src/database/adapters/memory.js +90 -0
  81. package/dist/src/database/adapters/mongo.d.ts +11 -0
  82. package/dist/src/database/adapters/mongo.js +141 -0
  83. package/dist/src/database/adapters/postgres.d.ts +10 -0
  84. package/dist/src/database/adapters/postgres.js +111 -0
  85. package/dist/src/database/adapters/sqlite.d.ts +10 -0
  86. package/dist/src/database/adapters/sqlite.js +42 -0
  87. package/dist/src/database/coalescer.d.ts +14 -0
  88. package/dist/src/database/coalescer.js +134 -0
  89. package/dist/src/database/manager.d.ts +35 -0
  90. package/dist/src/database/manager.js +87 -0
  91. package/dist/src/database/types.d.ts +20 -0
  92. package/dist/src/database/types.js +2 -0
  93. package/dist/src/index.d.ts +50 -0
  94. package/dist/src/index.js +91 -0
  95. package/dist/src/logger.js +45 -0
  96. package/dist/src/metrics.js +111 -0
  97. package/dist/src/middleware/compression.d.ts +2 -0
  98. package/dist/src/middleware/compression.js +133 -0
  99. package/dist/src/middleware/cors.d.ts +2 -0
  100. package/dist/src/middleware/cors.js +66 -0
  101. package/dist/src/middleware/presets.d.ts +16 -0
  102. package/dist/src/middleware/presets.js +52 -0
  103. package/dist/src/middleware/rate-limit.d.ts +14 -0
  104. package/dist/src/middleware/rate-limit.js +83 -0
  105. package/dist/src/middleware/security.d.ts +21 -0
  106. package/dist/src/middleware/security.js +69 -0
  107. package/dist/src/middleware/static.d.ts +11 -0
  108. package/dist/src/middleware/static.js +191 -0
  109. package/dist/src/native/index.d.ts +32 -0
  110. package/dist/src/native/index.js +141 -0
  111. package/dist/src/openapi/generator.d.ts +19 -0
  112. package/dist/src/openapi/generator.js +149 -0
  113. package/dist/src/presets.js +33 -0
  114. package/dist/src/radix-router.js +89 -0
  115. package/dist/src/radix-tree.js +81 -0
  116. package/dist/src/resources.js +25 -0
  117. package/dist/src/router/radix-router.d.ts +18 -0
  118. package/dist/src/router/radix-router.js +89 -0
  119. package/dist/src/router/radix-tree.d.ts +18 -0
  120. package/dist/src/router/radix-tree.js +131 -0
  121. package/dist/src/router/router.d.ts +34 -0
  122. package/dist/src/router/router.js +186 -0
  123. package/dist/src/router.js +138 -0
  124. package/dist/src/scheduler.js +85 -0
  125. package/dist/src/security.js +69 -0
  126. package/dist/src/server.js +685 -0
  127. package/dist/src/signals.js +31 -0
  128. package/dist/src/static.js +107 -0
  129. package/dist/src/stream.js +71 -0
  130. package/dist/src/tasks.js +87 -0
  131. package/dist/src/testing/index.d.ts +25 -0
  132. package/dist/src/testing/index.js +84 -0
  133. package/dist/src/testing.js +40 -0
  134. package/dist/src/types.js +19 -0
  135. package/dist/src/utils/cookies.d.ts +3 -0
  136. package/dist/src/utils/cookies.js +59 -0
  137. package/dist/src/utils/logger.d.ts +12 -0
  138. package/dist/src/utils/logger.js +45 -0
  139. package/dist/src/utils/signals.d.ts +6 -0
  140. package/dist/src/utils/signals.js +31 -0
  141. package/dist/src/utils/sse.d.ts +6 -0
  142. package/dist/src/utils/sse.js +32 -0
  143. package/dist/src/utils/testing.js +40 -0
  144. package/dist/src/validation/index.d.ts +3 -0
  145. package/dist/src/validation/index.js +19 -0
  146. package/dist/src/validation/simple.d.ts +5 -0
  147. package/dist/src/validation/simple.js +102 -0
  148. package/dist/src/validation/types.d.ts +32 -0
  149. package/dist/src/validation/types.js +12 -0
  150. package/dist/src/validation/zod.d.ts +4 -0
  151. package/dist/src/validation/zod.js +18 -0
  152. package/dist/src/views/index.d.ts +1 -0
  153. package/dist/src/views/index.js +17 -0
  154. package/dist/src/views/types.d.ts +3 -0
  155. package/dist/src/views/types.js +2 -0
  156. package/dist/src/worker-queue.js +73 -0
  157. package/dist/tests/adapters.test.d.ts +1 -0
  158. package/dist/tests/adapters.test.js +106 -0
  159. package/dist/tests/batch.test.d.ts +1 -0
  160. package/dist/tests/batch.test.js +117 -0
  161. package/dist/tests/body-parser.test.d.ts +1 -0
  162. package/dist/tests/body-parser.test.js +52 -0
  163. package/dist/tests/compression-sse.test.d.ts +1 -0
  164. package/dist/tests/compression-sse.test.js +87 -0
  165. package/dist/tests/cookies.test.d.ts +1 -0
  166. package/dist/tests/cookies.test.js +63 -0
  167. package/dist/tests/cors.test.d.ts +1 -0
  168. package/dist/tests/cors.test.js +55 -0
  169. package/dist/tests/database.test.d.ts +1 -0
  170. package/dist/tests/database.test.js +80 -0
  171. package/dist/tests/dx.test.d.ts +1 -0
  172. package/dist/tests/dx.test.js +114 -0
  173. package/dist/tests/ecosystem.test.d.ts +1 -0
  174. package/dist/tests/ecosystem.test.js +133 -0
  175. package/dist/tests/features.test.d.ts +1 -0
  176. package/dist/tests/features.test.js +47 -0
  177. package/dist/tests/fusion.test.d.ts +1 -0
  178. package/dist/tests/fusion.test.js +92 -0
  179. package/dist/tests/http-basic.test.d.ts +1 -0
  180. package/dist/tests/http-basic.test.js +124 -0
  181. package/dist/tests/logger.test.d.ts +1 -0
  182. package/dist/tests/logger.test.js +33 -0
  183. package/dist/tests/middleware.test.d.ts +1 -0
  184. package/dist/tests/middleware.test.js +109 -0
  185. package/dist/tests/native-adapter.test.d.ts +1 -0
  186. package/dist/tests/native-adapter.test.js +71 -0
  187. package/dist/tests/observability.test.d.ts +1 -0
  188. package/dist/tests/observability.test.js +59 -0
  189. package/dist/tests/openapi.test.d.ts +1 -0
  190. package/dist/tests/openapi.test.js +64 -0
  191. package/dist/tests/plugin.test.d.ts +1 -0
  192. package/dist/tests/plugin.test.js +65 -0
  193. package/dist/tests/plugins.test.d.ts +1 -0
  194. package/dist/tests/plugins.test.js +71 -0
  195. package/dist/tests/rate-limit.test.d.ts +1 -0
  196. package/dist/tests/rate-limit.test.js +77 -0
  197. package/dist/tests/resources.test.d.ts +1 -0
  198. package/dist/tests/resources.test.js +47 -0
  199. package/dist/tests/scheduler.test.d.ts +1 -0
  200. package/dist/tests/scheduler.test.js +46 -0
  201. package/dist/tests/schema-routes.test.d.ts +1 -0
  202. package/dist/tests/schema-routes.test.js +77 -0
  203. package/dist/tests/security.test.d.ts +1 -0
  204. package/dist/tests/security.test.js +83 -0
  205. package/dist/tests/server-db.test.d.ts +1 -0
  206. package/dist/tests/server-db.test.js +72 -0
  207. package/dist/tests/smoke.test.d.ts +1 -0
  208. package/dist/tests/smoke.test.js +10 -0
  209. package/dist/tests/sqlite-fusion.test.d.ts +1 -0
  210. package/dist/tests/sqlite-fusion.test.js +92 -0
  211. package/dist/tests/static.test.d.ts +1 -0
  212. package/dist/tests/static.test.js +102 -0
  213. package/dist/tests/stream.test.d.ts +1 -0
  214. package/dist/tests/stream.test.js +44 -0
  215. package/dist/tests/task-metrics.test.d.ts +1 -0
  216. package/dist/tests/task-metrics.test.js +53 -0
  217. package/dist/tests/tasks.test.d.ts +1 -0
  218. package/dist/tests/tasks.test.js +62 -0
  219. package/dist/tests/testing.test.d.ts +1 -0
  220. package/dist/tests/testing.test.js +47 -0
  221. package/dist/tests/validation.test.d.ts +1 -0
  222. package/dist/tests/validation.test.js +107 -0
  223. package/dist/tests/websocket.test.d.ts +1 -0
  224. package/dist/tests/websocket.test.js +146 -0
  225. package/dist/vitest.config.d.ts +2 -0
  226. package/dist/vitest.config.js +9 -0
  227. package/docs/FUSION.md +19 -0
  228. package/package.json +4 -15
  229. package/prebuilds/darwin-arm64/qhttpx.node +0 -0
  230. package/prebuilds/linux-x64/qhttpx.node +0 -0
  231. package/prebuilds/win32-x64/qhttpx.node +0 -0
  232. package/scripts/install-native.js +26 -0
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatabaseManager = void 0;
4
+ class DatabaseManager {
5
+ constructor(config) {
6
+ this.config = config;
7
+ this.connections = new Map();
8
+ }
9
+ /**
10
+ * Manually register an already initialized adapter instance
11
+ * @param name The connection name
12
+ * @param adapter The initialized adapter instance
13
+ */
14
+ registerConnection(name, adapter) {
15
+ this.connections.set(name, adapter);
16
+ }
17
+ /**
18
+ * Register a new database adapter type
19
+ * @param type The type identifier (e.g., 'postgres', 'mysql', 'mongo')
20
+ * @param adapter The adapter class
21
+ */
22
+ static registerAdapter(type, adapter) {
23
+ DatabaseManager.adapterRegistry.set(type, adapter);
24
+ }
25
+ /**
26
+ * Connect to a specific database or the default one
27
+ * @param name Connection name from config
28
+ */
29
+ async connect(name) {
30
+ const connectionName = name || this.config.default;
31
+ if (!connectionName) {
32
+ throw new Error('No connection name provided and no default connection configured.');
33
+ }
34
+ // Return existing connection if available and connected
35
+ const existingConnection = this.connections.get(connectionName);
36
+ if (existingConnection && existingConnection.isConnected()) {
37
+ return existingConnection;
38
+ }
39
+ const dbConfig = this.config.connections[connectionName];
40
+ if (!dbConfig) {
41
+ throw new Error(`Connection configuration for '${connectionName}' not found.`);
42
+ }
43
+ const AdapterClass = DatabaseManager.adapterRegistry.get(dbConfig.type);
44
+ if (!AdapterClass) {
45
+ throw new Error(`No adapter registered for database type '${dbConfig.type}'.`);
46
+ }
47
+ const adapter = new AdapterClass(dbConfig);
48
+ await adapter.connect();
49
+ this.connections.set(connectionName, adapter);
50
+ return adapter;
51
+ }
52
+ /**
53
+ * Disconnect a specific connection or all connections
54
+ * @param name Connection name (optional). If not provided, disconnects all.
55
+ */
56
+ async disconnect(name) {
57
+ if (name) {
58
+ const adapter = this.connections.get(name);
59
+ if (adapter) {
60
+ await adapter.disconnect();
61
+ this.connections.delete(name);
62
+ }
63
+ }
64
+ else {
65
+ const promises = Array.from(this.connections.values()).map(adapter => adapter.disconnect());
66
+ await Promise.all(promises);
67
+ this.connections.clear();
68
+ }
69
+ }
70
+ /**
71
+ * Get an active connection
72
+ * @param name Connection name
73
+ */
74
+ get(name) {
75
+ const connectionName = name || this.config.default;
76
+ if (!connectionName) {
77
+ throw new Error('No connection name provided and no default connection configured.');
78
+ }
79
+ const adapter = this.connections.get(connectionName);
80
+ if (!adapter) {
81
+ throw new Error(`Connection '${connectionName}' is not active. Call connect() first.`);
82
+ }
83
+ return adapter;
84
+ }
85
+ }
86
+ exports.DatabaseManager = DatabaseManager;
87
+ DatabaseManager.adapterRegistry = new Map();
@@ -0,0 +1,20 @@
1
+ export interface DatabaseConfig {
2
+ type: string;
3
+ url?: string;
4
+ host?: string;
5
+ port?: number;
6
+ username?: string;
7
+ password?: string;
8
+ database?: string;
9
+ options?: Record<string, any>;
10
+ }
11
+ export interface DatabaseAdapter {
12
+ connect(): Promise<void>;
13
+ disconnect(): Promise<void>;
14
+ query<T = any>(query: string | object, params?: any[]): Promise<T>;
15
+ isConnected(): boolean;
16
+ }
17
+ export interface DatabaseEngineOptions {
18
+ default?: string;
19
+ connections: Record<string, DatabaseConfig>;
20
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,50 @@
1
+ import { QHTTPX } from './core/server';
2
+ import type { QHTTPXOptions } from './core/types';
3
+ import { NativeAdapter } from './core/native-adapter';
4
+ export { QHTTPX } from './core/server';
5
+ export * from './core/types';
6
+ export * from './core/errors';
7
+ export * from './middleware/cors';
8
+ export { createSecurityHeadersMiddleware, type SecurityHeadersOptions, createSecureDefaults, type SecureDefaultsOptions, } from './middleware/security';
9
+ export * from './middleware/rate-limit';
10
+ export * from './middleware/static';
11
+ export * from './middleware/compression';
12
+ export * from './core/stream';
13
+ export * from './utils/logger';
14
+ export { BufferPool, BufferPoolConfig } from './core/buffer-pool';
15
+ export * from './testing';
16
+ export * from './utils/signals';
17
+ export * from './middleware/presets';
18
+ export * from './utils/cookies';
19
+ export * from './utils/sse';
20
+ export { fastJsonStringify, getStringifier } from './core/serializer';
21
+ export * from './database/types';
22
+ export * from './database/manager';
23
+ export * from './database/adapters/memory';
24
+ export * from './views';
25
+ export * from './validation';
26
+ export * from './database/adapters/sqlite';
27
+ export * from './database/adapters/postgres';
28
+ export * from './database/adapters/mongo';
29
+ export * from './core/fusion';
30
+ export * from './validation/types';
31
+ export * from './validation/simple';
32
+ export * from './openapi/generator';
33
+ export * from './client';
34
+ export { NativeAdapter } from './core/native-adapter';
35
+ export declare function createHttpApp(options?: QHTTPXOptions): QHTTPX;
36
+ export declare function createNativeApp(options?: QHTTPXOptions): NativeAdapter;
37
+ /**
38
+ * Singleton instance for quick start
39
+ * @example
40
+ * import { app } from 'qhttpx';
41
+ * app.get('/', ({ json }) => json({ hello: 'world' }));
42
+ */
43
+ export declare const app: QHTTPX;
44
+ /**
45
+ * Default export for simplified usage
46
+ * @example
47
+ * import QHTTPX from 'qhttpx';
48
+ * const app = QHTTPX();
49
+ */
50
+ export default createHttpApp;
@@ -0,0 +1,91 @@
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
+ exports.app = exports.NativeAdapter = exports.getStringifier = exports.fastJsonStringify = exports.BufferPool = exports.createSecureDefaults = exports.createSecurityHeadersMiddleware = exports.QHTTPX = void 0;
18
+ exports.createHttpApp = createHttpApp;
19
+ exports.createNativeApp = createNativeApp;
20
+ const server_1 = require("./core/server");
21
+ const presets_1 = require("./middleware/presets");
22
+ const native_adapter_1 = require("./core/native-adapter");
23
+ var server_2 = require("./core/server");
24
+ Object.defineProperty(exports, "QHTTPX", { enumerable: true, get: function () { return server_2.QHTTPX; } });
25
+ __exportStar(require("./core/types"), exports);
26
+ __exportStar(require("./core/errors"), exports);
27
+ __exportStar(require("./middleware/cors"), exports);
28
+ var security_1 = require("./middleware/security");
29
+ Object.defineProperty(exports, "createSecurityHeadersMiddleware", { enumerable: true, get: function () { return security_1.createSecurityHeadersMiddleware; } });
30
+ Object.defineProperty(exports, "createSecureDefaults", { enumerable: true, get: function () { return security_1.createSecureDefaults; } });
31
+ __exportStar(require("./middleware/rate-limit"), exports);
32
+ __exportStar(require("./middleware/static"), exports);
33
+ __exportStar(require("./middleware/compression"), exports);
34
+ __exportStar(require("./core/stream"), exports);
35
+ __exportStar(require("./utils/logger"), exports);
36
+ var buffer_pool_1 = require("./core/buffer-pool");
37
+ Object.defineProperty(exports, "BufferPool", { enumerable: true, get: function () { return buffer_pool_1.BufferPool; } });
38
+ __exportStar(require("./testing"), exports);
39
+ __exportStar(require("./utils/signals"), exports);
40
+ __exportStar(require("./middleware/presets"), exports);
41
+ __exportStar(require("./utils/cookies"), exports);
42
+ __exportStar(require("./utils/sse"), exports);
43
+ var serializer_1 = require("./core/serializer");
44
+ Object.defineProperty(exports, "fastJsonStringify", { enumerable: true, get: function () { return serializer_1.fastJsonStringify; } });
45
+ Object.defineProperty(exports, "getStringifier", { enumerable: true, get: function () { return serializer_1.getStringifier; } });
46
+ __exportStar(require("./database/types"), exports);
47
+ __exportStar(require("./database/manager"), exports);
48
+ __exportStar(require("./database/adapters/memory"), exports);
49
+ __exportStar(require("./views"), exports);
50
+ __exportStar(require("./validation"), exports);
51
+ __exportStar(require("./database/adapters/sqlite"), exports);
52
+ __exportStar(require("./database/adapters/postgres"), exports);
53
+ __exportStar(require("./database/adapters/mongo"), exports);
54
+ __exportStar(require("./core/fusion"), exports);
55
+ __exportStar(require("./validation/types"), exports);
56
+ __exportStar(require("./validation/simple"), exports);
57
+ __exportStar(require("./openapi/generator"), exports);
58
+ __exportStar(require("./client"), exports);
59
+ var native_adapter_2 = require("./core/native-adapter");
60
+ Object.defineProperty(exports, "NativeAdapter", { enumerable: true, get: function () { return native_adapter_2.NativeAdapter; } });
61
+ function createHttpApp(options = {}) {
62
+ const app = new server_1.QHTTPX(options);
63
+ // Skip middleware in ultra mode for maximum performance
64
+ if (options.performanceMode !== 'ultra') {
65
+ const middlewares = (0, presets_1.createApiPreset)({
66
+ rateLimit: options.rateLimit,
67
+ cors: options.cors,
68
+ compression: options.compression,
69
+ });
70
+ middlewares.forEach((mw) => app.use(mw));
71
+ }
72
+ return app;
73
+ }
74
+ function createNativeApp(options = {}) {
75
+ const app = createHttpApp(options);
76
+ return new native_adapter_1.NativeAdapter(app);
77
+ }
78
+ /**
79
+ * Singleton instance for quick start
80
+ * @example
81
+ * import { app } from 'qhttpx';
82
+ * app.get('/', ({ json }) => json({ hello: 'world' }));
83
+ */
84
+ exports.app = createHttpApp();
85
+ /**
86
+ * Default export for simplified usage
87
+ * @example
88
+ * import QHTTPX from 'qhttpx';
89
+ * const app = QHTTPX();
90
+ */
91
+ exports.default = createHttpApp;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLoggerMiddleware = createLoggerMiddleware;
4
+ function createLoggerMiddleware(options = {}) {
5
+ const sink = options.sink ??
6
+ ((entry) => {
7
+ const status = entry.status;
8
+ let color = '\x1b[37m';
9
+ if (status >= 200 && status < 300) {
10
+ color = '\x1b[32m';
11
+ }
12
+ else if (status === 404) {
13
+ color = '\x1b[33m';
14
+ }
15
+ else if (status >= 500) {
16
+ color = '\x1b[34m';
17
+ }
18
+ else if (status >= 400) {
19
+ color = '\x1b[35m';
20
+ }
21
+ const reset = '\x1b[0m';
22
+ const prefix = entry.requestId ? `${entry.requestId} ` : '';
23
+ const line = `${prefix}${entry.method} ${entry.path} ${status} ${entry.durationMs}ms`;
24
+ console.log(`${color}${line}${reset}`);
25
+ });
26
+ return async (ctx, next) => {
27
+ const start = ctx.requestStart ?? Date.now();
28
+ try {
29
+ await next();
30
+ }
31
+ finally {
32
+ const durationMs = Math.max(1, Date.now() - start);
33
+ const method = ctx.req.method || 'GET';
34
+ const path = ctx.url.pathname;
35
+ const status = ctx.res.statusCode || 200;
36
+ sink({
37
+ method,
38
+ path,
39
+ status,
40
+ durationMs,
41
+ requestId: ctx.requestId,
42
+ }, ctx);
43
+ }
44
+ };
45
+ }
@@ -0,0 +1,111 @@
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;
@@ -0,0 +1,2 @@
1
+ import { QHTTPXMiddleware, CompressionOptions } from '../core/types';
2
+ export declare function createCompressionMiddleware(options?: CompressionOptions): QHTTPXMiddleware;
@@ -0,0 +1,133 @@
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.createCompressionMiddleware = createCompressionMiddleware;
7
+ const zlib_1 = __importDefault(require("zlib"));
8
+ function createCompressionMiddleware(options = {}) {
9
+ const threshold = options.threshold ?? 1024;
10
+ const level = options.level ?? zlib_1.default.constants.Z_DEFAULT_COMPRESSION;
11
+ return async (ctx, next) => {
12
+ const req = ctx.req;
13
+ const res = ctx.res;
14
+ const acceptEncoding = req.headers['accept-encoding'] || '';
15
+ let stream;
16
+ let encoding = '';
17
+ if (/\bbr\b/.test(acceptEncoding)) {
18
+ stream = zlib_1.default.createBrotliCompress({
19
+ params: {
20
+ [zlib_1.default.constants.BROTLI_PARAM_QUALITY]: level,
21
+ },
22
+ });
23
+ encoding = 'br';
24
+ }
25
+ else if (/\bgzip\b/.test(acceptEncoding)) {
26
+ stream = zlib_1.default.createGzip({ level });
27
+ encoding = 'gzip';
28
+ }
29
+ else if (/\bdeflate\b/.test(acceptEncoding)) {
30
+ stream = zlib_1.default.createDeflate({ level });
31
+ encoding = 'deflate';
32
+ }
33
+ if (!stream) {
34
+ await next();
35
+ return;
36
+ }
37
+ const originalWrite = res.write;
38
+ const originalEnd = res.end;
39
+ // const originalSetHeader = res.setHeader;
40
+ // We need to defer compression decision until we know the content type/length
41
+ // But since we are streaming, we might just start compressing if headers are sent?
42
+ // Actually, we can hook into write/end.
43
+ let headersSent = false;
44
+ let compress = false;
45
+ // Helper to check if we should compress based on content-type
46
+ const shouldCompress = () => {
47
+ const contentType = res.getHeader('content-type');
48
+ if (!contentType)
49
+ return true; // Assume yes if unknown? Or no? Usually text/json is compressed.
50
+ const type = String(contentType).toLowerCase();
51
+ if (type.includes('text/event-stream')) {
52
+ return false;
53
+ }
54
+ return (type.includes('text') ||
55
+ type.includes('json') ||
56
+ type.includes('xml') ||
57
+ type.includes('javascript') ||
58
+ type.includes('svg'));
59
+ };
60
+ // Override write
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ res.write = function (chunk, ...args) {
63
+ if (!headersSent) {
64
+ if (shouldCompress()) {
65
+ compress = true;
66
+ // Disable auto-end because we handle the stream asynchronously
67
+ ctx.disableAutoEnd = true;
68
+ res.setHeader('Content-Encoding', encoding);
69
+ res.removeHeader('Content-Length');
70
+ res.setHeader('Vary', 'Accept-Encoding');
71
+ stream.on('data', (data) => {
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ originalWrite.call(res, data);
74
+ });
75
+ stream.on('end', () => {
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ originalEnd.call(res);
78
+ });
79
+ }
80
+ else {
81
+ compress = false;
82
+ }
83
+ headersSent = true;
84
+ }
85
+ if (compress && stream) {
86
+ return stream.write(chunk, ...args);
87
+ }
88
+ else {
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ return originalWrite.apply(res, [chunk, ...args]);
91
+ }
92
+ };
93
+ // Override end
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ res.end = function (chunk, ...args) {
96
+ if (!headersSent) {
97
+ const len = chunk ? Buffer.byteLength(chunk) : 0;
98
+ if (shouldCompress() && len >= threshold) {
99
+ compress = true;
100
+ // Disable auto-end because we handle the stream asynchronously
101
+ ctx.disableAutoEnd = true;
102
+ res.setHeader('Content-Encoding', encoding);
103
+ res.removeHeader('Content-Length');
104
+ res.setHeader('Vary', 'Accept-Encoding');
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ stream.on('data', (data) => originalWrite.call(res, data));
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ stream.on('end', () => originalEnd.call(res));
109
+ }
110
+ else {
111
+ compress = false;
112
+ }
113
+ headersSent = true;
114
+ }
115
+ if (compress && stream) {
116
+ if (chunk)
117
+ stream.write(chunk);
118
+ stream.end();
119
+ }
120
+ else {
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
+ return originalEnd.apply(res, [chunk, ...args]);
123
+ }
124
+ return res;
125
+ };
126
+ // Fix for the pipe issue:
127
+ // If we set compress=true, we should pipe stream to res ONCE.
128
+ // If we use { end: false }, we must manually end res.
129
+ // If we use { end: true }, stream.end() will end res.
130
+ // Let's refine the logic.
131
+ await next();
132
+ };
133
+ }
@@ -0,0 +1,2 @@
1
+ import { QHTTPXMiddleware, CorsOptions } from '../core/types';
2
+ export declare function createCorsMiddleware(options?: CorsOptions): QHTTPXMiddleware;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCorsMiddleware = createCorsMiddleware;
4
+ function resolveOrigin(origin, requestOrigin) {
5
+ if (!origin) {
6
+ return '*';
7
+ }
8
+ if (typeof origin === 'string') {
9
+ return origin;
10
+ }
11
+ if (Array.isArray(origin)) {
12
+ if (!requestOrigin) {
13
+ return null;
14
+ }
15
+ if (origin.includes(requestOrigin)) {
16
+ return requestOrigin;
17
+ }
18
+ return null;
19
+ }
20
+ return origin(requestOrigin);
21
+ }
22
+ function createCorsMiddleware(options = {}) {
23
+ const methodsHeader = options.methods && options.methods.length > 0
24
+ ? options.methods.join(', ')
25
+ : 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS';
26
+ const allowedHeadersHeader = options.allowedHeaders && options.allowedHeaders.length > 0
27
+ ? options.allowedHeaders.join(', ')
28
+ : undefined;
29
+ const exposedHeadersHeader = options.exposedHeaders && options.exposedHeaders.length > 0
30
+ ? options.exposedHeaders.join(', ')
31
+ : undefined;
32
+ const maxAgeHeader = typeof options.maxAgeSeconds === 'number'
33
+ ? String(options.maxAgeSeconds)
34
+ : undefined;
35
+ const allowCredentials = options.credentials ?? false;
36
+ return async (ctx, next) => {
37
+ const requestOriginHeader = ctx.req.headers.origin;
38
+ const resolvedOrigin = resolveOrigin(options.origin, requestOriginHeader);
39
+ if (resolvedOrigin) {
40
+ ctx.res.setHeader('access-control-allow-origin', resolvedOrigin);
41
+ if (allowCredentials) {
42
+ ctx.res.setHeader('access-control-allow-credentials', 'true');
43
+ }
44
+ if (exposedHeadersHeader) {
45
+ ctx.res.setHeader('access-control-expose-headers', exposedHeadersHeader);
46
+ }
47
+ }
48
+ if (ctx.req.method === 'OPTIONS') {
49
+ ctx.res.statusCode = 204;
50
+ ctx.res.setHeader('access-control-allow-methods', methodsHeader);
51
+ const requestHeaders = typeof ctx.req.headers['access-control-request-headers'] === 'string'
52
+ ? ctx.req.headers['access-control-request-headers']
53
+ : undefined;
54
+ const headersValue = allowedHeadersHeader || requestHeaders;
55
+ if (headersValue) {
56
+ ctx.res.setHeader('access-control-allow-headers', headersValue);
57
+ }
58
+ if (maxAgeHeader) {
59
+ ctx.res.setHeader('access-control-max-age', maxAgeHeader);
60
+ }
61
+ ctx.res.end();
62
+ return;
63
+ }
64
+ await next();
65
+ };
66
+ }
@@ -0,0 +1,16 @@
1
+ import { QHTTPXMiddleware, RateLimitOptions, CorsOptions, CompressionOptions } from '../core/types';
2
+ import { SecureDefaultsOptions } from './security';
3
+ import { LoggerOptions } from '../utils/logger';
4
+ import { StaticOptions } from './static';
5
+ export type ApiPresetOptions = {
6
+ security?: SecureDefaultsOptions;
7
+ logging?: LoggerOptions | boolean;
8
+ rateLimit?: RateLimitOptions;
9
+ cors?: CorsOptions | boolean;
10
+ compression?: CompressionOptions | boolean;
11
+ };
12
+ export declare function createApiPreset(options?: ApiPresetOptions): QHTTPXMiddleware[];
13
+ export type StaticAppPresetOptions = ApiPresetOptions & {
14
+ static: StaticOptions;
15
+ };
16
+ export declare function createStaticAppPreset(options: StaticAppPresetOptions): QHTTPXMiddleware[];