firebase-tools 15.5.0 → 15.6.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 (61) hide show
  1. package/lib/apiv2.js +11 -5
  2. package/lib/apphosting/backend.js +1 -1
  3. package/lib/commands/deploy.js +3 -0
  4. package/lib/commands/init.js +5 -0
  5. package/lib/config.js +1 -0
  6. package/lib/dataconnect/client.js +2 -2
  7. package/lib/dataconnect/prompts.js +14 -0
  8. package/lib/deploy/auth/deploy.js +63 -0
  9. package/lib/deploy/auth/index.js +9 -0
  10. package/lib/deploy/auth/prepare.js +26 -0
  11. package/lib/deploy/auth/release.js +5 -0
  12. package/lib/deploy/dataconnect/release.js +11 -0
  13. package/lib/deploy/functions/backend.js +10 -0
  14. package/lib/deploy/functions/build.js +2 -2
  15. package/lib/deploy/functions/checkIam.js +3 -2
  16. package/lib/deploy/functions/deploy.js +4 -2
  17. package/lib/deploy/functions/prepare.js +5 -2
  18. package/lib/deploy/functions/prepareFunctionsUpload.js +7 -5
  19. package/lib/deploy/functions/release/fabricator.js +96 -15
  20. package/lib/deploy/functions/release/index.js +2 -1
  21. package/lib/deploy/functions/runtimes/dart.js +42 -0
  22. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +4 -1
  23. package/lib/deploy/functions/runtimes/index.js +9 -1
  24. package/lib/deploy/functions/runtimes/supported/types.js +6 -0
  25. package/lib/deploy/index.js +2 -0
  26. package/lib/emulator/auth/index.js +23 -17
  27. package/lib/emulator/auth/operations.js +9 -1
  28. package/lib/emulator/downloadableEmulatorInfo.json +31 -31
  29. package/lib/emulator/hubExport.js +15 -1
  30. package/lib/ensureApiEnabled.js +2 -2
  31. package/lib/gcp/cloudbilling.js +6 -3
  32. package/lib/gcp/iam.js +1 -1
  33. package/lib/gcp/runv2.js +75 -3
  34. package/lib/gcp/serviceusage.js +2 -2
  35. package/lib/gemini/fdcExperience.js +0 -11
  36. package/lib/init/features/auth.js +72 -0
  37. package/lib/init/features/dataconnect/resolver.js +17 -3
  38. package/lib/init/features/functions/index.js +22 -14
  39. package/lib/init/features/functions/javascript.js +18 -3
  40. package/lib/init/features/functions/typescript.js +20 -3
  41. package/lib/init/features/functions/utils.js +10 -0
  42. package/lib/init/features/genkit/index.js +3 -2
  43. package/lib/init/features/index.js +6 -2
  44. package/lib/init/index.js +11 -1
  45. package/lib/management/provisioning/provision.js +3 -0
  46. package/lib/management/provisioning/types.js +7 -0
  47. package/lib/mcp/resources/guides/init_auth.js +19 -2
  48. package/lib/mcp/tools/apptesting/tests.js +6 -4
  49. package/lib/mcp/tools/core/init.js +31 -0
  50. package/lib/tsconfig.publish.tsbuildinfo +1 -1
  51. package/package.json +1 -1
  52. package/schema/firebase-config.json +41 -0
  53. package/templates/init/apptesting/smoke_test.yaml +1 -1
  54. package/templates/init/dataconnect/secondary_schema.gql +8 -0
  55. package/templates/init/functions/javascript/index-ongraphrequest.js +36 -0
  56. package/templates/init/functions/javascript/package-ongraphrequest.lint.json +28 -0
  57. package/templates/init/functions/javascript/package-ongraphrequest.nolint.json +25 -0
  58. package/templates/init/functions/typescript/index-ongraphrequest.ts +44 -0
  59. package/templates/init/functions/typescript/package-ongraphrequest.lint.json +33 -0
  60. package/templates/init/functions/typescript/package-ongraphrequest.nolint.json +27 -0
  61. package/lib/mcp/util.test.js +0 -468
@@ -6,7 +6,15 @@ const python = require("./python");
6
6
  const validate = require("../validate");
7
7
  const error_1 = require("../../../error");
8
8
  const supported = require("./supported");
