firebase-tools 15.15.0 → 15.16.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 (54) hide show
  1. package/lib/agentSkills.js +3 -2
  2. package/lib/apphosting/backend.js +7 -19
  3. package/lib/apphosting/localbuilds.js +32 -4
  4. package/lib/apphosting/prompts.js +32 -11
  5. package/lib/apphosting/secrets/index.js +43 -0
  6. package/lib/apphosting/yaml.js +2 -4
  7. package/lib/bin/firebase.js +10 -0
  8. package/lib/bin/mcp.js +12 -1
  9. package/lib/commands/apphosting-backends-create.js +4 -10
  10. package/lib/commands/dataconnect-sdk-generate.js +2 -2
  11. package/lib/commands/deploy.js +6 -1
  12. package/lib/config.js +1 -0
  13. package/lib/dataconnect/build.js +6 -0
  14. package/lib/dataconnect/names.js +1 -1
  15. package/lib/deploy/apphosting/prepare.js +5 -2
  16. package/lib/deploy/firestore/prepare.js +1 -1
  17. package/lib/deploy/functions/backend.js +22 -14
  18. package/lib/deploy/functions/prepare.js +8 -2
  19. package/lib/deploy/functions/release/fabricator.js +26 -14
  20. package/lib/deploy/functions/runtimes/python/index.js +3 -0
  21. package/lib/deploy/functions/runtimes/supported/types.js +6 -0
  22. package/lib/deploy/hosting/prepare.js +1 -0
  23. package/lib/emulator/adminSdkConfig.js +2 -1
  24. package/lib/emulator/apphosting/serve.js +2 -39
  25. package/lib/emulator/commandUtils.js +4 -1
  26. package/lib/emulator/controller.js +4 -3
  27. package/lib/emulator/dataconnectEmulator.js +7 -4
  28. package/lib/emulator/downloadableEmulatorInfo.json +30 -30
  29. package/lib/emulator/functionsEmulator.js +12 -6
  30. package/lib/emulator/functionsEmulatorRuntime.js +12 -5
  31. package/lib/emulator/functionsRuntimeWorker.js +6 -3
  32. package/lib/experiments.js +6 -1
  33. package/lib/frameworks/angular/index.js +6 -1
  34. package/lib/gcp/rules.js +8 -4
  35. package/lib/init/features/agentSkills.js +2 -2
  36. package/lib/init/features/apphosting.js +2 -8
  37. package/lib/init/features/dataconnect/index.js +26 -15
  38. package/lib/init/features/dataconnect/sdk.js +8 -3
  39. package/lib/init/features/firestore/rules.js +2 -1
  40. package/lib/init/features/functions/dart.js +2 -0
  41. package/lib/init/features/storage/rules.js +2 -1
  42. package/lib/mcp/apps/update_environment/mcp-app.js +138 -0
  43. package/lib/mcp/index.js +57 -2
  44. package/lib/mcp/resources/index.js +2 -0
  45. package/lib/mcp/resources/update_environment_ui.js +32 -0
  46. package/lib/mcp/tools/core/get_security_rules.js +2 -1
  47. package/lib/mcp/util.js +19 -0
  48. package/lib/rulesDeploy.js +35 -16
  49. package/lib/tsconfig.compile.tsbuildinfo +1 -1
  50. package/lib/tsconfig.publish.tsbuildinfo +1 -1
  51. package/package.json +1 -1
  52. package/schema/firebase-config.json +4 -2
  53. package/templates/init/functions/dart/analysis_options.yaml +30 -0
  54. package/templates/init/functions/dart/pubspec.yaml +1 -0
@@ -74,6 +74,7 @@ async function prepare(context, options) {
74
74
  return Promise.resolve();
75
75
  }
