firebase-tools 14.15.2 → 14.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/commands/firestore-bulkdelete.js +73 -0
- package/lib/commands/firestore-operations-cancel.js +44 -0
- package/lib/commands/firestore-operations-describe.js +29 -0
- package/lib/commands/firestore-operations-list.js +29 -0
- package/lib/commands/firestore-utils.js +15 -0
- package/lib/commands/functions-config-export.js +5 -2
- package/lib/commands/index.js +5 -0
- package/lib/config.js +16 -4
- package/lib/dataconnect/ensureApis.js +3 -3
- package/lib/deploy/functions/deploy.js +4 -3
- package/lib/deploy/functions/prepare.js +8 -6
- package/lib/emulator/commandUtils.js +7 -1
- package/lib/emulator/controller.js +15 -31
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/emulator/hub.js +7 -1
- package/lib/extensions/runtimes/common.js +3 -2
- package/lib/firestore/api.js +45 -0
- package/lib/firestore/pretty-print.js +23 -0
- package/lib/functions/projectConfig.js +69 -9
- package/lib/gcp/cloudfunctions.js +1 -6
- package/lib/gcp/cloudfunctionsv2.js +1 -9
- package/lib/gcp/cloudsql/cloudsqladmin.js +2 -2
- package/lib/init/features/dataconnect/create_app.js +7 -2
- package/lib/init/features/dataconnect/index.js +72 -56
- package/lib/init/features/dataconnect/sdk.js +23 -10
- package/lib/mcp/errors.js +2 -10
- package/lib/mcp/index.js +0 -3
- package/lib/mcp/prompts/crashlytics/connect.js +114 -0
- package/lib/mcp/prompts/crashlytics/index.js +2 -3
- package/lib/mcp/tools/auth/disable_user.js +1 -1
- package/lib/mcp/tools/auth/get_user.js +9 -2
- package/lib/mcp/tools/core/index.js +4 -0
- package/lib/mcp/tools/core/init.js +11 -2
- package/lib/mcp/tools/core/login.js +46 -0
- package/lib/mcp/tools/core/logout.js +62 -0
- package/lib/mcp/tools/dataconnect/index.js +2 -2
- package/lib/mcp/tools/dataconnect/{info.js → list_services.js} +5 -5
- package/lib/mcp/util.js +1 -17
- package/lib/serve/functions.js +4 -3
- package/lib/unzip.js +13 -0
- package/lib/utils.js +17 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +160 -59
- package/lib/mcp/prompts/crashlytics/common.js +0 -10
- package/lib/mcp/prompts/crashlytics/fix_issue.js +0 -89
- package/lib/mcp/prompts/crashlytics/prioritize_issues.js +0 -79
- package/lib/mcp/tools/database/set_rules.js +0 -41
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const fsi = require("../firestore/api");
|
|
6
|
+
const logger_1 = require("../logger");
|
|
7
|
+
const requirePermissions_1 = require("../requirePermissions");
|
|
8
|
+
const types_1 = require("../emulator/types");
|
|
9
|
+
const commandUtils_1 = require("../emulator/commandUtils");
|
|
10
|
+
const prompt_1 = require("../prompt");
|
|
11
|
+
const utils = require("../utils");
|
|
12
|
+
const clc = require("colorette");
|
|
13
|
+
const utils_1 = require("../utils");
|
|
14
|
+
const error_1 = require("../error");
|
|
15
|
+
function confirmationMessage(options, databaseId, collectionIds) {
|
|
16
|
+
const root = `projects/${options.project}/databases/${databaseId}/documents`;
|
|
17
|
+
return ("You are about to delete all documents in the following collection groups: " +
|
|
18
|
+
clc.cyan(collectionIds.map((item) => `"${item}"`).join(", ")) +
|
|
19
|
+
" in " +
|
|
20
|
+
clc.cyan(`"${root}"`) +
|
|
21
|
+
". Are you sure?");
|
|
22
|
+
}
|
|
23
|
+
exports.command = new command_1.Command("firestore:bulkdelete")
|
|
24
|
+
.description("managed bulk delete service to delete data from one or more collection groups")
|
|
25
|
+
.option("--database <databaseName>", 'Database ID for database to delete from. "(default)" if none is provided.')
|
|
26
|
+
.option("--collection-ids <collectionIds>", "A comma-separated list of collection group IDs to delete. Deletes all documents in the specified collection groups.")
|
|
27
|
+
.before(requirePermissions_1.requirePermissions, ["datastore.databases.bulkDeleteDocuments"])
|
|
28
|
+
.before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
|
|
29
|
+
.action(async (options) => {
|
|
30
|
+
if (!options.collectionIds) {
|
|
31
|
+
throw new error_1.FirebaseError("Missing required flag --collection-ids=[comma separated list of collection groups]");
|
|
32
|
+
}
|
|
33
|
+
let collectionIds = [];
|
|
34
|
+
try {
|
|
35
|
+
collectionIds = options.collectionIds
|
|
36
|
+
.split(",")
|
|
37
|
+
.filter((id) => id.trim() !== "");
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
throw new error_1.FirebaseError("The value for --collection-ids must a list of comma separated collection group names");
|
|
41
|
+
}
|
|
42
|
+
if (collectionIds.length === 0) {
|
|
43
|
+
throw new error_1.FirebaseError("Must specify at least one collection ID in --collection-ids.");
|
|
44
|
+
}
|
|
45
|
+
const databaseId = options.database || "(default)";
|
|
46
|
+
const api = new fsi.FirestoreApi();
|
|
47
|
+
const confirmed = await (0, prompt_1.confirm)({
|
|
48
|
+
message: confirmationMessage(options, databaseId, collectionIds),
|
|
49
|
+
default: false,
|
|
50
|
+
force: options.force,
|
|
51
|
+
nonInteractive: options.nonInteractive,
|
|
52
|
+
});
|
|
53
|
+
if (!confirmed) {
|
|
54
|
+
return utils.reject("Command aborted.", { exit: 1 });
|
|
55
|
+
}
|
|
56
|
+
const op = await api.bulkDeleteDocuments(options.project, databaseId, collectionIds);
|
|
57
|
+
if (options.json) {
|
|
58
|
+
logger_1.logger.info(JSON.stringify(op, undefined, 2));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (op.name) {
|
|
62
|
+
(0, utils_1.logSuccess)(`Successfully started bulk delete operation.`);
|
|
63
|
+
(0, utils_1.logBullet)(`Operation name: ` + clc.cyan(op.name));
|
|
64
|
+
(0, utils_1.logBullet)("You can monitor the operation's progress using the " +
|
|
65
|
+
clc.cyan(`gcloud firestore operations describe`) +
|
|
66
|
+
` command.`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
(0, utils_1.logLabeledError)(`Bulk Delete:`, `Failed to start a bulk delete operation.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return op;
|
|
73
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const fsi = require("../firestore/api");
|
|
6
|
+
const types_1 = require("../emulator/types");
|
|
7
|
+
const commandUtils_1 = require("../emulator/commandUtils");
|
|
8
|
+
const firestore_utils_1 = require("./firestore-utils");
|
|
9
|
+
const prompt_1 = require("../prompt");
|
|
10
|
+
const clc = require("colorette");
|
|
11
|
+
const utils = require("../utils");
|
|
12
|
+
const logger_1 = require("../logger");
|
|
13
|
+
exports.command = new command_1.Command("firestore:operations:cancel <operationName>")
|
|
14
|
+
.description("cancels a long-running Cloud Firestore admin operation")
|
|
15
|
+
.option("--database <databaseName>", 'Database ID for which the operation is running. "(default)" if none is provided.')
|
|
16
|
+
.option("--force", "Forces the operation cancellation without asking for confirmation")
|
|
17
|
+
.before(commandUtils_1.errorMissingProject)
|
|
18
|
+
.before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
|
|
19
|
+
.action(async (operationName, options) => {
|
|
20
|
+
const databaseId = options.database || "(default)";
|
|
21
|
+
operationName = (0, firestore_utils_1.getShortOperationName)(operationName);
|
|
22
|
+
if (!options.force) {
|
|
23
|
+
const fullName = `/projects/${options.project}/databases/${databaseId}/operations/${operationName}`;
|
|
24
|
+
const confirmMessage = `You are about to cancel the operation: ${clc.bold(clc.yellow(clc.underline(fullName)))}. Do you wish to continue?`;
|
|
25
|
+
const consent = await (0, prompt_1.confirm)(confirmMessage);
|
|
26
|
+
if (!consent) {
|
|
27
|
+
return utils.reject("Command aborted.", { exit: 1 });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const api = new fsi.FirestoreApi();
|
|
31
|
+
const status = await api.cancelOperation(options.project, databaseId, operationName);
|
|
32
|
+
if (options.json) {
|
|
33
|
+
logger_1.logger.info(JSON.stringify(status, undefined, 2));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
if (status.success) {
|
|
37
|
+
utils.logSuccess("Operation cancelled successfully.");
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
utils.logWarning("Canceling the operation failed.");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return status;
|
|
44
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const fsi = require("../firestore/api");
|
|
6
|
+
const logger_1 = require("../logger");
|
|
7
|
+
const types_1 = require("../emulator/types");
|
|
8
|
+
const commandUtils_1 = require("../emulator/commandUtils");
|
|
9
|
+
const pretty_print_1 = require("../firestore/pretty-print");
|
|
10
|
+
const firestore_utils_1 = require("./firestore-utils");
|
|
11
|
+
exports.command = new command_1.Command("firestore:operations:describe <operationName>")
|
|
12
|
+
.description("retrieves information about a Cloud Firestore admin operation")
|
|
13
|
+
.option("--database <databaseName>", 'Database ID for which the operation is running. "(default)" if none is provided.')
|
|
14
|
+
.before(commandUtils_1.errorMissingProject)
|
|
15
|
+
.before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
|
|
16
|
+
.action(async (operationName, options) => {
|
|
17
|
+
const databaseId = options.database || "(default)";
|
|
18
|
+
operationName = (0, firestore_utils_1.getShortOperationName)(operationName);
|
|
19
|
+
const api = new fsi.FirestoreApi();
|
|
20
|
+
const operation = await api.describeOperation(options.project, databaseId, operationName);
|
|
21
|
+
if (options.json) {
|
|
22
|
+
logger_1.logger.info(JSON.stringify(operation, undefined, 2));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const printer = new pretty_print_1.PrettyPrint();
|
|
26
|
+
printer.prettyPrintOperation(operation);
|
|
27
|
+
}
|
|
28
|
+
return operation;
|
|
29
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const fsi = require("../firestore/api");
|
|
6
|
+
const logger_1 = require("../logger");
|
|
7
|
+
const types_1 = require("../emulator/types");
|
|
8
|
+
const commandUtils_1 = require("../emulator/commandUtils");
|
|
9
|
+
const pretty_print_1 = require("../firestore/pretty-print");
|
|
10
|
+
exports.command = new command_1.Command("firestore:operations:list")
|
|
11
|
+
.description("list pending Cloud Firestore admin operations and their status")
|
|
12
|
+
.option("--database <databaseName>", 'Database ID for database to list operations for. "(default)" if none is provided.')
|
|
13
|
+
.option("--limit <number>", "The maximum number of operations to list. Uses 100 by default.")
|
|
14
|
+
.before(commandUtils_1.errorMissingProject)
|
|
15
|
+
.before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
const databaseId = options.database || "(default)";
|
|
18
|
+
const limit = options.limit === undefined ? 100 : Number(options.limit);
|
|
19
|
+
const api = new fsi.FirestoreApi();
|
|
20
|
+
const { operations } = await api.listOperations(options.project, databaseId, limit);
|
|
21
|
+
if (options.json) {
|
|
22
|
+
logger_1.logger.info(JSON.stringify(operations, undefined, 2));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const printer = new pretty_print_1.PrettyPrint();
|
|
26
|
+
printer.prettyPrintOperations(operations);
|
|
27
|
+
}
|
|
28
|
+
return operations;
|
|
29
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getShortOperationName = void 0;
|
|
4
|
+
const error_1 = require("../error");
|
|
5
|
+
function getShortOperationName(operationName) {
|
|
6
|
+
let opName = operationName;
|
|
7
|
+
if (operationName.includes("/operations/")) {
|
|
8
|
+
opName = operationName.split("/operations/")[1];
|
|
9
|
+
}
|
|
10
|
+
if (opName.length === 0 || opName.includes("/")) {
|
|
11
|
+
throw new error_1.FirebaseError(`"${operationName}" is not a valid operation name.`);
|
|
12
|
+
}
|
|
13
|
+
return opName;
|
|
14
|
+
}
|
|
15
|
+
exports.getShortOperationName = getShortOperationName;
|
|
@@ -78,7 +78,10 @@ exports.command = new command_1.Command("functions:config:export")
|
|
|
78
78
|
.before(requireInteractive_1.default)
|
|
79
79
|
.action(async (options) => {
|
|
80
80
|
const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
|
|
81
|
-
const
|
|
81
|
+
const configDir = (0, projectConfig_1.resolveConfigDir)(config);
|
|
82
|
+
if (!configDir) {
|
|
83
|
+
throw new error_1.FirebaseError("functions:config:export requires a local env directory. Set functions[].configDir in firebase.json when using remoteSource.");
|
|
84
|
+
}
|
|
82
85
|
let pInfos = configExport.getProjectInfos(options);
|
|
83
86
|
checkReservedAliases(pInfos);
|
|
84
87
|
(0, utils_1.logBullet)("Importing functions configs from projects [" +
|
|
@@ -111,6 +114,6 @@ exports.command = new command_1.Command("functions:config:export")
|
|
|
111
114
|
filesToWrite[".env"] =
|
|
112
115
|
`${header}# .env file contains environment variables that applies to all projects.\n`;
|
|
113
116
|
for (const [filename, content] of Object.entries(filesToWrite)) {
|
|
114
|
-
await options.config.askWriteProjectFile(path.join(
|
|
117
|
+
await options.config.askWriteProjectFile(path.join(configDir, filename), content);
|
|
115
118
|
}
|
|
116
119
|
});
|
package/lib/commands/index.js
CHANGED
|
@@ -96,8 +96,13 @@ function load(client) {
|
|
|
96
96
|
client.ext.dev.usage = loadCommand("ext-dev-usage");
|
|
97
97
|
client.firestore = {};
|
|
98
98
|
client.firestore.delete = loadCommand("firestore-delete");
|
|
99
|
+
client.firestore.bulkDelete = loadCommand("firestore-bulkdelete");
|
|
99
100
|
client.firestore.indexes = loadCommand("firestore-indexes-list");
|
|
100
101
|
client.firestore.locations = loadCommand("firestore-locations");
|
|
102
|
+
client.firestore.operations = {};
|
|
103
|
+
client.firestore.operations.cancel = loadCommand("firestore-operations-cancel");
|
|
104
|
+
client.firestore.operations.describe = loadCommand("firestore-operations-describe");
|
|
105
|
+
client.firestore.operations.list = loadCommand("firestore-operations-list");
|
|
101
106
|
client.firestore.databases = {};
|
|
102
107
|
client.firestore.databases.list = loadCommand("firestore-databases-list");
|
|
103
108
|
client.firestore.databases.get = loadCommand("firestore-databases-get");
|
package/lib/config.js
CHANGED
|
@@ -42,13 +42,25 @@ class Config {
|
|
|
42
42
|
});
|
|
43
43
|
if (this.get("functions")) {
|
|
44
44
|
if (this.projectDir && fsutils.dirExistsSync(this.path(Config.DEFAULT_FUNCTIONS_SOURCE))) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const funcs = this.get("functions");
|
|
46
|
+
if (Array.isArray(funcs)) {
|
|
47
|
+
let emptyIdx;
|
|
48
|
+
for (let i = 0; i < funcs.length; i++) {
|
|
49
|
+
const hasSource = this.get(`functions.[${i}].source`);
|
|
50
|
+
const hasRemote = this.get(`functions.[${i}].remoteSource`);
|
|
51
|
+
if (!hasSource && !hasRemote) {
|
|
52
|
+
emptyIdx = i;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (emptyIdx !== undefined) {
|
|
57
|
+
this.set(`functions.[${emptyIdx}].source`, Config.DEFAULT_FUNCTIONS_SOURCE);
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
else {
|
|
51
|
-
|
|
61
|
+
const hasSource = this.get("functions.source");
|
|
62
|
+
const hasRemote = this.get("functions.remoteSource");
|
|
63
|
+
if (!hasSource && !hasRemote) {
|
|
52
64
|
this.set("functions.source", Config.DEFAULT_FUNCTIONS_SOURCE);
|
|
53
65
|
}
|
|
54
66
|
}
|
|
@@ -4,10 +4,10 @@ exports.ensureGIFApis = exports.ensureApis = void 0;
|
|
|
4
4
|
const api = require("../api");
|
|
5
5
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
6
6
|
const prefix = "dataconnect";
|
|
7
|
-
async function ensureApis(projectId) {
|
|
7
|
+
async function ensureApis(projectId, silent = false) {
|
|
8
8
|
await Promise.all([
|
|
9
|
-
(0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix),
|
|
10
|
-
(0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix),
|
|
9
|
+
(0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix, silent),
|
|
10
|
+
(0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix, silent),
|
|
11
11
|
]);
|
|
12
12
|
}
|
|
13
13
|
exports.ensureApis = ensureApis;
|
|
@@ -51,7 +51,7 @@ async function uploadSourceV2(projectId, source, wantBackend) {
|
|
|
51
51
|
return res.storageSource;
|
|
52
52
|
}
|
|
53
53
|
async function uploadCodebase(context, codebase, wantBackend) {
|
|
54
|
-
var _a;
|
|
54
|
+
var _a, _b, _c, _d;
|
|
55
55
|
const source = (_a = context.sources) === null || _a === void 0 ? void 0 : _a[codebase];
|
|
56
56
|
if (!source || (!source.functionsSourceV1 && !source.functionsSourceV2)) {
|
|
57
57
|
return;
|
|
@@ -67,9 +67,10 @@ async function uploadCodebase(context, codebase, wantBackend) {
|
|
|
67
67
|
if (storage) {
|
|
68
68
|
source.storage = storage;
|
|
69
69
|
}
|
|
70
|
-
const
|
|
70
|
+
const cfg = (0, projectConfig_1.configForCodebase)(context.config, codebase);
|
|
71
|
+
const label = (_d = (_b = cfg.source) !== null && _b !== void 0 ? _b : (_c = cfg.remoteSource) === null || _c === void 0 ? void 0 : _c.dir) !== null && _d !== void 0 ? _d : "remote";
|
|
71
72
|
if (uploads.length) {
|
|
72
|
-
(0, utils_1.
|
|
73
|
+
(0, utils_1.logLabeledSuccess)("functions", `${clc.bold(label)} source uploaded successfully`);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
catch (err) {
|
|
@@ -73,12 +73,13 @@ async function prepare(context, options, payload) {
|
|
|
73
73
|
for (const [codebase, wantBuild] of Object.entries(wantBuilds)) {
|
|
74
74
|
const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
|
|
75
75
|
const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
|
|
76
|
+
const localCfg = (0, projectConfig_1.requireLocal)(config, "Remote sources are not supported.");
|
|
76
77
|
const userEnvOpt = {
|
|
77
|
-
functionsSource: options.config.path(
|
|
78
|
+
functionsSource: options.config.path(localCfg.source),
|
|
78
79
|
projectId: projectId,
|
|
79
80
|
projectAlias: options.projectAlias,
|
|
80
81
|
};
|
|
81
|
-
proto.convertIfPresent(userEnvOpt,
|
|
82
|
+
proto.convertIfPresent(userEnvOpt, localCfg, "configDir", (cd) => options.config.path(cd));
|
|
82
83
|
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
83
84
|
const envs = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
|
|
84
85
|
const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend({
|
|
@@ -142,20 +143,21 @@ async function prepare(context, options, payload) {
|
|
|
142
143
|
validate.endpointsAreUnique(wantBackends);
|
|
143
144
|
context.sources = {};
|
|
144
145
|
for (const [codebase, wantBackend] of Object.entries(wantBackends)) {
|
|
145
|
-
const
|
|
146
|
-
const
|
|
146
|
+
const cfg = (0, projectConfig_1.configForCodebase)(context.config, codebase);
|
|
147
|
+
const localCfg = (0, projectConfig_1.requireLocal)(cfg, "Remote sources are not supported.");
|
|
148
|
+
const sourceDirName = localCfg.source;
|
|
147
149
|
const sourceDir = options.config.path(sourceDirName);
|
|
148
150
|
const source = {};
|
|
149
151
|
if (backend.someEndpoint(wantBackend, () => true)) {
|
|
150
152
|
(0, utils_1.logLabeledBullet)("functions", `preparing ${clc.bold(sourceDirName)} directory for uploading...`);
|
|
151
153
|
}
|
|
152
154
|
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
|
|
153
|
-
const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir,
|
|
155
|
+
const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, localCfg);
|
|
154
156
|
source.functionsSourceV2 = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.pathToSource;
|
|
155
157
|
source.functionsSourceV2Hash = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.hash;
|
|
156
158
|
}
|
|
157
159
|
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
|
|
158
|
-
const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir,
|
|
160
|
+
const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, localCfg, runtimeConfig);
|
|
159
161
|
source.functionsSourceV1 = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.pathToSource;
|
|
160
162
|
source.functionsSourceV1Hash = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.hash;
|
|
161
163
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DEFAULT_CONFIG = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.DESC_VERBOSITY = exports.FLAG_VERBOSITY = exports.FLAG_VERBOSITY_NAME = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
|
|
3
|
+
exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.errorMissingProject = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DEFAULT_CONFIG = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.DESC_VERBOSITY = exports.FLAG_VERBOSITY = exports.FLAG_VERBOSITY_NAME = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const childProcess = require("child_process");
|
|
6
6
|
const controller = require("../emulator/controller");
|
|
@@ -86,6 +86,12 @@ async function warnEmulatorNotSupported(options, emulator) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
exports.warnEmulatorNotSupported = warnEmulatorNotSupported;
|
|
89
|
+
async function errorMissingProject(options) {
|
|
90
|
+
if (!options.project) {
|
|
91
|
+
throw new error_1.FirebaseError("Project is not defined. Either use `--project` or use `firebase use` to set your active project.");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.errorMissingProject = errorMissingProject;
|
|
89
95
|
async function beforeEmulatorCommand(options) {
|
|
90
96
|
const optionsWithDefaultConfig = Object.assign(Object.assign({}, options), { config: exports.DEFAULT_CONFIG });
|
|
91
97
|
const optionsWithConfig = options.config ? options : optionsWithDefaultConfig;
|
|
@@ -98,7 +98,7 @@ exports.filterEmulatorTargets = filterEmulatorTargets;
|
|
|
98
98
|
function shouldStart(options, name) {
|
|
99
99
|
var _a, _b;
|
|
100
100
|
if (name === types_1.Emulators.HUB) {
|
|
101
|
-
return
|
|
101
|
+
return true;
|
|
102
102
|
}
|
|
103
103
|
const targets = filterEmulatorTargets(options);
|
|
104
104
|
const emulatorInTargets = targets.includes(name);
|
|
@@ -109,7 +109,7 @@ function shouldStart(options, name) {
|
|
|
109
109
|
if (((_b = (_a = options.config.src.emulators) === null || _a === void 0 ? void 0 : _a.ui) === null || _b === void 0 ? void 0 : _b.enabled) === false) {
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
112
|
-
return
|
|
112
|
+
return targets.some((target) => types_1.EMULATORS_SUPPORTED_BY_UI.includes(target));
|
|
113
113
|
}
|
|
114
114
|
if (name === types_1.Emulators.FUNCTIONS && emulatorInTargets) {
|
|
115
115
|
try {
|
|
@@ -188,7 +188,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
188
188
|
}
|
|
189
189
|
const hubLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.HUB);
|
|
190
190
|
hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`);
|
|
191
|
-
const projectId = (0, projectUtils_1.getProjectId)(options) ||
|
|
191
|
+
const projectId = (0, projectUtils_1.getProjectId)(options) || hub_1.EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
|
|
192
192
|
const isDemoProject = constants_1.Constants.isDemoProject(projectId);
|
|
193
193
|
if (isDemoProject) {
|
|
194
194
|
hubLogger.logLabeled("BULLET", "emulators", `Detected demo project ID "${projectId}", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.`);
|
|
@@ -340,7 +340,8 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
340
340
|
const functionsCfg = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
341
341
|
utils.assertIsStringOrUndefined(options.extDevDir);
|
|
342
342
|
for (const cfg of functionsCfg) {
|
|
343
|
-
const
|
|
343
|
+
const localCfg = (0, projectConfig_1.requireLocal)(cfg, "Remote sources are not supported in the Functions emulator.");
|
|
344
|
+
const functionsDir = path.join(projectDir, localCfg.source);
|
|
344
345
|
const runtime = ((_g = options.extDevRuntime) !== null && _g !== void 0 ? _g : cfg.runtime);
|
|
345
346
|
if (runtime && !(0, supported_1.isRuntime)(runtime)) {
|
|
346
347
|
throw new error_1.FirebaseError(`Cannot load functions from ${functionsDir} because it has invalid runtime ${runtime}`);
|
|
@@ -348,14 +349,14 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
348
349
|
const backend = {
|
|
349
350
|
functionsDir,
|
|
350
351
|
runtime,
|
|
351
|
-
codebase:
|
|
352
|
-
prefix:
|
|
352
|
+
codebase: localCfg.codebase,
|
|
353
|
+
prefix: localCfg.prefix,
|
|
353
354
|
env: Object.assign({}, options.extDevEnv),
|
|
354
355
|
secretEnv: [],
|
|
355
356
|
predefinedTriggers: options.extDevTriggers,
|
|
356
|
-
ignore:
|
|
357
|
+
ignore: localCfg.ignore,
|
|
357
358
|
};
|
|
358
|
-
proto.convertIfPresent(backend,
|
|
359
|
+
proto.convertIfPresent(backend, localCfg, "configDir", (cd) => path.join(projectDir, cd));
|
|
359
360
|
emulatableBackends.push(backend);
|
|
360
361
|
}
|
|
361
362
|
}
|
|
@@ -372,7 +373,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
372
373
|
}
|
|
373
374
|
const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
|
|
374
375
|
const functionsAddr = legacyGetFirstAddr(types_1.Emulators.FUNCTIONS);
|
|
375
|
-
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
376
376
|
const inspectFunctions = commandUtils.parseInspectionPort(options);
|
|
377
377
|
if (inspectFunctions) {
|
|
378
378
|
functionsLogger.logLabeled("WARN", "functions", `You are running the Functions emulator in debug mode. This means that functions will execute in sequence rather than in parallel.`);
|
|
@@ -461,13 +461,8 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
461
461
|
firestoreLogger.logLabeled("WARN", "firestore", "The emulator will default to allowing all reads and writes. Learn more about this option: https://firebase.google.com/docs/emulator-suite/install_and_configure#security_rules_configuration.");
|
|
462
462
|
}
|
|
463
463
|
if (singleProjectModeEnabled) {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
args.single_project_mode_error = false;
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
firestoreLogger.logLabeled("DEBUG", "firestore", "Could not enable single_project_mode: missing projectId.");
|
|
470
|
-
}
|
|
464
|
+
args.single_project_mode = true;
|
|
465
|
+
args.single_project_mode_error = false;
|
|
471
466
|
}
|
|
472
467
|
const firestoreEmulator = new firestoreEmulator_1.FirestoreEmulator(args);
|
|
473
468
|
await startEmulator(firestoreEmulator);
|
|
@@ -525,9 +520,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
525
520
|
}
|
|
526
521
|
}
|
|
527
522
|
if (listenForEmulator.auth) {
|
|
528
|
-
if (!projectId) {
|
|
529
|
-
throw new error_1.FirebaseError(`Cannot start the ${constants_1.Constants.description(types_1.Emulators.AUTH)} without a project: run 'firebase init' or provide the --project flag`);
|
|
530
|
-
}
|
|
531
523
|
const authAddr = legacyGetFirstAddr(types_1.Emulators.AUTH);
|
|
532
524
|
const authEmulator = new auth_2.AuthEmulator({
|
|
533
525
|
host: authAddr.host,
|
|
@@ -546,9 +538,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
546
538
|
}
|
|
547
539
|
}
|
|
548
540
|
if (listenForEmulator.pubsub) {
|
|
549
|
-
if (!projectId) {
|
|
550
|
-
throw new error_1.FirebaseError("Cannot start the Pub/Sub emulator without a project: run 'firebase init' or provide the --project flag");
|
|
551
|
-
}
|
|
552
541
|
const pubsubAddr = legacyGetFirstAddr(types_1.Emulators.PUBSUB);
|
|
553
542
|
const pubsubEmulator = new pubsubEmulator_1.PubsubEmulator({
|
|
554
543
|
host: pubsubAddr.host,
|
|
@@ -571,7 +560,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
571
560
|
projectId,
|
|
572
561
|
auto_download: true,
|
|
573
562
|
configDir: config[0].source,
|
|
574
|
-
rc: options.rc,
|
|
575
563
|
config: options.config,
|
|
576
564
|
autoconnectToPostgres: true,
|
|
577
565
|
postgresListen: listenForEmulator["dataconnect.postgres"],
|
|
@@ -612,7 +600,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
612
600
|
const storageEmulator = new storage_1.StorageEmulator({
|
|
613
601
|
host: storageAddr.host,
|
|
614
602
|
port: storageAddr.port,
|
|
615
|
-
projectId
|
|
603
|
+
projectId,
|
|
616
604
|
rules: (0, config_1.getStorageRulesConfig)(projectId, options),
|
|
617
605
|
});
|
|
618
606
|
await startEmulator(storageEmulator);
|
|
@@ -671,13 +659,12 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
671
659
|
await startEmulator(loggingEmulator);
|
|
672
660
|
}
|
|
673
661
|
if (showUI && !shouldStart(options, types_1.Emulators.UI)) {
|
|
674
|
-
hubLogger.logLabeled("WARN", "emulators", "The Emulator UI is not starting
|
|
675
|
-
"emulators have a UI component
|
|
676
|
-
"determine the Project ID. Pass the --project flag to specify a project.");
|
|
662
|
+
hubLogger.logLabeled("WARN", "emulators", "The Emulator UI is not starting because none of the running " +
|
|
663
|
+
"emulators have a UI component.");
|
|
677
664
|
}
|
|
678
665
|
if (listenForEmulator.ui) {
|
|
679
666
|
const ui = new ui_1.EmulatorUI({
|
|
680
|
-
projectId
|
|
667
|
+
projectId,
|
|
681
668
|
listen: listenForEmulator[types_1.Emulators.UI],
|
|
682
669
|
});
|
|
683
670
|
await startEmulator(ui);
|
|
@@ -726,9 +713,6 @@ function getListenConfig(options, emulator) {
|
|
|
726
713
|
}
|
|
727
714
|
async function exportEmulatorData(exportPath, options, initiatedBy) {
|
|
728
715
|
const projectId = options.project;
|
|
729
|
-
if (!projectId) {
|
|
730
|
-
throw new error_1.FirebaseError("Could not determine project ID, make sure you're running in a Firebase project directory or add the --project flag.", { exit: 1 });
|
|
731
|
-
}
|
|
732
716
|
const hubClient = new hubClient_1.EmulatorHubClient(projectId);
|
|
733
717
|
if (!hubClient.foundHub()) {
|
|
734
718
|
throw new error_1.FirebaseError(`Did not find any running emulators for project ${clc.bold(projectId)}.`, { exit: 1 });
|
|
@@ -54,28 +54,28 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
57
|
+
"version": "2.13.0",
|
|
58
|
+
"expectedSize": 29475680,
|
|
59
|
+
"expectedChecksum": "8bceed44e84d08f135ad10b8b62337e9",
|
|
60
|
+
"expectedChecksumSHA256": "92e0cda1a3fd690421cae4edd7d985e5c5de7069e731ad200ba6988c2321d76c",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.13.0",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.13.0"
|
|
63
63
|
},
|
|
64
64
|
"win32": {
|
|
65
|
-
"version": "2.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
65
|
+
"version": "2.13.0",
|
|
66
|
+
"expectedSize": 29965312,
|
|
67
|
+
"expectedChecksum": "67794a3cb334805a06fffd872be7d67c",
|
|
68
|
+
"expectedChecksumSHA256": "2905c570d40e586dd1ca0aeee10785786dc0486500640064e4e1e43be3b97899",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.13.0",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.13.0.exe"
|
|
71
71
|
},
|
|
72
72
|
"linux": {
|
|
73
|
-
"version": "2.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
73
|
+
"version": "2.13.0",
|
|
74
|
+
"expectedSize": 29401272,
|
|
75
|
+
"expectedChecksum": "d72e185be8b782b8d4bca86d6bc76d2d",
|
|
76
|
+
"expectedChecksumSHA256": "87cb9541f157c3e9986b6eba80d161e6ef1debd17075e4d95e78861d3cdd353f",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.13.0",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.13.0"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
package/lib/emulator/hub.js
CHANGED
|
@@ -26,8 +26,13 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
26
26
|
}
|
|
27
27
|
static getLocatorFilePath(projectId) {
|
|
28
28
|
const dir = os.tmpdir();
|
|
29
|
+
if (!projectId) {
|
|
30
|
+
projectId = EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
|
|
31
|
+
}
|
|
29
32
|
const filename = `hub-${projectId}.json`;
|
|
30
|
-
|
|
33
|
+
const locatorPath = path.join(dir, filename);
|
|
34
|
+
logger_1.logger.debug(`Emulator locator file path: ${locatorPath}`);
|
|
35
|
+
return locatorPath;
|
|
31
36
|
}
|
|
32
37
|
constructor(args) {
|
|
33
38
|
super({
|
|
@@ -177,6 +182,7 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
177
182
|
}
|
|
178
183
|
}
|
|
179
184
|
exports.EmulatorHub = EmulatorHub;
|
|
185
|
+
EmulatorHub.MISSING_PROJECT_PLACEHOLDER = "demo-no-project";
|
|
180
186
|
EmulatorHub.CLI_VERSION = pkg.version;
|
|
181
187
|
EmulatorHub.PATH_EXPORT = "/_admin/export";
|
|
182
188
|
EmulatorHub.PATH_DISABLE_FUNCTIONS = "/functions/disableBackgroundTriggers";
|
|
@@ -135,13 +135,14 @@ exports.copyDirectory = copyDirectory;
|
|
|
135
135
|
async function getCodebaseRuntime(options) {
|
|
136
136
|
const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
137
137
|
const codebaseConfig = (0, projectConfig_1.configForCodebase)(config, options.codebase || projectConfig_1.DEFAULT_CODEBASE);
|
|
138
|
-
const
|
|
138
|
+
const localCfg = (0, projectConfig_1.requireLocal)(codebaseConfig);
|
|
139
|
+
const sourceDirName = localCfg.source;
|
|
139
140
|
const sourceDir = options.config.path(sourceDirName);
|
|
140
141
|
const delegateContext = {
|
|
141
142
|
projectId: "",
|
|
142
143
|
sourceDir,
|
|
143
144
|
projectDir: options.config.projectDir,
|
|
144
|
-
runtime:
|
|
145
|
+
runtime: localCfg.runtime,
|
|
145
146
|
};
|
|
146
147
|
let delegate;
|
|
147
148
|
try {
|