17d34a6fe3
## Context The front component source cache introduced in the sandbox refactor was a silent no-op: it ran inside the sandboxed worker (opaque-origin `allow-scripts` iframe), where the `caches` global does not exist. Every render re-fetched the component JS from the network — nothing ever appeared in Cache Storage. ## Change <img width="1580" height="622" alt="image" src="https://github.com/user-attachments/assets/06903abf-b313-4d15-8db4-80950d4bf5ba" /> Moves component source resolution and caching from the worker to the host, where Cache Storage works: - `fetchComponentSource`, `fetchComponentSourceFromNetwork`, `frontComponentCacheStorageService` and `extractComponentChecksumFromUrl` relocated from `remote/worker/utils/` to `host/utils/` (`buildAuthorizationHeadersFromAccessToken` to shared `utils/`, still used by the worker for SDK module fetches) - `FrontComponentWorkerEffect` resolves the source before `thread.imports.render(...)` (with a cancellation guard) and passes `componentSource` in the render payload - `loadFrontComponentModule` no longer fetches: it keeps only sandbox-side work (SDK import rewrite, blob URL creation, `import()`) - New: stale-entry eviction — writing a new checksummed entry deletes older entries of the same `front-components/{id}/` prefix ## Security invariant The host only fetches, hashes and caches the source string — it never executes it. Execution stays exclusively in the opaque-origin worker via blob URL import. SHA-256 checksum verification is kept on both cache read (poisoned-entry guard: any same-origin code can write to Cache Storage) and cache write. ## Out of scope SDK client module caching — follow-up tracked in [twentyhq/core-team-issues#2688](https://github.com/twentyhq/core-team-issues/issues/2688), requires content-addressed URLs (server-side checksum at SDK generation time, exposed via GraphQL and embedded in the `/rest/sdk-client/...` URL), then reuses this same host-side cache path. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22958?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. -->