76
76
  const versions = await Promise.all(configs.map(async (config) => {
77
+ await api.getSite(context.projectId, config.site);
77
78
  const labels = {
78
79
  ...deploymentTool.labels(),
79
80
  };
@@ -6,6 +6,7 @@ const api_1 = require("../api");
6
6
  const apiv2 = require("../apiv2");
7
7
  const configstore_1 = require("../configstore");
8
8
  const error_1 = require("../error");
9
+ const error_2 = require("../error");
9
10
  const logger_1 = require("../logger");
10
11
  const constants_1 = require("./constants");
11
12
  const _CONFIGSTORE_KEY = "adminsdkconfig";
@@ -50,7 +51,7 @@ async function getProjectAdminSdkConfig(projectId) {
50
51
  }
51
52
  catch (err) {
52
53
  throw new error_1.FirebaseError(`Failed to get Admin SDK for Firebase project ${projectId}. ` +
53
- "Please make sure the project exists and your account has permission to access it.", { exit: 2, original: err });
54
+ "Please make sure the project exists and your account has permission to access it.", { exit: 2, original: (0, error_2.getError)(err) });
54
55
  }
55
56
  }
56
57
  function setCacheAdminSdkConfig(projectId, config) {
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.start = start;
4
4
  exports.getEmulatorEnvs = getEmulatorEnvs;
5
5
  const net_1 = require("net");
6
- const clc = require("colorette");
7
6
  const portUtils_1 = require("../portUtils");
8
7
  const developmentServer_1 = require("./developmentServer");
9
8
  const constants_1 = require("../constants");
@@ -15,7 +14,7 @@ const projectPath_1 = require("../../projectPath");
15
14
  const registry_1 = require("../registry");
16
15
  const env_1 = require("../env");
17
16
  const error_1 = require("../../error");
18
- const secrets = require("../../gcp/secretManager");
17
+ const index_1 = require("../../apphosting/secrets/index");
19
18
  const utils_1 = require("../../utils");
20
19
  const apphosting = require("../../gcp/apphosting");
21
20
  const constants_2 = require("../constants");
@@ -24,42 +23,6 @@ const child_process_1 = require("child_process");
24
23
  const semver_1 = require("semver");
25
24
  const utils_2 = require("../../apphosting/utils");
26
25
  const apps_1 = require("../../management/apps");
27
- const secretResourceRegex = /^projects\/([^/]+)\/secrets\/([^/]+)(?:\/versions\/((?:latest)|\d+))?$/;
28
- const secretShorthandRegex = /^([^/@]+)(?:@((?:latest)|\d+))?$/;
29
- async function loadSecret(project, name) {
30
- let projectId;
31
- let secretId;
32
- let version;
33
- const match = secretResourceRegex.exec(name);
34
- if (match) {
35
- projectId = match[1];
36
- secretId = match[2];
37
- version = match[3] || "latest";
38
- }
39
- else {
40
- const match = secretShorthandRegex.exec(name);
41
- if (!match) {
42
- throw new error_1.FirebaseError(`Invalid secret name: ${name}`);
43
- }
44
- if (!project) {
45
- throw new error_1.FirebaseError(`Cannot load secret ${match[1]} without a project. ` +
46
- `Please use ${clc.bold("firebase use")} or pass the --project flag.`);
47
- }
48
- projectId = project;
49
- secretId = match[1];
50
- version = match[2] || "latest";
51
- }
52
- try {
53
- return await secrets.accessSecretVersion(projectId, secretId, version);
54
- }
55
- catch (err) {
56
- if (err?.original?.code === 403 || err?.original?.context?.response?.statusCode === 403) {
57
- (0, utils_1.logLabeledError)(types_1.Emulators.APPHOSTING, `Permission denied to access secret ${secretId}. Use ` +
58
- `${clc.bold("firebase apphosting:secrets:grantaccess")} to get permissions.`);
59
- }
60
- throw err;
61
- }
62
- }
63
26
  async function start(options) {
64
27
  const hostname = constants_1.DEFAULT_HOST;
65
28
  let port = options?.port ?? constants_1.DEFAULT_PORTS.apphosting;
@@ -94,7 +57,7 @@ async function start(options) {
94
57
  const apphostingLocalConfig = await (0, config_1.getLocalAppHostingConfiguration)(backendRoot);
95
58
  const resolveEnv = Object.entries(apphostingLocalConfig.env).map(async ([key, value]) => [
96
59
  key,
97
- value.value ? value.value : await loadSecret(options?.projectId, value.secret),
60
+ value.value ? value.value : await (0, index_1.loadSecret)(options?.projectId, value.secret),
98
61
  ]);
99
62
  const environmentVariablesToInject = {
100
63
  NODE_ENV: process.env.NODE_ENV,
@@ -23,6 +23,7 @@ const requireAuth_1 = require("../requireAuth");
23
23
  const requireConfig_1 = require("../requireConfig");
24
24
  const types_1 = require("./types");
25
25
  const error_1 = require("../error");
26
+ const error_2 = require("../error");
26
27
  const registry_1 = require("./registry");
27
28
  const projectUtils_1 = require("../projectUtils");
28
29
  const prompt_1 = require("../prompt");
@@ -330,7 +331,9 @@ async function checkJavaMajorVersion() {
330
331
  });
331
332
  }
332
333
  catch (err) {
333
- return reject(new error_1.FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, { original: err }));
334
+ return reject(new error_1.FirebaseError(`Could not spawn \`java -version\`. ${JAVA_HINT}`, {
335
+ original: (0, error_2.getError)(err),
336
+ }));
334
337
  }
335
338
  let output = "";
336
339
  let error = "";
@@ -20,6 +20,7 @@ const types_1 = require("./types");
20
20
  const constants_1 = require("./constants");
21
21
  const functionsEmulator_1 = require("./functionsEmulator");
22
22
  const error_1 = require("../error");
23
+ const error_2 = require("../error");
23
24
  const projectUtils_1 = require("../projectUtils");
24
25
  const commandUtils = require("./commandUtils");
25
26
  const hub_1 = require("./hub");
@@ -118,7 +119,7 @@ function shouldStart(options, name) {
118
119
  return true;
119
120
  }
120
121
  catch (err) {
121
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("ERROR", "functions", `Failed to start Functions emulator: ${err.message}`);
122
+ emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("ERROR", "functions", `Failed to start Functions emulator: ${(0, error_2.getErrMsg)(err)}`);
122
123
  return false;
123
124
  }
124
125
  }
@@ -501,7 +502,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
501
502
  }
