@sovereignbase/station-client 0.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.cjs ADDED
@@ -0,0 +1,346 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;/*
2
+ * Copyright 2026 Sovereignbase
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+
18
+ // src/StationClient/class.ts
19
+ var _msgpack = require('@msgpack/msgpack');
20
+ var StationClient = (_class = class {
21
+ __init() {this.eventTarget = new EventTarget()}
22
+
23
+
24
+
25
+ __init2() {this.instanceId = self.crypto.randomUUID()}
26
+ __init3() {this.onlineHandler = () => {
27
+ void this.opportunisticConnect();
28
+ }}
29
+ __init4() {this.broadcastChannel = null}
30
+ __init5() {this.webSocket = null}
31
+ __init6() {this.isLeader = false}
32
+ __init7() {this.isClosed = false}
33
+ __init8() {this.isConnecting = false}
34
+ __init9() {this.outboundQueue = []}
35
+ __init10() {this.pendingTransacts = /* @__PURE__ */ new Map()}
36
+ __init11() {this.pendingTransactTargets = /* @__PURE__ */ new Map()}
37
+ /**
38
+ * Initializes a new {@link StationClient} instance.
39
+ *
40
+ * @param webSocketUrl The base station WebSocket URL. When omitted, the instance operates in local-only mode.
41
+ */
42
+ constructor(webSocketUrl = "") {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);
43
+ this.webSocketUrl = webSocketUrl;
44
+ this.channelName = `origin-channel-lock::${this.webSocketUrl}`;
45
+ this.lockName = `origin-channel-lock::${this.webSocketUrl}`;
46
+ this.broadcastChannel = new BroadcastChannel(this.channelName);
47
+ this.broadcastChannel.onmessage = (event) => {
48
+ const envelope = event.data;
49
+ if (!envelope) return;
50
+ if (envelope.kind === "relay") {
51
+ this.eventTarget.dispatchEvent(
52
+ new CustomEvent("message", { detail: envelope.message })
53
+ );
54
+ if (!this.isLeader) return;
55
+ this.sendToStation(envelope.message);
56
+ return;
57
+ }
58
+ if (envelope.kind === "transact-response") {
59
+ if (envelope.target !== this.instanceId) return;
60
+ const pending = this.pendingTransacts.get(envelope.id);
61
+ if (!pending) return;
62
+ this.pendingTransacts.delete(envelope.id);
63
+ pending.cleanup();
64
+ pending.resolve(envelope.message);
65
+ return;
66
+ }
67
+ if (envelope.kind === "transact-abort") {
68
+ if (!this.isLeader) return;
69
+ const pendingTarget2 = this.pendingTransactTargets.get(envelope.id);
70
+ if (pendingTarget2) clearTimeout(pendingTarget2.timeoutId);
71
+ this.pendingTransactTargets.delete(envelope.id);
72
+ return;
73
+ }
74
+ if (!this.isLeader) return;
75
+ if (!this.webSocketUrl || self.navigator.onLine !== true || !this.webSocket || this.webSocket.readyState !== WebSocket.OPEN) {
76
+ _optionalChain([this, 'access', _ => _.broadcastChannel, 'optionalAccess', _2 => _2.postMessage, 'call', _3 => _3({
77
+ kind: "transact-response",
78
+ id: envelope.id,
79
+ target: envelope.source,
80
+ message: false
81
+ })]);
82
+ return;
83
+ }
84
+ const pendingTarget = this.pendingTransactTargets.get(envelope.id);
85
+ if (pendingTarget) clearTimeout(pendingTarget.timeoutId);
86
+ this.pendingTransactTargets.set(envelope.id, {
87
+ target: envelope.source,
88
+ timeoutId: setTimeout(() => {
89
+ this.pendingTransactTargets.delete(envelope.id);
90
+ }, _nullishCoalesce(envelope.ttlMs, () => ( 3e4)))
91
+ });
92
+ this.sendToStation([
93
+ "station-client-request",
94
+ envelope.id,
95
+ envelope.message
96
+ ]);
97
+ };
98
+ if (this.webSocketUrl && navigator.onLine) void this.opportunisticConnect();
99
+ if (this.webSocketUrl) {
100
+ self.addEventListener("online", this.onlineHandler);
101
+ }
102
+ }
103
+ /**main methods*/
104
+ /**
105
+ * Broadcasts a message to other same-origin contexts and opportunistically forwards it to the base station.
106
+ *
107
+ * @param message The message to broadcast.
108
+ */
109
+ relay(message) {
110
+ if (this.isClosed) return;
111
+ _optionalChain([this, 'access', _4 => _4.broadcastChannel, 'optionalAccess', _5 => _5.postMessage, 'call', _6 => _6({ kind: "relay", message })]);
112
+ this.sendToStation(message);
113
+ }
114
+ /**
115
+ * Sends a request to the base station and resolves with the corresponding response message.
116
+ *
117
+ * @param message The message to send.
118
+ * @param options Options that control cancellation and stale follower cleanup.
119
+ * @returns A promise that resolves with the response message, or `false` when the request cannot be issued.
120
+ */
121
+ transact(message, options = {}) {
122
+ if (this.isClosed) return Promise.resolve(false);
123
+ const id = self.crypto.randomUUID();
124
+ const { signal, ttlMs } = options;
125
+ return new Promise((resolve, reject) => {
126
+ const abortReason = () => _nullishCoalesce(_optionalChain([signal, 'optionalAccess', _7 => _7.reason]), () => ( new DOMException("The operation was aborted.", "AbortError")));
127
+ if (_optionalChain([signal, 'optionalAccess', _8 => _8.aborted])) {
128
+ reject(abortReason());
129
+ return;
130
+ }
131
+ if (!this.webSocketUrl || self.navigator.onLine !== true) {
132
+ resolve(false);
133
+ return;
134
+ }
135
+ if (this.isLeader && (!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN)) {
136
+ resolve(false);
137
+ return;
138
+ }
139
+ const handleAbort = () => {
140
+ this.pendingTransacts.delete(id);
141
+ const pendingTarget = this.pendingTransactTargets.get(id);
142
+ if (pendingTarget) clearTimeout(pendingTarget.timeoutId);
143
+ this.pendingTransactTargets.delete(id);
144
+ _optionalChain([signal, 'optionalAccess', _9 => _9.removeEventListener, 'call', _10 => _10("abort", handleAbort)]);
145
+ if (!this.isLeader) {
146
+ _optionalChain([this, 'access', _11 => _11.broadcastChannel, 'optionalAccess', _12 => _12.postMessage, 'call', _13 => _13({ kind: "transact-abort", id })]);
147
+ }
148
+ reject(abortReason());
149
+ };
150
+ this.pendingTransacts.set(id, {
151
+ resolve,
152
+ reject,
153
+ cleanup: () => {
154
+ _optionalChain([signal, 'optionalAccess', _14 => _14.removeEventListener, 'call', _15 => _15("abort", handleAbort)]);
155
+ }
156
+ });
157
+ _optionalChain([signal, 'optionalAccess', _16 => _16.addEventListener, 'call', _17 => _17("abort", handleAbort, { once: true })]);
158
+ if (this.isLeader) {
159
+ this.sendToStation(["station-client-request", id, message]);
160
+ return;
161
+ }
162
+ _optionalChain([this, 'access', _18 => _18.broadcastChannel, 'optionalAccess', _19 => _19.postMessage, 'call', _20 => _20({
163
+ kind: "transact",
164
+ id,
165
+ source: this.instanceId,
166
+ ttlMs,
167
+ message
168
+ })]);
169
+ });
170
+ }
171
+ /**
172
+ * Closes the client and releases its local and remote resources.
173
+ */
174
+ close() {
175
+ const wasLeader = this.isLeader;
176
+ const broadcastChannel = this.broadcastChannel;
177
+ this.isClosed = true;
178
+ self.removeEventListener("online", this.onlineHandler);
179
+ if (!wasLeader) {
180
+ for (const id of this.pendingTransacts.keys()) {
181
+ try {
182
+ _optionalChain([broadcastChannel, 'optionalAccess', _21 => _21.postMessage, 'call', _22 => _22({ kind: "transact-abort", id })]);
183
+ } catch (e) {
184
+ }
185
+ }
186
+ }
187
+ try {
188
+ _optionalChain([broadcastChannel, 'optionalAccess', _23 => _23.close, 'call', _24 => _24()]);
189
+ } catch (e2) {
190
+ }
191
+ try {
192
+ _optionalChain([this, 'access', _25 => _25.webSocket, 'optionalAccess', _26 => _26.close, 'call', _27 => _27(1e3, "closed")]);
193
+ } catch (e3) {
194
+ }
195
+ this.broadcastChannel = null;
196
+ this.webSocket = null;
197
+ this.isLeader = false;
198
+ this.outboundQueue.length = 0;
199
+ for (const pending of this.pendingTransacts.values()) {
200
+ pending.cleanup();
201
+ pending.reject(new Error("Station client closed"));
202
+ }
203
+ this.pendingTransacts.clear();
204
+ for (const pendingTarget of this.pendingTransactTargets.values()) {
205
+ clearTimeout(pendingTarget.timeoutId);
206
+ }
207
+ this.pendingTransactTargets.clear();
208
+ }
209
+ /**listeners*/
210
+ /**
211
+ * Appends an event listener for events whose type attribute value is `type`.
212
+ *
213
+ * @param type The event type to listen for.
214
+ * @param listener The callback that receives the event.
215
+ * @param options An options object that specifies characteristics about the event listener.
216
+ */
217
+ addEventListener(type, listener, options) {
218
+ this.eventTarget.addEventListener(
219
+ type,
220
+ listener,
221
+ options
222
+ );
223
+ }
224
+ /**
225
+ * Removes an event listener previously registered with {@link addEventListener}.
226
+ *
227
+ * @param type The event type to remove.
228
+ * @param listener The callback to remove.
229
+ * @param options An options object that specifies characteristics about the event listener.
230
+ */
231
+ removeEventListener(type, listener, options) {
232
+ this.eventTarget.removeEventListener(
233
+ type,
234
+ listener,
235
+ options
236
+ );
237
+ }
238
+ /**helpers*/
239
+ sendToStation(message) {
240
+ if (!this.isLeader || !this.webSocketUrl) return;
241
+ if (!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN) {
242
+ if (self.navigator.onLine) {
243
+ if (this.outboundQueue.length >= 64) this.outboundQueue.shift();
244
+ this.outboundQueue.push(message);
245
+ }
246
+ return;
247
+ }
248
+ try {
249
+ this.webSocket.send(_msgpack.encode.call(void 0, message));
250
+ } catch (e4) {
251
+ }
252
+ }
253
+ flushOutboundQueue() {
254
+ if (!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN) return;
255
+ while (this.outboundQueue.length > 0) {
256
+ const message = this.outboundQueue.shift();
257
+ if (!message) continue;
258
+ try {
259
+ this.webSocket.send(_msgpack.encode.call(void 0, message));
260
+ } catch (e5) {
261
+ this.outboundQueue.unshift(message);
262
+ return;
263
+ }
264
+ }
265
+ }
266
+ async opportunisticConnect() {
267
+ if (this.isClosed || this.isConnecting || !this.webSocketUrl) return;
268
+ if (!self.navigator.locks) return;
269
+ this.isConnecting = true;
270
+ try {
271
+ while (!this.isClosed) {
272
+ if (self.navigator.onLine !== true) return;
273
+ await self.navigator.locks.request(
274
+ this.lockName,
275
+ { ifAvailable: true },
276
+ async (lockHandle) => {
277
+ if (!lockHandle || this.isClosed) return;
278
+ this.isLeader = true;
279
+ let socket;
280
+ try {
281
+ socket = new WebSocket(this.webSocketUrl);
282
+ } catch (e6) {
283
+ this.isLeader = false;
284
+ this.webSocket = null;
285
+ return;
286
+ }
287
+ socket.binaryType = "arraybuffer";
288
+ this.webSocket = socket;
289
+ socket.onopen = () => {
290
+ this.flushOutboundQueue();
291
+ };
292
+ socket.onmessage = (event) => {
293
+ const message = _msgpack.decode.call(void 0, event.data);
294
+ if (!message) return;
295
+ if (Array.isArray(message) && message[0] === "station-client-response" && typeof message[1] === "string") {
296
+ const id = message[1];
297
+ const pendingTarget = this.pendingTransactTargets.get(id);
298
+ if (pendingTarget) {
299
+ clearTimeout(pendingTarget.timeoutId);
300
+ this.pendingTransactTargets.delete(id);
301
+ _optionalChain([this, 'access', _28 => _28.broadcastChannel, 'optionalAccess', _29 => _29.postMessage, 'call', _30 => _30({
302
+ kind: "transact-response",
303
+ id,
304
+ target: pendingTarget.target,
305
+ message: message[2]
306
+ })]);
307
+ return;
308
+ }
309
+ const pending = this.pendingTransacts.get(id);
310
+ if (!pending) return;
311
+ this.pendingTransacts.delete(id);
312
+ pending.cleanup();
313
+ pending.resolve(message[2]);
314
+ return;
315
+ }
316
+ this.eventTarget.dispatchEvent(
317
+ new CustomEvent("message", { detail: message })
318
+ );
319
+ _optionalChain([this, 'access', _31 => _31.broadcastChannel, 'optionalAccess', _32 => _32.postMessage, 'call', _33 => _33({
320
+ kind: "relay",
321
+ message
322
+ })]);
323
+ };
324
+ socket.onclose = () => {
325
+ if (this.webSocket === socket) this.webSocket = null;
326
+ this.isLeader = false;
327
+ };
328
+ await new Promise((resolve) => {
329
+ socket.addEventListener("close", () => resolve(), { once: true });
330
+ });
331
+ this.isLeader = false;
332
+ if (this.webSocket === socket) this.webSocket = null;
333
+ }
334
+ );
335
+ if (this.isClosed || self.navigator.onLine !== true) return;
336
+ await new Promise((resolve) => setTimeout(resolve, 1e4));
337
+ }
338
+ } finally {
339
+ this.isConnecting = false;
340
+ }
341
+ }
342
+ }, _class);
343
+
344
+
345
+ exports.StationClient = StationClient;
346
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["c:\\Users\\jorts\\station-client\\dist\\index.cjs"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,2CAAiD;AACjD,IAAI,cAAc,YAAE,MAAM;AAC1B,iBAAE,YAAY,EAAE,IAAI,WAAW,CAAC,EAAC;AACjC,EAAE;AACF,EAAE;AACF,EAAE;AACF,kBAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAC;AACvC,kBAAE,cAAc,EAAE,CAAC,EAAE,GAAG;AACxB,IAAI,KAAK,IAAI,CAAC,oBAAoB,CAAC,CAAC;AACpC,EAAE,EAAC;AACH,kBAAE,iBAAiB,EAAE,KAAI;AACzB,kBAAE,UAAU,EAAE,KAAI;AAClB,kBAAE,SAAS,EAAE,MAAK;AAClB,kBAAE,SAAS,EAAE,MAAK;AAClB,kBAAE,aAAa,EAAE,MAAK;AACtB,kBAAE,cAAc,EAAE,CAAC,EAAC;AACpB,mBAAE,iBAAiB,kBAAkB,IAAI,GAAG,CAAC,EAAC;AAC9C,mBAAE,uBAAuB,kBAAkB,IAAI,GAAG,CAAC,EAAC;AACpD;AACA;AACA;AACA;AACA;AACA,EAAE,WAAW,CAAC,aAAa,EAAE,EAAE,EAAE;AACjC,IAAI,IAAI,CAAC,aAAa,EAAE,YAAY;AACpC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC,qBAAqB,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,wBAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,oBAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,wBAAA;AACA,QAAA;AACA,0BAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,0BAAA;AACA,QAAA;AACA,MAAA;AACA,sBAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,sBAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,0BAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,sBAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,sBAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,gBAAA;AACA,gBAAA;AACA,kBAAA;AACA,kBAAA;AACA,kCAAA;AACA,oBAAA;AACA,oBAAA;AACA,oBAAA;AACA,oBAAA;AACA,kBAAA;AACA,kBAAA;AACA,gBAAA;AACA,gBAAA;AACA,gBAAA;AACA,gBAAA;AACA,gBAAA;AACA,gBAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,cAAA;AACA,8BAAA;AACA,gBAAA;AACA,gBAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA","file":"C:\\Users\\jorts\\station-client\\dist\\index.cjs","sourcesContent":[null]}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Maps station client event names to their event payload shapes.
3
+ *
4
+ * @template T The application message shape.
5
+ */
6
+ type StationClientEventMap<T extends Record<string, unknown>> = {
7
+ message: T;
8
+ };
9
+ /**
10
+ * Represents a strongly typed station client event listener.
11
+ *
12
+ * @template T The application message shape.
13
+ * @template K The event type.
14
+ */
15
+ type StationClientEventListener<T extends Record<string, unknown>, K extends keyof StationClientEventMap<T>> = ((event: CustomEvent<StationClientEventMap<T>[K]>) => void) | {
16
+ handleEvent(event: CustomEvent<StationClientEventMap<T>[K]>): void;
17
+ };
18
+ /**
19
+ * Resolves an event name to its corresponding listener type.
20
+ *
21
+ * @template T The application message shape.
22
+ * @template K The event type.
23
+ */
24
+ type StationClientEventListenerFor<T extends Record<string, unknown>, K extends string> = K extends keyof StationClientEventMap<T> ? StationClientEventListener<T, K> : EventListenerOrEventListenerObject;
25
+ /**
26
+ * Provides options for {@link StationClient.transact}.
27
+ */
28
+ type StationClientTransactOptions = {
29
+ /**
30
+ * An {@link AbortSignal} that can be used to cancel the operation.
31
+ */
32
+ signal?: AbortSignal;
33
+ /**
34
+ * The leader-side stale-entry time-to-live, in milliseconds.
35
+ */
36
+ ttlMs?: number;
37
+ };
38
+
39
+ /**
40
+ * Represents a base station client that coordinates local tab messaging and an opportunistic base station transport.
41
+ *
42
+ * @template T The application message shape.
43
+ */
44
+ declare class StationClient<T extends Record<string, unknown>> {
45
+ private readonly eventTarget;
46
+ private readonly lockName;
47
+ private readonly channelName;
48
+ private readonly webSocketUrl;
49
+ private readonly instanceId;
50
+ private readonly onlineHandler;
51
+ private broadcastChannel;
52
+ private webSocket;
53
+ private isLeader;
54
+ private isClosed;
55
+ private isConnecting;
56
+ private readonly outboundQueue;
57
+ private readonly pendingTransacts;
58
+ private readonly pendingTransactTargets;
59
+ /**
60
+ * Initializes a new {@link StationClient} instance.
61
+ *
62
+ * @param webSocketUrl The base station WebSocket URL. When omitted, the instance operates in local-only mode.
63
+ */
64
+ constructor(webSocketUrl?: string);
65
+ /**main methods*/
66
+ /**
67
+ * Broadcasts a message to other same-origin contexts and opportunistically forwards it to the base station.
68
+ *
69
+ * @param message The message to broadcast.
70
+ */
71
+ relay(message: T): void;
72
+ /**
73
+ * Sends a request to the base station and resolves with the corresponding response message.
74
+ *
75
+ * @param message The message to send.
76
+ * @param options Options that control cancellation and stale follower cleanup.
77
+ * @returns A promise that resolves with the response message, or `false` when the request cannot be issued.
78
+ */
79
+ transact(message: T, options?: StationClientTransactOptions): Promise<T | false>;
80
+ /**
81
+ * Closes the client and releases its local and remote resources.
82
+ */
83
+ close(): void;
84
+ /**listeners*/
85
+ /**
86
+ * Appends an event listener for events whose type attribute value is `type`.
87
+ *
88
+ * @param type The event type to listen for.
89
+ * @param listener The callback that receives the event.
90
+ * @param options An options object that specifies characteristics about the event listener.
91
+ */
92
+ addEventListener<K extends keyof StationClientEventMap<T>>(type: K, listener: StationClientEventListenerFor<T, K> | null, options?: boolean | AddEventListenerOptions): void;
93
+ /**
94
+ * Removes an event listener previously registered with {@link addEventListener}.
95
+ *
96
+ * @param type The event type to remove.
97
+ * @param listener The callback to remove.
98
+ * @param options An options object that specifies characteristics about the event listener.
99
+ */
100
+ removeEventListener<K extends keyof StationClientEventMap<T>>(type: K, listener: StationClientEventListenerFor<T, K> | null, options?: boolean | EventListenerOptions): void;
101
+ /**helpers*/
102
+ private sendToStation;
103
+ private flushOutboundQueue;
104
+ private opportunisticConnect;
105
+ }
106
+
107
+ export { StationClient };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Maps station client event names to their event payload shapes.
3
+ *
4
+ * @template T The application message shape.
5
+ */
6
+ type StationClientEventMap<T extends Record<string, unknown>> = {
7
+ message: T;
8
+ };
9
+ /**
10
+ * Represents a strongly typed station client event listener.
11
+ *
12
+ * @template T The application message shape.
13
+ * @template K The event type.
14
+ */
15
+ type StationClientEventListener<T extends Record<string, unknown>, K extends keyof StationClientEventMap<T>> = ((event: CustomEvent<StationClientEventMap<T>[K]>) => void) | {
16
+ handleEvent(event: CustomEvent<StationClientEventMap<T>[K]>): void;
17
+ };
18
+ /**
19
+ * Resolves an event name to its corresponding listener type.
20
+ *
21
+ * @template T The application message shape.
22
+ * @template K The event type.
23
+ */
24
+ type StationClientEventListenerFor<T extends Record<string, unknown>, K extends string> = K extends keyof StationClientEventMap<T> ? StationClientEventListener<T, K> : EventListenerOrEventListenerObject;
25
+ /**
26
+ * Provides options for {@link StationClient.transact}.
27
+ */
28
+ type StationClientTransactOptions = {
29
+ /**
30
+ * An {@link AbortSignal} that can be used to cancel the operation.
31
+ */
32
+ signal?: AbortSignal;
33
+ /**
34
+ * The leader-side stale-entry time-to-live, in milliseconds.
35
+ */
36
+ ttlMs?: number;
37
+ };
38
+
39
+ /**
40
+ * Represents a base station client that coordinates local tab messaging and an opportunistic base station transport.
41
+ *
42
+ * @template T The application message shape.
43
+ */
44
+ declare class StationClient<T extends Record<string, unknown>> {
45
+ private readonly eventTarget;
46
+ private readonly lockName;
47
+ private readonly channelName;
48
+ private readonly webSocketUrl;
49
+ private readonly instanceId;
50
+ private readonly onlineHandler;
51
+ private broadcastChannel;
52
+ private webSocket;
53
+ private isLeader;
54
+ private isClosed;
55
+ private isConnecting;
56
+ private readonly outboundQueue;
57
+ private readonly pendingTransacts;
58
+ private readonly pendingTransactTargets;
59
+ /**
60
+ * Initializes a new {@link StationClient} instance.
61
+ *
62
+ * @param webSocketUrl The base station WebSocket URL. When omitted, the instance operates in local-only mode.
63
+ */
64
+ constructor(webSocketUrl?: string);
65
+ /**main methods*/
66
+ /**
67
+ * Broadcasts a message to other same-origin contexts and opportunistically forwards it to the base station.
68
+ *
69
+ * @param message The message to broadcast.
70
+ */
71
+ relay(message: T): void;
72
+ /**
73
+ * Sends a request to the base station and resolves with the corresponding response message.
74
+ *
75
+ * @param message The message to send.
76
+ * @param options Options that control cancellation and stale follower cleanup.
77
+ * @returns A promise that resolves with the response message, or `false` when the request cannot be issued.
78
+ */
79
+ transact(message: T, options?: StationClientTransactOptions): Promise<T | false>;
80
+ /**
81
+ * Closes the client and releases its local and remote resources.
82
+ */
83
+ close(): void;
84
+ /**listeners*/
85
+ /**
86
+ * Appends an event listener for events whose type attribute value is `type`.
87
+ *
88
+ * @param type The event type to listen for.
89
+ * @param listener The callback that receives the event.
90
+ * @param options An options object that specifies characteristics about the event listener.
91
+ */
92
+ addEventListener<K extends keyof StationClientEventMap<T>>(type: K, listener: StationClientEventListenerFor<T, K> | null, options?: boolean | AddEventListenerOptions): void;
93
+ /**
94
+ * Removes an event listener previously registered with {@link addEventListener}.
95
+ *
96
+ * @param type The event type to remove.
97
+ * @param listener The callback to remove.
98
+ * @param options An options object that specifies characteristics about the event listener.
99
+ */
100
+ removeEventListener<K extends keyof StationClientEventMap<T>>(type: K, listener: StationClientEventListenerFor<T, K> | null, options?: boolean | EventListenerOptions): void;
101
+ /**helpers*/
102
+ private sendToStation;
103
+ private flushOutboundQueue;
104
+ private opportunisticConnect;
105
+ }
106
+
107
+ export { StationClient };