@streamlayer/feature-gamification 1.26.2 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { AdCampaigns } from '@streamlayer/sdk-web-types';
1
+ import { GamOptions } from '@streamlayer/sdk-web-types';
2
2
  export type PalNonceManager = {
3
3
  getNonce: () => string;
4
4
  sendAdClick: () => void;
@@ -9,8 +9,8 @@ export type GamNonceResult = {
9
9
  url: URL;
10
10
  manager?: PalNonceManager;
11
11
  };
12
- export declare const processGamAdvertisement: ({ gamOptions, gamBaseUrl }: AdCampaigns) => string;
13
- export declare const gam: (adContainer: HTMLDivElement, url: string) => () => Promise<{
12
+ export declare const processGamAdvertisement: ({ gamBaseUrl, $typeName: _, ...gamOptions }: GamOptions) => string;
13
+ export declare const gam: (url: string, adContainer?: HTMLDivElement) => () => Promise<{
14
14
  url: URL;
15
15
  manager: PalNonceManager;
16
16
  } | undefined>;
@@ -7,7 +7,9 @@ const encodeUrl = (urlStr) => {
7
7
  const url = new URL(urlStr);
8
8
  return url.toString();
9
9
  };
10
- export const processGamAdvertisement = ({ gamOptions, gamBaseUrl }) => {
10
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
11
+ // @ts-ignore
12
+ export const processGamAdvertisement = ({ gamBaseUrl, $typeName: _, ...gamOptions }) => {
11
13
  const url = `${gamBaseUrl}?`;
12
14
  const params = [];
13
15
  for (const rawKey in gamOptions) {
@@ -41,9 +43,10 @@ export const processGamAdvertisement = ({ gamOptions, gamBaseUrl }) => {
41
43
  }
42
44
  }
43
45
  }
46
+ console.log('url', new URL(url + params.join('&')));
44
47
  return url + params.join('&');
45
48
  };
46
- export const gam = (adContainer, url) => {
49
+ export const gam = (url, adContainer) => {
47
50
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
48
51
  // @ts-expect-error
49
52
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -78,8 +81,8 @@ export const gam = (adContainer, url) => {
78
81
  request.sessionId = deviceId;
79
82
  // Player support for VPAID 2.0, OMID 1.0, and SIMID 1.1
80
83
  request.supportedApiFrameworks = '2,7,9';
81
- request.videoHeight = adContainer.clientHeight;
82
- request.videoWidth = adContainer.clientWidth;
84
+ request.videoHeight = adContainer?.clientHeight ?? 0;
85
+ request.videoWidth = adContainer?.clientWidth ?? 0;
83
86
  const nonceManager = await nonceLoader.loadNonceManager(request);
84
87
  if (parsedUrl.searchParams.get('is_lat') === '[placeholder]') {
85
88
  parsedUrl.searchParams.set('is_lat', '1');
@@ -92,9 +95,9 @@ export const gam = (adContainer, url) => {
92
95
  }
93
96
  parsedUrl.searchParams.set('wta', '1');
94
97
  parsedUrl.searchParams.set('givn', nonceManager.getNonce());
98
+ console.log('res url', new URL(parsedUrl.toString()));
95
99
  return {
96
100
  url: parsedUrl,
97
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
98
101
  manager: nonceManager,
99
102
  };
100
103
  }
@@ -16,7 +16,12 @@ export type Advertisement = {
16
16
  isPaused?: boolean;
17
17
  isMuted?: boolean;
18
18
  hasNotification?: boolean;
19
- close?: () => void;
19
+ closeDelay?: number;
20
+ closePromise?: Promise<void>;
21
+ setCloseDelay?: (delay: number) => void;
22
+ close?: (options?: {
23
+ delay?: number;
24
+ }) => Promise<void>;
20
25
  togglePause?: (flag: boolean) => void;
21
26
  error?: string;
22
27
  toggleMute?: (flag: boolean) => void;
@@ -38,8 +43,10 @@ export type Advertisement = {
38
43
  */
39
44
  export declare const advertisement: ($slStreamId: GamificationBackground["slStreamId"], $feedSubscription: GamificationBackground["feedSubscription"], instance: StreamLayerContext) => {
40
45
  connect: () => void;
41
- hide: (notificationId?: string) => void;
42
- show: (advertisementId: string, data?: Awaited<ReturnType<typeof getPromotionDetail>>) => void;
46
+ hide: (notificationId?: string, options?: {
47
+ delay?: number;
48
+ }) => Promise<void>;
49
+ show: (advertisementId: string, data?: Awaited<ReturnType<typeof getPromotionDetail>>) => Promise<void>;
43
50
  open: (options?: {
44
51
  fromNotification?: boolean;
45
52
  }) => void;
@@ -1,11 +1,19 @@
1
1
  import { createMapStore, eventBus } from '@streamlayer/sdk-web-interfaces';
2
2
  import { QuestionStatus } from '@streamlayer/sdk-web-types';
3
3
  import { createLogger } from '@streamlayer/sdk-web-logger';
4
- import { NotificationEnabled, PromotionType } from '@streamlayer/sl-eslib/interactive/interactive.common_pb';
4
+ import { NotificationEnabled, PromotionType, UseGeneralLogo, } from '@streamlayer/sl-eslib/interactive/interactive.common_pb';
5
5
  import { $activePromotionId, getPromotionDetail } from '../queries';
6
6
  import { AdvertisementStorage } from './storage';
7
7
  import { parsePromotion } from './utils';
8
8
  import { processGamAdvertisement } from './externalAd';
9
+ const resolveAdvertisementLogos = (data) => {
10
+ if (data?.promotion?.sponsor?.useGeneralLogo === UseGeneralLogo.ENABLED) {
11
+ const generalTab = data.question?.promotion?.generalTabMetadata;
12
+ data.promotion.sponsor.classicLogo = generalTab?.advertiserLogo ?? data.promotion.sponsor.classicLogo;
13
+ data.promotion.sponsor.centerLogo = generalTab?.advertiserTransparentLogo ?? data.promotion.sponsor.centerLogo;
14
+ }
15
+ return data;
16
+ };
9
17
  const adHasBanner = (data) => (data?.promotion?.type === PromotionType.INGAME_IAB11_LBAR ||
10
18
  data?.promotion?.type === PromotionType.INGAME_IAB21_LBAR) &&
11
19
  !!data.promotion.additionalBanner?.imageUrl;
@@ -23,6 +31,7 @@ const adHasBanner = (data) => (data?.promotion?.type === PromotionType.INGAME_IA
23
31
  */
24
32
  export const advertisement = ($slStreamId, $feedSubscription, instance) => {
25
33
  let connected = false;
34
+ // let hideTimeout: ReturnType<typeof setTimeout> | null = null
26
35
  const transport = instance.transport;
27
36
  const logger = createLogger('advertisement');
28
37
  const storage = new AdvertisementStorage();
@@ -90,19 +99,37 @@ export const advertisement = ($slStreamId, $feedSubscription, instance) => {
90
99
  /**
91
100
  * Show advertisement by id
92
101
  */
93
- const show = (advertisementId, data) => {
102
+ const show = async (advertisementId, data) => {
103
+ // if (hideTimeout) {
104
+ // clearTimeout(hideTimeout)
105
+ // hideTimeout = null
106
+ // }
107
+ // close previous ad properly so "closed" event fires via subscribe
108
+ const current = $store.get();
109
+ if (current.data && current.data.question.id !== advertisementId) {
110
+ if (!current.closePromise) {
111
+ await $store.get().close?.();
112
+ }
113
+ else {
114
+ await current.closePromise;
115
+ }
116
+ }
94
117
  if (!data) {
95
118
  $store.setKey('loading', true);
96
119
  void getPromotionDetail(advertisementId, transport)
97
120
  .then((response) => $store.set({
98
121
  loading: false,
99
122
  error: undefined,
100
- data: response,
123
+ data: resolveAdvertisementLogos(response),
101
124
  hasNotification: response?.notification?.enabled === NotificationEnabled.NOTIFICATION_ENABLED,
102
- close: () => hide(response?.question.id),
125
+ close: (options) => {
126
+ console.log('close ad 1', response?.question.id);
127
+ return hide(response?.question.id, options);
128
+ },
103
129
  togglePause: (status) => pause(response?.question.id, status),
104
130
  toggleMute: (status) => mute(response?.question.id, status),
105
131
  isViewed: response && !!storage.isViewed(response.question.id),
132
+ setCloseDelay: (delay) => $store.setKey('closeDelay', delay),
106
133
  fireEvent: (event) => fireEvent(advertisementId, event),
107
134
  isEventFired: (event) => isEventFired(advertisementId, event),
108
135
  }))
@@ -115,15 +142,19 @@ export const advertisement = ($slStreamId, $feedSubscription, instance) => {
115
142
  }));
116
143
  }
117
144
  else {
145
+ const resolvedData = resolveAdvertisementLogos(data);
118
146
  $store.set({
119
147
  loading: false,
120
148
  error: undefined,
121
- data,
149
+ data: resolvedData,
122
150
  hasNotification: data?.notification?.enabled === NotificationEnabled.NOTIFICATION_ENABLED,
123
- close: () => hide(data.question.id),
151
+ close: (options) => {
152
+ return hide(data.question.id, options);
153
+ },
124
154
  togglePause: (status) => pause(data.question.id, status),
125
155
  toggleMute: (status) => mute(data.question.id, status),
126
156
  isViewed: !!storage.isViewed(data.question.id),
157
+ setCloseDelay: (delay) => $store.setKey('closeDelay', delay),
127
158
  fireEvent: (event) => fireEvent(advertisementId, event),
128
159
  isEventFired: (event) => isEventFired(advertisementId, event),
129
160
  });
@@ -179,10 +210,36 @@ export const advertisement = ($slStreamId, $feedSubscription, instance) => {
179
210
  });
180
211
  }
181
212
  });
182
- const hide = (notificationId) => {
183
- if (!notificationId || $store.get()?.data?.question.id === notificationId) {
184
- $store.set({});
213
+ const hide = (notificationId, options) => {
214
+ const promise = new Promise((resolve) => {
215
+ try {
216
+ if (!notificationId || $store.get()?.data?.question.id === notificationId) {
217
+ const current = $store.get();
218
+ if (current.hiding || !current.data) {
219
+ return resolve();
220
+ }
221
+ const delay = options?.delay ?? current.closeDelay;
222
+ if (delay) {
223
+ $store.setKey('hiding', true);
224
+ setTimeout(() => {
225
+ $store.set({});
226
+ resolve();
227
+ }, delay);
228
+ }
229
+ else {
230
+ resolve();
231
+ $store.set({});
232
+ }
233
+ }
234
+ }
235
+ catch (err) {
236
+ console.error(err);
237
+ }
238
+ });
239
+ if ($store.get()?.data?.question.id === notificationId) {
240
+ $store.setKey('closePromise', promise);
185
241
  }
242
+ return promise;
186
243
  };
187
244
  const pause = (notificationId, status) => {
188
245
  if ($store.get()?.data?.question.id === notificationId) {
@@ -202,7 +259,7 @@ export const advertisement = ($slStreamId, $feedSubscription, instance) => {
202
259
  $activeAdvertisement.subscribe((active, prevActive) => {
203
260
  if (active.data) {
204
261
  if (!prevActive?.data || active.data.id !== prevActive.data.id) {
205
- show(active.data.question.id, active.data);
262
+ void show(active.data.question.id, active.data);
206
263
  }
207
264
  }
208
265
  });
@@ -212,13 +269,13 @@ export const advertisement = ($slStreamId, $feedSubscription, instance) => {
212
269
  return;
213
270
  }
214
271
  if (promotion.question.status === QuestionStatus.RESOLVED) {
215
- hide(promotion.question.id);
272
+ void hide(promotion.question.id);
216
273
  logger.debug({ promotion }, 'resolved: %o');
217
274
  return;
218
275
  }
219
276
  if (promotion.question.status === QuestionStatus.ACTIVE) {
220
277
  logger.debug({ promotion }, 'active: %o');
221
- show(promotion.question.id, promotion);
278
+ void show(promotion.question.id, promotion);
222
279
  return;
223
280
  }
224
281
  logger.debug({ promotion }, 'skip: %o');
@@ -6,7 +6,6 @@ export const parsePromotion = (response) => {
6
6
  }
7
7
  const { options, notification, ...question } = questionItem;
8
8
  const promotionItem = options?.options.case === 'promotion' ? options.options.value : undefined;
9
- // eslint-disable-next-line consistent-return
10
9
  return {
11
10
  id: question.id,
12
11
  question,
package/lib/deepLink.js CHANGED
@@ -41,7 +41,7 @@ export const deepLink = (transport, $eventId, $externalEventId, $userId) => {
41
41
  loading: false,
42
42
  });
43
43
  }
44
- catch (error) {
44
+ catch (_error) {
45
45
  $store.set({
46
46
  loading: false,
47
47
  error: 'Failed to generate short link',
@@ -4,7 +4,7 @@ import { QuestionStatus, QuestionType, FeatureType, SilenceSetting, PickHistoryS
4
4
  import { NotificationType } from '@streamlayer/sdk-web-notifications';
5
5
  import '@streamlayer/sdk-web-core/store';
6
6
  import { onStart } from 'nanostores';
7
- import { AdUnit, FactoidMediaMode, NotificationEnabled, NotificationUseContentTexts, } from '@streamlayer/sl-eslib/interactive/interactive.common_pb';
7
+ import { AdUnit, FactoidMediaMode, NotificationEnabled, NotificationUseContentTexts, UseGeneralLogo, } from '@streamlayer/sl-eslib/interactive/interactive.common_pb';
8
8
  import * as queries from './queries';
9
9
  import * as actions from './queries/actions';
10
10
  import { leaderboard } from './leaderboard';
@@ -16,6 +16,12 @@ import { $questionByUser, questionByUser } from './queries';
16
16
  import { summary } from './userSummary';
17
17
  import { friendSummary } from './friendSummary';
18
18
  const InteractiveQuestionTypes = new Set([QuestionType.POLL, QuestionType.PREDICTION, QuestionType.TRIVIA]);
19
+ const resolveSponsorLogo = (promotion) => {
20
+ if (promotion?.sponsor?.useGeneralLogo === UseGeneralLogo.ENABLED) {
21
+ return promotion.generalTabMetadata?.advertiserTransparentLogo || promotion.sponsor?.logo;
22
+ }
23
+ return promotion?.sponsor?.logo;
24
+ };
19
25
  /**
20
26
  * Gamification (Games) Overlay
21
27
  * Includes:
@@ -218,7 +224,7 @@ export class Gamification extends AbstractFeature {
218
224
  }
219
225
  // eslint-disable-next-line no-empty
220
226
  }
221
- catch (e) { }
227
+ catch (_e) { }
222
228
  this.feedList.getStore().mutate(feedList);
223
229
  }
224
230
  }
@@ -254,7 +260,7 @@ export class Gamification extends AbstractFeature {
254
260
  appearance: question.appearance,
255
261
  sponsorship: question.sponsorship,
256
262
  adUnit: question?.adUnit,
257
- sponsorLogo: question.promotion?.sponsor?.logo,
263
+ sponsorLogo: resolveSponsorLogo(question.promotion),
258
264
  },
259
265
  },
260
266
  });
@@ -301,21 +307,16 @@ export class Gamification extends AbstractFeature {
301
307
  // @ts-expect-error
302
308
  attributes: {
303
309
  ...prev.attributes.attributes,
304
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
305
310
  // @ts-ignore
306
311
  value: {
307
312
  ...prev.attributes.attributes.value,
308
313
  ...feedItem.attributes.attributes.value,
309
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
310
314
  // @ts-ignore
311
315
  answerId: prev.attributes.attributes.value.answerId,
312
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
313
316
  // @ts-ignore
314
317
  status: prev.attributes.attributes.value.status,
315
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
316
318
  // @ts-ignore
317
319
  openForVoting:
318
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
319
320
  // @ts-ignore
320
321
  prev.attributes.attributes.value.openForVoting === false
321
322
  ? false
@@ -335,7 +336,6 @@ export class Gamification extends AbstractFeature {
335
336
  ...prev.attributes,
336
337
  attributes: {
337
338
  ...prev.attributes.attributes,
338
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
339
339
  // @ts-ignore
340
340
  value: {
341
341
  ...prev.attributes.attributes.value,
@@ -640,7 +640,7 @@ export class Gamification extends AbstractFeature {
640
640
  type: NotificationType.QUESTION,
641
641
  action: () => this.openQuestion(question.data?.question?.id, question.data?.feedItem),
642
642
  close: () => this.closeQuestion(question.data?.question?.id),
643
- autoHideDuration: 1000 * 60,
643
+ autoHideDuration: isAdUnit ? 0 : 1000 * 60,
644
644
  id: this.background.getCurrentSessionId({
645
645
  prefix: 'notification',
646
646
  entity: question.data.question.id,
@@ -657,7 +657,7 @@ export class Gamification extends AbstractFeature {
657
657
  appearance: question.data.question.appearance,
658
658
  sponsorship: question.data.question.sponsorship,
659
659
  adUnit: question.data.question?.adUnit,
660
- sponsorLogo: question.data.question.promotion?.sponsor?.logo,
660
+ sponsorLogo: resolveSponsorLogo(question.data.question.promotion),
661
661
  },
662
662
  },
663
663
  });
@@ -690,28 +690,33 @@ export class Gamification extends AbstractFeature {
690
690
  url: '',
691
691
  },
692
692
  };
693
- this.notifications.add({
694
- type: NotificationType.QUESTION,
695
- action: () => this.openQuestion(question?.data?.question?.id, question?.data?.feedItem),
696
- close: () => this.closeQuestion(question?.data?.question?.id),
697
- autoHideDuration: 1000 * 120,
698
- emitEvent: true,
699
- id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
700
- data: {
701
- questionId: question.data.question.id,
702
- questionType: question.data.question.type,
703
- insight: instantView,
704
- inApp: {
705
- useMediaFromContent: optionsValue?.useAsNotification === UseAsNotification.ENABLED,
706
- notification: question.data.question.notification,
707
- appearance: question.data.question.appearance,
708
- sponsorship: question.data.question.sponsorship,
709
- adUnit: question.data.question?.adUnit,
710
- sponsorLogo: question.data.question.promotion?.sponsor?.logo,
711
- isVideo: optionsValue?.mode === FactoidMediaMode.VIDEO,
693
+ if (question.data.question.notification?.enabled === NotificationEnabled.NOTIFICATION_ENABLED) {
694
+ this.notifications.add({
695
+ type: NotificationType.QUESTION,
696
+ action: () => this.openQuestion(question?.data?.question?.id, question?.data?.feedItem),
697
+ close: () => this.closeQuestion(question?.data?.question?.id),
698
+ autoHideDuration: isAdUnit ? 0 : 1000 * 120,
699
+ emitEvent: true,
700
+ id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
701
+ data: {
702
+ questionId: question.data.question.id,
703
+ questionType: question.data.question.type,
704
+ insight: instantView,
705
+ inApp: {
706
+ useMediaFromContent: optionsValue?.useAsNotification === UseAsNotification.ENABLED,
707
+ notification: question.data.question.notification,
708
+ appearance: question.data.question.appearance,
709
+ sponsorship: question.data.question.sponsorship,
710
+ adUnit: question.data.question?.adUnit,
711
+ sponsorLogo: resolveSponsorLogo(question.data.question.promotion),
712
+ isVideo: optionsValue?.mode === FactoidMediaMode.VIDEO,
713
+ },
712
714
  },
713
- },
714
- });
715
+ });
716
+ }
717
+ else {
718
+ this.openQuestion(question.data?.question?.id, question.data?.feedItem);
719
+ }
715
720
  }
716
721
  else if (question.data.question.type === QuestionType.TWEET &&
717
722
  question.data.question.notification !== undefined) {
@@ -740,24 +745,29 @@ export class Gamification extends AbstractFeature {
740
745
  tweetId: question.data.question.id,
741
746
  tweetMedia,
742
747
  };
743
- this.notifications.add({
744
- type: NotificationType.QUESTION,
745
- action: () => this.openQuestion(question.data?.question?.id, question.data?.feedItem),
746
- close: () => this.closeQuestion(question.data?.question?.id),
747
- autoHideDuration: 1000 * 120,
748
- emitEvent: true,
749
- id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
750
- data: {
751
- questionId: question.data.question.id,
752
- questionType: question.data.question.type,
753
- tweet: tweetView,
754
- inApp: {
755
- notification: question.data.question.notification,
756
- appearance: question.data.question.appearance,
757
- sponsorship: question.data.question.sponsorship,
748
+ if (question.data.question.notification?.enabled === NotificationEnabled.NOTIFICATION_ENABLED) {
749
+ this.notifications.add({
750
+ type: NotificationType.QUESTION,
751
+ action: () => this.openQuestion(question.data?.question?.id, question.data?.feedItem),
752
+ close: () => this.closeQuestion(question.data?.question?.id),
753
+ autoHideDuration: isAdUnit ? 0 : 1000 * 120,
754
+ emitEvent: true,
755
+ id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
756
+ data: {
757
+ questionId: question.data.question.id,
758
+ questionType: question.data.question.type,
759
+ tweet: tweetView,
760
+ inApp: {
761
+ notification: question.data.question.notification,
762
+ appearance: question.data.question.appearance,
763
+ sponsorship: question.data.question.sponsorship,
764
+ },
758
765
  },
759
- },
760
- });
766
+ });
767
+ }
768
+ else {
769
+ this.openQuestion(question.data?.question?.id, question.data?.feedItem);
770
+ }
761
771
  }
762
772
  }
763
773
  }
@@ -12,9 +12,9 @@ export const submitAnswer = async (transport, data) => {
12
12
  catch (error) {
13
13
  const cErr = ConnectError.from(error);
14
14
  if (cErr?.code === Code.AlreadyExists) {
15
- throw new Error(ERROR.ALREADY_VOTED);
15
+ throw new Error(ERROR.ALREADY_VOTED, { cause: error });
16
16
  }
17
- throw new Error(ERROR.UNKNOWN);
17
+ throw new Error(ERROR.UNKNOWN, { cause: error });
18
18
  }
19
19
  };
20
20
  export const submitInplay = (transport, eventId) => {
@@ -16,7 +16,7 @@ export const $deepLink = (transport, params) => {
16
16
  });
17
17
  return res.data?.attributes;
18
18
  }
19
- catch (error) {
19
+ catch (_error) {
20
20
  return undefined;
21
21
  }
22
22
  },
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@streamlayer/feature-gamification",
3
- "version": "1.26.2",
3
+ "version": "1.27.0",
4
4
  "peerDependencies": {
5
5
  "@bufbuild/protobuf": "^2.2.2",
6
6
  "@fastify/deepmerge": "^2.0.0",
7
7
  "@streamlayer/sl-eslib": "^5.229.1",
8
8
  "uuid": "^11.1.0",
9
9
  "nanostores": "^1.1.0",
10
- "@streamlayer/sdk-web-api": "^1.16.8",
11
- "@streamlayer/sdk-web-core": "^1.21.8",
12
- "@streamlayer/sdk-web-interfaces": "^1.9.16",
13
- "@streamlayer/sdk-web-logger": "^1.0.110",
14
- "@streamlayer/sdk-web-notifications": "^1.3.72",
15
- "@streamlayer/sdk-web-storage": "^1.0.110",
16
- "@streamlayer/sdk-web-types": "^1.17.8"
10
+ "@streamlayer/sdk-web-api": "^1.17.0",
11
+ "@streamlayer/sdk-web-core": "^1.22.0",
12
+ "@streamlayer/sdk-web-interfaces": "^1.10.0",
13
+ "@streamlayer/sdk-web-notifications": "^1.4.0",
14
+ "@streamlayer/sdk-web-logger": "^1.1.0",
15
+ "@streamlayer/sdk-web-storage": "^1.1.0",
16
+ "@streamlayer/sdk-web-types": "^1.18.0"
17
17
  },
18
18
  "devDependencies": {
19
19
  "tslib": "^2.7.0"