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.
- package/lib/agentSkills.js +3 -2
- package/lib/apphosting/backend.js +7 -19
- package/lib/apphosting/localbuilds.js +32 -4
- package/lib/apphosting/prompts.js +32 -11
- package/lib/apphosting/secrets/index.js +43 -0
- package/lib/apphosting/yaml.js +2 -4
- package/lib/bin/firebase.js +10 -0
- package/lib/bin/mcp.js +12 -1
- package/lib/commands/apphosting-backends-create.js +4 -10
- package/lib/commands/dataconnect-sdk-generate.js +2 -2
- package/lib/commands/deploy.js +6 -1
- package/lib/config.js +1 -0
- package/lib/dataconnect/build.js +6 -0
- package/lib/dataconnect/names.js +1 -1
- package/lib/deploy/apphosting/prepare.js +5 -2
- package/lib/deploy/firestore/prepare.js +1 -1
- package/lib/deploy/functions/backend.js +22 -14
- package/lib/deploy/functions/prepare.js +8 -2
- package/lib/deploy/functions/release/fabricator.js +26 -14
- package/lib/deploy/functions/runtimes/python/index.js +3 -0
- package/lib/deploy/functions/runtimes/supported/types.js +6 -0
- package/lib/deploy/hosting/prepare.js +1 -0
- package/lib/emulator/adminSdkConfig.js +2 -1
- package/lib/emulator/apphosting/serve.js +2 -39
- package/lib/emulator/commandUtils.js +4 -1
- package/lib/emulator/controller.js +4 -3
- package/lib/emulator/dataconnectEmulator.js +7 -4
- package/lib/emulator/downloadableEmulatorInfo.json +30 -30
- package/lib/emulator/functionsEmulator.js +12 -6
- package/lib/emulator/functionsEmulatorRuntime.js +12 -5
- package/lib/emulator/functionsRuntimeWorker.js +6 -3
- package/lib/experiments.js +6 -1
- package/lib/frameworks/angular/index.js +6 -1
- package/lib/gcp/rules.js +8 -4
- package/lib/init/features/agentSkills.js +2 -2
- package/lib/init/features/apphosting.js +2 -8
- package/lib/init/features/dataconnect/index.js +26 -15
- package/lib/init/features/dataconnect/sdk.js +8 -3
- package/lib/init/features/firestore/rules.js +2 -1
- package/lib/init/features/functions/dart.js +2 -0
- package/lib/init/features/storage/rules.js +2 -1
- package/lib/mcp/apps/update_environment/mcp-app.js +138 -0
- package/lib/mcp/index.js +57 -2
- package/lib/mcp/resources/index.js +2 -0
- package/lib/mcp/resources/update_environment_ui.js +32 -0
- package/lib/mcp/tools/core/get_security_rules.js +2 -1
- package/lib/mcp/util.js +19 -0
- package/lib/rulesDeploy.js +35 -16
- package/lib/tsconfig.compile.tsbuildinfo +1 -1
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +4 -2
- package/templates/init/functions/dart/analysis_options.yaml +30 -0
- package/templates/init/functions/dart/pubspec.yaml +1 -0
|
@@ -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
|
|
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}`, {
|
|
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
|
|
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: ${
|
|
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
|
|
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
|
-
|
|
274
|
-
|
|
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.
|
|
12
|
-
"expectedSize":
|
|
13
|
-
"expectedChecksum": "
|
|
14
|
-
"expectedChecksumSHA256": "
|
|
15
|
-
"remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.
|
|
16
|
-
"downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.
|
|
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.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
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.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
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.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
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.
|
|
82
|
-
"expectedSize":
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
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
|
-
|
|
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
|
|
1038
|
-
|
|
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}: ${
|
|
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
|
|
479
|
-
|
|
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
|
|
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",
|
|
590
|
-
res.status(500).send(
|
|
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 (
|
|
210
|
-
|
|
211
|
-
|
|
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
|
}
|
package/lib/experiments.js
CHANGED
|
@@ -76,7 +76,7 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
76
76
|
},
|
|
77
77
|
dartfunctions: {
|
|
78
78
|
shortDescription: "Enable Dart Functions.",
|
|
79
|
-
public:
|
|
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
|
-
|
|
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 = {
|
|
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
|
-
|
|
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
|
|
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 =
|
|
520
|
-
|
|
521
|
-
|
|
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
|
}
|