9
- const factories = [node.tryCreateDelegate, python.tryCreateDelegate];
9
+ const dart = require("./dart");
10
+ const experiments = require("../../../experiments");
11
+ const factories = [
12
+ node.tryCreateDelegate,
13
+ python.tryCreateDelegate,
14
+ (ctx) => experiments.isEnabled("functionsrunapionly")
15
+ ? dart.tryCreateDelegate(ctx)
16
+ : Promise.resolve(undefined),
17
+ ];
10
18
  async function getRuntimeDelegate(context) {
11
19
  const { projectDir, sourceDir, runtime } = context;
12
20
  if (runtime && !supported.isRuntime(runtime)) {
@@ -89,4 +89,10 @@ exports.RUNTIMES = runtimes({
89
89
  deprecationDate: "2029-10-10",
90
90
  decommissionDate: "2030-04-10",
91
91
  },
92
+ dart3: {
93
+ friendly: "Dart 3",
94
+ status: "experimental",
95
+ deprecationDate: "2030-01-01",
96
+ decommissionDate: "2030-01-01",
97
+ },
92
98
  });
@@ -21,6 +21,7 @@ const RemoteConfigTarget = require("./remoteconfig");
21
21
  const ExtensionsTarget = require("./extensions");
22
22
  const DataConnectTarget = require("./dataconnect");
23
23
  const AppHostingTarget = require("./apphosting");
24
+ const AuthTarget = require("./auth");
24
25
  const frameworks_1 = require("../frameworks");
25
26
  const prepare_1 = require("./hosting/prepare");
26
27
  const utils_2 = require("../utils");
@@ -37,6 +38,7 @@ const TARGETS = {
37
38
  extensions: ExtensionsTarget,
38
39
  dataconnect: DataConnectTarget,
39
40
  apphosting: AppHostingTarget,
41
+ auth: AuthTarget,
40
42
  };
41
43
  const chain = async function (fns, context, options, payload) {
42
44
  for (const latest of fns) {
@@ -69,23 +69,29 @@ class AuthEmulator {
69
69
  else {
70
70
  logger.logLabeled("WARN", "auth", `Skipped importing config because ${configPath} does not exist.`);
71
71
  }
72
- const accountsPath = path.join(authExportDir, "accounts.json");
73
- const accountsStat = await stat(accountsPath);
74
- if (accountsStat?.isFile()) {
75
- logger.logLabeled("BULLET", "auth", `Importing accounts from ${accountsPath}`);
76
- await importFromFile({
77
- method: "POST",
78
- host: utils.connectableHostname(host),
79
- port,
80
- path: `/identitytoolkit.googleapis.com/v1/projects/${projectId}/accounts:batchCreate`,
81
- headers: {
82
- Authorization: "Bearer owner",
83
- "Content-Type": "application/json",
84
- },
85
- }, accountsPath, { ignoreErrors: ["MISSING_USER_ACCOUNT"] });
86
- }
87
- else {
88
- logger.logLabeled("WARN", "auth", `Skipped importing accounts because ${accountsPath} does not exist.`);
72
+ const accountFiles = fs
73
+ .readdirSync(authExportDir)
74
+ .filter((fileName) => fileName.includes("accounts"));
75
+ for (const accountFile of accountFiles) {
76
+ const accountsPath = path.join(authExportDir, accountFile);
77
+ const accountsStat = await stat(accountsPath);
78
+ const tenantId = accountFile.replace(/accounts(-|)|.json/gm, "");
79
+ if (accountsStat?.isFile()) {
80
+ logger.logLabeled("BULLET", "auth", `Importing accounts from ${accountsPath}`);
81
+ await importFromFile({
82
+ method: "POST",
83
+ host: utils.connectableHostname(host),
84
+ port,
85
+ path: `/identitytoolkit.googleapis.com/v1/projects/${projectId}/tenants/${tenantId}/accounts:batchCreate`,
86
+ headers: {
87
+ Authorization: "Bearer owner",
88
+ "Content-Type": "application/json",
89
+ },
90
+ }, accountsPath, { ignoreErrors: ["MISSING_USER_ACCOUNT"] });
91
+ }
92
+ else {
93
+ logger.logLabeled("WARN", "auth", `Skipped importing accounts because ${accountsPath} does not exist.`);
94
+ }
89
95
  }
90
96
  }
91
97
  }
@@ -426,7 +426,15 @@ function batchDelete(state, reqBody) {
426
426
  function batchGet(state, reqBody, ctx) {
427
427
  (0, errors_1.assert)(!state.disableAuth, "PROJECT_DISABLED");
428
428
  const maxResults = Math.min(Math.floor(ctx.params.query.maxResults) || 20, 1000);
429
- const users = state.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
429
+ const queryTenantId = ctx.params.query.tenantId;
430
+ let users;
431
+ if (queryTenantId && state instanceof state_1.AgentProjectState) {
432
+ const tenant = state.getTenantProject(queryTenantId);
433
+ users = tenant.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
434
+ }
435
+ else {
436
+ users = state.queryUsers({}, { sortByField: "localId", order: "ASC", startToken: ctx.params.query.nextPageToken });
437
+ }
430
438
  let newPageToken = undefined;
431
439
  if (maxResults >= 0 && users.length >= maxResults) {
432
440
  users.length = maxResults;
@@ -44,46 +44,46 @@
44
44
  }
45
45
  },
46
46
  "pubsub": {
47
- "version": "0.8.25",
48
- "expectedSize": 52169500,
49
- "expectedChecksum": "ff855b79e84d26b2b5b2d56e84e0fb51",
50
- "expectedChecksumSHA256": "a69fe788be3b0b3866c55b7ed8790efa424a88e6f26843bdbe9d34b93c9509af",
51
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.25.zip",
52
- "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.25.zip",
53
- "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.25/pubsub-emulator/bin/cloud-pubsub-emulator"
47
+ "version": "0.8.27",
48
+ "expectedSize": 52924291,
49
+ "expectedChecksum": "cb0d35db6aa1bb5e3f7e2a5a690c631d",
50
+ "expectedChecksumSHA256": "0b793b420b608b68c200a0d15123c63967ac2863bbd9545ecb087d5b28871339",
51
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.27.zip",
52
+ "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.27.zip",
53
+ "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.27/pubsub-emulator/bin/cloud-pubsub-emulator"
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.1.1",
58
- "expectedSize": 30450528,
59
- "expectedChecksum": "06e49453e3c9f876d445972a3588965f",
60
- "expectedChecksumSHA256": "07dbbb5d1356597acc478d3c58dbf48e716b3795f4da0e7e174b0fd472b004d2",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.1.1",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.1"
57
+ "version": "3.1.3",
58
+ "expectedSize": 30753632,
59
+ "expectedChecksum": "db15fbb3d596b056390c08d065500c63",
60
+ "expectedChecksumSHA256": "f1b98402e9d8497e88a96de6ac80425aee3a61513eccc1fb76b4e387862625ee",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.1.3",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.1.1",
66
- "expectedSize": 29916818,
67
- "expectedChecksum": "477dd3da175dba6d7545ade62a8274f6",
68
- "expectedChecksumSHA256": "472fe448f25fa8546aa383406db78eaf4eec4d8b700bb325dbc65899aba1bd20",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.1.1",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.1"
65
+ "version": "3.1.3",
66
+ "expectedSize": 30203330,
67
+ "expectedChecksum": "8ac08faf1b797615d91c58f7ea781af0",
68
+ "expectedChecksumSHA256": "eb00f5bd3c97c17b617a9ce5f5153d6d9f20bc482e4ac091a6361f9ff3bd8841",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.1.3",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3"
71
71
  },
72
72
  "win32": {
73
- "version": "3.1.1",
74
- "expectedSize": 30951424,
75
- "expectedChecksum": "c51ec99bffd2f37fa2bb793f7cc74ea7",
76
- "expectedChecksumSHA256": "e03d0dae5f040b03d211d6741540ed11206b03548b230eb8e36338b5b57545ee",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.1.1",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.1.exe"
73
+ "version": "3.1.3",
74
+ "expectedSize": 31262208,
75
+ "expectedChecksum": "b6723bed0937b1a3fed6fdb64df39f4d",
76
+ "expectedChecksumSHA256": "3d213e26ab7ea4cd44083a32d882128e0ff9313a69795d88cb7a9f31042675ff",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.1.3",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.1.1",
82
- "expectedSize": 30372024,
83
- "expectedChecksum": "8a3977f07facaacaa9be3f9ec6bd90b7",
84
- "expectedChecksumSHA256": "5c36d16d4ded51dd887c939a82e8cdc7e84cb40a2c5d00cb02bff90ca65c2472",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.1.1",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.1"
81
+ "version": "3.1.3",
82
+ "expectedSize": 30679224,
83
+ "expectedChecksum": "f1d0f701d8e56669a93f00f1c10a7437",
84
+ "expectedChecksumSHA256": "826aa0fcbb28bad7ced433e6386ea1cce2407357a145da7111e800489c6cf3c9",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.1.3",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.1.3"
87
87
  }
88
88
  }
89
89
  }
@@ -166,8 +166,22 @@ class HubExport {
166
166
  if (!fs.existsSync(authExportPath)) {
167
167
  fs.mkdirSync(authExportPath);
168
168
  }
169
+ const tenantsRes = await registry_1.EmulatorRegistry.client(types_1.Emulators.AUTH).get(`/identitytoolkit.googleapis.com/v2/projects/${this.projectId}/tenants`, {
170
+ headers: { Authorization: "Bearer owner" },
171
+ });
172
+ const tenants = tenantsRes.body.tenants.map((instance) => instance.tenantId);
173
+ for (const tenantId of tenants) {
174
+ const accountsFile = path.join(authExportPath, `accounts-${tenantId}.json`);
175
+ logger_1.logger.debug(`Exporting auth users in Project ${this.projectId} ${tenantId} tenant to ${accountsFile}`);
176
+ await fetchToFile({
177
+ host,
178
+ port,
179
+ path: `/identitytoolkit.googleapis.com/v1/projects/${this.projectId}/accounts:batchGet?maxResults=-1&tenantId=${tenantId}`,
180
+ headers: { Authorization: "Bearer owner" },
181
+ }, accountsFile);
182
+ }
169
183
  const accountsFile = path.join(authExportPath, "accounts.json");
170
- logger_1.logger.debug(`Exporting auth users in Project ${this.projectId} to ${accountsFile}`);
184
+ logger_1.logger.debug(`Exporting auth users in Project ${this.projectId} default tenant to ${accountsFile}`);
171
185
  await fetchToFile({
172
186
  host,
173
187
  port,
@@ -27,7 +27,7 @@ async function check(projectId, apiUri, prefix, silent = false) {
27
27
  return true;
28
28
  }
29
29
  const res = await apiClient.get(`/projects/${projectId}/services/${apiName}`, {
30
- headers: { "x-goog-user-project": `projects/${projectId}` },
30
+ headers: { "x-goog-user-project": `${projectId}` },
31
31
  skipLog: { resBody: true },
32
32
  });
33
33
  const isEnabled = res.body.state === "ENABLED";
@@ -45,7 +45,7 @@ function isPermissionError(e) {
45
45
  async function enable(projectId, apiName) {
46
46
  try {
47
47
  await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, undefined, {
48
- headers: { "x-goog-user-project": `projects/${projectId}` },
48
+ headers: { "x-goog-user-project": `${projectId}` },
49
49
  skipLog: { resBody: true },
50
50
  });
51
51
  cacheEnabledAPI(projectId, apiName);
@@ -20,16 +20,19 @@ async function isBillingEnabled(setup) {
20
20
  return setup.isBillingEnabled;
21
21
  }
22
22
  async function checkBillingEnabled(projectId) {
23
- const res = await client.get(utils.endpoint(["projects", projectId, "billingInfo"]), { retryCodes: [500, 503] });
23
+ const res = await client.get(utils.endpoint(["projects", projectId, "billingInfo"]), {
24
+ retries: 3,
25
+ retryCodes: [429, 500, 503],
26
+ });
24
27
  return res.body.billingEnabled;
25
28
  }
26
29
  async function setBillingAccount(projectId, billingAccountName) {
27
30
  const res = await client.put(utils.endpoint(["projects", projectId, "billingInfo"]), {
28
31
  billingAccountName: billingAccountName,
29
- }, { retryCodes: [500, 503] });
32
+ }, { retryCodes: [429, 500, 503] });
30
33
  return res.body.billingEnabled;
31
34
  }
32
35
  async function listBillingAccounts() {
33
- const res = await client.get(utils.endpoint(["billingAccounts"]), { retryCodes: [500, 503] });
36
+ const res = await client.get(utils.endpoint(["billingAccounts"]), { retryCodes: [429, 500, 503] });
34
37
  return res.body.billingAccounts || [];
35
38
  }
package/lib/gcp/iam.js CHANGED
@@ -78,7 +78,7 @@ async function testResourceIamPermissions(origin, apiVersion, resourceName, perm
78
78
  };
79
79
  }
80
80
  async function testIamPermissions(projectId, permissions) {
81
- return testResourceIamPermissions((0, api_1.resourceManagerOrigin)(), "v1", `projects/${projectId}`, permissions, `projects/${projectId}`);
81
+ return testResourceIamPermissions((0, api_1.resourceManagerOrigin)(), "v1", `projects/${projectId}`, permissions, `${projectId}`);
82
82
  }
83
83
  function mergeBindings(policy, requiredBindings) {
84
84
  let updated = false;
package/lib/gcp/runv2.js CHANGED
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FIREBASE_FUNCTION_METADTA_ANNOTATION = exports.FUNCTION_SIGNATURE_TYPE_ENV = exports.FUNCTION_TARGET_ENV = exports.FUNCTION_ID_ANNOTATION = exports.FUNCTION_TARGET_ANNOTATION = exports.TRIGGER_TYPE_ANNOTATION = exports.CLIENT_NAME_LABEL = exports.RUNTIME_LABEL = exports.API_VERSION = void 0;
4
4
  exports.submitBuild = submitBuild;
5
5
  exports.updateService = updateService;
6
+ exports.createService = createService;
7
+ exports.deleteService = deleteService;
8
+ exports.getService = getService;
6
9
  exports.listServices = listServices;
7
10
  exports.endpointFromService = endpointFromService;
8
11
  exports.serviceFromEndpoint = serviceFromEndpoint;
@@ -51,6 +54,34 @@ async function updateService(service) {
51
54
  });
52
55
  return svc;
53
56
  }
57
+ async function createService(projectId, location, serviceId, service) {
58
+ const { name, ...serviceBody } = service;
59
+ const res = await client.post(`/projects/${projectId}/locations/${location}/services`, serviceBody, {
60
+ queryParams: {
61
+ serviceId,
62
+ },
63
+ });
64
+ const svc = await (0, operation_poller_1.pollOperation)({
65
+ apiOrigin: (0, api_1.runOrigin)(),
66
+ apiVersion: exports.API_VERSION,
67
+ operationResourceName: res.body.name,
68
+ });
69
+ return svc;
70
+ }
71
+ async function deleteService(projectId, location, serviceId) {
72
+ const name = `projects/${projectId}/locations/${location}/services/${serviceId}`;
73
+ const res = await client.delete(name);
74
+ await (0, operation_poller_1.pollOperation)({
75
+ apiOrigin: (0, api_1.runOrigin)(),
76
+ apiVersion: exports.API_VERSION,
77
+ operationResourceName: res.body.name,
78
+ });
79
+ }
80
+ async function getService(projectId, location, serviceId) {
81
+ const name = `projects/${projectId}/locations/${location}/services/${serviceId}`;
82
+ const res = await client.get(name);
83
+ return res.body;
84
+ }
54
85
  async function listServices(projectId) {
55
86
  const allServices = [];
56
87
  let pageToken = undefined;
@@ -115,6 +146,15 @@ function endpointFromService(service) {
115
146
  service.annotations?.[exports.FUNCTION_TARGET_ANNOTATION] ||
116
147
  service.annotations?.[exports.FUNCTION_ID_ANNOTATION] ||
117
148
  id,
149
+ timeoutSeconds: service.template.timeout
150
+ ? proto.secondsFromDuration(service.template.timeout)
151
+ : 60,
152
+ serviceAccount: service.template.serviceAccount || null,
153
+ ingressSettings: (service.annotations?.["run.googleapis.com/ingress"] === "internal"
154
+ ? "ALLOW_INTERNAL_ONLY"
155
+ : service.annotations?.["run.googleapis.com/ingress"] === "internal-and-cloud-load-balancing"
156
+ ? "ALLOW_INTERNAL_AND_GCLB"
157
+ : "ALLOW_ALL"),
118
158
  ...(service.annotations?.[exports.TRIGGER_TYPE_ANNOTATION] === "HTTP_TRIGGER"
119
159
  ? { httpsTrigger: {} }
120
160
  : {
@@ -124,7 +164,7 @@ function endpointFromService(service) {
124
164
  },
125
165
  }),
126
166
  };
127
- proto.renameIfPresent(endpoint, service.template, "concurrency", "containerConcurrency");
167
+ proto.renameIfPresent(endpoint, service.template, "concurrency", "maxInstanceRequestConcurrency");
128
168
  proto.renameIfPresent(endpoint, service.labels || {}, "codebase", constants_1.CODEBASE_LABEL);
129
169
  proto.renameIfPresent(endpoint, service.scaling || {}, "minInstances", "minInstanceCount");
130
170
  proto.renameIfPresent(endpoint, service.scaling || {}, "maxInstances", "maxInstanceCount");
@@ -141,6 +181,12 @@ function endpointFromService(service) {
141
181
  version: e.valueSource.secretKeyRef.version || "latest",
142
182
  };
143
183
  });
184
+ if (service.template.vpcAccess) {
185
+ endpoint.vpc = {
186
+ connector: service.template.vpcAccess.connector || "",
187
+ egressSettings: service.template.vpcAccess.egress || null,
188
+ };
189
+ }
144
190
  return endpoint;
145
191
  }
146
192
  function serviceFromEndpoint(endpoint, image) {
@@ -194,11 +240,14 @@ function serviceFromEndpoint(endpoint, image) {
194
240
  cpuIdle: true,
195
241
  startupCpuBoost: true,
196
242
  },
243
+ ...(endpoint.baseImageUri ? { baseImageUri: endpoint.baseImageUri } : {}),
244
+ ...(endpoint.command ? { command: endpoint.command } : {}),
245
+ ...(endpoint.args ? { args: endpoint.args } : {}),
197
246
  },
198
247
  ],
