@holo-js/flux 0.1.9 → 0.2.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.d.ts +11 -0
- package/dist/index.mjs +413 -2
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -84,6 +84,15 @@ interface FluxClient<TManifest extends GeneratedBroadcastManifest = GeneratedBro
|
|
|
84
84
|
type PusherConnectorOptions = {
|
|
85
85
|
readonly transport?: 'mock';
|
|
86
86
|
};
|
|
87
|
+
type HoloWebSocketConnectorOptions = {
|
|
88
|
+
readonly key?: string;
|
|
89
|
+
readonly host?: string;
|
|
90
|
+
readonly port?: number;
|
|
91
|
+
readonly path?: string;
|
|
92
|
+
readonly scheme?: 'http' | 'https' | 'ws' | 'wss';
|
|
93
|
+
readonly authEndpoint?: string;
|
|
94
|
+
readonly configEndpoint?: string;
|
|
95
|
+
};
|
|
87
96
|
type PusherConnectorDebug = {
|
|
88
97
|
emitEvent(channel: string, event: string, payload: BroadcastJsonObject): void;
|
|
89
98
|
emitNotification(channel: string, payload: BroadcastJsonObject): void;
|
|
@@ -96,6 +105,7 @@ type ConnectorDebugCarrier = {
|
|
|
96
105
|
type SubscriptionRegistry = Map<string, Set<() => void>>;
|
|
97
106
|
declare function createUnavailableConnector(): FluxConnector;
|
|
98
107
|
declare function createPusherConnector(options?: PusherConnectorOptions): FluxConnector & ConnectorDebugCarrier;
|
|
108
|
+
declare function createHoloWebSocketConnector(options?: HoloWebSocketConnectorOptions): FluxConnector;
|
|
99
109
|
declare function createSubscription<TManifest extends GeneratedBroadcastManifest, TChannel extends string>(channelName: TChannel, kind: FluxChannelKind, connector: FluxConnector, registry: SubscriptionRegistry): FluxSubscription<TManifest, TChannel> & {
|
|
100
110
|
readonly __presenceMembers: () => readonly BroadcastJsonObject[];
|
|
101
111
|
readonly __onPresenceChange: (callback: (members: readonly BroadcastJsonObject[]) => void) => () => void;
|
|
@@ -107,6 +117,7 @@ declare function getFluxClient(): FluxClient;
|
|
|
107
117
|
declare function resetFluxClient(): void;
|
|
108
118
|
declare const flux: FluxClient<GeneratedBroadcastManifest>;
|
|
109
119
|
declare const fluxInternals: {
|
|
120
|
+
createHoloWebSocketConnector: typeof createHoloWebSocketConnector;
|
|
110
121
|
createUnavailableConnector: typeof createUnavailableConnector;
|
|
111
122
|
createPusherConnector: typeof createPusherConnector;
|
|
112
123
|
createPresenceSubscription: typeof createPresenceSubscription;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
var WEB_SOCKET_OPEN = 1;
|
|
2
3
|
function normalizeRequiredString(value, label) {
|
|
3
4
|
const normalized = value.trim();
|
|
4
5
|
if (!normalized) {
|
|
@@ -210,6 +211,410 @@ function createPusherConnector(options = {}) {
|
|
|
210
211
|
}
|
|
211
212
|
});
|
|
212
213
|
}
|
|
214
|
+
function parseJsonObject(value) {
|
|
215
|
+
const parsed = JSON.parse(value);
|
|
216
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
217
|
+
return Object.freeze({});
|
|
218
|
+
}
|
|
219
|
+
return parsed;
|
|
220
|
+
}
|
|
221
|
+
function isRecord(value) {
|
|
222
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
223
|
+
}
|
|
224
|
+
function parseWireData(value) {
|
|
225
|
+
if (typeof value === "string") {
|
|
226
|
+
return parseJsonObject(value);
|
|
227
|
+
}
|
|
228
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
return Object.freeze({});
|
|
232
|
+
}
|
|
233
|
+
function normalizeOptionalConnectorString(value) {
|
|
234
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
235
|
+
}
|
|
236
|
+
function normalizeOptionalConnectorPort(value) {
|
|
237
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
238
|
+
}
|
|
239
|
+
function normalizeOptionalConnectorScheme(value) {
|
|
240
|
+
if (value === "http" || value === "https" || value === "ws" || value === "wss") {
|
|
241
|
+
return value;
|
|
242
|
+
}
|
|
243
|
+
return void 0;
|
|
244
|
+
}
|
|
245
|
+
function mergeHoloWebSocketConnectorOptions(discovered, explicit) {
|
|
246
|
+
return Object.freeze({
|
|
247
|
+
...discovered,
|
|
248
|
+
...typeof explicit.key === "undefined" ? {} : { key: explicit.key },
|
|
249
|
+
...typeof explicit.host === "undefined" ? {} : { host: explicit.host },
|
|
250
|
+
...typeof explicit.port === "undefined" ? {} : { port: explicit.port },
|
|
251
|
+
...typeof explicit.path === "undefined" ? {} : { path: explicit.path },
|
|
252
|
+
...typeof explicit.scheme === "undefined" ? {} : { scheme: explicit.scheme },
|
|
253
|
+
...typeof explicit.authEndpoint === "undefined" ? {} : { authEndpoint: explicit.authEndpoint },
|
|
254
|
+
...typeof explicit.configEndpoint === "undefined" ? {} : { configEndpoint: explicit.configEndpoint }
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function normalizeHoloWebSocketConnectorConfig(value) {
|
|
258
|
+
if (!isRecord(value)) {
|
|
259
|
+
return Object.freeze({});
|
|
260
|
+
}
|
|
261
|
+
return Object.freeze({
|
|
262
|
+
...normalizeOptionalConnectorString(value.key) ? { key: normalizeOptionalConnectorString(value.key) } : {},
|
|
263
|
+
...normalizeOptionalConnectorString(value.host) ? { host: normalizeOptionalConnectorString(value.host) } : {},
|
|
264
|
+
...normalizeOptionalConnectorPort(value.port) ? { port: normalizeOptionalConnectorPort(value.port) } : {},
|
|
265
|
+
...normalizeOptionalConnectorString(value.path) ? { path: normalizeOptionalConnectorString(value.path) } : {},
|
|
266
|
+
...normalizeOptionalConnectorScheme(value.scheme) ? { scheme: normalizeOptionalConnectorScheme(value.scheme) } : {},
|
|
267
|
+
...normalizeOptionalConnectorString(value.authEndpoint) ? { authEndpoint: normalizeOptionalConnectorString(value.authEndpoint) } : {}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function canDiscoverHoloWebSocketConnectorOptions(globals) {
|
|
271
|
+
return !!globals.fetch && !!globals.location && typeof globals.window !== "undefined";
|
|
272
|
+
}
|
|
273
|
+
function hasExplicitHoloWebSocketConnectorOptions(options) {
|
|
274
|
+
return typeof options.key !== "undefined" || typeof options.host !== "undefined" || typeof options.port !== "undefined" || typeof options.path !== "undefined" || typeof options.scheme !== "undefined" || typeof options.authEndpoint !== "undefined";
|
|
275
|
+
}
|
|
276
|
+
async function resolveHoloWebSocketConnectorOptions(options, globals) {
|
|
277
|
+
if (!canDiscoverHoloWebSocketConnectorOptions(globals)) {
|
|
278
|
+
return options;
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
const response = await globals.fetch(options.configEndpoint ?? "/broadcasting/config", {
|
|
282
|
+
headers: {
|
|
283
|
+
accept: "application/json"
|
|
284
|
+
},
|
|
285
|
+
credentials: "same-origin"
|
|
286
|
+
});
|
|
287
|
+
if (!response.ok) {
|
|
288
|
+
return options;
|
|
289
|
+
}
|
|
290
|
+
return mergeHoloWebSocketConnectorOptions(
|
|
291
|
+
normalizeHoloWebSocketConnectorConfig(await response.json()),
|
|
292
|
+
options
|
|
293
|
+
);
|
|
294
|
+
} catch {
|
|
295
|
+
return options;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function wireChannelName(kind, name) {
|
|
299
|
+
if (kind === "private") {
|
|
300
|
+
return `private-${name}`;
|
|
301
|
+
}
|
|
302
|
+
if (kind === "presence") {
|
|
303
|
+
return `presence-${name}`;
|
|
304
|
+
}
|
|
305
|
+
return name;
|
|
306
|
+
}
|
|
307
|
+
function resolveWireChannelKey(channel) {
|
|
308
|
+
if (channel.startsWith("private-")) {
|
|
309
|
+
return `private:${channel.slice("private-".length)}`;
|
|
310
|
+
}
|
|
311
|
+
if (channel.startsWith("presence-")) {
|
|
312
|
+
return `presence:${channel.slice("presence-".length)}`;
|
|
313
|
+
}
|
|
314
|
+
return `public:${channel}`;
|
|
315
|
+
}
|
|
316
|
+
function resolveBrowserHost(host, globals) {
|
|
317
|
+
const normalized = host?.trim();
|
|
318
|
+
if (!normalized || normalized === "0.0.0.0" || normalized === "127.0.0.1") {
|
|
319
|
+
return globals.location?.hostname?.trim() || normalized || "127.0.0.1";
|
|
320
|
+
}
|
|
321
|
+
return normalized;
|
|
322
|
+
}
|
|
323
|
+
function resolveWebSocketScheme(scheme, globals) {
|
|
324
|
+
if (scheme === "wss" || scheme === "https") {
|
|
325
|
+
return "wss";
|
|
326
|
+
}
|
|
327
|
+
if (scheme === "ws" || scheme === "http") {
|
|
328
|
+
return "ws";
|
|
329
|
+
}
|
|
330
|
+
return globals.location?.protocol === "https:" ? "wss" : "ws";
|
|
331
|
+
}
|
|
332
|
+
function createHoloWebSocketConnector(options = {}) {
|
|
333
|
+
const channels = /* @__PURE__ */ new Map();
|
|
334
|
+
const statusListeners = /* @__PURE__ */ new Set();
|
|
335
|
+
let status = "idle";
|
|
336
|
+
let socket;
|
|
337
|
+
let connecting;
|
|
338
|
+
let resolvedOptions;
|
|
339
|
+
let socketId;
|
|
340
|
+
const setStatus = (next) => {
|
|
341
|
+
if (status === next) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
status = next;
|
|
345
|
+
notifyStatusListeners(statusListeners, status);
|
|
346
|
+
};
|
|
347
|
+
const sendMessage = (message) => {
|
|
348
|
+
if (socket?.readyState === WEB_SOCKET_OPEN) {
|
|
349
|
+
socket.send(JSON.stringify(message));
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const authorizeSubscription = async (state) => {
|
|
353
|
+
if (state.kind === "public") {
|
|
354
|
+
return Object.freeze({});
|
|
355
|
+
}
|
|
356
|
+
const endpoint = resolvedOptions?.authEndpoint;
|
|
357
|
+
if (!endpoint) {
|
|
358
|
+
return Object.freeze({});
|
|
359
|
+
}
|
|
360
|
+
const globals = globalThis;
|
|
361
|
+
if (!globals.fetch) {
|
|
362
|
+
throw new Error("[@holo-js/flux] Private channel authorization requires fetch support in this runtime.");
|
|
363
|
+
}
|
|
364
|
+
const channel = wireChannelName(state.kind, state.name);
|
|
365
|
+
const currentSocketId = normalizeRequiredString(socketId ?? "", "Socket id");
|
|
366
|
+
const response = await globals.fetch(endpoint, {
|
|
367
|
+
method: "POST",
|
|
368
|
+
headers: {
|
|
369
|
+
accept: "application/json",
|
|
370
|
+
"content-type": "application/x-www-form-urlencoded;charset=UTF-8"
|
|
371
|
+
},
|
|
372
|
+
credentials: "same-origin",
|
|
373
|
+
body: new URLSearchParams({
|
|
374
|
+
channel_name: channel,
|
|
375
|
+
socket_id: currentSocketId
|
|
376
|
+
})
|
|
377
|
+
});
|
|
378
|
+
if (!response.ok) {
|
|
379
|
+
throw new Error(`[@holo-js/flux] Channel authorization failed with HTTP ${response.status}.`);
|
|
380
|
+
}
|
|
381
|
+
const body = await response.json();
|
|
382
|
+
if (!isRecord(body) || typeof body.auth !== "string") {
|
|
383
|
+
throw new Error("[@holo-js/flux] Channel authorization response is invalid.");
|
|
384
|
+
}
|
|
385
|
+
return Object.freeze({
|
|
386
|
+
auth: body.auth,
|
|
387
|
+
...typeof body.channel_data === "string" ? { channel_data: body.channel_data } : {}
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
const flushSubscriptions = () => {
|
|
391
|
+
for (const state of channels.values()) {
|
|
392
|
+
if (state.subscribed) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
state.subscribed = true;
|
|
396
|
+
if (state.kind === "public" || !resolvedOptions?.authEndpoint) {
|
|
397
|
+
sendMessage({
|
|
398
|
+
event: "pusher:subscribe",
|
|
399
|
+
data: {
|
|
400
|
+
channel: wireChannelName(state.kind, state.name)
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
void authorizeSubscription(state).then((auth) => {
|
|
406
|
+
sendMessage({
|
|
407
|
+
event: "pusher:subscribe",
|
|
408
|
+
data: {
|
|
409
|
+
channel: wireChannelName(state.kind, state.name),
|
|
410
|
+
...auth
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}, () => {
|
|
414
|
+
state.subscribed = false;
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
const handleMessage = (event) => {
|
|
419
|
+
if (typeof event.data !== "string") {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const message = parseJsonObject(event.data);
|
|
423
|
+
const eventName = typeof message.event === "string" ? message.event : "";
|
|
424
|
+
const channel = typeof message.channel === "string" ? message.channel : void 0;
|
|
425
|
+
const payload = parseWireData(message.data);
|
|
426
|
+
if (eventName === "pusher:connection_established") {
|
|
427
|
+
socketId = typeof payload.socket_id === "string" ? payload.socket_id : void 0;
|
|
428
|
+
flushSubscriptions();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (!channel) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const state = channels.get(resolveWireChannelKey(channel));
|
|
435
|
+
if (!state) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (eventName === "pusher_internal:subscription_succeeded") {
|
|
439
|
+
const presence = payload.presence;
|
|
440
|
+
if (presence && typeof presence === "object" && !Array.isArray(presence)) {
|
|
441
|
+
const hash = presence.hash;
|
|
442
|
+
state.members = Object.freeze(
|
|
443
|
+
hash && typeof hash === "object" && !Array.isArray(hash) ? Object.values(hash) : []
|
|
444
|
+
);
|
|
445
|
+
for (const callback of state.memberListeners) {
|
|
446
|
+
callback(state.members);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (eventName === "pusher_internal:member_added") {
|
|
452
|
+
const member = parseWireData(payload.member);
|
|
453
|
+
state.members = Object.freeze([...state.members, member]);
|
|
454
|
+
for (const callback of state.memberListeners) {
|
|
455
|
+
callback(state.members);
|
|
456
|
+
}
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (eventName === "pusher_internal:member_removed") {
|
|
460
|
+
const member = parseWireData(payload.member);
|
|
461
|
+
const key = presenceMemberKey(member);
|
|
462
|
+
state.members = Object.freeze(state.members.filter((candidate) => presenceMemberKey(candidate) !== key));
|
|
463
|
+
for (const callback of state.memberListeners) {
|
|
464
|
+
callback(state.members);
|
|
465
|
+
}
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (eventName.startsWith("client-")) {
|
|
469
|
+
const whisper = eventName.slice("client-".length);
|
|
470
|
+
for (const callback of state.whisperListeners.get(whisper) ?? []) {
|
|
471
|
+
callback(payload);
|
|
472
|
+
}
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
for (const callback of state.eventListeners.get(eventName) ?? []) {
|
|
476
|
+
callback(payload);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
const ensureConnected = async () => {
|
|
480
|
+
const globals = globalThis;
|
|
481
|
+
const WebSocketConstructor = globals.WebSocket;
|
|
482
|
+
if (!WebSocketConstructor || !canDiscoverHoloWebSocketConnectorOptions(globals) && !hasExplicitHoloWebSocketConnectorOptions(options)) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (socket?.readyState === WEB_SOCKET_OPEN) {
|
|
486
|
+
flushSubscriptions();
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (connecting) {
|
|
490
|
+
await connecting;
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
resolvedOptions = canDiscoverHoloWebSocketConnectorOptions(globals) ? await resolveHoloWebSocketConnectorOptions(options, globals) : options;
|
|
494
|
+
const scheme = resolveWebSocketScheme(resolvedOptions.scheme, globals);
|
|
495
|
+
const host = resolveBrowserHost(resolvedOptions.host, globals);
|
|
496
|
+
const port = resolvedOptions.port ?? 8080;
|
|
497
|
+
const path = resolvedOptions.path?.trim() || "/app";
|
|
498
|
+
const key = resolvedOptions.key?.trim() || "app-key";
|
|
499
|
+
const normalizedPath = `/${path.replace(/^\/+|\/+$/g, "")}`;
|
|
500
|
+
const url = `${scheme}://${host}:${port}${normalizedPath}/${encodeURIComponent(key)}`;
|
|
501
|
+
setStatus("connecting");
|
|
502
|
+
connecting = new Promise((resolve, reject) => {
|
|
503
|
+
const nextSocket = new WebSocketConstructor(url);
|
|
504
|
+
socket = nextSocket;
|
|
505
|
+
nextSocket.addEventListener("open", () => {
|
|
506
|
+
setStatus("connected");
|
|
507
|
+
flushSubscriptions();
|
|
508
|
+
resolve();
|
|
509
|
+
});
|
|
510
|
+
nextSocket.addEventListener("message", handleMessage);
|
|
511
|
+
nextSocket.addEventListener("close", () => {
|
|
512
|
+
socket = void 0;
|
|
513
|
+
connecting = void 0;
|
|
514
|
+
for (const state of channels.values()) {
|
|
515
|
+
state.subscribed = false;
|
|
516
|
+
}
|
|
517
|
+
setStatus("disconnected");
|
|
518
|
+
});
|
|
519
|
+
nextSocket.addEventListener("error", () => {
|
|
520
|
+
connecting = void 0;
|
|
521
|
+
setStatus("disconnected");
|
|
522
|
+
reject(new Error("[@holo-js/flux] WebSocket connection failed."));
|
|
523
|
+
});
|
|
524
|
+
}).finally(() => {
|
|
525
|
+
connecting = void 0;
|
|
526
|
+
});
|
|
527
|
+
await connecting;
|
|
528
|
+
};
|
|
529
|
+
const ensureChannel = (name, kind) => {
|
|
530
|
+
const key = `${kind}:${name}`;
|
|
531
|
+
const existing = channels.get(key);
|
|
532
|
+
if (existing) {
|
|
533
|
+
return existing;
|
|
534
|
+
}
|
|
535
|
+
const state = {
|
|
536
|
+
name,
|
|
537
|
+
kind,
|
|
538
|
+
eventListeners: /* @__PURE__ */ new Map(),
|
|
539
|
+
whisperListeners: /* @__PURE__ */ new Map(),
|
|
540
|
+
notificationListeners: /* @__PURE__ */ new Set(),
|
|
541
|
+
memberListeners: /* @__PURE__ */ new Set(),
|
|
542
|
+
members: Object.freeze([]),
|
|
543
|
+
subscribed: false
|
|
544
|
+
};
|
|
545
|
+
channels.set(key, state);
|
|
546
|
+
return state;
|
|
547
|
+
};
|
|
548
|
+
return Object.freeze({
|
|
549
|
+
async connect() {
|
|
550
|
+
await ensureConnected();
|
|
551
|
+
},
|
|
552
|
+
async disconnect() {
|
|
553
|
+
socket?.close();
|
|
554
|
+
socket = void 0;
|
|
555
|
+
connecting = void 0;
|
|
556
|
+
channels.clear();
|
|
557
|
+
setStatus("disconnected");
|
|
558
|
+
},
|
|
559
|
+
getStatus() {
|
|
560
|
+
return status;
|
|
561
|
+
},
|
|
562
|
+
onStatusChange(callback) {
|
|
563
|
+
statusListeners.add(callback);
|
|
564
|
+
return () => {
|
|
565
|
+
statusListeners.delete(callback);
|
|
566
|
+
};
|
|
567
|
+
},
|
|
568
|
+
subscribe(channel, kind) {
|
|
569
|
+
const state = ensureChannel(channel, kind);
|
|
570
|
+
void ensureConnected().catch(() => void 0);
|
|
571
|
+
return Object.freeze({
|
|
572
|
+
name: state.name,
|
|
573
|
+
kind: state.kind,
|
|
574
|
+
get members() {
|
|
575
|
+
return state.members;
|
|
576
|
+
},
|
|
577
|
+
onEvent(event, callback) {
|
|
578
|
+
return addCallback(state.eventListeners, event, callback);
|
|
579
|
+
},
|
|
580
|
+
onMembersChange(callback) {
|
|
581
|
+
state.memberListeners.add(callback);
|
|
582
|
+
return () => {
|
|
583
|
+
state.memberListeners.delete(callback);
|
|
584
|
+
};
|
|
585
|
+
},
|
|
586
|
+
onNotification(callback) {
|
|
587
|
+
state.notificationListeners.add(callback);
|
|
588
|
+
return () => {
|
|
589
|
+
state.notificationListeners.delete(callback);
|
|
590
|
+
};
|
|
591
|
+
},
|
|
592
|
+
onWhisper(name, callback) {
|
|
593
|
+
return addCallback(state.whisperListeners, name, callback);
|
|
594
|
+
},
|
|
595
|
+
async sendWhisper(name, payload) {
|
|
596
|
+
await ensureConnected();
|
|
597
|
+
sendMessage({
|
|
598
|
+
event: `client-${name}`,
|
|
599
|
+
channel: wireChannelName(state.kind, state.name),
|
|
600
|
+
data: payload
|
|
601
|
+
});
|
|
602
|
+
},
|
|
603
|
+
leave() {
|
|
604
|
+
if (state.subscribed) {
|
|
605
|
+
sendMessage({
|
|
606
|
+
event: "pusher:unsubscribe",
|
|
607
|
+
data: {
|
|
608
|
+
channel: wireChannelName(state.kind, state.name)
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
channels.delete(`${state.kind}:${state.name}`);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
213
618
|
function createSubscription(channelName, kind, connector, registry) {
|
|
214
619
|
const connectorChannel = connector.subscribe(channelName, kind);
|
|
215
620
|
let active = true;
|
|
@@ -450,7 +855,12 @@ function createFluxClient(options = {}) {
|
|
|
450
855
|
};
|
|
451
856
|
return Object.freeze(client);
|
|
452
857
|
}
|
|
453
|
-
|
|
858
|
+
function createDefaultFluxClient() {
|
|
859
|
+
return createFluxClient({
|
|
860
|
+
connector: createHoloWebSocketConnector()
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
var defaultFluxClient = createDefaultFluxClient();
|
|
454
864
|
function configureFluxClient(options) {
|
|
455
865
|
defaultFluxClient = "channel" in options ? options : createFluxClient(options);
|
|
456
866
|
return defaultFluxClient;
|
|
@@ -459,7 +869,7 @@ function getFluxClient() {
|
|
|
459
869
|
return defaultFluxClient;
|
|
460
870
|
}
|
|
461
871
|
function resetFluxClient() {
|
|
462
|
-
defaultFluxClient =
|
|
872
|
+
defaultFluxClient = createDefaultFluxClient();
|
|
463
873
|
}
|
|
464
874
|
var flux = new Proxy({}, {
|
|
465
875
|
get(_target, property) {
|
|
@@ -473,6 +883,7 @@ var flux = new Proxy({}, {
|
|
|
473
883
|
}
|
|
474
884
|
});
|
|
475
885
|
var fluxInternals = {
|
|
886
|
+
createHoloWebSocketConnector,
|
|
476
887
|
createUnavailableConnector,
|
|
477
888
|
createPusherConnector,
|
|
478
889
|
createPresenceSubscription,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/flux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Holo-JS Framework - framework-agnostic realtime client skeleton for broadcast and presence",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"test": "vitest --run"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@holo-js/broadcast": "^0.
|
|
26
|
+
"@holo-js/broadcast": "^0.2.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^22.10.2",
|