@kalamba/sdk 0.42.0 → 0.48.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.
package/dist/wrapper.js CHANGED
@@ -2,757 +2,808 @@
2
2
  * BSD 3-Clause License
3
3
  * Copyright (c) 2026, Kalamba Games Limited
4
4
  */
5
- import { n as RgsErrorAction } from "./errors-CakYyMwc.js";
5
+ import { n as RgsErrorAction } from "./errors-DpQKmNNj.js";
6
6
  import { flow, merge, sortBy, sortedUniq } from "lodash-es";
7
7
  import { v4 } from "uuid";
8
+ import { createStore } from "zustand/vanilla";
8
9
 
9
- //#region src/common/PayloadInjectionManager.ts
10
- /**
11
- * PayloadInjectionManager manages payload injection from multiple sources.
12
- * Each source can register either static payload data or async functions that will be merged together when creating the final payload for injection.
13
- */
14
- var PayloadInjectionManager = class {
15
- #injectors = /* @__PURE__ */ new Map();
16
- /**
17
- * Register or update payload injector from a specific source
18
- * @param source - Unique identifier for the payload source (e.g., 'bspot', 'freeRounds')
19
- * @param payload - Payload data or async function that receives playPayload and returns payload data
20
- */
21
- register(source, payloadProvider) {
22
- this.#injectors.set(source, payloadProvider);
23
- }
24
- /**
25
- * Remove payload injector from a specific source
26
- * @param source - Source identifier to remove
27
- */
28
- unregister(source) {
29
- this.#injectors.delete(source);
30
- }
31
- /**
32
- * Get merged payload from all registered injectors
33
- * @param playPayload - The play payload that will be passed to registered functions
34
- * @returns Promise resolving to merged payload object or undefined if no injectors registered
35
- */
36
- async getPayload(playPayload) {
37
- if (this.#injectors.size === 0) return;
38
- const merged = {};
39
- for (const payloadProvider of this.#injectors.values()) merge(merged, typeof payloadProvider === "function" ? await payloadProvider(playPayload) : payloadProvider);
40
- return merged;
10
+ //#region src/common/config/SdkConfigManager.ts
11
+ var SdkConfigManager = class {
12
+ config;
13
+ constructor() {
14
+ this.config = {
15
+ api: {
16
+ brand: "",
17
+ cashierUrl: void 0,
18
+ coinValueInCents: 1,
19
+ country: "",
20
+ currency: "",
21
+ game: "",
22
+ gameHistoryUrl: "",
23
+ homeUrl: void 0,
24
+ integration: "",
25
+ jurisdiction: null,
26
+ playMode: "FUN",
27
+ user: "",
28
+ username: "",
29
+ sessionId: v4(),
30
+ backendSessionId: void 0
31
+ },
32
+ ui: {
33
+ autoplay: {
34
+ enabled: false,
35
+ rounds: {
36
+ enabled: false,
37
+ showNoLimit: false,
38
+ showCustomLimit: false,
39
+ options: []
40
+ },
41
+ lossLimit: {
42
+ enabled: false,
43
+ showNoLimit: false,
44
+ showCustomLimit: false,
45
+ options: []
46
+ },
47
+ winLimit: {
48
+ enabled: false,
49
+ showNoLimit: false,
50
+ showCustomLimit: false,
51
+ options: []
52
+ },
53
+ simple: false,
54
+ stopOnFeature: false
55
+ },
56
+ cashier: {
57
+ enabled: false,
58
+ balanceThreshold: [0]
59
+ },
60
+ feature: {
61
+ allowTelemetry: false,
62
+ allowFullscreen: false,
63
+ allowGamble: false,
64
+ allowQuickStop: false,
65
+ showCashier: false,
66
+ showClock: false,
67
+ showCloseGame: false,
68
+ showCurrency: true,
69
+ showDeconstructedBet: false,
70
+ showFastPlay: false,
71
+ showFeatureBuy: false,
72
+ showFeatureIntro: false,
73
+ showFeatureOutro: false,
74
+ showFunModeBanner: false,
75
+ showHistory: false,
76
+ showLowWinCelebration: false,
77
+ showMaximumWin: false,
78
+ showNetPosition: false,
79
+ showPaylineLines: false,
80
+ showPaytable: false,
81
+ showPromoPanel: true,
82
+ showRoundId: false,
83
+ showRtp: false,
84
+ showRules: false,
85
+ showSessionDuration: false,
86
+ showUi: true
87
+ },
88
+ language: "en",
89
+ minimumSpinDuration: 0,
90
+ requestTimeoutMs: 1e4,
91
+ incompleteGameResolution: { type: "NONE" },
92
+ realityCheck: {
93
+ sessionDurationPeriodFormat: "minutes",
94
+ showCloseGame: false,
95
+ showHistory: false,
96
+ showNetPosition: false,
97
+ showSessionDuration: false,
98
+ showSumBets: false,
99
+ showSumWins: false
100
+ },
101
+ skipInsufficientFundsCheck: false,
102
+ skipSplash: false,
103
+ cheatPanel: false
104
+ }
105
+ };
41
106
  }
42
107
  };
43
108
 
44
109
  //#endregion
45
- //#region src/wrapper/KalambaSdkWrapper.ts
46
- const logIn = () => {};
47
- const logOut = () => {};
48
- var KalambaSdkWrapper = class {
49
- #messagePort;
50
- #plugins;
51
- #config = {
52
- gameName: "UNKNOWN",
53
- gameVersion: "unknown",
54
- showErrors: true,
55
- showFreeRounds: true,
56
- showPromoPanel: false,
57
- showRealityCheck: true,
58
- showBars: true,
59
- skipErrors: []
60
- };
61
- #state = {
62
- isSdkConfigured: false,
63
- balance: 0,
64
- bet: {
65
- base: 0,
66
- multiplier: 0
67
- },
68
- updateBalance: true,
69
- openGameResponse: void 0,
70
- lastPlayResponse: void 0,
71
- freeRoundId: void 0
72
- };
73
- #sdkConfigManager;
74
- #payloadInjectionManager;
75
- #wakeLock;
76
- constructor({ messagePort, plugins, sdkConfigManager }) {
77
- this.#messagePort = messagePort;
78
- this.#sdkConfigManager = new sdkConfigManager();
79
- this.#payloadInjectionManager = new PayloadInjectionManager();
80
- this.injectPayload = this.injectPayload.bind(this);
81
- this.#plugins = {
82
- rgs: new plugins.rgs(this.config, this.sdkConfig, this.state),
83
- casino: plugins.casino.map((casinoPlugin) => new casinoPlugin(this.config, this.sdkConfig, this.state, this.injectPayload)),
84
- telemetry: plugins.telemetry.map(({ plugin, trackers }) => new plugin(trackers.map((tracker) => new tracker(this.config, this.sdkConfig)), this.config, this.sdkConfig))
85
- };
86
- this.on("balance", ({ balance }) => {
87
- this.setState((state) => ({
88
- ...state,
89
- balance
90
- }));
91
- });
92
- this.on("bet", (bet) => {
93
- this.setState((state) => ({
94
- ...state,
95
- bet
96
- }));
97
- });
98
- this.on("sdk:playCycleStart", async () => {
99
- await this.requestWakeLock();
100
- if (this.state.freeRoundId) return;
101
- this.setState((state) => ({
102
- ...state,
103
- balance: state.balance - state.bet.base * state.bet.multiplier
104
- }));
105
- });
106
- this.on("sdk:play", async (payload) => {
107
- const payloadToInject = await this.#payloadInjectionManager.getPayload(payload.contract);
108
- this.send("play", {
109
- ...payload,
110
- payloadToInject
111
- });
112
- });
113
- this.on("sdk:configure", (payload) => {
114
- Object.assign(this.#config, payload);
115
- });
116
- this.on("freeRoundsPopup", () => {
117
- const freeRound = (this.state.lastPlayResponse ?? this.state.openGameResponse)?.freeRounds[0];
118
- if (freeRound && this.state.freeRoundId) this.send("freeRoundsInfo", freeRound);
119
- });
120
- this.on("rgs:openGameResponse", async (response) => {
121
- this.on("playReady", () => {
122
- this.handleFreeRounds(response.contract.freeRounds[0]);
123
- });
124
- this.sdkConfig.ui = await this.#sdkConfigManager.getConfig();
125
- this.setState((state) => ({
126
- ...state,
127
- isSdkConfigured: true
128
- }));
129
- this.send("configured", this.sdkConfig);
130
- this.send("legalBets", response.contract.bet.available);
131
- this.send("bet", response.contract.bet.lastPaid ?? response.contract.bet.default);
132
- this.setState((state) => ({
133
- ...state,
134
- openGameResponse: response.contract
135
- }));
136
- if (this.state.updateBalance) this.setState((state) => ({
137
- ...state,
138
- balance: response.contract.balance.coins
139
- }));
140
- });
141
- this.on("rgs:openGameError", async (error) => {
142
- if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
143
- messageKey: `Error.TIMEOUT`,
144
- messageCode: "TIMEOUT",
145
- type: "RELOAD"
146
- });
147
- if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
148
- messageKey: `RgsError.${error.data.code}`,
149
- messageCode: error.data.code,
150
- type: RgsErrorAction[error.data.code]
151
- });
152
- });
153
- this.on("rgs:playResponse", async (response) => {
154
- this.setState((state) => ({
155
- ...state,
156
- lastPlayResponse: response.contract
157
- }));
158
- });
159
- this.on("playCycleEnd", () => {
160
- this.releaseWakeLock();
161
- if (this.state.updateBalance) this.setState((state) => ({
162
- ...state,
163
- balance: this.state.lastPlayResponse.balance.coins
164
- }));
165
- this.handleFreeRounds(this.state.lastPlayResponse.freeRounds[0]);
166
- });
167
- this.on("rgs:playError", async (error) => {
168
- if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
169
- messageKey: `Error.TIMEOUT`,
170
- messageCode: "TIMEOUT",
171
- type: "RELOAD"
172
- });
173
- if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
174
- messageKey: `RgsError.${error.data.code}`,
175
- messageCode: error.data.code,
176
- type: RgsErrorAction[error.data.code]
177
- });
178
- });
179
- this.on("rgs:freeRoundsResponse", async (response) => {
180
- if (response.action === "ACCEPT") {
181
- const freeRound = (this.state.lastPlayResponse ?? this.state.openGameResponse)?.freeRounds[0];
182
- this.activateFreeRounds(freeRound);
183
- }
184
- });
185
- this.on("rgs:freeRoundsError", async (error) => {
186
- if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
187
- messageKey: `Error.TIMEOUT`,
188
- messageCode: "TIMEOUT",
189
- type: "RELOAD"
190
- });
191
- if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
192
- messageKey: `RgsError.${error.data.code}`,
193
- messageCode: error.data.code,
194
- type: RgsErrorAction[error.data.code]
195
- });
196
- });
197
- this.forwardMessages();
198
- document.addEventListener("visibilitychange", async () => {
199
- if (this.#wakeLock && document.visibilityState === "visible") await this.requestWakeLock();
200
- });
110
+ //#region src/common/config/KalambaSdkConfigManager.ts
111
+ function jurisdictionConfigOverrides(config, jurisdiction) {
112
+ function limitRounds(autoPlayRounds, { maxCount }) {
113
+ const limitCount = (rounds) => rounds?.filter((count) => count <= maxCount) ?? [];
114
+ const castToNumber = (rounds) => rounds?.map((count) => Number(count)) ?? [];
115
+ const rounds = [...autoPlayRounds, maxCount];
116
+ return flow([
117
+ castToNumber,
118
+ limitCount,
119
+ sortBy,
120
+ sortedUniq
121
+ ])(rounds);
201
122
  }
202
- get state() {
203
- const that = this;
204
- return new Proxy({}, {
205
- get(_target, prop) {
206
- return Reflect.get(that.#state, prop);
123
+ switch (jurisdiction) {
124
+ case "BG": return { autoplay: {
125
+ lossLimit: {
126
+ enabled: true,
127
+ showNoLimit: false
128
+ },
129
+ winLimit: { enabled: true }
130
+ } };
131
+ case "BR": return {
132
+ autoplay: {
133
+ lossLimit: {
134
+ enabled: true,
135
+ showNoLimit: false
136
+ },
137
+ winLimit: { enabled: true }
138
+ },
139
+ feature: { showPaylineLines: true }
140
+ };
141
+ case "CA": return {
142
+ autoplay: { enabled: false },
143
+ feature: {
144
+ showFastPlay: false,
145
+ allowQuickStop: false,
146
+ showFeatureBuy: false
147
+ },
148
+ minimumSpinDuration: 2.5
149
+ };
150
+ case "CO": return {
151
+ autoplay: { rounds: {
152
+ showNoLimit: false,
153
+ showCustomLimit: false,
154
+ options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
155
+ } },
156
+ feature: {
157
+ showFastPlay: false,
158
+ allowQuickStop: false
159
+ },
160
+ minimumSpinDuration: 3
161
+ };
162
+ case "CZ": return {
163
+ feature: {
164
+ showFastPlay: false,
165
+ showFeatureBuy: false,
166
+ allowGamble: false
167
+ },
168
+ minimumSpinDuration: 2
169
+ };
170
+ case "DE": return {
171
+ autoplay: { enabled: false },
172
+ feature: {
173
+ showFastPlay: false,
174
+ allowQuickStop: false,
175
+ showFeatureBuy: false,
176
+ showPromoPanel: false
177
+ },
178
+ minimumSpinDuration: 5
179
+ };
180
+ case "DK": return { minimumSpinDuration: 3 };
181
+ case "ES": return {
182
+ autoplay: { rounds: {
183
+ showNoLimit: false,
184
+ showCustomLimit: false,
185
+ options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
186
+ } },
187
+ feature: {
188
+ showFastPlay: false,
189
+ allowQuickStop: false
190
+ },
191
+ minimumSpinDuration: 3
192
+ };
193
+ case "GR": return {
194
+ feature: {
195
+ showFastPlay: false,
196
+ allowQuickStop: false,
197
+ showFeatureBuy: false
198
+ },
199
+ minimumSpinDuration: 3
200
+ };
201
+ case "IT": return { feature: {
202
+ showDeconstructedBet: true,
203
+ showPaylineLines: true
204
+ } };
205
+ case "NL": return {
206
+ autoplay: { enabled: false },
207
+ feature: { showFeatureBuy: false }
208
+ };
209
+ case "PT": return {
210
+ autoplay: { rounds: {
211
+ showNoLimit: false,
212
+ showCustomLimit: false,
213
+ options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
214
+ } },
215
+ feature: {
216
+ showFeatureBuy: false,
217
+ showFastPlay: false,
218
+ allowQuickStop: false
207
219
  },
208
- set(_target, prop, value) {
209
- that.setState((state) => ({
210
- ...state,
211
- [prop]: value
212
- }));
213
- return true;
214
- }
215
- });
216
- }
217
- setState(setter) {
218
- this.#state = setter(this.#state);
219
- this.send("state", this.#state);
220
- }
221
- forwardMessages() {
222
- this.forwardToPlugins("casino", [
223
- "sdk:autoplay",
224
- "sdk:bet",
225
- "sdk:cashier",
226
- "sdk:choice",
227
- "sdk:close",
228
- "sdk:error",
229
- "sdk:history",
230
- "sdk:loadEnd",
231
- "sdk:loadProgress",
232
- "sdk:loadStart",
233
- "sdk:playReady",
234
- "sdk:playCycleEnd",
235
- "sdk:playCycleStart",
236
- "sdk:playEnd",
237
- "sdk:playStart",
238
- "sdk:settings",
239
- "sdk:openGame",
240
- "sdk:configure",
241
- "sdk:fullscreen"
242
- ]);
243
- this.forwardToPlugins("casino", ["rgs:playResponse", "rgs:openGameResponse"]);
244
- this.forwardToPlugins("rgs", ["sdk:openGame", "sdk:history"]);
245
- this.forwardToPlugins("rgs", [
246
- "casino:play",
247
- "casino:cashier",
248
- "casino:close",
249
- "casino:history",
250
- "casino:getBalance"
251
- ]);
252
- this.forwardToPlugins("telemetry", [
253
- "sdk:autoplay",
254
- "sdk:error",
255
- "sdk:playCycleStart",
256
- "sdk:playCycleEnd",
257
- "sdk:loadStart",
258
- "sdk:loadProgress",
259
- "sdk:loadEnd",
260
- "sdk:telemetry.click",
261
- "sdk:telemetry.orientationChange"
262
- ]);
263
- this.forwardToPlugins("telemetry", ["rgs:openGameResponse", "rgs:playResponse"]);
264
- this.forwardToSdk([
265
- "casino:balance",
266
- "casino:bet",
267
- "casino:choice",
268
- "casino:freeze",
269
- "casino:help",
270
- "casino:paytable",
271
- "casino:resume",
272
- "casino:settings",
273
- "casino:suspend",
274
- "casino:unfreeze"
275
- ]);
276
- this.forwardToSdk([
277
- "rgs:balance",
278
- "rgs:openGameError",
279
- "rgs:openGameResponse",
280
- "rgs:playError",
281
- "rgs:playResponse",
282
- "rgs:realityCheck"
283
- ]);
284
- }
285
- get config() {
286
- const that = this;
287
- return new Proxy({}, {
288
- get(_target, prop) {
289
- return Reflect.get(that.#config, prop);
220
+ minimumSpinDuration: 3
221
+ };
222
+ case "SE": return {
223
+ autoplay: { rounds: {
224
+ showNoLimit: false,
225
+ showCustomLimit: false,
226
+ options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
227
+ } },
228
+ feature: {
229
+ showFastPlay: false,
230
+ allowQuickStop: false
290
231
  },
291
- set(_target, prop, value) {
292
- return Reflect.set(that.#config, prop, value);
232
+ minimumSpinDuration: 3
233
+ };
234
+ case "UK": return {
235
+ autoplay: { enabled: false },
236
+ feature: {
237
+ showFastPlay: false,
238
+ allowQuickStop: false,
239
+ showFeatureBuy: false
240
+ },
241
+ minimumSpinDuration: 2.5
242
+ };
243
+ case "US": return { feature: { showPaylineLines: true } };
244
+ case "MT": return { feature: { showClock: true } };
245
+ case "SOCIAL": return { feature: { showHistory: false } };
246
+ case "CLASS2": return {
247
+ autoplay: { enabled: false },
248
+ feature: {
249
+ showFeatureBuy: false,
250
+ showPromoPanel: false
293
251
  }
294
- });
252
+ };
295
253
  }
296
- get sdkConfig() {
297
- return this.#sdkConfigManager.config;
254
+ return {};
255
+ }
256
+ var KalambaSdkConfigManager = class extends SdkConfigManager {
257
+ async getConfig() {
258
+ let uiConfig = this.config.ui;
259
+ const operatorConfig = await this.fetchOperatorConfig();
260
+ uiConfig = merge({}, uiConfig, operatorConfig);
261
+ const searchParams = new URLSearchParams(window.location.search);
262
+ const badgeOverrides = JSON.parse(searchParams.get("badgeParameters") ?? "{}");
263
+ uiConfig = merge({}, uiConfig, badgeOverrides);
264
+ const jurisdictionOverrides = jurisdictionConfigOverrides(uiConfig, this.config.api.jurisdiction);
265
+ uiConfig = merge({}, uiConfig, jurisdictionOverrides);
266
+ return uiConfig;
298
267
  }
299
- forwardToPlugins(targetDomain, messages) {
300
- messages.forEach((message) => {
301
- const onMessage = (event) => {
302
- if (event.data.message !== `kalamba:${message}`) return;
303
- logIn("wrapper:forwardToPlugins", targetDomain, event.data.message.replace(/^kalamba:/, ""), event.data.payload);
304
- window.postMessage({
305
- message: `kalamba:wrapper-${targetDomain}:${message.replace(/^(.+):/, "")}`,
306
- payload: event.data.payload
307
- });
308
- };
309
- window.addEventListener("message", onMessage);
310
- });
268
+ get mappedIntegration() {
269
+ const integration = this.config.api.integration.toLowerCase();
270
+ const brand = this.config.api.brand.toLowerCase();
271
+ if (integration === "spinomenal") {
272
+ if (brand.startsWith("gg-")) return "groove";
273
+ } else if (integration === "pariplay") {
274
+ if (brand.startsWith("8x1")) return "pariplay-8x1";
275
+ } else if (/openbox-.*/.test(integration)) return "openbox";
276
+ else if (/oryx-.*/.test(integration)) return "oryx";
277
+ else if (/relax-.*/.test(integration)) return "relax";
278
+ return integration;
311
279
  }
312
- forwardToSdk(messages) {
313
- messages.forEach((message) => {
314
- const onMessage = (event) => {
315
- if (event.data.message !== `kalamba:${message}`) return;
316
- logOut("wrapper:forwardToSdk", event.data.message.replace(/^kalamba:/, ""), event.data.payload);
317
- this.#messagePort.postMessage({
318
- message: `kalamba:wrapper:${message.replace(/^(.+):/, "")}`,
319
- payload: event.data.payload
320
- }, "*");
321
- };
322
- window.addEventListener("message", onMessage);
323
- });
280
+ get mappedBrand() {
281
+ const integration = this.config.api.integration.toLowerCase();
282
+ const brand = this.config.api.brand.toLowerCase();
283
+ if (integration === "bspot" && this.config.api.playMode.toLowerCase() === "fun") return "demo";
284
+ return brand;
324
285
  }
325
- on(message, listener, options) {
326
- const onMessage = function onMessage(event) {
327
- if (!new RegExp(`^kalamba:${message.includes(":") ? "" : "(.+):"}${message}$`).test(event.data.message) || new RegExp(`^kalamba:wrapper-(.+):${message}`).test(event.data.message) || new RegExp(`^kalamba:(.+)-wrapper:${message}`).test(event.data.message)) return;
328
- logIn("on", event.data.message.replace(/^kalamba:/, ""), event.data.payload);
329
- listener(event.data.payload);
330
- if (options?.once) window.removeEventListener("message", onMessage);
286
+ mapUiConfig(config) {
287
+ if (!config) return {};
288
+ return {
289
+ autoplay: {
290
+ enabled: config.badge.autoplayType !== 5,
291
+ rounds: {
292
+ enabled: config.badge.autoplayType !== 5,
293
+ showNoLimit: false,
294
+ showCustomLimit: false,
295
+ options: config.badge.autoplayRounds ?? [
296
+ 5,
297
+ 10,
298
+ 25,
299
+ 75,
300
+ 100
301
+ ]
302
+ },
303
+ lossLimit: {
304
+ enabled: config.badge.autoplayType === 1 || config.badge.autoplayType === 4,
305
+ showNoLimit: config.badge.autoplayType === 1,
306
+ showCustomLimit: config.badge.autoplayType !== 6,
307
+ options: [
308
+ 5,
309
+ 25,
310
+ 50
311
+ ]
312
+ },
313
+ winLimit: {
314
+ enabled: config.badge.autoplayType === 1 || config.badge.autoplayType === 4,
315
+ showNoLimit: true,
316
+ showCustomLimit: config.badge.autoplayType !== 6,
317
+ options: [
318
+ 10,
319
+ 20,
320
+ 75
321
+ ]
322
+ },
323
+ simple: config.badge.autoplayType === 3
324
+ },
325
+ cashier: {
326
+ enabled: config.badge.showQuickDeposit,
327
+ balanceThreshold: config.badge.quickDepositBalanceThresholds
328
+ },
329
+ feature: {
330
+ allowTelemetry: true,
331
+ allowFullscreen: config.defaults.fullscreen === "true",
332
+ allowGamble: config.badge.gambleHideWithAutoCollect === void 0 ? true : !config.badge.gambleHideWithAutoCollect,
333
+ allowQuickStop: config.badge.minimumSpinDuration === 0,
334
+ showCashier: config.badge.showQuickDeposit,
335
+ showClock: config.badge.showClock,
336
+ showCloseGame: config.badge.showCloseGameButton && (!config.badge.showCloseGameButtonOptional || !!this.config.api.homeUrl),
337
+ showCurrency: config.badge.showCurrency,
338
+ showDeconstructedBet: config.badge.showDeconstructedBet,
339
+ showFastPlay: config.badge.showTurboButton,
340
+ showFeatureBuy: config.badge.showHyperBonus,
341
+ showFunModeBanner: config.badge.showFunBanner,
342
+ showHistory: config.badge.showGameHistory,
343
+ showPaylineLines: config.badge.showPaylineLines,
344
+ showPromoPanel: config.defaults.showMetafeatures !== "false",
345
+ showRoundId: config.badge.showRoundId,
346
+ showRules: config.badge.showGameRules,
347
+ showSessionDuration: config.badge.realityCheckShowSessionDuration,
348
+ showUi: config.badge.showGameUi
349
+ },
350
+ minimumSpinDuration: config.badge.minimumSpinDuration,
351
+ requestTimeoutMs: config.badge.requestTimeoutMs,
352
+ incompleteGameResolution: {
353
+ daysToAutoResolution: config.badge.incompleteGamesResolutionType === 3 ? config.badge.daysToAutoResolution ?? 90 : void 0,
354
+ type: config.badge.incompleteGamesResolutionType === 2 ? "MANUAL" : config.badge.incompleteGamesResolutionType === 3 ? "AUTOMATIC" : void 0
355
+ },
356
+ realityCheck: {
357
+ sessionDurationPeriodFormat: config.badge.realityCheckPeriodFormat,
358
+ showCloseGame: config.badge.realityCheckShowCloseGame,
359
+ showHistory: config.badge.realityCheckShowHistoryLink,
360
+ showNetPosition: config.badge.realityCheckShowNetGamingActivity,
361
+ showSessionDuration: config.badge.realityCheckShowSessionDuration,
362
+ showSumBets: config.badge.realityCheckShowSumBets,
363
+ showSumWins: config.badge.realityCheckShowSumWins
364
+ },
365
+ skipInsufficientFundsCheck: config.badge.skipInsufficientFundsCheck,
366
+ skipSplash: config.badge.loaderAutoContinue
331
367
  };
332
- window.addEventListener("message", onMessage);
333
- return () => window.removeEventListener("message", onMessage);
334
- }
335
- send(message, ...[payload]) {
336
- logOut("wrapper:send", message, payload);
337
- window.postMessage({
338
- message: `kalamba:wrapper:${message}`,
339
- payload
340
- });
341
- this.#messagePort.postMessage({
342
- message: `kalamba:wrapper:${message}`,
343
- payload
344
- }, "*");
345
- }
346
- injectPayload(...[source, payload]) {
347
- this.#payloadInjectionManager.register(source, payload);
348
- return () => this.#payloadInjectionManager.unregister(source);
349
- }
350
- activateFreeRounds(freeRound) {
351
- if (!freeRound) return;
352
- this.setState((state) => ({
353
- ...state,
354
- freeRoundId: freeRound.id
355
- }));
356
- this.#payloadInjectionManager.register("freeRounds", { freeRoundId: freeRound.id });
357
- this.send("legalBets", { [freeRound.conf.base]: [freeRound.conf.multiplier] });
358
- this.send("bet", {
359
- base: freeRound.conf.base,
360
- multiplier: freeRound.conf.multiplier
361
- });
362
368
  }
363
- handleFreeRounds(freeRound) {
364
- if (!freeRound) return;
365
- switch (freeRound.status) {
366
- case "ACTIVE":
367
- this.activateFreeRounds(freeRound);
368
- break;
369
- case "PENDING":
370
- this.send("freeRoundsOffer", freeRound);
371
- break;
372
- case "FINISHED":
373
- this.send("freeRoundsComplete", freeRound);
374
- this.setState((state) => ({
375
- ...state,
376
- freeRoundId: void 0
377
- }));
378
- this.#payloadInjectionManager.unregister("freeRounds");
379
- this.send("legalBets", this.state.openGameResponse.bet.available);
380
- this.send("bet", this.state.openGameResponse.bet.default);
381
- break;
382
- default: break;
369
+ async fetchOperatorConfig() {
370
+ const rootUrl = "https://awscdn.kalamba.net/games/games/build/";
371
+ const integration = this.mappedIntegration;
372
+ const brand = this.mappedBrand;
373
+ let config = {};
374
+ try {
375
+ config = await fetch(`${rootUrl}config/${integration}/cage/${brand}.json`).then((r) => r.json());
376
+ } catch (e) {
377
+ try {
378
+ config = await fetch(`${rootUrl}config/${integration}/operator.json`).then((r) => r.json());
379
+ } catch (e) {
380
+ config = await fetch(`${rootUrl}config/fun/operator.json`).then((r) => r.json());
381
+ }
383
382
  }
383
+ return this.mapUiConfig(config);
384
384
  }
385
- async requestWakeLock() {
386
- try {
387
- this.#wakeLock = await navigator.wakeLock.request("screen");
388
- } catch {}
385
+ };
386
+
387
+ //#endregion
388
+ //#region src/common/PayloadInjectionManager.ts
389
+ /**
390
+ * PayloadInjectionManager manages payload injection from multiple sources.
391
+ * Each source can register either static payload data or async functions that will be merged together when creating the final payload for injection.
392
+ */
393
+ var PayloadInjectionManager = class {
394
+ #injectors = /* @__PURE__ */ new Map();
395
+ /**
396
+ * Register or update payload injector from a specific source
397
+ * @param source - Unique identifier for the payload source (e.g., 'bspot', 'freeRounds')
398
+ * @param payload - Payload data or async function that receives playPayload and returns payload data
399
+ */
400
+ register(source, payloadProvider) {
401
+ this.#injectors.set(source, payloadProvider);
389
402
  }
390
- async releaseWakeLock() {
391
- if (!this.#wakeLock) return;
392
- await this.#wakeLock.release();
393
- this.#wakeLock = void 0;
403
+ /**
404
+ * Remove payload injector from a specific source
405
+ * @param source - Source identifier to remove
406
+ */
407
+ unregister(source) {
408
+ this.#injectors.delete(source);
409
+ }
410
+ /**
411
+ * Get merged payload from all registered injectors
412
+ * @param playPayload - The play payload that will be passed to registered functions
413
+ * @returns Promise resolving to merged payload object or undefined if no injectors registered
414
+ */
415
+ async getPayload(playPayload) {
416
+ if (this.#injectors.size === 0) return;
417
+ const merged = {};
418
+ for (const payloadProvider of this.#injectors.values()) merge(merged, typeof payloadProvider === "function" ? await payloadProvider(playPayload) : payloadProvider);
419
+ return merged;
394
420
  }
395
421
  };
396
422
 
397
423
  //#endregion
398
- //#region src/common/config/SdkConfigManager.ts
399
- var SdkConfigManager = class {
400
- config;
401
- constructor() {
402
- this.config = {
403
- api: {
404
- brand: "",
405
- cashierUrl: void 0,
406
- coinValueInCents: 1,
407
- country: "",
408
- currency: "",
409
- game: "",
410
- gameHistoryUrl: "",
411
- homeUrl: void 0,
412
- integration: "",
413
- jurisdiction: null,
414
- playMode: "FUN",
415
- user: "",
416
- sessionId: v4(),
417
- backendSessionId: void 0
418
- },
419
- ui: {
420
- autoplay: {
421
- enabled: false,
422
- rounds: {
423
- enabled: false,
424
- showNoLimit: false,
425
- showCustomLimit: false,
426
- options: []
427
- },
428
- lossLimit: {
429
- enabled: false,
430
- showNoLimit: false,
431
- showCustomLimit: false,
432
- options: []
433
- },
434
- winLimit: {
435
- enabled: false,
436
- showNoLimit: false,
437
- showCustomLimit: false,
438
- options: []
439
- },
440
- simple: false,
441
- stopOnFeature: false
442
- },
443
- cashier: {
444
- enabled: false,
445
- balanceThreshold: [0]
446
- },
447
- feature: {
448
- allowTelemetry: false,
449
- allowFullscreen: false,
450
- allowGamble: false,
451
- allowQuickStop: false,
452
- showCashier: false,
453
- showClock: false,
454
- showCloseGame: false,
455
- showCurrency: true,
456
- showDeconstructedBet: false,
457
- showFastPlay: false,
458
- showFeatureBuy: false,
459
- showFeatureIntro: false,
460
- showFeatureOutro: false,
461
- showFunModeBanner: false,
462
- showHistory: false,
463
- showLowWinCelebration: false,
464
- showMaximumWin: false,
465
- showNetPosition: false,
466
- showPaylineLines: false,
467
- showPaytable: false,
468
- showRoundId: false,
469
- showRtp: false,
470
- showRules: false,
471
- showSessionDuration: false,
472
- showUi: true
473
- },
474
- language: "en",
475
- minimumSpinDuration: 0,
476
- requestTimeoutMs: 1e4,
477
- incompleteGameResolution: { type: "NONE" },
478
- realityCheck: {
479
- sessionDurationPeriodFormat: "minutes",
480
- showCloseGame: false,
481
- showHistory: false,
482
- showNetPosition: false,
483
- showSessionDuration: false,
484
- showSumBets: false,
485
- showSumWins: false
486
- },
487
- skipInsufficientFundsCheck: false,
488
- skipSplash: false,
489
- cheatPanel: false
424
+ //#region src/wrapper/KalambaSdkWrapper.ts
425
+ /**
426
+ * Compare two semver strings. Returns:
427
+ * -1 if a < b, 0 if a === b, 1 if a > b
428
+ */
429
+ function compareSemver(a, b) {
430
+ const pa = a.split(".").map(Number);
431
+ const pb = b.split(".").map(Number);
432
+ for (let i = 0; i < 3; i++) {
433
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return -1;
434
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return 1;
435
+ }
436
+ return 0;
437
+ }
438
+ const logIn = () => {};
439
+ const logOut = () => {};
440
+ var KalambaSdkWrapper = class {
441
+ #messagePort;
442
+ #plugins;
443
+ #config = {
444
+ gameName: "UNKNOWN",
445
+ gameVersion: "unknown",
446
+ showErrors: true,
447
+ showFreeRounds: true,
448
+ showPromoPanel: true,
449
+ showRealityCheck: true,
450
+ showBars: true,
451
+ skipErrors: []
452
+ };
453
+ #store = createStore(() => ({
454
+ isSdkConfigured: false,
455
+ balance: 0,
456
+ bet: {
457
+ base: 0,
458
+ multiplier: 0
459
+ },
460
+ updateBalance: true,
461
+ openGameResponse: void 0,
462
+ lastPlayResponse: void 0,
463
+ freeRoundId: void 0,
464
+ playBlockers: []
465
+ }));
466
+ #sdkConfigManager;
467
+ #payloadInjectionManager;
468
+ #wakeLock;
469
+ #compatibility;
470
+ constructor({ messagePort, plugins, sdkConfigManager, compatibility }) {
471
+ this.#messagePort = messagePort;
472
+ this.#sdkConfigManager = new sdkConfigManager();
473
+ this.#compatibility = compatibility;
474
+ this.#payloadInjectionManager = new PayloadInjectionManager();
475
+ this.injectPayload = this.injectPayload.bind(this);
476
+ this.#plugins = {
477
+ rgs: new plugins.rgs(this.config, this.sdkConfig, this.state),
478
+ casino: plugins.casino.map((casinoPlugin) => new casinoPlugin(this.config, this.sdkConfig, this.state, this.injectPayload)),
479
+ telemetry: plugins.telemetry.map(({ plugin, trackers }) => new plugin(trackers.map((tracker) => new tracker(this.config, this.sdkConfig)), this.config, this.sdkConfig))
480
+ };
481
+ this.on("balance", ({ balance }) => {
482
+ this.store.setState((state) => ({
483
+ ...state,
484
+ balance
485
+ }));
486
+ });
487
+ this.on("bet", (bet) => {
488
+ this.store.setState((state) => ({
489
+ ...state,
490
+ bet
491
+ }));
492
+ });
493
+ this.on("sdk:playCycleStart", async () => {
494
+ await this.requestWakeLock();
495
+ if (this.state.freeRoundId) return;
496
+ this.send("balance", { balance: this.state.balance - this.state.bet.base * this.state.bet.multiplier });
497
+ });
498
+ this.on("sdk:play", async (payload) => {
499
+ this.store.setState((state) => ({
500
+ ...state,
501
+ playBlockers: []
502
+ }));
503
+ const payloadToInject = await this.#payloadInjectionManager.getPayload(payload.contract);
504
+ this.send("play", {
505
+ ...payload,
506
+ payloadToInject
507
+ });
508
+ });
509
+ this.on("sdk:configure", (payload) => {
510
+ Object.assign(this.#config, payload);
511
+ const result = this.checkCompatibility(payload.sdkVersion);
512
+ this.#compatibility.onCheck(result, () => this.send("wrapperConfigured"));
513
+ });
514
+ this.on("freeRoundsPopup", () => {
515
+ const freeRound = (this.state.lastPlayResponse ?? this.state.openGameResponse)?.freeRounds[0];
516
+ if (freeRound && this.state.freeRoundId) this.send("freeRoundsInfo", freeRound);
517
+ });
518
+ this.on("rgs:openGameResponse", async (response) => {
519
+ this.on("playReady", () => {
520
+ this.handleFreeRounds(response.contract.freeRounds[0]);
521
+ }, { once: true });
522
+ this.sdkConfig.ui = await this.#sdkConfigManager.getConfig();
523
+ this.store.setState((state) => ({
524
+ ...state,
525
+ isSdkConfigured: true
526
+ }));
527
+ this.send("configured", this.sdkConfig);
528
+ this.send("legalBets", response.contract.bet.available);
529
+ this.send("bet", response.contract.bet.lastPaid ?? response.contract.bet.default);
530
+ this.store.setState((state) => ({
531
+ ...state,
532
+ openGameResponse: response.contract
533
+ }));
534
+ if (this.state.updateBalance) this.send("balance", { balance: response.contract.balance.coins });
535
+ });
536
+ this.on("rgs:openGameError", async (error) => {
537
+ if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
538
+ messageKey: `Error.TIMEOUT`,
539
+ messageCode: "TIMEOUT",
540
+ type: "RELOAD"
541
+ });
542
+ if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
543
+ messageKey: `RgsError.${error.data.code}`,
544
+ messageCode: error.data.code,
545
+ type: RgsErrorAction[error.data.code]
546
+ });
547
+ });
548
+ this.on("rgs:playResponse", async (response) => {
549
+ this.store.setState((state) => ({
550
+ ...state,
551
+ lastPlayResponse: response.contract
552
+ }));
553
+ });
554
+ this.on("playEnd", async () => {
555
+ await Promise.all(this.state.playBlockers);
556
+ this.send("playReady");
557
+ });
558
+ this.on("playCycleEnd", () => {
559
+ this.releaseWakeLock();
560
+ if (this.state.updateBalance) this.send("balance", { balance: this.state.lastPlayResponse.balance.coins });
561
+ this.handleFreeRounds(this.state.lastPlayResponse.freeRounds[0]);
562
+ });
563
+ this.on("rgs:playError", async (error) => {
564
+ if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
565
+ messageKey: `Error.TIMEOUT`,
566
+ messageCode: "TIMEOUT",
567
+ type: "RELOAD"
568
+ });
569
+ if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
570
+ messageKey: `RgsError.${error.data.code}`,
571
+ messageCode: error.data.code,
572
+ type: RgsErrorAction[error.data.code]
573
+ });
574
+ });
575
+ this.on("rgs:freeRoundsResponse", async (response) => {
576
+ if (response.action === "ACCEPT") {
577
+ const freeRound = (this.state.lastPlayResponse ?? this.state.openGameResponse)?.freeRounds[0];
578
+ this.activateFreeRounds(freeRound);
490
579
  }
491
- };
580
+ });
581
+ this.on("rgs:freeRoundsError", async (error) => {
582
+ if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
583
+ messageKey: `Error.TIMEOUT`,
584
+ messageCode: "TIMEOUT",
585
+ type: "RELOAD"
586
+ });
587
+ if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
588
+ messageKey: `RgsError.${error.data.code}`,
589
+ messageCode: error.data.code,
590
+ type: RgsErrorAction[error.data.code]
591
+ });
592
+ });
593
+ this.forwardMessages();
594
+ document.addEventListener("visibilitychange", async () => {
595
+ if (this.#wakeLock && document.visibilityState === "visible") await this.requestWakeLock();
596
+ });
492
597
  }
493
- };
494
-
495
- //#endregion
496
- //#region src/common/config/KalambaSdkConfigManager.ts
497
- function jurisdictionConfigOverrides(config, jurisdiction) {
498
- function limitRounds(autoPlayRounds, { maxCount }) {
499
- const limitCount = (rounds) => rounds?.filter((count) => count <= maxCount) ?? [];
500
- const castToNumber = (rounds) => rounds?.map((count) => Number(count)) ?? [];
501
- const rounds = [...autoPlayRounds, maxCount];
502
- return flow([
503
- castToNumber,
504
- limitCount,
505
- sortBy,
506
- sortedUniq
507
- ])(rounds);
598
+ get store() {
599
+ return this.#store;
508
600
  }
509
- switch (jurisdiction) {
510
- case "BG": return { autoplay: {
511
- lossLimit: {
512
- enabled: true,
513
- showNoLimit: false
514
- },
515
- winLimit: { enabled: true }
516
- } };
517
- case "BR": return { feature: { showPaylineLines: true } };
518
- case "CA": return {
519
- autoplay: { enabled: false },
520
- feature: {
521
- showFastPlay: false,
522
- allowQuickStop: false,
523
- showFeatureBuy: false
524
- },
525
- minimumSpinDuration: 2.5
526
- };
527
- case "CO": return {
528
- autoplay: { rounds: {
529
- showNoLimit: false,
530
- showCustomLimit: false,
531
- options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
532
- } },
533
- feature: {
534
- showFastPlay: false,
535
- allowQuickStop: false
536
- },
537
- minimumSpinDuration: 3
538
- };
539
- case "CZ": return {
540
- feature: {
541
- showFastPlay: false,
542
- showFeatureBuy: false,
543
- allowGamble: false
544
- },
545
- minimumSpinDuration: 2
546
- };
547
- case "DE": return {
548
- autoplay: { enabled: false },
549
- feature: {
550
- showFastPlay: false,
551
- allowQuickStop: false,
552
- showFeatureBuy: false
553
- },
554
- minimumSpinDuration: 5
555
- };
556
- case "DK": return { minimumSpinDuration: 3 };
557
- case "ES": return {
558
- autoplay: { rounds: {
559
- showNoLimit: false,
560
- showCustomLimit: false,
561
- options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
562
- } },
563
- feature: {
564
- showFastPlay: false,
565
- allowQuickStop: false
566
- },
567
- minimumSpinDuration: 3
568
- };
569
- case "GR": return {
570
- feature: {
571
- showFastPlay: false,
572
- allowQuickStop: false,
573
- showFeatureBuy: false
601
+ get state() {
602
+ const that = this;
603
+ return new Proxy({}, {
604
+ get(_target, prop) {
605
+ return Reflect.get(that.#store.getState(), prop);
574
606
  },
575
- minimumSpinDuration: 3
576
- };
577
- case "IT": return { feature: {
578
- showDeconstructedBet: true,
579
- showPaylineLines: true
580
- } };
581
- case "NL": return {
582
- autoplay: { enabled: false },
583
- feature: { showFeatureBuy: false }
607
+ set(_target, prop, value) {
608
+ that.#store.setState((state) => ({
609
+ ...state,
610
+ [prop]: value
611
+ }));
612
+ return true;
613
+ }
614
+ });
615
+ }
616
+ checkCompatibility(sdkVersion) {
617
+ const { manifest } = this.#compatibility;
618
+ const currentEntry = manifest[0];
619
+ if (!currentEntry || !sdkVersion || currentEntry.minSdk === "newest") return {
620
+ status: "compatible",
621
+ sdkVersion
584
622
  };
585
- case "PT": return {
586
- autoplay: { rounds: {
587
- showNoLimit: false,
588
- showCustomLimit: false,
589
- options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
590
- } },
591
- feature: {
592
- showFeatureBuy: false,
593
- showFastPlay: false,
594
- allowQuickStop: false
595
- },
596
- minimumSpinDuration: 3
623
+ if (compareSemver(sdkVersion, currentEntry.minSdk) >= 0) return {
624
+ status: "compatible",
625
+ sdkVersion,
626
+ eol: currentEntry.eol
597
627
  };
598
- case "SE": return {
599
- autoplay: { rounds: {
600
- showNoLimit: false,
601
- showCustomLimit: false,
602
- options: limitRounds(config.autoplay.rounds.options, { maxCount: 100 })
603
- } },
604
- feature: {
605
- showFastPlay: false,
606
- allowQuickStop: false
607
- },
608
- minimumSpinDuration: 3
628
+ return {
629
+ status: "incompatible",
630
+ sdkVersion,
631
+ fallback: manifest.filter((entry) => entry.wrapper && compareSemver(sdkVersion, entry.minSdk) >= 0 && (!entry.eol || /* @__PURE__ */ new Date() <= new Date(entry.eol))).sort((a, b) => compareSemver(b.wrapper, a.wrapper))[0]
609
632
  };
610
- case "UK": return {
611
- autoplay: { enabled: false },
612
- feature: {
613
- showFastPlay: false,
614
- allowQuickStop: false,
615
- showFeatureBuy: false
633
+ }
634
+ forwardMessages() {
635
+ this.forwardToPlugins("casino", [
636
+ "sdk:autoplay",
637
+ "sdk:bet",
638
+ "sdk:cashier",
639
+ "sdk:choice",
640
+ "sdk:close",
641
+ "sdk:error",
642
+ "sdk:history",
643
+ "sdk:loadEnd",
644
+ "sdk:loadProgress",
645
+ "sdk:loadStart",
646
+ "sdk:playReady",
647
+ "sdk:playCycleEnd",
648
+ "sdk:playCycleStart",
649
+ "sdk:playEnd",
650
+ "sdk:playStart",
651
+ "sdk:settings",
652
+ "sdk:openGame",
653
+ "sdk:configure",
654
+ "sdk:fullscreen"
655
+ ]);
656
+ this.forwardToPlugins("casino", ["rgs:playResponse", "rgs:openGameResponse"]);
657
+ this.forwardToPlugins("rgs", ["sdk:openGame", "sdk:history"]);
658
+ this.forwardToPlugins("rgs", [
659
+ "casino:play",
660
+ "casino:cashier",
661
+ "casino:close",
662
+ "casino:history",
663
+ "casino:getBalance"
664
+ ]);
665
+ this.forwardToPlugins("telemetry", [
666
+ "sdk:autoplay",
667
+ "sdk:error",
668
+ "sdk:playCycleStart",
669
+ "sdk:playCycleEnd",
670
+ "sdk:loadStart",
671
+ "sdk:loadProgress",
672
+ "sdk:loadEnd",
673
+ "sdk:telemetry.click",
674
+ "sdk:telemetry.orientationChange"
675
+ ]);
676
+ this.forwardToPlugins("telemetry", ["rgs:openGameResponse", "rgs:playResponse"]);
677
+ this.forwardToSdk([
678
+ "casino:balance",
679
+ "casino:bet",
680
+ "casino:choice",
681
+ "casino:freeze",
682
+ "casino:help",
683
+ "casino:paytable",
684
+ "casino:resume",
685
+ "casino:settings",
686
+ "casino:suspend",
687
+ "casino:unfreeze"
688
+ ]);
689
+ this.forwardToSdk([
690
+ "rgs:balance",
691
+ "rgs:openGameError",
692
+ "rgs:openGameResponse",
693
+ "rgs:playError",
694
+ "rgs:playResponse",
695
+ "rgs:realityCheck"
696
+ ]);
697
+ }
698
+ get config() {
699
+ const that = this;
700
+ return new Proxy({}, {
701
+ get(_target, prop) {
702
+ return Reflect.get(that.#config, prop);
616
703
  },
617
- minimumSpinDuration: 2.5
618
- };
619
- case "US": return { feature: { showPaylineLines: true } };
620
- case "MT": return { feature: { showClock: true } };
621
- case "SOCIAL": return { feature: { showHistory: false } };
622
- case "CLASS2": return {
623
- autoplay: { enabled: false },
624
- feature: { showFeatureBuy: false }
625
- };
704
+ set(_target, prop, value) {
705
+ return Reflect.set(that.#config, prop, value);
706
+ }
707
+ });
626
708
  }
627
- return {};
628
- }
629
- var KalambaSdkConfigManager = class extends SdkConfigManager {
630
- async getConfig() {
631
- let uiConfig = this.config.ui;
632
- const operatorConfig = await this.fetchOperatorConfig();
633
- uiConfig = merge({}, uiConfig, operatorConfig);
634
- const searchParams = new URLSearchParams(window.location.search);
635
- const badgeOverrides = JSON.parse(searchParams.get("badgeParameters") ?? "{}");
636
- uiConfig = merge({}, uiConfig, badgeOverrides);
637
- const jurisdictionOverrides = jurisdictionConfigOverrides(uiConfig, this.config.api.jurisdiction);
638
- uiConfig = merge({}, uiConfig, jurisdictionOverrides);
639
- return uiConfig;
709
+ get sdkConfig() {
710
+ return this.#sdkConfigManager.config;
640
711
  }
641
- get mappedIntegration() {
642
- const integration = this.config.api.integration.toLowerCase();
643
- const brand = this.config.api.brand.toLowerCase();
644
- if (integration === "spinomenal") {
645
- if (brand.startsWith("gg-")) return "groove";
646
- } else if (integration === "pariplay") {
647
- if (brand.startsWith("8x1")) return "pariplay-8x1";
648
- } else if (/openbox-.*/.test(integration)) return "openbox";
649
- else if (/oryx-.*/.test(integration)) return "oryx";
650
- else if (/relax-.*/.test(integration)) return "relax";
651
- return integration;
712
+ forwardToPlugins(targetDomain, messages) {
713
+ messages.forEach((message) => {
714
+ const onMessage = (event) => {
715
+ if (event.data.message !== `kalamba:${message}`) return;
716
+ logIn("wrapper:forwardToPlugins", targetDomain, event.data.message.replace(/^kalamba:/, ""), event.data.payload);
717
+ window.postMessage({
718
+ message: `kalamba:wrapper-${targetDomain}:${message.replace(/^(.+):/, "")}`,
719
+ payload: event.data.payload
720
+ });
721
+ };
722
+ window.addEventListener("message", onMessage);
723
+ });
652
724
  }
653
- get mappedBrand() {
654
- const integration = this.config.api.integration.toLowerCase();
655
- const brand = this.config.api.brand.toLowerCase();
656
- if (integration === "bspot" && this.config.api.playMode.toLowerCase() === "fun") return "demo";
657
- return brand;
725
+ forwardToSdk(messages) {
726
+ messages.forEach((message) => {
727
+ const onMessage = (event) => {
728
+ if (event.data.message !== `kalamba:${message}`) return;
729
+ logOut("wrapper:forwardToSdk", event.data.message.replace(/^kalamba:/, ""), event.data.payload);
730
+ this.#messagePort.postMessage({
731
+ message: `kalamba:wrapper:${message.replace(/^(.+):/, "")}`,
732
+ payload: event.data.payload
733
+ }, "*");
734
+ };
735
+ window.addEventListener("message", onMessage);
736
+ });
658
737
  }
659
- mapUiConfig(config) {
660
- if (!config) return {};
661
- return {
662
- autoplay: {
663
- enabled: config.badge.autoplayType !== 5,
664
- rounds: {
665
- enabled: config.badge.autoplayType !== 5,
666
- showNoLimit: false,
667
- showCustomLimit: false,
668
- options: config.badge.autoplayRounds ?? [
669
- 5,
670
- 10,
671
- 25,
672
- 75,
673
- 100
674
- ]
675
- },
676
- lossLimit: {
677
- enabled: config.badge.autoplayType === 1 || config.badge.autoplayType === 4,
678
- showNoLimit: config.badge.autoplayType === 1,
679
- showCustomLimit: config.badge.autoplayType !== 6,
680
- options: [
681
- 5,
682
- 25,
683
- 50
684
- ]
685
- },
686
- winLimit: {
687
- enabled: config.badge.autoplayType === 1 || config.badge.autoplayType === 4,
688
- showNoLimit: true,
689
- showCustomLimit: config.badge.autoplayType !== 6,
690
- options: [
691
- 10,
692
- 20,
693
- 75
694
- ]
695
- },
696
- simple: config.badge.autoplayType === 3
697
- },
698
- cashier: {
699
- enabled: config.badge.showQuickDeposit,
700
- balanceThreshold: config.badge.quickDepositBalanceThresholds
701
- },
702
- feature: {
703
- allowTelemetry: true,
704
- allowFullscreen: config.defaults.fullscreen === "true",
705
- allowGamble: config.badge.gambleHideWithAutoCollect === void 0 ? true : !config.badge.gambleHideWithAutoCollect,
706
- allowQuickStop: config.badge.minimumSpinDuration === 0,
707
- showCashier: config.badge.showQuickDeposit,
708
- showClock: config.badge.showClock,
709
- showCloseGame: config.badge.showCloseGameButton && (!config.badge.showCloseGameButtonOptional || !!this.config.api.homeUrl),
710
- showCurrency: config.badge.showCurrency,
711
- showDeconstructedBet: config.badge.showDeconstructedBet,
712
- showFastPlay: config.badge.showTurboButton,
713
- showFeatureBuy: config.badge.showHyperBonus,
714
- showFunModeBanner: config.badge.showFunBanner,
715
- showHistory: config.badge.showGameHistory,
716
- showPaylineLines: config.badge.showPaylineLines,
717
- showRoundId: config.badge.showRoundId,
718
- showRules: config.badge.showGameRules,
719
- showSessionDuration: config.badge.realityCheckShowSessionDuration,
720
- showUi: config.badge.showGameUi
721
- },
722
- minimumSpinDuration: config.badge.minimumSpinDuration,
723
- requestTimeoutMs: config.badge.requestTimeoutMs,
724
- incompleteGameResolution: {
725
- daysToAutoResolution: config.badge.incompleteGamesResolutionType === 3 ? config.badge.daysToAutoResolution ?? 90 : void 0,
726
- type: config.badge.incompleteGamesResolutionType === 2 ? "MANUAL" : config.badge.incompleteGamesResolutionType === 3 ? "AUTOMATIC" : void 0
727
- },
728
- realityCheck: {
729
- sessionDurationPeriodFormat: config.badge.realityCheckPeriodFormat,
730
- showCloseGame: config.badge.realityCheckShowCloseGame,
731
- showHistory: config.badge.realityCheckShowHistoryLink,
732
- showNetPosition: config.badge.realityCheckShowNetGamingActivity,
733
- showSessionDuration: config.badge.realityCheckShowSessionDuration,
734
- showSumBets: config.badge.realityCheckShowSumBets,
735
- showSumWins: config.badge.realityCheckShowSumWins
736
- },
737
- skipInsufficientFundsCheck: config.badge.skipInsufficientFundsCheck,
738
- skipSplash: config.badge.loaderAutoContinue
738
+ on(message, listener, options) {
739
+ const onMessage = function onMessage(event) {
740
+ if (!new RegExp(`^kalamba:${message.includes(":") ? "" : "(.+):"}${message}$`).test(event.data.message) || new RegExp(`^kalamba:wrapper-(.+):${message}`).test(event.data.message) || new RegExp(`^kalamba:(.+)-wrapper:${message}`).test(event.data.message)) return;
741
+ logIn("on", event.data.message.replace(/^kalamba:/, ""), event.data.payload);
742
+ listener(event.data.payload);
743
+ if (options?.once) window.removeEventListener("message", onMessage);
739
744
  };
745
+ window.addEventListener("message", onMessage);
746
+ return () => window.removeEventListener("message", onMessage);
740
747
  }
741
- async fetchOperatorConfig() {
742
- const rootUrl = "https://awscdn.kalamba.net/games/games/build/";
743
- const integration = this.mappedIntegration;
744
- const brand = this.mappedBrand;
745
- let config = {};
746
- try {
747
- config = await fetch(`${rootUrl}config/${integration}/cage/${brand}.json`).then((r) => r.json());
748
- } catch (e) {
749
- try {
750
- config = await fetch(`${rootUrl}config/${integration}/operator.json`).then((r) => r.json());
751
- } catch (e) {
752
- config = await fetch(`${rootUrl}config/fun/operator.json`).then((r) => r.json());
753
- }
748
+ send(message, ...[payload]) {
749
+ logOut("wrapper:send", message, payload);
750
+ window.postMessage({
751
+ message: `kalamba:wrapper:${message}`,
752
+ payload
753
+ });
754
+ this.#messagePort.postMessage({
755
+ message: `kalamba:wrapper:${message}`,
756
+ payload
757
+ }, "*");
758
+ }
759
+ injectPayload(...[source, payload]) {
760
+ this.#payloadInjectionManager.register(source, payload);
761
+ return () => this.#payloadInjectionManager.unregister(source);
762
+ }
763
+ activateFreeRounds(freeRound) {
764
+ if (!freeRound || this.state.freeRoundId) return;
765
+ this.store.setState((state) => ({
766
+ ...state,
767
+ freeRoundId: freeRound.id
768
+ }));
769
+ this.#payloadInjectionManager.register("freeRounds", { freeRoundId: freeRound.id });
770
+ this.send("legalBets", { [freeRound.conf.base]: [freeRound.conf.multiplier] });
771
+ this.send("bet", {
772
+ base: freeRound.conf.base,
773
+ multiplier: freeRound.conf.multiplier
774
+ });
775
+ }
776
+ handleFreeRounds(freeRound) {
777
+ if (!freeRound) return;
778
+ switch (freeRound.status) {
779
+ case "ACTIVE":
780
+ this.activateFreeRounds(freeRound);
781
+ break;
782
+ case "PENDING":
783
+ this.send("freeRoundsOffer", freeRound);
784
+ break;
785
+ case "FINISHED":
786
+ this.send("freeRoundsComplete", freeRound);
787
+ this.store.setState((state) => ({
788
+ ...state,
789
+ freeRoundId: void 0
790
+ }));
791
+ this.#payloadInjectionManager.unregister("freeRounds");
792
+ this.send("legalBets", this.state.openGameResponse.bet.available);
793
+ this.send("bet", this.state.openGameResponse.bet.default);
794
+ break;
795
+ default: break;
754
796
  }
755
- return this.mapUiConfig(config);
797
+ }
798
+ async requestWakeLock() {
799
+ try {
800
+ this.#wakeLock = await navigator.wakeLock.request("screen");
801
+ } catch {}
802
+ }
803
+ async releaseWakeLock() {
804
+ if (!this.#wakeLock) return;
805
+ await this.#wakeLock.release();
806
+ this.#wakeLock = void 0;
756
807
  }
757
808
  };
758
809