@series-inc/rundot-game-sdk 5.2.0 → 5.3.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { MockAdsApi, MockLifecycleApi, MockAnalyticsApi, getSandboxConfig, createMockStorageApi, MockAvatarApi, MockNavigationApi, MockNotificationsApi, MockPopupsApi, SandboxProfileApi, MockDeviceApi, MockEnvironmentApi, MockSystemApi, MockCdnApi, MockTimeApi, MockAiApi, MockHapticsApi, MockFeaturesApi, MockLoggingApi, MockIapApi, MockSocialApi, initializeRoomsApi, MockPreloaderApi, MockSharedAssetsApi, RundotGameRoom, getCloudRunUrl, buildFunctionsBaseUrl } from './chunk-4ROGEXJO.js';
1
+ import { MockAdsApi, MockLifecycleApi, MockAnalyticsApi, getSandboxConfig, createMockStorageApi, MockAvatarApi, MockNavigationApi, MockNotificationsApi, MockPopupsApi, SandboxProfileApi, MockDeviceApi, MockEnvironmentApi, MockSystemApi, MockCdnApi, MockTimeApi, MockAiApi, MockHapticsApi, MockFeaturesApi, MockLoggingApi, MockIapApi, MockSocialApi, initializeRoomsApi, MockPreloaderApi, MockSharedAssetsApi, RundotGameRoom, getCloudRunUrl, buildFunctionsBaseUrl } from './chunk-DHC4VLMR.js';
2
2
  import './chunk-CJU2J3S6.js';
3
3
 
4
4
  // src/firebase/localSandboxIdentity.ts
@@ -23,6 +23,7 @@ var APP_NAME = "rundot-game-sandbox";
23
23
  var firebaseClientPromise = null;
24
24
  var cachedClient = null;
25
25
  var LOCAL_TEST_PASSWORD = "rundot-game-dev-password";
