From 59bc04ed0b436035d7e1afac2492cab82f60b3b2 Mon Sep 17 00:00:00 2001 From: Mike Shi Date: Sun, 8 Nov 2026 22:33:49 +0000 Subject: [PATCH 1/2] test(e2e): pin the Playwright browser locale to en-US On LANG=C.UTF-8 hosts headless Chromium reports navigator.language as en-US@posix, which Intl rejects. uPlot calls Intl.NumberFormat with it at import time, so the app crashes and e2e global setup times out. --- packages/app/playwright.config.ts | 4 ++++ packages/app/tests/e2e/global-setup-fullstack.ts | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/app/playwright.config.ts b/packages/app/playwright.config.ts index 78524bd2f9..d2fe0b4b32 100644 --- a/packages/app/playwright.config.ts +++ b/packages/app/playwright.config.ts @@ -75,6 +75,10 @@ export default defineConfig({ screenshot: 'only-on-failure', /* Record video on failure */ video: 'retain-on-failure', + // Without this, Chromium derives navigator.language from the host locale. + // On LANG=C.UTF-8 hosts that is 'en-US@posix', which Intl rejects, and + // uPlot calls Intl.NumberFormat(navigator.language) at import time. + locale: 'en-US', }, /* Global test timeout - CI needs more time than local */ diff --git a/packages/app/tests/e2e/global-setup-fullstack.ts b/packages/app/tests/e2e/global-setup-fullstack.ts index 9c196b6900..a0c92624e7 100644 --- a/packages/app/tests/e2e/global-setup-fullstack.ts +++ b/packages/app/tests/e2e/global-setup-fullstack.ts @@ -145,7 +145,11 @@ async function globalSetup(_config: FullConfig) { console.log('Creating test user and logging in'); const browser = await chromium.launch(); - const context = await browser.newContext({ baseURL: APP_URL }); + // Global setup doesn't inherit `use` from playwright.config.ts. + const context = await browser.newContext({ + baseURL: APP_URL, + locale: 'en-US', + }); const page = await context.newPage(); try { From 601718227846f9f6020035d8e8d41ab2167f00f9 Mon Sep 17 00:00:00 2001 From: Mike Shi Date: Sun, 8 Nov 2026 22:37:39 +0000 Subject: [PATCH 2/2] fix(app): don't crash on POSIX-style browser locales uPlot builds Intl.NumberFormat(navigator.language) at import time and throws on tags like en-US@posix, which Chromium reports on LANG=C.UTF-8 hosts. Patch it to fall back to en-US. --- .changeset/uplot-posix-locale.md | 5 +++ .../patches/uplot-npm-1.6.32-350d2da00d.patch | 40 +++++++++++++++++++ packages/app/package.json | 2 +- packages/app/playwright.config.ts | 5 +-- .../__tests__/uplotLocale.test.ts | 23 +++++++++++ yarn.lock | 11 ++++- 6 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 .changeset/uplot-posix-locale.md create mode 100644 .yarn/patches/uplot-npm-1.6.32-350d2da00d.patch create mode 100644 packages/app/src/components/DBHeatmapChart/__tests__/uplotLocale.test.ts diff --git a/.changeset/uplot-posix-locale.md b/.changeset/uplot-posix-locale.md new file mode 100644 index 0000000000..2b502a2829 --- /dev/null +++ b/.changeset/uplot-posix-locale.md @@ -0,0 +1,5 @@ +--- +'@hyperdx/app': patch +--- + +fix: The app no longer crashes on load in browsers that report a POSIX-style language such as `en-US@posix` diff --git a/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch b/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch new file mode 100644 index 0000000000..80a885b116 --- /dev/null +++ b/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch @@ -0,0 +1,40 @@ +diff --git a/dist/uPlot.cjs.js b/dist/uPlot.cjs.js +index 98b7582dff68d4f4d9e0068cc77bec9d33ea4904..d137fbcb9f24358d7e59350907181106fc5b231d 100644 +--- a/dist/uPlot.cjs.js ++++ b/dist/uPlot.cjs.js +@@ -441,7 +441,14 @@ function _rangeNum(_min, _max, cfg) { + } + + // alternative: https://stackoverflow.com/a/2254896 +-const numFormatter = new Intl.NumberFormat(domEnv ? nav.language : 'en-US'); ++// Patched: Intl rejects POSIX-style tags such as 'en-US@posix', which some ++// browsers report as navigator.language. Fall back instead of throwing at import. ++let numFormatter; ++try { ++ numFormatter = new Intl.NumberFormat(domEnv ? nav.language : 'en-US'); ++} catch (e) { ++ numFormatter = new Intl.NumberFormat('en-US'); ++} + const fmtNum = val => numFormatter.format(val); + + const M = Math; +diff --git a/dist/uPlot.esm.js b/dist/uPlot.esm.js +index 87473c9cfb3366f57883cb803114a87c64d4b6b8..740bd3626a1f8bd6bbe352878e55e898e759784c 100644 +--- a/dist/uPlot.esm.js ++++ b/dist/uPlot.esm.js +@@ -439,7 +439,14 @@ function _rangeNum(_min, _max, cfg) { + } + + // alternative: https://stackoverflow.com/a/2254896 +-const numFormatter = new Intl.NumberFormat(domEnv ? nav.language : 'en-US'); ++// Patched: Intl rejects POSIX-style tags such as 'en-US@posix', which some ++// browsers report as navigator.language. Fall back instead of throwing at import. ++let numFormatter; ++try { ++ numFormatter = new Intl.NumberFormat(domEnv ? nav.language : 'en-US'); ++} catch (e) { ++ numFormatter = new Intl.NumberFormat('en-US'); ++} + const fmtNum = val => numFormatter.format(val); + + const M = Math; diff --git a/packages/app/package.json b/packages/app/package.json index 829742797e..101e226cce 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -100,7 +100,7 @@ "store2": "^2.14.3", "strip-ansi": "^6.0.1", "timestamp-nano": "^1.0.1", - "uplot": "^1.6.31", + "uplot": "patch:uplot@npm%3A1.6.32#~/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch", "uplot-react": "^1.2.2", "zod": "3.25" }, diff --git a/packages/app/playwright.config.ts b/packages/app/playwright.config.ts index d2fe0b4b32..c5bca85edd 100644 --- a/packages/app/playwright.config.ts +++ b/packages/app/playwright.config.ts @@ -75,9 +75,8 @@ export default defineConfig({ screenshot: 'only-on-failure', /* Record video on failure */ video: 'retain-on-failure', - // Without this, Chromium derives navigator.language from the host locale. - // On LANG=C.UTF-8 hosts that is 'en-US@posix', which Intl rejects, and - // uPlot calls Intl.NumberFormat(navigator.language) at import time. + // Otherwise Chromium takes navigator.language from the host locale, so + // number and date formatting would vary between machines. locale: 'en-US', }, diff --git a/packages/app/src/components/DBHeatmapChart/__tests__/uplotLocale.test.ts b/packages/app/src/components/DBHeatmapChart/__tests__/uplotLocale.test.ts new file mode 100644 index 0000000000..d18b2f3ac9 --- /dev/null +++ b/packages/app/src/components/DBHeatmapChart/__tests__/uplotLocale.test.ts @@ -0,0 +1,23 @@ +// uPlot builds an Intl.NumberFormat from navigator.language at import time. +// Chromium on LANG=C.UTF-8 hosts reports 'en-US@posix', which Intl rejects; +// our yarn patch makes uPlot fall back to 'en-US' instead of throwing. +describe('uplot locale patch', () => { + let languageSpy: jest.SpyInstance; + + afterEach(() => { + languageSpy.mockRestore(); + }); + + it('imports and formats numbers when navigator.language is not a valid BCP 47 tag', async () => { + languageSpy = jest + .spyOn(window.navigator, 'language', 'get') + .mockReturnValue('en-US@posix'); + + expect(() => new Intl.NumberFormat('en-US@posix')).toThrow(RangeError); + + await jest.isolateModulesAsync(async () => { + const { default: uPlot } = await import('uplot'); + expect(uPlot.fmtNum(1234.5)).toBe('1,234.5'); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 1840c79899..0617f98e62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4976,7 +4976,7 @@ __metadata: timestamp-nano: "npm:^1.0.1" ts-jest: "npm:^29.4.11" typescript: "npm:^6.0.3" - uplot: "npm:^1.6.31" + uplot: "patch:uplot@npm%3A1.6.32#~/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch" uplot-react: "npm:^1.2.2" zod: "npm:3.25" languageName: unknown @@ -30603,13 +30603,20 @@ __metadata: languageName: node linkType: hard -"uplot@npm:^1.6.31": +"uplot@npm:1.6.32": version: 1.6.32 resolution: "uplot@npm:1.6.32" checksum: 10c0/4d9cdd5f53371656cfc178ea4ae0d8c9bdbe4c98c99df847926d7e5f5a5bcb8043561219da2979fee7616aabe3619d80fee093befdfe39aae65e6621284c39b1 languageName: node linkType: hard +"uplot@patch:uplot@npm%3A1.6.32#~/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch": + version: 1.6.32 + resolution: "uplot@patch:uplot@npm%3A1.6.32#~/.yarn/patches/uplot-npm-1.6.32-350d2da00d.patch::version=1.6.32&hash=d25b41" + checksum: 10c0/8eac8ee3e294a2834f6295eea230bd6dba6c7031a0d67f79f02eacec15ceaada5e82accd9ef1b6462326ec35e9475779f072b9be7625550a3e2f1d8fc70d2470 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1"