koin.js 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -266,6 +266,7 @@ interface UseNostalgistOptions {
266
266
  initialVolume?: number;
267
267
  romFileName?: string;
268
268
  shader?: string;
269
+ romId?: string;
269
270
  }
270
271
  interface UseNostalgistReturn {
271
272
  status: EmulatorStatus;
@@ -306,7 +307,7 @@ interface UseNostalgistReturn {
306
307
  getNostalgistInstance: () => Nostalgist | null;
307
308
  isPerformanceMode: boolean;
308
309
  }
309
- declare const useNostalgist: ({ system, romUrl, core, biosUrl, initialState, getCanvasElement, keyboardControls, gamepadBindings, retroAchievements, onReady, onError, initialVolume, romFileName, shader, }: UseNostalgistOptions) => UseNostalgistReturn;
310
+ declare const useNostalgist: ({ system, romUrl, core, biosUrl, initialState, getCanvasElement, keyboardControls, gamepadBindings, retroAchievements, onReady, onError, initialVolume, romFileName, shader, romId, }: UseNostalgistOptions) => UseNostalgistReturn;
310
311
 
311
312
  /**
312
313
  * Available CRT Shader Presets
package/dist/index.d.ts CHANGED
@@ -266,6 +266,7 @@ interface UseNostalgistOptions {
266
266
  initialVolume?: number;
267
267
  romFileName?: string;
268
268
  shader?: string;
269
+ romId?: string;
269
270
  }
270
271
  interface UseNostalgistReturn {
271
272
  status: EmulatorStatus;
@@ -306,7 +307,7 @@ interface UseNostalgistReturn {
306
307
  getNostalgistInstance: () => Nostalgist | null;
307
308
  isPerformanceMode: boolean;
308
309
  }
309
- declare const useNostalgist: ({ system, romUrl, core, biosUrl, initialState, getCanvasElement, keyboardControls, gamepadBindings, retroAchievements, onReady, onError, initialVolume, romFileName, shader, }: UseNostalgistOptions) => UseNostalgistReturn;
310
+ declare const useNostalgist: ({ system, romUrl, core, biosUrl, initialState, getCanvasElement, keyboardControls, gamepadBindings, retroAchievements, onReady, onError, initialVolume, romFileName, shader, romId, }: UseNostalgistOptions) => UseNostalgistReturn;
310
311
 
311
312
  /**
312
313
  * Available CRT Shader Presets
package/dist/index.js CHANGED
@@ -5646,10 +5646,45 @@ function getCore2(system) {
5646
5646
  return getCore(system);
5647
5647
  }
5648
5648
 
5649
+ // src/lib/rom-cache.ts
5650
+ var CACHE_NAME = "koin-rom-cache-v1";
5651
+ async function getCachedRom(romId) {
5652
+ if (typeof caches === "undefined") return null;
5653
+ try {
5654
+ const cache = await caches.open(CACHE_NAME);
5655
+ const response = await cache.match(romId);
5656
+ if (response) {
5657
+ console.log(`[Cache] Hit for ROM ${romId}`);
5658
+ return await response.blob();
5659
+ }
5660
+ } catch (e) {
5661
+ console.warn("[Cache] Read failed:", e);
5662
+ }
5663
+ return null;
5664
+ }
5665
+ async function fetchAndCacheRom(romId, url) {
5666
+ const response = await fetch(url);
5667
+ if (!response.ok) {
5668
+ throw new Error(`Failed to fetch ROM: ${response.statusText}`);
5669
+ }
5670
+ const blob = await response.blob();
5671
+ if (typeof caches !== "undefined") {
5672
+ try {
5673
+ const cache = await caches.open(CACHE_NAME);
5674
+ await cache.put(romId, new Response(blob));
5675
+ console.log(`[Cache] Cached ROM ${romId}`);
5676
+ } catch (e) {
5677
+ console.warn("[Cache] Write failed:", e);
5678
+ }
5679
+ }
5680
+ return blob;
5681
+ }
5682
+
5649
5683
  // src/hooks/emulator/useEmulatorCore.ts
5650
5684
  function useEmulatorCore({
5651
5685
  system,
5652
5686
  romUrl,
5687
+ romId,
5653
5688
  core: coreOverride,
5654
5689
  biosUrl,
5655
5690
  initialState,
@@ -5731,7 +5766,25 @@ function useEmulatorCore({
5731
5766
  setError(null);
5732
5767
  const core = coreOverride || getCore2(system);
5733
5768
  let romOption = romUrl;
5734
- if (romFileName) {
5769
+ if (romId) {
5770
+ try {
5771
+ let blob = await getCachedRom(romId);
5772
+ if (!blob) {
5773
+ console.log(`[Nostalgist] Fetching and caching ROM ${romId}`);
5774
+ blob = await fetchAndCacheRom(romId, romUrl);
5775
+ } else {
5776
+ console.log(`[Nostalgist] Loaded ROM ${romId} from cache`);
5777
+ }
5778
+ if (blob) {
5779
+ romOption = {
5780
+ fileName: romFileName || "rom.bin",
5781
+ fileContent: blob
5782
+ };
5783
+ }
5784
+ } catch (err) {
5785
+ console.error("[Nostalgist] Cache/Fetch error, falling back to direct URL:", err);
5786
+ }
5787
+ } else if (romFileName) {
5735
5788
  romOption = { fileName: romFileName, fileContent: romUrl };
5736
5789
  }
5737
5790
  const inputConfig = buildRetroArchConfig({
@@ -6398,7 +6451,8 @@ var useNostalgist = ({
6398
6451
  onError,
6399
6452
  initialVolume = 100,
6400
6453
  romFileName,
6401
- shader
6454
+ shader,
6455
+ romId
6402
6456
  }) => {
6403
6457
  const isHeavySystem = React5.useMemo(() => {
6404
6458
  return PERFORMANCE_TIER_2_SYSTEMS.has(system.toUpperCase());
@@ -6426,6 +6480,7 @@ var useNostalgist = ({
6426
6480
  } = useEmulatorCore({
6427
6481
  system,
6428
6482
  romUrl,
6483
+ romId,
6429
6484
  core,
6430
6485
  biosUrl,
6431
6486
  initialState,
@@ -6619,6 +6674,7 @@ function useControls(system, onNotify) {
6619
6674
  function useGameSession(props) {
6620
6675
  const {
6621
6676
  romUrl,
6677
+ romId,
6622
6678
  romFileName,
6623
6679
  system,
6624
6680
  core,
@@ -6667,6 +6723,7 @@ function useGameSession(props) {
6667
6723
  const nostalgist = useNostalgist({
6668
6724
  system,
6669
6725
  romUrl,
6726
+ romId,
6670
6727
  romFileName,
6671
6728
  core,
6672
6729
  biosUrl,
@@ -7440,8 +7497,39 @@ function usePlayerPersistence(onSettingsChange) {
7440
7497
  updateSettings
7441
7498
  };
7442
7499
  }
7500
+
7501
+ // src/lib/telemetry.ts
7502
+ var sendTelemetry = (eventName, params = {}) => {
7503
+ if (typeof window === "undefined") return;
7504
+ try {
7505
+ fetch("https://koin.theretrosaga.com/api/telemetry", {
7506
+ method: "POST",
7507
+ headers: {
7508
+ "Content-Type": "application/json"
7509
+ },
7510
+ body: JSON.stringify({
7511
+ event_name: eventName,
7512
+ params: {
7513
+ ...params,
7514
+ url: window.location.href,
7515
+ referrer: document.referrer,
7516
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
7517
+ }
7518
+ })
7519
+ }).catch(() => {
7520
+ });
7521
+ } catch (e) {
7522
+ }
7523
+ };
7443
7524
  var GamePlayer = React5.memo(function GamePlayer2(props) {
7444
7525
  const { settings, updateSettings, isLoaded: settingsLoaded } = usePlayerPersistence();
7526
+ React5.useEffect(() => {
7527
+ sendTelemetry("game_start", {
7528
+ system: props.system,
7529
+ core: props.core,
7530
+ game: props.title || "unknown"
7531
+ });
7532
+ }, [props.system, props.core, props.title]);
7445
7533
  const [biosModalOpen, setBiosModalOpen] = React5.useState(false);
7446
7534
  const [showShortcutsModal, setShowShortcutsModal] = React5.useState(false);
7447
7535
  const effectiveShader = props.shader !== void 0 ? props.shader : settings.shader;
@@ -7539,6 +7627,13 @@ var GamePlayer = React5.memo(function GamePlayer2(props) {
7539
7627
  status,
7540
7628
  isPerformanceMode
7541
7629
  } = nostalgist;
7630
+ React5.useEffect(() => {
7631
+ if (status === "running") {
7632
+ console.log("[Koin Debug] Status:", status);
7633
+ console.log("[Koin Debug] isPerformanceMode:", isPerformanceMode);
7634
+ console.log("[Koin Debug] crossOriginIsolated:", typeof window !== "undefined" ? window.crossOriginIsolated : "N/A");
7635
+ }
7636
+ }, [status, isPerformanceMode]);
7542
7637
  React5.useEffect(() => {
7543
7638
  if (settingsLoaded) {
7544
7639
  setVolume(settings.volume);
@@ -7732,6 +7827,18 @@ var GamePlayer = React5.memo(function GamePlayer2(props) {
7732
7827
  ]
7733
7828
  }
7734
7829
  ),
7830
+ !isPerformanceMode && (status === "running" || status === "paused") && /* @__PURE__ */ jsxRuntime.jsx(
7831
+ "div",
7832
+ {
7833
+ className: "bg-red-900/80 backdrop-blur-md px-2 py-1 rounded border border-red-500/50 flex items-center gap-1.5",
7834
+ title: "Standard Mode (Single Threaded)",
7835
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] uppercase font-bold tracking-wider text-red-200", children: [
7836
+ "Standard (",
7837
+ typeof window !== "undefined" && window.crossOriginIsolated ? "Isolated" : "Not Isolated",
7838
+ ")"
7839
+ ] })
7840
+ }
7841
+ ),
7735
7842
  settings.showInputDisplay && (status === "running" || status === "paused") && /* @__PURE__ */ jsxRuntime.jsx(
7736
7843
  InputDisplay_default,
7737
7844
  {