26
+ var cliRecoveryPromise = null;
26
27
  async function getFirebaseClient() {
27
28
  if (cachedClient) {
28
29
  return cachedClient;
@@ -30,8 +31,54 @@ async function getFirebaseClient() {
30
31
  if (!firebaseClientPromise) {
31
32
  firebaseClientPromise = initializeFirebaseClient();
32
33
  }
33
- cachedClient = await firebaseClientPromise;
34
- return cachedClient;
34
+ try {
35
+ cachedClient = await firebaseClientPromise;
36
+ return cachedClient;
37
+ } catch (error) {
38
+ firebaseClientPromise = null;
39
+ throw error;
40
+ }
41
+ }
42
+ async function exchangeRefreshToken(refreshToken, apiKey) {
43
+ const response = await fetch(
44
+ `https://securetoken.googleapis.com/v1/token?key=${apiKey}`,
45
+ {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
48
+ body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`
49
+ }
50
+ );
51
+ if (!response.ok) {
52
+ const text = await response.text();
53
+ throw new Error(`[RUN.game SDK] Refresh token exchange failed (${response.status}): ${text}`);
54
+ }
55
+ const data = await response.json();
56
+ const idToken = data.id_token;
57
+ if (typeof idToken !== "string" || idToken.length === 0) {
58
+ throw new Error("[RUN.game SDK] Refresh token exchange: response missing id_token");
59
+ }
60
+ return { idToken };
61
+ }
62
+ async function exchangeForCustomToken(idToken, cloudRunBaseUrl, playerId) {
63
+ const url = `${cloudRunBaseUrl}/v1/sandbox-auth/exchange`;
64
+ const response = await fetch(url, {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ "Authorization": `Bearer ${idToken}`
69
+ },
70
+ body: JSON.stringify(playerId ? { playerId } : {})
71
+ });
72
+ if (!response.ok) {
73
+ const text = await response.text();
74
+ throw new Error(`[RUN.game SDK] Custom token exchange failed (${response.status}): ${text}`);
75
+ }
76
+ const data = await response.json();
77
+ const customToken = data.customToken;
78
+ if (typeof customToken !== "string" || customToken.length === 0) {
79
+ throw new Error("[RUN.game SDK] Custom token exchange: response missing customToken");
80
+ }
81
+ return customToken;
35
82
  }
36
83
  async function initializeFirebaseClient() {
37
84
  const config = getSandboxConfig();
@@ -41,6 +88,7 @@ async function initializeFirebaseClient() {
41
88
  "[Run.game SDK] Cannot initialize Firebase: Sandbox config not available. Make sure the Vite plugin is configured correctly."
42
89
  );
43
90
  }
91
+ const isLocalMode = config.target === "local";
44
92
  console.log("[RUN.game SDK] Importing Firebase modules...");
45
93
  const [
46
94
  firebaseApp,
@@ -51,9 +99,35 @@ async function initializeFirebaseClient() {
51
99
  import('firebase/auth'),
52
100
  import('firebase/firestore')
53
101
  ]);
54
- const { initializeApp, getApps, getApp } = firebaseApp;
55
- const { getAuth, GoogleAuthProvider, signInWithPopup, signInWithRedirect, getRedirectResult, signInWithEmailAndPassword, createUserWithEmailAndPassword, browserLocalPersistence, setPersistence, connectAuthEmulator } = firebaseAuth;
56
- const { getFirestore, connectFirestoreEmulator, doc, collection, getDoc, setDoc, updateDoc, deleteDoc, addDoc, query, where, orderBy, limit, onSnapshot, serverTimestamp, deleteField } = firebaseFirestore;
102
+ const { initializeApp, getApps } = firebaseApp;
103
+ const {
104
+ getAuth,
105
+ signInWithEmailAndPassword,
106
+ createUserWithEmailAndPassword,
107
+ signInWithCustomToken,
108
+ browserLocalPersistence,
109
+ browserSessionPersistence,
110
+ setPersistence,
111
+ connectAuthEmulator
112
+ } = firebaseAuth;
113
+ const {
114
+ getFirestore,
115
+ connectFirestoreEmulator,
116
+ doc,
117
+ collection,
118
+ getDoc,
119
+ setDoc,
120
+ updateDoc,
121
+ deleteDoc,
122
+ addDoc,
123
+ query,
124
+ where,
125
+ orderBy,
126
+ limit,
127
+ onSnapshot,
128
+ serverTimestamp,
129
+ deleteField
130
+ } = firebaseFirestore;
57
131
  console.log("[RUN.game SDK] Firebase modules imported");
58
132
  console.log("[RUN.game SDK] Getting/creating Firebase app...");
59
133
  const existingApps = getApps();
@@ -63,7 +137,6 @@ async function initializeFirebaseClient() {
63
137
  console.log("[RUN.game SDK] Getting auth...");
64
138
  const auth = getAuth(app);
65
139
  console.log("[RUN.game SDK] Auth ready");
66
- const isLocalMode = config.target === "local";
67
140
  if (isLocalMode && config.authEmulatorHost) {
68
141
  console.log("[RUN.game SDK] Connecting to Auth emulator:", config.authEmulatorHost);
69
142
  try {
@@ -75,9 +148,10 @@ async function initializeFirebaseClient() {
75
148
  } else {
76
149
  console.log("[RUN.game SDK] Using real Firebase Auth (target:", config.target, ")");
77
150
  }
78
- console.log("[RUN.game SDK] Setting persistence...");
151
+ const persistence = isLocalMode ? browserLocalPersistence : browserSessionPersistence;
152
+ console.log("[RUN.game SDK] Setting persistence:", isLocalMode ? "local (IndexedDB)" : "session (per-tab)");
79
153
  try {
80
- await setPersistence(auth, browserLocalPersistence);
154
+ await setPersistence(auth, persistence);
81
155
  console.log("[RUN.game SDK] Persistence set");
82
156
  } catch (e) {
83
157
  console.log("[RUN.game SDK] Persistence error:", e);
@@ -95,26 +169,61 @@ async function initializeFirebaseClient() {
95
169
  }, 3e3);
96
170
  });
97
171
  console.log("[RUN.game SDK] Auth state restored, user:", initialUser?.uid ?? "none");
98
- const googleProvider = new GoogleAuthProvider();
99
- if (!initialUser) {
100
- console.log("[RUN.game SDK] No persisted user, checking for redirect result...");
172
+ if (isLocalMode && !initialUser) {
173
+ let localPlayerId = "player1";
101
174
  try {
102
- const redirectResult = await getRedirectResult(auth);
103
- if (redirectResult?.user) {
104
- console.log("[RUN.game SDK] Sign-in redirect completed successfully, user:", redirectResult.user.uid);
105
- } else {
106
- console.log("[RUN.game SDK] No redirect result (normal page load or redirect state lost)");
175
+ const stored = localStorage.getItem("rundot-game-sandbox-selected-player");
176
+ if (stored && /^player\d+$/.test(stored.trim().toLowerCase())) {
177
+ localPlayerId = stored.trim().toLowerCase();
107
178
  }
179
+ } catch {
180
+ }
181
+ const email = buildLocalSandboxEmail(normalizeLocalSandboxPlayerId(localPlayerId));
182
+ console.log("[RUN.game SDK] Local auto-sign-in as", localPlayerId, "(", email, ")");
183
+ try {
184
+ await signInWithEmailAndPassword(auth, email, LOCAL_TEST_PASSWORD);
108
185
  } catch (error) {
109
- console.error("[RUN.game SDK] Redirect sign-in error:", error);
186
+ const authError = error;
187
+ if (authError.code === "auth/user-not-found" || authError.code === "auth/invalid-credential") {
188
+ console.log("[RUN.game SDK] Local test user not found, creating:", email);
189
+ await createUserWithEmailAndPassword(auth, email, LOCAL_TEST_PASSWORD);
190
+ } else {
191
+ console.warn("[RUN.game SDK] Local auto-sign-in failed (emulator may not be ready):", error);
192
+ }
110
193
  }
111
- } else {
112
- console.log("[RUN.game SDK] User already restored from persistence, skipping redirect check");
194
+ console.log("[RUN.game SDK] Local auto-sign-in complete. User:", auth.currentUser?.uid ?? "none");
195
+ }
196
+ let manualSignout = false;
197
+ try {
198
+ manualSignout = sessionStorage.getItem("rundot-game-sandbox-manual-signout") === "true";
199
+ } catch {
200
+ }
201
+ if (!isLocalMode && !initialUser && manualSignout) {
202
+ console.log('[RUN.game SDK] Skipping CLI auto-auth: developer manually signed out. Click "Sign in" in the toolbar to re-enable.');
203
+ } else if (!isLocalMode && !initialUser) {
204
+ const { cliRefreshToken, cliApiKey } = config;
205
+ if (!cliRefreshToken || !cliApiKey) {
206
+ throw new Error(
207
+ "[RUN.game SDK] No auth session and no CLI token. Run `rundot login --env <env>` and restart."
208
+ );
209
+ }
210
+ console.log("[RUN.game SDK] Bootstrapping auth from CLI refresh token...");
211
+ const { idToken } = await exchangeRefreshToken(cliRefreshToken, cliApiKey);
212
+ const cloudRunBase = config.cloudRunUrl || "/__rundotcloudrun";
213
+ let bootstrapPlayerId;
214
+ try {
215
+ const stored = sessionStorage.getItem("rundot-game-sandbox-active-player");
216
+ if (stored) bootstrapPlayerId = stored;
217
+ } catch {
218
+ }
219
+ const customToken = await exchangeForCustomToken(idToken, cloudRunBase, bootstrapPlayerId);
220
+ await signInWithCustomToken(auth, customToken);
221
+ console.log("[RUN.game SDK] CLI auth bootstrap successful. User:", auth.currentUser?.uid, "Player:", bootstrapPlayerId || "default");
113
222
  }
114
223
  console.log("[RUN.game SDK] Current auth state:", auth.currentUser ? `signed in as ${auth.currentUser.uid}` : "not signed in");
115
224
  console.log("[RUN.game SDK] Getting Firestore...");
116
225
  const firestore = getFirestore(app);
117
- if (config.target === "local" && config.firestoreEmulatorHost) {
226
+ if (isLocalMode && config.firestoreEmulatorHost) {
118
227
  console.log("[RUN.game SDK] Connecting to Firestore emulator:", config.firestoreEmulatorHost);
119
228
  try {
120
229
  const [host, port] = config.firestoreEmulatorHost.split(":");
@@ -136,15 +245,6 @@ async function initializeFirebaseClient() {
136
245
  getProfileId() {
137
246
  return auth.currentUser?.uid ?? null;
138
247
  },
139
- async signInWithGoogle() {
140
- if (isLocalMode) {
141
- return this.signInWithLocalPlayer("player1");
142
- }
143
- console.log("[RUN.game SDK] Signing in with Google popup...");
144
- const result = await signInWithPopup(auth, googleProvider);
145
- console.log("[RUN.game SDK] Sign-in successful, user:", result.user.uid);
146
- return result;
147
- },
148
248
  async signInWithLocalPlayer(playerId) {
149
249
  if (!isLocalMode) {
150
250
  throw new Error("[Run.game SDK] signInWithLocalPlayer is only available in local (emulator) mode.");
@@ -185,21 +285,78 @@ async function initializeFirebaseClient() {
185
285
  const user = auth.currentUser;
186
286
  console.log("[RUN.game SDK] getIdTokenOrThrow: currentUser =", user ? user.uid : null);
187
287
  if (!user) {
188
- throw new Error(
189
- "[Run.game SDK] Not signed in. Click the Run.game Sandbox toolbar to sign in."
190
- );
288
+ const hint = isLocalMode ? "Click the Run.game Sandbox toolbar to sign in." : "Run `rundot login --env <env>` and restart the dev server.";
289
+ throw new Error(`[RUN.game SDK] Not signed in. ${hint}`);
191
290
  }
192
- console.log("[RUN.game SDK] getIdTokenOrThrow: Forcing token refresh...");
193
291
  try {
292
+ console.log("[RUN.game SDK] getIdTokenOrThrow: Forcing token refresh...");
194
293
  const token = await user.getIdToken(true);
195
294
  console.log("[RUN.game SDK] getIdTokenOrThrow: Got fresh token, length:", token?.length);
196
295
  return token;
197
- } catch (error) {
198
- console.error("[RUN.game SDK] getIdTokenOrThrow: Token refresh failed:", error);
199
- console.log("[RUN.game SDK] getIdTokenOrThrow: Signing out stale session...");
200
- await auth.signOut();
296
+ } catch (refreshError) {
297
+ const sandboxConfig = getSandboxConfig();
298
+ if (sandboxConfig?.target === "local") {
299
+ const currentEmail = user.email;
300
+ if (currentEmail) {
301
+ console.warn("[RUN.game SDK] Token refresh failed (local), re-authenticating as", currentEmail, "...", refreshError);
302
+ try {
303
+ await signInWithEmailAndPassword(auth, currentEmail, LOCAL_TEST_PASSWORD);
304
+ const freshUser = auth.currentUser;
305
+ if (freshUser) {
306
+ const token = await freshUser.getIdToken(true);
307
+ console.log("[RUN.game SDK] Local re-auth successful, fresh token length:", token?.length);
308
+ return token;
309
+ }
310
+ } catch (reAuthError) {
311
+ console.error("[RUN.game SDK] Local re-auth failed:", reAuthError);
312
+ }
313
+ }
314
+ console.warn("[RUN.game SDK] Local recovery failed, signing out as last resort");
315
+ await auth.signOut();
316
+ throw new Error(
317
+ "[RUN.game SDK] Session expired. Please sign in again via the Run.game toolbar."
318
+ );
319
+ }
320
+ console.warn("[RUN.game SDK] Token refresh failed, attempting CLI recovery...", refreshError);
321
+ const recoveryRefreshToken = sandboxConfig?.cliRefreshToken;
322
+ const recoveryApiKey = sandboxConfig?.cliApiKey;
323
+ if (recoveryRefreshToken && recoveryApiKey) {
324
+ if (cliRecoveryPromise) {
325
+ return cliRecoveryPromise;
326
+ }
327
+ cliRecoveryPromise = (async () => {
328
+ try {
329
+ const { idToken } = await exchangeRefreshToken(
330
+ recoveryRefreshToken,
331
+ recoveryApiKey
332
+ );
333
+ const cloudRunBase = sandboxConfig.cloudRunUrl || "/__rundotcloudrun";
334
+ let activePlayerId;
335
+ try {
336
+ const stored = sessionStorage.getItem("rundot-game-sandbox-active-player");
337
+ if (stored) activePlayerId = stored;
338
+ } catch {
339
+ }
340
+ const customToken = await exchangeForCustomToken(idToken, cloudRunBase, activePlayerId);
341
+ await signInWithCustomToken(auth, customToken);
342
+ const recoveredUser = auth.currentUser;
343
+ if (!recoveredUser) {
344
+ throw new Error("[RUN.game SDK] CLI recovery: signInWithCustomToken succeeded but currentUser is null");
345
+ }
346
+ console.log("[RUN.game SDK] CLI recovery successful. Player:", activePlayerId || "default");
347
+ return await recoveredUser.getIdToken(true);
348
+ } finally {
349
+ cliRecoveryPromise = null;
350
+ }
351
+ })();
352
+ try {
353
+ return await cliRecoveryPromise;
354
+ } catch (recoveryError) {
355
+ console.error("[RUN.game SDK] CLI recovery failed:", recoveryError);
356
+ }
357
+ }
201
358
  throw new Error(
202
- "[Run.game SDK] Session expired. Please sign in again via the Run.game toolbar."
359
+ "[RUN.game SDK] Session expired and recovery failed. Run `rundot login --env <env>` and restart."
203
360
  );
204
361
  }
205
362
  },
@@ -1575,10 +1732,13 @@ var SandboxHost = class {
1575
1732
  setupSandboxSignIn() {
1576
1733
  if (typeof window === "undefined") return;
1577
1734
  const broadcastAuthState = (user) => {
1735
+ const isCliAuth = user?.providerData?.length === 0;
1736
+ const authSource = isCliAuth ? "cli" : "local";
1578
1737
  window.dispatchEvent(
1579
1738
  new CustomEvent("rundot-game-auth-state-changed", {
1580
1739
  detail: {
1581
1740
  signedIn: user !== null,
1741
+ source: authSource,
1582
1742
  user: user ? {
1583
1743
  uid: user.uid,
1584
1744
  email: user.email,
@@ -1641,18 +1801,27 @@ var SandboxHost = class {
1641
1801
  };
1642
1802
  window.addEventListener("rundot-game-check-auth-state", checkAuthHandler);
1643
1803
  this.cleanupFunctions.push(() => window.removeEventListener("rundot-game-check-auth-state", checkAuthHandler));
1644
- window.__RUNDOT_GAME_SANDBOX_SIGN_IN__ = () => {
1645
- if (this.client) {
1646
- return this.client.signInWithGoogle().then(() => console.log("[RUN.game SDK] Google sign-in successful")).catch((error) => {
1647
- console.error("[RUN.game SDK] Google sign-in failed:", error);
1648
- throw error;
1649
- });
1804
+ window.__RUNDOT_GAME_SANDBOX_SWITCH_PLAYER__ = async (playerId) => {
1805
+ const sandboxConfig = getSandboxConfig();
1806
+ if (!sandboxConfig?.cliRefreshToken || !sandboxConfig?.cliApiKey) {
1807
+ throw new Error("[RUN.game SDK] Player switching requires CLI auth (dev/staging only).");
1650
1808
  }
1651
- if (!this.clientReady) {
1652
- throw new Error("[RUN.game SDK] Firebase not configured.");
1653
- }
1654
- console.warn("[RUN.game SDK] Firebase client still initializing, please wait a moment...");
1655
- throw new Error("[RUN.game SDK] Firebase client still initializing. Please wait a moment and try again.");
1809
+ console.log("[RUN.game SDK] Switching to player:", playerId ?? "(default)");
1810
+ const { idToken } = await exchangeRefreshToken(
1811
+ sandboxConfig.cliRefreshToken,
1812
+ sandboxConfig.cliApiKey
1813
+ );
1814
+ const cloudRunBase = sandboxConfig.cloudRunUrl || "/__rundotcloudrun";
1815
+ const customToken = await exchangeForCustomToken(
1816
+ idToken,
1817
+ cloudRunBase,
1818
+ playerId
1819
+ // undefined = real profile, 'player1' = synthetic
1820
+ );
1821
+ const { signInWithCustomToken } = await import('firebase/auth');
1822
+ const client = await getFirebaseClient();
1823
+ await signInWithCustomToken(client.auth, customToken);
1824
+ window.location.reload();
1656
1825
  };
1657
1826
  window.__RUNDOT_GAME_SANDBOX_SIGN_IN_AS__ = (playerId) => {
1658
1827
  if (this.client) {
@@ -1678,7 +1847,7 @@ var SandboxHost = class {
1678
1847
  }
1679
1848
  };
1680
1849
  this.cleanupFunctions.push(() => {
1681
- delete window.__RUNDOT_GAME_SANDBOX_SIGN_IN__;
1850
+ delete window.__RUNDOT_GAME_SANDBOX_SWITCH_PLAYER__;
1682
1851
  delete window.__RUNDOT_GAME_SANDBOX_SIGN_IN_AS__;
1683
1852
  delete window.__RUNDOT_GAME_SANDBOX_SIGN_OUT__;
1684
1853
  });
@@ -2172,5 +2341,5 @@ var SandboxHost = class {
2172
2341
  };
2173
2342
 
2174
2343
  export { SandboxHost };
2175
- //# sourceMappingURL=SandboxHost-OSPEPZ37.js.map
2176
- //# sourceMappingURL=SandboxHost-OSPEPZ37.js.map
2344
+ //# sourceMappingURL=SandboxHost-2VX2B4AS.js.map
2345
+ //# sourceMappingURL=SandboxHost-2VX2B4AS.js.map