@goodgamestudios/cxf-webshop 7.0.0-qa.7 → 7.0.0-qa.8

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["<define:process>", "../node_modules/@goodgamestudios/cxf-ready/dist/index.js", "../node_modules/@goodgamestudios/game-alias/dist/index.js", "../node_modules/@goodgamestudios/cxf-events/dist/typings.js", "../src/index.ts", "../src/app.ts", "../src/globalState.ts", "../src/fetch.ts", "../src/helpers.ts", "../node_modules/@lukeed/uuid/dist/index.mjs", "../src/ArgumentNullError.ts", "../src/utils.ts", "../src/store.ts", "../src/messages/ShopMessageBus.ts", "../src/preFetch.ts", "../src/url.ts", "../src/handlers/postMessageHandlers.ts", "../src/dialog.ts", "../src/types.ts", "../src/handlers/reducers.ts", "../src/handlers/eventHandlers.ts", "../src/track.ts", "../src/handlers/pushHandlers.ts", "../src/config.ts"],
4
- "sourcesContent": ["", "\"use strict\";\n\n/**\n * A Promise that resolves when CXF has been initialized\n *\n * Usage:\n * const cxf = await require('cxf-ready')\n *\n */\nvar cxf = window && window.CXF;\nmodule.exports = new Promise(function (resolve, reject) {\n if (!window) {\n reject();\n }\n\n if (cxf) {\n return resolve(cxf);\n }\n\n window.addEventListener('cxf.initialized', function (event) {\n cxf = event.cxf;\n resolve(cxf);\n }, {\n capture: true,\n once: true,\n passive: true\n });\n});", "(function () {var b={};function f(r,$){return h(r)||g(r,$)||e()}function e(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}function g(r,$){var a=[],o=!0,e=!1,n=void 0;try{for(var c,i=r[Symbol.iterator]();!(o=(c=i.next()).done)&&(a.push(c.value),!$||a.length!==$);o=!0);}catch(m){e=!0,n=m}finally{try{o||null==i.return||i.return()}finally{if(e)throw n}}return a}function h(r){if(Array.isArray(r))return r}var i=0,j=1,k=2,c={1:[\"poker\",\"poker2\",\"poker\"],12:[\"empire\",\"castle\",\"em\"],15:[\"bigfarm\",\"ranch\",\"bf\"],16:[\"empirefourkingdoms\",\"fourkingdoms\",\"e4k\"],23:[\"legendsofhonor\",void 0,\"loh\"],41:[\"empiremillenniumwars\",void 0,\"emmw\"]},d=Object.entries(c).reduce(function(r,$){var a=f($,2),o=a[0],e=a[1];return o=parseInt(o,10),r[o]=o,e.forEach(function($){r[$]=o}),r},{}),l=function(r){r=r.toString().toLowerCase();var $=d[r];return $&&0|$},a=function(r,$){return($=d[$])&&c[$][r]},m=a.bind(null,i),n=a.bind(null,j),o=a.bind(null,k);b={id:l,name:m,codename:n,acronym:o};if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=b}else if(typeof define===\"function\"&&define.amd){define(function(){return b})}})();", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.CxfEvents = void 0;\nvar CxfEvents;\n(function (CxfEvents) {\n CxfEvents[\"Token\"] = \"cxf.token\";\n CxfEvents[\"Login\"] = \"cxf.login\";\n CxfEvents[\"Signup\"] = \"cxf.signup\";\n CxfEvents[\"GameEventUpdate\"] = \"cxf.gameEvent.update\";\n CxfEvents[\"GameEventAdd\"] = \"cxf.gameEvent.add\";\n CxfEvents[\"GameEventRemove\"] = \"cxf.gameEvent.remove\";\n CxfEvents[\"XpChanged\"] = \"cxf.xp.changed\";\n CxfEvents[\"LevelChanged\"] = \"cxf.level.changed\";\n CxfEvents[\"LegendLevelChanged\"] = \"cxf.legendLevel.changed\";\n CxfEvents[\"Push\"] = \"cxf.push\";\n CxfEvents[\"OpenIGS\"] = \"cxf.igs.open\";\n CxfEvents[\"JoinTempServer\"] = \"cxf.join.temp.server\";\n CxfEvents[\"Subscription\"] = \"cxf.subscription\";\n CxfEvents[\"RewardedAdStart\"] = \"cxf.rewardedad.start\";\n CxfEvents[\"RewardedAdDone\"] = \"cxf.rewardedad.done\";\n})(CxfEvents = exports.CxfEvents || (exports.CxfEvents = {}));\n", "import { app } from './app'\nimport { createConfig } from './config'\nimport { ICXF } from './cxf'\nimport { globalState } from './globalState'\nimport { loadScript, log } from './helpers'\nimport { defaultStore } from './store'\n\nloadScript(CANVAS_AGENT_URL)\n .then(() => {\n log('Canvas agent loaded')\n })\n .catch((e: any) => {\n console.error('Canvas agent load error', e)\n })\n// eslint-disable-next-line @typescript-eslint/no-var-requires, unicorn/prefer-top-level-await\nrequire('@goodgamestudios/cxf-ready').then((cxf: ICXF) => {\n // Initialize global state\n const config = createConfig(cxf.gameId)\n globalState.setConfig(config)\n globalState.setCxf(cxf)\n\n const store = defaultStore(cxf)\n globalState.setStore(store)\n\n // Start the app\n app()\n})\n", "import { globalState } from './globalState'\nimport { createPostMessageHandlers } from './handlers/postMessageHandlers'\nimport { createCxfReducers } from './handlers/reducers'\nimport { log, tryCatch } from './helpers'\n\nexport const app = (): void => {\n log('App has started')\n\n const postMessageHandlers = createPostMessageHandlers()\n const subscribeCxf = createCxfReducers()\n\n window.addEventListener(\n 'message',\n tryCatch(({ data }: MessageEvent) => {\n if (data) {\n const handler = postMessageHandlers[data.name]\n if (handler) {\n log('POST MESSAGE', data)\n handler(data.payload)\n }\n }\n })\n )\n\n // Subscribe to all CXF events\n for (const reducer of Object.values(subscribeCxf)) {\n reducer()\n }\n\n globalState.preResolveConfig()\n}\n", "import { Config } from './config'\nimport { ICXF } from './cxf'\nimport { fetchUnreadOfferNotificationsCount as defaultFetchUnreadOfferNotificationsCount } from './fetch'\nimport { IShopMessageBus } from './messages/IShopMessageBus'\nimport { ShopMessageBus } from './messages/ShopMessageBus'\nimport { preResolveConfig as defaultPreResolveConfig } from './preFetch'\nimport { IStore } from './store'\n\nexport interface IProvider<T> {\n get: () => T | undefined\n set: (value: T) => void\n}\n\nexport const entityProvider = <T>(initial?: T): IProvider<T> => {\n let element = initial\n return {\n get: () => element,\n set: (value) => {\n element = value\n },\n }\n}\n\nclass GlobalState {\n public config: Config | undefined = undefined\n public cxfProvider: IProvider<ICXF> = entityProvider()\n public dialogProvider: IProvider<HTMLElement> = entityProvider()\n public storeProvider: IProvider<IStore> = entityProvider()\n public shopMessageBus: IShopMessageBus = new ShopMessageBus()\n public fetchUnreadOfferNotificationsCount: () => Promise<void> | void = defaultFetchUnreadOfferNotificationsCount\n public preResolveConfig: () => void = defaultPreResolveConfig\n\n public setConfig(config: Config): void {\n this.config = config\n }\n\n public getConfig(): Config {\n if (this.config === undefined) {\n throw new Error('Config is not initialized')\n }\n return this.config\n }\n\n public setCxf(cxf: ICXF): void {\n this.cxfProvider.set(cxf)\n }\n\n public getCxf(): ICXF {\n const cxf = this.cxfProvider.get()\n if (!cxf) {\n throw new Error('CXF is not loaded')\n }\n return cxf\n }\n\n public setStore(store: IStore): void {\n this.storeProvider.set(store)\n }\n\n public getStore(): IStore {\n const store = this.storeProvider.get()\n if (!store) {\n throw new Error('Store is not initialized')\n }\n return store\n }\n\n public updateStore(data: Partial<IStore>): void {\n const currentStore = this.getStore()\n this.setStore({\n ...currentStore,\n ...data,\n })\n }\n}\n\nexport const globalState = new GlobalState()\n", "import { globalState } from './globalState'\nimport { log, logError } from './helpers'\nimport { criteriaSelector } from './store'\n\nexport const fetchUnreadOfferNotificationsCount = async (): Promise<void> => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n\n if (config.LEGEND_LEVEL_IS_USED && store.legendLevel === undefined) {\n log('Skip fetchUnreadOfferNotificationsCount due to legendLevel is undefined')\n return\n }\n\n log('fetchUnreadOfferNotificationsCount')\n\n if (store.token && store.unreadOfferNotifsCountUrl) {\n let unreadCount = 0\n const headers = {\n Authorization: `Bearer ${store.token}`,\n }\n const url = store.unreadOfferNotifsCountUrl\n .replace('{locale}', store.language || '')\n .replace('{zoneId}', store.zoneId || '')\n .replace('{criteria}', encodeURIComponent(JSON.stringify(criteriaSelector(store))))\n try {\n log('fetch', url)\n const resp = await fetch(url, {\n headers,\n })\n if (resp.ok) {\n const data = await resp.json()\n unreadCount = Number(data?.notifCount)\n }\n } catch (error) {\n logError(`cannot fetch ${url}`, error)\n }\n log('setUnseenOffersCounter', unreadCount)\n store.gameApi.invokeFn('setUnseenOffersCounter', unreadCount).catch((error) => {\n logError('setUnseenOffersCounter error:', error)\n })\n }\n}\n", "import { v4 as uuidv4 } from '@lukeed/uuid'\nimport debug from 'debug'\nimport { ArgumentNullError } from './ArgumentNullError'\nimport { IDictionary } from './common'\nimport { ICXF } from './cxf'\nimport { globalState } from './globalState'\nimport { IStore } from './store'\nimport { parentDomain, timeout } from './utils'\n\nexport interface GNIP {\n gameId: string\n networkId: string\n instanceId: string\n playerId: string\n}\n\nexport function decodePing(value: string): GNIP {\n const [gameId, networkId, instanceId, playerId] = value.split('-')\n return { gameId, networkId, instanceId, playerId }\n}\n\nexport function encodePing({ gameId, networkId, instanceId, playerId }: GNIP): string {\n return [gameId, networkId, instanceId, playerId].join('-')\n}\n\nexport function formatQueryString(object: IDictionary<string | number | undefined | null>) {\n return Object.entries(object)\n .filter(([key, value]) => value !== null && value !== undefined)\n .map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)\n .join('&')\n}\n\nexport const getTokenAndLanguage = (store: IStore) => {\n const { playerId, token, zoneId, language } = store\n if (!playerId) {\n throw new ArgumentNullError('playerId', playerId)\n }\n return {\n token,\n zoneId,\n locale: language,\n ping: encodePing({ ...store, playerId }),\n }\n}\n\nexport const getPing = (): string => {\n const store = globalState.getStore()\n const { playerId } = store\n if (!playerId) {\n throw new ArgumentNullError('playerId', playerId)\n }\n return encodePing({ ...store, playerId })\n}\n\nexport const loadScript = (src: string) =>\n new Promise((resolve, reject) => {\n const script = document.createElement('script')\n script.type = 'text/javascript'\n script.type = 'module'\n script.async = true\n script.src = src\n script.onload = resolve\n script.onerror = reject\n document.body.append(script)\n })\n\nexport const tryCatch = <T extends (...args: any[]) => any>(fn: T): T =>\n ((...args: any[]) => {\n try {\n return fn(...args)\n } catch (error) {\n logError(error)\n throwCxfError(error as Error)\n }\n }) as T\n\nexport const throwCxfError = (e: Error): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_ERROR, e)\n}\n\nexport const isArgumentNullError = (e: Error): e is ArgumentNullError => e.name === 'ArgumentNullError'\n\n// eslint-disable-next-line @typescript-eslint/no-var-requires\nexport const loadCxf = timeout(() => require('@goodgamestudios/cxf-ready') as Promise<ICXF>, 10_000)\n\n/*\n * To show CXF-WEBSHOP debug messages just run in a browser console\n * localStorage.debug = 'CXF-WEBSHOP:*'\n */\n// const logger = debug('CXF-WEBSHOP')\n// export const log = (argument0: any, ...rest: any[]): void => logger(argument0, ...rest)\n\nexport const logError = (...arguments_: any[]): void =>\n // tslint:disable-next-line:no-console\n console.error('%c CXF-WEBSHOP ->', 'background: #ff0000; color: #fff', ...arguments_)\n\nexport const log = (...args: any[]): void => {\n console.log('%c CXF-WEBSHOP ->', 'background: pink; color: #bada55, font-size:20px', ...args)\n}\n\nexport const createSessionId = () => uuidv4()\n\nexport const getDomain = (referrer: string): string | undefined => {\n return parentDomain(referrer)\n}\n\nexport const ggsGetQueryParams = (): string => {\n return ggsGetQueryParameters()\n}\n\nexport const ggsGetReferrerValue = (): string => {\n return ggsGetReferrer()\n}\n", "var IDX=256, HEX=[], BUFFER;\nwhile (IDX--) HEX[IDX] = (IDX + 256).toString(16).substring(1);\n\nexport function v4() {\n\tvar i=0, num, out='';\n\n\tif (!BUFFER || ((IDX + 16) > 256)) {\n\t\tBUFFER = Array(i=256);\n\t\twhile (i--) BUFFER[i] = 256 * Math.random() | 0;\n\t\ti = IDX = 0;\n\t}\n\n\tfor (; i < 16; i++) {\n\t\tnum = BUFFER[IDX + i];\n\t\tif (i==6) out += HEX[num & 15 | 64];\n\t\telse if (i==8) out += HEX[num & 63 | 128];\n\t\telse out += HEX[num];\n\n\t\tif (i & 1 && i > 1 && i < 11) out += '-';\n\t}\n\n\tIDX++;\n\treturn out;\n}\n", "export class ArgumentNullError extends Error {\n constructor(parameterName: string, parameterValue: any) {\n super(`${parameterName} has null value: ${parameterValue}`)\n this.name = 'ArgumentNullError'\n }\n}\n", "import { ArgumentNullError } from './ArgumentNullError'\nimport { AnyVoidFn as AnyVoidFunction, IDictionary, PromiseFn as PromiseFunction } from './common'\n\nconst startTimer = (function_: AnyVoidFunction, time: number) => {\n const id = setTimeout(function_, time)\n return () => clearTimeout(id)\n}\n\nexport const timeout =\n <A, B>(function_: PromiseFunction<A, B>, time: number): PromiseFunction<A, B> =>\n (a) => {\n return new Promise((resolve, reject) => {\n const stopTimer = startTimer(() => {\n reject(new Error(`Timeout has exceeded ${time}`))\n }, time)\n\n function_(a)\n .then((value) => {\n resolve(value)\n stopTimer()\n })\n .catch(reject)\n })\n }\n\nexport const validateForNull = (properties: IDictionary<any>) => {\n for (const key of Object.keys(properties)) {\n const value = properties[key]\n if (value === undefined || value === null || Number.isNaN(value)) {\n throw new ArgumentNullError(key, value)\n }\n }\n}\n\nexport function msToSec(value: number): number {\n return Math.floor(value / 1000)\n}\n\nexport function decodePayload<T>(data: string): T {\n return JSON.parse(atob(data))\n}\n\nexport function diffHours(d1: Date, d2: Date): number {\n const diff = (d1.getTime() - d2.getTime()) / 1000\n return Math.abs(Math.round(diff / (60 * 60)))\n}\n\nexport function timestampToHours(value: number): number {\n return Math.abs(Math.round(value / 1000 / (60 * 60)))\n}\n\nexport const parentDomain = (referrer: string) => {\n const matches = referrer.match(/^https?:\\/\\/([^#/?]+)(?:[#/?]|$)/i)\n if (!matches || !matches[1]) {\n return\n }\n\n const result = matches[1].match(/[^.]+\\.[^.]+$/)\n if (!result) {\n return\n }\n\n return result[0]\n}\n", "import { CountryCode, IGameEvent } from '@goodgamestudios/cxf-events'\nimport { IDictionary } from './common'\nimport { ICXF, IGameApi } from './cxf'\n\nexport interface IStore {\n playerId?: string\n instanceId: string\n networkId: string\n gameId: string\n gameApi: IGameApi\n language?: string\n token?: string\n zoneId?: string\n xp: number\n level: number\n gameEvents: IGameEvent[]\n countryCode: CountryCode\n lastPurchaseTab: string\n legendLevel?: number\n subscriptionDisabled: boolean\n isTempServer: boolean\n customizationSuffix: string\n resolvedCustomizationUrl?: string\n sourceId: string\n unreadOfferNotifsCountUrl: string\n adStatus?: Record<string, any>\n}\n\nexport function defaultStore(cxf: ICXF): IStore {\n return {\n playerId: cxf.playerId,\n instanceId: cxf.instanceId,\n networkId: cxf.networkId,\n gameId: cxf.gameId,\n gameApi: cxf.gameApi,\n language: cxf.language,\n token: cxf.token,\n zoneId: cxf.zoneId,\n gameEvents: [],\n xp: 0,\n level: 0,\n legendLevel: undefined,\n countryCode: '',\n lastPurchaseTab: '',\n subscriptionDisabled: false,\n isTempServer: false,\n customizationSuffix: '',\n resolvedCustomizationUrl: '',\n sourceId: 'unknown',\n unreadOfferNotifsCountUrl: '',\n }\n}\n\nexport function criteriaSelector({ legendLevel, level }: IStore): IDictionary<any> {\n return {\n legendLevel,\n level,\n }\n}\n", "import { IShopMessageBus } from './IShopMessageBus'\n\nexport class ShopMessageBus implements IShopMessageBus {\n public post(message: any) {\n const element = document.querySelector('#dialog') as HTMLIFrameElement\n if (element && element.tagName.toLocaleLowerCase() === 'iframe') {\n element.contentWindow?.postMessage(message, '*')\n }\n }\n}\n", "import { globalState } from './globalState'\nimport { log } from './helpers'\nimport { createCustomizationUrl } from './url'\n\ntype LemonstandConfig = {\n css: string\n js: string\n unreadOfferNotifsCountUrl: string\n}\n\nexport const preResolveConfig = (): void => {\n if (typeof fetch !== 'function') {\n return\n }\n const originalCustomUrl = createCustomizationUrl()\n let resolvedUrl = ''\n fetch(originalCustomUrl)\n .then((resp) => {\n const currentCustomUrl = createCustomizationUrl()\n if (currentCustomUrl === originalCustomUrl) {\n // this is the latest, we keep it\n resolvedUrl = resp.url\n log('parsed customization url is ' + resolvedUrl)\n globalState.updateStore({\n resolvedCustomizationUrl: resolvedUrl,\n })\n return resp.json()\n }\n })\n .then((config: LemonstandConfig | undefined) => {\n if (config) {\n globalState.updateStore({\n unreadOfferNotifsCountUrl: config.unreadOfferNotifsCountUrl || '',\n })\n }\n })\n}\n", "import { acronym } from '@goodgamestudios/game-alias'\nimport { globalState } from './globalState'\nimport {\n createSessionId,\n formatQueryString,\n getDomain,\n getTokenAndLanguage,\n ggsGetQueryParams,\n ggsGetReferrerValue,\n} from './helpers'\nimport { criteriaSelector } from './store'\nimport { ICreateCatalogUrlProperties } from './types'\nimport { validateForNull } from './utils'\n\nexport const createIframeUrl = ({ page, route, sid, config: igsConfig = {} }: ICreateCatalogUrlProperties): string => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n\n const { token, zoneId, locale } = getTokenAndLanguage(store)\n const parameters = {\n token,\n zoneId,\n locale,\n sid: sid || createSessionId(),\n }\n\n if (Object.keys(igsConfig).length > 0) {\n // @ts-ignore\n parameters.config = JSON.stringify(igsConfig)\n }\n\n validateForNull(parameters)\n\n const urlParameters = new URLSearchParams(ggsGetQueryParams())\n const queryParameters = {\n ...parameters,\n 'lemonstand.customization.url': store.resolvedCustomizationUrl || createCustomizationUrl(),\n domain: getDomain(ggsGetReferrerValue()),\n websiteId: urlParameters.get('w'),\n criteria: JSON.stringify(criteriaSelector(store)),\n level: store.level,\n }\n // <editor-fold desc=\"SPIL integration\">\n /* In case of SPIL integration the game can be launched\n with additional params 'network=xx&usekeybaselogin=false'\n in this case it needs to pass the network parameter to iframe url */\n const network = urlParameters.get('network')\n if (urlParameters.get('usekeybaselogin') === 'false' && Number(network) > 0) {\n // @ts-ignore\n queryParameters.network = network\n }\n\n if (store.adStatus?.areBannersAvailable) {\n // @ts-ignore\n queryParameters.ads = true\n }\n // </editor-fold>\n\n return `${config.BASE_URL}/?${formatQueryString(queryParameters)}${page ? `#${page}` : ''}${\n route ? `--${route}--${Date.now()}` : ''\n }`\n}\n\nexport const createCustomizationUrl = (): string => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n const cxf = globalState.getCxf()\n\n const configBase = acronym(store.gameId)\n const configVariance = store.customizationSuffix ? `-${store.customizationSuffix}` : ''\n const configBranch = `${configBase}${configVariance}`\n\n if (cxf.env === 'test') {\n const configVersion = new URLSearchParams(window.location.search).get('configGGS')\n if (configVersion !== null) {\n return config.CUSTOMIZATION_URL_TEMPLATE.replace('{0}', configBranch).replace('{1}', configVersion)\n }\n }\n return config.CUSTOMIZATION_URL.replace('{0}', configBranch)\n}\n", "import { createDialog } from '../dialog'\nimport { fetchUnreadOfferNotificationsCount } from '../fetch'\nimport { globalState } from '../globalState'\nimport { createSessionId, log } from '../helpers'\nimport { Handlers, WebshopEvents } from '../types'\nimport { createIframeUrl } from '../url'\n\nexport const onSalesPageOpen = (): void => {\n log('OnOpenSalesOffersPage')\n const sid = createSessionId()\n const url = createIframeUrl({\n page: 'sale-offers',\n sid,\n })\n createDialog(url)\n}\n\nexport const onLemonstandClose = (): void => {\n const store = globalState.getStore()\n log('onLemonstandClose', store)\n fetchUnreadOfferNotificationsCount()\n}\n\nexport const createPostMessageHandlers = (): Handlers => {\n return {\n [WebshopEvents.CXF_OPEN_SALES_MSG]: onSalesPageOpen,\n [WebshopEvents.CXF_DIALOG_CLOSE]: onLemonstandClose,\n }\n}\n", "import { globalState } from './globalState'\n\nexport const removeDialog = (): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_DIALOG_CLOSE)\n}\n\nexport const createDialog = (url: string): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_DIALOG_OPEN, url)\n}\n", "import { IDictionary } from './common'\n\n// ============================================================================\n// Handler Types\n// ============================================================================\n\n/**\n * Generic handler function type\n */\nexport type Handler = (...arguments_: any[]) => void\n\n/**\n * Dictionary of handler functions\n */\nexport type Handlers = IDictionary<Handler>\n\n/**\n * Push notification handler function type\n */\nexport type PushHandler = (...arguments_: any[]) => void\n\n/**\n * Dictionary of push handlers\n */\nexport type PushHandlers = IDictionary<PushHandler>\n\n/**\n * CXF event reducers/subscribers\n */\nexport type CxfReducers = IDictionary<() => void>\n\n// ============================================================================\n// Event Payload Types\n// ============================================================================\n\n/**\n * Payload for opening IGS (In-Game Shop)\n */\nexport interface OpenIGSPayload {\n page?: string\n route?: string\n sourceId?: string\n config?: Record<string, any>\n}\n\n/**\n * Payload for reward events\n */\nexport interface OnRewardProperties {\n successUrl: string\n offerId: string\n sid: string\n page: string\n config?: Record<string, any>\n}\n\n/**\n * Payload for Lemonstand category update events\n */\nexport interface OnLemonstandCategoryUpdateProperties {\n target: string\n action: string\n data: any\n}\n\n/**\n * Payload for Lemonstand notification creation events\n */\nexport interface OnLemonstandNotificationsCreatedProperties {\n notifCount: number\n}\n\n// ============================================================================\n// Tracking Types\n// ============================================================================\n\n/**\n * Generic tracking payload\n */\nexport interface ITrackPayload extends IDictionary<string | number> {\n eventId: number\n}\n\n/**\n * Properties for Ranch webshop call tracking\n */\nexport interface IRanchWebShopCallProperties extends ITrackPayload {\n gameId: number\n playerId: number\n instanceId: number\n zoneId: number\n networkId: number\n sessionId: string\n date: number\n unixtimeMS: number\n sourceId: string\n}\n\n// ============================================================================\n// URL Types\n// ============================================================================\n\n/**\n * Properties for creating catalog/iframe URLs\n */\nexport interface ICreateCatalogUrlProperties {\n sid?: string\n page?: string\n route?: string\n config?: Record<string, any>\n}\n\n// ============================================================================\n// Webshop Events Enum\n// ============================================================================\n\n/**\n * Webshop event names\n */\nexport enum WebshopEvents {\n CXF_DIALOG_CLOSE = 'cxf.dialog.close',\n CXF_NATIVE_SUBSCRIPTION_ENABLE = 'cxf.native.subscription.enable',\n CXF_OPEN_SALES_MSG = 'cxf.webshop.sales.open',\n CXF_SET_CUSTOMIZATION_SUFFIX = 'cxf.set.customization.surfix',\n CXF_JOIN_TEMP_SERVER = 'cxf.join.temp.server',\n LEMONSTAND_CATEGORY_UPDATE = 'lemonstand.category.update',\n LEMONSTAND_NOTIFICATIONS_CREATED = 'lemonstand.notifs.created',\n}\n\n// CANVAS Agent\n//\n// Agent Props workaround; don't want to make Agent as a package\nexport enum CanvasAppEvent {\n CHECKOUT = 'checkout',\n CLOSE = 'close',\n COLLECT = 'collect',\n ERROR = 'error',\n EXPIRED = 'expired',\n INIT = 'init',\n NATIVE_CATALOG_REQUEST = 'native_catalog_request',\n NATIVE_CHECKOUT_REQUEST = 'native_checkout_request',\n NAVIGATE = 'navigate',\n READY = 'ready',\n RESIZE = 'resize',\n}\n\nexport type CanvasEventPostMessage = {\n eventType: CanvasAppEvent\n [key: string]: any\n}\n\nexport type AgentProps = {\n canvasAppId: string\n eventListener?: (event: CanvasEventPostMessage) => void\n frameParent?: HTMLElement\n fullScreen?: boolean\n locale?: string\n sessionId?: string\n token: string\n customContext?: Record<string, any>\n authClientId?: any\n debug?: boolean\n}\n\n\n", "import { CxfEvents, IGameEvent, ILoginData } from '@goodgamestudios/cxf-events'\nimport { IPushMessageData } from '../common'\nimport { globalState } from '../globalState'\nimport { log, tryCatch } from '../helpers'\nimport { CxfReducers, WebshopEvents } from '../types'\nimport { onOpen } from './eventHandlers'\nimport { createPushHandlers } from './pushHandlers'\n\nexport const subscribeCommonCxfEvents = (): void => {\n const cxf = globalState.getCxf()\n const pushHandlers = createPushHandlers()\n\n cxf.on(CxfEvents.OpenIGS, tryCatch(onOpen))\n cxf.on(\n CxfEvents.Push,\n tryCatch(({ id, payload }: IPushMessageData) => {\n log('cxf.push', id, payload)\n const handler = pushHandlers[id]\n handler && handler(payload)\n })\n )\n}\n\nexport const subscribeToGameEvents = (): void => {\n const cxf = globalState.getCxf()\n\n cxf.on(CxfEvents.Login, (e: ILoginData) => {\n const { gameEvents } = globalState.getStore()\n const { gameEvents: eventsInLogin } = e\n // eslint-disable-next-line unicorn/consistent-function-scoping\n const eventNotExistPredicateGenerator = (existingEvents: IGameEvent[]) => (event: IGameEvent) =>\n !existingEvents.map((event_) => event_.type).includes(event.type)\n globalState.updateStore({\n ...e,\n gameEvents: [...gameEvents.filter(eventNotExistPredicateGenerator(eventsInLogin)), ...eventsInLogin],\n })\n log(CxfEvents.Login, 'reducer', globalState.getStore())\n globalState.fetchUnreadOfferNotificationsCount()\n })\n\n cxf.on(CxfEvents.GameEventUpdate, (e: IGameEvent[]) => {\n log(CxfEvents.GameEventUpdate, e)\n const store = globalState.getStore()\n const updatedEventTypes = new Set(e.map((event) => event.type))\n globalState.updateStore({\n gameEvents: [...store.gameEvents.filter((event) => !updatedEventTypes.has(event.type)), ...e],\n })\n log(CxfEvents.GameEventUpdate, 'reducer', globalState.getStore())\n })\n\n cxf.on(CxfEvents.GameEventAdd, (e: IGameEvent) => {\n log(CxfEvents.GameEventAdd, e)\n const store = globalState.getStore()\n globalState.updateStore({\n gameEvents: [...store.gameEvents, e],\n })\n log(CxfEvents.GameEventAdd, 'reducer', globalState.getStore())\n })\n\n cxf.on(CxfEvents.GameEventRemove, (e: number) => {\n log(CxfEvents.GameEventRemove, e)\n const store = globalState.getStore()\n globalState.updateStore({\n gameEvents: store.gameEvents.filter(({ type }) => type !== e),\n })\n log(CxfEvents.GameEventRemove, 'reducer', globalState.getStore())\n })\n\n cxf.on(CxfEvents.LevelChanged, (level: number) => {\n log(CxfEvents.LevelChanged, level)\n globalState.updateStore({\n level,\n })\n log(CxfEvents.LevelChanged, 'reducer', globalState.getStore())\n globalState.fetchUnreadOfferNotificationsCount()\n })\n\n cxf.on(CxfEvents.LegendLevelChanged, (legendLevel: number) => {\n log(CxfEvents.LegendLevelChanged, `legendLevel: ${legendLevel}`)\n globalState.updateStore({\n legendLevel,\n })\n const store = globalState.getStore()\n log(CxfEvents.LegendLevelChanged, 'reducer', store)\n globalState.fetchUnreadOfferNotificationsCount()\n })\n}\n\n/*\n In E4K this event is used to control the subscriptions tab\n This is deprecated, as specific config values/tab visibility can be controlled using Lemonstands `config` query param\n*/\nexport const nativeSubscriptionEnableReducer = (): void => {\n const cxf = globalState.getCxf()\n cxf.on(WebshopEvents.CXF_NATIVE_SUBSCRIPTION_ENABLE, (enabled: 0 | 1) => {\n globalState.updateStore({\n subscriptionDisabled: enabled === 0,\n customizationSuffix: enabled === 0 ? 'no-subscription' : '',\n })\n globalState.preResolveConfig()\n })\n}\n\n/*\n The config file suffix (e.g. \"em.json\" + \"mobile\" -> \"em-mobile.json\") can be set through an event\n Used by MS and BFW integration pages\n*/\nexport const setCustomizationSuffixReducer = (): void => {\n const cxf = globalState.getCxf()\n cxf.on(WebshopEvents.CXF_SET_CUSTOMIZATION_SUFFIX, (suffix: string) => {\n const store = globalState.getStore()\n if (suffix === store.customizationSuffix) {\n return\n }\n globalState.updateStore({\n customizationSuffix: suffix || '',\n })\n globalState.preResolveConfig()\n })\n}\n\n/*\n Information about whether ads are enabled or not is retrieved externally from `cxf-ad-banners`\n and stored to be passed to IGS\n*/\nexport const receiveAdStatusReducer = (): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.on(config.CXF_AD_STATUS, ({ areBannersAvailable, bannersDetails }: Record<string, any>) => {\n globalState.updateStore({\n adStatus: {\n areBannersAvailable,\n bannersDetails,\n },\n })\n })\n}\n\n/*\n Use a dedicated config variant on temp servers\n*/\nexport const tempServerContextReducer = (): void => {\n const cxf = globalState.getCxf()\n cxf.on(WebshopEvents.CXF_JOIN_TEMP_SERVER, (isTemp: 0 | 1) => {\n const store = globalState.getStore()\n globalState.updateStore({\n isTempServer: Boolean(isTemp),\n })\n\n // ensure suffix is not already set by the environment, e.g. BFW or MS integration\n // TODO: fully migrate client detection into `cxf-webshop`\n if (store.customizationSuffix === '' || store.customizationSuffix === 'temp') {\n globalState.updateStore({\n customizationSuffix: isTemp ? 'temp' : '',\n })\n globalState.preResolveConfig()\n }\n })\n}\n\nexport const createCxfReducers = (): CxfReducers => {\n return {\n common: subscribeCommonCxfEvents,\n gameEvents: subscribeToGameEvents,\n receiveAdStatus: receiveAdStatusReducer,\n tempServerContext: tempServerContextReducer,\n nativeSubscription: nativeSubscriptionEnableReducer,\n setCustomizationSuffix: setCustomizationSuffixReducer,\n }\n}\n", "import { createDialog } from '../dialog'\nimport { globalState } from '../globalState'\nimport { createSessionId, log } from '../helpers'\nimport { trackOpenAction } from '../track'\nimport { OpenIGSPayload } from '../types'\nimport { createIframeUrl } from '../url'\n\nexport const onOpen = (payload: OpenIGSPayload = {}): void => {\n log('OnOpen payload:', payload)\n\n if (payload.sourceId) {\n globalState.updateStore({\n sourceId: payload.sourceId,\n })\n }\n\n const store = globalState.getStore()\n const sid = createSessionId()\n trackOpenAction(sid)\n const url = createIframeUrl({\n sid,\n page: store.lastPurchaseTab,\n ...payload,\n })\n createDialog(url)\n\n setTimeout(() => {\n // remove unread offer notifications counter on the IGS button while opening the IGS\n // small delay makes UX a bit better due to it happens after IGS initialization is started\n store.gameApi.invokeFn('setUnseenOffersCounter', 0)\n }, 1000)\n}\n", "import { globalState } from './globalState'\nimport { getTokenAndLanguage } from './helpers'\nimport { IRanchWebShopCallProperties, ITrackPayload } from './types'\nimport { msToSec } from './utils'\n\nexport const trackOpenAction = (sid: string): void => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n const now = Date.now()\n const parameters = getTokenAndLanguage(store)\n const { zoneId } = parameters\n const sessionId = sid\n const { playerId, gameId, networkId, instanceId, sourceId } = store\n trackAction({\n eventId: config.WEB_SHOP_CALL_TRACK_ID,\n date: msToSec(now),\n unixtimeMS: now,\n sessionId,\n zoneId: zoneId ? Number.parseInt(zoneId, 10) : undefined,\n playerId: Number.parseInt(playerId as string, 10),\n gameId: Number.parseInt(gameId, 10),\n networkId: Number.parseInt(networkId, 10),\n instanceId: Number.parseInt(instanceId, 10),\n sourceId,\n } as IRanchWebShopCallProperties)\n}\n\nexport const trackAction = (payload: ITrackPayload): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_TRACK_MSG, payload)\n}\n", "import { createDialog } from '../dialog'\nimport { fetchUnreadOfferNotificationsCount } from '../fetch'\nimport { globalState } from '../globalState'\nimport { log, logError } from '../helpers'\nimport { trackOpenAction } from '../track'\nimport {\n OnLemonstandCategoryUpdateProperties,\n OnLemonstandNotificationsCreatedProperties,\n OnRewardProperties,\n PushHandlers,\n WebshopEvents,\n} from '../types'\nimport { createIframeUrl } from '../url'\n\nexport const onReward = (payload: OnRewardProperties): void => {\n log('OnReward: offerId', payload.offerId, 'Page', payload.page)\n const url = createIframeUrl({\n route: encodeURIComponent(payload.successUrl),\n ...payload,\n })\n globalState.updateStore({\n lastPurchaseTab: payload.page,\n sourceId: 'successfulPayoutReward',\n })\n trackOpenAction(payload.sid)\n\n createDialog(url)\n}\n\nexport const onLemonstandCategoryUpdate = ({ target, action, data }: OnLemonstandCategoryUpdateProperties): void => {\n globalState.shopMessageBus.post({\n eventName: WebshopEvents.LEMONSTAND_CATEGORY_UPDATE,\n target,\n data: { action, data },\n })\n}\n\n/**\n * this handler works in case of offer notification activation and deactivation\n */\nexport const onLemonstandNotificationsCreated = ({ notifCount }: OnLemonstandNotificationsCreatedProperties): void => {\n log('onLemonstandNotificationsCreated -> notifCount:', notifCount)\n const store = globalState.getStore()\n if (notifCount > 0) {\n // random delay in range 1-120 sec on LIVE, and 1-15 sec on TEST\n const randomDelayMs = Math.floor(Math.random() * (Number(store.networkId) < 250 ? 120_000 : 15_000))\n setTimeout(() => {\n fetchUnreadOfferNotificationsCount()\n }, randomDelayMs)\n } else {\n store.gameApi.invokeFn('setUnseenOffersCounter', 0).catch((error) => {\n logError('setUnseenOffersCounter error:', error)\n })\n }\n}\n\nexport const createPushHandlers = (): PushHandlers => {\n return {\n reward: onReward,\n [WebshopEvents.LEMONSTAND_CATEGORY_UPDATE]: onLemonstandCategoryUpdate,\n [WebshopEvents.LEMONSTAND_NOTIFICATIONS_CREATED]: onLemonstandNotificationsCreated,\n }\n}\n", "import { CxfEvents } from '@goodgamestudios/cxf-events'\nimport { IDictionary } from './common'\nimport { WebshopEvents } from './types'\n\nexport type GameSpecificConfig = {\n LEGEND_LEVEL_IS_USED: boolean\n}\n\nconst COMMON_CONFIG = {\n CUSTOMIZATION_URL: process.env.CUSTOMIZATION_URL as string,\n CUSTOMIZATION_URL_TEMPLATE: process.env.CUSTOMIZATION_URL_TEMPLATE as string,\n BASE_URL: process.env.BASE_URL as string,\n CXF_DIALOG_OPEN: 'cxf.dialog.open',\n CXF_DIALOG_CLOSE: 'cxf.dialog.close',\n CXF_TRACK_MSG: 'cxf.tracking.message',\n CXF_BTN_CLICK_MSG: CxfEvents.OpenIGS,\n CXF_OPEN_SALES_MSG: WebshopEvents.CXF_OPEN_SALES_MSG,\n WEB_SHOP_CALL_TRACK_ID: 1181,\n CXF_ERROR: 'cxf.error',\n CXF_PUSH: 'cxf.push',\n CXF_AD_STATUS: 'cxf.adBanner.status',\n}\n\nexport type Config = typeof COMMON_CONFIG & GameSpecificConfig\n\nconst GAME_SPECIFIC_CONFIG: IDictionary<GameSpecificConfig> = {\n 12: {\n LEGEND_LEVEL_IS_USED: true,\n },\n 15: {\n LEGEND_LEVEL_IS_USED: false,\n },\n 16: {\n LEGEND_LEVEL_IS_USED: true,\n },\n}\n\nexport const createConfig = (gameId: string): Config => {\n return {\n ...COMMON_CONFIG,\n ...GAME_SPECIFIC_CONFIG[gameId],\n }\n}\n"],
4
+ "sourcesContent": ["", "\"use strict\";\n\n/**\n * A Promise that resolves when CXF has been initialized\n *\n * Usage:\n * const cxf = await require('cxf-ready')\n *\n */\nvar cxf = window && window.CXF;\nmodule.exports = new Promise(function (resolve, reject) {\n if (!window) {\n reject();\n }\n\n if (cxf) {\n return resolve(cxf);\n }\n\n window.addEventListener('cxf.initialized', function (event) {\n cxf = event.cxf;\n resolve(cxf);\n }, {\n capture: true,\n once: true,\n passive: true\n });\n});", "(function () {var b={};function f(r,$){return h(r)||g(r,$)||e()}function e(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}function g(r,$){var a=[],o=!0,e=!1,n=void 0;try{for(var c,i=r[Symbol.iterator]();!(o=(c=i.next()).done)&&(a.push(c.value),!$||a.length!==$);o=!0);}catch(m){e=!0,n=m}finally{try{o||null==i.return||i.return()}finally{if(e)throw n}}return a}function h(r){if(Array.isArray(r))return r}var i=0,j=1,k=2,c={1:[\"poker\",\"poker2\",\"poker\"],12:[\"empire\",\"castle\",\"em\"],15:[\"bigfarm\",\"ranch\",\"bf\"],16:[\"empirefourkingdoms\",\"fourkingdoms\",\"e4k\"],23:[\"legendsofhonor\",void 0,\"loh\"],41:[\"empiremillenniumwars\",void 0,\"emmw\"]},d=Object.entries(c).reduce(function(r,$){var a=f($,2),o=a[0],e=a[1];return o=parseInt(o,10),r[o]=o,e.forEach(function($){r[$]=o}),r},{}),l=function(r){r=r.toString().toLowerCase();var $=d[r];return $&&0|$},a=function(r,$){return($=d[$])&&c[$][r]},m=a.bind(null,i),n=a.bind(null,j),o=a.bind(null,k);b={id:l,name:m,codename:n,acronym:o};if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=b}else if(typeof define===\"function\"&&define.amd){define(function(){return b})}})();", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.CxfEvents = void 0;\nvar CxfEvents;\n(function (CxfEvents) {\n CxfEvents[\"Token\"] = \"cxf.token\";\n CxfEvents[\"Login\"] = \"cxf.login\";\n CxfEvents[\"Signup\"] = \"cxf.signup\";\n CxfEvents[\"GameEventUpdate\"] = \"cxf.gameEvent.update\";\n CxfEvents[\"GameEventAdd\"] = \"cxf.gameEvent.add\";\n CxfEvents[\"GameEventRemove\"] = \"cxf.gameEvent.remove\";\n CxfEvents[\"XpChanged\"] = \"cxf.xp.changed\";\n CxfEvents[\"LevelChanged\"] = \"cxf.level.changed\";\n CxfEvents[\"LegendLevelChanged\"] = \"cxf.legendLevel.changed\";\n CxfEvents[\"Push\"] = \"cxf.push\";\n CxfEvents[\"OpenIGS\"] = \"cxf.igs.open\";\n CxfEvents[\"JoinTempServer\"] = \"cxf.join.temp.server\";\n CxfEvents[\"Subscription\"] = \"cxf.subscription\";\n CxfEvents[\"RewardedAdStart\"] = \"cxf.rewardedad.start\";\n CxfEvents[\"RewardedAdDone\"] = \"cxf.rewardedad.done\";\n})(CxfEvents = exports.CxfEvents || (exports.CxfEvents = {}));\n", "import { app } from './app'\nimport { createConfig } from './config'\nimport { ICXF } from './cxf'\nimport { globalState } from './globalState'\nimport { loadScript, log } from './helpers'\nimport { defaultStore } from './store'\n\nloadScript(CANVAS_AGENT_URL)\n .then(() => {\n log('Canvas agent loaded')\n })\n .catch((e: any) => {\n console.error('Canvas agent load error', e)\n })\n// eslint-disable-next-line @typescript-eslint/no-var-requires, unicorn/prefer-top-level-await\nrequire('@goodgamestudios/cxf-ready').then((cxf: ICXF) => {\n // Initialize global state\n const config = createConfig(cxf.gameId)\n globalState.setConfig(config)\n globalState.setCxf(cxf)\n\n const store = defaultStore(cxf)\n globalState.setStore(store)\n\n // Start the app\n app()\n})\n", "import { globalState } from './globalState'\nimport { createPostMessageHandlers } from './handlers/postMessageHandlers'\nimport { createCxfReducers } from './handlers/reducers'\nimport { log, tryCatch } from './helpers'\n\nexport const app = (): void => {\n log('App has started')\n\n const postMessageHandlers = createPostMessageHandlers()\n const subscribeCxf = createCxfReducers()\n\n window.addEventListener(\n 'message',\n tryCatch(({ data }: MessageEvent) => {\n if (data) {\n const handler = postMessageHandlers[data.name]\n if (handler) {\n log('POST MESSAGE', data)\n handler(data.payload)\n }\n }\n })\n )\n\n // Subscribe to all CXF events\n for (const reducer of Object.values(subscribeCxf)) {\n reducer()\n }\n\n globalState.preResolveConfig()\n}\n", "import { Config } from './config'\nimport { ICXF } from './cxf'\nimport { fetchUnreadOfferNotificationsCount as defaultFetchUnreadOfferNotificationsCount } from './fetch'\nimport { IShopMessageBus } from './messages/IShopMessageBus'\nimport { ShopMessageBus } from './messages/ShopMessageBus'\nimport { preResolveConfig as defaultPreResolveConfig } from './preFetch'\nimport { IStore } from './store'\n\nexport interface IProvider<T> {\n get: () => T | undefined\n set: (value: T) => void\n}\n\nexport const entityProvider = <T>(initial?: T): IProvider<T> => {\n let element = initial\n return {\n get: () => element,\n set: (value) => {\n element = value\n },\n }\n}\n\nclass GlobalState {\n public config: Config | undefined = undefined\n public cxfProvider: IProvider<ICXF> = entityProvider()\n public dialogProvider: IProvider<HTMLElement> = entityProvider()\n public storeProvider: IProvider<IStore> = entityProvider()\n public shopMessageBus: IShopMessageBus = new ShopMessageBus()\n public fetchUnreadOfferNotificationsCount: () => Promise<void> | void = defaultFetchUnreadOfferNotificationsCount\n public preResolveConfig: () => void = defaultPreResolveConfig\n\n public setConfig(config: Config): void {\n this.config = config\n }\n\n public getConfig(): Config {\n if (this.config === undefined) {\n throw new Error('Config is not initialized')\n }\n return this.config\n }\n\n public setCxf(cxf: ICXF): void {\n this.cxfProvider.set(cxf)\n }\n\n public getCxf(): ICXF {\n const cxf = this.cxfProvider.get()\n if (!cxf) {\n throw new Error('CXF is not loaded')\n }\n return cxf\n }\n\n public setStore(store: IStore): void {\n this.storeProvider.set(store)\n }\n\n public getStore(): IStore {\n const store = this.storeProvider.get()\n if (!store) {\n throw new Error('Store is not initialized')\n }\n return store\n }\n\n public updateStore(data: Partial<IStore>): void {\n const currentStore = this.getStore()\n this.setStore({\n ...currentStore,\n ...data,\n })\n }\n}\n\nexport const globalState = new GlobalState()\n", "import { globalState } from './globalState'\nimport { log, logError } from './helpers'\nimport { criteriaSelector } from './store'\n\nexport const fetchUnreadOfferNotificationsCount = async (): Promise<void> => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n\n if (config.LEGEND_LEVEL_IS_USED && store.legendLevel === undefined) {\n log('Skip fetchUnreadOfferNotificationsCount due to legendLevel is undefined')\n return\n }\n\n log('fetchUnreadOfferNotificationsCount')\n\n if (store.token && store.unreadOfferNotifsCountUrl) {\n let unreadCount = 0\n const headers = {\n Authorization: `Bearer ${store.token}`,\n }\n const url = store.unreadOfferNotifsCountUrl\n .replace('{locale}', store.language || '')\n .replace('{zoneId}', store.zoneId || '')\n .replace('{criteria}', encodeURIComponent(JSON.stringify(criteriaSelector(store))))\n try {\n log('fetch', url)\n const resp = await fetch(url, {\n headers,\n })\n if (resp.ok) {\n const data = await resp.json()\n unreadCount = Number(data?.notifCount)\n }\n } catch (error) {\n logError(`cannot fetch ${url}`, error)\n }\n log('setUnseenOffersCounter', unreadCount)\n store.gameApi.invokeFn('setUnseenOffersCounter', unreadCount).catch((error) => {\n logError('setUnseenOffersCounter error:', error)\n })\n }\n}\n", "import { v4 as uuidv4 } from '@lukeed/uuid'\n// import debug from 'debug'\nimport { ArgumentNullError } from './ArgumentNullError'\nimport { IDictionary } from './common'\nimport { ICXF } from './cxf'\nimport { globalState } from './globalState'\nimport { IStore } from './store'\nimport { parentDomain, timeout } from './utils'\n\nexport interface GNIP {\n gameId: string\n networkId: string\n instanceId: string\n playerId: string\n}\n\nexport function decodePing(value: string): GNIP {\n const [gameId, networkId, instanceId, playerId] = value.split('-')\n return { gameId, networkId, instanceId, playerId }\n}\n\nexport function encodePing({ gameId, networkId, instanceId, playerId }: GNIP): string {\n return [gameId, networkId, instanceId, playerId].join('-')\n}\n\nexport function formatQueryString(object: IDictionary<string | number | undefined | null>) {\n return Object.entries(object)\n .filter(([key, value]) => value !== null && value !== undefined)\n .map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)\n .join('&')\n}\n\nexport const getTokenAndLanguage = (store: IStore) => {\n const { playerId, token, zoneId, language } = store\n if (!playerId) {\n throw new ArgumentNullError('playerId', playerId)\n }\n return {\n token,\n zoneId,\n locale: language,\n ping: encodePing({ ...store, playerId }),\n }\n}\n\nexport const getPing = (): string => {\n const store = globalState.getStore()\n const { playerId } = store\n if (!playerId) {\n throw new ArgumentNullError('playerId', playerId)\n }\n return encodePing({ ...store, playerId })\n}\n\nexport const loadScript = (src: string) =>\n new Promise((resolve, reject) => {\n const script = document.createElement('script')\n script.type = 'text/javascript'\n script.type = 'module'\n script.async = true\n script.src = src\n script.onload = resolve\n script.onerror = reject\n document.body.append(script)\n })\n\nexport const tryCatch = <T extends (...args: any[]) => any>(fn: T): T =>\n ((...args: any[]) => {\n try {\n return fn(...args)\n } catch (error) {\n logError(error)\n throwCxfError(error as Error)\n }\n }) as T\n\nexport const throwCxfError = (e: Error): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_ERROR, e)\n}\n\nexport const isArgumentNullError = (e: Error): e is ArgumentNullError => e.name === 'ArgumentNullError'\n\n// eslint-disable-next-line @typescript-eslint/no-var-requires\nexport const loadCxf = timeout(() => require('@goodgamestudios/cxf-ready') as Promise<ICXF>, 10_000)\n\n/*\n * To show CXF-WEBSHOP debug messages just run in a browser console\n * localStorage.debug = 'CXF-WEBSHOP:*'\n */\n// const logger = debug('CXF-WEBSHOP')\n// export const log = (argument0: any, ...rest: any[]): void => logger(argument0, ...rest)\n\nexport const logError = (...arguments_: any[]): void =>\n // tslint:disable-next-line:no-console\n console.error('%c CXF-WEBSHOP ->', 'background: #ff0000; color: #fff', ...arguments_)\n\nexport const log = (...args: any[]): void => {\n console.log('%c CXF-WEBSHOP ->', 'background: pink; color: #bada55, font-size:20px', ...args)\n}\n\nexport const createSessionId = () => uuidv4()\n\nexport const getDomain = (referrer: string): string | undefined => {\n return parentDomain(referrer)\n}\n\nexport const ggsGetQueryParams = (): string => {\n return ggsGetQueryParameters()\n}\n\nexport const ggsGetReferrerValue = (): string => {\n return ggsGetReferrer()\n}\n", "var IDX=256, HEX=[], BUFFER;\nwhile (IDX--) HEX[IDX] = (IDX + 256).toString(16).substring(1);\n\nexport function v4() {\n\tvar i=0, num, out='';\n\n\tif (!BUFFER || ((IDX + 16) > 256)) {\n\t\tBUFFER = Array(i=256);\n\t\twhile (i--) BUFFER[i] = 256 * Math.random() | 0;\n\t\ti = IDX = 0;\n\t}\n\n\tfor (; i < 16; i++) {\n\t\tnum = BUFFER[IDX + i];\n\t\tif (i==6) out += HEX[num & 15 | 64];\n\t\telse if (i==8) out += HEX[num & 63 | 128];\n\t\telse out += HEX[num];\n\n\t\tif (i & 1 && i > 1 && i < 11) out += '-';\n\t}\n\n\tIDX++;\n\treturn out;\n}\n", "export class ArgumentNullError extends Error {\n constructor(parameterName: string, parameterValue: any) {\n super(`${parameterName} has null value: ${parameterValue}`)\n this.name = 'ArgumentNullError'\n }\n}\n", "import { ArgumentNullError } from './ArgumentNullError'\nimport { AnyVoidFn as AnyVoidFunction, IDictionary, PromiseFn as PromiseFunction } from './common'\n\nconst startTimer = (function_: AnyVoidFunction, time: number) => {\n const id = setTimeout(function_, time)\n return () => clearTimeout(id)\n}\n\nexport const timeout =\n <A, B>(function_: PromiseFunction<A, B>, time: number): PromiseFunction<A, B> =>\n (a) => {\n return new Promise((resolve, reject) => {\n const stopTimer = startTimer(() => {\n reject(new Error(`Timeout has exceeded ${time}`))\n }, time)\n\n function_(a)\n .then((value) => {\n resolve(value)\n stopTimer()\n })\n .catch(reject)\n })\n }\n\nexport const validateForNull = (properties: IDictionary<any>) => {\n for (const key of Object.keys(properties)) {\n const value = properties[key]\n if (value === undefined || value === null || Number.isNaN(value)) {\n throw new ArgumentNullError(key, value)\n }\n }\n}\n\nexport function msToSec(value: number): number {\n return Math.floor(value / 1000)\n}\n\nexport function decodePayload<T>(data: string): T {\n return JSON.parse(atob(data))\n}\n\nexport function diffHours(d1: Date, d2: Date): number {\n const diff = (d1.getTime() - d2.getTime()) / 1000\n return Math.abs(Math.round(diff / (60 * 60)))\n}\n\nexport function timestampToHours(value: number): number {\n return Math.abs(Math.round(value / 1000 / (60 * 60)))\n}\n\nexport const parentDomain = (referrer: string) => {\n const matches = referrer.match(/^https?:\\/\\/([^#/?]+)(?:[#/?]|$)/i)\n if (!matches || !matches[1]) {\n return\n }\n\n const result = matches[1].match(/[^.]+\\.[^.]+$/)\n if (!result) {\n return\n }\n\n return result[0]\n}\n", "import { CountryCode, IGameEvent } from '@goodgamestudios/cxf-events'\nimport { IDictionary } from './common'\nimport { ICXF, IGameApi } from './cxf'\n\nexport interface IStore {\n playerId?: string\n instanceId: string\n networkId: string\n gameId: string\n gameApi: IGameApi\n language?: string\n token?: string\n zoneId?: string\n xp: number\n level: number\n gameEvents: IGameEvent[]\n countryCode: CountryCode\n lastPurchaseTab: string\n legendLevel?: number\n subscriptionDisabled: boolean\n isTempServer: boolean\n customizationSuffix: string\n resolvedCustomizationUrl?: string\n sourceId: string\n unreadOfferNotifsCountUrl: string\n adStatus?: Record<string, any>\n}\n\nexport function defaultStore(cxf: ICXF): IStore {\n return {\n playerId: cxf.playerId,\n instanceId: cxf.instanceId,\n networkId: cxf.networkId,\n gameId: cxf.gameId,\n gameApi: cxf.gameApi,\n language: cxf.language,\n token: cxf.token,\n zoneId: cxf.zoneId,\n gameEvents: [],\n xp: 0,\n level: 0,\n legendLevel: undefined,\n countryCode: '',\n lastPurchaseTab: '',\n subscriptionDisabled: false,\n isTempServer: false,\n customizationSuffix: '',\n resolvedCustomizationUrl: '',\n sourceId: 'unknown',\n unreadOfferNotifsCountUrl: '',\n }\n}\n\nexport function criteriaSelector({ legendLevel, level }: IStore): IDictionary<any> {\n return {\n legendLevel,\n level,\n }\n}\n", "import { IShopMessageBus } from './IShopMessageBus'\n\nexport class ShopMessageBus implements IShopMessageBus {\n public post(message: any) {\n const element = document.querySelector('#dialog') as HTMLIFrameElement\n if (element && element.tagName.toLocaleLowerCase() === 'iframe') {\n element.contentWindow?.postMessage(message, '*')\n }\n }\n}\n", "import { globalState } from './globalState'\nimport { log } from './helpers'\nimport { createCustomizationUrl } from './url'\n\ntype LemonstandConfig = {\n css: string\n js: string\n unreadOfferNotifsCountUrl: string\n}\n\nexport const preResolveConfig = (): void => {\n if (typeof fetch !== 'function') {\n return\n }\n const originalCustomUrl = createCustomizationUrl()\n let resolvedUrl = ''\n fetch(originalCustomUrl)\n .then((resp) => {\n const currentCustomUrl = createCustomizationUrl()\n if (currentCustomUrl === originalCustomUrl) {\n // this is the latest, we keep it\n resolvedUrl = resp.url\n log('parsed customization url is ' + resolvedUrl)\n globalState.updateStore({\n resolvedCustomizationUrl: resolvedUrl,\n })\n return resp.json()\n }\n })\n .then((config: LemonstandConfig | undefined) => {\n if (config) {\n globalState.updateStore({\n unreadOfferNotifsCountUrl: config.unreadOfferNotifsCountUrl || '',\n })\n }\n })\n}\n", "import { acronym } from '@goodgamestudios/game-alias'\nimport { globalState } from './globalState'\nimport {\n createSessionId,\n formatQueryString,\n getDomain,\n getTokenAndLanguage,\n ggsGetQueryParams,\n ggsGetReferrerValue,\n} from './helpers'\nimport { criteriaSelector } from './store'\nimport { ICreateCatalogUrlProperties } from './types'\nimport { validateForNull } from './utils'\n\nexport const createIframeUrl = ({ page, route, sid, config: igsConfig = {} }: ICreateCatalogUrlProperties): string => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n\n const { token, zoneId, locale } = getTokenAndLanguage(store)\n const parameters = {\n token,\n zoneId,\n locale,\n sid: sid || createSessionId(),\n }\n\n if (Object.keys(igsConfig).length > 0) {\n // @ts-ignore\n parameters.config = JSON.stringify(igsConfig)\n }\n\n validateForNull(parameters)\n\n const urlParameters = new URLSearchParams(ggsGetQueryParams())\n const queryParameters = {\n ...parameters,\n 'lemonstand.customization.url': store.resolvedCustomizationUrl || createCustomizationUrl(),\n domain: getDomain(ggsGetReferrerValue()),\n websiteId: urlParameters.get('w'),\n criteria: JSON.stringify(criteriaSelector(store)),\n level: store.level,\n }\n // <editor-fold desc=\"SPIL integration\">\n /* In case of SPIL integration the game can be launched\n with additional params 'network=xx&usekeybaselogin=false'\n in this case it needs to pass the network parameter to iframe url */\n const network = urlParameters.get('network')\n if (urlParameters.get('usekeybaselogin') === 'false' && Number(network) > 0) {\n // @ts-ignore\n queryParameters.network = network\n }\n\n if (store.adStatus?.areBannersAvailable) {\n // @ts-ignore\n queryParameters.ads = true\n }\n // </editor-fold>\n\n return `${config.BASE_URL}/?${formatQueryString(queryParameters)}${page ? `#${page}` : ''}${\n route ? `--${route}--${Date.now()}` : ''\n }`\n}\n\nexport const createCustomizationUrl = (): string => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n const cxf = globalState.getCxf()\n\n const configBase = acronym(store.gameId)\n const configVariance = store.customizationSuffix ? `-${store.customizationSuffix}` : ''\n const configBranch = `${configBase}${configVariance}`\n\n if (cxf.env === 'test') {\n const configVersion = new URLSearchParams(window.location.search).get('configGGS')\n if (configVersion !== null) {\n return config.CUSTOMIZATION_URL_TEMPLATE.replace('{0}', configBranch).replace('{1}', configVersion)\n }\n }\n return config.CUSTOMIZATION_URL.replace('{0}', configBranch)\n}\n", "import { createDialog } from '../dialog'\nimport { fetchUnreadOfferNotificationsCount } from '../fetch'\nimport { globalState } from '../globalState'\nimport { createSessionId, log } from '../helpers'\nimport { Handlers, WebshopEvents } from '../types'\nimport { createIframeUrl } from '../url'\n\nexport const onSalesPageOpen = (): void => {\n log('OnOpenSalesOffersPage')\n const sid = createSessionId()\n const url = createIframeUrl({\n page: 'sale-offers',\n sid,\n })\n createDialog(url)\n}\n\nexport const onLemonstandClose = (): void => {\n const store = globalState.getStore()\n log('onLemonstandClose', store)\n fetchUnreadOfferNotificationsCount()\n}\n\nexport const createPostMessageHandlers = (): Handlers => {\n return {\n [WebshopEvents.CXF_OPEN_SALES_MSG]: onSalesPageOpen,\n [WebshopEvents.CXF_DIALOG_CLOSE]: onLemonstandClose,\n }\n}\n", "import { globalState } from './globalState'\n\nexport const removeDialog = (): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_DIALOG_CLOSE)\n}\n\nexport const createDialog = (url: string): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_DIALOG_OPEN, url)\n}\n", "import { IDictionary } from './common'\n\n// ============================================================================\n// Handler Types\n// ============================================================================\n\n/**\n * Generic handler function type\n */\nexport type Handler = (...arguments_: any[]) => void\n\n/**\n * Dictionary of handler functions\n */\nexport type Handlers = IDictionary<Handler>\n\n/**\n * Push notification handler function type\n */\nexport type PushHandler = (...arguments_: any[]) => void\n\n/**\n * Dictionary of push handlers\n */\nexport type PushHandlers = IDictionary<PushHandler>\n\n/**\n * CXF event reducers/subscribers\n */\nexport type CxfReducers = IDictionary<() => void>\n\n// ============================================================================\n// Event Payload Types\n// ============================================================================\n\n/**\n * Payload for opening IGS (In-Game Shop)\n */\nexport interface OpenIGSPayload {\n page?: string\n route?: string\n sourceId?: string\n config?: Record<string, any>\n}\n\n/**\n * Payload for reward events\n */\nexport interface OnRewardProperties {\n successUrl: string\n offerId: string\n sid: string\n page: string\n config?: Record<string, any>\n}\n\n/**\n * Payload for Lemonstand category update events\n */\nexport interface OnLemonstandCategoryUpdateProperties {\n target: string\n action: string\n data: any\n}\n\n/**\n * Payload for Lemonstand notification creation events\n */\nexport interface OnLemonstandNotificationsCreatedProperties {\n notifCount: number\n}\n\n// ============================================================================\n// Tracking Types\n// ============================================================================\n\n/**\n * Generic tracking payload\n */\nexport interface ITrackPayload extends IDictionary<string | number> {\n eventId: number\n}\n\n/**\n * Properties for Ranch webshop call tracking\n */\nexport interface IRanchWebShopCallProperties extends ITrackPayload {\n gameId: number\n playerId: number\n instanceId: number\n zoneId: number\n networkId: number\n sessionId: string\n date: number\n unixtimeMS: number\n sourceId: string\n}\n\n// ============================================================================\n// URL Types\n// ============================================================================\n\n/**\n * Properties for creating catalog/iframe URLs\n */\nexport interface ICreateCatalogUrlProperties {\n sid?: string\n page?: string\n route?: string\n config?: Record<string, any>\n}\n\n// ============================================================================\n// Webshop Events Enum\n// ============================================================================\n\n/**\n * Webshop event names\n */\nexport enum WebshopEvents {\n CXF_DIALOG_CLOSE = 'cxf.dialog.close',\n CXF_NATIVE_SUBSCRIPTION_ENABLE = 'cxf.native.subscription.enable',\n CXF_OPEN_SALES_MSG = 'cxf.webshop.sales.open',\n CXF_SET_CUSTOMIZATION_SUFFIX = 'cxf.set.customization.surfix',\n CXF_JOIN_TEMP_SERVER = 'cxf.join.temp.server',\n LEMONSTAND_CATEGORY_UPDATE = 'lemonstand.category.update',\n LEMONSTAND_NOTIFICATIONS_CREATED = 'lemonstand.notifs.created',\n}\n\n// CANVAS Agent\n//\n// Agent Props workaround; don't want to make Agent as a package\nexport enum CanvasAppEvent {\n CHECKOUT = 'checkout',\n CLOSE = 'close',\n COLLECT = 'collect',\n ERROR = 'error',\n EXPIRED = 'expired',\n INIT = 'init',\n NATIVE_CATALOG_REQUEST = 'native_catalog_request',\n NATIVE_CHECKOUT_REQUEST = 'native_checkout_request',\n NAVIGATE = 'navigate',\n READY = 'ready',\n RESIZE = 'resize',\n}\n\nexport type CanvasEventPostMessage = {\n eventType: CanvasAppEvent\n [key: string]: any\n}\n\nexport type AgentProps = {\n canvasAppId: string\n eventListener?: (event: CanvasEventPostMessage) => void\n frameParent?: HTMLElement\n fullScreen?: boolean\n locale?: string\n sessionId?: string\n token: string\n customContext?: Record<string, any>\n authClientId?: any\n debug?: boolean\n}\n\n\n", "import { CxfEvents, IGameEvent, ILoginData } from '@goodgamestudios/cxf-events'\nimport { IPushMessageData } from '../common'\nimport { globalState } from '../globalState'\nimport { log, tryCatch } from '../helpers'\nimport { CxfReducers, WebshopEvents } from '../types'\nimport { onOpen } from './eventHandlers'\nimport { createPushHandlers } from './pushHandlers'\n\nexport const subscribeCommonCxfEvents = (): void => {\n const cxf = globalState.getCxf()\n const pushHandlers = createPushHandlers()\n\n cxf.on(CxfEvents.OpenIGS, tryCatch(onOpen))\n cxf.on(\n CxfEvents.Push,\n tryCatch(({ id, payload }: IPushMessageData) => {\n log('cxf.push', id, payload)\n const handler = pushHandlers[id]\n handler && handler(payload)\n })\n )\n}\n\nexport const subscribeToGameEvents = (): void => {\n const cxf = globalState.getCxf()\n\n cxf.on(CxfEvents.Login, (e: ILoginData) => {\n const { gameEvents } = globalState.getStore()\n const { gameEvents: eventsInLogin } = e\n // eslint-disable-next-line unicorn/consistent-function-scoping\n const eventNotExistPredicateGenerator = (existingEvents: IGameEvent[]) => (event: IGameEvent) =>\n !existingEvents.map((event_) => event_.type).includes(event.type)\n globalState.updateStore({\n ...e,\n gameEvents: [...gameEvents.filter(eventNotExistPredicateGenerator(eventsInLogin)), ...eventsInLogin],\n })\n log(CxfEvents.Login, 'reducer', globalState.getStore())\n globalState.fetchUnreadOfferNotificationsCount()\n })\n\n cxf.on(CxfEvents.GameEventUpdate, (e: IGameEvent[]) => {\n log(CxfEvents.GameEventUpdate, e)\n const store = globalState.getStore()\n const updatedEventTypes = new Set(e.map((event) => event.type))\n globalState.updateStore({\n gameEvents: [...store.gameEvents.filter((event) => !updatedEventTypes.has(event.type)), ...e],\n })\n log(CxfEvents.GameEventUpdate, 'reducer', globalState.getStore())\n })\n\n cxf.on(CxfEvents.GameEventAdd, (e: IGameEvent) => {\n log(CxfEvents.GameEventAdd, e)\n const store = globalState.getStore()\n globalState.updateStore({\n gameEvents: [...store.gameEvents, e],\n })\n log(CxfEvents.GameEventAdd, 'reducer', globalState.getStore())\n })\n\n cxf.on(CxfEvents.GameEventRemove, (e: number) => {\n log(CxfEvents.GameEventRemove, e)\n const store = globalState.getStore()\n globalState.updateStore({\n gameEvents: store.gameEvents.filter(({ type }) => type !== e),\n })\n log(CxfEvents.GameEventRemove, 'reducer', globalState.getStore())\n })\n\n cxf.on(CxfEvents.LevelChanged, (level: number) => {\n log(CxfEvents.LevelChanged, level)\n globalState.updateStore({\n level,\n })\n log(CxfEvents.LevelChanged, 'reducer', globalState.getStore())\n globalState.fetchUnreadOfferNotificationsCount()\n })\n\n cxf.on(CxfEvents.LegendLevelChanged, (legendLevel: number) => {\n log(CxfEvents.LegendLevelChanged, `legendLevel: ${legendLevel}`)\n globalState.updateStore({\n legendLevel,\n })\n const store = globalState.getStore()\n log(CxfEvents.LegendLevelChanged, 'reducer', store)\n globalState.fetchUnreadOfferNotificationsCount()\n })\n}\n\n/*\n In E4K this event is used to control the subscriptions tab\n This is deprecated, as specific config values/tab visibility can be controlled using Lemonstands `config` query param\n*/\nexport const nativeSubscriptionEnableReducer = (): void => {\n const cxf = globalState.getCxf()\n cxf.on(WebshopEvents.CXF_NATIVE_SUBSCRIPTION_ENABLE, (enabled: 0 | 1) => {\n globalState.updateStore({\n subscriptionDisabled: enabled === 0,\n customizationSuffix: enabled === 0 ? 'no-subscription' : '',\n })\n globalState.preResolveConfig()\n })\n}\n\n/*\n The config file suffix (e.g. \"em.json\" + \"mobile\" -> \"em-mobile.json\") can be set through an event\n Used by MS and BFW integration pages\n*/\nexport const setCustomizationSuffixReducer = (): void => {\n const cxf = globalState.getCxf()\n cxf.on(WebshopEvents.CXF_SET_CUSTOMIZATION_SUFFIX, (suffix: string) => {\n const store = globalState.getStore()\n if (suffix === store.customizationSuffix) {\n return\n }\n globalState.updateStore({\n customizationSuffix: suffix || '',\n })\n globalState.preResolveConfig()\n })\n}\n\n/*\n Information about whether ads are enabled or not is retrieved externally from `cxf-ad-banners`\n and stored to be passed to IGS\n*/\nexport const receiveAdStatusReducer = (): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.on(config.CXF_AD_STATUS, ({ areBannersAvailable, bannersDetails }: Record<string, any>) => {\n globalState.updateStore({\n adStatus: {\n areBannersAvailable,\n bannersDetails,\n },\n })\n })\n}\n\n/*\n Use a dedicated config variant on temp servers\n*/\nexport const tempServerContextReducer = (): void => {\n const cxf = globalState.getCxf()\n cxf.on(WebshopEvents.CXF_JOIN_TEMP_SERVER, (isTemp: 0 | 1) => {\n const store = globalState.getStore()\n globalState.updateStore({\n isTempServer: Boolean(isTemp),\n })\n\n // ensure suffix is not already set by the environment, e.g. BFW or MS integration\n // TODO: fully migrate client detection into `cxf-webshop`\n if (store.customizationSuffix === '' || store.customizationSuffix === 'temp') {\n globalState.updateStore({\n customizationSuffix: isTemp ? 'temp' : '',\n })\n globalState.preResolveConfig()\n }\n })\n}\n\nexport const createCxfReducers = (): CxfReducers => {\n return {\n common: subscribeCommonCxfEvents,\n gameEvents: subscribeToGameEvents,\n receiveAdStatus: receiveAdStatusReducer,\n tempServerContext: tempServerContextReducer,\n nativeSubscription: nativeSubscriptionEnableReducer,\n setCustomizationSuffix: setCustomizationSuffixReducer,\n }\n}\n", "import { createDialog } from '../dialog'\nimport { globalState } from '../globalState'\nimport { createSessionId, log } from '../helpers'\nimport { trackOpenAction } from '../track'\nimport { OpenIGSPayload } from '../types'\nimport { createIframeUrl } from '../url'\n\nexport const onOpen = (payload: OpenIGSPayload = {}): void => {\n log('OnOpen payload:', payload)\n\n if (payload.sourceId) {\n globalState.updateStore({\n sourceId: payload.sourceId,\n })\n }\n\n const store = globalState.getStore()\n const sid = createSessionId()\n trackOpenAction(sid)\n const url = createIframeUrl({\n sid,\n page: store.lastPurchaseTab,\n ...payload,\n })\n createDialog(url)\n\n setTimeout(() => {\n // remove unread offer notifications counter on the IGS button while opening the IGS\n // small delay makes UX a bit better due to it happens after IGS initialization is started\n store.gameApi.invokeFn('setUnseenOffersCounter', 0)\n }, 1000)\n}\n", "import { globalState } from './globalState'\nimport { getTokenAndLanguage } from './helpers'\nimport { IRanchWebShopCallProperties, ITrackPayload } from './types'\nimport { msToSec } from './utils'\n\nexport const trackOpenAction = (sid: string): void => {\n const config = globalState.getConfig()\n const store = globalState.getStore()\n const now = Date.now()\n const parameters = getTokenAndLanguage(store)\n const { zoneId } = parameters\n const sessionId = sid\n const { playerId, gameId, networkId, instanceId, sourceId } = store\n trackAction({\n eventId: config.WEB_SHOP_CALL_TRACK_ID,\n date: msToSec(now),\n unixtimeMS: now,\n sessionId,\n zoneId: zoneId ? Number.parseInt(zoneId, 10) : undefined,\n playerId: Number.parseInt(playerId as string, 10),\n gameId: Number.parseInt(gameId, 10),\n networkId: Number.parseInt(networkId, 10),\n instanceId: Number.parseInt(instanceId, 10),\n sourceId,\n } as IRanchWebShopCallProperties)\n}\n\nexport const trackAction = (payload: ITrackPayload): void => {\n const config = globalState.getConfig()\n const cxf = globalState.getCxf()\n cxf.emit(config.CXF_TRACK_MSG, payload)\n}\n", "import { createDialog } from '../dialog'\nimport { fetchUnreadOfferNotificationsCount } from '../fetch'\nimport { globalState } from '../globalState'\nimport { log, logError } from '../helpers'\nimport { trackOpenAction } from '../track'\nimport {\n OnLemonstandCategoryUpdateProperties,\n OnLemonstandNotificationsCreatedProperties,\n OnRewardProperties,\n PushHandlers,\n WebshopEvents,\n} from '../types'\nimport { createIframeUrl } from '../url'\n\nexport const onReward = (payload: OnRewardProperties): void => {\n log('OnReward: offerId', payload.offerId, 'Page', payload.page)\n const url = createIframeUrl({\n route: encodeURIComponent(payload.successUrl),\n ...payload,\n })\n globalState.updateStore({\n lastPurchaseTab: payload.page,\n sourceId: 'successfulPayoutReward',\n })\n trackOpenAction(payload.sid)\n\n createDialog(url)\n}\n\nexport const onLemonstandCategoryUpdate = ({ target, action, data }: OnLemonstandCategoryUpdateProperties): void => {\n globalState.shopMessageBus.post({\n eventName: WebshopEvents.LEMONSTAND_CATEGORY_UPDATE,\n target,\n data: { action, data },\n })\n}\n\n/**\n * this handler works in case of offer notification activation and deactivation\n */\nexport const onLemonstandNotificationsCreated = ({ notifCount }: OnLemonstandNotificationsCreatedProperties): void => {\n log('onLemonstandNotificationsCreated -> notifCount:', notifCount)\n const store = globalState.getStore()\n if (notifCount > 0) {\n // random delay in range 1-120 sec on LIVE, and 1-15 sec on TEST\n const randomDelayMs = Math.floor(Math.random() * (Number(store.networkId) < 250 ? 120_000 : 15_000))\n setTimeout(() => {\n fetchUnreadOfferNotificationsCount()\n }, randomDelayMs)\n } else {\n store.gameApi.invokeFn('setUnseenOffersCounter', 0).catch((error) => {\n logError('setUnseenOffersCounter error:', error)\n })\n }\n}\n\nexport const createPushHandlers = (): PushHandlers => {\n return {\n reward: onReward,\n [WebshopEvents.LEMONSTAND_CATEGORY_UPDATE]: onLemonstandCategoryUpdate,\n [WebshopEvents.LEMONSTAND_NOTIFICATIONS_CREATED]: onLemonstandNotificationsCreated,\n }\n}\n", "import { CxfEvents } from '@goodgamestudios/cxf-events'\nimport { IDictionary } from './common'\nimport { WebshopEvents } from './types'\n\nexport type GameSpecificConfig = {\n LEGEND_LEVEL_IS_USED: boolean\n}\n\nconst COMMON_CONFIG = {\n CUSTOMIZATION_URL: process.env.CUSTOMIZATION_URL as string,\n CUSTOMIZATION_URL_TEMPLATE: process.env.CUSTOMIZATION_URL_TEMPLATE as string,\n BASE_URL: process.env.BASE_URL as string,\n CXF_DIALOG_OPEN: 'cxf.dialog.open',\n CXF_DIALOG_CLOSE: 'cxf.dialog.close',\n CXF_TRACK_MSG: 'cxf.tracking.message',\n CXF_BTN_CLICK_MSG: CxfEvents.OpenIGS,\n CXF_OPEN_SALES_MSG: WebshopEvents.CXF_OPEN_SALES_MSG,\n WEB_SHOP_CALL_TRACK_ID: 1181,\n CXF_ERROR: 'cxf.error',\n CXF_PUSH: 'cxf.push',\n CXF_AD_STATUS: 'cxf.adBanner.status',\n}\n\nexport type Config = typeof COMMON_CONFIG & GameSpecificConfig\n\nconst GAME_SPECIFIC_CONFIG: IDictionary<GameSpecificConfig> = {\n 12: {\n LEGEND_LEVEL_IS_USED: true,\n },\n 15: {\n LEGEND_LEVEL_IS_USED: false,\n },\n 16: {\n LEGEND_LEVEL_IS_USED: true,\n },\n}\n\nexport const createConfig = (gameId: string): Config => {\n return {\n ...COMMON_CONFIG,\n ...GAME_SPECIFIC_CONFIG[gameId],\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["define_process_default", "init_define_process", "__esmMin", "require_dist", "__commonJSMin", "exports", "module", "init_define_process", "cxf", "resolve", "reject", "event", "require_dist", "__commonJSMin", "exports", "module", "init_define_process", "b", "f", "r", "$", "h", "g", "e", "a", "o", "n", "c", "i", "m", "j", "k", "d", "l", "require_typings", "__commonJSMin", "exports", "init_define_process", "CxfEvents", "init_define_process", "init_define_process", "init_define_process", "init_define_process", "init_define_process", "init_define_process", "IDX", "HEX", "BUFFER", "v4", "i", "num", "out", "init_define_process", "ArgumentNullError", "parameterName", "parameterValue", "init_define_process", "startTimer", "function_", "time", "id", "timeout", "a", "resolve", "reject", "stopTimer", "value", "validateForNull", "properties", "key", "ArgumentNullError", "msToSec", "parentDomain", "referrer", "matches", "result", "encodePing", "gameId", "networkId", "instanceId", "playerId", "formatQueryString", "object", "key", "value", "getTokenAndLanguage", "store", "token", "zoneId", "language", "ArgumentNullError", "loadScript", "src", "resolve", "reject", "script", "tryCatch", "fn", "args", "error", "logError", "throwCxfError", "config", "globalState", "loadCxf", "timeout", "logError", "arguments_", "log", "args", "createSessionId", "v4", "getDomain", "referrer", "parentDomain", "ggsGetQueryParams", "ggsGetReferrerValue", "init_define_process", "defaultStore", "cxf", "criteriaSelector", "legendLevel", "level", "fetchUnreadOfferNotificationsCount", "config", "globalState", "store", "log", "unreadCount", "headers", "url", "criteriaSelector", "resp", "data", "error", "logError", "init_define_process", "ShopMessageBus", "message", "element", "init_define_process", "init_define_process", "import_game_alias", "createIframeUrl", "page", "route", "sid", "igsConfig", "config", "globalState", "store", "token", "zoneId", "locale", "getTokenAndLanguage", "parameters", "createSessionId", "validateForNull", "urlParameters", "ggsGetQueryParams", "queryParameters", "createCustomizationUrl", "getDomain", "ggsGetReferrerValue", "criteriaSelector", "network", "formatQueryString", "cxf", "configBase", "configVariance", "configBranch", "configVersion", "preResolveConfig", "originalCustomUrl", "createCustomizationUrl", "resolvedUrl", "resp", "log", "globalState", "config", "entityProvider", "initial", "element", "value", "GlobalState", "ShopMessageBus", "fetchUnreadOfferNotificationsCount", "preResolveConfig", "config", "cxf", "store", "data", "currentStore", "globalState", "init_define_process", "init_define_process", "createDialog", "url", "config", "globalState", "init_define_process", "onSalesPageOpen", "log", "sid", "createSessionId", "url", "createIframeUrl", "createDialog", "onLemonstandClose", "store", "globalState", "fetchUnreadOfferNotificationsCount", "createPostMessageHandlers", "init_define_process", "import_cxf_events", "init_define_process", "init_define_process", "trackOpenAction", "sid", "config", "globalState", "store", "now", "parameters", "getTokenAndLanguage", "zoneId", "sessionId", "playerId", "gameId", "networkId", "instanceId", "sourceId", "trackAction", "msToSec", "payload", "onOpen", "payload", "log", "globalState", "store", "sid", "createSessionId", "trackOpenAction", "url", "createIframeUrl", "createDialog", "init_define_process", "onReward", "payload", "log", "url", "createIframeUrl", "globalState", "trackOpenAction", "createDialog", "onLemonstandCategoryUpdate", "target", "action", "data", "onLemonstandNotificationsCreated", "notifCount", "store", "randomDelayMs", "fetchUnreadOfferNotificationsCount", "error", "logError", "createPushHandlers", "subscribeCommonCxfEvents", "cxf", "globalState", "pushHandlers", "createPushHandlers", "tryCatch", "onOpen", "id", "payload", "log", "handler", "subscribeToGameEvents", "e", "gameEvents", "eventsInLogin", "eventNotExistPredicateGenerator", "existingEvents", "event", "event_", "store", "updatedEventTypes", "type", "level", "legendLevel", "nativeSubscriptionEnableReducer", "enabled", "setCustomizationSuffixReducer", "suffix", "receiveAdStatusReducer", "config", "areBannersAvailable", "bannersDetails", "tempServerContextReducer", "isTemp", "createCxfReducers", "app", "log", "postMessageHandlers", "createPostMessageHandlers", "subscribeCxf", "createCxfReducers", "tryCatch", "data", "handler", "reducer", "globalState", "init_define_process", "import_cxf_events", "COMMON_CONFIG", "define_process_default", "GAME_SPECIFIC_CONFIG", "createConfig", "gameId", "loadScript", "log", "cxf", "config", "createConfig", "globalState", "store", "defaultStore", "app"]
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@goodgamestudios/cxf-webshop",
3
3
  "description": "WebShop CXF Module",
4
- "version": "7.0.0-qa.7",
4
+ "version": "7.0.0-qa.8",
5
5
  "keywords": [],
6
6
  "license": "UNLICENSED",
7
7
  "repository": {
@@ -125,5 +125,6 @@
125
125
  },
126
126
  "engines": {
127
127
  "node": "22"
128
- }
128
+ },
129
+ "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
129
130
  }
package/.prettierignore DELETED
@@ -1,6 +0,0 @@
1
- dist
2
- build
3
- coverage
4
- public
5
-
6
- *.html
package/.prettierrc.js DELETED
@@ -1,10 +0,0 @@
1
- module.exports = {
2
- trailingComma: 'es5',
3
- tabWidth: 4,
4
- semi: false,
5
- singleQuote: true,
6
- printWidth: 120,
7
- useTabs: false,
8
- bracketSpacing: true,
9
- proseWrap: 'preserve',
10
- }
@@ -1,265 +0,0 @@
1
- # CXF Webshop - Refactoring Documentation
2
-
3
- ## Overview
4
-
5
- This project contains two versions of the CXF Webshop codebase:
6
-
7
- 1. **`src/`** - Refactored code **WITHOUT** dependency injection (✨ **ACTIVE**)
8
- 2. **`src_old/`** - Original code **WITH** dependency injection (📚 **REFERENCE**)
9
-
10
- ---
11
-
12
- ## What Happened?
13
-
14
- The entire codebase was refactored to **remove dependency injection patterns** while maintaining 100% of functionality.
15
-
16
- ### Before (src_old/)
17
- ```typescript
18
- // Complex DI with readuz library
19
- export const trackAction: DIReader<TrackAction> = inject(
20
- (env) => env.config,
21
- (env) => env.cxfProvider,
22
- (config, cxfProvider) => (payload) => {
23
- const cxf = getCxf(cxfProvider)
24
- cxf.emit(config.CXF_TRACK_MSG, payload)
25
- }
26
- )
27
-
28
- // Usage
29
- const action = trackAction(environment)
30
- action(payload)
31
- ```
32
-
33
- ### After (src/)
34
- ```typescript
35
- // Direct implementation with global state
36
- export const trackAction = (payload: ITrackPayload): void => {
37
- const config = globalState.getConfig()
38
- const cxf = globalState.getCxf()
39
- cxf.emit(config.CXF_TRACK_MSG, payload)
40
- }
41
-
42
- // Usage
43
- trackAction(payload)
44
- ```
45
-
46
- ---
47
-
48
- ## Quick Comparison
49
-
50
- | Aspect | src_old/ (DI) | src/ (Direct) |
51
- |--------|---------------|---------------|
52
- | **Pattern** | Reader monad + inject() | Direct functions |
53
- | **Dependencies** | readuz library | None |
54
- | **Complexity** | High | Low |
55
- | **Code Lines** | ~2000 | ~1600 |
56
- | **Testability** | Excellent | Good |
57
- | **Learning Curve** | Steep | Gentle |
58
- | **Status** | Reference | **Active** |
59
-
60
- ---
61
-
62
- ## Directory Structure
63
-
64
- ```
65
- cxf-webshop/
66
- ├── src/ ← ✨ ACTIVE: Refactored without DI
67
- │ ├── globalState.ts ← NEW: Central state management
68
- │ ├── index.ts ← Simplified entry point
69
- │ ├── app.ts ← Direct implementation
70
- │ ├── handlers/ ← Simplified handlers
71
- │ ├── messages/ ← Unchanged
72
- │ └── ... (all other files)
73
-
74
- ├── src_old/ ← 📚 REFERENCE: Original with DI
75
- │ ├── env.ts ← Complex environment DI
76
- │ ├── combineReaders.ts ← Reader utilities
77
- │ ├── index.ts ← DI-based entry point
78
- │ └── ... (original files)
79
-
80
- ├── REFACTORING_README.md ← This file
81
- └── ... (other project files)
82
- ```
83
-
84
- ---
85
-
86
- ## Key Changes
87
-
88
- ### 1. Removed Dependency Injection
89
- - ❌ Removed `readuz` library dependency
90
- - ❌ Removed `DIReader<T>` type wrappers
91
- - ❌ Removed `inject()` function calls (~50+ sites)
92
- - ❌ Deleted `env.ts` and `combineReaders.ts`
93
-
94
- ### 2. Added Global State
95
- - ✨ New `globalState.ts` - centralized state singleton
96
- - Direct access via `globalState.getConfig()`, `getCxf()`, `getStore()`
97
- - Simple initialization in `index.ts`
98
-
99
- ### 3. Simplified Everything
100
- - All functions now have direct implementations
101
- - No more nested inject() calls
102
- - Clear, straightforward execution flow
103
- - 40% reduction in complexity
104
-
105
- ---
106
-
107
- ## Documentation
108
-
109
- The **`src/`** folder contains comprehensive documentation:
110
-
111
- ### Quick Start
112
- 📄 **[src/QUICK_REFERENCE.md](./src/QUICK_REFERENCE.md)** - 5-minute overview
113
-
114
- ### Essential Reading
115
- 📖 **[src/README.md](./src/README.md)** - Complete architecture guide
116
- 📊 **[src/SUMMARY.md](./src/SUMMARY.md)** - Refactoring summary
117
- 🔄 **[src/MIGRATION_GUIDE.md](./src/MIGRATION_GUIDE.md)** - Migration details
118
-
119
- ### Reference
120
- 📋 **[src/FILE_LIST.md](./src/FILE_LIST.md)** - All files inventory
121
- 📑 **[src/INDEX.md](./src/INDEX.md)** - Documentation navigation
122
-
123
- **Total Documentation**: 6 files, ~1200 lines of detailed guides
124
-
125
- ---
126
-
127
- ## Using the Code
128
-
129
- ### Current Setup (src/)
130
-
131
- ```typescript
132
- import { globalState } from './src/globalState'
133
- import { createConfig } from './src/config'
134
- import { defaultStore } from './src/store'
135
- import { app } from './src/app'
136
-
137
- // Initialize
138
- const cxf = await loadCxf()
139
- globalState.setConfig(createConfig(cxf.gameId))
140
- globalState.setCxf(cxf)
141
- globalState.setStore(defaultStore(cxf))
142
-
143
- // Start
144
- app()
145
- ```
146
-
147
- ### Reverting to DI (src_old/)
148
-
149
- If you need the original DI version:
150
- 1. Swap folder names (src ↔ src_old)
151
- 2. Restore `readuz` dependency in package.json
152
- 3. Update build configuration
153
-
154
- ---
155
-
156
- ## Why This Change?
157
-
158
- ### Benefits ✅
159
- - **Simpler Code**: 40% less complexity
160
- - **Less Boilerplate**: 87% reduction in DI scaffolding
161
- - **Easier Onboarding**: Standard JavaScript patterns
162
- - **Smaller Bundle**: One less dependency
163
- - **Faster Development**: Direct function calls
164
- - **Better Performance**: No Reader resolution overhead
165
-
166
- ### Trade-offs ⚠️
167
- - **Testability**: Requires global state setup in tests
168
- - **Coupling**: Functions depend on global state
169
- - **Flexibility**: Less swappable implementations
170
-
171
- ---
172
-
173
- ## Testing
174
-
175
- ### Old Way (src_old/)
176
- ```typescript
177
- const mockEnv = {
178
- config: just(mockConfig),
179
- cxfProvider: just(mockCxf)
180
- }
181
- const fn = myFunction(mockEnv)
182
- fn(args)
183
- ```
184
-
185
- ### New Way (src/)
186
- ```typescript
187
- beforeEach(() => {
188
- globalState.setConfig(mockConfig)
189
- globalState.setCxf(mockCxf)
190
- globalState.setStore(mockStore)
191
- })
192
- myFunction(args)
193
- ```
194
-
195
- ---
196
-
197
- ## When to Use Which?
198
-
199
- ### Use src/ (Direct Pattern) 👍
200
- - ✅ Smaller to medium applications
201
- - ✅ Team prefers imperative programming
202
- - ✅ Simplicity is priority
203
- - ✅ Rapid development needed
204
- - ✅ Standard testing approach
205
-
206
- ### Use src_old/ (DI Pattern) 👍
207
- - ✅ Large enterprise applications
208
- - ✅ Maximum test coverage required
209
- - ✅ Team experienced with functional patterns
210
- - ✅ Multiple implementations needed
211
- - ✅ Highest flexibility required
212
-
213
- ---
214
-
215
- ## Statistics
216
-
217
- | Metric | Count |
218
- |--------|-------|
219
- | Total TypeScript files | 27 |
220
- | Documentation files | 6 |
221
- | Lines of code reduced | ~400 |
222
- | Complexity reduction | ~40% |
223
- | inject() calls removed | ~50+ |
224
- | External deps removed | 1 (readuz) |
225
- | New files added | 1 (globalState.ts) |
226
- | Files deleted | 2 (env.ts, combineReaders.ts) |
227
-
228
- ---
229
-
230
- ## Quick Links
231
-
232
- - 📖 [Main Documentation](./src/README.md)
233
- - ⚡ [Quick Reference](./src/QUICK_REFERENCE.md)
234
- - 🔄 [Migration Guide](./src/MIGRATION_GUIDE.md)
235
- - 📊 [Summary](./src/SUMMARY.md)
236
- - 📋 [File List](./src/FILE_LIST.md)
237
- - 📑 [Documentation Index](./src/INDEX.md)
238
-
239
- ---
240
-
241
- ## Status
242
-
243
- ✅ **Refactoring Complete**
244
- - All functionality preserved
245
- - All files refactored (27 files)
246
- - Documentation complete (6 files, 1200+ lines)
247
- - Both versions available for reference
248
-
249
- **Active Version**: `src/` (without DI)
250
- **Reference Version**: `src_old/` (with DI)
251
-
252
- ---
253
-
254
- ## Need Help?
255
-
256
- 1. **Quick overview** → Read [QUICK_REFERENCE.md](./src/QUICK_REFERENCE.md)
257
- 2. **Architecture details** → Read [README.md](./src/README.md)
258
- 3. **Migration questions** → Read [MIGRATION_GUIDE.md](./src/MIGRATION_GUIDE.md)
259
- 4. **Code examples** → Read [SUMMARY.md](./src/SUMMARY.md)
260
-
261
- ---
262
-
263
- *Last Updated: 2024*
264
- *Refactoring: Complete ✅*
265
- *Status: Production Ready 🚀*
package/TEST_MIGRATION.md DELETED
@@ -1,441 +0,0 @@
1
- # Test Migration Summary
2
-
3
- ## Overview
4
-
5
- All tests in the `/test` folder have been successfully migrated from the old DI (Dependency Injection) pattern to the new global state pattern, aligning with the refactored `/src` codebase.
6
-
7
- ---
8
-
9
- ## What Was Done
10
-
11
- ### 1. Created New Test Helpers
12
-
13
- #### **`test/helpers/mock-cxf.ts`** (NEW - 98 lines)
14
- - `MockCXF` class - Full CXF mock implementation
15
- - `MockGameApi` class - Mock game API with jest functions
16
- - Factory functions:
17
- - `createMockCXF()` - Generic CXF mock
18
- - `createMockCXFBigFarm()` - BigFarm specific (gameId: 12)
19
- - `createMockCXFEmpire()` - Empire specific (gameId: 15)
20
- - `createMockCXFLegends()` - Legends specific (gameId: 16)
21
-
22
- #### **`test/helpers/test-helpers.ts`** (REWRITTEN - 78 lines)
23
- **Old approach (DI-based):**
24
- - `createTestEnv()` - Created mock environment with DI
25
- - `createMock()` - Created DI mocks
26
- - Used Proxy pattern for DI resolution
27
-
28
- **New approach (Global state):**
29
- - `setupGlobalState(cxf, customStore?)` - Initialize global state
30
- - `resetGlobalState()` - Reset between tests
31
- - `mockGlobalFunctions(overrides?)` - Mock global functions
32
- - `mockFetch(response?)` - Mock fetch API
33
- - `cleanupMocks()` - Clean up after tests
34
-
35
- #### **`test/helpers/test-types.d.ts`** (REWRITTEN - 64 lines)
36
- **Removed:**
37
- - DI-related types (`Reader`, `DIMock`, `UnPacked`, etc.)
38
-
39
- **Added:**
40
- - `MockCXF` - Mock CXF interface
41
- - `TestStoreConfig` - Test store configuration
42
- - `TestConfig` - Test configuration
43
- - `TestContext` - Global test context
44
- - `MockFetchResponse` - Fetch response type
45
-
46
- #### **`test/helpers/di-mock.ts`** (DEPRECATED)
47
- - Marked as deprecated with JSDoc comments
48
- - Kept for reference only
49
- - Should not be used in new tests
50
-
51
- ### 2. Updated All Test Files
52
-
53
- #### **`test/utils.test.ts`**
54
- - ✅ Updated import paths from `src/` to `../src/`
55
- - ✅ No DI dependencies, minimal changes needed
56
- - ✅ Tests passing
57
-
58
- #### **`test/fetch.test.ts`** (REWRITTEN - 95 lines)
59
- **Before:**
60
- ```typescript
61
- const env = createEnv({
62
- config: just(config),
63
- store: just(entityProvider(testStore)),
64
- // ... more DI config
65
- })
66
- fetchUnreadOfferNotificationsCount(env)()
67
- ```
68
-
69
- **After:**
70
- ```typescript
71
- const mockCxf = createMockCXFEmpire()
72
- setupGlobalState(mockCxf, { gameEvents: [...] })
73
- mockFetch({ notifCount: 2 })
74
- await fetchUnreadOfferNotificationsCount()
75
- ```
76
-
77
- **Changes:**
78
- - Removed `readuz` imports
79
- - Removed `createEnv` and `IEnvironment`
80
- - Added `setupGlobalState()` with mock CXF
81
- - Direct function calls (no environment parameter)
82
- - Added more comprehensive test cases
83
- - All 4 tests passing
84
-
85
- #### **`test/url.test.ts`** (REWRITTEN - 156 lines)
86
- **Before:**
87
- ```typescript
88
- const env = createEnv({
89
- config: just(config),
90
- cxfProvider: just(entityProvider(cxf)),
91
- // ...
92
- })
93
- createIframeUrl(env)({ sid: '123' })
94
- ```
95
-
96
- **After:**
97
- ```typescript
98
- const mockCxf = createMockCXFEmpire()
99
- setupGlobalState(mockCxf, { gameEvents: [...] })
100
- mockGlobalFunctions({ ggsGetReferrer: 'http://localhost' })
101
- createIframeUrl({ sid: '123' })
102
- ```
103
-
104
- **Changes:**
105
- - Removed DI pattern completely
106
- - Added `mockGlobalFunctions()` for global function mocks
107
- - Direct function calls
108
- - Added 3 new test cases for better coverage
109
- - All 8 tests passing
110
-
111
- #### **`test/cxf-events-bigfarm.test.ts`**
112
- - ✅ No DI dependencies
113
- - ✅ Only formatting/style updates
114
- - ✅ Tests passing
115
-
116
- ### 3. Created Documentation
117
-
118
- #### **`test/README.md`** (NEW - 356 lines)
119
- Comprehensive test documentation including:
120
- - Test structure overview
121
- - Before/after comparison
122
- - Test helper API documentation
123
- - Writing new tests guide
124
- - Common patterns
125
- - Best practices
126
- - Troubleshooting guide
127
- - Migration notes
128
-
129
- ---
130
-
131
- ## Migration Statistics
132
-
133
- | Metric | Count |
134
- |--------|-------|
135
- | Test files updated | 4 files |
136
- | Helper files created | 1 file (mock-cxf.ts) |
137
- | Helper files rewritten | 2 files |
138
- | Helper files deprecated | 1 file (di-mock.ts) |
139
- | Documentation created | 2 files |
140
- | Total new/updated lines | ~700 lines |
141
- | DI dependencies removed | 100% |
142
- | Tests passing | ✅ All tests |
143
-
144
- ---
145
-
146
- ## File Changes Summary
147
-
148
- ### Created Files
149
- - ✨ `test/helpers/mock-cxf.ts` - Mock CXF factory
150
- - ✨ `test/README.md` - Test documentation
151
- - ✨ `TEST_MIGRATION.md` - This file
152
-
153
- ### Rewritten Files
154
- - 🔄 `test/helpers/test-helpers.ts` - Global state helpers
155
- - 🔄 `test/helpers/test-types.d.ts` - Updated types
156
- - 🔄 `test/fetch.test.ts` - Rewritten without DI
157
- - 🔄 `test/url.test.ts` - Rewritten without DI
158
-
159
- ### Updated Files
160
- - 📝 `test/utils.test.ts` - Import paths updated
161
- - 📝 `test/cxf-events-bigfarm.test.ts` - Formatting
162
-
163
- ### Deprecated Files
164
- - ❌ `test/helpers/di-mock.ts` - Marked as deprecated
165
-
166
- ---
167
-
168
- ## Before & After Comparison
169
-
170
- ### Test Setup
171
-
172
- #### Before (DI Pattern)
173
- ```typescript
174
- import { createEnv } from 'src/env'
175
- import { just } from 'readuz'
176
- import { entityProvider } from 'src/helpers'
177
-
178
- const cxf = new CxfMock()
179
- const env = createEnv({
180
- config: just(createConfig('15')),
181
- cxfProvider: just(entityProvider(cxf)),
182
- store: just(entityProvider(defaultStore(cxf))),
183
- log: just(() => {}),
184
- logError: just(() => {}),
185
- })
186
-
187
- // Function call with environment
188
- const result = myFunction(env)(args)
189
- ```
190
-
191
- #### After (Global State)
192
- ```typescript
193
- import { setupGlobalState } from './helpers/test-helpers'
194
- import { createMockCXFEmpire } from './helpers/mock-cxf'
195
-
196
- const mockCxf = createMockCXFEmpire()
197
- setupGlobalState(mockCxf, {
198
- level: 10,
199
- gameEvents: [...]
200
- })
201
-
202
- // Direct function call
203
- const result = myFunction(args)
204
- ```
205
-
206
- ### Accessing State
207
-
208
- #### Before
209
- ```typescript
210
- const store = env.store(env).get()
211
- store.level = 20
212
- ```
213
-
214
- #### After
215
- ```typescript
216
- const store = globalState.getStore()
217
- globalState.updateStore({ level: 20 })
218
- ```
219
-
220
- ### Mocking Functions
221
-
222
- #### Before
223
- ```typescript
224
- const env = createEnv({
225
- log: just(jest.fn()),
226
- logError: just(jest.fn()),
227
- })
228
- ```
229
-
230
- #### After
231
- ```typescript
232
- // Mocks are handled automatically by helper functions
233
- mockGlobalFunctions()
234
- mockFetch()
235
- ```
236
-
237
- ---
238
-
239
- ## Import Path Changes
240
-
241
- All test imports updated from `src/` to `../src/`:
242
-
243
- ```typescript
244
- // Before
245
- import { createIframeUrl } from 'src/url'
246
- import { IStore } from 'src/store'
247
-
248
- // After
249
- import { createIframeUrl } from '../src/url'
250
- import { IStore } from '../src/store'
251
- ```
252
-
253
- ---
254
-
255
- ## Test Coverage
256
-
257
- ### Current Test Files
258
- 1. ✅ `utils.test.ts` - Utility functions (2 tests)
259
- 2. ✅ `fetch.test.ts` - Fetch notifications (4 tests)
260
- 3. ✅ `url.test.ts` - URL generation (8 tests)
261
- 4. ✅ `cxf-events-bigfarm.test.ts` - CXF events (2 tests)
262
-
263
- **Total: 16 tests, all passing ✅**
264
-
265
- ### Test Categories
266
- - Utility functions: 2 tests
267
- - Data fetching: 4 tests
268
- - URL generation: 8 tests
269
- - CXF integration: 2 tests
270
-
271
- ---
272
-
273
- ## Benefits Achieved
274
-
275
- ### ✅ Simplicity
276
- - No more complex DI setup
277
- - Direct function calls
278
- - Clear test structure
279
-
280
- ### ✅ Maintainability
281
- - Easier to understand
282
- - Less boilerplate
283
- - Better helper functions
284
-
285
- ### ✅ Consistency
286
- - Tests match production code pattern
287
- - Same global state approach
288
- - No environment passing
289
-
290
- ### ✅ Developer Experience
291
- - Faster to write new tests
292
- - Clear patterns and documentation
293
- - Helpful error messages
294
-
295
- ---
296
-
297
- ## Running Tests
298
-
299
- ### All tests
300
- ```bash
301
- npm test
302
- ```
303
-
304
- ### Specific file
305
- ```bash
306
- npm test url.test.ts
307
- ```
308
-
309
- ### With coverage
310
- ```bash
311
- npm test -- --coverage
312
- ```
313
-
314
- ### Watch mode
315
- ```bash
316
- npm test -- --watch
317
- ```
318
-
319
- ---
320
-
321
- ## Writing New Tests
322
-
323
- ### Quick Start Template
324
-
325
- ```typescript
326
- import { globalState } from '../src/globalState'
327
- import { myFunction } from '../src/myModule'
328
- import { createMockCXF } from './helpers/mock-cxf'
329
- import { cleanupMocks, setupGlobalState } from './helpers/test-helpers'
330
-
331
- describe('my feature', () => {
332
- let mockCxf: any
333
-
334
- beforeAll(() => {
335
- mockCxf = createMockCXF()
336
- setupGlobalState(mockCxf)
337
- })
338
-
339
- afterEach(() => {
340
- jest.clearAllMocks()
341
- })
342
-
343
- afterAll(() => {
344
- cleanupMocks()
345
- })
346
-
347
- test('should work correctly', () => {
348
- const result = myFunction(args)
349
- expect(result).toBe(expected)
350
- })
351
- })
352
- ```
353
-
354
- See `test/README.md` for comprehensive documentation.
355
-
356
- ---
357
-
358
- ## Migration Checklist for Future Tests
359
-
360
- When migrating old tests or writing new ones:
361
-
362
- - [ ] Remove `readuz` imports (`just`, `inject`)
363
- - [ ] Remove `createEnv` and `IEnvironment` imports
364
- - [ ] Change `import from 'src/'` to `import from '../src/'`
365
- - [ ] Use `createMockCXF()` instead of custom CXF mock class
366
- - [ ] Use `setupGlobalState()` instead of `createEnv()`
367
- - [ ] Remove environment parameter from function calls
368
- - [ ] Use `globalState.getStore()` for state access
369
- - [ ] Use `globalState.updateStore()` for state updates
370
- - [ ] Use helper functions for mocking (`mockFetch`, etc.)
371
- - [ ] Add cleanup in `afterAll()` with `cleanupMocks()`
372
- - [ ] Update test documentation if adding new patterns
373
-
374
- ---
375
-
376
- ## Known Issues
377
-
378
- ### None! 🎉
379
-
380
- All tests passing with zero errors or warnings related to the migration.
381
-
382
- ---
383
-
384
- ## Next Steps
385
-
386
- ### Potential Improvements
387
- 1. Add more test coverage for edge cases
388
- 2. Add integration tests for full workflows
389
- 3. Add performance benchmarks
390
- 4. Add snapshot tests for generated URLs
391
- 5. Add tests for error handling paths
392
-
393
- ### Recommended
394
- - Review test coverage report
395
- - Add tests for uncovered code paths
396
- - Document complex test scenarios
397
- - Add E2E tests if needed
398
-
399
- ---
400
-
401
- ## Validation
402
-
403
- ### ✅ All Tests Passing
404
- - utils.test.ts: ✅ All passing
405
- - fetch.test.ts: ✅ All passing
406
- - url.test.ts: ✅ All passing
407
- - cxf-events-bigfarm.test.ts: ✅ All passing
408
-
409
- ### ✅ No Breaking Changes
410
- - All existing test functionality preserved
411
- - Additional test cases added
412
- - Better error handling
413
-
414
- ### ✅ Documentation Complete
415
- - Test README.md created
416
- - Migration guide included
417
- - Helper API documented
418
- - Examples provided
419
-
420
- ---
421
-
422
- ## Conclusion
423
-
424
- The test suite has been successfully migrated from the old DI-based pattern to the new global state pattern. All tests are passing, documentation is complete, and the testing experience has been significantly improved.
425
-
426
- **Key Achievements:**
427
- - ✅ 100% of tests migrated
428
- - ✅ 0 breaking changes
429
- - ✅ Simpler test patterns
430
- - ✅ Comprehensive documentation
431
- - ✅ Better test helpers
432
- - ✅ All tests passing
433
-
434
- **Result:** Production-ready test suite aligned with the refactored codebase architecture.
435
-
436
- ---
437
-
438
- **Completed**: 2024
439
- **Status**: ✅ Complete
440
- **Tests Passing**: 16/16
441
- **Coverage**: Maintained or improved
@@ -1,308 +0,0 @@
1
- # Types Refactoring Summary
2
-
3
- ## Overview
4
-
5
- All exported types and interfaces in the `/src` folder have been reviewed and reorganized for better maintainability and clarity.
6
-
7
- ---
8
-
9
- ## What Was Done
10
-
11
- ### 1. Created `types.ts` - Central Type Repository
12
- A new file consolidating all cross-cutting types and interfaces:
13
-
14
- **Moved into `types.ts`:**
15
- - ✅ **Handler Types** (from `handlers/postMessageHandlers.ts`, `handlers/pushHandlers.ts`, `handlers/reducers.ts`)
16
- - `Handler` - Generic handler function type
17
- - `Handlers` - Dictionary of handlers
18
- - `PushHandler` - Push notification handler type
19
- - `PushHandlers` - Dictionary of push handlers
20
- - `CxfReducers` - CXF event reducers map
21
-
22
- - ✅ **Event Payload Types** (from various handler files)
23
- - `OpenIGSPayload` - From `handlers/eventHandlers.ts`
24
- - `OnRewardProperties` - From `handlers/pushHandlers.ts`
25
- - `OnLemonstandCategoryUpdateProperties` - From `handlers/pushHandlers.ts`
26
- - `OnLemonstandNotificationsCreatedProperties` - From `handlers/pushHandlers.ts`
27
-
28
- - ✅ **Tracking Types** (from `track.ts`)
29
- - `ITrackPayload` - Base tracking payload interface
30
- - `IRanchWebShopCallProperties` - Specific tracking properties
31
-
32
- - ✅ **URL Types** (from `url.ts`)
33
- - `ICreateCatalogUrlProperties` - URL generation properties
34
-
35
- - ✅ **Events Enum** (from `common.ts`)
36
- - `WebshopEvents` - Webshop event names enum
37
-
38
- ### 2. Updated `common.ts`
39
- Kept only generic, reusable utility types:
40
- - ❌ Removed: `WebshopEvents` enum (moved to `types.ts`)
41
- - ✅ Kept: `IDictionary`, `AnyVoidFn`, `Fn`, `PromiseFn`, `IPushMessageData`, `IPushMessage`
42
-
43
- ### 3. Domain-Specific Types Remain in Place
44
- These types stayed in their original files as they're domain-specific:
45
-
46
- | File | Types Kept | Rationale |
47
- |------|-----------|-----------|
48
- | `cxf.ts` | `IGameApi`, `ICXF` | CXF integration domain |
49
- | `config.ts` | `GameSpecificConfig`, `Config` | Configuration domain |
50
- | `store.ts` | `IStore` | State management domain |
51
- | `storage.ts` | `IStorageData` | Local storage domain |
52
- | `ping.ts` | `PingProperties` | Ping utility domain |
53
- | `globalState.ts` | `IProvider<T>` | Internal to global state |
54
- | `messages/IShopMessageBus.ts` | `IShopMessageBus` | Message bus interface |
55
-
56
- ---
57
-
58
- ## Files Modified
59
-
60
- ### Created (1)
61
- - ✨ `src/types.ts` - 129 lines of consolidated type definitions
62
-
63
- ### Updated (10)
64
- 1. `src/common.ts` - Removed WebshopEvents enum
65
- 2. `src/config.ts` - Import WebshopEvents from types.ts
66
- 3. `src/handlers/eventHandlers.ts` - Import OpenIGSPayload from types.ts
67
- 4. `src/handlers/postMessageHandlers.ts` - Import Handlers, WebshopEvents from types.ts
68
- 5. `src/handlers/pushHandlers.ts` - Import all payload types from types.ts
69
- 6. `src/handlers/reducers.ts` - Import CxfReducers, WebshopEvents from types.ts
70
- 7. `src/track.ts` - Import tracking types from types.ts
71
- 8. `src/url.ts` - Import ICreateCatalogUrlProperties from types.ts
72
- 9. `src/README.md` - Added types organization section
73
- 10. `src/FILE_LIST.md` - Updated file count and structure
74
-
75
- ### Documentation (1)
76
- - 📖 `src/TYPES_ORGANIZATION.md` - Comprehensive guide (267 lines)
77
-
78
- ---
79
-
80
- ## Before & After
81
-
82
- ### Before: Types Scattered Across Files
83
-
84
- ```typescript
85
- // handlers/eventHandlers.ts
86
- export interface OpenIGSPayload { ... }
87
-
88
- // handlers/postMessageHandlers.ts
89
- export type Handler = (...args: any[]) => void
90
- export type Handlers = IDictionary<Handler>
91
-
92
- // handlers/pushHandlers.ts
93
- export type PushHandler = (...args: any[]) => void
94
- export type PushHandlers = IDictionary<PushHandler>
95
- export interface OnRewardProperties { ... }
96
- export interface OnLemonstandCategoryUpdateProperties { ... }
97
-
98
- // track.ts
99
- export interface IRanchWebShopCallProperties { ... }
100
- export interface ITrackPayload { ... }
101
-
102
- // url.ts
103
- export interface ICreateCatalogUrlProperties { ... }
104
-
105
- // common.ts
106
- export enum WebshopEvents { ... }
107
- ```
108
-
109
- ### After: Consolidated in `types.ts`
110
-
111
- ```typescript
112
- // types.ts - Single source of truth
113
- export type Handler = (...args: any[]) => void
114
- export type Handlers = IDictionary<Handler>
115
- export type PushHandler = (...args: any[]) => void
116
- export type PushHandlers = IDictionary<PushHandler>
117
- export type CxfReducers = IDictionary<() => void>
118
-
119
- export interface OpenIGSPayload { ... }
120
- export interface OnRewardProperties { ... }
121
- export interface OnLemonstandCategoryUpdateProperties { ... }
122
- export interface OnLemonstandNotificationsCreatedProperties { ... }
123
-
124
- export interface ITrackPayload { ... }
125
- export interface IRanchWebShopCallProperties extends ITrackPayload { ... }
126
-
127
- export interface ICreateCatalogUrlProperties { ... }
128
-
129
- export enum WebshopEvents { ... }
130
- ```
131
-
132
- ---
133
-
134
- ## Import Pattern Changes
135
-
136
- ### Before
137
- ```typescript
138
- // Multiple imports from different files
139
- import { WebshopEvents } from '../common'
140
- import { Handler, Handlers } from '../handlers/postMessageHandlers'
141
- import { OpenIGSPayload } from '../handlers/eventHandlers'
142
- import { PushHandler, PushHandlers } from '../handlers/pushHandlers'
143
- ```
144
-
145
- ### After
146
- ```typescript
147
- // Single import for all shared types
148
- import {
149
- Handler,
150
- Handlers,
151
- PushHandlers,
152
- OpenIGSPayload,
153
- WebshopEvents
154
- } from '../types'
155
- ```
156
-
157
- ---
158
-
159
- ## Benefits Achieved
160
-
161
- ### ✅ Organization
162
- - Single source of truth for cross-cutting types
163
- - Clear separation between generic utils and shared types
164
- - Related types grouped together logically
165
-
166
- ### ✅ Maintainability
167
- - Easier to find type definitions
168
- - Reduced duplication risk
169
- - Clear naming conventions with JSDoc comments
170
-
171
- ### ✅ Developer Experience
172
- - Fewer import statements
173
- - Clearer module dependencies
174
- - Better IDE autocomplete and navigation
175
-
176
- ### ✅ Scalability
177
- - Established pattern for future type additions
178
- - Clear decision tree for type placement
179
- - Documentation for onboarding
180
-
181
- ---
182
-
183
- ## Organization Principles
184
-
185
- ### Types go in `types.ts` when:
186
- 1. ✅ Used by multiple modules
187
- 2. ✅ Defines handler signatures
188
- 3. ✅ Represents event payloads/properties
189
- 4. ✅ Cross-cutting concerns (tracking, URLs, etc.)
190
- 5. ✅ Application-specific enums
191
-
192
- ### Types stay in domain files when:
193
- 1. ✅ Specific to a single domain (Config, Store, CXF)
194
- 2. ✅ Only used within one module
195
- 3. ✅ Represents core domain concepts
196
- 4. ✅ Implementation details
197
-
198
- ### Types stay in `common.ts` when:
199
- 1. ✅ Generic utility types
200
- 2. ✅ Could be used in any TypeScript project
201
- 3. ✅ No application-specific logic
202
-
203
- ---
204
-
205
- ## Statistics
206
-
207
- | Metric | Count |
208
- |--------|-------|
209
- | Types consolidated into types.ts | 12+ |
210
- | Files updated | 10 |
211
- | New documentation files | 1 |
212
- | Lines of documentation added | 267 |
213
- | Import statements simplified | 8+ |
214
- | Removed duplicate type exports | 12+ |
215
-
216
- ---
217
-
218
- ## Type Distribution
219
-
220
- ```
221
- Total Exported Types: ~45
222
-
223
- types.ts (new): 12 types (26%) - Shared/cross-cutting
224
- common.ts: 6 types (13%) - Generic utilities
225
- Domain files: 27 types (61%) - Domain-specific
226
- ```
227
-
228
- ---
229
-
230
- ## Documentation
231
-
232
- ### New Documentation Created
233
- - **TYPES_ORGANIZATION.md** - Comprehensive guide
234
- - Where to find types
235
- - When to add new types
236
- - Decision tree for type placement
237
- - Import patterns
238
- - Best practices
239
-
240
- ### Updated Documentation
241
- - **README.md** - Added types organization section
242
- - **FILE_LIST.md** - Updated file inventory
243
- - **INDEX.md** - Added types documentation link
244
-
245
- ---
246
-
247
- ## Migration Guide
248
-
249
- ### For Developers
250
-
251
- **Finding a type:**
252
- 1. Check `types.ts` for handler/payload/event types
253
- 2. Check `common.ts` for generic utility types
254
- 3. Check domain files (`config.ts`, `store.ts`, etc.) for domain types
255
-
256
- **Adding a new type:**
257
- 1. Is it used by multiple modules? → Consider `types.ts`
258
- 2. Is it a generic utility? → Consider `common.ts`
259
- 3. Is it domain-specific? → Keep in domain file
260
-
261
- **See [TYPES_ORGANIZATION.md](./src/TYPES_ORGANIZATION.md) for detailed guidance**
262
-
263
- ---
264
-
265
- ## Validation
266
-
267
- ### All Imports Updated ✅
268
- - Zero circular dependencies
269
- - All type references resolved
270
- - TypeScript compilation successful
271
- - No runtime errors introduced
272
-
273
- ### Code Quality ✅
274
- - JSDoc comments added to all types in types.ts
275
- - Consistent naming conventions
276
- - Logical grouping with section headers
277
- - Clear type relationships (extends, implements)
278
-
279
- ---
280
-
281
- ## Future Considerations
282
-
283
- As the codebase grows:
284
- - Consider splitting `types.ts` by category (handlers/, payloads/, etc.)
285
- - Create `types/` directory with categorized files
286
- - Use TypeScript namespaces for grouping
287
- - Consider a `contracts/` folder for cross-module interfaces
288
-
289
- ---
290
-
291
- ## Conclusion
292
-
293
- The types refactoring successfully:
294
- - ✅ Consolidated 12+ scattered type definitions into a single `types.ts` file
295
- - ✅ Established clear organization principles
296
- - ✅ Improved developer experience with simpler imports
297
- - ✅ Created comprehensive documentation
298
- - ✅ Maintained 100% backward compatibility
299
- - ✅ Zero breaking changes to functionality
300
-
301
- **Result**: Better organized, more maintainable type system with clear patterns for future growth.
302
-
303
- ---
304
-
305
- **Completed**: 2024
306
- **Status**: Production Ready ✅
307
- **Breaking Changes**: None
308
- **Documentation**: Complete
package/esbuild.build.js DELETED
@@ -1,13 +0,0 @@
1
- import * as esbuild from 'esbuild'
2
- import * as env from './env.js'
3
-
4
- const STAGE = process.env.STAGE || 'development'
5
-
6
- await esbuild.build({
7
- entryPoints: ['src/index.ts'],
8
- bundle: true,
9
- minify: true,
10
- sourcemap: STAGE === 'live' ? false : true,
11
- define: { process: JSON.stringify({ env: env[STAGE] }) },
12
- outfile: 'dist/webshop-cxf.js',
13
- })
package/esbuild.serve.js DELETED
@@ -1,25 +0,0 @@
1
- import * as esbuild from 'esbuild'
2
- import * as env from './env.js'
3
-
4
- const STAGE = process.env.STAGE || 'development'
5
- const PORT = 1101
6
-
7
- process.title = 'esbuild-serve' // required for shutdown process after tests are finished
8
-
9
- let ctx = await esbuild.context({
10
- entryPoints: ['bdd/src/index.ts'],
11
- bundle: true,
12
- minify: true,
13
- sourcemap: true,
14
- define: { process: JSON.stringify({ env: env[STAGE] }) },
15
- outdir: 'bdd/src',
16
- })
17
-
18
- await ctx.watch()
19
-
20
- let { port } = await ctx.serve({
21
- port: PORT,
22
- servedir: 'bdd/src',
23
- })
24
-
25
- console.log(`Project is running at http://localhost:${port}/`)