@quiltt/core 3.6.12 → 3.6.13
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/CHANGELOG.md +6 -0
- package/dist/{SubscriptionLink-client-CGUaoxuA.js → SubscriptionLink-client-C-9ecaeZ.js} +2 -2
- package/dist/index.d.ts +1 -76
- package/dist/index.js +2 -2
- package/package.json +3 -1
- package/src/api/graphql/links/ActionCableLink.ts +5 -4
- package/src/api/graphql/links/BatchHttpLink.ts +2 -2
- package/src/api/graphql/links/HttpLink.ts +1 -1
- package/src/api/graphql/links/VersionLink.ts +1 -1
- package/dist/index-client-AzYaJaCS.js +0 -560
- package/src/api/graphql/links/actioncable/README.md +0 -1
- package/src/api/graphql/links/actioncable/adapters.ts +0 -4
- package/src/api/graphql/links/actioncable/connection.ts +0 -188
- package/src/api/graphql/links/actioncable/connection_monitor.ts +0 -141
- package/src/api/graphql/links/actioncable/consumer.ts +0 -86
- package/src/api/graphql/links/actioncable/index.ts +0 -35
- package/src/api/graphql/links/actioncable/internal.ts +0 -19
- package/src/api/graphql/links/actioncable/logger.ts +0 -18
- package/src/api/graphql/links/actioncable/subscription.ts +0 -45
- package/src/api/graphql/links/actioncable/subscription_guarantor.ts +0 -54
- package/src/api/graphql/links/actioncable/subscriptions.ts +0 -113
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { d as debugging } from './SubscriptionLink-client-CGUaoxuA.js';
|
|
3
|
-
|
|
4
|
-
var adapters = {
|
|
5
|
-
logger: typeof globalThis !== 'undefined' ? globalThis.console : undefined,
|
|
6
|
-
WebSocket: typeof globalThis !== 'undefined' ? globalThis.WebSocket : undefined
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
class Logger {
|
|
10
|
-
get enabled() {
|
|
11
|
-
return debugging;
|
|
12
|
-
}
|
|
13
|
-
log(...messages) {
|
|
14
|
-
if (adapters.logger && this.enabled) {
|
|
15
|
-
messages.push(Date.now().toString());
|
|
16
|
-
adapters.logger.log('[ActionCable]', ...messages);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const logger = new Logger();
|
|
21
|
-
|
|
22
|
-
// @ts-nocheck
|
|
23
|
-
// Responsible for ensuring the cable connection is in good health by validating the heartbeat pings sent from the server, and attempting
|
|
24
|
-
// revival reconnections if things go astray. Internal class, not intended for direct user manipulation.
|
|
25
|
-
const now = ()=>new Date().getTime();
|
|
26
|
-
const secondsSince = (time)=>(now() - time) / 1000;
|
|
27
|
-
class ConnectionMonitor {
|
|
28
|
-
constructor(connection){
|
|
29
|
-
this.visibilityDidChange = this.visibilityDidChange.bind(this);
|
|
30
|
-
this.connection = connection;
|
|
31
|
-
this.reconnectAttempts = 0;
|
|
32
|
-
}
|
|
33
|
-
start() {
|
|
34
|
-
if (!this.isRunning()) {
|
|
35
|
-
this.startedAt = now();
|
|
36
|
-
// biome-ignore lint/performance/noDelete: <explanation>
|
|
37
|
-
delete this.stoppedAt;
|
|
38
|
-
this.startPolling();
|
|
39
|
-
addEventListener('visibilitychange', this.visibilityDidChange);
|
|
40
|
-
logger.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
stop() {
|
|
44
|
-
if (this.isRunning()) {
|
|
45
|
-
this.stoppedAt = now();
|
|
46
|
-
this.stopPolling();
|
|
47
|
-
removeEventListener('visibilitychange', this.visibilityDidChange);
|
|
48
|
-
logger.log('ConnectionMonitor stopped');
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
isRunning() {
|
|
52
|
-
return this.startedAt && !this.stoppedAt;
|
|
53
|
-
}
|
|
54
|
-
recordPing() {
|
|
55
|
-
this.pingedAt = now();
|
|
56
|
-
}
|
|
57
|
-
recordConnect() {
|
|
58
|
-
this.reconnectAttempts = 0;
|
|
59
|
-
this.recordPing();
|
|
60
|
-
// biome-ignore lint/performance/noDelete: <explanation>
|
|
61
|
-
delete this.disconnectedAt;
|
|
62
|
-
logger.log('ConnectionMonitor recorded connect');
|
|
63
|
-
}
|
|
64
|
-
recordDisconnect() {
|
|
65
|
-
this.disconnectedAt = now();
|
|
66
|
-
logger.log('ConnectionMonitor recorded disconnect');
|
|
67
|
-
}
|
|
68
|
-
// Private
|
|
69
|
-
startPolling() {
|
|
70
|
-
this.stopPolling();
|
|
71
|
-
this.poll();
|
|
72
|
-
}
|
|
73
|
-
stopPolling() {
|
|
74
|
-
clearTimeout(this.pollTimeout);
|
|
75
|
-
}
|
|
76
|
-
poll() {
|
|
77
|
-
this.pollTimeout = setTimeout(()=>{
|
|
78
|
-
this.reconnectIfStale();
|
|
79
|
-
this.poll();
|
|
80
|
-
}, this.getPollInterval());
|
|
81
|
-
}
|
|
82
|
-
getPollInterval() {
|
|
83
|
-
const { staleThreshold, reconnectionBackoffRate } = this.constructor;
|
|
84
|
-
const backoff = (1 + reconnectionBackoffRate) ** Math.min(this.reconnectAttempts, 10);
|
|
85
|
-
const jitterMax = this.reconnectAttempts === 0 ? 1.0 : reconnectionBackoffRate;
|
|
86
|
-
const jitter = jitterMax * Math.random();
|
|
87
|
-
return staleThreshold * 1000 * backoff * (1 + jitter);
|
|
88
|
-
}
|
|
89
|
-
reconnectIfStale() {
|
|
90
|
-
if (this.connectionIsStale()) {
|
|
91
|
-
logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
|
|
92
|
-
this.reconnectAttempts++;
|
|
93
|
-
if (this.disconnectedRecently()) {
|
|
94
|
-
logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
|
|
95
|
-
} else {
|
|
96
|
-
logger.log('ConnectionMonitor reopening');
|
|
97
|
-
this.connection.reopen();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
get refreshedAt() {
|
|
102
|
-
return this.pingedAt ? this.pingedAt : this.startedAt;
|
|
103
|
-
}
|
|
104
|
-
connectionIsStale() {
|
|
105
|
-
return secondsSince(this.refreshedAt) > this.constructor.staleThreshold;
|
|
106
|
-
}
|
|
107
|
-
disconnectedRecently() {
|
|
108
|
-
return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
|
|
109
|
-
}
|
|
110
|
-
visibilityDidChange() {
|
|
111
|
-
if (document.visibilityState === 'visible') {
|
|
112
|
-
setTimeout(()=>{
|
|
113
|
-
if (this.connectionIsStale() || !this.connection.isOpen()) {
|
|
114
|
-
logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`);
|
|
115
|
-
this.connection.reopen();
|
|
116
|
-
}
|
|
117
|
-
}, 200);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
ConnectionMonitor.staleThreshold = 6 // Server::Connections::BEAT_INTERVAL * 2 (missed two pings)
|
|
122
|
-
;
|
|
123
|
-
ConnectionMonitor.reconnectionBackoffRate = 0.15;
|
|
124
|
-
|
|
125
|
-
const INTERNAL = {
|
|
126
|
-
message_types: {
|
|
127
|
-
welcome: 'welcome',
|
|
128
|
-
disconnect: 'disconnect',
|
|
129
|
-
ping: 'ping',
|
|
130
|
-
confirmation: 'confirm_subscription',
|
|
131
|
-
rejection: 'reject_subscription'
|
|
132
|
-
},
|
|
133
|
-
disconnect_reasons: {
|
|
134
|
-
unauthorized: 'unauthorized',
|
|
135
|
-
invalid_request: 'invalid_request',
|
|
136
|
-
server_restart: 'server_restart',
|
|
137
|
-
remote: 'remote'
|
|
138
|
-
},
|
|
139
|
-
default_mount_path: '/cable',
|
|
140
|
-
protocols: [
|
|
141
|
-
'actioncable-v1-json',
|
|
142
|
-
'actioncable-unsupported'
|
|
143
|
-
]
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// @ts-nocheck
|
|
147
|
-
// Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
|
|
148
|
-
const { message_types, protocols } = INTERNAL;
|
|
149
|
-
const supportedProtocols = protocols.slice(0, protocols.length - 1);
|
|
150
|
-
const indexOf = [].indexOf;
|
|
151
|
-
class Connection {
|
|
152
|
-
constructor(consumer){
|
|
153
|
-
this.open = this.open.bind(this);
|
|
154
|
-
this.consumer = consumer;
|
|
155
|
-
this.subscriptions = this.consumer.subscriptions;
|
|
156
|
-
this.monitor = new ConnectionMonitor(this);
|
|
157
|
-
this.disconnected = true;
|
|
158
|
-
}
|
|
159
|
-
send(data) {
|
|
160
|
-
if (this.isOpen()) {
|
|
161
|
-
this.webSocket.send(JSON.stringify(data));
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
open() {
|
|
167
|
-
if (this.isActive()) {
|
|
168
|
-
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
const socketProtocols = [
|
|
172
|
-
...protocols,
|
|
173
|
-
...this.consumer.subprotocols || []
|
|
174
|
-
];
|
|
175
|
-
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
|
|
176
|
-
if (this.webSocket) {
|
|
177
|
-
this.uninstallEventHandlers();
|
|
178
|
-
}
|
|
179
|
-
this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
|
|
180
|
-
this.installEventHandlers();
|
|
181
|
-
this.monitor.start();
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
close({ allowReconnect } = {
|
|
185
|
-
allowReconnect: true
|
|
186
|
-
}) {
|
|
187
|
-
if (!allowReconnect) {
|
|
188
|
-
this.monitor.stop();
|
|
189
|
-
}
|
|
190
|
-
// Avoid closing websockets in a "connecting" state due to Safari 15.1+ bug. See: https://github.com/rails/rails/issues/43835#issuecomment-1002288478
|
|
191
|
-
if (this.isOpen()) {
|
|
192
|
-
return this.webSocket.close();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
reopen() {
|
|
196
|
-
logger.log(`Reopening WebSocket, current state is ${this.getState()}`);
|
|
197
|
-
if (this.isActive()) {
|
|
198
|
-
try {
|
|
199
|
-
return this.close();
|
|
200
|
-
} catch (error) {
|
|
201
|
-
logger.log('Failed to reopen WebSocket', error);
|
|
202
|
-
} finally{
|
|
203
|
-
logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
|
|
204
|
-
setTimeout(this.open, this.constructor.reopenDelay);
|
|
205
|
-
}
|
|
206
|
-
} else {
|
|
207
|
-
return this.open();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
getProtocol() {
|
|
211
|
-
if (this.webSocket) {
|
|
212
|
-
return this.webSocket.protocol;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
isOpen() {
|
|
216
|
-
return this.isState('open');
|
|
217
|
-
}
|
|
218
|
-
isActive() {
|
|
219
|
-
return this.isState('open', 'connecting');
|
|
220
|
-
}
|
|
221
|
-
triedToReconnect() {
|
|
222
|
-
return this.monitor.reconnectAttempts > 0;
|
|
223
|
-
}
|
|
224
|
-
// Private
|
|
225
|
-
isProtocolSupported() {
|
|
226
|
-
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
|
|
227
|
-
}
|
|
228
|
-
isState(...states) {
|
|
229
|
-
return indexOf.call(states, this.getState()) >= 0;
|
|
230
|
-
}
|
|
231
|
-
getState() {
|
|
232
|
-
if (this.webSocket) {
|
|
233
|
-
for(const state in adapters.WebSocket){
|
|
234
|
-
if (adapters.WebSocket[state] === this.webSocket.readyState) {
|
|
235
|
-
return state.toLowerCase();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
installEventHandlers() {
|
|
242
|
-
for(const eventName in this.events){
|
|
243
|
-
const handler = this.events[eventName].bind(this);
|
|
244
|
-
this.webSocket[`on${eventName}`] = handler;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
uninstallEventHandlers() {
|
|
248
|
-
for(const eventName in this.events){
|
|
249
|
-
this.webSocket[`on${eventName}`] = ()=>{};
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
Connection.reopenDelay = 500;
|
|
254
|
-
Connection.prototype.events = {
|
|
255
|
-
message (event) {
|
|
256
|
-
if (!this.isProtocolSupported()) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const { identifier, message, reason, reconnect, type } = JSON.parse(event.data);
|
|
260
|
-
switch(type){
|
|
261
|
-
case message_types.welcome:
|
|
262
|
-
if (this.triedToReconnect()) {
|
|
263
|
-
this.reconnectAttempted = true;
|
|
264
|
-
}
|
|
265
|
-
this.monitor.recordConnect();
|
|
266
|
-
return this.subscriptions.reload();
|
|
267
|
-
case message_types.disconnect:
|
|
268
|
-
logger.log(`Disconnecting. Reason: ${reason}`);
|
|
269
|
-
return this.close({
|
|
270
|
-
allowReconnect: reconnect
|
|
271
|
-
});
|
|
272
|
-
case message_types.ping:
|
|
273
|
-
return this.monitor.recordPing();
|
|
274
|
-
case message_types.confirmation:
|
|
275
|
-
this.subscriptions.confirmSubscription(identifier);
|
|
276
|
-
if (this.reconnectAttempted) {
|
|
277
|
-
this.reconnectAttempted = false;
|
|
278
|
-
return this.subscriptions.notify(identifier, 'connected', {
|
|
279
|
-
reconnected: true
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
return this.subscriptions.notify(identifier, 'connected', {
|
|
283
|
-
reconnected: false
|
|
284
|
-
});
|
|
285
|
-
case message_types.rejection:
|
|
286
|
-
return this.subscriptions.reject(identifier);
|
|
287
|
-
default:
|
|
288
|
-
return this.subscriptions.notify(identifier, 'received', message);
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
open () {
|
|
292
|
-
logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`);
|
|
293
|
-
this.disconnected = false;
|
|
294
|
-
if (!this.isProtocolSupported()) {
|
|
295
|
-
logger.log('Protocol is unsupported. Stopping monitor and disconnecting.');
|
|
296
|
-
return this.close({
|
|
297
|
-
allowReconnect: false
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
close (_event) {
|
|
302
|
-
logger.log('WebSocket onclose event');
|
|
303
|
-
if (this.disconnected) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
this.disconnected = true;
|
|
307
|
-
this.monitor.recordDisconnect();
|
|
308
|
-
return this.subscriptions.notifyAll('disconnected', {
|
|
309
|
-
willAttemptReconnect: this.monitor.isRunning()
|
|
310
|
-
});
|
|
311
|
-
},
|
|
312
|
-
error () {
|
|
313
|
-
logger.log('WebSocket onerror event');
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
const extend = (object, properties)=>{
|
|
318
|
-
if (properties !== null) {
|
|
319
|
-
for(const key in properties){
|
|
320
|
-
const value = properties[key];
|
|
321
|
-
object[key] = value;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return object;
|
|
325
|
-
};
|
|
326
|
-
class Subscription {
|
|
327
|
-
// biome-ignore lint/style/useDefaultParameterLast: <explanation>
|
|
328
|
-
constructor(consumer, params = {}, mixin){
|
|
329
|
-
this.consumer = consumer;
|
|
330
|
-
this.identifier = JSON.stringify(params);
|
|
331
|
-
extend(this, mixin);
|
|
332
|
-
}
|
|
333
|
-
// Perform a channel action with the optional data passed as an attribute
|
|
334
|
-
perform(action, data = {}) {
|
|
335
|
-
data.action = action;
|
|
336
|
-
return this.send(data);
|
|
337
|
-
}
|
|
338
|
-
send(data) {
|
|
339
|
-
return this.consumer.send({
|
|
340
|
-
command: 'message',
|
|
341
|
-
identifier: this.identifier,
|
|
342
|
-
data: JSON.stringify(data)
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
unsubscribe() {
|
|
346
|
-
return this.consumer.subscriptions.remove(this);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Responsible for ensuring channel subscribe command is confirmed, retrying until confirmation is received.
|
|
351
|
-
// Internal class, not intended for direct user manipulation.
|
|
352
|
-
class SubscriptionGuarantor {
|
|
353
|
-
constructor(subscriptions){
|
|
354
|
-
this.subscriptions = subscriptions;
|
|
355
|
-
this.pendingSubscriptions = [];
|
|
356
|
-
}
|
|
357
|
-
guarantee(subscription) {
|
|
358
|
-
if (this.pendingSubscriptions.indexOf(subscription) === -1) {
|
|
359
|
-
logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
|
|
360
|
-
this.pendingSubscriptions.push(subscription);
|
|
361
|
-
} else {
|
|
362
|
-
logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
|
|
363
|
-
}
|
|
364
|
-
this.startGuaranteeing();
|
|
365
|
-
}
|
|
366
|
-
forget(subscription) {
|
|
367
|
-
logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
|
|
368
|
-
this.pendingSubscriptions = this.pendingSubscriptions.filter((s)=>s !== subscription);
|
|
369
|
-
}
|
|
370
|
-
startGuaranteeing() {
|
|
371
|
-
this.stopGuaranteeing();
|
|
372
|
-
this.retrySubscribing();
|
|
373
|
-
}
|
|
374
|
-
stopGuaranteeing() {
|
|
375
|
-
clearTimeout(this.retryTimeout);
|
|
376
|
-
}
|
|
377
|
-
retrySubscribing() {
|
|
378
|
-
this.retryTimeout = setTimeout(()=>{
|
|
379
|
-
if (this.subscriptions && typeof this.subscriptions.subscribe === 'function') {
|
|
380
|
-
this.pendingSubscriptions.map((subscription)=>{
|
|
381
|
-
logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
|
|
382
|
-
this.subscriptions.subscribe(subscription);
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
}, 500);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Collection class for creating (and internally managing) channel subscriptions.
|
|
390
|
-
// The only method intended to be triggered by the user is ActionCable.Subscriptions#create,
|
|
391
|
-
// and it should be called through the consumer like so:
|
|
392
|
-
//
|
|
393
|
-
// App = {}
|
|
394
|
-
// App.cable = ActionCable.createConsumer("ws://example.com/accounts/1")
|
|
395
|
-
// App.appearance = App.cable.subscriptions.create("AppearanceChannel")
|
|
396
|
-
//
|
|
397
|
-
// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.
|
|
398
|
-
class Subscriptions {
|
|
399
|
-
constructor(consumer){
|
|
400
|
-
this.consumer = consumer;
|
|
401
|
-
this.guarantor = new SubscriptionGuarantor(this);
|
|
402
|
-
this.subscriptions = [];
|
|
403
|
-
}
|
|
404
|
-
create(channelName, mixin) {
|
|
405
|
-
const channel = channelName;
|
|
406
|
-
const params = typeof channel === 'object' ? channel : {
|
|
407
|
-
channel
|
|
408
|
-
};
|
|
409
|
-
const subscription = new Subscription(this.consumer, params, mixin);
|
|
410
|
-
return this.add(subscription);
|
|
411
|
-
}
|
|
412
|
-
// Private
|
|
413
|
-
add(subscription) {
|
|
414
|
-
this.subscriptions.push(subscription);
|
|
415
|
-
this.consumer.ensureActiveConnection();
|
|
416
|
-
this.notify(subscription, 'initialized');
|
|
417
|
-
this.subscribe(subscription);
|
|
418
|
-
return subscription;
|
|
419
|
-
}
|
|
420
|
-
remove(subscription) {
|
|
421
|
-
this.forget(subscription);
|
|
422
|
-
if (!this.findAll(subscription.identifier).length) {
|
|
423
|
-
this.sendCommand(subscription, 'unsubscribe');
|
|
424
|
-
}
|
|
425
|
-
return subscription;
|
|
426
|
-
}
|
|
427
|
-
reject(identifier) {
|
|
428
|
-
return this.findAll(identifier).map((subscription)=>{
|
|
429
|
-
this.forget(subscription);
|
|
430
|
-
this.notify(subscription, 'rejected');
|
|
431
|
-
return subscription;
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
forget(subscription) {
|
|
435
|
-
this.guarantor.forget(subscription);
|
|
436
|
-
this.subscriptions = this.subscriptions.filter((s)=>s !== subscription);
|
|
437
|
-
return subscription;
|
|
438
|
-
}
|
|
439
|
-
findAll(identifier) {
|
|
440
|
-
return this.subscriptions.filter((s)=>s.identifier === identifier);
|
|
441
|
-
}
|
|
442
|
-
reload() {
|
|
443
|
-
return this.subscriptions.map((subscription)=>this.subscribe(subscription));
|
|
444
|
-
}
|
|
445
|
-
notifyAll(callbackName, ...args) {
|
|
446
|
-
return this.subscriptions.map((subscription)=>this.notify(subscription, callbackName, ...args));
|
|
447
|
-
}
|
|
448
|
-
notify(subscription, callbackName, ...args) {
|
|
449
|
-
let subscriptions;
|
|
450
|
-
if (typeof subscription === 'string') {
|
|
451
|
-
subscriptions = this.findAll(subscription);
|
|
452
|
-
} else {
|
|
453
|
-
subscriptions = [
|
|
454
|
-
subscription
|
|
455
|
-
];
|
|
456
|
-
}
|
|
457
|
-
return subscriptions.map((subscription)=>typeof subscription[callbackName] === 'function' ? subscription[callbackName](...args) : undefined);
|
|
458
|
-
}
|
|
459
|
-
subscribe(subscription) {
|
|
460
|
-
if (this.sendCommand(subscription, 'subscribe')) {
|
|
461
|
-
this.guarantor.guarantee(subscription);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
confirmSubscription(identifier) {
|
|
465
|
-
logger.log(`Subscription confirmed ${identifier}`);
|
|
466
|
-
this.findAll(identifier).map((subscription)=>this.guarantor.forget(subscription));
|
|
467
|
-
}
|
|
468
|
-
sendCommand(subscription, command) {
|
|
469
|
-
const { identifier } = subscription;
|
|
470
|
-
return this.consumer.send({
|
|
471
|
-
command,
|
|
472
|
-
identifier
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// The ActionCable.Consumer establishes the connection to a server-side Ruby Connection object. Once established,
|
|
478
|
-
// the ActionCable.ConnectionMonitor will ensure that its properly maintained through heartbeats and checking for stale updates.
|
|
479
|
-
// The Consumer instance is also the gateway to establishing subscriptions to desired channels through the #createSubscription
|
|
480
|
-
// method.
|
|
481
|
-
//
|
|
482
|
-
// The following example shows how this can be set up:
|
|
483
|
-
//
|
|
484
|
-
// App = {}
|
|
485
|
-
// App.cable = ActionCable.createConsumer("ws://example.com/accounts/1")
|
|
486
|
-
// App.appearance = App.cable.subscriptions.create("AppearanceChannel")
|
|
487
|
-
//
|
|
488
|
-
// For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.
|
|
489
|
-
//
|
|
490
|
-
// When a consumer is created, it automatically connects with the server.
|
|
491
|
-
//
|
|
492
|
-
// To disconnect from the server, call
|
|
493
|
-
//
|
|
494
|
-
// App.cable.disconnect()
|
|
495
|
-
//
|
|
496
|
-
// and to restart the connection:
|
|
497
|
-
//
|
|
498
|
-
// App.cable.connect()
|
|
499
|
-
//
|
|
500
|
-
// Any channel subscriptions which existed prior to disconnecting will
|
|
501
|
-
// automatically resubscribe.
|
|
502
|
-
class Consumer {
|
|
503
|
-
constructor(url){
|
|
504
|
-
this._url = url;
|
|
505
|
-
this.subscriptions = new Subscriptions(this);
|
|
506
|
-
this.connection = new Connection(this);
|
|
507
|
-
this.subprotocols = [];
|
|
508
|
-
}
|
|
509
|
-
get url() {
|
|
510
|
-
return createWebSocketURL(this._url);
|
|
511
|
-
}
|
|
512
|
-
send(data) {
|
|
513
|
-
return this.connection.send(data);
|
|
514
|
-
}
|
|
515
|
-
connect() {
|
|
516
|
-
return this.connection.open();
|
|
517
|
-
}
|
|
518
|
-
disconnect() {
|
|
519
|
-
return this.connection.close({
|
|
520
|
-
allowReconnect: false
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
ensureActiveConnection() {
|
|
524
|
-
if (!this.connection.isActive()) {
|
|
525
|
-
return this.connection.open();
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
addSubProtocol(subprotocol) {
|
|
529
|
-
this.subprotocols = [
|
|
530
|
-
...this.subprotocols,
|
|
531
|
-
subprotocol
|
|
532
|
-
];
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
function createWebSocketURL(url) {
|
|
536
|
-
if (typeof url === 'function') {
|
|
537
|
-
url = url();
|
|
538
|
-
}
|
|
539
|
-
if (url && !/^wss?:/i.test(url)) {
|
|
540
|
-
const a = document.createElement('a');
|
|
541
|
-
a.href = url;
|
|
542
|
-
// biome-ignore lint/correctness/noSelfAssign: <explanation>
|
|
543
|
-
a.href = a.href;
|
|
544
|
-
a.protocol = a.protocol.replace('http', 'ws');
|
|
545
|
-
return a.href;
|
|
546
|
-
}
|
|
547
|
-
return url;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function createConsumer(url = getConfig('url') || INTERNAL.default_mount_path) {
|
|
551
|
-
return new Consumer(url);
|
|
552
|
-
}
|
|
553
|
-
function getConfig(name) {
|
|
554
|
-
const element = document.head.querySelector(`meta[name='action-cable-${name}']`);
|
|
555
|
-
if (element) {
|
|
556
|
-
return element.getAttribute('content');
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
export { createConsumer as c };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
This is a patched version of @rails/actioncable now that https://github.com/rails/rails/pull/47939 has been merged in and released.
|