firebase-tools 14.14.0 → 14.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/lib/apphosting/backend.js +40 -15
  2. package/lib/auth.js +37 -2
  3. package/lib/commands/apphosting-backends-create.js +8 -5
  4. package/lib/commands/dataconnect-sdk-generate.js +3 -5
  5. package/lib/commands/dataconnect-sql-diff.js +2 -2
  6. package/lib/commands/dataconnect-sql-grant.js +2 -2
  7. package/lib/commands/dataconnect-sql-migrate.js +2 -2
  8. package/lib/commands/dataconnect-sql-setup.js +2 -2
  9. package/lib/commands/dataconnect-sql-shell.js +2 -2
  10. package/lib/commands/init.js +3 -0
  11. package/lib/commands/login.js +12 -7
  12. package/lib/crashlytics/addNote.js +27 -0
  13. package/lib/crashlytics/deleteNote.js +23 -0
  14. package/lib/crashlytics/getIssueDetails.js +5 -20
  15. package/lib/crashlytics/getSampleCrash.js +6 -20
  16. package/lib/crashlytics/listNotes.js +29 -0
  17. package/lib/crashlytics/listTopDevices.js +33 -0
  18. package/lib/crashlytics/listTopIssues.js +8 -23
  19. package/lib/crashlytics/listTopOperatingSystems.js +32 -0
  20. package/lib/crashlytics/listTopVersions.js +32 -0
  21. package/lib/crashlytics/updateIssue.js +35 -0
  22. package/lib/crashlytics/utils.js +38 -0
  23. package/lib/dataconnect/appFinder.js +103 -0
  24. package/lib/dataconnect/load.js +105 -6
  25. package/lib/deploy/dataconnect/prepare.js +1 -3
  26. package/lib/emulator/controller.js +2 -2
  27. package/lib/emulator/downloadableEmulatorInfo.json +17 -17
  28. package/lib/init/features/dataconnect/create_app.js +48 -0
  29. package/lib/init/features/dataconnect/index.js +19 -30
  30. package/lib/init/features/dataconnect/sdk.js +218 -161
  31. package/lib/init/features/index.js +3 -2
  32. package/lib/init/index.js +5 -1
  33. package/lib/management/apps.js +3 -3
  34. package/lib/mcp/prompts/core/deploy.js +51 -8
  35. package/lib/mcp/prompts/crashlytics/common.js +10 -0
  36. package/lib/mcp/prompts/crashlytics/fix_issue.js +89 -0
  37. package/lib/mcp/prompts/crashlytics/index.js +6 -0
  38. package/lib/mcp/prompts/crashlytics/prioritize_issues.js +79 -0
  39. package/lib/mcp/prompts/index.js +3 -2
  40. package/lib/mcp/tools/core/init.js +5 -1
  41. package/lib/mcp/tools/crashlytics/add_note.js +32 -0
  42. package/lib/mcp/tools/crashlytics/constants.js +11 -0
  43. package/lib/mcp/tools/crashlytics/delete_note.js +35 -0
  44. package/lib/mcp/tools/crashlytics/get_issue_details.js +2 -4
  45. package/lib/mcp/tools/crashlytics/get_sample_crash.js +4 -4
  46. package/lib/mcp/tools/crashlytics/index.js +16 -2
  47. package/lib/mcp/tools/crashlytics/list_notes.js +37 -0
  48. package/lib/mcp/tools/crashlytics/list_top_devices.js +33 -0
  49. package/lib/mcp/tools/crashlytics/list_top_issues.js +5 -7
  50. package/lib/mcp/tools/crashlytics/list_top_operating_systems.js +33 -0
  51. package/lib/mcp/tools/crashlytics/list_top_versions.js +33 -0
  52. package/lib/mcp/tools/crashlytics/update_issue.js +37 -0
  53. package/lib/mcp/tools/dataconnect/execute_graphql.js +2 -2
  54. package/lib/mcp/tools/dataconnect/execute_graphql_read.js +2 -2
  55. package/lib/mcp/tools/dataconnect/execute_mutation.js +2 -2
  56. package/lib/mcp/tools/dataconnect/execute_query.js +2 -2
  57. package/lib/mcp/tools/dataconnect/generate_operation.js +2 -2
  58. package/lib/mcp/tools/dataconnect/get_connector.js +2 -2
  59. package/lib/mcp/tools/dataconnect/get_schema.js +2 -2
  60. package/lib/utils.js +11 -1
  61. package/package.json +1 -1
  62. package/templates/init/dataconnect/connector.yaml +0 -16
  63. package/templates/init/dataconnect/dataconnect.yaml +2 -1
  64. package/templates/init/dataconnect/mutations.gql +29 -29
  65. package/templates/init/dataconnect/queries.gql +73 -73
  66. package/templates/init/dataconnect/schema.gql +48 -48
  67. package/lib/dataconnect/fileUtils.js +0 -168
