@hellotext/hellotext 2.1.2 → 2.1.4

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.
Files changed (56) hide show
  1. package/dist/hellotext.js +1 -1
  2. package/lib/api/forms.cjs +2 -1
  3. package/lib/api/forms.js +2 -1
  4. package/lib/api/webchat/messages.cjs +4 -3
  5. package/lib/api/webchat/messages.js +4 -2
  6. package/lib/api/webchats.cjs +2 -0
  7. package/lib/api/webchats.js +3 -1
  8. package/lib/builders/logo_builder.cjs +1 -1
  9. package/lib/builders/logo_builder.js +1 -1
  10. package/lib/channels/application_channel.cjs +9 -6
  11. package/lib/channels/application_channel.js +9 -6
  12. package/lib/channels/webchat_channel.cjs +42 -17
  13. package/lib/channels/webchat_channel.js +42 -17
  14. package/lib/controllers/webchat/emoji_picker_controller.cjs +6 -6
  15. package/lib/controllers/webchat/emoji_picker_controller.js +5 -14
  16. package/lib/controllers/webchat_controller.cjs +174 -47
  17. package/lib/controllers/webchat_controller.js +175 -48
  18. package/lib/core/configuration/locale.cjs +87 -0
  19. package/lib/core/configuration/locale.js +81 -0
  20. package/lib/core/configuration.cjs +11 -0
  21. package/lib/core/configuration.js +11 -0
  22. package/lib/core/index.cjs +15 -1
  23. package/lib/core/index.js +4 -2
  24. package/lib/locales/en.cjs +1 -1
  25. package/lib/locales/en.js +1 -1
  26. package/lib/locales/es.cjs +1 -1
  27. package/lib/locales/es.js +1 -1
  28. package/lib/models/business.cjs +11 -0
  29. package/lib/models/business.js +11 -0
  30. package/lib/models/form_collection.cjs +1 -0
  31. package/lib/models/form_collection.js +2 -1
  32. package/lib/models/index.cjs +4 -4
  33. package/lib/models/index.js +4 -4
  34. package/lib/models/query.cjs +1 -2
  35. package/lib/models/query.js +1 -2
  36. package/lib/models/session.cjs +1 -1
  37. package/lib/models/session.js +1 -1
  38. package/package.json +2 -1
  39. package/src/api/forms.js +3 -1
  40. package/src/api/webchat/messages.js +8 -6
  41. package/src/api/webchats.js +3 -1
  42. package/src/builders/logo_builder.js +1 -1
  43. package/src/channels/application_channel.js +12 -9
  44. package/src/channels/webchat_channel.js +33 -19
  45. package/src/controllers/webchat/emoji_picker_controller.js +9 -15
  46. package/src/controllers/webchat_controller.js +164 -43
  47. package/src/core/configuration/locale.js +50 -0
  48. package/src/core/configuration.js +11 -0
  49. package/src/core/index.js +3 -1
  50. package/src/locales/en.js +4 -4
  51. package/src/locales/es.js +4 -4
  52. package/src/models/business.js +12 -0
  53. package/src/models/form_collection.js +2 -1
  54. package/src/models/index.js +3 -3
  55. package/src/models/query.js +1 -3
  56. package/src/models/session.js +1 -1
@@ -5,6 +5,7 @@ import WebchatMessagesAPI from '../api/webchat/messages'
5
5
  import WebchatChannel from '../channels/webchat_channel'
6
6
  import Hellotext from '../hellotext'
7
7
 
8
+ import { Locale } from '../core'
8
9
  import { Webchat as WebchatConfiguration, behaviors } from '../core/configuration/webchat'
9
10
 
10
11
  import { LogoBuilder } from '../builders/logo_builder'
@@ -21,8 +22,15 @@ export default class extends Controller {
21
22
  autoPlacement: { type: Boolean, default: false },
22
23
  disabled: { type: Boolean, default: false },
23
24
  nextPage: { type: Number, default: undefined },
25
+ fullScreenThreshold: { type: Number, default: 1024 },
26
+ typingIndicatorKeepAlive: { type: Number, default: 30000 }, // 30 seconds
27
+ offset: { type: Number, default: 24 },
28
+ padding: { type: Number, default: 24 },
29
+ optimisticTypingIndicatorWait: { type: Number, default: 1000 }, // 1 second
24
30
  }
