|
1 | 1 | import {deepEqual} from 'fast-equals'; |
2 | 2 | import type {ValueOf} from 'type-fest'; |
3 | | -import lodashPick from 'lodash/pick'; |
4 | 3 | import _ from 'underscore'; |
5 | 4 | import DevTools from './DevTools'; |
6 | 5 | import * as Logger from './Logger'; |
@@ -103,6 +102,8 @@ const deferredInitTask = createDeferredTask(); |
103 | 102 |
|
104 | 103 | // Holds a set of collection member IDs which updates will be ignored when using Onyx methods. |
105 | 104 | let skippableCollectionMemberIDs = new Set<string>(); |
| 105 | +// Holds a set of keys that should always be merged into snapshot entries. |
| 106 | +let snapshotMergeKeys = new Set<string>(); |
106 | 107 |
|
107 | 108 | function getSnapshotKey(): OnyxKey | null { |
108 | 109 | return snapshotKey; |
@@ -143,13 +144,27 @@ function getSkippableCollectionMemberIDs(): Set<string> { |
143 | 144 | return skippableCollectionMemberIDs; |
144 | 145 | } |
145 | 146 |
|
| 147 | +/** |
| 148 | + * Getter - returns the snapshot merge keys allowlist. |
| 149 | + */ |
| 150 | +function getSnapshotMergeKeys(): Set<string> { |
| 151 | + return snapshotMergeKeys; |
| 152 | +} |
| 153 | + |
146 | 154 | /** |
147 | 155 | * Setter - sets the skippable collection member IDs. |
148 | 156 | */ |
149 | 157 | function setSkippableCollectionMemberIDs(ids: Set<string>): void { |
150 | 158 | skippableCollectionMemberIDs = ids; |
151 | 159 | } |
152 | 160 |
|
| 161 | +/** |
| 162 | + * Setter - sets the snapshot merge keys allowlist. |
| 163 | + */ |
| 164 | +function setSnapshotMergeKeys(keys: Set<string>): void { |
| 165 | + snapshotMergeKeys = keys; |
| 166 | +} |
| 167 | + |
153 | 168 | /** |
154 | 169 | * Sets the initial values for the Onyx store |
155 | 170 | * |
@@ -1234,7 +1249,15 @@ function updateSnapshots<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>, me |
1234 | 1249 | } |
1235 | 1250 |
|
1236 | 1251 | const oldValue = updatedData[key] || {}; |
1237 | | - const newValue = lodashPick(value, Object.keys(snapshotData[key])); |
| 1252 | + |
| 1253 | + // Snapshot entries are stored as a "shape" of the last known data per key, so by default we only |
| 1254 | + // merge fields that already exist in the snapshot to avoid unintentionally bloating snapshot data. |
| 1255 | + // Some clients need specific fields (like pending status) even when they are missing in the snapshot, |
| 1256 | + // so we allow an explicit, opt-in list of keys to always include during snapshot merges. |
| 1257 | + const snapshotExistingKeys = Object.keys(snapshotData[key] || {}); |
| 1258 | + const allowedNewKeys = getSnapshotMergeKeys(); |
| 1259 | + const keysToCopy = new Set([...snapshotExistingKeys, ...allowedNewKeys]); |
| 1260 | + const newValue = typeof value === 'object' && value !== null ? utils.pick(value as Record<string, unknown>, [...keysToCopy]) : {}; |
1238 | 1261 |
|
1239 | 1262 | updatedData = {...updatedData, [key]: Object.assign(oldValue, newValue)}; |
1240 | 1263 | } |
@@ -1704,6 +1727,8 @@ const OnyxUtils = { |
1704 | 1727 | unsubscribeFromKey, |
1705 | 1728 | getSkippableCollectionMemberIDs, |
1706 | 1729 | setSkippableCollectionMemberIDs, |
| 1730 | + getSnapshotMergeKeys, |
| 1731 | + setSnapshotMergeKeys, |
1707 | 1732 | storeKeyBySubscriptions, |
1708 | 1733 | deleteKeyBySubscriptions, |
1709 | 1734 | addKeyToRecentlyAccessedIfNeeded, |
|
0 commit comments