@wvdsh/sdk-js 1.3.1 → 1.3.3

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/index.d.ts CHANGED
@@ -1139,8 +1139,12 @@ declare class WavedashSDK extends EventTarget {
1139
1139
  * fetcher wired into `ConvexClient.setAuth` and honors `forceRefresh` so the
1140
1140
  * server can invalidate a stale token.
1141
1141
  *
1142
+ * Same-origin POST to /auth/refresh on the play domain — the
1143
+ * gameplaySession cookie set by the play server during playKey exchange
1144
+ * authenticates the request, so no cross-origin credentials handling needed.
1145
+ *
1142
1146
  * Concurrent callers share a single in-flight fetch to avoid duplicate
1143
- * requests to the parent's gameplay-token endpoint.
1147
+ * refresh round-trips.
1144
1148
  */
1145
1149
  private getAuthToken;
1146
1150
  /**
@@ -1150,8 +1154,11 @@ declare class WavedashSDK extends EventTarget {
1150
1154
  */
1151
1155
  ensureGameplayJwt(): Promise<string>;
1152
1156
  /**
1153
- * Set up listeners for page unload events to end gameplay session.
1154
- * Uses both beforeunload and pagehide for maximum reliability.
1157
+ * Set up listeners that flush the end-of-session request when the iframe
1158
+ * is going away. We listen for three signals:
1159
+ * - `beforeunload` / `pagehide` on our own window: covers tab close, hard
1160
+ * reload, and top-level navigation of the parent.
1161
+ * - `END_SESSION` postMessage from the parent: covers parent SPA navigation
1155
1162
  */
1156
1163
  private setupSessionEndListeners;
1157
1164
  }
package/dist/index.js CHANGED
@@ -3033,18 +3033,13 @@ var WavedashLogger = class {
3033
3033
  };
3034
3034
 
3035
3035
  // src/utils/parentOrigin.ts
