From ed48729bdb55be078049823ec74eb05e87da106d Mon Sep 17 00:00:00 2001 From: sureshchouksey8 Date: Thu, 14 May 2026 17:04:01 +0530 Subject: [PATCH 1/2] Normalize OBJ loader cache keys --- src/hooks/use-global-obj-loader.ts | 31 +++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/hooks/use-global-obj-loader.ts b/src/hooks/use-global-obj-loader.ts index 69422095..59a5e9f9 100644 --- a/src/hooks/use-global-obj-loader.ts +++ b/src/hooks/use-global-obj-loader.ts @@ -28,7 +28,7 @@ export function useGlobalObjLoader( useEffect(() => { if (!url) return - const cleanUrl = url.replace(/&cachebust_origin=$/, "") + const cleanUrl = normalizeObjLoaderCacheUrl(url) const cache = window.TSCIRCUIT_OBJ_LOADER_CACHE let hasUrlChanged = false @@ -120,3 +120,32 @@ export function useGlobalObjLoader( return obj } + +export function normalizeObjLoaderCacheUrl(url: string): string { + try { + const hasProtocol = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url) + const parsedUrl = new URL( + url, + globalThis.location?.href ?? "https://tscircuit.local", + ) + parsedUrl.searchParams.delete("cachebust_origin") + + const sortedParams = new URLSearchParams( + Array.from(parsedUrl.searchParams.entries()).sort(([a], [b]) => + a.localeCompare(b), + ), + ) + parsedUrl.search = sortedParams.toString() + + if (!hasProtocol) { + return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}` + } + + return parsedUrl.toString() + } catch { + return url + .replace(/([?&])cachebust_origin=[^&]*/g, "$1") + .replace(/[?&]$/, "") + .replace("?&", "?") + } +} From 7f42dee1ca5d47000fa3dc183e640537804c2e0d Mon Sep 17 00:00:00 2001 From: sureshchouksey8 Date: Thu, 14 May 2026 17:04:15 +0530 Subject: [PATCH 2/2] Add OBJ loader cache key tests --- tests/normalize-obj-loader-cache-url.test.ts | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/normalize-obj-loader-cache-url.test.ts diff --git a/tests/normalize-obj-loader-cache-url.test.ts b/tests/normalize-obj-loader-cache-url.test.ts new file mode 100644 index 00000000..a00e7e2a --- /dev/null +++ b/tests/normalize-obj-loader-cache-url.test.ts @@ -0,0 +1,28 @@ +import { expect, test } from "bun:test" +import { normalizeObjLoaderCacheUrl } from "../src/hooks/use-global-obj-loader" + +test("removes cachebust_origin from remote EasyEDA model URLs", () => { + const url = + "https://modelcdn.tscircuit.com/easyeda_models/download?uuid=c886ec2b42464573a88fc1f647577a49&pn=C5184526&cachebust_origin=https%3A%2F%2Ftscircuit.com" + + expect(normalizeObjLoaderCacheUrl(url)).toBe( + "https://modelcdn.tscircuit.com/easyeda_models/download?pn=C5184526&uuid=c886ec2b42464573a88fc1f647577a49", + ) +}) + +test("normalizes cache-equivalent model URLs to the same key", () => { + const first = + "https://modelcdn.tscircuit.com/easyeda_models/download?uuid=abc&pn=C1&cachebust_origin=https%3A%2F%2Ftscircuit.com" + const second = + "https://modelcdn.tscircuit.com/easyeda_models/download?cachebust_origin=http%3A%2F%2Flocalhost%3A3020&pn=C1&uuid=abc" + + expect(normalizeObjLoaderCacheUrl(first)).toBe( + normalizeObjLoaderCacheUrl(second), + ) +}) + +test("preserves relative model URLs without cachebust params", () => { + expect(normalizeObjLoaderCacheUrl("/easyeda-models/part.obj")).toBe( + "/easyeda-models/part.obj", + ) +})