199
- containerConcurrency: endpoint.concurrency || backend.DEFAULT_CONCURRENCY,
248
+ maxInstanceRequestConcurrency: endpoint.concurrency || backend.DEFAULT_CONCURRENCY,
200
249
  };
201
- proto.renameIfPresent(template, endpoint, "containerConcurrency", "concurrency");
250
+ proto.renameIfPresent(template, endpoint, "maxInstanceRequestConcurrency", "concurrency");
202
251
  const service = {
203
252
  name: `projects/${endpoint.project}/locations/${endpoint.region}/services/${functionNameToServiceName(endpoint.id)}`,
204
253
  labels,
@@ -211,5 +260,28 @@ function serviceFromEndpoint(endpoint, image) {
211
260
  proto.renameIfPresent(service.scaling, endpoint, "minInstanceCount", "minInstances");
212
261
  proto.renameIfPresent(service.scaling, endpoint, "maxInstanceCount", "maxInstances");
213
262
  }
263
+ if (endpoint.serviceAccount) {
264
+ template.serviceAccount = endpoint.serviceAccount;
265
+ }
266
+ if (endpoint.timeoutSeconds) {
267
+ template.timeout = proto.durationFromSeconds(endpoint.timeoutSeconds);
268
+ }
269
+ if (endpoint.vpc) {
270
+ template.vpcAccess = {
271
+ connector: endpoint.vpc.connector,
272
+ egress: endpoint.vpc.egressSettings || undefined,
273
+ };
274
+ }
275
+ if (endpoint.ingressSettings) {
276
+ const ingressAnnotation = endpoint.ingressSettings === "ALLOW_INTERNAL_ONLY"
277
+ ? "internal"
278
+ : endpoint.ingressSettings === "ALLOW_INTERNAL_AND_GCLB"
279
+ ? "internal-and-cloud-load-balancing"
280
+ : "all";
281
+ service.annotations = {
282
+ ...service.annotations,
283
+ "run.googleapis.com/ingress": ingressAnnotation,
284
+ };
285
+ }
214
286
  return service;
215
287
  }
