@@ -199,34 +199,51 @@ export class DataConnectApiClient {
199199 return new FirebaseDataConnectError ( code , message ) ;
200200 }
201201
202- /**
203- * Converts JSON data into a GraphQL literal string.
204- * Handles nested objects, arrays, strings, numbers, and booleans.
205- * Ensures strings are properly escaped.
206- */
207- private objectToString ( data : any ) : string {
208- if ( typeof data !== 'object' || data === null ) {
209- if ( typeof data === 'string' ) {
210- // Properly escape double quotes and backslashes within strings
211- const escapedString = data . replace ( / \/ / g, '\\' ) . replace ( / , " / g , '"' ) ;
212- return `"${ escapedString } "` ;
213- }
214- // Handle numbers, booleans, null directly
202+ /**
203+ * Converts JSON data into a GraphQL literal string.
204+ * Handles nested objects, arrays, strings, numbers, and booleans.
205+ * Ensures strings are properly escaped.
206+ */
207+ private objectToString ( data : any ) : string {
208+ if ( typeof data === 'string' ) {
209+ const escapedString = data
210+ . replace ( / \\ / g , '\\\\' ) // Replace \ with \\
211+ . replace ( / " / g, '\\"' ) ; // Replace " with \"
212+ return `"${ escapedString } "` ;
213+ }
214+ if ( typeof data === 'number' || typeof data === 'boolean' || data === null ) {
215215 return String ( data ) ;
216216 }
217-
218217 if ( validator . isArray ( data ) ) {
219218 const elements = data . map ( item => this . objectToString ( item ) ) . join ( ', ' ) ;
220219 return `[${ elements } ]` ;
221220 }
221+ if ( typeof data === 'object' && data !== null ) {
222+ // Filter out properties where the value is undefined BEFORE mapping
223+ const kvPairs = Object . entries ( data )
224+ . filter ( ( [ , val ] ) => val !== undefined )
225+ . map ( ( [ key , val ] ) => {
226+ // GraphQL object keys are typically unquoted.
227+ return `${ key } : ${ this . objectToString ( val ) } ` ;
228+ } ) ;
229+
230+ if ( kvPairs . length === 0 ) {
231+ return '{}' ; // Represent an object with no defined properties as {}
232+ }
233+ return `{ ${ kvPairs . join ( ', ' ) } }` ;
234+ }
235+
236+ // If value is undefined (and not an object property, which is handled above,
237+ // e.g., if objectToString(undefined) is called directly or for an array element)
238+ // it should be represented as 'null'.
239+ if ( typeof data === 'undefined' ) {
240+ return 'null' ;
241+ }
222242
223- // Handle plain objects
224- const entries = Object . entries ( data ) . map ( ( [ key , value ] ) => {
225- // GraphQL object keys are typically unquoted identifiers
226- return `${ key } : ${ this . objectToString ( value ) } ` ;
227- } ) ;
228-
229- return `{ ${ entries . join ( ', ' ) } }` ;
243+ // Fallback for any other types (e.g., Symbol, BigInt - though less common in GQL contexts)
244+ // Consider how these should be handled or if an error should be thrown.
245+ // For now, simple string conversion.
246+ return String ( data ) ;
230247 }
231248
232249 /**
@@ -239,13 +256,13 @@ export class DataConnectApiClient {
239256 if ( ! validator . isNonEmptyString ( tableName ) ) {
240257 throw new FirebaseDataConnectError ( 'invalid-argument' , '`tableName` must be a non-empty string.' ) ;
241258 }
242- if ( ! validator . isNonNullObject ( data ) ) {
243- throw new FirebaseDataConnectError ( 'invalid-argument' , '`data` must be a non-null object.' ) ;
244- }
245259 if ( validator . isArray ( data ) ) {
246260 throw new FirebaseDataConnectError (
247261 'invalid-argument' , '`data` must be an object, not an array, for single insert.' ) ;
248262 }
263+ if ( ! validator . isNonNullObject ( data ) ) {
264+ throw new FirebaseDataConnectError ( 'invalid-argument' , '`data` must be a non-null object.' ) ;
265+ }
249266
250267 try {
251268 const gqlDataString = this . objectToString ( data ) ;
@@ -291,13 +308,13 @@ export class DataConnectApiClient {
291308 if ( ! validator . isNonEmptyString ( tableName ) ) {
292309 throw new FirebaseDataConnectError ( 'invalid-argument' , '`tableName` must be a non-empty string.' ) ;
293310 }
294- if ( ! validator . isNonNullObject ( data ) ) {
295- throw new FirebaseDataConnectError ( 'invalid-argument' , '`data` must be a non-null object.' ) ;
296- }
297311 if ( validator . isArray ( data ) ) {
298312 throw new FirebaseDataConnectError (
299313 'invalid-argument' , '`data` must be an object, not an array, for single upsert.' ) ;
300314 }
315+ if ( ! validator . isNonNullObject ( data ) ) {
316+ throw new FirebaseDataConnectError ( 'invalid-argument' , '`data` must be a non-null object.' ) ;
317+ }
301318
302319 try {
303320 const gqlDataString = this . objectToString ( data ) ;
0 commit comments