From 2056c8592e7c556d6b7710d675e33ba2d19eb821 Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Mon, 23 Mar 2026 21:42:38 +0100 Subject: [PATCH 1/5] Fix crash when Firestore document contains null fields --- lib/src/firestore-to-proto.spec.ts | 14 ++++++++++++++ lib/src/firestore-to-proto.ts | 2 ++ 2 files changed, 16 insertions(+) diff --git a/lib/src/firestore-to-proto.spec.ts b/lib/src/firestore-to-proto.spec.ts index 45d392009..1128f2ca4 100644 --- a/lib/src/firestore-to-proto.spec.ts +++ b/lib/src/firestore-to-proto.spec.ts @@ -110,6 +110,20 @@ describe('toMessage()', () => { }, }), }, + { + desc: 'skips null nested message', + input: { + '4': null, + }, + expected: new Job(), + }, + { + desc: 'skips null map field', + input: { + '4': null, + }, + expected: new Survey(), + }, { desc: 'converts enum value', input: { diff --git a/lib/src/firestore-to-proto.ts b/lib/src/firestore-to-proto.ts index cc8309e14..65bbd4c29 100644 --- a/lib/src/firestore-to-proto.ts +++ b/lib/src/firestore-to-proto.ts @@ -96,6 +96,7 @@ function toMapValue( valueType: string, nestedObject: [key: string] ): any | null { + if (!nestedObject) return null; const messageMap: { [key: string]: any } = {}; for (const key in nestedObject) { const firestoreValue = nestedObject[key]; @@ -151,6 +152,7 @@ function toMessageOrEnumValue( if (constructor && nestedDescriptor) { if (!nestedDescriptor) return Error(`Unknown message type ${nestedDescriptor}`); + if (firestoreValue === null || firestoreValue === undefined) return null; // TODO: Check firestoreValue is a DocumentData. return toMessageInternal( firestoreValue as DocumentData, From 5a2fe71ea7dcf5ce79618825165cbddfbd61b918 Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Mon, 23 Mar 2026 21:46:13 +0100 Subject: [PATCH 2/5] removed unused code --- lib/src/firestore-to-proto.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/firestore-to-proto.ts b/lib/src/firestore-to-proto.ts index 65bbd4c29..41d1fc8a0 100644 --- a/lib/src/firestore-to-proto.ts +++ b/lib/src/firestore-to-proto.ts @@ -150,8 +150,6 @@ function toMessageOrEnumValue( ? registry.getMessageDescriptor(constructor) : null; if (constructor && nestedDescriptor) { - if (!nestedDescriptor) - return Error(`Unknown message type ${nestedDescriptor}`); if (firestoreValue === null || firestoreValue === undefined) return null; // TODO: Check firestoreValue is a DocumentData. return toMessageInternal( From 896a5c84d01c4c47cf7de911ab00db186d859e1e Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Mon, 23 Mar 2026 21:47:09 +0100 Subject: [PATCH 3/5] added missing undefined check --- lib/src/firestore-to-proto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/firestore-to-proto.ts b/lib/src/firestore-to-proto.ts index 41d1fc8a0..2a2ec15a1 100644 --- a/lib/src/firestore-to-proto.ts +++ b/lib/src/firestore-to-proto.ts @@ -77,7 +77,7 @@ function toMessageValue( if (!descriptor.fields) return null; const field = descriptor.fields[fieldName]; const fieldType = field?.type; - if (field.keyType) { + if (field?.keyType) { if (field.keyType !== 'string') return Error(`${field.keyType} map keys not supported`); // TODO: Check that firestoreValue is an object. From e7dea3ffd3b42d7d8f65cf8454bfd9ce8e18cac1 Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Tue, 24 Mar 2026 17:37:45 +0100 Subject: [PATCH 4/5] check if firestoreValue is an object before passing it to toMapValue --- lib/src/firestore-to-proto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/firestore-to-proto.ts b/lib/src/firestore-to-proto.ts index 2a2ec15a1..52fc9a4c4 100644 --- a/lib/src/firestore-to-proto.ts +++ b/lib/src/firestore-to-proto.ts @@ -80,7 +80,8 @@ function toMessageValue( if (field?.keyType) { if (field.keyType !== 'string') return Error(`${field.keyType} map keys not supported`); - // TODO: Check that firestoreValue is an object. + if (typeof firestoreValue !== 'object' || Array.isArray(firestoreValue)) + return Error(`Expected object, but got ${firestoreValue}`); return toMapValue(messageTypePath, fieldType, firestoreValue); } else if (field?.rule === 'repeated') { if (!Array.isArray(firestoreValue)) From fdf8519b32d1a8d4c5d7ab74fa1f905f46efa21c Mon Sep 17 00:00:00 2001 From: Roberto Fontanarosa Date: Tue, 24 Mar 2026 17:37:57 +0100 Subject: [PATCH 5/5] added more tests --- lib/src/firestore-to-proto.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/src/firestore-to-proto.spec.ts b/lib/src/firestore-to-proto.spec.ts index 1128f2ca4..f3182d465 100644 --- a/lib/src/firestore-to-proto.spec.ts +++ b/lib/src/firestore-to-proto.spec.ts @@ -124,6 +124,20 @@ describe('toMessage()', () => { }, expected: new Survey(), }, + { + desc: 'skips non-object map field', + input: { + '4': 'not an object', + }, + expected: new Survey(), + }, + { + desc: 'skips array map field', + input: { + '4': [1, 2, 3], + }, + expected: new Survey(), + }, { desc: 'converts enum value', input: {