Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@
"audit-ci": "^7.1.0",
"is-ci": "^4.1.0",
"lefthook": "^2.1.10",
"oxfmt": "^0.58.0",
"oxlint": "^1.73.0",
"oxfmt": "^0.60.0",
"oxlint": "^1.75.0",
"rimraf": "^6.1.3",
"typescript": "^6.0.3",
"vite": "^8.1.4"
"vite": "^8.1.5"
},
"engines": {
"node": ">=24.11.0",
"pnpm": ">=11.0.0"
},
"packageManager": "pnpm@11.9.0"
"packageManager": "pnpm@11.17.0"
}
44 changes: 22 additions & 22 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,47 @@
"@jpinsonneau/html-to-image": "^1.11.13",
"@noble/ciphers": "^2.2.0",
"@posthog/nextjs-config": "^1.9.68",
"@radix-ui/react-accordion": "^1.2.16",
"@radix-ui/react-dialog": "^1.1.19",
"@radix-ui/react-label": "^2.1.11",
"@radix-ui/react-popover": "^1.1.19",
"@radix-ui/react-select": "^2.3.3",
"@radix-ui/react-slot": "^1.3.0",
"@radix-ui/react-switch": "^1.3.3",
"@radix-ui/react-tabs": "^1.1.17",
"@radix-ui/react-tooltip": "^1.2.12",
"@radix-ui/react-accordion": "^1.2.20",
"@radix-ui/react-dialog": "^1.1.23",
"@radix-ui/react-label": "^2.1.15",
"@radix-ui/react-popover": "^1.1.23",
"@radix-ui/react-select": "^2.3.7",
"@radix-ui/react-slot": "^1.3.3",
"@radix-ui/react-switch": "^1.3.7",
"@radix-ui/react-tabs": "^1.1.21",
"@radix-ui/react-tooltip": "^1.2.16",
"@react-three/fiber": "^9.6.1",
"@semianalysisai/inferencex-constants": "workspace:*",
"@semianalysisai/inferencex-db": "workspace:*",
"@shikijs/rehype": "^4.3.1",
"@tailwindcss/typography": "^0.5.20",
"@tanstack/react-query": "^5.101.2",
"@tanstack/react-query": "^5.101.4",
"@vercel/analytics": "^2.0.1",
"@vercel/blob": "^2.6.1",
"@vercel/speed-insights": "^2.0.0",
"adm-zip": "^0.5.18",
"adm-zip": "^0.6.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"d3": "^7.9.0",
"dompurify": "^3.4.11",
"dompurify": "^3.4.12",
"gray-matter": "^4.0.3",
"iwanthue": "^2.0.0",
"lucide-react": "^1.24.0",
"iwanthue": "^2.1.0",
"lucide-react": "^1.27.0",
"mp4-muxer": "^5.2.2",
"next": "^16.2.10",
"next": "^16.2.12",
"next-mdx-remote": "^6.0.0",
"next-themes": "^0.4.6",
"posthog-js": "^1.399.1",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"posthog-js": "^1.407.2",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"remark-gfm": "^4.0.1",
"shiki": "^4.3.1",
"tailwind-merge": "^3.6.0",
"three": "^0.185.1"
},
"devDependencies": {
"@bahmutov/cypress-esbuild-preprocessor": "^2.2.8",
"@tailwindcss/postcss": "^4.3.2",
"@tailwindcss/postcss": "^4.3.3",
"@types/adm-zip": "^0.5.8",
"@types/d3": "^7.4.3",
"@types/mdast": "^4.0.4",
Expand All @@ -82,15 +82,15 @@
"@types/react-dom": "^19.2.3",
"@types/three": "^0.185.1",
"@vitest/coverage-v8": "^4.1.10",
"cypress": "^15.18.1",
"cypress": "^15.19.0",
"cypress-split": "^1.25.0",
"dotenv": "^17.4.2",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.28.1",
"fake-indexeddb": "^6.2.5",
"jsdom": "^29.1.1",
"tailwindcss": "^4.3.2",
"tsx": "^4.23.0",
"tailwindcss": "^4.3.3",
"tsx": "^4.23.1",
"tw-animate-css": "^1.4.0",
"typescript": "^6.0.3",
"vitest": "^4.1.10"
Expand Down
58 changes: 35 additions & 23 deletions packages/app/src/lib/chunk-load-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites
import { installChunkLoadRecovery } from './chunk-load-recovery';

const KEY = 'chunk_reload';
const reload = vi.fn();
let originalLocation: Location;

// JSDOM forbids spying on `window.location.reload`, but the implementation
// always sets the sessionStorage gate immediately BEFORE calling reload(),
// so the gate's `'1'` state is a 1:1 proxy for "reload was attempted." We
// also stub `reload` to a no-op via Object.defineProperty so the bare
// `window.location.reload()` call doesn't blow up the JSDOM test environment.
// JSDOM exposes `Location.reload` as non-configurable. In this isolated test
// environment, replace Location with a prototype-preserving copy that stubs only reload.

function chunkErr(): Error {
const e = new Error('Loading chunk 123 failed');
Expand All @@ -19,10 +18,18 @@ function chunkErr(): Error {

describe('installChunkLoadRecovery', () => {
beforeAll(() => {
originalLocation = window.location;
const descriptors = Object.getOwnPropertyDescriptors(originalLocation);
Reflect.deleteProperty(descriptors, 'reload');
const location = Object.create(Object.getPrototypeOf(originalLocation));
Object.defineProperties(location, descriptors);
Object.defineProperty(location, 'reload', {
configurable: true,
value: reload,
});
Object.defineProperty(window, 'location', {
configurable: true,
writable: true,
value: { ...window.location, reload: vi.fn() },
value: location,
});
installChunkLoadRecovery();
});
Expand All @@ -32,7 +39,10 @@ describe('installChunkLoadRecovery', () => {
});

afterAll(() => {
// best-effort cleanup; subsequent suites get a fresh JSDOM anyway
Object.defineProperty(window, 'location', {
configurable: true,
value: originalLocation,
});
});

it('sets the reload gate on ChunkLoadError from an error event', () => {
Expand All @@ -51,14 +61,11 @@ describe('installChunkLoadRecovery', () => {
expect(sessionStorage.getItem(KEY)).toBe('1');
});

it('only sets the gate once across multiple chunk errors in one session', () => {
const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
setItemSpy.mockClear();
it('reloads only once across multiple chunk errors in one session', () => {
reload.mockClear();
Comment thread
adibarra marked this conversation as resolved.
window.dispatchEvent(new ErrorEvent('error', { error: chunkErr() }));
window.dispatchEvent(new ErrorEvent('error', { error: chunkErr() }));
const writes = setItemSpy.mock.calls.filter((c) => c[0] === KEY);
expect(writes).toHaveLength(1);
setItemSpy.mockRestore();
expect(reload).toHaveBeenCalledTimes(1);
});

it('sets the gate on an unhandled rejection with a chunk-error reason', () => {
Expand All @@ -68,14 +75,19 @@ describe('installChunkLoadRecovery', () => {
expect(sessionStorage.getItem(KEY)).toBe('1');
});

it('is idempotent: repeated installs do not duplicate the gate write', () => {
const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
setItemSpy.mockClear();
installChunkLoadRecovery();
installChunkLoadRecovery();
window.dispatchEvent(new ErrorEvent('error', { error: chunkErr() }));
const writes = setItemSpy.mock.calls.filter((c) => c[0] === KEY);
expect(writes).toHaveLength(1);
setItemSpy.mockRestore();
it('installs each error listener only once', async () => {
const addEventListener = vi.spyOn(window, 'addEventListener');
vi.resetModules();
// Import after resetting module state so the listener spy captures initial installation.
const { installChunkLoadRecovery: install } = await import('./chunk-load-recovery');

install();
install();

expect(addEventListener).toHaveBeenCalledTimes(2);
expect(addEventListener.mock.calls.map(([type]) => type)).toEqual(
expect.arrayContaining(['error', 'unhandledrejection']),
);
addEventListener.mockRestore();
});
});
33 changes: 33 additions & 0 deletions packages/app/src/test/setup-local-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class MemoryStorage implements Storage {
readonly #values = new Map<string, string>();

get length(): number {
return this.#values.size;
}

clear(): void {
this.#values.clear();
}

getItem(key: string): string | null {
return this.#values.get(key) ?? null;
}

key(index: number): string | null {
return [...this.#values.keys()][index] ?? null;
}

removeItem(key: string): void {
this.#values.delete(key);
}

setItem(key: string, value: string): void {
this.#values.set(String(key), String(value));
}
}

// Override Node's unavailable experimental implementation without reading its warning getter.
Object.defineProperty(globalThis, 'localStorage', {
configurable: true,
value: new MemoryStorage(),
});
1 change: 1 addition & 0 deletions packages/app/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from 'path';
export default defineConfig({
test: {
environment: 'node',
setupFiles: ['src/test/setup-local-storage.ts'],
include: ['src/**/*.test.ts', 'src/**/*.test.tsx'],
coverage: {
provider: 'v8',
Expand Down
4 changes: 2 additions & 2 deletions packages/db/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
"@types/node": "^26.1.1",
"@types/stream-json": "^1.7.8",
"@vitest/coverage-v8": "^4.1.10",
"adm-zip": "^0.5.18",
"adm-zip": "^0.6.0",
"dotenv-cli": "^11.0.0",
"tsx": "^4.23.0",
"tsx": "^4.23.1",
"typescript": "^6.0.3",
"vitest": "^4.1.10"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"devDependencies": {
"@types/node": "^26.1.1",
"dotenv-cli": "^11.0.0",
"tsx": "^4.23.0",
"tsx": "^4.23.1",
"typescript": "^6.0.3",
"vitest": "^4.1.10"
}
Expand Down
Loading
Loading