25
31
 
32
+ static classes = ['fadeOut']
33
+
26
34
  static targets = [
27
35
  'trigger',
28
36
  'popover',
@@ -36,12 +44,13 @@ export default class extends Controller {
36
44
  'messageTemplate',
37
45
  'messagesContainer',
38
46
  'title',
39
- 'onlineStatus',
40
47
  'attachmentImage',
41
48
  'footer',
42
49
  'toolbar',
43
50
  'message',
44
51
  'unreadCounter',
52
+ 'typingIndicator',
53
+ 'typingIndicatorTemplate',
45
54
  ]
46
55
 
47
56
  initialize() {
@@ -56,9 +65,8 @@ export default class extends Controller {
56
65
  this.files = []
57
66
 
58
67
  this.onMessageReceived = this.onMessageReceived.bind(this)
59
- this.onConversationAssignment = this.onConversationAssignment.bind(this)
60
- this.onAgentOnline = this.onAgentOnline.bind(this)
61
68
  this.onMessageReaction = this.onMessageReaction.bind(this)
69
+ this.onTypingStart = this.onTypingStart.bind(this)
62
70
 
63
71
  this.onScroll = this.onScroll.bind(this)
64
72
 
@@ -77,9 +85,8 @@ export default class extends Controller {
77
85
  this.setupFloatingUI({ trigger: this.triggerTarget, popover: this.popoverTarget })
78
86
 
79
87
  this.webChatChannel.onMessage(this.onMessageReceived)
80
- this.webChatChannel.onConversationAssignment(this.onConversationAssignment)
88
+ this.webChatChannel.onTypingStart(this.onTypingStart)
81
89
 
82
- this.webChatChannel.onAgentOnline(this.onAgentOnline)
83
90
  this.webChatChannel.onReaction(this.onMessageReaction)
84
91
 
85
92
  this.messagesContainerTarget.addEventListener('scroll', this.onScroll)
@@ -88,7 +95,7 @@ export default class extends Controller {
88
95
  this.toolbarTarget.appendChild(LogoBuilder.build())
89
96
  }
90
97
 
91
- if (localStorage.getItem(`hellotext--webchat--${this.idValue}`) === 'opened') {
98
+ if (this.shouldOpenOnMount) {
92
99
  this.openValue = true
93
100
  }
94
101
 
@@ -102,12 +109,94 @@ export default class extends Controller {
102
109
  this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent)
103
110
  this.messagesContainerTarget.removeEventListener('scroll', this.onScroll)
104
111
 
112
+ // Clean up typing indicator timeouts
113
+ this.clearTypingIndicator()
114
+
105
115
  this.broadcastChannel.close()
106
116
  this.floatingUICleanup()
107
117
 
108
118
  super.disconnect()
109
119
  }
110
120
 
121
+ onTypingStart() {
122
+ if (this.typingIndicatorVisible) {
123
+ return this.resetTypingIndicatorTimer()
124
+ }
125
+
126
+ this.showTypingIndicator()
127
+ }
128
+
129
+ showOptimisticTypingIndicator() {
130
+ if (this.typingIndicatorVisible) return
131
+
132
+ this.showTypingIndicator()
133
+ }
134
+
135
+ showTypingIndicator() {
136
+ this.clearTypingIndicator()
137
+
138
+ this.typingIndicatorVisible = true
139
+ const indicator = this.typingIndicatorTemplateTarget.cloneNode(true)
140
+
141
+ indicator.setAttribute('data-hellotext--webchat-target', 'typingIndicator')
142
+ indicator.style.display = 'flex'
143
+
144
+ this.messagesContainerTarget.appendChild(indicator)
145
+
146
+ requestAnimationFrame(() => {
147
+ this.messagesContainerTarget.scroll({
148
+ top: this.messagesContainerTarget.scrollHeight,
149
+ behavior: 'instant',
150
+ })
151
+ })
152
+
153
+ const timeout = this.typingIndicatorKeepAliveValue
154
+
155
+ this.incomingTypingIndicatorTimeout = setTimeout(() => {
156
+ this.clearTypingIndicator()
157
+ }, timeout)
158
+ }
159
+
160
+ resetTypingIndicatorTimer() {
161
+ if (!this.typingIndicatorVisible) return
162
+
163
+ // Clear existing timeout
164
+ clearTimeout(this.incomingTypingIndicatorTimeout)
165
+ clearTimeout(this.optimisticTypingTimeout)
166
+
167
+ // Use unified timeout for all typing indicators
168
+ const timeout = this.typingIndicatorKeepAliveValue
169
+
170
+ this.incomingTypingIndicatorTimeout = setTimeout(() => {
171
+ this.clearTypingIndicator()
172
+ }, timeout)
173
+ }
174
+
175
+ clearTypingIndicator() {
176
+ if (this.hasTypingIndicatorTarget) {
177
+ this.typingIndicatorTarget.remove()
178
+ }
179
+
180
+ this.typingIndicatorVisible = false
181
+
182
+ clearTimeout(this.incomingTypingIndicatorTimeout)
183
+ clearTimeout(this.optimisticTypingTimeout)
184
+ }
185
+
186
+ onMessageInputChange() {
187
+ this.resizeInput()
188
+ clearTimeout(this.typingIndicatorTimeout)
189
+
190
+ if (!this.hasSentTypingIndicator) {
191
+ this.webChatChannel.startTypingIndicator()
192
+ this.hasSentTypingIndicator = true
193
+ }
194
+
195
+ this.typingIndicatorTimeout = setTimeout(() => {
196
+ this.hasSentTypingIndicator = false
197
+ }, 3000)
198
+ }
199
+
111
200
  onOutboundMessageSent(event) {
112
201
  const { data } = event
113
202
 
@@ -116,7 +205,13 @@ export default class extends Controller {
116
205
  const element = new DOMParser().parseFromString(data.element, 'text/html').body
117
206
  .firstElementChild
118
207
 
119
- this.messagesContainerTarget.appendChild(element)
208
+ // Insert message before typing indicator if one exists
209
+ if (this.typingIndicatorVisible && this.hasTypingIndicatorTarget) {
210
+ this.messagesContainerTarget.insertBefore(element, this.typingIndicatorTarget)
211
+ } else {
212
+ this.messagesContainerTarget.appendChild(element)
213
+ }
214
+
120
215
  element.scrollIntoView({ behavior: 'instant' })
121
216
  },
122
217
  'message:failed': data => {
@@ -205,8 +300,18 @@ export default class extends Controller {
205
300
  }
206
301
  }
207
302
 
303
+ closePopover() {
304
+ this.popoverTarget.classList.add(...this.fadeOutClasses)
305
+
306
+ setTimeout(() => {
307
+ this.openValue = false
308
+ }, 250)
309
+ }
310
+
208
311
  onPopoverOpened() {
209
- this.inputTarget.focus()
312
+ if (!this.onMobile) {
313
+ this.inputTarget.focus()
314
+ }
210
315
 
211
316
  if (!this.scrolled) {
212
317
  requestAnimationFrame(() => {
@@ -220,7 +325,6 @@ export default class extends Controller {
220
325
  }
221
326
 
222
327
  Hellotext.eventEmitter.dispatch('webchat:opened')
223
-
224
328
  localStorage.setItem(`hellotext--webchat--${this.idValue}`, 'opened')
225
329
 
226
330
  if (this.unreadCounterTarget.style.display === 'none') return
@@ -233,11 +337,6 @@ export default class extends Controller {
233
337
 
234
338
  onPopoverClosed() {
235
339
  Hellotext.eventEmitter.dispatch('webchat:closed')
236
-
237
- setTimeout(() => {
238
- this.inputTarget.value = ''
239
- })
240
-
241
340
  localStorage.setItem(`hellotext--webchat--${this.idValue}`, 'closed')
242
341
  }
243
342
 
@@ -266,7 +365,7 @@ export default class extends Controller {
266
365
  }
267
366
 
268
367
  onMessageReceived(message) {
269
- const { body, attachments } = message
368
+ const { id, body, attachments } = message
270
369
 
271
370
  const div = document.createElement('div')
272
371
  div.innerHTML = body
@@ -287,6 +386,11 @@ export default class extends Controller {
287
386
  })
288
387
  }
289
388
 
389
+ // Clear any typing indicator when message is received
390
+ if (this.typingIndicatorVisible) {
391
+ this.clearTypingIndicator()
392
+ }
393
+
290
394
  this.messagesContainerTarget.appendChild(element)
291
395
 
292
396
  Hellotext.eventEmitter.dispatch('webchat:message:received', {
@@ -295,9 +399,11 @@ export default class extends Controller {
295
399
  })
296
400
 
297
401
  element.scrollIntoView({ behavior: 'smooth' })
298
- this.setOfflineTimeout()
299
402
 
300
- if (this.openValue) return
403
+ if (this.openValue) {
404
+ this.messagesAPI.markAsSeen(id)
405
+ return
406
+ }
301
407
 
302
408
  this.unreadCounterTarget.style.display = 'flex'
303
409
 
@@ -305,24 +411,20 @@ export default class extends Controller {
305
411
  this.unreadCounterTarget.innerText = unreadCount > 99 ? '99+' : unreadCount
306
412
  }
307
413
 
308
- onConversationAssignment(conversation) {
309
- const { to: user } = conversation
414
+ resizeInput() {
415
+ const maxHeight = 96
310
416
 
311
- this.titleTarget.innerText = user.name
417
+ // Temporarily reset height to its natural content size
418
+ this.inputTarget.style.height = 'auto'
312
419
 
313
- if (user.online) {
314
- this.onlineStatusTarget.style.display = 'flex'
315
- } else {
316
- this.onlineStatusTarget.style.display = 'none'
317
- }
318
- }
420
+ // Set the height to the scrollHeight, which is the minimum height
421
+ // the element needs to fit its content without a scrollbar.
422
+ const scrollHeight = this.inputTarget.scrollHeight
319
423
 
320
- onAgentOnline() {
321
- this.onlineStatusTarget.style.display = 'flex'
322
- this.setOfflineTimeout()
424
+ this.inputTarget.style.height = `${Math.min(scrollHeight, maxHeight)}px`
323
425
  }
324
426
 
325
- async sendMessage() {
427
+ async sendMessage(e) {
326
428
  const formData = new FormData()
327
429
 
328
430
  const message = {
@@ -331,6 +433,10 @@ export default class extends Controller {
331
433
  }
332
434
 
333
435
  if (this.inputTarget.value.trim().length === 0 && this.files.length === 0) {
436
+ if (e && e.target) {
437
+ e.preventDefault()
438
+ }
439
+
334
440
  return
335
441
  }
336
442
 
@@ -345,6 +451,7 @@ export default class extends Controller {
345
451
  })
346
452
 
347
453
  formData.append('session', Hellotext.session)
454
+ formData.append('locale', Locale.toString())
348
455
 
349
456
  const element = this.messageTemplateTarget.cloneNode(true)
350
457
 
@@ -369,7 +476,13 @@ export default class extends Controller {
369
476
  })
370
477
  }
371
478
 
372
- this.messagesContainerTarget.appendChild(element)
479
+ // Insert message before typing indicator if one exists
480
+ if (this.typingIndicatorVisible && this.hasTypingIndicatorTarget) {
481
+ this.messagesContainerTarget.insertBefore(element, this.typingIndicatorTarget)
482
+ } else {
483
+ this.messagesContainerTarget.appendChild(element)
484
+ }
485
+
373
486
  element.scrollIntoView({ behavior: 'smooth' })
374
487
 
375
488
  this.broadcastChannel.postMessage({
@@ -378,6 +491,7 @@ export default class extends Controller {
378
491
  })
379
492
 
380
493
  this.inputTarget.value = ''
494
+ this.resizeInput()
381
495
  this.files = []
382
496
 
383
497
  this.attachmentInputTarget.value = ''
@@ -410,6 +524,15 @@ export default class extends Controller {
410
524
  this.webChatChannel.updateSubscriptionWith(this.conversationIdValue)
411
525
  }
412
526
 
527
+ if (this.typingIndicatorVisible) {
528
+ this.resetTypingIndicatorTimer()
529
+ } else {
530
+ clearTimeout(this.optimisticTypingTimeout)
531
+ this.optimisticTypingTimeout = setTimeout(() => {
532
+ this.showOptimisticTypingIndicator()
533
+ }, this.optimisticTypingIndicatorWaitValue)
534
+ }
535
+
413
536
  this.attachmentContainerTarget.style.display = ''
414
537
  }
415
538
 
@@ -522,23 +645,21 @@ export default class extends Controller {
522
645
  this.inputTarget.focus()
523
646
  }
524
647
 
525
- setOfflineTimeout() {
526
- clearTimeout(this.offlineTimeout)
527
-
528
- this.offlineTimeout = setTimeout(() => {
529
- this.onlineStatusTarget.style.display = 'none'
530
- }, this.fiveMinutes)
531
- }
532
-
533
648
  byteToMegabyte(bytes) {
534
649
  return Math.ceil(bytes / 1024 / 1024)
535
650
  }
536
651
 
537
- get fiveMinutes() {
538
- return 300000
652
+ get middlewares() {
653
+ return [offset(this.offsetValue), shift({ padding: this.paddingValue }), flip()]
654
+ }
655
+
656
+ get shouldOpenOnMount() {
657
+ return (
658
+ localStorage.getItem(`hellotext--webchat--${this.idValue}`) === 'opened' && !this.onMobile
659
+ )
539
660
  }
540
661
 
541
- get middlewares() {
542
- return [offset(5), shift({ padding: 24 }), flip()]
662
+ get onMobile() {
663
+ return window.matchMedia(`(max-width: ${this.fullScreenThresholdValue}px)`).matches
543
664
  }
544
665
  }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @class Locale
3
+ * @classdesc
4
+ * Handles locale detection and configuration for the Hellotext library.
5
+ * Provides automatic locale detection from HTML lang attribute, meta tags,
6
+ * and browser language with fallback to 'en'.
7
+ */
8
+ class Locale {
9
+ static _identifier
10
+
11
+ /**
12
+ * Sets the locale identifier explicitly.
13
+ * @param {string} value - The locale identifier (e.g., 'en', 'es')
14
+ */
15
+ static set identifier(value) {
16
+ this._identifier = value
17
+ }
18
+
19
+ /**
20
+ * Gets the effective locale identifier.
21
+ * Falls back to auto-detection if not explicitly set.
22
+ * @returns {string} The locale identifier
23
+ */
24
+ static get identifier() {
25
+ if (this._identifier) return this._identifier
26
+ return this.#fromHtmlLangProperty || this.#fromMetaTag || this.#fromBrowserLanguage || 'en'
27
+ }
28
+
29
+ /**
30
+ * Returns the locale identifier as a string.
31
+ * @returns {string} The locale identifier
32
+ */
33
+ static toString() {
34
+ return this.identifier
35
+ }
36
+
37
+ static get #fromHtmlLangProperty() {
38
+ return document?.documentElement?.lang
39
+ }
40
+
41
+ static get #fromMetaTag() {
42
+ return document?.querySelector('meta[name="locale"]')?.content
43
+ }
44
+
45
+ static get #fromBrowserLanguage() {
46
+ return navigator?.language?.split('-')[0] // Extract primary language
47
+ }
48
+ }
49
+
50
+ export { Locale }
@@ -1,4 +1,5 @@
1
1
  import { Forms } from './configuration/forms'
2
+ import { Locale } from './configuration/locale'
2
3
  import { Webchat } from './configuration/webchat'
3
4
 
4
5
  /**
@@ -9,9 +10,11 @@ import { Webchat } from './configuration/webchat'
9
10
  * @property {String} [session] - session id
10
11
  * @property {Forms} [forms] - form configuration
11
12
  * @property {Webchat} [webchat] - webchat configuration
13
+ * @property {Locale} [locale] - locale configuration
12
14
  */
13
15
  class Configuration {
14
16
  static apiRoot = 'https://api.hellotext.com/v1'
17
+ static actionCableUrl = 'wss://www.hellotext.com/cable'
15
18
 
16
19
  static autoGenerateSession = true
17
20
  static session = null
@@ -43,6 +46,14 @@ class Configuration {
43
46
  return this
44
47
  }
45
48
 
49
+ static set locale(locale) {
50
+ Locale.identifier = locale
51
+ }
52
+
53
+ static get locale() {
54
+ return Locale.toString()
55
+ }
56
+
46
57
  static endpoint(path) {
47
58
  return `${this.apiRoot}/${path}`
48
59
  }
package/src/core/index.js CHANGED
@@ -1,2 +1,4 @@
1
- export { default as Event } from './event'
2
1
  export { Configuration } from './configuration'
2
+ export { Locale } from './configuration/locale'
3
+ export { Webchat } from './configuration/webchat'
4
+ export { default as Event } from './event'
package/src/locales/en.js CHANGED
@@ -1,15 +1,15 @@
1
1
  export default {
2
2
  white_label: {
3
- powered_by: 'Powered by',
3
+ powered_by: 'By',
4
4
  },
5
5
  errors: {
6
6
  parameter_not_unique: 'This value is taken.',
7
- blank: 'This field is required.'
7
+ blank: 'This field is required.',
8
8
  },
9
9
  forms: {
10
10
  phone: 'Click the link sent via SMS to verify your submission.',
11
11
  email: 'Click the link sent via email to verify your submission.',
12
12
  phone_and_email: 'Click the links sent via SMS and email to verify your submission.',
13
- none: 'Your submission has been received.'
14
- }
13
+ none: 'Your submission has been received.',
14
+ },
15
15
  }
package/src/locales/es.js CHANGED
@@ -1,15 +1,15 @@
1
1
  export default {
2
2
  white_label: {
3
- powered_by: 'Desarrollado por',
3
+ powered_by: 'Por',
4
4
  },
5
5
  errors: {
6
6
  parameter_not_unique: 'Este valor ya está en uso.',
7
- blank: 'Este campo es obligatorio.'
7
+ blank: 'Este campo es obligatorio.',
8
8
  },
9
9
  forms: {
10
10
  phone: 'Haga clic en el enlace enviado por SMS para verificar su envío.',
11
11
  email: 'Haga clic en el enlace enviado por e-mail para verificar su envío.',
12
12
  phone_and_email: 'Haga clic en los enlaces enviados por SMS y e-mail para verificar su envío.',
13
- none: 'Su envío ha sido recibido.'
14
- }
13
+ none: 'Su envío ha sido recibido.',
14
+ },
15
15
  }
@@ -30,6 +30,18 @@ class Business {
30
30
  return this.data.whitelist !== 'disabled'
31
31
  }
32
32
 
33
+ setLocale(locale) {
34
+ if (!locales[locale]) {
35
+ return console.warn(`Locale ${locale} not found`)
36
+ }
37
+
38
+ if (!this.data) {
39
+ this.data = {}
40
+ }
41
+
42
+ this.data.locale = locale
43
+ }
44
+
33
45
  get locale() {
34
46
  return locales[this.data.locale]
35
47
  }
@@ -1,7 +1,7 @@
1
1
  import API from '../api/forms'
2
2
  import Hellotext from '../hellotext'
3
3
 
4
- import { Configuration } from '../core'
4
+ import { Configuration, Locale } from '../core'
5
5
  import { Form } from './form'
6
6
 
7
7
  import { NotInitializedError } from '../errors'
@@ -84,6 +84,7 @@ class FormCollection {
84
84
 
85
85
  if (!Hellotext.business.data) {
86
86
  Hellotext.business.setData(data.business)
87
+ Hellotext.business.setLocale(Locale.toString())
87
88
  }
88
89
 
89
90
  if (!Hellotext.business.enabledWhitelist) {
@@ -1,7 +1,7 @@
1
1
  export { Business } from './business'
2
- export { Form } from './form'
3
- export { Query } from './query'
4
2
  export { Cookies } from './cookies'
3
+ export { Form } from './form'
5
4
  export { FormCollection } from './form_collection'
6
- export { Webchat } from './webchat'
5
+ export { Query } from './query'
7
6
  export { Session } from './session'
7
+ export { Webchat } from './webchat'
@@ -1,5 +1,3 @@
1
- import { Cookies } from './cookies'
2
-
3
1
  class Query {
4
2
  static get inPreviewMode() {
5
3
  return new this().inPreviewMode
@@ -22,7 +20,7 @@ class Query {
22
20
  }
23
21
 
24
22
  get session() {
25
- return this.get('session') || Cookies.get('hello_session')
23
+ return this.get('session')
26
24
  }
27
25
 
28
26
  toHellotextParam(param) {
@@ -18,7 +18,7 @@ class Session {
18
18
  static initialize() {
19
19
  this.#query = new Query()
20
20
 
21
- this.session = Configuration.session || this.#query.session || Cookies.get('hello_session')
21
+ this.session = this.#query.session || Configuration.session || Cookies.get('hello_session')
22
22
 
23
23
  if (!this.session && Configuration.autoGenerateSession) {
24
24
  this.session = crypto.randomUUID()