htmx.org 4.0.0-alpha3 → 4.0.0-alpha5

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.
@@ -0,0 +1,614 @@
1
+ (() => {
2
+ let api;
3
+
4
+ // Helper to get attribute value, checking colon, hyphen, and plain variants
5
+ function getWsAttribute(element, attrName) {
6
+ // Try colon variant first (hx-ws:connect)
7
+ let colonValue = api.attributeValue(element, 'hx-ws:' + attrName);
8
+ if (colonValue !== null && colonValue !== undefined) return colonValue;
9
+
10
+ // Try hyphen variant for JSX (hx-ws-connect)
11
+ let hyphenValue = api.attributeValue(element, 'hx-ws-' + attrName);
12
+ if (hyphenValue !== null && hyphenValue !== undefined) return hyphenValue;
13
+
14
+ // For 'send', also check plain 'hx-ws' (marker attribute)
15
+ if (attrName === 'send') {
16
+ let plainValue = api.attributeValue(element, 'hx-ws');
17
+ if (plainValue !== null && plainValue !== undefined) return plainValue;
18
+ }
19
+
20
+ return null;
21
+ }
22
+
23
+ // Helper to check if element has WebSocket attribute (any variant)
24
+ function hasWsAttribute(element, attrName) {
25
+ let value = getWsAttribute(element, attrName);
26
+ return value !== null && value !== undefined;
27
+ }
28
+
29
+ // ========================================
30
+ // CONFIGURATION
31
+ // ========================================
32
+
33
+ function getConfig() {
34
+ const defaults = {
35
+ reconnect: true,
36
+ reconnectDelay: 1000,
37
+ reconnectMaxDelay: 30000,
38
+ reconnectJitter: true,
39
+ autoConnect: false,
40
+ pauseInBackground: true
41
+ };
42
+ return { ...defaults, ...(htmx.config.websockets || {}) };
43
+ }
44
+
45
+ // ========================================
46
+ // CONNECTION REGISTRY
47
+ // ========================================
48
+
49
+ const connectionRegistry = new Map();
50
+
51
+ function getOrCreateConnection(url, element) {
52
+ if (connectionRegistry.has(url)) {
53
+ let entry = connectionRegistry.get(url);
54
+ entry.refCount++;
55
+ entry.elements.add(element);
56
+ return entry;
57
+ }
58
+
59
+ let entry = {
60
+ socket: null,
61
+ refCount: 1,
62
+ elements: new Set([element]),
63
+ reconnectAttempts: 0,
64
+ reconnectTimer: null,
65
+ pendingRequests: new Map()
66
+ };
67
+
68
+ connectionRegistry.set(url, entry);
69
+ createWebSocket(url, entry);
70
+ return entry;
71
+ }
72
+
73
+ function createWebSocket(url, entry) {
74
+ let firstElement = entry.elements.values().next().value;
75
+ if (firstElement) {
76
+ if (!triggerEvent(firstElement, 'htmx:before:ws:connect', { url })) {
77
+ return;
78
+ }
79
+ }
80
+
81
+ // Close and remove listeners from old socket
82
+ if (entry.socket) {
83
+ let oldSocket = entry.socket;
84
+ entry.socket = null;
85
+
86
+ oldSocket.onopen = null;
87
+ oldSocket.onmessage = null;
88
+ oldSocket.onclose = null;
89
+ oldSocket.onerror = null;
90
+
91
+ try {
92
+ if (oldSocket.readyState === WebSocket.OPEN || oldSocket.readyState === WebSocket.CONNECTING) {
93
+ oldSocket.close();
94
+ }
95
+ } catch (e) {}
96
+ }
97
+
98
+ try {
99
+ entry.socket = new WebSocket(url);
100
+
101
+ entry.socket.addEventListener('open', () => {
102
+ // Don't reset reconnectAttempts immediately - allow backoff to persist across quick reconnections
103
+ // It will naturally decrease as the connection remains stable
104
+ if (firstElement) {
105
+ triggerEvent(firstElement, 'htmx:after:ws:connect', { url, socket: entry.socket });
106
+ }
107
+ });
108
+
109
+ entry.socket.addEventListener('message', (event) => {
110
+ handleMessage(entry, event);
111
+ });
112
+
113
+ entry.socket.addEventListener('close', (event) => {
114
+ // Check if this socket is still the active one
115
+ if (event.target !== entry.socket) return;
116
+
117
+ if (firstElement) {
118
+ triggerEvent(firstElement, 'htmx:ws:close', { url });
119
+ }
120
+
121
+ // Check if entry is still valid (not cleared)
122
+ if (!connectionRegistry.has(url)) return;
123
+
124
+ let config = getConfig();
125
+ if (config.reconnect && entry.refCount > 0) {
126
+ scheduleReconnect(url, entry);
127
+ } else {
128
+ connectionRegistry.delete(url);
129
+ }
130
+ });
131
+
132
+ entry.socket.addEventListener('error', (error) => {
133
+ if (firstElement) {
134
+ triggerEvent(firstElement, 'htmx:ws:error', { url, error });
135
+ }
136
+ });
137
+ } catch (error) {
138
+ if (firstElement) {
139
+ triggerEvent(firstElement, 'htmx:ws:error', { url, error });
140
+ }
141
+ }
142
+ }
143
+
144
+ function scheduleReconnect(url, entry) {
145
+ let config = getConfig();
146
+
147
+ // Increment attempts before calculating delay for proper exponential backoff
148
+ let attempts = entry.reconnectAttempts;
149
+ entry.reconnectAttempts++;
150
+
151
+ let delay = Math.min(
152
+ (config.reconnectDelay || 1000) * Math.pow(2, attempts),
153
+ config.reconnectMaxDelay || 30000
154
+ );
155
+
156
+ if (config.reconnectJitter) {
157
+ delay = delay * (0.75 + Math.random() * 0.5);
158
+ }
159
+
160
+ entry.reconnectTimer = setTimeout(() => {
161
+ if (entry.refCount > 0) {
162
+ let firstElement = entry.elements.values().next().value;
163
+ if (firstElement) {
164
+ triggerEvent(firstElement, 'htmx:ws:reconnect', { url, attempts });
165
+ }
166
+ createWebSocket(url, entry);
167
+ }
168
+ }, delay);
169
+ }
170
+
171
+ function decrementRef(url, element) {
172
+ if (!connectionRegistry.has(url)) return;
173
+
174
+ let entry = connectionRegistry.get(url);
175
+ entry.elements.delete(element);
176
+ entry.refCount--;
177
+
178
+ if (entry.refCount <= 0) {
179
+ if (entry.reconnectTimer) {
180
+ clearTimeout(entry.reconnectTimer);
181
+ }
182
+ if (entry.socket && entry.socket.readyState === WebSocket.OPEN) {
183
+ entry.socket.close();
184
+ }
185
+ connectionRegistry.delete(url);
186
+ }
187
+ }
188
+
189
+ // ========================================
190
+ // MESSAGE SENDING
191
+ // ========================================
192
+
193
+ function sendMessage(element, event) {
194
+ // Find connection URL
195
+ let url = getWsAttribute(element, 'send');
196
+ if (!url) {
197
+ // Look for nearest ancestor with hx-ws:connect or hx-ws-connect
198
+ let prefix = htmx.config.prefix || '';
199
+ let ancestor = element.closest('[' + prefix + 'hx-ws\\:connect],[' + prefix + 'hx-ws-connect]');
200
+ if (ancestor) {
201
+ url = getWsAttribute(ancestor, 'connect');
202
+ }
203
+ }
204
+
205
+ if (!url) {
206
+ console.error('No WebSocket connection found for hx-ws:send element', element);
207
+ return;
208
+ }
209
+
210
+ let entry = connectionRegistry.get(url);
211
+ if (!entry || !entry.socket || entry.socket.readyState !== WebSocket.OPEN) {
212
+ triggerEvent(element, 'htmx:wsSendError', { url, error: 'Connection not open' });
213
+ return;
214
+ }
215
+
216
+ // Build message
217
+ let form = element.form || element.closest('form');
218
+ let body = api.collectFormData(element, form, event.submitter);
219
+ api.handleHxVals(element, body);
220
+
221
+ let values = {};
222
+ for (let [key, value] of body) {
223
+ values[key] = value;
224
+ }
225
+
226
+ let requestId = generateUUID();
227
+ let message = {
228
+ type: 'request',
229
+ request_id: requestId,
230
+ event: event.type,
231
+ values: values,
232
+ path: url
233
+ };
234
+
235
+ if (element.id) {
236
+ message.id = element.id;
237
+ }
238
+
239
+ // Allow modification via event
240
+ let detail = { message, element, url };
241
+ if (!triggerEvent(element, 'htmx:before:ws:send', detail)) {
242
+ return;
243
+ }
244
+
245
+ try {
246
+ entry.socket.send(JSON.stringify(detail.message));
247
+
248
+ // Store pending request for response matching
249
+ entry.pendingRequests.set(requestId, { element, timestamp: Date.now() });
250
+
251
+ triggerEvent(element, 'htmx:after:ws:send', { message: detail.message, url });
252
+ } catch (error) {
253
+ triggerEvent(element, 'htmx:wsSendError', { url, error });
254
+ }
255
+ }
256
+
257
+ function generateUUID() {
258
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
259
+ let r = Math.random() * 16 | 0;
260
+ let v = c === 'x' ? r : (r & 0x3 | 0x8);
261
+ return v.toString(16);
262
+ });
263
+ }
264
+
265
+ // ========================================
266
+ // MESSAGE RECEIVING & ROUTING
267
+ // ========================================
268
+
269
+ function handleMessage(entry, event) {
270
+ let envelope;
271
+ try {
272
+ envelope = JSON.parse(event.data);
273
+ } catch (e) {
274
+ // Not JSON, emit unknown message event
275
+ let firstElement = entry.elements.values().next().value;
276
+ if (firstElement) {
277
+ triggerEvent(firstElement, 'htmx:wsUnknownMessage', { data: event.data });
278
+ }
279
+ return;
280
+ }
281
+
282
+ // Apply defaults for channel and format
283
+ envelope.channel = envelope.channel || 'ui';
284
+ envelope.format = envelope.format || 'html';
285
+
286
+ // Find target element for this message
287
+ let targetElement = null;
288
+ if (envelope.request_id && entry.pendingRequests.has(envelope.request_id)) {
289
+ targetElement = entry.pendingRequests.get(envelope.request_id).element;
290
+ entry.pendingRequests.delete(envelope.request_id);
291
+ } else {
292
+ // Use first element in the connection
293
+ targetElement = entry.elements.values().next().value;
294
+ }
295
+
296
+ // Emit before:message event (cancelable)
297
+ if (!triggerEvent(targetElement, 'htmx:before:ws:message', { envelope, element: targetElement })) {
298
+ return;
299
+ }
300
+
301
+ // Route based on channel
302
+ if (envelope.channel === 'ui' && envelope.format === 'html') {
303
+ handleHtmlMessage(targetElement, envelope);
304
+ } else if (envelope.channel && (envelope.channel === 'audio' || envelope.channel === 'json' || envelope.channel === 'binary')) {
305
+ // Known custom channel - emit event for extensions to handle
306
+ triggerEvent(targetElement, 'htmx:wsMessage', { ...envelope, element: targetElement });
307
+ } else {
308
+ // Unknown channel/format - emit unknown message event
309
+ triggerEvent(targetElement, 'htmx:wsUnknownMessage', { ...envelope, element: targetElement });
310
+ }
311
+
312
+ triggerEvent(targetElement, 'htmx:after:ws:message', { envelope, element: targetElement });
313
+ }
314
+
315
+ // ========================================
316
+ // HTML PARTIAL HANDLING
317
+ // ========================================
318
+
319
+ function handleHtmlMessage(element, envelope) {
320
+ let parser = new DOMParser();
321
+ let doc = parser.parseFromString(envelope.payload, 'text/html');
322
+
323
+ // Find all hx-partial elements
324
+ let partials = doc.querySelectorAll('hx-partial');
325
+
326
+ if (partials.length === 0) {
327
+ // No partials, treat entire payload as content for element's target
328
+ let target = resolveTarget(element, envelope.target);
329
+ if (target) {
330
+ swapContent(target, envelope.payload, element, envelope.swap);
331
+ }
332
+ return;
333
+ }
334
+
335
+ partials.forEach(partial => {
336
+ let targetId = partial.getAttribute('id');
337
+ if (!targetId) return;
338
+
339
+ let target = document.getElementById(targetId);
340
+ if (!target) return;
341
+
342
+ swapContent(target, partial.innerHTML, element);
343
+ });
344
+ }
345
+
346
+ function resolveTarget(element, envelopeTarget) {
347
+ if (envelopeTarget) {
348
+ if (envelopeTarget === 'this') {
349
+ return element;
350
+ }
351
+ return document.querySelector(envelopeTarget);
352
+ }
353
+ let targetSelector = api.attributeValue(element, 'hx-target');
354
+ if (targetSelector) {
355
+ if (targetSelector === 'this') {
356
+ return element;
357
+ }
358
+ return document.querySelector(targetSelector);
359
+ }
360
+ return element;
361
+ }
362
+
363
+ function swapContent(target, content, sourceElement, envelopeSwap) {
364
+ let swapStyle = envelopeSwap || api.attributeValue(sourceElement, 'hx-swap') || htmx.config.defaultSwap;
365
+
366
+ // Parse swap style (just get the main style, ignore modifiers for now)
367
+ let style = swapStyle.split(' ')[0];
368
+
369
+ // Normalize swap style
370
+ style = normalizeSwapStyle(style);
371
+
372
+ // Perform swap
373
+ switch (style) {
374
+ case 'innerHTML':
375
+ target.innerHTML = content;
376
+ break;
377
+ case 'outerHTML':
378
+ target.outerHTML = content;
379
+ break;
380
+ case 'beforebegin':
381
+ target.insertAdjacentHTML('beforebegin', content);
382
+ break;
383
+ case 'afterbegin':
384
+ target.insertAdjacentHTML('afterbegin', content);
385
+ break;
386
+ case 'beforeend':
387
+ target.insertAdjacentHTML('beforeend', content);
388
+ break;
389
+ case 'afterend':
390
+ target.insertAdjacentHTML('afterend', content);
391
+ break;
392
+ case 'delete':
393
+ target.remove();
394
+ break;
395
+ case 'none':
396
+ // Do nothing
397
+ break;
398
+ default:
399
+ target.innerHTML = content;
400
+ }
401
+
402
+ // Process new content with HTMX
403
+ htmx.process(target);
404
+ }
405
+
406
+ function normalizeSwapStyle(style) {
407
+ return style === 'before' ? 'beforebegin' :
408
+ style === 'after' ? 'afterend' :
409
+ style === 'prepend' ? 'afterbegin' :
410
+ style === 'append' ? 'beforeend' : style;
411
+ }
412
+
413
+ // ========================================
414
+ // EVENT HELPERS
415
+ // ========================================
416
+
417
+ function triggerEvent(element, eventName, detail = {}) {
418
+ if (!element) return true;
419
+ return htmx.trigger(element, eventName, detail);
420
+ }
421
+
422
+ // ========================================
423
+ // ELEMENT LIFECYCLE
424
+ // ========================================
425
+
426
+ function initializeElement(element) {
427
+ if (element._htmx?.wsInitialized) return;
428
+
429
+ let connectUrl = getWsAttribute(element, 'connect');
430
+ if (!connectUrl) return;
431
+
432
+ element._htmx = element._htmx || {};
433
+ element._htmx.wsInitialized = true;
434
+
435
+ let config = getConfig();
436
+ let triggerSpec = api.attributeValue(element, 'hx-trigger');
437
+
438
+ if (!triggerSpec && config.autoConnect === true) {
439
+ // Auto-connect on element initialization
440
+ getOrCreateConnection(connectUrl, element);
441
+ element._htmx = element._htmx || {};
442
+ element._htmx.wsUrl = connectUrl;
443
+ } else if (triggerSpec) {
444
+ // Connect based on trigger
445
+ let specs = api.parseTriggerSpecs(triggerSpec);
446
+ if (specs.length > 0) {
447
+ let spec = specs[0];
448
+ if (spec.name === 'load') {
449
+ getOrCreateConnection(connectUrl, element);
450
+ element._htmx = element._htmx || {};
451
+ element._htmx.wsUrl = connectUrl;
452
+ } else {
453
+ // Set up event listener for other triggers
454
+ element.addEventListener(spec.name, () => {
455
+ if (!element._htmx?.wsUrl) {
456
+ getOrCreateConnection(connectUrl, element);
457
+ element._htmx = element._htmx || {};
458
+ element._htmx.wsUrl = connectUrl;
459
+ }
460
+ }, { once: true });
461
+ }
462
+ }
463
+ }
464
+ }
465
+
466
+ function initializeSendElement(element) {
467
+ if (element._htmx?.wsSendInitialized) return;
468
+
469
+ let sendUrl = getWsAttribute(element, 'send');
470
+ let triggerSpec = api.attributeValue(element, 'hx-trigger');
471
+
472
+ if (!triggerSpec) {
473
+ // Default trigger based on element type
474
+ triggerSpec = element.matches('form') ? 'submit' :
475
+ element.matches('input:not([type=button]),select,textarea') ? 'change' :
476
+ 'click';
477
+ }
478
+
479
+ let specs = api.parseTriggerSpecs(triggerSpec);
480
+ if (specs.length > 0) {
481
+ let spec = specs[0];
482
+
483
+ let handler = (evt) => {
484
+ // Prevent default for forms
485
+ if (element.matches('form') && evt.type === 'submit') {
486
+ evt.preventDefault();
487
+ }
488
+
489
+ // If this element has its own URL, ensure connection exists
490
+ if (sendUrl) {
491
+ if (!element._htmx?.wsUrl) {
492
+ getOrCreateConnection(sendUrl, element);
493
+ element._htmx = element._htmx || {};
494
+ element._htmx.wsUrl = sendUrl;
495
+ }
496
+ }
497
+
498
+ sendMessage(element, evt);
499
+ };
500
+
501
+ element.addEventListener(spec.name, handler);
502
+ element._htmx = element._htmx || {};
503
+ element._htmx.wsSendInitialized = true;
504
+ element._htmx.wsSendHandler = handler;
505
+ element._htmx.wsSendEvent = spec.name;
506
+ }
507
+ }
508
+
509
+ function cleanupElement(element) {
510
+ if (element._htmx?.wsUrl) {
511
+ decrementRef(element._htmx.wsUrl, element);
512
+ }
513
+
514
+ if (element._htmx?.wsSendHandler) {
515
+ element.removeEventListener(element._htmx.wsSendEvent, element._htmx.wsSendHandler);
516
+ }
517
+ }
518
+
519
+ // ========================================
520
+ // BACKWARD COMPATIBILITY
521
+ // ========================================
522
+
523
+ function checkLegacyAttributes(element) {
524
+ // Check for old ws-connect / ws-send attributes
525
+ if (element.hasAttribute('ws-connect') || element.hasAttribute('ws-send')) {
526
+ console.warn('HTMX WebSocket: Legacy attributes ws-connect and ws-send are deprecated. Please use hx-ws:connect/hx-ws-connect and hx-ws:send/hx-ws-send instead.');
527
+
528
+ // Map legacy attributes to new ones (prefer hyphen variant for broader compatibility)
529
+ if (element.hasAttribute('ws-connect')) {
530
+ let url = element.getAttribute('ws-connect');
531
+ if (!element.hasAttribute('hx-ws-connect')) {
532
+ element.setAttribute('hx-ws-connect', url);
533
+ }
534
+ }
535
+
536
+ if (element.hasAttribute('ws-send')) {
537
+ if (!element.hasAttribute('hx-ws-send')) {
538
+ element.setAttribute('hx-ws-send', '');
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ // ========================================
545
+ // EXTENSION REGISTRATION
546
+ // ========================================
547
+
548
+ htmx.registerExtension('ws', {
549
+ init: (internalAPI) => {
550
+ api = internalAPI;
551
+
552
+ // Initialize default config if not set
553
+ if (!htmx.config.websockets) {
554
+ htmx.config.websockets = {};
555
+ }
556
+ },
557
+
558
+ htmx_after_process: (element) => {
559
+ const processNode = (node) => {
560
+ // Check for legacy attributes
561
+ checkLegacyAttributes(node);
562
+
563
+ // Initialize WebSocket connection elements (check both variants)
564
+ if (hasWsAttribute(node, 'connect')) {
565
+ initializeElement(node);
566
+ }
567
+
568
+ // Initialize send elements (check both variants)
569
+ if (hasWsAttribute(node, 'send')) {
570
+ initializeSendElement(node);
571
+ }
572
+ };
573
+
574
+ // Process the element itself
575
+ processNode(element);
576
+
577
+ // Process descendants
578
+ element.querySelectorAll('[hx-ws\\:connect], [hx-ws-connect], [hx-ws\\:send], [hx-ws-send], [hx-ws], [ws-connect], [ws-send]').forEach(processNode);
579
+ },
580
+
581
+ htmx_before_cleanup: (element) => {
582
+ cleanupElement(element);
583
+ }
584
+ });
585
+
586
+ // Expose registry for testing
587
+ if (typeof window !== 'undefined' && window.htmx) {
588
+ window.htmx.ext = window.htmx.ext || {};
589
+ window.htmx.ext.ws = {
590
+ getRegistry: () => ({
591
+ clear: () => {
592
+ let entries = Array.from(connectionRegistry.values());
593
+ connectionRegistry.clear(); // Clear first to prevent reconnects
594
+
595
+ entries.forEach(entry => {
596
+ entry.refCount = 0; // Prevent pending timeouts from reconnecting
597
+ if (entry.reconnectTimer) {
598
+ clearTimeout(entry.reconnectTimer);
599
+ }
600
+ if (entry.socket) {
601
+ // Remove listeners if possible or just close
602
+ entry.socket.close();
603
+ }
604
+ entry.elements.clear();
605
+ entry.pendingRequests.clear();
606
+ });
607
+ },
608
+ get: (key) => connectionRegistry.get(key),
609
+ has: (key) => connectionRegistry.has(key),
610
+ size: connectionRegistry.size
611
+ })
612
+ };
613
+ }
614
+ })();
@@ -0,0 +1 @@
1
+ (()=>{let e;function t(t,n){let r=e.attributeValue(t,"hx-ws:"+n);if(null!=r)return r;let s=e.attributeValue(t,"hx-ws-"+n);if(null!=s)return s;if("send"===n){let n=e.attributeValue(t,"hx-ws");if(null!=n)return n}return null}function n(e,n){let r=t(e,n);return null!=r}function r(){return{reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectJitter:!0,autoConnect:!1,pauseInBackground:!0,...htmx.config.websockets||{}}}const s=new Map;function o(e,t){if(s.has(e)){let n=s.get(e);return n.refCount++,n.elements.add(t),n}let n={socket:null,refCount:1,elements:new Set([t]),reconnectAttempts:0,reconnectTimer:null,pendingRequests:new Map};return s.set(e,n),c(e,n),n}function c(t,n){let o=n.elements.values().next().value;if(!o||i(o,"htmx:before:ws:connect",{url:t})){if(n.socket){let e=n.socket;n.socket=null,e.onopen=null,e.onmessage=null,e.onclose=null,e.onerror=null;try{e.readyState!==WebSocket.OPEN&&e.readyState!==WebSocket.CONNECTING||e.close()}catch(e){}}try{n.socket=new WebSocket(t),n.socket.addEventListener("open",()=>{o&&i(o,"htmx:after:ws:connect",{url:t,socket:n.socket})}),n.socket.addEventListener("message",t=>{!function(t,n){let r;try{r=JSON.parse(n.data)}catch(e){let r=t.elements.values().next().value;return void(r&&i(r,"htmx:wsUnknownMessage",{data:n.data}))}r.channel=r.channel||"ui",r.format=r.format||"html";let s=null;r.request_id&&t.pendingRequests.has(r.request_id)?(s=t.pendingRequests.get(r.request_id).element,t.pendingRequests.delete(r.request_id)):s=t.elements.values().next().value;if(!i(s,"htmx:before:ws:message",{envelope:r,element:s}))return;"ui"===r.channel&&"html"===r.format?function(t,n){let r=(new DOMParser).parseFromString(n.payload,"text/html"),s=r.querySelectorAll("hx-partial");if(0===s.length){let r=function(t,n){if(n)return"this"===n?t:document.querySelector(n);let r=e.attributeValue(t,"hx-target");if(r)return"this"===r?t:document.querySelector(r);return t}(t,n.target);return void(r&&l(r,n.payload,t,n.swap))}s.forEach(e=>{let n=e.getAttribute("id");if(!n)return;let r=document.getElementById(n);r&&l(r,e.innerHTML,t)})}(s,r):!r.channel||"audio"!==r.channel&&"json"!==r.channel&&"binary"!==r.channel?i(s,"htmx:wsUnknownMessage",{...r,element:s}):i(s,"htmx:wsMessage",{...r,element:s});i(s,"htmx:after:ws:message",{envelope:r,element:s})}(n,t)}),n.socket.addEventListener("close",e=>{if(e.target!==n.socket)return;if(o&&i(o,"htmx:ws:close",{url:t}),!s.has(t))return;r().reconnect&&n.refCount>0?function(e,t){let n=r(),s=t.reconnectAttempts;t.reconnectAttempts++;let o=Math.min((n.reconnectDelay||1e3)*Math.pow(2,s),n.reconnectMaxDelay||3e4);n.reconnectJitter&&(o*=.75+.5*Math.random());t.reconnectTimer=setTimeout(()=>{if(t.refCount>0){let n=t.elements.values().next().value;n&&i(n,"htmx:ws:reconnect",{url:e,attempts:s}),c(e,t)}},o)}(t,n):s.delete(t)}),n.socket.addEventListener("error",e=>{o&&i(o,"htmx:ws:error",{url:t,error:e})})}catch(e){o&&i(o,"htmx:ws:error",{url:t,error:e})}}}function a(n,r){let o=t(n,"send");if(!o){let e=htmx.config.prefix||"",r=n.closest("["+e+"hx-ws\\:connect],["+e+"hx-ws-connect]");r&&(o=t(r,"connect"))}if(!o)return void console.error("No WebSocket connection found for hx-ws:send element",n);let c=s.get(o);if(!c||!c.socket||c.socket.readyState!==WebSocket.OPEN)return void i(n,"htmx:wsSendError",{url:o,error:"Connection not open"});let a=n.form||n.closest("form"),l=e.collectFormData(n,a,r.submitter);e.handleHxVals(n,l);let u={};for(let[e,t]of l)u[e]=t;let m="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){let t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)}),x={type:"request",request_id:m,event:r.type,values:u,path:o};n.id&&(x.id=n.id);let h={message:x,element:n,url:o};if(i(n,"htmx:before:ws:send",h))try{c.socket.send(JSON.stringify(h.message)),c.pendingRequests.set(m,{element:n,timestamp:Date.now()}),i(n,"htmx:after:ws:send",{message:h.message,url:o})}catch(e){i(n,"htmx:wsSendError",{url:o,error:e})}}function l(t,n,r,s){let o=(s||e.attributeValue(r,"hx-swap")||htmx.config.defaultSwap).split(" ")[0];switch(o=function(e){return"before"===e?"beforebegin":"after"===e?"afterend":"prepend"===e?"afterbegin":"append"===e?"beforeend":e}(o),o){case"innerHTML":default:t.innerHTML=n;break;case"outerHTML":t.outerHTML=n;break;case"beforebegin":t.insertAdjacentHTML("beforebegin",n);break;case"afterbegin":t.insertAdjacentHTML("afterbegin",n);break;case"beforeend":t.insertAdjacentHTML("beforeend",n);break;case"afterend":t.insertAdjacentHTML("afterend",n);break;case"delete":t.remove();case"none":}htmx.process(t)}function i(e,t,n={}){return!e||htmx.trigger(e,t,n)}function u(e){e._htmx?.wsUrl&&function(e,t){if(!s.has(e))return;let n=s.get(e);n.elements.delete(t),n.refCount--,n.refCount<=0&&(n.reconnectTimer&&clearTimeout(n.reconnectTimer),n.socket&&n.socket.readyState===WebSocket.OPEN&&n.socket.close(),s.delete(e))}(e._htmx.wsUrl,e),e._htmx?.wsSendHandler&&e.removeEventListener(e._htmx.wsSendEvent,e._htmx.wsSendHandler)}htmx.registerExtension("ws",{init:t=>{e=t,htmx.config.websockets||(htmx.config.websockets={})},htmx_after_process:s=>{const c=s=>{!function(e){if(e.hasAttribute("ws-connect")||e.hasAttribute("ws-send")){if(console.warn("HTMX WebSocket: Legacy attributes ws-connect and ws-send are deprecated. Please use hx-ws:connect/hx-ws-connect and hx-ws:send/hx-ws-send instead."),e.hasAttribute("ws-connect")){let t=e.getAttribute("ws-connect");e.hasAttribute("hx-ws-connect")||e.setAttribute("hx-ws-connect",t)}e.hasAttribute("ws-send")&&(e.hasAttribute("hx-ws-send")||e.setAttribute("hx-ws-send",""))}}(s),n(s,"connect")&&function(n){if(n._htmx?.wsInitialized)return;let s=t(n,"connect");if(!s)return;n._htmx=n._htmx||{},n._htmx.wsInitialized=!0;let c=r(),a=e.attributeValue(n,"hx-trigger");if(a||!0!==c.autoConnect){if(a){let t=e.parseTriggerSpecs(a);if(t.length>0){let e=t[0];"load"===e.name?(o(s,n),n._htmx=n._htmx||{},n._htmx.wsUrl=s):n.addEventListener(e.name,()=>{n._htmx?.wsUrl||(o(s,n),n._htmx=n._htmx||{},n._htmx.wsUrl=s)},{once:!0})}}}else o(s,n),n._htmx=n._htmx||{},n._htmx.wsUrl=s}(s),n(s,"send")&&function(n){if(n._htmx?.wsSendInitialized)return;let r=t(n,"send"),s=e.attributeValue(n,"hx-trigger");s||(s=n.matches("form")?"submit":n.matches("input:not([type=button]),select,textarea")?"change":"click");let c=e.parseTriggerSpecs(s);if(c.length>0){let e=c[0],t=e=>{n.matches("form")&&"submit"===e.type&&e.preventDefault(),r&&(n._htmx?.wsUrl||(o(r,n),n._htmx=n._htmx||{},n._htmx.wsUrl=r)),a(n,e)};n.addEventListener(e.name,t),n._htmx=n._htmx||{},n._htmx.wsSendInitialized=!0,n._htmx.wsSendHandler=t,n._htmx.wsSendEvent=e.name}}(s)};c(s),s.querySelectorAll("[hx-ws\\:connect], [hx-ws-connect], [hx-ws\\:send], [hx-ws-send], [hx-ws], [ws-connect], [ws-send]").forEach(c)},htmx_before_cleanup:e=>{u(e)}}),"undefined"!=typeof window&&window.htmx&&(window.htmx.ext=window.htmx.ext||{},window.htmx.ext.ws={getRegistry:()=>({clear:()=>{let e=Array.from(s.values());s.clear(),e.forEach(e=>{e.refCount=0,e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.socket&&e.socket.close(),e.elements.clear(),e.pendingRequests.clear()})},get:e=>s.get(e),has:e=>s.has(e),size:s.size})})})();
@@ -0,0 +1 @@
1
+ {"version":3,"names":["api","getWsAttribute","element","attrName","colonValue","attributeValue","hyphenValue","plainValue","hasWsAttribute","value","getConfig","reconnect","reconnectDelay","reconnectMaxDelay","reconnectJitter","autoConnect","pauseInBackground","htmx","config","websockets","connectionRegistry","Map","getOrCreateConnection","url","has","entry","get","refCount","elements","add","socket","Set","reconnectAttempts","reconnectTimer","pendingRequests","set","createWebSocket","firstElement","values","next","triggerEvent","oldSocket","onopen","onmessage","onclose","onerror","readyState","WebSocket","OPEN","CONNECTING","close","e","addEventListener","event","envelope","JSON","parse","data","channel","format","targetElement","request_id","delete","doc","DOMParser","parseFromString","payload","partials","querySelectorAll","length","target","envelopeTarget","document","querySelector","targetSelector","resolveTarget","swapContent","swap","forEach","partial","targetId","getAttribute","getElementById","innerHTML","handleHtmlMessage","handleMessage","attempts","delay","Math","min","pow","random","setTimeout","scheduleReconnect","error","sendMessage","prefix","ancestor","closest","console","form","body","collectFormData","submitter","handleHxVals","key","requestId","replace","c","r","toString","message","type","path","id","detail","send","stringify","timestamp","Date","now","content","sourceElement","envelopeSwap","style","defaultSwap","split","normalizeSwapStyle","outerHTML","insertAdjacentHTML","remove","process","eventName","trigger","cleanupElement","_htmx","wsUrl","clearTimeout","decrementRef","wsSendHandler","removeEventListener","wsSendEvent","registerExtension","init","internalAPI","htmx_after_process","processNode","node","hasAttribute","warn","setAttribute","checkLegacyAttributes","wsInitialized","connectUrl","triggerSpec","specs","parseTriggerSpecs","spec","name","once","initializeElement","wsSendInitialized","sendUrl","matches","handler","evt","preventDefault","initializeSendElement","htmx_before_cleanup","window","ext","ws","getRegistry","clear","entries","Array","from","size"],"sources":["dist/ext/hx-ws.js"],"mappings":"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","ignoreList":[]}