502
503
  }
503
504
  catch (e) {
504
- databaseLogger.log("DEBUG", `Failed to retrieve default database instance: ${JSON.stringify(e)}`);
505
+ databaseLogger.log("DEBUG", `Failed to retrieve default database instance: ${(0, error_2.getErrMsg)(e)}`);
505
506
  }
506
507
  const rc = dbRulesConfig.normalizeRulesConfig(dbRulesConfig.getRulesConfig(projectId, options), options);
507
508
  logger_1.logger.debug("database rules config: ", JSON.stringify(rc));
@@ -770,7 +771,7 @@ async function exportEmulatorData(exportPath, options, initiatedBy) {
770
771
  catch (e) {
771
772
  throw new error_1.FirebaseError("Export request failed, see emulator logs for more information.", {
772
773
  exit: 1,
773
- original: e,
774
+ original: (0, error_2.getError)(e),
774
775
  });
775
776
  }
776
777
  utils.logSuccess("Export complete");
@@ -11,6 +11,7 @@ const constants_1 = require("./constants");
11
11
  const downloadableEmulators_1 = require("./downloadableEmulators");
12
12
  const types_1 = require("./types");
13
13
  const error_1 = require("../error");
14
+ const error_2 = require("../error");
14
15
  const emulatorLogger_1 = require("./emulatorLogger");
15
16
  const types_2 = require("../dataconnect/types");
16
17
  const portUtils_1 = require("./portUtils");
@@ -48,7 +49,7 @@ class DataConnectEmulator {
48
49
  }
49
50
  }
50
51
  catch (err) {
51
- this.logger.log("DEBUG", `'fdc build' failed with error: ${err.message}`);
52
+ this.logger.log("DEBUG", `'fdc build' failed with error: ${(0, error_2.getErrMsg)(err)}`);
52
53
  }
53
54
  const env = await DataConnectEmulator.getEnv(this.args.account, this.args.extraEnv);