@@ -22,7 +22,7 @@ const serviceUsagePollerOptions = {
22
22
  async function generateServiceIdentity(projectNumber, service, prefix) {
23
23
  utils.logLabeledBullet(prefix, `generating the service identity for ${(0, colorette_1.bold)(service)}...`);
24
24
  try {
25
- const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`, {}, { headers: { "x-goog-user-project": `projects/${projectNumber}` } });
25
+ const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`, {}, { headers: { "x-goog-user-project": `${projectNumber}` } });
26
26
  return res.body;
27
27
  }
28
28
  catch (err) {
@@ -39,6 +39,6 @@ async function generateServiceIdentityAndPoll(projectNumber, service, prefix) {
39
39
  await poller.pollOperation({
40
40
  ...serviceUsagePollerOptions,
41
41
  operationResourceName: op.name,
42
- headers: { "x-goog-user-project": `projects/${projectNumber}` },
42
+ headers: { "x-goog-user-project": `${projectNumber}` },
43
43
  });
44
44
  }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PROMPT_GENERATE_SEED_DATA = exports.PROMPT_GENERATE_CONNECTOR = void 0;
4
4
  exports.generateSchema = generateSchema;
5
- exports.chatWithFirebase = chatWithFirebase;
6
5
  exports.generateOperation = generateOperation;
7
6
  exports.extractCodeBlock = extractCodeBlock;
8
7
  const apiv2_1 = require("../apiv2");
@@ -10,7 +9,6 @@ const api_1 = require("../api");
10
9
  const error_1 = require("../error");
11
10
  const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.cloudAiCompanionOrigin)(), auth: true });
