@kalamba/sdk 0.43.0 → 0.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/wrapper.cjs CHANGED
@@ -3,760 +3,809 @@
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
209
220
  },
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);
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
294
232
  },
295
- set(_target, prop, value) {
296
- return Reflect.set(that.#config, prop, value);
233
+ minimumSpinDuration: 3
234
+ };
235
+ case "UK": return {
236
+ autoplay: { enabled: false },
237
+ feature: {
238
+ showFastPlay: false,
239
+ allowQuickStop: false,
240
+ showFeatureBuy: false
241
+ },
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
- });
253
+ };
299
254
  }
300
- get sdkConfig() {
301
- return this.#sdkConfigManager.config;
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;
302
268
  }
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
- });
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;
315
280
  }
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
- });
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;
328
286
  }
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);
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
335
368
  };
336
- window.addEventListener("message", onMessage);
337
- return () => window.removeEventListener("message", onMessage);
338
- }
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
- }, "*");
349
- }
350
- injectPayload(...[source, payload]) {
351
- this.#payloadInjectionManager.register(source, payload);
352
- return () => this.#payloadInjectionManager.unregister(source);
353
- }
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
- });
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 hostname = typeof window !== "undefined" ? window.location?.hostname ?? "" : "";
372
+ const rootUrl = hostname.includes(".dev.") ? "https://games.dev.kl.luckycdn.net/games/kalamba/" : hostname.includes(".submissions.") ? "https://games.submissions.kl.luckycdn.net/games/kalamba/" : hostname.includes(".test.") ? "https://awscdn.test.kalamba.net/games/games/" : "https://awscdn.kalamba.net/games/games/";
373
+ const integration = this.mappedIntegration;
374
+ const brand = this.mappedBrand;
375
+ let config = {};
376
+ try {
377
+ config = await fetch(`${rootUrl}config/${integration}/cage/${brand}.json`).then((r) => r.json());
378
+ } catch {
379
+ try {
380
+ config = await fetch(`${rootUrl}config/${integration}/operator.json`).then((r) => r.json());
381
+ } catch {
382
+ config = await fetch(`${rootUrl}config/fun/operator.json`).then((r) => r.json());
383
+ }
387
384
  }
385
+ return this.mapUiConfig(config);
388
386
  }
