bb22b216db
Fixes #22973 In Firefox, front components rendered a blank panel with only `DataCloneError: Exception object could not be cloned` in the console. Accessing `caches` in the opaque-origin sandbox worker throws a Gecko `Exception` (worker-side CacheStorage code up to v2.22, or any component code touching it since), and `@quilted/threads` posts thrown values raw over the MessagePort. Firefox cannot structured-clone these exceptions, so the error report itself failed and the render promise never settled. Thread errors are now flattened to clonable payloads and rehydrated on the other side, so the real error surfaces in the error box instead of silently hanging the panel. Also makes the CacheStorage guards exception-safe (v2.23 already moved that code host-side, which removed the main trigger). Verified end to end in stock Firefox 149: before, a component touching `caches` hangs silently; after, render rejects with the full `NS_ERROR_FAILURE` diagnostic. Chromium behavior unchanged. ```mermaid sequenceDiagram participant Host as Host (React) participant Worker as Sandbox worker (null origin) participant Threads as @quilted/threads rect rgb(250, 235, 235) note over Host,Threads: Before — Firefox hangs Host->>Worker: render(component) Worker->>Worker: throws Gecko Exception<br/>(typeof caches) Worker->>Threads: postMessage(rawException) Threads--xHost: DataCloneError:<br/>Exception could not be cloned note over Host: CALL_RESULT never arrives<br/>render() promise never settles → blank panel end rect rgb(232, 245, 233) note over Host,Threads: After — error surfaces Host->>Worker: render(component) Worker->>Worker: throws Gecko Exception Worker->>Threads: serialize → { name, message, stack } Threads->>Host: postMessage(clonable payload) Host->>Host: rehydrate → Error, reject render() note over Host: error box shows NS_ERROR_FAILURE end ``` <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23213?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
44 lines
1.3 KiB
JavaScript
44 lines
1.3 KiB
JavaScript
import { readFileSync } from 'fs';
|
|
import { dirname, resolve } from 'path';
|
|
import { pathsToModuleNameMapper } from 'ts-jest';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
const tsConfigPath = resolve(__dirname, './tsconfig.json');
|
|
const tsConfig = JSON.parse(readFileSync(tsConfigPath, 'utf8'));
|
|
|
|
const jestConfig = {
|
|
displayName: 'twenty-front-component-renderer',
|
|
preset: '../../jest.preset.js',
|
|
testEnvironment: 'jsdom',
|
|
setupFiles: ['<rootDir>/jest.setup.mjs'],
|
|
transformIgnorePatterns: ['node_modules/(?!@quilted/)'],
|
|
transform: {
|
|
'^.+\\.(mjs|[tj]sx?)$': [
|
|
'@swc/jest',
|
|
{
|
|
jsc: {
|
|
parser: { syntax: 'typescript', tsx: true },
|
|
transform: { react: { runtime: 'automatic' } },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
moduleNameMapper: {
|
|
...pathsToModuleNameMapper(tsConfig.compilerOptions.paths, {
|
|
prefix: '<rootDir>/',
|
|
}),
|
|
'^@quilted/threads$':
|
|
'<rootDir>/../../node_modules/@quilted/threads/build/esm/index.mjs',
|
|
'^@quilted/events$':
|
|
'<rootDir>/../../node_modules/@quilted/events/build/esm/index.mjs',
|
|
},
|
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'mjs'],
|
|
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
|
coverageDirectory: './coverage',
|
|
};
|
|
|
|
export default jestConfig;
|