@series-inc/venus-sdk 3.4.2-beta.2 → 3.4.3-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.
@@ -0,0 +1,1889 @@
1
+ import { MockAdsApi, MockLifecycleApi, MockAnalyticsApi, getSandboxConfig, createMockStorageApi, MockAvatarApi, MockNavigationApi, MockNotificationsApi, MockPopupsApi, MockProfileApi, MockDeviceApi, MockEnvironmentApi, MockSystemApi, MockCdnApi, MockTimeApi, MockAiApi, MockHapticsApi, MockFeaturesApi, MockLoggingApi, MockIapApi, MockSocialApi, initializeRoomsApi, MockPreloaderApi, MockSharedAssetsApi, VenusRoom, buildFunctionsBaseUrl } from './chunk-Q7SNANYR.js';
2
+ import './chunk-3APM3V2M.js';
3
+
4
+ // src/firebase/firebaseClient.ts
5
+ var APP_NAME = "venus-sandbox";
6
+ var firebaseClientPromise = null;
7
+ var cachedClient = null;
8
+ async function getFirebaseClient() {
9
+ if (cachedClient) {
10
+ return cachedClient;
11
+ }
12
+ if (!firebaseClientPromise) {
13
+ firebaseClientPromise = initializeFirebaseClient();
14
+ }
15
+ cachedClient = await firebaseClientPromise;
16
+ return cachedClient;
17
+ }
18
+ async function initializeFirebaseClient() {
19
+ console.log("[Venus SDK] === FIREBASE CLIENT BUILD #4 ===");
20
+ const config = getSandboxConfig();
21
+ console.log("[Venus SDK] Config:", config?.target, config?.gameId);
22
+ if (!config || !config.firebaseConfig) {
23
+ throw new Error(
24
+ "[Venus SDK] Cannot initialize Firebase: Sandbox config not available. Make sure the Vite plugin is configured correctly."
25
+ );
26
+ }
27
+ console.log("[Venus SDK] Importing Firebase modules...");
28
+ const [
29
+ firebaseApp,
30
+ firebaseAuth,
31
+ firebaseFirestore
32
+ ] = await Promise.all([
33
+ import('firebase/app'),
34
+ import('firebase/auth'),
35
+ import('firebase/firestore')
36
+ ]);
37
+ const { initializeApp, getApps, getApp } = firebaseApp;
38
+ const { getAuth, GoogleAuthProvider, signInWithPopup, signInWithRedirect, getRedirectResult, signInWithEmailAndPassword, createUserWithEmailAndPassword, browserLocalPersistence, setPersistence, connectAuthEmulator } = firebaseAuth;
39
+ const { getFirestore, connectFirestoreEmulator, doc, collection, getDoc, setDoc, updateDoc, deleteDoc, addDoc, query, where, orderBy, limit, onSnapshot, serverTimestamp, deleteField } = firebaseFirestore;
40
+ console.log("[Venus SDK] Firebase modules imported");
41
+ console.log("[Venus SDK] Getting/creating Firebase app...");
42
+ const existingApps = getApps();
43
+ const existingApp = existingApps.find((app2) => app2.name === APP_NAME);
44
+ const app = existingApp ?? initializeApp(config.firebaseConfig, APP_NAME);
45
+ console.log("[Venus SDK] Firebase app ready:", app.name);
46
+ console.log("[Venus SDK] Getting auth...");
47
+ const auth = getAuth(app);
48
+ console.log("[Venus SDK] Auth ready");
49
+ const isLocalMode = config.target === "local";
50
+ if (isLocalMode && config.authEmulatorHost) {
51
+ console.log("[Venus SDK] Connecting to Auth emulator:", config.authEmulatorHost);
52
+ try {
53
+ connectAuthEmulator(auth, `http://${config.authEmulatorHost}`, { disableWarnings: true });
54
+ console.log("[Venus SDK] Auth emulator connected");
55
+ } catch (e) {
56
+ console.log("[Venus SDK] Auth emulator connection error (may already be connected):", e);
57
+ }
58
+ } else {
59
+ console.log("[Venus SDK] Using real Firebase Auth (target:", config.target, ")");
60
+ }
61
+ console.log("[Venus SDK] Setting persistence...");
62
+ try {
63
+ await setPersistence(auth, browserLocalPersistence);
64
+ console.log("[Venus SDK] Persistence set");
65
+ } catch (e) {
66
+ console.log("[Venus SDK] Persistence error:", e);
67
+ }
68
+ console.log("[Venus SDK] Waiting for auth state restoration...");
69
+ const initialUser = await new Promise((resolve) => {
70
+ const unsubscribe = auth.onAuthStateChanged((user) => {
71
+ console.log("[Venus SDK] onAuthStateChanged fired, user:", user?.uid ?? "null");
72
+ unsubscribe();
73
+ resolve(user);
74
+ });
75
+ setTimeout(() => {
76
+ unsubscribe();
77
+ resolve(null);
78
+ }, 3e3);
79
+ });
80
+ console.log("[Venus SDK] Auth state restored, user:", initialUser?.uid ?? "none");
81
+ const googleProvider = new GoogleAuthProvider();
82
+ if (!initialUser) {
83
+ console.log("[Venus SDK] No persisted user, checking for redirect result...");
84
+ try {
85
+ const redirectResult = await getRedirectResult(auth);
86
+ if (redirectResult?.user) {
87
+ console.log("[Venus SDK] Sign-in redirect completed successfully, user:", redirectResult.user.uid);
88
+ } else {
89
+ console.log("[Venus SDK] No redirect result (normal page load or redirect state lost)");
90
+ }
91
+ } catch (error) {
92
+ console.error("[Venus SDK] Redirect sign-in error:", error);
93
+ }
94
+ } else {
95
+ console.log("[Venus SDK] User already restored from persistence, skipping redirect check");
96
+ }
97
+ console.log("[Venus SDK] Current auth state:", auth.currentUser ? `signed in as ${auth.currentUser.uid}` : "not signed in");
98
+ console.log("[Venus SDK] Getting Firestore...");
99
+ const firestore = getFirestore(app);
100
+ if (config.target === "local" && config.firestoreEmulatorHost) {
101
+ console.log("[Venus SDK] Connecting to Firestore emulator:", config.firestoreEmulatorHost);
102
+ try {
103
+ const [host, port] = config.firestoreEmulatorHost.split(":");
104
+ connectFirestoreEmulator(firestore, host, parseInt(port, 10));
105
+ console.log("[Venus SDK] Firestore emulator connected");
106
+ } catch (e) {
107
+ console.log("[Venus SDK] Firestore emulator already connected or error:", e);
108
+ }
109
+ }
110
+ const client = {
111
+ app,
112
+ auth,
113
+ firestore,
114
+ config,
115
+ // Auth helpers
116
+ isSignedIn() {
117
+ return auth.currentUser !== null;
118
+ },
119
+ getProfileId() {
120
+ return auth.currentUser?.uid ?? null;
121
+ },
122
+ async signInWithGoogle() {
123
+ if (isLocalMode) {
124
+ console.log("[Venus SDK] Local mode: signing in with test user...");
125
+ const testEmail = "dev@series.ai";
126
+ const testPassword = "venus-dev-password";
127
+ try {
128
+ const result2 = await signInWithEmailAndPassword(auth, testEmail, testPassword);
129
+ console.log("[Venus SDK] Sign-in successful, user:", result2.user.uid);
130
+ return result2;
131
+ } catch (error) {
132
+ const authError = error;
133
+ if (authError.code === "auth/user-not-found" || authError.code === "auth/invalid-credential") {
134
+ console.log("[Venus SDK] Test user not found, creating...");
135
+ const result2 = await createUserWithEmailAndPassword(auth, testEmail, testPassword);
136
+ console.log("[Venus SDK] Test user created, user:", result2.user.uid);
137
+ return result2;
138
+ }
139
+ throw error;
140
+ }
141
+ }
142
+ console.log("[Venus SDK] Signing in with Google popup...");
143
+ const result = await signInWithPopup(auth, googleProvider);
144
+ console.log("[Venus SDK] Sign-in successful, user:", result.user.uid);
145
+ return result;
146
+ },
147
+ async signOut() {
148
+ return auth.signOut();
149
+ },
150
+ async getIdToken() {
151
+ const user = auth.currentUser;
152
+ if (!user) {
153
+ console.log("[Venus SDK] getIdToken: No current user");
154
+ return null;
155
+ }
156
+ console.log("[Venus SDK] getIdToken: Refreshing token for user:", user.uid);
157
+ const token = await user.getIdToken(true);
158
+ console.log("[Venus SDK] getIdToken: Got token, length:", token?.length);
159
+ return token;
160
+ },
161
+ async getIdTokenOrThrow() {
162
+ const user = auth.currentUser;
163
+ console.log("[Venus SDK] getIdTokenOrThrow: currentUser =", user ? user.uid : null);
164
+ if (!user) {
165
+ throw new Error(
166
+ "[Venus SDK] Not signed in. Click the Venus Sandbox toolbar to sign in."
167
+ );
168
+ }
169
+ console.log("[Venus SDK] getIdTokenOrThrow: Forcing token refresh...");
170
+ try {
171
+ const token = await user.getIdToken(true);
172
+ console.log("[Venus SDK] getIdTokenOrThrow: Got fresh token, length:", token?.length);
173
+ return token;
174
+ } catch (error) {
175
+ console.error("[Venus SDK] getIdTokenOrThrow: Token refresh failed:", error);
176
+ console.log("[Venus SDK] getIdTokenOrThrow: Signing out stale session...");
177
+ await auth.signOut();
178
+ throw new Error(
179
+ "[Venus SDK] Session expired. Please sign in again via the Venus toolbar."
180
+ );
181
+ }
182
+ },
183
+ getCurrentUser() {
184
+ return auth.currentUser;
185
+ },
186
+ onAuthStateChanged(callback) {
187
+ return auth.onAuthStateChanged(callback);
188
+ },
189
+ // Firestore helpers
190
+ doc,
191
+ collection,
192
+ getDoc,
193
+ setDoc,
194
+ updateDoc,
195
+ deleteDoc,
196
+ addDoc,
197
+ query,
198
+ where,
199
+ orderBy,
200
+ limit,
201
+ onSnapshot,
202
+ serverTimestamp,
203
+ deleteField
204
+ };
205
+ console.log("[Venus SDK] Firebase client initialization complete!");
206
+ return client;
207
+ }
208
+ async function observeFirestoreDocument(path, onChange) {
209
+ const client = await getFirebaseClient();
210
+ const segments = path.split("/").filter((segment) => segment.length > 0);
211
+ if (segments.length === 0) {
212
+ throw new Error(`[Venus SDK] Invalid Firestore path "${path}".`);
213
+ }
214
+ const ref = client.doc(client.firestore, segments.join("/"));
215
+ const unsubscribe = client.onSnapshot(
216
+ ref,
217
+ (snapshot) => {
218
+ onChange(snapshot.exists() ? snapshot.data() : null);
219
+ },
220
+ (error) => {
221
+ console.error("[Venus SDK] Firestore subscription error:", error);
222
+ }
223
+ );
224
+ return unsubscribe;
225
+ }
226
+ async function observeFirestoreCollection(collectionPath, options, onChange) {
227
+ const client = await getFirebaseClient();
228
+ const collectionRef = client.collection(client.firestore, collectionPath);
229
+ const constraints = [];
230
+ if (options.filters) {
231
+ for (const filter of options.filters) {
232
+ constraints.push(client.where(filter.field, filter.op, filter.value));
233
+ }
234
+ }
235
+ if (options.orderBy) {
236
+ for (const order of options.orderBy) {
237
+ constraints.push(client.orderBy(order.field, order.direction));
238
+ }
239
+ }
240
+ if (options.limitCount) {
241
+ constraints.push(client.limit(options.limitCount));
242
+ }
243
+ const q = client.query(collectionRef, ...constraints);
244
+ const unsubscribe = client.onSnapshot(
245
+ q,
246
+ (snapshot) => {
247
+ const docs = snapshot.docs.map((doc) => ({
248
+ id: doc.id,
249
+ ...doc.data()
250
+ }));
251
+ onChange(docs);
252
+ },
253
+ (error) => {
254
+ console.error("[Venus SDK] Firestore collection subscription error:", error);
255
+ }
256
+ );
257
+ return unsubscribe;
258
+ }
259
+
260
+ // src/storage/FirestoreStorageApi.ts
261
+ var FirestoreStorageApi = class {
262
+ type;
263
+ appId;
264
+ client = null;
265
+ constructor(type, appId) {
266
+ this.type = type;
267
+ this.appId = appId;
268
+ if (type === "app" && !appId) {
269
+ throw new Error("[Venus SDK] App storage requires an appId");
270
+ }
271
+ }
272
+ async getClient() {
273
+ if (!this.client) {
274
+ this.client = await getFirebaseClient();
275
+ }
276
+ return this.client;
277
+ }
278
+ getProfileId() {
279
+ if (!this.client) {
280
+ throw new Error("[Venus SDK] Firebase not initialized. Call getClient() first.");
281
+ }
282
+ const profileId = this.client.getProfileId();
283
+ if (!profileId) {
284
+ throw new Error("[Venus SDK] Not signed in. Click the Venus toolbar to sign in with Google.");
285
+ }
286
+ return profileId;
287
+ }
288
+ async getDocRef() {
289
+ const client = await this.getClient();
290
+ const profileId = this.getProfileId();
291
+ if (this.type === "app") {
292
+ return client.doc(client.firestore, `profiles/${profileId}/h5AppStorage/${this.appId}`);
293
+ } else {
294
+ return client.doc(client.firestore, `profiles/${profileId}/h5GlobalStorage/data`);
295
+ }
296
+ }
297
+ async getItem(key) {
298
+ const client = await this.getClient();
299
+ const docRef = await this.getDocRef();
300
+ const snapshot = await client.getDoc(docRef);
301
+ if (!snapshot.exists()) {
302
+ return null;
303
+ }
304
+ const data = snapshot.data();
305
+ const value = data?.[key];
306
+ if (value === void 0 || value === null) {
307
+ return null;
308
+ }
309
+ return typeof value === "string" ? value : JSON.stringify(value);
310
+ }
311
+ async setItem(key, value) {
312
+ const client = await this.getClient();
313
+ const docRef = await this.getDocRef();
314
+ await client.setDoc(
315
+ docRef,
316
+ {
317
+ [key]: value,
318
+ lastUpdated: client.serverTimestamp()
319
+ },
320
+ { merge: true }
321
+ );
322
+ }
323
+ async removeItem(key) {
324
+ const client = await this.getClient();
325
+ const docRef = await this.getDocRef();
326
+ try {
327
+ await client.updateDoc(docRef, {
328
+ [key]: client.deleteField()
329
+ });
330
+ } catch (error) {
331
+ const err = error;
332
+ if (err.code !== "not-found") {
333
+ throw error;
334
+ }
335
+ }
336
+ }
337
+ async clear() {
338
+ const client = await this.getClient();
339
+ const docRef = await this.getDocRef();
340
+ try {
341
+ await client.deleteDoc(docRef);
342
+ } catch (error) {
343
+ const err = error;
344
+ if (err.code !== "not-found") {
345
+ throw error;
346
+ }
347
+ }
348
+ }
349
+ async key(index) {
350
+ const client = await this.getClient();
351
+ const docRef = await this.getDocRef();
352
+ const snapshot = await client.getDoc(docRef);
353
+ if (!snapshot.exists()) {
354
+ return null;
355
+ }
356
+ const data = snapshot.data() || {};
357
+ const keys = Object.keys(data).filter((k) => k !== "lastUpdated");
358
+ if (index < 0 || index >= keys.length) {
359
+ return null;
360
+ }
361
+ return keys[index];
362
+ }
363
+ async length() {
364
+ const client = await this.getClient();
365
+ const docRef = await this.getDocRef();
366
+ const snapshot = await client.getDoc(docRef);
367
+ if (!snapshot.exists()) {
368
+ return 0;
369
+ }
370
+ const data = snapshot.data() || {};
371
+ return Object.keys(data).filter((k) => k !== "lastUpdated").length;
372
+ }
373
+ async getAllItems() {
374
+ const client = await this.getClient();
375
+ const docRef = await this.getDocRef();
376
+ const snapshot = await client.getDoc(docRef);
377
+ if (!snapshot.exists()) {
378
+ return [];
379
+ }
380
+ const data = snapshot.data() || {};
381
+ return Object.keys(data).filter((k) => k !== "lastUpdated");
382
+ }
383
+ async getAllData() {
384
+ const client = await this.getClient();
385
+ const docRef = await this.getDocRef();
386
+ const snapshot = await client.getDoc(docRef);
387
+ if (!snapshot.exists()) {
388
+ return {};
389
+ }
390
+ const data = snapshot.data() || {};
391
+ const result = {};
392
+ for (const [key, value] of Object.entries(data)) {
393
+ if (key === "lastUpdated") continue;
394
+ result[key] = typeof value === "string" ? value : JSON.stringify(value);
395
+ }
396
+ return result;
397
+ }
398
+ async setMultipleItems(items) {
399
+ const client = await this.getClient();
400
+ const docRef = await this.getDocRef();
401
+ const updates = {
402
+ lastUpdated: client.serverTimestamp()
403
+ };
404
+ for (const item of items) {
405
+ updates[item.key] = item.value;
406
+ }
407
+ await client.setDoc(docRef, updates, { merge: true });
408
+ }
409
+ async removeMultipleItems(keys) {
410
+ const client = await this.getClient();
411
+ const docRef = await this.getDocRef();
412
+ const updates = {};
413
+ for (const key of keys) {
414
+ updates[key] = client.deleteField();
415
+ }
416
+ try {
417
+ await client.updateDoc(docRef, updates);
418
+ } catch (error) {
419
+ const err = error;
420
+ if (err.code !== "not-found") {
421
+ throw error;
422
+ }
423
+ }
424
+ }
425
+ };
426
+
427
+ // src/http/callRemoteFunction.ts
428
+ async function callRemoteFunction(functionName, payload) {
429
+ console.log("[Venus SDK] callRemoteFunction:", functionName);
430
+ const config = getSandboxConfig();
431
+ if (!config) {
432
+ throw new Error(
433
+ "[Venus SDK] Sandbox mode is not enabled. Make sure the Vite plugin is configured and you are running in dev mode."
434
+ );
435
+ }
436
+ console.log("[Venus SDK] callRemoteFunction: Getting Firebase client...");
437
+ const client = await getFirebaseClient();
438
+ console.log("[Venus SDK] callRemoteFunction: Getting ID token...");
439
+ const idToken = await client.getIdTokenOrThrow();
440
+ console.log("[Venus SDK] callRemoteFunction: Got token, making request...");
441
+ const baseUrl = buildFunctionsBaseUrl(config);
442
+ const url = `${baseUrl}/${functionName}`;
443
+ const response = await fetchWithRetry(url, {
444
+ method: "POST",
445
+ headers: {
446
+ "Content-Type": "application/json",
447
+ "Authorization": `Bearer ${idToken}`
448
+ },
449
+ body: JSON.stringify(payload ?? {})
450
+ }, config.rpcMaxRetries ?? 0, config.rpcRetryDelayMs ?? 1e3);
451
+ if (!response.ok) {
452
+ const errorMessage = await extractErrorMessage(response, functionName);
453
+ throw new Error(errorMessage);
454
+ }
455
+ const text = await response.text();
456
+ if (!text || text.trim().length === 0) {
457
+ return void 0;
458
+ }
459
+ try {
460
+ return JSON.parse(text);
461
+ } catch {
462
+ throw new Error(
463
+ `[Venus SDK] Backend call to ${functionName} returned invalid JSON: ${text.substring(0, 100)}`
464
+ );
465
+ }
466
+ }
467
+ async function fetchWithRetry(url, options, maxRetries, retryDelayMs) {
468
+ let lastError = null;
469
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
470
+ try {
471
+ const response = await fetch(url, options);
472
+ if (response.ok || response.status >= 400 && response.status < 500) {
473
+ return response;
474
+ }
475
+ if (attempt < maxRetries) {
476
+ await delay(retryDelayMs);
477
+ continue;
478
+ }
479
+ return response;
480
+ } catch (error) {
481
+ lastError = error instanceof Error ? error : new Error(String(error));
482
+ if (attempt < maxRetries) {
483
+ await delay(retryDelayMs);
484
+ continue;
485
+ }
486
+ throw lastError;
487
+ }
488
+ }
489
+ throw lastError ?? new Error("[Venus SDK] Request failed with no error details");
490
+ }
491
+ async function extractErrorMessage(response, functionName) {
492
+ try {
493
+ const errorBody = await response.json();
494
+ const message = errorBody.error || errorBody.message || response.statusText;
495
+ return `[Venus SDK] Backend call to ${functionName} failed (${response.status}): ${message}`;
496
+ } catch {
497
+ return `[Venus SDK] Backend call to ${functionName} failed (${response.status}): ${response.statusText}`;
498
+ }
499
+ }
500
+ function delay(ms) {
501
+ return new Promise((resolve) => setTimeout(resolve, ms));
502
+ }
503
+
504
+ // src/simulation/HttpSimulationApi.ts
505
+ var HttpSimulationApi = class {
506
+ appId;
507
+ _simulationConfig = null;
508
+ callRemote;
509
+ getProfileId;
510
+ activeSubscriptions = /* @__PURE__ */ new Set();
511
+ constructor(appId, options) {
512
+ this.appId = appId;
513
+ this.callRemote = options.callRemote ?? callRemoteFunction;
514
+ this.getProfileId = options.getProfileId;
515
+ }
516
+ isEnabled() {
517
+ return true;
518
+ }
519
+ // ===== SUBSCRIPTION (FIRESTORE) =====
520
+ async subscribeAsync(options) {
521
+ this.ensureValidSubscribeOptions(options);
522
+ const roomId = options.roomId;
523
+ const unsubscribers = [];
524
+ let lastInventorySignature = null;
525
+ let lastRunsSignature = null;
526
+ let latestInventory = {};
527
+ let latestRuns = [];
528
+ const sendUpdate = () => {
529
+ const snapshot = {
530
+ type: "snapshot",
531
+ entities: Object.entries(latestInventory).map(([entityId, quantity]) => ({
532
+ entityId,
533
+ quantity
534
+ })),
535
+ activeRuns: latestRuns,
536
+ timestamp: Date.now()
537
+ };
538
+ options.onUpdate(snapshot);
539
+ };
540
+ const statePath = roomId ? `h5_rooms/${roomId}/h5_simulation/${this.appId}` : `profiles/${this.getProfileId()}/h5_simulation/${this.appId}`;
541
+ const unsubscribeState = await observeFirestoreDocument(
542
+ statePath,
543
+ (doc) => {
544
+ const inventory = doc?.inventory ?? {};
545
+ const signature = JSON.stringify(inventory);
546
+ if (signature !== lastInventorySignature) {
547
+ lastInventorySignature = signature;
548
+ latestInventory = inventory;
549
+ sendUpdate();
550
+ }
551
+ }
552
+ );
553
+ unsubscribers.push(unsubscribeState);
554
+ if (options.activeRuns) {
555
+ const filters = roomId ? [
556
+ { field: "roomId", op: "==", value: roomId },
557
+ { field: "appId", op: "==", value: this.appId },
558
+ { field: "status", op: "in", value: ["running", "awaiting_collection"] }
559
+ ] : [
560
+ { field: "profileId", op: "==", value: this.getProfileId() },
561
+ { field: "appId", op: "==", value: this.appId },
562
+ { field: "status", op: "in", value: ["running", "awaiting_collection"] }
563
+ ];
564
+ const unsubscribeRuns = await observeFirestoreCollection(
565
+ "h5_simulation_runs",
566
+ {
567
+ filters,
568
+ orderBy: [{ field: "expiresAt", direction: "desc" }]
569
+ },
570
+ (docs) => {
571
+ const runs = docs.map((doc) => ({
572
+ id: doc.id,
573
+ recipeId: doc.recipeId,
574
+ status: doc.status,
575
+ startTime: normalizeTimestamp(doc.startTime),
576
+ expiresAt: normalizeTimestamp(doc.expiresAt),
577
+ entity: doc.entity,
578
+ inputs: doc.inputs,
579
+ outputs: doc.outputs
580
+ }));
581
+ const signature = JSON.stringify(runs);
582
+ if (signature !== lastRunsSignature) {
583
+ lastRunsSignature = signature;
584
+ latestRuns = runs;
585
+ sendUpdate();
586
+ }
587
+ }
588
+ );
589
+ unsubscribers.push(unsubscribeRuns);
590
+ }
591
+ const unsubscribe = () => {
592
+ for (const unsub of unsubscribers) {
593
+ unsub();
594
+ }
595
+ this.activeSubscriptions.delete(unsubscribe);
596
+ };
597
+ this.activeSubscriptions.add(unsubscribe);
598
+ return unsubscribe;
599
+ }
600
+ // ===== STATE & CONFIG (HTTP) =====
601
+ async getStateAsync(roomId) {
602
+ return this.callRemote("apiGetSimulationState", {
603
+ appId: this.appId,
604
+ roomId
605
+ });
606
+ }
607
+ async getConfigAsync(roomId) {
608
+ if (this._simulationConfig) {
609
+ return this._simulationConfig;
610
+ }
611
+ const config = await this.callRemote("apiGetSimulationConfig", {
612
+ appId: this.appId,
613
+ roomId
614
+ });
615
+ if (config) {
616
+ this._simulationConfig = config;
617
+ return config;
618
+ }
619
+ throw new Error("No simulation configuration available");
620
+ }
621
+ // ===== RECIPE EXECUTION (HTTP) =====
622
+ executeRecipeAsync(recipeId, inputs, options) {
623
+ return this.callRemote("apiExecuteSimulationRecipe", {
624
+ appId: this.appId,
625
+ recipeId,
626
+ inputs,
627
+ roomId: options?.roomId,
628
+ batchAmount: options?.batchAmount,
629
+ allowPartialBatch: options?.allowPartialBatch,
630
+ entity: options?.entity
631
+ });
632
+ }
633
+ collectRecipeAsync(runId) {
634
+ return this.callRemote("apiCollectRecipeRewards", {
635
+ appId: this.appId,
636
+ runId
637
+ });
638
+ }
639
+ getActiveRunsAsync(options) {
640
+ return this.callRemote("apiGetSimulationActiveRuns", {
641
+ appId: this.appId,
642
+ roomId: options?.roomId
643
+ });
644
+ }
645
+ executeScopedRecipeAsync(recipeId, entity, inputs, options) {
646
+ return this.callRemote("apiExecuteScopedRecipe", {
647
+ appId: this.appId,
648
+ recipeId,
649
+ entity,
650
+ inputs,
651
+ roomId: options?.roomId ?? null,
652
+ options
653
+ });
654
+ }
655
+ getAvailableRecipesAsync(options) {
656
+ return this.callRemote("apiGetAvailableRecipes", {
657
+ appId: this.appId,
658
+ roomId: options?.roomId || null,
659
+ includeActorRecipes: options?.includeActorRecipes || false
660
+ });
661
+ }
662
+ getRecipeRequirementsAsync(recipe) {
663
+ return this.callRemote("apiGetRecipeRequirements", {
664
+ appId: this.appId,
665
+ recipeId: recipe.recipeId,
666
+ entity: recipe.entity,
667
+ batchAmount: recipe.batchAmount
668
+ });
669
+ }
670
+ getBatchRecipeRequirementsAsync(recipes) {
671
+ return this.callRemote("apiGetBatchRecipeRequirements", {
672
+ appId: this.appId,
673
+ recipes
674
+ });
675
+ }
676
+ triggerRecipeChainAsync(recipeId, options) {
677
+ return this.callRemote("apiTriggerRecipeChain", {
678
+ appId: this.appId,
679
+ triggerRecipeId: recipeId,
680
+ context: options?.context,
681
+ roomId: options?.roomId
682
+ });
683
+ }
684
+ // ===== FIELD RESOLUTION & METADATA (HTTP) =====
685
+ async resolveFieldValueAsync(entityId, fieldPath, entity) {
686
+ const response = await this.callRemote("apiResolveFieldValue", {
687
+ appId: this.appId,
688
+ entityId,
689
+ fieldPath,
690
+ entity
691
+ });
692
+ return response.value;
693
+ }
694
+ getEntityMetadataAsync(entityId) {
695
+ return this.callRemote("apiGetEntityMetadata", {
696
+ appId: this.appId,
697
+ entityId
698
+ });
699
+ }
700
+ // ===== SLOT MANAGEMENT (HTTP) =====
701
+ async getSlotContainersAsync() {
702
+ const response = await this.callRemote(
703
+ "apiGetSlotContainers",
704
+ { appId: this.appId }
705
+ );
706
+ return response.containers || [];
707
+ }
708
+ async getSlotAssignmentsAsync(containerId) {
709
+ const response = await this.callRemote("apiGetSlotAssignments", {
710
+ appId: this.appId,
711
+ containerId
712
+ });
713
+ return Array.isArray(response) ? response : response.assignments || [];
714
+ }
715
+ assignItemToSlotAsync(containerId, slotId, itemId) {
716
+ return this.callRemote("apiAssignItemToSlot", {
717
+ appId: this.appId,
718
+ containerId,
719
+ slotId,
720
+ itemId
721
+ });
722
+ }
723
+ removeItemFromSlotAsync(containerId, slotId) {
724
+ return this.callRemote("apiRemoveItemFromSlot", {
725
+ appId: this.appId,
726
+ containerId,
727
+ slotId
728
+ });
729
+ }
730
+ async getAvailableItemsAsync(containerId, slotId) {
731
+ const response = await this.callRemote(
732
+ "apiGetAvailableItemsForSlot",
733
+ {
734
+ appId: this.appId,
735
+ containerId,
736
+ slotId
737
+ }
738
+ );
739
+ return response.availableItems || [];
740
+ }
741
+ calculatePowerPreviewAsync(containerId, slotId, candidateItemId) {
742
+ return this.callRemote("apiCalculatePowerPreview", {
743
+ appId: this.appId,
744
+ containerId,
745
+ slotId,
746
+ candidateItemId
747
+ });
748
+ }
749
+ validateSlotAssignmentAsync(containerId, slotId, itemId) {
750
+ return this.callRemote("apiValidateSlotAssignment", {
751
+ appId: this.appId,
752
+ containerId,
753
+ slotId,
754
+ itemId
755
+ });
756
+ }
757
+ executeBatchOperationsAsync(operations, validateOnly) {
758
+ return this.callRemote("apiBatchSlotOperations", {
759
+ appId: this.appId,
760
+ operations,
761
+ validateOnly
762
+ });
763
+ }
764
+ // ===== HELPERS =====
765
+ ensureValidSubscribeOptions(options) {
766
+ if (typeof options !== "object" || options === null) {
767
+ throw new Error("Simulation subscribe requires an options object");
768
+ }
769
+ const opts = options;
770
+ if (typeof opts.onUpdate !== "function") {
771
+ throw new Error("Simulation subscribe requires an onUpdate callback");
772
+ }
773
+ const hasFilter = Array.isArray(opts.entities) && opts.entities.length > 0 || Array.isArray(opts.tags) && opts.tags.length > 0 || Boolean(opts.activeRuns);
774
+ if (!hasFilter) {
775
+ throw new Error(
776
+ "Simulation subscribe requires at least one filter (entities, tags, activeRuns)"
777
+ );
778
+ }
779
+ }
780
+ destroy() {
781
+ for (const unsubscribe of this.activeSubscriptions) {
782
+ unsubscribe();
783
+ }
784
+ this.activeSubscriptions.clear();
785
+ }
786
+ };
787
+ function normalizeTimestamp(value) {
788
+ if (value === null || value === void 0) {
789
+ return 0;
790
+ }
791
+ if (typeof value === "object" && "toMillis" in value && typeof value.toMillis === "function") {
792
+ return value.toMillis();
793
+ }
794
+ if (typeof value === "object" && "_seconds" in value) {
795
+ const ts = value;
796
+ return ts._seconds * 1e3 + Math.floor((ts._nanoseconds ?? 0) / 1e6);
797
+ }
798
+ if (typeof value === "number") {
799
+ return value;
800
+ }
801
+ return 0;
802
+ }
803
+
804
+ // src/rooms/HttpRoomsApi.ts
805
+ var HttpRoomsApi = class {
806
+ appId;
807
+ callRemote;
808
+ getProfileId;
809
+ constructor(appId, options) {
810
+ this.appId = appId;
811
+ this.callRemote = options.callRemote ?? callRemoteFunction;
812
+ this.getProfileId = options.getProfileId;
813
+ }
814
+ async createRoomAsync(options) {
815
+ const response = await this.callRemote("apiCreateH5Room", {
816
+ appId: this.appId,
817
+ maxPlayers: options.maxPlayers ?? 4,
818
+ gameType: options.gameType,
819
+ isPrivate: options.isPrivate ?? false,
820
+ roomCode: options.roomCode,
821
+ name: options.name,
822
+ description: options.description,
823
+ customMetadata: options.customMetadata,
824
+ data: options.data
825
+ });
826
+ return this.wrapRoomResponse(response);
827
+ }
828
+ async joinOrCreateRoomAsync(options) {
829
+ const response = await this.callRemote("apiJoinOrCreateH5Room", {
830
+ appId: this.appId,
831
+ matchCriteria: options.matchCriteria,
832
+ createOptions: {
833
+ maxPlayers: options.createOptions.maxPlayers ?? 4,
834
+ gameType: options.createOptions.gameType,
835
+ isPrivate: options.createOptions.isPrivate ?? false,
836
+ name: options.createOptions.name,
837
+ description: options.createOptions.description,
838
+ customMetadata: options.createOptions.customMetadata,
839
+ data: options.createOptions.data
840
+ }
841
+ });
842
+ return {
843
+ action: response.action,
844
+ room: this.wrapRoomResponse(response),
845
+ playersJoined: response.playersJoined
846
+ };
847
+ }
848
+ async joinRoomByCodeAsync(roomCode) {
849
+ const response = await this.callRemote("apiJoinH5RoomByCode", {
850
+ appId: this.appId,
851
+ roomCode
852
+ });
853
+ return this.wrapRoomResponse(response);
854
+ }
855
+ async getUserRoomsAsync(options) {
856
+ const response = await this.callRemote("apiGetUserH5Rooms", {
857
+ appId: this.appId,
858
+ includeArchived: options?.includeArchived ?? false
859
+ });
860
+ const rooms = response.rooms || [];
861
+ return rooms.map((roomPayload) => new VenusRoom(roomPayload));
862
+ }
863
+ async leaveRoomAsync(room) {
864
+ await this.callRemote("apiLeaveH5Room", {
865
+ appId: this.appId,
866
+ roomId: room.id
867
+ });
868
+ }
869
+ async kickPlayerAsync(room, targetProfileId, options) {
870
+ await this.callRemote("apiKickH5RoomPlayer", {
871
+ appId: this.appId,
872
+ roomId: room.id,
873
+ targetProfileId,
874
+ reason: options?.reason
875
+ });
876
+ }
877
+ async startRoomGameAsync(room, options) {
878
+ await this.callRemote("apiStartH5Game", {
879
+ appId: this.appId,
880
+ roomId: room.id,
881
+ gameConfig: options?.gameConfig,
882
+ turnOrder: options?.turnOrder
883
+ });
884
+ }
885
+ /**
886
+ * Wrap a room response into a VenusRoom instance.
887
+ */
888
+ wrapRoomResponse(response) {
889
+ if (response.room) {
890
+ return new VenusRoom(response.room);
891
+ }
892
+ return new VenusRoom({
893
+ id: response.roomId || "",
894
+ roomCode: response.roomCode || "",
895
+ ...response
896
+ });
897
+ }
898
+ };
899
+
900
+ // src/rooms/FirestoreRoomsApi.ts
901
+ var FirestoreRoomsApi = class {
902
+ client = null;
903
+ activeSubscriptions = /* @__PURE__ */ new Set();
904
+ async getClient() {
905
+ if (!this.client) {
906
+ this.client = await getFirebaseClient();
907
+ }
908
+ return this.client;
909
+ }
910
+ getProfileId() {
911
+ if (!this.client) {
912
+ throw new Error("[Venus SDK] Firebase not initialized. Call getClient() first.");
913
+ }
914
+ const profileId = this.client.getProfileId();
915
+ if (!profileId) {
916
+ throw new Error("[Venus SDK] Not signed in. Click the Venus toolbar to sign in with Google.");
917
+ }
918
+ return profileId;
919
+ }
920
+ // ===== ROOM DATA OPERATIONS =====
921
+ async getRoomDataAsync(room) {
922
+ const client = await this.getClient();
923
+ const roomRef = client.doc(client.firestore, `h5_rooms/${room.id}`);
924
+ const snapshot = await client.getDoc(roomRef);
925
+ if (!snapshot.exists()) {
926
+ throw new Error(`Room ${room.id} not found`);
927
+ }
928
+ const roomData = snapshot.data();
929
+ return roomData?.data || {};
930
+ }
931
+ async updateRoomDataAsync(room, updates, options) {
932
+ const client = await this.getClient();
933
+ const roomRef = client.doc(client.firestore, `h5_rooms/${room.id}`);
934
+ const merge = options?.merge !== false;
935
+ const updateData = {
936
+ data: merge ? updates : updates,
937
+ updatedAt: client.serverTimestamp()
938
+ };
939
+ await client.updateDoc(roomRef, updateData);
940
+ }
941
+ // ===== MESSAGE OPERATIONS =====
942
+ async sendRoomMessageAsync(room, request) {
943
+ const client = await this.getClient();
944
+ const messagesRef = client.collection(client.firestore, `h5_rooms/${room.id}/messages`);
945
+ const messageDoc = await client.addDoc(messagesRef, {
946
+ roomId: room.id,
947
+ senderId: this.getProfileId(),
948
+ content: request.message.content || "",
949
+ type: request.message.type || "chat",
950
+ metadata: request.metadata || {},
951
+ timestamp: client.serverTimestamp()
952
+ });
953
+ return messageDoc.id;
954
+ }
955
+ // ===== PROPOSED MOVES =====
956
+ async proposeMoveAsync(room, request) {
957
+ const client = await this.getClient();
958
+ const movesRef = client.collection(client.firestore, `h5_rooms/${room.id}/proposed_moves`);
959
+ const moveDoc = await client.addDoc(movesRef, {
960
+ proposerProfileId: this.getProfileId(),
961
+ roomId: room.id,
962
+ gameSpecificState: request.gameSpecificState,
963
+ moveType: request.moveType,
964
+ clientContext: request.clientContext || {},
965
+ clientProposalId: request.clientProposalId,
966
+ timestamp: client.serverTimestamp(),
967
+ serverGenericValidationStatus: "pending"
968
+ });
969
+ return { proposedMoveId: moveDoc.id };
970
+ }
971
+ async validateMoveAsync(room, moveId, verdict) {
972
+ const client = await this.getClient();
973
+ const moveRef = client.doc(client.firestore, `h5_rooms/${room.id}/proposed_moves/${moveId}`);
974
+ await client.updateDoc(moveRef, {
975
+ serverGenericValidationStatus: verdict.isValid ? "valid" : "invalid",
976
+ serverGenericValidationReason: verdict.reason || null,
977
+ validatorId: verdict.validatorId || this.getProfileId(),
978
+ updatedAt: client.serverTimestamp()
979
+ });
980
+ return {
981
+ success: true,
982
+ moveId,
983
+ isValid: verdict.isValid,
984
+ reason: verdict.reason
985
+ };
986
+ }
987
+ // ===== SUBSCRIPTIONS =====
988
+ async subscribeAsync(room, options) {
989
+ const client = await this.getClient();
990
+ const unsubscribers = [];
991
+ if (options?.onData) {
992
+ const roomRef = client.doc(client.firestore, `h5_rooms/${room.id}`);
993
+ const unsubscribeData = client.onSnapshot(
994
+ roomRef,
995
+ (snapshot) => {
996
+ if (snapshot.exists()) {
997
+ const roomData = {
998
+ id: snapshot.id,
999
+ ...snapshot.data()
1000
+ };
1001
+ const update = {
1002
+ type: "H5_ROOM_DATA_UPDATED",
1003
+ roomId: room.id,
1004
+ roomData,
1005
+ timestamp: Date.now()
1006
+ };
1007
+ options.onData(update);
1008
+ }
1009
+ },
1010
+ (error) => {
1011
+ console.error("[Venus SDK] Room subscription error:", error);
1012
+ }
1013
+ );
1014
+ unsubscribers.push(unsubscribeData);
1015
+ }
1016
+ if (options?.onMessages) {
1017
+ const messagesRef = client.collection(client.firestore, `h5_rooms/${room.id}/messages`);
1018
+ const messagesQuery = client.query(
1019
+ messagesRef,
1020
+ client.orderBy("timestamp", "desc"),
1021
+ client.limit(50)
1022
+ );
1023
+ const unsubscribeMessages = client.onSnapshot(
1024
+ messagesQuery,
1025
+ (snapshot) => {
1026
+ snapshot.docChanges().forEach((change) => {
1027
+ const messageData = {
1028
+ id: change.doc.id,
1029
+ ...change.doc.data()
1030
+ };
1031
+ let eventType;
1032
+ if (change.type === "added") {
1033
+ eventType = "H5_ROOM_MESSAGE_RECEIVED";
1034
+ } else if (change.type === "modified") {
1035
+ eventType = "H5_ROOM_MESSAGE_UPDATED";
1036
+ } else {
1037
+ eventType = "H5_ROOM_MESSAGE_DELETED";
1038
+ }
1039
+ const event = {
1040
+ type: eventType,
1041
+ roomId: room.id,
1042
+ message: messageData,
1043
+ timestamp: Date.now()
1044
+ };
1045
+ options.onMessages(event);
1046
+ });
1047
+ },
1048
+ (error) => {
1049
+ console.error("[Venus SDK] Messages subscription error:", error);
1050
+ }
1051
+ );
1052
+ unsubscribers.push(unsubscribeMessages);
1053
+ }
1054
+ if (options?.onGameEvents) {
1055
+ const movesRef = client.collection(client.firestore, `h5_rooms/${room.id}/proposed_moves`);
1056
+ const movesQuery = client.query(
1057
+ movesRef,
1058
+ client.orderBy("timestamp", "desc"),
1059
+ client.limit(10)
1060
+ );
1061
+ const unsubscribeMoves = client.onSnapshot(
1062
+ movesQuery,
1063
+ (snapshot) => {
1064
+ snapshot.docChanges().forEach((change) => {
1065
+ const moveData = {
1066
+ id: change.doc.id,
1067
+ ...change.doc.data()
1068
+ };
1069
+ const event = {
1070
+ type: "app:h5:proposedMoveValidationUpdated",
1071
+ roomId: room.id,
1072
+ proposedMoveData: moveData,
1073
+ proposedMoveId: change.doc.id,
1074
+ changeType: change.type,
1075
+ timestamp: Date.now()
1076
+ };
1077
+ options.onGameEvents(event);
1078
+ });
1079
+ },
1080
+ (error) => {
1081
+ console.error("[Venus SDK] Proposed moves subscription error:", error);
1082
+ }
1083
+ );
1084
+ unsubscribers.push(unsubscribeMoves);
1085
+ }
1086
+ const unsubscribe = () => {
1087
+ for (const unsub of unsubscribers) {
1088
+ unsub();
1089
+ }
1090
+ this.activeSubscriptions.delete(unsubscribe);
1091
+ };
1092
+ this.activeSubscriptions.add(unsubscribe);
1093
+ return unsubscribe;
1094
+ }
1095
+ /**
1096
+ * Clean up all active subscriptions.
1097
+ */
1098
+ destroy() {
1099
+ for (const unsubscribe of this.activeSubscriptions) {
1100
+ unsubscribe();
1101
+ }
1102
+ this.activeSubscriptions.clear();
1103
+ }
1104
+ };
1105
+
1106
+ // src/rooms/CompositeRemoteRoomsApi.ts
1107
+ var CompositeRemoteRoomsApi = class {
1108
+ httpApi;
1109
+ firestoreApi;
1110
+ constructor(httpApi, firestoreApi) {
1111
+ this.httpApi = httpApi;
1112
+ this.firestoreApi = firestoreApi;
1113
+ }
1114
+ // ===== HTTP-backed lifecycle operations =====
1115
+ async createRoomAsync(options) {
1116
+ return this.httpApi.createRoomAsync(options);
1117
+ }
1118
+ async joinOrCreateRoomAsync(options) {
1119
+ return this.httpApi.joinOrCreateRoomAsync(options);
1120
+ }
1121
+ async joinRoomByCodeAsync(roomCode) {
1122
+ return this.httpApi.joinRoomByCodeAsync(roomCode);
1123
+ }
1124
+ async getUserRoomsAsync(options) {
1125
+ return this.httpApi.getUserRoomsAsync(options);
1126
+ }
1127
+ async leaveRoomAsync(room) {
1128
+ return this.httpApi.leaveRoomAsync(room);
1129
+ }
1130
+ async kickPlayerAsync(room, targetProfileId, options) {
1131
+ return this.httpApi.kickPlayerAsync(room, targetProfileId, options);
1132
+ }
1133
+ async startRoomGameAsync(room, options) {
1134
+ return this.httpApi.startRoomGameAsync(room, options);
1135
+ }
1136
+ // ===== Firestore-backed data operations =====
1137
+ async subscribeAsync(room, options) {
1138
+ return this.firestoreApi.subscribeAsync(room, options);
1139
+ }
1140
+ async updateRoomDataAsync(room, updates, options) {
1141
+ return this.firestoreApi.updateRoomDataAsync(room, updates, options);
1142
+ }
1143
+ async getRoomDataAsync(room) {
1144
+ return this.firestoreApi.getRoomDataAsync(room);
1145
+ }
1146
+ async sendRoomMessageAsync(room, message) {
1147
+ return this.firestoreApi.sendRoomMessageAsync(room, message);
1148
+ }
1149
+ async proposeMoveAsync(room, request) {
1150
+ return this.firestoreApi.proposeMoveAsync(room, request);
1151
+ }
1152
+ async validateMoveAsync(room, moveId, verdict) {
1153
+ return this.firestoreApi.validateMoveAsync(room, moveId, verdict);
1154
+ }
1155
+ /**
1156
+ * Clean up all active subscriptions.
1157
+ */
1158
+ destroy() {
1159
+ this.firestoreApi.destroy();
1160
+ }
1161
+ };
1162
+
1163
+ // src/leaderboard/HttpLeaderboardApi.ts
1164
+ var HttpLeaderboardApi = class {
1165
+ appId;
1166
+ callRemote;
1167
+ getProfileId;
1168
+ constructor(appId, options) {
1169
+ this.appId = appId;
1170
+ this.callRemote = options.callRemote ?? callRemoteFunction;
1171
+ this.getProfileId = options.getProfileId;
1172
+ }
1173
+ async createScoreToken(mode) {
1174
+ return this.callRemote("apiCreateScoreToken", {
1175
+ appId: this.appId,
1176
+ mode: mode || "default"
1177
+ });
1178
+ }
1179
+ async submitScore(params) {
1180
+ return this.callRemote("apiSubmitScore", {
1181
+ appId: this.appId,
1182
+ token: params.token,
1183
+ score: params.score,
1184
+ duration: params.duration,
1185
+ mode: params.mode || "default",
1186
+ telemetry: params.telemetry,
1187
+ metadata: params.metadata
1188
+ });
1189
+ }
1190
+ async getPagedScores(options) {
1191
+ return this.callRemote("apiGetPagedScores", {
1192
+ appId: this.appId,
1193
+ mode: options?.mode || "default",
1194
+ period: options?.period || "alltime",
1195
+ periodDate: options?.periodDate,
1196
+ cursor: options?.cursor,
1197
+ limit: options?.limit,
1198
+ variant: options?.variant || "standard",
1199
+ topCount: options?.topCount,
1200
+ contextAhead: options?.contextAhead,
1201
+ contextBehind: options?.contextBehind
1202
+ });
1203
+ }
1204
+ async getMyRank(options) {
1205
+ return this.callRemote("apiGetMyRank", {
1206
+ appId: this.appId,
1207
+ mode: options?.mode || "default",
1208
+ period: options?.period || "alltime",
1209
+ periodDate: options?.periodDate
1210
+ });
1211
+ }
1212
+ async getPodiumScores(options) {
1213
+ return this.callRemote("apiGetPodiumScores", {
1214
+ appId: this.appId,
1215
+ mode: options?.mode || "default",
1216
+ period: options?.period || "alltime",
1217
+ periodDate: options?.periodDate,
1218
+ topCount: options?.topCount,
1219
+ contextAhead: options?.contextAhead,
1220
+ contextBehind: options?.contextBehind
1221
+ });
1222
+ }
1223
+ };
1224
+
1225
+ // src/SandboxHost.ts
1226
+ var SandboxHost = class {
1227
+ ads;
1228
+ analytics;
1229
+ deviceCache;
1230
+ appStorage;
1231
+ globalStorage;
1232
+ avatar3d;
1233
+ navigation;
1234
+ notifications;
1235
+ popups;
1236
+ profile;
1237
+ system;
1238
+ cdn;
1239
+ time;
1240
+ ai;
1241
+ haptics;
1242
+ features;
1243
+ lifecycle;
1244
+ simulation;
1245
+ rooms;
1246
+ logging;
1247
+ iap;
1248
+ leaderboard;
1249
+ preloader;
1250
+ social;
1251
+ context;
1252
+ state = 0 /* PLAYING */;
1253
+ get isInitialized() {
1254
+ return this._isInitialized;
1255
+ }
1256
+ venusApi;
1257
+ _isInitialized = false;
1258
+ _overlay;
1259
+ _mockLifecyclesApi;
1260
+ _mockAdsApi;
1261
+ client = null;
1262
+ clientReady = null;
1263
+ authUnsubscribe = null;
1264
+ cleanupFunctions = [];
1265
+ constructor(venusApi) {
1266
+ this.venusApi = venusApi;
1267
+ this._overlay = this.createOverlay();
1268
+ this._mockAdsApi = new MockAdsApi(this._overlay);
1269
+ this._mockLifecyclesApi = new MockLifecycleApi();
1270
+ this.ads = this._mockAdsApi;
1271
+ this.analytics = new MockAnalyticsApi();
1272
+ const sandboxConfig = getSandboxConfig();
1273
+ const gameId = sandboxConfig.gameId;
1274
+ console.log("[Venus SDK] SandboxHost: Connecting to:", sandboxConfig.target);
1275
+ const getProfileId = () => {
1276
+ if (this.client && this.client.isSignedIn()) {
1277
+ return this.client.getProfileId() || "unknown-user";
1278
+ }
1279
+ return "unknown-user";
1280
+ };
1281
+ this.deviceCache = createMockStorageApi("deviceCache", "");
1282
+ this.appStorage = new FirestoreStorageApi("app", gameId);
1283
+ this.globalStorage = new FirestoreStorageApi("global");
1284
+ this.simulation = new HttpSimulationApi(gameId, { getProfileId });
1285
+ this.leaderboard = new HttpLeaderboardApi(gameId, { getProfileId });
1286
+ const httpRoomsApi = new HttpRoomsApi(gameId, { getProfileId });
1287
+ const firestoreRoomsApi = new FirestoreRoomsApi();
1288
+ this.rooms = new CompositeRemoteRoomsApi(httpRoomsApi, firestoreRoomsApi);
1289
+ this.avatar3d = new MockAvatarApi(venusApi);
1290
+ this.navigation = new MockNavigationApi(venusApi);
1291
+ this.notifications = new MockNotificationsApi(venusApi);
1292
+ this.popups = new MockPopupsApi(this._overlay);
1293
+ this.profile = new MockProfileApi(venusApi);
1294
+ const deviceApi = new MockDeviceApi(venusApi);
1295
+ const environmentApi = new MockEnvironmentApi(venusApi);
1296
+ this.system = new MockSystemApi(deviceApi, environmentApi, venusApi);
1297
+ this.cdn = new MockCdnApi(venusApi);
1298
+ this.time = new MockTimeApi(venusApi);
1299
+ this.ai = new MockAiApi();
1300
+ this.haptics = new MockHapticsApi(venusApi);
1301
+ this.features = new MockFeaturesApi();
1302
+ this.lifecycle = this._mockLifecyclesApi;
1303
+ this.logging = new MockLoggingApi();
1304
+ this.iap = new MockIapApi();
1305
+ this.social = new MockSocialApi();
1306
+ initializeRoomsApi(this.venusApi, this);
1307
+ this.preloader = new MockPreloaderApi();
1308
+ venusApi.isMock = () => true;
1309
+ this.venusApi.sharedAssets = new MockSharedAssetsApi(this.venusApi);
1310
+ this.setupSandboxSignIn();
1311
+ }
1312
+ /**
1313
+ * Set up global sign-in/sign-out functions and auth state broadcasting.
1314
+ */
1315
+ setupSandboxSignIn() {
1316
+ if (typeof window === "undefined") return;
1317
+ const broadcastAuthState = (user) => {
1318
+ window.dispatchEvent(
1319
+ new CustomEvent("venus-auth-state-changed", {
1320
+ detail: {
1321
+ signedIn: user !== null,
1322
+ user: user ? {
1323
+ uid: user.uid,
1324
+ email: user.email,
1325
+ displayName: user.displayName
1326
+ } : null
1327
+ }
1328
+ })
1329
+ );
1330
+ };
1331
+ console.log("[Venus SDK] SandboxHost: Initializing Firebase client...");
1332
+ this.clientReady = getFirebaseClient().then((client) => {
1333
+ this.client = client;
1334
+ console.log("[Venus SDK] SandboxHost: Firebase client initialized, currentUser:", client.getCurrentUser()?.uid ?? "none");
1335
+ this.authUnsubscribe = client.onAuthStateChanged(async (user) => {
1336
+ console.log("[Venus SDK] SandboxHost: Auth state changed, user:", user?.uid ?? "signed out");
1337
+ broadcastAuthState(user);
1338
+ if (user) {
1339
+ await this.ensureProfileExists(user);
1340
+ }
1341
+ });
1342
+ return client;
1343
+ });
1344
+ this.clientReady.catch((error) => {
1345
+ console.error("[Venus SDK] SandboxHost: Firebase client init failed:", error);
1346
+ });
1347
+ const checkAuthHandler = async () => {
1348
+ const client = await getFirebaseClient();
1349
+ broadcastAuthState(client.getCurrentUser());
1350
+ };
1351
+ window.addEventListener("venus-check-auth-state", checkAuthHandler);
1352
+ this.cleanupFunctions.push(() => window.removeEventListener("venus-check-auth-state", checkAuthHandler));
1353
+ window.__VENUS_SANDBOX_SIGN_IN__ = () => {
1354
+ if (this.client) {
1355
+ return this.client.signInWithGoogle().then(() => console.log("[Venus SDK] Google sign-in successful")).catch((error) => {
1356
+ console.error("[Venus SDK] Google sign-in failed:", error);
1357
+ throw error;
1358
+ });
1359
+ }
1360
+ if (!this.clientReady) {
1361
+ throw new Error("[Venus SDK] Firebase not configured.");
1362
+ }
1363
+ console.warn("[Venus SDK] Firebase client still initializing, please wait a moment...");
1364
+ throw new Error("[Venus SDK] Firebase client still initializing. Please wait a moment and try again.");
1365
+ };
1366
+ window.__VENUS_SANDBOX_SIGN_OUT__ = async () => {
1367
+ try {
1368
+ const client = await getFirebaseClient();
1369
+ await client.signOut();
1370
+ console.log("[Venus SDK] Sign-out successful");
1371
+ } catch (error) {
1372
+ console.error("[Venus SDK] Sign-out failed:", error);
1373
+ throw error;
1374
+ }
1375
+ };
1376
+ this.cleanupFunctions.push(() => {
1377
+ delete window.__VENUS_SANDBOX_SIGN_IN__;
1378
+ delete window.__VENUS_SANDBOX_SIGN_OUT__;
1379
+ });
1380
+ }
1381
+ /**
1382
+ * Ensure a profile exists for the signed-in user.
1383
+ * Checks if profile exists first to avoid overwriting existing profiles on staging/dev.
1384
+ * Only creates a new profile if one doesn't exist.
1385
+ */
1386
+ async ensureProfileExists(user) {
1387
+ try {
1388
+ console.log("[Venus SDK] Checking if profile exists for user:", user.uid);
1389
+ try {
1390
+ const existingProfile = await callRemoteFunction("getProfile", {});
1391
+ if (existingProfile?.id) {
1392
+ console.log("[Venus SDK] Profile already exists:", existingProfile.id);
1393
+ return;
1394
+ }
1395
+ } catch (checkError) {
1396
+ const err = checkError;
1397
+ if (!err?.message?.includes("not found") && err?.status !== 404) {
1398
+ console.warn("[Venus SDK] Error checking profile, will attempt creation:", err?.message);
1399
+ }
1400
+ }
1401
+ console.log("[Venus SDK] No profile found, creating one for user:", user.uid);
1402
+ const shortId = user.uid.slice(-8);
1403
+ const timestamp = Date.now();
1404
+ await callRemoteFunction("createUserProfile", {
1405
+ name: user.displayName || `Dev User ${shortId}`,
1406
+ username: `dev_${shortId}_${timestamp}`,
1407
+ description: "Auto-created for sandbox development",
1408
+ isAnonymous: true,
1409
+ // Uses retry logic for username conflicts
1410
+ onboardingCompleted: true
1411
+ });
1412
+ console.log("[Venus SDK] Profile created for user:", user.uid);
1413
+ } catch (error) {
1414
+ const message = error instanceof Error ? error.message : String(error);
1415
+ console.warn("[Venus SDK] Could not ensure profile exists:", message);
1416
+ }
1417
+ }
1418
+ initialize(_options) {
1419
+ this._isInitialized = true;
1420
+ this.venusApi._profileData = this.profile.getCurrentProfile();
1421
+ this.venusApi._deviceData = this.system.getDevice();
1422
+ this.venusApi._environmentData = this.system.getEnvironment();
1423
+ this.venusApi._localeData = this.venusApi._mock?.locale || "en-US";
1424
+ this.venusApi._languageCodeData = this.venusApi._mock?.languageCode || "en";
1425
+ this.context = {
1426
+ initializeAsleep: false,
1427
+ safeArea: this.venusApi._safeAreaData
1428
+ };
1429
+ return Promise.resolve(this.context);
1430
+ }
1431
+ // ============================================================
1432
+ // Overlay and UI methods (shared with MockHost)
1433
+ // ============================================================
1434
+ createOverlay() {
1435
+ const overlayContainer = document.createElement("div");
1436
+ overlayContainer.id = "venus-sandbox-overlay";
1437
+ overlayContainer.style.cssText = `
1438
+ position: fixed;
1439
+ top: 0;
1440
+ left: 0;
1441
+ width: 100%;
1442
+ height: 100%;
1443
+ pointer-events: none;
1444
+ z-index: 10000;
1445
+ `;
1446
+ document.body.appendChild(overlayContainer);
1447
+ const menuButton = this.createOverlayButton(
1448
+ "close",
1449
+ "Menu",
1450
+ { x: window.innerWidth - 48, y: 16, width: 32, height: 32 },
1451
+ () => {
1452
+ this.handleMenuButtonClicked();
1453
+ },
1454
+ "rgba(59, 130, 246, 0.7)",
1455
+ "#FFFFFF"
1456
+ );
1457
+ overlayContainer.appendChild(menuButton);
1458
+ const adOverlay = this.setupAdOverlay();
1459
+ const actionSheet = this.setupActionSheetOverlay();
1460
+ const resizeHandler = () => {
1461
+ this.updateOverlayLayout();
1462
+ };
1463
+ window.addEventListener("resize", resizeHandler);
1464
+ this.cleanupFunctions.push(() => window.removeEventListener("resize", resizeHandler));
1465
+ return {
1466
+ container: overlayContainer,
1467
+ elements: {
1468
+ menuButton
1469
+ },
1470
+ appVisibilityState: "visible",
1471
+ actionSheetOverlay: actionSheet,
1472
+ adOverlay,
1473
+ showAdOverlay: () => {
1474
+ return this.showAdOverlay("rewarded");
1475
+ },
1476
+ showActionSheet: (items, options) => {
1477
+ return this.showActionSheetOverlay(items, options);
1478
+ }
1479
+ };
1480
+ }
1481
+ async handleMenuButtonClicked() {
1482
+ if (this.state === 0 /* PLAYING */) {
1483
+ this.triggerLifecycleEvent("PAUSE" /* PAUSE */);
1484
+ this.state = 1 /* PAUSED */;
1485
+ }
1486
+ const actionSheetItems = [];
1487
+ const awakeAction = { label: "\u23F0 Awake", id: "awakeAction" };
1488
+ const sleepAction = { label: "\u{1F4A4} Sleep", id: "sleepAction" };
1489
+ const quitAction = { label: "\u{1F6D1} Quit", id: "quitAction" };
1490
+ const playAction = { label: "\u25B6\uFE0F Play", id: "playAction" };
1491
+ if (this.state === 1 /* PAUSED */) {
1492
+ actionSheetItems.push(sleepAction);
1493
+ } else if (this.state === 2 /* SLEEPING */) {
1494
+ actionSheetItems.push(awakeAction);
1495
+ }
1496
+ if (this.state !== 3 /* TERMINATED */) {
1497
+ actionSheetItems.push(quitAction);
1498
+ } else if (this.state === 3 /* TERMINATED */) {
1499
+ actionSheetItems.push(playAction);
1500
+ }
1501
+ const action = await this.showActionSheetOverlay(actionSheetItems);
1502
+ if (action === awakeAction.id) {
1503
+ this.tryAwake();
1504
+ } else if (action === sleepAction.id) {
1505
+ this.trySleep();
1506
+ } else if (action === playAction.id) {
1507
+ this.tryPlay();
1508
+ } else if (action === quitAction.id) {
1509
+ this.tryQuit();
1510
+ } else {
1511
+ this.tryResume();
1512
+ }
1513
+ }
1514
+ tryAwake() {
1515
+ if (this.state === 2 /* SLEEPING */) {
1516
+ this.triggerLifecycleEvent("AWAKE" /* AWAKE */);
1517
+ this.state = 1 /* PAUSED */;
1518
+ }
1519
+ }
1520
+ trySleep() {
1521
+ if (this.state === 1 /* PAUSED */) {
1522
+ this.triggerLifecycleEvent("SLEEP" /* SLEEP */);
1523
+ this.state = 2 /* SLEEPING */;
1524
+ }
1525
+ }
1526
+ tryPlay() {
1527
+ if (this.state === 3 /* TERMINATED */) {
1528
+ this.triggerLifecycleEvent("AWAKE" /* AWAKE */);
1529
+ this.state = 1 /* PAUSED */;
1530
+ this.triggerLifecycleEvent("RESUME" /* RESUME */);
1531
+ this.state = 0 /* PLAYING */;
1532
+ }
1533
+ }
1534
+ tryQuit() {
1535
+ if (this.state === 0 /* PLAYING */) {
1536
+ this.triggerLifecycleEvent("PAUSE" /* PAUSE */);
1537
+ this.state = 1 /* PAUSED */;
1538
+ }
1539
+ if (this.state === 1 /* PAUSED */) {
1540
+ this.triggerLifecycleEvent("SLEEP" /* SLEEP */);
1541
+ this.state = 2 /* SLEEPING */;
1542
+ }
1543
+ if (this.state === 2 /* SLEEPING */) {
1544
+ this.triggerLifecycleEvent("QUIT" /* QUIT */);
1545
+ this.state = 3 /* TERMINATED */;
1546
+ }
1547
+ }
1548
+ tryResume() {
1549
+ if (this.state === 1 /* PAUSED */) {
1550
+ this.triggerLifecycleEvent("RESUME" /* RESUME */);
1551
+ this.state = 0 /* PLAYING */;
1552
+ }
1553
+ }
1554
+ async showAdOverlay(type) {
1555
+ return new Promise((resolve) => {
1556
+ const overlay = this._overlay;
1557
+ const adOverlay = overlay.adOverlay;
1558
+ overlay.adOverlay.innerHTML = "";
1559
+ const heading = document.createElement("h1");
1560
+ heading.style.cssText = `
1561
+ font-size: 24px;
1562
+ margin-bottom: 20px;
1563
+ text-align: center;
1564
+ `;
1565
+ heading.innerText = type === "interstitial" ? "INTERSTITIAL AD" : "REWARDED VIDEO AD";
1566
+ overlay.adOverlay.appendChild(heading);
1567
+ const content = document.createElement("div");
1568
+ content.style.cssText = `
1569
+ width: 300px;
1570
+ height: 250px;
1571
+ background-color: #1e40af;
1572
+ display: flex;
1573
+ align-items: center;
1574
+ justify-content: center;
1575
+ margin-bottom: 20px;
1576
+ border-radius: 8px;
1577
+ `;
1578
+ content.innerText = "Mock Ad Content";
1579
+ adOverlay.appendChild(content);
1580
+ const timer = document.createElement("p");
1581
+ timer.style.cssText = `
1582
+ margin-bottom: 20px;
1583
+ font-size: 16px;
1584
+ `;
1585
+ timer.innerText = "Normally ads would have a timer...";
1586
+ adOverlay.appendChild(timer);
1587
+ const okButton = document.createElement("button");
1588
+ okButton.style.cssText = `
1589
+ padding: 10px 40px;
1590
+ background-color: #2563eb;
1591
+ color: white;
1592
+ border: none;
1593
+ border-radius: 4px;
1594
+ font-size: 16px;
1595
+ cursor: pointer;
1596
+ `;
1597
+ okButton.innerText = "Close Ad";
1598
+ adOverlay.appendChild(okButton);
1599
+ adOverlay.style.display = "flex";
1600
+ okButton.onclick = () => {
1601
+ this.hideAdOverlay();
1602
+ resolve(true);
1603
+ };
1604
+ });
1605
+ }
1606
+ hideAdOverlay() {
1607
+ const overlay = this._overlay;
1608
+ if (overlay.adOverlay) {
1609
+ overlay.adOverlay.style.display = "none";
1610
+ }
1611
+ }
1612
+ setupActionSheetOverlay() {
1613
+ const actionSheetOverlay = document.createElement("div");
1614
+ actionSheetOverlay.id = "venus-action-sheet-overlay";
1615
+ actionSheetOverlay.style.cssText = `
1616
+ position: fixed;
1617
+ top: 0;
1618
+ left: 0;
1619
+ width: 100%;
1620
+ height: 100%;
1621
+ background-color: rgba(0, 0, 0, 0.5);
1622
+ display: flex;
1623
+ align-items: center;
1624
+ justify-content: center;
1625
+ z-index: 10600;
1626
+ font-family: sans-serif;
1627
+ display: none;
1628
+ `;
1629
+ document.body.appendChild(actionSheetOverlay);
1630
+ return actionSheetOverlay;
1631
+ }
1632
+ createOverlayButton(id, text, position, onClick, background, color) {
1633
+ const button = document.createElement("button");
1634
+ button.id = `venus-sandbox-${id}-button`;
1635
+ button.innerText = text;
1636
+ button.style.cssText = `
1637
+ position: absolute;
1638
+ left: ${position.x}px;
1639
+ top: ${position.y}px;
1640
+ width: ${position.width}px;
1641
+ min-width: ${position.width}px;
1642
+ height: ${position.height}px;
1643
+ background: ${background};
1644
+ color: ${color};
1645
+ border: none;
1646
+ border-radius: 8px;
1647
+ font-family: sans-serif;
1648
+ font-weight: bold;
1649
+ font-size: ${Math.min(position.width, position.height) / 3}px;
1650
+ cursor: pointer;
1651
+ pointer-events: auto;
1652
+ display: flex;
1653
+ align-items: center;
1654
+ justify-content: center;
1655
+ opacity: 0.9;
1656
+ transition: opacity 0.2s;
1657
+ `;
1658
+ button.addEventListener("click", onClick);
1659
+ button.addEventListener("mouseover", () => {
1660
+ button.style.opacity = "1";
1661
+ });
1662
+ button.addEventListener("mouseout", () => {
1663
+ button.style.opacity = "0.9";
1664
+ });
1665
+ return button;
1666
+ }
1667
+ updateOverlayLayout() {
1668
+ const overlay = this._overlay;
1669
+ const menuBtn = overlay.elements.menuButton;
1670
+ menuBtn.style.left = `${window.innerWidth - 48}px`;
1671
+ menuBtn.style.top = "16px";
1672
+ menuBtn.style.width = "32px";
1673
+ menuBtn.style.minWidth = "32px";
1674
+ menuBtn.style.height = "32px";
1675
+ }
1676
+ triggerLifecycleEvent(name) {
1677
+ console.log("Trigger Lifecycle Event: ", name);
1678
+ if (name === "PAUSE" /* PAUSE */) {
1679
+ this._mockLifecyclesApi.triggerPauseCallbacks();
1680
+ } else if (name === "RESUME" /* RESUME */) {
1681
+ this._mockLifecyclesApi.triggerResumeCallbacks();
1682
+ } else if (name === "QUIT" /* QUIT */) {
1683
+ this._mockLifecyclesApi.triggerQuitCallbacks();
1684
+ } else if (name === "AWAKE" /* AWAKE */) {
1685
+ this._mockLifecyclesApi.triggerAwakeCallbacks();
1686
+ } else if (name === "SLEEP" /* SLEEP */) {
1687
+ this._mockLifecyclesApi.triggerSleepCallbacks();
1688
+ }
1689
+ }
1690
+ setupAdOverlay() {
1691
+ const adOverlay = document.createElement("div");
1692
+ adOverlay.id = "venus-ad-overlay";
1693
+ adOverlay.style.cssText = `
1694
+ position: fixed;
1695
+ top: 0;
1696
+ left: 0;
1697
+ width: 100%;
1698
+ height: 100%;
1699
+ background-color: rgba(37, 99, 235, 0.9);
1700
+ color: white;
1701
+ display: flex;
1702
+ flex-direction: column;
1703
+ align-items: center;
1704
+ justify-content: center;
1705
+ z-index: 10500;
1706
+ font-family: sans-serif;
1707
+ display: none;
1708
+ `;
1709
+ document.body.appendChild(adOverlay);
1710
+ return adOverlay;
1711
+ }
1712
+ showActionSheetOverlay(items, options) {
1713
+ return new Promise((resolve) => {
1714
+ const overlay = this._overlay;
1715
+ overlay.actionSheetOverlay.innerHTML = "";
1716
+ overlay.actionSheetOverlay.style.display = "flex";
1717
+ const actionSheet = document.createElement("div");
1718
+ actionSheet.className = "venus-action-sheet";
1719
+ actionSheet.style.cssText = `
1720
+ background-color: white;
1721
+ border-radius: 8px;
1722
+ width: 80%;
1723
+ max-width: 400px;
1724
+ max-height: 80%;
1725
+ display: flex;
1726
+ flex-direction: column;
1727
+ overflow: hidden;
1728
+ color: black;
1729
+ `;
1730
+ if (options?.title) {
1731
+ const titleContainer = document.createElement("div");
1732
+ titleContainer.style.cssText = `
1733
+ padding: 16px;
1734
+ border-bottom: 1px solid #eaeaea;
1735
+ font-weight: bold;
1736
+ font-size: 18px;
1737
+ text-align: center;
1738
+ color: black;
1739
+ `;
1740
+ titleContainer.innerText = options.title;
1741
+ actionSheet.appendChild(titleContainer);
1742
+ }
1743
+ if (options?.message) {
1744
+ const messageContainer = document.createElement("div");
1745
+ messageContainer.style.cssText = `
1746
+ padding: 8px 16px;
1747
+ color: #666;
1748
+ font-size: 14px;
1749
+ text-align: center;
1750
+ `;
1751
+ messageContainer.innerText = options.message;
1752
+ actionSheet.appendChild(messageContainer);
1753
+ }
1754
+ const optionsContainer = document.createElement("div");
1755
+ optionsContainer.style.cssText = `
1756
+ overflow-y: auto;
1757
+ max-height: 300px;
1758
+ `;
1759
+ items.forEach((item, index) => {
1760
+ const optionItem = document.createElement("div");
1761
+ optionItem.style.cssText = `
1762
+ padding: 12px 16px;
1763
+ border-bottom: 1px solid #eaeaea;
1764
+ cursor: pointer;
1765
+ display: flex;
1766
+ align-items: center;
1767
+ transition: background-color 0.2s;
1768
+ color: black;
1769
+ `;
1770
+ optionItem.addEventListener("mouseover", () => {
1771
+ optionItem.style.backgroundColor = "#f5f5f5";
1772
+ });
1773
+ optionItem.addEventListener("mouseout", () => {
1774
+ optionItem.style.backgroundColor = "white";
1775
+ });
1776
+ if (item.icon) {
1777
+ const iconSpan = document.createElement("span");
1778
+ iconSpan.style.cssText = `
1779
+ margin-right: 12px;
1780
+ font-size: 16px;
1781
+ `;
1782
+ iconSpan.innerText = item.icon;
1783
+ optionItem.appendChild(iconSpan);
1784
+ }
1785
+ const labelSpan = document.createElement("span");
1786
+ labelSpan.style.cssText = `color: black;`;
1787
+ labelSpan.innerText = item.label;
1788
+ optionItem.appendChild(labelSpan);
1789
+ optionItem.addEventListener("click", () => {
1790
+ this.hideActionSheetOverlay();
1791
+ const optionId = item.id !== void 0 ? item.id : index;
1792
+ resolve(optionId);
1793
+ });
1794
+ optionsContainer.appendChild(optionItem);
1795
+ });
1796
+ actionSheet.appendChild(optionsContainer);
1797
+ if (!options?.disableCancel) {
1798
+ const cancelButton = document.createElement("div");
1799
+ cancelButton.style.cssText = `
1800
+ padding: 14px 16px;
1801
+ text-align: center;
1802
+ font-weight: bold;
1803
+ cursor: pointer;
1804
+ color: #3b82f6;
1805
+ border-top: 1px solid #eaeaea;
1806
+ `;
1807
+ cancelButton.innerText = options?.cancelButtonText || "Cancel";
1808
+ cancelButton.addEventListener("click", () => {
1809
+ this.hideActionSheetOverlay();
1810
+ resolve(null);
1811
+ });
1812
+ actionSheet.appendChild(cancelButton);
1813
+ }
1814
+ if (!options?.disableCancel) {
1815
+ const closeButton = document.createElement("div");
1816
+ closeButton.style.cssText = `
1817
+ position: absolute;
1818
+ top: 8px;
1819
+ right: 8px;
1820
+ width: 24px;
1821
+ height: 24px;
1822
+ border-radius: 12px;
1823
+ background-color: rgba(0,0,0,0.1);
1824
+ color: #666;
1825
+ display: flex;
1826
+ align-items: center;
1827
+ justify-content: center;
1828
+ cursor: pointer;
1829
+ font-size: 14px;
1830
+ `;
1831
+ closeButton.innerText = "\u2715";
1832
+ closeButton.addEventListener("click", () => {
1833
+ this.hideActionSheetOverlay();
1834
+ resolve(null);
1835
+ });
1836
+ actionSheet.appendChild(closeButton);
1837
+ overlay.actionSheetOverlay.appendChild(actionSheet);
1838
+ }
1839
+ });
1840
+ }
1841
+ hideActionSheetOverlay() {
1842
+ const overlay = this._overlay;
1843
+ if (overlay.actionSheetOverlay) {
1844
+ overlay.actionSheetOverlay.style.display = "none";
1845
+ }
1846
+ }
1847
+ /**
1848
+ * Clean up all resources held by SandboxHost.
1849
+ * Call this when the host is no longer needed to prevent memory leaks.
1850
+ */
1851
+ destroy() {
1852
+ if (this.authUnsubscribe) {
1853
+ this.authUnsubscribe();
1854
+ this.authUnsubscribe = null;
1855
+ }
1856
+ for (const cleanup of this.cleanupFunctions) {
1857
+ try {
1858
+ cleanup();
1859
+ } catch (e) {
1860
+ console.warn("[Venus SDK] Cleanup error:", e);
1861
+ }
1862
+ }
1863
+ this.cleanupFunctions.length = 0;
1864
+ if (this.simulation && "destroy" in this.simulation) {
1865
+ this.simulation.destroy();
1866
+ }
1867
+ if (this.rooms && "destroy" in this.rooms) {
1868
+ this.rooms.destroy();
1869
+ }
1870
+ if (typeof document !== "undefined") {
1871
+ const overlay = this._overlay;
1872
+ if (overlay.container?.parentNode) {
1873
+ overlay.container.parentNode.removeChild(overlay.container);
1874
+ }
1875
+ if (overlay.adOverlay?.parentNode) {
1876
+ overlay.adOverlay.parentNode.removeChild(overlay.adOverlay);
1877
+ }
1878
+ if (overlay.actionSheetOverlay?.parentNode) {
1879
+ overlay.actionSheetOverlay.parentNode.removeChild(overlay.actionSheetOverlay);
1880
+ }
1881
+ }
1882
+ this.client = null;
1883
+ this.clientReady = null;
1884
+ }
1885
+ };
1886
+
1887
+ export { SandboxHost };
1888
+ //# sourceMappingURL=SandboxHost-JNX5UBGL.js.map
1889
+ //# sourceMappingURL=SandboxHost-JNX5UBGL.js.map