@monterosa/sdk-interact-kit 2.0.0-rc.2 → 2.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2095 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var sdkUtil = require('@monterosa/sdk-util');
6
+ var sdkConnectKit = require('@monterosa/sdk-connect-kit');
7
+ var sdkCore = require('@monterosa/sdk-core');
8
+ var sdkInteractInterop = require('@monterosa/sdk-interact-interop');
9
+ var sdkStorageKit = require('@monterosa/sdk-storage-kit');
10
+
11
+ /**
12
+ * @license
13
+ * @monterosa/sdk-interact-kit
14
+ *
15
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
16
+ *
17
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
18
+ */
19
+ /**
20
+ * @internal
21
+ */
22
+ exports.State = void 0;
23
+ (function (State) {
24
+ State["Ok"] = "ok";
25
+ State["Error"] = "error";
26
+ })(exports.State || (exports.State = {}));
27
+ /**
28
+ * @internal
29
+ */
30
+ exports.Klass = void 0;
31
+ (function (Klass) {
32
+ Klass["Listings"] = "listings";
33
+ Klass["History"] = "history";
34
+ Klass["Create"] = "create";
35
+ Klass["Revoke"] = "revoke";
36
+ Klass["Stop"] = "stop";
37
+ Klass["Reveal"] = "reveal";
38
+ Klass["Poll"] = "p";
39
+ Klass["Feedback"] = "fb";
40
+ Klass["Eoc"] = "eoc";
41
+ Klass["Vote"] = "v";
42
+ Klass["SubsCounter"] = "subscounter";
43
+ })(exports.Klass || (exports.Klass = {}));
44
+ /**
45
+ * @internal
46
+ */
47
+ exports.Channel = void 0;
48
+ (function (Channel) {
49
+ Channel["Poll"] = "p";
50
+ Channel["Live"] = "l";
51
+ })(exports.Channel || (exports.Channel = {}));
52
+
53
+ /**
54
+ * @license
55
+ * @monterosa/sdk-interact-kit
56
+ *
57
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
58
+ *
59
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
60
+ */
61
+ class ConnectImpl extends sdkUtil.Emitter {
62
+ constructor(host) {
63
+ super();
64
+ this.host = host;
65
+ this._state = exports.State.Ok;
66
+ this._enmasse = null;
67
+ }
68
+ get state() {
69
+ return this._state;
70
+ }
71
+ set state(state) {
72
+ if (state === this._state) {
73
+ return;
74
+ }
75
+ this._state = state;
76
+ this.emit('state', this._state);
77
+ }
78
+ get enmasse() {
79
+ if (this._enmasse === null) {
80
+ throw new Error('Connect is not initialised, init() method should be called first.');
81
+ }
82
+ return this._enmasse;
83
+ }
84
+ set enmasse(enmasse) {
85
+ this._enmasse = enmasse;
86
+ }
87
+ async init() {
88
+ this.enmasse = await sdkConnectKit.getConnect(this.host);
89
+ this.enmasse.on('message', this.handleMessage.bind(this));
90
+ this.enmasse.on('state', this.handleState.bind(this));
91
+ }
92
+ async connect() {
93
+ await sdkConnectKit.connect(this.enmasse);
94
+ }
95
+ disconnect() {
96
+ sdkConnectKit.disconnect(this.enmasse);
97
+ }
98
+ async subscribeProject(id) {
99
+ await sdkConnectKit.subscribe(this.enmasse, `${exports.Channel.Live}-${id}`);
100
+ }
101
+ unsubscribeProject(id) {
102
+ sdkConnectKit.unsubscribe(this.enmasse, `${exports.Channel.Live}-${id}`);
103
+ }
104
+ async subscribeEvent(id) {
105
+ await Promise.all([
106
+ sdkConnectKit.subscribe(this.enmasse, `${exports.Channel.Live}-${id}`),
107
+ sdkConnectKit.subscribe(this.enmasse, `${exports.Channel.Poll}-${id}`),
108
+ ]);
109
+ }
110
+ unsubscribeEvent(id) {
111
+ sdkConnectKit.unsubscribe(this.enmasse, `${exports.Channel.Live}-${id}`);
112
+ sdkConnectKit.unsubscribe(this.enmasse, `${exports.Channel.Poll}-${id}`);
113
+ }
114
+ sendVote(eventId, pollId, answer) {
115
+ sdkConnectKit.send(this.enmasse, `${exports.Channel.Poll}-${eventId}`, exports.Klass.Vote, [
116
+ pollId,
117
+ answer,
118
+ '',
119
+ ]);
120
+ }
121
+ handleMessage(message) {
122
+ const { klass, body: data, channel } = message;
123
+ const id = channel.substring(channel.indexOf('-') + 1);
124
+ this.emit('message', {
125
+ id,
126
+ klass,
127
+ data,
128
+ });
129
+ }
130
+ handleState(state, ...args) {
131
+ switch (state) {
132
+ case sdkConnectKit.ConnState.Disconnected:
133
+ this.state = exports.State.Error;
134
+ break;
135
+ case sdkConnectKit.ConnState.Connecting: {
136
+ const [attempt] = args;
137
+ if (attempt > 1) {
138
+ this.state = exports.State.Error;
139
+ }
140
+ break;
141
+ }
142
+ case sdkConnectKit.ConnState.Connected:
143
+ this.state = exports.State.Ok;
144
+ break;
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * @license
151
+ * @monterosa/sdk-interact-kit
152
+ *
153
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
154
+ *
155
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
156
+ */
157
+ const getConnectMemoized = sdkUtil.memoizePromise(async (host) => {
158
+ const connect = new ConnectImpl(host);
159
+ await connect.init();
160
+ await connect.connect();
161
+ return connect;
162
+ }, (host) => host);
163
+ /**
164
+ * @internal
165
+ */
166
+ function getConnect(host) {
167
+ return getConnectMemoized(host);
168
+ }
169
+
170
+ /**
171
+ * @license
172
+ * @monterosa/sdk-interact-kit
173
+ *
174
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
175
+ *
176
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
177
+ */
178
+ /**
179
+ * Provides states of the connection health
180
+ */
181
+ exports.ConnectionHealthState = void 0;
182
+ (function (ConnectionHealthState) {
183
+ /**
184
+ * The client is connected and the connection is healthy
185
+ */
186
+ ConnectionHealthState["Ok"] = "ok";
187
+ /**
188
+ * The client is either trying to establish a connection but failing, or
189
+ * the client has been explicitly {@link @monterosa/sdk-connect-kit#disconnect() | disconnected}
190
+ */
191
+ ConnectionHealthState["Error"] = "error";
192
+ })(exports.ConnectionHealthState || (exports.ConnectionHealthState = {}));
193
+
194
+ /**
195
+ * @license
196
+ * @monterosa/sdk-interact-kit
197
+ *
198
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
199
+ *
200
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
201
+ */
202
+ class ConnectionHealthImpl extends sdkUtil.Emitter {
203
+ constructor(connect) {
204
+ super();
205
+ this.connect = connect;
206
+ this._state = exports.ConnectionHealthState.Ok;
207
+ this.connect.on('state', this.handleState.bind(this));
208
+ }
209
+ get state() {
210
+ return this._state;
211
+ }
212
+ set state(state) {
213
+ this._state = state;
214
+ this.emit('state', state);
215
+ }
216
+ handleState() {
217
+ switch (this.connect.state) {
218
+ default:
219
+ case exports.State.Ok:
220
+ this.state = exports.ConnectionHealthState.Ok;
221
+ break;
222
+ case exports.State.Error:
223
+ this.state = exports.ConnectionHealthState.Error;
224
+ break;
225
+ }
226
+ }
227
+ }
228
+
229
+ /**
230
+ * @license
231
+ * @monterosa/sdk-interact-kit
232
+ *
233
+ * Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
234
+ *
235
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
236
+ */
237
+ const getConnectionHealthMemoized = sdkUtil.memoizePromise(async (sdk) => {
238
+ const connect = await getConnect(sdk.options.host);
239
+ const connectionHealth = new ConnectionHealthImpl(connect);
240
+ return connectionHealth;
241
+ }, (sdk) => sdk.options.host);
242
+ /**
243
+ * Returns {@link ConnectionHealth | connection health} instance
244
+ *
245
+ * @param sdk - The SDK instance to monitor
246
+ * @returns The connection health instance.
247
+ */
248
+ function getConnectionHealth(sdk = sdkCore.getSdk()) {
249
+ return getConnectionHealthMemoized(sdk);
250
+ }
251
+ /**
252
+ * Adds an observer for when
253
+ * {@link ConnectionHealth | connection health state} changed
254
+ *
255
+ * @param connectionHealth - The instance to observe
256
+ * @param callback - Called with the new state
257
+ */
258
+ function onConnectionHealthState(connectionHealth, callback) {
259
+ return sdkUtil.subscribe(connectionHealth, 'state', callback);
260
+ }
261
+
262
+ /**
263
+ * @license
264
+ * @monterosa/sdk-interact-kit
265
+ *
266
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
267
+ *
268
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
269
+ */
270
+ const DEFAULT_IGNORE_TIME = 30;
271
+
272
+ /**
273
+ * @license
274
+ * @monterosa/sdk-interact-kit
275
+ *
276
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
277
+ *
278
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
279
+ */
280
+ /**
281
+ * Converts tag for identifying languages (rfc5646) into ISO language code
282
+ */
283
+ function tagToLanguage(tag) {
284
+ // The tag is split not only by `-` as its stated in the rfc
285
+ // but also by `_` as it is returned by Studio for some locales
286
+ const chunks = tag.split(/[-_]/);
287
+ return chunks[0];
288
+ }
289
+ /**
290
+ * Returns either:
291
+ * * a locale from the list of provided locales that correspond OS locale preferences
292
+ * * or default locale if OS locale doesn't correspond to any from the provided list
293
+ *
294
+ * * @internal
295
+ */
296
+ function getLocale(locales) {
297
+ // The list of user's preferred language tags,
298
+ const languageTags = [
299
+ // where the first item is the user's default language
300
+ navigator.language,
301
+ // and others are the user's preferred languages
302
+ ...navigator.languages,
303
+ ];
304
+ // firstly, trying to check if one of languages tag
305
+ // has a full match with passed locales
306
+ for (const tag of languageTags) {
307
+ if (locales.includes(tag)) {
308
+ // if language tag has a match return it right away
309
+ return tag;
310
+ }
311
+ }
312
+ // secondly, checking if user's language (ISO string)
313
+ // has occurrences in the list of locales
314
+ for (const tag of languageTags) {
315
+ const userLang = tagToLanguage(tag);
316
+ for (const locale of locales) {
317
+ const studioLang = tagToLanguage(locale);
318
+ if (studioLang === userLang) {
319
+ // return the first locale that has the same
320
+ // language ISO string as the user language tag
321
+ return locale;
322
+ }
323
+ }
324
+ }
325
+ // if none locales matches users preferred languages tags
326
+ // return default locale
327
+ return locales[0];
328
+ }
329
+ function localise(value, locale) {
330
+ if (Object.prototype.hasOwnProperty.call(value, locale)) {
331
+ return value[locale];
332
+ }
333
+ return value.all;
334
+ }
335
+
336
+ /**
337
+ * @license
338
+ * @monterosa/sdk-interact-kit
339
+ *
340
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
341
+ *
342
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
343
+ */
344
+ /**
345
+ * @internal
346
+ */
347
+ class ProjectImpl extends sdkUtil.Emitter {
348
+ constructor(options, context) {
349
+ super();
350
+ this.eventsChecksum = null;
351
+ this.id = options.id;
352
+ this.host = options.host;
353
+ this._context = context;
354
+ }
355
+ get context() {
356
+ return this._context;
357
+ }
358
+ get embedUrl() {
359
+ var _a;
360
+ return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.embed;
361
+ }
362
+ get uuid() {
363
+ var _a;
364
+ return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.uuid;
365
+ }
366
+ get events() {
367
+ var _a;
368
+ return ((_a = this._listings) === null || _a === void 0 ? void 0 : _a.events) || [];
369
+ }
370
+ get listings() {
371
+ return this._listings;
372
+ }
373
+ set listings(newListings) {
374
+ const createdEvents = this._calculateCreatedEvents(newListings);
375
+ const updatedEvents = this._calculateUpdatedEvents(newListings);
376
+ const deletedEvents = this._calculateDeletedEvents(newListings);
377
+ this._listings = Object.assign({}, newListings);
378
+ this._updateLocales(newListings);
379
+ this._updateEventsChecksum(newListings);
380
+ this._emitListingsEvents(createdEvents, updatedEvents, deletedEvents);
381
+ }
382
+ /**
383
+ * Calculates events that were created (present in new listings but not in old)
384
+ */
385
+ _calculateCreatedEvents(newListings) {
386
+ if (!this._listings) {
387
+ return [];
388
+ }
389
+ return newListings.events.filter(({ id }) => !this._listings.events.some((event) => id === event.id));
390
+ }
391
+ /**
392
+ * Calculates events that were updated (present in old listings and new,
393
+ * but with different digest)
394
+ */
395
+ _calculateUpdatedEvents(newListings) {
396
+ if (!this._listings) {
397
+ return [];
398
+ }
399
+ return newListings.events.filter((event) => {
400
+ // find event data in new listings
401
+ const existingEvent = this._listings.events.find(({ id }) => event.id === id);
402
+ // return true if event data is found and digest is different
403
+ return existingEvent && event.digest !== existingEvent.digest;
404
+ });
405
+ }
406
+ /**
407
+ * Calculates events that were deleted (present in old listings but not in new)
408
+ */
409
+ _calculateDeletedEvents(newListings) {
410
+ if (!this._listings) {
411
+ return [];
412
+ }
413
+ return this._listings.events.filter(({ id }) => !newListings.events.some((event) => id === event.id));
414
+ }
415
+ /**
416
+ * Processes and updates locales from project listings
417
+ */
418
+ _updateLocales(newListings) {
419
+ this._locales = newListings.project.locales
420
+ // clone the array first
421
+ .map((locale) => (Object.assign({}, locale)))
422
+ // sort an array so that the default locale is the first
423
+ .sort((locale) => (locale.default ? -1 : 0))
424
+ // return only keys of the locales
425
+ .map((locale) => locale.key);
426
+ this._locale = getLocale(this._locales);
427
+ }
428
+ /**
429
+ * Updates the events checksum based on new listings
430
+ */
431
+ _updateEventsChecksum(newListings) {
432
+ this.eventsChecksum = sdkUtil.checksum(newListings.events.map((event) => event.digest).join(''));
433
+ }
434
+ /**
435
+ * Emits events based on created and deleted events
436
+ */
437
+ _emitListingsEvents(createdEvents, updatedEvents, deletedEvents) {
438
+ const hasCreatedEvents = createdEvents.length > 0;
439
+ const hasDeletedEvents = deletedEvents.length > 0;
440
+ const hasUpdatedEvents = updatedEvents.length > 0;
441
+ const hasChanges = hasCreatedEvents || hasDeletedEvents || hasUpdatedEvents;
442
+ if (hasCreatedEvents) {
443
+ this.emit('listings_events_created', createdEvents);
444
+ }
445
+ if (hasDeletedEvents) {
446
+ this.emit('listings_events_deleted', deletedEvents);
447
+ }
448
+ if (hasUpdatedEvents) {
449
+ this.emit('listings_events_updated', updatedEvents);
450
+ }
451
+ if (hasChanges) {
452
+ this.emit('listings');
453
+ }
454
+ }
455
+ get isLocalisationSupported() {
456
+ var _a;
457
+ return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.is_localisation_supported;
458
+ }
459
+ get locale() {
460
+ return this._locale;
461
+ }
462
+ set locale(locale) {
463
+ if (!this.locales.includes(locale)) {
464
+ throw new Error(`Unknown locale name "${locale}"`);
465
+ }
466
+ this._locale = locale;
467
+ }
468
+ get locales() {
469
+ return this._locales;
470
+ }
471
+ get fields() {
472
+ if (this._fields !== undefined) {
473
+ return localise(this._fields, this.locale);
474
+ }
475
+ return {};
476
+ }
477
+ /**
478
+ * Sets custom fields
479
+ *
480
+ * It accepts both Localised<Record<string, unknown>> and Record<string, unknown> to
481
+ * avoid mistyping between setter and getter
482
+ *
483
+ * @internal
484
+ */
485
+ set fields(value) {
486
+ const isUpdate = typeof this._fields !== 'undefined';
487
+ this._fields = Object.assign({}, value);
488
+ if (isUpdate) {
489
+ this.emit('updated');
490
+ }
491
+ }
492
+ get extensions() {
493
+ var _a;
494
+ return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.assets;
495
+ }
496
+ get historyIgnore() {
497
+ var _a;
498
+ const value = (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.history_ignore;
499
+ if (value && value > 0) {
500
+ return value;
501
+ }
502
+ return DEFAULT_IGNORE_TIME;
503
+ }
504
+ // TODO: must be implemented
505
+ // eslint-disable-next-line
506
+ delete() { }
507
+ }
508
+
509
+ /**
510
+ * @license
511
+ * @monterosa/sdk-interact-kit
512
+ *
513
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
514
+ *
515
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
516
+ */
517
+ const projects = new Map();
518
+ const connects = new Map();
519
+ async function buildProject(options, context) {
520
+ const { id, host } = options;
521
+ if (projects.has(id)) {
522
+ return projects.get(id);
523
+ }
524
+ /**
525
+ * Create "bare" instance of the project. Until listings and settings are
526
+ * loaded it can't be used
527
+ */
528
+ const project = new ProjectImpl(options, context);
529
+ /**
530
+ * Add it to the cache of projects, so that we could find it to populate it
531
+ * with the data when /listings/ message is coming from the EnMasse.
532
+ */
533
+ projects.set(id, project);
534
+ /**
535
+ * Waiting when the listings and the project settings come and we would be
536
+ * able to populate the project instance
537
+ */
538
+ try {
539
+ const setup = await setupConnect(host);
540
+ await setup.subscribeProject(id);
541
+ /**
542
+ * By the time goLive successfully resolved listings must be present
543
+ * in project. If they are not, then there is no /listings/ in the channel's cache.
544
+ * This might be due to either studio is malfunctioning or project id is incorrect.
545
+ */
546
+ if (!project.listings) {
547
+ throw new Error(`Unable to fetch project ${id} listings. Possibly wrong project id.`);
548
+ }
549
+ project.fields = await sdkInteractInterop.fetchSettings(project.host, project.id);
550
+ return project;
551
+ }
552
+ catch (err) {
553
+ /**
554
+ * If anything goes wrong we need to unsubscribe from the EnMasse project's
555
+ * channel by switching it to the offline mode.
556
+ */
557
+ if (connects.has(id)) {
558
+ const connect = connects.get(id);
559
+ connect.unsubscribeProject(id);
560
+ }
561
+ /**
562
+ * And project should be removed from the list of cached projects so that
563
+ * it won't be automatically returned upon consequent buildProject call
564
+ */
565
+ projects.delete(id);
566
+ throw err;
567
+ }
568
+ }
569
+ async function setupConnect(host) {
570
+ if (connects.has(host)) {
571
+ return connects.get(host);
572
+ }
573
+ const connect = await getConnect(host);
574
+ connect.on('message', handleConnectMessage$1);
575
+ return connect;
576
+ }
577
+ async function handleConnectMessage$1(message) {
578
+ const { id, klass, data } = message;
579
+ const project = projects.get(id);
580
+ if (!project) {
581
+ return;
582
+ }
583
+ switch (klass) {
584
+ case exports.Klass.Listings:
585
+ await handleListings(project, data[0]);
586
+ break;
587
+ }
588
+ }
589
+ async function handleListings(project, data) {
590
+ var _a;
591
+ const listings = JSON.parse(data);
592
+ const newDigest = listings.project.app_settings_digest;
593
+ const oldDigest = (_a = project.listings) === null || _a === void 0 ? void 0 : _a.project.app_settings_digest;
594
+ project.listings = listings;
595
+ try {
596
+ if (oldDigest !== undefined && newDigest !== oldDigest) {
597
+ project.fields = await sdkInteractInterop.fetchSettings(project.host, project.id);
598
+ }
599
+ }
600
+ catch (_b) {
601
+ console.warn(`Unable to fetch project ${project.id} settings`);
602
+ }
603
+ }
604
+
605
+ /**
606
+ * @license
607
+ * @monterosa/sdk-interact-kit
608
+ *
609
+ * Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
610
+ *
611
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
612
+ */
613
+ /**
614
+ * @internal
615
+ */
616
+ const getProjectMemoized = sdkUtil.memoizePromise(async (sdk) => {
617
+ const project = await buildProject({
618
+ id: sdk.options.projectId,
619
+ host: sdk.options.host,
620
+ }, {
621
+ sdk,
622
+ });
623
+ return project;
624
+ }, (sdk) => sdk.options.projectId);
625
+ /**
626
+ * Returns {@link InteractProject | Project instance} associated
627
+ * with the
628
+ * {@link @monterosa/sdk-core#MonterosaSdk | configured SDK}
629
+ *
630
+ * @param sdk - The SDK instance. Defaults to the default SDK.
631
+ * @returns The Project instance.
632
+ */
633
+ function getProject(sdk = sdkCore.getSdk()) {
634
+ return getProjectMemoized(sdk);
635
+ }
636
+ /**
637
+ * Adds an observer for when
638
+ * {@link InteractProject | Project fields} are updated
639
+ *
640
+ * @param project - The Project to observe
641
+ * @param callback - Called when Project fields change
642
+ */
643
+ function onProjectFieldsUpdated(project, callback) {
644
+ return sdkUtil.subscribe(project, 'updated', callback);
645
+ }
646
+
647
+ /******************************************************************************
648
+ Copyright (c) Microsoft Corporation.
649
+
650
+ Permission to use, copy, modify, and/or distribute this software for any
651
+ purpose with or without fee is hereby granted.
652
+
653
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
654
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
655
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
656
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
657
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
658
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
659
+ PERFORMANCE OF THIS SOFTWARE.
660
+ ***************************************************************************** */
661
+
662
+ function __values(o) {
663
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
664
+ if (m) return m.call(o);
665
+ if (o && typeof o.length === "number") return {
666
+ next: function () {
667
+ if (o && i >= o.length) o = void 0;
668
+ return { value: o && o[i++], done: !o };
669
+ }
670
+ };
671
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
672
+ }
673
+
674
+ function __asyncValues(o) {
675
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
676
+ var m = o[Symbol.asyncIterator], i;
677
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
678
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
679
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
680
+ }
681
+
682
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
683
+ var e = new Error(message);
684
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
685
+ };
686
+
687
+ /**
688
+ * @license
689
+ * @monterosa/sdk-interact-kit
690
+ *
691
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
692
+ *
693
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
694
+ */
695
+ async function fetchHistory(host, id) {
696
+ const response = await fetch(`https://${host}/events/${id.substring(0, 2)}/${id}/history.json`);
697
+ if (!response.ok) {
698
+ throw new Error(`Failed to fetch event ${id} history`);
699
+ }
700
+ const data = await response.json();
701
+ return data;
702
+ }
703
+
704
+ /**
705
+ * @license
706
+ * @monterosa/sdk-interact-kit
707
+ *
708
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
709
+ *
710
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
711
+ */
712
+ /**
713
+ * Describes the Event state.
714
+ */
715
+ exports.EventState = void 0;
716
+ (function (EventState) {
717
+ /**
718
+ * The Event is in the `upcoming` state when its {@link InteractEvent | startAt}
719
+ * less than the {@link @monterosa/sdk-util#now | current time}.
720
+ */
721
+ EventState["Upcoming"] = "upcoming";
722
+ /**
723
+ * The Event is in the `active` state when its {@link InteractEvent | startAt}
724
+ * equal or more than the {@link @monterosa/sdk-util#now | current time} and less than the
725
+ * {@link InteractEvent | endAt}.
726
+ */
727
+ EventState["Active"] = "active";
728
+ /**
729
+ * @internal
730
+ */
731
+ EventState["Prolonged"] = "prolonged";
732
+ /**
733
+ * The Event is in the `finished` state when its {@link InteractEvent | endAt}
734
+ * equal or more than the {@link @monterosa/sdk-util#now | current time}.
735
+ */
736
+ EventState["Finished"] = "finished";
737
+ })(exports.EventState || (exports.EventState = {}));
738
+ var InitState;
739
+ (function (InitState) {
740
+ InitState["Uninitialised"] = "uninitialised";
741
+ InitState["Initialising"] = "initialising";
742
+ InitState["Initialised"] = "initialised";
743
+ })(InitState || (InitState = {}));
744
+
745
+ /**
746
+ * @license
747
+ * @monterosa/sdk-interact-kit
748
+ *
749
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
750
+ *
751
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
752
+ */
753
+ /**
754
+ * @internal
755
+ */
756
+ class EventImpl extends sdkUtil.Emitter {
757
+ constructor(data, context) {
758
+ super();
759
+ this._initState = InitState.Uninitialised;
760
+ this._history = null;
761
+ this._data = data;
762
+ this._context = context;
763
+ this._state = this.calculateState();
764
+ this._internalState = this.calculateInternalState();
765
+ this.boundHandleState = this.handleState.bind(this);
766
+ this.boundHandleInternalState = this.handleInternalState.bind(this);
767
+ this.boundHandleEventsUpdated = this.handleEventsUpdated.bind(this);
768
+ this.unsubscribeStateHandler = sdkUtil.onTick(this.boundHandleState);
769
+ this.unsubscribeInternalStateHandler = sdkUtil.onTick(this.boundHandleInternalState);
770
+ this.unsubscribeListingsHandler = sdkUtil.subscribe(this.context.project, 'listings_events_updated', this.boundHandleEventsUpdated);
771
+ }
772
+ calculateState() {
773
+ if (!this._data.started || sdkUtil.now() < this.startAt) {
774
+ return exports.EventState.Upcoming;
775
+ }
776
+ if (sdkUtil.now() < this.endAt) {
777
+ return exports.EventState.Active;
778
+ }
779
+ return exports.EventState.Finished;
780
+ }
781
+ calculateInternalState() {
782
+ if (!this._data.started || sdkUtil.now() < this.startAt) {
783
+ return exports.EventState.Upcoming;
784
+ }
785
+ if (sdkUtil.now() < this.endAt) {
786
+ return exports.EventState.Active;
787
+ }
788
+ if (sdkUtil.now() < this.endAt + this._data.extra_time) {
789
+ return exports.EventState.Prolonged;
790
+ }
791
+ return exports.EventState.Finished;
792
+ }
793
+ handleState() {
794
+ const state = this.calculateState();
795
+ if (state === exports.EventState.Finished) {
796
+ this.unsubscribeStateHandler();
797
+ }
798
+ if (state !== this._state) {
799
+ this._state = state;
800
+ this.emit('state', state);
801
+ }
802
+ }
803
+ handleInternalState() {
804
+ const internalState = this.calculateInternalState();
805
+ if (internalState === exports.EventState.Finished) {
806
+ this.unsubscribeInternalStateHandler();
807
+ }
808
+ if (internalState !== this._internalState) {
809
+ this._internalState = internalState;
810
+ this.emit('internal_state', internalState);
811
+ }
812
+ }
813
+ handleEventsUpdated(events) {
814
+ const data = events.find(({ id }) => id === this.id);
815
+ if (data && this._data.digest !== data.digest) {
816
+ this._data = data;
817
+ // Forcing state recalculation to ensure the event is in the correct state
818
+ this.handleState();
819
+ this.handleInternalState();
820
+ this.emit('updated');
821
+ }
822
+ }
823
+ /** @internal */
824
+ destroy() {
825
+ this.unsubscribeStateHandler();
826
+ this.unsubscribeInternalStateHandler();
827
+ this.unsubscribeListingsHandler();
828
+ }
829
+ get context() {
830
+ return this._context;
831
+ }
832
+ get id() {
833
+ return this._data.id;
834
+ }
835
+ get name() {
836
+ return this._data.name;
837
+ }
838
+ get startAt() {
839
+ return this._data.start_at;
840
+ }
841
+ get startAtIso() {
842
+ return this._data.start_at_iso;
843
+ }
844
+ get endAt() {
845
+ return this._data.end_at;
846
+ }
847
+ get endAtIso() {
848
+ return this._data.end_at_iso;
849
+ }
850
+ get duration() {
851
+ return this._data.duration;
852
+ }
853
+ get fields() {
854
+ const { locale } = this.context.project;
855
+ return localise(this._data.custom_fields, locale);
856
+ }
857
+ get state() {
858
+ return this._state;
859
+ }
860
+ get internalState() {
861
+ return this._internalState;
862
+ }
863
+ get initState() {
864
+ return this._initState;
865
+ }
866
+ set initState(state) {
867
+ this._initState = state;
868
+ }
869
+ get history() {
870
+ return this._history;
871
+ }
872
+ set history(value) {
873
+ this._history = value;
874
+ this.emit('history', value);
875
+ }
876
+ get canSubscribe() {
877
+ return (this._internalState === exports.EventState.Active ||
878
+ this._internalState === exports.EventState.Prolonged);
879
+ }
880
+ get canLoadHistory() {
881
+ return sdkUtil.now() >= this.startAt + this.context.project.historyIgnore;
882
+ }
883
+ }
884
+
885
+ /**
886
+ * @license
887
+ * @monterosa/sdk-interact-kit
888
+ *
889
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
890
+ *
891
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
892
+ */
893
+ const events = new Map();
894
+ const healthListeners = new Map();
895
+ function buildEvent(options, context) {
896
+ if (events.has(options.id)) {
897
+ return events.get(options.id);
898
+ }
899
+ const event = new EventImpl(options, context);
900
+ event.on('internal_state', handleEventInternalState.bind(event));
901
+ events.set(options.id, event);
902
+ return event;
903
+ }
904
+ async function init(event) {
905
+ if (event.initState === InitState.Initialising) {
906
+ return Promise.reject(new Error("can't initialise event, event is already being initialised"));
907
+ }
908
+ if (event.initState === InitState.Initialised) {
909
+ return Promise.reject(new Error("can't initialise event, event is already initialised"));
910
+ }
911
+ event.initState = InitState.Initialising;
912
+ if (event.canLoadHistory && event.history === null) {
913
+ const history = await fetchHistory(event.context.project.host, event.id);
914
+ event.history = history;
915
+ }
916
+ if (event.canSubscribe) {
917
+ await subscribe(event);
918
+ }
919
+ await listenConnHealth(event);
920
+ event.initState = InitState.Initialised;
921
+ return Promise.resolve();
922
+ }
923
+ async function subscribe(event) {
924
+ if (!event.canSubscribe) {
925
+ return Promise.reject(new Error("Event can't be subscribed"));
926
+ }
927
+ const connect = await getConnect(event.context.project.host);
928
+ await connect.subscribeEvent(event.id);
929
+ return Promise.resolve();
930
+ }
931
+ async function unsubscribe(event) {
932
+ const connect = await getConnect(event.context.project.host);
933
+ await connect.unsubscribeEvent(event.id);
934
+ return Promise.resolve();
935
+ }
936
+ async function listenConnHealth(event) {
937
+ const { project } = event.context;
938
+ if (healthListeners.has(project.id)) {
939
+ return;
940
+ }
941
+ const connect = await getConnect(project.host);
942
+ if (connect.enmasse !== null) {
943
+ const unsub = sdkConnectKit.onConnected(connect.enmasse, handleEnmasseConnected);
944
+ healthListeners.set(project.id, unsub);
945
+ }
946
+ }
947
+ async function handleEventInternalState(state) {
948
+ if (state === exports.EventState.Finished) {
949
+ await unsubscribe(this);
950
+ }
951
+ }
952
+ async function handleEnmasseConnected() {
953
+ var _a, e_1, _b, _c;
954
+ try {
955
+ for (var _d = true, _e = __asyncValues(Array.from(events.values())), _f; _f = await _e.next(), _a = _f.done, !_a;) {
956
+ _c = _f.value;
957
+ _d = false;
958
+ try {
959
+ const event = _c;
960
+ if (event.initState === InitState.Initialised) {
961
+ const history = await fetchHistory(event.context.project.host, event.id);
962
+ event.history = history;
963
+ }
964
+ }
965
+ finally {
966
+ _d = true;
967
+ }
968
+ }
969
+ }
970
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
971
+ finally {
972
+ try {
973
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
974
+ }
975
+ finally { if (e_1) throw e_1.error; }
976
+ }
977
+ }
978
+
979
+ /**
980
+ * @internal
981
+ */
982
+ const getEventsMemoized = sdkUtil.memoizePromise(async (project) => {
983
+ if (project === undefined) {
984
+ project = await getProject();
985
+ }
986
+ const context = Object.assign(Object.assign({}, project.context), { project });
987
+ return (project.events
988
+ // Calling builder function that creates an Event instance.
989
+ // Existing Event will be returned if it already exists in the cache
990
+ .map((data) => buildEvent(data, context))
991
+ // Sort Events so the oldest Event is first, and the most recent is last
992
+ .sort((a, b) => a.startAt - b.startAt));
993
+ },
994
+ /**
995
+ * Resolver function that determines the cache key for memoization.
996
+ *
997
+ * The cache key is used to determine whether a cached result can be reused.
998
+ * It prioritizes `eventsChecksum` over `id` to ensure cache invalidation when
999
+ * the events list changes, even if the project ID remains the same.
1000
+ *
1001
+ * @param project - Optional project instance. If not provided, attempts to
1002
+ * resolve using the default project from the SDK.
1003
+ * @returns A cache key string derived from the project's checksum/id or SDK projectId
1004
+ */
1005
+ (project) => {
1006
+ var _a, _b;
1007
+ // If project is provided, use its checksum (if available) or id as cache key
1008
+ if (project !== undefined) {
1009
+ return (_a = project.eventsChecksum) !== null && _a !== void 0 ? _a : project.id;
1010
+ }
1011
+ // If no project provided, try to get default project from SDK
1012
+ const sdk = sdkCore.getSdk();
1013
+ const defaultProject = projects.get(sdk.options.projectId);
1014
+ // Use default project's checksum/id if available
1015
+ if (defaultProject) {
1016
+ return (_b = defaultProject.eventsChecksum) !== null && _b !== void 0 ? _b : defaultProject.id;
1017
+ }
1018
+ // Fallback to SDK's projectId option
1019
+ return sdk.options.projectId;
1020
+ });
1021
+ /**
1022
+ * Returns all Events in a Project, including all active Events and
1023
+ * past/upcoming Events, according to Listings settings in Project Setup
1024
+ * in Studio.
1025
+ *
1026
+ * @param project - A Project instance. If not provided,
1027
+ * the one from the default SDK will be fetched.
1028
+ * @returns A promise that resolves to an array of Events.
1029
+ */
1030
+ function getEvents(project) {
1031
+ return getEventsMemoized(project);
1032
+ }
1033
+ /**
1034
+ * @internal
1035
+ */
1036
+ const getEventMemoized = sdkUtil.memoizePromise(async (id, project) => {
1037
+ if (events.has(id)) {
1038
+ return events.get(id);
1039
+ }
1040
+ if (project === undefined) {
1041
+ project = await getProject();
1042
+ }
1043
+ const context = Object.assign(Object.assign({}, project.context), { project });
1044
+ const data = project.events.find((eventData) => eventData.id === id);
1045
+ if (data !== undefined) {
1046
+ const event = buildEvent(data, context);
1047
+ if (event.canLoadHistory) {
1048
+ const history = await fetchHistory(project.host, id);
1049
+ event.history = history;
1050
+ }
1051
+ return event;
1052
+ }
1053
+ try {
1054
+ const history = await fetchHistory(project.host, id);
1055
+ const event = buildEvent(history.config, context);
1056
+ event.history = history;
1057
+ return event;
1058
+ }
1059
+ catch (err) {
1060
+ return null;
1061
+ }
1062
+ }, (id) => id);
1063
+ /**
1064
+ * Returns an Event by its id
1065
+ *
1066
+ * @param id - Id of the Event
1067
+ * @param project - A Project instance. If not provided,
1068
+ * the one from the default SDK will be fetched.
1069
+ *
1070
+ * @returns The Event associated with the provided id,
1071
+ * or null if no such Event exists in the Project.
1072
+ */
1073
+ function getEvent(id, project) {
1074
+ return getEventMemoized(id, project);
1075
+ }
1076
+ /**
1077
+ * Adds an observer for when
1078
+ * {@link InteractEvent | Event state} changed
1079
+ *
1080
+ * @param event - The Event to observe
1081
+ * @param callback - Called with the new state
1082
+ */
1083
+ function onEventState(event, callback) {
1084
+ return sdkUtil.subscribe(event, 'state', callback);
1085
+ }
1086
+ /**
1087
+ * Adds an observer for when Event's data changed
1088
+ *
1089
+ * @param event - The Event to observe
1090
+ * @param callback - Called when the Event data changes
1091
+ */
1092
+ function onEventUpdated(event, callback) {
1093
+ return sdkUtil.subscribe(event, 'updated', callback);
1094
+ }
1095
+ /**
1096
+ * Adds an observer that is called when an Event is added to listings.
1097
+ *
1098
+ * @remarks
1099
+ * The following actions will result in adding an Event to listings:
1100
+ *
1101
+ * - The start of the Event in Studio
1102
+ *
1103
+ * - The scheduling of a future Event in Studio, when listings are configured
1104
+ * to include future Events in Project Settings.
1105
+ *
1106
+ * - When a future Event starts, a new future Event may be added to the list,
1107
+ * depending on the Project configuration, to ensure a minimum number of
1108
+ * upcoming Events are always available.
1109
+ *
1110
+ * @param project - The Project to observe
1111
+ * @param callback - Called with the added Event
1112
+ */
1113
+ function onEventAdded(project, callback) {
1114
+ return sdkUtil.subscribe(project, 'listings_events_created', async (data) => {
1115
+ var _a, e_1, _b, _c;
1116
+ try {
1117
+ for (var _d = true, data_1 = __asyncValues(data), data_1_1; data_1_1 = await data_1.next(), _a = data_1_1.done, !_a;) {
1118
+ _c = data_1_1.value;
1119
+ _d = false;
1120
+ try {
1121
+ const options = _c;
1122
+ const event = await getEvent(options.id, project);
1123
+ if (event !== null) {
1124
+ callback(event);
1125
+ }
1126
+ }
1127
+ finally {
1128
+ _d = true;
1129
+ }
1130
+ }
1131
+ }
1132
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
1133
+ finally {
1134
+ try {
1135
+ if (!_d && !_a && (_b = data_1.return)) await _b.call(data_1);
1136
+ }
1137
+ finally { if (e_1) throw e_1.error; }
1138
+ }
1139
+ });
1140
+ }
1141
+ /**
1142
+ * Adds an observer that is called when an Event is removed from listings.
1143
+ *
1144
+ * @remarks
1145
+ * The following actions will result in removing an Event from listings:
1146
+ *
1147
+ * - The deletion of the Event from Studio
1148
+ *
1149
+ * - The change of listings configuration in Project Settings to exclude future
1150
+ * or past Events.
1151
+ *
1152
+ * - The Event is removed after a period of time since its completion. This period
1153
+ * is calculated as 45 seconds plus the Maximum allowed delay (configured in
1154
+ * Project Settings in Studio).
1155
+ *
1156
+ * @param project - The Project to observe
1157
+ * @param callback - Called with the removed Event
1158
+ */
1159
+ function onEventRemoved(project, callback) {
1160
+ return sdkUtil.subscribe(project, 'listings_events_deleted', async (data) => {
1161
+ var _a, e_2, _b, _c;
1162
+ try {
1163
+ for (var _d = true, data_2 = __asyncValues(data), data_2_1; data_2_1 = await data_2.next(), _a = data_2_1.done, !_a;) {
1164
+ _c = data_2_1.value;
1165
+ _d = false;
1166
+ try {
1167
+ const options = _c;
1168
+ const event = await getEvent(options.id, project);
1169
+ if (event !== null) {
1170
+ callback(event);
1171
+ }
1172
+ }
1173
+ finally {
1174
+ _d = true;
1175
+ }
1176
+ }
1177
+ }
1178
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
1179
+ finally {
1180
+ try {
1181
+ if (!_d && !_a && (_b = data_2.return)) await _b.call(data_2);
1182
+ }
1183
+ finally { if (e_2) throw e_2.error; }
1184
+ }
1185
+ });
1186
+ }
1187
+
1188
+ /**
1189
+ * @license
1190
+ * @monterosa/sdk-interact-kit
1191
+ *
1192
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
1193
+ *
1194
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1195
+ */
1196
+ /**
1197
+ * A map of potential error codes to compare with errors thrown by
1198
+ * vote() or validateAnswer() functions.
1199
+ */
1200
+ exports.AnswerError = void 0;
1201
+ (function (AnswerError) {
1202
+ /** Selected option index is out of range. */
1203
+ AnswerError["OptionIndexOutOfRange"] = "out_of_range";
1204
+ /** Fewer options selected than the minimum required. */
1205
+ AnswerError["BelowMinVoteOptions"] = "below_min_vote_options";
1206
+ /** More options selected than the maximum allowed. */
1207
+ AnswerError["AboveMaxVoteOptions"] = "above_max_vote_options";
1208
+ /** User has exceeded the maximum votes allowed. */
1209
+ AnswerError["AboveMaxVotesPerUser"] = "above_max_per_user";
1210
+ /** A single option received more votes than allowed. */
1211
+ AnswerError["AboveMaxVotesPerOption"] = "above_max_per_option";
1212
+ /** Attempted to vote on a non-interactive element. */
1213
+ AnswerError["VotedOnNonInteractiveElement"] = "non_interactive_element";
1214
+ /** Attempted to vote on a closed element. */
1215
+ AnswerError["VotedOnClosedElement"] = "closed_element";
1216
+ /** No vote value was provided. */
1217
+ AnswerError["EmptyVote"] = "empty_vote";
1218
+ })(exports.AnswerError || (exports.AnswerError = {}));
1219
+ const AnswerErrorMessages = {
1220
+ [exports.AnswerError.OptionIndexOutOfRange]: (max, idx) => `Options index ${idx} is out of range [0..${max}]`,
1221
+ [exports.AnswerError.BelowMinVoteOptions]: (min, total) => `Below min (${min}) vote options: ${total}`,
1222
+ [exports.AnswerError.AboveMaxVoteOptions]: (max, total) => `Above max (${max}) vote options: ${total}`,
1223
+ [exports.AnswerError.AboveMaxVotesPerUser]: (max, total) => `Above max (${max}) votes per user: ${total}`,
1224
+ [exports.AnswerError.AboveMaxVotesPerOption]: (max, total) => `Above max (${max}) votes per option: ${total}`,
1225
+ [exports.AnswerError.VotedOnNonInteractiveElement]: (id) => `Non interact element ${id}`,
1226
+ [exports.AnswerError.VotedOnClosedElement]: (id) => `Element ${id} is closed`,
1227
+ [exports.AnswerError.EmptyVote]: (indices) => `Empty value for options indices: ${indices}`,
1228
+ };
1229
+ /**
1230
+ * The ElementState represents the potential states an Element is in.
1231
+ */
1232
+ exports.ElementState = void 0;
1233
+ (function (ElementState) {
1234
+ /**
1235
+ * Element is opened and active. The user can vote if
1236
+ * the Element is `interactive`
1237
+ */
1238
+ ElementState["Opened"] = "opened";
1239
+ /**
1240
+ * Element is closed and the user can no longer vote for
1241
+ * `interactive` Elements
1242
+ */
1243
+ ElementState["Closed"] = "closed";
1244
+ })(exports.ElementState || (exports.ElementState = {}));
1245
+
1246
+ /**
1247
+ * @license
1248
+ * @monterosa/sdk-interact-kit
1249
+ *
1250
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
1251
+ *
1252
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1253
+ */
1254
+ /**
1255
+ * An `Answer` contains data of the user answer. Can be used to manage number of
1256
+ * votes that the user placed for a specific option.
1257
+ */
1258
+ class Answer {
1259
+ constructor(answers = []) {
1260
+ this._answers = new Map();
1261
+ for (const { option, value } of answers) {
1262
+ this._answers.set(option, value);
1263
+ }
1264
+ }
1265
+ /**
1266
+ * The array of user answers
1267
+ */
1268
+ get answers() {
1269
+ return Array.from(this._answers.entries()).map(([option, value]) => ({
1270
+ option,
1271
+ value,
1272
+ }));
1273
+ }
1274
+ /**
1275
+ * Instantiate Answer class with the provided options indices. Number
1276
+ * of votes will be set to 1 for all options
1277
+ *
1278
+ * @example
1279
+ * ```javascript
1280
+ * // user votes for the first and fourth options
1281
+ * const answer = Answer.fromIndices([0, 3]);
1282
+ * ```
1283
+ *
1284
+ * @param indices - The array of option indices
1285
+ */
1286
+ static fromIndices(indices) {
1287
+ return new Answer(indices.map((option) => ({
1288
+ option,
1289
+ value: 1,
1290
+ })));
1291
+ }
1292
+ /**
1293
+ * Adds a vote for the provided option index
1294
+ *
1295
+ * @param option - The index of the option, starting with 0
1296
+ * @param value - The number of votes that will be placed against the option
1297
+ */
1298
+ set(option, value) {
1299
+ this._answers.set(option, value);
1300
+ return this;
1301
+ }
1302
+ /**
1303
+ * Removes the vote for the provided option index
1304
+ *
1305
+ * @param option - The index of the option, starting with 0
1306
+ */
1307
+ remove(option) {
1308
+ this._answers.delete(option);
1309
+ return this;
1310
+ }
1311
+ /** @internal */
1312
+ toJSON() {
1313
+ return this.answers;
1314
+ }
1315
+ /** @internal */
1316
+ static fromJSON(value) {
1317
+ return new Answer(JSON.parse(value));
1318
+ }
1319
+ }
1320
+
1321
+ /**
1322
+ * @license
1323
+ * @monterosa/sdk-interact-kit
1324
+ *
1325
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
1326
+ *
1327
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1328
+ */
1329
+ /**
1330
+ * Describes Element types
1331
+ */
1332
+ exports.ElementType = void 0;
1333
+ (function (ElementType) {
1334
+ /**
1335
+ * Data Element
1336
+ */
1337
+ ElementType["Data"] = "data";
1338
+ /**
1339
+ * Poll Element
1340
+ */
1341
+ ElementType["Poll"] = "poll";
1342
+ /**
1343
+ * Regular poll Element
1344
+ */
1345
+ ElementType["RegularPoll"] = "rpoll";
1346
+ /**
1347
+ * Diametric poll Element
1348
+ */
1349
+ ElementType["DiametricPoll"] = "dpoll";
1350
+ /**
1351
+ * Emoting poll Element
1352
+ */
1353
+ ElementType["EmotingPoll"] = "emo";
1354
+ /**
1355
+ * Powerbar Element
1356
+ */
1357
+ ElementType["Powerbar"] = "powerbar";
1358
+ /**
1359
+ * Prediction Element
1360
+ */
1361
+ ElementType["Prediction"] = "prediction";
1362
+ /**
1363
+ * Trivia Element
1364
+ */
1365
+ ElementType["Trivia"] = "trivia";
1366
+ })(exports.ElementType || (exports.ElementType = {}));
1367
+ var ShowResultsMode;
1368
+ (function (ShowResultsMode) {
1369
+ ShowResultsMode["OnVote"] = "vote";
1370
+ ShowResultsMode["OnClose"] = "close";
1371
+ ShowResultsMode["OnEventEnd"] = "event_end";
1372
+ ShowResultsMode["Manual"] = "manual";
1373
+ ShowResultsMode["Never"] = "never";
1374
+ ShowResultsMode["Always"] = "always";
1375
+ })(ShowResultsMode || (ShowResultsMode = {}));
1376
+
1377
+ /**
1378
+ * @license
1379
+ * @monterosa/sdk-interact-kit
1380
+ *
1381
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
1382
+ *
1383
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1384
+ */
1385
+ /**
1386
+ * @internal
1387
+ */
1388
+ class ElementImpl extends sdkUtil.Emitter {
1389
+ constructor(data, context) {
1390
+ var _a, _b;
1391
+ super();
1392
+ this._results = null;
1393
+ this._userAnswer = null;
1394
+ this._correctOption = null;
1395
+ this._data = data;
1396
+ this._context = context;
1397
+ this._results = ((_a = data.data) === null || _a === void 0 ? void 0 : _a.results) || null;
1398
+ this._correctOption = ((_b = data.data) === null || _b === void 0 ? void 0 : _b.correct_option)
1399
+ ? data.data.correct_option - 1
1400
+ : null;
1401
+ this._state = this.calculateState();
1402
+ this.unsubscribeStateHandler = sdkUtil.onTick(() => this.handleState());
1403
+ }
1404
+ calculateState() {
1405
+ if (sdkUtil.now() < this.publishedAt + this.duration) {
1406
+ return exports.ElementState.Opened;
1407
+ }
1408
+ return exports.ElementState.Closed;
1409
+ }
1410
+ handleState() {
1411
+ const state = this.calculateState();
1412
+ if (state === exports.ElementState.Closed) {
1413
+ this.unsubscribeStateHandler();
1414
+ }
1415
+ if (state !== this._state) {
1416
+ this._state = state;
1417
+ this.emit('state', state);
1418
+ }
1419
+ }
1420
+ update(data) {
1421
+ this._data = data;
1422
+ this.handleState();
1423
+ }
1424
+ destroy() {
1425
+ this.unsubscribeStateHandler();
1426
+ }
1427
+ get context() {
1428
+ return this._context;
1429
+ }
1430
+ get id() {
1431
+ return this._data.id;
1432
+ }
1433
+ get eventId() {
1434
+ return this.context.event.id;
1435
+ }
1436
+ get state() {
1437
+ return this._state;
1438
+ }
1439
+ get type() {
1440
+ return this._data.type;
1441
+ }
1442
+ get contentType() {
1443
+ return this._data.content_type;
1444
+ }
1445
+ get publishedAt() {
1446
+ return this._data.published_at;
1447
+ }
1448
+ get publishedAtIso() {
1449
+ return this._data.published_at_iso;
1450
+ }
1451
+ get updatedAt() {
1452
+ return this._data.updated_at;
1453
+ }
1454
+ get updatedAtIso() {
1455
+ return this._data.updated_at_iso;
1456
+ }
1457
+ get duration() {
1458
+ return this._data.duration;
1459
+ }
1460
+ get fields() {
1461
+ const { locale } = this.context.project;
1462
+ return localise(this._data.custom_fields, locale);
1463
+ }
1464
+ get interactive() {
1465
+ return this.type !== exports.ElementType.Data;
1466
+ }
1467
+ get pollId() {
1468
+ return this._data.data.id || null;
1469
+ }
1470
+ get question() {
1471
+ const { locale } = this.context.project;
1472
+ if (this._data.data.question === undefined) {
1473
+ return null;
1474
+ }
1475
+ return localise(this._data.data.question, locale);
1476
+ }
1477
+ get answerOptions() {
1478
+ const { locale } = this.context.project;
1479
+ if (this._data.data.options === undefined) {
1480
+ return null;
1481
+ }
1482
+ return localise(this._data.data.options, locale);
1483
+ }
1484
+ get minOptionsPerVote() {
1485
+ return this._data.data.min_options_per_vote || 1;
1486
+ }
1487
+ get maxOptionsPerVote() {
1488
+ return this._data.data.max_options_per_vote || 1;
1489
+ }
1490
+ get maxVotesPerOption() {
1491
+ return this._data.data.max_votes_per_option || 1;
1492
+ }
1493
+ get maxVotesPerUser() {
1494
+ return this._data.data.max_votes_per_user || null;
1495
+ }
1496
+ get userAnswer() {
1497
+ return this._userAnswer;
1498
+ }
1499
+ set userAnswer(value) {
1500
+ this._userAnswer = value;
1501
+ }
1502
+ get hasBeenAnswered() {
1503
+ return this._userAnswer !== null;
1504
+ }
1505
+ get showResultsMode() {
1506
+ return this._data.data.reveal_results_mode || null;
1507
+ }
1508
+ get canShowResults() {
1509
+ switch (this.showResultsMode) {
1510
+ case ShowResultsMode.OnVote:
1511
+ return this.hasBeenAnswered || this.state === exports.ElementState.Closed;
1512
+ case ShowResultsMode.OnClose:
1513
+ return this.state === exports.ElementState.Closed;
1514
+ case ShowResultsMode.Manual:
1515
+ return this._results !== null;
1516
+ case ShowResultsMode.OnEventEnd:
1517
+ return this.context.event.state === exports.EventState.Finished;
1518
+ case ShowResultsMode.Never:
1519
+ return false;
1520
+ case ShowResultsMode.Always:
1521
+ default:
1522
+ return true;
1523
+ }
1524
+ }
1525
+ get results() {
1526
+ if (!this.canShowResults) {
1527
+ return null;
1528
+ }
1529
+ return this._results;
1530
+ }
1531
+ set results(results) {
1532
+ // skip setting results if they weren't changed
1533
+ if (JSON.stringify(results) === JSON.stringify(this.results)) {
1534
+ return;
1535
+ }
1536
+ this._results = results;
1537
+ if (this.canShowResults) {
1538
+ this.emit('results');
1539
+ }
1540
+ }
1541
+ get correctOption() {
1542
+ return this._correctOption;
1543
+ }
1544
+ set correctOption(value) {
1545
+ this._correctOption = value;
1546
+ }
1547
+ get isCorrectOptionRevealed() {
1548
+ return this._correctOption !== null;
1549
+ }
1550
+ }
1551
+
1552
+ /**
1553
+ * @license
1554
+ * @monterosa/sdk-interact-kit
1555
+ *
1556
+ * Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
1557
+ *
1558
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1559
+ */
1560
+ const elements = new Map();
1561
+ async function buildElement(options, context) {
1562
+ if (elements.has(options.id)) {
1563
+ return elements.get(options.id);
1564
+ }
1565
+ const element = new ElementImpl(options, context);
1566
+ await restoreAnswer(element);
1567
+ elements.set(element.id, element);
1568
+ return element;
1569
+ }
1570
+ function getElementByPolld(pollId) {
1571
+ return Array.from(elements.values()).find((element) => element.pollId === pollId);
1572
+ }
1573
+ const configureEventMemoized = sdkUtil.memoizePromise(async (event) => {
1574
+ event.on('history', handleEventHistory);
1575
+ if (event.history !== null) {
1576
+ handleEventHistory(event.history);
1577
+ }
1578
+ await configureConnect(event.context.sdk.options.host);
1579
+ await init(event);
1580
+ }, (event) => event.id);
1581
+ async function configureEvent(event) {
1582
+ return configureEventMemoized(event);
1583
+ }
1584
+ const configureConnectMemoized = sdkUtil.memoizePromise(async (host) => {
1585
+ const connect = await getConnect(host);
1586
+ connect.on('message', handleConnectMessage);
1587
+ }, (host) => host);
1588
+ async function configureConnect(host) {
1589
+ return configureConnectMemoized(host);
1590
+ }
1591
+ async function handleConnectMessage(message) {
1592
+ const { klass } = message;
1593
+ switch (klass) {
1594
+ case exports.Klass.Create:
1595
+ await handleCreateMessage(message);
1596
+ break;
1597
+ case exports.Klass.Revoke:
1598
+ await handleRevokeMessage(message);
1599
+ break;
1600
+ case exports.Klass.Feedback:
1601
+ await handleFeedbackMessage(message);
1602
+ break;
1603
+ case exports.Klass.Reveal:
1604
+ await handleRevealMessage(message);
1605
+ break;
1606
+ }
1607
+ }
1608
+ async function handleCreateMessage(message) {
1609
+ const { id, data } = message;
1610
+ const options = JSON.parse(data[0]);
1611
+ const event = await getEvent(id);
1612
+ if (event === null) {
1613
+ return;
1614
+ }
1615
+ if (elements.has(options.id)) {
1616
+ const element = elements.get(options.id);
1617
+ if (options.updated_at > element.updatedAt ||
1618
+ // This is a workaround to handle a Studio issue when two /create/ messages
1619
+ // are sent upon correct option reveal. The first /create/ message contains
1620
+ // the same duration but a different updated_at timestamp. The second /create/
1621
+ // message contains the same updated_at timestamp but a different duration.
1622
+ // Therefore, we need to check if the duration is different to ensure that
1623
+ // the element is updated correctly.
1624
+ options.duration !== element.duration) {
1625
+ element.update(options);
1626
+ event.emit('update', element);
1627
+ }
1628
+ }
1629
+ else {
1630
+ const context = Object.assign(Object.assign({}, event.context), { event });
1631
+ const element = await buildElement(options, context);
1632
+ event.emit('publish', element);
1633
+ }
1634
+ }
1635
+ function handleRevokeMessage(message) {
1636
+ const { data } = message;
1637
+ const { id } = JSON.parse(data[0]);
1638
+ if (!elements.has(id)) {
1639
+ return;
1640
+ }
1641
+ const element = elements.get(id);
1642
+ elements.delete(id);
1643
+ element.destroy();
1644
+ element.context.event.emit('revoke', element);
1645
+ }
1646
+ async function handleFeedbackMessage(message) {
1647
+ const { data } = message;
1648
+ // Feedback message format:
1649
+ //
1650
+ // [pollId, isFinal, sources, counters, timestamp]
1651
+ const pollId = +data[0];
1652
+ const counters = data[3];
1653
+ const element = getElementByPolld(pollId);
1654
+ if (!element) {
1655
+ return;
1656
+ }
1657
+ const votes = counters.split(':').map((value) => +value);
1658
+ const percentages = sdkUtil.calculatePercentage(votes);
1659
+ const results = votes.map((item, idx) => ({
1660
+ percentage: percentages[idx],
1661
+ votes: item,
1662
+ }));
1663
+ element.results = results;
1664
+ }
1665
+ function handleRevealMessage(message) {
1666
+ const { data } = message;
1667
+ // Reveal message format:
1668
+ //
1669
+ // [pollId, index (1-based), timestamp]
1670
+ const pollId = +data[0];
1671
+ const correctOption = +data[1] - 1;
1672
+ const element = getElementByPolld(pollId);
1673
+ if (!element) {
1674
+ return;
1675
+ }
1676
+ if (element.correctOption !== correctOption) {
1677
+ element.correctOption = correctOption;
1678
+ element.context.event.emit('update', element);
1679
+ }
1680
+ }
1681
+ async function handleEventHistory(history) {
1682
+ var _a, e_1, _b, _c;
1683
+ if (history.timeline.length === 0) {
1684
+ return;
1685
+ }
1686
+ const event = await getEvent(history.config.id);
1687
+ if (event === null) {
1688
+ return;
1689
+ }
1690
+ const context = Object.assign(Object.assign({}, event.context), { event });
1691
+ try {
1692
+ for (var _d = true, _e = __asyncValues(history.timeline), _f; _f = await _e.next(), _a = _f.done, !_a;) {
1693
+ _c = _f.value;
1694
+ _d = false;
1695
+ try {
1696
+ const snapshot = _c;
1697
+ if (!elements.has(snapshot.id)) {
1698
+ await buildElement(snapshot, context);
1699
+ }
1700
+ }
1701
+ finally {
1702
+ _d = true;
1703
+ }
1704
+ }
1705
+ }
1706
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
1707
+ finally {
1708
+ try {
1709
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
1710
+ }
1711
+ finally { if (e_1) throw e_1.error; }
1712
+ }
1713
+ }
1714
+ async function sendAnswer(element, userAnswer) {
1715
+ const { project, event } = element.context;
1716
+ const connect = await getConnect(project.host);
1717
+ const answer = userAnswer.answers
1718
+ .map(({ option, value }) => [option + 1, value])
1719
+ .flat()
1720
+ .join(':');
1721
+ connect.sendVote(event.id, element.pollId, answer);
1722
+ }
1723
+ async function storeAnswer(element, userAnswer) {
1724
+ try {
1725
+ element.userAnswer = userAnswer;
1726
+ await sdkStorageKit.storageWrite(`element_${element.id}_vote`, JSON.stringify(userAnswer));
1727
+ }
1728
+ catch (err) {
1729
+ console.warn(`Failed to store user answer: ${sdkUtil.getErrorMessage(err)}`);
1730
+ }
1731
+ }
1732
+ async function restoreAnswer(element) {
1733
+ const userAnswer = await sdkStorageKit.storageRead(`element_${element.id}_vote`);
1734
+ if (userAnswer !== null) {
1735
+ element.userAnswer = Answer.fromJSON(userAnswer);
1736
+ }
1737
+ }
1738
+
1739
+ /**
1740
+ * @license
1741
+ * @monterosa/sdk-interact-kit
1742
+ *
1743
+ * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
1744
+ *
1745
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1746
+ */
1747
+ /**
1748
+ * @internal
1749
+ */
1750
+ const getElementMemoized = sdkUtil.memoizePromise(async (event, id) => {
1751
+ await configureEvent(event);
1752
+ const element = Array.from(elements.values()).find((el) => el.id === id);
1753
+ return element || null;
1754
+ }, (event, id) => id);
1755
+ /**
1756
+ * Returns an Element of a specific Event by its id.
1757
+ *
1758
+ * @param event - The Event that owns the Element
1759
+ * @param id - The Element identifier
1760
+ */
1761
+ function getElement(event, id) {
1762
+ return getElementMemoized(event, id);
1763
+ }
1764
+ /**
1765
+ * @internal
1766
+ */
1767
+ const getElementsMemoized = sdkUtil.memoizePromise(async (event) => {
1768
+ await configureEvent(event);
1769
+ return (Array.from(elements.values())
1770
+ // Get Elements which belong only to the provided Event
1771
+ .filter((element) => element.eventId === event.id)
1772
+ // Sort Elements so the oldest Element is first, and the most recent is last
1773
+ .sort((a, b) => a.publishedAt - b.publishedAt));
1774
+ }, (event) => event.id, {
1775
+ clearOnResolve: true,
1776
+ });
1777
+ /**
1778
+ * Returns the list of Elements published in a specific Event
1779
+ *
1780
+ * @param event - The Event to fetch Elements for
1781
+ * @returns The published Elements, sorted oldest first.
1782
+ */
1783
+ function getElements(event) {
1784
+ return getElementsMemoized(event);
1785
+ }
1786
+ function answer(element, ...value) {
1787
+ const answerObj = answerParametersToAnswer(...value);
1788
+ validateAnswer(element, ...value);
1789
+ sendAnswer(element, answerObj);
1790
+ storeAnswer(element, answerObj);
1791
+ }
1792
+ function validateAnswer(element, ...value) {
1793
+ const answerObj = answerParametersToAnswer(...value);
1794
+ const { answers } = answerObj;
1795
+ const indices = answers.map(({ option }) => option);
1796
+ const values = answers.map(({ value: v }) => v);
1797
+ const maxIndex = Math.max(...indices);
1798
+ const maxValue = Math.max(...values);
1799
+ const sumValues = values.reduce((memo, val) => memo + val, 0);
1800
+ const emptyIndices = values.reduce((memo, val, idx) => {
1801
+ if (val === 0) {
1802
+ memo.push(indices[idx]);
1803
+ }
1804
+ return memo;
1805
+ }, []);
1806
+ if (!element.interactive) {
1807
+ throw sdkUtil.createError(exports.AnswerError.VotedOnNonInteractiveElement, AnswerErrorMessages, element.id);
1808
+ }
1809
+ if (element.state === exports.ElementState.Closed) {
1810
+ throw sdkUtil.createError(exports.AnswerError.VotedOnClosedElement, AnswerErrorMessages, element.id);
1811
+ }
1812
+ if (maxIndex > element.answerOptions.length - 1) {
1813
+ throw sdkUtil.createError(exports.AnswerError.OptionIndexOutOfRange, AnswerErrorMessages, element.answerOptions.length - 1, maxIndex);
1814
+ }
1815
+ if (element.minOptionsPerVote !== null &&
1816
+ indices.length < element.minOptionsPerVote) {
1817
+ throw sdkUtil.createError(exports.AnswerError.BelowMinVoteOptions, AnswerErrorMessages, element.minOptionsPerVote, indices.length);
1818
+ }
1819
+ if (element.maxOptionsPerVote !== null &&
1820
+ indices.length > element.maxOptionsPerVote) {
1821
+ throw sdkUtil.createError(exports.AnswerError.AboveMaxVoteOptions, AnswerErrorMessages, element.maxOptionsPerVote, indices.length);
1822
+ }
1823
+ if (sumValues > element.maxVotesPerUser) {
1824
+ throw sdkUtil.createError(exports.AnswerError.AboveMaxVotesPerUser, AnswerErrorMessages, element.maxVotesPerUser, sumValues);
1825
+ }
1826
+ if (maxValue > element.maxVotesPerOption) {
1827
+ throw sdkUtil.createError(exports.AnswerError.AboveMaxVotesPerOption, AnswerErrorMessages, element.maxVotesPerOption, maxValue);
1828
+ }
1829
+ if (emptyIndices.length > 0) {
1830
+ throw sdkUtil.createError(exports.AnswerError.EmptyVote, AnswerErrorMessages, emptyIndices.join(', '));
1831
+ }
1832
+ }
1833
+ function answerParametersToAnswer(...value) {
1834
+ const answerValues = value.flat();
1835
+ if (isAnswer(answerValues[0])) {
1836
+ return answerValues[0];
1837
+ }
1838
+ if (isNumbers(answerValues)) {
1839
+ return Answer.fromIndices(answerValues);
1840
+ }
1841
+ if (isAnswerValues(answerValues)) {
1842
+ return new Answer(answerValues);
1843
+ }
1844
+ return new Answer();
1845
+ }
1846
+ function isAnswer(value) {
1847
+ return value instanceof Answer;
1848
+ }
1849
+ function isAnswerValues(value) {
1850
+ return value.every((v) => Object.prototype.hasOwnProperty.call(v, 'option') &&
1851
+ Object.prototype.hasOwnProperty.call(v, 'value'));
1852
+ }
1853
+ function isNumbers(value) {
1854
+ return value.every((v) => typeof v === 'number');
1855
+ }
1856
+ /**
1857
+ * Adds an observer for when
1858
+ * {@link InteractElement | Element results} are updated
1859
+ *
1860
+ * @param element - The Element to observe
1861
+ * @param callback - Called when results are updated
1862
+ */
1863
+ function onElementResults(element, callback) {
1864
+ return sdkUtil.subscribe(element, 'results', callback);
1865
+ }
1866
+ /**
1867
+ * Adds an observer for when
1868
+ * {@link InteractElement | Element state} changed
1869
+ *
1870
+ * @param element - The Element to observe
1871
+ * @param callback - Called when the state changes
1872
+ */
1873
+ function onElementStateChanged(element, callback) {
1874
+ return sdkUtil.subscribe(element, 'state', callback);
1875
+ }
1876
+ /**
1877
+ * Adds an observer for when
1878
+ * {@link InteractElement | Element fields} are updated
1879
+ *
1880
+ * @param event - The Event containing the Elements
1881
+ * @param callback - Called with the updated Element
1882
+ */
1883
+ function onElementUpdated(event, callback) {
1884
+ configureEvent(event);
1885
+ return sdkUtil.subscribe(event, 'update', callback);
1886
+ }
1887
+ /**
1888
+ * Adds an observer for when a new Element is published
1889
+ *
1890
+ * @param event - The Event to observe
1891
+ * @param callback - Called with the published Element
1892
+ */
1893
+ function onElementPublished(event, callback) {
1894
+ configureEvent(event);
1895
+ return sdkUtil.subscribe(event, 'publish', (element) => {
1896
+ if (event.initState === InitState.Initialised) {
1897
+ callback(element);
1898
+ }
1899
+ });
1900
+ }
1901
+ /**
1902
+ * Adds an observer for when an Element is revoked
1903
+ *
1904
+ * @param event - The Event to observe
1905
+ * @param callback - Called with the revoked Element
1906
+ */
1907
+ function onElementRevoked(event, callback) {
1908
+ configureEvent(event);
1909
+ return sdkUtil.subscribe(event, 'revoke', callback);
1910
+ }
1911
+
1912
+ /**
1913
+ * @license
1914
+ * @monterosa/sdk-interact-kit
1915
+ *
1916
+ * Copyright © 2025 Monterosa Productions Limited. All rights reserved.
1917
+ *
1918
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1919
+ */
1920
+ exports.PresenceCounterState = void 0;
1921
+ (function (PresenceCounterState) {
1922
+ PresenceCounterState["Opened"] = "opened";
1923
+ PresenceCounterState["Closed"] = "closed";
1924
+ })(exports.PresenceCounterState || (exports.PresenceCounterState = {}));
1925
+
1926
+ /**
1927
+ * @license
1928
+ * @monterosa/sdk-interact-kit
1929
+ *
1930
+ * Copyright © 2023 Monterosa Productions Limited. All rights reserved.
1931
+ *
1932
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1933
+ */
1934
+ const logger = new sdkCore.Logger('@monterosa/sdk-interact-kit');
1935
+
1936
+ /**
1937
+ * @license
1938
+ * @monterosa/sdk-interact-kit
1939
+ *
1940
+ * Copyright © 2025 Monterosa Productions Limited. All rights reserved.
1941
+ *
1942
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
1943
+ */
1944
+ class PresenceCounterImpl extends sdkUtil.Emitter {
1945
+ constructor(host, id) {
1946
+ super();
1947
+ this.host = host;
1948
+ this.id = id;
1949
+ this.state = exports.PresenceCounterState.Closed;
1950
+ this.lastCount = 0;
1951
+ this.connect = null;
1952
+ this.boundHandleMessage = this.handleMessage.bind(this);
1953
+ this.boundHandleTimeout = this.handleTimeout.bind(this);
1954
+ }
1955
+ async init() {
1956
+ this.connect = await getConnect(this.host);
1957
+ this.connect.on('message', this.boundHandleMessage);
1958
+ }
1959
+ get isOpened() {
1960
+ return this.state === exports.PresenceCounterState.Opened;
1961
+ }
1962
+ async handleMessage(message) {
1963
+ const { id, klass, data } = message;
1964
+ if (id !== this.id || klass !== exports.Klass.SubsCounter) {
1965
+ return;
1966
+ }
1967
+ try {
1968
+ const subsData = JSON.parse(data[0]);
1969
+ this.handleCounterUpdate(subsData.counter);
1970
+ this.resetTimeout(subsData.next_update_at);
1971
+ }
1972
+ catch (e) {
1973
+ logger.warn("Couldn't parse subscounter message.");
1974
+ }
1975
+ }
1976
+ handleCounterUpdate(count) {
1977
+ if (count < 0) {
1978
+ return;
1979
+ }
1980
+ let shouldEmitUpdated = false;
1981
+ let shouldEmitState = false;
1982
+ if (this.lastCount !== count) {
1983
+ shouldEmitUpdated = true;
1984
+ this.lastCount = count;
1985
+ }
1986
+ if (this.state === exports.PresenceCounterState.Closed) {
1987
+ shouldEmitState = true;
1988
+ this.state = exports.PresenceCounterState.Opened;
1989
+ }
1990
+ if (shouldEmitUpdated) {
1991
+ this.emit('updated', this.lastCount);
1992
+ }
1993
+ if (shouldEmitState) {
1994
+ this.emit('state', exports.PresenceCounterState.Opened);
1995
+ }
1996
+ }
1997
+ handleTimeout() {
1998
+ this.state = exports.PresenceCounterState.Closed;
1999
+ this.emit('state', exports.PresenceCounterState.Closed);
2000
+ }
2001
+ resetTimeout(nextUpdateAt) {
2002
+ clearTimeout(this.closeTimeout);
2003
+ this.closeTimeout = setTimeout(this.boundHandleTimeout,
2004
+ // because of the time difference between the server and the client,
2005
+ // we need to add 3 seconds to the timeout
2006
+ (nextUpdateAt - sdkUtil.now() + 3) * 1000);
2007
+ }
2008
+ destroy() {
2009
+ if (this.connect !== null) {
2010
+ this.connect.off('message', this.boundHandleMessage);
2011
+ }
2012
+ }
2013
+ }
2014
+
2015
+ /**
2016
+ * @license
2017
+ * @monterosa/sdk-interact-kit
2018
+ *
2019
+ * Copyright © 2025 Monterosa Productions Limited. All rights reserved.
2020
+ *
2021
+ * More details on the license can be found at https://www.monterosa.co/sdk/license
2022
+ */
2023
+ const presenceCounters = new Map();
2024
+ const getPresenceCounterMemoized = sdkUtil.memoizePromise(async (host, id) => {
2025
+ if (presenceCounters.has(id)) {
2026
+ return presenceCounters.get(id);
2027
+ }
2028
+ const presenceCounter = new PresenceCounterImpl(host, id);
2029
+ await presenceCounter.init();
2030
+ presenceCounters.set(id, presenceCounter);
2031
+ return presenceCounter;
2032
+ }, (_, id) => id);
2033
+ async function getPresenceCounter(project) {
2034
+ if (project === undefined) {
2035
+ project = await getProject();
2036
+ }
2037
+ return getPresenceCounterMemoized(project.context.sdk.options.host, project.id);
2038
+ }
2039
+ function onPresenceCounterUpdate(presenceCounter, callback) {
2040
+ presenceCounter.on('updated', callback);
2041
+ return () => presenceCounter.off('updated', callback);
2042
+ }
2043
+ function onPresenceCounterOpen(presenceCounter, callback) {
2044
+ const handler = (state) => {
2045
+ if (state === exports.PresenceCounterState.Opened) {
2046
+ callback();
2047
+ }
2048
+ };
2049
+ presenceCounter.on('state', handler);
2050
+ return () => presenceCounter.off('state', handler);
2051
+ }
2052
+ function onPresenceCounterClose(presenceCounter, callback) {
2053
+ const handler = (state) => {
2054
+ if (state === exports.PresenceCounterState.Closed) {
2055
+ callback();
2056
+ }
2057
+ };
2058
+ presenceCounter.on('state', handler);
2059
+ return () => presenceCounter.off('state', handler);
2060
+ }
2061
+
2062
+ exports.Answer = Answer;
2063
+ exports.ElementImpl = ElementImpl;
2064
+ exports.EventImpl = EventImpl;
2065
+ exports.ProjectImpl = ProjectImpl;
2066
+ exports.answer = answer;
2067
+ exports.getConnect = getConnect;
2068
+ exports.getConnectionHealth = getConnectionHealth;
2069
+ exports.getElement = getElement;
2070
+ exports.getElementMemoized = getElementMemoized;
2071
+ exports.getElements = getElements;
2072
+ exports.getElementsMemoized = getElementsMemoized;
2073
+ exports.getEvent = getEvent;
2074
+ exports.getEventMemoized = getEventMemoized;
2075
+ exports.getEvents = getEvents;
2076
+ exports.getEventsMemoized = getEventsMemoized;
2077
+ exports.getPresenceCounter = getPresenceCounter;
2078
+ exports.getProject = getProject;
2079
+ exports.getProjectMemoized = getProjectMemoized;
2080
+ exports.onConnectionHealthState = onConnectionHealthState;
2081
+ exports.onElementPublished = onElementPublished;
2082
+ exports.onElementResults = onElementResults;
2083
+ exports.onElementRevoked = onElementRevoked;
2084
+ exports.onElementStateChanged = onElementStateChanged;
2085
+ exports.onElementUpdated = onElementUpdated;
2086
+ exports.onEventAdded = onEventAdded;
2087
+ exports.onEventRemoved = onEventRemoved;
2088
+ exports.onEventState = onEventState;
2089
+ exports.onEventUpdated = onEventUpdated;
2090
+ exports.onPresenceCounterClose = onPresenceCounterClose;
2091
+ exports.onPresenceCounterOpen = onPresenceCounterOpen;
2092
+ exports.onPresenceCounterUpdate = onPresenceCounterUpdate;
2093
+ exports.onProjectFieldsUpdated = onProjectFieldsUpdated;
2094
+ exports.validateAnswer = validateAnswer;
2095
+ //# sourceMappingURL=index.cjs.map