@rivalis/fleet 8.0.0

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/lib/wire.js ADDED
@@ -0,0 +1,507 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/wire/index.ts
21
+ var wire_exports = {};
22
+ __export(wire_exports, {
23
+ ALLOWED_ENDPOINT_SCHEMES: () => ALLOWED_ENDPOINT_SCHEMES,
24
+ MAX_ENDPOINT_URL_LENGTH: () => MAX_ENDPOINT_URL_LENGTH,
25
+ MAX_INFLIGHT_COMMANDS: () => MAX_INFLIGHT_COMMANDS,
26
+ MAX_LABELS: () => MAX_LABELS,
27
+ MAX_LABEL_KEY_LENGTH: () => MAX_LABEL_KEY_LENGTH,
28
+ MAX_LABEL_VALUE_LENGTH: () => MAX_LABEL_VALUE_LENGTH,
29
+ MAX_NAME_LENGTH: () => MAX_NAME_LENGTH,
30
+ MAX_ROOMS: () => MAX_ROOMS,
31
+ MAX_ROOM_CONNECTIONS: () => MAX_ROOM_CONNECTIONS,
32
+ MAX_ROOM_ID_LENGTH: () => MAX_ROOM_ID_LENGTH,
33
+ MAX_ROOM_TYPES: () => MAX_ROOM_TYPES,
34
+ MAX_ROOM_TYPE_LENGTH: () => MAX_ROOM_TYPE_LENGTH,
35
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
36
+ Topics: () => Topics,
37
+ WS_SUBPROTOCOL: () => WS_SUBPROTOCOL,
38
+ WireVersionError: () => WireVersionError,
39
+ decodeFrame: () => decodeFrame,
40
+ encodeFrame: () => encodeFrame,
41
+ syncPayloadSchema: () => syncPayloadSchema,
42
+ validateSnapshot: () => validateSnapshot
43
+ });
44
+ module.exports = __toCommonJS(wire_exports);
45
+
46
+ // src/wire/topics.ts
47
+ var PROTOCOL_VERSION = 3;
48
+ var WS_SUBPROTOCOL = "rivalis-fleet.v1";
49
+ var MAX_INFLIGHT_COMMANDS = 32;
50
+ var Topics = {
51
+ /** orch → agent: assigns id + heartbeat (poll cadence) on join; followed by the first poll. */
52
+ hello: "fleet/hello",
53
+ /** orch → agent: state poll. Carries `knownHash` (dedup) + the last recorded `status` (echo). */
54
+ poll: "fleet/poll",
55
+ /** agent → orch: poll reply. Full snapshot when the hash differs from `knownHash`, hash-only otherwise. */
56
+ state: "fleet/state",
57
+ /** orch → agent: command push. */
58
+ cmd: "fleet/cmd",
59
+ /** agent → orch: command result. */
60
+ ack: "fleet/ack"
61
+ };
62
+
63
+ // src/wire/snapshotSchema.ts
64
+ var MAX_ENDPOINT_URL_LENGTH = 512;
65
+ var MAX_NAME_LENGTH = 64;
66
+ var MAX_LABELS = 32;
67
+ var MAX_LABEL_KEY_LENGTH = 64;
68
+ var MAX_LABEL_VALUE_LENGTH = 64;
69
+ var MAX_ROOM_TYPES = 256;
70
+ var MAX_ROOMS = 5e4;
71
+ var MAX_ROOM_ID_LENGTH = 256;
72
+ var MAX_ROOM_TYPE_LENGTH = 64;
73
+ var MAX_ROOM_CONNECTIONS = 1e6;
74
+ var ALLOWED_ENDPOINT_SCHEMES = /* @__PURE__ */ new Set(["ws:", "wss:", "http:", "https:"]);
75
+ var syncPayloadSchema = {
76
+ endpointUrl: { type: "string", required: true, max: MAX_ENDPOINT_URL_LENGTH },
77
+ name: { type: "string", required: true, max: MAX_NAME_LENGTH },
78
+ labels: { type: "object", required: true },
79
+ roomTypes: { type: "array", required: true, max: MAX_ROOM_TYPES, items: { type: "string", max: MAX_ROOM_TYPE_LENGTH } },
80
+ rooms: { type: "array", required: true, max: MAX_ROOMS, items: { type: "object" } }
81
+ };
82
+ function checkRule(field, value, rule) {
83
+ if (value === void 0 || value === null) {
84
+ return rule.required === true ? `${field} is required` : null;
85
+ }
86
+ switch (rule.type) {
87
+ case "string":
88
+ if (typeof value !== "string") {
89
+ return `${field} must be a string`;
90
+ }
91
+ if (rule.max !== void 0 && value.length > rule.max) {
92
+ return `${field} exceeds ${rule.max} characters`;
93
+ }
94
+ if (rule.min !== void 0 && value.length < rule.min) {
95
+ return `${field} must be at least ${rule.min} characters`;
96
+ }
97
+ if (rule.pattern !== void 0 && !new RegExp(rule.pattern).test(value)) {
98
+ return `${field} has an invalid format`;
99
+ }
100
+ return null;
101
+ case "number":
102
+ case "integer":
103
+ if (typeof value !== "number" || rule.type === "integer" && !Number.isInteger(value)) {
104
+ return `${field} must be a number`;
105
+ }
106
+ if (rule.max !== void 0 && value > rule.max) {
107
+ return `${field} exceeds ${rule.max}`;
108
+ }
109
+ if (rule.min !== void 0 && value < rule.min) {
110
+ return `${field} is below ${rule.min}`;
111
+ }
112
+ return null;
113
+ case "boolean":
114
+ return typeof value === "boolean" ? null : `${field} must be a boolean`;
115
+ case "object":
116
+ return typeof value === "object" && !Array.isArray(value) ? null : `${field} must be an object`;
117
+ case "array":
118
+ if (!Array.isArray(value)) {
119
+ return `${field} must be an array`;
120
+ }
121
+ if (rule.max !== void 0 && value.length > rule.max) {
122
+ return `${field} exceeds ${rule.max} entries`;
123
+ }
124
+ if (rule.min !== void 0 && value.length < rule.min) {
125
+ return `${field} must have at least ${rule.min} entries`;
126
+ }
127
+ if (rule.items !== void 0) {
128
+ for (const entry of value) {
129
+ const reason = checkRule(`${field} entry`, entry, rule.items);
130
+ if (reason !== null) {
131
+ return reason;
132
+ }
133
+ }
134
+ }
135
+ return null;
136
+ default:
137
+ return null;
138
+ }
139
+ }
140
+ function checkSchema(schema, data) {
141
+ for (const key of Object.keys(schema)) {
142
+ const rule = schema[key];
143
+ if (rule === void 0) {
144
+ continue;
145
+ }
146
+ const reason = checkRule(key, data[key], rule);
147
+ if (reason !== null) {
148
+ return reason;
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ function validateSnapshot(payload) {
154
+ const data = payload;
155
+ const reason = checkSchema(syncPayloadSchema, data);
156
+ if (reason !== null) {
157
+ return reason;
158
+ }
159
+ let parsed;
160
+ try {
161
+ parsed = new URL(payload.endpointUrl);
162
+ } catch {
163
+ return "endpointUrl is not a valid URL";
164
+ }
165
+ if (!ALLOWED_ENDPOINT_SCHEMES.has(parsed.protocol)) {
166
+ return "endpointUrl scheme is not allowed";
167
+ }
168
+ const labels = payload.labels;
169
+ const labelKeys = Object.keys(labels);
170
+ if (labelKeys.length > MAX_LABELS) {
171
+ return `labels exceeds ${MAX_LABELS} entries`;
172
+ }
173
+ for (const key of labelKeys) {
174
+ if (key.length > MAX_LABEL_KEY_LENGTH) {
175
+ return `a label key exceeds ${MAX_LABEL_KEY_LENGTH} characters`;
176
+ }
177
+ const value = labels[key];
178
+ if (typeof value !== "string" || value.length > MAX_LABEL_VALUE_LENGTH) {
179
+ return `a label value is not a string of at most ${MAX_LABEL_VALUE_LENGTH} characters`;
180
+ }
181
+ }
182
+ const rooms = payload.rooms;
183
+ for (const entry of rooms) {
184
+ const id = entry.id;
185
+ if (typeof id !== "string" || id.length > MAX_ROOM_ID_LENGTH) {
186
+ return `a room id is not a string of at most ${MAX_ROOM_ID_LENGTH} characters`;
187
+ }
188
+ const type = entry.type;
189
+ if (typeof type !== "string" || type.length > MAX_ROOM_TYPE_LENGTH) {
190
+ return `a room type is not a string of at most ${MAX_ROOM_TYPE_LENGTH} characters`;
191
+ }
192
+ const connections = entry.connections;
193
+ if (typeof connections !== "number" || connections > MAX_ROOM_CONNECTIONS) {
194
+ return `a room connections value exceeds ${MAX_ROOM_CONNECTIONS}`;
195
+ }
196
+ }
197
+ return null;
198
+ }
199
+
200
+ // src/wire/serializer.ts
201
+ var import_node_module = require("module");
202
+ var import_meta = {};
203
+ var WIRE_MAJOR = PROTOCOL_VERSION;
204
+ var WIRE_MINOR = 0;
205
+ var HEADER_BYTES = 2;
206
+ var WireVersionError = class extends Error {
207
+ /** The major byte read off the incompatible frame (123 for a legacy JSON `{...}` frame). */
208
+ theirVersion;
209
+ /** This build's protocol major. */
210
+ ourVersion;
211
+ constructor(theirVersion) {
212
+ super(
213
+ `fleet wire protocol version mismatch: peer speaks major v${theirVersion}, this build speaks v${PROTOCOL_VERSION} \u2014 agents and orchestrator must run the same @rivalis/fleet major (\xA77). A v1 (JSON) peer against a v${PROTOCOL_VERSION} peer is exactly this case; upgrade both halves in lockstep.`
214
+ );
215
+ this.name = "WireVersionError";
216
+ this.theirVersion = theirVersion;
217
+ this.ourVersion = PROTOCOL_VERSION;
218
+ }
219
+ };
220
+ var Type = {
221
+ Label: "Label",
222
+ SyncRoom: "SyncRoom",
223
+ Capacity: "Capacity",
224
+ AckRoom: "AckRoom",
225
+ Hello: "Hello",
226
+ Poll: "Poll",
227
+ State: "State",
228
+ Cmd: "Cmd",
229
+ Ack: "Ack"
230
+ };
231
+ var TOPIC_TYPE = {
232
+ [Topics.hello]: Type.Hello,
233
+ [Topics.poll]: Type.Poll,
234
+ [Topics.state]: Type.State,
235
+ [Topics.cmd]: Type.Cmd,
236
+ [Topics.ack]: Type.Ack
237
+ };
238
+ var serializer = null;
239
+ function getSerializer() {
240
+ if (serializer !== null) {
241
+ return serializer;
242
+ }
243
+ const metaUrl = import_meta.url;
244
+ const req = metaUrl ? (0, import_node_module.createRequire)(metaUrl) : require;
245
+ const mod = req("@toolcase/serializer");
246
+ const Serializer = mod.Serializer ?? mod.default;
247
+ const F = Serializer.FieldType;
248
+ const s = new Serializer("fleet");
249
+ s.define(Type.Label, [
250
+ { key: "key", type: F.STRING, rule: "optional" },
251
+ { key: "value", type: F.STRING, rule: "optional" }
252
+ ]);
253
+ s.define(Type.SyncRoom, [
254
+ { key: "id", type: F.STRING, rule: "optional" },
255
+ { key: "type", type: F.STRING, rule: "optional" },
256
+ { key: "connections", type: F.UINT32, rule: "optional" },
257
+ { key: "origin", type: F.STRING, rule: "optional" }
258
+ ]);
259
+ s.define(Type.Capacity, [
260
+ // null = unlimited (§6). Absent on the wire ⇒ null; an explicit 0 ⇒ 0.
261
+ { key: "maxConnections", type: F.INT32, rule: "optional", default: null },
262
+ { key: "maxRooms", type: F.INT32, rule: "optional", default: null }
263
+ ]);
264
+ s.define(Type.AckRoom, [
265
+ { key: "id", type: F.STRING, rule: "optional" },
266
+ { key: "type", type: F.STRING, rule: "optional" }
267
+ ]);
268
+ s.define(Type.Hello, [
269
+ { key: "instanceId", type: F.STRING, rule: "optional" },
270
+ { key: "protocolVersion", type: F.UINT32, rule: "optional" },
271
+ { key: "heartbeatMs", type: F.UINT32, rule: "optional" }
272
+ ]);
273
+ s.define(Type.Poll, [
274
+ { key: "reqId", type: F.STRING, rule: "optional" },
275
+ // Absent ⇒ null (no prior state / forced full, subsumes the old fleet/resync).
276
+ { key: "knownHash", type: F.STRING, rule: "optional" },
277
+ { key: "status", type: F.STRING, rule: "optional" }
278
+ ]);
279
+ s.define(Type.State, [
280
+ { key: "reqId", type: F.STRING, rule: "optional" },
281
+ // full=false is a hash-only liveness reply: the snapshot fields below are
282
+ // omitted on the wire (preserving the old sync/ping dedup, orch-initiated).
283
+ { key: "full", type: F.BOOL, rule: "optional" },
284
+ { key: "seq", type: F.UINT32, rule: "optional" },
285
+ { key: "hash", type: F.STRING, rule: "optional" },
286
+ { key: "name", type: F.STRING, rule: "optional" },
287
+ { key: "processUid", type: F.STRING, rule: "optional" },
288
+ { key: "agentVersion", type: F.STRING, rule: "optional" },
289
+ { key: "protocolVersion", type: F.UINT32, rule: "optional" },
290
+ { key: "endpointUrl", type: F.STRING, rule: "optional" },
291
+ { key: "labels", type: Type.Label, rule: "repeated" },
292
+ { key: "capacity", type: Type.Capacity, rule: "optional" },
293
+ { key: "autoCreate", type: F.BOOL, rule: "optional" },
294
+ { key: "roomTypes", type: F.STRING, rule: "repeated" },
295
+ { key: "rooms", type: Type.SyncRoom, rule: "repeated" },
296
+ { key: "status", type: F.STRING, rule: "optional" }
297
+ ]);
298
+ s.define(Type.Cmd, [
299
+ { key: "cmdId", type: F.STRING, rule: "optional" },
300
+ { key: "op", type: F.STRING, rule: "optional" },
301
+ { key: "roomId", type: F.STRING, rule: "optional" },
302
+ { key: "roomType", type: F.STRING, rule: "optional" }
303
+ ]);
304
+ s.define(Type.Ack, [
305
+ { key: "cmdId", type: F.STRING, rule: "optional" },
306
+ { key: "ok", type: F.BOOL, rule: "optional" },
307
+ { key: "error", type: F.STRING, rule: "optional" },
308
+ { key: "alreadyGone", type: F.BOOL, rule: "optional" },
309
+ { key: "room", type: Type.AckRoom, rule: "optional" },
310
+ // APPEND-ONLY (task 003): the room-already-exists signal must stay LAST so
311
+ // existing tags are unmoved (see the append-only tag rule in the file header).
312
+ { key: "exists", type: F.BOOL, rule: "optional" }
313
+ ]);
314
+ serializer = s;
315
+ return s;
316
+ }
317
+ function present(obj, key) {
318
+ return obj !== null && obj !== void 0 && Object.prototype.hasOwnProperty.call(obj, key);
319
+ }
320
+ function labelsToList(labels) {
321
+ return Object.entries(labels ?? {}).map(([key, value]) => ({ key, value }));
322
+ }
323
+ function labelsFromList(list) {
324
+ const labels = {};
325
+ for (const entry of list ?? []) {
326
+ labels[entry.key ?? ""] = entry.value ?? "";
327
+ }
328
+ return labels;
329
+ }
330
+ function capacityToMessage(capacity) {
331
+ return {
332
+ maxConnections: capacity?.maxConnections ?? null,
333
+ maxRooms: capacity?.maxRooms ?? null
334
+ };
335
+ }
336
+ function capacityFromMessage(capacity) {
337
+ return {
338
+ // Absent ⇒ null (unlimited, §6); an explicit 0 is preserved as 0.
339
+ maxConnections: present(capacity, "maxConnections") ? capacity.maxConnections : null,
340
+ maxRooms: present(capacity, "maxRooms") ? capacity.maxRooms : null
341
+ };
342
+ }
343
+ function stateToMessage(p) {
344
+ if (!p.full) {
345
+ return { reqId: p.reqId, full: false, seq: p.seq, hash: p.hash };
346
+ }
347
+ return {
348
+ reqId: p.reqId,
349
+ full: true,
350
+ seq: p.seq,
351
+ hash: p.hash,
352
+ name: p.name,
353
+ processUid: p.processUid,
354
+ agentVersion: p.agentVersion,
355
+ protocolVersion: p.protocolVersion,
356
+ endpointUrl: p.endpointUrl,
357
+ labels: labelsToList(p.labels),
358
+ capacity: capacityToMessage(p.capacity),
359
+ autoCreate: p.autoCreate,
360
+ roomTypes: p.roomTypes ?? [],
361
+ rooms: (p.rooms ?? []).map((r) => ({
362
+ id: r.id,
363
+ type: r.type,
364
+ connections: r.connections,
365
+ origin: r.origin
366
+ })),
367
+ status: p.status
368
+ };
369
+ }
370
+ function stateFromMessage(m) {
371
+ return {
372
+ reqId: m.reqId ?? "",
373
+ full: m.full ?? false,
374
+ seq: m.seq ?? 0,
375
+ hash: m.hash ?? "",
376
+ name: m.name ?? "",
377
+ processUid: m.processUid ?? "",
378
+ agentVersion: m.agentVersion ?? "",
379
+ protocolVersion: m.protocolVersion ?? 0,
380
+ endpointUrl: m.endpointUrl ?? "",
381
+ labels: labelsFromList(m.labels),
382
+ capacity: capacityFromMessage(m.capacity),
383
+ autoCreate: m.autoCreate ?? false,
384
+ roomTypes: Array.isArray(m.roomTypes) ? m.roomTypes : [],
385
+ rooms: (Array.isArray(m.rooms) ? m.rooms : []).map((r) => ({
386
+ id: r.id ?? "",
387
+ type: r.type ?? "",
388
+ connections: r.connections ?? 0,
389
+ origin: r.origin ?? "local"
390
+ })),
391
+ status: m.status ?? "active"
392
+ };
393
+ }
394
+ function toMessage(topic, payload) {
395
+ switch (topic) {
396
+ case Topics.state:
397
+ return stateToMessage(payload);
398
+ case Topics.poll: {
399
+ const p = payload;
400
+ const msg = { reqId: p.reqId, status: p.status };
401
+ if (p.knownHash !== null && p.knownHash !== void 0) {
402
+ msg.knownHash = p.knownHash;
403
+ }
404
+ return msg;
405
+ }
406
+ default:
407
+ return payload;
408
+ }
409
+ }
410
+ function fromMessage(topic, m) {
411
+ switch (topic) {
412
+ case Topics.hello:
413
+ return {
414
+ instanceId: m.instanceId ?? "",
415
+ protocolVersion: m.protocolVersion ?? 0,
416
+ heartbeatMs: m.heartbeatMs ?? 0
417
+ };
418
+ case Topics.poll:
419
+ return {
420
+ reqId: m.reqId ?? "",
421
+ // Absent knownHash ⇒ null (no prior state / forced full).
422
+ knownHash: present(m, "knownHash") ? m.knownHash : null,
423
+ status: m.status ?? "active"
424
+ };
425
+ case Topics.state:
426
+ return stateFromMessage(m);
427
+ case Topics.cmd: {
428
+ const cmd = { cmdId: m.cmdId ?? "", op: m.op };
429
+ if (present(m, "roomId")) {
430
+ cmd.roomId = m.roomId;
431
+ }
432
+ if (present(m, "roomType")) {
433
+ cmd.roomType = m.roomType;
434
+ }
435
+ return cmd;
436
+ }
437
+ case Topics.ack: {
438
+ const ack = { cmdId: m.cmdId ?? "", ok: m.ok ?? false };
439
+ if (present(m, "error")) {
440
+ ack.error = m.error;
441
+ }
442
+ if (present(m, "alreadyGone")) {
443
+ ack.alreadyGone = m.alreadyGone;
444
+ }
445
+ if (present(m, "exists")) {
446
+ ack.exists = m.exists;
447
+ }
448
+ if (present(m, "room")) {
449
+ ack.room = { id: m.room.id ?? "", type: m.room.type ?? "" };
450
+ }
451
+ return ack;
452
+ }
453
+ default:
454
+ return m;
455
+ }
456
+ }
457
+ function encodeFrame(topic, payload) {
458
+ const type = TOPIC_TYPE[topic];
459
+ if (type === void 0) {
460
+ throw new Error(`fleet wire: no message type for topic=${topic}`);
461
+ }
462
+ const body = getSerializer().encode(type, toMessage(topic, payload));
463
+ const frame = new Uint8Array(HEADER_BYTES + body.length);
464
+ frame[0] = WIRE_MAJOR;
465
+ frame[1] = WIRE_MINOR;
466
+ frame.set(body, HEADER_BYTES);
467
+ return frame;
468
+ }
469
+ function decodeFrame(topic, frame) {
470
+ const type = TOPIC_TYPE[topic];
471
+ if (type === void 0) {
472
+ throw new Error(`fleet wire: no message type for topic=${topic}`);
473
+ }
474
+ if (frame === null || frame === void 0 || frame.length < HEADER_BYTES) {
475
+ throw new Error("fleet wire: truncated frame (shorter than the 2-byte version header)");
476
+ }
477
+ const major = frame[0];
478
+ if (major !== WIRE_MAJOR) {
479
+ throw new WireVersionError(major);
480
+ }
481
+ const body = frame.subarray(HEADER_BYTES);
482
+ const decoded = getSerializer().decode(type, body);
483
+ return fromMessage(topic, decoded);
484
+ }
485
+ // Annotate the CommonJS export names for ESM import in node:
486
+ 0 && (module.exports = {
487
+ ALLOWED_ENDPOINT_SCHEMES,
488
+ MAX_ENDPOINT_URL_LENGTH,
489
+ MAX_INFLIGHT_COMMANDS,
490
+ MAX_LABELS,
491
+ MAX_LABEL_KEY_LENGTH,
492
+ MAX_LABEL_VALUE_LENGTH,
493
+ MAX_NAME_LENGTH,
494
+ MAX_ROOMS,
495
+ MAX_ROOM_CONNECTIONS,
496
+ MAX_ROOM_ID_LENGTH,
497
+ MAX_ROOM_TYPES,
498
+ MAX_ROOM_TYPE_LENGTH,
499
+ PROTOCOL_VERSION,
500
+ Topics,
501
+ WS_SUBPROTOCOL,
502
+ WireVersionError,
503
+ decodeFrame,
504
+ encodeFrame,
505
+ syncPayloadSchema,
506
+ validateSnapshot
507
+ });
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@rivalis/fleet",
3
+ "version": "8.0.0",
4
+ "description": "Fleet orchestration for Rivalis: instance discovery, room placement, cluster control",
5
+ "source": "src/main.ts",
6
+ "main": "lib/main.js",
7
+ "module": "lib/module.js",
8
+ "types": "lib/main.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./lib/main.d.ts",
12
+ "import": "./lib/module.js",
13
+ "require": "./lib/main.js"
14
+ }
15
+ },
16
+ "bin": {
17
+ "rivalis-fleet": "bin/rivalis-fleet.js"
18
+ },
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "scripts": {
23
+ "dev": "tsup --watch",
24
+ "build": "tsup",
25
+ "test": "npm run build && node --test \"test/**/*.test.mts\""
26
+ },
27
+ "dependencies": {
28
+ "@fastify/cors": "^11.2.0",
29
+ "@toolcase/node": "^4.0.0",
30
+ "@toolcase/serializer": "3.x",
31
+ "commander": "^12.1.0",
32
+ "fastify": "^5.8.5",
33
+ "redis": "^5.12.1"
34
+ },
35
+ "peerDependencies": {
36
+ "@rivalis/core": ">=8 <9",
37
+ "@rivalis/node": ">=8 <9",
38
+ "@toolcase/base": "3.x",
39
+ "@toolcase/logging": "3.x",
40
+ "ws": "8.x"
41
+ },
42
+ "devDependencies": {
43
+ "@rivalis/core": "*",
44
+ "@rivalis/node": "*"
45
+ },
46
+ "keywords": [
47
+ "realtime",
48
+ "websockets",
49
+ "orchestration",
50
+ "fleet",
51
+ "cluster",
52
+ "multiplayer",
53
+ "rivalis"
54
+ ],
55
+ "directories": {
56
+ "lib": "lib"
57
+ },
58
+ "files": [
59
+ "lib",
60
+ "bin"
61
+ ],
62
+ "publishConfig": {
63
+ "access": "public",
64
+ "registry": "https://registry.npmjs.org/"
65
+ },
66
+ "repository": {
67
+ "type": "git",
68
+ "url": "git+https://github.com/kalevski/rivalis.git"
69
+ },
70
+ "author": {
71
+ "name": "kalevski"
72
+ },
73
+ "license": "MIT",
74
+ "bugs": {
75
+ "url": "https://github.com/kalevski/rivalis/issues"
76
+ },
77
+ "homepage": "https://github.com/kalevski/rivalis#readme"
78
+ }