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