fix: prevent localStorage bloat from derived fields on mock metadata (#18809)
## Summary - On unauthenticated pages (login), mocked object metadata was being hydrated into the store with `readableFields` and `updatableFields` attached. These derived arrays duplicate every field per object, inflating `objectMetadataItems` in localStorage from ~70 KB to ~2 MB. Combined with other metadata keys, this exceeded Safari's 5 MB quota and caused `QuotaExceededError`, preventing real data from replacing mock data on login. - Extracted flat mock data into a dedicated file (`generatedMockObjectMetadataItemsWithRelated.ts`) and use it for store hydration, keeping the enriched version (`generatedMockObjectMetadataItems`) only for tests. - Completed `splitObjectMetadataItemWithRelated`'s contract by stripping `readableFields` and `updatableFields` at runtime (not just via TypeScript's `Omit`), matching the `FlatObjectMetadataItem` return type that omits all four relational properties. ## Root cause 1. `MinimalMetadataLoadEffect` loads mocked metadata on unauthenticated pages. 2. `generatedMockObjectMetadataItems` was produced by `enrichObjectMetadataItemsWithPermissions`, which attaches `readableFields` and `updatableFields` (full copies of the `fields` array). 3. `splitObjectMetadataItemWithRelated` only destructured `fields` and `indexMetadatas` — `readableFields`/`updatableFields` leaked into `...objectProperties` at runtime because `Omit` is a compile-time-only guard. 4. These bloated objects were written to localStorage, consuming ~2 MB instead of ~70 KB. 5. On login, the intermediate state (old composite mock `current` + new flat real `draft`) exceeded the 5 MB quota, causing a `QuotaExceededError` deadlock. ## Test plan - [ ] Open the app in a fresh Safari private window (empty localStorage) - [ ] Verify the login page loads without errors - [ ] Log in and verify metadata loads correctly - [ ] Check `localStorage` size — `metadataStoreState__objectMetadataItems` should be ~70 KB, not ~2 MB - [ ] Run existing tests: `npx nx test twenty-front` — no regressions from the mock data split Made with [Cursor](https://cursor.com) --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+5
-3
@@ -11,11 +11,11 @@ export const useLoadMockedMinimalMetadata = () => {
|
||||
const loadMockedMinimalMetadata = useCallback(async () => {
|
||||
resetMetadataStore();
|
||||
const [
|
||||
{ generatedMockObjectMetadataItems },
|
||||
{ generatedMockObjectMetadataItemsWithRelated },
|
||||
{ mockedViews },
|
||||
{ mockedNavigationMenuItems },
|
||||
] = await Promise.all([
|
||||
import('~/testing/utils/generatedMockObjectMetadataItems'),
|
||||
import('~/testing/utils/generatedMockObjectMetadataItemsWithRelated'),
|
||||
import('~/testing/mock-data/generated/metadata/views/mock-views-data'),
|
||||
import(
|
||||
'~/testing/mock-data/generated/metadata/navigation-menu-items/mock-navigation-menu-items-data'
|
||||
@@ -23,7 +23,9 @@ export const useLoadMockedMinimalMetadata = () => {
|
||||
]);
|
||||
|
||||
const { flatObjects, flatFields, flatIndexes } =
|
||||
splitObjectMetadataItemWithRelated(generatedMockObjectMetadataItems);
|
||||
splitObjectMetadataItemWithRelated(
|
||||
generatedMockObjectMetadataItemsWithRelated,
|
||||
);
|
||||
|
||||
replaceDraft('objectMetadataItems', flatObjects, MOCKED_COLLECTION_HASH);
|
||||
replaceDraft('fieldMetadataItems', flatFields, MOCKED_COLLECTION_HASH);
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { enrichObjectMetadataItemsWithPermissions } from '@/object-metadata/utils/enrichObjectMetadataItemsWithPermissions';
|
||||
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
||||
import { type ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/metadata/objects/mock-objects-metadata';
|
||||
import { generatedMockObjectMetadataItemsWithRelated } from '~/testing/utils/generatedMockObjectMetadataItemsWithRelated';
|
||||
|
||||
export const generatedMockObjectMetadataItems: ObjectMetadataItem[] =
|
||||
enrichObjectMetadataItemsWithPermissions({
|
||||
objectMetadataItems: mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems:
|
||||
mockedStandardObjectMetadataQueryResult as unknown as ObjectMetadataItemsQuery,
|
||||
}),
|
||||
objectMetadataItems: generatedMockObjectMetadataItemsWithRelated,
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { type ObjectMetadataItemWithRelated } from '@/metadata-store/utils/splitObjectMetadataItemWithRelated';
|
||||
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
||||
|
||||
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/metadata/objects/mock-objects-metadata';
|
||||
|
||||
export const generatedMockObjectMetadataItemsWithRelated: ObjectMetadataItemWithRelated[] =
|
||||
mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems: mockedStandardObjectMetadataQueryResult,
|
||||
});
|
||||
Reference in New Issue
Block a user