shardwire 0.0.1 → 0.0.3
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 +163 -55
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +44 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +22 -3
package/README.md
CHANGED
|
@@ -1,14 +1,73 @@
|
|
|
1
1
|
# shardwire
|
|
2
2
|
|
|
3
|
-
Lightweight TypeScript library
|
|
3
|
+
> Lightweight TypeScript library for building a Discord-hosted WebSocket command and event bridge.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/shardwire)
|
|
6
|
+
[](https://www.npmjs.com/package/shardwire)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
|
|
11
|
+
`shardwire` helps you expose strongly-typed bot capabilities over WebSocket so dashboards, admin tools, and backend services can send commands to your Discord bot host and subscribe to events in real time.
|
|
12
|
+
|
|
13
|
+
[Quick Start](#quick-start) • [Why shardwire](#why-shardwire) • [Host Setup](#host-setup) • [Consumer Setup](#consumer-setup) • [API Overview](#api-overview) • [Configuration](#configuration) • [Security Notes](#security-notes)
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Why shardwire](#why-shardwire)
|
|
18
|
+
- [Features](#features)
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Host Setup](#host-setup)
|
|
21
|
+
- [Consumer Setup](#consumer-setup)
|
|
22
|
+
- [Token-Only Host (No Existing discord.js Client)](#token-only-host-no-existing-discordjs-client)
|
|
23
|
+
- [Reconnect and Timeout Hardening](#reconnect-and-timeout-hardening)
|
|
24
|
+
- [API Overview](#api-overview)
|
|
25
|
+
- [Configuration](#configuration)
|
|
26
|
+
- [Error Model](#error-model)
|
|
27
|
+
- [Compatibility](#compatibility)
|
|
28
|
+
- [Security Notes](#security-notes)
|
|
29
|
+
- [Roadmap Constraints (v1)](#roadmap-constraints-v1)
|
|
30
|
+
|
|
31
|
+
## Why shardwire
|
|
32
|
+
|
|
33
|
+
Running bot logic inside your Discord host process while orchestrating it from external services is a common pattern, but wiring this safely and ergonomically takes time. `shardwire` gives you a focused transport layer with a typed contract so you can:
|
|
34
|
+
|
|
35
|
+
- call bot-hosted commands from apps and services,
|
|
36
|
+
- stream real-time events back to consumers,
|
|
37
|
+
- keep payloads typed end-to-end with TypeScript,
|
|
38
|
+
- start quickly without deploying extra infrastructure.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Typed command RPC** from consumers to host (`send` -> `CommandResult`).
|
|
43
|
+
- **Real-time pub/sub events** from host to all authenticated consumers.
|
|
44
|
+
- **Single factory API** (`createShardwire`) with host and consumer overloads.
|
|
45
|
+
- **Built-in reliability controls** with reconnect backoff, jitter, and timeouts.
|
|
46
|
+
- **Runtime input validation** for config, names, and JSON-serializable payloads.
|
|
47
|
+
- **Optional token-only Discord mode** where shardwire can own client lifecycle.
|
|
48
|
+
- **Dual package output** for ESM and CJS consumers.
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
Install:
|
|
6
53
|
|
|
7
54
|
```bash
|
|
8
55
|
pnpm add shardwire
|
|
9
56
|
```
|
|
10
57
|
|
|
11
|
-
|
|
58
|
+
Define shared message contracts:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
type Commands = {
|
|
62
|
+
"ban-user": { userId: string };
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type Events = {
|
|
66
|
+
"member-joined": { userId: string; guildId: string };
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Host Setup
|
|
12
71
|
|
|
13
72
|
```ts
|
|
14
73
|
import { createShardwire } from "shardwire";
|
|
@@ -25,19 +84,21 @@ const wire = createShardwire<Commands, Events>({
|
|
|
25
84
|
client: discordClient,
|
|
26
85
|
server: {
|
|
27
86
|
port: 3001,
|
|
28
|
-
|
|
87
|
+
secrets: [process.env.SHARDWIRE_SECRET!],
|
|
88
|
+
primarySecretId: "s0",
|
|
29
89
|
},
|
|
90
|
+
name: "bot-host",
|
|
30
91
|
});
|
|
31
92
|
|
|
32
93
|
wire.onCommand("ban-user", async ({ userId }) => {
|
|
33
94
|
await guild.members.ban(userId);
|
|
34
|
-
return { banned: true };
|
|
95
|
+
return { banned: true, userId };
|
|
35
96
|
});
|
|
36
97
|
|
|
37
98
|
wire.emitEvent("member-joined", { userId: "123", guildId: "456" });
|
|
38
99
|
```
|
|
39
100
|
|
|
40
|
-
## Consumer
|
|
101
|
+
## Consumer Setup
|
|
41
102
|
|
|
42
103
|
```ts
|
|
43
104
|
import { createShardwire } from "shardwire";
|
|
@@ -53,89 +114,136 @@ type Events = {
|
|
|
53
114
|
const wire = createShardwire<Commands, Events>({
|
|
54
115
|
url: "ws://localhost:3001/shardwire",
|
|
55
116
|
secret: process.env.SHARDWIRE_SECRET!,
|
|
117
|
+
secretId: "s0",
|
|
56
118
|
});
|
|
57
119
|
|
|
58
120
|
const result = await wire.send("ban-user", { userId: "123" });
|
|
59
121
|
|
|
60
|
-
|
|
61
|
-
console.log(
|
|
122
|
+
if (result.ok) {
|
|
123
|
+
console.log("Command succeeded:", result.data);
|
|
124
|
+
} else {
|
|
125
|
+
console.error("Command failed:", result.error.code, result.error.message);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
wire.on("member-joined", (payload, meta) => {
|
|
129
|
+
console.log("event", payload, meta.ts);
|
|
62
130
|
});
|
|
63
131
|
```
|
|
64
132
|
|
|
65
|
-
##
|
|
133
|
+
## Token-Only Host (No Existing discord.js Client)
|
|
66
134
|
|
|
67
|
-
|
|
135
|
+
If you do not already manage a `discord.js` client, provide a bot token and shardwire can initialize and own the client lifecycle.
|
|
68
136
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
137
|
+
```ts
|
|
138
|
+
const wire = createShardwire<Commands, Events>({
|
|
139
|
+
token: process.env.DISCORD_BOT_TOKEN!,
|
|
140
|
+
server: {
|
|
141
|
+
port: 3001,
|
|
142
|
+
secrets: [process.env.SHARDWIRE_SECRET!],
|
|
143
|
+
primarySecretId: "s0",
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
```
|
|
73
147
|
|
|
74
|
-
|
|
148
|
+
> [!IMPORTANT]
|
|
149
|
+
> Keep `DISCORD_BOT_TOKEN` and `SHARDWIRE_SECRET` in environment variables. Never commit them.
|
|
75
150
|
|
|
76
|
-
|
|
77
|
-
- `SHARDWIRE_PORT` for host (default: `3001`)
|
|
78
|
-
- `SHARDWIRE_URL` for consumer (default: `ws://localhost:3001/shardwire`)
|
|
151
|
+
## Reconnect and Timeout Hardening
|
|
79
152
|
|
|
80
|
-
|
|
153
|
+
For unstable networks, tune reconnect behavior and request timeout explicitly:
|
|
81
154
|
|
|
82
|
-
|
|
155
|
+
```ts
|
|
156
|
+
const wire = createShardwire({
|
|
157
|
+
url: "ws://bot-host:3001/shardwire",
|
|
158
|
+
secret: process.env.SHARDWIRE_SECRET!,
|
|
159
|
+
secretId: "s0",
|
|
160
|
+
requestTimeoutMs: 10_000,
|
|
161
|
+
reconnect: {
|
|
162
|
+
enabled: true,
|
|
163
|
+
initialDelayMs: 500,
|
|
164
|
+
maxDelayMs: 10_000,
|
|
165
|
+
jitter: true,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## API Overview
|
|
171
|
+
|
|
172
|
+
### Host API
|
|
83
173
|
|
|
84
174
|
- `wire.onCommand(name, handler)` register a command handler.
|
|
85
175
|
- `wire.emitEvent(name, payload)` emit an event to all connected consumers.
|
|
86
176
|
- `wire.broadcast(name, payload)` alias of `emitEvent`.
|
|
87
|
-
- `wire.close()`
|
|
177
|
+
- `wire.close()` close the server and active sockets.
|
|
88
178
|
|
|
89
|
-
### Consumer
|
|
179
|
+
### Consumer API
|
|
90
180
|
|
|
91
|
-
- `wire.send(name, payload, options?)` send command
|
|
181
|
+
- `wire.send(name, payload, options?)` send command and await typed result.
|
|
92
182
|
- `wire.on(name, handler)` subscribe to events.
|
|
93
|
-
- `wire.off(name, handler)`
|
|
183
|
+
- `wire.off(name, handler)` unsubscribe a specific handler.
|
|
94
184
|
- `wire.connected()` check authenticated connection state.
|
|
95
185
|
- `wire.close()` close socket and stop reconnect attempts.
|
|
96
186
|
|
|
97
|
-
|
|
187
|
+
## Configuration
|
|
98
188
|
|
|
99
|
-
|
|
189
|
+
### Host options
|
|
100
190
|
|
|
101
|
-
-
|
|
102
|
-
-
|
|
191
|
+
- `server.port` required port.
|
|
192
|
+
- `server.secrets` required shared secret list for authentication.
|
|
193
|
+
- `server.primarySecretId` optional preferred secret id (for example `"s0"`).
|
|
194
|
+
- `server.path` optional WebSocket path (default `/shardwire`).
|
|
195
|
+
- `server.host` optional bind host.
|
|
196
|
+
- `server.heartbeatMs` heartbeat interval.
|
|
197
|
+
- `server.commandTimeoutMs` command execution timeout.
|
|
198
|
+
- `server.maxPayloadBytes` WebSocket payload size limit.
|
|
199
|
+
- `server.corsOrigins` CORS allowlist for browser clients.
|
|
200
|
+
- `client` existing `discord.js` client (optional).
|
|
201
|
+
- `token` Discord bot token (optional, enables token-only mode).
|
|
202
|
+
|
|
203
|
+
### Consumer options
|
|
103
204
|
|
|
104
|
-
|
|
205
|
+
- `url` host endpoint (for example `ws://localhost:3001/shardwire`).
|
|
206
|
+
- `secret` shared secret matching host.
|
|
207
|
+
- `secretId` optional secret id (for example `"s0"`) used during handshake.
|
|
208
|
+
- `requestTimeoutMs` default timeout for `send`.
|
|
209
|
+
- `reconnect` reconnect policy (`enabled`, delays, jitter).
|
|
210
|
+
- `webSocketFactory` optional custom client implementation.
|
|
105
211
|
|
|
106
|
-
|
|
212
|
+
> [!NOTE]
|
|
213
|
+
> Invalid configuration, empty command/event names, or non-serializable payloads throw synchronously with clear errors.
|
|
107
214
|
|
|
108
|
-
|
|
109
|
-
- Consumer config requires non-empty `url` and `secret`.
|
|
110
|
-
- Command/event names must be non-empty strings.
|
|
111
|
-
- Command/event payloads must be JSON-serializable.
|
|
215
|
+
## Error Model
|
|
112
216
|
|
|
113
|
-
|
|
217
|
+
`send()` resolves to:
|
|
218
|
+
|
|
219
|
+
- success: `{ ok: true, requestId, ts, data }`
|
|
220
|
+
- failure: `{ ok: false, requestId, ts, error }`
|
|
114
221
|
|
|
115
|
-
|
|
222
|
+
Failure codes:
|
|
116
223
|
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
224
|
+
- `UNAUTHORIZED`
|
|
225
|
+
- `TIMEOUT`
|
|
226
|
+
- `COMMAND_NOT_FOUND`
|
|
227
|
+
- `VALIDATION_ERROR`
|
|
228
|
+
- `INTERNAL_ERROR`
|
|
121
229
|
|
|
122
|
-
##
|
|
230
|
+
## Compatibility
|
|
123
231
|
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
232
|
+
- Node.js `>=18.18`
|
|
233
|
+
- TypeScript-first API
|
|
234
|
+
- `discord.js` `^14` as optional peer dependency
|
|
235
|
+
- ESM + CJS package exports
|
|
128
236
|
|
|
129
|
-
##
|
|
237
|
+
## Security Notes
|
|
130
238
|
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
239
|
+
- Use strong, rotated secrets via environment variables.
|
|
240
|
+
- Rotate with overlapping `server.secrets` entries and explicit `secretId` cutovers.
|
|
241
|
+
- Set payload and timeout limits appropriate for your workload.
|
|
242
|
+
- Configure `server.corsOrigins` when exposing browser consumers.
|
|
135
243
|
|
|
136
|
-
## v1
|
|
244
|
+
## Roadmap Constraints (v1)
|
|
137
245
|
|
|
138
|
-
- Single package, no external
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
246
|
+
- Single npm package, no external infrastructure requirement.
|
|
247
|
+
- Host process embeds WebSocket server.
|
|
248
|
+
- Single-host process model (no cross-host sharding in v1).
|
|
249
|
+
- Shared-secret handshake with in-memory dedupe and pending-request tracking.
|
package/dist/index.d.mts
CHANGED
|
@@ -35,7 +35,7 @@ interface CommandFailure {
|
|
|
35
35
|
requestId: string;
|
|
36
36
|
ts: number;
|
|
37
37
|
error: {
|
|
38
|
-
code: "
|
|
38
|
+
code: "UNAUTHORIZED" | "TIMEOUT" | "COMMAND_NOT_FOUND" | "VALIDATION_ERROR" | "INTERNAL_ERROR";
|
|
39
39
|
message: string;
|
|
40
40
|
details?: unknown;
|
|
41
41
|
};
|
|
@@ -47,7 +47,8 @@ interface HostOptions<C extends CommandMap, E extends EventMap> {
|
|
|
47
47
|
server: {
|
|
48
48
|
port: number;
|
|
49
49
|
host?: string;
|
|
50
|
-
|
|
50
|
+
secrets: string[];
|
|
51
|
+
primarySecretId?: string;
|
|
51
52
|
path?: string;
|
|
52
53
|
heartbeatMs?: number;
|
|
53
54
|
commandTimeoutMs?: number;
|
|
@@ -60,6 +61,7 @@ interface HostOptions<C extends CommandMap, E extends EventMap> {
|
|
|
60
61
|
interface ConsumerOptions<C extends CommandMap, E extends EventMap> {
|
|
61
62
|
url: string;
|
|
62
63
|
secret: string;
|
|
64
|
+
secretId?: string;
|
|
63
65
|
webSocketFactory?: (url: string) => {
|
|
64
66
|
readyState: number;
|
|
65
67
|
send(data: string): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface CommandFailure {
|
|
|
35
35
|
requestId: string;
|
|
36
36
|
ts: number;
|
|
37
37
|
error: {
|
|
38
|
-
code: "
|
|
38
|
+
code: "UNAUTHORIZED" | "TIMEOUT" | "COMMAND_NOT_FOUND" | "VALIDATION_ERROR" | "INTERNAL_ERROR";
|
|
39
39
|
message: string;
|
|
40
40
|
details?: unknown;
|
|
41
41
|
};
|
|
@@ -47,7 +47,8 @@ interface HostOptions<C extends CommandMap, E extends EventMap> {
|
|
|
47
47
|
server: {
|
|
48
48
|
port: number;
|
|
49
49
|
host?: string;
|
|
50
|
-
|
|
50
|
+
secrets: string[];
|
|
51
|
+
primarySecretId?: string;
|
|
51
52
|
path?: string;
|
|
52
53
|
heartbeatMs?: number;
|
|
53
54
|
commandTimeoutMs?: number;
|
|
@@ -60,6 +61,7 @@ interface HostOptions<C extends CommandMap, E extends EventMap> {
|
|
|
60
61
|
interface ConsumerOptions<C extends CommandMap, E extends EventMap> {
|
|
61
62
|
url: string;
|
|
62
63
|
secret: string;
|
|
64
|
+
secretId?: string;
|
|
63
65
|
webSocketFactory?: (url: string) => {
|
|
64
66
|
readyState: number;
|
|
65
67
|
send(data: string): void;
|
package/dist/index.js
CHANGED
|
@@ -113,8 +113,16 @@ function assertHostOptions(options) {
|
|
|
113
113
|
throw new Error("Host mode requires a server configuration.");
|
|
114
114
|
}
|
|
115
115
|
assertPositiveNumber("server.port", options.server.port);
|
|
116
|
-
if (!
|
|
117
|
-
throw new Error("server.
|
|
116
|
+
if (!Array.isArray(options.server.secrets) || options.server.secrets.length === 0) {
|
|
117
|
+
throw new Error("server.secrets must contain at least one secret.");
|
|
118
|
+
}
|
|
119
|
+
for (const [index, secret] of options.server.secrets.entries()) {
|
|
120
|
+
if (!isNonEmptyString(secret)) {
|
|
121
|
+
throw new Error(`server.secrets[${index}] must be a non-empty string.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (options.server.primarySecretId !== void 0 && !options.server.secrets.some((_, index) => options.server.primarySecretId === `s${index}`)) {
|
|
125
|
+
throw new Error("server.primarySecretId must reference an existing secret id.");
|
|
118
126
|
}
|
|
119
127
|
if (options.server.heartbeatMs !== void 0) {
|
|
120
128
|
assertPositiveNumber("server.heartbeatMs", options.server.heartbeatMs);
|
|
@@ -133,6 +141,9 @@ function assertConsumerOptions(options) {
|
|
|
133
141
|
if (!isNonEmptyString(options.secret)) {
|
|
134
142
|
throw new Error("Consumer mode requires `secret`.");
|
|
135
143
|
}
|
|
144
|
+
if (options.secretId !== void 0 && !isNonEmptyString(options.secretId)) {
|
|
145
|
+
throw new Error("Consumer option `secretId` must be a non-empty string.");
|
|
146
|
+
}
|
|
136
147
|
if (options.requestTimeoutMs !== void 0) {
|
|
137
148
|
assertPositiveNumber("requestTimeoutMs", options.requestTimeoutMs);
|
|
138
149
|
}
|
|
@@ -255,7 +266,10 @@ function createConsumerShardwire(options) {
|
|
|
255
266
|
socket.on("open", () => {
|
|
256
267
|
reconnectAttempts = 0;
|
|
257
268
|
isAuthed = false;
|
|
258
|
-
const hello = makeEnvelope("auth.hello", {
|
|
269
|
+
const hello = makeEnvelope("auth.hello", {
|
|
270
|
+
secret: options.secret,
|
|
271
|
+
secretId: options.secretId
|
|
272
|
+
});
|
|
259
273
|
socket?.send(stringifyEnvelope(hello));
|
|
260
274
|
authTimeoutTimer = setTimeout(() => {
|
|
261
275
|
if (!isAuthed) {
|
|
@@ -347,7 +361,7 @@ function createConsumerShardwire(options) {
|
|
|
347
361
|
requestId: sendOptions?.requestId ?? "unknown",
|
|
348
362
|
ts: Date.now(),
|
|
349
363
|
error: {
|
|
350
|
-
code: "
|
|
364
|
+
code: "UNAUTHORIZED",
|
|
351
365
|
message: error instanceof Error ? error.message : "Failed to authenticate."
|
|
352
366
|
}
|
|
353
367
|
};
|
|
@@ -525,6 +539,9 @@ function isSecretValid(provided, expected) {
|
|
|
525
539
|
}
|
|
526
540
|
return (0, import_node_crypto2.timingSafeEqual)(providedBuffer, expectedBuffer);
|
|
527
541
|
}
|
|
542
|
+
function getSecretId(secretIndex) {
|
|
543
|
+
return `s${secretIndex}`;
|
|
544
|
+
}
|
|
528
545
|
|
|
529
546
|
// src/transport/ws/host-server.ts
|
|
530
547
|
var CLOSE_AUTH_REQUIRED = 4001;
|
|
@@ -643,13 +660,33 @@ var HostWebSocketServer = class {
|
|
|
643
660
|
return;
|
|
644
661
|
}
|
|
645
662
|
const payload = envelope.payload;
|
|
646
|
-
|
|
663
|
+
const providedSecret = payload?.secret;
|
|
664
|
+
const knownSecrets = this.config.options.server.secrets;
|
|
665
|
+
const secretId = payload?.secretId;
|
|
666
|
+
let authReason = null;
|
|
667
|
+
if (!providedSecret) {
|
|
668
|
+
authReason = "invalid_secret";
|
|
669
|
+
} else if (secretId) {
|
|
670
|
+
const secretIndex = knownSecrets.findIndex((_, index) => getSecretId(index) === secretId);
|
|
671
|
+
if (secretIndex < 0) {
|
|
672
|
+
authReason = "unknown_secret_id";
|
|
673
|
+
} else {
|
|
674
|
+
const expectedSecret = knownSecrets[secretIndex];
|
|
675
|
+
if (!expectedSecret || !isSecretValid(providedSecret, expectedSecret)) {
|
|
676
|
+
authReason = "invalid_secret";
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
} else if (!knownSecrets.some((secret) => isSecretValid(providedSecret, secret))) {
|
|
680
|
+
authReason = "invalid_secret";
|
|
681
|
+
}
|
|
682
|
+
if (authReason) {
|
|
647
683
|
this.safeSend(
|
|
648
684
|
state.socket,
|
|
649
685
|
stringifyEnvelope(
|
|
650
686
|
makeEnvelope("auth.error", {
|
|
651
|
-
code: "
|
|
652
|
-
|
|
687
|
+
code: "UNAUTHORIZED",
|
|
688
|
+
reason: authReason,
|
|
689
|
+
message: "Authentication failed."
|
|
653
690
|
})
|
|
654
691
|
)
|
|
655
692
|
);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/transport/ws/consumer-socket.ts","../src/utils/id.ts","../src/utils/backoff.ts","../src/utils/logger.ts","../src/core/protocol.ts","../src/runtime/validation.ts","../src/consumer/index.ts","../src/discord/client.ts","../src/runtime/reliability.ts","../src/transport/ws/host-server.ts","../src/runtime/security.ts","../src/host/index.ts"],"sourcesContent":["import { createConsumerShardwire } from \"./consumer\";\nimport { resolveDiscordClient } from \"./discord/client\";\nimport { createHostShardwire } from \"./host\";\nimport { assertConsumerOptions, assertHostOptions } from \"./runtime/validation\";\nimport type {\n CommandMap,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"./core/types\";\n\nfunction isHostOptions<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): options is HostOptions<C, E> {\n return \"server\" in options;\n}\n\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E>,\n): HostShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): HostShardwire<C, E> | ConsumerShardwire<C, E> {\n if (!isHostOptions(options)) {\n assertConsumerOptions(options);\n return createConsumerShardwire<C, E>(options);\n }\n assertHostOptions(options);\n\n let ownedClientPromise: Promise<{ destroy: () => void } | undefined> | undefined;\n\n if (!options.client && options.token) {\n ownedClientPromise = resolveDiscordClient(options)\n .then((state) => {\n if (!state.owned || !state.client) {\n return undefined;\n }\n return {\n destroy: () => state.client?.destroy(),\n };\n })\n .catch((error) => {\n options.logger?.error?.(\"Failed to initialize discord.js client from token.\", {\n error: String(error),\n });\n return undefined;\n });\n }\n\n return createHostShardwire<C, E>(options, {\n onClose: async () => {\n const owned = await ownedClientPromise;\n owned?.destroy();\n },\n });\n}\n\nexport type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandResult,\n CommandSuccess,\n ConsumerOptions,\n ConsumerShardwire,\n DiscordClientLike,\n EventMap,\n EventMeta,\n HostOptions,\n HostShardwire,\n ShardwireLogger,\n Unsubscribe,\n} from \"./core/types\";\n","import { WebSocket } from \"ws\";\n\nexport interface WebSocketLike {\n readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n on(event: \"open\", listener: () => void): void;\n on(event: \"message\", listener: (data: unknown) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: \"error\", listener: (error: unknown) => void): void;\n once(event: \"close\", listener: () => void): void;\n}\n\nexport function createNodeWebSocket(url: string): WebSocketLike {\n return new WebSocket(url) as unknown as WebSocketLike;\n}\n","import { randomUUID } from \"node:crypto\";\n\nexport function createRequestId(): string {\n return randomUUID();\n}\n\nexport function createConnectionId(): string {\n return randomUUID();\n}\n","export interface BackoffConfig {\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n}\n\nexport function getBackoffDelay(attempt: number, config: BackoffConfig): number {\n const base = Math.min(config.maxDelayMs, config.initialDelayMs * 2 ** attempt);\n if (!config.jitter) {\n return base;\n }\n const spread = Math.floor(base * 0.2);\n const min = Math.max(0, base - spread);\n const max = base + spread;\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","import type { ShardwireLogger } from \"../core/types\";\n\nexport function withLogger(logger?: ShardwireLogger): Required<ShardwireLogger> {\n return {\n debug: logger?.debug ?? (() => undefined),\n info: logger?.info ?? (() => undefined),\n warn: logger?.warn ?? (() => undefined),\n error: logger?.error ?? (() => undefined),\n };\n}\n","import type { CommandResult } from \"./types\";\n\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type WireType =\n | \"auth.hello\"\n | \"auth.ok\"\n | \"auth.error\"\n | \"command.request\"\n | \"command.result\"\n | \"command.error\"\n | \"event.emit\"\n | \"ping\"\n | \"pong\";\n\nexport type WireEnvelope<TType extends WireType = WireType, TPayload = unknown> = {\n v: typeof PROTOCOL_VERSION;\n type: TType;\n ts: number;\n requestId?: string;\n source?: string;\n payload: TPayload;\n};\n\nexport interface AuthHelloPayload {\n secret: string;\n clientName?: string;\n}\n\nexport interface AuthOkPayload {\n connectionId: string;\n}\n\nexport interface AuthErrorPayload {\n code: \"AUTH_ERROR\";\n message: string;\n}\n\nexport interface CommandRequestPayload {\n name: string;\n data: unknown;\n}\n\nexport interface EventEmitPayload {\n name: string;\n data: unknown;\n}\n\nexport type CommandResultPayload = CommandResult;\n\nexport function makeEnvelope<TType extends WireType, TPayload>(\n type: TType,\n payload: TPayload,\n extras?: { requestId?: string; source?: string },\n): WireEnvelope<TType, TPayload> {\n const envelope: WireEnvelope<TType, TPayload> = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n payload,\n };\n if (extras?.requestId) {\n envelope.requestId = extras.requestId;\n }\n if (extras?.source) {\n envelope.source = extras.source;\n }\n return envelope;\n}\n\nexport function parseEnvelope(raw: string): WireEnvelope {\n const parsed = JSON.parse(raw) as WireEnvelope;\n if (!parsed || parsed.v !== PROTOCOL_VERSION || typeof parsed.type !== \"string\") {\n throw new Error(\"Invalid wire envelope.\");\n }\n return parsed;\n}\n\nexport function stringifyEnvelope(envelope: WireEnvelope): string {\n return JSON.stringify(envelope);\n}\n","import type { ConsumerOptions, HostOptions } from \"../core/types\";\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction assertPositiveNumber(name: string, value: unknown): void {\n if (typeof value !== \"number\" || Number.isNaN(value) || value <= 0) {\n throw new Error(`${name} must be a positive number.`);\n }\n}\n\nexport function assertHostOptions(options: HostOptions<any, any>): void {\n if (!options.server) {\n throw new Error(\"Host mode requires a server configuration.\");\n }\n assertPositiveNumber(\"server.port\", options.server.port);\n if (!isNonEmptyString(options.server.secret)) {\n throw new Error(\"server.secret is required.\");\n }\n if (options.server.heartbeatMs !== undefined) {\n assertPositiveNumber(\"server.heartbeatMs\", options.server.heartbeatMs);\n }\n if (options.server.commandTimeoutMs !== undefined) {\n assertPositiveNumber(\"server.commandTimeoutMs\", options.server.commandTimeoutMs);\n }\n if (options.server.maxPayloadBytes !== undefined) {\n assertPositiveNumber(\"server.maxPayloadBytes\", options.server.maxPayloadBytes);\n }\n}\n\nexport function assertConsumerOptions(options: ConsumerOptions<any, any>): void {\n if (!isNonEmptyString(options.url)) {\n throw new Error(\"Consumer mode requires `url`.\");\n }\n if (!isNonEmptyString(options.secret)) {\n throw new Error(\"Consumer mode requires `secret`.\");\n }\n if (options.requestTimeoutMs !== undefined) {\n assertPositiveNumber(\"requestTimeoutMs\", options.requestTimeoutMs);\n }\n if (options.reconnect?.initialDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.initialDelayMs\", options.reconnect.initialDelayMs);\n }\n if (options.reconnect?.maxDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.maxDelayMs\", options.reconnect.maxDelayMs);\n }\n}\n\nexport function assertMessageName(kind: \"command\" | \"event\", name: string): void {\n if (!isNonEmptyString(name)) {\n throw new Error(`${kind} name must be a non-empty string.`);\n }\n}\n\nexport function assertJsonPayload(kind: \"command\" | \"event\", name: string, payload: unknown): void {\n try {\n JSON.stringify(payload);\n } catch {\n throw new Error(`${kind} \"${name}\" payload must be JSON-serializable.`);\n }\n}\n","import type {\n CommandFailure,\n CommandMap,\n CommandResult,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n EventMeta,\n} from \"../core/types\";\nimport { createNodeWebSocket, type WebSocketLike } from \"../transport/ws/consumer-socket\";\nimport { createRequestId } from \"../utils/id\";\nimport { getBackoffDelay } from \"../utils/backoff\";\nimport { withLogger } from \"../utils/logger\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthErrorPayload,\n type CommandResultPayload,\n type EventEmitPayload,\n} from \"../core/protocol\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\ntype EventHandler = (payload: unknown, meta: EventMeta) => void;\n\ninterface PendingRequest {\n resolve: (value: CommandResult) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10000;\n\nexport function createConsumerShardwire<C extends CommandMap, E extends EventMap>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E> {\n const logger = withLogger(options.logger);\n const reconnectEnabled = options.reconnect?.enabled ?? true;\n const initialDelayMs = options.reconnect?.initialDelayMs ?? 500;\n const maxDelayMs = options.reconnect?.maxDelayMs ?? 10000;\n const jitter = options.reconnect?.jitter ?? true;\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n let socket: WebSocketLike | null = null;\n let isClosed = false;\n let isAuthed = false;\n let reconnectAttempts = 0;\n let reconnectTimer: NodeJS.Timeout | null = null;\n let connectPromise: Promise<void> | null = null;\n let connectResolve: (() => void) | null = null;\n let connectReject: ((error: Error) => void) | null = null;\n let authTimeoutTimer: NodeJS.Timeout | null = null;\n\n const pendingRequests = new Map<string, PendingRequest>();\n const eventHandlers = new Map<string, Set<EventHandler>>();\n\n function clearAuthTimeout(): void {\n if (authTimeoutTimer) {\n clearTimeout(authTimeoutTimer);\n authTimeoutTimer = null;\n }\n }\n\n function resolveConnect(): void {\n clearAuthTimeout();\n connectResolve?.();\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function rejectConnect(message: string): void {\n clearAuthTimeout();\n if (connectReject) {\n connectReject(new Error(message));\n }\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function sendRaw(data: string): void {\n if (!socket || socket.readyState !== 1) {\n throw new Error(\"Shardwire consumer is not connected.\");\n }\n socket.send(data);\n }\n\n function rejectAllPending(reason: string): void {\n for (const [requestId, pending] of pendingRequests.entries()) {\n clearTimeout(pending.timer);\n pending.reject(new Error(reason));\n pendingRequests.delete(requestId);\n }\n }\n\n function scheduleReconnect(): void {\n if (isClosed || !reconnectEnabled || reconnectTimer) {\n return;\n }\n const delay = getBackoffDelay(reconnectAttempts, { initialDelayMs, maxDelayMs, jitter });\n reconnectAttempts += 1;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n void connect().catch((error) => {\n logger.warn(\"Reconnect attempt failed.\", { error: String(error) });\n });\n }, delay);\n }\n\n function handleEvent(name: string, payload: unknown, meta: EventMeta): void {\n const handlers = eventHandlers.get(name);\n if (!handlers || handlers.size === 0) {\n return;\n }\n for (const handler of handlers) {\n try {\n handler(payload, meta);\n } catch (error) {\n logger.warn(\"Event handler threw an error.\", { name, error: String(error) });\n }\n }\n }\n\n async function connect(): Promise<void> {\n if (isClosed) {\n return;\n }\n if (socket && socket.readyState === 1 && isAuthed) {\n return;\n }\n if (connectPromise) {\n return connectPromise;\n }\n\n connectPromise = new Promise<void>((resolve, reject) => {\n connectResolve = resolve;\n connectReject = reject;\n });\n\n socket = options.webSocketFactory\n ? (options.webSocketFactory(options.url) as WebSocketLike)\n : createNodeWebSocket(options.url);\n\n socket.on(\"open\", () => {\n reconnectAttempts = 0;\n isAuthed = false;\n const hello = makeEnvelope(\"auth.hello\", { secret: options.secret });\n socket?.send(stringifyEnvelope(hello));\n authTimeoutTimer = setTimeout(() => {\n if (!isAuthed) {\n rejectConnect(\"Shardwire auth timed out.\");\n socket?.close();\n }\n }, requestTimeoutMs);\n });\n\n socket.on(\"message\", (raw) => {\n try {\n const serialized = typeof raw === \"string\" ? raw : String(raw);\n const envelope = parseEnvelope(serialized);\n switch (envelope.type) {\n case \"auth.ok\":\n isAuthed = true;\n resolveConnect();\n break;\n case \"auth.error\": {\n const payload = envelope.payload as AuthErrorPayload;\n logger.error(\"Authentication failed for consumer.\", {\n code: payload.code,\n message: payload.message,\n });\n rejectConnect(payload.message);\n rejectAllPending(\"Shardwire authentication failed.\");\n socket?.close();\n break;\n }\n case \"command.result\":\n case \"command.error\": {\n const requestId = envelope.requestId;\n if (!requestId) {\n return;\n }\n const pending = pendingRequests.get(requestId);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.resolve(envelope.payload as CommandResultPayload);\n pendingRequests.delete(requestId);\n break;\n }\n case \"event.emit\": {\n const payload = envelope.payload as EventEmitPayload;\n const meta: EventMeta = { ts: envelope.ts };\n if (envelope.source) {\n meta.source = envelope.source;\n }\n handleEvent(payload.name, payload.data, meta);\n break;\n }\n case \"ping\":\n sendRaw(stringifyEnvelope(makeEnvelope(\"pong\", {})));\n break;\n default:\n break;\n }\n } catch (error) {\n logger.warn(\"Failed to parse consumer message.\", { error: String(error) });\n }\n });\n\n socket.on(\"close\", () => {\n rejectConnect(\"Shardwire connection closed.\");\n isAuthed = false;\n if (!isClosed) {\n scheduleReconnect();\n }\n });\n\n socket.on(\"error\", (error) => {\n logger.warn(\"Consumer socket error.\", { error: String(error) });\n });\n\n return connectPromise;\n }\n\n void connect().catch((error) => {\n logger.warn(\"Initial connection attempt failed.\", { error: String(error) });\n });\n\n return {\n mode: \"consumer\",\n async send(name, payload, sendOptions) {\n assertMessageName(\"command\", name);\n assertJsonPayload(\"command\", name, payload);\n if (!isAuthed) {\n try {\n await connect();\n } catch (error) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"AUTH_ERROR\",\n message: error instanceof Error ? error.message : \"Failed to authenticate.\",\n },\n } satisfies CommandFailure;\n }\n }\n if (!socket || socket.readyState !== 1) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: \"Not connected to Shardwire host.\",\n },\n } satisfies CommandFailure;\n }\n\n const requestId = sendOptions?.requestId ?? createRequestId();\n const timeoutMs = sendOptions?.timeoutMs ?? requestTimeoutMs;\n\n const promise = new Promise<CommandResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingRequests.delete(requestId);\n reject(new Error(`Command \"${name}\" timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n pendingRequests.set(requestId, { resolve, reject, timer });\n });\n\n sendRaw(\n stringifyEnvelope(\n makeEnvelope(\n \"command.request\",\n {\n name,\n data: payload,\n },\n { requestId },\n ),\n ),\n );\n\n try {\n return await promise;\n } catch (error) {\n return {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: error instanceof Error ? error.message : \"Command request timeout.\",\n },\n } satisfies CommandFailure;\n }\n },\n on(name, handler) {\n assertMessageName(\"event\", name);\n const casted = handler as EventHandler;\n const existing = eventHandlers.get(name);\n if (existing) {\n existing.add(casted);\n } else {\n eventHandlers.set(name, new Set<EventHandler>([casted]));\n }\n return () => {\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(casted);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n };\n },\n off(name, handler) {\n assertMessageName(\"event\", name);\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n },\n connected() {\n return Boolean(socket && socket.readyState === 1 && isAuthed);\n },\n async close() {\n isClosed = true;\n isAuthed = false;\n rejectConnect(\"Shardwire consumer has been closed.\");\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n rejectAllPending(\"Shardwire consumer has been closed.\");\n if (!socket) {\n return;\n }\n await new Promise<void>((resolve) => {\n const current = socket;\n if (!current) {\n resolve();\n return;\n }\n current.once(\"close\", () => resolve());\n current.close();\n });\n socket = null;\n },\n };\n}\n","import type { DiscordClientLike, HostOptions } from \"../core/types\";\n\ninterface OwnedClientState {\n client?: DiscordClientLike;\n owned: boolean;\n}\n\nexport async function resolveDiscordClient<C extends Record<string, unknown>, E extends Record<string, unknown>>(\n options: HostOptions<C, E>,\n): Promise<OwnedClientState> {\n if (options.client) {\n return { client: options.client, owned: false };\n }\n\n if (!options.token) {\n return { owned: false };\n }\n\n const discordModule = await import(\"discord.js\");\n const created = new discordModule.Client({\n intents: [],\n });\n await created.login(options.token);\n return { client: created as unknown as DiscordClientLike, owned: true };\n}\n","export async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutMessage = \"Operation timed out.\",\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(timeoutMessage));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timeout);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n}\n\nexport class DedupeCache<T> {\n private readonly cache = new Map<string, { value: T; expiresAt: number }>();\n\n constructor(private readonly ttlMs: number) {}\n\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n this.cache.delete(key);\n return undefined;\n }\n return entry.value;\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });\n }\n}\n","import { WebSocketServer, type WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthHelloPayload,\n type CommandRequestPayload,\n type EventEmitPayload,\n type WireEnvelope,\n} from \"../../core/protocol\";\nimport type { CommandFailure, CommandSuccess, HostOptions, ShardwireLogger } from \"../../core/types\";\nimport { withLogger } from \"../../utils/logger\";\nimport { createConnectionId } from \"../../utils/id\";\nimport type { HostConnectionState } from \"../../runtime/state\";\nimport { isSecretValid } from \"../../runtime/security\";\n\nconst CLOSE_AUTH_REQUIRED = 4001;\nconst CLOSE_AUTH_FAILED = 4003;\nconst CLOSE_INVALID_PAYLOAD = 4004;\n\ninterface HostServerConfig {\n options: HostOptions<any, any>;\n onCommandRequest: (\n connection: HostConnectionState,\n payload: CommandRequestPayload,\n requestId: string,\n source?: string,\n ) => Promise<CommandSuccess | CommandFailure>;\n}\n\nexport class HostWebSocketServer {\n private readonly wss: WebSocketServer;\n private readonly connections = new Map<WebSocket, HostConnectionState>();\n private readonly logger: Required<ShardwireLogger>;\n private readonly heartbeatMs: number;\n private readonly authTimeoutMs = 5000;\n private readonly interval: NodeJS.Timeout;\n\n constructor(private readonly config: HostServerConfig) {\n const serverConfig = config.options.server;\n this.heartbeatMs = serverConfig.heartbeatMs ?? 30000;\n this.logger = withLogger(config.options.logger);\n\n this.wss = new WebSocketServer({\n host: serverConfig.host,\n port: serverConfig.port,\n path: serverConfig.path ?? \"/shardwire\",\n maxPayload: serverConfig.maxPayloadBytes ?? 65536,\n });\n\n this.wss.on(\"connection\", (socket, request) => this.handleConnection(socket, request));\n this.wss.on(\"error\", (error) =>\n this.logger.error(\"Shardwire host server error.\", { error: String(error) }),\n );\n\n this.interval = setInterval(() => {\n this.checkHeartbeats();\n }, this.heartbeatMs);\n }\n\n emitEvent(name: string, data: unknown, source?: string): void {\n const envelope = makeEnvelope(\n \"event.emit\",\n { name, data } satisfies EventEmitPayload,\n source ? { source } : undefined,\n );\n const raw = stringifyEnvelope(envelope);\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n this.safeSend(state.socket, raw);\n }\n }\n\n async close(): Promise<void> {\n clearInterval(this.interval);\n for (const connection of this.connections.values()) {\n connection.socket.close();\n }\n this.connections.clear();\n await new Promise<void>((resolve, reject) => {\n this.wss.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n private handleConnection(socket: WebSocket, request: IncomingMessage): void {\n const allowlist = this.config.options.server.corsOrigins;\n if (allowlist && allowlist.length > 0) {\n const origin = request.headers.origin;\n if (!origin || !allowlist.includes(origin)) {\n socket.close(CLOSE_AUTH_FAILED, \"Origin not allowed.\");\n return;\n }\n }\n\n const state: HostConnectionState = {\n id: createConnectionId(),\n socket,\n authenticated: false,\n lastHeartbeatAt: Date.now(),\n };\n this.connections.set(socket, state);\n\n const authTimer = setTimeout(() => {\n if (!state.authenticated) {\n socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n }\n }, this.authTimeoutMs);\n\n socket.on(\"message\", async (raw) => {\n try {\n const parsed = parseEnvelope(raw.toString());\n await this.handleMessage(state, parsed);\n } catch (error) {\n this.logger.warn(\"Invalid message payload from client.\", { error: String(error) });\n socket.close(CLOSE_INVALID_PAYLOAD, \"Invalid payload.\");\n }\n });\n\n socket.on(\"close\", () => {\n clearTimeout(authTimer);\n this.connections.delete(socket);\n });\n\n socket.on(\"error\", (error) =>\n this.logger.warn(\"Socket error.\", { connectionId: state.id, error: String(error) }),\n );\n }\n\n private async handleMessage(state: HostConnectionState, envelope: WireEnvelope): Promise<void> {\n if (envelope.type === \"ping\") {\n state.lastHeartbeatAt = Date.now();\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"pong\", {})));\n return;\n }\n if (envelope.type === \"pong\") {\n state.lastHeartbeatAt = Date.now();\n return;\n }\n\n if (!state.authenticated) {\n if (envelope.type !== \"auth.hello\") {\n state.socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n return;\n }\n\n const payload = envelope.payload as AuthHelloPayload;\n if (!payload?.secret || !isSecretValid(payload.secret, this.config.options.server.secret)) {\n this.safeSend(\n state.socket,\n stringifyEnvelope(\n makeEnvelope(\"auth.error\", {\n code: \"AUTH_ERROR\",\n message: \"Invalid shared secret.\",\n }),\n ),\n );\n state.socket.close(CLOSE_AUTH_FAILED, \"Invalid secret.\");\n return;\n }\n\n state.authenticated = true;\n state.lastHeartbeatAt = Date.now();\n if (payload.clientName) {\n state.clientName = payload.clientName;\n }\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"auth.ok\", { connectionId: state.id })),\n );\n return;\n }\n\n if (envelope.type === \"command.request\") {\n const payload = envelope.payload as CommandRequestPayload;\n if (!envelope.requestId || !payload?.name) {\n const invalid: CommandFailure = {\n ok: false,\n requestId: envelope.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"VALIDATION_ERROR\",\n message: \"Invalid command request envelope.\",\n },\n };\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"command.error\", invalid, { requestId: invalid.requestId })),\n );\n return;\n }\n\n const response = await this.config.onCommandRequest(\n state,\n payload,\n envelope.requestId,\n envelope.source,\n );\n const responseType = response.ok ? \"command.result\" : \"command.error\";\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(responseType, response, { requestId: response.requestId })),\n );\n return;\n }\n }\n\n private checkHeartbeats(): void {\n const now = Date.now();\n const threshold = this.heartbeatMs * 2;\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n\n if (now - state.lastHeartbeatAt > threshold) {\n state.socket.terminate();\n this.connections.delete(state.socket);\n continue;\n }\n\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"ping\", {})));\n }\n }\n\n private safeSend(socket: WebSocket, payload: string): void {\n if (socket.readyState === 1) {\n socket.send(payload);\n }\n }\n}\n","import { timingSafeEqual } from \"node:crypto\";\n\nexport function isSecretValid(provided: string, expected: string): boolean {\n const providedBuffer = Buffer.from(provided);\n const expectedBuffer = Buffer.from(expected);\n\n if (providedBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(providedBuffer, expectedBuffer);\n}\n","import type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandSuccess,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"../core/types\";\nimport { withTimeout, DedupeCache } from \"../runtime/reliability\";\nimport type { CommandHandler } from \"../runtime/state\";\nimport { HostWebSocketServer } from \"../transport/ws/host-server\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 10000;\n\nexport function createHostShardwire<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E>,\n runtimeHooks?: {\n onClose?: () => Promise<void> | void;\n },\n): HostShardwire<C, E> {\n const commandHandlers = new Map<string, CommandHandler>();\n const commandTimeoutMs = options.server.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;\n const dedupeCache = new DedupeCache<CommandSuccess | CommandFailure>(commandTimeoutMs * 2);\n\n const hostServer = new HostWebSocketServer({\n options,\n onCommandRequest: async (connection, payload, requestId, source) => {\n const cacheKey = `${requestId}:${payload.name}`;\n const cached = dedupeCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const handler = commandHandlers.get(payload.name);\n if (!handler) {\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"COMMAND_NOT_FOUND\",\n message: `No command handler registered for \"${payload.name}\".`,\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n\n const context: CommandContext = {\n requestId,\n connectionId: connection.id,\n receivedAt: Date.now(),\n };\n if (source) {\n context.source = source;\n }\n\n try {\n const maybePromise = Promise.resolve(handler(payload.data, context));\n const value = await withTimeout(\n maybePromise,\n commandTimeoutMs,\n `Command \"${payload.name}\" timed out after ${commandTimeoutMs}ms.`,\n );\n const success: CommandSuccess = {\n ok: true,\n requestId,\n ts: Date.now(),\n data: value,\n };\n dedupeCache.set(cacheKey, success);\n return success;\n } catch (error) {\n const isTimeout = error instanceof Error && /timed out/i.test(error.message);\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: isTimeout ? \"TIMEOUT\" : \"INTERNAL_ERROR\",\n message: error instanceof Error ? error.message : \"Unknown command execution error.\",\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n },\n });\n\n return {\n mode: \"host\",\n onCommand(name, handler) {\n assertMessageName(\"command\", name);\n commandHandlers.set(name, handler as CommandHandler);\n return () => {\n commandHandlers.delete(name);\n };\n },\n emitEvent(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n broadcast(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n close() {\n return hostServer.close().then(async () => {\n await runtimeHooks?.onClose?.();\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,gBAA0B;AAanB,SAAS,oBAAoB,KAA4B;AAC9D,SAAO,IAAI,oBAAU,GAAG;AAC1B;;;ACfA,yBAA2B;AAEpB,SAAS,kBAA0B;AACxC,aAAO,+BAAW;AACpB;AAEO,SAAS,qBAA6B;AAC3C,aAAO,+BAAW;AACpB;;;ACFO,SAAS,gBAAgB,SAAiB,QAA+B;AAC9E,QAAM,OAAO,KAAK,IAAI,OAAO,YAAY,OAAO,iBAAiB,KAAK,OAAO;AAC7E,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,MAAM,OAAO,GAAG;AACpC,QAAM,MAAM,KAAK,IAAI,GAAG,OAAO,MAAM;AACrC,QAAM,MAAM,OAAO;AACnB,SAAO,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,MAAM,EAAE,IAAI;AACvD;;;ACbO,SAAS,WAAW,QAAqD;AAC9E,SAAO;AAAA,IACL,OAAO,QAAQ,UAAU,MAAM;AAAA,IAC/B,MAAM,QAAQ,SAAS,MAAM;AAAA,IAC7B,MAAM,QAAQ,SAAS,MAAM;AAAA,IAC7B,OAAO,QAAQ,UAAU,MAAM;AAAA,EACjC;AACF;;;ACPO,IAAM,mBAAmB;AAgDzB,SAAS,aACd,MACA,SACA,QAC+B;AAC/B,QAAM,WAA0C;AAAA,IAC9C,GAAG;AAAA,IACH;AAAA,IACA,IAAI,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACA,MAAI,QAAQ,WAAW;AACrB,aAAS,YAAY,OAAO;AAAA,EAC9B;AACA,MAAI,QAAQ,QAAQ;AAClB,aAAS,SAAS,OAAO;AAAA,EAC3B;AACA,SAAO;AACT;AAEO,SAAS,cAAc,KAA2B;AACvD,QAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,MAAI,CAAC,UAAU,OAAO,MAAM,oBAAoB,OAAO,OAAO,SAAS,UAAU;AAC/E,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,SAAS,kBAAkB,UAAgC;AAChE,SAAO,KAAK,UAAU,QAAQ;AAChC;;;AC9EA,SAAS,iBAAiB,OAAiC;AACzD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS;AAC5D;AAEA,SAAS,qBAAqB,MAAc,OAAsB;AAChE,MAAI,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,KAAK,SAAS,GAAG;AAClE,UAAM,IAAI,MAAM,GAAG,IAAI,6BAA6B;AAAA,EACtD;AACF;AAEO,SAAS,kBAAkB,SAAsC;AACtE,MAAI,CAAC,QAAQ,QAAQ;AACnB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,uBAAqB,eAAe,QAAQ,OAAO,IAAI;AACvD,MAAI,CAAC,iBAAiB,QAAQ,OAAO,MAAM,GAAG;AAC5C,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACA,MAAI,QAAQ,OAAO,gBAAgB,QAAW;AAC5C,yBAAqB,sBAAsB,QAAQ,OAAO,WAAW;AAAA,EACvE;AACA,MAAI,QAAQ,OAAO,qBAAqB,QAAW;AACjD,yBAAqB,2BAA2B,QAAQ,OAAO,gBAAgB;AAAA,EACjF;AACA,MAAI,QAAQ,OAAO,oBAAoB,QAAW;AAChD,yBAAqB,0BAA0B,QAAQ,OAAO,eAAe;AAAA,EAC/E;AACF;AAEO,SAAS,sBAAsB,SAA0C;AAC9E,MAAI,CAAC,iBAAiB,QAAQ,GAAG,GAAG;AAClC,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,MAAI,CAAC,iBAAiB,QAAQ,MAAM,GAAG;AACrC,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AACA,MAAI,QAAQ,qBAAqB,QAAW;AAC1C,yBAAqB,oBAAoB,QAAQ,gBAAgB;AAAA,EACnE;AACA,MAAI,QAAQ,WAAW,mBAAmB,QAAW;AACnD,yBAAqB,4BAA4B,QAAQ,UAAU,cAAc;AAAA,EACnF;AACA,MAAI,QAAQ,WAAW,eAAe,QAAW;AAC/C,yBAAqB,wBAAwB,QAAQ,UAAU,UAAU;AAAA,EAC3E;AACF;AAEO,SAAS,kBAAkB,MAA2B,MAAoB;AAC/E,MAAI,CAAC,iBAAiB,IAAI,GAAG;AAC3B,UAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;AAAA,EAC5D;AACF;AAEO,SAAS,kBAAkB,MAA2B,MAAc,SAAwB;AACjG,MAAI;AACF,SAAK,UAAU,OAAO;AAAA,EACxB,QAAQ;AACN,UAAM,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,sCAAsC;AAAA,EACxE;AACF;;;AC9BA,IAAM,6BAA6B;AAE5B,SAAS,wBACd,SACyB;AACzB,QAAM,SAAS,WAAW,QAAQ,MAAM;AACxC,QAAM,mBAAmB,QAAQ,WAAW,WAAW;AACvD,QAAM,iBAAiB,QAAQ,WAAW,kBAAkB;AAC5D,QAAM,aAAa,QAAQ,WAAW,cAAc;AACpD,QAAM,SAAS,QAAQ,WAAW,UAAU;AAC5C,QAAM,mBAAmB,QAAQ,oBAAoB;AAErD,MAAI,SAA+B;AACnC,MAAI,WAAW;AACf,MAAI,WAAW;AACf,MAAI,oBAAoB;AACxB,MAAI,iBAAwC;AAC5C,MAAI,iBAAuC;AAC3C,MAAI,iBAAsC;AAC1C,MAAI,gBAAiD;AACrD,MAAI,mBAA0C;AAE9C,QAAM,kBAAkB,oBAAI,IAA4B;AACxD,QAAM,gBAAgB,oBAAI,IAA+B;AAEzD,WAAS,mBAAyB;AAChC,QAAI,kBAAkB;AACpB,mBAAa,gBAAgB;AAC7B,yBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,WAAS,iBAAuB;AAC9B,qBAAiB;AACjB,qBAAiB;AACjB,qBAAiB;AACjB,oBAAgB;AAChB,qBAAiB;AAAA,EACnB;AAEA,WAAS,cAAc,SAAuB;AAC5C,qBAAiB;AACjB,QAAI,eAAe;AACjB,oBAAc,IAAI,MAAM,OAAO,CAAC;AAAA,IAClC;AACA,qBAAiB;AACjB,oBAAgB;AAChB,qBAAiB;AAAA,EACnB;AAEA,WAAS,QAAQ,MAAoB;AACnC,QAAI,CAAC,UAAU,OAAO,eAAe,GAAG;AACtC,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,WAAO,KAAK,IAAI;AAAA,EAClB;AAEA,WAAS,iBAAiB,QAAsB;AAC9C,eAAW,CAAC,WAAW,OAAO,KAAK,gBAAgB,QAAQ,GAAG;AAC5D,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,OAAO,IAAI,MAAM,MAAM,CAAC;AAChC,sBAAgB,OAAO,SAAS;AAAA,IAClC;AAAA,EACF;AAEA,WAAS,oBAA0B;AACjC,QAAI,YAAY,CAAC,oBAAoB,gBAAgB;AACnD;AAAA,IACF;AACA,UAAM,QAAQ,gBAAgB,mBAAmB,EAAE,gBAAgB,YAAY,OAAO,CAAC;AACvF,yBAAqB;AACrB,qBAAiB,WAAW,MAAM;AAChC,uBAAiB;AACjB,WAAK,QAAQ,EAAE,MAAM,CAAC,UAAU;AAC9B,eAAO,KAAK,6BAA6B,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,MACnE,CAAC;AAAA,IACH,GAAG,KAAK;AAAA,EACV;AAEA,WAAS,YAAY,MAAc,SAAkB,MAAuB;AAC1E,UAAM,WAAW,cAAc,IAAI,IAAI;AACvC,QAAI,CAAC,YAAY,SAAS,SAAS,GAAG;AACpC;AAAA,IACF;AACA,eAAW,WAAW,UAAU;AAC9B,UAAI;AACF,gBAAQ,SAAS,IAAI;AAAA,MACvB,SAAS,OAAO;AACd,eAAO,KAAK,iCAAiC,EAAE,MAAM,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,UAAyB;AACtC,QAAI,UAAU;AACZ;AAAA,IACF;AACA,QAAI,UAAU,OAAO,eAAe,KAAK,UAAU;AACjD;AAAA,IACF;AACA,QAAI,gBAAgB;AAClB,aAAO;AAAA,IACT;AAEA,qBAAiB,IAAI,QAAc,CAAC,SAAS,WAAW;AACtD,uBAAiB;AACjB,sBAAgB;AAAA,IAClB,CAAC;AAED,aAAS,QAAQ,mBACZ,QAAQ,iBAAiB,QAAQ,GAAG,IACrC,oBAAoB,QAAQ,GAAG;AAEnC,WAAO,GAAG,QAAQ,MAAM;AACtB,0BAAoB;AACpB,iBAAW;AACX,YAAM,QAAQ,aAAa,cAAc,EAAE,QAAQ,QAAQ,OAAO,CAAC;AACnE,cAAQ,KAAK,kBAAkB,KAAK,CAAC;AACrC,yBAAmB,WAAW,MAAM;AAClC,YAAI,CAAC,UAAU;AACb,wBAAc,2BAA2B;AACzC,kBAAQ,MAAM;AAAA,QAChB;AAAA,MACF,GAAG,gBAAgB;AAAA,IACrB,CAAC;AAED,WAAO,GAAG,WAAW,CAAC,QAAQ;AAC5B,UAAI;AACF,cAAM,aAAa,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;AAC7D,cAAM,WAAW,cAAc,UAAU;AACzC,gBAAQ,SAAS,MAAM;AAAA,UACrB,KAAK;AACH,uBAAW;AACX,2BAAe;AACf;AAAA,UACF,KAAK,cAAc;AACjB,kBAAM,UAAU,SAAS;AACzB,mBAAO,MAAM,uCAAuC;AAAA,cAClD,MAAM,QAAQ;AAAA,cACd,SAAS,QAAQ;AAAA,YACnB,CAAC;AACD,0BAAc,QAAQ,OAAO;AAC7B,6BAAiB,kCAAkC;AACnD,oBAAQ,MAAM;AACd;AAAA,UACF;AAAA,UACA,KAAK;AAAA,UACL,KAAK,iBAAiB;AACpB,kBAAM,YAAY,SAAS;AAC3B,gBAAI,CAAC,WAAW;AACd;AAAA,YACF;AACA,kBAAM,UAAU,gBAAgB,IAAI,SAAS;AAC7C,gBAAI,CAAC,SAAS;AACZ;AAAA,YACF;AACA,yBAAa,QAAQ,KAAK;AAC1B,oBAAQ,QAAQ,SAAS,OAA+B;AACxD,4BAAgB,OAAO,SAAS;AAChC;AAAA,UACF;AAAA,UACA,KAAK,cAAc;AACjB,kBAAM,UAAU,SAAS;AACzB,kBAAM,OAAkB,EAAE,IAAI,SAAS,GAAG;AAC1C,gBAAI,SAAS,QAAQ;AACnB,mBAAK,SAAS,SAAS;AAAA,YACzB;AACA,wBAAY,QAAQ,MAAM,QAAQ,MAAM,IAAI;AAC5C;AAAA,UACF;AAAA,UACA,KAAK;AACH,oBAAQ,kBAAkB,aAAa,QAAQ,CAAC,CAAC,CAAC,CAAC;AACnD;AAAA,UACF;AACE;AAAA,QACJ;AAAA,MACF,SAAS,OAAO;AACd,eAAO,KAAK,qCAAqC,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,MAC3E;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACvB,oBAAc,8BAA8B;AAC5C,iBAAW;AACX,UAAI,CAAC,UAAU;AACb,0BAAkB;AAAA,MACpB;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,UAAU;AAC5B,aAAO,KAAK,0BAA0B,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,IAChE,CAAC;AAED,WAAO;AAAA,EACT;AAEA,OAAK,QAAQ,EAAE,MAAM,CAAC,UAAU;AAC9B,WAAO,KAAK,sCAAsC,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,EAC5E,CAAC;AAED,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,KAAK,MAAM,SAAS,aAAa;AACrC,wBAAkB,WAAW,IAAI;AACjC,wBAAkB,WAAW,MAAM,OAAO;AAC1C,UAAI,CAAC,UAAU;AACb,YAAI;AACF,gBAAM,QAAQ;AAAA,QAChB,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,WAAW,aAAa,aAAa;AAAA,YACrC,IAAI,KAAK,IAAI;AAAA,YACb,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,YACpD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,UAAU,OAAO,eAAe,GAAG;AACtC,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,WAAW,aAAa,aAAa;AAAA,UACrC,IAAI,KAAK,IAAI;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAEA,YAAM,YAAY,aAAa,aAAa,gBAAgB;AAC5D,YAAM,YAAY,aAAa,aAAa;AAE5C,YAAM,UAAU,IAAI,QAAuB,CAAC,SAAS,WAAW;AAC9D,cAAM,QAAQ,WAAW,MAAM;AAC7B,0BAAgB,OAAO,SAAS;AAChC,iBAAO,IAAI,MAAM,YAAY,IAAI,qBAAqB,SAAS,KAAK,CAAC;AAAA,QACvE,GAAG,SAAS;AAEZ,wBAAgB,IAAI,WAAW,EAAE,SAAS,QAAQ,MAAM,CAAC;AAAA,MAC3D,CAAC;AAED;AAAA,QACE;AAAA,UACE;AAAA,YACE;AAAA,YACA;AAAA,cACE;AAAA,cACA,MAAM;AAAA,YACR;AAAA,YACA,EAAE,UAAU;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AACF,eAAO,MAAM;AAAA,MACf,SAAS,OAAO;AACd,eAAO;AAAA,UACL,IAAI;AAAA,UACJ;AAAA,UACA,IAAI,KAAK,IAAI;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,GAAG,MAAM,SAAS;AAChB,wBAAkB,SAAS,IAAI;AAC/B,YAAM,SAAS;AACf,YAAM,WAAW,cAAc,IAAI,IAAI;AACvC,UAAI,UAAU;AACZ,iBAAS,IAAI,MAAM;AAAA,MACrB,OAAO;AACL,sBAAc,IAAI,MAAM,oBAAI,IAAkB,CAAC,MAAM,CAAC,CAAC;AAAA,MACzD;AACA,aAAO,MAAM;AACX,cAAM,WAAW,cAAc,IAAI,IAAI;AACvC,YAAI,CAAC,UAAU;AACb;AAAA,QACF;AACA,iBAAS,OAAO,MAAM;AACtB,YAAI,SAAS,SAAS,GAAG;AACvB,wBAAc,OAAO,IAAI;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAAA,IACA,IAAI,MAAM,SAAS;AACjB,wBAAkB,SAAS,IAAI;AAC/B,YAAM,WAAW,cAAc,IAAI,IAAI;AACvC,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AACA,eAAS,OAAO,OAAuB;AACvC,UAAI,SAAS,SAAS,GAAG;AACvB,sBAAc,OAAO,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,YAAY;AACV,aAAO,QAAQ,UAAU,OAAO,eAAe,KAAK,QAAQ;AAAA,IAC9D;AAAA,IACA,MAAM,QAAQ;AACZ,iBAAW;AACX,iBAAW;AACX,oBAAc,qCAAqC;AACnD,UAAI,gBAAgB;AAClB,qBAAa,cAAc;AAC3B,yBAAiB;AAAA,MACnB;AACA,uBAAiB,qCAAqC;AACtD,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AACA,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,cAAM,UAAU;AAChB,YAAI,CAAC,SAAS;AACZ,kBAAQ;AACR;AAAA,QACF;AACA,gBAAQ,KAAK,SAAS,MAAM,QAAQ,CAAC;AACrC,gBAAQ,MAAM;AAAA,MAChB,CAAC;AACD,eAAS;AAAA,IACX;AAAA,EACF;AACF;;;ACjWA,eAAsB,qBACpB,SAC2B;AAC3B,MAAI,QAAQ,QAAQ;AAClB,WAAO,EAAE,QAAQ,QAAQ,QAAQ,OAAO,MAAM;AAAA,EAChD;AAEA,MAAI,CAAC,QAAQ,OAAO;AAClB,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AAEA,QAAM,gBAAgB,MAAM,OAAO,YAAY;AAC/C,QAAM,UAAU,IAAI,cAAc,OAAO;AAAA,IACvC,SAAS,CAAC;AAAA,EACZ,CAAC;AACD,QAAM,QAAQ,MAAM,QAAQ,KAAK;AACjC,SAAO,EAAE,QAAQ,SAAyC,OAAO,KAAK;AACxE;;;ACxBA,eAAsB,YACpB,SACA,WACA,iBAAiB,wBACL;AACZ,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,IAAI,MAAM,cAAc,CAAC;AAAA,IAClC,GAAG,SAAS;AAEZ,YACG,KAAK,CAAC,UAAU;AACf,mBAAa,OAAO;AACpB,cAAQ,KAAK;AAAA,IACf,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,mBAAa,OAAO;AACpB,aAAO,KAAK;AAAA,IACd,CAAC;AAAA,EACL,CAAC;AACH;AAEO,IAAM,cAAN,MAAqB;AAAA,EAG1B,YAA6B,OAAe;AAAf;AAAA,EAAgB;AAAA,EAAhB;AAAA,EAFZ,QAAQ,oBAAI,IAA6C;AAAA,EAI1E,IAAI,KAA4B;AAC9B,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AACA,QAAI,MAAM,aAAa,KAAK,IAAI,GAAG;AACjC,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,IAAI,KAAa,OAAgB;AAC/B,SAAK,MAAM,IAAI,KAAK,EAAE,OAAO,WAAW,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC;AAAA,EACnE;AACF;;;AC1CA,IAAAA,aAAgD;;;ACAhD,IAAAC,sBAAgC;AAEzB,SAAS,cAAc,UAAkB,UAA2B;AACzE,QAAM,iBAAiB,OAAO,KAAK,QAAQ;AAC3C,QAAM,iBAAiB,OAAO,KAAK,QAAQ;AAE3C,MAAI,eAAe,WAAW,eAAe,QAAQ;AACnD,WAAO;AAAA,EACT;AAEA,aAAO,qCAAgB,gBAAgB,cAAc;AACvD;;;ADMA,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,wBAAwB;AAYvB,IAAM,sBAAN,MAA0B;AAAA,EAQ/B,YAA6B,QAA0B;AAA1B;AAC3B,UAAM,eAAe,OAAO,QAAQ;AACpC,SAAK,cAAc,aAAa,eAAe;AAC/C,SAAK,SAAS,WAAW,OAAO,QAAQ,MAAM;AAE9C,SAAK,MAAM,IAAI,2BAAgB;AAAA,MAC7B,MAAM,aAAa;AAAA,MACnB,MAAM,aAAa;AAAA,MACnB,MAAM,aAAa,QAAQ;AAAA,MAC3B,YAAY,aAAa,mBAAmB;AAAA,IAC9C,CAAC;AAED,SAAK,IAAI,GAAG,cAAc,CAAC,QAAQ,YAAY,KAAK,iBAAiB,QAAQ,OAAO,CAAC;AACrF,SAAK,IAAI;AAAA,MAAG;AAAA,MAAS,CAAC,UACpB,KAAK,OAAO,MAAM,gCAAgC,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,IAC5E;AAEA,SAAK,WAAW,YAAY,MAAM;AAChC,WAAK,gBAAgB;AAAA,IACvB,GAAG,KAAK,WAAW;AAAA,EACrB;AAAA,EApB6B;AAAA,EAPZ;AAAA,EACA,cAAc,oBAAI,IAAoC;AAAA,EACtD;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EAwBjB,UAAU,MAAc,MAAe,QAAuB;AAC5D,UAAM,WAAW;AAAA,MACf;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,MACb,SAAS,EAAE,OAAO,IAAI;AAAA,IACxB;AACA,UAAM,MAAM,kBAAkB,QAAQ;AAEtC,eAAW,SAAS,KAAK,YAAY,OAAO,GAAG;AAC7C,UAAI,CAAC,MAAM,eAAe;AACxB;AAAA,MACF;AACA,WAAK,SAAS,MAAM,QAAQ,GAAG;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,kBAAc,KAAK,QAAQ;AAC3B,eAAW,cAAc,KAAK,YAAY,OAAO,GAAG;AAClD,iBAAW,OAAO,MAAM;AAAA,IAC1B;AACA,SAAK,YAAY,MAAM;AACvB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAK,IAAI,MAAM,CAAC,UAAU;AACxB,YAAI,OAAO;AACT,iBAAO,KAAK;AACZ;AAAA,QACF;AACA,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAiB,QAAmB,SAAgC;AAC1E,UAAM,YAAY,KAAK,OAAO,QAAQ,OAAO;AAC7C,QAAI,aAAa,UAAU,SAAS,GAAG;AACrC,YAAM,SAAS,QAAQ,QAAQ;AAC/B,UAAI,CAAC,UAAU,CAAC,UAAU,SAAS,MAAM,GAAG;AAC1C,eAAO,MAAM,mBAAmB,qBAAqB;AACrD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAA6B;AAAA,MACjC,IAAI,mBAAmB;AAAA,MACvB;AAAA,MACA,eAAe;AAAA,MACf,iBAAiB,KAAK,IAAI;AAAA,IAC5B;AACA,SAAK,YAAY,IAAI,QAAQ,KAAK;AAElC,UAAM,YAAY,WAAW,MAAM;AACjC,UAAI,CAAC,MAAM,eAAe;AACxB,eAAO,MAAM,qBAAqB,0BAA0B;AAAA,MAC9D;AAAA,IACF,GAAG,KAAK,aAAa;AAErB,WAAO,GAAG,WAAW,OAAO,QAAQ;AAClC,UAAI;AACF,cAAM,SAAS,cAAc,IAAI,SAAS,CAAC;AAC3C,cAAM,KAAK,cAAc,OAAO,MAAM;AAAA,MACxC,SAAS,OAAO;AACd,aAAK,OAAO,KAAK,wCAAwC,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AACjF,eAAO,MAAM,uBAAuB,kBAAkB;AAAA,MACxD;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACvB,mBAAa,SAAS;AACtB,WAAK,YAAY,OAAO,MAAM;AAAA,IAChC,CAAC;AAED,WAAO;AAAA,MAAG;AAAA,MAAS,CAAC,UAClB,KAAK,OAAO,KAAK,iBAAiB,EAAE,cAAc,MAAM,IAAI,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,IACpF;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,OAA4B,UAAuC;AAC7F,QAAI,SAAS,SAAS,QAAQ;AAC5B,YAAM,kBAAkB,KAAK,IAAI;AACjC,WAAK,SAAS,MAAM,QAAQ,kBAAkB,aAAa,QAAQ,CAAC,CAAC,CAAC,CAAC;AACvE;AAAA,IACF;AACA,QAAI,SAAS,SAAS,QAAQ;AAC5B,YAAM,kBAAkB,KAAK,IAAI;AACjC;AAAA,IACF;AAEA,QAAI,CAAC,MAAM,eAAe;AACxB,UAAI,SAAS,SAAS,cAAc;AAClC,cAAM,OAAO,MAAM,qBAAqB,0BAA0B;AAClE;AAAA,MACF;AAEA,YAAM,UAAU,SAAS;AACzB,UAAI,CAAC,SAAS,UAAU,CAAC,cAAc,QAAQ,QAAQ,KAAK,OAAO,QAAQ,OAAO,MAAM,GAAG;AACzF,aAAK;AAAA,UACH,MAAM;AAAA,UACN;AAAA,YACE,aAAa,cAAc;AAAA,cACzB,MAAM;AAAA,cACN,SAAS;AAAA,YACX,CAAC;AAAA,UACH;AAAA,QACF;AACA,cAAM,OAAO,MAAM,mBAAmB,iBAAiB;AACvD;AAAA,MACF;AAEA,YAAM,gBAAgB;AACtB,YAAM,kBAAkB,KAAK,IAAI;AACjC,UAAI,QAAQ,YAAY;AACtB,cAAM,aAAa,QAAQ;AAAA,MAC7B;AACA,WAAK;AAAA,QACH,MAAM;AAAA,QACN,kBAAkB,aAAa,WAAW,EAAE,cAAc,MAAM,GAAG,CAAC,CAAC;AAAA,MACvE;AACA;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,mBAAmB;AACvC,YAAM,UAAU,SAAS;AACzB,UAAI,CAAC,SAAS,aAAa,CAAC,SAAS,MAAM;AACzC,cAAM,UAA0B;AAAA,UAC9B,IAAI;AAAA,UACJ,WAAW,SAAS,aAAa;AAAA,UACjC,IAAI,KAAK,IAAI;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AACA,aAAK;AAAA,UACH,MAAM;AAAA,UACN,kBAAkB,aAAa,iBAAiB,SAAS,EAAE,WAAW,QAAQ,UAAU,CAAC,CAAC;AAAA,QAC5F;AACA;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,KAAK,OAAO;AAAA,QACjC;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AACA,YAAM,eAAe,SAAS,KAAK,mBAAmB;AACtD,WAAK;AAAA,QACH,MAAM;AAAA,QACN,kBAAkB,aAAa,cAAc,UAAU,EAAE,WAAW,SAAS,UAAU,CAAC,CAAC;AAAA,MAC3F;AACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,KAAK,cAAc;AAErC,eAAW,SAAS,KAAK,YAAY,OAAO,GAAG;AAC7C,UAAI,CAAC,MAAM,eAAe;AACxB;AAAA,MACF;AAEA,UAAI,MAAM,MAAM,kBAAkB,WAAW;AAC3C,cAAM,OAAO,UAAU;AACvB,aAAK,YAAY,OAAO,MAAM,MAAM;AACpC;AAAA,MACF;AAEA,WAAK,SAAS,MAAM,QAAQ,kBAAkB,aAAa,QAAQ,CAAC,CAAC,CAAC,CAAC;AAAA,IACzE;AAAA,EACF;AAAA,EAEQ,SAAS,QAAmB,SAAuB;AACzD,QAAI,OAAO,eAAe,GAAG;AAC3B,aAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF;AACF;;;AElOA,IAAM,6BAA6B;AAE5B,SAAS,oBACd,SACA,cAGqB;AACrB,QAAM,kBAAkB,oBAAI,IAA4B;AACxD,QAAM,mBAAmB,QAAQ,OAAO,oBAAoB;AAC5D,QAAM,cAAc,IAAI,YAA6C,mBAAmB,CAAC;AAEzF,QAAM,aAAa,IAAI,oBAAoB;AAAA,IACzC;AAAA,IACA,kBAAkB,OAAO,YAAY,SAAS,WAAW,WAAW;AAClE,YAAM,WAAW,GAAG,SAAS,IAAI,QAAQ,IAAI;AAC7C,YAAM,SAAS,YAAY,IAAI,QAAQ;AACvC,UAAI,QAAQ;AACV,eAAO;AAAA,MACT;AAEA,YAAM,UAAU,gBAAgB,IAAI,QAAQ,IAAI;AAChD,UAAI,CAAC,SAAS;AACZ,cAAM,UAA0B;AAAA,UAC9B,IAAI;AAAA,UACJ;AAAA,UACA,IAAI,KAAK,IAAI;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS,sCAAsC,QAAQ,IAAI;AAAA,UAC7D;AAAA,QACF;AACA,oBAAY,IAAI,UAAU,OAAO;AACjC,eAAO;AAAA,MACT;AAEA,YAAM,UAA0B;AAAA,QAC9B;AAAA,QACA,cAAc,WAAW;AAAA,QACzB,YAAY,KAAK,IAAI;AAAA,MACvB;AACA,UAAI,QAAQ;AACV,gBAAQ,SAAS;AAAA,MACnB;AAEA,UAAI;AACF,cAAM,eAAe,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,OAAO,CAAC;AACnE,cAAM,QAAQ,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA,YAAY,QAAQ,IAAI,qBAAqB,gBAAgB;AAAA,QAC/D;AACA,cAAM,UAA0B;AAAA,UAC9B,IAAI;AAAA,UACJ;AAAA,UACA,IAAI,KAAK,IAAI;AAAA,UACb,MAAM;AAAA,QACR;AACA,oBAAY,IAAI,UAAU,OAAO;AACjC,eAAO;AAAA,MACT,SAAS,OAAO;AACd,cAAM,YAAY,iBAAiB,SAAS,aAAa,KAAK,MAAM,OAAO;AAC3E,cAAM,UAA0B;AAAA,UAC9B,IAAI;AAAA,UACJ;AAAA,UACA,IAAI,KAAK,IAAI;AAAA,UACb,OAAO;AAAA,YACL,MAAM,YAAY,YAAY;AAAA,YAC9B,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UACpD;AAAA,QACF;AACA,oBAAY,IAAI,UAAU,OAAO;AACjC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAM,SAAS;AACvB,wBAAkB,WAAW,IAAI;AACjC,sBAAgB,IAAI,MAAM,OAAyB;AACnD,aAAO,MAAM;AACX,wBAAgB,OAAO,IAAI;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,UAAU,MAAM,SAAS;AACvB,wBAAkB,SAAS,IAAI;AAC/B,wBAAkB,SAAS,MAAM,OAAO;AACxC,iBAAW,UAAU,MAAM,SAAS,QAAQ,IAAI;AAAA,IAClD;AAAA,IACA,UAAU,MAAM,SAAS;AACvB,wBAAkB,SAAS,IAAI;AAC/B,wBAAkB,SAAS,MAAM,OAAO;AACxC,iBAAW,UAAU,MAAM,SAAS,QAAQ,IAAI;AAAA,IAClD;AAAA,IACA,QAAQ;AACN,aAAO,WAAW,MAAM,EAAE,KAAK,YAAY;AACzC,cAAM,cAAc,UAAU;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AZvGA,SAAS,cACP,SAC8B;AAC9B,SAAO,YAAY;AACrB;AAQO,SAAS,gBACd,SAC+C;AAC/C,MAAI,CAAC,cAAc,OAAO,GAAG;AAC3B,0BAAsB,OAAO;AAC7B,WAAO,wBAA8B,OAAO;AAAA,EAC9C;AACA,oBAAkB,OAAO;AAEzB,MAAI;AAEJ,MAAI,CAAC,QAAQ,UAAU,QAAQ,OAAO;AACpC,yBAAqB,qBAAqB,OAAO,EAC9C,KAAK,CAAC,UAAU;AACf,UAAI,CAAC,MAAM,SAAS,CAAC,MAAM,QAAQ;AACjC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,SAAS,MAAM,MAAM,QAAQ,QAAQ;AAAA,MACvC;AAAA,IACF,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,cAAQ,QAAQ,QAAQ,sDAAsD;AAAA,QAC5E,OAAO,OAAO,KAAK;AAAA,MACrB,CAAC;AACD,aAAO;AAAA,IACT,CAAC;AAAA,EACL;AAEA,SAAO,oBAA0B,SAAS;AAAA,IACxC,SAAS,YAAY;AACnB,YAAM,QAAQ,MAAM;AACpB,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF,CAAC;AACH;","names":["import_ws","import_node_crypto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/transport/ws/consumer-socket.ts","../src/utils/id.ts","../src/utils/backoff.ts","../src/utils/logger.ts","../src/core/protocol.ts","../src/runtime/validation.ts","../src/consumer/index.ts","../src/discord/client.ts","../src/runtime/reliability.ts","../src/transport/ws/host-server.ts","../src/runtime/security.ts","../src/host/index.ts"],"sourcesContent":["import { createConsumerShardwire } from \"./consumer\";\nimport { resolveDiscordClient } from \"./discord/client\";\nimport { createHostShardwire } from \"./host\";\nimport { assertConsumerOptions, assertHostOptions } from \"./runtime/validation\";\nimport type {\n CommandMap,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"./core/types\";\n\nfunction isHostOptions<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): options is HostOptions<C, E> {\n return \"server\" in options;\n}\n\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E>,\n): HostShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): HostShardwire<C, E> | ConsumerShardwire<C, E> {\n if (!isHostOptions(options)) {\n assertConsumerOptions(options);\n return createConsumerShardwire<C, E>(options);\n }\n assertHostOptions(options);\n\n let ownedClientPromise: Promise<{ destroy: () => void } | undefined> | undefined;\n\n if (!options.client && options.token) {\n ownedClientPromise = resolveDiscordClient(options)\n .then((state) => {\n if (!state.owned || !state.client) {\n return undefined;\n }\n return {\n destroy: () => state.client?.destroy(),\n };\n })\n .catch((error) => {\n options.logger?.error?.(\"Failed to initialize discord.js client from token.\", {\n error: String(error),\n });\n return undefined;\n });\n }\n\n return createHostShardwire<C, E>(options, {\n onClose: async () => {\n const owned = await ownedClientPromise;\n owned?.destroy();\n },\n });\n}\n\nexport type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandResult,\n CommandSuccess,\n ConsumerOptions,\n ConsumerShardwire,\n DiscordClientLike,\n EventMap,\n EventMeta,\n HostOptions,\n HostShardwire,\n ShardwireLogger,\n Unsubscribe,\n} from \"./core/types\";\n","import { WebSocket } from \"ws\";\n\nexport interface WebSocketLike {\n readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n on(event: \"open\", listener: () => void): void;\n on(event: \"message\", listener: (data: unknown) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: \"error\", listener: (error: unknown) => void): void;\n once(event: \"close\", listener: () => void): void;\n}\n\nexport function createNodeWebSocket(url: string): WebSocketLike {\n return new WebSocket(url) as unknown as WebSocketLike;\n}\n","import { randomUUID } from \"node:crypto\";\n\nexport function createRequestId(): string {\n return randomUUID();\n}\n\nexport function createConnectionId(): string {\n return randomUUID();\n}\n","export interface BackoffConfig {\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n}\n\nexport function getBackoffDelay(attempt: number, config: BackoffConfig): number {\n const base = Math.min(config.maxDelayMs, config.initialDelayMs * 2 ** attempt);\n if (!config.jitter) {\n return base;\n }\n const spread = Math.floor(base * 0.2);\n const min = Math.max(0, base - spread);\n const max = base + spread;\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","import type { ShardwireLogger } from \"../core/types\";\n\nexport function withLogger(logger?: ShardwireLogger): Required<ShardwireLogger> {\n return {\n debug: logger?.debug ?? (() => undefined),\n info: logger?.info ?? (() => undefined),\n warn: logger?.warn ?? (() => undefined),\n error: logger?.error ?? (() => undefined),\n };\n}\n","import type { CommandResult } from \"./types\";\n\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type WireType =\n | \"auth.hello\"\n | \"auth.ok\"\n | \"auth.error\"\n | \"command.request\"\n | \"command.result\"\n | \"command.error\"\n | \"event.emit\"\n | \"ping\"\n | \"pong\";\n\nexport type WireEnvelope<TType extends WireType = WireType, TPayload = unknown> = {\n v: typeof PROTOCOL_VERSION;\n type: TType;\n ts: number;\n requestId?: string;\n source?: string;\n payload: TPayload;\n};\n\nexport interface AuthHelloPayload {\n secret: string;\n secretId?: string;\n clientName?: string;\n}\n\nexport interface AuthOkPayload {\n connectionId: string;\n}\n\nexport interface AuthErrorPayload {\n code: \"UNAUTHORIZED\";\n reason: \"unknown_secret_id\" | \"invalid_secret\";\n message: string;\n}\n\nexport interface CommandRequestPayload {\n name: string;\n data: unknown;\n}\n\nexport interface EventEmitPayload {\n name: string;\n data: unknown;\n}\n\nexport type CommandResultPayload = CommandResult;\n\nexport function makeEnvelope<TType extends WireType, TPayload>(\n type: TType,\n payload: TPayload,\n extras?: { requestId?: string; source?: string },\n): WireEnvelope<TType, TPayload> {\n const envelope: WireEnvelope<TType, TPayload> = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n payload,\n };\n if (extras?.requestId) {\n envelope.requestId = extras.requestId;\n }\n if (extras?.source) {\n envelope.source = extras.source;\n }\n return envelope;\n}\n\nexport function parseEnvelope(raw: string): WireEnvelope {\n const parsed = JSON.parse(raw) as WireEnvelope;\n if (!parsed || parsed.v !== PROTOCOL_VERSION || typeof parsed.type !== \"string\") {\n throw new Error(\"Invalid wire envelope.\");\n }\n return parsed;\n}\n\nexport function stringifyEnvelope(envelope: WireEnvelope): string {\n return JSON.stringify(envelope);\n}\n","import type { ConsumerOptions, HostOptions } from \"../core/types\";\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction assertPositiveNumber(name: string, value: unknown): void {\n if (typeof value !== \"number\" || Number.isNaN(value) || value <= 0) {\n throw new Error(`${name} must be a positive number.`);\n }\n}\n\nexport function assertHostOptions(options: HostOptions<any, any>): void {\n if (!options.server) {\n throw new Error(\"Host mode requires a server configuration.\");\n }\n assertPositiveNumber(\"server.port\", options.server.port);\n if (!Array.isArray(options.server.secrets) || options.server.secrets.length === 0) {\n throw new Error(\"server.secrets must contain at least one secret.\");\n }\n for (const [index, secret] of options.server.secrets.entries()) {\n if (!isNonEmptyString(secret)) {\n throw new Error(`server.secrets[${index}] must be a non-empty string.`);\n }\n }\n if (\n options.server.primarySecretId !== undefined &&\n !options.server.secrets.some((_, index) => options.server.primarySecretId === `s${index}`)\n ) {\n throw new Error(\"server.primarySecretId must reference an existing secret id.\");\n }\n if (options.server.heartbeatMs !== undefined) {\n assertPositiveNumber(\"server.heartbeatMs\", options.server.heartbeatMs);\n }\n if (options.server.commandTimeoutMs !== undefined) {\n assertPositiveNumber(\"server.commandTimeoutMs\", options.server.commandTimeoutMs);\n }\n if (options.server.maxPayloadBytes !== undefined) {\n assertPositiveNumber(\"server.maxPayloadBytes\", options.server.maxPayloadBytes);\n }\n}\n\nexport function assertConsumerOptions(options: ConsumerOptions<any, any>): void {\n if (!isNonEmptyString(options.url)) {\n throw new Error(\"Consumer mode requires `url`.\");\n }\n if (!isNonEmptyString(options.secret)) {\n throw new Error(\"Consumer mode requires `secret`.\");\n }\n if (options.secretId !== undefined && !isNonEmptyString(options.secretId)) {\n throw new Error(\"Consumer option `secretId` must be a non-empty string.\");\n }\n if (options.requestTimeoutMs !== undefined) {\n assertPositiveNumber(\"requestTimeoutMs\", options.requestTimeoutMs);\n }\n if (options.reconnect?.initialDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.initialDelayMs\", options.reconnect.initialDelayMs);\n }\n if (options.reconnect?.maxDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.maxDelayMs\", options.reconnect.maxDelayMs);\n }\n}\n\nexport function assertMessageName(kind: \"command\" | \"event\", name: string): void {\n if (!isNonEmptyString(name)) {\n throw new Error(`${kind} name must be a non-empty string.`);\n }\n}\n\nexport function assertJsonPayload(kind: \"command\" | \"event\", name: string, payload: unknown): void {\n try {\n JSON.stringify(payload);\n } catch {\n throw new Error(`${kind} \"${name}\" payload must be JSON-serializable.`);\n }\n}\n","import type {\n CommandFailure,\n CommandMap,\n CommandResult,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n EventMeta,\n} from \"../core/types\";\nimport { createNodeWebSocket, type WebSocketLike } from \"../transport/ws/consumer-socket\";\nimport { createRequestId } from \"../utils/id\";\nimport { getBackoffDelay } from \"../utils/backoff\";\nimport { withLogger } from \"../utils/logger\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthErrorPayload,\n type CommandResultPayload,\n type EventEmitPayload,\n} from \"../core/protocol\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\ntype EventHandler = (payload: unknown, meta: EventMeta) => void;\n\ninterface PendingRequest {\n resolve: (value: CommandResult) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10000;\n\nexport function createConsumerShardwire<C extends CommandMap, E extends EventMap>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E> {\n const logger = withLogger(options.logger);\n const reconnectEnabled = options.reconnect?.enabled ?? true;\n const initialDelayMs = options.reconnect?.initialDelayMs ?? 500;\n const maxDelayMs = options.reconnect?.maxDelayMs ?? 10000;\n const jitter = options.reconnect?.jitter ?? true;\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n let socket: WebSocketLike | null = null;\n let isClosed = false;\n let isAuthed = false;\n let reconnectAttempts = 0;\n let reconnectTimer: NodeJS.Timeout | null = null;\n let connectPromise: Promise<void> | null = null;\n let connectResolve: (() => void) | null = null;\n let connectReject: ((error: Error) => void) | null = null;\n let authTimeoutTimer: NodeJS.Timeout | null = null;\n\n const pendingRequests = new Map<string, PendingRequest>();\n const eventHandlers = new Map<string, Set<EventHandler>>();\n\n function clearAuthTimeout(): void {\n if (authTimeoutTimer) {\n clearTimeout(authTimeoutTimer);\n authTimeoutTimer = null;\n }\n }\n\n function resolveConnect(): void {\n clearAuthTimeout();\n connectResolve?.();\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function rejectConnect(message: string): void {\n clearAuthTimeout();\n if (connectReject) {\n connectReject(new Error(message));\n }\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function sendRaw(data: string): void {\n if (!socket || socket.readyState !== 1) {\n throw new Error(\"Shardwire consumer is not connected.\");\n }\n socket.send(data);\n }\n\n function rejectAllPending(reason: string): void {\n for (const [requestId, pending] of pendingRequests.entries()) {\n clearTimeout(pending.timer);\n pending.reject(new Error(reason));\n pendingRequests.delete(requestId);\n }\n }\n\n function scheduleReconnect(): void {\n if (isClosed || !reconnectEnabled || reconnectTimer) {\n return;\n }\n const delay = getBackoffDelay(reconnectAttempts, { initialDelayMs, maxDelayMs, jitter });\n reconnectAttempts += 1;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n void connect().catch((error) => {\n logger.warn(\"Reconnect attempt failed.\", { error: String(error) });\n });\n }, delay);\n }\n\n function handleEvent(name: string, payload: unknown, meta: EventMeta): void {\n const handlers = eventHandlers.get(name);\n if (!handlers || handlers.size === 0) {\n return;\n }\n for (const handler of handlers) {\n try {\n handler(payload, meta);\n } catch (error) {\n logger.warn(\"Event handler threw an error.\", { name, error: String(error) });\n }\n }\n }\n\n async function connect(): Promise<void> {\n if (isClosed) {\n return;\n }\n if (socket && socket.readyState === 1 && isAuthed) {\n return;\n }\n if (connectPromise) {\n return connectPromise;\n }\n\n connectPromise = new Promise<void>((resolve, reject) => {\n connectResolve = resolve;\n connectReject = reject;\n });\n\n socket = options.webSocketFactory\n ? (options.webSocketFactory(options.url) as WebSocketLike)\n : createNodeWebSocket(options.url);\n\n socket.on(\"open\", () => {\n reconnectAttempts = 0;\n isAuthed = false;\n const hello = makeEnvelope(\"auth.hello\", {\n secret: options.secret,\n secretId: options.secretId,\n });\n socket?.send(stringifyEnvelope(hello));\n authTimeoutTimer = setTimeout(() => {\n if (!isAuthed) {\n rejectConnect(\"Shardwire auth timed out.\");\n socket?.close();\n }\n }, requestTimeoutMs);\n });\n\n socket.on(\"message\", (raw) => {\n try {\n const serialized = typeof raw === \"string\" ? raw : String(raw);\n const envelope = parseEnvelope(serialized);\n switch (envelope.type) {\n case \"auth.ok\":\n isAuthed = true;\n resolveConnect();\n break;\n case \"auth.error\": {\n const payload = envelope.payload as AuthErrorPayload;\n logger.error(\"Authentication failed for consumer.\", {\n code: payload.code,\n message: payload.message,\n });\n rejectConnect(payload.message);\n rejectAllPending(\"Shardwire authentication failed.\");\n socket?.close();\n break;\n }\n case \"command.result\":\n case \"command.error\": {\n const requestId = envelope.requestId;\n if (!requestId) {\n return;\n }\n const pending = pendingRequests.get(requestId);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.resolve(envelope.payload as CommandResultPayload);\n pendingRequests.delete(requestId);\n break;\n }\n case \"event.emit\": {\n const payload = envelope.payload as EventEmitPayload;\n const meta: EventMeta = { ts: envelope.ts };\n if (envelope.source) {\n meta.source = envelope.source;\n }\n handleEvent(payload.name, payload.data, meta);\n break;\n }\n case \"ping\":\n sendRaw(stringifyEnvelope(makeEnvelope(\"pong\", {})));\n break;\n default:\n break;\n }\n } catch (error) {\n logger.warn(\"Failed to parse consumer message.\", { error: String(error) });\n }\n });\n\n socket.on(\"close\", () => {\n rejectConnect(\"Shardwire connection closed.\");\n isAuthed = false;\n if (!isClosed) {\n scheduleReconnect();\n }\n });\n\n socket.on(\"error\", (error) => {\n logger.warn(\"Consumer socket error.\", { error: String(error) });\n });\n\n return connectPromise;\n }\n\n void connect().catch((error) => {\n logger.warn(\"Initial connection attempt failed.\", { error: String(error) });\n });\n\n return {\n mode: \"consumer\",\n async send(name, payload, sendOptions) {\n assertMessageName(\"command\", name);\n assertJsonPayload(\"command\", name, payload);\n if (!isAuthed) {\n try {\n await connect();\n } catch (error) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"UNAUTHORIZED\",\n message: error instanceof Error ? error.message : \"Failed to authenticate.\",\n },\n } satisfies CommandFailure;\n }\n }\n if (!socket || socket.readyState !== 1) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: \"Not connected to Shardwire host.\",\n },\n } satisfies CommandFailure;\n }\n\n const requestId = sendOptions?.requestId ?? createRequestId();\n const timeoutMs = sendOptions?.timeoutMs ?? requestTimeoutMs;\n\n const promise = new Promise<CommandResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingRequests.delete(requestId);\n reject(new Error(`Command \"${name}\" timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n pendingRequests.set(requestId, { resolve, reject, timer });\n });\n\n sendRaw(\n stringifyEnvelope(\n makeEnvelope(\n \"command.request\",\n {\n name,\n data: payload,\n },\n { requestId },\n ),\n ),\n );\n\n try {\n return await promise;\n } catch (error) {\n return {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: error instanceof Error ? error.message : \"Command request timeout.\",\n },\n } satisfies CommandFailure;\n }\n },\n on(name, handler) {\n assertMessageName(\"event\", name);\n const casted = handler as EventHandler;\n const existing = eventHandlers.get(name);\n if (existing) {\n existing.add(casted);\n } else {\n eventHandlers.set(name, new Set<EventHandler>([casted]));\n }\n return () => {\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(casted);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n };\n },\n off(name, handler) {\n assertMessageName(\"event\", name);\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n },\n connected() {\n return Boolean(socket && socket.readyState === 1 && isAuthed);\n },\n async close() {\n isClosed = true;\n isAuthed = false;\n rejectConnect(\"Shardwire consumer has been closed.\");\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n rejectAllPending(\"Shardwire consumer has been closed.\");\n if (!socket) {\n return;\n }\n await new Promise<void>((resolve) => {\n const current = socket;\n if (!current) {\n resolve();\n return;\n }\n current.once(\"close\", () => resolve());\n current.close();\n });\n socket = null;\n },\n };\n}\n","import type { DiscordClientLike, HostOptions } from \"../core/types\";\n\ninterface OwnedClientState {\n client?: DiscordClientLike;\n owned: boolean;\n}\n\nexport async function resolveDiscordClient<C extends Record<string, unknown>, E extends Record<string, unknown>>(\n options: HostOptions<C, E>,\n): Promise<OwnedClientState> {\n if (options.client) {\n return { client: options.client, owned: false };\n }\n\n if (!options.token) {\n return { owned: false };\n }\n\n const discordModule = await import(\"discord.js\");\n const created = new discordModule.Client({\n intents: [],\n });\n await created.login(options.token);\n return { client: created as unknown as DiscordClientLike, owned: true };\n}\n","export async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutMessage = \"Operation timed out.\",\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(timeoutMessage));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timeout);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n}\n\nexport class DedupeCache<T> {\n private readonly cache = new Map<string, { value: T; expiresAt: number }>();\n\n constructor(private readonly ttlMs: number) {}\n\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n this.cache.delete(key);\n return undefined;\n }\n return entry.value;\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });\n }\n}\n","import { WebSocketServer, type WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthHelloPayload,\n type CommandRequestPayload,\n type EventEmitPayload,\n type WireEnvelope,\n} from \"../../core/protocol\";\nimport type { CommandFailure, CommandSuccess, HostOptions, ShardwireLogger } from \"../../core/types\";\nimport { withLogger } from \"../../utils/logger\";\nimport { createConnectionId } from \"../../utils/id\";\nimport type { HostConnectionState } from \"../../runtime/state\";\nimport { getSecretId, isSecretValid } from \"../../runtime/security\";\n\nconst CLOSE_AUTH_REQUIRED = 4001;\nconst CLOSE_AUTH_FAILED = 4003;\nconst CLOSE_INVALID_PAYLOAD = 4004;\n\ninterface HostServerConfig {\n options: HostOptions<any, any>;\n onCommandRequest: (\n connection: HostConnectionState,\n payload: CommandRequestPayload,\n requestId: string,\n source?: string,\n ) => Promise<CommandSuccess | CommandFailure>;\n}\n\nexport class HostWebSocketServer {\n private readonly wss: WebSocketServer;\n private readonly connections = new Map<WebSocket, HostConnectionState>();\n private readonly logger: Required<ShardwireLogger>;\n private readonly heartbeatMs: number;\n private readonly authTimeoutMs = 5000;\n private readonly interval: NodeJS.Timeout;\n\n constructor(private readonly config: HostServerConfig) {\n const serverConfig = config.options.server;\n this.heartbeatMs = serverConfig.heartbeatMs ?? 30000;\n this.logger = withLogger(config.options.logger);\n\n this.wss = new WebSocketServer({\n host: serverConfig.host,\n port: serverConfig.port,\n path: serverConfig.path ?? \"/shardwire\",\n maxPayload: serverConfig.maxPayloadBytes ?? 65536,\n });\n\n this.wss.on(\"connection\", (socket, request) => this.handleConnection(socket, request));\n this.wss.on(\"error\", (error) =>\n this.logger.error(\"Shardwire host server error.\", { error: String(error) }),\n );\n\n this.interval = setInterval(() => {\n this.checkHeartbeats();\n }, this.heartbeatMs);\n }\n\n emitEvent(name: string, data: unknown, source?: string): void {\n const envelope = makeEnvelope(\n \"event.emit\",\n { name, data } satisfies EventEmitPayload,\n source ? { source } : undefined,\n );\n const raw = stringifyEnvelope(envelope);\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n this.safeSend(state.socket, raw);\n }\n }\n\n async close(): Promise<void> {\n clearInterval(this.interval);\n for (const connection of this.connections.values()) {\n connection.socket.close();\n }\n this.connections.clear();\n await new Promise<void>((resolve, reject) => {\n this.wss.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n private handleConnection(socket: WebSocket, request: IncomingMessage): void {\n const allowlist = this.config.options.server.corsOrigins;\n if (allowlist && allowlist.length > 0) {\n const origin = request.headers.origin;\n if (!origin || !allowlist.includes(origin)) {\n socket.close(CLOSE_AUTH_FAILED, \"Origin not allowed.\");\n return;\n }\n }\n\n const state: HostConnectionState = {\n id: createConnectionId(),\n socket,\n authenticated: false,\n lastHeartbeatAt: Date.now(),\n };\n this.connections.set(socket, state);\n\n const authTimer = setTimeout(() => {\n if (!state.authenticated) {\n socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n }\n }, this.authTimeoutMs);\n\n socket.on(\"message\", async (raw) => {\n try {\n const parsed = parseEnvelope(raw.toString());\n await this.handleMessage(state, parsed);\n } catch (error) {\n this.logger.warn(\"Invalid message payload from client.\", { error: String(error) });\n socket.close(CLOSE_INVALID_PAYLOAD, \"Invalid payload.\");\n }\n });\n\n socket.on(\"close\", () => {\n clearTimeout(authTimer);\n this.connections.delete(socket);\n });\n\n socket.on(\"error\", (error) =>\n this.logger.warn(\"Socket error.\", { connectionId: state.id, error: String(error) }),\n );\n }\n\n private async handleMessage(state: HostConnectionState, envelope: WireEnvelope): Promise<void> {\n if (envelope.type === \"ping\") {\n state.lastHeartbeatAt = Date.now();\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"pong\", {})));\n return;\n }\n if (envelope.type === \"pong\") {\n state.lastHeartbeatAt = Date.now();\n return;\n }\n\n if (!state.authenticated) {\n if (envelope.type !== \"auth.hello\") {\n state.socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n return;\n }\n\n const payload = envelope.payload as AuthHelloPayload;\n const providedSecret = payload?.secret;\n const knownSecrets = this.config.options.server.secrets;\n const secretId = payload?.secretId;\n let authReason: \"unknown_secret_id\" | \"invalid_secret\" | null = null;\n\n if (!providedSecret) {\n authReason = \"invalid_secret\";\n } else if (secretId) {\n const secretIndex = knownSecrets.findIndex((_, index) => getSecretId(index) === secretId);\n if (secretIndex < 0) {\n authReason = \"unknown_secret_id\";\n } else {\n const expectedSecret = knownSecrets[secretIndex];\n if (!expectedSecret || !isSecretValid(providedSecret, expectedSecret)) {\n authReason = \"invalid_secret\";\n }\n }\n } else if (!knownSecrets.some((secret) => isSecretValid(providedSecret, secret))) {\n authReason = \"invalid_secret\";\n }\n\n if (authReason) {\n this.safeSend(\n state.socket,\n stringifyEnvelope(\n makeEnvelope(\"auth.error\", {\n code: \"UNAUTHORIZED\",\n reason: authReason,\n message: \"Authentication failed.\",\n }),\n ),\n );\n state.socket.close(CLOSE_AUTH_FAILED, \"Invalid secret.\");\n return;\n }\n\n state.authenticated = true;\n state.lastHeartbeatAt = Date.now();\n if (payload.clientName) {\n state.clientName = payload.clientName;\n }\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"auth.ok\", { connectionId: state.id })),\n );\n return;\n }\n\n if (envelope.type === \"command.request\") {\n const payload = envelope.payload as CommandRequestPayload;\n if (!envelope.requestId || !payload?.name) {\n const invalid: CommandFailure = {\n ok: false,\n requestId: envelope.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"VALIDATION_ERROR\",\n message: \"Invalid command request envelope.\",\n },\n };\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"command.error\", invalid, { requestId: invalid.requestId })),\n );\n return;\n }\n\n const response = await this.config.onCommandRequest(\n state,\n payload,\n envelope.requestId,\n envelope.source,\n );\n const responseType = response.ok ? \"command.result\" : \"command.error\";\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(responseType, response, { requestId: response.requestId })),\n );\n return;\n }\n }\n\n private checkHeartbeats(): void {\n const now = Date.now();\n const threshold = this.heartbeatMs * 2;\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n\n if (now - state.lastHeartbeatAt > threshold) {\n state.socket.terminate();\n this.connections.delete(state.socket);\n continue;\n }\n\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"ping\", {})));\n }\n }\n\n private safeSend(socket: WebSocket, payload: string): void {\n if (socket.readyState === 1) {\n socket.send(payload);\n }\n }\n}\n","import { timingSafeEqual } from \"node:crypto\";\n\nexport function isSecretValid(provided: string, expected: string): boolean {\n const providedBuffer = Buffer.from(provided);\n const expectedBuffer = Buffer.from(expected);\n\n if (providedBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(providedBuffer, expectedBuffer);\n}\n\nexport function getSecretId(secretIndex: number): string {\n return `s${secretIndex}`;\n}\n","import type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandSuccess,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"../core/types\";\nimport { withTimeout, DedupeCache } from \"../runtime/reliability\";\nimport type { CommandHandler } from \"../runtime/state\";\nimport { HostWebSocketServer } from \"../transport/ws/host-server\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 10000;\n\nexport function createHostShardwire<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E>,\n runtimeHooks?: {\n onClose?: () => Promise<void> | void;\n },\n): HostShardwire<C, E> {\n const commandHandlers = new Map<string, CommandHandler>();\n const commandTimeoutMs = options.server.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;\n const dedupeCache = new DedupeCache<CommandSuccess | CommandFailure>(commandTimeoutMs * 2);\n\n const hostServer = new HostWebSocketServer({\n options,\n onCommandRequest: async (connection, payload, requestId, source) => {\n const cacheKey = `${requestId}:${payload.name}`;\n const cached = dedupeCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const handler = commandHandlers.get(payload.name);\n if (!handler) {\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"COMMAND_NOT_FOUND\",\n message: `No command handler registered for \"${payload.name}\".`,\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n\n const context: CommandContext = {\n requestId,\n connectionId: connection.id,\n receivedAt: Date.now(),\n };\n if (source) {\n context.source = source;\n }\n\n try {\n const maybePromise = Promise.resolve(handler(payload.data, context));\n const value = await withTimeout(\n maybePromise,\n commandTimeoutMs,\n `Command \"${payload.name}\" timed out after ${commandTimeoutMs}ms.`,\n );\n const success: CommandSuccess = {\n ok: true,\n requestId,\n ts: Date.now(),\n data: value,\n };\n dedupeCache.set(cacheKey, success);\n return success;\n } catch (error) {\n const isTimeout = error instanceof Error && /timed out/i.test(error.message);\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: isTimeout ? \"TIMEOUT\" : \"INTERNAL_ERROR\",\n message: error instanceof Error ? error.message : \"Unknown command execution error.\",\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n },\n });\n\n return {\n mode: \"host\",\n onCommand(name, handler) {\n assertMessageName(\"command\", name);\n commandHandlers.set(name, handler as CommandHandler);\n return () => {\n commandHandlers.delete(name);\n };\n },\n emitEvent(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n broadcast(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n close() {\n return hostServer.close().then(async () => {\n await runtimeHooks?.onClose?.();\n });\n },\n };\n}\n"],"mappings":"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","names":["import_ws","import_node_crypto"]}
|
package/dist/index.mjs
CHANGED
|
@@ -77,8 +77,16 @@ function assertHostOptions(options) {
|
|
|
77
77
|
throw new Error("Host mode requires a server configuration.");
|
|
78
78
|
}
|
|
79
79
|
assertPositiveNumber("server.port", options.server.port);
|
|
80
|
-
if (!
|
|
81
|
-
throw new Error("server.
|
|
80
|
+
if (!Array.isArray(options.server.secrets) || options.server.secrets.length === 0) {
|
|
81
|
+
throw new Error("server.secrets must contain at least one secret.");
|
|
82
|
+
}
|
|
83
|
+
for (const [index, secret] of options.server.secrets.entries()) {
|
|
84
|
+
if (!isNonEmptyString(secret)) {
|
|
85
|
+
throw new Error(`server.secrets[${index}] must be a non-empty string.`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (options.server.primarySecretId !== void 0 && !options.server.secrets.some((_, index) => options.server.primarySecretId === `s${index}`)) {
|
|
89
|
+
throw new Error("server.primarySecretId must reference an existing secret id.");
|
|
82
90
|
}
|
|
83
91
|
if (options.server.heartbeatMs !== void 0) {
|
|
84
92
|
assertPositiveNumber("server.heartbeatMs", options.server.heartbeatMs);
|
|
@@ -97,6 +105,9 @@ function assertConsumerOptions(options) {
|
|
|
97
105
|
if (!isNonEmptyString(options.secret)) {
|
|
98
106
|
throw new Error("Consumer mode requires `secret`.");
|
|
99
107
|
}
|
|
108
|
+
if (options.secretId !== void 0 && !isNonEmptyString(options.secretId)) {
|
|
109
|
+
throw new Error("Consumer option `secretId` must be a non-empty string.");
|
|
110
|
+
}
|
|
100
111
|
if (options.requestTimeoutMs !== void 0) {
|
|
101
112
|
assertPositiveNumber("requestTimeoutMs", options.requestTimeoutMs);
|
|
102
113
|
}
|
|
@@ -219,7 +230,10 @@ function createConsumerShardwire(options) {
|
|
|
219
230
|
socket.on("open", () => {
|
|
220
231
|
reconnectAttempts = 0;
|
|
221
232
|
isAuthed = false;
|
|
222
|
-
const hello = makeEnvelope("auth.hello", {
|
|
233
|
+
const hello = makeEnvelope("auth.hello", {
|
|
234
|
+
secret: options.secret,
|
|
235
|
+
secretId: options.secretId
|
|
236
|
+
});
|
|
223
237
|
socket?.send(stringifyEnvelope(hello));
|
|
224
238
|
authTimeoutTimer = setTimeout(() => {
|
|
225
239
|
if (!isAuthed) {
|
|
@@ -311,7 +325,7 @@ function createConsumerShardwire(options) {
|
|
|
311
325
|
requestId: sendOptions?.requestId ?? "unknown",
|
|
312
326
|
ts: Date.now(),
|
|
313
327
|
error: {
|
|
314
|
-
code: "
|
|
328
|
+
code: "UNAUTHORIZED",
|
|
315
329
|
message: error instanceof Error ? error.message : "Failed to authenticate."
|
|
316
330
|
}
|
|
317
331
|
};
|
|
@@ -489,6 +503,9 @@ function isSecretValid(provided, expected) {
|
|
|
489
503
|
}
|
|
490
504
|
return timingSafeEqual(providedBuffer, expectedBuffer);
|
|
491
505
|
}
|
|
506
|
+
function getSecretId(secretIndex) {
|
|
507
|
+
return `s${secretIndex}`;
|
|
508
|
+
}
|
|
492
509
|
|
|
493
510
|
// src/transport/ws/host-server.ts
|
|
494
511
|
var CLOSE_AUTH_REQUIRED = 4001;
|
|
@@ -607,13 +624,33 @@ var HostWebSocketServer = class {
|
|
|
607
624
|
return;
|
|
608
625
|
}
|
|
609
626
|
const payload = envelope.payload;
|
|
610
|
-
|
|
627
|
+
const providedSecret = payload?.secret;
|
|
628
|
+
const knownSecrets = this.config.options.server.secrets;
|
|
629
|
+
const secretId = payload?.secretId;
|
|
630
|
+
let authReason = null;
|
|
631
|
+
if (!providedSecret) {
|
|
632
|
+
authReason = "invalid_secret";
|
|
633
|
+
} else if (secretId) {
|
|
634
|
+
const secretIndex = knownSecrets.findIndex((_, index) => getSecretId(index) === secretId);
|
|
635
|
+
if (secretIndex < 0) {
|
|
636
|
+
authReason = "unknown_secret_id";
|
|
637
|
+
} else {
|
|
638
|
+
const expectedSecret = knownSecrets[secretIndex];
|
|
639
|
+
if (!expectedSecret || !isSecretValid(providedSecret, expectedSecret)) {
|
|
640
|
+
authReason = "invalid_secret";
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
} else if (!knownSecrets.some((secret) => isSecretValid(providedSecret, secret))) {
|
|
644
|
+
authReason = "invalid_secret";
|
|
645
|
+
}
|
|
646
|
+
if (authReason) {
|
|
611
647
|
this.safeSend(
|
|
612
648
|
state.socket,
|
|
613
649
|
stringifyEnvelope(
|
|
614
650
|
makeEnvelope("auth.error", {
|
|
615
|
-
code: "
|
|
616
|
-
|
|
651
|
+
code: "UNAUTHORIZED",
|
|
652
|
+
reason: authReason,
|
|
653
|
+
message: "Authentication failed."
|
|
617
654
|
})
|
|
618
655
|
)
|
|
619
656
|
);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/ws/consumer-socket.ts","../src/utils/id.ts","../src/utils/backoff.ts","../src/utils/logger.ts","../src/core/protocol.ts","../src/runtime/validation.ts","../src/consumer/index.ts","../src/discord/client.ts","../src/runtime/reliability.ts","../src/transport/ws/host-server.ts","../src/runtime/security.ts","../src/host/index.ts","../src/index.ts"],"sourcesContent":["import { WebSocket } from \"ws\";\n\nexport interface WebSocketLike {\n readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n on(event: \"open\", listener: () => void): void;\n on(event: \"message\", listener: (data: unknown) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: \"error\", listener: (error: unknown) => void): void;\n once(event: \"close\", listener: () => void): void;\n}\n\nexport function createNodeWebSocket(url: string): WebSocketLike {\n return new WebSocket(url) as unknown as WebSocketLike;\n}\n","import { randomUUID } from \"node:crypto\";\n\nexport function createRequestId(): string {\n return randomUUID();\n}\n\nexport function createConnectionId(): string {\n return randomUUID();\n}\n","export interface BackoffConfig {\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n}\n\nexport function getBackoffDelay(attempt: number, config: BackoffConfig): number {\n const base = Math.min(config.maxDelayMs, config.initialDelayMs * 2 ** attempt);\n if (!config.jitter) {\n return base;\n }\n const spread = Math.floor(base * 0.2);\n const min = Math.max(0, base - spread);\n const max = base + spread;\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","import type { ShardwireLogger } from \"../core/types\";\n\nexport function withLogger(logger?: ShardwireLogger): Required<ShardwireLogger> {\n return {\n debug: logger?.debug ?? (() => undefined),\n info: logger?.info ?? (() => undefined),\n warn: logger?.warn ?? (() => undefined),\n error: logger?.error ?? (() => undefined),\n };\n}\n","import type { CommandResult } from \"./types\";\n\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type WireType =\n | \"auth.hello\"\n | \"auth.ok\"\n | \"auth.error\"\n | \"command.request\"\n | \"command.result\"\n | \"command.error\"\n | \"event.emit\"\n | \"ping\"\n | \"pong\";\n\nexport type WireEnvelope<TType extends WireType = WireType, TPayload = unknown> = {\n v: typeof PROTOCOL_VERSION;\n type: TType;\n ts: number;\n requestId?: string;\n source?: string;\n payload: TPayload;\n};\n\nexport interface AuthHelloPayload {\n secret: string;\n clientName?: string;\n}\n\nexport interface AuthOkPayload {\n connectionId: string;\n}\n\nexport interface AuthErrorPayload {\n code: \"AUTH_ERROR\";\n message: string;\n}\n\nexport interface CommandRequestPayload {\n name: string;\n data: unknown;\n}\n\nexport interface EventEmitPayload {\n name: string;\n data: unknown;\n}\n\nexport type CommandResultPayload = CommandResult;\n\nexport function makeEnvelope<TType extends WireType, TPayload>(\n type: TType,\n payload: TPayload,\n extras?: { requestId?: string; source?: string },\n): WireEnvelope<TType, TPayload> {\n const envelope: WireEnvelope<TType, TPayload> = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n payload,\n };\n if (extras?.requestId) {\n envelope.requestId = extras.requestId;\n }\n if (extras?.source) {\n envelope.source = extras.source;\n }\n return envelope;\n}\n\nexport function parseEnvelope(raw: string): WireEnvelope {\n const parsed = JSON.parse(raw) as WireEnvelope;\n if (!parsed || parsed.v !== PROTOCOL_VERSION || typeof parsed.type !== \"string\") {\n throw new Error(\"Invalid wire envelope.\");\n }\n return parsed;\n}\n\nexport function stringifyEnvelope(envelope: WireEnvelope): string {\n return JSON.stringify(envelope);\n}\n","import type { ConsumerOptions, HostOptions } from \"../core/types\";\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction assertPositiveNumber(name: string, value: unknown): void {\n if (typeof value !== \"number\" || Number.isNaN(value) || value <= 0) {\n throw new Error(`${name} must be a positive number.`);\n }\n}\n\nexport function assertHostOptions(options: HostOptions<any, any>): void {\n if (!options.server) {\n throw new Error(\"Host mode requires a server configuration.\");\n }\n assertPositiveNumber(\"server.port\", options.server.port);\n if (!isNonEmptyString(options.server.secret)) {\n throw new Error(\"server.secret is required.\");\n }\n if (options.server.heartbeatMs !== undefined) {\n assertPositiveNumber(\"server.heartbeatMs\", options.server.heartbeatMs);\n }\n if (options.server.commandTimeoutMs !== undefined) {\n assertPositiveNumber(\"server.commandTimeoutMs\", options.server.commandTimeoutMs);\n }\n if (options.server.maxPayloadBytes !== undefined) {\n assertPositiveNumber(\"server.maxPayloadBytes\", options.server.maxPayloadBytes);\n }\n}\n\nexport function assertConsumerOptions(options: ConsumerOptions<any, any>): void {\n if (!isNonEmptyString(options.url)) {\n throw new Error(\"Consumer mode requires `url`.\");\n }\n if (!isNonEmptyString(options.secret)) {\n throw new Error(\"Consumer mode requires `secret`.\");\n }\n if (options.requestTimeoutMs !== undefined) {\n assertPositiveNumber(\"requestTimeoutMs\", options.requestTimeoutMs);\n }\n if (options.reconnect?.initialDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.initialDelayMs\", options.reconnect.initialDelayMs);\n }\n if (options.reconnect?.maxDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.maxDelayMs\", options.reconnect.maxDelayMs);\n }\n}\n\nexport function assertMessageName(kind: \"command\" | \"event\", name: string): void {\n if (!isNonEmptyString(name)) {\n throw new Error(`${kind} name must be a non-empty string.`);\n }\n}\n\nexport function assertJsonPayload(kind: \"command\" | \"event\", name: string, payload: unknown): void {\n try {\n JSON.stringify(payload);\n } catch {\n throw new Error(`${kind} \"${name}\" payload must be JSON-serializable.`);\n }\n}\n","import type {\n CommandFailure,\n CommandMap,\n CommandResult,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n EventMeta,\n} from \"../core/types\";\nimport { createNodeWebSocket, type WebSocketLike } from \"../transport/ws/consumer-socket\";\nimport { createRequestId } from \"../utils/id\";\nimport { getBackoffDelay } from \"../utils/backoff\";\nimport { withLogger } from \"../utils/logger\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthErrorPayload,\n type CommandResultPayload,\n type EventEmitPayload,\n} from \"../core/protocol\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\ntype EventHandler = (payload: unknown, meta: EventMeta) => void;\n\ninterface PendingRequest {\n resolve: (value: CommandResult) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10000;\n\nexport function createConsumerShardwire<C extends CommandMap, E extends EventMap>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E> {\n const logger = withLogger(options.logger);\n const reconnectEnabled = options.reconnect?.enabled ?? true;\n const initialDelayMs = options.reconnect?.initialDelayMs ?? 500;\n const maxDelayMs = options.reconnect?.maxDelayMs ?? 10000;\n const jitter = options.reconnect?.jitter ?? true;\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n let socket: WebSocketLike | null = null;\n let isClosed = false;\n let isAuthed = false;\n let reconnectAttempts = 0;\n let reconnectTimer: NodeJS.Timeout | null = null;\n let connectPromise: Promise<void> | null = null;\n let connectResolve: (() => void) | null = null;\n let connectReject: ((error: Error) => void) | null = null;\n let authTimeoutTimer: NodeJS.Timeout | null = null;\n\n const pendingRequests = new Map<string, PendingRequest>();\n const eventHandlers = new Map<string, Set<EventHandler>>();\n\n function clearAuthTimeout(): void {\n if (authTimeoutTimer) {\n clearTimeout(authTimeoutTimer);\n authTimeoutTimer = null;\n }\n }\n\n function resolveConnect(): void {\n clearAuthTimeout();\n connectResolve?.();\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function rejectConnect(message: string): void {\n clearAuthTimeout();\n if (connectReject) {\n connectReject(new Error(message));\n }\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function sendRaw(data: string): void {\n if (!socket || socket.readyState !== 1) {\n throw new Error(\"Shardwire consumer is not connected.\");\n }\n socket.send(data);\n }\n\n function rejectAllPending(reason: string): void {\n for (const [requestId, pending] of pendingRequests.entries()) {\n clearTimeout(pending.timer);\n pending.reject(new Error(reason));\n pendingRequests.delete(requestId);\n }\n }\n\n function scheduleReconnect(): void {\n if (isClosed || !reconnectEnabled || reconnectTimer) {\n return;\n }\n const delay = getBackoffDelay(reconnectAttempts, { initialDelayMs, maxDelayMs, jitter });\n reconnectAttempts += 1;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n void connect().catch((error) => {\n logger.warn(\"Reconnect attempt failed.\", { error: String(error) });\n });\n }, delay);\n }\n\n function handleEvent(name: string, payload: unknown, meta: EventMeta): void {\n const handlers = eventHandlers.get(name);\n if (!handlers || handlers.size === 0) {\n return;\n }\n for (const handler of handlers) {\n try {\n handler(payload, meta);\n } catch (error) {\n logger.warn(\"Event handler threw an error.\", { name, error: String(error) });\n }\n }\n }\n\n async function connect(): Promise<void> {\n if (isClosed) {\n return;\n }\n if (socket && socket.readyState === 1 && isAuthed) {\n return;\n }\n if (connectPromise) {\n return connectPromise;\n }\n\n connectPromise = new Promise<void>((resolve, reject) => {\n connectResolve = resolve;\n connectReject = reject;\n });\n\n socket = options.webSocketFactory\n ? (options.webSocketFactory(options.url) as WebSocketLike)\n : createNodeWebSocket(options.url);\n\n socket.on(\"open\", () => {\n reconnectAttempts = 0;\n isAuthed = false;\n const hello = makeEnvelope(\"auth.hello\", { secret: options.secret });\n socket?.send(stringifyEnvelope(hello));\n authTimeoutTimer = setTimeout(() => {\n if (!isAuthed) {\n rejectConnect(\"Shardwire auth timed out.\");\n socket?.close();\n }\n }, requestTimeoutMs);\n });\n\n socket.on(\"message\", (raw) => {\n try {\n const serialized = typeof raw === \"string\" ? raw : String(raw);\n const envelope = parseEnvelope(serialized);\n switch (envelope.type) {\n case \"auth.ok\":\n isAuthed = true;\n resolveConnect();\n break;\n case \"auth.error\": {\n const payload = envelope.payload as AuthErrorPayload;\n logger.error(\"Authentication failed for consumer.\", {\n code: payload.code,\n message: payload.message,\n });\n rejectConnect(payload.message);\n rejectAllPending(\"Shardwire authentication failed.\");\n socket?.close();\n break;\n }\n case \"command.result\":\n case \"command.error\": {\n const requestId = envelope.requestId;\n if (!requestId) {\n return;\n }\n const pending = pendingRequests.get(requestId);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.resolve(envelope.payload as CommandResultPayload);\n pendingRequests.delete(requestId);\n break;\n }\n case \"event.emit\": {\n const payload = envelope.payload as EventEmitPayload;\n const meta: EventMeta = { ts: envelope.ts };\n if (envelope.source) {\n meta.source = envelope.source;\n }\n handleEvent(payload.name, payload.data, meta);\n break;\n }\n case \"ping\":\n sendRaw(stringifyEnvelope(makeEnvelope(\"pong\", {})));\n break;\n default:\n break;\n }\n } catch (error) {\n logger.warn(\"Failed to parse consumer message.\", { error: String(error) });\n }\n });\n\n socket.on(\"close\", () => {\n rejectConnect(\"Shardwire connection closed.\");\n isAuthed = false;\n if (!isClosed) {\n scheduleReconnect();\n }\n });\n\n socket.on(\"error\", (error) => {\n logger.warn(\"Consumer socket error.\", { error: String(error) });\n });\n\n return connectPromise;\n }\n\n void connect().catch((error) => {\n logger.warn(\"Initial connection attempt failed.\", { error: String(error) });\n });\n\n return {\n mode: \"consumer\",\n async send(name, payload, sendOptions) {\n assertMessageName(\"command\", name);\n assertJsonPayload(\"command\", name, payload);\n if (!isAuthed) {\n try {\n await connect();\n } catch (error) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"AUTH_ERROR\",\n message: error instanceof Error ? error.message : \"Failed to authenticate.\",\n },\n } satisfies CommandFailure;\n }\n }\n if (!socket || socket.readyState !== 1) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: \"Not connected to Shardwire host.\",\n },\n } satisfies CommandFailure;\n }\n\n const requestId = sendOptions?.requestId ?? createRequestId();\n const timeoutMs = sendOptions?.timeoutMs ?? requestTimeoutMs;\n\n const promise = new Promise<CommandResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingRequests.delete(requestId);\n reject(new Error(`Command \"${name}\" timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n pendingRequests.set(requestId, { resolve, reject, timer });\n });\n\n sendRaw(\n stringifyEnvelope(\n makeEnvelope(\n \"command.request\",\n {\n name,\n data: payload,\n },\n { requestId },\n ),\n ),\n );\n\n try {\n return await promise;\n } catch (error) {\n return {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: error instanceof Error ? error.message : \"Command request timeout.\",\n },\n } satisfies CommandFailure;\n }\n },\n on(name, handler) {\n assertMessageName(\"event\", name);\n const casted = handler as EventHandler;\n const existing = eventHandlers.get(name);\n if (existing) {\n existing.add(casted);\n } else {\n eventHandlers.set(name, new Set<EventHandler>([casted]));\n }\n return () => {\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(casted);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n };\n },\n off(name, handler) {\n assertMessageName(\"event\", name);\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n },\n connected() {\n return Boolean(socket && socket.readyState === 1 && isAuthed);\n },\n async close() {\n isClosed = true;\n isAuthed = false;\n rejectConnect(\"Shardwire consumer has been closed.\");\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n rejectAllPending(\"Shardwire consumer has been closed.\");\n if (!socket) {\n return;\n }\n await new Promise<void>((resolve) => {\n const current = socket;\n if (!current) {\n resolve();\n return;\n }\n current.once(\"close\", () => resolve());\n current.close();\n });\n socket = null;\n },\n };\n}\n","import type { DiscordClientLike, HostOptions } from \"../core/types\";\n\ninterface OwnedClientState {\n client?: DiscordClientLike;\n owned: boolean;\n}\n\nexport async function resolveDiscordClient<C extends Record<string, unknown>, E extends Record<string, unknown>>(\n options: HostOptions<C, E>,\n): Promise<OwnedClientState> {\n if (options.client) {\n return { client: options.client, owned: false };\n }\n\n if (!options.token) {\n return { owned: false };\n }\n\n const discordModule = await import(\"discord.js\");\n const created = new discordModule.Client({\n intents: [],\n });\n await created.login(options.token);\n return { client: created as unknown as DiscordClientLike, owned: true };\n}\n","export async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutMessage = \"Operation timed out.\",\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(timeoutMessage));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timeout);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n}\n\nexport class DedupeCache<T> {\n private readonly cache = new Map<string, { value: T; expiresAt: number }>();\n\n constructor(private readonly ttlMs: number) {}\n\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n this.cache.delete(key);\n return undefined;\n }\n return entry.value;\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });\n }\n}\n","import { WebSocketServer, type WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthHelloPayload,\n type CommandRequestPayload,\n type EventEmitPayload,\n type WireEnvelope,\n} from \"../../core/protocol\";\nimport type { CommandFailure, CommandSuccess, HostOptions, ShardwireLogger } from \"../../core/types\";\nimport { withLogger } from \"../../utils/logger\";\nimport { createConnectionId } from \"../../utils/id\";\nimport type { HostConnectionState } from \"../../runtime/state\";\nimport { isSecretValid } from \"../../runtime/security\";\n\nconst CLOSE_AUTH_REQUIRED = 4001;\nconst CLOSE_AUTH_FAILED = 4003;\nconst CLOSE_INVALID_PAYLOAD = 4004;\n\ninterface HostServerConfig {\n options: HostOptions<any, any>;\n onCommandRequest: (\n connection: HostConnectionState,\n payload: CommandRequestPayload,\n requestId: string,\n source?: string,\n ) => Promise<CommandSuccess | CommandFailure>;\n}\n\nexport class HostWebSocketServer {\n private readonly wss: WebSocketServer;\n private readonly connections = new Map<WebSocket, HostConnectionState>();\n private readonly logger: Required<ShardwireLogger>;\n private readonly heartbeatMs: number;\n private readonly authTimeoutMs = 5000;\n private readonly interval: NodeJS.Timeout;\n\n constructor(private readonly config: HostServerConfig) {\n const serverConfig = config.options.server;\n this.heartbeatMs = serverConfig.heartbeatMs ?? 30000;\n this.logger = withLogger(config.options.logger);\n\n this.wss = new WebSocketServer({\n host: serverConfig.host,\n port: serverConfig.port,\n path: serverConfig.path ?? \"/shardwire\",\n maxPayload: serverConfig.maxPayloadBytes ?? 65536,\n });\n\n this.wss.on(\"connection\", (socket, request) => this.handleConnection(socket, request));\n this.wss.on(\"error\", (error) =>\n this.logger.error(\"Shardwire host server error.\", { error: String(error) }),\n );\n\n this.interval = setInterval(() => {\n this.checkHeartbeats();\n }, this.heartbeatMs);\n }\n\n emitEvent(name: string, data: unknown, source?: string): void {\n const envelope = makeEnvelope(\n \"event.emit\",\n { name, data } satisfies EventEmitPayload,\n source ? { source } : undefined,\n );\n const raw = stringifyEnvelope(envelope);\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n this.safeSend(state.socket, raw);\n }\n }\n\n async close(): Promise<void> {\n clearInterval(this.interval);\n for (const connection of this.connections.values()) {\n connection.socket.close();\n }\n this.connections.clear();\n await new Promise<void>((resolve, reject) => {\n this.wss.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n private handleConnection(socket: WebSocket, request: IncomingMessage): void {\n const allowlist = this.config.options.server.corsOrigins;\n if (allowlist && allowlist.length > 0) {\n const origin = request.headers.origin;\n if (!origin || !allowlist.includes(origin)) {\n socket.close(CLOSE_AUTH_FAILED, \"Origin not allowed.\");\n return;\n }\n }\n\n const state: HostConnectionState = {\n id: createConnectionId(),\n socket,\n authenticated: false,\n lastHeartbeatAt: Date.now(),\n };\n this.connections.set(socket, state);\n\n const authTimer = setTimeout(() => {\n if (!state.authenticated) {\n socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n }\n }, this.authTimeoutMs);\n\n socket.on(\"message\", async (raw) => {\n try {\n const parsed = parseEnvelope(raw.toString());\n await this.handleMessage(state, parsed);\n } catch (error) {\n this.logger.warn(\"Invalid message payload from client.\", { error: String(error) });\n socket.close(CLOSE_INVALID_PAYLOAD, \"Invalid payload.\");\n }\n });\n\n socket.on(\"close\", () => {\n clearTimeout(authTimer);\n this.connections.delete(socket);\n });\n\n socket.on(\"error\", (error) =>\n this.logger.warn(\"Socket error.\", { connectionId: state.id, error: String(error) }),\n );\n }\n\n private async handleMessage(state: HostConnectionState, envelope: WireEnvelope): Promise<void> {\n if (envelope.type === \"ping\") {\n state.lastHeartbeatAt = Date.now();\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"pong\", {})));\n return;\n }\n if (envelope.type === \"pong\") {\n state.lastHeartbeatAt = Date.now();\n return;\n }\n\n if (!state.authenticated) {\n if (envelope.type !== \"auth.hello\") {\n state.socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n return;\n }\n\n const payload = envelope.payload as AuthHelloPayload;\n if (!payload?.secret || !isSecretValid(payload.secret, this.config.options.server.secret)) {\n this.safeSend(\n state.socket,\n stringifyEnvelope(\n makeEnvelope(\"auth.error\", {\n code: \"AUTH_ERROR\",\n message: \"Invalid shared secret.\",\n }),\n ),\n );\n state.socket.close(CLOSE_AUTH_FAILED, \"Invalid secret.\");\n return;\n }\n\n state.authenticated = true;\n state.lastHeartbeatAt = Date.now();\n if (payload.clientName) {\n state.clientName = payload.clientName;\n }\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"auth.ok\", { connectionId: state.id })),\n );\n return;\n }\n\n if (envelope.type === \"command.request\") {\n const payload = envelope.payload as CommandRequestPayload;\n if (!envelope.requestId || !payload?.name) {\n const invalid: CommandFailure = {\n ok: false,\n requestId: envelope.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"VALIDATION_ERROR\",\n message: \"Invalid command request envelope.\",\n },\n };\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"command.error\", invalid, { requestId: invalid.requestId })),\n );\n return;\n }\n\n const response = await this.config.onCommandRequest(\n state,\n payload,\n envelope.requestId,\n envelope.source,\n );\n const responseType = response.ok ? \"command.result\" : \"command.error\";\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(responseType, response, { requestId: response.requestId })),\n );\n return;\n }\n }\n\n private checkHeartbeats(): void {\n const now = Date.now();\n const threshold = this.heartbeatMs * 2;\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n\n if (now - state.lastHeartbeatAt > threshold) {\n state.socket.terminate();\n this.connections.delete(state.socket);\n continue;\n }\n\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"ping\", {})));\n }\n }\n\n private safeSend(socket: WebSocket, payload: string): void {\n if (socket.readyState === 1) {\n socket.send(payload);\n }\n }\n}\n","import { timingSafeEqual } from \"node:crypto\";\n\nexport function isSecretValid(provided: string, expected: string): boolean {\n const providedBuffer = Buffer.from(provided);\n const expectedBuffer = Buffer.from(expected);\n\n if (providedBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(providedBuffer, expectedBuffer);\n}\n","import type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandSuccess,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"../core/types\";\nimport { withTimeout, DedupeCache } from \"../runtime/reliability\";\nimport type { CommandHandler } from \"../runtime/state\";\nimport { HostWebSocketServer } from \"../transport/ws/host-server\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 10000;\n\nexport function createHostShardwire<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E>,\n runtimeHooks?: {\n onClose?: () => Promise<void> | void;\n },\n): HostShardwire<C, E> {\n const commandHandlers = new Map<string, CommandHandler>();\n const commandTimeoutMs = options.server.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;\n const dedupeCache = new DedupeCache<CommandSuccess | CommandFailure>(commandTimeoutMs * 2);\n\n const hostServer = new HostWebSocketServer({\n options,\n onCommandRequest: async (connection, payload, requestId, source) => {\n const cacheKey = `${requestId}:${payload.name}`;\n const cached = dedupeCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const handler = commandHandlers.get(payload.name);\n if (!handler) {\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"COMMAND_NOT_FOUND\",\n message: `No command handler registered for \"${payload.name}\".`,\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n\n const context: CommandContext = {\n requestId,\n connectionId: connection.id,\n receivedAt: Date.now(),\n };\n if (source) {\n context.source = source;\n }\n\n try {\n const maybePromise = Promise.resolve(handler(payload.data, context));\n const value = await withTimeout(\n maybePromise,\n commandTimeoutMs,\n `Command \"${payload.name}\" timed out after ${commandTimeoutMs}ms.`,\n );\n const success: CommandSuccess = {\n ok: true,\n requestId,\n ts: Date.now(),\n data: value,\n };\n dedupeCache.set(cacheKey, success);\n return success;\n } catch (error) {\n const isTimeout = error instanceof Error && /timed out/i.test(error.message);\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: isTimeout ? \"TIMEOUT\" : \"INTERNAL_ERROR\",\n message: error instanceof Error ? error.message : \"Unknown command execution error.\",\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n },\n });\n\n return {\n mode: \"host\",\n onCommand(name, handler) {\n assertMessageName(\"command\", name);\n commandHandlers.set(name, handler as CommandHandler);\n return () => {\n commandHandlers.delete(name);\n };\n },\n emitEvent(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n broadcast(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n close() {\n return hostServer.close().then(async () => {\n await runtimeHooks?.onClose?.();\n });\n },\n };\n}\n","import { createConsumerShardwire } from \"./consumer\";\nimport { resolveDiscordClient } from \"./discord/client\";\nimport { createHostShardwire } from \"./host\";\nimport { assertConsumerOptions, assertHostOptions } from \"./runtime/validation\";\nimport type {\n CommandMap,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"./core/types\";\n\nfunction isHostOptions<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): options is HostOptions<C, E> {\n return \"server\" in options;\n}\n\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E>,\n): HostShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): HostShardwire<C, E> | ConsumerShardwire<C, E> {\n if (!isHostOptions(options)) {\n assertConsumerOptions(options);\n return createConsumerShardwire<C, E>(options);\n }\n assertHostOptions(options);\n\n let ownedClientPromise: Promise<{ destroy: () => void } | undefined> | undefined;\n\n if (!options.client && options.token) {\n ownedClientPromise = resolveDiscordClient(options)\n .then((state) => {\n if (!state.owned || !state.client) {\n return undefined;\n }\n return {\n destroy: () => state.client?.destroy(),\n };\n })\n .catch((error) => {\n options.logger?.error?.(\"Failed to initialize discord.js client from token.\", {\n error: String(error),\n });\n return undefined;\n });\n }\n\n return createHostShardwire<C, E>(options, {\n onClose: async () => {\n const owned = await ownedClientPromise;\n owned?.destroy();\n },\n });\n}\n\nexport type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandResult,\n CommandSuccess,\n ConsumerOptions,\n ConsumerShardwire,\n DiscordClientLike,\n EventMap,\n EventMeta,\n HostOptions,\n HostShardwire,\n ShardwireLogger,\n Unsubscribe,\n} from \"./core/types\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/transport/ws/consumer-socket.ts","../src/utils/id.ts","../src/utils/backoff.ts","../src/utils/logger.ts","../src/core/protocol.ts","../src/runtime/validation.ts","../src/consumer/index.ts","../src/discord/client.ts","../src/runtime/reliability.ts","../src/transport/ws/host-server.ts","../src/runtime/security.ts","../src/host/index.ts","../src/index.ts"],"sourcesContent":["import { WebSocket } from \"ws\";\n\nexport interface WebSocketLike {\n readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n on(event: \"open\", listener: () => void): void;\n on(event: \"message\", listener: (data: unknown) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: \"error\", listener: (error: unknown) => void): void;\n once(event: \"close\", listener: () => void): void;\n}\n\nexport function createNodeWebSocket(url: string): WebSocketLike {\n return new WebSocket(url) as unknown as WebSocketLike;\n}\n","import { randomUUID } from \"node:crypto\";\n\nexport function createRequestId(): string {\n return randomUUID();\n}\n\nexport function createConnectionId(): string {\n return randomUUID();\n}\n","export interface BackoffConfig {\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n}\n\nexport function getBackoffDelay(attempt: number, config: BackoffConfig): number {\n const base = Math.min(config.maxDelayMs, config.initialDelayMs * 2 ** attempt);\n if (!config.jitter) {\n return base;\n }\n const spread = Math.floor(base * 0.2);\n const min = Math.max(0, base - spread);\n const max = base + spread;\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n","import type { ShardwireLogger } from \"../core/types\";\n\nexport function withLogger(logger?: ShardwireLogger): Required<ShardwireLogger> {\n return {\n debug: logger?.debug ?? (() => undefined),\n info: logger?.info ?? (() => undefined),\n warn: logger?.warn ?? (() => undefined),\n error: logger?.error ?? (() => undefined),\n };\n}\n","import type { CommandResult } from \"./types\";\n\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type WireType =\n | \"auth.hello\"\n | \"auth.ok\"\n | \"auth.error\"\n | \"command.request\"\n | \"command.result\"\n | \"command.error\"\n | \"event.emit\"\n | \"ping\"\n | \"pong\";\n\nexport type WireEnvelope<TType extends WireType = WireType, TPayload = unknown> = {\n v: typeof PROTOCOL_VERSION;\n type: TType;\n ts: number;\n requestId?: string;\n source?: string;\n payload: TPayload;\n};\n\nexport interface AuthHelloPayload {\n secret: string;\n secretId?: string;\n clientName?: string;\n}\n\nexport interface AuthOkPayload {\n connectionId: string;\n}\n\nexport interface AuthErrorPayload {\n code: \"UNAUTHORIZED\";\n reason: \"unknown_secret_id\" | \"invalid_secret\";\n message: string;\n}\n\nexport interface CommandRequestPayload {\n name: string;\n data: unknown;\n}\n\nexport interface EventEmitPayload {\n name: string;\n data: unknown;\n}\n\nexport type CommandResultPayload = CommandResult;\n\nexport function makeEnvelope<TType extends WireType, TPayload>(\n type: TType,\n payload: TPayload,\n extras?: { requestId?: string; source?: string },\n): WireEnvelope<TType, TPayload> {\n const envelope: WireEnvelope<TType, TPayload> = {\n v: PROTOCOL_VERSION,\n type,\n ts: Date.now(),\n payload,\n };\n if (extras?.requestId) {\n envelope.requestId = extras.requestId;\n }\n if (extras?.source) {\n envelope.source = extras.source;\n }\n return envelope;\n}\n\nexport function parseEnvelope(raw: string): WireEnvelope {\n const parsed = JSON.parse(raw) as WireEnvelope;\n if (!parsed || parsed.v !== PROTOCOL_VERSION || typeof parsed.type !== \"string\") {\n throw new Error(\"Invalid wire envelope.\");\n }\n return parsed;\n}\n\nexport function stringifyEnvelope(envelope: WireEnvelope): string {\n return JSON.stringify(envelope);\n}\n","import type { ConsumerOptions, HostOptions } from \"../core/types\";\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction assertPositiveNumber(name: string, value: unknown): void {\n if (typeof value !== \"number\" || Number.isNaN(value) || value <= 0) {\n throw new Error(`${name} must be a positive number.`);\n }\n}\n\nexport function assertHostOptions(options: HostOptions<any, any>): void {\n if (!options.server) {\n throw new Error(\"Host mode requires a server configuration.\");\n }\n assertPositiveNumber(\"server.port\", options.server.port);\n if (!Array.isArray(options.server.secrets) || options.server.secrets.length === 0) {\n throw new Error(\"server.secrets must contain at least one secret.\");\n }\n for (const [index, secret] of options.server.secrets.entries()) {\n if (!isNonEmptyString(secret)) {\n throw new Error(`server.secrets[${index}] must be a non-empty string.`);\n }\n }\n if (\n options.server.primarySecretId !== undefined &&\n !options.server.secrets.some((_, index) => options.server.primarySecretId === `s${index}`)\n ) {\n throw new Error(\"server.primarySecretId must reference an existing secret id.\");\n }\n if (options.server.heartbeatMs !== undefined) {\n assertPositiveNumber(\"server.heartbeatMs\", options.server.heartbeatMs);\n }\n if (options.server.commandTimeoutMs !== undefined) {\n assertPositiveNumber(\"server.commandTimeoutMs\", options.server.commandTimeoutMs);\n }\n if (options.server.maxPayloadBytes !== undefined) {\n assertPositiveNumber(\"server.maxPayloadBytes\", options.server.maxPayloadBytes);\n }\n}\n\nexport function assertConsumerOptions(options: ConsumerOptions<any, any>): void {\n if (!isNonEmptyString(options.url)) {\n throw new Error(\"Consumer mode requires `url`.\");\n }\n if (!isNonEmptyString(options.secret)) {\n throw new Error(\"Consumer mode requires `secret`.\");\n }\n if (options.secretId !== undefined && !isNonEmptyString(options.secretId)) {\n throw new Error(\"Consumer option `secretId` must be a non-empty string.\");\n }\n if (options.requestTimeoutMs !== undefined) {\n assertPositiveNumber(\"requestTimeoutMs\", options.requestTimeoutMs);\n }\n if (options.reconnect?.initialDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.initialDelayMs\", options.reconnect.initialDelayMs);\n }\n if (options.reconnect?.maxDelayMs !== undefined) {\n assertPositiveNumber(\"reconnect.maxDelayMs\", options.reconnect.maxDelayMs);\n }\n}\n\nexport function assertMessageName(kind: \"command\" | \"event\", name: string): void {\n if (!isNonEmptyString(name)) {\n throw new Error(`${kind} name must be a non-empty string.`);\n }\n}\n\nexport function assertJsonPayload(kind: \"command\" | \"event\", name: string, payload: unknown): void {\n try {\n JSON.stringify(payload);\n } catch {\n throw new Error(`${kind} \"${name}\" payload must be JSON-serializable.`);\n }\n}\n","import type {\n CommandFailure,\n CommandMap,\n CommandResult,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n EventMeta,\n} from \"../core/types\";\nimport { createNodeWebSocket, type WebSocketLike } from \"../transport/ws/consumer-socket\";\nimport { createRequestId } from \"../utils/id\";\nimport { getBackoffDelay } from \"../utils/backoff\";\nimport { withLogger } from \"../utils/logger\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthErrorPayload,\n type CommandResultPayload,\n type EventEmitPayload,\n} from \"../core/protocol\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\ntype EventHandler = (payload: unknown, meta: EventMeta) => void;\n\ninterface PendingRequest {\n resolve: (value: CommandResult) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10000;\n\nexport function createConsumerShardwire<C extends CommandMap, E extends EventMap>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E> {\n const logger = withLogger(options.logger);\n const reconnectEnabled = options.reconnect?.enabled ?? true;\n const initialDelayMs = options.reconnect?.initialDelayMs ?? 500;\n const maxDelayMs = options.reconnect?.maxDelayMs ?? 10000;\n const jitter = options.reconnect?.jitter ?? true;\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n let socket: WebSocketLike | null = null;\n let isClosed = false;\n let isAuthed = false;\n let reconnectAttempts = 0;\n let reconnectTimer: NodeJS.Timeout | null = null;\n let connectPromise: Promise<void> | null = null;\n let connectResolve: (() => void) | null = null;\n let connectReject: ((error: Error) => void) | null = null;\n let authTimeoutTimer: NodeJS.Timeout | null = null;\n\n const pendingRequests = new Map<string, PendingRequest>();\n const eventHandlers = new Map<string, Set<EventHandler>>();\n\n function clearAuthTimeout(): void {\n if (authTimeoutTimer) {\n clearTimeout(authTimeoutTimer);\n authTimeoutTimer = null;\n }\n }\n\n function resolveConnect(): void {\n clearAuthTimeout();\n connectResolve?.();\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function rejectConnect(message: string): void {\n clearAuthTimeout();\n if (connectReject) {\n connectReject(new Error(message));\n }\n connectResolve = null;\n connectReject = null;\n connectPromise = null;\n }\n\n function sendRaw(data: string): void {\n if (!socket || socket.readyState !== 1) {\n throw new Error(\"Shardwire consumer is not connected.\");\n }\n socket.send(data);\n }\n\n function rejectAllPending(reason: string): void {\n for (const [requestId, pending] of pendingRequests.entries()) {\n clearTimeout(pending.timer);\n pending.reject(new Error(reason));\n pendingRequests.delete(requestId);\n }\n }\n\n function scheduleReconnect(): void {\n if (isClosed || !reconnectEnabled || reconnectTimer) {\n return;\n }\n const delay = getBackoffDelay(reconnectAttempts, { initialDelayMs, maxDelayMs, jitter });\n reconnectAttempts += 1;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n void connect().catch((error) => {\n logger.warn(\"Reconnect attempt failed.\", { error: String(error) });\n });\n }, delay);\n }\n\n function handleEvent(name: string, payload: unknown, meta: EventMeta): void {\n const handlers = eventHandlers.get(name);\n if (!handlers || handlers.size === 0) {\n return;\n }\n for (const handler of handlers) {\n try {\n handler(payload, meta);\n } catch (error) {\n logger.warn(\"Event handler threw an error.\", { name, error: String(error) });\n }\n }\n }\n\n async function connect(): Promise<void> {\n if (isClosed) {\n return;\n }\n if (socket && socket.readyState === 1 && isAuthed) {\n return;\n }\n if (connectPromise) {\n return connectPromise;\n }\n\n connectPromise = new Promise<void>((resolve, reject) => {\n connectResolve = resolve;\n connectReject = reject;\n });\n\n socket = options.webSocketFactory\n ? (options.webSocketFactory(options.url) as WebSocketLike)\n : createNodeWebSocket(options.url);\n\n socket.on(\"open\", () => {\n reconnectAttempts = 0;\n isAuthed = false;\n const hello = makeEnvelope(\"auth.hello\", {\n secret: options.secret,\n secretId: options.secretId,\n });\n socket?.send(stringifyEnvelope(hello));\n authTimeoutTimer = setTimeout(() => {\n if (!isAuthed) {\n rejectConnect(\"Shardwire auth timed out.\");\n socket?.close();\n }\n }, requestTimeoutMs);\n });\n\n socket.on(\"message\", (raw) => {\n try {\n const serialized = typeof raw === \"string\" ? raw : String(raw);\n const envelope = parseEnvelope(serialized);\n switch (envelope.type) {\n case \"auth.ok\":\n isAuthed = true;\n resolveConnect();\n break;\n case \"auth.error\": {\n const payload = envelope.payload as AuthErrorPayload;\n logger.error(\"Authentication failed for consumer.\", {\n code: payload.code,\n message: payload.message,\n });\n rejectConnect(payload.message);\n rejectAllPending(\"Shardwire authentication failed.\");\n socket?.close();\n break;\n }\n case \"command.result\":\n case \"command.error\": {\n const requestId = envelope.requestId;\n if (!requestId) {\n return;\n }\n const pending = pendingRequests.get(requestId);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n pending.resolve(envelope.payload as CommandResultPayload);\n pendingRequests.delete(requestId);\n break;\n }\n case \"event.emit\": {\n const payload = envelope.payload as EventEmitPayload;\n const meta: EventMeta = { ts: envelope.ts };\n if (envelope.source) {\n meta.source = envelope.source;\n }\n handleEvent(payload.name, payload.data, meta);\n break;\n }\n case \"ping\":\n sendRaw(stringifyEnvelope(makeEnvelope(\"pong\", {})));\n break;\n default:\n break;\n }\n } catch (error) {\n logger.warn(\"Failed to parse consumer message.\", { error: String(error) });\n }\n });\n\n socket.on(\"close\", () => {\n rejectConnect(\"Shardwire connection closed.\");\n isAuthed = false;\n if (!isClosed) {\n scheduleReconnect();\n }\n });\n\n socket.on(\"error\", (error) => {\n logger.warn(\"Consumer socket error.\", { error: String(error) });\n });\n\n return connectPromise;\n }\n\n void connect().catch((error) => {\n logger.warn(\"Initial connection attempt failed.\", { error: String(error) });\n });\n\n return {\n mode: \"consumer\",\n async send(name, payload, sendOptions) {\n assertMessageName(\"command\", name);\n assertJsonPayload(\"command\", name, payload);\n if (!isAuthed) {\n try {\n await connect();\n } catch (error) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"UNAUTHORIZED\",\n message: error instanceof Error ? error.message : \"Failed to authenticate.\",\n },\n } satisfies CommandFailure;\n }\n }\n if (!socket || socket.readyState !== 1) {\n return {\n ok: false,\n requestId: sendOptions?.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: \"Not connected to Shardwire host.\",\n },\n } satisfies CommandFailure;\n }\n\n const requestId = sendOptions?.requestId ?? createRequestId();\n const timeoutMs = sendOptions?.timeoutMs ?? requestTimeoutMs;\n\n const promise = new Promise<CommandResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingRequests.delete(requestId);\n reject(new Error(`Command \"${name}\" timed out after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n pendingRequests.set(requestId, { resolve, reject, timer });\n });\n\n sendRaw(\n stringifyEnvelope(\n makeEnvelope(\n \"command.request\",\n {\n name,\n data: payload,\n },\n { requestId },\n ),\n ),\n );\n\n try {\n return await promise;\n } catch (error) {\n return {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"TIMEOUT\",\n message: error instanceof Error ? error.message : \"Command request timeout.\",\n },\n } satisfies CommandFailure;\n }\n },\n on(name, handler) {\n assertMessageName(\"event\", name);\n const casted = handler as EventHandler;\n const existing = eventHandlers.get(name);\n if (existing) {\n existing.add(casted);\n } else {\n eventHandlers.set(name, new Set<EventHandler>([casted]));\n }\n return () => {\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(casted);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n };\n },\n off(name, handler) {\n assertMessageName(\"event\", name);\n const handlers = eventHandlers.get(name);\n if (!handlers) {\n return;\n }\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n eventHandlers.delete(name);\n }\n },\n connected() {\n return Boolean(socket && socket.readyState === 1 && isAuthed);\n },\n async close() {\n isClosed = true;\n isAuthed = false;\n rejectConnect(\"Shardwire consumer has been closed.\");\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n rejectAllPending(\"Shardwire consumer has been closed.\");\n if (!socket) {\n return;\n }\n await new Promise<void>((resolve) => {\n const current = socket;\n if (!current) {\n resolve();\n return;\n }\n current.once(\"close\", () => resolve());\n current.close();\n });\n socket = null;\n },\n };\n}\n","import type { DiscordClientLike, HostOptions } from \"../core/types\";\n\ninterface OwnedClientState {\n client?: DiscordClientLike;\n owned: boolean;\n}\n\nexport async function resolveDiscordClient<C extends Record<string, unknown>, E extends Record<string, unknown>>(\n options: HostOptions<C, E>,\n): Promise<OwnedClientState> {\n if (options.client) {\n return { client: options.client, owned: false };\n }\n\n if (!options.token) {\n return { owned: false };\n }\n\n const discordModule = await import(\"discord.js\");\n const created = new discordModule.Client({\n intents: [],\n });\n await created.login(options.token);\n return { client: created as unknown as DiscordClientLike, owned: true };\n}\n","export async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutMessage = \"Operation timed out.\",\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(timeoutMessage));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timeout);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n}\n\nexport class DedupeCache<T> {\n private readonly cache = new Map<string, { value: T; expiresAt: number }>();\n\n constructor(private readonly ttlMs: number) {}\n\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n if (!entry) {\n return undefined;\n }\n if (entry.expiresAt <= Date.now()) {\n this.cache.delete(key);\n return undefined;\n }\n return entry.value;\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, { value, expiresAt: Date.now() + this.ttlMs });\n }\n}\n","import { WebSocketServer, type WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport {\n makeEnvelope,\n parseEnvelope,\n stringifyEnvelope,\n type AuthHelloPayload,\n type CommandRequestPayload,\n type EventEmitPayload,\n type WireEnvelope,\n} from \"../../core/protocol\";\nimport type { CommandFailure, CommandSuccess, HostOptions, ShardwireLogger } from \"../../core/types\";\nimport { withLogger } from \"../../utils/logger\";\nimport { createConnectionId } from \"../../utils/id\";\nimport type { HostConnectionState } from \"../../runtime/state\";\nimport { getSecretId, isSecretValid } from \"../../runtime/security\";\n\nconst CLOSE_AUTH_REQUIRED = 4001;\nconst CLOSE_AUTH_FAILED = 4003;\nconst CLOSE_INVALID_PAYLOAD = 4004;\n\ninterface HostServerConfig {\n options: HostOptions<any, any>;\n onCommandRequest: (\n connection: HostConnectionState,\n payload: CommandRequestPayload,\n requestId: string,\n source?: string,\n ) => Promise<CommandSuccess | CommandFailure>;\n}\n\nexport class HostWebSocketServer {\n private readonly wss: WebSocketServer;\n private readonly connections = new Map<WebSocket, HostConnectionState>();\n private readonly logger: Required<ShardwireLogger>;\n private readonly heartbeatMs: number;\n private readonly authTimeoutMs = 5000;\n private readonly interval: NodeJS.Timeout;\n\n constructor(private readonly config: HostServerConfig) {\n const serverConfig = config.options.server;\n this.heartbeatMs = serverConfig.heartbeatMs ?? 30000;\n this.logger = withLogger(config.options.logger);\n\n this.wss = new WebSocketServer({\n host: serverConfig.host,\n port: serverConfig.port,\n path: serverConfig.path ?? \"/shardwire\",\n maxPayload: serverConfig.maxPayloadBytes ?? 65536,\n });\n\n this.wss.on(\"connection\", (socket, request) => this.handleConnection(socket, request));\n this.wss.on(\"error\", (error) =>\n this.logger.error(\"Shardwire host server error.\", { error: String(error) }),\n );\n\n this.interval = setInterval(() => {\n this.checkHeartbeats();\n }, this.heartbeatMs);\n }\n\n emitEvent(name: string, data: unknown, source?: string): void {\n const envelope = makeEnvelope(\n \"event.emit\",\n { name, data } satisfies EventEmitPayload,\n source ? { source } : undefined,\n );\n const raw = stringifyEnvelope(envelope);\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n this.safeSend(state.socket, raw);\n }\n }\n\n async close(): Promise<void> {\n clearInterval(this.interval);\n for (const connection of this.connections.values()) {\n connection.socket.close();\n }\n this.connections.clear();\n await new Promise<void>((resolve, reject) => {\n this.wss.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n }\n\n private handleConnection(socket: WebSocket, request: IncomingMessage): void {\n const allowlist = this.config.options.server.corsOrigins;\n if (allowlist && allowlist.length > 0) {\n const origin = request.headers.origin;\n if (!origin || !allowlist.includes(origin)) {\n socket.close(CLOSE_AUTH_FAILED, \"Origin not allowed.\");\n return;\n }\n }\n\n const state: HostConnectionState = {\n id: createConnectionId(),\n socket,\n authenticated: false,\n lastHeartbeatAt: Date.now(),\n };\n this.connections.set(socket, state);\n\n const authTimer = setTimeout(() => {\n if (!state.authenticated) {\n socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n }\n }, this.authTimeoutMs);\n\n socket.on(\"message\", async (raw) => {\n try {\n const parsed = parseEnvelope(raw.toString());\n await this.handleMessage(state, parsed);\n } catch (error) {\n this.logger.warn(\"Invalid message payload from client.\", { error: String(error) });\n socket.close(CLOSE_INVALID_PAYLOAD, \"Invalid payload.\");\n }\n });\n\n socket.on(\"close\", () => {\n clearTimeout(authTimer);\n this.connections.delete(socket);\n });\n\n socket.on(\"error\", (error) =>\n this.logger.warn(\"Socket error.\", { connectionId: state.id, error: String(error) }),\n );\n }\n\n private async handleMessage(state: HostConnectionState, envelope: WireEnvelope): Promise<void> {\n if (envelope.type === \"ping\") {\n state.lastHeartbeatAt = Date.now();\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"pong\", {})));\n return;\n }\n if (envelope.type === \"pong\") {\n state.lastHeartbeatAt = Date.now();\n return;\n }\n\n if (!state.authenticated) {\n if (envelope.type !== \"auth.hello\") {\n state.socket.close(CLOSE_AUTH_REQUIRED, \"Authentication required.\");\n return;\n }\n\n const payload = envelope.payload as AuthHelloPayload;\n const providedSecret = payload?.secret;\n const knownSecrets = this.config.options.server.secrets;\n const secretId = payload?.secretId;\n let authReason: \"unknown_secret_id\" | \"invalid_secret\" | null = null;\n\n if (!providedSecret) {\n authReason = \"invalid_secret\";\n } else if (secretId) {\n const secretIndex = knownSecrets.findIndex((_, index) => getSecretId(index) === secretId);\n if (secretIndex < 0) {\n authReason = \"unknown_secret_id\";\n } else {\n const expectedSecret = knownSecrets[secretIndex];\n if (!expectedSecret || !isSecretValid(providedSecret, expectedSecret)) {\n authReason = \"invalid_secret\";\n }\n }\n } else if (!knownSecrets.some((secret) => isSecretValid(providedSecret, secret))) {\n authReason = \"invalid_secret\";\n }\n\n if (authReason) {\n this.safeSend(\n state.socket,\n stringifyEnvelope(\n makeEnvelope(\"auth.error\", {\n code: \"UNAUTHORIZED\",\n reason: authReason,\n message: \"Authentication failed.\",\n }),\n ),\n );\n state.socket.close(CLOSE_AUTH_FAILED, \"Invalid secret.\");\n return;\n }\n\n state.authenticated = true;\n state.lastHeartbeatAt = Date.now();\n if (payload.clientName) {\n state.clientName = payload.clientName;\n }\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"auth.ok\", { connectionId: state.id })),\n );\n return;\n }\n\n if (envelope.type === \"command.request\") {\n const payload = envelope.payload as CommandRequestPayload;\n if (!envelope.requestId || !payload?.name) {\n const invalid: CommandFailure = {\n ok: false,\n requestId: envelope.requestId ?? \"unknown\",\n ts: Date.now(),\n error: {\n code: \"VALIDATION_ERROR\",\n message: \"Invalid command request envelope.\",\n },\n };\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(\"command.error\", invalid, { requestId: invalid.requestId })),\n );\n return;\n }\n\n const response = await this.config.onCommandRequest(\n state,\n payload,\n envelope.requestId,\n envelope.source,\n );\n const responseType = response.ok ? \"command.result\" : \"command.error\";\n this.safeSend(\n state.socket,\n stringifyEnvelope(makeEnvelope(responseType, response, { requestId: response.requestId })),\n );\n return;\n }\n }\n\n private checkHeartbeats(): void {\n const now = Date.now();\n const threshold = this.heartbeatMs * 2;\n\n for (const state of this.connections.values()) {\n if (!state.authenticated) {\n continue;\n }\n\n if (now - state.lastHeartbeatAt > threshold) {\n state.socket.terminate();\n this.connections.delete(state.socket);\n continue;\n }\n\n this.safeSend(state.socket, stringifyEnvelope(makeEnvelope(\"ping\", {})));\n }\n }\n\n private safeSend(socket: WebSocket, payload: string): void {\n if (socket.readyState === 1) {\n socket.send(payload);\n }\n }\n}\n","import { timingSafeEqual } from \"node:crypto\";\n\nexport function isSecretValid(provided: string, expected: string): boolean {\n const providedBuffer = Buffer.from(provided);\n const expectedBuffer = Buffer.from(expected);\n\n if (providedBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(providedBuffer, expectedBuffer);\n}\n\nexport function getSecretId(secretIndex: number): string {\n return `s${secretIndex}`;\n}\n","import type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandSuccess,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"../core/types\";\nimport { withTimeout, DedupeCache } from \"../runtime/reliability\";\nimport type { CommandHandler } from \"../runtime/state\";\nimport { HostWebSocketServer } from \"../transport/ws/host-server\";\nimport { assertJsonPayload, assertMessageName } from \"../runtime/validation\";\n\nconst DEFAULT_COMMAND_TIMEOUT_MS = 10000;\n\nexport function createHostShardwire<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E>,\n runtimeHooks?: {\n onClose?: () => Promise<void> | void;\n },\n): HostShardwire<C, E> {\n const commandHandlers = new Map<string, CommandHandler>();\n const commandTimeoutMs = options.server.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;\n const dedupeCache = new DedupeCache<CommandSuccess | CommandFailure>(commandTimeoutMs * 2);\n\n const hostServer = new HostWebSocketServer({\n options,\n onCommandRequest: async (connection, payload, requestId, source) => {\n const cacheKey = `${requestId}:${payload.name}`;\n const cached = dedupeCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const handler = commandHandlers.get(payload.name);\n if (!handler) {\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: \"COMMAND_NOT_FOUND\",\n message: `No command handler registered for \"${payload.name}\".`,\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n\n const context: CommandContext = {\n requestId,\n connectionId: connection.id,\n receivedAt: Date.now(),\n };\n if (source) {\n context.source = source;\n }\n\n try {\n const maybePromise = Promise.resolve(handler(payload.data, context));\n const value = await withTimeout(\n maybePromise,\n commandTimeoutMs,\n `Command \"${payload.name}\" timed out after ${commandTimeoutMs}ms.`,\n );\n const success: CommandSuccess = {\n ok: true,\n requestId,\n ts: Date.now(),\n data: value,\n };\n dedupeCache.set(cacheKey, success);\n return success;\n } catch (error) {\n const isTimeout = error instanceof Error && /timed out/i.test(error.message);\n const failure: CommandFailure = {\n ok: false,\n requestId,\n ts: Date.now(),\n error: {\n code: isTimeout ? \"TIMEOUT\" : \"INTERNAL_ERROR\",\n message: error instanceof Error ? error.message : \"Unknown command execution error.\",\n },\n };\n dedupeCache.set(cacheKey, failure);\n return failure;\n }\n },\n });\n\n return {\n mode: \"host\",\n onCommand(name, handler) {\n assertMessageName(\"command\", name);\n commandHandlers.set(name, handler as CommandHandler);\n return () => {\n commandHandlers.delete(name);\n };\n },\n emitEvent(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n broadcast(name, payload) {\n assertMessageName(\"event\", name);\n assertJsonPayload(\"event\", name, payload);\n hostServer.emitEvent(name, payload, options.name);\n },\n close() {\n return hostServer.close().then(async () => {\n await runtimeHooks?.onClose?.();\n });\n },\n };\n}\n","import { createConsumerShardwire } from \"./consumer\";\nimport { resolveDiscordClient } from \"./discord/client\";\nimport { createHostShardwire } from \"./host\";\nimport { assertConsumerOptions, assertHostOptions } from \"./runtime/validation\";\nimport type {\n CommandMap,\n ConsumerOptions,\n ConsumerShardwire,\n EventMap,\n HostOptions,\n HostShardwire,\n} from \"./core/types\";\n\nfunction isHostOptions<C extends CommandMap, E extends EventMap>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): options is HostOptions<C, E> {\n return \"server\" in options;\n}\n\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E>,\n): HostShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: ConsumerOptions<C, E>,\n): ConsumerShardwire<C, E>;\nexport function createShardwire<C extends CommandMap = {}, E extends EventMap = {}>(\n options: HostOptions<C, E> | ConsumerOptions<C, E>,\n): HostShardwire<C, E> | ConsumerShardwire<C, E> {\n if (!isHostOptions(options)) {\n assertConsumerOptions(options);\n return createConsumerShardwire<C, E>(options);\n }\n assertHostOptions(options);\n\n let ownedClientPromise: Promise<{ destroy: () => void } | undefined> | undefined;\n\n if (!options.client && options.token) {\n ownedClientPromise = resolveDiscordClient(options)\n .then((state) => {\n if (!state.owned || !state.client) {\n return undefined;\n }\n return {\n destroy: () => state.client?.destroy(),\n };\n })\n .catch((error) => {\n options.logger?.error?.(\"Failed to initialize discord.js client from token.\", {\n error: String(error),\n });\n return undefined;\n });\n }\n\n return createHostShardwire<C, E>(options, {\n onClose: async () => {\n const owned = await ownedClientPromise;\n owned?.destroy();\n },\n });\n}\n\nexport type {\n CommandContext,\n CommandFailure,\n CommandMap,\n CommandResult,\n CommandSuccess,\n ConsumerOptions,\n ConsumerShardwire,\n DiscordClientLike,\n EventMap,\n EventMeta,\n HostOptions,\n HostShardwire,\n ShardwireLogger,\n Unsubscribe,\n} from \"./core/types\";\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shardwire",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Lightweight Discord-hosted WebSocket bridge for commands and events.",
|
|
5
|
+
"homepage": "https://www.npmjs.com/package/shardwire",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/unloopedmido/shardwire.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/unloopedmido/shardwire/issues",
|
|
12
|
+
"email": "cored.developments@gmail.com"
|
|
13
|
+
},
|
|
5
14
|
"main": "./dist/index.js",
|
|
6
15
|
"module": "./dist/index.mjs",
|
|
7
16
|
"types": "./dist/index.d.ts",
|
|
@@ -24,11 +33,18 @@
|
|
|
24
33
|
},
|
|
25
34
|
"keywords": [
|
|
26
35
|
"discord",
|
|
36
|
+
"discord-bot",
|
|
27
37
|
"websocket",
|
|
38
|
+
"websocket-bridge",
|
|
28
39
|
"realtime",
|
|
29
|
-
"typescript"
|
|
40
|
+
"typescript",
|
|
41
|
+
"rpc",
|
|
42
|
+
"command-bus",
|
|
43
|
+
"event-stream",
|
|
44
|
+
"typed-events",
|
|
45
|
+
"discord-api"
|
|
30
46
|
],
|
|
31
|
-
"author": "",
|
|
47
|
+
"author": "Mohammed Haism <cored.developments@gmail.com>",
|
|
32
48
|
"license": "MIT",
|
|
33
49
|
"files": [
|
|
34
50
|
"dist"
|
|
@@ -55,5 +71,8 @@
|
|
|
55
71
|
"typescript": "^5.9.2",
|
|
56
72
|
"vitest": "^3.2.4"
|
|
57
73
|
},
|
|
74
|
+
"publishConfig": {
|
|
75
|
+
"access": "public"
|
|
76
|
+
},
|
|
58
77
|
"packageManager": "pnpm@10.22.0"
|
|
59
78
|
}
|