@@ -26,6 +26,8 @@ class _TypeCheckers {
2626 static final pubsubNamespace = TypeChecker .fromRuntime (ff.PubSubNamespace );
2727 static final firestoreNamespace =
2828 TypeChecker .fromRuntime (ff.FirestoreNamespace );
29+ static final databaseNamespace =
30+ TypeChecker .fromRuntime (ff.DatabaseNamespace );
2931}
3032
3133/// The main builder that generates functions.yaml.
@@ -132,6 +134,16 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
132134 }
133135 }
134136
137+ // Check for Database function declarations
138+ if (target != null && _isDatabaseNamespace (target)) {
139+ if (methodName == 'onValueCreated' ||
140+ methodName == 'onValueUpdated' ||
141+ methodName == 'onValueDeleted' ||
142+ methodName == 'onValueWritten' ) {
143+ _extractDatabaseFunction (node, methodName);
144+ }
145+ }
146+
135147 // Check for parameter definitions (top-level function calls with no target)
136148 if (target == null && _isParamDefinition (methodName)) {
137149 _extractParameterFromMethod (node, methodName);
@@ -236,6 +248,13 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
236248 return _TypeCheckers .firestoreNamespace.isExactlyType (staticType);
237249 }
238250
251+ /// Checks if the target is firebase.database.
252+ bool _isDatabaseNamespace (Expression target) {
253+ final staticType = target.staticType;
254+ if (staticType == null ) return false ;
255+ return _TypeCheckers .databaseNamespace.isExactlyType (staticType);
256+ }
257+
239258 /// Checks if this is a parameter definition function.
240259 bool _isParamDefinition (String name) =>
241260 name == 'defineString' ||
@@ -336,6 +355,44 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
336355 );
337356 }
338357
358+ /// Extracts a Database function declaration.
359+ void _extractDatabaseFunction (MethodInvocation node, String methodName) {
360+ // Extract ref path from named argument
361+ final refArg = _findNamedArg (node, 'ref' );
362+ if (refArg == null ) return ;
363+
364+ final refPath = _extractStringLiteral (refArg);
365+ if (refPath == null ) return ;
366+
367+ // Extract options if present (for instance)
368+ final optionsArg = _findNamedArg (node, 'options' );
369+ String ? instance;
370+
371+ if (optionsArg is InstanceCreationExpression ) {
372+ instance = _extractStringField (optionsArg, 'instance' );
373+ }
374+
375+ // Generate function name from ref path and event type
376+ // Similar to how we do it in database_namespace.dart
377+ final sanitizedPath = refPath
378+ .replaceAll (RegExp (r'^/+|/+$' ), '' ) // Remove leading/trailing slashes
379+ .replaceAll ('/' , '_' )
380+ .replaceAll ('{' , '' )
381+ .replaceAll ('}' , '' )
382+ .replaceAll ('-' , '' );
383+ final functionName = '${methodName }_$sanitizedPath ' ;
384+
385+ endpoints[functionName] = _EndpointSpec (
386+ name: functionName,
387+ type: 'database' ,
388+ databaseEventType: methodName,
389+ refPath: refPath,
390+ instance: instance ?? '*' ,
391+ options: optionsArg is InstanceCreationExpression ? optionsArg : null ,
392+ variableToParamName: _variableToParamName,
393+ );
394+ }
395+
339396 /// Extracts a parameter definition from FunctionExpressionInvocation.
340397 void _extractParameter (
341398 FunctionExpressionInvocation node,
@@ -538,16 +595,22 @@ class _EndpointSpec {
538595 this .documentPath,
539596 this .database,
540597 this .namespace,
598+ this .databaseEventType,
599+ this .refPath,
600+ this .instance,
541601 this .options,
542602 this .variableToParamName = const {},
543603 });
544604 final String name;
545- final String type; // 'https', 'callable', 'pubsub', 'firestore'
605+ final String type; // 'https', 'callable', 'pubsub', 'firestore', 'database'
546606 final String ? topic; // For Pub/Sub functions
547607 final String ? firestoreEventType; // For Firestore: onDocumentCreated, etc.
548608 final String ? documentPath; // For Firestore: users/{userId}
549609 final String ? database; // For Firestore: (default) or database name
550610 final String ? namespace; // For Firestore: (default) or namespace
611+ final String ? databaseEventType; // For Database: onValueCreated, etc.
612+ final String ? refPath; // For Database: /users/{userId}
613+ final String ? instance; // For Database: database instance or '*'
551614 final InstanceCreationExpression ? options;
552615 final Map <String , String > variableToParamName;
553616
@@ -1261,6 +1324,27 @@ String _generateYaml(
12611324 buffer.writeln (' document: "${endpoint .documentPath }"' );
12621325 }
12631326
1327+ buffer.writeln (' retry: false' );
1328+ } else if (endpoint.type == 'database' &&
1329+ endpoint.databaseEventType != null &&
1330+ endpoint.refPath != null ) {
1331+ // Map Dart method name to Database CloudEvent type
1332+ final eventType = _mapDatabaseEventType (endpoint.databaseEventType! );
1333+
1334+ buffer.writeln (' eventTrigger:' );
1335+ buffer.writeln (' eventType: "$eventType "' );
1336+ // Database triggers use empty eventFilters
1337+ buffer.writeln (' eventFilters: {}' );
1338+
1339+ // Both ref and instance go in eventFilterPathPatterns
1340+ // The ref path should not have a leading slash to match Node.js format
1341+ final normalizedRef = endpoint.refPath! .startsWith ('/' )
1342+ ? endpoint.refPath! .substring (1 )
1343+ : endpoint.refPath! ;
1344+ buffer.writeln (' eventFilterPathPatterns:' );
1345+ buffer.writeln (' ref: "$normalizedRef "' );
1346+ buffer.writeln (' instance: "${endpoint .instance ?? '*' }"' );
1347+
12641348 buffer.writeln (' retry: false' );
12651349 }
12661350 }
@@ -1278,6 +1362,15 @@ String _mapFirestoreEventType(String methodName) => switch (methodName) {
12781362 _ => throw ArgumentError ('Unknown Firestore event type: $methodName ' ),
12791363 };
12801364
1365+ /// Maps Database method name to CloudEvent event type.
1366+ String _mapDatabaseEventType (String methodName) => switch (methodName) {
1367+ 'onValueCreated' => 'google.firebase.database.ref.v1.created' ,
1368+ 'onValueUpdated' => 'google.firebase.database.ref.v1.updated' ,
1369+ 'onValueDeleted' => 'google.firebase.database.ref.v1.deleted' ,
1370+ 'onValueWritten' => 'google.firebase.database.ref.v1.written' ,
1371+ _ => throw ArgumentError ('Unknown Database event type: $methodName ' ),
1372+ };
1373+
12811374/// Converts a value to YAML format.
12821375String _yamlValue (dynamic value) {
12831376 if (value is String ) {
0 commit comments