3036
- function deriveParentOrigin() {
3037
- if (typeof window === "undefined") return "";
3038
- const iframeHost = window.location.host;
3039
- const match = iframeHost.match(/^[\w-]+\.(builds|local)\.(.+)$/);
3040
- if (match) {
3041
- const parentDomain = match[2];
3042
- return `${window.location.protocol}//${parentDomain}`;
3043
- }
3044
- console.error(`Invalid iframe hostname pattern: ${iframeHost}`);
3045
- return "";
3036
+ var _parentOrigin = "";
3037
+ function setParentOrigin(origin) {
3038
+ _parentOrigin = origin;
3039
+ }
3040
+ function getParentOrigin() {
3041
+ return _parentOrigin;
3046
3042
  }
3047
- var parentOrigin = deriveParentOrigin();
3048
3043
 
3049
3044
  // src/utils/iframeMessenger.ts
3050
3045
  var RESPONSE_TIMEOUT_MS = 15e3;
@@ -3052,7 +3047,7 @@ var IFrameMessenger = class {
3052
3047
  constructor() {
3053
3048
  // Arrow function automatically captures 'this' from the class instance
3054
3049
  this.handleMessage = (event) => {
3055
- if (event.origin !== parentOrigin) {
3050
+ if (event.origin !== getParentOrigin()) {
3056
3051
  if (event.source !== window) {
3057
3052
  console.warn(`Ignored message from untrusted origin: ${event.origin}`);
3058
3053
  }
@@ -3097,12 +3092,14 @@ var IFrameMessenger = class {
3097
3092
  this.listeners.get(type)?.delete(listener);
3098
3093
  }
3099
3094
  postToParent(requestType, data) {
3095
+ const parentOrigin = getParentOrigin();
3100
3096
  if (typeof window === "undefined" || !parentOrigin) return false;
3101
3097
  window.parent.postMessage({ type: requestType, ...data }, parentOrigin);
3102
3098
  return true;
3103
3099
  }
3104
3100
  async requestFromParent(requestType, data) {
3105
3101
  return new Promise((resolve, reject) => {
3102
+ const parentOrigin = getParentOrigin();
3106
3103
  if (typeof window === "undefined" || !parentOrigin) {
3107
3104
  reject(new Error("Parent origin not found"));
3108
3105
  return;
@@ -4175,8 +4172,12 @@ var WavedashSDK = class extends EventTarget {
4175
4172
  * fetcher wired into `ConvexClient.setAuth` and honors `forceRefresh` so the
4176
4173
  * server can invalidate a stale token.
4177
4174
  *
4175
+ * Same-origin POST to /auth/refresh on the play domain — the
4176
+ * gameplaySession cookie set by the play server during playKey exchange
4177
+ * authenticates the request, so no cross-origin credentials handling needed.
4178
+ *
4178
4179
  * Concurrent callers share a single in-flight fetch to avoid duplicate
4179
- * requests to the parent's gameplay-token endpoint.
4180
+ * refresh round-trips.
4180
4181
  */
4181
4182
  getAuthToken(forceRefresh = false) {
4182
4183
  if (!forceRefresh && this.gameplayJwt) {
@@ -4186,14 +4187,12 @@ var WavedashSDK = class extends EventTarget {
4186
4187
  return this.gameplayJwtPromise;
4187
4188
  }
4188
4189
  const promise = (async () => {
4189
- const response = await fetch(
4190
- `${parentOrigin}/auth/gameplay_token/${this.gameCloudId}`,
4191
- {
4192
- credentials: "include"
4193
- }
4194
- );
4190
+ const response = await fetch("/auth/refresh", {
4191
+ method: "POST",
4192
+ credentials: "same-origin"
4193
+ });
4195
4194
  if (!response.ok) {
4196
- throw new Error(`Failed to fetch gameplay token: ${response.status}`);
4195
+ throw new Error(`Failed to refresh gameplay token: ${response.status}`);
4197
4196
  }
4198
4197
  this.gameplayJwt = await response.text();
4199
4198
  return this.gameplayJwt;
@@ -4214,48 +4213,48 @@ var WavedashSDK = class extends EventTarget {
4214
4213
  return this.getAuthToken();
4215
4214
  }
4216
4215
  /**
4217
- * Set up listeners for page unload events to end gameplay session.
4218
- * Uses both beforeunload and pagehide for maximum reliability.
4216
+ * Set up listeners that flush the end-of-session request when the iframe
4217
+ * is going away. We listen for three signals:
4218
+ * - `beforeunload` / `pagehide` on our own window: covers tab close, hard
4219
+ * reload, and top-level navigation of the parent.
4220
+ * - `END_SESSION` postMessage from the parent: covers parent SPA navigation
4219
4221
  */
4220
4222
  setupSessionEndListeners() {
4221
4223
  const endSessionEndpoint = `${this.convexHttpUrl}/gameplay/end-session`;
4222
- void this.ensureGameplayJwt().then(
4223
- (jwt) => fetch(endSessionEndpoint, {
4224
- method: "POST",
4225
- headers: {
4226
- "Content-Type": "application/json",
4227
- Authorization: `Bearer ${jwt}`
4228
- },
4229
- body: JSON.stringify({ _type: "warmup" })
4230
- })
4231
- ).catch(() => {
4232
- });
4233
- const endGameplaySession = (_event) => {
4224
+ const endGameplaySession = () => {
4234
4225
  if (this.sessionEndSent) return;
4226
+ if (!this.gameplayJwt) return;
4235
4227
  this.sessionEndSent = true;
4236
4228
  const pendingData = this.statsManager.getPendingData();
4237
4229
  this.lobbyManager.destroy();
4238
4230
  this.heartbeatManager.destroy();
4239
4231
  this.statsManager.destroy();
4240
- const sessionEndData = {};
4232
+ const body = {
4233
+ gameplayJwt: this.gameplayJwt
4234
+ };
4241
4235
  if (pendingData?.stats?.length) {
4242
- sessionEndData.stats = pendingData.stats;
4236
+ body.stats = pendingData.stats;
4243
4237
  }
4244
4238
  if (pendingData?.achievements?.length) {
4245
- sessionEndData.achievements = pendingData.achievements;
4239
+ body.achievements = pendingData.achievements;
4240
+ }
4241
+ const payload = JSON.stringify(body);
4242
+ const beaconSent = navigator?.sendBeacon?.(endSessionEndpoint, payload);
4243
+ if (!beaconSent) {
4244
+ fetch(endSessionEndpoint, {
4245
+ method: "POST",
4246
+ body: payload,
4247
+ keepalive: true
4248
+ }).catch(() => {
4249
+ });
4246
4250
  }
4247
- fetch(endSessionEndpoint, {
4248
- method: "POST",
4249
- body: JSON.stringify(sessionEndData),
4250
- keepalive: true,
4251
- headers: {
4252
- "Content-Type": "application/json",
4253
- Authorization: `Bearer ${this.gameplayJwt}`
4254
- }
4255
- });
4256
4251
  };
4257
4252
  window.addEventListener("beforeunload", endGameplaySession);
4258
4253
  window.addEventListener("pagehide", endGameplaySession);
4254
+ iframeMessenger.addEventListener(
4255
+ IFRAME_MESSAGE_TYPE5.END_SESSION,
4256
+ endGameplaySession
4257
+ );
4259
4258
  }
4260
4259
  };
4261
4260
  function setupWavedashSDK() {
@@ -4278,6 +4277,7 @@ function setupWavedashSDK() {
4278
4277
  `Wavedash SDK: failed to parse ?${UrlParams.SdkConfig}= as JSON: ${message}`
4279
4278
  );
4280
4279
  }
4280
+ setParentOrigin(sdkConfig.parentOrigin);
4281
4281
  const sdk = new WavedashSDK(sdkConfig);
4282
4282
  window.Wavedash = sdk;
4283
4283
  window.WavedashJS = sdk;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wvdsh/sdk-js",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "type": "module",
5
5
  "description": "Wavedash JavaScript SDK",
6
6
  "main": "./dist/client.js",
@@ -49,7 +49,7 @@
49
49
  "typescript-eslint": "^8.52.0"
50
50
  },
51
51
  "dependencies": {
52
- "@wvdsh/api": "^0.1.3",
52
+ "@wvdsh/api": "^0.1.10",
53
53
  "convex": "^1.34.0",
54
54
  "lodash.debounce": "^4.0.8"
55
55
  }