@@ -5,7 +5,7 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const dataplane = require("../../../dataconnect/dataplaneClient");
8
- const fileUtils_1 = require("../../../dataconnect/fileUtils");
8
+ const load_1 = require("../../../dataconnect/load");
9
9
  const converter_1 = require("./converter");
10
10
  const emulator_1 = require("./emulator");
11
11
  exports.execute_mutation = (0, tool_1.tool)({
@@ -36,7 +36,7 @@ exports.execute_mutation = (0, tool_1.tool)({
36
36
  requiresAuth: true,
37
37
  },
38
38
  }, async ({ operationName, service_id, connector_id, variables: unparsedVariables, use_emulator }, { projectId, config, host }) => {
39
- const serviceInfo = await (0, fileUtils_1.pickService)(projectId, config, service_id || undefined);
39
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
40
40
  let apiClient;
41
41
  if (!connector_id) {
42
42
  if (serviceInfo.connectorInfo.length === 0) {
@@ -5,7 +5,7 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const dataplane = require("../../../dataconnect/dataplaneClient");
8
- const fileUtils_1 = require("../../../dataconnect/fileUtils");
8
+ const load_1 = require("../../../dataconnect/load");
9
9
  const converter_1 = require("./converter");
10
10
  const emulator_1 = require("./emulator");
11
11
  exports.execute_query = (0, tool_1.tool)({
@@ -36,7 +36,7 @@ exports.execute_query = (0, tool_1.tool)({
36
36
  requiresAuth: true,
37
37
  },
38
38
  }, async ({ operationName, service_id, connector_id, variables: unparsedVariables, use_emulator }, { projectId, config, host }) => {
39
- const serviceInfo = await (0, fileUtils_1.pickService)(projectId, config, service_id || undefined);
39
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
40
40
  let apiClient;
41
41
  if (!connector_id) {
42
42
  if (serviceInfo.connectorInfo.length === 0) {
@@ -5,7 +5,7 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const fdcExperience_1 = require("../../../gemini/fdcExperience");
8
- const fileUtils_1 = require("../../../dataconnect/fileUtils");
8
+ const load_1 = require("../../../dataconnect/load");
9
9
  exports.generate_operation = (0, tool_1.tool)({
10
10
  name: "generate_operation",
11
11
  description: "Generates a single Firebase Data Connect query or mutation based on the currently deployed schema and the provided prompt.",
@@ -28,7 +28,7 @@ exports.generate_operation = (0, tool_1.tool)({
28
28
  requiresGemini: true,
29
29
  },
30
30
  }, async ({ prompt, service_id }, { projectId, config }) => {
31
- const serviceInfo = await (0, fileUtils_1.pickService)(projectId, config, service_id || undefined);
31
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
32
32
  const schema = await (0, fdcExperience_1.generateOperation)(prompt, serviceInfo.serviceName, projectId);
33
33
  return (0, util_1.toContent)(schema);
34
34
  });
@@ -5,7 +5,7 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const client = require("../../../dataconnect/client");
8
- const fileUtils_1 = require("../../../dataconnect/fileUtils");
8
+ const load_1 = require("../../../dataconnect/load");
9
9
  const converter_1 = require("./converter");
10
10
  exports.get_connectors = (0, tool_1.tool)({
11
11
  name: "get_connectors",
@@ -25,7 +25,7 @@ exports.get_connectors = (0, tool_1.tool)({
25
25
  requiresAuth: true,
26
26
  },
27
27
  }, async ({ service_id }, { projectId, config }) => {
28
- const serviceInfo = await (0, fileUtils_1.pickService)(projectId, config, service_id || undefined);
28
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
29
29
  const connectors = await client.listConnectors(serviceInfo.serviceName, ["*"]);
30
30
  return (0, util_1.toContent)(connectors.map(converter_1.connectorToText).join("\n\n"));
31
31
  });
@@ -5,7 +5,7 @@ const zod_1 = require("zod");
5
5
  const tool_1 = require("../../tool");
6
6
  const util_1 = require("../../util");
7
7
  const client = require("../../../dataconnect/client");
8
- const fileUtils_1 = require("../../../dataconnect/fileUtils");
8
+ const load_1 = require("../../../dataconnect/load");
9
9
  const converter_1 = require("./converter");
10
10
  exports.get_schema = (0, tool_1.tool)({
11
11
  name: "get_schema",
@@ -25,7 +25,7 @@ exports.get_schema = (0, tool_1.tool)({
25
25
  requiresAuth: true,
26
26
  },
27
27
  }, async ({ service_id }, { projectId, config }) => {
28
- const serviceInfo = await (0, fileUtils_1.pickService)(projectId, config, service_id || undefined);
28
+ const serviceInfo = await (0, load_1.pickService)(projectId, config, service_id || undefined);
29
29
  const schemas = await client.listSchemas(serviceInfo.serviceName, ["*"]);
30
30
  return (0, util_1.toContent)(schemas === null || schemas === void 0 ? void 0 : schemas.map(converter_1.schemaToText).join("\n\n"));
31
31
  });
package/lib/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.openInBrowserPopup = exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.sleep = exports.promiseWithSpinner = exports.tryParse = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarningToStderr = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.setVSCodeEnvVars = exports.getInheritedOption = exports.consoleUrl = exports.vscodeEnvVars = exports.envOverrides = exports.IS_WINDOWS = void 0;
4
- exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generatePassword = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = void 0;
4
+ exports.newUniqueId = exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generatePassword = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = void 0;
5
5
  const fs = require("fs-extra");
6
6
  const tty = require("tty");
7
7
  const path = require("node:path");
@@ -647,3 +647,13 @@ function deepEqual(a, b) {
647
647
  return true;
648
648
  }
649
649
  exports.deepEqual = deepEqual;
650
+ function newUniqueId(recommended, existingIDs) {
651
+ let id = recommended;
652
+ let i = 1;
653
+ while (existingIDs.includes(id)) {
654
+ id = `${recommended}-${i}`;
655
+ i++;
656
+ }
657
+ return id;
658
+ }
659
+ exports.newUniqueId = newUniqueId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.14.0",
3
+ "version": "14.15.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -1,17 +1 @@
1
1
  connectorId: __connectorId__
2
- ## ## Here's an example of how to add generated SDKs.
3
- ## ## You'll need to replace the outputDirs with ones pointing to where you want the generated code in your app.
4
- # generate:
5
- # javascriptSdk:
6
- # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
7
- # package: "@firebasegen/my-connector"
8
- # packageJsonDir: < Optional. Path to your Javascript app's package.json>
9
- # swiftSdk:
10
- # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
11
- # package: DefaultConnector
12
- # kotlinSdk:
13
- # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
14
- # package: connectors.default
15
- # dartSdk:
16
- # outputDir: <Path where you want the generated SDK to be written to, relative to this file>
17
- # package: default_connector
@@ -8,5 +8,6 @@ schema:
8
8
  database: __cloudSqlDatabase__
9
9
  cloudSql:
10
10
  instanceId: __cloudSqlInstanceId__
11
- # schemaValidation: "COMPATIBLE"
11
+ # schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly.
12
+ # schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect.
12
13
  connectorDirs: __connectorDirs__
@@ -1,33 +1,33 @@
1
- # # Example mutations for a simple movie app
1
+ # Example mutations for a simple movie app
2
2
 
3
- # # Create a movie based on user input
4
- # mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!)
5
- # @auth(level: USER_EMAIL_VERIFIED, insecureReason: "Any email verified users can create a new movie.") {
6
- # movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl })
7
- # }
3
+ # Create a movie based on user input
4
+ mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!)
5
+ @auth(level: USER_EMAIL_VERIFIED, insecureReason: "Any email verified users can create a new movie.") {
6
+ movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl })
7
+ }
8
8
 
9
- # # Upsert (update or insert) a user's username based on their auth.uid
10
- # mutation UpsertUser($username: String!) @auth(level: USER) {
11
- # # The "auth.uid" server value ensures that users can only register their own user.
12
- # user_upsert(data: { id_expr: "auth.uid", username: $username })
13
- # }
9
+ # Upsert (update or insert) a user's username based on their auth.uid
10
+ mutation UpsertUser($username: String!) @auth(level: USER) {
11
+ # The "auth.uid" server value ensures that users can only register their own user.
12
+ user_upsert(data: { id_expr: "auth.uid", username: $username })
13
+ }
14
14
 
15
- # # Add a review for a movie
16
- # mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
17
- # @auth(level: USER) {
18
- # review_upsert(
19
- # data: {
20
- # userId_expr: "auth.uid"
21
- # movieId: $movieId
22
- # rating: $rating
23
- # reviewText: $reviewText
24
- # # reviewDate defaults to today in the schema. No need to set it manually.
25
- # }
26
- # )
27
- # }
15
+ # Add a review for a movie
16
+ mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
17
+ @auth(level: USER) {
18
+ review_upsert(
19
+ data: {
20
+ userId_expr: "auth.uid"
21
+ movieId: $movieId
22
+ rating: $rating
23
+ reviewText: $reviewText
24
+ # reviewDate defaults to today in the schema. No need to set it manually.
25
+ }
26
+ )
27
+ }
28
28
 
29
- # # Logged in user can delete their review for a movie
30
- # mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
31
- # # The "auth.uid" server value ensures that users can only delete their own reviews.
32
- # review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
33
- # }
29
+ # Logged in user can delete their review for a movie
30
+ mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
31
+ # The "auth.uid" server value ensures that users can only delete their own reviews.
32
+ review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
33
+ }
@@ -1,78 +1,78 @@
1
- # # Example queries for a simple movie app.
1
+ # Example queries for a simple movie app.
2
2
 
3
- # # @auth() directives control who can call each operation.
4
- # # Anyone should be able to list all movies, so the auth level is set to PUBLIC
5
- # query ListMovies @auth(level: PUBLIC, insecureReason: "Anyone can list all movies.") {
6
- # movies {
7
- # id
8
- # title
9
- # imageUrl
10
- # genre
11
- # }
12
- # }
3
+ # @auth() directives control who can call each operation.
4
+ # Anyone should be able to list all movies, so the auth level is set to PUBLIC
5
+ query ListMovies @auth(level: PUBLIC, insecureReason: "Anyone can list all movies.") {
6
+ movies {
7
+ id
8
+ title
9
+ imageUrl
10
+ genre
11
+ }
12
+ }
13
13
 
14
- # # List all users, only admins should be able to list all users, so we use NO_ACCESS
15
- # query ListUsers @auth(level: NO_ACCESS) {
16
- # users {
17
- # id
18
- # username
19
- # }
20
- # }
14
+ # List all users, only admins should be able to list all users, so we use NO_ACCESS
15
+ query ListUsers @auth(level: NO_ACCESS) {
16
+ users {
17
+ id
18
+ username
19
+ }
20
+ }
21
21
 
22
- # # Logged in users can list all their reviews and movie titles associated with the review
23
- # # Since the query uses the uid of the current authenticated user, we set auth level to USER
24
- # query ListUserReviews @auth(level: USER) {
25
- # user(key: { id_expr: "auth.uid" }) {
26
- # id
27
- # username
28
- # # <field>_on_<foreign_key_field> makes it easy to grab info from another table
29
- # # Here, we use it to grab all the reviews written by the user.
30
- # reviews: reviews_on_user {
31
- # rating
32
- # reviewDate
33
- # reviewText
34
- # movie {
35
- # id
36
- # title
37
- # }
38
- # }
39
- # }
40
- # }
22
+ # Logged in users can list all their reviews and movie titles associated with the review
23
+ # Since the query uses the uid of the current authenticated user, we set auth level to USER
24
+ query ListUserReviews @auth(level: USER) {
25
+ user(key: { id_expr: "auth.uid" }) {
26
+ id
27
+ username
28
+ # <field>_on_<foreign_key_field> makes it easy to grab info from another table
29
+ # Here, we use it to grab all the reviews written by the user.
30
+ reviews: reviews_on_user {
31
+ rating
32
+ reviewDate
33
+ reviewText
34
+ movie {
35
+ id
36
+ title
37
+ }
38
+ }
39
+ }
40
+ }
41
41
 
42
- # # Get movie by id
43
- # query GetMovieById($id: UUID!) @auth(level: PUBLIC, insecureReason: "Anyone can get a movie by id.") {
44
- # movie(id: $id) {
45
- # id
46
- # title
47
- # imageUrl
48
- # genre
49
- # metadata: movieMetadata_on_movie {
50
- # rating
51
- # releaseYear
52
- # description
53
- # }
54
- # reviews: reviews_on_movie {
55
- # reviewText
56
- # reviewDate
57
- # rating
58
- # user {
59
- # id
60
- # username
61
- # }
62
- # }
63
- # }
64
- # }
42
+ # Get movie by id
43
+ query GetMovieById($id: UUID!) @auth(level: PUBLIC, insecureReason: "Anyone can get a movie by id.") {
44
+ movie(id: $id) {
45
+ id
46
+ title
47
+ imageUrl
48
+ genre
49
+ metadata: movieMetadata_on_movie {
50
+ rating
51
+ releaseYear
52
+ description
53
+ }
54
+ reviews: reviews_on_movie {
55
+ reviewText
56
+ reviewDate
57
+ rating
58
+ user {
59
+ id
60
+ username
61
+ }
62
+ }
63
+ }
64
+ }
65
65
 
66
- # # Search for movies, actors, and reviews
67
- # query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC, insecureReason: "Anyone can search for movies.") {
68
- # movies(
69
- # where: {
70
- # _and: [{ genre: { eq: $genre } }, { title: { contains: $titleInput } }]
71
- # }
72
- # ) {
73
- # id
74
- # title
75
- # genre
76
- # imageUrl
77
- # }
78
- # }
66
+ # Search for movies, actors, and reviews
67
+ query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC, insecureReason: "Anyone can search for movies.") {
68
+ movies(
69
+ where: {
70
+ _and: [{ genre: { eq: $genre } }, { title: { contains: $titleInput } }]
71
+ }
72
+ ) {
73
+ id
74
+ title
75
+ genre
76
+ imageUrl
77
+ }
78
+ }
@@ -1,52 +1,52 @@
1
- # # Example schema for simple movie review app
1
+ # Example schema for simple movie review app
2
2
 
3
- # # User table is keyed by Firebase Auth UID.
4
- # type User @table {
5
- # # `@default(expr: "auth.uid")` sets it to Firebase Auth UID during insert and upsert.
6
- # id: String! @default(expr: "auth.uid")
7
- # username: String! @col(dataType: "varchar(50)")
8
- # # The `user: User!` field in the Review table generates the following one-to-many query field.
9
- # # reviews_on_user: [Review!]!
10
- # # The `Review` join table the following many-to-many query field.
11
- # # movies_via_Review: [Movie!]!
12
- # }
3
+ # User table is keyed by Firebase Auth UID.
4
+ type User @table {
5
+ # `@default(expr: "auth.uid")` sets it to Firebase Auth UID during insert and upsert.
6
+ id: String! @default(expr: "auth.uid")
7
+ username: String! @col(dataType: "varchar(50)")
8
+ # The `user: User!` field in the Review table generates the following one-to-many query field.
9
+ # reviews_on_user: [Review!]!
10
+ # The `Review` join table the following many-to-many query field.
11
+ # movies_via_Review: [Movie!]!
12
+ }
13
13
 
14
- # # Movie is keyed by a randomly generated UUID.
15
- # type Movie @table {
16
- # # If you do not pass a 'key' to `@table`, Data Connect automatically adds the following 'id' column.
17
- # # Feel free to uncomment and customize it.
18
- # # id: UUID! @default(expr: "uuidV4()")
19
- # title: String!
20
- # imageUrl: String!
21
- # genre: String
22
- # }
14
+ # Movie is keyed by a randomly generated UUID.
15
+ type Movie @table {
16
+ # If you do not pass a 'key' to `@table`, Data Connect automatically adds the following 'id' column.
17
+ # Feel free to uncomment and customize it.
18
+ # id: UUID! @default(expr: "uuidV4()")
19
+ title: String!
20
+ imageUrl: String!
21
+ genre: String
22
+ }
23
23
 
24
- # # MovieMetadata is a metadata attached to a Movie.
25
- # # Movie <-> MovieMetadata is a one-to-one relationship
26
- # type MovieMetadata @table {
27
- # # @unique ensures each Movie can only one MovieMetadata.
28
- # movie: Movie! @unique
29
- # # The movie field adds the following foreign key field. Feel free to uncomment and customize it.
30
- # # movieId: UUID!
31
- # rating: Float
32
- # releaseYear: Int
33
- # description: String
34
- # }
24
+ # MovieMetadata is a metadata attached to a Movie.
25
+ # Movie <-> MovieMetadata is a one-to-one relationship
26
+ type MovieMetadata @table {
27
+ # @unique ensures each Movie can only one MovieMetadata.
28
+ movie: Movie! @unique
29
+ # The movie field adds the following foreign key field. Feel free to uncomment and customize it.
30
+ # movieId: UUID!
31
+ rating: Float
32
+ releaseYear: Int
33
+ description: String
34
+ }
35
35
 
36
- # # Reviews is a join table between User and Movie.
37
- # # It has a composite primary keys `userUid` and `movieId`.
38
- # # A user can leave reviews for many movies. A movie can have reviews from many users.
39
- # # User <-> Review is a one-to-many relationship
40
- # # Movie <-> Review is a one-to-many relationship
41
- # # Movie <-> User is a many-to-many relationship
42
- # type Review @table(name: "Reviews", key: ["movie", "user"]) {
43
- # user: User!
44
- # # The user field adds the following foreign key field. Feel free to uncomment and customize it.
45
- # # userUid: String!
46
- # movie: Movie!
47
- # # The movie field adds the following foreign key field. Feel free to uncomment and customize it.
48
- # # movieId: UUID!
49
- # rating: Int
50
- # reviewText: String
51
- # reviewDate: Date! @default(expr: "request.time")
52
- # }
36
+ # Reviews is a join table between User and Movie.
37
+ # It has a composite primary keys `userUid` and `movieId`.
38
+ # A user can leave reviews for many movies. A movie can have reviews from many users.
39
+ # User <-> Review is a one-to-many relationship
40
+ # Movie <-> Review is a one-to-many relationship
41
+ # Movie <-> User is a many-to-many relationship
42
+ type Review @table(name: "Reviews", key: ["movie", "user"]) {
43
+ user: User!
44
+ # The user field adds the following foreign key field. Feel free to uncomment and customize it.
45
+ # userUid: String!
46
+ movie: Movie!
47
+ # The movie field adds the following foreign key field. Feel free to uncomment and customize it.
48
+ # movieId: UUID!
49
+ rating: Int
50
+ reviewText: String
51
+ reviewDate: Date! @default(expr: "request.time")
52
+ }
@@ -1,168 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFrameworksFromPackageJson = exports.frameworksMap = exports.SUPPORTED_FRAMEWORKS = exports.resolvePackageJson = exports.getPlatformFromFolder = exports.pickService = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = void 0;
4
- const fs = require("fs-extra");
5
- const path = require("path");
6
- const clc = require("colorette");
7
- const glob_1 = require("glob");
8
- const error_1 = require("../error");
9
- const types_1 = require("./types");
10
- const utils_1 = require("../utils");
11
- const load_1 = require("./load");
12
- function readFirebaseJson(config) {
13
- if (!(config === null || config === void 0 ? void 0 : config.has("dataconnect"))) {
14
- return [];
15
- }
16
- const validator = (cfg) => {
17
- if (!cfg["source"]) {
18
- throw new error_1.FirebaseError("Invalid firebase.json: DataConnect requires `source`");
19
- }
20
- return {
21
- source: cfg["source"],
22
- };
23
- };
24
- const configs = config.get("dataconnect");
25
- if (typeof configs === "object" && !Array.isArray(configs)) {
26
- return [validator(configs)];
27
- }
28
- else if (Array.isArray(configs)) {
29
- return configs.map(validator);
30
- }
31
- else {
32
- throw new error_1.FirebaseError("Invalid firebase.json: dataconnect should be of the form { source: string }");
33
- }
34
- }
35
- exports.readFirebaseJson = readFirebaseJson;
36
- async function readDataConnectYaml(sourceDirectory) {
37
- const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "dataconnect.yaml");
38
- const dataconnectYaml = await (0, utils_1.wrappedSafeLoad)(file.source);
39
- return validateDataConnectYaml(dataconnectYaml);
40
- }
41
- exports.readDataConnectYaml = readDataConnectYaml;
42
- function validateDataConnectYaml(unvalidated) {
43
- if (!unvalidated["location"]) {
44
- throw new error_1.FirebaseError("Missing required field 'location' in dataconnect.yaml");
45
- }
46
- return unvalidated;
47
- }
48
- async function readConnectorYaml(sourceDirectory) {
49
- const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "connector.yaml");
50
- const connectorYaml = await (0, utils_1.wrappedSafeLoad)(file.source);
51
- return validateConnectorYaml(connectorYaml);
52
- }
53
- exports.readConnectorYaml = readConnectorYaml;
54
- function validateConnectorYaml(unvalidated) {
55
- return unvalidated;
56
- }
57
- async function readGQLFiles(sourceDir) {
58
- if (!fs.existsSync(sourceDir)) {
59
- return [];
60
- }
61
- const files = await (0, glob_1.glob)("**/*.{gql,graphql}", { cwd: sourceDir, absolute: true, nodir: true });
62
- return files.map((f) => toFile(sourceDir, f));
63
- }
64
- exports.readGQLFiles = readGQLFiles;
65
- function toFile(sourceDir, fullPath) {
66
- const relPath = path.relative(sourceDir, fullPath);
67
- if (!fs.existsSync(fullPath)) {
68
- throw new error_1.FirebaseError(`file ${fullPath} not found`);
69
- }
70
- const content = fs.readFileSync(fullPath).toString();
71
- return {
72
- path: relPath,
73
- content,
74
- };
75
- }
76
- async function pickService(projectId, config, serviceId) {
77
- const serviceCfgs = readFirebaseJson(config);
78
- let serviceInfo;
79
- if (serviceCfgs.length === 0) {
80
- throw new error_1.FirebaseError("No Data Connect services found in firebase.json." +
81
- `\nYou can run ${clc.bold("firebase init dataconnect")} to add a Data Connect service.`);
82
- }
83
- else if (serviceCfgs.length === 1) {
84
- serviceInfo = await (0, load_1.load)(projectId, config, serviceCfgs[0].source);
85
- if (serviceId && serviceId !== serviceInfo.dataConnectYaml.serviceId) {
86
- throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${serviceInfo.dataConnectYaml.serviceId}.` +
87
- `\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
88
- }
89
- return serviceInfo;
90
- }
91
- else {
92
- if (!serviceId) {
93
- throw new error_1.FirebaseError("Multiple Data Connect services found in firebase.json. Please specify a service ID to use.");
94
- }
95
- const infos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, config, c.source)));
96
- const maybe = infos.find((i) => i.dataConnectYaml.serviceId === serviceId);
97
- if (!maybe) {
98
- throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${infos.map((i) => i.dataConnectYaml.serviceId).join(", ")}.` +
99
- `\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
100
- }
101
- return maybe;
102
- }
103
- }
104
- exports.pickService = pickService;
105
- const WEB_INDICATORS = ["package.json", "package-lock.json", "node_modules"];
106
- const IOS_INDICATORS = ["info.plist", "podfile", "package.swift", ".xcodeproj"];
107
- const ANDROID_INDICATORS = ["androidmanifest.xml", "build.gradle", "build.gradle.kts"];
108
- const DART_INDICATORS = ["pubspec.yaml", "pubspec.lock"];
109
- const IOS_POSTFIX_INDICATORS = [".xcworkspace", ".xcodeproj"];
110
- async function getPlatformFromFolder(dirPath) {
111
- const fileNames = await fs.readdir(dirPath);
112
- let hasWeb = false;
113
- let hasAndroid = false;
114
- let hasIOS = false;
115
- let hasDart = false;
116
- for (const fileName of fileNames) {
117
- const cleanedFileName = fileName.toLowerCase();
118
- hasWeb || (hasWeb = WEB_INDICATORS.some((indicator) => indicator === cleanedFileName));
119
- hasAndroid || (hasAndroid = ANDROID_INDICATORS.some((indicator) => indicator === cleanedFileName));
120
- hasIOS || (hasIOS = IOS_INDICATORS.some((indicator) => indicator === cleanedFileName) ||
121
- IOS_POSTFIX_INDICATORS.some((indicator) => cleanedFileName.endsWith(indicator)));
122
- hasDart || (hasDart = DART_INDICATORS.some((indicator) => indicator === cleanedFileName));
123
- }
124
- if (!hasWeb && !hasAndroid && !hasIOS && !hasDart) {
125
- return types_1.Platform.NONE;
126
- }
127
- else if (hasWeb && !hasAndroid && !hasIOS && !hasDart) {
128
- return types_1.Platform.WEB;
129
- }
130
- else if (hasAndroid && !hasWeb && !hasIOS && !hasDart) {
131
- return types_1.Platform.ANDROID;
132
- }
133
- else if (hasIOS && !hasWeb && !hasAndroid && !hasDart) {
134
- return types_1.Platform.IOS;
135
- }
136
- else if (hasDart && !hasWeb && !hasIOS && !hasAndroid) {
137
- return types_1.Platform.FLUTTER;
138
- }
139
- return types_1.Platform.MULTIPLE;
140
- }
141
- exports.getPlatformFromFolder = getPlatformFromFolder;
142
- async function resolvePackageJson(packageJsonPath) {
143
- let validPackageJsonPath = packageJsonPath;
144
- if (!packageJsonPath.endsWith("package.json")) {
145
- validPackageJsonPath = path.join(packageJsonPath, "package.json");
146
- }
147
- validPackageJsonPath = path.resolve(validPackageJsonPath);
148
- try {
149
- return JSON.parse((await fs.readFile(validPackageJsonPath)).toString());
150
- }
151
- catch (_a) {
152
- return undefined;
153
- }
154
- }
155
- exports.resolvePackageJson = resolvePackageJson;
156
- exports.SUPPORTED_FRAMEWORKS = ["react", "angular"];
157
- exports.frameworksMap = {
158
- react: ["react", "next"],
159
- angular: ["@angular/core"],
160
- };
161
- function getFrameworksFromPackageJson(packageJson) {
162
- var _a, _b;
163
- const devDependencies = Object.keys((_a = packageJson.devDependencies) !== null && _a !== void 0 ? _a : {});
164
- const dependencies = Object.keys((_b = packageJson.dependencies) !== null && _b !== void 0 ? _b : {});
165
- const allDeps = Array.from(new Set([...devDependencies, ...dependencies]));
166
- return exports.SUPPORTED_FRAMEWORKS.filter((framework) => exports.frameworksMap[framework].find((dep) => allDeps.includes(dep)));
167
- }
168
- exports.getFrameworksFromPackageJson = getFrameworksFromPackageJson;