@looplay/sdk 0.7.0 → 0.8.2

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/README.md CHANGED
@@ -10,8 +10,7 @@ API Reference: https://docs.looplay.gg/build-on-loopplay/looplay-sdk
10
10
  - [Core concept: one SDK integration = one game](#core-concept-one-sdk-integration--one-game)
11
11
  - [Tracking a game — hosted on Looplay or published anywhere else](#tracking-a-game--hosted-on-looplay-or-published-anywhere-else)
12
12
  - [`LooplaySDK` — direct integration (your own backend/auth)](#looplaysdk--direct-integration-your-own-backendauth)
13
- - [Store — read-only browsing](#store--read-only-browsing)
14
- - [Purchases require your own backend](#purchases-require-your-own-backend)
13
+ - [Store — browsing and purchases](#store--browsing-and-purchases)
15
14
  - [Rewarded ads via parent window](#rewarded-ads-via-parent-window)
16
15
  - [Drop-in `<script>` tag (no build step required)](#drop-in-script-tag-no-build-step-required)
17
16
  - [Auth storage tradeoff](#auth-storage-tradeoff)
@@ -128,34 +127,100 @@ const profile = await sdk.getMyProfile();
128
127
  throws if the game doesn't exist or isn't live — a lightweight, read-only,
129
128
  unauthenticated check. Use `'none'` (default) to skip it.
130
129
 
131
- ## Store — read-only browsing
130
+ ## Store — browsing and purchases
132
131
 
133
- The SDK exposes read-only access to the game's coin store — storefront,
134
- public asset catalog, and (when authenticated) the caller's own owned
135
- assets and purchase history:
132
+ The SDK exposes the game's coin store — storefront, public asset catalog,
133
+ the caller's own owned assets/purchase history, and (for a logged-in
134
+ player) purchasing an offer:
136
135
 
137
136
  ```ts
138
- const store = await sdk.api!.getStore(gameId); // sections + offers, no auth required
139
- const assets = await sdk.api!.listGameAssets(gameId); // public asset catalog, no auth required
140
- const owned = await sdk.api!.listMyGameAssets(gameId); // requires a logged-in user
137
+ const store = await sdk.api!.getStore(gameId); // sections + offers, no auth required
138
+ const assets = await sdk.api!.listGameAssets(gameId); // public asset catalog, no auth required
139
+ const owned = await sdk.api!.listMyGameAssets(gameId); // requires a logged-in user
141
140
  const purchases = await sdk.api!.listMyStorePurchases(gameId); // requires a logged-in user
141
+
142
+ // Spends the player's coin balance — requires a logged-in user, nothing
143
+ // else. No backend or secret needed: safe to call directly from the game.
144
+ const result = await sdk.api!.purchaseStoreOffer(gameId, 'starter_pack', { quantity: 1 });
145
+ ```
146
+
147
+ `purchaseStoreOffer` signs a short-lived checkout token and redeems it in
148
+ one call — the same safety mechanism as the play-attempt token used for
149
+ tracking, not a static secret. This is enough for creators with no backend
150
+ of their own.
151
+
152
+ ### Real-time updates (optional, separate import)
153
+
154
+ Purchases (and any other balance change — quest rewards, referral payouts,
155
+ ...) also push a WebSocket event, useful for reacting from a different tab
156
+ than the one that made the call, or after a purchase completes out-of-band
157
+ (e.g. a hosted-checkout redirect flow). This lives in a **separate
158
+ `@looplay/sdk/realtime` subpath**, not the main `@looplay/sdk` import — it
159
+ pulls in `socket.io-client`, which most integrations (tracking-only or
160
+ store-browsing-only) don't need:
161
+
162
+ ```bash
163
+ npm install socket.io-client # peer dependency, only needed if you import '@looplay/sdk/realtime'
164
+ ```
165
+
166
+ ```ts
167
+ import { WsClient } from '@looplay/sdk/realtime';
168
+
169
+ const ws = new WsClient({
170
+ baseUrl: 'https://api.looplay.gg',
171
+ getAccessToken: () => sdk.getAccessToken(), // reuse whatever you already pass to LooplaySDK/ApiClient
172
+ });
173
+
174
+ const unsubscribe = ws.onStorePurchase((event) => {
175
+ console.log(`Got ${event.quantity}x ${event.name}`, event.items);
176
+ });
177
+
178
+ ws.onBalanceChange((event) => {
179
+ console.log('Balance changed', event.changeAmount, event.balanceType);
180
+ });
181
+
182
+ // later
183
+ unsubscribe();
142
184
  ```
143
185
 
144
- ## Purchases require your own backend
186
+ The connection is opened lazily on the first `onBalanceChange`/
187
+ `onStorePurchase` call and closed once the last listener unsubscribes — a
188
+ `WsClient` you never subscribe on never opens a socket. Call
189
+ `ws.disconnect()` to close it immediately regardless of active listeners.
190
+
191
+ ### Backend-initiated purchases (optional)
192
+
193
+ If you *do* have your own backend and want to initiate a purchase without
194
+ the player's browser involved (e.g. an admin grant, or a payment webhook),
195
+ use `ServiceClient`'s creator+game secret-pair methods instead — never
196
+ embed `x-creator-key`/`x-game-secret` in client/browser code:
197
+
198
+ ```ts
199
+ import { ServiceClient } from '@looplay/sdk';
145
200
 
146
- There is **no purchase call in this SDK** — spending a player's coin
147
- balance always requires the creator+game *secret* pair
148
- (`x-creator-key`/`x-game-secret`), which must never be embedded in
149
- client/browser code. To let players buy a store offer:
201
+ const service = new ServiceClient({ baseUrl: 'https://api.looplay.gg' });
150
202
 
151
- 1. Implement a checkout endpoint on your own backend.
152
- 2. From that backend, call gbs-service directly with your secret pair:
153
- `POST /sdk/games/store/checkout-intent` then `POST /sdk/games/store/purchase`.
154
- 3. Have the game client call your backend endpoint, not gbs-service
155
- directly, for the purchase step.
203
+ const auth = {
204
+ creatorKey: process.env.LOOPLAY_CREATOR_KEY!,
205
+ gameSecret: process.env.LOOPLAY_GAME_SECRET!,
206
+ bearerToken: playerAccessToken, // the specific player you're purchasing on behalf of
207
+ };
156
208
 
157
- Browsing (storefront, catalog, owned assets, purchase history) is safe
158
- client-side and covered by the read-only calls above.
209
+ const intent = await service.createGameStoreCheckoutIntent(auth, {
210
+ offerCode: 'starter_pack',
211
+ quantity: 1,
212
+ });
213
+
214
+ const result = await service.purchaseGameStoreOffer(auth, {
215
+ offerCode: intent.offerCode,
216
+ requestId: intent.requestId,
217
+ quantity: intent.quantity,
218
+ checkoutToken: intent.checkoutToken,
219
+ });
220
+ ```
221
+
222
+ The player (if connected) receives the same `store_purchase_completed`
223
+ WebSocket event either way.
159
224
 
160
225
  ## Rewarded ads via parent window
161
226
 
@@ -222,6 +287,21 @@ directly, no bundler needed:
222
287
  </script>
223
288
  ```
224
289
 
290
+ This bundle does **not** include real-time (`WsClient`)/`socket.io-client` —
291
+ that's a separate opt-in browser bundle, only needed if you want
292
+ `onBalanceChange`/`onStorePurchase`:
293
+
294
+ ```html
295
+ <script src="https://unpkg.com/@looplay/sdk/realtime/browser"></script>
296
+ <script>
297
+ const ws = new LooplaySDKRealtime.WsClient({
298
+ baseUrl: 'https://api.looplay.gg',
299
+ getAccessToken: async () => myAccessToken,
300
+ });
301
+ ws.onStorePurchase((event) => console.log('purchased', event));
302
+ </script>
303
+ ```
304
+
225
305
  **Security note**: the origin check relies on `parentOrigin` (explicit option)
226
306
  or `document.referrer`. Referrer can be stripped by `Referrer-Policy`, browser
227
307
  privacy settings, or extensions — when that happens the check is skipped and
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ var socket_ioClient = require('socket.io-client');
4
+
5
+ // src/apps/ws-client.ts
6
+ var WsClient = class {
7
+ constructor(options) {
8
+ this.options = options;
9
+ }
10
+ options;
11
+ socket;
12
+ connectPromise;
13
+ balanceListeners = /* @__PURE__ */ new Set();
14
+ purchaseListeners = /* @__PURE__ */ new Set();
15
+ /** Fires on every balance change — purchases, quest rewards, referral payouts, etc. */
16
+ onBalanceChange(listener) {
17
+ this.balanceListeners.add(listener);
18
+ void this.ensureConnected();
19
+ return () => {
20
+ this.balanceListeners.delete(listener);
21
+ this.disconnectIfIdle();
22
+ };
23
+ }
24
+ /** Fires once a store purchase finishes — see `StorePurchaseCompletedEvent`. */
25
+ onStorePurchase(listener) {
26
+ this.purchaseListeners.add(listener);
27
+ void this.ensureConnected();
28
+ return () => {
29
+ this.purchaseListeners.delete(listener);
30
+ this.disconnectIfIdle();
31
+ };
32
+ }
33
+ /** Closes the socket immediately, regardless of active listeners. */
34
+ disconnect() {
35
+ this.socket?.disconnect();
36
+ this.socket = void 0;
37
+ this.connectPromise = void 0;
38
+ }
39
+ async ensureConnected() {
40
+ if (this.socket?.connected) return this.socket;
41
+ if (this.connectPromise) return this.connectPromise;
42
+ this.connectPromise = (async () => {
43
+ const token = await this.options.getAccessToken();
44
+ if (!token) return void 0;
45
+ const socket = socket_ioClient.io(this.options.baseUrl, {
46
+ transports: ["websocket"],
47
+ auth: { token: `Bearer ${token}` }
48
+ });
49
+ socket.on("balance_change", (payload) => {
50
+ for (const listener of this.balanceListeners) listener(payload);
51
+ });
52
+ socket.on("store_purchase_completed", (payload) => {
53
+ for (const listener of this.purchaseListeners) listener(payload);
54
+ });
55
+ this.socket = socket;
56
+ return socket;
57
+ })();
58
+ try {
59
+ return await this.connectPromise;
60
+ } finally {
61
+ this.connectPromise = void 0;
62
+ }
63
+ }
64
+ disconnectIfIdle() {
65
+ if (this.balanceListeners.size === 0 && this.purchaseListeners.size === 0) {
66
+ this.disconnect();
67
+ }
68
+ }
69
+ };
70
+
71
+ exports.WsClient = WsClient;
72
+ //# sourceMappingURL=looplay-sdk-realtime.cjs.js.map
73
+ //# sourceMappingURL=looplay-sdk-realtime.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/apps/ws-client.ts"],"names":["io"],"mappings":";;;;;AAsBO,IAAM,WAAN,MAAe;AAAA,EAMpB,YAA6B,OAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAA2B;AAAA,EAA3B,OAAA;AAAA,EALrB,MAAA;AAAA,EACA,cAAA;AAAA,EACS,gBAAA,uBAAuB,GAAA,EAAkC;AAAA,EACzD,iBAAA,uBAAwB,GAAA,EAA2C;AAAA;AAAA,EAKpF,gBAAgB,QAAA,EAAoD;AAClE,IAAA,IAAA,CAAK,gBAAA,CAAiB,IAAI,QAAQ,CAAA;AAClC,IAAA,KAAK,KAAK,eAAA,EAAgB;AAC1B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,gBAAA,CAAiB,OAAO,QAAQ,CAAA;AACrC,MAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,IACxB,CAAA;AAAA,EACF;AAAA;AAAA,EAGA,gBAAgB,QAAA,EAA6D;AAC3E,IAAA,IAAA,CAAK,iBAAA,CAAkB,IAAI,QAAQ,CAAA;AACnC,IAAA,KAAK,KAAK,eAAA,EAAgB;AAC1B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,iBAAA,CAAkB,OAAO,QAAQ,CAAA;AACtC,MAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,IACxB,CAAA;AAAA,EACF;AAAA;AAAA,EAGA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,QAAQ,UAAA,EAAW;AACxB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAA;AAAA,EACxB;AAAA,EAEA,MAAc,eAAA,GAA+C;AAC3D,IAAA,IAAI,IAAA,CAAK,MAAA,EAAQ,SAAA,EAAW,OAAO,IAAA,CAAK,MAAA;AACxC,IAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,OAAO,IAAA,CAAK,cAAA;AAErC,IAAA,IAAA,CAAK,kBAAkB,YAAY;AACjC,MAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,OAAA,CAAQ,cAAA,EAAe;AAGhD,MAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AAEnB,MAAA,MAAM,MAAA,GAASA,kBAAA,CAAG,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS;AAAA,QACtC,UAAA,EAAY,CAAC,WAAW,CAAA;AAAA,QACxB,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAG,OAClC,CAAA;AAED,MAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,OAAA,KAAgC;AAC3D,QAAA,KAAA,MAAW,QAAA,IAAY,IAAA,CAAK,gBAAA,EAAkB,QAAA,CAAS,OAAO,CAAA;AAAA,MAChE,CAAC,CAAA;AACD,MAAA,MAAA,CAAO,EAAA,CAAG,0BAAA,EAA4B,CAAC,OAAA,KAAyC;AAC9E,QAAA,KAAA,MAAW,QAAA,IAAY,IAAA,CAAK,iBAAA,EAAmB,QAAA,CAAS,OAAO,CAAA;AAAA,MACjE,CAAC,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,MAAA,OAAO,MAAA;AAAA,IACT,CAAA,GAAG;AAEH,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,IAAA,CAAK,cAAA;AAAA,IACpB,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,cAAA,GAAiB,MAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,gBAAA,GAAyB;AAC/B,IAAA,IAAI,KAAK,gBAAA,CAAiB,IAAA,KAAS,KAAK,IAAA,CAAK,iBAAA,CAAkB,SAAS,CAAA,EAAG;AACzE,MAAA,IAAA,CAAK,UAAA,EAAW;AAAA,IAClB;AAAA,EACF;AACF","file":"looplay-sdk-realtime.cjs.js","sourcesContent":["import { io, type Socket } from 'socket.io-client';\nimport type { BalanceChangeEvent, StorePurchaseCompletedEvent } from './LooplaySDK.types';\n\nexport interface WsClientOptions {\n baseUrl: string;\n /** Resolves the current player's access token — re-checked on every (re)connect. */\n getAccessToken: () => Promise<string | undefined>;\n}\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * Lazy-connecting Socket.IO client mirroring gbs-service's AppGateway.\n * Both events it exposes (`balance_change`, `store_purchase_completed`) are\n * pushed only to the authenticated player's own room — there is nothing to\n * receive without an access token, so `subscribe*` is a no-op until one is\n * available.\n *\n * The connection is opened on the first subscription and closed once the\n * last listener unsubscribes — a game that never calls `onBalanceChange`/\n * `onStorePurchase` never opens a socket at all.\n */\nexport class WsClient {\n private socket?: Socket;\n private connectPromise?: Promise<Socket | undefined>;\n private readonly balanceListeners = new Set<Listener<BalanceChangeEvent>>();\n private readonly purchaseListeners = new Set<Listener<StorePurchaseCompletedEvent>>();\n\n constructor(private readonly options: WsClientOptions) {}\n\n /** Fires on every balance change — purchases, quest rewards, referral payouts, etc. */\n onBalanceChange(listener: Listener<BalanceChangeEvent>): () => void {\n this.balanceListeners.add(listener);\n void this.ensureConnected();\n return () => {\n this.balanceListeners.delete(listener);\n this.disconnectIfIdle();\n };\n }\n\n /** Fires once a store purchase finishes — see `StorePurchaseCompletedEvent`. */\n onStorePurchase(listener: Listener<StorePurchaseCompletedEvent>): () => void {\n this.purchaseListeners.add(listener);\n void this.ensureConnected();\n return () => {\n this.purchaseListeners.delete(listener);\n this.disconnectIfIdle();\n };\n }\n\n /** Closes the socket immediately, regardless of active listeners. */\n disconnect(): void {\n this.socket?.disconnect();\n this.socket = undefined;\n this.connectPromise = undefined;\n }\n\n private async ensureConnected(): Promise<Socket | undefined> {\n if (this.socket?.connected) return this.socket;\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = (async () => {\n const token = await this.options.getAccessToken();\n // Both events are user-room-scoped — an unauthenticated socket would\n // never receive anything, so don't bother opening one.\n if (!token) return undefined;\n\n const socket = io(this.options.baseUrl, {\n transports: ['websocket'],\n auth: { token: `Bearer ${token}` },\n });\n\n socket.on('balance_change', (payload: BalanceChangeEvent) => {\n for (const listener of this.balanceListeners) listener(payload);\n });\n socket.on('store_purchase_completed', (payload: StorePurchaseCompletedEvent) => {\n for (const listener of this.purchaseListeners) listener(payload);\n });\n\n this.socket = socket;\n return socket;\n })();\n\n try {\n return await this.connectPromise;\n } finally {\n this.connectPromise = undefined;\n }\n }\n\n private disconnectIfIdle(): void {\n if (this.balanceListeners.size === 0 && this.purchaseListeners.size === 0) {\n this.disconnect();\n }\n }\n}\n"]}
@@ -0,0 +1,71 @@
1
+ import { io } from 'socket.io-client';
2
+
3
+ // src/apps/ws-client.ts
4
+ var WsClient = class {
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ options;
9
+ socket;
10
+ connectPromise;
11
+ balanceListeners = /* @__PURE__ */ new Set();
12
+ purchaseListeners = /* @__PURE__ */ new Set();
13
+ /** Fires on every balance change — purchases, quest rewards, referral payouts, etc. */
14
+ onBalanceChange(listener) {
15
+ this.balanceListeners.add(listener);
16
+ void this.ensureConnected();
17
+ return () => {
18
+ this.balanceListeners.delete(listener);
19
+ this.disconnectIfIdle();
20
+ };
21
+ }
22
+ /** Fires once a store purchase finishes — see `StorePurchaseCompletedEvent`. */
23
+ onStorePurchase(listener) {
24
+ this.purchaseListeners.add(listener);
25
+ void this.ensureConnected();
26
+ return () => {
27
+ this.purchaseListeners.delete(listener);
28
+ this.disconnectIfIdle();
29
+ };
30
+ }
31
+ /** Closes the socket immediately, regardless of active listeners. */
32
+ disconnect() {
33
+ this.socket?.disconnect();
34
+ this.socket = void 0;
35
+ this.connectPromise = void 0;
36
+ }
37
+ async ensureConnected() {
38
+ if (this.socket?.connected) return this.socket;
39
+ if (this.connectPromise) return this.connectPromise;
40
+ this.connectPromise = (async () => {
41
+ const token = await this.options.getAccessToken();
42
+ if (!token) return void 0;
43
+ const socket = io(this.options.baseUrl, {
44
+ transports: ["websocket"],
45
+ auth: { token: `Bearer ${token}` }
46
+ });
47
+ socket.on("balance_change", (payload) => {
48
+ for (const listener of this.balanceListeners) listener(payload);
49
+ });
50
+ socket.on("store_purchase_completed", (payload) => {
51
+ for (const listener of this.purchaseListeners) listener(payload);
52
+ });
53
+ this.socket = socket;
54
+ return socket;
55
+ })();
56
+ try {
57
+ return await this.connectPromise;
58
+ } finally {
59
+ this.connectPromise = void 0;
60
+ }
61
+ }
62
+ disconnectIfIdle() {
63
+ if (this.balanceListeners.size === 0 && this.purchaseListeners.size === 0) {
64
+ this.disconnect();
65
+ }
66
+ }
67
+ };
68
+
69
+ export { WsClient };
70
+ //# sourceMappingURL=looplay-sdk-realtime.esm.js.map
71
+ //# sourceMappingURL=looplay-sdk-realtime.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/apps/ws-client.ts"],"names":[],"mappings":";;;AAsBO,IAAM,WAAN,MAAe;AAAA,EAMpB,YAA6B,OAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAA2B;AAAA,EAA3B,OAAA;AAAA,EALrB,MAAA;AAAA,EACA,cAAA;AAAA,EACS,gBAAA,uBAAuB,GAAA,EAAkC;AAAA,EACzD,iBAAA,uBAAwB,GAAA,EAA2C;AAAA;AAAA,EAKpF,gBAAgB,QAAA,EAAoD;AAClE,IAAA,IAAA,CAAK,gBAAA,CAAiB,IAAI,QAAQ,CAAA;AAClC,IAAA,KAAK,KAAK,eAAA,EAAgB;AAC1B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,gBAAA,CAAiB,OAAO,QAAQ,CAAA;AACrC,MAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,IACxB,CAAA;AAAA,EACF;AAAA;AAAA,EAGA,gBAAgB,QAAA,EAA6D;AAC3E,IAAA,IAAA,CAAK,iBAAA,CAAkB,IAAI,QAAQ,CAAA;AACnC,IAAA,KAAK,KAAK,eAAA,EAAgB;AAC1B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,iBAAA,CAAkB,OAAO,QAAQ,CAAA;AACtC,MAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,IACxB,CAAA;AAAA,EACF;AAAA;AAAA,EAGA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,QAAQ,UAAA,EAAW;AACxB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,cAAA,GAAiB,MAAA;AAAA,EACxB;AAAA,EAEA,MAAc,eAAA,GAA+C;AAC3D,IAAA,IAAI,IAAA,CAAK,MAAA,EAAQ,SAAA,EAAW,OAAO,IAAA,CAAK,MAAA;AACxC,IAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,OAAO,IAAA,CAAK,cAAA;AAErC,IAAA,IAAA,CAAK,kBAAkB,YAAY;AACjC,MAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,OAAA,CAAQ,cAAA,EAAe;AAGhD,MAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AAEnB,MAAA,MAAM,MAAA,GAAS,EAAA,CAAG,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS;AAAA,QACtC,UAAA,EAAY,CAAC,WAAW,CAAA;AAAA,QACxB,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAG,OAClC,CAAA;AAED,MAAA,MAAA,CAAO,EAAA,CAAG,gBAAA,EAAkB,CAAC,OAAA,KAAgC;AAC3D,QAAA,KAAA,MAAW,QAAA,IAAY,IAAA,CAAK,gBAAA,EAAkB,QAAA,CAAS,OAAO,CAAA;AAAA,MAChE,CAAC,CAAA;AACD,MAAA,MAAA,CAAO,EAAA,CAAG,0BAAA,EAA4B,CAAC,OAAA,KAAyC;AAC9E,QAAA,KAAA,MAAW,QAAA,IAAY,IAAA,CAAK,iBAAA,EAAmB,QAAA,CAAS,OAAO,CAAA;AAAA,MACjE,CAAC,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,MAAA,OAAO,MAAA;AAAA,IACT,CAAA,GAAG;AAEH,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,IAAA,CAAK,cAAA;AAAA,IACpB,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,cAAA,GAAiB,MAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,gBAAA,GAAyB;AAC/B,IAAA,IAAI,KAAK,gBAAA,CAAiB,IAAA,KAAS,KAAK,IAAA,CAAK,iBAAA,CAAkB,SAAS,CAAA,EAAG;AACzE,MAAA,IAAA,CAAK,UAAA,EAAW;AAAA,IAClB;AAAA,EACF;AACF","file":"looplay-sdk-realtime.esm.js","sourcesContent":["import { io, type Socket } from 'socket.io-client';\nimport type { BalanceChangeEvent, StorePurchaseCompletedEvent } from './LooplaySDK.types';\n\nexport interface WsClientOptions {\n baseUrl: string;\n /** Resolves the current player's access token — re-checked on every (re)connect. */\n getAccessToken: () => Promise<string | undefined>;\n}\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * Lazy-connecting Socket.IO client mirroring gbs-service's AppGateway.\n * Both events it exposes (`balance_change`, `store_purchase_completed`) are\n * pushed only to the authenticated player's own room — there is nothing to\n * receive without an access token, so `subscribe*` is a no-op until one is\n * available.\n *\n * The connection is opened on the first subscription and closed once the\n * last listener unsubscribes — a game that never calls `onBalanceChange`/\n * `onStorePurchase` never opens a socket at all.\n */\nexport class WsClient {\n private socket?: Socket;\n private connectPromise?: Promise<Socket | undefined>;\n private readonly balanceListeners = new Set<Listener<BalanceChangeEvent>>();\n private readonly purchaseListeners = new Set<Listener<StorePurchaseCompletedEvent>>();\n\n constructor(private readonly options: WsClientOptions) {}\n\n /** Fires on every balance change — purchases, quest rewards, referral payouts, etc. */\n onBalanceChange(listener: Listener<BalanceChangeEvent>): () => void {\n this.balanceListeners.add(listener);\n void this.ensureConnected();\n return () => {\n this.balanceListeners.delete(listener);\n this.disconnectIfIdle();\n };\n }\n\n /** Fires once a store purchase finishes — see `StorePurchaseCompletedEvent`. */\n onStorePurchase(listener: Listener<StorePurchaseCompletedEvent>): () => void {\n this.purchaseListeners.add(listener);\n void this.ensureConnected();\n return () => {\n this.purchaseListeners.delete(listener);\n this.disconnectIfIdle();\n };\n }\n\n /** Closes the socket immediately, regardless of active listeners. */\n disconnect(): void {\n this.socket?.disconnect();\n this.socket = undefined;\n this.connectPromise = undefined;\n }\n\n private async ensureConnected(): Promise<Socket | undefined> {\n if (this.socket?.connected) return this.socket;\n if (this.connectPromise) return this.connectPromise;\n\n this.connectPromise = (async () => {\n const token = await this.options.getAccessToken();\n // Both events are user-room-scoped — an unauthenticated socket would\n // never receive anything, so don't bother opening one.\n if (!token) return undefined;\n\n const socket = io(this.options.baseUrl, {\n transports: ['websocket'],\n auth: { token: `Bearer ${token}` },\n });\n\n socket.on('balance_change', (payload: BalanceChangeEvent) => {\n for (const listener of this.balanceListeners) listener(payload);\n });\n socket.on('store_purchase_completed', (payload: StorePurchaseCompletedEvent) => {\n for (const listener of this.purchaseListeners) listener(payload);\n });\n\n this.socket = socket;\n return socket;\n })();\n\n try {\n return await this.connectPromise;\n } finally {\n this.connectPromise = undefined;\n }\n }\n\n private disconnectIfIdle(): void {\n if (this.balanceListeners.size === 0 && this.purchaseListeners.size === 0) {\n this.disconnect();\n }\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ var LooplaySDKRealtime=(function(exports){'use strict';var Ve=Object.defineProperty;var Me=(s,e)=>{for(var t in e)Ve(s,t,{get:e[t],enumerable:true});};var d=Object.create(null);d.open="0";d.close="1";d.ping="2";d.pong="3";d.message="4";d.upgrade="5";d.noop="6";var B=Object.create(null);Object.keys(d).forEach(s=>{B[d[s]]=s;});var N={type:"error",data:"parser error"};var le=typeof Blob=="function"||typeof Blob<"u"&&Object.prototype.toString.call(Blob)==="[object BlobConstructor]",pe=typeof ArrayBuffer=="function",de=s=>typeof ArrayBuffer.isView=="function"?ArrayBuffer.isView(s):s&&s.buffer instanceof ArrayBuffer,L=({type:s,data:e},t,r)=>le&&e instanceof Blob?t?r(e):fe(e,r):pe&&(e instanceof ArrayBuffer||de(e))?t?r(e):fe(new Blob([e]),r):r(d[s]+(e||"")),fe=(s,e)=>{let t=new FileReader;return t.onload=function(){let r=t.result.split(",")[1];e("b"+(r||""));},t.readAsDataURL(s)};function ue(s){return s instanceof Uint8Array?s:s instanceof ArrayBuffer?new Uint8Array(s):new Uint8Array(s.buffer,s.byteOffset,s.byteLength)}var Q;function me(s,e){if(le&&s.data instanceof Blob)return s.data.arrayBuffer().then(ue).then(e);if(pe&&(s.data instanceof ArrayBuffer||de(s.data)))return e(ue(s.data));L(s,false,t=>{Q||(Q=new TextEncoder),e(Q.encode(t));});}var ye="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",P=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(let s=0;s<ye.length;s++)P[ye.charCodeAt(s)]=s;var ge=s=>{let e=s.length*.75,t=s.length,r,i=0,n,o,c,h;s[s.length-1]==="="&&(e--,s[s.length-2]==="="&&e--);let m=new ArrayBuffer(e),p=new Uint8Array(m);for(r=0;r<t;r+=4)n=P[s.charCodeAt(r)],o=P[s.charCodeAt(r+1)],c=P[s.charCodeAt(r+2)],h=P[s.charCodeAt(r+3)],p[i++]=n<<2|o>>4,p[i++]=(o&15)<<4|c>>2,p[i++]=(c&3)<<6|h&63;return m};var He=typeof ArrayBuffer=="function",q=(s,e)=>{if(typeof s!="string")return {type:"message",data:_e(s,e)};let t=s.charAt(0);return t==="b"?{type:"message",data:We(s.substring(1),e)}:B[t]?s.length>1?{type:B[t],data:s.substring(1)}:{type:B[t]}:N},We=(s,e)=>{if(He){let t=ge(s);return _e(t,e)}else return {base64:true,data:s}},_e=(s,e)=>e==="blob"?s instanceof Blob?s:new Blob([s]):s instanceof ArrayBuffer?s:s.buffer;var be="",we=(s,e)=>{let t=s.length,r=new Array(t),i=0;s.forEach((n,o)=>{L(n,false,c=>{r[o]=c,++i===t&&e(r.join(be));});});},Ee=(s,e)=>{let t=s.split(be),r=[];for(let i=0;i<t.length;i++){let n=q(t[i],e);if(r.push(n),n.type==="error")break}return r};function ve(){return new TransformStream({transform(s,e){me(s,t=>{let r=t.length,i;if(r<126)i=new Uint8Array(1),new DataView(i.buffer).setUint8(0,r);else if(r<65536){i=new Uint8Array(3);let n=new DataView(i.buffer);n.setUint8(0,126),n.setUint16(1,r);}else {i=new Uint8Array(9);let n=new DataView(i.buffer);n.setUint8(0,127),n.setBigUint64(1,BigInt(r));}s.data&&typeof s.data!="string"&&(i[0]|=128),e.enqueue(i),e.enqueue(t);});}})}var G;function M(s){return s.reduce((e,t)=>e+t.length,0)}function H(s,e){if(s[0].length===e)return s.shift();let t=new Uint8Array(e),r=0;for(let i=0;i<e;i++)t[i]=s[0][r++],r===s[0].length&&(s.shift(),r=0);return s.length&&r<s[0].length&&(s[0]=s[0].slice(r)),t}function ke(s,e){G||(G=new TextDecoder);let t=[],r=0,i=-1,n=false;return new TransformStream({transform(o,c){for(t.push(o);;){if(r===0){if(M(t)<1)break;let h=H(t,1);n=(h[0]&128)===128,i=h[0]&127,i<126?r=3:i===126?r=1:r=2;}else if(r===1){if(M(t)<2)break;let h=H(t,2);i=new DataView(h.buffer,h.byteOffset,h.length).getUint16(0),r=3;}else if(r===2){if(M(t)<8)break;let h=H(t,8),m=new DataView(h.buffer,h.byteOffset,h.length),p=m.getUint32(0);if(p>Math.pow(2,21)-1){c.enqueue(N);break}i=p*Math.pow(2,32)+m.getUint32(4),r=3;}else {if(M(t)<i)break;let h=H(t,i);c.enqueue(q(n?h:G.decode(h),e)),r=0;}if(i===0||i>s){c.enqueue(N);break}}}})}var j=4;function f(s){if(s)return Ke(s)}function Ke(s){for(var e in f.prototype)s[e]=f.prototype[e];return s}f.prototype.on=f.prototype.addEventListener=function(s,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+s]=this._callbacks["$"+s]||[]).push(e),this};f.prototype.once=function(s,e){function t(){this.off(s,t),e.apply(this,arguments);}return t.fn=e,this.on(s,t),this};f.prototype.off=f.prototype.removeListener=f.prototype.removeAllListeners=f.prototype.removeEventListener=function(s,e){if(this._callbacks=this._callbacks||{},arguments.length==0)return this._callbacks={},this;var t=this._callbacks["$"+s];if(!t)return this;if(arguments.length==1)return delete this._callbacks["$"+s],this;for(var r,i=0;i<t.length;i++)if(r=t[i],r===e||r.fn===e){t.splice(i,1);break}return t.length===0&&delete this._callbacks["$"+s],this};f.prototype.emit=function(s){this._callbacks=this._callbacks||{};for(var e=new Array(arguments.length-1),t=this._callbacks["$"+s],r=1;r<arguments.length;r++)e[r-1]=arguments[r];if(t){t=t.slice(0);for(var r=0,i=t.length;r<i;++r)t[r].apply(this,e);}return this};f.prototype.emitReserved=f.prototype.emit;f.prototype.listeners=function(s){return this._callbacks=this._callbacks||{},this._callbacks["$"+s]||[]};f.prototype.hasListeners=function(s){return !!this.listeners(s).length};var y=typeof Promise=="function"&&typeof Promise.resolve=="function"?e=>Promise.resolve().then(e):(e,t)=>t(e,0),u=typeof self<"u"?self:typeof window<"u"?window:Function("return this")(),xe="arraybuffer";function W(s,...e){return e.reduce((t,r)=>(s.hasOwnProperty(r)&&(t[r]=s[r]),t),{})}var Ye=u.setTimeout,Je=u.clearTimeout;function g(s,e){e.useNativeTimers?(s.setTimeoutFn=Ye.bind(u),s.clearTimeoutFn=Je.bind(u)):(s.setTimeoutFn=u.setTimeout.bind(u),s.clearTimeoutFn=u.clearTimeout.bind(u));}var ze=1.33;function Te(s){return typeof s=="string"?Xe(s):Math.ceil((s.byteLength||s.size)*ze)}function Xe(s){let e=0,t=0;for(let r=0,i=s.length;r<i;r++)e=s.charCodeAt(r),e<128?t+=1:e<2048?t+=2:e<55296||e>=57344?t+=3:(r++,t+=4);return t}function K(){return Date.now().toString(36).substring(3)+Math.random().toString(36).substring(2,5)}function Ae(s){let e="";for(let t in s)s.hasOwnProperty(t)&&(e.length&&(e+="&"),e+=encodeURIComponent(t)+"="+encodeURIComponent(s[t]));return e}function Ce(s){let e={},t=s.split("&");for(let r=0,i=t.length;r<i;r++){let n=t[r].split("=");e[decodeURIComponent(n[0])]=decodeURIComponent(n[1]);}return e}var Y=class extends Error{constructor(e,t,r){super(e),this.description=t,this.context=r,this.type="TransportError";}},_=class extends f{constructor(e){super(),this.writable=false,g(this,e),this.opts=e,this.query=e.query,this.socket=e.socket,this.supportsBinary=!e.forceBase64;}onError(e,t,r){return super.emitReserved("error",new Y(e,t,r)),this}open(){return this.readyState="opening",this.doOpen(),this}close(){return (this.readyState==="opening"||this.readyState==="open")&&(this.doClose(),this.onClose()),this}send(e){this.readyState==="open"&&this.write(e);}onOpen(){this.readyState="open",this.writable=true,super.emitReserved("open");}onData(e){let t=q(e,this.socket.binaryType);this.onPacket(t);}onPacket(e){super.emitReserved("packet",e);}onClose(e){this.readyState="closed",super.emitReserved("close",e);}pause(e){}createUri(e,t={}){return e+"://"+this._hostname()+this._port()+this.opts.path+this._query(t)}_hostname(){let e=this.opts.hostname;return e.indexOf(":")===-1?e:"["+e+"]"}_port(){return this.opts.port&&(this.opts.secure&&Number(this.opts.port)!==443||!this.opts.secure&&Number(this.opts.port)!==80)?":"+this.opts.port:""}_query(e){let t=Ae(e);return t.length?"?"+t:""}};var D=class extends _{constructor(){super(...arguments),this._polling=false;}get name(){return "polling"}doOpen(){this._poll();}pause(e){this.readyState="pausing";let t=()=>{this.readyState="paused",e();};if(this._polling||!this.writable){let r=0;this._polling&&(r++,this.once("pollComplete",function(){--r||t();})),this.writable||(r++,this.once("drain",function(){--r||t();}));}else t();}_poll(){this._polling=true,this.doPoll(),this.emitReserved("poll");}onData(e){let t=r=>{if(this.readyState==="opening"&&r.type==="open"&&this.onOpen(),r.type==="close")return this.onClose({description:"transport closed by the server"}),false;this.onPacket(r);};Ee(e,this.socket.binaryType).forEach(t),this.readyState!=="closed"&&(this._polling=false,this.emitReserved("pollComplete"),this.readyState==="open"&&this._poll());}doClose(){let e=()=>{this.write([{type:"close"}]);};this.readyState==="open"?e():this.once("open",e);}write(e){this.writable=false,we(e,t=>{this.doWrite(t,()=>{this.writable=true,this.emitReserved("drain");});});}uri(){let e=this.opts.secure?"https":"http",t=this.query||{};return this.opts.timestampRequests!==false&&(t[this.opts.timestampParam]=K()),!this.supportsBinary&&!t.sid&&(t.b64=1),this.createUri(e,t)}};var Se=false;try{Se=typeof XMLHttpRequest<"u"&&"withCredentials"in new XMLHttpRequest;}catch{}var Re=Se;function $e(){}var Z=class extends D{constructor(e){if(super(e),typeof location<"u"){let t=location.protocol==="https:",r=location.port;r||(r=t?"443":"80"),this.xd=typeof location<"u"&&e.hostname!==location.hostname||r!==e.port;}}doWrite(e,t){let r=this.request({method:"POST",data:e});r.on("success",t),r.on("error",(i,n)=>{this.onError("xhr post error",i,n);});}doPoll(){let e=this.request();e.on("data",this.onData.bind(this)),e.on("error",(t,r)=>{this.onError("xhr poll error",t,r);}),this.pollXhr=e;}},w=class s extends f{constructor(e,t,r){super(),this.createRequest=e,g(this,r),this._opts=r,this._method=r.method||"GET",this._uri=t,this._data=r.data!==void 0?r.data:null,this._create();}_create(){var e;let t=W(this._opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");t.xdomain=!!this._opts.xd;let r=this._xhr=this.createRequest(t);try{r.open(this._method,this._uri,!0);try{if(this._opts.extraHeaders){r.setDisableHeaderCheck&&r.setDisableHeaderCheck(!0);for(let i in this._opts.extraHeaders)this._opts.extraHeaders.hasOwnProperty(i)&&r.setRequestHeader(i,this._opts.extraHeaders[i]);}}catch{}if(this._method==="POST")try{r.setRequestHeader("Content-type","text/plain;charset=UTF-8");}catch{}try{r.setRequestHeader("Accept","*/*");}catch{}(e=this._opts.cookieJar)===null||e===void 0||e.addCookies(r),"withCredentials"in r&&(r.withCredentials=this._opts.withCredentials),this._opts.requestTimeout&&(r.timeout=this._opts.requestTimeout),r.onreadystatechange=()=>{var i;r.readyState===3&&((i=this._opts.cookieJar)===null||i===void 0||i.parseCookies(r.getResponseHeader("set-cookie"))),r.readyState===4&&(r.status===200||r.status===1223?this._onLoad():this.setTimeoutFn(()=>{this._onError(typeof r.status=="number"?r.status:0);},0));},r.send(this._data);}catch(i){this.setTimeoutFn(()=>{this._onError(i);},0);return}typeof document<"u"&&(this._index=s.requestsCount++,s.requests[this._index]=this);}_onError(e){this.emitReserved("error",e,this._xhr),this._cleanup(true);}_cleanup(e){if(!(typeof this._xhr>"u"||this._xhr===null)){if(this._xhr.onreadystatechange=$e,e)try{this._xhr.abort();}catch{}typeof document<"u"&&delete s.requests[this._index],this._xhr=null;}}_onLoad(){let e=this._xhr.responseText;e!==null&&(this.emitReserved("data",e),this.emitReserved("success"),this._cleanup());}abort(){this._cleanup();}};w.requestsCount=0;w.requests={};if(typeof document<"u"){if(typeof attachEvent=="function")attachEvent("onunload",Oe);else if(typeof addEventListener=="function"){let s="onpagehide"in u?"pagehide":"unload";addEventListener(s,Oe,false);}}function Oe(){for(let s in w.requests)w.requests.hasOwnProperty(s)&&w.requests[s].abort();}var Qe=(function(){let s=Be({xdomain:false});return s&&s.responseType!==null})(),E=class extends Z{constructor(e){super(e);let t=e&&e.forceBase64;this.supportsBinary=Qe&&!t;}request(e={}){return Object.assign(e,{xd:this.xd},this.opts),new w(Be,this.uri(),e)}};function Be(s){let e=s.xdomain;try{if(typeof XMLHttpRequest<"u"&&(!e||Re))return new XMLHttpRequest}catch{}if(!e)try{return new u[["Active"].concat("Object").join("X")]("Microsoft.XMLHTTP")}catch{}}var Ne=typeof navigator<"u"&&typeof navigator.product=="string"&&navigator.product.toLowerCase()==="reactnative",te=class extends _{get name(){return "websocket"}doOpen(){let e=this.uri(),t=this.opts.protocols,r=Ne?{}:W(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(r.headers=this.opts.extraHeaders);try{this.ws=this.createSocket(e,t,r);}catch(i){return this.emitReserved("error",i)}this.ws.binaryType=this.socket.binaryType,this.addEventListeners();}addEventListeners(){this.ws.onopen=()=>{this.opts.autoUnref&&this.ws._socket.unref(),this.onOpen();},this.ws.onclose=e=>this.onClose({description:"websocket connection closed",context:e}),this.ws.onmessage=e=>this.onData(e.data),this.ws.onerror=e=>this.onError("websocket error",e);}write(e){this.writable=false;for(let t=0;t<e.length;t++){let r=e[t],i=t===e.length-1;L(r,this.supportsBinary,n=>{try{this.doWrite(r,n);}catch{}i&&y(()=>{this.writable=true,this.emitReserved("drain");},this.setTimeoutFn);});}}doClose(){typeof this.ws<"u"&&(this.ws.onerror=()=>{},this.ws.close(),this.ws=null);}uri(){let e=this.opts.secure?"wss":"ws",t=this.query||{};return this.opts.timestampRequests&&(t[this.opts.timestampParam]=K()),this.supportsBinary||(t.b64=1),this.createUri(e,t)}},ee=u.WebSocket||u.MozWebSocket,v=class extends te{createSocket(e,t,r){return Ne?new ee(e,t,r):t?new ee(e,t):new ee(e)}doWrite(e,t){this.ws.send(t);}};var T=class extends _{get name(){return "webtransport"}doOpen(){try{this._transport=new WebTransport(this.createUri("https"),this.opts.transportOptions[this.name]);}catch(e){return this.emitReserved("error",e)}this._transport.closed.then(()=>{this.onClose();}).catch(e=>{this.onError("webtransport error",e);}),this._transport.ready.then(()=>{this._transport.createBidirectionalStream().then(e=>{let t=ke(Number.MAX_SAFE_INTEGER,this.socket.binaryType),r=e.readable.pipeThrough(t).getReader(),i=ve();i.readable.pipeTo(e.writable),this._writer=i.writable.getWriter();let n=()=>{r.read().then(({done:c,value:h})=>{c||(this.onPacket(h),n());}).catch(c=>{});};n();let o={type:"open"};this.query.sid&&(o.data=`{"sid":"${this.query.sid}"}`),this._writer.write(o).then(()=>this.onOpen());});});}write(e){this.writable=false;for(let t=0;t<e.length;t++){let r=e[t],i=t===e.length-1;this._writer.write(r).then(()=>{i&&y(()=>{this.writable=true,this.emitReserved("drain");},this.setTimeoutFn);});}}doClose(){var e;(e=this._transport)===null||e===void 0||e.close();}};var se={websocket:v,webtransport:T,polling:E};var Ge=/^(?:(?![^:@\/?#]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@\/?#]*)(?::([^:@\/?#]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,je=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];function A(s){if(s.length>8e3)throw "URI too long";let e=s,t=s.indexOf("["),r=s.indexOf("]");t!=-1&&r!=-1&&(s=s.substring(0,t)+s.substring(t,r).replace(/:/g,";")+s.substring(r,s.length));let i=Ge.exec(s||""),n={},o=14;for(;o--;)n[je[o]]=i[o]||"";return t!=-1&&r!=-1&&(n.source=e,n.host=n.host.substring(1,n.host.length-1).replace(/;/g,":"),n.authority=n.authority.replace("[","").replace("]","").replace(/;/g,":"),n.ipv6uri=true),n.pathNames=Ze(n,n.path),n.queryKey=et(n,n.query),n}function Ze(s,e){let t=/\/{2,9}/g,r=e.replace(t,"/").split("/");return (e.slice(0,1)=="/"||e.length===0)&&r.splice(0,1),e.slice(-1)=="/"&&r.splice(r.length-1,1),r}function et(s,e){let t={};return e.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(r,i,n){i&&(t[i]=n);}),t}var re=typeof addEventListener=="function"&&typeof removeEventListener=="function",J=[];re&&addEventListener("offline",()=>{J.forEach(s=>s());},false);var k=class s extends f{constructor(e,t){if(super(),this.binaryType=xe,this.writeBuffer=[],this._prevBufferLen=0,this._pingInterval=-1,this._pingTimeout=-1,this._maxPayload=-1,this._pingTimeoutTime=1/0,e&&typeof e=="object"&&(t=e,e=null),e){let r=A(e);t.hostname=r.host,t.secure=r.protocol==="https"||r.protocol==="wss",t.port=r.port,r.query&&(t.query=r.query);}else t.host&&(t.hostname=A(t.host).host);g(this,t),this.secure=t.secure!=null?t.secure:typeof location<"u"&&location.protocol==="https:",t.hostname&&!t.port&&(t.port=this.secure?"443":"80"),this.hostname=t.hostname||(typeof location<"u"?location.hostname:"localhost"),this.port=t.port||(typeof location<"u"&&location.port?location.port:this.secure?"443":"80"),this.transports=[],this._transportsByName={},t.transports.forEach(r=>{let i=r.prototype.name;this.transports.push(i),this._transportsByName[i]=r;}),this.opts=Object.assign({path:"/engine.io",agent:false,withCredentials:false,upgrade:true,timestampParam:"t",rememberUpgrade:false,addTrailingSlash:true,rejectUnauthorized:true,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:false},t),this.opts.path=this.opts.path.replace(/\/$/,"")+(this.opts.addTrailingSlash?"/":""),typeof this.opts.query=="string"&&(this.opts.query=Ce(this.opts.query)),re&&(this.opts.closeOnBeforeunload&&(this._beforeunloadEventListener=()=>{this.transport&&(this.transport.removeAllListeners(),this.transport.close());},addEventListener("beforeunload",this._beforeunloadEventListener,false)),this.hostname!=="localhost"&&(this._offlineEventListener=()=>{this._onClose("transport close",{description:"network connection lost"});},J.push(this._offlineEventListener))),this.opts.withCredentials&&(this._cookieJar=void 0),this._open();}createTransport(e){let t=Object.assign({},this.opts.query);t.EIO=j,t.transport=e,this.id&&(t.sid=this.id);let r=Object.assign({},this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port},this.opts.transportOptions[e]);return new this._transportsByName[e](r)}_open(){if(this.transports.length===0){this.setTimeoutFn(()=>{this.emitReserved("error","No transports available");},0);return}let e=this.opts.rememberUpgrade&&s.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1?"websocket":this.transports[0];this.readyState="opening";let t=this.createTransport(e);t.open(),this.setTransport(t);}setTransport(e){this.transport&&this.transport.removeAllListeners(),this.transport=e,e.on("drain",this._onDrain.bind(this)).on("packet",this._onPacket.bind(this)).on("error",this._onError.bind(this)).on("close",t=>this._onClose("transport close",t));}onOpen(){this.readyState="open",s.priorWebsocketSuccess=this.transport.name==="websocket",this.emitReserved("open"),this.flush();}_onPacket(e){if(this.readyState==="opening"||this.readyState==="open"||this.readyState==="closing")switch(this.emitReserved("packet",e),this.emitReserved("heartbeat"),e.type){case "open":this.onHandshake(JSON.parse(e.data));break;case "ping":this._sendPacket("pong"),this.emitReserved("ping"),this.emitReserved("pong"),this._resetPingTimeout();break;case "error":let t=new Error("server error");t.code=e.data,this._onError(t);break;case "message":this.emitReserved("data",e.data),this.emitReserved("message",e.data);break}}onHandshake(e){this.emitReserved("handshake",e),this.id=e.sid,this.transport.query.sid=e.sid,this._pingInterval=e.pingInterval,this._pingTimeout=e.pingTimeout,this._maxPayload=e.maxPayload,this.onOpen(),this.readyState!=="closed"&&this._resetPingTimeout();}_resetPingTimeout(){this.clearTimeoutFn(this._pingTimeoutTimer);let e=this._pingInterval+this._pingTimeout;this._pingTimeoutTime=Date.now()+e,this._pingTimeoutTimer=this.setTimeoutFn(()=>{this._onClose("ping timeout");},e),this.opts.autoUnref&&this._pingTimeoutTimer.unref();}_onDrain(){this.writeBuffer.splice(0,this._prevBufferLen),this._prevBufferLen=0,this.writeBuffer.length===0?this.emitReserved("drain"):this.flush();}flush(){if(this.readyState!=="closed"&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){let e=this._getWritablePackets();this.transport.send(e),this._prevBufferLen=e.length,this.emitReserved("flush");}}_getWritablePackets(){if(!(this._maxPayload&&this.transport.name==="polling"&&this.writeBuffer.length>1))return this.writeBuffer;let t=1;for(let r=0;r<this.writeBuffer.length;r++){let i=this.writeBuffer[r].data;if(i&&(t+=Te(i)),r>0&&t>this._maxPayload)return this.writeBuffer.slice(0,r);t+=2;}return this.writeBuffer}_hasPingExpired(){if(!this._pingTimeoutTime)return true;let e=Date.now()>this._pingTimeoutTime;return e&&(this._pingTimeoutTime=0,y(()=>{this._onClose("ping timeout");},this.setTimeoutFn)),e}write(e,t,r){return this._sendPacket("message",e,t,r),this}send(e,t,r){return this._sendPacket("message",e,t,r),this}_sendPacket(e,t,r,i){if(typeof t=="function"&&(i=t,t=void 0),typeof r=="function"&&(i=r,r=null),this.readyState==="closing"||this.readyState==="closed")return;r=r||{},r.compress=r.compress!==false;let n={type:e,data:t,options:r};this.emitReserved("packetCreate",n),this.writeBuffer.push(n),i&&this.once("flush",i),this.flush();}close(){let e=()=>{this._onClose("forced close"),this.transport.close();},t=()=>{this.off("upgrade",t),this.off("upgradeError",t),e();},r=()=>{this.once("upgrade",t),this.once("upgradeError",t);};return (this.readyState==="opening"||this.readyState==="open")&&(this.readyState="closing",this.writeBuffer.length?this.once("drain",()=>{this.upgrading?r():e();}):this.upgrading?r():e()),this}_onError(e){if(s.priorWebsocketSuccess=false,this.opts.tryAllTransports&&this.transports.length>1&&this.readyState==="opening")return this.transports.shift(),this._open();this.emitReserved("error",e),this._onClose("transport error",e);}_onClose(e,t){if(this.readyState==="opening"||this.readyState==="open"||this.readyState==="closing"){if(this.clearTimeoutFn(this._pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),re&&(this._beforeunloadEventListener&&removeEventListener("beforeunload",this._beforeunloadEventListener,false),this._offlineEventListener)){let r=J.indexOf(this._offlineEventListener);r!==-1&&J.splice(r,1);}this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this._prevBufferLen=0;}}};k.protocol=j;var z=class extends k{constructor(){super(...arguments),this._upgrades=[];}onOpen(){if(super.onOpen(),this.readyState==="open"&&this.opts.upgrade)for(let e=0;e<this._upgrades.length;e++)this._probe(this._upgrades[e]);}_probe(e){let t=this.createTransport(e),r=false;k.priorWebsocketSuccess=false;let i=()=>{r||(t.send([{type:"ping",data:"probe"}]),t.once("packet",b=>{if(!r)if(b.type==="pong"&&b.data==="probe"){if(this.upgrading=true,this.emitReserved("upgrading",t),!t)return;k.priorWebsocketSuccess=t.name==="websocket",this.transport.pause(()=>{r||this.readyState!=="closed"&&(p(),this.setTransport(t),t.send([{type:"upgrade"}]),this.emitReserved("upgrade",t),t=null,this.upgrading=false,this.flush());});}else {let O=new Error("probe error");O.transport=t.name,this.emitReserved("upgradeError",O);}}));};function n(){r||(r=true,p(),t.close(),t=null);}let o=b=>{let O=new Error("probe error: "+b);O.transport=t.name,n(),this.emitReserved("upgradeError",O);};function c(){o("transport closed");}function h(){o("socket closed");}function m(b){t&&b.name!==t.name&&n();}let p=()=>{t.removeListener("open",i),t.removeListener("error",o),t.removeListener("close",c),this.off("close",h),this.off("upgrading",m);};t.once("open",i),t.once("error",o),t.once("close",c),this.once("close",h),this.once("upgrading",m),this._upgrades.indexOf("webtransport")!==-1&&e!=="webtransport"?this.setTimeoutFn(()=>{r||t.open();},200):t.open();}onHandshake(e){this._upgrades=this._filterUpgrades(e.upgrades),super.onHandshake(e);}_filterUpgrades(e){let t=[];for(let r=0;r<e.length;r++)~this.transports.indexOf(e[r])&&t.push(e[r]);return t}},C=class extends z{constructor(e,t={}){let r=typeof e=="object",i=r?{...e}:{...t};(!i.transports||i.transports&&typeof i.transports[0]=="string")&&(i.transports=(i.transports||["polling","websocket","webtransport"]).map(n=>se[n]).filter(n=>!!n)),super(r?i:e,i);}};function Le(s,e="",t){let r=s;t=t||typeof location<"u"&&location,s==null&&(s=t.protocol+"//"+t.host),typeof s=="string"&&(s.charAt(0)==="/"&&(s.charAt(1)==="/"?s=t.protocol+s:s=t.host+s),/^(https?|wss?):\/\//.test(s)||(typeof t<"u"?s=t.protocol+"//"+s:s="https://"+s),r=A(s)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";let n=r.host.indexOf(":")!==-1?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+n+":"+r.port+e,r.href=r.protocol+"://"+n+(t&&t.port===r.port?"":":"+r.port),r}var ce={};Me(ce,{Decoder:()=>oe,Encoder:()=>ne,PacketType:()=>a,isPacketValid:()=>ht,protocol:()=>Ue});var st=typeof ArrayBuffer=="function",rt=s=>typeof ArrayBuffer.isView=="function"?ArrayBuffer.isView(s):s.buffer instanceof ArrayBuffer,Pe=Object.prototype.toString,it=typeof Blob=="function"||typeof Blob<"u"&&Pe.call(Blob)==="[object BlobConstructor]",nt=typeof File=="function"||typeof File<"u"&&Pe.call(File)==="[object FileConstructor]";function U(s){return st&&(s instanceof ArrayBuffer||rt(s))||it&&s instanceof Blob||nt&&s instanceof File}function I(s,e){if(!s||typeof s!="object")return false;if(Array.isArray(s)){for(let t=0,r=s.length;t<r;t++)if(I(s[t]))return true;return false}if(U(s))return true;if(s.toJSON&&typeof s.toJSON=="function"&&arguments.length===1)return I(s.toJSON(),true);for(let t in s)if(Object.prototype.hasOwnProperty.call(s,t)&&I(s[t]))return true;return false}function qe(s){let e=[],t=s.data,r=s;return r.data=X(t,e),r.attachments=e.length,{packet:r,buffers:e}}function X(s,e,t){if(!s)return s;if(U(s)){let r={_placeholder:true,num:e.length};return e.push(s),r}else if(Array.isArray(s)){let r=new Array(s.length);for(let i=0;i<s.length;i++)r[i]=X(s[i],e);return r}else if(typeof s=="object"&&!(s instanceof Date)){if(s.toJSON&&typeof s.toJSON=="function"&&!t)return X(s.toJSON(),e,true);let r={};for(let i in s)Object.prototype.hasOwnProperty.call(s,i)&&(r[i]=X(s[i],e));return r}return s}function De(s,e){return s.data=ie(s.data,e),delete s.attachments,s}function ie(s,e){if(!s)return s;if(s&&s._placeholder===true){if(typeof s.num=="number"&&s.num>=0&&s.num<e.length)return e[s.num];throw new Error("illegal attachments")}else if(Array.isArray(s))for(let t=0;t<s.length;t++)s[t]=ie(s[t],e);else if(typeof s=="object")for(let t in s)Object.prototype.hasOwnProperty.call(s,t)&&(s[t]=ie(s[t],e));return s}var Ie=["connect","connect_error","disconnect","disconnecting","newListener","removeListener"],Ue=5,a;(function(s){s[s.CONNECT=0]="CONNECT",s[s.DISCONNECT=1]="DISCONNECT",s[s.EVENT=2]="EVENT",s[s.ACK=3]="ACK",s[s.CONNECT_ERROR=4]="CONNECT_ERROR",s[s.BINARY_EVENT=5]="BINARY_EVENT",s[s.BINARY_ACK=6]="BINARY_ACK";})(a||(a={}));var ne=class{constructor(e){this.replacer=e;}encode(e){return (e.type===a.EVENT||e.type===a.ACK)&&I(e)?this.encodeAsBinary({type:e.type===a.EVENT?a.BINARY_EVENT:a.BINARY_ACK,nsp:e.nsp,data:e.data,id:e.id}):[this.encodeAsString(e)]}encodeAsString(e){let t=""+e.type;return (e.type===a.BINARY_EVENT||e.type===a.BINARY_ACK)&&(t+=e.attachments+"-"),e.nsp&&e.nsp!=="/"&&(t+=e.nsp+","),e.id!=null&&(t+=e.id),e.data!=null&&(t+=JSON.stringify(e.data,this.replacer)),t}encodeAsBinary(e){let t=qe(e),r=this.encodeAsString(t.packet),i=t.buffers;return i.unshift(r),i}},oe=class s extends f{constructor(e){super(),this.opts=Object.assign({reviver:void 0,maxAttachments:10},typeof e=="function"?{reviver:e}:e);}add(e){let t;if(typeof e=="string"){if(this.reconstructor)throw new Error("got plaintext data when reconstructing a packet");t=this.decodeString(e);let r=t.type===a.BINARY_EVENT;r||t.type===a.BINARY_ACK?(t.type=r?a.EVENT:a.ACK,this.reconstructor=new ae(t)):super.emitReserved("decoded",t);}else if(U(e)||e.base64)if(this.reconstructor)t=this.reconstructor.takeBinaryData(e),t&&(this.reconstructor=null,super.emitReserved("decoded",t));else throw new Error("got binary data when not reconstructing a packet");else throw new Error("Unknown type: "+e)}decodeString(e){let t=0,r={type:Number(e.charAt(0))};if(a[r.type]===void 0)throw new Error("unknown packet type "+r.type);if(r.type===a.BINARY_EVENT||r.type===a.BINARY_ACK){let n=t+1;for(;e.charAt(++t)!=="-"&&t!=e.length;);let o=e.substring(n,t);if(o!=Number(o)||e.charAt(t)!=="-")throw new Error("Illegal attachments");let c=Number(o);if(!Fe(c)||c<1)throw new Error("Illegal attachments");if(c>this.opts.maxAttachments)throw new Error("too many attachments");r.attachments=c;}if(e.charAt(t+1)==="/"){let n=t+1;for(;++t&&!(e.charAt(t)===","||t===e.length););r.nsp=e.substring(n,t);}else r.nsp="/";let i=e.charAt(t+1);if(i!==""&&Number(i)==i){let n=t+1;for(;++t;){let o=e.charAt(t);if(o==null||Number(o)!=o){--t;break}if(t===e.length)break}r.id=Number(e.substring(n,t+1));}if(e.charAt(++t)){let n=this.tryParse(e.substr(t));if(s.isPayloadValid(r.type,n))r.data=n;else throw new Error("invalid payload")}return r}tryParse(e){try{return JSON.parse(e,this.opts.reviver)}catch{return false}}static isPayloadValid(e,t){switch(e){case a.CONNECT:return $(t);case a.DISCONNECT:return t===void 0;case a.CONNECT_ERROR:return typeof t=="string"||$(t);case a.EVENT:case a.BINARY_EVENT:return Array.isArray(t)&&(typeof t[0]=="number"||typeof t[0]=="string"&&Ie.indexOf(t[0])===-1);case a.ACK:case a.BINARY_ACK:return Array.isArray(t)}}destroy(){this.reconstructor&&(this.reconstructor.finishedReconstruction(),this.reconstructor=null);}},ae=class{constructor(e){this.packet=e,this.buffers=[],this.reconPack=e;}takeBinaryData(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){let t=De(this.reconPack,this.buffers);return this.finishedReconstruction(),t}return null}finishedReconstruction(){this.reconPack=null,this.buffers=[];}};function ot(s){return typeof s=="string"}var Fe=Number.isInteger||function(s){return typeof s=="number"&&isFinite(s)&&Math.floor(s)===s};function at(s){return s===void 0||Fe(s)}function $(s){return Object.prototype.toString.call(s)==="[object Object]"}function ct(s,e){switch(s){case a.CONNECT:return e===void 0||$(e);case a.DISCONNECT:return e===void 0;case a.EVENT:return Array.isArray(e)&&(typeof e[0]=="number"||typeof e[0]=="string"&&Ie.indexOf(e[0])===-1);case a.ACK:return Array.isArray(e);case a.CONNECT_ERROR:return typeof e=="string"||$(e);default:return false}}function ht(s){return ot(s.nsp)&&at(s.id)&&ct(s.type,s.data)}function l(s,e,t){return s.on(e,t),function(){s.off(e,t);}}var ft=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1}),S=class extends f{constructor(e,t,r){super(),this.connected=false,this.recovered=false,this.receiveBuffer=[],this.sendBuffer=[],this._queue=[],this._queueSeq=0,this.ids=0,this.acks={},this.flags={},this.io=e,this.nsp=t,r&&r.auth&&(this.auth=r.auth),this._opts=Object.assign({},r),this.io._autoConnect&&this.open();}get disconnected(){return !this.connected}subEvents(){if(this.subs)return;let e=this.io;this.subs=[l(e,"open",this.onopen.bind(this)),l(e,"packet",this.onpacket.bind(this)),l(e,"error",this.onerror.bind(this)),l(e,"close",this.onclose.bind(this))];}get active(){return !!this.subs}connect(){return this.connected?this:(this.subEvents(),this.io._reconnecting||this.io.open(),this.io._readyState==="open"&&this.onopen(),this)}open(){return this.connect()}send(...e){return e.unshift("message"),this.emit.apply(this,e),this}emit(e,...t){var r,i,n;if(ft.hasOwnProperty(e))throw new Error('"'+e.toString()+'" is a reserved event name');if(t.unshift(e),this._opts.retries&&!this.flags.fromQueue&&!this.flags.volatile)return this._addToQueue(t),this;let o={type:a.EVENT,data:t};if(o.options={},o.options.compress=this.flags.compress!==false,typeof t[t.length-1]=="function"){let p=this.ids++,b=t.pop();this._registerAckCallback(p,b),o.id=p;}let c=(i=(r=this.io.engine)===null||r===void 0?void 0:r.transport)===null||i===void 0?void 0:i.writable,h=this.connected&&!(!((n=this.io.engine)===null||n===void 0)&&n._hasPingExpired());return this.flags.volatile&&!c||(h?(this.notifyOutgoingListeners(o),this.packet(o)):this.sendBuffer.push(o)),this.flags={},this}_registerAckCallback(e,t){var r;let i=(r=this.flags.timeout)!==null&&r!==void 0?r:this._opts.ackTimeout;if(i===void 0){this.acks[e]=t;return}let n=this.io.setTimeoutFn(()=>{delete this.acks[e];for(let c=0;c<this.sendBuffer.length;c++)this.sendBuffer[c].id===e&&this.sendBuffer.splice(c,1);t.call(this,new Error("operation has timed out"));},i),o=(...c)=>{this.io.clearTimeoutFn(n),t.apply(this,c);};o.withError=true,this.acks[e]=o;}emitWithAck(e,...t){return new Promise((r,i)=>{let n=(o,c)=>o?i(o):r(c);n.withError=true,t.push(n),this.emit(e,...t);})}_addToQueue(e){let t;typeof e[e.length-1]=="function"&&(t=e.pop());let r={id:this._queueSeq++,tryCount:0,pending:false,args:e,flags:Object.assign({fromQueue:true},this.flags)};e.push((i,...n)=>(this._queue[0],i!==null?r.tryCount>this._opts.retries&&(this._queue.shift(),t&&t(i)):(this._queue.shift(),t&&t(null,...n)),r.pending=false,this._drainQueue())),this._queue.push(r),this._drainQueue();}_drainQueue(e=false){if(!this.connected||this._queue.length===0)return;let t=this._queue[0];t.pending&&!e||(t.pending=true,t.tryCount++,this.flags=t.flags,this.emit.apply(this,t.args));}packet(e){e.nsp=this.nsp,this.io._packet(e);}onopen(){typeof this.auth=="function"?this.auth(e=>{this._sendConnectPacket(e);}):this._sendConnectPacket(this.auth);}_sendConnectPacket(e){this.packet({type:a.CONNECT,data:this._pid?Object.assign({pid:this._pid,offset:this._lastOffset},e):e});}onerror(e){this.connected||this.emitReserved("connect_error",e);}onclose(e,t){this.connected=false,delete this.id,this.emitReserved("disconnect",e,t),this._clearAcks();}_clearAcks(){Object.keys(this.acks).forEach(e=>{if(!this.sendBuffer.some(r=>String(r.id)===e)){let r=this.acks[e];delete this.acks[e],r.withError&&r.call(this,new Error("socket has been disconnected"));}});}onpacket(e){if(e.nsp===this.nsp)switch(e.type){case a.CONNECT:e.data&&e.data.sid?this.onconnect(e.data.sid,e.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case a.EVENT:case a.BINARY_EVENT:this.onevent(e);break;case a.ACK:case a.BINARY_ACK:this.onack(e);break;case a.DISCONNECT:this.ondisconnect();break;case a.CONNECT_ERROR:this.destroy();let r=new Error(e.data.message);r.data=e.data.data,this.emitReserved("connect_error",r);break}}onevent(e){let t=e.data||[];e.id!=null&&t.push(this.ack(e.id)),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t));}emitEvent(e){if(this._anyListeners&&this._anyListeners.length){let t=this._anyListeners.slice();for(let r of t)r.apply(this,e);}super.emit.apply(this,e),this._pid&&e.length&&typeof e[e.length-1]=="string"&&(this._lastOffset=e[e.length-1]);}ack(e){let t=this,r=false;return function(...i){r||(r=true,t.packet({type:a.ACK,id:e,data:i}));}}onack(e){let t=this.acks[e.id];typeof t=="function"&&(delete this.acks[e.id],t.withError&&e.data.unshift(null),t.apply(this,e.data));}onconnect(e,t){this.id=e,this.recovered=t&&this._pid===t,this._pid=t,this.connected=true,this.emitBuffered(),this._drainQueue(true),this.emitReserved("connect");}emitBuffered(){this.receiveBuffer.forEach(e=>this.emitEvent(e)),this.receiveBuffer=[],this.sendBuffer.forEach(e=>{this.notifyOutgoingListeners(e),this.packet(e);}),this.sendBuffer=[];}ondisconnect(){this.destroy(),this.onclose("io server disconnect");}destroy(){this.subs&&(this.subs.forEach(e=>e()),this.subs=void 0),this.io._destroy(this);}disconnect(){return this.connected&&this.packet({type:a.DISCONNECT}),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}close(){return this.disconnect()}compress(e){return this.flags.compress=e,this}get volatile(){return this.flags.volatile=true,this}timeout(e){return this.flags.timeout=e,this}onAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}prependAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}offAny(e){if(!this._anyListeners)return this;if(e){let t=this._anyListeners;for(let r=0;r<t.length;r++)if(e===t[r])return t.splice(r,1),this}else this._anyListeners=[];return this}listenersAny(){return this._anyListeners||[]}onAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.push(e),this}prependAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.unshift(e),this}offAnyOutgoing(e){if(!this._anyOutgoingListeners)return this;if(e){let t=this._anyOutgoingListeners;for(let r=0;r<t.length;r++)if(e===t[r])return t.splice(r,1),this}else this._anyOutgoingListeners=[];return this}listenersAnyOutgoing(){return this._anyOutgoingListeners||[]}notifyOutgoingListeners(e){if(this._anyOutgoingListeners&&this._anyOutgoingListeners.length){let t=this._anyOutgoingListeners.slice();for(let r of t)r.apply(this,e.data);}}};function x(s){s=s||{},this.ms=s.min||100,this.max=s.max||1e4,this.factor=s.factor||2,this.jitter=s.jitter>0&&s.jitter<=1?s.jitter:0,this.attempts=0;}x.prototype.duration=function(){var s=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),t=Math.floor(e*this.jitter*s);s=(Math.floor(e*10)&1)==0?s-t:s+t;}return Math.min(s,this.max)|0};x.prototype.reset=function(){this.attempts=0;};x.prototype.setMin=function(s){this.ms=s;};x.prototype.setMax=function(s){this.max=s;};x.prototype.setJitter=function(s){this.jitter=s;};var R=class extends f{constructor(e,t){var r;super(),this.nsps={},this.subs=[],e&&typeof e=="object"&&(t=e,e=void 0),t=t||{},t.path=t.path||"/socket.io",this.opts=t,g(this,t),this.reconnection(t.reconnection!==false),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor((r=t.randomizationFactor)!==null&&r!==void 0?r:.5),this.backoff=new x({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(t.timeout==null?2e4:t.timeout),this._readyState="closed",this.uri=e;let i=t.parser||ce;this.encoder=new i.Encoder,this.decoder=new i.Decoder,this._autoConnect=t.autoConnect!==false,this._autoConnect&&this.open();}reconnection(e){return arguments.length?(this._reconnection=!!e,e||(this.skipReconnect=true),this):this._reconnection}reconnectionAttempts(e){return e===void 0?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}reconnectionDelay(e){var t;return e===void 0?this._reconnectionDelay:(this._reconnectionDelay=e,(t=this.backoff)===null||t===void 0||t.setMin(e),this)}randomizationFactor(e){var t;return e===void 0?this._randomizationFactor:(this._randomizationFactor=e,(t=this.backoff)===null||t===void 0||t.setJitter(e),this)}reconnectionDelayMax(e){var t;return e===void 0?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,(t=this.backoff)===null||t===void 0||t.setMax(e),this)}timeout(e){return arguments.length?(this._timeout=e,this):this._timeout}maybeReconnectOnOpen(){!this._reconnecting&&this._reconnection&&this.backoff.attempts===0&&this.reconnect();}open(e){if(~this._readyState.indexOf("open"))return this;this.engine=new C(this.uri,this.opts);let t=this.engine,r=this;this._readyState="opening",this.skipReconnect=false;let i=l(t,"open",function(){r.onopen(),e&&e();}),n=c=>{this.cleanup(),this._readyState="closed",this.emitReserved("error",c),e?e(c):this.maybeReconnectOnOpen();},o=l(t,"error",n);if(this._timeout!==false){let c=this._timeout,h=this.setTimeoutFn(()=>{i(),n(new Error("timeout")),t.close();},c);this.opts.autoUnref&&h.unref(),this.subs.push(()=>{this.clearTimeoutFn(h);});}return this.subs.push(i),this.subs.push(o),this}connect(e){return this.open(e)}onopen(){this.cleanup(),this._readyState="open",this.emitReserved("open");let e=this.engine;this.subs.push(l(e,"ping",this.onping.bind(this)),l(e,"data",this.ondata.bind(this)),l(e,"error",this.onerror.bind(this)),l(e,"close",this.onclose.bind(this)),l(this.decoder,"decoded",this.ondecoded.bind(this)));}onping(){this.emitReserved("ping");}ondata(e){try{this.decoder.add(e);}catch(t){this.onclose("parse error",t);}}ondecoded(e){y(()=>{this.emitReserved("packet",e);},this.setTimeoutFn);}onerror(e){this.emitReserved("error",e);}socket(e,t){let r=this.nsps[e];return r?this._autoConnect&&!r.active&&r.connect():(r=new S(this,e,t),this.nsps[e]=r),r}_destroy(e){let t=Object.keys(this.nsps);for(let r of t)if(this.nsps[r].active)return;this._close();}_packet(e){let t=this.encoder.encode(e);for(let r=0;r<t.length;r++)this.engine.write(t[r],e.options);}cleanup(){this.subs.forEach(e=>e()),this.subs.length=0,this.decoder.destroy();}_close(){this.skipReconnect=true,this._reconnecting=false,this.onclose("forced close");}disconnect(){return this._close()}onclose(e,t){var r;this.cleanup(),(r=this.engine)===null||r===void 0||r.close(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect();}reconnect(){if(this._reconnecting||this.skipReconnect)return this;let e=this;if(this.backoff.attempts>=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=false;else {let t=this.backoff.duration();this._reconnecting=true;let r=this.setTimeoutFn(()=>{e.skipReconnect||(this.emitReserved("reconnect_attempt",e.backoff.attempts),!e.skipReconnect&&e.open(i=>{i?(e._reconnecting=false,e.reconnect(),this.emitReserved("reconnect_error",i)):e.onreconnect();}));},t);this.opts.autoUnref&&r.unref(),this.subs.push(()=>{this.clearTimeoutFn(r);});}}onreconnect(){let e=this.backoff.attempts;this._reconnecting=false,this.backoff.reset(),this.emitReserved("reconnect",e);}};var F={};function V(s,e){typeof s=="object"&&(e=s,s=void 0),e=e||{};let t=Le(s,e.path||"/socket.io"),r=t.source,i=t.id,n=t.path,o=F[i]&&n in F[i].nsps,c=e.forceNew||e["force new connection"]||e.multiplex===false||o,h;return c?h=new R(r,e):(F[i]||(F[i]=new R(r,e)),h=F[i]),t.query&&!e.query&&(e.query=t.queryKey),h.socket(t.path,e)}Object.assign(V,{Manager:R,Socket:S,io:V,connect:V});var he=class{constructor(e){this.options=e;}options;socket;connectPromise;balanceListeners=new Set;purchaseListeners=new Set;onBalanceChange(e){return this.balanceListeners.add(e),this.ensureConnected(),()=>{this.balanceListeners.delete(e),this.disconnectIfIdle();}}onStorePurchase(e){return this.purchaseListeners.add(e),this.ensureConnected(),()=>{this.purchaseListeners.delete(e),this.disconnectIfIdle();}}disconnect(){this.socket?.disconnect(),this.socket=void 0,this.connectPromise=void 0;}async ensureConnected(){if(this.socket?.connected)return this.socket;if(this.connectPromise)return this.connectPromise;this.connectPromise=(async()=>{let e=await this.options.getAccessToken();if(!e)return;let t=V(this.options.baseUrl,{transports:["websocket"],auth:{token:`Bearer ${e}`}});return t.on("balance_change",r=>{for(let i of this.balanceListeners)i(r);}),t.on("store_purchase_completed",r=>{for(let i of this.purchaseListeners)i(r);}),this.socket=t,t})();try{return await this.connectPromise}finally{this.connectPromise=void 0;}}disconnectIfIdle(){this.balanceListeners.size===0&&this.purchaseListeners.size===0&&this.disconnect();}};
2
+ exports.WsClient=he;return exports;})({});//# sourceMappingURL=looplay-sdk-realtime.min.js.map
3
+ //# sourceMappingURL=looplay-sdk-realtime.min.js.map