firebase-tools 13.29.3 → 13.30.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.
- package/lib/extensions/provisioningHelper.js +1 -1
- package/lib/frameworks/utils.js +3 -1
- package/lib/gcp/cloudfunctionsv2.js +3 -0
- package/lib/init/features/genkit/index.js +8 -2
- package/package.json +1 -1
- package/templates/genkit/firebase.1.0.0.template +66 -0
- package/templates/init/dataconnect/mutations.gql +9 -26
- package/templates/init/dataconnect/queries.gql +9 -14
- package/templates/init/dataconnect/schema.gql +28 -21
|
@@ -97,7 +97,7 @@ async function isStorageProvisioned(projectId) {
|
|
|
97
97
|
return !!((_b = (_a = resp.body) === null || _a === void 0 ? void 0 : _a.buckets) === null || _b === void 0 ? void 0 : _b.find((bucket) => {
|
|
98
98
|
const bucketResourceName = bucket.name;
|
|
99
99
|
const bucketResourceNameTokens = bucketResourceName.split("/");
|
|
100
|
-
const pattern = "^" + projectId + "(.[[a-z0-9]+)*.appspot.com$";
|
|
100
|
+
const pattern = "^" + projectId + "(.[[a-z0-9]+)*.(appspot.com|firebasestorage.app)$";
|
|
101
101
|
return new RegExp(pattern).test(bucketResourceNameTokens[bucketResourceNameTokens.length - 1]);
|
|
102
102
|
}));
|
|
103
103
|
}
|
package/lib/frameworks/utils.js
CHANGED
|
@@ -123,7 +123,9 @@ function simpleProxy(hostOrRequestHandler) {
|
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
else {
|
|
126
|
-
const proxiedRes = proxyResponse(originalReq, originalRes,
|
|
126
|
+
const proxiedRes = proxyResponse(originalReq, originalRes, () => {
|
|
127
|
+
void hostOrRequestHandler(originalReq, originalRes, next);
|
|
128
|
+
});
|
|
127
129
|
await hostOrRequestHandler(originalReq, proxiedRes, next);
|
|
128
130
|
}
|
|
129
131
|
};
|
|
@@ -259,6 +259,9 @@ function functionFromEndpoint(endpoint) {
|
|
|
259
259
|
}
|
|
260
260
|
else if (backend.isCallableTriggered(endpoint)) {
|
|
261
261
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
|
|
262
|
+
if (endpoint.callableTrigger.genkitAction) {
|
|
263
|
+
gcfFunction.labels["genkit-action"] = "true";
|
|
264
|
+
}
|
|
262
265
|
}
|
|
263
266
|
else if (backend.isBlockingTriggered(endpoint)) {
|
|
264
267
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.BLOCKING_LABEL]: constants_1.BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
|
|
@@ -14,9 +14,11 @@ const ensureApiEnabled_1 = require("../../../ensureApiEnabled");
|
|
|
14
14
|
const logger_1 = require("../../../logger");
|
|
15
15
|
const error_1 = require("../../../error");
|
|
16
16
|
const utils_1 = require("../../../utils");
|
|
17
|
+
const UNKNOWN_VERSION_TOO_HIGH = "2.0.0";
|
|
18
|
+
const LATEST_TEMPLATE = "1.0.0";
|
|
17
19
|
async function getGenkitVersion() {
|
|
18
20
|
let genkitVersion;
|
|
19
|
-
let templateVersion =
|
|
21
|
+
let templateVersion = LATEST_TEMPLATE;
|
|
20
22
|
let useInit = false;
|
|
21
23
|
let stopInstall = false;
|
|
22
24
|
if (process.env.GENKIT_DEV_VERSION && typeof process.env.GENKIT_DEV_VERSION === "string") {
|
|
@@ -39,7 +41,7 @@ async function getGenkitVersion() {
|
|
|
39
41
|
if (!genkitVersion) {
|
|
40
42
|
throw new error_1.FirebaseError("Unable to determine genkit version to install");
|
|
41
43
|
}
|
|
42
|
-
if (semver.gte(genkitVersion,
|
|
44
|
+
if (semver.gte(genkitVersion, UNKNOWN_VERSION_TOO_HIGH)) {
|
|
43
45
|
const continueInstall = await (0, prompt_1.confirm)({
|
|
44
46
|
message: clc.yellow(`WARNING: The latest version of Genkit (${genkitVersion}) isn't supported by this\n` +
|
|
45
47
|
"version of firebase-tools. You can proceed, but the provided sample code may\n" +
|
|
@@ -51,7 +53,11 @@ async function getGenkitVersion() {
|
|
|
51
53
|
stopInstall = true;
|
|
52
54
|
}
|
|
53
55
|
}
|
|
56
|
+
else if (semver.gte(genkitVersion, "1.0.0-rc.1")) {
|
|
57
|
+
templateVersion = "1.0.0";
|
|
58
|
+
}
|
|
54
59
|
else if (semver.gte(genkitVersion, "0.6.0")) {
|
|
60
|
+
templateVersion = "0.9.0";
|
|
55
61
|
}
|
|
56
62
|
else {
|
|
57
63
|
templateVersion = "";
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Import the Genkit core libraries and plugins.
|
|
2
|
+
import {genkit, z} from "genkit";
|
|
3
|
+
$GENKIT_CONFIG_IMPORTS
|
|
4
|
+
$GENKIT_MODEL_IMPORT
|
|
5
|
+
|
|
6
|
+
// Cloud Functions for Firebase supports Genkit natively. The onCallGenkit function creates a callable
|
|
7
|
+
// function from a Genkit action. It automatically implements streaming if your flow does.
|
|
8
|
+
// The https library also has other utility methods such as hasClaim, which verifies that
|
|
9
|
+
// a caller's token has a specific claim (optionally matching a specific value)
|
|
10
|
+
import { onCallGenkit, hasClaim } from "firebase-functions/https";
|
|
11
|
+
|
|
12
|
+
// Genkit models generally depend on an API key. APIs should be stored in Cloud Secret Manager so that
|
|
13
|
+
// access to these sensitive values can be controlled. defineSecret does this for you automatically.
|
|
14
|
+
// If you are using Google generative AI you can get an API key at https://aistudio.google.com/app/apikey
|
|
15
|
+
import { defineSecret } from "firebase-functions/params";
|
|
16
|
+
const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");
|
|
17
|
+
|
|
18
|
+
const ai = genkit({
|
|
19
|
+
plugins: [
|
|
20
|
+
$GENKIT_CONFIG_PLUGINS
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Define a simple flow that prompts an LLM to generate menu suggestions.
|
|
25
|
+
const menuSuggestionFlow = ai.defineFlow({
|
|
26
|
+
name: "menuSuggestionFlow",
|
|
27
|
+
inputSchema: z.string(),
|
|
28
|
+
outputSchema: z.string(),
|
|
29
|
+
streamSchema: z.string(),
|
|
30
|
+
}, async (subject, { sendChunk }) => {
|
|
31
|
+
// Construct a request and send it to the model API.
|
|
32
|
+
const prompt =
|
|
33
|
+
`Suggest an item for the menu of a ${subject} themed restaurant`;
|
|
34
|
+
const { response, stream } = ai.generateStream({
|
|
35
|
+
model: $GENKIT_MODEL,
|
|
36
|
+
prompt: prompt,
|
|
37
|
+
config: {
|
|
38
|
+
temperature: 1,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
for await (const chunk of stream) {
|
|
43
|
+
sendChunk(chunk.text);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle the response from the model API. In this sample, we just
|
|
47
|
+
// convert it to a string, but more complicated flows might coerce the
|
|
48
|
+
// response into structured output or chain the response into another
|
|
49
|
+
// LLM call, etc.
|
|
50
|
+
return (await response).text;
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export const menuSuggestion = onCallGenkit({
|
|
55
|
+
// Uncomment to enable AppCheck. This can reduce costs by ensuring only your Verified
|
|
56
|
+
// app users can use your API. Read more at https://firebase.google.com/docs/app-check/cloud-functions
|
|
57
|
+
// enforceAppCheck: true,
|
|
58
|
+
|
|
59
|
+
// authPolicy can be any callback that accepts an AuthData (a uid and tokens dictionary) and the
|
|
60
|
+
// request data. The isSignedIn() and hasClaim() helpers can be used to simplify. The following
|
|
61
|
+
// will require the user to have the email_verified claim, for example.
|
|
62
|
+
// authPolicy: hasClaim("email_verified"),
|
|
63
|
+
|
|
64
|
+
// Grant access to the API key to this function:
|
|
65
|
+
secrets: [apiKey],
|
|
66
|
+
}, menuSuggestionFlow);
|
|
@@ -1,36 +1,20 @@
|
|
|
1
1
|
# # Example mutations for a simple movie app
|
|
2
2
|
|
|
3
3
|
# # Create a movie based on user input
|
|
4
|
-
# mutation CreateMovie(
|
|
5
|
-
#
|
|
6
|
-
# $genre:
|
|
7
|
-
# $imageUrl: String!
|
|
8
|
-
# ) @auth(level: USER_EMAIL_VERIFIED) {
|
|
9
|
-
# movie_insert(
|
|
10
|
-
# data: {
|
|
11
|
-
# title: $title
|
|
12
|
-
# genre: $genre
|
|
13
|
-
# imageUrl: $imageUrl
|
|
14
|
-
# }
|
|
15
|
-
# )
|
|
4
|
+
# mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!)
|
|
5
|
+
# @auth(level: USER_EMAIL_VERIFIED) {
|
|
6
|
+
# movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl })
|
|
16
7
|
# }
|
|
17
8
|
|
|
18
9
|
# # Upsert (update or insert) a user's username based on their auth.uid
|
|
19
10
|
# mutation UpsertUser($username: String!) @auth(level: USER) {
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# id_expr: "auth.uid"
|
|
23
|
-
# username: $username
|
|
24
|
-
# }
|
|
25
|
-
# )
|
|
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 })
|
|
26
13
|
# }
|
|
27
14
|
|
|
28
15
|
# # Add a review for a movie
|
|
29
|
-
# mutation AddReview(
|
|
30
|
-
#
|
|
31
|
-
# $rating: Int!
|
|
32
|
-
# $reviewText: String!
|
|
33
|
-
# ) @auth(level: USER) {
|
|
16
|
+
# mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
|
|
17
|
+
# @auth(level: USER) {
|
|
34
18
|
# review_upsert(
|
|
35
19
|
# data: {
|
|
36
20
|
# userId_expr: "auth.uid"
|
|
@@ -43,8 +27,7 @@
|
|
|
43
27
|
# }
|
|
44
28
|
|
|
45
29
|
# # Logged in user can delete their review for a movie
|
|
46
|
-
# mutation DeleteReview(
|
|
47
|
-
#
|
|
48
|
-
# ) @auth(level: USER) {
|
|
30
|
+
# mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
|
|
31
|
+
# # The "auth.uid" server value ensures that users can only delete their own reviews.
|
|
49
32
|
# review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
|
|
50
33
|
# }
|
|
@@ -13,19 +13,21 @@
|
|
|
13
13
|
|
|
14
14
|
# # List all users, only admins should be able to list all users, so we use NO_ACCESS
|
|
15
15
|
# query ListUsers @auth(level: NO_ACCESS) {
|
|
16
|
-
# users {
|
|
16
|
+
# users {
|
|
17
|
+
# id
|
|
18
|
+
# username
|
|
19
|
+
# }
|
|
17
20
|
# }
|
|
18
21
|
|
|
19
|
-
# # Logged in
|
|
20
|
-
# # Since the query
|
|
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
|
|
21
24
|
# query ListUserReviews @auth(level: USER) {
|
|
22
|
-
# user(key: {id_expr: "auth.uid"}) {
|
|
25
|
+
# user(key: { id_expr: "auth.uid" }) {
|
|
23
26
|
# id
|
|
24
27
|
# username
|
|
25
28
|
# # <field>_on_<foreign_key_field> makes it easy to grab info from another table
|
|
26
29
|
# # Here, we use it to grab all the reviews written by the user.
|
|
27
30
|
# reviews: reviews_on_user {
|
|
28
|
-
# id
|
|
29
31
|
# rating
|
|
30
32
|
# reviewDate
|
|
31
33
|
# reviewText
|
|
@@ -50,7 +52,6 @@
|
|
|
50
52
|
# description
|
|
51
53
|
# }
|
|
52
54
|
# reviews: reviews_on_movie {
|
|
53
|
-
# id
|
|
54
55
|
# reviewText
|
|
55
56
|
# reviewDate
|
|
56
57
|
# rating
|
|
@@ -63,16 +64,10 @@
|
|
|
63
64
|
# }
|
|
64
65
|
|
|
65
66
|
# # Search for movies, actors, and reviews
|
|
66
|
-
# query SearchMovie(
|
|
67
|
-
# $titleInput: String
|
|
68
|
-
# $genre: String
|
|
69
|
-
# ) @auth(level: PUBLIC) {
|
|
67
|
+
# query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC) {
|
|
70
68
|
# movies(
|
|
71
69
|
# where: {
|
|
72
|
-
# _and: [
|
|
73
|
-
# { genre: { eq: $genre } }
|
|
74
|
-
# { title: { contains: $titleInput } }
|
|
75
|
-
# ]
|
|
70
|
+
# _and: [{ genre: { eq: $genre } }, { title: { contains: $titleInput } }]
|
|
76
71
|
# }
|
|
77
72
|
# ) {
|
|
78
73
|
# id
|
|
@@ -1,44 +1,51 @@
|
|
|
1
1
|
# # Example schema for simple movie review app
|
|
2
2
|
|
|
3
|
-
# #
|
|
4
|
-
# # Suppose a user can leave reviews for movies
|
|
5
|
-
# # user -> reviews is a one to many relationship,
|
|
6
|
-
# # movie -> reviews is a one to many relationship
|
|
7
|
-
# # movie <-> user is a many to many relationship
|
|
3
|
+
# # User table is keyed by Firebase Auth UID.
|
|
8
4
|
# type User @table {
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# #
|
|
13
|
-
# #
|
|
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!]!
|
|
14
12
|
# }
|
|
15
13
|
|
|
16
|
-
# #
|
|
14
|
+
# # Movie is keyed by a randomly generated UUID.
|
|
17
15
|
# type Movie @table {
|
|
18
|
-
# #
|
|
19
|
-
# #
|
|
20
|
-
# id: UUID! @default(expr: "uuidV4()")
|
|
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()")
|
|
21
19
|
# title: String!
|
|
22
20
|
# imageUrl: String!
|
|
23
21
|
# genre: String
|
|
24
22
|
# }
|
|
25
23
|
|
|
26
|
-
# # Movie
|
|
27
|
-
# # Movie
|
|
24
|
+
# # MovieMetadata is a metadata attached to a Movie.
|
|
25
|
+
# # Movie <-> MovieMetadata is a one-to-one relationship
|
|
28
26
|
# type MovieMetadata @table {
|
|
29
|
-
# # @unique
|
|
30
|
-
# movie: Movie! @unique
|
|
31
|
-
# #
|
|
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!
|
|
32
31
|
# rating: Float
|
|
33
32
|
# releaseYear: Int
|
|
34
33
|
# description: String
|
|
35
34
|
# }
|
|
36
35
|
|
|
37
|
-
# # Reviews
|
|
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
|
|
38
42
|
# type Review @table(name: "Reviews", key: ["movie", "user"]) {
|
|
39
|
-
# id: UUID! @default(expr: "uuidV4()")
|
|
40
43
|
# user: User!
|
|
44
|
+
# # The user field adds the following foreign key field. Feel free to uncomment and customize it.
|
|
45
|
+
# # userUid: String!
|
|
41
46
|
# movie: Movie!
|
|
47
|
+
# # The movie field adds the following foreign key field. Feel free to uncomment and customize it.
|
|
48
|
+
# # movieId: UUID!
|
|
42
49
|
# rating: Int
|
|
43
50
|
# reviewText: String
|
|
44
51
|
# reviewDate: Date! @default(expr: "request.time")
|