@series-inc/rundot-game-sdk 5.0.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,1551 @@
1
+ import path, { join, resolve, dirname } from 'path';
2
+ import fs, { existsSync, readFileSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { createRequire } from 'module';
5
+
6
+ // src/vite/RundotGameLibrariesPlugin.ts
7
+
8
+ // src/shared-assets/embeddedLibrariesManifest.ts
9
+ var DEFAULT_SHARED_LIB_CDN_BASE = "https://venus-static-01293ak.web.app/libs";
10
+ var EMBEDDED_LIBRARIES = [
11
+ {
12
+ libraryKey: "phaser@3.90.0",
13
+ assetKey: "library:phaser@3.90.0",
14
+ packageName: "phaser",
15
+ version: "3.90.0",
16
+ globalVar: "Phaser",
17
+ cdnPath: "phaser/3.90.0/phaser.min.js",
18
+ moduleSpecifiers: [{ match: "exact", value: "phaser" }],
19
+ loadStage: 0,
20
+ enabled: true
21
+ },
22
+ {
23
+ libraryKey: "react@18.3.1",
24
+ assetKey: "library:react@18.3.1",
25
+ packageName: "react",
26
+ version: "18.3.1",
27
+ globalVar: "React",
28
+ cdnPath: "react/18.3.1/react.production.min.js",
29
+ moduleSpecifiers: [
30
+ { match: "exact", value: "react", behavior: "namespace" },
31
+ { match: "exact", value: "react/jsx-runtime", behavior: "react-jsx-runtime" },
32
+ {
33
+ match: "exact",
34
+ value: "react/jsx-dev-runtime",
35
+ behavior: "react-jsx-dev-runtime"
36
+ }
37
+ ],
38
+ loadStage: 0,
39
+ // Must load before ReactDOM
40
+ enabled: true
41
+ },
42
+ {
43
+ libraryKey: "react-dom@18.3.1",
44
+ assetKey: "library:react-dom@18.3.1",
45
+ packageName: "react-dom",
46
+ version: "18.3.1",
47
+ globalVar: "ReactDOM",
48
+ cdnPath: "react-dom/18.3.1/react-dom.production.min.js",
49
+ moduleSpecifiers: [
50
+ { match: "exact", value: "react-dom", behavior: "namespace" },
51
+ { match: "exact", value: "react-dom/client", behavior: "namespace" }
52
+ ],
53
+ loadStage: 1,
54
+ // Depends on React (stage 0)
55
+ enabled: true
56
+ },
57
+ {
58
+ libraryKey: "three@0.170.0",
59
+ assetKey: "library:three@0.170.0",
60
+ packageName: "three",
61
+ version: "0.170.0",
62
+ globalVar: "THREE",
63
+ cdnPath: "three/r170/three.min.js",
64
+ moduleSpecifiers: [
65
+ { match: "exact", value: "three", behavior: "namespace" }
66
+ ],
67
+ loadStage: 0,
68
+ enabled: true
69
+ },
70
+ {
71
+ libraryKey: "matter-js@0.19.0",
72
+ assetKey: "library:matter-js@0.19.0",
73
+ packageName: "matter-js",
74
+ version: "0.19.0",
75
+ globalVar: "Matter",
76
+ cdnPath: "matter-js/0.19.0/matter.min.js",
77
+ moduleSpecifiers: [{ match: "exact", value: "matter-js" }],
78
+ loadStage: 0,
79
+ enabled: true
80
+ },
81
+ {
82
+ libraryKey: "inkjs@2.2.0",
83
+ assetKey: "library:inkjs@2.2.0",
84
+ packageName: "inkjs",
85
+ version: "2.2.0",
86
+ globalVar: "inkjs",
87
+ cdnPath: "inkjs/2.2.0/ink.min.js",
88
+ moduleSpecifiers: [{ match: "exact", value: "inkjs" }],
89
+ loadStage: 0,
90
+ enabled: true
91
+ },
92
+ {
93
+ libraryKey: "zustand@5.0.3",
94
+ assetKey: "library:zustand@5.0.3",
95
+ packageName: "zustand",
96
+ version: "5.0.3",
97
+ globalVar: "zustand",
98
+ cdnPath: "zustand/5.0.3/zustand.min.js",
99
+ moduleSpecifiers: [
100
+ { match: "exact", value: "zustand" },
101
+ { match: "exact", value: "zustand/middleware" }
102
+ ],
103
+ loadStage: 1,
104
+ // Depends on React (stage 0)
105
+ enabled: true
106
+ },
107
+ {
108
+ libraryKey: "ammo.js@2024.11",
109
+ assetKey: "library:ammo.js@2024.11",
110
+ packageName: "ammo.js",
111
+ version: "2024.11",
112
+ globalVar: "Ammo",
113
+ cdnPath: "ammo/2024.11/ammo.js",
114
+ moduleSpecifiers: [
115
+ { match: "exact", value: "ammo.js" },
116
+ { match: "exact", value: "ammo.js/builds/ammo.wasm.js" }
117
+ ],
118
+ loadStage: 0,
119
+ enabled: false
120
+ // Not ready yet - WASM loading needs additional work
121
+ }
122
+ ];
123
+ var EMBEDDED_LIBRARY_BY_KEY = EMBEDDED_LIBRARIES.reduce(
124
+ (acc, lib) => {
125
+ acc[lib.libraryKey] = lib;
126
+ return acc;
127
+ },
128
+ {}
129
+ );
130
+ var MODULE_TO_LIBRARY_SPECIFIERS = EMBEDDED_LIBRARIES.filter(
131
+ (lib) => lib.enabled
132
+ ).flatMap(
133
+ (lib) => lib.moduleSpecifiers.map((specifier) => ({
134
+ ...specifier,
135
+ libraryKey: lib.libraryKey
136
+ }))
137
+ );
138
+
139
+ // src/shared-assets/generated/threeExports.ts
140
+ var EMBEDDED_THREE_VERSION = "0.170.0";
141
+ var EMBEDDED_THREE_NAMED_EXPORTS = [
142
+ "ACESFilmicToneMapping",
143
+ "AddEquation",
144
+ "AdditiveAnimationBlendMode",
145
+ "AdditiveBlending",
146
+ "AddOperation",
147
+ "AgXToneMapping",
148
+ "AlphaFormat",
149
+ "AlwaysCompare",
150
+ "AlwaysDepth",
151
+ "AlwaysStencilFunc",
152
+ "AmbientLight",
153
+ "AnimationAction",
154
+ "AnimationClip",
155
+ "AnimationLoader",
156
+ "AnimationMixer",
157
+ "AnimationObjectGroup",
158
+ "AnimationUtils",
159
+ "ArcCurve",
160
+ "ArrayCamera",
161
+ "ArrowHelper",
162
+ "AttachedBindMode",
163
+ "Audio",
164
+ "AudioAnalyser",
165
+ "AudioContext",
166
+ "AudioListener",
167
+ "AudioLoader",
168
+ "AxesHelper",
169
+ "BackSide",
170
+ "BasicDepthPacking",
171
+ "BasicShadowMap",
172
+ "BatchedMesh",
173
+ "Bone",
174
+ "BooleanKeyframeTrack",
175
+ "Box2",
176
+ "Box3",
177
+ "Box3Helper",
178
+ "BoxGeometry",
179
+ "BoxHelper",
180
+ "BufferAttribute",
181
+ "BufferGeometry",
182
+ "BufferGeometryLoader",
183
+ "ByteType",
184
+ "Cache",
185
+ "Camera",
186
+ "CameraHelper",
187
+ "CanvasTexture",
188
+ "CapsuleGeometry",
189
+ "CatmullRomCurve3",
190
+ "CineonToneMapping",
191
+ "CircleGeometry",
192
+ "ClampToEdgeWrapping",
193
+ "Clock",
194
+ "Color",
195
+ "ColorKeyframeTrack",
196
+ "ColorManagement",
197
+ "CompressedArrayTexture",
198
+ "CompressedCubeTexture",
199
+ "CompressedTexture",
200
+ "CompressedTextureLoader",
201
+ "ConeGeometry",
202
+ "ConstantAlphaFactor",
203
+ "ConstantColorFactor",
204
+ "Controls",
205
+ "createCanvasElement",
206
+ "CubeCamera",
207
+ "CubeReflectionMapping",
208
+ "CubeRefractionMapping",
209
+ "CubeTexture",
210
+ "CubeTextureLoader",
211
+ "CubeUVReflectionMapping",
212
+ "CubicBezierCurve",
213
+ "CubicBezierCurve3",
214
+ "CubicInterpolant",
215
+ "CullFaceBack",
216
+ "CullFaceFront",
217
+ "CullFaceFrontBack",
218
+ "CullFaceNone",
219
+ "Curve",
220
+ "CurvePath",
221
+ "CustomBlending",
222
+ "CustomToneMapping",
223
+ "CylinderGeometry",
224
+ "Cylindrical",
225
+ "Data3DTexture",
226
+ "DataArrayTexture",
227
+ "DataTexture",
228
+ "DataTextureLoader",
229
+ "DataUtils",
230
+ "DecrementStencilOp",
231
+ "DecrementWrapStencilOp",
232
+ "DefaultLoadingManager",
233
+ "DepthFormat",
234
+ "DepthStencilFormat",
235
+ "DepthTexture",
236
+ "DetachedBindMode",
237
+ "DirectionalLight",
238
+ "DirectionalLightHelper",
239
+ "DiscreteInterpolant",
240
+ "DodecahedronGeometry",
241
+ "DoubleSide",
242
+ "DstAlphaFactor",
243
+ "DstColorFactor",
244
+ "DynamicCopyUsage",
245
+ "DynamicDrawUsage",
246
+ "DynamicReadUsage",
247
+ "EdgesGeometry",
248
+ "EllipseCurve",
249
+ "EqualCompare",
250
+ "EqualDepth",
251
+ "EqualStencilFunc",
252
+ "EquirectangularReflectionMapping",
253
+ "EquirectangularRefractionMapping",
254
+ "Euler",
255
+ "EventDispatcher",
256
+ "ExtrudeGeometry",
257
+ "FileLoader",
258
+ "Float16BufferAttribute",
259
+ "Float32BufferAttribute",
260
+ "FloatType",
261
+ "Fog",
262
+ "FogExp2",
263
+ "FramebufferTexture",
264
+ "FrontSide",
265
+ "Frustum",
266
+ "GLBufferAttribute",
267
+ "GLSL1",
268
+ "GLSL3",
269
+ "GreaterCompare",
270
+ "GreaterDepth",
271
+ "GreaterEqualCompare",
272
+ "GreaterEqualDepth",
273
+ "GreaterEqualStencilFunc",
274
+ "GreaterStencilFunc",
275
+ "GridHelper",
276
+ "Group",
277
+ "HalfFloatType",
278
+ "HemisphereLight",
279
+ "HemisphereLightHelper",
280
+ "IcosahedronGeometry",
281
+ "ImageBitmapLoader",
282
+ "ImageLoader",
283
+ "ImageUtils",
284
+ "IncrementStencilOp",
285
+ "IncrementWrapStencilOp",
286
+ "InstancedBufferAttribute",
287
+ "InstancedBufferGeometry",
288
+ "InstancedInterleavedBuffer",
289
+ "InstancedMesh",
290
+ "Int16BufferAttribute",
291
+ "Int32BufferAttribute",
292
+ "Int8BufferAttribute",
293
+ "InterleavedBuffer",
294
+ "InterleavedBufferAttribute",
295
+ "Interpolant",
296
+ "InterpolateDiscrete",
297
+ "InterpolateLinear",
298
+ "InterpolateSmooth",
299
+ "IntType",
300
+ "InvertStencilOp",
301
+ "KeepStencilOp",
302
+ "KeyframeTrack",
303
+ "LatheGeometry",
304
+ "Layers",
305
+ "LessCompare",
306
+ "LessDepth",
307
+ "LessEqualCompare",
308
+ "LessEqualDepth",
309
+ "LessEqualStencilFunc",
310
+ "LessStencilFunc",
311
+ "Light",
312
+ "LightProbe",
313
+ "Line",
314
+ "Line3",
315
+ "LinearFilter",
316
+ "LinearInterpolant",
317
+ "LinearMipmapLinearFilter",
318
+ "LinearMipMapLinearFilter",
319
+ "LinearMipmapNearestFilter",
320
+ "LinearMipMapNearestFilter",
321
+ "LinearSRGBColorSpace",
322
+ "LinearToneMapping",
323
+ "LinearTransfer",
324
+ "LineBasicMaterial",
325
+ "LineCurve",
326
+ "LineCurve3",
327
+ "LineDashedMaterial",
328
+ "LineLoop",
329
+ "LineSegments",
330
+ "Loader",
331
+ "LoaderUtils",
332
+ "LoadingManager",
333
+ "LOD",
334
+ "LoopOnce",
335
+ "LoopPingPong",
336
+ "LoopRepeat",
337
+ "LuminanceAlphaFormat",
338
+ "LuminanceFormat",
339
+ "Material",
340
+ "MaterialLoader",
341
+ "MathUtils",
342
+ "Matrix2",
343
+ "Matrix3",
344
+ "Matrix4",
345
+ "MaxEquation",
346
+ "Mesh",
347
+ "MeshBasicMaterial",
348
+ "MeshDepthMaterial",
349
+ "MeshDistanceMaterial",
350
+ "MeshLambertMaterial",
351
+ "MeshMatcapMaterial",
352
+ "MeshNormalMaterial",
353
+ "MeshPhongMaterial",
354
+ "MeshPhysicalMaterial",
355
+ "MeshStandardMaterial",
356
+ "MeshToonMaterial",
357
+ "MinEquation",
358
+ "MirroredRepeatWrapping",
359
+ "MixOperation",
360
+ "MOUSE",
361
+ "MultiplyBlending",
362
+ "MultiplyOperation",
363
+ "NearestFilter",
364
+ "NearestMipmapLinearFilter",
365
+ "NearestMipMapLinearFilter",
366
+ "NearestMipmapNearestFilter",
367
+ "NearestMipMapNearestFilter",
368
+ "NeutralToneMapping",
369
+ "NeverCompare",
370
+ "NeverDepth",
371
+ "NeverStencilFunc",
372
+ "NoBlending",
373
+ "NoColorSpace",
374
+ "NormalAnimationBlendMode",
375
+ "NormalBlending",
376
+ "NotEqualCompare",
377
+ "NotEqualDepth",
378
+ "NotEqualStencilFunc",
379
+ "NoToneMapping",
380
+ "NumberKeyframeTrack",
381
+ "Object3D",
382
+ "ObjectLoader",
383
+ "ObjectSpaceNormalMap",
384
+ "OctahedronGeometry",
385
+ "OneFactor",
386
+ "OneMinusConstantAlphaFactor",
387
+ "OneMinusConstantColorFactor",
388
+ "OneMinusDstAlphaFactor",
389
+ "OneMinusDstColorFactor",
390
+ "OneMinusSrcAlphaFactor",
391
+ "OneMinusSrcColorFactor",
392
+ "OrthographicCamera",
393
+ "Path",
394
+ "PCFShadowMap",
395
+ "PCFSoftShadowMap",
396
+ "PerspectiveCamera",
397
+ "Plane",
398
+ "PlaneGeometry",
399
+ "PlaneHelper",
400
+ "PMREMGenerator",
401
+ "PointLight",
402
+ "PointLightHelper",
403
+ "Points",
404
+ "PointsMaterial",
405
+ "PolarGridHelper",
406
+ "PolyhedronGeometry",
407
+ "PositionalAudio",
408
+ "PropertyBinding",
409
+ "PropertyMixer",
410
+ "QuadraticBezierCurve",
411
+ "QuadraticBezierCurve3",
412
+ "Quaternion",
413
+ "QuaternionKeyframeTrack",
414
+ "QuaternionLinearInterpolant",
415
+ "RawShaderMaterial",
416
+ "Ray",
417
+ "Raycaster",
418
+ "RectAreaLight",
419
+ "RED_GREEN_RGTC2_Format",
420
+ "RED_RGTC1_Format",
421
+ "RedFormat",
422
+ "RedIntegerFormat",
423
+ "ReinhardToneMapping",
424
+ "RenderTarget",
425
+ "RepeatWrapping",
426
+ "ReplaceStencilOp",
427
+ "ReverseSubtractEquation",
428
+ "REVISION",
429
+ "RGB_BPTC_SIGNED_Format",
430
+ "RGB_BPTC_UNSIGNED_Format",
431
+ "RGB_ETC1_Format",
432
+ "RGB_ETC2_Format",
433
+ "RGB_PVRTC_2BPPV1_Format",
434
+ "RGB_PVRTC_4BPPV1_Format",
435
+ "RGB_S3TC_DXT1_Format",
436
+ "RGBA_ASTC_10x10_Format",
437
+ "RGBA_ASTC_10x5_Format",
438
+ "RGBA_ASTC_10x6_Format",
439
+ "RGBA_ASTC_10x8_Format",
440
+ "RGBA_ASTC_12x10_Format",
441
+ "RGBA_ASTC_12x12_Format",
442
+ "RGBA_ASTC_4x4_Format",
443
+ "RGBA_ASTC_5x4_Format",
444
+ "RGBA_ASTC_5x5_Format",
445
+ "RGBA_ASTC_6x5_Format",
446
+ "RGBA_ASTC_6x6_Format",
447
+ "RGBA_ASTC_8x5_Format",
448
+ "RGBA_ASTC_8x6_Format",
449
+ "RGBA_ASTC_8x8_Format",
450
+ "RGBA_BPTC_Format",
451
+ "RGBA_ETC2_EAC_Format",
452
+ "RGBA_PVRTC_2BPPV1_Format",
453
+ "RGBA_PVRTC_4BPPV1_Format",
454
+ "RGBA_S3TC_DXT1_Format",
455
+ "RGBA_S3TC_DXT3_Format",
456
+ "RGBA_S3TC_DXT5_Format",
457
+ "RGBADepthPacking",
458
+ "RGBAFormat",
459
+ "RGBAIntegerFormat",
460
+ "RGBDepthPacking",
461
+ "RGBFormat",
462
+ "RGBIntegerFormat",
463
+ "RGDepthPacking",
464
+ "RGFormat",
465
+ "RGIntegerFormat",
466
+ "RingGeometry",
467
+ "Scene",
468
+ "ShaderChunk",
469
+ "ShaderLib",
470
+ "ShaderMaterial",
471
+ "ShadowMaterial",
472
+ "Shape",
473
+ "ShapeGeometry",
474
+ "ShapePath",
475
+ "ShapeUtils",
476
+ "ShortType",
477
+ "SIGNED_RED_GREEN_RGTC2_Format",
478
+ "SIGNED_RED_RGTC1_Format",
479
+ "Skeleton",
480
+ "SkeletonHelper",
481
+ "SkinnedMesh",
482
+ "Source",
483
+ "Sphere",
484
+ "SphereGeometry",
485
+ "Spherical",
486
+ "SphericalHarmonics3",
487
+ "SplineCurve",
488
+ "SpotLight",
489
+ "SpotLightHelper",
490
+ "Sprite",
491
+ "SpriteMaterial",
492
+ "SrcAlphaFactor",
493
+ "SrcAlphaSaturateFactor",
494
+ "SrcColorFactor",
495
+ "SRGBColorSpace",
496
+ "SRGBTransfer",
497
+ "StaticCopyUsage",
498
+ "StaticDrawUsage",
499
+ "StaticReadUsage",
500
+ "StereoCamera",
501
+ "StreamCopyUsage",
502
+ "StreamDrawUsage",
503
+ "StreamReadUsage",
504
+ "StringKeyframeTrack",
505
+ "SubtractEquation",
506
+ "SubtractiveBlending",
507
+ "TangentSpaceNormalMap",
508
+ "TetrahedronGeometry",
509
+ "Texture",
510
+ "TextureLoader",
511
+ "TextureUtils",
512
+ "TorusGeometry",
513
+ "TorusKnotGeometry",
514
+ "TOUCH",
515
+ "Triangle",
516
+ "TriangleFanDrawMode",
517
+ "TrianglesDrawMode",
518
+ "TriangleStripDrawMode",
519
+ "TubeGeometry",
520
+ "Uint16BufferAttribute",
521
+ "Uint32BufferAttribute",
522
+ "Uint8BufferAttribute",
523
+ "Uint8ClampedBufferAttribute",
524
+ "Uniform",
525
+ "UniformsGroup",
526
+ "UniformsLib",
527
+ "UniformsUtils",
528
+ "UnsignedByteType",
529
+ "UnsignedInt248Type",
530
+ "UnsignedInt5999Type",
531
+ "UnsignedIntType",
532
+ "UnsignedShort4444Type",
533
+ "UnsignedShort5551Type",
534
+ "UnsignedShortType",
535
+ "UVMapping",
536
+ "Vector2",
537
+ "Vector3",
538
+ "Vector4",
539
+ "VectorKeyframeTrack",
540
+ "VideoTexture",
541
+ "VSMShadowMap",
542
+ "WebGL3DRenderTarget",
543
+ "WebGLArrayRenderTarget",
544
+ "WebGLCoordinateSystem",
545
+ "WebGLCubeRenderTarget",
546
+ "WebGLMultipleRenderTargets",
547
+ "WebGLRenderer",
548
+ "WebGLRenderTarget",
549
+ "WebGLUtils",
550
+ "WebGPUCoordinateSystem",
551
+ "WireframeGeometry",
552
+ "WrapAroundEnding",
553
+ "ZeroCurvatureEnding",
554
+ "ZeroFactor",
555
+ "ZeroSlopeEnding",
556
+ "ZeroStencilOp"
557
+ ];
558
+
559
+ // src/shared-assets/generated/reactExports.ts
560
+ var EMBEDDED_REACT_VERSION = "18.3.1";
561
+ var EMBEDDED_REACT_NAMED_EXPORTS = [
562
+ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
563
+ "act",
564
+ "Children",
565
+ "cloneElement",
566
+ "Component",
567
+ "createContext",
568
+ "createElement",
569
+ "createFactory",
570
+ "createRef",
571
+ "forwardRef",
572
+ "Fragment",
573
+ "isValidElement",
574
+ "lazy",
575
+ "memo",
576
+ "Profiler",
577
+ "PureComponent",
578
+ "startTransition",
579
+ "StrictMode",
580
+ "Suspense",
581
+ "unstable_act",
582
+ "useCallback",
583
+ "useContext",
584
+ "useDebugValue",
585
+ "useDeferredValue",
586
+ "useEffect",
587
+ "useId",
588
+ "useImperativeHandle",
589
+ "useInsertionEffect",
590
+ "useLayoutEffect",
591
+ "useMemo",
592
+ "useReducer",
593
+ "useRef",
594
+ "useState",
595
+ "useSyncExternalStore",
596
+ "useTransition",
597
+ "version"
598
+ ];
599
+
600
+ // src/shared-assets/generated/reactDomExports.ts
601
+ var EMBEDDED_REACT_DOM_VERSION = "18.3.1";
602
+ var EMBEDDED_REACT_DOM_NAMED_EXPORTS = [
603
+ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
604
+ "createPortal",
605
+ "createRoot",
606
+ "findDOMNode",
607
+ "flushSync",
608
+ "hydrate",
609
+ "hydrateRoot",
610
+ "render",
611
+ "unmountComponentAtNode",
612
+ "unstable_batchedUpdates",
613
+ "unstable_renderSubtreeIntoContainer",
614
+ "version"
615
+ ];
616
+
617
+ // src/vite/RundotGameLibrariesPlugin.ts
618
+ async function resolveInstalledPackageVersion(pluginContext, packageName, cache) {
619
+ if (cache.has(packageName)) {
620
+ return cache.get(packageName) ?? null;
621
+ }
622
+ const readVersion = (packageJsonPath) => {
623
+ const raw = fs.readFileSync(packageJsonPath, "utf8");
624
+ const parsed = JSON.parse(raw);
625
+ return typeof parsed?.version === "string" ? parsed.version : null;
626
+ };
627
+ const findNearestPackageJson = (fromFilePath) => {
628
+ let dir = path.dirname(fromFilePath);
629
+ while (true) {
630
+ const candidate = path.join(dir, "package.json");
631
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
632
+ return candidate;
633
+ }
634
+ const parent = path.dirname(dir);
635
+ if (parent === dir) return null;
636
+ dir = parent;
637
+ }
638
+ };
639
+ try {
640
+ const pkgJsonResolved = await pluginContext.resolve(
641
+ `${packageName}/package.json`,
642
+ void 0,
643
+ { skipSelf: true }
644
+ );
645
+ if (pkgJsonResolved?.id && fs.existsSync(pkgJsonResolved.id)) {
646
+ const v = readVersion(pkgJsonResolved.id);
647
+ cache.set(packageName, v);
648
+ return v;
649
+ }
650
+ const entryResolved = await pluginContext.resolve(packageName, void 0, {
651
+ skipSelf: true
652
+ });
653
+ if (entryResolved?.id) {
654
+ const pkgJsonPath = findNearestPackageJson(entryResolved.id);
655
+ const v = pkgJsonPath ? readVersion(pkgJsonPath) : null;
656
+ cache.set(packageName, v);
657
+ return v;
658
+ }
659
+ } catch {
660
+ }
661
+ cache.set(packageName, null);
662
+ return null;
663
+ }
664
+ function generateVirtualModule(lib, spec, importPath) {
665
+ const errorCheck = `
666
+ // Wait for shim to complete loading libraries
667
+ if (window.__rundotGameLibraryShim && window.__rundotGameLibraryShim.ready) {
668
+ try {
669
+ await window.__rundotGameLibraryShim.ready();
670
+ } catch (error) {
671
+ console.error('[RUN.game Libraries] Shim bootstrap failed:', error);
672
+ throw error;
673
+ }
674
+ } else {
675
+ console.warn('[RUN.game Libraries] No shim found - running without embedded libraries');
676
+ }
677
+
678
+ // Verify library was loaded
679
+ if (!window.__rundotGameLibraryExports || !window.__rundotGameLibraryExports['${lib.libraryKey}']) {
680
+ console.error('[RUN.game Libraries] ${lib.packageName} library not loaded');
681
+ console.error('[RUN.game Libraries] Config:', window.__rundotGameLibrariesConfig);
682
+ console.error('[RUN.game Libraries] Exports:', window.__rundotGameLibraryExports);
683
+ throw new Error(
684
+ '${lib.packageName} not loaded (${importPath}). Check console for errors.'
685
+ );
686
+ }
687
+ `;
688
+ const libVar = `window.__rundotGameLibraryExports['${lib.libraryKey}']`;
689
+ const renderForwardedNamedExports = (names, chunkSize = 50) => {
690
+ if (names.length === 0) return "";
691
+ const chunks = [];
692
+ for (let i = 0; i < names.length; i += chunkSize) {
693
+ const slice = names.slice(i, i + chunkSize).join(", ");
694
+ chunks.push(`export const { ${slice} } = lib;`);
695
+ }
696
+ return `
697
+ ${chunks.join("\n")}
698
+ `;
699
+ };
700
+ switch (spec.behavior) {
701
+ case "react-jsx-runtime":
702
+ return `
703
+ const React = await (async function() {
704
+ ${errorCheck}
705
+ return ${libVar};
706
+ })();
707
+
708
+ export const jsx = React.jsx || function(type, props, key) {
709
+ return React.createElement(type, { ...props, key });
710
+ };
711
+ export const jsxs = React.jsxs || jsx;
712
+ export const Fragment = React.Fragment;
713
+ `;
714
+ case "react-jsx-dev-runtime":
715
+ return `
716
+ const React = await (async function() {
717
+ ${errorCheck}
718
+ return ${libVar};
719
+ })();
720
+
721
+ export const jsxDEV = React.jsxDEV || React.createElement;
722
+ export const Fragment = React.Fragment;
723
+ `;
724
+ case "namespace":
725
+ let namedExports = "";
726
+ if (lib.packageName === "react") {
727
+ if (lib.version !== EMBEDDED_REACT_VERSION) {
728
+ namedExports = `
729
+ throw new Error('[RUN.game Libraries] Embedded react version mismatch: manifest has ${lib.version} but generated export list is for ${EMBEDDED_REACT_VERSION}. Run: npm run generate:embedded-react-exports (packages/api)');`;
730
+ } else {
731
+ namedExports = renderForwardedNamedExports(EMBEDDED_REACT_NAMED_EXPORTS);
732
+ }
733
+ } else if (lib.packageName === "react-dom") {
734
+ if (lib.version !== EMBEDDED_REACT_DOM_VERSION) {
735
+ namedExports = `
736
+ throw new Error('[RUN.game Libraries] Embedded react-dom version mismatch: manifest has ${lib.version} but generated export list is for ${EMBEDDED_REACT_DOM_VERSION}. Run: npm run generate:embedded-react-exports (packages/api)');`;
737
+ } else {
738
+ namedExports = renderForwardedNamedExports(EMBEDDED_REACT_DOM_NAMED_EXPORTS);
739
+ }
740
+ } else if (lib.packageName === "three") {
741
+ if (lib.version !== EMBEDDED_THREE_VERSION) {
742
+ namedExports = `
743
+ throw new Error('[RUN.game Libraries] Embedded three version mismatch: manifest has ${lib.version} but generated export list is for ${EMBEDDED_THREE_VERSION}. Run: npm run generate:embedded-three-exports (packages/api)');`;
744
+ } else {
745
+ namedExports = renderForwardedNamedExports(EMBEDDED_THREE_NAMED_EXPORTS);
746
+ }
747
+ } else {
748
+ namedExports = "";
749
+ }
750
+ return `
751
+ const lib = await (async function() {
752
+ ${errorCheck}
753
+ return ${libVar};
754
+ })();
755
+
756
+ export default lib;
757
+ ${namedExports}
758
+ `;
759
+ case "default":
760
+ case void 0:
761
+ return `
762
+ const lib = await (async function() {
763
+ ${errorCheck}
764
+ return ${libVar};
765
+ })();
766
+
767
+ export default lib;
768
+ `;
769
+ default:
770
+ throw new Error(
771
+ `Unknown behavior: ${spec.behavior} for ${lib.packageName}. Valid behaviors: default, namespace, react-jsx-runtime, react-jsx-dev-runtime`
772
+ );
773
+ }
774
+ }
775
+ function rundotGameLibrariesPlugin(options = {}) {
776
+ const cdnDir = path.resolve(options.cdnDir || "./cdn");
777
+ const debug = options.debug || false;
778
+ const requiredLibraries = /* @__PURE__ */ new Set();
779
+ let isServe = false;
780
+ let enabled = false;
781
+ let appName = options.appName;
782
+ const installedVersionCache = /* @__PURE__ */ new Map();
783
+ const loggedMismatch = /* @__PURE__ */ new Set();
784
+ return {
785
+ name: "vite-plugin-rundot-game-libraries",
786
+ enforce: "pre",
787
+ // Run before other plugins to intercept imports
788
+ async buildStart() {
789
+ if (!enabled) return;
790
+ const uniquePackages = Array.from(
791
+ new Set(
792
+ EMBEDDED_LIBRARIES.filter((l) => l.enabled).map((l) => l.packageName)
793
+ )
794
+ );
795
+ for (const pkgName of uniquePackages) {
796
+ await resolveInstalledPackageVersion(this, pkgName, installedVersionCache);
797
+ }
798
+ },
799
+ // 0. Configure Vite build options
800
+ config(config) {
801
+ if (!enabled) return;
802
+ if (!config.build) config.build = {};
803
+ if (!config.build.rollupOptions) config.build.rollupOptions = {};
804
+ if (config.build.rollupOptions.output) {
805
+ const output = config.build.rollupOptions.output;
806
+ const outputs = Array.isArray(output) ? output : [output];
807
+ outputs.forEach((o) => {
808
+ if (o.manualChunks && typeof o.manualChunks === "object") {
809
+ const chunks = o.manualChunks;
810
+ EMBEDDED_LIBRARIES.forEach((lib) => {
811
+ if (lib.enabled && chunks[lib.packageName]) {
812
+ delete chunks[lib.packageName];
813
+ }
814
+ });
815
+ }
816
+ });
817
+ }
818
+ },
819
+ // Log plugin status
820
+ configResolved(config) {
821
+ isServe = config.command === "serve";
822
+ enabled = !isServe && process.env.RUNDOT_GAME_DISABLE_EMBEDDED_LIBS !== "true";
823
+ if (!appName && config.root) {
824
+ const rootParts = config.root.split(path.sep);
825
+ const h5Index = rootParts.indexOf("H5");
826
+ if (h5Index >= 0 && h5Index < rootParts.length - 1) {
827
+ appName = rootParts[h5Index + 1];
828
+ if (debug) {
829
+ console.log(
830
+ `[RUN.game Libraries] Auto-detected app name from folder structure: ${appName}`
831
+ );
832
+ }
833
+ } else if (debug) {
834
+ console.log(
835
+ "[RUN.game Libraries] Not in H5 folder structure - CDN asset serving disabled (embedded libraries still work)"
836
+ );
837
+ }
838
+ }
839
+ if (isServe) {
840
+ return;
841
+ }
842
+ if (!enabled) {
843
+ console.log(
844
+ "\n[RUN.game Libraries] Building with bundled libraries (standalone mode)"
845
+ );
846
+ }
847
+ },
848
+ // 1. Intercept library imports
849
+ async resolveId(id, importer) {
850
+ if (!enabled) return null;
851
+ const match = MODULE_TO_LIBRARY_SPECIFIERS.find((spec) => {
852
+ if (spec.match === "exact") {
853
+ return spec.value === id;
854
+ } else if (spec.match === "prefix") {
855
+ return id.startsWith(spec.value);
856
+ }
857
+ return false;
858
+ });
859
+ if (match) {
860
+ const lib = EMBEDDED_LIBRARY_BY_KEY[match.libraryKey];
861
+ if (!lib) {
862
+ throw new Error(
863
+ `[RUN.game Libraries] Internal error: no embedded library for key ${match.libraryKey}`
864
+ );
865
+ }
866
+ const installed = await resolveInstalledPackageVersion(
867
+ this,
868
+ lib.packageName,
869
+ installedVersionCache
870
+ );
871
+ if (installed !== lib.version) {
872
+ if (debug && !loggedMismatch.has(lib.packageName)) {
873
+ loggedMismatch.add(lib.packageName);
874
+ console.log(
875
+ `[RUN.game Libraries] Skipping embedded ${lib.packageName}: installed=${installed ?? "unknown"} embedded=${lib.version}`
876
+ );
877
+ }
878
+ return null;
879
+ }
880
+ requiredLibraries.add(match.libraryKey);
881
+ return `\0virtual:rundot-game-lib:${match.libraryKey}:${id}`;
882
+ }
883
+ return null;
884
+ },
885
+ // 2. Generate virtual module
886
+ load(id) {
887
+ if (!id.startsWith("\0virtual:rundot-game-lib:")) {
888
+ return null;
889
+ }
890
+ const parts = id.split(":");
891
+ const libraryKey = parts[2];
892
+ const importPath = parts.slice(3).join(":");
893
+ const lib = EMBEDDED_LIBRARY_BY_KEY[libraryKey];
894
+ if (!lib) {
895
+ const availableKeys = Object.keys(EMBEDDED_LIBRARY_BY_KEY).join(", ");
896
+ throw new Error(
897
+ `Unknown library: ${libraryKey}. Available libraries: ${availableKeys}`
898
+ );
899
+ }
900
+ const spec = lib.moduleSpecifiers.find((s) => {
901
+ if (s.match === "exact") return s.value === importPath;
902
+ if (s.match === "prefix") return importPath.startsWith(s.value);
903
+ return false;
904
+ });
905
+ if (!spec) {
906
+ const validSpecifiers = lib.moduleSpecifiers.map((s) => s.value).join(", ");
907
+ throw new Error(
908
+ `Unknown module specifier: ${importPath} for ${libraryKey}. Valid specifiers: ${validSpecifiers}`
909
+ );
910
+ }
911
+ const virtualModule = generateVirtualModule(lib, spec, importPath);
912
+ return `// Virtual module: ${importPath}
913
+ ${virtualModule}`;
914
+ },
915
+ // 3. Transform HTML - inject config and shim for web support
916
+ transformIndexHtml(html) {
917
+ if (!enabled || requiredLibraries.size === 0) {
918
+ return html;
919
+ }
920
+ const manifest = {};
921
+ requiredLibraries.forEach((libraryKey) => {
922
+ const lib = EMBEDDED_LIBRARY_BY_KEY[libraryKey];
923
+ manifest[libraryKey] = {
924
+ assetKey: lib.assetKey,
925
+ cdnPath: lib.cdnPath,
926
+ globalVar: lib.globalVar,
927
+ loadStage: lib.loadStage
928
+ };
929
+ });
930
+ const embeddedLibsScript = `
931
+ <script>
932
+ // Set config (may be overridden by RUN.game host)
933
+ if (!window.__rundotGameLibrariesConfig) {
934
+ window.__rundotGameLibrariesConfig = {
935
+ enabled: true,
936
+ required: ${JSON.stringify(Array.from(requiredLibraries))},
937
+ manifest: ${JSON.stringify(manifest, null, 2)},
938
+ cdnBase: '${DEFAULT_SHARED_LIB_CDN_BASE}',
939
+ useHost: false, // Web uses CDN
940
+ debug: ${debug}
941
+ };
942
+ }
943
+
944
+ // Minimal shim for web (CDN loading)
945
+ if (!window.__rundotGameLibraryShim) {
946
+ var bootstrapResolve, bootstrapReject;
947
+ var bootstrapPromise = new Promise(function(resolve, reject) {
948
+ bootstrapResolve = resolve;
949
+ bootstrapReject = reject;
950
+ });
951
+
952
+ window.__rundotGameLibraryShim = {
953
+ ready: function() { return bootstrapPromise; },
954
+ __initialized: true
955
+ };
956
+
957
+ // Auto-load libraries from CDN by stage (respects dependencies)
958
+ var config = window.__rundotGameLibrariesConfig;
959
+ if (!config.required || config.required.length === 0) {
960
+ bootstrapResolve();
961
+ } else {
962
+ // Group libraries by load stage
963
+ var librariesByStage = {};
964
+ for (var i = 0; i < config.required.length; i++) {
965
+ var libraryKey = config.required[i];
966
+ var entry = config.manifest[libraryKey];
967
+ var stage = entry.loadStage || 0;
968
+ if (!librariesByStage[stage]) librariesByStage[stage] = [];
969
+ librariesByStage[stage].push({ libraryKey: libraryKey, entry: entry });
970
+ }
971
+
972
+ // Load stages sequentially, libraries within each stage in parallel
973
+ var stages = Object.keys(librariesByStage).sort(function(a, b) { return parseInt(a, 10) - parseInt(b, 10); });
974
+
975
+ (async function loadByStages() {
976
+ for (var s = 0; s < stages.length; s++) {
977
+ var stage = stages[s];
978
+ var libs = librariesByStage[stage];
979
+
980
+ // Load all libraries in this stage in parallel
981
+ var stagePromises = libs.map(function(lib) {
982
+ var base = config.cdnBase || '';
983
+ if (!base.endsWith('/')) base += '/';
984
+ var path = lib.entry.cdnPath;
985
+ if (path.charAt(0) === '/') path = path.substring(1);
986
+ var url = base + path;
987
+ return fetch(url)
988
+ .then(function(r) {
989
+ if (!r.ok) throw new Error('HTTP ' + r.status + ' fetching ' + url);
990
+ return r.text();
991
+ })
992
+ .then(function(src) {
993
+ try {
994
+ (new Function(src)).call(window);
995
+ } catch (evalErr) {
996
+ throw new Error('Failed to evaluate ' + lib.libraryKey + ': ' + evalErr.message);
997
+ }
998
+ if (!window.__rundotGameLibraryExports) window.__rundotGameLibraryExports = {};
999
+ var exported = window[lib.entry.globalVar];
1000
+ if (!exported) {
1001
+ throw new Error(lib.libraryKey + ' loaded but window.' + lib.entry.globalVar + ' is undefined. Check CDN file.');
1002
+ }
1003
+ window.__rundotGameLibraryExports[lib.libraryKey] = exported;
1004
+ })
1005
+ .catch(function(err) {
1006
+ throw new Error('[' + lib.libraryKey + '] ' + err.message);
1007
+ });
1008
+ });
1009
+
1010
+ await Promise.all(stagePromises);
1011
+ }
1012
+ })()
1013
+ .then(function() {
1014
+ bootstrapResolve();
1015
+ })
1016
+ .catch(function(err) {
1017
+ console.error('[RUN.game Libraries] Library loading failed:', err.message || err);
1018
+ bootstrapReject(err);
1019
+ });
1020
+ }
1021
+ }
1022
+ </script>`;
1023
+ return html.replace("<head>", "<head>" + embeddedLibsScript);
1024
+ },
1025
+ // 4. Serve CDN assets in dev mode (only if in H5 folder structure)
1026
+ configureServer(server) {
1027
+ if (!enabled || !appName) {
1028
+ return;
1029
+ }
1030
+ if (debug) {
1031
+ console.log(
1032
+ `[RUN.game Libraries] Dev server will serve /${appName}/* from ${cdnDir}`
1033
+ );
1034
+ }
1035
+ server.middlewares.use(
1036
+ (req, res, next) => {
1037
+ if (!req.url) return next();
1038
+ const urlPath = req.url.split("?")[0];
1039
+ if (!urlPath.startsWith(`/${appName}/`)) return next();
1040
+ const relativePath = urlPath.replace(`/${appName}/`, "");
1041
+ const filePath = path.join(cdnDir, relativePath);
1042
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
1043
+ const ext = path.extname(filePath).toLowerCase();
1044
+ const contentTypes = {
1045
+ ".png": "image/png",
1046
+ ".jpg": "image/jpeg",
1047
+ ".jpeg": "image/jpeg",
1048
+ ".gif": "image/gif",
1049
+ ".webp": "image/webp",
1050
+ ".mp3": "audio/mpeg",
1051
+ ".mp4": "video/mp4",
1052
+ ".ogg": "audio/ogg",
1053
+ ".wav": "audio/wav",
1054
+ ".json": "application/json"
1055
+ };
1056
+ res.setHeader(
1057
+ "Content-Type",
1058
+ contentTypes[ext] || "application/octet-stream"
1059
+ );
1060
+ res.setHeader("Cache-Control", "public, max-age=3600");
1061
+ return res.end(fs.readFileSync(filePath));
1062
+ }
1063
+ next();
1064
+ }
1065
+ );
1066
+ },
1067
+ // 5. Emit manifest for debugging
1068
+ generateBundle() {
1069
+ if (!enabled || requiredLibraries.size === 0) return;
1070
+ const libraries = {};
1071
+ const libraryNames = [];
1072
+ let totalSavingsKB = 0;
1073
+ requiredLibraries.forEach((libraryKey) => {
1074
+ const lib = EMBEDDED_LIBRARY_BY_KEY[libraryKey];
1075
+ libraries[libraryKey] = {
1076
+ assetKey: lib.assetKey,
1077
+ cdnPath: lib.cdnPath,
1078
+ globalVar: lib.globalVar,
1079
+ packageName: lib.packageName,
1080
+ version: lib.version,
1081
+ moduleSpecifiers: lib.moduleSpecifiers.map((s) => s.value)
1082
+ };
1083
+ libraryNames.push(lib.packageName);
1084
+ const sizes = {
1085
+ phaser: 1100,
1086
+ react: 138,
1087
+ "react-dom": 42,
1088
+ three: 600,
1089
+ "matter-js": 100,
1090
+ inkjs: 50
1091
+ };
1092
+ totalSavingsKB += sizes[lib.packageName] || 0;
1093
+ });
1094
+ const manifest = {
1095
+ enabled: true,
1096
+ required: Array.from(requiredLibraries),
1097
+ libraries,
1098
+ cdnBase: DEFAULT_SHARED_LIB_CDN_BASE,
1099
+ buildTime: (/* @__PURE__ */ new Date()).toISOString(),
1100
+ plugin: "@series-inc/rundot-game-sdk/vite"
1101
+ };
1102
+ this.emitFile({
1103
+ type: "asset",
1104
+ fileName: "rundot-game-libraries.manifest.json",
1105
+ source: JSON.stringify(manifest, null, 2)
1106
+ });
1107
+ const libList = libraryNames.join(", ");
1108
+ const savingsMsg = totalSavingsKB > 0 ? ` (~${totalSavingsKB}KB saved)` : "";
1109
+ console.log(
1110
+ `
1111
+ [RUN.game Libraries] Using embedded libraries to reduce bundle size: ${libList}${savingsMsg}`
1112
+ );
1113
+ }
1114
+ };
1115
+ }
1116
+ function cdnPlugin() {
1117
+ let config;
1118
+ return {
1119
+ name: "remove-dev-assets",
1120
+ apply: "build",
1121
+ // Hook to capture the resolved config
1122
+ configResolved(resolvedConfig) {
1123
+ config = resolvedConfig;
1124
+ },
1125
+ closeBundle() {
1126
+ const outDir = config.build.outDir;
1127
+ const devAssetsDir = path.resolve(config.root, outDir, "cdn");
1128
+ if (fs.existsSync(devAssetsDir)) {
1129
+ fs.rmSync(devAssetsDir, { recursive: true, force: true });
1130
+ console.log(`[Cleanup] Removed cdn from: ${devAssetsDir}`);
1131
+ }
1132
+ }
1133
+ };
1134
+ }
1135
+
1136
+ // raw-loader:/home/cooper/venus-sdk/packages/api/src/vite/sandboxToolbarStyle.css
1137
+ var sandboxToolbarStyle_default = {};
1138
+
1139
+ // compiled-string-loader:/home/cooper/venus-sdk/packages/api/src/vite/sandboxToolbarScript.ts
1140
+ var sandboxToolbarScript_default = 'const LOCAL_STORAGE_POS_KEY = "rundot-game-sandbox-pos";\nconst LOCAL_STORAGE_PLAYER_KEY = "rundot-game-sandbox-selected-player";\nconst LOCAL_STORAGE_MAX_PLAYER_KEY = "rundot-game-sandbox-max-player";\nlet inMemorySelectedPlayerId = "player1";\nfunction getMaxPlayerNumber() {\n const selected = getSelectedPlayerId();\n const selectedMatch = String(selected).match(/^player(\\d+)$/);\n const selectedN = selectedMatch ? parseInt(selectedMatch[1], 10) : 1;\n try {\n const raw = localStorage.getItem(LOCAL_STORAGE_MAX_PLAYER_KEY);\n const n = parseInt(String(raw || "").trim(), 10);\n const safe = Number.isFinite(n) && n >= 1 ? n : 1;\n return Math.max(safe, Number.isFinite(selectedN) ? selectedN : 1);\n } catch {\n return Math.max(1, Number.isFinite(selectedN) ? selectedN : 1);\n }\n}\nfunction setMaxPlayerNumber(n) {\n const safe = Number.isFinite(n) && n >= 1 ? Math.floor(n) : 1;\n try {\n localStorage.setItem(LOCAL_STORAGE_MAX_PLAYER_KEY, String(safe));\n } catch {\n }\n return safe;\n}\nfunction getSelectedPlayerId() {\n try {\n const raw = localStorage.getItem(LOCAL_STORAGE_PLAYER_KEY);\n const candidate = (raw || "").trim().toLowerCase();\n if (!candidate) return "player1";\n if (!/^player\\d+$/.test(candidate)) return "player1";\n const n = parseInt(candidate.replace("player", ""), 10);\n if (!Number.isFinite(n) || n < 1) return "player1";\n const normalized = `player${n}`;\n inMemorySelectedPlayerId = normalized;\n return normalized;\n } catch {\n return inMemorySelectedPlayerId || "player1";\n }\n}\nfunction setSelectedPlayerId(playerId) {\n const normalized = String(playerId || "").trim().toLowerCase();\n const safe = /^player\\d+$/.test(normalized) ? normalized : "player1";\n inMemorySelectedPlayerId = safe;\n try {\n localStorage.setItem(LOCAL_STORAGE_PLAYER_KEY, safe);\n } catch {\n }\n return safe;\n}\nfunction nextPlayerId(currentPlayerId) {\n const current = String(currentPlayerId || "").trim().toLowerCase();\n const match = current.match(/^player(\\d+)$/);\n const n = match ? parseInt(match[1], 10) : 1;\n const next = Number.isFinite(n) && n >= 1 ? n + 1 : 2;\n return `player${next}`;\n}\nfunction buildPlayerSelectOptions(selectedPlayerId, maxPlayerNumber) {\n const selected = String(selectedPlayerId || "player1").trim().toLowerCase();\n const maxN = Number.isFinite(maxPlayerNumber) && maxPlayerNumber >= 1 ? maxPlayerNumber : 1;\n let html = "";\n for (let i = 1; i <= maxN; i++) {\n const value = `player${i}`;\n const selectedAttr = value === selected ? " selected" : "";\n html += `<option value="${value}"${selectedAttr}>${value}</option>`;\n }\n html += `<option value="__new__">+ New player\\u2026</option>`;\n return html;\n}\n;\n(async () => {\n try {\n if (!shouldRenderToolbar()) {\n return;\n }\n await ensureDomReady();\n const sandboxConfig = window.__RUNDOT_GAME_SANDBOX__ || {};\n const sandboxEnabled = isSandboxEnabled(sandboxConfig);\n if (!sandboxEnabled) {\n return;\n }\n createFloatingToolbar(sandboxConfig);\n } catch (error) {\n console.error("[RUN.game Sandbox] Failed to render toolbar:", error);\n }\n})();\nfunction shouldRenderToolbar() {\n if (typeof window === "undefined") {\n return false;\n }\n const host = location.hostname;\n return host === "localhost" || host === "127.0.0.1" || host.endsWith(".local");\n}\nfunction ensureDomReady() {\n if (document.readyState === "loading") {\n return new Promise((resolve) => {\n document.addEventListener("DOMContentLoaded", resolve, { once: true });\n });\n }\n return Promise.resolve();\n}\nfunction isSandboxEnabled(sandboxConfig) {\n return Boolean(sandboxConfig && sandboxConfig.enabled === true);\n}\nfunction createFloatingToolbar(sandboxConfig) {\n const fab = document.createElement("div");\n fab.id = "rundot-game-sandbox-toolbar";\n const target = String(sandboxConfig?.target || "local").toLowerCase();\n const envGradients = {\n local: "linear-gradient(120deg, #f59e0b, #d97706)",\n dev: "linear-gradient(120deg, #0891b2, #10b981)",\n staging: "linear-gradient(120deg, #8b5cf6, #6366f1)"\n };\n const envLabels = {\n local: "LOCAL",\n dev: "DEV",\n staging: "STAGING"\n };\n const initialGradient = envGradients[target] || envGradients.local;\n const envLabel = envLabels[target] || "LOCAL";\n const savedPos = localStorage.getItem(LOCAL_STORAGE_POS_KEY);\n let posRight = 16;\n let posY = Math.round((window.innerHeight - 36) / 2);\n if (savedPos) {\n try {\n const parsed = JSON.parse(savedPos);\n posRight = parsed.right;\n posY = parsed.y;\n } catch (e) {\n }\n }\n posRight = Math.min(Math.max(0, posRight), window.innerWidth - 36);\n posY = Math.min(Math.max(0, posY), window.innerHeight - 36);\n fab.style.cssText = `\n position: fixed;\n right: ${posRight}px;\n top: ${posY}px;\n width: auto;\n min-width: 160px;\n height: 36px;\n padding: 0;\n margin: 0;\n border: none;\n border-radius: 999px;\n background: ${initialGradient};\n color: #ffffff;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 12px;\n font-weight: 600;\n letter-spacing: 0.02em;\n cursor: grab;\n user-select: none;\n z-index: 2147483601;\n box-shadow: 0 12px 25px rgba(0, 0, 0, 0.35);\n display: flex;\n align-items: center;\n gap: 0;\n pointer-events: auto;\n border: 1px solid rgba(255, 255, 255, 0.2);\n `;\n fab.dataset.target = target;\n fab.dataset.envGradient = initialGradient;\n const envBadge = document.createElement("div");\n envBadge.style.cssText = `\n padding: 0 10px;\n height: 100%;\n display: flex;\n align-items: center;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n background: rgba(0, 0, 0, 0.2);\n border-top-left-radius: 999px;\n border-bottom-left-radius: 999px;\n `;\n envBadge.textContent = envLabel;\n const labelDiv = document.createElement("div");\n labelDiv.style.cssText = `\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 12px;\n height: 100%;\n font-size: 12px;\n font-weight: 600;\n background: rgba(0, 0, 0, 0.1);\n cursor: pointer;\n `;\n labelDiv.textContent = "\\u25CB Sign in";\n const handleDiv = document.createElement("div");\n handleDiv.style.cssText = `\n width: 36px;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n font-weight: 600;\n background: rgba(0, 0, 0, 0.25);\n border-top-right-radius: 999px;\n border-bottom-right-radius: 999px;\n cursor: grab;\n `;\n handleDiv.textContent = "\\u271C";\n fab.appendChild(envBadge);\n fab.appendChild(labelDiv);\n fab.appendChild(handleDiv);\n const toolbarRoot = document.getElementById("rundot-game-toolbar-root");\n (toolbarRoot || document.body).appendChild(fab);\n setupDragHandling(fab, handleDiv);\n setupCollapseExpand(fab, envBadge, labelDiv);\n labelDiv.addEventListener("click", () => {\n openAuthModal(sandboxConfig, getCurrentAuthState());\n });\n window.addEventListener("rundot-game-auth-state-changed", (e) => {\n updateToolbarState(fab, labelDiv, e.detail);\n });\n setTimeout(() => {\n checkInitialAuthState(fab, labelDiv, sandboxConfig);\n }, 500);\n}\nlet currentAuthState = { signedIn: false, user: null };\nfunction getCurrentAuthState() {\n return currentAuthState;\n}\nfunction checkInitialAuthState(fab, labelDiv, sandboxConfig) {\n window.dispatchEvent(new CustomEvent("rundot-game-check-auth-state"));\n setTimeout(() => {\n if (!currentAuthState.signedIn) {\n updateToolbarState(fab, labelDiv, { signedIn: false, user: null });\n }\n }, 2e3);\n}\nfunction updateToolbarState(fab, labelDiv, authState) {\n currentAuthState = authState;\n const envGradient = fab.dataset.envGradient || "linear-gradient(120deg, #f59e0b, #d97706)";\n const isLocal = String(fab.dataset.target || "").toLowerCase() === "local";\n if (authState.signedIn && authState.user) {\n fab.style.background = envGradient;\n fab.style.opacity = "1";\n const displayName = authState.user.displayName || authState.user.email || "Signed in";\n labelDiv.textContent = `\\u2713 ${truncate(displayName, 14)}`;\n } else {\n fab.style.background = envGradient;\n fab.style.opacity = "0.85";\n labelDiv.textContent = isLocal ? `\\u25CB ${getSelectedPlayerId()}` : "\\u25CB Sign in";\n }\n}\nfunction truncate(str, maxLength) {\n if (str.length <= maxLength) return str;\n return str.substring(0, maxLength - 1) + "\\u2026";\n}\nfunction setupCollapseExpand(fab, envBadge, labelDiv) {\n let collapseTimeout;\n let isCollapsed = false;\n const collapse = () => {\n fab.style.minWidth = "36px";\n fab.style.opacity = "0.4";\n envBadge.style.display = "none";\n labelDiv.style.display = "none";\n isCollapsed = true;\n };\n const expand = () => {\n clearTimeout(collapseTimeout);\n fab.style.minWidth = "160px";\n fab.style.opacity = "1";\n envBadge.style.display = "flex";\n labelDiv.style.display = "flex";\n isCollapsed = false;\n };\n const scheduleCollapse = () => {\n clearTimeout(collapseTimeout);\n collapseTimeout = setTimeout(collapse, 5e3);\n };\n scheduleCollapse();\n fab.addEventListener("mouseenter", expand);\n fab.addEventListener("mouseleave", () => {\n if (!isCollapsed) scheduleCollapse();\n });\n}\nfunction setupDragHandling(fab, handleDiv) {\n let isDragging = false;\n let startX, startY, offsetRight, offsetY;\n fab.addEventListener("mousedown", (e) => {\n isDragging = true;\n startX = e.clientX;\n startY = e.clientY;\n offsetRight = window.innerWidth - fab.offsetLeft - fab.offsetWidth;\n offsetY = fab.offsetTop;\n fab.style.cursor = "grabbing";\n e.preventDefault();\n });\n document.addEventListener("mousemove", (e) => {\n if (!isDragging) return;\n let newRight = offsetRight - (e.clientX - startX);\n let newTop = offsetY + (e.clientY - startY);\n newRight = Math.min(Math.max(0, newRight), window.innerWidth - 36);\n newTop = Math.min(Math.max(0, newTop), window.innerHeight - 36);\n fab.style.right = `${newRight}px`;\n fab.style.top = `${newTop}px`;\n });\n document.addEventListener("mouseup", () => {\n if (!isDragging) return;\n isDragging = false;\n fab.style.cursor = "grab";\n const currentRight = window.innerWidth - fab.offsetLeft - fab.offsetWidth;\n localStorage.setItem(\n LOCAL_STORAGE_POS_KEY,\n JSON.stringify({\n right: currentRight,\n y: fab.offsetTop\n })\n );\n });\n}\nfunction openAuthModal(sandboxConfig, authState) {\n const overlay = document.createElement("div");\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 2147483602;\n backdrop-filter: blur(4px);\n `;\n const content = document.createElement("div");\n content.style.cssText = `\n background: #111827;\n color: #F3F4F6;\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 16px;\n width: 90%;\n max-width: 420px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);\n font-family: system-ui, -apple-system, sans-serif;\n overflow: hidden;\n `;\n const target = String(sandboxConfig?.target || "local").toLowerCase();\n const targetUpper = target.toUpperCase();\n const gameId = sandboxConfig?.gameId || "Unknown";\n const isSignedIn = authState.signedIn;\n const user = authState.user;\n const isLocal = target === "local";\n const selectedPlayerId = isLocal ? getSelectedPlayerId() : null;\n const maxPlayerNumber = isLocal ? getMaxPlayerNumber() : 1;\n if (isLocal) {\n setMaxPlayerNumber(maxPlayerNumber);\n }\n const envConfig = {\n local: {\n gradient: "linear-gradient(120deg, #f59e0b, #d97706)",\n badge: "#f59e0b",\n badgeBg: "rgba(245, 158, 11, 0.15)",\n label: "LOCAL",\n subtitle: "Firebase Emulator"\n },\n dev: {\n gradient: "linear-gradient(120deg, #0891b2, #10b981)",\n badge: "#10b981",\n badgeBg: "rgba(16, 185, 129, 0.15)",\n label: "DEV",\n subtitle: "Development Server"\n },\n staging: {\n gradient: "linear-gradient(120deg, #8b5cf6, #6366f1)",\n badge: "#8b5cf6",\n badgeBg: "rgba(139, 92, 246, 0.15)",\n label: "STAGING",\n subtitle: "Staging Server"\n }\n };\n const env = envConfig[target] || envConfig.local;\n const statusIcon = isSignedIn ? "\\u2713" : "\\u25CB";\n const statusText = isSignedIn ? "Signed in" : "Not signed in";\n const statusColor = isSignedIn ? "#10b981" : "#6b7280";\n const userDisplay = user ? user.displayName || user.email || "Unknown" : "\\u2014";\n const userIdShort = user?.uid ? user.uid.slice(0, 8) + "..." : "";\n content.innerHTML = `\n <div style="\n background: ${env.gradient};\n padding: 20px 24px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n ">\n <div style="display: flex; align-items: center; gap: 10px;">\n <span style="font-size: 24px;">\\u{1FA90}</span>\n <div>\n <div style="font-size: 16px; font-weight: 700;">RUN.game Sandbox</div>\n <div style="font-size: 12px; opacity: 0.9;">${env.subtitle}</div>\n </div>\n </div>\n <button id="close-modal" style="\n background: rgba(0, 0, 0, 0.15);\n border: none;\n color: white;\n width: 32px;\n height: 32px;\n border-radius: 999px;\n cursor: pointer;\n font-size: 18px;\n ">\\xD7</button>\n </div>\n\n <div style="padding: 24px;">\n <div style="display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px;">\n <span style="\n padding: 4px 10px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n background: ${env.badgeBg};\n color: ${env.badge};\n ">${targetUpper}</span>\n <span style="\n padding: 4px 10px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n background: rgba(255, 255, 255, 0.08);\n color: rgba(255, 255, 255, 0.85);\n ">gameId: ${gameId}</span>\n </div>\n\n <div style="\n padding: 14px 16px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 10px;\n margin-bottom: 14px;\n ">\n <div style="display:flex; align-items:center; justify-content:space-between; gap: 10px; flex-wrap: wrap;">\n <div style="display:flex; align-items:center; gap: 10px; flex: 1; min-width: 0;">\n <div style="\n width: 30px;\n height: 30px;\n border-radius: 999px;\n display:flex;\n align-items:center;\n justify-content:center;\n background: rgba(255,255,255,0.06);\n border: 1px solid rgba(255,255,255,0.08);\n font-weight: 800;\n color: ${statusColor};\n ">${statusIcon}</div>\n <div style="min-width: 0;">\n ${// Local emulator UX: when signed out, show the player dropdown right where the status goes.\n // When signed in, show the normal status + user display.\n !isSignedIn && isLocal ? `\n <div style="font-size: 11px; font-weight: 700; color: rgba(255,255,255,0.75); letter-spacing: 0.05em; text-transform: uppercase;">Local Player</div>\n <select id="local-player-select" style="\n margin-top: 6px;\n width: 180px;\n max-width: 45vw;\n min-width: 130px;\n background: rgba(0, 0, 0, 0.25);\n color: #F3F4F6;\n border: 1px solid rgba(255, 255, 255, 0.10);\n border-radius: 8px;\n padding: 8px 10px;\n font-size: 13px;\n font-weight: 800;\n outline: none;\n appearance: auto;\n ">\n ${buildPlayerSelectOptions(selectedPlayerId, maxPlayerNumber)}\n </select>\n ` : `\n <div style="font-size: 13px; font-weight: 700;">${statusText}</div>\n <div style="font-size: 11px; color: rgba(255,255,255,0.6); margin-top: 2px;">\n ${isSignedIn ? `${userDisplay} ${userIdShort ? `(${userIdShort})` : ""}` : "Click sign in to authenticate"}\n </div>\n `}\n </div>\n </div>\n <div>\n ${isSignedIn ? `\n <button id="sign-out" style="\n background: rgba(239, 68, 68, 0.12);\n color: #fecaca;\n border: 1px solid rgba(239, 68, 68, 0.25);\n border-radius: 8px;\n padding: 8px 10px;\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n ">Sign out</button>\n ` : `\n <button id="sign-in-google" style="\n background: rgba(255, 255, 255, 0.12);\n color: #ffffff;\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n padding: 8px 10px;\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n ">Sign in</button>\n `}\n </div>\n </div>\n </div>\n\n <div style="\n padding: 12px 14px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 10px;\n margin-bottom: 14px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.75);\n line-height: 1.5;\n ">\n <div style="font-weight: 600; margin-bottom: 4px; color: #c7d2fe;">\\u{1F4A1} Change environment:</div>\n <code style="\n display: block;\n background: rgba(0, 0, 0, 0.3);\n padding: 8px 10px;\n border-radius: 5px;\n font-family: \'SF Mono\', Monaco, monospace;\n font-size: 11px;\n color: #e0e7ff;\n margin-top: 6px;\n ">rundot-game set-env &lt;local|dev|staging&gt;</code>\n </div>\n\n ${isLocal ? `\n <div style="\n padding: 10px 14px;\n background: rgba(245, 158, 11, 0.08);\n border: 1px solid rgba(245, 158, 11, 0.15);\n border-radius: 8px;\n font-size: 11px;\n color: #fcd34d;\n line-height: 1.5;\n margin-bottom: 14px;\n ">\n <strong>Emulator mode:</strong> Profiles are auto-created on first sign-in.\n <a href="http://localhost:4000" target="_blank" style="color: #fbbf24; text-decoration: underline; margin-left: 4px;">Open Emulator UI \\u2192</a>\n </div>\n ` : ""}\n\n <!-- Local player selection is shown in the auth card (signed-out local mode) -->\n\n <div style="display:flex; gap: 10px;">\n <button id="reload-page" style="\n flex: 1;\n background: rgba(255, 255, 255, 0.06);\n color: rgba(255, 255, 255, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.10);\n border-radius: 10px;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n ">Reload page</button>\n </div>\n </div>\n `;\n overlay.appendChild(content);\n document.body.appendChild(overlay);\n const $ = (selector) => content.querySelector(selector);\n const closeModal = () => overlay.remove();\n const closeBtn = $("#close-modal");\n if (closeBtn) closeBtn.addEventListener("click", closeModal);\n overlay.addEventListener("click", (e) => {\n if (e.target === overlay) closeModal();\n });\n $("#reload-page")?.addEventListener("click", () => {\n window.location.reload();\n });\n const playerSelect = $("#local-player-select");\n if (playerSelect) {\n playerSelect.addEventListener("change", () => {\n const value = String(playerSelect.value || "").trim().toLowerCase();\n if (value === "__new__") {\n const nextId = nextPlayerId(getSelectedPlayerId());\n const match = String(nextId).match(/^player(\\d+)$/);\n const n = match ? parseInt(match[1], 10) : getMaxPlayerNumber() + 1;\n setMaxPlayerNumber(n);\n const saved = setSelectedPlayerId(`player${n}`);\n playerSelect.innerHTML = buildPlayerSelectOptions(saved, getMaxPlayerNumber());\n playerSelect.value = saved;\n } else {\n setSelectedPlayerId(value);\n }\n const toolbar = document.getElementById("rundot-game-sandbox-toolbar");\n const toolbarLabelDiv = toolbar?.children?.[1];\n const isLocalTarget = (toolbar?.dataset?.target || "").toLowerCase() === "local";\n if (toolbarLabelDiv && isLocalTarget && !currentAuthState.signedIn) {\n toolbarLabelDiv.textContent = `\\u25CB ${getSelectedPlayerId()}`;\n }\n });\n }\n const signInBtn = $("#sign-in-google");\n if (signInBtn) {\n const originalGradient = env.gradient;\n signInBtn.addEventListener("click", async () => {\n try {\n signInBtn.textContent = "Signing in...";\n signInBtn.disabled = true;\n const waitForSignedIn = (timeoutMs) => {\n return new Promise((resolve) => {\n let done = false;\n const cleanup = () => {\n if (done) return;\n done = true;\n window.removeEventListener("rundot-game-auth-state-changed", handler);\n clearTimeout(timer);\n };\n const handler = (e) => {\n if (e?.detail?.signedIn === true) {\n cleanup();\n resolve(true);\n }\n };\n const timer = setTimeout(() => {\n cleanup();\n resolve(false);\n }, timeoutMs);\n if (currentAuthState?.signedIn === true) {\n cleanup();\n resolve(true);\n return;\n }\n window.addEventListener("rundot-game-auth-state-changed", handler);\n });\n };\n const signedInWait = waitForSignedIn(8e3);\n let signInError = null;\n if (isLocal && window.__RUNDOT_GAME_SANDBOX_SIGN_IN_AS__) {\n try {\n const p = window.__RUNDOT_GAME_SANDBOX_SIGN_IN_AS__(getSelectedPlayerId());\n p?.catch?.((err) => {\n signInError = err;\n });\n } catch (err) {\n signInError = err;\n }\n } else if (window.__RUNDOT_GAME_SANDBOX_SIGN_IN__) {\n try {\n const p = window.__RUNDOT_GAME_SANDBOX_SIGN_IN__();\n p?.catch?.((err) => {\n signInError = err;\n });\n } catch (err) {\n signInError = err;\n }\n } else {\n throw new Error("[RUN.game Sandbox] Sign-in is not available (missing global hook).");\n }\n const signedIn = await signedInWait;\n if (!signedIn) {\n if (signInError) throw signInError;\n throw new Error("[RUN.game Sandbox] Sign-in timed out. Try reloading and signing in again.");\n }\n signInBtn.textContent = "\\u2713 Success!";\n signInBtn.style.background = "linear-gradient(120deg, #10b981, #059669)";\n setTimeout(() => {\n closeModal();\n window.location.reload();\n }, 700);\n } catch (error) {\n console.error("[RUN.game Sandbox] Sign-in failed:", error);\n signInBtn.textContent = "Sign in";\n signInBtn.disabled = false;\n signInBtn.style.background = originalGradient;\n alert("Sign-in failed. Check console for details.");\n }\n });\n }\n const signOutBtn = $("#sign-out");\n if (signOutBtn) {\n signOutBtn.addEventListener("click", async () => {\n try {\n signOutBtn.textContent = "Signing out...";\n signOutBtn.disabled = true;\n if (window.__RUNDOT_GAME_SANDBOX_SIGN_OUT__) {\n await window.__RUNDOT_GAME_SANDBOX_SIGN_OUT__();\n }\n closeModal();\n window.location.reload();\n } catch (error) {\n console.error("[RUN.game Sandbox] Sign-out failed:", error);\n signOutBtn.textContent = "Sign out";\n signOutBtn.disabled = false;\n alert("Sign-out failed. Check console for details.");\n }\n });\n }\n}\n';
1141
+
1142
+ // src/vite/sandboxToolbar.ts
1143
+ function createSandboxToolbarTags() {
1144
+ const styleTag = {
1145
+ tag: "style",
1146
+ attrs: { id: "rundot-game-sandbox-style" },
1147
+ children: sandboxToolbarStyle_default,
1148
+ injectTo: "head"
1149
+ };
1150
+ const scriptTag = {
1151
+ tag: "script",
1152
+ attrs: {
1153
+ type: "module",
1154
+ id: "rundot-game-sandbox-script",
1155
+ "data-rundot-game-ignore": "true"
1156
+ },
1157
+ children: sandboxToolbarScript_default,
1158
+ injectTo: "body"
1159
+ };
1160
+ return [styleTag, scriptTag];
1161
+ }
1162
+
1163
+ // src/firebase/firebaseConfigs.ts
1164
+ var FIREBASE_CONFIGS = {
1165
+ // Local mode uses dev Firebase credentials (auth emulator doesn't support Google OAuth)
1166
+ local: {
1167
+ apiKey: "AIzaSyCSYBs5QrFUeaE8QC2Jfp6834enCzbCJqQ",
1168
+ authDomain: "dev-venus-app.firebaseapp.com",
1169
+ projectId: "dev-venus-app",
1170
+ storageBucket: "dev-venus-app.appspot.com"
1171
+ },
1172
+ dev: {
1173
+ apiKey: "AIzaSyCSYBs5QrFUeaE8QC2Jfp6834enCzbCJqQ",
1174
+ authDomain: "dev-venus-app.firebaseapp.com",
1175
+ projectId: "dev-venus-app",
1176
+ storageBucket: "dev-venus-app.appspot.com"
1177
+ },
1178
+ staging: {
1179
+ apiKey: "AIzaSyCfv4HGtNjStUsN0IL-TUNLYSNSZ3KhXN4",
1180
+ authDomain: "staging-venus-app.firebaseapp.com",
1181
+ projectId: "staging-venus-app",
1182
+ storageBucket: "staging-venus-app.appspot.com"
1183
+ }
1184
+ };
1185
+ var FUNCTIONS_REGIONS = {
1186
+ local: "us-central1",
1187
+ dev: "us-central1",
1188
+ staging: "us-central1"
1189
+ };
1190
+ var DEFAULT_EMULATOR_HOSTS = {
1191
+ functions: "localhost:5001",
1192
+ firestore: "localhost:8080",
1193
+ auth: "localhost:9099"
1194
+ };
1195
+
1196
+ // src/vite/RundotGameSandboxPlugin.ts
1197
+ var REQUIRED_FIREBASE_VERSION = "^12.7.0";
1198
+ var GAME_CONFIG_FILENAME = "game.config.json";
1199
+ var RUNDOT_GAME_CONFIG_PATH = join(homedir(), ".rundot-game", "config.json");
1200
+ function rundotGameSandboxPlugin(options = {}) {
1201
+ let projectRoot = process.cwd();
1202
+ let isServeCommand = false;
1203
+ let warningLogged = false;
1204
+ let firebaseMissing = false;
1205
+ const logWarning = (message) => {
1206
+ if (!warningLogged) {
1207
+ warningLogged = true;
1208
+ console.warn(`[RUN.game Sandbox] ${message}`);
1209
+ }
1210
+ };
1211
+ return {
1212
+ name: "rundot-game-sandbox",
1213
+ apply: "serve",
1214
+ enforce: "pre",
1215
+ // Run before other plugins to set up optimizeDeps exclusions
1216
+ config(config, env) {
1217
+ projectRoot = resolveProjectRoot(config);
1218
+ isServeCommand = env.command === "serve";
1219
+ if (isServeCommand && !options.disabled) {
1220
+ const hasFirebase = checkFirebaseDependency(projectRoot);
1221
+ if (!hasFirebase) {
1222
+ firebaseMissing = true;
1223
+ logFirebaseMissing();
1224
+ return {
1225
+ ...config,
1226
+ optimizeDeps: {
1227
+ ...config?.optimizeDeps,
1228
+ exclude: [
1229
+ ...config?.optimizeDeps?.exclude || [],
1230
+ "@series-inc/rundot-game-sdk",
1231
+ "firebase",
1232
+ "firebase/app",
1233
+ "firebase/auth",
1234
+ "firebase/firestore"
1235
+ ]
1236
+ }
1237
+ };
1238
+ } else {
1239
+ let sandboxConfig = null;
1240
+ try {
1241
+ sandboxConfig = buildSandboxConfig(projectRoot, options);
1242
+ } catch {
1243
+ }
1244
+ const proxyConfig = buildProxyConfig(sandboxConfig);
1245
+ return {
1246
+ ...config,
1247
+ define: {
1248
+ ...config?.define,
1249
+ "import.meta.env.VITE_APP_MODE": JSON.stringify("live")
1250
+ },
1251
+ optimizeDeps: {
1252
+ ...config?.optimizeDeps,
1253
+ include: [
1254
+ ...config?.optimizeDeps?.include || [],
1255
+ "firebase/app",
1256
+ "firebase/auth",
1257
+ "firebase/firestore"
1258
+ ]
1259
+ },
1260
+ server: {
1261
+ ...config?.server,
1262
+ proxy: {
1263
+ ...config?.server?.proxy,
1264
+ ...proxyConfig
1265
+ }
1266
+ }
1267
+ };
1268
+ }
1269
+ }
1270
+ return config;
1271
+ },
1272
+ async transformIndexHtml(html) {
1273
+ if (!isServeCommand || options.disabled) {
1274
+ return html;
1275
+ }
1276
+ if (firebaseMissing) {
1277
+ const errorMessage = `Firebase is not installed.
1278
+
1279
+ Add "firebase": "${REQUIRED_FIREBASE_VERSION}" to your package.json devDependencies, then run npm install.
1280
+
1281
+ Firebase is only needed for sandbox mode (local development) and is excluded from production builds.`;
1282
+ const errorScript = buildErrorOverlayScript(errorMessage, "local");
1283
+ return {
1284
+ html,
1285
+ tags: [
1286
+ {
1287
+ tag: "script",
1288
+ attrs: { type: "module" },
1289
+ children: errorScript,
1290
+ injectTo: "body"
1291
+ }
1292
+ ]
1293
+ };
1294
+ }
1295
+ try {
1296
+ const config = buildSandboxConfig(projectRoot, options);
1297
+ if (!config) {
1298
+ return html;
1299
+ }
1300
+ const injectedHtml = injectRundotGameShell(html);
1301
+ const configScript = `window.__RUNDOT_GAME_SANDBOX__ = ${JSON.stringify(config)};`;
1302
+ console.log(`[RUN.game Sandbox] Enabled for ${config.target} environment (gameId: ${config.gameId})`);
1303
+ return {
1304
+ html: injectedHtml,
1305
+ tags: [
1306
+ {
1307
+ tag: "script",
1308
+ attrs: { type: "module" },
1309
+ children: configScript,
1310
+ injectTo: "head"
1311
+ },
1312
+ ...createSandboxToolbarTags()
1313
+ ]
1314
+ };
1315
+ } catch (error) {
1316
+ const message = error instanceof Error ? error.message : String(error);
1317
+ logWarning(message);
1318
+ const target = options.target ?? "local";
1319
+ const errorScript = buildErrorOverlayScript(message, target);
1320
+ return {
1321
+ html,
1322
+ tags: [
1323
+ {
1324
+ tag: "script",
1325
+ attrs: { type: "module" },
1326
+ children: errorScript,
1327
+ injectTo: "body"
1328
+ }
1329
+ ]
1330
+ };
1331
+ }
1332
+ }
1333
+ };
1334
+ }
1335
+ function buildSandboxConfig(projectRoot, options) {
1336
+ let gameId = options.gameId?.trim();
1337
+ if (!gameId) {
1338
+ const gameConfigPath = findGameConfigPath(projectRoot);
1339
+ if (gameConfigPath) {
1340
+ const gameConfig = readGameConfig(gameConfigPath);
1341
+ gameId = gameConfig.gameId?.trim();
1342
+ }
1343
+ }
1344
+ if (!gameId) {
1345
+ throw new Error(
1346
+ `[RUN.game Sandbox] Missing gameId. Either:
1347
+ 1. Add gameId to vite.config: rundotGameSandboxPlugin({ gameId: 'your-app-id' })
1348
+ 2. Create game.config.json with { "gameId": "your-app-id" }
1349
+ 3. Run "rundot-game configure-game" if using RUN.game CLI`
1350
+ );
1351
+ }
1352
+ const cliConfig = readRundotGameCliConfig();
1353
+ const target = options.target ?? coerceTarget(cliConfig?.activeEnv);
1354
+ const envConfig = cliConfig?.sandbox?.[target];
1355
+ if (envConfig?.enabled === false) {
1356
+ return null;
1357
+ }
1358
+ const firebaseConfig = FIREBASE_CONFIGS[target];
1359
+ const config = {
1360
+ enabled: true,
1361
+ target,
1362
+ gameId,
1363
+ firebaseConfig,
1364
+ functionsRegion: FUNCTIONS_REGIONS[target],
1365
+ pollingInterval: envConfig?.pollingInterval ?? 2e3,
1366
+ rpcMaxRetries: envConfig?.rpcMaxRetries ?? 0,
1367
+ rpcRetryDelayMs: envConfig?.rpcRetryDelayMs ?? 1e3
1368
+ };
1369
+ if (target === "local") {
1370
+ config.functionsEmulatorHost = DEFAULT_EMULATOR_HOSTS.functions;
1371
+ config.firestoreEmulatorHost = DEFAULT_EMULATOR_HOSTS.firestore;
1372
+ config.authEmulatorHost = DEFAULT_EMULATOR_HOSTS.auth;
1373
+ }
1374
+ if (envConfig?.backendUrl) {
1375
+ config.backendUrl = envConfig.backendUrl;
1376
+ }
1377
+ return config;
1378
+ }
1379
+ function readRundotGameCliConfig() {
1380
+ try {
1381
+ if (!existsSync(RUNDOT_GAME_CONFIG_PATH)) {
1382
+ return null;
1383
+ }
1384
+ const raw = readFileSync(RUNDOT_GAME_CONFIG_PATH, "utf-8");
1385
+ return JSON.parse(raw);
1386
+ } catch {
1387
+ return null;
1388
+ }
1389
+ }
1390
+ function readGameConfig(pathname) {
1391
+ try {
1392
+ const raw = readFileSync(pathname, "utf-8");
1393
+ return JSON.parse(raw);
1394
+ } catch (error) {
1395
+ const message = error instanceof Error ? error.message : "Unknown error";
1396
+ throw new Error(`Failed to parse ${pathname}: ${message}`);
1397
+ }
1398
+ }
1399
+ function findGameConfigPath(startDir) {
1400
+ let current = resolve(startDir);
1401
+ const visited = /* @__PURE__ */ new Set();
1402
+ while (true) {
1403
+ const candidate = join(current, GAME_CONFIG_FILENAME);
1404
+ if (existsSync(candidate)) {
1405
+ return candidate;
1406
+ }
1407
+ if (visited.has(current)) {
1408
+ break;
1409
+ }
1410
+ visited.add(current);
1411
+ const parent = dirname(current);
1412
+ if (parent === current) {
1413
+ break;
1414
+ }
1415
+ current = parent;
1416
+ }
1417
+ return null;
1418
+ }
1419
+ function resolveProjectRoot(config) {
1420
+ if (config?.root && config.root.length > 0) {
1421
+ return resolve(config.root);
1422
+ }
1423
+ return process.cwd();
1424
+ }
1425
+ function coerceTarget(value) {
1426
+ if (value === "local" || value === "dev" || value === "staging") {
1427
+ return value;
1428
+ }
1429
+ return "staging";
1430
+ }
1431
+ function injectRundotGameShell(html) {
1432
+ const match = html.match(/<body([^>]*)>([\s\S]*)<\/body>/i);
1433
+ if (!match) {
1434
+ return html;
1435
+ }
1436
+ const [, attrs, contents] = match;
1437
+ const serialized = JSON.stringify(contents).replace(/<\/script>/gi, "<\\/script>");
1438
+ const shell = `
1439
+ <body${attrs}>
1440
+ <div id="rundot-game-toolbar-root" aria-live="polite"></div>
1441
+ <div id="rundot-game-app-root" data-rundot-game-app-root style="display:contents;">
1442
+ ${contents}
1443
+ </div>
1444
+ <noscript id="rundot-game-app-placeholder">
1445
+ RUN.game sandbox requires JavaScript enabled.
1446
+ </noscript>
1447
+ <script id="rundot-game-app-capture">
1448
+ window.__RUNDOT_GAME_APP_HTML = ${serialized};
1449
+ </script>
1450
+ </body>
1451
+ `;
1452
+ return html.replace(/<body([\s\S]*)<\/body>/i, shell);
1453
+ }
1454
+ function checkFirebaseDependency(projectRoot) {
1455
+ try {
1456
+ const require2 = createRequire(join(projectRoot, "package.json"));
1457
+ require2.resolve("firebase/app");
1458
+ return true;
1459
+ } catch {
1460
+ return false;
1461
+ }
1462
+ }
1463
+ function logFirebaseMissing() {
1464
+ console.error(`
1465
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1466
+ \u2551 \u2551
1467
+ \u2551 IMPORTANT: The RUN.game sandbox plugin requires Firebase. \u2551
1468
+ \u2551 \u2551
1469
+ \u2551 Add the following to your package.json devDependencies: \u2551
1470
+ \u2551 \u2551
1471
+ \u2551 "firebase": "${REQUIRED_FIREBASE_VERSION}" \u2551
1472
+ \u2551 \u2551
1473
+ \u2551 Then run: \u2551
1474
+ \u2551 \u2551
1475
+ \u2551 npm install \u2551
1476
+ \u2551 \u2551
1477
+ \u2551 WHY devDependencies? \u2551
1478
+ \u2551 Firebase is only used during local development (sandbox mode). It is \u2551
1479
+ \u2551 dynamically imported and excluded from production builds, so it should \u2551
1480
+ \u2551 be a devDependency to keep production bundles small. \u2551
1481
+ \u2551 \u2551
1482
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1483
+ `);
1484
+ }
1485
+ function buildErrorOverlayScript(message, target) {
1486
+ const escapedMessage = message.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
1487
+ return `
1488
+ (function() {
1489
+ var overlay = document.createElement('div');
1490
+ overlay.id = 'rundot-game-sandbox-error';
1491
+ overlay.style.cssText = 'position:fixed;inset:0;z-index:999999;background:rgba(0,0,0,0.92);color:#fff;font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;padding:20px;';
1492
+
1493
+ var content = document.createElement('div');
1494
+ content.style.cssText = 'max-width:600px;text-align:center;';
1495
+
1496
+ var icon = document.createElement('div');
1497
+ icon.style.cssText = 'font-size:48px;margin-bottom:16px;';
1498
+ icon.textContent = '\u26A0\uFE0F';
1499
+
1500
+ var title = document.createElement('h1');
1501
+ title.style.cssText = 'font-size:24px;font-weight:600;margin:0 0 16px;color:#ff6b6b;';
1502
+ title.textContent = 'RUN.game Sandbox Error';
1503
+
1504
+ var msg = document.createElement('p');
1505
+ msg.style.cssText = 'font-size:14px;line-height:1.6;margin:0 0 24px;color:#ccc;';
1506
+ msg.textContent = '${escapedMessage}';
1507
+
1508
+ var hint = document.createElement('div');
1509
+ hint.style.cssText = 'background:rgba(255,255,255,0.1);border-radius:8px;padding:16px;font-size:13px;color:#aaa;';
1510
+ hint.innerHTML = '<strong style="color:#fff;">Environment:</strong> ${target}<br>Check your game.config.json and ~/.rundot-game/config.json';
1511
+
1512
+ content.appendChild(icon);
1513
+ content.appendChild(title);
1514
+ content.appendChild(msg);
1515
+ content.appendChild(hint);
1516
+ overlay.appendChild(content);
1517
+
1518
+ if (document.body) {
1519
+ document.body.appendChild(overlay);
1520
+ } else {
1521
+ document.addEventListener('DOMContentLoaded', function() {
1522
+ document.body.appendChild(overlay);
1523
+ });
1524
+ }
1525
+ })();
1526
+ `;
1527
+ }
1528
+ var RUNDOT_GAME_API_PROXY_PATH = "/__rundotgameapi";
1529
+ function buildProxyConfig(config) {
1530
+ if (!config || config.target === "local") {
1531
+ return {};
1532
+ }
1533
+ const projectId = config.firebaseConfig?.projectId;
1534
+ const region = config.functionsRegion || "us-central1";
1535
+ if (!projectId) {
1536
+ return {};
1537
+ }
1538
+ const targetUrl = `https://${region}-${projectId}.cloudfunctions.net`;
1539
+ console.log(`[RUN.game Sandbox] Setting up CORS proxy: ${RUNDOT_GAME_API_PROXY_PATH}/* -> ${targetUrl}/*`);
1540
+ return {
1541
+ [RUNDOT_GAME_API_PROXY_PATH]: {
1542
+ target: targetUrl,
1543
+ changeOrigin: true,
1544
+ rewrite: (path3) => path3.replace(new RegExp(`^${RUNDOT_GAME_API_PROXY_PATH}`), "")
1545
+ }
1546
+ };
1547
+ }
1548
+
1549
+ export { cdnPlugin, rundotGameLibrariesPlugin, rundotGameSandboxPlugin };
1550
+ //# sourceMappingURL=index.js.map
1551
+ //# sourceMappingURL=index.js.map