@silasdevs/transport 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,571 @@
1
+ // src/protocol.ts
2
+ function defaultGenerateId() {
3
+ return crypto.getRandomValues(new Uint32Array(1))[0];
4
+ }
5
+ function defaultEncode(message) {
6
+ return JSON.stringify(message);
7
+ }
8
+ function defaultDecode(raw) {
9
+ try {
10
+ return JSON.parse(raw);
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ function resolveSchema(input) {
16
+ return {
17
+ fields: input?.fields ?? {},
18
+ codes: input?.codes,
19
+ generateId: input?.generateId ?? defaultGenerateId,
20
+ encode: input?.encode ?? defaultEncode,
21
+ decode: input?.decode ?? defaultDecode,
22
+ flattenOutgoing: input?.flattenOutgoing ?? false,
23
+ includeIdInRequest: input?.includeIdInRequest ?? false
24
+ };
25
+ }
26
+ function normalizeIncoming(raw, schema) {
27
+ const f = schema.fields;
28
+ let channel = "";
29
+ if (f.responseChannel) {
30
+ channel = String(raw[f.responseChannel] ?? "");
31
+ }
32
+ if (!channel && f.subscriptionChannel) {
33
+ channel = String(raw[f.subscriptionChannel] ?? "");
34
+ }
35
+ if (!channel) {
36
+ channel = "*";
37
+ }
38
+ const rawId = f.messageId ? Number(raw[f.messageId] ?? 0) : 0;
39
+ const messageId = Number.isNaN(rawId) ? 0 : rawId;
40
+ const isEvent = !messageId && channel !== "*";
41
+ const bodyField = isEvent && f.eventBody ? f.eventBody : f.body;
42
+ return {
43
+ channel,
44
+ messageId,
45
+ code: f.code ? String(raw[f.code] ?? "") : "",
46
+ description: f.description ? String(raw[f.description] ?? "") : "",
47
+ error: f.error ? raw[f.error] : void 0,
48
+ data: bodyField ? raw[bodyField] ?? {} : {},
49
+ raw
50
+ };
51
+ }
52
+ function buildOutgoing(msg, messageId, schema) {
53
+ const f = schema.fields;
54
+ const wire = {};
55
+ if (f.requestChannel) {
56
+ wire[f.requestChannel] = msg.channel ?? "*";
57
+ }
58
+ if (schema.includeIdInRequest) {
59
+ wire[f.messageId ?? "id"] = messageId;
60
+ }
61
+ if (msg.data) {
62
+ const data = msg.data;
63
+ if (schema.flattenOutgoing) {
64
+ for (const key of Object.keys(data)) {
65
+ wire[key] = data[key];
66
+ }
67
+ } else if (f.payload) {
68
+ wire[f.payload] = data;
69
+ }
70
+ }
71
+ return wire;
72
+ }
73
+
74
+ // src/events.ts
75
+ function createEmitter() {
76
+ const listeners = /* @__PURE__ */ new Map();
77
+ function getSet(event) {
78
+ let set = listeners.get(event);
79
+ if (!set) {
80
+ set = /* @__PURE__ */ new Set();
81
+ listeners.set(event, set);
82
+ }
83
+ return set;
84
+ }
85
+ function on(event, callback) {
86
+ const set = getSet(event);
87
+ const cb = callback;
88
+ set.add(cb);
89
+ return () => {
90
+ set.delete(cb);
91
+ };
92
+ }
93
+ function once(event, callback) {
94
+ const wrapper = (data) => {
95
+ off(event, wrapper);
96
+ callback(data);
97
+ };
98
+ return on(event, wrapper);
99
+ }
100
+ function emit(event, data) {
101
+ const set = listeners.get(event);
102
+ if (!set) return;
103
+ for (const cb of [...set]) {
104
+ cb(data);
105
+ }
106
+ }
107
+ function off(event, callback) {
108
+ const set = listeners.get(event);
109
+ if (set) set.delete(callback);
110
+ }
111
+ function removeAll(event) {
112
+ if (event !== void 0) {
113
+ listeners.delete(event);
114
+ } else {
115
+ listeners.clear();
116
+ }
117
+ }
118
+ return { on, once, emit, off, removeAll };
119
+ }
120
+
121
+ // src/handlers.ts
122
+ function createHandlerStore() {
123
+ const ephemeral = /* @__PURE__ */ new Map();
124
+ const persistent = /* @__PURE__ */ new Map();
125
+ const idToChannel = /* @__PURE__ */ new Map();
126
+ function getEphemeralMap(channel) {
127
+ let map = ephemeral.get(channel);
128
+ if (!map) {
129
+ map = /* @__PURE__ */ new Map();
130
+ ephemeral.set(channel, map);
131
+ }
132
+ return map;
133
+ }
134
+ function getPersistentMap(channel) {
135
+ let map = persistent.get(channel);
136
+ if (!map) {
137
+ map = /* @__PURE__ */ new Map();
138
+ persistent.set(channel, map);
139
+ }
140
+ return map;
141
+ }
142
+ function add(channel, key, handler) {
143
+ if (handler.type === "ephemeral" && typeof key === "number") {
144
+ getEphemeralMap(channel).set(key, handler);
145
+ idToChannel.set(key, channel);
146
+ } else if (handler.type === "persistent" && typeof key === "string") {
147
+ getPersistentMap(channel).set(key, handler);
148
+ } else {
149
+ throw new Error(
150
+ `Invalid handler registration: type=${handler.type}, key type=${typeof key}. Ephemeral handlers require a numeric key (messageId), persistent require a string key (name).`
151
+ );
152
+ }
153
+ return () => {
154
+ remove(channel, key);
155
+ };
156
+ }
157
+ function remove(channel, key) {
158
+ if (typeof key === "number") {
159
+ const map = ephemeral.get(channel);
160
+ if (!map) return false;
161
+ const deleted = map.delete(key);
162
+ if (deleted) idToChannel.delete(key);
163
+ if (map.size === 0) ephemeral.delete(channel);
164
+ return deleted;
165
+ } else {
166
+ const map = persistent.get(channel);
167
+ if (!map) return false;
168
+ const deleted = map.delete(key);
169
+ if (map.size === 0) persistent.delete(channel);
170
+ return deleted;
171
+ }
172
+ }
173
+ function execute(message) {
174
+ const channel = message.channel;
175
+ const msgId = message.messageId;
176
+ const ephMap = ephemeral.get(channel);
177
+ if (ephMap && msgId !== 0) {
178
+ const handler = ephMap.get(msgId);
179
+ if (handler) {
180
+ const result = handler.callback(message);
181
+ if (result !== false) {
182
+ ephMap.delete(msgId);
183
+ idToChannel.delete(msgId);
184
+ if (ephMap.size === 0) ephemeral.delete(channel);
185
+ }
186
+ return true;
187
+ }
188
+ }
189
+ const perMap = persistent.get(channel);
190
+ if (perMap && perMap.size > 0) {
191
+ for (const handler of perMap.values()) {
192
+ handler.callback(message);
193
+ }
194
+ return true;
195
+ }
196
+ if (msgId !== 0) {
197
+ const resolvedChannel = idToChannel.get(msgId);
198
+ if (resolvedChannel && resolvedChannel !== channel) {
199
+ const fallbackMap = ephemeral.get(resolvedChannel);
200
+ if (fallbackMap) {
201
+ const handler = fallbackMap.get(msgId);
202
+ if (handler) {
203
+ const result = handler.callback(message);
204
+ if (result !== false) {
205
+ fallbackMap.delete(msgId);
206
+ idToChannel.delete(msgId);
207
+ if (fallbackMap.size === 0) ephemeral.delete(resolvedChannel);
208
+ }
209
+ return true;
210
+ }
211
+ }
212
+ }
213
+ }
214
+ return false;
215
+ }
216
+ function hasEphemeral(channel, messageId) {
217
+ return ephemeral.get(channel)?.has(messageId) ?? false;
218
+ }
219
+ function clear() {
220
+ ephemeral.clear();
221
+ persistent.clear();
222
+ idToChannel.clear();
223
+ }
224
+ function clearStale(channel) {
225
+ if (channel) {
226
+ const map = ephemeral.get(channel);
227
+ if (map) {
228
+ for (const msgId of map.keys()) {
229
+ idToChannel.delete(msgId);
230
+ }
231
+ }
232
+ ephemeral.delete(channel);
233
+ } else {
234
+ ephemeral.clear();
235
+ idToChannel.clear();
236
+ }
237
+ }
238
+ function findChannelByMessageId(messageId) {
239
+ return idToChannel.get(messageId);
240
+ }
241
+ return { add, remove, execute, hasEphemeral, findChannelByMessageId, clear, clearStale };
242
+ }
243
+
244
+ // src/connection.ts
245
+ var WS_CONNECTING = 0;
246
+ var WS_OPEN = 1;
247
+ var WS_CLOSING = 2;
248
+ var WS_CLOSED = 3;
249
+ function createConnection(deps) {
250
+ const { schema, emitter, handlers } = deps;
251
+ let ws = null;
252
+ let state = "disconnected";
253
+ let reconnectTimer = null;
254
+ let reconnectAttempt = 0;
255
+ let destroyed = false;
256
+ function resolveUrl() {
257
+ return typeof deps.url === "function" ? deps.url() : deps.url;
258
+ }
259
+ function log(...args) {
260
+ if (deps.isDebug()) {
261
+ console.log("[silas/transport]", ...args);
262
+ }
263
+ }
264
+ function setState(next) {
265
+ state = next;
266
+ }
267
+ function clearReconnectTimer() {
268
+ if (reconnectTimer !== null) {
269
+ clearTimeout(reconnectTimer);
270
+ reconnectTimer = null;
271
+ }
272
+ }
273
+ function computeDelay() {
274
+ if (!deps.reconnect) return 0;
275
+ const { delayMs, backoff } = deps.reconnect;
276
+ if (backoff === "exponential") {
277
+ return Math.min(delayMs * Math.pow(2, reconnectAttempt), 6e4);
278
+ }
279
+ return delayMs;
280
+ }
281
+ function scheduleReconnect() {
282
+ if (!deps.reconnect || !deps.reconnect.auto || destroyed) return;
283
+ if (reconnectAttempt >= deps.reconnect.maxAttempts) {
284
+ log("Max reconnect attempts reached:", deps.reconnect.maxAttempts);
285
+ return;
286
+ }
287
+ const delay = computeDelay();
288
+ reconnectAttempt++;
289
+ setState("reconnecting");
290
+ emitter.emit("reconnecting", { attempt: reconnectAttempt, delayMs: delay });
291
+ log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempt})`);
292
+ reconnectTimer = setTimeout(() => {
293
+ reconnectTimer = null;
294
+ connect();
295
+ }, delay);
296
+ }
297
+ function onOpen(evt) {
298
+ reconnectAttempt = 0;
299
+ setState("connected");
300
+ log("Connected");
301
+ emitter.emit("connected", evt);
302
+ }
303
+ function onClose(evt) {
304
+ ws = null;
305
+ const prev = state;
306
+ setState("disconnected");
307
+ log("Disconnected", evt.code, evt.reason);
308
+ emitter.emit("disconnected", {
309
+ code: evt.code,
310
+ reason: evt.reason,
311
+ wasClean: evt.wasClean
312
+ });
313
+ if (prev !== "disconnected" && !destroyed) {
314
+ scheduleReconnect();
315
+ }
316
+ }
317
+ function onError(evt) {
318
+ log("WebSocket error", evt);
319
+ emitter.emit("error", evt);
320
+ }
321
+ function onMessage(evt) {
322
+ const raw = typeof evt.data === "string" ? evt.data : String(evt.data);
323
+ emitter.emit("message:raw", { data: raw });
324
+ const parsed = schema.decode(raw);
325
+ if (!parsed) {
326
+ log("Failed to decode message:", raw);
327
+ return;
328
+ }
329
+ const message = normalizeIncoming(parsed, schema);
330
+ log("\u2190", message.channel, message.messageId, message.code);
331
+ emitter.emit("message:parsed", message);
332
+ if (!message.channel && message.messageId === 0) {
333
+ log("Message missing channel and messageId, dropping");
334
+ return;
335
+ }
336
+ const handled = handlers.execute(message);
337
+ if (handled) {
338
+ log("Handler matched:", message.channel, message.messageId);
339
+ } else {
340
+ emitter.emit("message:unhandled", message);
341
+ log("Unhandled message:", message.channel, message.messageId);
342
+ }
343
+ }
344
+ function connect() {
345
+ if (destroyed) return;
346
+ if (ws) {
347
+ const rs = ws.readyState;
348
+ if (rs === WS_OPEN || rs === WS_CONNECTING) {
349
+ log("Already connected/connecting, skipping");
350
+ return;
351
+ }
352
+ }
353
+ clearReconnectTimer();
354
+ const url = resolveUrl();
355
+ log("Connecting to", url);
356
+ setState("connecting");
357
+ emitter.emit("connecting", void 0);
358
+ try {
359
+ ws = new WebSocket(url);
360
+ } catch (err) {
361
+ log("WebSocket constructor error:", err);
362
+ emitter.emit("error", new Event("error"));
363
+ scheduleReconnect();
364
+ return;
365
+ }
366
+ ws.onopen = onOpen;
367
+ ws.onclose = onClose;
368
+ ws.onerror = onError;
369
+ ws.onmessage = onMessage;
370
+ }
371
+ function disconnect(options) {
372
+ clearReconnectTimer();
373
+ reconnectAttempt = 0;
374
+ if (options?.clean) {
375
+ handlers.clearStale();
376
+ }
377
+ if (ws) {
378
+ const rs = ws.readyState;
379
+ if (rs !== WS_CLOSED && rs !== WS_CLOSING) {
380
+ log("Closing WebSocket");
381
+ ws.close();
382
+ }
383
+ ws = null;
384
+ }
385
+ setState("disconnected");
386
+ }
387
+ function send(payload) {
388
+ emitter.emit("send:before", { payload });
389
+ if (!ws || ws.readyState !== WS_OPEN) {
390
+ const reason = ws ? `WebSocket readyState=${ws.readyState}` : "No WebSocket instance";
391
+ log("Send failed:", reason);
392
+ emitter.emit("send:error", { payload, reason });
393
+ if (!ws || ws.readyState === WS_CLOSED || ws.readyState === WS_CLOSING) {
394
+ if (deps.reconnect?.auto && !destroyed) {
395
+ clearReconnectTimer();
396
+ reconnectTimer = setTimeout(() => {
397
+ reconnectTimer = null;
398
+ connect();
399
+ }, 1e3);
400
+ }
401
+ }
402
+ return;
403
+ }
404
+ const encoded = schema.encode(payload);
405
+ log("\u2192", payload);
406
+ ws.send(encoded);
407
+ emitter.emit("send:after", { payload });
408
+ }
409
+ function getState() {
410
+ return state;
411
+ }
412
+ function destroy() {
413
+ destroyed = true;
414
+ disconnect();
415
+ handlers.clear();
416
+ }
417
+ return { connect, disconnect, send, getState, destroy };
418
+ }
419
+
420
+ // src/transport.ts
421
+ var DEFAULT_RECONNECT = {
422
+ auto: true,
423
+ delayMs: 1e4,
424
+ maxAttempts: Infinity,
425
+ backoff: "fixed"
426
+ };
427
+ function createTransport(options) {
428
+ const schema = resolveSchema(options.protocol);
429
+ const emitter = createEmitter();
430
+ const handlers = createHandlerStore();
431
+ let debugEnabled = options.debug ?? false;
432
+ const reconnectConfig = options.reconnect === false ? void 0 : { ...DEFAULT_RECONNECT, ...options.reconnect ?? {} };
433
+ const connection = createConnection({
434
+ schema,
435
+ emitter,
436
+ handlers,
437
+ reconnect: reconnectConfig,
438
+ url: options.url,
439
+ isDebug: () => debugEnabled
440
+ });
441
+ function resolveChannel(msg) {
442
+ return msg.channel ?? "*";
443
+ }
444
+ function newMessageId(channel) {
445
+ const MAX_ATTEMPTS = 10;
446
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
447
+ const id = schema.generateId();
448
+ if (id > 0 && handlers.findChannelByMessageId(id) === void 0) {
449
+ return id;
450
+ }
451
+ }
452
+ throw new Error(
453
+ `Failed to generate a unique message ID for channel "${channel}" after ${MAX_ATTEMPTS} attempts. Check your generateId() implementation.`
454
+ );
455
+ }
456
+ function send(msg) {
457
+ const channel = resolveChannel(msg);
458
+ const id = newMessageId(channel);
459
+ const wire = buildOutgoing(msg, id, schema);
460
+ connection.send(wire);
461
+ }
462
+ function request(msg, opts) {
463
+ const timeout = opts?.timeout ?? 3e4;
464
+ return new Promise((resolve, reject) => {
465
+ const channel = resolveChannel(msg);
466
+ const id = newMessageId(channel);
467
+ let timer = null;
468
+ const unsub = handlers.add(channel, id, {
469
+ type: "ephemeral",
470
+ callback(response) {
471
+ const codes = schema.codes;
472
+ if (codes?.interim && response.code === codes.interim) {
473
+ return false;
474
+ }
475
+ if (timer !== null) {
476
+ clearTimeout(timer);
477
+ timer = null;
478
+ }
479
+ if (codes?.error && codes.error.includes(response.code)) {
480
+ const error2 = {
481
+ code: response.code,
482
+ error: response.error,
483
+ data: response.data,
484
+ response
485
+ };
486
+ reject(error2);
487
+ return;
488
+ }
489
+ if (!codes?.success || response.code === codes.success) {
490
+ resolve(response);
491
+ return;
492
+ }
493
+ const error = {
494
+ code: response.code,
495
+ error: response.error,
496
+ data: response.data,
497
+ response
498
+ };
499
+ reject(error);
500
+ }
501
+ });
502
+ if (timeout > 0) {
503
+ timer = setTimeout(() => {
504
+ timer = null;
505
+ unsub();
506
+ reject(new Error(
507
+ `Request timeout after ${timeout}ms: ${channel}`
508
+ ));
509
+ }, timeout);
510
+ }
511
+ const wire = buildOutgoing(msg, id, schema);
512
+ connection.send(wire);
513
+ });
514
+ }
515
+ function fire(msg, callback, _opts) {
516
+ const channel = resolveChannel(msg);
517
+ const id = newMessageId(channel);
518
+ const unsub = handlers.add(channel, id, {
519
+ type: "ephemeral",
520
+ callback
521
+ });
522
+ const wire = buildOutgoing(msg, id, schema);
523
+ connection.send(wire);
524
+ return unsub;
525
+ }
526
+ function addHandler(channel, name, callback) {
527
+ return handlers.add(channel, name, {
528
+ type: "persistent",
529
+ callback,
530
+ name
531
+ });
532
+ }
533
+ function removeHandler(channel, name) {
534
+ return handlers.remove(channel, name);
535
+ }
536
+ function on(event, callback) {
537
+ return emitter.on(event, callback);
538
+ }
539
+ function once(event, callback) {
540
+ return emitter.once(event, callback);
541
+ }
542
+ function debug(enabled) {
543
+ debugEnabled = enabled;
544
+ }
545
+ function destroy() {
546
+ connection.destroy();
547
+ emitter.removeAll();
548
+ }
549
+ const transport = {
550
+ connect: () => connection.connect(),
551
+ disconnect: (opts) => connection.disconnect(opts),
552
+ get state() {
553
+ return connection.getState();
554
+ },
555
+ send,
556
+ request,
557
+ fire,
558
+ addHandler,
559
+ removeHandler,
560
+ on,
561
+ once,
562
+ protocol: schema,
563
+ debug,
564
+ destroy
565
+ };
566
+ return transport;
567
+ }
568
+
569
+ export { buildOutgoing, createEmitter, createHandlerStore, createTransport, normalizeIncoming, resolveSchema };
570
+ //# sourceMappingURL=index.js.map
571
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protocol.ts","../src/events.ts","../src/handlers.ts","../src/connection.ts","../src/transport.ts"],"names":["error"],"mappings":"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","file":"index.js","sourcesContent":["// =============================================================================\n// @silas/transport — Protocol\n//\n// Protocol schema helpers for normalizing incoming and building outgoing\n// wire-format messages. Includes `resolveSchema()` which applies sensible\n// defaults so consumers can provide a minimal (or empty) schema.\n// =============================================================================\n\nimport type {\n ProtocolSchema,\n ResolvedProtocolSchema,\n IncomingMessage,\n OutgoingMessage,\n} from './types.js';\n\n// ======================== DEFAULTS ===========================================\n\n/** Cryptographically random 32-bit unsigned integer. */\nfunction defaultGenerateId(): number {\n return crypto.getRandomValues(new Uint32Array(1))[0];\n}\n\nfunction defaultEncode(message: Record<string, unknown>): string {\n return JSON.stringify(message);\n}\n\nfunction defaultDecode(raw: string): Record<string, unknown> | null {\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n// ======================== RESOLVE ============================================\n\n/**\n * Apply defaults to a partial `ProtocolSchema`, producing a fully\n * resolved schema ready for internal use.\n */\nexport function resolveSchema(input?: ProtocolSchema): ResolvedProtocolSchema {\n return {\n fields: input?.fields ?? {},\n codes: input?.codes,\n generateId: input?.generateId ?? defaultGenerateId,\n encode: input?.encode ?? defaultEncode,\n decode: input?.decode ?? defaultDecode,\n flattenOutgoing: input?.flattenOutgoing ?? false,\n includeIdInRequest: input?.includeIdInRequest ?? false,\n };\n}\n\n// ======================== NORMALIZE / BUILD ==================================\n\n/**\n * Transform a raw wire message into the canonical IncomingMessage shape.\n * Reads field names from the protocol schema so the rest of the library\n * can work with a stable, protocol-agnostic structure.\n */\nexport function normalizeIncoming(\n raw: Record<string, unknown>,\n schema: ResolvedProtocolSchema,\n): IncomingMessage {\n const f = schema.fields;\n\n // Channel resolution: responseChannel → subscriptionChannel → '*'\n let channel = '';\n if (f.responseChannel) {\n channel = String(raw[f.responseChannel] ?? '');\n }\n if (!channel && f.subscriptionChannel) {\n channel = String(raw[f.subscriptionChannel] ?? '');\n }\n if (!channel) {\n channel = '*';\n }\n\n const rawId = f.messageId ? Number(raw[f.messageId] ?? 0) : 0;\n const messageId = Number.isNaN(rawId) ? 0 : rawId;\n\n // Event detection: has a resolved channel but no messageId.\n // For events, prefer eventBody over body.\n const isEvent = !messageId && channel !== '*';\n const bodyField = isEvent && f.eventBody ? f.eventBody : f.body;\n\n return {\n channel,\n messageId,\n code: f.code ? String(raw[f.code] ?? '') : '',\n description: f.description ? String(raw[f.description] ?? '') : '',\n error: f.error ? raw[f.error] : undefined,\n data: bodyField ? (raw[bodyField] as Record<string, unknown>) ?? {} : {},\n raw,\n };\n}\n\n/**\n * Build a wire-format message object from an OutgoingMessage.\n *\n * If `flattenOutgoing` is true, data keys are spread onto the root\n * alongside the channel and message ID fields.\n *\n * If false, data is nested under the data field name.\n */\nexport function buildOutgoing<PData = Record<string, unknown>>(\n msg: OutgoingMessage<PData>,\n messageId: number,\n schema: ResolvedProtocolSchema,\n): Record<string, unknown> {\n const f = schema.fields;\n const wire: Record<string, unknown> = {};\n\n // Only include the channel field when the schema defines a requestChannel.\n if (f.requestChannel) {\n wire[f.requestChannel] = msg.channel ?? '*';\n }\n\n // Only include the messageId on the wire when the schema opts in or use a default \"id\" falback field.\n if (schema.includeIdInRequest) {\n wire[f.messageId ?? 'id'] = messageId;\n }\n\n if (msg.data) {\n const data = msg.data as Record<string, unknown>;\n if (schema.flattenOutgoing) {\n // Flatten: spread data keys onto the root object.\n for (const key of Object.keys(data)) {\n wire[key] = data[key];\n }\n } else if (f.payload) {\n // Nested: data goes under its own field.\n wire[f.payload] = data;\n }\n }\n\n return wire;\n}\n","// =============================================================================\n// @silas/transport — Typed Event Emitter\n//\n// Minimal typed emitter (~50 lines). Replaces the 16 hook/set_hook pairs\n// Replaces setter-based hooks (e.g. set_hook / hook pattern)\n// with a standard event system:\n// transport.on('connected', fn) instead of set_al_abrir_conexion(fn)\n// =============================================================================\n\n/** Generic typed event emitter. */\nexport interface Emitter<TEvents extends object = Record<string, unknown>> {\n on<K extends keyof TEvents>(\n event: K,\n callback: (data: TEvents[K]) => void,\n ): () => void;\n\n once<K extends keyof TEvents>(\n event: K,\n callback: (data: TEvents[K]) => void,\n ): () => void;\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void;\n\n off<K extends keyof TEvents>(\n event: K,\n callback: (data: TEvents[K]) => void,\n ): void;\n\n removeAll(event?: keyof TEvents): void;\n}\n\ntype Listener = (data: never) => void;\n\n/**\n * Create a minimal typed event emitter.\n *\n * ```ts\n * const ee = createEmitter<TransportEvents>();\n * const unsub = ee.on('connected', (evt) => { ... });\n * ee.emit('connected', evt);\n * unsub(); // or ee.off('connected', callback)\n * ```\n */\nexport function createEmitter<\n TEvents extends object = Record<string, unknown>,\n>(): Emitter<TEvents> {\n const listeners = new Map<keyof TEvents, Set<Listener>>();\n\n function getSet(event: keyof TEvents): Set<Listener> {\n let set = listeners.get(event);\n if (!set) {\n set = new Set();\n listeners.set(event, set);\n }\n return set;\n }\n\n function on<K extends keyof TEvents>(\n event: K,\n callback: (data: TEvents[K]) => void,\n ): () => void {\n const set = getSet(event);\n const cb = callback as Listener;\n set.add(cb);\n return () => { set.delete(cb); };\n }\n\n function once<K extends keyof TEvents>(\n event: K,\n callback: (data: TEvents[K]) => void,\n ): () => void {\n const wrapper = (data: TEvents[K]): void => {\n off(event, wrapper);\n callback(data);\n };\n return on(event, wrapper);\n }\n\n function emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n const set = listeners.get(event);\n if (!set) return;\n // Iterate a snapshot so listeners can safely remove themselves.\n for (const cb of [...set]) {\n (cb as (data: TEvents[K]) => void)(data);\n }\n }\n\n function off<K extends keyof TEvents>(\n event: K,\n callback: (data: TEvents[K]) => void,\n ): void {\n const set = listeners.get(event);\n if (set) set.delete(callback as Listener);\n }\n\n function removeAll(event?: keyof TEvents): void {\n if (event !== undefined) {\n listeners.delete(event);\n } else {\n listeners.clear();\n }\n }\n\n return { on, once, emit, off, removeAll };\n}\n","// =============================================================================\n// @silas/transport — Unified Handler Registry\n//\n// Replaces separate persistent and ephemeral handler systems with a single\n// registry.\n//\n// Routing priority:\n// 1. Ephemeral handler matched by (channel, messageId) — exact match\n// 2. Persistent handlers matched by (channel) — all executed\n// 3. If nothing matched → message is unhandled\n//\n// Ephemeral handlers auto-remove after a definitive response (callback\n// returns true or void). Return false to keep alive (interim pattern).\n// =============================================================================\n\nimport type { Handler, IncomingMessage } from './types.js';\n\n// ======================== HANDLER STORE ======================================\n\nexport interface HandlerStore {\n /**\n * Register a handler.\n * - Persistent: key is the handler name (string).\n * - Ephemeral: key is the messageId (number).\n * Returns an unsubscribe function.\n */\n add(channel: string, key: string | number, handler: Handler): () => void;\n\n /** Remove a handler by channel + key. */\n remove(channel: string, key: string | number): boolean;\n\n /**\n * Route an incoming message to the appropriate handler(s).\n * Returns true if at least one handler processed the message.\n */\n execute(message: IncomingMessage): boolean;\n\n /** Check if an ephemeral handler exists for (channel, messageId). */\n hasEphemeral(channel: string, messageId: number): boolean;\n\n /** Look up the channel for a given messageId via the secondary index. */\n findChannelByMessageId(messageId: number): string | undefined;\n\n /** Clear all handlers. */\n clear(): void;\n\n /**\n * Clear stale ephemeral handlers for a given channel (or all channels).\n */\n clearStale(channel?: string): void;\n}\n\n// ======================== IMPLEMENTATION =====================================\n\n/**\n * Create a new handler store.\n *\n * Internal structure:\n * ephemeral: Map< channel, Map< messageId (number), Handler > >\n * persistent: Map< channel, Map< name (string), Handler > >\n */\nexport function createHandlerStore(): HandlerStore {\n const ephemeral = new Map<string, Map<number, Handler>>();\n const persistent = new Map<string, Map<string, Handler>>();\n /** Secondary index: messageId → channel for O(1) ID-only lookups. */\n const idToChannel = new Map<number, string>();\n\n // ---- helpers ----\n\n function getEphemeralMap(channel: string): Map<number, Handler> {\n let map = ephemeral.get(channel);\n if (!map) { map = new Map(); ephemeral.set(channel, map); }\n return map;\n }\n\n function getPersistentMap(channel: string): Map<string, Handler> {\n let map = persistent.get(channel);\n if (!map) { map = new Map(); persistent.set(channel, map); }\n return map;\n }\n\n // ---- public API ----\n\n function add(\n channel: string,\n key: string | number,\n handler: Handler,\n ): () => void {\n if (handler.type === 'ephemeral' && typeof key === 'number') {\n getEphemeralMap(channel).set(key, handler);\n idToChannel.set(key, channel);\n } else if (handler.type === 'persistent' && typeof key === 'string') {\n getPersistentMap(channel).set(key, handler);\n } else {\n throw new Error(\n `Invalid handler registration: type=${handler.type}, key type=${typeof key}. ` +\n `Ephemeral handlers require a numeric key (messageId), persistent require a string key (name).`,\n );\n }\n\n // Return unsubscribe.\n return () => { remove(channel, key); };\n }\n\n function remove(channel: string, key: string | number): boolean {\n if (typeof key === 'number') {\n const map = ephemeral.get(channel);\n if (!map) return false;\n const deleted = map.delete(key);\n if (deleted) idToChannel.delete(key);\n if (map.size === 0) ephemeral.delete(channel);\n return deleted;\n } else {\n const map = persistent.get(channel);\n if (!map) return false;\n const deleted = map.delete(key);\n if (map.size === 0) persistent.delete(channel);\n return deleted;\n }\n }\n\n function execute(message: IncomingMessage): boolean {\n const channel = message.channel;\n const msgId = message.messageId;\n\n // 1. Try ephemeral handler (exact channel + messageId match).\n const ephMap = ephemeral.get(channel);\n if (ephMap && msgId !== 0) {\n const handler = ephMap.get(msgId);\n if (handler) {\n const result = handler.callback(message);\n // Auto-remove unless callback explicitly returns false (interim).\n if (result !== false) {\n ephMap.delete(msgId);\n idToChannel.delete(msgId);\n if (ephMap.size === 0) ephemeral.delete(channel);\n }\n return true;\n }\n }\n\n // 2. Fall through to persistent handlers.\n const perMap = persistent.get(channel);\n if (perMap && perMap.size > 0) {\n for (const handler of perMap.values()) {\n handler.callback(message);\n }\n return true;\n }\n\n // 3. ID-only fallback: response arrived with no channel (or wrong channel)\n // but has a messageId — look up the original channel via secondary index.\n if (msgId !== 0) {\n const resolvedChannel = idToChannel.get(msgId);\n if (resolvedChannel && resolvedChannel !== channel) {\n const fallbackMap = ephemeral.get(resolvedChannel);\n if (fallbackMap) {\n const handler = fallbackMap.get(msgId);\n if (handler) {\n const result = handler.callback(message);\n if (result !== false) {\n fallbackMap.delete(msgId);\n idToChannel.delete(msgId);\n if (fallbackMap.size === 0) ephemeral.delete(resolvedChannel);\n }\n return true;\n }\n }\n }\n }\n\n // 4. Unhandled.\n return false;\n }\n\n function hasEphemeral(channel: string, messageId: number): boolean {\n return ephemeral.get(channel)?.has(messageId) ?? false;\n }\n\n function clear(): void {\n ephemeral.clear();\n persistent.clear();\n idToChannel.clear();\n }\n\n function clearStale(channel?: string): void {\n if (channel) {\n // Remove index entries for this channel's ephemeral handlers.\n const map = ephemeral.get(channel);\n if (map) {\n for (const msgId of map.keys()) {\n idToChannel.delete(msgId);\n }\n }\n ephemeral.delete(channel);\n } else {\n // Clear all ephemeral + index.\n ephemeral.clear();\n idToChannel.clear();\n }\n }\n\n function findChannelByMessageId(messageId: number): string | undefined {\n return idToChannel.get(messageId);\n }\n\n return { add, remove, execute, hasEphemeral, findChannelByMessageId, clear, clearStale };\n}\n","// =============================================================================\n// @silas/transport — Connection Manager\n//\n// Encapsulates all WebSocket lifecycle: connect, disconnect, send, receive,\n// auto-reconnect.\n//\n// Features:\n// - Configurable reconnect (delay, max attempts, backoff)\n// - Event-based lifecycle\n// - Idempotent connect\n// - Stale handler cleanup on disconnect\n// =============================================================================\n\nimport type {\n ResolvedProtocolSchema,\n TransportState,\n TransportEvents,\n ReconnectOptions,\n IncomingMessage,\n} from './types.js';\nimport type { Emitter } from './events.js';\nimport type { HandlerStore } from './handlers.js';\nimport { normalizeIncoming } from './protocol.js';\n\n// ======================== TYPES ==============================================\n\n/** @internal */\nexport interface ConnectionDeps {\n /** Resolved protocol schema. */\n schema: ResolvedProtocolSchema;\n /** Event emitter. */\n emitter: Emitter<TransportEvents>;\n /** Handler store for routing inbound messages. */\n handlers: HandlerStore;\n /** Resolved reconnect config (undefined = disabled). */\n reconnect: Required<ReconnectOptions> | undefined;\n /** URL string or lazy getter. */\n url: string | (() => string);\n /** Debug flag reference (getter so it reads the live value). */\n isDebug: () => boolean;\n}\n\nexport interface Connection {\n connect(): void;\n disconnect(options?: { clean?: boolean }): void;\n send(payload: Record<string, unknown>): void;\n getState(): TransportState;\n destroy(): void;\n}\n\n// ======================== IMPLEMENTATION =====================================\n\n/** WebSocket readyState constants (mirrors the spec). */\nconst WS_CONNECTING = 0;\nconst WS_OPEN = 1;\nconst WS_CLOSING = 2;\nconst WS_CLOSED = 3;\n\nexport function createConnection(deps: ConnectionDeps): Connection {\n const { schema, emitter, handlers } = deps;\n\n let ws: WebSocket | null = null;\n let state: TransportState = 'disconnected';\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let reconnectAttempt = 0;\n let destroyed = false;\n\n // ---- helpers ----\n\n function resolveUrl(): string {\n return typeof deps.url === 'function' ? deps.url() : deps.url;\n }\n\n function log(...args: unknown[]): void {\n if (deps.isDebug()) {\n console.log('[silas/transport]', ...args);\n }\n }\n\n function setState(next: TransportState): void {\n state = next;\n }\n\n function clearReconnectTimer(): void {\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n }\n\n function computeDelay(): number {\n if (!deps.reconnect) return 0;\n const { delayMs, backoff } = deps.reconnect;\n if (backoff === 'exponential') {\n // Exponential backoff capped at 60s.\n return Math.min(delayMs * Math.pow(2, reconnectAttempt), 60_000);\n }\n return delayMs;\n }\n\n function scheduleReconnect(): void {\n if (!deps.reconnect || !deps.reconnect.auto || destroyed) return;\n if (reconnectAttempt >= deps.reconnect.maxAttempts) {\n log('Max reconnect attempts reached:', deps.reconnect.maxAttempts);\n return;\n }\n\n const delay = computeDelay();\n reconnectAttempt++;\n setState('reconnecting');\n emitter.emit('reconnecting', { attempt: reconnectAttempt, delayMs: delay });\n log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempt})`);\n\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n }\n\n // ---- WebSocket event handlers ----\n\n function onOpen(evt: Event): void {\n reconnectAttempt = 0;\n setState('connected');\n log('Connected');\n emitter.emit('connected', evt);\n }\n\n function onClose(evt: CloseEvent): void {\n ws = null;\n const prev = state;\n setState('disconnected');\n log('Disconnected', evt.code, evt.reason);\n emitter.emit('disconnected', {\n code: evt.code,\n reason: evt.reason,\n wasClean: evt.wasClean,\n });\n\n // Auto-reconnect if not intentionally disconnected.\n if (prev !== 'disconnected' && !destroyed) {\n scheduleReconnect();\n }\n }\n\n function onError(evt: Event): void {\n log('WebSocket error', evt);\n emitter.emit('error', evt);\n }\n\n function onMessage(evt: MessageEvent): void {\n const raw = typeof evt.data === 'string' ? evt.data : String(evt.data);\n emitter.emit('message:raw', { data: raw });\n\n // Decode.\n const parsed = schema.decode(raw);\n if (!parsed) {\n log('Failed to decode message:', raw);\n return;\n }\n\n // Normalize to canonical shape.\n const message: IncomingMessage = normalizeIncoming(parsed, schema);\n log('←', message.channel, message.messageId, message.code);\n emitter.emit('message:parsed', message);\n\n // Validate minimum fields.\n // Allow messages through if they have a channel OR a messageId (for ID-only\n // fallback routing — e.g. responses that omit the channel field).\n if (!message.channel && message.messageId === 0) {\n log('Message missing channel and messageId, dropping');\n return;\n }\n\n // Route to handlers.\n const handled = handlers.execute(message);\n if (handled) {\n log('Handler matched:', message.channel, message.messageId);\n } else {\n emitter.emit('message:unhandled', message);\n log('Unhandled message:', message.channel, message.messageId);\n }\n }\n\n // ---- public API ----\n\n function connect(): void {\n if (destroyed) return;\n\n // Idempotent: don't re-open if already connecting or connected.\n if (ws) {\n const rs = ws.readyState;\n if (rs === WS_OPEN || rs === WS_CONNECTING) {\n log('Already connected/connecting, skipping');\n return;\n }\n }\n\n clearReconnectTimer();\n const url = resolveUrl();\n log('Connecting to', url);\n setState('connecting');\n emitter.emit('connecting', undefined);\n\n try {\n ws = new WebSocket(url);\n } catch (err) {\n log('WebSocket constructor error:', err);\n emitter.emit('error', new Event('error'));\n scheduleReconnect();\n return;\n }\n\n ws.onopen = onOpen;\n ws.onclose = onClose;\n ws.onerror = onError;\n ws.onmessage = onMessage;\n }\n\n function disconnect(options?: { clean?: boolean }): void {\n clearReconnectTimer();\n reconnectAttempt = 0;\n\n if (options?.clean) {\n handlers.clearStale();\n }\n\n if (ws) {\n const rs = ws.readyState;\n if (rs !== WS_CLOSED && rs !== WS_CLOSING) {\n log('Closing WebSocket');\n ws.close();\n }\n ws = null;\n }\n\n setState('disconnected');\n }\n\n function send(payload: Record<string, unknown>): void {\n emitter.emit('send:before', { payload });\n\n if (!ws || ws.readyState !== WS_OPEN) {\n const reason = ws\n ? `WebSocket readyState=${ws.readyState}`\n : 'No WebSocket instance';\n log('Send failed:', reason);\n emitter.emit('send:error', { payload, reason });\n\n // Attempt reconnect on send failure.\n if (!ws || ws.readyState === WS_CLOSED || ws.readyState === WS_CLOSING) {\n if (deps.reconnect?.auto && !destroyed) {\n clearReconnectTimer();\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, 1_000);\n }\n }\n return;\n }\n\n const encoded = schema.encode(payload);\n log('→', payload);\n ws.send(encoded);\n emitter.emit('send:after', { payload });\n }\n\n function getState(): TransportState {\n return state;\n }\n\n function destroy(): void {\n destroyed = true;\n disconnect();\n handlers.clear();\n }\n\n return { connect, disconnect, send, getState, destroy };\n}\n","// =============================================================================\n// @silas/transport — Main Factory\n//\n// createTransport() composes connection + handlers + events + protocol into\n// the unified Transport API. This is the primary public entry point.\n//\n// Three sending modes:\n// request(msg) → Promise<IncomingMessage> (modern async)\n// fire(msg, callback) → () => void (callback pattern)\n// send(msg) → void (fire-and-forget)\n// =============================================================================\n\nimport type {\n Transport,\n TransportOptions,\n TransportEvents,\n TransportError,\n IncomingMessage,\n OutgoingMessage,\n RequestOptions,\n FireOptions,\n HandlerCallback,\n ReconnectOptions,\n} from './types.js';\nimport { buildOutgoing, resolveSchema } from './protocol.js';\nimport { createEmitter } from './events.js';\nimport { createHandlerStore } from './handlers.js';\nimport { createConnection } from './connection.js';\n\n// ======================== DEFAULT RECONNECT ==================================\n\nconst DEFAULT_RECONNECT: Required<ReconnectOptions> = {\n auto: true,\n delayMs: 10_000,\n maxAttempts: Infinity,\n backoff: 'fixed',\n};\n\n// ======================== FACTORY ============================================\n\n/**\n * Create a Transport instance.\n *\n * ```ts\n * const transport = createTransport({\n * url: 'wss://api.example.com/ws',\n * protocol: myProtocol,\n * });\n *\n * transport.connect();\n * const res = await transport.request({ channel: 'getUser', data: { id: 5 } });\n * ```\n */\nexport function createTransport(options: TransportOptions): Transport {\n // ---- resolve config ----\n const schema = resolveSchema(options.protocol);\n const emitter = createEmitter<TransportEvents>();\n const handlers = createHandlerStore();\n\n let debugEnabled = options.debug ?? false;\n\n const reconnectConfig: Required<ReconnectOptions> | undefined =\n options.reconnect === false\n ? undefined\n : { ...DEFAULT_RECONNECT, ...(options.reconnect ?? {}) };\n\n const connection = createConnection({\n schema,\n emitter,\n handlers,\n reconnect: reconnectConfig,\n url: options.url,\n isDebug: () => debugEnabled,\n });\n\n // ---- generate unique message ID with collision avoidance ----\n\n function resolveChannel(msg: OutgoingMessage<unknown>): string {\n return msg.channel ?? '*';\n }\n\n function newMessageId(channel: string): number {\n const MAX_ATTEMPTS = 10;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const id = schema.generateId();\n if (id > 0 && handlers.findChannelByMessageId(id) === undefined) {\n return id;\n }\n }\n throw new Error(\n `Failed to generate a unique message ID for channel \"${channel}\" after ${MAX_ATTEMPTS} attempts. ` +\n `Check your generateId() implementation.`,\n );\n }\n\n // ---- public API ----\n\n function send<PData = Record<string, unknown>>(msg: OutgoingMessage<PData>): void {\n const channel = resolveChannel(msg);\n const id = newMessageId(channel);\n const wire = buildOutgoing(msg, id, schema);\n connection.send(wire);\n }\n\n function request<BData = Record<string, unknown>, PData = Record<string, unknown>, E = unknown>(\n msg: OutgoingMessage<PData>,\n opts?: RequestOptions,\n ): Promise<IncomingMessage<BData, E>> {\n const timeout = opts?.timeout ?? 30_000;\n\n return new Promise<IncomingMessage<BData, E>>((resolve, reject) => {\n const channel = resolveChannel(msg);\n const id = newMessageId(channel);\n \n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const unsub = handlers.add(channel, id, {\n type: 'ephemeral',\n callback(response: IncomingMessage): boolean | void {\n const codes = schema.codes;\n\n // 1. Interim — keep listening.\n if (codes?.interim && response.code === codes.interim) {\n return false;\n }\n\n // Definitive response — clean up timer.\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n\n // 2. Explicit error match — reject.\n if (codes?.error && codes.error.includes(response.code)) {\n const error: TransportError = {\n code: response.code,\n error: response.error,\n data: response.data,\n response,\n };\n reject(error);\n return; // auto-remove\n }\n\n // 3. Success: explicit match OR no success code defined (treat all as success).\n if (!codes?.success || response.code === codes.success) {\n resolve(response as IncomingMessage<BData, E>);\n return; // auto-remove\n }\n\n // 4. Success code is defined but response doesn't match — treat as error.\n const error: TransportError = {\n code: response.code,\n error: response.error,\n data: response.data,\n response,\n };\n reject(error);\n // Return void → auto-remove handler.\n },\n });\n\n // Timeout.\n if (timeout > 0) {\n timer = setTimeout(() => {\n timer = null;\n unsub();\n reject(new Error(\n `Request timeout after ${timeout}ms: ${channel}`,\n ));\n }, timeout);\n }\n\n // Send.\n const wire = buildOutgoing(msg, id, schema);\n connection.send(wire);\n });\n }\n\n function fire<BData = Record<string, unknown>, PData = Record<string, unknown>, E = unknown>(\n msg: OutgoingMessage<PData>,\n callback: HandlerCallback<BData, E>,\n _opts?: FireOptions,\n ): () => void {\n const channel = resolveChannel(msg);\n const id = newMessageId(channel);\n\n const unsub = handlers.add(channel, id, {\n type: 'ephemeral',\n callback: callback as HandlerCallback,\n });\n\n const wire = buildOutgoing(msg, id, schema);\n connection.send(wire);\n\n return unsub;\n }\n\n function addHandler<BData = Record<string, unknown>, E = unknown>(\n channel: string,\n name: string,\n callback: HandlerCallback<BData, E>,\n ): () => void {\n return handlers.add(channel, name, {\n type: 'persistent',\n callback: callback as HandlerCallback,\n name,\n });\n }\n\n function removeHandler(channel: string, name: string): boolean {\n return handlers.remove(channel, name);\n }\n\n function on<K extends keyof TransportEvents>(\n event: K,\n callback: (data: TransportEvents[K]) => void,\n ): () => void {\n return emitter.on(event, callback);\n }\n\n function once<K extends keyof TransportEvents>(\n event: K,\n callback: (data: TransportEvents[K]) => void,\n ): () => void {\n return emitter.once(event, callback);\n }\n\n function debug(enabled: boolean): void {\n debugEnabled = enabled;\n }\n\n function destroy(): void {\n connection.destroy();\n emitter.removeAll();\n }\n\n // ---- compose the Transport object ----\n\n const transport: Transport = {\n connect: () => connection.connect(),\n disconnect: (opts) => connection.disconnect(opts),\n get state() { return connection.getState(); },\n\n send,\n request,\n fire,\n\n addHandler,\n removeHandler,\n\n on,\n once,\n\n protocol: schema,\n debug,\n destroy,\n };\n\n return transport;\n}\n"]}