@morojs/moro 1.8.11 → 1.8.13
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/README.md +4 -2
- package/dist/core/cluster/thread-cluster.d.ts +25 -0
- package/dist/core/cluster/thread-cluster.js +95 -3
- package/dist/core/cluster/thread-cluster.js.map +1 -1
- package/dist/core/config/config-validator.js +15 -0
- package/dist/core/config/config-validator.js.map +1 -1
- package/dist/core/config/index.js +19 -2
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/framework.d.ts +26 -0
- package/dist/core/framework.js +195 -6
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +17 -0
- package/dist/core/http/http-server.js +21 -1
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/http2-server.d.ts +13 -0
- package/dist/core/http/http2-server.js +11 -0
- package/dist/core/http/http2-server.js.map +1 -1
- package/dist/core/http/moro-engine-server.d.ts +28 -0
- package/dist/core/http/moro-engine-server.js +118 -12
- package/dist/core/http/moro-engine-server.js.map +1 -1
- package/dist/core/http/utils/body-type.d.ts +12 -0
- package/dist/core/http/utils/body-type.js +70 -0
- package/dist/core/http/utils/body-type.js.map +1 -0
- package/dist/core/http/utils/ssl-config.d.ts +11 -0
- package/dist/core/http/utils/ssl-config.js +3 -0
- package/dist/core/http/utils/ssl-config.js.map +1 -1
- package/dist/core/http/utils/uws-worker-clustering.js +5 -4
- package/dist/core/http/utils/uws-worker-clustering.js.map +1 -1
- package/dist/core/http/uws-http-server.d.ts +3 -0
- package/dist/core/http/uws-http-server.js +22 -1
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/middleware/built-in/body-parsers/index.d.ts +18 -0
- package/dist/core/middleware/built-in/body-parsers/index.js +41 -0
- package/dist/core/middleware/built-in/body-parsers/index.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.d.ts +32 -8
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +117 -25
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
- package/dist/core/middleware/built-in/compression/core.js +5 -0
- package/dist/core/middleware/built-in/compression/core.js.map +1 -1
- package/dist/core/middleware/built-in/session/core.d.ts +1 -0
- package/dist/core/middleware/built-in/session/core.js.map +1 -1
- package/dist/core/middleware/built-in/static/core.d.ts +18 -0
- package/dist/core/middleware/built-in/static/core.js +128 -7
- package/dist/core/middleware/built-in/static/core.js.map +1 -1
- package/dist/core/networking/adapters/engine-adapter.d.ts +2 -2
- package/dist/core/networking/adapters/engine-adapter.js +40 -1
- package/dist/core/networking/adapters/engine-adapter.js.map +1 -1
- package/dist/core/networking/adapters/socketio-adapter.d.ts +2 -2
- package/dist/core/networking/adapters/socketio-adapter.js +10 -1
- package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.d.ts +2 -2
- package/dist/core/networking/adapters/uws-adapter.js +54 -3
- package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
- package/dist/core/networking/adapters/ws-adapter.d.ts +9 -2
- package/dist/core/networking/adapters/ws-adapter.js +97 -12
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
- package/dist/core/networking/websocket-adapter.d.ts +19 -1
- package/dist/core/networking/websocket-adapter.js.map +1 -1
- package/dist/core/routing/index.d.ts +12 -7
- package/dist/core/routing/index.js +7 -2
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/routing/unified-router.d.ts +48 -9
- package/dist/core/routing/unified-router.js +112 -34
- package/dist/core/routing/unified-router.js.map +1 -1
- package/dist/core/utilities/package-utils.d.ts +11 -0
- package/dist/core/utilities/package-utils.js +3 -0
- package/dist/core/utilities/package-utils.js.map +1 -1
- package/dist/core/workers/worker-manager.js +2 -2
- package/dist/core/workers/worker-manager.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/moro.d.ts +29 -18
- package/dist/moro.js +63 -7
- package/dist/moro.js.map +1 -1
- package/dist/types/cache.d.ts +2 -0
- package/dist/types/cache.js.map +1 -1
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/http.d.ts +6 -0
- package/dist/types/http.js.map +1 -1
- package/dist/types/session.d.ts +1 -0
- package/dist/types/session.js.map +1 -1
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ import { createFrameworkLogger } from '../logger/index.js';
|
|
|
20
20
|
import { loadNativeEngine, getNativeEngineLoadErrors, } from '../utilities/package-utils.js';
|
|
21
21
|
import { parseMultipart as parseMultipartBuffer, } from './utils/multipart-parser.js';
|
|
22
22
|
import { resolveCompressionSettings, compressBuffer, isCompressible, negotiateEncoding, } from './utils/compression.js';
|
|
23
|
+
import { bodyIsText } from './utils/body-type.js';
|
|
23
24
|
import { LazyEventEmitter } from './utils/lazy-event-emitter.js';
|
|
24
25
|
import { parseRawQueryString as parseUwsQueryString } from './utils/query-parser.js';
|
|
25
26
|
import { PathMatcher } from '../routing/path-matcher.js';
|
|
@@ -148,6 +149,12 @@ export class EngineRequest extends LazyEventEmitter {
|
|
|
148
149
|
method;
|
|
149
150
|
path;
|
|
150
151
|
body = null;
|
|
152
|
+
/** Undecoded request body bytes (parseBody keeps the same Buffer it parsed) */
|
|
153
|
+
_rawBody = undefined;
|
|
154
|
+
/** The request body exactly as received, before any parsing; null when empty. */
|
|
155
|
+
get rawBody() {
|
|
156
|
+
return this._rawBody ?? null;
|
|
157
|
+
}
|
|
151
158
|
_server;
|
|
152
159
|
_reqId;
|
|
153
160
|
_params = undefined;
|
|
@@ -1468,6 +1475,13 @@ export class MoroEngineServer {
|
|
|
1468
1475
|
/** @internal */ _setStaticRoute;
|
|
1469
1476
|
_staticOn = false;
|
|
1470
1477
|
staticRoutes = [];
|
|
1478
|
+
// Engine-answered parameter routes (engine >= 1.1.9, capabilities.paramRoutes):
|
|
1479
|
+
// one variable path segment between prefix and suffix, echoed as the body,
|
|
1480
|
+
// sent without calling into JS. Same guard and same re-apply story as the
|
|
1481
|
+
// static routes above.
|
|
1482
|
+
/** @internal */ _setParamRoute;
|
|
1483
|
+
_paramOn = false;
|
|
1484
|
+
paramRoutes = [];
|
|
1471
1485
|
// Batched pipelined dispatch (engine >= 1.1.6, capabilities.batchDispatch):
|
|
1472
1486
|
// the engine parses complete pipelined requests ahead and delivers them in
|
|
1473
1487
|
// one onRequestBatch(count) call; the descriptors/control/paths buffers are
|
|
@@ -1593,6 +1607,10 @@ export class MoroEngineServer {
|
|
|
1593
1607
|
this._setStaticRoute = surface.setStaticRoute;
|
|
1594
1608
|
this._staticOn = true;
|
|
1595
1609
|
}
|
|
1610
|
+
if (this._capabilities?.paramRoutes === true && typeof surface.setParamRoute === 'function') {
|
|
1611
|
+
this._setParamRoute = surface.setParamRoute;
|
|
1612
|
+
this._paramOn = true;
|
|
1613
|
+
}
|
|
1596
1614
|
if (this._capabilities?.batchDispatch === true &&
|
|
1597
1615
|
typeof surface.getBatchBuffers === 'function' &&
|
|
1598
1616
|
typeof surface.getPath === 'function') {
|
|
@@ -1619,7 +1637,11 @@ export class MoroEngineServer {
|
|
|
1619
1637
|
this.handle = Object.defineProperties({
|
|
1620
1638
|
engine: '@morojs/engine',
|
|
1621
1639
|
address: () => this.isListening
|
|
1622
|
-
? {
|
|
1640
|
+
? {
|
|
1641
|
+
address: this.host || '::',
|
|
1642
|
+
family: (this.host || '::').includes(':') ? 'IPv6' : 'IPv4',
|
|
1643
|
+
port: this.port || 0,
|
|
1644
|
+
}
|
|
1623
1645
|
: null,
|
|
1624
1646
|
close: (cb) => {
|
|
1625
1647
|
this.close(cb);
|
|
@@ -1709,13 +1731,58 @@ export class MoroEngineServer {
|
|
|
1709
1731
|
}
|
|
1710
1732
|
}
|
|
1711
1733
|
// listen() after close() registers a fresh native server; give it every
|
|
1712
|
-
// static route the old one had.
|
|
1734
|
+
// static and parameter route the old one had.
|
|
1713
1735
|
_reapplyStaticRoutes() {
|
|
1714
1736
|
const fn = this._setStaticRoute;
|
|
1715
|
-
if (fn
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1737
|
+
if (fn !== undefined) {
|
|
1738
|
+
for (const r of this.staticRoutes) {
|
|
1739
|
+
fn(this.serverId, r.methodIdx, r.path, r.status, r.headersFlat, r.body);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
const pfn = this._setParamRoute;
|
|
1743
|
+
if (pfn !== undefined) {
|
|
1744
|
+
for (const r of this.paramRoutes) {
|
|
1745
|
+
pfn(this.serverId, r.methodIdx, r.prefix, r.suffix, r.status, r.headersFlat);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
/** Whether this engine build answers parameter routes itself. */
|
|
1750
|
+
get paramRoutesEnabled() {
|
|
1751
|
+
return this._paramOn;
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Register a route with one variable path segment, between `prefix` and
|
|
1755
|
+
* `suffix`, that the engine answers itself with that segment as the body:
|
|
1756
|
+
* "/user/" + "" for `/user/:id`. The segment goes out undecoded, as it is
|
|
1757
|
+
* on the wire. Returns false when this engine cannot take it (no
|
|
1758
|
+
* capability, compression on, or an unindexed method), so the caller keeps
|
|
1759
|
+
* its JS handler. Registering the same (method, prefix, suffix) again
|
|
1760
|
+
* replaces the earlier reply.
|
|
1761
|
+
*/
|
|
1762
|
+
setParamRoute(method, prefix, suffix, status = 200, headers) {
|
|
1763
|
+
const fn = this._setParamRoute;
|
|
1764
|
+
if (!this._paramOn || fn === undefined)
|
|
1765
|
+
return false;
|
|
1766
|
+
if (this._compression.enabled)
|
|
1767
|
+
return false;
|
|
1768
|
+
const methodIdx = METHODS.indexOf(method.toUpperCase());
|
|
1769
|
+
if (methodIdx < 0 || methodIdx >= STATIC_ROUTE_METHODS)
|
|
1770
|
+
return false;
|
|
1771
|
+
const headersFlat = flattenHeaders(headers);
|
|
1772
|
+
const entry = { methodIdx, prefix, suffix, status, headersFlat };
|
|
1773
|
+
const at = this.paramRoutes.findIndex(r => r.methodIdx === methodIdx && r.prefix === prefix && r.suffix === suffix);
|
|
1774
|
+
if (at === -1)
|
|
1775
|
+
this.paramRoutes.push(entry);
|
|
1776
|
+
else
|
|
1777
|
+
this.paramRoutes[at] = entry;
|
|
1778
|
+
fn(this.serverId, methodIdx, prefix, suffix, status, headersFlat);
|
|
1779
|
+
return true;
|
|
1780
|
+
}
|
|
1781
|
+
/** Drop every engine-answered parameter route; the JS routes are untouched. */
|
|
1782
|
+
clearParamRoutes() {
|
|
1783
|
+
this.paramRoutes.length = 0;
|
|
1784
|
+
if (this._paramOn && typeof this._engine.clearParamRoutes === 'function') {
|
|
1785
|
+
this._engine.clearParamRoutes(this.serverId);
|
|
1719
1786
|
}
|
|
1720
1787
|
}
|
|
1721
1788
|
// Register callbacks + limits with the native engine, returning the new
|
|
@@ -2113,7 +2180,9 @@ export class MoroEngineServer {
|
|
|
2113
2180
|
// closes, in this loop iteration's check phase. One per burst is enough,
|
|
2114
2181
|
// so it is armed once and re-armed only after it has fired.
|
|
2115
2182
|
_armMicrotaskDrain() {
|
|
2116
|
-
|
|
2183
|
+
// An engine that opens a Node callback scope around every callback
|
|
2184
|
+
// (capabilities.callbackScope) drains the queue itself on return.
|
|
2185
|
+
if (this._drainArmed || this._capabilities?.callbackScope === true)
|
|
2117
2186
|
return;
|
|
2118
2187
|
this._drainArmed = true;
|
|
2119
2188
|
setImmediate(() => {
|
|
@@ -2277,6 +2346,9 @@ export class MoroEngineServer {
|
|
|
2277
2346
|
throw error;
|
|
2278
2347
|
}
|
|
2279
2348
|
const buffer = Buffer.from(raw);
|
|
2349
|
+
// getBody() handed us a stable copy, so the raw bytes cost nothing to keep
|
|
2350
|
+
// (webhook signature checks need them even for JSON bodies)
|
|
2351
|
+
httpReq._rawBody = buffer;
|
|
2280
2352
|
try {
|
|
2281
2353
|
if (contentType.includes('application/json')) {
|
|
2282
2354
|
httpReq.body = JSON.parse(buffer.toString('utf-8'));
|
|
@@ -2295,7 +2367,9 @@ export class MoroEngineServer {
|
|
|
2295
2367
|
httpReq.body = parseMultipartBuffer(buffer, contentType, this.multipartLimits);
|
|
2296
2368
|
}
|
|
2297
2369
|
else {
|
|
2298
|
-
|
|
2370
|
+
// Text decodes to a string; binary (octet-stream, images, protobuf,
|
|
2371
|
+
// ...) stays a Buffer - decoding it would corrupt it irreversibly.
|
|
2372
|
+
httpReq.body = bodyIsText(contentType) ? buffer.toString('utf-8') : buffer;
|
|
2299
2373
|
}
|
|
2300
2374
|
}
|
|
2301
2375
|
catch (parseError) {
|
|
@@ -2445,7 +2519,10 @@ export class MoroEngineServer {
|
|
|
2445
2519
|
this.logger.warn('Server is already listening', 'Listen');
|
|
2446
2520
|
return;
|
|
2447
2521
|
}
|
|
2448
|
-
|
|
2522
|
+
// No host = every interface, dual-stack, exactly like Node's
|
|
2523
|
+
// server.listen(port): a client that resolves `localhost` to ::1 first
|
|
2524
|
+
// must not be refused. Hosts without IPv6 fall back to IPv4-any below.
|
|
2525
|
+
const host = typeof hostOrCallback === 'string' ? hostOrCallback : '::';
|
|
2449
2526
|
const cb = typeof hostOrCallback === 'function' ? hostOrCallback : callback;
|
|
2450
2527
|
try {
|
|
2451
2528
|
// listen() after close(): the old native server's handles are torn
|
|
@@ -2459,11 +2536,23 @@ export class MoroEngineServer {
|
|
|
2459
2536
|
}
|
|
2460
2537
|
// Synchronous bind; throws on bind errors, returns the actual port
|
|
2461
2538
|
// (meaningful when port 0 asks for an ephemeral one)
|
|
2462
|
-
|
|
2539
|
+
let boundHost = host;
|
|
2540
|
+
let actualPort;
|
|
2541
|
+
try {
|
|
2542
|
+
actualPort = this._engine.listen(this.serverId, host, port);
|
|
2543
|
+
}
|
|
2544
|
+
catch (error) {
|
|
2545
|
+
const code = error?.code;
|
|
2546
|
+
if (host !== '::' || (code !== 'EAFNOSUPPORT' && code !== 'EADDRNOTAVAIL'))
|
|
2547
|
+
throw error;
|
|
2548
|
+
// IPv6 not configured on this host - IPv4-any is what Node does too
|
|
2549
|
+
boundHost = '0.0.0.0';
|
|
2550
|
+
actualPort = this._engine.listen(this.serverId, boundHost, port);
|
|
2551
|
+
}
|
|
2463
2552
|
this.port = typeof actualPort === 'number' ? actualPort : port;
|
|
2464
|
-
this.host =
|
|
2553
|
+
this.host = boundHost;
|
|
2465
2554
|
this.isListening = true;
|
|
2466
|
-
this.logger.info(`Moro engine HTTP server listening on ${
|
|
2555
|
+
this.logger.info(`Moro engine HTTP server listening on ${boundHost}:${this.port}`, 'Listen');
|
|
2467
2556
|
if (cb)
|
|
2468
2557
|
cb();
|
|
2469
2558
|
}
|
|
@@ -2537,6 +2626,23 @@ export class MoroEngineServer {
|
|
|
2537
2626
|
callback(error instanceof Error ? error : new Error(String(error)));
|
|
2538
2627
|
}
|
|
2539
2628
|
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Replace the TLS certificate/key for every handshake from now on
|
|
2631
|
+
* (engine capabilities.tlsReload). Established connections keep the context
|
|
2632
|
+
* they handshaked with. Validated by the engine before the swap: a bad
|
|
2633
|
+
* config throws and the current certificate keeps serving.
|
|
2634
|
+
*/
|
|
2635
|
+
updateSsl(ssl) {
|
|
2636
|
+
if (!this._ssl || !this.isSsl) {
|
|
2637
|
+
throw new Error('updateSsl: this server was not started with TLS (server.ssl)');
|
|
2638
|
+
}
|
|
2639
|
+
if (!this._capabilities?.tlsReload || typeof this._engine.updateSsl !== 'function') {
|
|
2640
|
+
throw new Error('TLS reload needs @morojs/engine with capabilities.tlsReload (engine >= 1.1.8)');
|
|
2641
|
+
}
|
|
2642
|
+
this._engine.updateSsl(this.serverId, ssl);
|
|
2643
|
+
// A later close() -> listen() cycle re-registers with the new material
|
|
2644
|
+
this._ssl = ssl;
|
|
2645
|
+
}
|
|
2540
2646
|
getServer() {
|
|
2541
2647
|
// Stable observability handle (listening/address) - there is no app object
|
|
2542
2648
|
return this.handle;
|