@kalamba/sdk 0.43.0 → 0.48.1

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