12
11
  const SCHEMA_GENERATOR_EXPERIENCE = "/appeco/firebase/fdc-schema-generator";
13
- const GEMINI_IN_FIREBASE_EXPERIENCE = "/appeco/firebase/firebase-chat/free";
14
12
  const OPERATION_GENERATION_EXPERIENCE = "/appeco/firebase/fdc-query-generator";
15
13
  const FIREBASE_CHAT_REQUEST_CONTEXT_TYPE_NAME = "type.googleapis.com/google.cloud.cloudaicompanion.v1main.FirebaseChatRequestContext";
16
14
  exports.PROMPT_GENERATE_CONNECTOR = "Create 4 operations for an app using the instance schema with proper authentication.";
@@ -24,15 +22,6 @@ async function generateSchema(prompt, project, chatHistory = []) {
24
22
  });
25
23
  return extractCodeBlock(res.body.output.messages[0].content);
26
24
  }
27
- async function chatWithFirebase(prompt, project, chatHistory = []) {
28
- const res = await apiClient.post(`/v1beta/projects/${project}/locations/global/instances/default:completeTask`, {
29
- input: { messages: [...chatHistory, { content: prompt, author: "USER" }] },
30
- experienceContext: {
31
- experience: GEMINI_IN_FIREBASE_EXPERIENCE,
32
- },
33
- });
34
- return res.body;
35
- }
36
25
  async function generateOperation(prompt, service, project, chatHistory = []) {
37
26
  const res = await apiClient.post(`/v1beta/projects/${project}/locations/global/instances/default:completeTask`, {
38
27
  input: { messages: [...chatHistory, { content: prompt, author: "USER" }] },
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.askQuestions = askQuestions;
4
+ exports.actuate = actuate;
5
+ const clc = require("colorette");
6
+ const prompt_1 = require("../../prompt");
7
+ const logger_1 = require("../../logger");
8
+ async function askQuestions(setup) {
9
+ const authConfig = setup.config.auth;
10
+ const choices = [
11
+ {
12
+ name: "Google Sign-In",
13
+ value: "google",
14
+ checked: !!authConfig?.providers?.googleSignIn,
15
+ },
16
+ {
17
+ name: "Email/Password",
18
+ value: "email",
19
+ checked: !!authConfig?.providers?.emailPassword,
20
+ },
21
+ {
22
+ name: "Anonymous",
23
+ value: "anonymous",
24
+ checked: !!authConfig?.providers?.anonymous,
25
+ },
26
+ ];
27
+ const providers = await (0, prompt_1.checkbox)({
28
+ message: "Which providers would you like to enable? If you don't see a provider here, go to the Firebase Console to set it up.",
29
+ choices: choices,
30
+ });
31
+ const providersConfig = {};
32
+ if (providers.includes("anonymous")) {
33
+ providersConfig.anonymous = true;
34
+ }
35
+ if (providers.includes("email")) {
36
+ providersConfig.emailPassword = true;
37
+ }
38
+ if (providers.includes("google")) {
39
+ logger_1.logger.info("");
40
+ logger_1.logger.info("Configuring Google Sign-In...");
41
+ const oAuthBrandDisplayName = await (0, prompt_1.input)({
42
+ message: "What display name would you like to use for your OAuth brand?",
43
+ default: authConfig?.providers?.googleSignIn?.oAuthBrandDisplayName ||
44
+ setup.project?.projectId ||
45
+ "My App",
46
+ });
47
+ const supportEmail = await (0, prompt_1.input)({
48
+ message: "What support email would you like to register for your OAuth brand?",
49
+ default: authConfig?.providers?.googleSignIn?.supportEmail ||
50
+ (setup.project ? `support@${setup.project.projectId}.firebaseapp.com` : undefined),
51
+ });
52
+ providersConfig.googleSignIn = {
53
+ oAuthBrandDisplayName,
54
+ supportEmail,
55
+ };
56
+ }
57
+ if (!setup.featureInfo) {
58
+ setup.featureInfo = {};
59
+ }
60
+ setup.featureInfo.auth = { providers: providersConfig };
61
+ }
62
+ async function actuate(setup, config) {
63
+ const authConfig = setup.featureInfo?.auth;
64
+ if (!authConfig) {
65
+ return;
66
+ }
67
+ config.set("auth", authConfig);
68
+ config.writeProjectFile("firebase.json", config.src);
69
+ logger_1.logger.info("");
70
+ logger_1.logger.info("Generated firebase.json with auth configuration.");
71
+ logger_1.logger.info("Run " + clc.bold("firebase deploy") + " to enable these providers.");
72
+ }
@@ -4,7 +4,6 @@ exports.askQuestions = askQuestions;
4
4
  exports.actuate = actuate;
5
5
  exports.addSchemaToDataConnectYaml = addSchemaToDataConnectYaml;
6
6
  const clc = require("colorette");
7
- const fs = require("fs-extra");
8
7
  const path_1 = require("path");
9
8
  const yaml = require("yaml");
10
9
  const prompt_1 = require("../../../prompt");
@@ -14,11 +13,15 @@ const names_1 = require("../../../dataconnect/names");
14
13
  const experiments = require("../../../experiments");
15
14
  const cloudbilling_1 = require("../../../gcp/cloudbilling");
16
15
  const track_1 = require("../../../track");
17
- async function askQuestions(setup, config) {
16
+ const functions = require("../functions");
17
+ const templates_1 = require("../../../templates");
18
+ const SCHEMA_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/secondary_schema.gql");
19
+ async function askQuestions(setup, config, options) {
18
20
  const resolverInfo = {
19
21
  id: "",
20
22
  uri: "",
21
23
  serviceInfo: {},
24
+ shouldInitFunctions: false,
22
25
  };
23
26
  const serviceInfos = await (0, load_1.loadAll)(setup.projectId || "", config);
24
27
  if (!serviceInfos.length) {
@@ -50,8 +53,15 @@ async function askQuestions(setup, config) {
50
53
  " as the custom resolver URL. To change this, update your " +
51
54
  clc.bold(`dataconnect.yaml`) +
52
55
  " later.");
56
+ resolverInfo.shouldInitFunctions = await (0, prompt_1.confirm)({
57
+ message: "Would you like to proceed with initializing a functions codebase with sample custom resolvers code?",
58
+ default: true,
59
+ });
53
60
  setup.featureInfo = setup.featureInfo || {};
54
61
  setup.featureInfo.dataconnectResolver = resolverInfo;
62
+ if (resolverInfo.shouldInitFunctions) {
63
+ await functions.askQuestions(setup, config, options);
64
+ }
55
65
  }
56
66
  async function actuate(setup, config) {
57
67
  if (!experiments.isEnabled("fdcwebhooks")) {
@@ -76,6 +86,9 @@ async function actuate(setup, config) {
76
86
  : "missing",
77
87
  }, Date.now() - startTime);
78
88
  }
89
+ if (resolverInfo.shouldInitFunctions) {
90
+ await functions.actuate(setup, config);
91
+ }
79
92
  }
80
93
  function actuateWithInfo(config, info) {
81
94
  const dataConnectYaml = JSON.parse(JSON.stringify(info.serviceInfo?.dataConnectYaml));
@@ -83,8 +96,9 @@ function actuateWithInfo(config, info) {
83
96
  info.serviceInfo.dataConnectYaml = dataConnectYaml;
84
97
  const dataConnectYamlContents = yaml.stringify(dataConnectYaml);
85
98
  const dataConnectYamlPath = (0, path_1.join)(info.serviceInfo.sourceDirectory, "dataconnect.yaml");
99
+ const dataConnectSchemaPath = (0, path_1.join)(info.serviceInfo.sourceDirectory, `schema_${info.id}`, "schema.gql");
86
100
  config.writeProjectFile((0, path_1.relative)(config.projectDir, dataConnectYamlPath), dataConnectYamlContents);
87
- fs.ensureFileSync((0, path_1.join)(info.serviceInfo.sourceDirectory, `schema_${info.id}`, "schema.gql"));
101
+ config.writeProjectFile((0, path_1.relative)(config.projectDir, dataConnectSchemaPath), SCHEMA_TEMPLATE);
88
102
  }
89
103
  function addSchemaToDataConnectYaml(dataConnectYaml, info) {
90
104
  const secondarySchema = {