qhttpx 2.3.1 → 2.3.2
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/dist/index.d.mts +65 -27
- package/dist/index.d.ts +65 -27
- package/dist/index.js +43 -12
- package/dist/index.mjs +43 -12
- package/dist/qhttpx-core-new.node +0 -0
- package/dist/qhttpx-core-new.win32-x64-msvc.node +0 -0
- package/examples/cluster_demo.ts +11 -9
- package/examples/cors_demo.ts +6 -6
- package/examples/db_auth_demo.ts +27 -22
- package/examples/hello.ts +4 -0
- package/examples/http2_demo.ts +3 -3
- package/examples/magic-dev.ts +6 -6
- package/examples/middleware_demo.ts +18 -16
- package/examples/mongo_demo.ts +24 -20
- package/examples/observability_demo.ts +8 -6
- package/examples/static_demo.ts +3 -3
- package/examples/tls_demo.ts +3 -3
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -87,7 +87,7 @@ interface RouteOptions {
|
|
|
87
87
|
priority?: 'critical' | 'interactive' | 'background';
|
|
88
88
|
sloTarget?: number;
|
|
89
89
|
}
|
|
90
|
-
interface Route {
|
|
90
|
+
interface Route$1 {
|
|
91
91
|
method: string;
|
|
92
92
|
path: string;
|
|
93
93
|
handler?: Handler;
|
|
@@ -161,6 +161,7 @@ interface FluentBuilder$1 {
|
|
|
161
161
|
expiresIn?: string;
|
|
162
162
|
}): this;
|
|
163
163
|
respond(status?: number): void;
|
|
164
|
+
respond(handler: Handler): void;
|
|
164
165
|
secure(): this;
|
|
165
166
|
autoFilter(table: string, allow: string[], options?: {
|
|
166
167
|
sort?: string[];
|
|
@@ -208,18 +209,23 @@ interface App$1 {
|
|
|
208
209
|
auth: {
|
|
209
210
|
setJwtSecret(secret: string): void;
|
|
210
211
|
};
|
|
211
|
-
get(path: string, config: RouteConfig):
|
|
212
|
-
get(path: string, handler: Handler):
|
|
213
|
-
get(path: string):
|
|
214
|
-
|
|
215
|
-
post(path: string,
|
|
216
|
-
post(path: string):
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
put(path: string):
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
212
|
+
get(path: string, config: RouteConfig): Route$1;
|
|
213
|
+
get(path: string, handler: Handler): Route$1;
|
|
214
|
+
get(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
215
|
+
get(path: string): FluentBuilder$1;
|
|
216
|
+
post(path: string, config: RouteConfig): Route$1;
|
|
217
|
+
post(path: string, handler: Handler): Route$1;
|
|
218
|
+
post(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
219
|
+
post(path: string): FluentBuilder$1;
|
|
220
|
+
put(path: string, config: RouteConfig): Route$1;
|
|
221
|
+
put(path: string, handler: Handler): Route$1;
|
|
222
|
+
put(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
223
|
+
put(path: string): FluentBuilder$1;
|
|
224
|
+
delete(path: string, config: RouteConfig): Route$1;
|
|
225
|
+
delete(path: string, handler: Handler): Route$1;
|
|
226
|
+
delete(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
227
|
+
delete(path: string): FluentBuilder$1;
|
|
228
|
+
addRoute(method: string, path: string): Route$1;
|
|
223
229
|
listen(port: number, callback?: () => void): void;
|
|
224
230
|
listen(options: ListenOptions): void;
|
|
225
231
|
stop(): void;
|
|
@@ -260,6 +266,8 @@ declare class FluentBuilder {
|
|
|
260
266
|
private requestSchema?;
|
|
261
267
|
private responseSchemaDef?;
|
|
262
268
|
private queryBuilder?;
|
|
269
|
+
private rateLimitOptions?;
|
|
270
|
+
private cacheOptions?;
|
|
263
271
|
constructor(app: App$1, method: string, path: string);
|
|
264
272
|
desc(description: string): this;
|
|
265
273
|
auth(strategy?: string): this;
|
|
@@ -408,26 +416,56 @@ declare class App implements App$1 {
|
|
|
408
416
|
headers?: string[];
|
|
409
417
|
credentials?: boolean;
|
|
410
418
|
}): this;
|
|
411
|
-
get(path: string, config: RouteConfig):
|
|
412
|
-
get(path: string, handler: Handler):
|
|
413
|
-
get(path: string, options: RouteOptions, handler: Handler):
|
|
419
|
+
get(path: string, config: RouteConfig): Route;
|
|
420
|
+
get(path: string, handler: Handler): Route;
|
|
421
|
+
get(path: string, options: RouteOptions, handler: Handler): Route;
|
|
414
422
|
get(path: string): FluentBuilder;
|
|
415
|
-
post(path: string, config: RouteConfig):
|
|
416
|
-
post(path: string, handler: Handler):
|
|
417
|
-
post(path: string, options: RouteOptions, handler: Handler):
|
|
423
|
+
post(path: string, config: RouteConfig): Route;
|
|
424
|
+
post(path: string, handler: Handler): Route;
|
|
425
|
+
post(path: string, options: RouteOptions, handler: Handler): Route;
|
|
418
426
|
post(path: string): FluentBuilder;
|
|
419
|
-
put(path: string, config: RouteConfig):
|
|
420
|
-
put(path: string, handler: Handler):
|
|
421
|
-
put(path: string, options: RouteOptions, handler: Handler):
|
|
427
|
+
put(path: string, config: RouteConfig): Route;
|
|
428
|
+
put(path: string, handler: Handler): Route;
|
|
429
|
+
put(path: string, options: RouteOptions, handler: Handler): Route;
|
|
422
430
|
put(path: string): FluentBuilder;
|
|
423
|
-
delete(path: string, config: RouteConfig):
|
|
424
|
-
delete(path: string, handler: Handler):
|
|
425
|
-
delete(path: string, options: RouteOptions, handler: Handler):
|
|
431
|
+
delete(path: string, config: RouteConfig): Route;
|
|
432
|
+
delete(path: string, handler: Handler): Route;
|
|
433
|
+
delete(path: string, options: RouteOptions, handler: Handler): Route;
|
|
426
434
|
delete(path: string): FluentBuilder;
|
|
427
|
-
|
|
435
|
+
addRoute(method: string, path: string): Route;
|
|
428
436
|
registerHandler(handler: Handler, serializer?: (doc: any) => string): number;
|
|
429
437
|
listen(portOrOptions: number | ListenOptions, cb?: () => void): void;
|
|
430
438
|
stop(): void;
|
|
431
439
|
}
|
|
440
|
+
declare class Route implements RouteBuilder {
|
|
441
|
+
path: string;
|
|
442
|
+
method: string;
|
|
443
|
+
private app;
|
|
444
|
+
handlerId: number | null;
|
|
445
|
+
routeConfig: RouteConfig | undefined;
|
|
446
|
+
options: any;
|
|
447
|
+
description: string | undefined;
|
|
448
|
+
middlewares: Middleware[];
|
|
449
|
+
handler?: Handler;
|
|
450
|
+
private serializer;
|
|
451
|
+
constructor(path: string, method: string, app: App);
|
|
452
|
+
desc(description: string): this;
|
|
453
|
+
auth(strategy?: string): this;
|
|
454
|
+
jwt(): this;
|
|
455
|
+
cache(options: {
|
|
456
|
+
ttl: number;
|
|
457
|
+
key?: string;
|
|
458
|
+
}): this;
|
|
459
|
+
rateLimit(options: {
|
|
460
|
+
limit: number;
|
|
461
|
+
window: number;
|
|
462
|
+
}): this;
|
|
463
|
+
query(schemaBuilder: (q: QueryBuilder) => void): this;
|
|
464
|
+
schema(def: any): this;
|
|
465
|
+
priority(level: 'critical' | 'interactive' | 'background'): this;
|
|
466
|
+
slo(targetMs: number): this;
|
|
467
|
+
responseSchema(def: any): this;
|
|
468
|
+
respond(handler: Handler): void;
|
|
469
|
+
}
|
|
432
470
|
|
|
433
|
-
export { App, type DatabaseContext, type EnvContext, FluentBuilder, type Handler, type ListenOptions, type Middleware, type NextFunction, Q, type QueryBuilder, type QueryField, type RequestContext, type RequestMetrics, type RequestSnapshot, type Route, type RouteBuilder, type RouteConfig, type RouteOptions, TestClient, type UploadConfig, type WebSocket, type WsHandler, createTestClient };
|
|
471
|
+
export { App, type DatabaseContext, type EnvContext, FluentBuilder, type Handler, type ListenOptions, type Middleware, type NextFunction, Q, type QueryBuilder, type QueryField, type RequestContext, type RequestMetrics, type RequestSnapshot, type Route$1 as Route, type RouteBuilder, type RouteConfig, type RouteOptions, TestClient, type UploadConfig, type WebSocket, type WsHandler, createTestClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ interface RouteOptions {
|
|
|
87
87
|
priority?: 'critical' | 'interactive' | 'background';
|
|
88
88
|
sloTarget?: number;
|
|
89
89
|
}
|
|
90
|
-
interface Route {
|
|
90
|
+
interface Route$1 {
|
|
91
91
|
method: string;
|
|
92
92
|
path: string;
|
|
93
93
|
handler?: Handler;
|
|
@@ -161,6 +161,7 @@ interface FluentBuilder$1 {
|
|
|
161
161
|
expiresIn?: string;
|
|
162
162
|
}): this;
|
|
163
163
|
respond(status?: number): void;
|
|
164
|
+
respond(handler: Handler): void;
|
|
164
165
|
secure(): this;
|
|
165
166
|
autoFilter(table: string, allow: string[], options?: {
|
|
166
167
|
sort?: string[];
|
|
@@ -208,18 +209,23 @@ interface App$1 {
|
|
|
208
209
|
auth: {
|
|
209
210
|
setJwtSecret(secret: string): void;
|
|
210
211
|
};
|
|
211
|
-
get(path: string, config: RouteConfig):
|
|
212
|
-
get(path: string, handler: Handler):
|
|
213
|
-
get(path: string):
|
|
214
|
-
|
|
215
|
-
post(path: string,
|
|
216
|
-
post(path: string):
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
put(path: string):
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
212
|
+
get(path: string, config: RouteConfig): Route$1;
|
|
213
|
+
get(path: string, handler: Handler): Route$1;
|
|
214
|
+
get(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
215
|
+
get(path: string): FluentBuilder$1;
|
|
216
|
+
post(path: string, config: RouteConfig): Route$1;
|
|
217
|
+
post(path: string, handler: Handler): Route$1;
|
|
218
|
+
post(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
219
|
+
post(path: string): FluentBuilder$1;
|
|
220
|
+
put(path: string, config: RouteConfig): Route$1;
|
|
221
|
+
put(path: string, handler: Handler): Route$1;
|
|
222
|
+
put(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
223
|
+
put(path: string): FluentBuilder$1;
|
|
224
|
+
delete(path: string, config: RouteConfig): Route$1;
|
|
225
|
+
delete(path: string, handler: Handler): Route$1;
|
|
226
|
+
delete(path: string, options: RouteOptions, handler: Handler): Route$1;
|
|
227
|
+
delete(path: string): FluentBuilder$1;
|
|
228
|
+
addRoute(method: string, path: string): Route$1;
|
|
223
229
|
listen(port: number, callback?: () => void): void;
|
|
224
230
|
listen(options: ListenOptions): void;
|
|
225
231
|
stop(): void;
|
|
@@ -260,6 +266,8 @@ declare class FluentBuilder {
|
|
|
260
266
|
private requestSchema?;
|
|
261
267
|
private responseSchemaDef?;
|
|
262
268
|
private queryBuilder?;
|
|
269
|
+
private rateLimitOptions?;
|
|
270
|
+
private cacheOptions?;
|
|
263
271
|
constructor(app: App$1, method: string, path: string);
|
|
264
272
|
desc(description: string): this;
|
|
265
273
|
auth(strategy?: string): this;
|
|
@@ -408,26 +416,56 @@ declare class App implements App$1 {
|
|
|
408
416
|
headers?: string[];
|
|
409
417
|
credentials?: boolean;
|
|
410
418
|
}): this;
|
|
411
|
-
get(path: string, config: RouteConfig):
|
|
412
|
-
get(path: string, handler: Handler):
|
|
413
|
-
get(path: string, options: RouteOptions, handler: Handler):
|
|
419
|
+
get(path: string, config: RouteConfig): Route;
|
|
420
|
+
get(path: string, handler: Handler): Route;
|
|
421
|
+
get(path: string, options: RouteOptions, handler: Handler): Route;
|
|
414
422
|
get(path: string): FluentBuilder;
|
|
415
|
-
post(path: string, config: RouteConfig):
|
|
416
|
-
post(path: string, handler: Handler):
|
|
417
|
-
post(path: string, options: RouteOptions, handler: Handler):
|
|
423
|
+
post(path: string, config: RouteConfig): Route;
|
|
424
|
+
post(path: string, handler: Handler): Route;
|
|
425
|
+
post(path: string, options: RouteOptions, handler: Handler): Route;
|
|
418
426
|
post(path: string): FluentBuilder;
|
|
419
|
-
put(path: string, config: RouteConfig):
|
|
420
|
-
put(path: string, handler: Handler):
|
|
421
|
-
put(path: string, options: RouteOptions, handler: Handler):
|
|
427
|
+
put(path: string, config: RouteConfig): Route;
|
|
428
|
+
put(path: string, handler: Handler): Route;
|
|
429
|
+
put(path: string, options: RouteOptions, handler: Handler): Route;
|
|
422
430
|
put(path: string): FluentBuilder;
|
|
423
|
-
delete(path: string, config: RouteConfig):
|
|
424
|
-
delete(path: string, handler: Handler):
|
|
425
|
-
delete(path: string, options: RouteOptions, handler: Handler):
|
|
431
|
+
delete(path: string, config: RouteConfig): Route;
|
|
432
|
+
delete(path: string, handler: Handler): Route;
|
|
433
|
+
delete(path: string, options: RouteOptions, handler: Handler): Route;
|
|
426
434
|
delete(path: string): FluentBuilder;
|
|
427
|
-
|
|
435
|
+
addRoute(method: string, path: string): Route;
|
|
428
436
|
registerHandler(handler: Handler, serializer?: (doc: any) => string): number;
|
|
429
437
|
listen(portOrOptions: number | ListenOptions, cb?: () => void): void;
|
|
430
438
|
stop(): void;
|
|
431
439
|
}
|
|
440
|
+
declare class Route implements RouteBuilder {
|
|
441
|
+
path: string;
|
|
442
|
+
method: string;
|
|
443
|
+
private app;
|
|
444
|
+
handlerId: number | null;
|
|
445
|
+
routeConfig: RouteConfig | undefined;
|
|
446
|
+
options: any;
|
|
447
|
+
description: string | undefined;
|
|
448
|
+
middlewares: Middleware[];
|
|
449
|
+
handler?: Handler;
|
|
450
|
+
private serializer;
|
|
451
|
+
constructor(path: string, method: string, app: App);
|
|
452
|
+
desc(description: string): this;
|
|
453
|
+
auth(strategy?: string): this;
|
|
454
|
+
jwt(): this;
|
|
455
|
+
cache(options: {
|
|
456
|
+
ttl: number;
|
|
457
|
+
key?: string;
|
|
458
|
+
}): this;
|
|
459
|
+
rateLimit(options: {
|
|
460
|
+
limit: number;
|
|
461
|
+
window: number;
|
|
462
|
+
}): this;
|
|
463
|
+
query(schemaBuilder: (q: QueryBuilder) => void): this;
|
|
464
|
+
schema(def: any): this;
|
|
465
|
+
priority(level: 'critical' | 'interactive' | 'background'): this;
|
|
466
|
+
slo(targetMs: number): this;
|
|
467
|
+
responseSchema(def: any): this;
|
|
468
|
+
respond(handler: Handler): void;
|
|
469
|
+
}
|
|
432
470
|
|
|
433
|
-
export { App, type DatabaseContext, type EnvContext, FluentBuilder, type Handler, type ListenOptions, type Middleware, type NextFunction, Q, type QueryBuilder, type QueryField, type RequestContext, type RequestMetrics, type RequestSnapshot, type Route, type RouteBuilder, type RouteConfig, type RouteOptions, TestClient, type UploadConfig, type WebSocket, type WsHandler, createTestClient };
|
|
471
|
+
export { App, type DatabaseContext, type EnvContext, FluentBuilder, type Handler, type ListenOptions, type Middleware, type NextFunction, Q, type QueryBuilder, type QueryField, type RequestContext, type RequestMetrics, type RequestSnapshot, type Route$1 as Route, type RouteBuilder, type RouteConfig, type RouteOptions, TestClient, type UploadConfig, type WebSocket, type WsHandler, createTestClient };
|
package/dist/index.js
CHANGED
|
@@ -557,6 +557,8 @@ var FluentBuilder = class {
|
|
|
557
557
|
requestSchema;
|
|
558
558
|
responseSchemaDef;
|
|
559
559
|
queryBuilder;
|
|
560
|
+
rateLimitOptions;
|
|
561
|
+
cacheOptions;
|
|
560
562
|
// RouteBuilder compatibility methods
|
|
561
563
|
desc(description) {
|
|
562
564
|
this.steps.push({
|
|
@@ -577,6 +579,7 @@ var FluentBuilder = class {
|
|
|
577
579
|
return this;
|
|
578
580
|
}
|
|
579
581
|
cache(options) {
|
|
582
|
+
this.cacheOptions = options;
|
|
580
583
|
this.steps.push({
|
|
581
584
|
name: "cache",
|
|
582
585
|
fn: (ctx, state) => {
|
|
@@ -586,6 +589,7 @@ var FluentBuilder = class {
|
|
|
586
589
|
return this;
|
|
587
590
|
}
|
|
588
591
|
rateLimit(options) {
|
|
592
|
+
this.rateLimitOptions = options;
|
|
589
593
|
this.steps.push({
|
|
590
594
|
name: "rateLimit",
|
|
591
595
|
fn: (ctx, state) => {
|
|
@@ -827,7 +831,10 @@ var FluentBuilder = class {
|
|
|
827
831
|
}
|
|
828
832
|
const status = handlerOrStatus;
|
|
829
833
|
const handler = async (ctx) => {
|
|
830
|
-
let state = {
|
|
834
|
+
let state = {
|
|
835
|
+
params: ctx.params || {},
|
|
836
|
+
query: ctx.query || {}
|
|
837
|
+
};
|
|
831
838
|
let finalStatus = status;
|
|
832
839
|
try {
|
|
833
840
|
for (const step of this.steps) {
|
|
@@ -848,7 +855,8 @@ var FluentBuilder = class {
|
|
|
848
855
|
ctx.json({ error: message }, status2);
|
|
849
856
|
}
|
|
850
857
|
};
|
|
851
|
-
const route = this.app
|
|
858
|
+
const route = this.app.addRoute(this.method, this.path);
|
|
859
|
+
route.handler = handler;
|
|
852
860
|
this.applyOptionsToRoute(route);
|
|
853
861
|
}
|
|
854
862
|
applyOptionsToRoute(route) {
|
|
@@ -861,6 +869,12 @@ var FluentBuilder = class {
|
|
|
861
869
|
if (this.queryBuilder) {
|
|
862
870
|
route.query(this.queryBuilder);
|
|
863
871
|
}
|
|
872
|
+
if (this.rateLimitOptions) {
|
|
873
|
+
route.rateLimit(this.rateLimitOptions);
|
|
874
|
+
}
|
|
875
|
+
if (this.cacheOptions) {
|
|
876
|
+
route.cache(this.cacheOptions);
|
|
877
|
+
}
|
|
864
878
|
if (this.useJwt) {
|
|
865
879
|
route.jwt();
|
|
866
880
|
}
|
|
@@ -1634,76 +1648,88 @@ var App = class {
|
|
|
1634
1648
|
return this;
|
|
1635
1649
|
}
|
|
1636
1650
|
get(path2, arg2, arg3) {
|
|
1637
|
-
const fluentBuilder = new FluentBuilder(this, "GET", path2);
|
|
1638
1651
|
if (arg3) {
|
|
1639
1652
|
const route = this.addRoute("GET", path2);
|
|
1640
1653
|
route.options = arg2;
|
|
1641
1654
|
route.handler = arg3;
|
|
1655
|
+
return route;
|
|
1642
1656
|
} else if (arg2) {
|
|
1643
1657
|
const route = this.addRoute("GET", path2);
|
|
1644
1658
|
if (typeof arg2 === "function") {
|
|
1645
1659
|
route.handler = arg2;
|
|
1660
|
+
return route;
|
|
1646
1661
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1647
1662
|
route.routeConfig = arg2;
|
|
1663
|
+
return route;
|
|
1648
1664
|
} else {
|
|
1649
1665
|
route.options = arg2;
|
|
1666
|
+
return route;
|
|
1650
1667
|
}
|
|
1651
1668
|
}
|
|
1652
|
-
return
|
|
1669
|
+
return new FluentBuilder(this, "GET", path2);
|
|
1653
1670
|
}
|
|
1654
1671
|
post(path2, arg2, arg3) {
|
|
1655
|
-
const fluentBuilder = new FluentBuilder(this, "POST", path2);
|
|
1656
1672
|
if (arg3) {
|
|
1657
1673
|
const route = this.addRoute("POST", path2);
|
|
1658
1674
|
route.options = arg2;
|
|
1659
1675
|
route.handler = arg3;
|
|
1676
|
+
return route;
|
|
1660
1677
|
} else if (arg2) {
|
|
1661
1678
|
const route = this.addRoute("POST", path2);
|
|
1662
1679
|
if (typeof arg2 === "function") {
|
|
1663
1680
|
route.handler = arg2;
|
|
1681
|
+
return route;
|
|
1664
1682
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1665
1683
|
route.routeConfig = arg2;
|
|
1684
|
+
return route;
|
|
1666
1685
|
} else {
|
|
1667
1686
|
route.options = arg2;
|
|
1687
|
+
return route;
|
|
1668
1688
|
}
|
|
1669
1689
|
}
|
|
1670
|
-
return
|
|
1690
|
+
return new FluentBuilder(this, "POST", path2);
|
|
1671
1691
|
}
|
|
1672
1692
|
put(path2, arg2, arg3) {
|
|
1673
|
-
const fluentBuilder = new FluentBuilder(this, "PUT", path2);
|
|
1674
1693
|
if (arg3) {
|
|
1675
1694
|
const route = this.addRoute("PUT", path2);
|
|
1676
1695
|
route.options = arg2;
|
|
1677
1696
|
route.handler = arg3;
|
|
1697
|
+
return route;
|
|
1678
1698
|
} else if (arg2) {
|
|
1679
1699
|
const route = this.addRoute("PUT", path2);
|
|
1680
1700
|
if (typeof arg2 === "function") {
|
|
1681
1701
|
route.handler = arg2;
|
|
1702
|
+
return route;
|
|
1682
1703
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1683
1704
|
route.routeConfig = arg2;
|
|
1705
|
+
return route;
|
|
1684
1706
|
} else {
|
|
1685
1707
|
route.options = arg2;
|
|
1708
|
+
return route;
|
|
1686
1709
|
}
|
|
1687
1710
|
}
|
|
1688
|
-
return
|
|
1711
|
+
return new FluentBuilder(this, "PUT", path2);
|
|
1689
1712
|
}
|
|
1690
1713
|
delete(path2, arg2, arg3) {
|
|
1691
|
-
const fluentBuilder = new FluentBuilder(this, "DELETE", path2);
|
|
1692
1714
|
if (arg3) {
|
|
1693
1715
|
const route = this.addRoute("DELETE", path2);
|
|
1694
1716
|
route.options = arg2;
|
|
1695
1717
|
route.handler = arg3;
|
|
1718
|
+
return route;
|
|
1696
1719
|
} else if (arg2) {
|
|
1697
1720
|
const route = this.addRoute("DELETE", path2);
|
|
1698
1721
|
if (typeof arg2 === "function") {
|
|
1699
1722
|
route.handler = arg2;
|
|
1723
|
+
return route;
|
|
1700
1724
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1701
1725
|
route.routeConfig = arg2;
|
|
1726
|
+
return route;
|
|
1702
1727
|
} else {
|
|
1703
1728
|
route.options = arg2;
|
|
1729
|
+
return route;
|
|
1704
1730
|
}
|
|
1705
1731
|
}
|
|
1706
|
-
return
|
|
1732
|
+
return new FluentBuilder(this, "DELETE", path2);
|
|
1707
1733
|
}
|
|
1708
1734
|
addRoute(method, path2) {
|
|
1709
1735
|
const route = new Route(path2, method, this);
|
|
@@ -1840,9 +1866,14 @@ var App = class {
|
|
|
1840
1866
|
options
|
|
1841
1867
|
);
|
|
1842
1868
|
}
|
|
1843
|
-
} else if (route.handlerId) {
|
|
1869
|
+
} else if (route.handlerId || route.handler) {
|
|
1844
1870
|
try {
|
|
1845
|
-
|
|
1871
|
+
if (!route.handlerId && route.handler) {
|
|
1872
|
+
route.handlerId = this.registerHandler(route.handler);
|
|
1873
|
+
}
|
|
1874
|
+
if (route.handlerId) {
|
|
1875
|
+
this.engine.registerRoute(route.method, enginePath, route.handlerId, options);
|
|
1876
|
+
}
|
|
1846
1877
|
} catch (e) {
|
|
1847
1878
|
console.error(`Failed to register route ${route.method} ${route.path}:`, e);
|
|
1848
1879
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -466,6 +466,8 @@ var FluentBuilder = class {
|
|
|
466
466
|
requestSchema;
|
|
467
467
|
responseSchemaDef;
|
|
468
468
|
queryBuilder;
|
|
469
|
+
rateLimitOptions;
|
|
470
|
+
cacheOptions;
|
|
469
471
|
// RouteBuilder compatibility methods
|
|
470
472
|
desc(description) {
|
|
471
473
|
this.steps.push({
|
|
@@ -486,6 +488,7 @@ var FluentBuilder = class {
|
|
|
486
488
|
return this;
|
|
487
489
|
}
|
|
488
490
|
cache(options) {
|
|
491
|
+
this.cacheOptions = options;
|
|
489
492
|
this.steps.push({
|
|
490
493
|
name: "cache",
|
|
491
494
|
fn: (ctx, state) => {
|
|
@@ -495,6 +498,7 @@ var FluentBuilder = class {
|
|
|
495
498
|
return this;
|
|
496
499
|
}
|
|
497
500
|
rateLimit(options) {
|
|
501
|
+
this.rateLimitOptions = options;
|
|
498
502
|
this.steps.push({
|
|
499
503
|
name: "rateLimit",
|
|
500
504
|
fn: (ctx, state) => {
|
|
@@ -736,7 +740,10 @@ var FluentBuilder = class {
|
|
|
736
740
|
}
|
|
737
741
|
const status = handlerOrStatus;
|
|
738
742
|
const handler = async (ctx) => {
|
|
739
|
-
let state = {
|
|
743
|
+
let state = {
|
|
744
|
+
params: ctx.params || {},
|
|
745
|
+
query: ctx.query || {}
|
|
746
|
+
};
|
|
740
747
|
let finalStatus = status;
|
|
741
748
|
try {
|
|
742
749
|
for (const step of this.steps) {
|
|
@@ -757,7 +764,8 @@ var FluentBuilder = class {
|
|
|
757
764
|
ctx.json({ error: message }, status2);
|
|
758
765
|
}
|
|
759
766
|
};
|
|
760
|
-
const route = this.app
|
|
767
|
+
const route = this.app.addRoute(this.method, this.path);
|
|
768
|
+
route.handler = handler;
|
|
761
769
|
this.applyOptionsToRoute(route);
|
|
762
770
|
}
|
|
763
771
|
applyOptionsToRoute(route) {
|
|
@@ -770,6 +778,12 @@ var FluentBuilder = class {
|
|
|
770
778
|
if (this.queryBuilder) {
|
|
771
779
|
route.query(this.queryBuilder);
|
|
772
780
|
}
|
|
781
|
+
if (this.rateLimitOptions) {
|
|
782
|
+
route.rateLimit(this.rateLimitOptions);
|
|
783
|
+
}
|
|
784
|
+
if (this.cacheOptions) {
|
|
785
|
+
route.cache(this.cacheOptions);
|
|
786
|
+
}
|
|
773
787
|
if (this.useJwt) {
|
|
774
788
|
route.jwt();
|
|
775
789
|
}
|
|
@@ -1543,76 +1557,88 @@ var App = class {
|
|
|
1543
1557
|
return this;
|
|
1544
1558
|
}
|
|
1545
1559
|
get(path, arg2, arg3) {
|
|
1546
|
-
const fluentBuilder = new FluentBuilder(this, "GET", path);
|
|
1547
1560
|
if (arg3) {
|
|
1548
1561
|
const route = this.addRoute("GET", path);
|
|
1549
1562
|
route.options = arg2;
|
|
1550
1563
|
route.handler = arg3;
|
|
1564
|
+
return route;
|
|
1551
1565
|
} else if (arg2) {
|
|
1552
1566
|
const route = this.addRoute("GET", path);
|
|
1553
1567
|
if (typeof arg2 === "function") {
|
|
1554
1568
|
route.handler = arg2;
|
|
1569
|
+
return route;
|
|
1555
1570
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1556
1571
|
route.routeConfig = arg2;
|
|
1572
|
+
return route;
|
|
1557
1573
|
} else {
|
|
1558
1574
|
route.options = arg2;
|
|
1575
|
+
return route;
|
|
1559
1576
|
}
|
|
1560
1577
|
}
|
|
1561
|
-
return
|
|
1578
|
+
return new FluentBuilder(this, "GET", path);
|
|
1562
1579
|
}
|
|
1563
1580
|
post(path, arg2, arg3) {
|
|
1564
|
-
const fluentBuilder = new FluentBuilder(this, "POST", path);
|
|
1565
1581
|
if (arg3) {
|
|
1566
1582
|
const route = this.addRoute("POST", path);
|
|
1567
1583
|
route.options = arg2;
|
|
1568
1584
|
route.handler = arg3;
|
|
1585
|
+
return route;
|
|
1569
1586
|
} else if (arg2) {
|
|
1570
1587
|
const route = this.addRoute("POST", path);
|
|
1571
1588
|
if (typeof arg2 === "function") {
|
|
1572
1589
|
route.handler = arg2;
|
|
1590
|
+
return route;
|
|
1573
1591
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1574
1592
|
route.routeConfig = arg2;
|
|
1593
|
+
return route;
|
|
1575
1594
|
} else {
|
|
1576
1595
|
route.options = arg2;
|
|
1596
|
+
return route;
|
|
1577
1597
|
}
|
|
1578
1598
|
}
|
|
1579
|
-
return
|
|
1599
|
+
return new FluentBuilder(this, "POST", path);
|
|
1580
1600
|
}
|
|
1581
1601
|
put(path, arg2, arg3) {
|
|
1582
|
-
const fluentBuilder = new FluentBuilder(this, "PUT", path);
|
|
1583
1602
|
if (arg3) {
|
|
1584
1603
|
const route = this.addRoute("PUT", path);
|
|
1585
1604
|
route.options = arg2;
|
|
1586
1605
|
route.handler = arg3;
|
|
1606
|
+
return route;
|
|
1587
1607
|
} else if (arg2) {
|
|
1588
1608
|
const route = this.addRoute("PUT", path);
|
|
1589
1609
|
if (typeof arg2 === "function") {
|
|
1590
1610
|
route.handler = arg2;
|
|
1611
|
+
return route;
|
|
1591
1612
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1592
1613
|
route.routeConfig = arg2;
|
|
1614
|
+
return route;
|
|
1593
1615
|
} else {
|
|
1594
1616
|
route.options = arg2;
|
|
1617
|
+
return route;
|
|
1595
1618
|
}
|
|
1596
1619
|
}
|
|
1597
|
-
return
|
|
1620
|
+
return new FluentBuilder(this, "PUT", path);
|
|
1598
1621
|
}
|
|
1599
1622
|
delete(path, arg2, arg3) {
|
|
1600
|
-
const fluentBuilder = new FluentBuilder(this, "DELETE", path);
|
|
1601
1623
|
if (arg3) {
|
|
1602
1624
|
const route = this.addRoute("DELETE", path);
|
|
1603
1625
|
route.options = arg2;
|
|
1604
1626
|
route.handler = arg3;
|
|
1627
|
+
return route;
|
|
1605
1628
|
} else if (arg2) {
|
|
1606
1629
|
const route = this.addRoute("DELETE", path);
|
|
1607
1630
|
if (typeof arg2 === "function") {
|
|
1608
1631
|
route.handler = arg2;
|
|
1632
|
+
return route;
|
|
1609
1633
|
} else if ("json" in arg2 || "text" in arg2 || "upload" in arg2) {
|
|
1610
1634
|
route.routeConfig = arg2;
|
|
1635
|
+
return route;
|
|
1611
1636
|
} else {
|
|
1612
1637
|
route.options = arg2;
|
|
1638
|
+
return route;
|
|
1613
1639
|
}
|
|
1614
1640
|
}
|
|
1615
|
-
return
|
|
1641
|
+
return new FluentBuilder(this, "DELETE", path);
|
|
1616
1642
|
}
|
|
1617
1643
|
addRoute(method, path) {
|
|
1618
1644
|
const route = new Route(path, method, this);
|
|
@@ -1749,9 +1775,14 @@ var App = class {
|
|
|
1749
1775
|
options
|
|
1750
1776
|
);
|
|
1751
1777
|
}
|
|
1752
|
-
} else if (route.handlerId) {
|
|
1778
|
+
} else if (route.handlerId || route.handler) {
|
|
1753
1779
|
try {
|
|
1754
|
-
|
|
1780
|
+
if (!route.handlerId && route.handler) {
|
|
1781
|
+
route.handlerId = this.registerHandler(route.handler);
|
|
1782
|
+
}
|
|
1783
|
+
if (route.handlerId) {
|
|
1784
|
+
this.engine.registerRoute(route.method, enginePath, route.handlerId, options);
|
|
1785
|
+
}
|
|
1755
1786
|
} catch (e) {
|
|
1756
1787
|
console.error(`Failed to register route ${route.method} ${route.path}:`, e);
|
|
1757
1788
|
}
|
|
Binary file
|
|
Binary file
|
package/examples/cluster_demo.ts
CHANGED
|
@@ -7,18 +7,20 @@ const app = Q.app();
|
|
|
7
7
|
// Just by connecting Redis, all rate limits become distributed!
|
|
8
8
|
// app.db.connectRedis("redis://127.0.0.1:6379");
|
|
9
9
|
|
|
10
|
-
app.get('/expensive'
|
|
11
|
-
|
|
12
|
-
})
|
|
13
|
-
.
|
|
10
|
+
app.get('/expensive')
|
|
11
|
+
.rateLimit({ limit: 10, window: 60 })
|
|
12
|
+
.use(() => ({ data: "Expensive computation" }))
|
|
13
|
+
.respond();
|
|
14
14
|
|
|
15
15
|
// 2. Native Redis Access
|
|
16
16
|
// Use the shared Redis connection for your own data
|
|
17
|
-
app.get('/cache'
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
app.get('/cache')
|
|
18
|
+
.use(async () => {
|
|
19
|
+
await app.db.redis.set("my_key", "hello from rust", 300);
|
|
20
|
+
const val = await app.db.redis.get("my_key");
|
|
21
|
+
return { val };
|
|
22
|
+
})
|
|
23
|
+
.respond();
|
|
22
24
|
|
|
23
25
|
app.listen(3000, () => {
|
|
24
26
|
console.log('Server running on http://localhost:3000');
|
package/examples/cors_demo.ts
CHANGED
|
@@ -10,13 +10,13 @@ app.cors({
|
|
|
10
10
|
credentials: true
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
app.get('/'
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
app.get('/')
|
|
14
|
+
.use(() => ({ message: 'Hello with CORS!' }))
|
|
15
|
+
.respond();
|
|
16
16
|
|
|
17
|
-
app.post('/data'
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
app.post('/data')
|
|
18
|
+
.use(() => ({ status: 'received' }))
|
|
19
|
+
.respond();
|
|
20
20
|
|
|
21
21
|
const PORT = 4000;
|
|
22
22
|
app.listen(PORT, () => {
|
package/examples/db_auth_demo.ts
CHANGED
|
@@ -19,35 +19,40 @@ const SECRET = 'native-super-secret-key-123';
|
|
|
19
19
|
// the connection might fail, but the architecture is valid.
|
|
20
20
|
const PG_URL = "postgres://user:pass@localhost:5432/mydb";
|
|
21
21
|
|
|
22
|
-
app.get('/public'
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
app.get('/public')
|
|
23
|
+
.use(() => ({ message: "Public Access OK" }))
|
|
24
|
+
.respond();
|
|
25
25
|
|
|
26
26
|
// Protected Route (Native JWT Check)
|
|
27
27
|
// If the token is invalid, Rust rejects it. Node.js never sees the request.
|
|
28
|
-
app.get('/protected'
|
|
29
|
-
|
|
30
|
-
})
|
|
31
|
-
.
|
|
28
|
+
app.get('/protected')
|
|
29
|
+
.jwt() // Enable Native JWT Policy
|
|
30
|
+
.use(() => ({ message: "You have valid Native JWT!" }))
|
|
31
|
+
.respond();
|
|
32
32
|
|
|
33
33
|
// Database Route
|
|
34
|
-
app.get('/users'
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
34
|
+
app.get('/users')
|
|
35
|
+
.use(async (ctx) => {
|
|
36
|
+
// This executes SQL in Rust and returns JSON string directly
|
|
37
|
+
// Zero serialization overhead in Node.js!
|
|
38
|
+
try {
|
|
39
|
+
const result = await app.db.query("SELECT * FROM users");
|
|
40
|
+
// Result is a JSON string string from Rust
|
|
41
|
+
return JSON.parse(result);
|
|
42
|
+
} catch (e: any) {
|
|
43
|
+
// Fluent API handles errors if we throw with status
|
|
44
|
+
throw { status: 500, message: e.message };
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.respond();
|
|
45
48
|
|
|
46
49
|
// Helper to generate token for testing
|
|
47
|
-
app.get('/login'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
50
|
+
app.get('/login')
|
|
51
|
+
.use(() => {
|
|
52
|
+
const token = jsonwebtoken.sign({ sub: 'user123' }, SECRET, { expiresIn: '1h' });
|
|
53
|
+
return { token };
|
|
54
|
+
})
|
|
55
|
+
.respond();
|
|
51
56
|
|
|
52
57
|
app.listen(3000, () => {
|
|
53
58
|
console.log('Server running on http://localhost:3000');
|
package/examples/hello.ts
CHANGED
package/examples/http2_demo.ts
CHANGED
|
@@ -5,9 +5,9 @@ import http from 'http';
|
|
|
5
5
|
|
|
6
6
|
const app = Q.app();
|
|
7
7
|
|
|
8
|
-
app.get('/h2'
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
app.get('/h2')
|
|
9
|
+
.use(() => 'Hello from QHTTPX!')
|
|
10
|
+
.respond();
|
|
11
11
|
|
|
12
12
|
app.listen(3003, async () => {
|
|
13
13
|
console.log('Server listening on port 3003');
|
package/examples/magic-dev.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { Q } from '../src';
|
|
|
3
3
|
|
|
4
4
|
const app = Q.app();
|
|
5
5
|
|
|
6
|
-
app.get('/'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
app.get('/')
|
|
7
|
+
.use(() => ({
|
|
8
|
+
message: "Hello from Magic Dev Mode! ✨",
|
|
9
|
+
timestamp: new Date().toISOString()
|
|
10
|
+
}))
|
|
11
|
+
.respond();
|
|
12
12
|
|
|
13
13
|
app.listen(3000, () => {
|
|
14
14
|
console.log("Server running on http://localhost:3000");
|
|
@@ -4,28 +4,30 @@ const app = Q.app();
|
|
|
4
4
|
|
|
5
5
|
// 1. Rate Limited Route
|
|
6
6
|
// Limit: 5 requests per 10 seconds
|
|
7
|
-
app.get('/limited'
|
|
8
|
-
|
|
9
|
-
})
|
|
10
|
-
.
|
|
7
|
+
app.get('/limited')
|
|
8
|
+
.rateLimit({ limit: 5, window: 10 })
|
|
9
|
+
.use(() => ({ message: "I am rate limited!" }))
|
|
10
|
+
.respond();
|
|
11
11
|
|
|
12
12
|
// 2. Cached Route
|
|
13
13
|
// TTL: 10 seconds
|
|
14
14
|
// The handler sleeps for 2 seconds to simulate slow work
|
|
15
|
-
app.get('/cached'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
app.get('/cached')
|
|
16
|
+
.cache({ ttl: 10 })
|
|
17
|
+
.use(async () => {
|
|
18
|
+
// Simulate slow DB call
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
20
|
+
return {
|
|
21
|
+
message: "I am cached!",
|
|
22
|
+
timestamp: Date.now()
|
|
23
|
+
};
|
|
24
|
+
})
|
|
25
|
+
.respond();
|
|
24
26
|
|
|
25
27
|
// 3. Normal Route
|
|
26
|
-
app.get('/normal'
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
app.get('/normal')
|
|
29
|
+
.use(() => ({ message: "I am normal" }))
|
|
30
|
+
.respond();
|
|
29
31
|
|
|
30
32
|
app.listen(3000, () => {
|
|
31
33
|
console.log('Middleware Demo running on http://localhost:3000');
|
package/examples/mongo_demo.ts
CHANGED
|
@@ -6,30 +6,34 @@ const app = Q.app();
|
|
|
6
6
|
// 1. Native Query Caching
|
|
7
7
|
// SQL executed in Rust, result cached in Rust memory (DashMap)
|
|
8
8
|
// Second request returns instantly without DB roundtrip!
|
|
9
|
-
app.get('/users/cached'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
9
|
+
app.get('/users/cached')
|
|
10
|
+
.use(async (ctx) => {
|
|
11
|
+
try {
|
|
12
|
+
// Query with 60 second TTL
|
|
13
|
+
const json = await app.db.query("SELECT * FROM users", 60);
|
|
14
|
+
return JSON.parse(json);
|
|
15
|
+
} catch (e: any) {
|
|
16
|
+
return { error: e.message };
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.respond();
|
|
18
20
|
|
|
19
21
|
// 2. Native MongoDB Support
|
|
20
22
|
// Query executed in Rust via mongodb crate
|
|
21
23
|
// BSON -> JSON conversion happens in Rust
|
|
22
|
-
app.get('/logs'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
24
|
+
app.get('/logs')
|
|
25
|
+
.use(async (ctx) => {
|
|
26
|
+
try {
|
|
27
|
+
const logs = await app.db.mongo("my_db", "logs").find({
|
|
28
|
+
level: "error",
|
|
29
|
+
timestamp: { $gt: 1700000000 }
|
|
30
|
+
});
|
|
31
|
+
return logs;
|
|
32
|
+
} catch (e: any) {
|
|
33
|
+
return { error: e.message };
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
.respond();
|
|
33
37
|
|
|
34
38
|
app.listen(3000, () => {
|
|
35
39
|
console.log('Server running on http://localhost:3000');
|
|
@@ -7,13 +7,15 @@ const app = Q.app();
|
|
|
7
7
|
// Run with RUST_LOG=info node observability_demo.js
|
|
8
8
|
app.enableLogging();
|
|
9
9
|
|
|
10
|
-
app.get('/'
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
app.get('/')
|
|
11
|
+
.use(() => ({ message: "Hello Observability" }))
|
|
12
|
+
.respond();
|
|
13
13
|
|
|
14
|
-
app.get('/error'
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
app.get('/error')
|
|
15
|
+
.use(() => {
|
|
16
|
+
throw new Error("Something went wrong");
|
|
17
|
+
})
|
|
18
|
+
.respond();
|
|
17
19
|
|
|
18
20
|
// 2. Metrics are automatically exposed at /metrics
|
|
19
21
|
// Try: curl http://localhost:3000/metrics
|
package/examples/static_demo.ts
CHANGED
|
@@ -19,9 +19,9 @@ fs.writeFileSync(path.join(publicDir, '1mb.dat'), buffer);
|
|
|
19
19
|
// Serve static files
|
|
20
20
|
app.static('/public', './examples/public');
|
|
21
21
|
|
|
22
|
-
app.get('/'
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
app.get('/')
|
|
23
|
+
.use(() => ({ hello: 'world' }))
|
|
24
|
+
.respond();
|
|
25
25
|
|
|
26
26
|
app.listen(PORT, () => {
|
|
27
27
|
console.log(`Static File Server running on port ${PORT}`);
|
package/examples/tls_demo.ts
CHANGED
|
@@ -16,9 +16,9 @@ if (!fs.existsSync(certPath) || !fs.existsSync(keyPath)) {
|
|
|
16
16
|
process.exit(1);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
app.get('/'
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
app.get('/')
|
|
20
|
+
.use(() => ({ message: "Secure Hello from Native HTTP/2!" }))
|
|
21
|
+
.respond();
|
|
22
22
|
|
|
23
23
|
app.listen({
|
|
24
24
|
port: 3000,
|