54
55
  await (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, {
@@ -239,7 +240,7 @@ class DataConnectEmulator {
239
240
  if (i === MAX_RETRIES) {
240
241
  throw err;
241
242
  }
242
- this.logger.logLabeled("DEBUG", "SQL Connect", `Retrying connectToPostgress call (${i} of ${MAX_RETRIES} attempts): ${err}`);
243
+ this.logger.logLabeled("DEBUG", "SQL Connect", `Retrying connectToPostgress call (${i} of ${MAX_RETRIES} attempts): ${(0, error_2.getErrMsg)(err)}`);
243
244
  await new Promise((resolve) => setTimeout(resolve, 2000));
244
245
  }
245
246
  }
@@ -270,8 +271,10 @@ class DataConnectEmulatorClient {
270
271
  return res;
271
272
  }
272
273
  catch (err) {
273
- if (err.status === 500) {
274
- throw new error_1.FirebaseError(`SQL Connect emulator: ${err?.context?.body?.message}`);
274
+ const status = (0, error_2.getErrStatus)(err);
275
+ if (status === 500) {
276
+ const message = err?.context?.body?.message;
277
+ throw new error_1.FirebaseError(`SQL Connect emulator: ${message || String(err)}`);
275
278
  }
276
279
  throw err;
277
280
  }
@@ -8,12 +8,12 @@
8
8
  "downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
9
9
  },
10
10
  "firestore": {
11
- "version": "1.20.4",
12
- "expectedSize": 136551710,
13
- "expectedChecksum": "8397dac992e02454914dec28d23b6ed8",
14
- "expectedChecksumSHA256": "49ee7814741b481ae5becfdaa7b55693bdf7a03363cd184e35c36c1595a9643",
15
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.4.jar",
16
- "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.4.jar"
11
+ "version": "1.21.0",
12
+ "expectedSize": 138093843,
13
+ "expectedChecksum": "6449ec69bc1228e36591ff6dad94fe1a",
14
+ "expectedChecksumSHA256": "c3d3680a89d946a90a027365ea14c26c6472a162bcf37f099bbb1ebd66d25e8e",
15
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.21.0.jar",
16
+ "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.21.0.jar"
17
17
  },
18
18
  "storage": {
19
19
  "version": "1.1.3",
@@ -54,36 +54,36 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.4.5",
58
- "expectedSize": 32269984,
59
- "expectedChecksum": "5c50dbd6f508998f748195691b5b0678",
60
- "expectedChecksumSHA256": "2053e5c47dbe52c923be196cfd7ff70b31f9e2fbde0e7eaeb20c31ee91e8a8b2",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.5",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5"
57
+ "version": "3.4.6",
58
+ "expectedSize": 32361376,
59
+ "expectedChecksum": "85cf160307a0ff39b8e625676b89789e",
60
+ "expectedChecksumSHA256": "4f66d53776975c32df3c86870602965d2a5a495a14d86906cdbea84c8e40156b",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.6",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.4.5",
66
- "expectedSize": 30437250,
67
- "expectedChecksum": "40f531afa3664284bc319f9721388816",
68
- "expectedChecksumSHA256": "8dd59223a43b0e07ac2c7e40abb5e1a56ea7b7b787d23ceed20f8fe9af9c88e0",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.5",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5"
65
+ "version": "3.4.6",
66
+ "expectedSize": 30504594,
67
+ "expectedChecksum": "c1e5c18caa1fadcaf813c98bc05a8d77",
68
+ "expectedChecksumSHA256": "1934ec8f6e8788a5521801072df278950b9525b0294c97cc009e65a7446d5567",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.6",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6"
71
71
  },
72
72
  "win32": {
73
- "version": "3.4.5",
74
- "expectedSize": 32311296,
75
- "expectedChecksum": "406caa49a4d48c03ade3da4a4a6b08b3",
76
- "expectedChecksumSHA256": "e6e392f5bc3c32faa95d9b1139497492e51c46cb354e30e65c20d575ee3ba9cf",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.5",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5.exe"
73
+ "version": "3.4.6",
74
+ "expectedSize": 32401408,
75
+ "expectedChecksum": "c57c0ca3c50f358122ac741e501c9fde",
76
+ "expectedChecksumSHA256": "ed4bde0ebf9499ffd8915091f9ec3886a1c5ba145f54261b1c8c701a38268984",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.6",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.4.5",
82
- "expectedSize": 31428792,
83
- "expectedChecksum": "2e09522cff69142b6882e71516ad0e3c",
84
- "expectedChecksumSHA256": "86565d7724762e45a76a4627791b60248cfea81f332d45709d69be0e71a3c6ec",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.5",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.5"
81
+ "version": "3.4.6",
82
+ "expectedSize": 31518904,
83
+ "expectedChecksum": "b05791a643618dd8fbdb5e56b474c45b",
84
+ "expectedChecksumSHA256": "716ca10ddb2341873fc0febcffbc84420ac1f20262c3d08f9422cee025d1f60e",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.6",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6"
87
87
  }
88
88
  }
