@gravito/ripple 4.0.0 → 4.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/dist/atlas/src/DB.d.ts +51 -4
- package/dist/atlas/src/config/index.d.ts +1 -1
- package/dist/atlas/src/config/loadConfig.d.ts +0 -7
- package/dist/atlas/src/connection/Connection.d.ts +4 -0
- package/dist/atlas/src/connection/ConnectionManager.d.ts +75 -6
- package/dist/atlas/src/connection/ReplicaConnectionPool.d.ts +54 -0
- package/dist/atlas/src/drivers/MySQLDriver.d.ts +16 -3
- package/dist/atlas/src/drivers/PostgresDriver.d.ts +15 -2
- package/dist/atlas/src/index.d.ts +15 -3
- package/dist/atlas/src/observability/AtlasMetrics.d.ts +22 -0
- package/dist/atlas/src/orm/Repository.d.ts +247 -0
- package/dist/atlas/src/orm/index.d.ts +1 -0
- package/dist/atlas/src/orm/model/Model.d.ts +11 -2
- package/dist/atlas/src/orm/model/concerns/HasAttributes.d.ts +14 -0
- package/dist/atlas/src/orm/model/concerns/HasPersistence.d.ts +5 -0
- package/dist/atlas/src/orm/model/decorators.d.ts +29 -0
- package/dist/atlas/src/orm/model/index.d.ts +1 -1
- package/dist/atlas/src/orm/schema/SchemaRegistry.d.ts +1 -0
- package/dist/atlas/src/pool/AdaptivePoolManager.d.ts +98 -0
- package/dist/atlas/src/pool/PoolHealthChecker.d.ts +91 -0
- package/dist/atlas/src/pool/PoolStrategy.d.ts +129 -0
- package/dist/atlas/src/pool/PoolWarmer.d.ts +92 -0
- package/dist/atlas/src/query/QueryBuilder.d.ts +71 -1
- package/dist/atlas/src/query/RelationshipResolver.d.ts +23 -0
- package/dist/atlas/src/schema/MigrationGenerator.d.ts +45 -0
- package/dist/atlas/src/schema/SchemaDiff.d.ts +73 -0
- package/dist/atlas/src/schema/TypeGenerator.d.ts +57 -0
- package/dist/atlas/src/schema/TypeWriter.d.ts +42 -0
- package/dist/atlas/src/sharding/ShardingManager.d.ts +59 -0
- package/dist/atlas/src/types/index.d.ts +83 -1
- package/dist/atlas/src/utils/CursorEncoding.d.ts +63 -0
- package/dist/core/src/ConfigManager.d.ts +39 -0
- package/dist/core/src/Container/RequestScopeManager.d.ts +62 -0
- package/dist/core/src/Container/RequestScopeMetrics.d.ts +144 -0
- package/dist/core/src/Container.d.ts +45 -0
- package/dist/core/src/ErrorHandler.d.ts +3 -0
- package/dist/core/src/HookManager.d.ts +95 -0
- package/dist/core/src/PlanetCore.d.ts +89 -0
- package/dist/core/src/RequestContext.d.ts +97 -0
- package/dist/core/src/ServiceProvider.d.ts +22 -0
- package/dist/core/src/adapters/PhotonAdapter.d.ts +4 -0
- package/dist/core/src/adapters/bun/BunContext.d.ts +4 -0
- package/dist/core/src/cli/queue-commands.d.ts +6 -0
- package/dist/core/src/engine/AOTRouter.d.ts +6 -1
- package/dist/core/src/engine/FastContext.d.ts +23 -0
- package/dist/core/src/engine/Gravito.d.ts +0 -1
- package/dist/core/src/engine/MinimalContext.d.ts +21 -0
- package/dist/core/src/engine/types.d.ts +3 -0
- package/dist/core/src/error-handling/RequestScopeErrorContext.d.ts +126 -0
- package/dist/core/src/events/BackpressureManager.d.ts +215 -0
- package/dist/core/src/events/DeadLetterQueue.d.ts +75 -1
- package/dist/core/src/events/EventBackend.d.ts +2 -1
- package/dist/core/src/events/EventOptions.d.ts +99 -4
- package/dist/core/src/events/EventPriorityQueue.d.ts +105 -6
- package/dist/core/src/events/FlowControlStrategy.d.ts +109 -0
- package/dist/core/src/events/MessageQueueBridge.d.ts +184 -0
- package/dist/core/src/events/PriorityEscalationManager.d.ts +82 -0
- package/dist/core/src/events/RetryScheduler.d.ts +104 -0
- package/dist/core/src/events/WorkerPool.d.ts +98 -0
- package/dist/core/src/events/WorkerPoolConfig.d.ts +153 -0
- package/dist/core/src/events/WorkerPoolMetrics.d.ts +65 -0
- package/dist/core/src/events/aggregation/AggregationWindow.d.ts +77 -0
- package/dist/core/src/events/aggregation/DeduplicationManager.d.ts +135 -0
- package/dist/core/src/events/aggregation/EventAggregationManager.d.ts +108 -0
- package/dist/core/src/events/aggregation/EventBatcher.d.ts +99 -0
- package/dist/core/src/events/aggregation/types.d.ts +117 -0
- package/dist/core/src/events/index.d.ts +11 -0
- package/dist/core/src/events/observability/OTelEventMetrics.d.ts +92 -0
- package/dist/core/src/events/observability/StreamWorkerMetrics.d.ts +76 -0
- package/dist/core/src/events/observability/index.d.ts +4 -0
- package/dist/core/src/events/types.d.ts +59 -0
- package/dist/core/src/health/HealthProvider.d.ts +67 -0
- package/dist/core/src/http/types.d.ts +19 -0
- package/dist/core/src/index.d.ts +13 -1
- package/dist/core/src/observability/Metrics.d.ts +244 -0
- package/dist/core/src/observability/QueueDashboard.d.ts +136 -0
- package/dist/core/src/reliability/DeadLetterQueueManager.d.ts +34 -0
- package/dist/index.js +403 -40
- package/dist/index.js.map +11 -9
- package/dist/photon/src/index.d.ts +5 -0
- package/dist/photon/src/middleware/ratelimit-redis.d.ts +50 -0
- package/dist/photon/src/middleware/ratelimit.d.ts +4 -0
- package/dist/ripple/src/RippleServer.d.ts +0 -1
- package/dist/ripple/src/engines/UWebSocketsEngine.d.ts +97 -0
- package/dist/ripple/src/engines/WsEngine.d.ts +69 -0
- package/dist/ripple/src/engines/index.d.ts +4 -0
- package/dist/ripple/src/serializers/ISerializer.d.ts +1 -1
- package/dist/ripple/src/serializers/JsonSerializer.d.ts +1 -1
- package/dist/ripple/src/serializers/ProtobufSerializer.d.ts +6 -3
- package/dist/ripple/src/types.d.ts +11 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -745,7 +745,7 @@ var require_writer = __commonJS((exports, module) => {
|
|
|
745
745
|
this.tail = this.head;
|
|
746
746
|
this.states = null;
|
|
747
747
|
}
|
|
748
|
-
var create = function
|
|
748
|
+
var create = function create2() {
|
|
749
749
|
return util.Buffer ? function create_buffer_setup() {
|
|
750
750
|
return (Writer.create = function create_buffer() {
|
|
751
751
|
return new BufferWriter;
|
|
@@ -969,12 +969,12 @@ var require_reader = __commonJS((exports, module) => {
|
|
|
969
969
|
if (buffer instanceof Uint8Array || Array.isArray(buffer))
|
|
970
970
|
return new Reader(buffer);
|
|
971
971
|
throw Error("illegal buffer");
|
|
972
|
-
} : function
|
|
972
|
+
} : function create_array2(buffer) {
|
|
973
973
|
if (Array.isArray(buffer))
|
|
974
974
|
return new Reader(buffer);
|
|
975
975
|
throw Error("illegal buffer");
|
|
976
976
|
};
|
|
977
|
-
var create = function
|
|
977
|
+
var create = function create2() {
|
|
978
978
|
return util.Buffer ? function create_buffer_setup(buffer) {
|
|
979
979
|
return (Reader.create = function create_buffer(buffer2) {
|
|
980
980
|
return util.Buffer.isBuffer(buffer2) ? new BufferReader(buffer2) : create_array(buffer2);
|
|
@@ -1400,10 +1400,10 @@ var require_fetch = __commonJS((exports, module) => {
|
|
|
1400
1400
|
// ../../node_modules/.bun/@protobufjs+path@1.1.2/node_modules/@protobufjs/path/index.js
|
|
1401
1401
|
var require_path = __commonJS((exports) => {
|
|
1402
1402
|
var path = exports;
|
|
1403
|
-
var isAbsolute = path.isAbsolute = function
|
|
1403
|
+
var isAbsolute = path.isAbsolute = function isAbsolute2(path2) {
|
|
1404
1404
|
return /^(?:\/|\w+:)/.test(path2);
|
|
1405
1405
|
};
|
|
1406
|
-
var normalize = path.normalize = function
|
|
1406
|
+
var normalize = path.normalize = function normalize2(path2) {
|
|
1407
1407
|
path2 = path2.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
|
|
1408
1408
|
var parts = path2.split("/"), absolute = isAbsolute(path2), prefix = "";
|
|
1409
1409
|
if (absolute)
|
|
@@ -5302,8 +5302,9 @@ class NATSDriver {
|
|
|
5302
5302
|
return this._initialized;
|
|
5303
5303
|
}
|
|
5304
5304
|
async init() {
|
|
5305
|
-
if (this._initialized)
|
|
5305
|
+
if (this._initialized) {
|
|
5306
5306
|
return;
|
|
5307
|
+
}
|
|
5307
5308
|
this.logger.info("Initializing NATSDriver", {
|
|
5308
5309
|
servers: this.config.servers ?? "nats://localhost:4222"
|
|
5309
5310
|
});
|
|
@@ -5957,7 +5958,7 @@ class BunEngine {
|
|
|
5957
5958
|
hostname: this.config.hostname,
|
|
5958
5959
|
tls: this.config.tls,
|
|
5959
5960
|
development: this.config.development,
|
|
5960
|
-
fetch: (
|
|
5961
|
+
fetch: (_req, _server) => {
|
|
5961
5962
|
return new Response("WebSocket endpoint. Use upgrade() to connect.", { status: 404 });
|
|
5962
5963
|
},
|
|
5963
5964
|
websocket: {
|
|
@@ -5968,19 +5969,21 @@ class BunEngine {
|
|
|
5968
5969
|
},
|
|
5969
5970
|
message: (ws, message) => {
|
|
5970
5971
|
const socket = this.sockets.get(ws.data.id);
|
|
5971
|
-
if (!socket)
|
|
5972
|
+
if (!socket) {
|
|
5972
5973
|
return;
|
|
5974
|
+
}
|
|
5973
5975
|
const data = typeof message === "string" ? message : message instanceof Buffer ? new Uint8Array(message.buffer, message.byteOffset, message.byteLength) : new Uint8Array(message);
|
|
5974
5976
|
this.messageHandler?.(socket, data);
|
|
5975
5977
|
},
|
|
5976
5978
|
close: (ws, code, reason) => {
|
|
5977
5979
|
const socket = this.sockets.get(ws.data.id);
|
|
5978
|
-
if (!socket)
|
|
5980
|
+
if (!socket) {
|
|
5979
5981
|
return;
|
|
5982
|
+
}
|
|
5980
5983
|
this.disconnectionHandler?.(socket, code, reason);
|
|
5981
5984
|
this.sockets.delete(ws.data.id);
|
|
5982
5985
|
},
|
|
5983
|
-
drain: (
|
|
5986
|
+
drain: (_ws) => {}
|
|
5984
5987
|
}
|
|
5985
5988
|
});
|
|
5986
5989
|
}
|
|
@@ -5992,8 +5995,9 @@ class BunEngine {
|
|
|
5992
5995
|
this.sockets.clear();
|
|
5993
5996
|
}
|
|
5994
5997
|
broadcast(topic, data, excludeSocketId) {
|
|
5995
|
-
if (!this.server)
|
|
5998
|
+
if (!this.server) {
|
|
5996
5999
|
return;
|
|
6000
|
+
}
|
|
5997
6001
|
if (excludeSocketId) {
|
|
5998
6002
|
for (const [id, socket] of this.sockets) {
|
|
5999
6003
|
if (id !== excludeSocketId && socket.raw.isSubscribed(topic)) {
|
|
@@ -6025,6 +6029,324 @@ class BunEngine {
|
|
|
6025
6029
|
}
|
|
6026
6030
|
}
|
|
6027
6031
|
|
|
6032
|
+
// src/engines/UWebSocketsEngine.ts
|
|
6033
|
+
import { randomUUID } from "crypto";
|
|
6034
|
+
|
|
6035
|
+
class UWebSocketsRippleSocket {
|
|
6036
|
+
ws;
|
|
6037
|
+
constructor(ws) {
|
|
6038
|
+
this.ws = ws;
|
|
6039
|
+
}
|
|
6040
|
+
get id() {
|
|
6041
|
+
return this.ws.getUserData().id;
|
|
6042
|
+
}
|
|
6043
|
+
get data() {
|
|
6044
|
+
return this.ws.getUserData();
|
|
6045
|
+
}
|
|
6046
|
+
send(data, compress) {
|
|
6047
|
+
if (typeof data === "string") {
|
|
6048
|
+
this.ws.send(data, false, compress);
|
|
6049
|
+
} else {
|
|
6050
|
+
const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
6051
|
+
this.ws.send(buffer, true, compress);
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
close(_code, _reason) {
|
|
6055
|
+
this.ws.close();
|
|
6056
|
+
}
|
|
6057
|
+
getBufferedAmount() {
|
|
6058
|
+
return this.ws.getBufferedAmount();
|
|
6059
|
+
}
|
|
6060
|
+
subscribe(topic) {
|
|
6061
|
+
this.ws.subscribe(topic);
|
|
6062
|
+
}
|
|
6063
|
+
unsubscribe(topic) {
|
|
6064
|
+
this.ws.unsubscribe(topic);
|
|
6065
|
+
}
|
|
6066
|
+
publish(topic, data) {
|
|
6067
|
+
if (typeof data === "string") {
|
|
6068
|
+
this.ws.publish(topic, data, false);
|
|
6069
|
+
} else {
|
|
6070
|
+
const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
6071
|
+
this.ws.publish(topic, buffer, true);
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
get raw() {
|
|
6075
|
+
return this.ws;
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6079
|
+
class UWebSocketsEngine {
|
|
6080
|
+
config;
|
|
6081
|
+
name = "node-uws";
|
|
6082
|
+
app;
|
|
6083
|
+
uws;
|
|
6084
|
+
connectionHandler;
|
|
6085
|
+
disconnectionHandler;
|
|
6086
|
+
messageHandler;
|
|
6087
|
+
sockets = new Map;
|
|
6088
|
+
listenSocket;
|
|
6089
|
+
constructor(config = {}) {
|
|
6090
|
+
this.config = config;
|
|
6091
|
+
}
|
|
6092
|
+
onConnection(handler) {
|
|
6093
|
+
this.connectionHandler = handler;
|
|
6094
|
+
}
|
|
6095
|
+
onDisconnection(handler) {
|
|
6096
|
+
this.disconnectionHandler = handler;
|
|
6097
|
+
}
|
|
6098
|
+
onMessage(handler) {
|
|
6099
|
+
this.messageHandler = handler;
|
|
6100
|
+
}
|
|
6101
|
+
async listen(port) {
|
|
6102
|
+
try {
|
|
6103
|
+
this.uws = await import("uWebSockets.js");
|
|
6104
|
+
} catch (_error) {
|
|
6105
|
+
throw new Error("uWebSockets.js is not installed. Install it with: npm install uWebSockets.js@uNetworking/uWebSockets.js#v20.44.0");
|
|
6106
|
+
}
|
|
6107
|
+
if (!this.uws) {
|
|
6108
|
+
throw new Error("Failed to load uWebSockets.js module");
|
|
6109
|
+
}
|
|
6110
|
+
this.app = this.config.tls ? this.uws.App({
|
|
6111
|
+
cert_file_name: this.config.tls.cert,
|
|
6112
|
+
key_file_name: this.config.tls.key
|
|
6113
|
+
}) : this.uws.App();
|
|
6114
|
+
this.app.ws("/*", {
|
|
6115
|
+
compression: this.config.compression ?? this.uws.SHARED_COMPRESSOR,
|
|
6116
|
+
maxPayloadLength: this.config.maxPayloadLength ?? 16777216,
|
|
6117
|
+
idleTimeout: this.config.idleTimeout ?? 120,
|
|
6118
|
+
maxBackpressure: this.config.maxBackpressure ?? 1048576,
|
|
6119
|
+
open: (ws) => {
|
|
6120
|
+
const data = ws.getUserData();
|
|
6121
|
+
const id = randomUUID();
|
|
6122
|
+
Object.assign(data, {
|
|
6123
|
+
id,
|
|
6124
|
+
channels: new Set,
|
|
6125
|
+
remoteAddress: undefined
|
|
6126
|
+
});
|
|
6127
|
+
const socket = new UWebSocketsRippleSocket(ws);
|
|
6128
|
+
this.sockets.set(socket.id, socket);
|
|
6129
|
+
this.connectionHandler?.(socket);
|
|
6130
|
+
},
|
|
6131
|
+
message: (ws, message, isBinary) => {
|
|
6132
|
+
const data = ws.getUserData();
|
|
6133
|
+
const socket = this.sockets.get(data.id);
|
|
6134
|
+
if (!socket) {
|
|
6135
|
+
return;
|
|
6136
|
+
}
|
|
6137
|
+
const payload = isBinary ? new Uint8Array(message) : new TextDecoder().decode(message);
|
|
6138
|
+
this.messageHandler?.(socket, payload);
|
|
6139
|
+
},
|
|
6140
|
+
drain: (ws) => {
|
|
6141
|
+
if (this.config.development) {
|
|
6142
|
+
const buffered = ws.getBufferedAmount();
|
|
6143
|
+
if (buffered > 0) {
|
|
6144
|
+
console.log(`[uWS] Draining backpressure: ${buffered} bytes`);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
},
|
|
6148
|
+
close: (ws, code, message) => {
|
|
6149
|
+
const data = ws.getUserData();
|
|
6150
|
+
const socket = this.sockets.get(data.id);
|
|
6151
|
+
if (!socket) {
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
6154
|
+
const reason = new TextDecoder().decode(message);
|
|
6155
|
+
this.disconnectionHandler?.(socket, code, reason);
|
|
6156
|
+
this.sockets.delete(data.id);
|
|
6157
|
+
}
|
|
6158
|
+
});
|
|
6159
|
+
return new Promise((resolve, reject) => {
|
|
6160
|
+
this.app?.listen(port, (listenSocket) => {
|
|
6161
|
+
if (listenSocket) {
|
|
6162
|
+
this.listenSocket = listenSocket;
|
|
6163
|
+
resolve();
|
|
6164
|
+
} else {
|
|
6165
|
+
reject(new Error(`Failed to listen on port ${port}`));
|
|
6166
|
+
}
|
|
6167
|
+
});
|
|
6168
|
+
});
|
|
6169
|
+
}
|
|
6170
|
+
async close() {
|
|
6171
|
+
for (const socket of this.sockets.values()) {
|
|
6172
|
+
socket.close();
|
|
6173
|
+
}
|
|
6174
|
+
this.sockets.clear();
|
|
6175
|
+
if (this.listenSocket && this.uws) {
|
|
6176
|
+
this.listenSocket = undefined;
|
|
6177
|
+
}
|
|
6178
|
+
this.app = undefined;
|
|
6179
|
+
}
|
|
6180
|
+
broadcast(topic, data, excludeSocketId) {
|
|
6181
|
+
if (!this.app) {
|
|
6182
|
+
throw new Error("Engine not started. Call listen() first.");
|
|
6183
|
+
}
|
|
6184
|
+
if (excludeSocketId) {
|
|
6185
|
+
for (const socket of this.sockets.values()) {
|
|
6186
|
+
if (socket.id !== excludeSocketId) {
|
|
6187
|
+
socket.send(data);
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
} else {
|
|
6191
|
+
if (typeof data === "string") {
|
|
6192
|
+
this.app.publish(topic, data, false);
|
|
6193
|
+
} else {
|
|
6194
|
+
const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
6195
|
+
this.app.publish(topic, buffer, true);
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
getConnectedSockets() {
|
|
6200
|
+
return Array.from(this.sockets.values());
|
|
6201
|
+
}
|
|
6202
|
+
getSocket(id) {
|
|
6203
|
+
return this.sockets.get(id);
|
|
6204
|
+
}
|
|
6205
|
+
upgrade(_req, _data) {
|
|
6206
|
+
throw new Error("upgrade() is not supported in uWebSocketsEngine. Use the ws() route instead.");
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
|
|
6210
|
+
// src/engines/WsEngine.ts
|
|
6211
|
+
import { WebSocketServer } from "ws";
|
|
6212
|
+
|
|
6213
|
+
class WsRippleSocket {
|
|
6214
|
+
ws;
|
|
6215
|
+
clientData;
|
|
6216
|
+
engine;
|
|
6217
|
+
constructor(ws, clientData, engine) {
|
|
6218
|
+
this.ws = ws;
|
|
6219
|
+
this.clientData = clientData;
|
|
6220
|
+
this.engine = engine;
|
|
6221
|
+
}
|
|
6222
|
+
get id() {
|
|
6223
|
+
return this.clientData.id;
|
|
6224
|
+
}
|
|
6225
|
+
get data() {
|
|
6226
|
+
return this.clientData;
|
|
6227
|
+
}
|
|
6228
|
+
send(data, compress) {
|
|
6229
|
+
this.ws.send(data, { compress });
|
|
6230
|
+
}
|
|
6231
|
+
close(code, reason) {
|
|
6232
|
+
this.ws.close(code, reason);
|
|
6233
|
+
}
|
|
6234
|
+
getBufferedAmount() {
|
|
6235
|
+
return this.ws.bufferedAmount;
|
|
6236
|
+
}
|
|
6237
|
+
subscribe(topic) {
|
|
6238
|
+
this.engine.subscribe(this.id, topic);
|
|
6239
|
+
}
|
|
6240
|
+
unsubscribe(topic) {
|
|
6241
|
+
this.engine.unsubscribe(this.id, topic);
|
|
6242
|
+
}
|
|
6243
|
+
publish(topic, data) {
|
|
6244
|
+
this.engine.broadcast(topic, data);
|
|
6245
|
+
}
|
|
6246
|
+
get raw() {
|
|
6247
|
+
return this.ws;
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
|
|
6251
|
+
class WsEngine {
|
|
6252
|
+
config;
|
|
6253
|
+
name = "node-ws";
|
|
6254
|
+
wss;
|
|
6255
|
+
connectionHandler;
|
|
6256
|
+
disconnectionHandler;
|
|
6257
|
+
messageHandler;
|
|
6258
|
+
sockets = new Map;
|
|
6259
|
+
subscriptions = new Map;
|
|
6260
|
+
constructor(config = {}) {
|
|
6261
|
+
this.config = config;
|
|
6262
|
+
}
|
|
6263
|
+
onConnection(handler) {
|
|
6264
|
+
this.connectionHandler = handler;
|
|
6265
|
+
}
|
|
6266
|
+
onDisconnection(handler) {
|
|
6267
|
+
this.disconnectionHandler = handler;
|
|
6268
|
+
}
|
|
6269
|
+
onMessage(handler) {
|
|
6270
|
+
this.messageHandler = handler;
|
|
6271
|
+
}
|
|
6272
|
+
async listen(port) {
|
|
6273
|
+
this.wss = new WebSocketServer({
|
|
6274
|
+
port,
|
|
6275
|
+
host: this.config.hostname,
|
|
6276
|
+
path: this.config.path
|
|
6277
|
+
});
|
|
6278
|
+
this.wss.on("connection", (ws, req) => {
|
|
6279
|
+
const clientData = {
|
|
6280
|
+
id: crypto.randomUUID(),
|
|
6281
|
+
remoteAddress: req.socket.remoteAddress,
|
|
6282
|
+
channels: new Set
|
|
6283
|
+
};
|
|
6284
|
+
const socket = new WsRippleSocket(ws, clientData, this);
|
|
6285
|
+
this.sockets.set(socket.id, socket);
|
|
6286
|
+
ws.on("message", (data, isBinary) => {
|
|
6287
|
+
let payload;
|
|
6288
|
+
if (isBinary) {
|
|
6289
|
+
payload = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
6290
|
+
} else {
|
|
6291
|
+
payload = data.toString();
|
|
6292
|
+
}
|
|
6293
|
+
this.messageHandler?.(socket, payload);
|
|
6294
|
+
});
|
|
6295
|
+
ws.on("close", (code, reason) => {
|
|
6296
|
+
this.cleanupSocket(socket.id);
|
|
6297
|
+
this.sockets.delete(socket.id);
|
|
6298
|
+
this.disconnectionHandler?.(socket, code, reason.toString());
|
|
6299
|
+
});
|
|
6300
|
+
this.connectionHandler?.(socket);
|
|
6301
|
+
});
|
|
6302
|
+
return new Promise((resolve) => {
|
|
6303
|
+
this.wss?.once("listening", () => resolve());
|
|
6304
|
+
});
|
|
6305
|
+
}
|
|
6306
|
+
async close() {
|
|
6307
|
+
return new Promise((resolve, reject) => {
|
|
6308
|
+
if (!this.wss) {
|
|
6309
|
+
return resolve();
|
|
6310
|
+
}
|
|
6311
|
+
this.wss.close((err) => {
|
|
6312
|
+
if (err) {
|
|
6313
|
+
return reject(err);
|
|
6314
|
+
}
|
|
6315
|
+
this.sockets.clear();
|
|
6316
|
+
this.subscriptions.clear();
|
|
6317
|
+
resolve();
|
|
6318
|
+
});
|
|
6319
|
+
});
|
|
6320
|
+
}
|
|
6321
|
+
subscribe(socketId, topic) {
|
|
6322
|
+
if (!this.subscriptions.has(topic)) {
|
|
6323
|
+
this.subscriptions.set(topic, new Set);
|
|
6324
|
+
}
|
|
6325
|
+
this.subscriptions.get(topic)?.add(socketId);
|
|
6326
|
+
}
|
|
6327
|
+
unsubscribe(socketId, topic) {
|
|
6328
|
+
this.subscriptions.get(topic)?.delete(socketId);
|
|
6329
|
+
}
|
|
6330
|
+
cleanupSocket(socketId) {
|
|
6331
|
+
for (const subscribers of this.subscriptions.values()) {
|
|
6332
|
+
subscribers.delete(socketId);
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
broadcast(topic, data, excludeSocketId) {
|
|
6336
|
+
const subscribers = this.subscriptions.get(topic);
|
|
6337
|
+
if (!subscribers) {
|
|
6338
|
+
return;
|
|
6339
|
+
}
|
|
6340
|
+
for (const socketId of subscribers) {
|
|
6341
|
+
if (socketId === excludeSocketId) {
|
|
6342
|
+
continue;
|
|
6343
|
+
}
|
|
6344
|
+
const socket = this.sockets.get(socketId);
|
|
6345
|
+
socket?.send(data);
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
}
|
|
6349
|
+
|
|
6028
6350
|
// src/middleware/InterceptorManager.ts
|
|
6029
6351
|
class InterceptorManager {
|
|
6030
6352
|
interceptors;
|
|
@@ -6073,7 +6395,7 @@ class RippleMetrics {
|
|
|
6073
6395
|
export() {
|
|
6074
6396
|
const activeConnections = this.tracker.getActiveConnections();
|
|
6075
6397
|
const pendingAcks = this.ackManager?.getPendingCount() ?? 0;
|
|
6076
|
-
return [
|
|
6398
|
+
return `${[
|
|
6077
6399
|
`# HELP ${this.prefix}_connections_active Currently active WebSocket connections`,
|
|
6078
6400
|
`# TYPE ${this.prefix}_connections_active gauge`,
|
|
6079
6401
|
`${this.prefix}_connections_active ${activeConnections}`,
|
|
@@ -6084,7 +6406,7 @@ class RippleMetrics {
|
|
|
6084
6406
|
`# TYPE ${this.prefix}_slow_clients_total counter`,
|
|
6085
6407
|
`${this.prefix}_slow_clients_total ${this.slowClients}`
|
|
6086
6408
|
].join(`
|
|
6087
|
-
`)
|
|
6409
|
+
`)}
|
|
6088
6410
|
`;
|
|
6089
6411
|
}
|
|
6090
6412
|
}
|
|
@@ -6185,14 +6507,14 @@ var ClientMessageProto = null;
|
|
|
6185
6507
|
var ServerMessageProto = null;
|
|
6186
6508
|
|
|
6187
6509
|
class ProtobufSerializer {
|
|
6188
|
-
|
|
6510
|
+
options;
|
|
6189
6511
|
contentType = "protobuf";
|
|
6190
6512
|
broadcastCache = null;
|
|
6191
6513
|
initialized = false;
|
|
6192
|
-
constructor(
|
|
6193
|
-
this.
|
|
6194
|
-
if (!protoPath) {
|
|
6195
|
-
this.protoPath = this.resolveProtoPath();
|
|
6514
|
+
constructor(options = {}) {
|
|
6515
|
+
this.options = options;
|
|
6516
|
+
if (!options.protoPath) {
|
|
6517
|
+
this.options.protoPath = this.resolveProtoPath();
|
|
6196
6518
|
}
|
|
6197
6519
|
}
|
|
6198
6520
|
resolveProtoPath() {
|
|
@@ -6214,11 +6536,12 @@ class ProtobufSerializer {
|
|
|
6214
6536
|
return join(__dirname2, "../proto/ripple.proto");
|
|
6215
6537
|
}
|
|
6216
6538
|
async init() {
|
|
6217
|
-
if (this.initialized)
|
|
6539
|
+
if (this.initialized) {
|
|
6218
6540
|
return;
|
|
6541
|
+
}
|
|
6219
6542
|
try {
|
|
6220
|
-
if (!existsSync(this.protoPath)) {
|
|
6221
|
-
throw new Error(`Proto file not found at: ${this.protoPath}
|
|
6543
|
+
if (!existsSync(this.options.protoPath)) {
|
|
6544
|
+
throw new Error(`Proto file not found at: ${this.options.protoPath}
|
|
6222
6545
|
` + `Working directory: ${process.cwd()}
|
|
6223
6546
|
` + `Please ensure ripple.proto exists or provide a custom protoPath.`);
|
|
6224
6547
|
}
|
|
@@ -6230,13 +6553,13 @@ class ProtobufSerializer {
|
|
|
6230
6553
|
Install it with: npm install protobufjs
|
|
6231
6554
|
Or use JSON serializer instead: new RippleServer({ serializer: 'json' })`);
|
|
6232
6555
|
}
|
|
6233
|
-
protoRoot = await protobuf.load(this.protoPath);
|
|
6556
|
+
protoRoot = await protobuf.load(this.options.protoPath);
|
|
6234
6557
|
ClientMessageProto = protoRoot.lookupType("ripple.ClientMessage");
|
|
6235
6558
|
ServerMessageProto = protoRoot.lookupType("ripple.ServerMessage");
|
|
6236
6559
|
this.initialized = true;
|
|
6237
6560
|
} catch (error) {
|
|
6238
6561
|
throw new Error(`Failed to initialize ProtobufSerializer: ${error.message}
|
|
6239
|
-
Proto path attempted: ${this.protoPath}
|
|
6562
|
+
Proto path attempted: ${this.options.protoPath}
|
|
6240
6563
|
Ensure 'protobufjs' is installed or switch to JSON serializer.`);
|
|
6241
6564
|
}
|
|
6242
6565
|
}
|
|
@@ -6244,8 +6567,9 @@ Ensure 'protobufjs' is installed or switch to JSON serializer.`);
|
|
|
6244
6567
|
this.checkInitialized();
|
|
6245
6568
|
const payload = this.toProtoPayload(message);
|
|
6246
6569
|
const errMsg = ServerMessageProto.verify(payload);
|
|
6247
|
-
if (errMsg)
|
|
6570
|
+
if (errMsg) {
|
|
6248
6571
|
throw Error(errMsg);
|
|
6572
|
+
}
|
|
6249
6573
|
const msg = ServerMessageProto.create(payload);
|
|
6250
6574
|
return ServerMessageProto.encode(msg).finish();
|
|
6251
6575
|
}
|
|
@@ -6256,7 +6580,7 @@ Ensure 'protobufjs' is installed or switch to JSON serializer.`);
|
|
|
6256
6580
|
const obj = ClientMessageProto.toObject(decoded, {
|
|
6257
6581
|
enums: String,
|
|
6258
6582
|
longs: String,
|
|
6259
|
-
bytes: String,
|
|
6583
|
+
bytes: this.options.pure ? undefined : String,
|
|
6260
6584
|
defaults: true,
|
|
6261
6585
|
oneofs: true
|
|
6262
6586
|
});
|
|
@@ -6341,8 +6665,9 @@ Ensure 'protobufjs' is installed or switch to JSON serializer.`);
|
|
|
6341
6665
|
} : undefined
|
|
6342
6666
|
};
|
|
6343
6667
|
}
|
|
6344
|
-
if (obj.unsubscribe)
|
|
6668
|
+
if (obj.unsubscribe) {
|
|
6345
6669
|
return { type: "unsubscribe", channel: obj.unsubscribe.channel };
|
|
6670
|
+
}
|
|
6346
6671
|
if (obj.whisper) {
|
|
6347
6672
|
return {
|
|
6348
6673
|
type: "whisper",
|
|
@@ -6351,10 +6676,12 @@ Ensure 'protobufjs' is installed or switch to JSON serializer.`);
|
|
|
6351
6676
|
data: this.decodeData(obj.whisper.data)
|
|
6352
6677
|
};
|
|
6353
6678
|
}
|
|
6354
|
-
if (obj.ping)
|
|
6679
|
+
if (obj.ping) {
|
|
6355
6680
|
return { type: "ping" };
|
|
6356
|
-
|
|
6681
|
+
}
|
|
6682
|
+
if (obj.ack) {
|
|
6357
6683
|
return { type: "ack", seq: obj.ack.seq };
|
|
6684
|
+
}
|
|
6358
6685
|
if (obj.binary) {
|
|
6359
6686
|
return {
|
|
6360
6687
|
type: "binary",
|
|
@@ -6366,13 +6693,30 @@ Ensure 'protobufjs' is installed or switch to JSON serializer.`);
|
|
|
6366
6693
|
throw new Error("Unknown client message payload");
|
|
6367
6694
|
}
|
|
6368
6695
|
encodeData(data) {
|
|
6369
|
-
if (
|
|
6696
|
+
if (this.options.pure) {
|
|
6697
|
+
if (data instanceof Uint8Array) {
|
|
6698
|
+
return data;
|
|
6699
|
+
}
|
|
6700
|
+
if (Object.prototype.toString.call(data) === "[object Uint8Array]") {
|
|
6701
|
+
return data;
|
|
6702
|
+
}
|
|
6703
|
+
if (Buffer.isBuffer(data)) {
|
|
6704
|
+
return data;
|
|
6705
|
+
}
|
|
6706
|
+
throw new Error("In pure mode, data must be Uint8Array or Buffer");
|
|
6707
|
+
}
|
|
6708
|
+
if (typeof data === "string") {
|
|
6370
6709
|
return new TextEncoder().encode(data);
|
|
6371
|
-
|
|
6710
|
+
}
|
|
6711
|
+
if (data instanceof Uint8Array) {
|
|
6372
6712
|
return data;
|
|
6713
|
+
}
|
|
6373
6714
|
return new TextEncoder().encode(JSON.stringify(data));
|
|
6374
6715
|
}
|
|
6375
6716
|
decodeData(data) {
|
|
6717
|
+
if (this.options.pure) {
|
|
6718
|
+
return data;
|
|
6719
|
+
}
|
|
6376
6720
|
if (typeof data === "string") {
|
|
6377
6721
|
try {
|
|
6378
6722
|
return JSON.parse(atob(data));
|
|
@@ -6605,7 +6949,7 @@ class RippleServer {
|
|
|
6605
6949
|
this.authorizer = config.authorizer;
|
|
6606
6950
|
this.tracker = config.connectionTracker ?? new DefaultConnectionTracker(this.logger);
|
|
6607
6951
|
this.healthChecker = new HealthChecker(this, this.driver);
|
|
6608
|
-
this.serializer = config.serializer === "protobuf" ? new ProtobufSerializer : new JsonSerializer;
|
|
6952
|
+
this.serializer = config.serializer === "protobuf" ? new ProtobufSerializer(config.serializerOptions) : new JsonSerializer;
|
|
6609
6953
|
if (config.reconnection?.enabled) {
|
|
6610
6954
|
this.sessionManager = new SessionManager({
|
|
6611
6955
|
sessionTTL: config.reconnection.sessionTTL ?? 60000,
|
|
@@ -6630,6 +6974,19 @@ class RippleServer {
|
|
|
6630
6974
|
hostname: config.hostname,
|
|
6631
6975
|
development: true
|
|
6632
6976
|
});
|
|
6977
|
+
case "node-uws":
|
|
6978
|
+
return new UWebSocketsEngine({
|
|
6979
|
+
port: config.port,
|
|
6980
|
+
hostname: config.hostname,
|
|
6981
|
+
development: true
|
|
6982
|
+
});
|
|
6983
|
+
case "node-ws":
|
|
6984
|
+
return new WsEngine({
|
|
6985
|
+
port: config.port,
|
|
6986
|
+
hostname: config.hostname,
|
|
6987
|
+
path: config.path,
|
|
6988
|
+
development: true
|
|
6989
|
+
});
|
|
6633
6990
|
default:
|
|
6634
6991
|
throw new Error(`Unsupported runtime: ${runtime}`);
|
|
6635
6992
|
}
|
|
@@ -6787,12 +7144,14 @@ class RippleServer {
|
|
|
6787
7144
|
}
|
|
6788
7145
|
async handleBinaryMessage(ws, message) {
|
|
6789
7146
|
const buffer = message;
|
|
6790
|
-
if (buffer.length < 4)
|
|
7147
|
+
if (buffer.length < 4) {
|
|
6791
7148
|
return;
|
|
7149
|
+
}
|
|
6792
7150
|
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
6793
7151
|
const headerLength = view.getInt32(0, true);
|
|
6794
|
-
if (buffer.length < 4 + headerLength)
|
|
7152
|
+
if (buffer.length < 4 + headerLength) {
|
|
6795
7153
|
return;
|
|
7154
|
+
}
|
|
6796
7155
|
try {
|
|
6797
7156
|
const headerBytes = buffer.subarray(4, 4 + headerLength);
|
|
6798
7157
|
const headerRaw = new TextDecoder().decode(headerBytes);
|
|
@@ -6818,8 +7177,9 @@ class RippleServer {
|
|
|
6818
7177
|
}
|
|
6819
7178
|
broadcastBinaryToChannel(channel, event, data, excludeClientId) {
|
|
6820
7179
|
const subscribers = this.channels.getSubscribers(channel);
|
|
6821
|
-
if (subscribers.length === 0)
|
|
7180
|
+
if (subscribers.length === 0) {
|
|
6822
7181
|
return;
|
|
7182
|
+
}
|
|
6823
7183
|
const header = JSON.stringify({ type: "binary", channel, event });
|
|
6824
7184
|
const headerBuffer = Buffer.from(header);
|
|
6825
7185
|
const totalBuffer = Buffer.allocUnsafe(4 + headerBuffer.length + data.byteLength);
|
|
@@ -6827,8 +7187,9 @@ class RippleServer {
|
|
|
6827
7187
|
headerBuffer.copy(totalBuffer, 4);
|
|
6828
7188
|
Buffer.from(data).copy(totalBuffer, 4 + headerBuffer.length);
|
|
6829
7189
|
for (const ws of subscribers) {
|
|
6830
|
-
if (excludeClientId && ws.data.id === excludeClientId)
|
|
7190
|
+
if (excludeClientId && ws.data.id === excludeClientId) {
|
|
6831
7191
|
continue;
|
|
7192
|
+
}
|
|
6832
7193
|
ws.send(totalBuffer);
|
|
6833
7194
|
}
|
|
6834
7195
|
}
|
|
@@ -6871,7 +7232,6 @@ class RippleServer {
|
|
|
6871
7232
|
}
|
|
6872
7233
|
this.ackManager.clearClient(ws.data.id);
|
|
6873
7234
|
}
|
|
6874
|
-
handleDrain(_ws) {}
|
|
6875
7235
|
async handleSubscribe(ws, channel, _auth) {
|
|
6876
7236
|
if (requiresAuth(channel)) {
|
|
6877
7237
|
if (!this.authorizer) {
|
|
@@ -6956,8 +7316,9 @@ class RippleServer {
|
|
|
6956
7316
|
}
|
|
6957
7317
|
broadcastToChannel(channel, event, data, excludeClientId, options) {
|
|
6958
7318
|
const subscribers = this.channels.getSubscribers(channel);
|
|
6959
|
-
if (subscribers.length === 0)
|
|
7319
|
+
if (subscribers.length === 0) {
|
|
6960
7320
|
return;
|
|
7321
|
+
}
|
|
6961
7322
|
const message = event === "presence" ? {
|
|
6962
7323
|
type: "presence",
|
|
6963
7324
|
channel,
|
|
@@ -6966,8 +7327,9 @@ class RippleServer {
|
|
|
6966
7327
|
} : { type: "event", channel, event, data };
|
|
6967
7328
|
const serialized = this.serializer.serializeForBroadcast(message);
|
|
6968
7329
|
for (const ws of subscribers) {
|
|
6969
|
-
if (excludeClientId && ws.data.id === excludeClientId)
|
|
7330
|
+
if (excludeClientId && ws.data.id === excludeClientId) {
|
|
6970
7331
|
continue;
|
|
7332
|
+
}
|
|
6971
7333
|
this.interceptors.execute({ ws, message, direction: "outgoing", channel, event }, async () => {
|
|
6972
7334
|
if (options?.needAck) {
|
|
6973
7335
|
const { seq, promise } = this.ackManager.register(ws.data.id, options.timeout);
|
|
@@ -7071,8 +7433,9 @@ class RippleServer {
|
|
|
7071
7433
|
}
|
|
7072
7434
|
async shutdown() {
|
|
7073
7435
|
this.logger.info("Shutting down RippleServer");
|
|
7074
|
-
if (this.pingInterval)
|
|
7436
|
+
if (this.pingInterval) {
|
|
7075
7437
|
clearInterval(this.pingInterval);
|
|
7438
|
+
}
|
|
7076
7439
|
await this.driver.shutdown?.();
|
|
7077
7440
|
}
|
|
7078
7441
|
}
|
|
@@ -7166,4 +7529,4 @@ export {
|
|
|
7166
7529
|
AckManager
|
|
7167
7530
|
};
|
|
7168
7531
|
|
|
7169
|
-
//# debugId=
|
|
7532
|
+
//# debugId=5BEEEDA722E6EBC364756E2164756E21
|