fix(config): prevent config.patch from destroying arrays when patch entries lack id (#18030)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a857df9e32
Co-authored-by: stakeswky <64798754+stakeswky@users.noreply.github.com>
Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com>
Reviewed-by: @sebslight
This commit is contained in:
不做了睡大觉
2026-02-16 21:13:51 +08:00
committed by GitHub
parent 3a277e394e
commit cb391f4bdc
4 changed files with 130 additions and 7 deletions

View File

@@ -14,23 +14,34 @@ function isObjectWithStringId(value: unknown): value is Record<string, unknown>
}
function mergeObjectArraysById(base: unknown[], patch: unknown[], options: MergePatchOptions) {
if (!base.every(isObjectWithStringId) || !patch.every(isObjectWithStringId)) {
// Require all *base* entries to have string ids — if the existing array
// isn't id-keyed there's nothing sensible to merge against.
if (!base.every(isObjectWithStringId)) {
return undefined;
}
const merged = [...base] as Array<Record<string, unknown> & { id: string }>;
const indexById = new Map<string, number>();
for (const [index, entry] of merged.entries()) {
indexById.set(entry.id, index);
}
for (const entry of patch) {
const existingIndex = indexById.get(entry.id);
if (existingIndex === undefined) {
merged.push(structuredClone(entry));
indexById.set(entry.id, merged.length - 1);
for (const patchEntry of patch) {
// Patch entries without a valid id are appended as-is (best-effort).
// This prevents the entire merge from falling back to full replacement
// just because one patch element is missing an id field.
if (!isObjectWithStringId(patchEntry)) {
merged.push(structuredClone(patchEntry) as Record<string, unknown> & { id: string });
continue;
}
merged[existingIndex] = applyMergePatch(merged[existingIndex], entry, options) as Record<
const existingIndex = indexById.get(patchEntry.id);
if (existingIndex === undefined) {
merged.push(structuredClone(patchEntry));
indexById.set(patchEntry.id, merged.length - 1);
continue;
}
merged[existingIndex] = applyMergePatch(merged[existingIndex], patchEntry, options) as Record<
string,
unknown
> & { id: string };