qhttpx 1.8.1 → 1.8.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.
- package/CHANGELOG.md +18 -0
- package/README.md +114 -276
- package/assets/logo.svg +25 -0
- package/dist/package.json +39 -6
- package/dist/src/benchmarks/quantam-users.d.ts +1 -0
- package/dist/src/benchmarks/simple-json.d.ts +1 -0
- package/dist/src/benchmarks/ultra-mode.d.ts +1 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/client/index.d.ts +17 -0
- package/dist/src/core/batch.d.ts +24 -0
- package/dist/src/core/body-parser.d.ts +15 -0
- package/dist/src/core/buffer-pool.d.ts +41 -0
- package/dist/src/core/config.d.ts +7 -0
- package/dist/src/core/fusion.d.ts +14 -0
- package/dist/src/core/logger.d.ts +22 -0
- package/dist/src/core/metrics.d.ts +45 -0
- package/dist/src/core/resources.d.ts +9 -0
- package/dist/src/core/scheduler.d.ts +34 -0
- package/dist/src/core/scope.d.ts +26 -0
- package/dist/src/core/serializer.d.ts +10 -0
- package/dist/src/core/server.d.ts +86 -0
- package/dist/src/core/server.js +122 -94
- package/dist/src/core/stream.d.ts +15 -0
- package/dist/src/core/tasks.d.ts +29 -0
- package/dist/src/core/types.d.ts +134 -0
- package/dist/src/core/websocket.d.ts +25 -0
- package/dist/src/core/worker-queue.d.ts +41 -0
- package/dist/src/database/adapters/memory.d.ts +21 -0
- package/dist/src/database/adapters/mongo.d.ts +11 -0
- package/dist/src/database/adapters/postgres.d.ts +10 -0
- package/dist/src/database/adapters/sqlite.d.ts +10 -0
- package/dist/src/database/coalescer.d.ts +14 -0
- package/dist/src/database/manager.d.ts +35 -0
- package/dist/src/database/types.d.ts +20 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.js +15 -1
- package/dist/src/middleware/compression.d.ts +6 -0
- package/dist/src/middleware/cors.d.ts +11 -0
- package/dist/src/middleware/presets.d.ts +13 -0
- package/dist/src/middleware/rate-limit.d.ts +32 -0
- package/dist/src/middleware/security.d.ts +22 -0
- package/dist/src/middleware/static.d.ts +11 -0
- package/dist/src/openapi/generator.d.ts +19 -0
- package/dist/src/router/radix-router.d.ts +18 -0
- package/dist/src/router/radix-tree.d.ts +16 -0
- package/dist/src/router/router.d.ts +33 -0
- package/dist/src/testing/index.d.ts +25 -0
- package/dist/src/utils/cookies.d.ts +3 -0
- package/dist/src/utils/logger.d.ts +12 -0
- package/dist/src/utils/signals.d.ts +6 -0
- package/dist/src/utils/sse.d.ts +6 -0
- package/dist/src/validation/index.d.ts +3 -0
- package/dist/src/validation/simple.d.ts +5 -0
- package/dist/src/validation/types.d.ts +32 -0
- package/dist/src/validation/zod.d.ts +4 -0
- package/dist/src/views/index.d.ts +1 -0
- package/dist/src/views/types.d.ts +3 -0
- package/dist/tests/adapters.test.d.ts +1 -0
- package/dist/tests/batch.test.d.ts +1 -0
- package/dist/tests/body-parser.test.d.ts +1 -0
- package/dist/tests/compression-sse.test.d.ts +1 -0
- package/dist/tests/cookies.test.d.ts +1 -0
- package/dist/tests/cors.test.d.ts +1 -0
- package/dist/tests/database.test.d.ts +1 -0
- package/dist/tests/dx.test.d.ts +1 -0
- package/dist/tests/dx.test.js +100 -50
- package/dist/tests/ecosystem.test.d.ts +1 -0
- package/dist/tests/features.test.d.ts +1 -0
- package/dist/tests/fusion.test.d.ts +1 -0
- package/dist/tests/http-basic.test.d.ts +1 -0
- package/dist/tests/logger.test.d.ts +1 -0
- package/dist/tests/middleware.test.d.ts +1 -0
- package/dist/tests/observability.test.d.ts +1 -0
- package/dist/tests/openapi.test.d.ts +1 -0
- package/dist/tests/plugin.test.d.ts +1 -0
- package/dist/tests/plugins.test.d.ts +1 -0
- package/dist/tests/rate-limit.test.d.ts +1 -0
- package/dist/tests/resources.test.d.ts +1 -0
- package/dist/tests/scheduler.test.d.ts +1 -0
- package/dist/tests/schema-routes.test.d.ts +1 -0
- package/dist/tests/security.test.d.ts +1 -0
- package/dist/tests/server-db.test.d.ts +1 -0
- package/dist/tests/smoke.test.d.ts +1 -0
- package/dist/tests/sqlite-fusion.test.d.ts +1 -0
- package/dist/tests/static.test.d.ts +1 -0
- package/dist/tests/stream.test.d.ts +1 -0
- package/dist/tests/task-metrics.test.d.ts +1 -0
- package/dist/tests/tasks.test.d.ts +1 -0
- package/dist/tests/testing.test.d.ts +1 -0
- package/dist/tests/validation.test.d.ts +1 -0
- package/dist/tests/websocket.test.d.ts +1 -0
- package/dist/vitest.config.d.ts +2 -0
- package/package.json +39 -6
- package/src/core/server.ts +130 -91
- package/src/core/types.ts +14 -4
- package/src/index.ts +16 -0
- package/tests/dx.test.ts +109 -57
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
+
import { URL } from 'url';
|
|
3
|
+
import type { BufferPool, BufferPoolConfig } from './buffer-pool';
|
|
4
|
+
import type { DatabaseManager } from '../database/manager';
|
|
5
|
+
import type { RouteSchema, Validator } from '../validation/types';
|
|
6
|
+
import type { ViewEngine } from '../views/types';
|
|
7
|
+
import type { RequestFusionOptions } from './fusion';
|
|
8
|
+
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
9
|
+
export declare enum RoutePriority {
|
|
10
|
+
CRITICAL = "critical",
|
|
11
|
+
STANDARD = "standard",
|
|
12
|
+
BEST_EFFORT = "best-effort"
|
|
13
|
+
}
|
|
14
|
+
export type RouteOptions = {
|
|
15
|
+
priority?: RoutePriority;
|
|
16
|
+
};
|
|
17
|
+
export declare class HttpError extends Error {
|
|
18
|
+
status: number;
|
|
19
|
+
code?: string;
|
|
20
|
+
details?: unknown;
|
|
21
|
+
constructor(status: number, message?: string, options?: {
|
|
22
|
+
code?: string;
|
|
23
|
+
details?: unknown;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export type CookieOptions = {
|
|
27
|
+
domain?: string;
|
|
28
|
+
path?: string;
|
|
29
|
+
expires?: Date;
|
|
30
|
+
maxAge?: number;
|
|
31
|
+
httpOnly?: boolean;
|
|
32
|
+
secure?: boolean;
|
|
33
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
34
|
+
};
|
|
35
|
+
export type QHTTPXFile = {
|
|
36
|
+
filename: string;
|
|
37
|
+
encoding: string;
|
|
38
|
+
mimeType: string;
|
|
39
|
+
data: Buffer;
|
|
40
|
+
size: number;
|
|
41
|
+
};
|
|
42
|
+
export type QHTTPXContext = {
|
|
43
|
+
readonly req: IncomingMessage;
|
|
44
|
+
readonly res: ServerResponse;
|
|
45
|
+
readonly url: URL;
|
|
46
|
+
readonly params: Record<string, string>;
|
|
47
|
+
readonly query: Record<string, string | string[]>;
|
|
48
|
+
body: unknown;
|
|
49
|
+
files?: Record<string, QHTTPXFile | QHTTPXFile[]>;
|
|
50
|
+
readonly cookies: Record<string, string>;
|
|
51
|
+
state: Record<string, any>;
|
|
52
|
+
readonly bufferPool: BufferPool;
|
|
53
|
+
requestId: string | undefined;
|
|
54
|
+
requestStart: number | undefined;
|
|
55
|
+
serializer?: (value: unknown) => string;
|
|
56
|
+
readonly json: (body: unknown, status?: number) => void;
|
|
57
|
+
readonly send: (body: string | Buffer, status?: number) => void;
|
|
58
|
+
readonly html: (html: string, status?: number) => void;
|
|
59
|
+
readonly redirect: (url: string, status?: number) => void;
|
|
60
|
+
readonly setCookie: (name: string, value: string, options?: CookieOptions) => void;
|
|
61
|
+
readonly db?: DatabaseManager;
|
|
62
|
+
readonly call?: (op: string, params: any) => Promise<any>;
|
|
63
|
+
readonly render: (view: string, locals?: Record<string, any>) => Promise<void>;
|
|
64
|
+
readonly validate: <T = unknown>(schema: unknown, data?: unknown) => Promise<T>;
|
|
65
|
+
disableAutoEnd?: boolean;
|
|
66
|
+
readonly path: string;
|
|
67
|
+
readonly next?: () => Promise<void>;
|
|
68
|
+
};
|
|
69
|
+
export type QHTTPXOpHandler = (params: any, ctx: QHTTPXContext) => any | Promise<any>;
|
|
70
|
+
export type QHTTPXHandler = (ctx: QHTTPXContext) => void | Promise<void>;
|
|
71
|
+
export type QHTTPXRouteOptions = {
|
|
72
|
+
schema?: RouteSchema | Record<string, any>;
|
|
73
|
+
handler: QHTTPXHandler;
|
|
74
|
+
priority?: RoutePriority;
|
|
75
|
+
};
|
|
76
|
+
export type QHTTPXMiddleware = (ctx: QHTTPXContext, next: () => Promise<void>) => void | Promise<void>;
|
|
77
|
+
export type QHTTPXErrorContext = QHTTPXContext & {
|
|
78
|
+
error: unknown;
|
|
79
|
+
};
|
|
80
|
+
export type QHTTPXErrorHandler = (ctx: QHTTPXErrorContext) => void | Promise<void>;
|
|
81
|
+
export type QHTTPXNotFoundHandler = (ctx: QHTTPXContext) => void | Promise<void>;
|
|
82
|
+
export type QHTTPXMethodNotAllowedHandler = (ctx: QHTTPXContext, allowedMethods: HTTPMethod[]) => void | Promise<void>;
|
|
83
|
+
export type QHTTPXTraceEvent = {
|
|
84
|
+
type: 'request_start';
|
|
85
|
+
method: string;
|
|
86
|
+
path: string;
|
|
87
|
+
requestId?: string;
|
|
88
|
+
} | {
|
|
89
|
+
type: 'request_end';
|
|
90
|
+
method: string;
|
|
91
|
+
path: string;
|
|
92
|
+
statusCode: number;
|
|
93
|
+
durationMs: number;
|
|
94
|
+
requestId?: string;
|
|
95
|
+
};
|
|
96
|
+
export type QHTTPXTracer = (event: QHTTPXTraceEvent) => void | Promise<void>;
|
|
97
|
+
export type QHTTPXTaskHandler = (payload: unknown) => void | Promise<void>;
|
|
98
|
+
export type QHTTPXTaskOptions = {
|
|
99
|
+
maxRetries?: number;
|
|
100
|
+
backoffMs?: number;
|
|
101
|
+
};
|
|
102
|
+
export type PerformanceMode = 'balanced' | 'ultra';
|
|
103
|
+
export type QHTTPXOptions = {
|
|
104
|
+
name?: string;
|
|
105
|
+
workers?: 'auto' | number;
|
|
106
|
+
maxConcurrency?: number;
|
|
107
|
+
requestTimeoutMs?: number;
|
|
108
|
+
maxMemoryBytes?: number;
|
|
109
|
+
maxBodyBytes?: number;
|
|
110
|
+
keepAliveTimeoutMs?: number;
|
|
111
|
+
headersTimeoutMs?: number;
|
|
112
|
+
metricsEnabled?: boolean;
|
|
113
|
+
jsonSerializer?: (value: unknown) => string | Buffer;
|
|
114
|
+
bufferPoolConfig?: BufferPoolConfig;
|
|
115
|
+
errorHandler?: QHTTPXErrorHandler;
|
|
116
|
+
notFoundHandler?: QHTTPXNotFoundHandler;
|
|
117
|
+
methodNotAllowedHandler?: QHTTPXMethodNotAllowedHandler;
|
|
118
|
+
tracer?: QHTTPXTracer;
|
|
119
|
+
performanceMode?: PerformanceMode;
|
|
120
|
+
database?: DatabaseManager;
|
|
121
|
+
enableBatching?: boolean | {
|
|
122
|
+
endpoint: string;
|
|
123
|
+
};
|
|
124
|
+
validator?: Validator;
|
|
125
|
+
enableRequestFusion?: boolean | RequestFusionOptions;
|
|
126
|
+
viewEngine?: ViewEngine;
|
|
127
|
+
viewsPath?: string;
|
|
128
|
+
};
|
|
129
|
+
export type QHTTPXPlugin<Options = any> = (app: any, // We use 'any' here to avoid circular dependency with QHTTPX class, or we could use an interface
|
|
130
|
+
options: Options) => void | Promise<void>;
|
|
131
|
+
export type QHTTPXPluginOptions = {
|
|
132
|
+
prefix?: string;
|
|
133
|
+
[key: string]: any;
|
|
134
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { IncomingMessage } from 'http';
|
|
2
|
+
import { Duplex } from 'stream';
|
|
3
|
+
import { WebSocket } from 'ws';
|
|
4
|
+
export interface QHTTPXWebSocket extends WebSocket {
|
|
5
|
+
join(room: string): void;
|
|
6
|
+
leave(room: string): void;
|
|
7
|
+
id: string;
|
|
8
|
+
}
|
|
9
|
+
export type WSHandler = (ws: QHTTPXWebSocket, req: IncomingMessage) => void;
|
|
10
|
+
export declare class WebSocketManager {
|
|
11
|
+
private readonly requestIdGenerator;
|
|
12
|
+
private readonly wss;
|
|
13
|
+
private readonly handlers;
|
|
14
|
+
private readonly rooms;
|
|
15
|
+
constructor(requestIdGenerator: () => string);
|
|
16
|
+
register(path: string, handler: WSHandler): void;
|
|
17
|
+
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): Promise<void>;
|
|
18
|
+
private join;
|
|
19
|
+
private leave;
|
|
20
|
+
private leaveAll;
|
|
21
|
+
to(room: string): {
|
|
22
|
+
emit: (data: unknown) => void;
|
|
23
|
+
};
|
|
24
|
+
broadcast(data: unknown): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock-free (or lock-minimal) work queue for per-worker task distribution.
|
|
3
|
+
* Uses a simple ring buffer for high throughput.
|
|
4
|
+
*/
|
|
5
|
+
export type WorkItem<T> = {
|
|
6
|
+
id: number;
|
|
7
|
+
task: T;
|
|
8
|
+
priority: number;
|
|
9
|
+
};
|
|
10
|
+
export declare class WorkerQueue<T> {
|
|
11
|
+
private readonly capacity;
|
|
12
|
+
private readonly buffer;
|
|
13
|
+
private writeIndex;
|
|
14
|
+
private readIndex;
|
|
15
|
+
private size;
|
|
16
|
+
constructor(capacity?: number);
|
|
17
|
+
/**
|
|
18
|
+
* Enqueue a work item. Returns true if successful, false if queue is full.
|
|
19
|
+
*/
|
|
20
|
+
enqueue(item: WorkItem<T>): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Dequeue a work item. Returns undefined if queue is empty.
|
|
23
|
+
*/
|
|
24
|
+
dequeue(): WorkItem<T> | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Peek at the next item without removing it.
|
|
27
|
+
*/
|
|
28
|
+
peek(): WorkItem<T> | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Check if the queue is empty.
|
|
31
|
+
*/
|
|
32
|
+
isEmpty(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get the current size of the queue.
|
|
35
|
+
*/
|
|
36
|
+
getSize(): number;
|
|
37
|
+
/**
|
|
38
|
+
* Get the capacity of the queue.
|
|
39
|
+
*/
|
|
40
|
+
getCapacity(): number;
|
|
41
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DatabaseAdapter, DatabaseConfig } from '../types';
|
|
2
|
+
export declare class MemoryAdapter implements DatabaseAdapter {
|
|
3
|
+
private config;
|
|
4
|
+
private connected;
|
|
5
|
+
private collections;
|
|
6
|
+
constructor(config: DatabaseConfig);
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
disconnect(): Promise<void>;
|
|
9
|
+
isConnected(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Execute a query against the in-memory database
|
|
12
|
+
* @param query Object specifying collection, action, and parameters
|
|
13
|
+
* @example
|
|
14
|
+
* // Find
|
|
15
|
+
* query({ collection: 'users', action: 'find', filter: { id: 1 } })
|
|
16
|
+
* // Insert
|
|
17
|
+
* query({ collection: 'users', action: 'insert', data: { name: 'Alice' } })
|
|
18
|
+
*/
|
|
19
|
+
query<T = any>(query: any): Promise<T>;
|
|
20
|
+
private matches;
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DatabaseAdapter, DatabaseConfig } from '../types';
|
|
2
|
+
export declare class MongoAdapter implements DatabaseAdapter {
|
|
3
|
+
private client;
|
|
4
|
+
private db;
|
|
5
|
+
private config;
|
|
6
|
+
constructor(config: DatabaseConfig);
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
disconnect(): Promise<void>;
|
|
9
|
+
query<T = any>(query: string | object, params?: any[]): Promise<T>;
|
|
10
|
+
isConnected(): boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DatabaseAdapter, DatabaseConfig } from '../types';
|
|
2
|
+
export declare class PostgresAdapter implements DatabaseAdapter {
|
|
3
|
+
private pool;
|
|
4
|
+
private config;
|
|
5
|
+
constructor(config: DatabaseConfig);
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
disconnect(): Promise<void>;
|
|
8
|
+
query<T = any>(sql: string, params?: any[]): Promise<T>;
|
|
9
|
+
isConnected(): boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DatabaseAdapter, DatabaseConfig } from '../types';
|
|
2
|
+
export declare class SQLiteAdapter implements DatabaseAdapter {
|
|
3
|
+
private db;
|
|
4
|
+
private config;
|
|
5
|
+
constructor(config: DatabaseConfig);
|
|
6
|
+
connect(): Promise<void>;
|
|
7
|
+
disconnect(): Promise<void>;
|
|
8
|
+
query<T = any>(sql: string, params?: any[]): Promise<T>;
|
|
9
|
+
isConnected(): boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DatabaseAdapter } from './types';
|
|
2
|
+
export declare class QueryCoalescer<T = unknown> {
|
|
3
|
+
private pending;
|
|
4
|
+
private timeout;
|
|
5
|
+
private readonly adapter;
|
|
6
|
+
constructor(adapter: DatabaseAdapter);
|
|
7
|
+
/**
|
|
8
|
+
* Intercepts a query and attempts to coalesce it with others.
|
|
9
|
+
* Only supports simple queries of the form "SELECT ... WHERE col = ?" for now.
|
|
10
|
+
*/
|
|
11
|
+
query(query: string, params?: unknown[]): Promise<T>;
|
|
12
|
+
private scheduleFlush;
|
|
13
|
+
private flush;
|
|
14
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DatabaseAdapter, DatabaseConfig, DatabaseEngineOptions } from './types';
|
|
2
|
+
export type AdapterConstructor = new (config: DatabaseConfig) => DatabaseAdapter;
|
|
3
|
+
export declare class DatabaseManager {
|
|
4
|
+
private config;
|
|
5
|
+
private static adapterRegistry;
|
|
6
|
+
private connections;
|
|
7
|
+
constructor(config: DatabaseEngineOptions);
|
|
8
|
+
/**
|
|
9
|
+
* Manually register an already initialized adapter instance
|
|
10
|
+
* @param name The connection name
|
|
11
|
+
* @param adapter The initialized adapter instance
|
|
12
|
+
*/
|
|
13
|
+
registerConnection(name: string, adapter: DatabaseAdapter): void;
|
|
14
|
+
/**
|
|
15
|
+
* Register a new database adapter type
|
|
16
|
+
* @param type The type identifier (e.g., 'postgres', 'mysql', 'mongo')
|
|
17
|
+
* @param adapter The adapter class
|
|
18
|
+
*/
|
|
19
|
+
static registerAdapter(type: string, adapter: AdapterConstructor): void;
|
|
20
|
+
/**
|
|
21
|
+
* Connect to a specific database or the default one
|
|
22
|
+
* @param name Connection name from config
|
|
23
|
+
*/
|
|
24
|
+
connect(name?: string): Promise<DatabaseAdapter>;
|
|
25
|
+
/**
|
|
26
|
+
* Disconnect a specific connection or all connections
|
|
27
|
+
* @param name Connection name (optional). If not provided, disconnects all.
|
|
28
|
+
*/
|
|
29
|
+
disconnect(name?: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Get an active connection
|
|
32
|
+
* @param name Connection name
|
|
33
|
+
*/
|
|
34
|
+
get(name?: string): DatabaseAdapter;
|
|
35
|
+
}
|
|
@@ -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,45 @@
|
|
|
1
|
+
import { QHTTPX } from './core/server';
|
|
2
|
+
import type { QHTTPXOptions } from './core/types';
|
|
3
|
+
export { QHTTPX } from './core/server';
|
|
4
|
+
export * from './core/types';
|
|
5
|
+
export * from './middleware/cors';
|
|
6
|
+
export * from './middleware/security';
|
|
7
|
+
export * from './middleware/static';
|
|
8
|
+
export * from './middleware/compression';
|
|
9
|
+
export * from './core/stream';
|
|
10
|
+
export * from './utils/logger';
|
|
11
|
+
export { BufferPool, BufferPoolConfig } from './core/buffer-pool';
|
|
12
|
+
export * from './testing';
|
|
13
|
+
export * from './utils/signals';
|
|
14
|
+
export * from './middleware/presets';
|
|
15
|
+
export * from './utils/cookies';
|
|
16
|
+
export * from './utils/sse';
|
|
17
|
+
export { fastJsonStringify, getStringifier } from './core/serializer';
|
|
18
|
+
export * from './database/types';
|
|
19
|
+
export * from './database/manager';
|
|
20
|
+
export * from './database/adapters/memory';
|
|
21
|
+
export * from './views';
|
|
22
|
+
export * from './validation';
|
|
23
|
+
export * from './database/adapters/sqlite';
|
|
24
|
+
export * from './database/adapters/postgres';
|
|
25
|
+
export * from './database/adapters/mongo';
|
|
26
|
+
export * from './core/fusion';
|
|
27
|
+
export * from './validation/types';
|
|
28
|
+
export * from './validation/simple';
|
|
29
|
+
export * from './openapi/generator';
|
|
30
|
+
export * from './client';
|
|
31
|
+
export declare function createHttpApp(options?: QHTTPXOptions): QHTTPX;
|
|
32
|
+
/**
|
|
33
|
+
* Singleton instance for quick start
|
|
34
|
+
* @example
|
|
35
|
+
* import { app } from 'qhttpx';
|
|
36
|
+
* app.get('/', ({ json }) => json({ hello: 'world' }));
|
|
37
|
+
*/
|
|
38
|
+
export declare const app: QHTTPX;
|
|
39
|
+
/**
|
|
40
|
+
* Default export for simplified usage
|
|
41
|
+
* @example
|
|
42
|
+
* import QHTTPX from 'qhttpx';
|
|
43
|
+
* const app = QHTTPX();
|
|
44
|
+
*/
|
|
45
|
+
export default createHttpApp;
|
package/dist/src/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getStringifier = exports.fastJsonStringify = exports.BufferPool = exports.QHTTPX = void 0;
|
|
17
|
+
exports.app = exports.getStringifier = exports.fastJsonStringify = exports.BufferPool = exports.QHTTPX = void 0;
|
|
18
18
|
exports.createHttpApp = createHttpApp;
|
|
19
19
|
const server_1 = require("./core/server");
|
|
20
20
|
const presets_1 = require("./middleware/presets");
|
|
@@ -59,3 +59,17 @@ function createHttpApp(options = {}) {
|
|
|
59
59
|
}
|
|
60
60
|
return app;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Singleton instance for quick start
|
|
64
|
+
* @example
|
|
65
|
+
* import { app } from 'qhttpx';
|
|
66
|
+
* app.get('/', ({ json }) => json({ hello: 'world' }));
|
|
67
|
+
*/
|
|
68
|
+
exports.app = createHttpApp();
|
|
69
|
+
/**
|
|
70
|
+
* Default export for simplified usage
|
|
71
|
+
* @example
|
|
72
|
+
* import QHTTPX from 'qhttpx';
|
|
73
|
+
* const app = QHTTPX();
|
|
74
|
+
*/
|
|
75
|
+
exports.default = createHttpApp;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { QHTTPXMiddleware } from '../core/types';
|
|
2
|
+
export type CorsOrigin = string | string[] | ((origin: string | undefined) => string | null | undefined);
|
|
3
|
+
export type CorsOptions = {
|
|
4
|
+
origin?: CorsOrigin;
|
|
5
|
+
methods?: string[];
|
|
6
|
+
allowedHeaders?: string[];
|
|
7
|
+
exposedHeaders?: string[];
|
|
8
|
+
credentials?: boolean;
|
|
9
|
+
maxAgeSeconds?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function createCorsMiddleware(options?: CorsOptions): QHTTPXMiddleware;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { QHTTPXMiddleware } 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
|
+
};
|
|
9
|
+
export declare function createApiPreset(options?: ApiPresetOptions): QHTTPXMiddleware[];
|
|
10
|
+
export type StaticAppPresetOptions = ApiPresetOptions & {
|
|
11
|
+
static: StaticOptions;
|
|
12
|
+
};
|
|
13
|
+
export declare function createStaticAppPreset(options: StaticAppPresetOptions): QHTTPXMiddleware[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { QHTTPXContext, QHTTPXMiddleware } from '../core/types';
|
|
2
|
+
export interface RateLimitStore {
|
|
3
|
+
increment(key: string, windowMs: number): Promise<{
|
|
4
|
+
total: number;
|
|
5
|
+
resetTime: number;
|
|
6
|
+
}>;
|
|
7
|
+
decrement(key: string): Promise<void>;
|
|
8
|
+
reset(key: string): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare class MemoryStore implements RateLimitStore {
|
|
11
|
+
private hits;
|
|
12
|
+
private interval?;
|
|
13
|
+
constructor(clearPeriodMs?: number);
|
|
14
|
+
increment(key: string, windowMs: number): Promise<{
|
|
15
|
+
total: number;
|
|
16
|
+
resetTime: number;
|
|
17
|
+
}>;
|
|
18
|
+
decrement(key: string): Promise<void>;
|
|
19
|
+
reset(key: string): Promise<void>;
|
|
20
|
+
private cleanup;
|
|
21
|
+
}
|
|
22
|
+
export interface RateLimitOptions {
|
|
23
|
+
windowMs?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
message?: string | object;
|
|
26
|
+
statusCode?: number;
|
|
27
|
+
headers?: boolean;
|
|
28
|
+
keyGenerator?: (ctx: QHTTPXContext) => string;
|
|
29
|
+
skip?: (ctx: QHTTPXContext) => boolean;
|
|
30
|
+
store?: RateLimitStore;
|
|
31
|
+
}
|
|
32
|
+
export declare const rateLimit: (options?: RateLimitOptions) => QHTTPXMiddleware;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { QHTTPXContext, QHTTPXMiddleware } from '../core/types';
|
|
2
|
+
import { CorsOptions } from './cors';
|
|
3
|
+
export type SecurityHeadersOptions = {
|
|
4
|
+
contentSecurityPolicy?: string | null;
|
|
5
|
+
referrerPolicy?: string | null;
|
|
6
|
+
xFrameOptions?: 'DENY' | 'SAMEORIGIN' | null;
|
|
7
|
+
xContentTypeOptions?: 'nosniff' | null;
|
|
8
|
+
xXssProtection?: '0' | '1; mode=block' | null;
|
|
9
|
+
strictTransportSecurity?: string | null;
|
|
10
|
+
};
|
|
11
|
+
export declare function createSecurityHeadersMiddleware(options?: SecurityHeadersOptions): QHTTPXMiddleware;
|
|
12
|
+
export type SecureDefaultsOptions = {
|
|
13
|
+
cors?: CorsOptions;
|
|
14
|
+
securityHeaders?: SecurityHeadersOptions;
|
|
15
|
+
};
|
|
16
|
+
export declare function createSecureDefaults(options?: SecureDefaultsOptions): QHTTPXMiddleware[];
|
|
17
|
+
export type RateLimitOptions = {
|
|
18
|
+
maxRequests: number;
|
|
19
|
+
windowMs: number;
|
|
20
|
+
keyGenerator?: (ctx: QHTTPXContext) => string;
|
|
21
|
+
};
|
|
22
|
+
export declare function createRateLimitMiddleware(options: RateLimitOptions): QHTTPXMiddleware;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { QHTTPXMiddleware } from '../core/types';
|
|
2
|
+
export type StaticOptions = {
|
|
3
|
+
root: string;
|
|
4
|
+
index?: string;
|
|
5
|
+
fallthrough?: boolean;
|
|
6
|
+
etag?: boolean;
|
|
7
|
+
lastModified?: boolean;
|
|
8
|
+
maxAge?: number;
|
|
9
|
+
immutable?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function createStaticMiddleware(options: StaticOptions): QHTTPXMiddleware;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Router } from '../router/router';
|
|
2
|
+
export type OpenAPIOptions = {
|
|
3
|
+
info: {
|
|
4
|
+
title: string;
|
|
5
|
+
version: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
};
|
|
8
|
+
servers?: {
|
|
9
|
+
url: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
export declare class OpenAPIGenerator {
|
|
14
|
+
private router;
|
|
15
|
+
private options;
|
|
16
|
+
constructor(router: Router, options: OpenAPIOptions);
|
|
17
|
+
generate(): object;
|
|
18
|
+
private convertSchema;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Radix tree router compiled into a flat array structure for zero-allocation matching.
|
|
3
|
+
* Built once at server startup (frozen).
|
|
4
|
+
*/
|
|
5
|
+
import { HTTPMethod, QHTTPXHandler } from '../core/types';
|
|
6
|
+
export type CompiledRadixMatch = {
|
|
7
|
+
handler: QHTTPXHandler;
|
|
8
|
+
params: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
export declare class RadixRouter {
|
|
11
|
+
private roots;
|
|
12
|
+
private isFrozen;
|
|
13
|
+
register(method: HTTPMethod, path: string, handler: QHTTPXHandler): void;
|
|
14
|
+
match(method: HTTPMethod, path: string): CompiledRadixMatch | undefined;
|
|
15
|
+
freeze(): void;
|
|
16
|
+
isFrozenRouter(): boolean;
|
|
17
|
+
private normalize;
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { QHTTPXHandler, RoutePriority } from '../core/types';
|
|
2
|
+
export type RouteData = {
|
|
3
|
+
handler: QHTTPXHandler;
|
|
4
|
+
priority: RoutePriority;
|
|
5
|
+
};
|
|
6
|
+
export type MatchResult = {
|
|
7
|
+
handler: QHTTPXHandler;
|
|
8
|
+
priority: RoutePriority;
|
|
9
|
+
params: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
export declare class RadixTree {
|
|
12
|
+
private root;
|
|
13
|
+
insert(segments: string[], handler: QHTTPXHandler, priority: RoutePriority): void;
|
|
14
|
+
lookup(segments: string[]): MatchResult | null;
|
|
15
|
+
private find;
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HTTPMethod, QHTTPXHandler, RouteOptions, RoutePriority } from '../core/types';
|
|
2
|
+
import { RouteSchema } from '../validation/types';
|
|
3
|
+
type RouteDefinition = {
|
|
4
|
+
path: string;
|
|
5
|
+
segments: string[];
|
|
6
|
+
handler: QHTTPXHandler;
|
|
7
|
+
priority: RoutePriority;
|
|
8
|
+
schema?: RouteSchema | Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export type RouteMatch = {
|
|
11
|
+
handler: QHTTPXHandler;
|
|
12
|
+
params: Record<string, string>;
|
|
13
|
+
priority: RoutePriority;
|
|
14
|
+
};
|
|
15
|
+
export declare class Router {
|
|
16
|
+
private readonly methodBuckets;
|
|
17
|
+
private readonly radixTrees;
|
|
18
|
+
private isFrozen;
|
|
19
|
+
register(method: HTTPMethod, path: string, handler: QHTTPXHandler, options?: RouteOptions & {
|
|
20
|
+
schema?: RouteSchema | Record<string, unknown>;
|
|
21
|
+
}): void;
|
|
22
|
+
getRoutes(): Map<HTTPMethod, RouteDefinition[]>;
|
|
23
|
+
match(method: HTTPMethod, path: string): RouteMatch | undefined;
|
|
24
|
+
getAllowedMethods(path: string): HTTPMethod[];
|
|
25
|
+
/**
|
|
26
|
+
* Freeze the router after server starts.
|
|
27
|
+
* Prevents further route registration and builds derived structures for optimized matching.
|
|
28
|
+
*/
|
|
29
|
+
freeze(): void;
|
|
30
|
+
isFrozenRouter(): boolean;
|
|
31
|
+
private normalize;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { QHTTPX } from '../core/server';
|
|
2
|
+
import { HTTPMethod } from '../core/types';
|
|
3
|
+
export declare class TestClient {
|
|
4
|
+
private app;
|
|
5
|
+
private server;
|
|
6
|
+
private baseURL;
|
|
7
|
+
constructor(app: QHTTPX);
|
|
8
|
+
/**
|
|
9
|
+
* Starts the server on a random port.
|
|
10
|
+
* Automatically called by request methods if not started.
|
|
11
|
+
*/
|
|
12
|
+
start(): Promise<void>;
|
|
13
|
+
stop(): Promise<void>;
|
|
14
|
+
request(method: HTTPMethod, path: string, options?: {
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
body?: any;
|
|
17
|
+
query?: Record<string, string | number | boolean>;
|
|
18
|
+
}): Promise<Response>;
|
|
19
|
+
get(path: string, options?: Omit<Parameters<TestClient['request']>[2], 'body'>): Promise<Response>;
|
|
20
|
+
post(path: string, body?: any, options?: Omit<Parameters<TestClient['request']>[2], 'body'>): Promise<Response>;
|
|
21
|
+
put(path: string, body?: any, options?: Omit<Parameters<TestClient['request']>[2], 'body'>): Promise<Response>;
|
|
22
|
+
delete(path: string, options?: Omit<Parameters<TestClient['request']>[2], 'body'>): Promise<Response>;
|
|
23
|
+
patch(path: string, body?: any, options?: Omit<Parameters<TestClient['request']>[2], 'body'>): Promise<Response>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createTestClient(app: QHTTPX): TestClient;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { QHTTPXContext, QHTTPXMiddleware } from '../core/types';
|
|
2
|
+
export type LogEntry = {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
status: number;
|
|
6
|
+
durationMs: number;
|
|
7
|
+
requestId?: string;
|
|
8
|
+
};
|
|
9
|
+
export type LoggerOptions = {
|
|
10
|
+
sink?: (entry: LogEntry, ctx: QHTTPXContext) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function createLoggerMiddleware(options?: LoggerOptions): QHTTPXMiddleware;
|