389
- async requestWakeLock() {
390
- try {
391
- this.#wakeLock = await navigator.wakeLock.request("screen");
392
- } catch {}
387
+ };
388
+
389
+ //#endregion
390
+ //#region src/common/PayloadInjectionManager.ts
391
+ /**
392
+ * PayloadInjectionManager manages payload injection from multiple sources.
393
+ * Each source can register either static payload data or async functions that will be merged together when creating the final payload for injection.
394
+ */
395
+ var PayloadInjectionManager = class {
396
+ #injectors = /* @__PURE__ */ new Map();
397
+ /**
398
+ * Register or update payload injector from a specific source
399
+ * @param source - Unique identifier for the payload source (e.g., 'bspot', 'freeRounds')
400
+ * @param payload - Payload data or async function that receives playPayload and returns payload data
401
+ */
402
+ register(source, payloadProvider) {
403
+ this.#injectors.set(source, payloadProvider);
393
404
  }
394
- async releaseWakeLock() {
395
- if (!this.#wakeLock) return;
396
- await this.#wakeLock.release();
397
- this.#wakeLock = void 0;
405
+ /**
406
+ * Remove payload injector from a specific source
407
+ * @param source - Source identifier to remove
408
+ */
409
+ unregister(source) {
410
+ this.#injectors.delete(source);
411
+ }
412
+ /**
413
+ * Get merged payload from all registered injectors
414
+ * @param playPayload - The play payload that will be passed to registered functions
415
+ * @returns Promise resolving to merged payload object or undefined if no injectors registered
416
+ */
417
+ async getPayload(playPayload) {
418
+ if (this.#injectors.size === 0) return;
419
+ const merged = {};
420
+ for (const payloadProvider of this.#injectors.values()) (0, lodash_es.merge)(merged, typeof payloadProvider === "function" ? await payloadProvider(playPayload) : payloadProvider);
421
+ return merged;
398
422
  }
399
423
  };
400
424
 
401
425
  //#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
426
+ //#region src/wrapper/KalambaSdkWrapper.ts
427
+ /**
428
+ * Compare two semver strings. Returns:
429
+ * -1 if a < b, 0 if a === b, 1 if a > b
430
+ */
431
+ function compareSemver(a, b) {
432
+ const pa = a.split(".").map(Number);
433
+ const pb = b.split(".").map(Number);
434
+ for (let i = 0; i < 3; i++) {
435
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return -1;
436
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return 1;
437
+ }
438
+ return 0;
439
+ }
440
+ const logIn = () => {};
441
+ const logOut = () => {};
442
+ var KalambaSdkWrapper = class {
443
+ #messagePort;
444
+ #plugins;
445
+ #config = {
446
+ gameName: "UNKNOWN",
447
+ gameVersion: "unknown",
448
+ showErrors: true,
449
+ showFreeRounds: true,
450
+ showPromoPanel: true,
451
+ showRealityCheck: true,
452
+ showBars: true,
453
+ skipErrors: []
454
+ };
455
+ #store = (0, zustand_vanilla.createStore)(() => ({
456
+ isSdkConfigured: false,
457
+ balance: 0,
458
+ bet: {
459
+ base: 0,
460
+ multiplier: 0
461
+ },
462
+ updateBalance: true,
463
+ openGameResponse: void 0,
464
+ lastPlayResponse: void 0,
465
+ freeRoundId: void 0,
466
+ playBlockers: []
467
+ }));
468
+ #sdkConfigManager;
469
+ #payloadInjectionManager;
470
+ #wakeLock;
471
+ #compatibility;
472
+ constructor({ messagePort, plugins, sdkConfigManager, compatibility }) {
473
+ this.#messagePort = messagePort;
474
+ this.#sdkConfigManager = new sdkConfigManager();
475
+ this.#compatibility = compatibility;
476
+ this.#payloadInjectionManager = new PayloadInjectionManager();
477
+ this.injectPayload = this.injectPayload.bind(this);
478
+ this.#plugins = {
479
+ rgs: new plugins.rgs(this.config, this.sdkConfig, this.state),
480
+ casino: plugins.casino.map((casinoPlugin) => new casinoPlugin(this.config, this.sdkConfig, this.state, this.injectPayload)),
481
+ telemetry: plugins.telemetry.map(({ plugin, trackers }) => new plugin(trackers.map((tracker) => new tracker(this.config, this.sdkConfig)), this.config, this.sdkConfig))
482
+ };
483
+ this.on("balance", ({ balance }) => {
484
+ this.store.setState((state) => ({
485
+ ...state,
486
+ balance
487
+ }));
488
+ });
489
+ this.on("bet", (bet) => {
490
+ this.store.setState((state) => ({
491
+ ...state,
492
+ bet
493
+ }));
494
+ });
495
+ this.on("sdk:playCycleStart", async () => {
496
+ await this.requestWakeLock();
497
+ if (this.state.freeRoundId) return;
498
+ this.send("balance", { balance: this.state.balance - this.state.bet.base * this.state.bet.multiplier });
499
+ });
500
+ this.on("sdk:play", async (payload) => {
501
+ this.store.setState((state) => ({
502
+ ...state,
503
+ playBlockers: []
504
+ }));
505
+ const payloadToInject = await this.#payloadInjectionManager.getPayload(payload.contract);
506
+ this.send("play", {
507
+ ...payload,
508
+ payloadToInject
509
+ });
510
+ });
511
+ this.on("sdk:configure", (payload) => {
512
+ Object.assign(this.#config, payload);
513
+ const result = this.checkCompatibility(payload.sdkVersion);
514
+ this.#compatibility.onCheck(result, () => this.send("wrapperConfigured"));
515
+ });
516
+ this.on("freeRoundsPopup", () => {
517
+ const freeRound = (this.state.lastPlayResponse ?? this.state.openGameResponse)?.freeRounds[0];
518
+ if (freeRound && this.state.freeRoundId) this.send("freeRoundsInfo", freeRound);
519
+ });
520
+ this.on("rgs:openGameResponse", async (response) => {
521
+ this.on("playReady", () => {
522
+ this.handleFreeRounds(response.contract.freeRounds[0]);
523
+ }, { once: true });
524
+ this.sdkConfig.ui = await this.#sdkConfigManager.getConfig();
525
+ this.store.setState((state) => ({
526
+ ...state,
527
+ isSdkConfigured: true
528
+ }));
529
+ this.send("configured", this.sdkConfig);
530
+ this.send("legalBets", response.contract.bet.available);
531
+ this.send("bet", response.contract.bet.lastPaid ?? response.contract.bet.default);
532
+ this.store.setState((state) => ({
533
+ ...state,
534
+ openGameResponse: response.contract
535
+ }));
536
+ if (this.state.updateBalance) this.send("balance", { balance: response.contract.balance.coins });
537
+ });
538
+ this.on("rgs:openGameError", async (error) => {
539
+ if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
540
+ messageKey: `Error.TIMEOUT`,
541
+ messageCode: "TIMEOUT",
542
+ type: "RELOAD"
543
+ });
544
+ if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
545
+ messageKey: `RgsError.${error.data.code}`,
546
+ messageCode: error.data.code,
547
+ type: require_errors.RgsErrorAction[error.data.code]
548
+ });
549
+ });
550
+ this.on("rgs:playResponse", async (response) => {
551
+ this.store.setState((state) => ({
552
+ ...state,
553
+ lastPlayResponse: response.contract
554
+ }));
555
+ });
556
+ this.on("playEnd", async () => {
557
+ await Promise.all(this.state.playBlockers);
558
+ this.send("playReady");
559
+ });
560
+ this.on("playCycleEnd", () => {
561
+ this.releaseWakeLock();
562
+ if (this.state.updateBalance) this.send("balance", { balance: this.state.lastPlayResponse.balance.coins });
563
+ this.handleFreeRounds(this.state.lastPlayResponse.freeRounds[0]);
564
+ });
565
+ this.on("rgs:playError", async (error) => {
566
+ if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
567
+ messageKey: `Error.TIMEOUT`,
568
+ messageCode: "TIMEOUT",
569
+ type: "RELOAD"
570
+ });
571
+ if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
572
+ messageKey: `RgsError.${error.data.code}`,
573
+ messageCode: error.data.code,
574
+ type: require_errors.RgsErrorAction[error.data.code]
575
+ });
576
+ });
577
+ this.on("rgs:freeRoundsResponse", async (response) => {
578
+ if (response.action === "ACCEPT") {
579
+ const freeRound = (this.state.lastPlayResponse ?? this.state.openGameResponse)?.freeRounds[0];
580
+ this.activateFreeRounds(freeRound);
494
581
  }
495
- };
582
+ });
583
+ this.on("rgs:freeRoundsError", async (error) => {
584
+ if (error.type === "timeout" && !this.config.skipErrors.includes("TIMEOUT")) this.send("error", {
585
+ messageKey: `Error.TIMEOUT`,
586
+ messageCode: "TIMEOUT",
587
+ type: "RELOAD"
588
+ });
589
+ if (error.type === "error" && !this.config.skipErrors.includes(error.data.code)) this.send("error", {
590
+ messageKey: `RgsError.${error.data.code}`,
591
+ messageCode: error.data.code,
592
+ type: require_errors.RgsErrorAction[error.data.code]
593
+ });
594
+ });
595
+ this.forwardMessages();
596
+ document.addEventListener("visibilitychange", async () => {
597
+ if (this.#wakeLock && document.visibilityState === "visible") await this.requestWakeLock();
598
+ });
496
599
  }
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);
600
+ get store() {
601
+ return this.#store;
512
602
  }
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
603
+ get state() {
604
+ const that = this;
605
+ return new Proxy({}, {
606
+ get(_target, prop) {
607
+ return Reflect.get(that.#store.getState(), prop);
578
608
  },
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 }
609
+ set(_target, prop, value) {
610
+ that.#store.setState((state) => ({
611
+ ...state,
612
+ [prop]: value
613
+ }));
614
+ return true;
615
+ }
616
+ });
617
+ }
618
+ checkCompatibility(sdkVersion) {
619
+ const { manifest } = this.#compatibility;
620
+ const currentEntry = manifest[0];
621
+ if (!currentEntry || !sdkVersion || currentEntry.minSdk === "newest") return {
622
+ status: "compatible",
623
+ sdkVersion
588
624
  };
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
625
+ if (compareSemver(sdkVersion, currentEntry.minSdk) >= 0) return {
626
+ status: "compatible",
627
+ sdkVersion,
628
+ eol: currentEntry.eol
601
629
  };
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
630
+ return {
631
+ status: "incompatible",
632
+ sdkVersion,
633
+ 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
634
  };
614
- case "UK": return {
615
- autoplay: { enabled: false },
616
- feature: {
617
- showFastPlay: false,
618
- allowQuickStop: false,
619
- showFeatureBuy: false
635
+ }
636
+ forwardMessages() {
637
+ this.forwardToPlugins("casino", [
638
+ "sdk:autoplay",
639
+ "sdk:bet",
640
+ "sdk:cashier",
641
+ "sdk:choice",
642
+ "sdk:close",
643
+ "sdk:error",
644
+ "sdk:history",
645
+ "sdk:loadEnd",
646
+ "sdk:loadProgress",
647
+ "sdk:loadStart",
648
+ "sdk:playReady",
649
+ "sdk:playCycleEnd",
650
+ "sdk:playCycleStart",
651
+ "sdk:playEnd",
652
+ "sdk:playStart",
653
+ "sdk:settings",
654
+ "sdk:openGame",
655
+ "sdk:configure",
656
+ "sdk:fullscreen"
657
+ ]);
658
+ this.forwardToPlugins("casino", ["rgs:playResponse", "rgs:openGameResponse"]);
659
+ this.forwardToPlugins("rgs", ["sdk:openGame", "sdk:history"]);
660
+ this.forwardToPlugins("rgs", [
661
+ "casino:play",
662
+ "casino:cashier",
663
+ "casino:close",
664
+ "casino:history",
665
+ "casino:getBalance"
666
+ ]);
667
+ this.forwardToPlugins("telemetry", [
668
+ "sdk:autoplay",
669
+ "sdk:error",
670
+ "sdk:playCycleStart",
671
+ "sdk:playCycleEnd",
672
+ "sdk:loadStart",
673
+ "sdk:loadProgress",
674
+ "sdk:loadEnd",
675
+ "sdk:telemetry.click",
676
+ "sdk:telemetry.orientationChange"
677
+ ]);
678
+ this.forwardToPlugins("telemetry", ["rgs:openGameResponse", "rgs:playResponse"]);
679
+ this.forwardToSdk([
680
+ "casino:balance",
681
+ "casino:bet",
682
+ "casino:choice",
683
+ "casino:freeze",
684
+ "casino:help",
685
+ "casino:paytable",
686
+ "casino:resume",
687
+ "casino:settings",
688
+ "casino:suspend",
689
+ "casino:unfreeze"
690
+ ]);
691
+ this.forwardToSdk([
692
+ "rgs:balance",
693
+ "rgs:openGameError",
694
+ "rgs:openGameResponse",
695
+ "rgs:playError",
696
+ "rgs:playResponse",
697
+ "rgs:realityCheck"
698
+ ]);
699
+ }
700
+ get config() {
701
+ const that = this;
702
+ return new Proxy({}, {
703
+ get(_target, prop) {
704
+ return Reflect.get(that.#config, prop);
620
705
  },
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
- };
706
+ set(_target, prop, value) {
707
+ return Reflect.set(that.#config, prop, value);
708
+ }
709
+ });
630
710
  }
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;
711
+ get sdkConfig() {
712
+ return this.#sdkConfigManager.config;
644
713
  }
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;
714
+ forwardToPlugins(targetDomain, messages) {
715
+ messages.forEach((message) => {
716
+ const onMessage = (event) => {
717
+ if (event.data.message !== `kalamba:${message}`) return;
718
+ logIn("wrapper:forwardToPlugins", targetDomain, event.data.message.replace(/^kalamba:/, ""), event.data.payload);
719
+ window.postMessage({
720
+ message: `kalamba:wrapper-${targetDomain}:${message.replace(/^(.+):/, "")}`,
721
+ payload: event.data.payload
722
+ });
723
+ };
724
+ window.addEventListener("message", onMessage);
725
+ });
656
726
  }
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;
727
+ forwardToSdk(messages) {
728
+ messages.forEach((message) => {
729
+ const onMessage = (event) => {
730
+ if (event.data.message !== `kalamba:${message}`) return;
731
+ logOut("wrapper:forwardToSdk", event.data.message.replace(/^kalamba:/, ""), event.data.payload);
732
+ this.#messagePort.postMessage({
733
+ message: `kalamba:wrapper:${message.replace(/^(.+):/, "")}`,
734
+ payload: event.data.payload
735
+ }, "*");
736
+ };
737
+ window.addEventListener("message", onMessage);
738
+ });
662
739
  }
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
740
+ on(message, listener, options) {
741
+ const onMessage = function onMessage(event) {
742
+ 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;
743
+ logIn("on", event.data.message.replace(/^kalamba:/, ""), event.data.payload);
744
+ listener(event.data.payload);
745
+ if (options?.once) window.removeEventListener("message", onMessage);
743
746
  };
747
+ window.addEventListener("message", onMessage);
748
+ return () => window.removeEventListener("message", onMessage);
744
749
  }
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
- }
750
+ send(message, ...[payload]) {
751
+ logOut("wrapper:send", message, payload);
752
+ window.postMessage({
753
+ message: `kalamba:wrapper:${message}`,
754
+ payload
755
+ });
756
+ this.#messagePort.postMessage({
757
+ message: `kalamba:wrapper:${message}`,
758
+ payload
759
+ }, "*");
760
+ }
761
+ injectPayload(...[source, payload]) {
762
+ this.#payloadInjectionManager.register(source, payload);
763
+ return () => this.#payloadInjectionManager.unregister(source);
764
+ }
765
+ activateFreeRounds(freeRound) {
766
+ if (!freeRound || this.state.freeRoundId) return;
767
+ this.store.setState((state) => ({
768
+ ...state,
769
+ freeRoundId: freeRound.id
770
+ }));
771
+ this.#payloadInjectionManager.register("freeRounds", { freeRoundId: freeRound.id });
772
+ this.send("legalBets", { [freeRound.conf.base]: [freeRound.conf.multiplier] });
773
+ this.send("bet", {
774
+ base: freeRound.conf.base,
775
+ multiplier: freeRound.conf.multiplier
776
+ });
777
+ }
778
+ handleFreeRounds(freeRound) {
779
+ if (!freeRound) return;
780
+ switch (freeRound.status) {
781
+ case "ACTIVE":
782
+ this.activateFreeRounds(freeRound);
783
+ break;
784
+ case "PENDING":
785
+ this.send("freeRoundsOffer", freeRound);
786
+ break;
787
+ case "FINISHED":
788
+ this.send("freeRoundsComplete", freeRound);
789
+ this.store.setState((state) => ({
790
+ ...state,
791
+ freeRoundId: void 0
792
+ }));
793
+ this.#payloadInjectionManager.unregister("freeRounds");
794
+ this.send("legalBets", this.state.openGameResponse.bet.available);
795
+ this.send("bet", this.state.openGameResponse.bet.default);
796
+ break;
797
+ default: break;
758
798
  }
759
- return this.mapUiConfig(config);
799
+ }
800
+ async requestWakeLock() {
801
+ try {
802
+ this.#wakeLock = await navigator.wakeLock.request("screen");
803
+ } catch {}
804
+ }
805
+ async releaseWakeLock() {
806
+ if (!this.#wakeLock) return;
807
+ await this.#wakeLock.release();
808
+ this.#wakeLock = void 0;
760
809
  }
761
810
  };
762
811