@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.
- package/dist/webshop-cxf.js.map +1 -1
- package/package.json +3 -2
- package/.prettierignore +0 -6
- package/.prettierrc.js +0 -10
- package/REFACTORING_README.md +0 -265
- package/TEST_MIGRATION.md +0 -441
- package/TYPES_REFACTORING.md +0 -308
- package/esbuild.build.js +0 -13
- package/esbuild.serve.js +0 -25
package/dist/webshop-cxf.js.map
CHANGED
|
@@ -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.
|
|
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
package/.prettierrc.js
DELETED
package/REFACTORING_README.md
DELETED
|
@@ -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
|
package/TYPES_REFACTORING.md
DELETED
|
@@ -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}/`)
|