@kalamba/sdk 0.43.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,760 +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
- 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
219
+ },
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
208
231
  },
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);
232
+ minimumSpinDuration: 3
233
+ };
234
+ case "UK": return {
235
+ autoplay: { enabled: false },
236
+ feature: {
237
+ showFastPlay: false,
238
+ allowQuickStop: false,
239
+ showFeatureBuy: false
293
240
  },
294
- set(_target, prop, value) {
295
- return Reflect.set(that.#config, prop, value);
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
- });
298
- }
299
- get sdkConfig() {
300
- return this.#sdkConfigManager.config;
301
- }
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
- });
314
- }
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
- });
327
- }
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);
334
252
  };
335
- window.addEventListener("message", onMessage);
336
- return () => window.removeEventListener("message", onMessage);
337
253
  }
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
- }, "*");
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;
348
267
  }
349
- injectPayload(...[source, payload]) {
350
- this.#payloadInjectionManager.register(source, payload);
351
- return () => this.#payloadInjectionManager.unregister(source);
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;
352
279
  }
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
- });
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;
285
+ }
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
367
+ };
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 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
+ }
386
382
  }
383
+ return this.mapUiConfig(config);
387
384
  }
388
- async requestWakeLock() {
389
- try {
390
- this.#wakeLock = await navigator.wakeLock.request("screen");
391
- } 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);
392
402
  }
393
- async releaseWakeLock() {
394
- if (!this.#wakeLock) return;
395
- await this.#wakeLock.release();
396
- 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;
397
420
  }
398
421
  };
399
422
 
400
423
  //#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
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);
493
579
  }
494
- };
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
+ });
495
597
  }
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);
598
+ get store() {
599
+ return this.#store;
511
600
  }
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
601
+ get state() {
602
+ const that = this;
603
+ return new Proxy({}, {
604
+ get(_target, prop) {
605
+ return Reflect.get(that.#store.getState(), prop);
577
606
  },
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 }
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
587
622
  };
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
623
+ if (compareSemver(sdkVersion, currentEntry.minSdk) >= 0) return {
624
+ status: "compatible",
625
+ sdkVersion,
626
+ eol: currentEntry.eol
600
627
  };
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
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]
612
632
  };
613
- case "UK": return {
614
- autoplay: { enabled: false },
615
- feature: {
616
- showFastPlay: false,
617
- allowQuickStop: false,
618
- 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);
619
703
  },
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
- };
704
+ set(_target, prop, value) {
705
+ return Reflect.set(that.#config, prop, value);
706
+ }
707
+ });
629
708
  }
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;
709
+ get sdkConfig() {
710
+ return this.#sdkConfigManager.config;
643
711
  }
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;
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
+ });
655
724
  }
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;
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
+ });
661
737
  }
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
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);
742
744
  };
745
+ window.addEventListener("message", onMessage);
746
+ return () => window.removeEventListener("message", onMessage);
743
747
  }
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
- }
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;
757
796
  }
758
- 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;
759
807
  }
760
808
  };
761
809