qhttpx 1.9.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +28 -35
- package/dist/examples/api-server.js +38 -35
- package/dist/examples/basic.js +3 -4
- package/dist/examples/compression.js +6 -8
- package/dist/examples/cors.js +5 -6
- package/dist/examples/errors.js +12 -11
- package/dist/examples/file-upload.js +4 -6
- package/dist/examples/fusion.js +6 -6
- package/dist/examples/rate-limiting.js +10 -10
- package/dist/examples/validation.js +5 -6
- package/dist/examples/websockets.js +3 -4
- package/dist/package.json +3 -8
- package/dist/src/benchmarks/quantam-users.js +2 -2
- package/dist/src/benchmarks/quick-bench.js +57 -0
- package/dist/src/benchmarks/simple-json.js +133 -22
- package/dist/src/benchmarks/ultra-mode.js +8 -38
- package/dist/src/core/context-pool.d.ts +12 -0
- package/dist/src/core/context-pool.js +34 -0
- package/dist/src/core/fusion.js +0 -2
- package/dist/src/core/metrics.d.ts +3 -1
- package/dist/src/core/metrics.js +11 -5
- package/dist/src/core/scheduler.d.ts +4 -0
- package/dist/src/core/scheduler.js +75 -34
- package/dist/src/core/scope.d.ts +23 -8
- package/dist/src/core/scope.js +53 -14
- package/dist/src/core/serializer.d.ts +1 -1
- package/dist/src/core/serializer.js +45 -7
- package/dist/src/core/server.d.ts +51 -10
- package/dist/src/core/server.js +688 -259
- package/dist/src/core/timer.d.ts +11 -0
- package/dist/src/core/timer.js +29 -0
- package/dist/src/core/types.d.ts +41 -13
- package/dist/src/core/types.js +6 -6
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +19 -20
- package/dist/src/middleware/security.js +6 -1
- package/dist/src/router/radix-tree.d.ts +5 -2
- package/dist/src/router/radix-tree.js +58 -14
- package/dist/src/router/router.d.ts +5 -2
- package/dist/src/router/router.js +80 -63
- package/dist/tests/fusion.test.js +4 -4
- package/dist/tests/rate-limit.test.js +2 -2
- package/dist/tests/schema-routes.test.js +3 -1
- package/docs/AEGIS.md +18 -28
- package/docs/BENCHMARKS.md +8 -6
- package/docs/DATABASE.md +4 -4
- package/docs/MIDDLEWARE.md +3 -3
- package/docs/ROUTING.md +21 -13
- package/docs/VALIDATION.md +9 -31
- package/package.json +3 -8
- package/binding.gyp +0 -18
- package/dist/src/benchmarks/compare-frameworks.js +0 -119
- package/dist/src/benchmarks/compare.js +0 -288
- package/dist/src/buffer-pool.js +0 -70
- package/dist/src/config.js +0 -50
- package/dist/src/cookies.js +0 -59
- package/dist/src/core/native-adapter.d.ts +0 -11
- package/dist/src/core/native-adapter.js +0 -211
- package/dist/src/cors.js +0 -66
- package/dist/src/logger.js +0 -45
- package/dist/src/metrics.js +0 -111
- package/dist/src/native/index.d.ts +0 -32
- package/dist/src/native/index.js +0 -141
- package/dist/src/presets.js +0 -33
- package/dist/src/radix-router.js +0 -89
- package/dist/src/radix-tree.js +0 -81
- package/dist/src/resources.js +0 -25
- package/dist/src/router.js +0 -138
- package/dist/src/scheduler.js +0 -85
- package/dist/src/security.js +0 -69
- package/dist/src/server.js +0 -685
- package/dist/src/signals.js +0 -31
- package/dist/src/static.js +0 -107
- package/dist/src/stream.js +0 -71
- package/dist/src/tasks.js +0 -87
- package/dist/src/testing.js +0 -40
- package/dist/src/types.js +0 -19
- package/dist/src/utils/testing.js +0 -40
- package/dist/src/worker-queue.js +0 -73
- package/dist/tests/native-adapter.test.d.ts +0 -1
- package/dist/tests/native-adapter.test.js +0 -71
- package/prebuilds/darwin-arm64/qhttpx.node +0 -0
- package/prebuilds/linux-x64/qhttpx.node +0 -0
- package/prebuilds/win32-x64/qhttpx.node +0 -0
- package/scripts/install-native.js +0 -26
- package/src/native/README.md +0 -31
- package/src/native/addon.cc +0 -8
- package/src/native/index.ts +0 -158
- package/src/native/picohttpparser.c +0 -608
- package/src/native/picohttpparser.h +0 -76
- package/src/native/server.cc +0 -264
- package/src/native/server.h +0 -30
- /package/dist/src/benchmarks/{compare.d.ts → quick-bench.d.ts} +0 -0
package/dist/src/radix-router.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Radix tree router compiled into a flat array structure for zero-allocation matching.
|
|
4
|
-
* Built once at server startup (frozen).
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.RadixRouter = void 0;
|
|
8
|
-
class RadixRouter {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.roots = new Map();
|
|
11
|
-
this.isFrozen = false;
|
|
12
|
-
}
|
|
13
|
-
register(method, path, handler) {
|
|
14
|
-
if (this.isFrozen) {
|
|
15
|
-
console.warn(`Radix router is frozen. Late route registration (${method} ${path}) may not be optimized.`);
|
|
16
|
-
}
|
|
17
|
-
if (!this.roots.has(method)) {
|
|
18
|
-
this.roots.set(method, {});
|
|
19
|
-
}
|
|
20
|
-
const root = this.roots.get(method);
|
|
21
|
-
const segments = this.normalize(path);
|
|
22
|
-
let node = root;
|
|
23
|
-
for (const segment of segments) {
|
|
24
|
-
if (!node.children) {
|
|
25
|
-
node.children = new Map();
|
|
26
|
-
}
|
|
27
|
-
const isParam = segment.startsWith(':');
|
|
28
|
-
const key = isParam ? segment.slice(1) : segment;
|
|
29
|
-
if (!node.children.has(key)) {
|
|
30
|
-
node.children.set(key, {
|
|
31
|
-
paramKey: isParam ? key : undefined,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
node = node.children.get(key);
|
|
35
|
-
}
|
|
36
|
-
node.handler = handler;
|
|
37
|
-
}
|
|
38
|
-
match(method, path) {
|
|
39
|
-
const root = this.roots.get(method);
|
|
40
|
-
if (!root) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
const segments = this.normalize(path);
|
|
44
|
-
const params = {};
|
|
45
|
-
let node = root;
|
|
46
|
-
for (const segment of segments) {
|
|
47
|
-
if (!node.children) {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
// Try exact match first
|
|
51
|
-
let child = node.children.get(segment);
|
|
52
|
-
// If no exact match, try param match
|
|
53
|
-
if (!child) {
|
|
54
|
-
for (const [key, candidate] of node.children.entries()) {
|
|
55
|
-
if (candidate.paramKey && typeof key === 'string') {
|
|
56
|
-
params[key] = segment;
|
|
57
|
-
child = candidate;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (!child) {
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
node = child;
|
|
66
|
-
}
|
|
67
|
-
if (!node.handler) {
|
|
68
|
-
return undefined;
|
|
69
|
-
}
|
|
70
|
-
return { handler: node.handler, params };
|
|
71
|
-
}
|
|
72
|
-
freeze() {
|
|
73
|
-
if (this.isFrozen) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
this.isFrozen = true;
|
|
77
|
-
// Future: compile to flat array structure here
|
|
78
|
-
}
|
|
79
|
-
isFrozenRouter() {
|
|
80
|
-
return this.isFrozen;
|
|
81
|
-
}
|
|
82
|
-
normalize(path) {
|
|
83
|
-
if (!path || path === '/') {
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
|
-
return path.split('/').filter((segment) => segment.length > 0);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
exports.RadixRouter = RadixRouter;
|
package/dist/src/radix-tree.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RadixTree = void 0;
|
|
4
|
-
class Node {
|
|
5
|
-
constructor() {
|
|
6
|
-
// Map segment -> Node
|
|
7
|
-
this.children = new Map();
|
|
8
|
-
// Parameter child node (only one allowed per level for simplicity)
|
|
9
|
-
this.paramChild = null;
|
|
10
|
-
this.paramName = null;
|
|
11
|
-
// Data if this node is a route end
|
|
12
|
-
this.data = null;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
class RadixTree {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.root = new Node();
|
|
18
|
-
}
|
|
19
|
-
insert(segments, handler, priority) {
|
|
20
|
-
let node = this.root;
|
|
21
|
-
for (const segment of segments) {
|
|
22
|
-
if (segment.startsWith(':')) {
|
|
23
|
-
const paramName = segment.slice(1);
|
|
24
|
-
if (!node.paramChild) {
|
|
25
|
-
node.paramChild = new Node();
|
|
26
|
-
node.paramChild.paramName = paramName;
|
|
27
|
-
}
|
|
28
|
-
else if (node.paramChild.paramName !== paramName) {
|
|
29
|
-
throw new Error(`Cannot have two different parameter names at the same level: "${node.paramChild.paramName}" and "${paramName}"`);
|
|
30
|
-
}
|
|
31
|
-
node = node.paramChild;
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
if (!node.children.has(segment)) {
|
|
35
|
-
node.children.set(segment, new Node());
|
|
36
|
-
}
|
|
37
|
-
node = node.children.get(segment);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
node.data = { handler, priority };
|
|
41
|
-
}
|
|
42
|
-
lookup(segments) {
|
|
43
|
-
// Use a stack-based approach or recursion.
|
|
44
|
-
// For simplicity and correctness with backtracking, we'll use recursion.
|
|
45
|
-
// To minimize allocations, we pass the same params object and only copy on success?
|
|
46
|
-
// Actually, creating a params object is inevitable for the result.
|
|
47
|
-
return this.find(this.root, segments, 0);
|
|
48
|
-
}
|
|
49
|
-
find(node, segments, index) {
|
|
50
|
-
if (index === segments.length) {
|
|
51
|
-
if (node.data) {
|
|
52
|
-
return {
|
|
53
|
-
handler: node.data.handler,
|
|
54
|
-
priority: node.data.priority,
|
|
55
|
-
params: {},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const segment = segments[index];
|
|
61
|
-
// 1. Try exact match
|
|
62
|
-
const child = node.children.get(segment);
|
|
63
|
-
if (child) {
|
|
64
|
-
const result = this.find(child, segments, index + 1);
|
|
65
|
-
if (result) {
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// 2. Try param match
|
|
70
|
-
if (node.paramChild) {
|
|
71
|
-
const result = this.find(node.paramChild, segments, index + 1);
|
|
72
|
-
if (result) {
|
|
73
|
-
// If match found down this path, add current param to it
|
|
74
|
-
result.params[node.paramChild.paramName] = segment;
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
exports.RadixTree = RadixTree;
|
package/dist/src/resources.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.calculateWorkerCount = calculateWorkerCount;
|
|
7
|
-
exports.isResourceOverloaded = isResourceOverloaded;
|
|
8
|
-
const os_1 = __importDefault(require("os"));
|
|
9
|
-
function calculateWorkerCount(setting) {
|
|
10
|
-
if (typeof setting === 'number') {
|
|
11
|
-
if (!Number.isFinite(setting) || setting <= 0) {
|
|
12
|
-
return 1;
|
|
13
|
-
}
|
|
14
|
-
return Math.floor(setting);
|
|
15
|
-
}
|
|
16
|
-
const cpuCount = os_1.default.cpus().length || 1;
|
|
17
|
-
return Math.max(1, cpuCount);
|
|
18
|
-
}
|
|
19
|
-
function isResourceOverloaded(sample, thresholds) {
|
|
20
|
-
if (thresholds.maxRssBytes !== undefined &&
|
|
21
|
-
sample.rssBytes > thresholds.maxRssBytes) {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
package/dist/src/router.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Router = void 0;
|
|
4
|
-
const radix_tree_1 = require("./radix-tree");
|
|
5
|
-
const types_1 = require("./types");
|
|
6
|
-
class Router {
|
|
7
|
-
constructor() {
|
|
8
|
-
// Per-method route buckets
|
|
9
|
-
this.methodBuckets = new Map([
|
|
10
|
-
['GET', []],
|
|
11
|
-
['POST', []],
|
|
12
|
-
['PUT', []],
|
|
13
|
-
['DELETE', []],
|
|
14
|
-
]);
|
|
15
|
-
// Derived structures (built at freeze time)
|
|
16
|
-
this.radixTrees = new Map();
|
|
17
|
-
// Freeze state
|
|
18
|
-
this.isFrozen = false;
|
|
19
|
-
}
|
|
20
|
-
register(method, path, handler, options) {
|
|
21
|
-
if (this.isFrozen) {
|
|
22
|
-
console.warn(`Router is frozen. Late route registration (${method} ${path}) may not be optimized.`);
|
|
23
|
-
}
|
|
24
|
-
const segments = this.normalize(path);
|
|
25
|
-
const bucket = this.methodBuckets.get(method);
|
|
26
|
-
if (bucket) {
|
|
27
|
-
bucket.push({
|
|
28
|
-
segments,
|
|
29
|
-
handler,
|
|
30
|
-
priority: options?.priority ?? types_1.RoutePriority.STANDARD,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
match(method, path) {
|
|
35
|
-
// Fast path for frozen router
|
|
36
|
-
if (this.isFrozen) {
|
|
37
|
-
const tree = this.radixTrees.get(method);
|
|
38
|
-
if (tree) {
|
|
39
|
-
const segments = this.normalize(path);
|
|
40
|
-
const match = tree.lookup(segments);
|
|
41
|
-
if (match) {
|
|
42
|
-
return match;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
// Slow path for unfrozen router (legacy behavior)
|
|
48
|
-
const segments = this.normalize(path);
|
|
49
|
-
const bucket = this.methodBuckets.get(method);
|
|
50
|
-
if (!bucket || bucket.length === 0) {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
// Try to match against routes
|
|
54
|
-
for (const route of bucket) {
|
|
55
|
-
if (route.segments.length !== segments.length) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
const params = {};
|
|
59
|
-
let matched = true;
|
|
60
|
-
for (let i = 0; i < route.segments.length; i += 1) {
|
|
61
|
-
const pattern = route.segments[i];
|
|
62
|
-
const value = segments[i];
|
|
63
|
-
if (pattern.startsWith(':')) {
|
|
64
|
-
const key = pattern.slice(1);
|
|
65
|
-
params[key] = value;
|
|
66
|
-
}
|
|
67
|
-
else if (pattern !== value) {
|
|
68
|
-
matched = false;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (matched) {
|
|
73
|
-
return {
|
|
74
|
-
handler: route.handler,
|
|
75
|
-
params,
|
|
76
|
-
priority: route.priority,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
getAllowedMethods(path) {
|
|
83
|
-
const segments = this.normalize(path);
|
|
84
|
-
const methods = [];
|
|
85
|
-
for (const [method, routes] of this.methodBuckets.entries()) {
|
|
86
|
-
for (const route of routes) {
|
|
87
|
-
if (route.segments.length !== segments.length) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
let matched = true;
|
|
91
|
-
for (let i = 0; i < route.segments.length; i += 1) {
|
|
92
|
-
const pattern = route.segments[i];
|
|
93
|
-
const value = segments[i];
|
|
94
|
-
if (pattern.startsWith(':')) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if (pattern !== value) {
|
|
98
|
-
matched = false;
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (matched) {
|
|
103
|
-
methods.push(method);
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return methods;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Freeze the router after server starts.
|
|
112
|
-
* Prevents further route registration and builds derived structures for optimized matching.
|
|
113
|
-
*/
|
|
114
|
-
freeze() {
|
|
115
|
-
if (this.isFrozen) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
this.isFrozen = true;
|
|
119
|
-
// Build derived structures for faster matching
|
|
120
|
-
for (const [method, routes] of this.methodBuckets.entries()) {
|
|
121
|
-
const tree = new radix_tree_1.RadixTree();
|
|
122
|
-
for (const route of routes) {
|
|
123
|
-
tree.insert(route.segments, route.handler, route.priority);
|
|
124
|
-
}
|
|
125
|
-
this.radixTrees.set(method, tree);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
isFrozenRouter() {
|
|
129
|
-
return this.isFrozen;
|
|
130
|
-
}
|
|
131
|
-
normalize(path) {
|
|
132
|
-
if (!path || path === '/') {
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
return path.split('/').filter((segment) => segment.length > 0);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
exports.Router = Router;
|
package/dist/src/scheduler.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Scheduler = void 0;
|
|
4
|
-
const worker_queue_1 = require("./worker-queue");
|
|
5
|
-
const types_1 = require("./types");
|
|
6
|
-
class Scheduler {
|
|
7
|
-
constructor(options = {}) {
|
|
8
|
-
this.inFlight = 0;
|
|
9
|
-
this.nextWorkerIndex = 0;
|
|
10
|
-
const max = options.maxConcurrency ?? Infinity;
|
|
11
|
-
this.maxConcurrency = max > 0 ? max : Infinity;
|
|
12
|
-
// Initialize per-worker queues
|
|
13
|
-
this.workerCount = options.workers ?? 1;
|
|
14
|
-
this.perWorkerQueues = [];
|
|
15
|
-
for (let i = 0; i < this.workerCount; i += 1) {
|
|
16
|
-
this.perWorkerQueues.push(new worker_queue_1.WorkerQueue(1024));
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
getCurrentInFlight() {
|
|
20
|
-
return this.inFlight;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Get scheduler statistics (queued tasks per worker, etc.)
|
|
24
|
-
*/
|
|
25
|
-
getStats() {
|
|
26
|
-
return {
|
|
27
|
-
inFlight: this.inFlight,
|
|
28
|
-
maxConcurrency: this.maxConcurrency,
|
|
29
|
-
workers: this.workerCount,
|
|
30
|
-
perWorkerStats: this.perWorkerQueues.map((q, i) => ({
|
|
31
|
-
workerId: i,
|
|
32
|
-
queued: q.getSize(),
|
|
33
|
-
})),
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
async run(task, options) {
|
|
37
|
-
const priority = options.priority ?? types_1.RoutePriority.STANDARD;
|
|
38
|
-
let threshold = this.maxConcurrency;
|
|
39
|
-
if (priority === types_1.RoutePriority.BEST_EFFORT) {
|
|
40
|
-
// Shed best-effort requests if we are above 80% capacity
|
|
41
|
-
threshold = Math.max(1, Math.floor(this.maxConcurrency * 0.8));
|
|
42
|
-
}
|
|
43
|
-
else if (priority === types_1.RoutePriority.STANDARD) {
|
|
44
|
-
// Shed standard requests if we are above 95% capacity
|
|
45
|
-
threshold = Math.max(1, Math.floor(this.maxConcurrency * 0.95));
|
|
46
|
-
}
|
|
47
|
-
// CRITICAL allows up to 100%
|
|
48
|
-
if (this.inFlight >= threshold) {
|
|
49
|
-
if (options.onOverloaded) {
|
|
50
|
-
options.onOverloaded();
|
|
51
|
-
}
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
this.inFlight += 1;
|
|
55
|
-
let timeoutId;
|
|
56
|
-
try {
|
|
57
|
-
if (!options.timeoutMs || options.timeoutMs <= 0) {
|
|
58
|
-
const result = task();
|
|
59
|
-
if (result && typeof result.then === 'function') {
|
|
60
|
-
await result;
|
|
61
|
-
}
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const taskPromise = Promise.resolve(task()).then(() => 'ok');
|
|
65
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
66
|
-
timeoutId = setTimeout(() => {
|
|
67
|
-
resolve('timeout');
|
|
68
|
-
}, options.timeoutMs);
|
|
69
|
-
});
|
|
70
|
-
const result = await Promise.race([taskPromise, timeoutPromise]);
|
|
71
|
-
if (result === 'timeout') {
|
|
72
|
-
if (options.onTimeout) {
|
|
73
|
-
options.onTimeout();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
if (timeoutId) {
|
|
79
|
-
clearTimeout(timeoutId);
|
|
80
|
-
}
|
|
81
|
-
this.inFlight -= 1;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
exports.Scheduler = Scheduler;
|
package/dist/src/security.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createSecurityHeadersMiddleware = createSecurityHeadersMiddleware;
|
|
4
|
-
exports.createSecureDefaults = createSecureDefaults;
|
|
5
|
-
exports.createRateLimitMiddleware = createRateLimitMiddleware;
|
|
6
|
-
const cors_1 = require("./cors");
|
|
7
|
-
function createSecurityHeadersMiddleware(options = {}) {
|
|
8
|
-
const { contentSecurityPolicy = "default-src 'self'", referrerPolicy = 'no-referrer', xFrameOptions = 'SAMEORIGIN', xContentTypeOptions = 'nosniff', xXssProtection = '1; mode=block', strictTransportSecurity, } = options;
|
|
9
|
-
return async (ctx, next) => {
|
|
10
|
-
if (contentSecurityPolicy) {
|
|
11
|
-
ctx.res.setHeader('content-security-policy', contentSecurityPolicy);
|
|
12
|
-
}
|
|
13
|
-
if (referrerPolicy) {
|
|
14
|
-
ctx.res.setHeader('referrer-policy', referrerPolicy);
|
|
15
|
-
}
|
|
16
|
-
if (xFrameOptions) {
|
|
17
|
-
ctx.res.setHeader('x-frame-options', xFrameOptions);
|
|
18
|
-
}
|
|
19
|
-
if (xContentTypeOptions) {
|
|
20
|
-
ctx.res.setHeader('x-content-type-options', xContentTypeOptions);
|
|
21
|
-
}
|
|
22
|
-
if (xXssProtection) {
|
|
23
|
-
ctx.res.setHeader('x-xss-protection', xXssProtection);
|
|
24
|
-
}
|
|
25
|
-
if (strictTransportSecurity) {
|
|
26
|
-
ctx.res.setHeader('strict-transport-security', strictTransportSecurity);
|
|
27
|
-
}
|
|
28
|
-
await next();
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function createSecureDefaults(options = {}) {
|
|
32
|
-
const middlewares = [];
|
|
33
|
-
middlewares.push((0, cors_1.createCorsMiddleware)(options.cors));
|
|
34
|
-
middlewares.push(createSecurityHeadersMiddleware(options.securityHeaders));
|
|
35
|
-
return middlewares;
|
|
36
|
-
}
|
|
37
|
-
function createRateLimitMiddleware(options) {
|
|
38
|
-
const maxRequests = options.maxRequests;
|
|
39
|
-
const windowMs = options.windowMs;
|
|
40
|
-
const keyGenerator = options.keyGenerator ??
|
|
41
|
-
((ctx) => {
|
|
42
|
-
const addr = ctx.req.socket.remoteAddress;
|
|
43
|
-
return addr || 'anonymous';
|
|
44
|
-
});
|
|
45
|
-
const buckets = new Map();
|
|
46
|
-
return async (ctx, next) => {
|
|
47
|
-
const now = Date.now();
|
|
48
|
-
const key = keyGenerator(ctx);
|
|
49
|
-
const existing = buckets.get(key);
|
|
50
|
-
let bucket = existing;
|
|
51
|
-
if (!bucket || bucket.resetAt <= now) {
|
|
52
|
-
bucket = {
|
|
53
|
-
count: 0,
|
|
54
|
-
resetAt: now + windowMs,
|
|
55
|
-
};
|
|
56
|
-
buckets.set(key, bucket);
|
|
57
|
-
}
|
|
58
|
-
bucket.count += 1;
|
|
59
|
-
if (bucket.count > maxRequests) {
|
|
60
|
-
if (!ctx.res.headersSent) {
|
|
61
|
-
ctx.res.statusCode = 429;
|
|
62
|
-
ctx.res.setHeader('content-type', 'text/plain; charset=utf-8');
|
|
63
|
-
}
|
|
64
|
-
ctx.res.end('Too Many Requests');
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
await next();
|
|
68
|
-
};
|
|
69
|
-
}
|