89
89
  }
@@ -522,7 +522,8 @@ class FunctionsEmulator {
522
522
  await this.startRuntime(emulatableBackend);
523
523
  }
524
524
  catch (e) {
525
- this.logger.logLabeled("ERROR", `Failed to start functions in ${emulatableBackend.functionsDir}: ${e}`);
525
+ const message = e instanceof Error ? e.message : String(e);
526
+ this.logger.logLabeled("ERROR", `Failed to start functions in ${emulatableBackend.functionsDir}: ${message}`);
526
527
  }
527
528
  }
528
529
  }
@@ -716,7 +717,7 @@ class FunctionsEmulator {
716
717
  await client.post(apiPath, bundle, { headers: { Authorization: "Bearer owner" } });
717
718
  }
718
719
  catch (err) {
719
- this.logger.log("WARN", "Error adding Realtime Database function: " + err);
720
+ this.logger.log("WARN", "Error adding Realtime Database function: " + String(err));
720
721
  throw err;
721
722
  }
722
723
  return true;
@@ -779,7 +780,7 @@ class FunctionsEmulator {
779
780
  signature === "cloudevent" ? await client.post(path, bundle) : await client.put(path, bundle);
780
781
  }
781
782
  catch (err) {
782
- this.logger.log("WARN", "Error adding firestore function: " + err);
783
+ this.logger.log("WARN", "Error adding firestore function: " + String(err));
783
784
  throw err;
784
785
  }
785
786
  return true;
@@ -1034,8 +1035,12 @@ class FunctionsEmulator {
1034
1035
  secretEnvs = functionsEnv.parseStrict(data);
1035
1036
  }
1036
1037
  catch (e) {
1037
- if (e.code !== "ENOENT") {
1038
- this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
1038
+ if (typeof e === "object" && e !== null && "code" in e) {
1039
+ const code = e.code;
1040
+ if (code !== "ENOENT") {
1041
+ const message = e instanceof Error ? e.message : String(e);
1042
+ this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${message}`);
1043
+ }
1039
1044
  }
1040
1045
  }
1041
1046
  const secrets = trigger?.secretEnvironmentVariables || backend.secretEnv;
@@ -1273,8 +1278,9 @@ class FunctionsEmulator {
1273
1278
  await this.startRuntime(record.backend, trigger);
1274
1279
  }
1275
1280
  catch (e) {
1281
+ const message = e instanceof Error ? e.message : String(e);
1276
1282
  this.logger.logLabeled("ERROR", `Failed to handle request for function ${trigger.id}`);
1277
- this.logger.logLabeled("ERROR", `Failed to start functions in ${record.backend.functionsDir}: ${e}`);
1283
+ this.logger.logLabeled("ERROR", `Failed to start functions in ${record.backend.functionsDir}: ${message}`);
1278
1284
  return;
1279
1285
  }
1280
1286
  }
@@ -6,6 +6,7 @@ const bodyParser = require("body-parser");
6
6
  const url_1 = require("url");
7
7
  const _ = require("lodash");
8
8
  const types_1 = require("./types");
9
+ const error_1 = require("../error");
9
10
  const constants_1 = require("./constants");
10
11
  const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
11
12
  const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
@@ -475,8 +476,14 @@ async function loadTriggers() {
475
476
  triggerModule = require(process.cwd());
476
477
  }
477
478
  catch (err) {
478
- if (err.code !== "ERR_REQUIRE_ESM") {
479
- await moduleResolutionDetective(err);
479
+ if (typeof err === "object" && err !== null && "code" in err) {
480
+ const code = err.code;
481
+ if (code !== "ERR_REQUIRE_ESM") {
482
+ await moduleResolutionDetective(err);
483
+ throw err;
484
+ }
485
+ }
486
+ else {
480
487
  throw err;
481
488
  }
482
489
  const modulePath = require.resolve(process.cwd());
@@ -528,7 +535,7 @@ async function main() {
528
535
  functionModule = await loadTriggers();
529
536
  }
530
537
  catch (e) {
531
- new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`).log();
538
+ new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${(0, error_1.getErrMsg)(e)}`).log();
532
539
  await flushAndExit(1);
533
540
  }
534
541
  const app = express();
@@ -586,8 +593,8 @@ async function main() {
586
593
  }
587
594
  }
588
595
  catch (err) {
589
- new types_1.EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log();
590
- res.status(500).send(err.message);
596
+ new types_1.EmulatorLog("FATAL", "runtime-error", (0, error_1.getErrStack)(err)).log();
597
+ res.status(500).send((0, error_1.getErrMsg)(err));
591
598
  }
592
599
  });
593
600
  app.listen(process.env.PORT, () => {
@@ -206,9 +206,12 @@ class RuntimeWorker {
206
206
  break;
207
207
  }
208
208
  catch (err) {
209
- if (["ECONNREFUSED", "ENOENT"].includes(err?.code)) {
210
- await sleep(100);
211
- continue;
209
+ if (typeof err === "object" && err !== null && "code" in err) {
210
+ const code = err.code;
211
+ if (typeof code === "string" && ["ECONNREFUSED", "ENOENT"].includes(code)) {
212
+ await sleep(100);
213
+ continue;
214
+ }
212
215
  }
213
216
  throw err;
214
217
  }
@@ -76,7 +76,7 @@ exports.ALL_EXPERIMENTS = experiments({
76
76
  },
77
77
  dartfunctions: {
78
78
  shortDescription: "Enable Dart Functions.",
79
- public: false,
79
+ public: true,
80
80
  default: false,
81
81
  },
82
82
  emulatoruisnapshot: {
@@ -158,6 +158,11 @@ exports.ALL_EXPERIMENTS = experiments({
158
158
  default: false,
159
159
  public: true,
160
160
  },
161
+ mcpapps: {
162
+ shortDescription: "Enables MCP Apps features",
163
+ fullDescription: "Enables MCP Apps features, including returning UI resource URIs.",
164
+ public: true,
165
+ },
161
166
  fdcift: {
162
167
  shortDescription: "Enable instrumentless trial for SQL Connect",
163
168
  default: true,
@@ -30,7 +30,12 @@ async function discover(dir) {
30
30
  if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "angular.json"))))
31
31
  return;
32
32
  const version = (0, utils_2.getAngularVersion)(dir);
33
- return { mayWantBackend: true, version };
33
+ const mayWantBackend = !!(0, utils_1.findDependency)("@angular/platform-server", {
34
+ cwd: dir,
35
+ depth: 0,
36
+ omitDev: false,
37
+ });
38
+ return { mayWantBackend, version };
34
39
  }
35
40
  function init(setup, config) {
36
41
  (0, child_process_1.execSync)(`npx --yes -p @angular/cli@"${exports.supportedRange}" ng new ${setup.projectId} --directory ${setup.featureInfo.hosting.source} --skip-git`, {
package/lib/gcp/rules.js CHANGED
@@ -28,8 +28,7 @@ function _handleErrorResponse(response) {
28
28
  code: 2,
29
29
  });
30
30
  }
31
- async function getLatestRulesetName(projectId, service, resourceName) {
32
- const releases = await listAllReleases(projectId);
31
+ async function getLatestRulesetName(projectId, service, releases, resourceName) {
33
32
  let prefix = `projects/${projectId}/releases/${service}`;
34
33
  if (resourceName) {
35
34
  prefix += `/${resourceName}`;
@@ -111,8 +110,13 @@ async function deleteRuleset(projectId, id) {
111
110
  }
112
111
  return _handleErrorResponse(response);
113
112
  }
114
- async function createRuleset(projectId, files) {
115
- const payload = { source: { files } };
113
+ async function createRuleset(projectId, files, attachmentPoint) {
114
+ const payload = {
115
+ source: { files },
116
+ };
117
+ if (attachmentPoint) {
118
+ payload.attachment_point = attachmentPoint;
119
+ }
116
120
  const response = await apiClient.post(`/projects/${projectId}/rulesets`, payload, { skipLog: { body: true } });
117
121
  if (response.status === 200) {
118
122
  logger_1.logger.debug("[rules] created ruleset", response.body.name);
@@ -5,10 +5,10 @@ exports.actuate = actuate;
5
5
  const agentSkills_1 = require("../../agentSkills");
6
6
  const logger_1 = require("../../logger");
7
7
  const error_1 = require("../../error");
8
- async function askQuestions(setup) {
8
+ async function askQuestions(setup, config, options) {
9
9
  try {
10
10
  logger_1.logger.info("If you are using an AI coding agent, Firebase Agent Skills make it an expert at Firebase.");
11
- const shouldInstall = await (0, agentSkills_1.promptForAgentSkills)();
11
+ const shouldInstall = await (0, agentSkills_1.promptForAgentSkills)(options);
12
12
  setup.featureInfo = setup.featureInfo || {};
13
13
  setup.featureInfo.agentSkills = { shouldInstall };
14
14
  }
@@ -52,16 +52,10 @@ async function doSetup(setup, config, options) {
52
52
  if (!webApp) {
53
53
  utils.logWarning(`Firebase web app not set`);
54
54
  }
55
- const experiments = await dynamicImport("./experiments");
56
55
  const prompts = await dynamicImport("./apphosting/prompts");
57
- let runtime = experiments.isEnabled("abiu") ? prompts.DEFAULT_RUNTIME : undefined;
58
- let automaticBaseImageUpdatesDisabled = experiments.isEnabled("abiu") ? false : undefined;
59
- if (experiments.isEnabled("abiu") && !options.nonInteractive) {
60
- runtime = await prompts.promptRuntime(projectId, location);
61
- automaticBaseImageUpdatesDisabled = !(await prompts.promptAutomaticBaseImageUpdates());
62
- }
56
+ const runtime = await prompts.resolveRuntime(projectId, location, options.nonInteractive);
63
57
  const createBackendSpinner = ora("Creating your new backend...").start();
64
- const backend = await (0, backend_1.createBackend)(projectId, location, backendId, null, undefined, webApp?.id, "/", runtime, automaticBaseImageUpdatesDisabled);
58
+ const backend = await (0, backend_1.createBackend)(projectId, location, backendId, null, undefined, webApp?.id, "/", runtime);
65
59
  createBackendSpinner.succeed(`Successfully created backend!\n\t${backend.name}\n`);
66
60
  }
67
61
  (0, utils_1.logBullet)(`${clc.yellow("===")} Deploy local source setup`);
@@ -55,7 +55,7 @@ const templateServiceInfo = {
55
55
  ],
56
56
  seedDataGql: SEED_DATA_TEMPLATE,
57
57
  };
58
- async function askQuestions(setup) {
58
+ async function askQuestions(setup, config, options) {
59
59
  const info = {
60
60
  flow: "",
61
61
  appDescription: "",
@@ -67,14 +67,15 @@ async function askQuestions(setup) {
67
67
  };
68
68
  if (setup.projectId) {
69
69
  await (0, ensureApis_1.ensureApis)(setup.projectId);
70
- await promptForExistingServices(setup, info);
71
- if (!info.serviceGql) {
70
+ await promptForExistingServices(setup, info, options);
71
+ if (!info.serviceGql && !options.nonInteractive) {
72
72
  if (!configstore_1.configstore.get("gemini")) {
73
73
  (0, utils_1.logBullet)("Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data");
74
74
  }
75
75
  const wantToGenerate = await (0, prompt_1.confirm)({
76
76
  message: "Do you want to generate schema and queries with Gemini?",
77
77
  default: false,
78
+ nonInteractive: options.nonInteractive,
78
79
  });
79
80
  if (wantToGenerate) {
80
81
  configstore_1.configstore.set("gemini", true);
@@ -87,14 +88,15 @@ async function askQuestions(setup) {
87
88
  }
88
89
  return "Please enter a description for your app idea.";
89
90
  },
91
+ nonInteractive: options.nonInteractive,
90
92
  });
91
93
  }
92
94
  }
93
- await promptForCloudSQL(setup, info);
95
+ await promptForCloudSQL(setup, info, options);
94
96
  }
95
97
  setup.featureInfo = setup.featureInfo || {};
96
98
  setup.featureInfo.dataconnect = info;
97
- await sdk.askQuestions(setup);
99
+ await sdk.askQuestions(setup, config, options);
98
100
  }
99
101
  async function actuate(setup, config, options) {
100
102
  const dir = config.get("dataconnect.source", "dataconnect");
@@ -337,7 +339,7 @@ function subConnectorYamlValues(replacementValues) {
337
339
  }
338
340
  return replaced;
339
341
  }
340
- async function promptForExistingServices(setup, info) {
342
+ async function promptForExistingServices(setup, info, options) {
341
343
  if (!setup.projectId) {
342
344
  return;
343
345
  }
@@ -345,7 +347,7 @@ async function promptForExistingServices(setup, info) {
345
347
  if (!existingServices.length) {
346
348
  return;
347
349
  }
348
- const choice = await chooseExistingService(existingServices);
350
+ const choice = await chooseExistingService(existingServices, options);
349
351
  if (!choice) {
350
352
  const existingServiceIds = existingServices.map((s) => s.name.split("/").pop());
351
353
  info.serviceId = (0, utils_1.newUniqueId)(defaultServiceId(), existingServiceIds);
@@ -423,7 +425,7 @@ async function downloadService(info, serviceName) {
423
425
  });
424
426
  }
425
427
  }
426
- async function chooseExistingService(existing) {
428
+ async function chooseExistingService(existing, options) {
427
429
  const fdcConnector = (0, utils_1.envOverride)("FDC_CONNECTOR", "");
428
430
  const fdcService = (0, utils_1.envOverride)("FDC_SERVICE", "");
429
431
  const serviceEnvVar = fdcConnector || fdcService;
@@ -452,9 +454,11 @@ async function chooseExistingService(existing) {
452
454
  return await (0, prompt_1.select)({
453
455
  message: "Your project already has existing services. Which would you like to set up local files for?",
454
456
  choices,
457
+ default: choices[0].value,
458
+ nonInteractive: options.nonInteractive,
455
459
  });
456
460
  }
457
- async function promptForCloudSQL(setup, info) {
461
+ async function promptForCloudSQL(setup, info, options) {
458
462
  if (!setup.projectId) {
459
463
  return;
460
464
  }
@@ -496,6 +500,8 @@ async function promptForCloudSQL(setup, info) {
496
500
  info.cloudSqlInstanceId = await (0, prompt_1.select)({
497
501
  message: `Which CloudSQL instance would you like to use?`,
498
502
  choices,
503
+ default: choices[0].value,
504
+ nonInteractive: options.nonInteractive,
499
505
  });
500
506
  if (info.cloudSqlInstanceId !== "") {
501
507
  info.flow += "_pick_existing_csql";
@@ -510,16 +516,20 @@ async function promptForCloudSQL(setup, info) {
510
516
  info.cloudSqlInstanceId = await (0, prompt_1.input)({
511
517
  message: `What ID would you like to use for your new CloudSQL instance?`,
512
518
  default: (0, utils_1.newUniqueId)(`${defaultServiceId().toLowerCase()}-fdc`, instances.map((i) => i.name)),
519
+ nonInteractive: options.nonInteractive,
513
520
  });
514
521
  }
515
522
  }
516
523
  }
517
524
  if (info.locationId === "") {
518
- await promptForLocation(setup, info);
519
- info.shouldProvisionCSQL = await (0, prompt_1.confirm)({
520
- message: `Would you like to provision your ${freeTrialAvailable ? "free trial " : ""}Cloud SQL instance and database now?`,
521
- default: true,
522
- });
525
+ await promptForLocation(setup, info, options);
526
+ info.shouldProvisionCSQL =
527
+ !options.nonInteractive &&
528
+ (await (0, prompt_1.confirm)({
529
+ message: `Would you like to provision your ${freeTrialAvailable ? "free trial " : ""}Cloud SQL instance and database now?`,
530
+ default: !options.nonInteractive,
531
+ nonInteractive: options.nonInteractive,
532
+ }));
523
533
  }
524
534
  if (info.cloudSqlInstanceId !== "" && info.cloudSqlDatabase === "") {
525
535
  try {
@@ -533,13 +543,14 @@ async function promptForCloudSQL(setup, info) {
533
543
  }
534
544
  return;
535
545
  }
536
- async function promptForLocation(setup, info) {
546
+ async function promptForLocation(setup, info, options) {
537
547
  if (info.locationId === "") {
538
548
  const choices = await locationChoices(setup);
539
549
  info.locationId = await (0, prompt_1.select)({
540
550
  message: "What location would you like to use?",
541
551
  choices,
542
552
  default: exports.FDC_DEFAULT_REGION,
553
+ nonInteractive: options.nonInteractive,
543
554
  });
544
555
  }
545
556
  }