firebase-tools 13.29.0 → 13.29.2
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/appdistribution-distribute.js +1 -1
- package/lib/dataconnect/build.js +63 -4
- package/lib/dataconnect/freeTrial.js +12 -6
- package/lib/dataconnect/graphqlError.js +27 -1
- package/lib/dataconnect/provisionCloudSql.js +18 -13
- package/lib/dataconnect/schemaMigration.js +2 -1
- package/lib/deploy/dataconnect/prepare.js +1 -1
- package/lib/emulator/apphosting/serve.js +9 -1
- package/lib/emulator/controller.js +2 -1
- package/lib/emulator/dataconnect/pgliteServer.js +4 -7
- package/lib/emulator/dataconnectEmulator.js +9 -5
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/hub.js +1 -1
- package/lib/emulator/hubClient.js +5 -0
- package/lib/experiments.js +6 -0
- package/lib/firebaseConfigValidate.js +7 -5
- package/lib/gcp/cloudfunctionsv2.js +0 -6
- package/lib/init/features/dataconnect/index.js +17 -2
- package/package.json +5 -4
- package/schema/firebase-config.json +1067 -2127
|
@@ -51,7 +51,7 @@ exports.command = new command_1.Command("appdistribution:distribute <release-bin
|
|
|
51
51
|
const testCases = (0, options_parser_util_1.parseIntoStringArray)(options.testCaseIds, options.testCaseIdsFile);
|
|
52
52
|
const testDevices = (0, options_parser_util_1.parseTestDevices)(options.testDevices, options.testDevicesFile);
|
|
53
53
|
if (testCases.length && (options.testUsernameResource || options.testPasswordResource)) {
|
|
54
|
-
throw new error_1.FirebaseError("Password and username resource names are not supported for the
|
|
54
|
+
throw new error_1.FirebaseError("Password and username resource names are not supported for the testing agent.");
|
|
55
55
|
}
|
|
56
56
|
const loginCredential = (0, options_parser_util_1.getLoginCredential)({
|
|
57
57
|
username: options.testUsername,
|
package/lib/dataconnect/build.js
CHANGED
|
@@ -1,15 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.build = void 0;
|
|
3
|
+
exports.handleBuildErrors = exports.build = void 0;
|
|
4
4
|
const dataconnectEmulator_1 = require("../emulator/dataconnectEmulator");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
|
+
const experiments = require("../experiments");
|
|
7
|
+
const prompt_1 = require("../prompt");
|
|
8
|
+
const utils = require("../utils");
|
|
6
9
|
const graphqlError_1 = require("./graphqlError");
|
|
7
|
-
async function build(options, configDir) {
|
|
10
|
+
async function build(options, configDir, dryRun) {
|
|
8
11
|
var _a, _b;
|
|
9
|
-
const
|
|
12
|
+
const args = { configDir };
|
|
13
|
+
if (experiments.isEnabled("fdcconnectorevolution") && options.projectId) {
|
|
14
|
+
const flags = process.env["DATA_CONNECT_PREVIEW"];
|
|
15
|
+
if (flags) {
|
|
16
|
+
process.env["DATA_CONNECT_PREVIEW"] = flags + ",conn_evolution";
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
process.env["DATA_CONNECT_PREVIEW"] = "conn_evolution";
|
|
20
|
+
}
|
|
21
|
+
args.projectId = options.projectId;
|
|
22
|
+
}
|
|
23
|
+
const buildResult = await dataconnectEmulator_1.DataConnectEmulator.build(args);
|
|
10
24
|
if ((_a = buildResult === null || buildResult === void 0 ? void 0 : buildResult.errors) === null || _a === void 0 ? void 0 : _a.length) {
|
|
11
|
-
|
|
25
|
+
await handleBuildErrors(buildResult.errors, options.nonInteractive, options.force, dryRun);
|
|
12
26
|
}
|
|
13
27
|
return (_b = buildResult === null || buildResult === void 0 ? void 0 : buildResult.metadata) !== null && _b !== void 0 ? _b : {};
|
|
14
28
|
}
|
|
15
29
|
exports.build = build;
|
|
30
|
+
async function handleBuildErrors(errors, nonInteractive, force, dryRun) {
|
|
31
|
+
if (errors.filter((w) => { var _a; return !((_a = w.extensions) === null || _a === void 0 ? void 0 : _a.warningLevel); }).length) {
|
|
32
|
+
throw new error_1.FirebaseError(`There are errors in your schema and connector files:\n${errors.map(graphqlError_1.prettify).join("\n")}`);
|
|
33
|
+
}
|
|
34
|
+
const interactiveAcks = errors.filter((w) => { var _a; return ((_a = w.extensions) === null || _a === void 0 ? void 0 : _a.warningLevel) === "INTERACTIVE_ACK"; });
|
|
35
|
+
const requiredAcks = errors.filter((w) => { var _a; return ((_a = w.extensions) === null || _a === void 0 ? void 0 : _a.warningLevel) === "REQUIRE_ACK"; });
|
|
36
|
+
const choices = [
|
|
37
|
+
{ name: "Acknowledge all changes and proceed", value: "proceed" },
|
|
38
|
+
{ name: "Reject changes and abort", value: "abort" },
|
|
39
|
+
];
|
|
40
|
+
if (requiredAcks.length) {
|
|
41
|
+
utils.logLabeledWarning("dataconnect", `There are changes in your schema or connectors that may break your existing applications. These changes require explicit acknowledgement to proceed. You may either reject the changes and update your sources with the suggested workaround(s), if any, or acknowledge these changes and proceed with the deployment:\n` +
|
|
42
|
+
(0, graphqlError_1.prettifyWithWorkaround)(requiredAcks));
|
|
43
|
+
if (nonInteractive && !force) {
|
|
44
|
+
throw new error_1.FirebaseError("Explicit acknowledgement required for breaking schema or connector changes. Rerun this command with --force to deploy these changes.");
|
|
45
|
+
}
|
|
46
|
+
else if (!nonInteractive && !force && !dryRun) {
|
|
47
|
+
const result = await (0, prompt_1.promptOnce)({
|
|
48
|
+
message: "Would you like to proceed with these breaking changes?",
|
|
49
|
+
type: "list",
|
|
50
|
+
choices,
|
|
51
|
+
default: "abort",
|
|
52
|
+
});
|
|
53
|
+
if (result === "abort") {
|
|
54
|
+
throw new error_1.FirebaseError(`Deployment aborted.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (interactiveAcks.length) {
|
|
59
|
+
utils.logLabeledWarning("dataconnect", `There are changes in your schema or connectors that may cause unexpected behavior in your existing applications:\n` +
|
|
60
|
+
interactiveAcks.map(graphqlError_1.prettify).join("\n"));
|
|
61
|
+
if (!nonInteractive && !force && !dryRun) {
|
|
62
|
+
const result = await (0, prompt_1.promptOnce)({
|
|
63
|
+
message: "Would you like to proceed with these changes?",
|
|
64
|
+
type: "list",
|
|
65
|
+
choices,
|
|
66
|
+
default: "proceed",
|
|
67
|
+
});
|
|
68
|
+
if (result === "abort") {
|
|
69
|
+
throw new error_1.FirebaseError(`Deployment aborted.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.handleBuildErrors = handleBuildErrors;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.upgradeInstructions = exports.printFreeTrialUnavailable = exports.getFreeTrialInstanceId = exports.checkFreeTrialInstanceUsed = exports.freeTrialTermsLink = void 0;
|
|
3
|
+
exports.upgradeInstructions = exports.printFreeTrialUnavailable = exports.isFreeTrialError = exports.getFreeTrialInstanceId = exports.checkFreeTrialInstanceUsed = exports.freeTrialTermsLink = void 0;
|
|
4
4
|
const cloudmonitoring_1 = require("../gcp/cloudmonitoring");
|
|
5
5
|
const cloudsqladmin_1 = require("../gcp/cloudsql/cloudsqladmin");
|
|
6
6
|
const utils = require("../utils");
|
|
@@ -36,17 +36,23 @@ async function getFreeTrialInstanceId(projectId) {
|
|
|
36
36
|
return (_a = instances.find((i) => { var _a; return ((_a = i.settings.userLabels) === null || _a === void 0 ? void 0 : _a["firebase-data-connect"]) === "ft"; })) === null || _a === void 0 ? void 0 : _a.name;
|
|
37
37
|
}
|
|
38
38
|
exports.getFreeTrialInstanceId = getFreeTrialInstanceId;
|
|
39
|
+
async function isFreeTrialError(err, projectId) {
|
|
40
|
+
return err.message.includes("Quota Exhausted") && (await checkFreeTrialInstanceUsed(projectId))
|
|
41
|
+
? true
|
|
42
|
+
: false;
|
|
43
|
+
}
|
|
44
|
+
exports.isFreeTrialError = isFreeTrialError;
|
|
39
45
|
function printFreeTrialUnavailable(projectId, configYamlPath, instanceId) {
|
|
40
46
|
if (!instanceId) {
|
|
41
|
-
utils.logLabeledError("
|
|
42
|
-
utils.logLabeledError("
|
|
47
|
+
utils.logLabeledError("dataconnect", "The CloudSQL free trial has already been used on this project.");
|
|
48
|
+
utils.logLabeledError("dataconnect", `You may create or use a paid CloudSQL instance by visiting https://console.cloud.google.com/sql/instances`);
|
|
43
49
|
return;
|
|
44
50
|
}
|
|
45
|
-
utils.logLabeledError("
|
|
51
|
+
utils.logLabeledError("dataconnect", `Project '${projectId} already has a CloudSQL instance '${instanceId}' on the Firebase Data Connect no-cost trial.`);
|
|
46
52
|
const reuseHint = `To use a different database in the same instance, ${clc.bold(`change the ${clc.blue("instanceId")} to "${instanceId}"`)} and update ${clc.blue("location")} in ` +
|
|
47
53
|
`${clc.green(configYamlPath)}.`;
|
|
48
|
-
utils.logLabeledError("
|
|
49
|
-
utils.logLabeledError("
|
|
54
|
+
utils.logLabeledError("dataconnect", reuseHint);
|
|
55
|
+
utils.logLabeledError("dataconnect", `Alternatively, you may create a new (paid) CloudSQL instance at https://console.cloud.google.com/sql/instances`);
|
|
50
56
|
}
|
|
51
57
|
exports.printFreeTrialUnavailable = printFreeTrialUnavailable;
|
|
52
58
|
function upgradeInstructions(projectId) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prettify = void 0;
|
|
3
|
+
exports.prettifyWithWorkaround = exports.prettify = void 0;
|
|
4
|
+
const Table = require("cli-table");
|
|
4
5
|
function prettify(err) {
|
|
5
6
|
var _a, _b, _c, _d;
|
|
6
7
|
const message = err.message;
|
|
@@ -14,3 +15,28 @@ function prettify(err) {
|
|
|
14
15
|
return header.length ? `${header}: ${message}` : message;
|
|
15
16
|
}
|
|
16
17
|
exports.prettify = prettify;
|
|
18
|
+
function prettifyWithWorkaround(errs) {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
const table = new Table({
|
|
21
|
+
head: ["Issue", "Workaround", "Reason"],
|
|
22
|
+
style: { head: ["yellow"] },
|
|
23
|
+
});
|
|
24
|
+
for (const e of errs) {
|
|
25
|
+
if (!((_b = (_a = e.extensions) === null || _a === void 0 ? void 0 : _a.workarounds) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
26
|
+
table.push([prettify(e), "", ""]);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const workarounds = e.extensions.workarounds;
|
|
30
|
+
for (let i = 0; i < workarounds.length; i++) {
|
|
31
|
+
if (i === 0) {
|
|
32
|
+
table.push([prettify(e), workarounds[i].Description, workarounds[i].Reason]);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
table.push(["", workarounds[i].Description, workarounds[i].Reason]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return table.toString();
|
|
41
|
+
}
|
|
42
|
+
exports.prettifyWithWorkaround = prettifyWithWorkaround;
|
|
@@ -33,11 +33,6 @@ async function provisionCloudSql(args) {
|
|
|
33
33
|
if (err.status !== 404) {
|
|
34
34
|
throw err;
|
|
35
35
|
}
|
|
36
|
-
const freeTrialInstanceId = await (0, freeTrial_1.getFreeTrialInstanceId)(projectId);
|
|
37
|
-
if (await (0, freeTrial_1.checkFreeTrialInstanceUsed)(projectId)) {
|
|
38
|
-
(0, freeTrial_1.printFreeTrialUnavailable)(projectId, configYamlPath, freeTrialInstanceId);
|
|
39
|
-
throw new error_1.FirebaseError("No-cost Cloud SQL trial has already been used on this project.");
|
|
40
|
-
}
|
|
41
36
|
const cta = dryRun ? "It will be created on your next deploy" : "Creating it now.";
|
|
42
37
|
silent ||
|
|
43
38
|
utils.logLabeledBullet("dataconnect", `CloudSQL instance '${instanceId}' not found.` +
|
|
@@ -45,15 +40,25 @@ async function provisionCloudSql(args) {
|
|
|
45
40
|
`\nThis instance is provided under the terms of the Data Connect no-cost trial ${(0, freeTrial_1.freeTrialTermsLink)()}` +
|
|
46
41
|
`\nMonitor the progress at ${cloudSqlAdminClient.instanceConsoleLink(projectId, instanceId)}`);
|
|
47
42
|
if (!dryRun) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
try {
|
|
44
|
+
const newInstance = await (0, utils_1.promiseWithSpinner)(() => cloudSqlAdminClient.createInstance(projectId, locationId, instanceId, enableGoogleMlIntegration, waitForCreation), "Creating your instance...");
|
|
45
|
+
if (newInstance) {
|
|
46
|
+
silent || utils.logLabeledBullet("dataconnect", "Instance created");
|
|
47
|
+
connectionName = (newInstance === null || newInstance === void 0 ? void 0 : newInstance.connectionName) || "";
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
silent ||
|
|
51
|
+
utils.logLabeledBullet("dataconnect", "Cloud SQL instance creation started - it should be ready shortly. Database and users will be created on your next deploy.");
|
|
52
|
+
return connectionName;
|
|
53
|
+
}
|
|
52
54
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
catch (err) {
|
|
56
|
+
if (await (0, freeTrial_1.isFreeTrialError)(err, projectId)) {
|
|
57
|
+
const freeTrialInstanceId = await (0, freeTrial_1.getFreeTrialInstanceId)(projectId);
|
|
58
|
+
(0, freeTrial_1.printFreeTrialUnavailable)(projectId, configYamlPath, freeTrialInstanceId);
|
|
59
|
+
throw new error_1.FirebaseError("No-cost Cloud SQL trial has already been used on this project.");
|
|
60
|
+
}
|
|
61
|
+
throw err;
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
}
|
|
@@ -345,6 +345,7 @@ async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, datab
|
|
|
345
345
|
{
|
|
346
346
|
postgresql: {
|
|
347
347
|
database: databaseId,
|
|
348
|
+
schemaValidation: "NONE",
|
|
348
349
|
cloudSql: {
|
|
349
350
|
instance: instanceId,
|
|
350
351
|
},
|
|
@@ -361,7 +362,7 @@ async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, datab
|
|
|
361
362
|
if ((postgresql === null || postgresql === void 0 ? void 0 : postgresql.database) !== databaseId) {
|
|
362
363
|
(0, utils_1.logLabeledWarning)("dataconnect", `Switching connected Postgres database from ${postgresql === null || postgresql === void 0 ? void 0 : postgresql.database} to ${databaseId}`);
|
|
363
364
|
}
|
|
364
|
-
if (!postgresql || postgresql.schemaValidation
|
|
365
|
+
if (!postgresql || postgresql.schemaValidation !== "NONE") {
|
|
365
366
|
return;
|
|
366
367
|
}
|
|
367
368
|
postgresql.schemaValidation = "STRICT";
|
|
@@ -32,7 +32,7 @@ async function default_1(context, options) {
|
|
|
32
32
|
const filters = (0, filters_1.getResourceFilters)(options);
|
|
33
33
|
const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, options.config, c.source)));
|
|
34
34
|
for (const si of serviceInfos) {
|
|
35
|
-
si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory);
|
|
35
|
+
si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory, options.dryRun);
|
|
36
36
|
}
|
|
37
37
|
const unmatchedFilters = filters === null || filters === void 0 ? void 0 : filters.filter((f) => {
|
|
38
38
|
const serviceMatched = serviceInfos.some((s) => s.dataConnectYaml.serviceId === f.serviceId);
|
|
@@ -10,6 +10,8 @@ const developmentServer_2 = require("./developmentServer");
|
|
|
10
10
|
const types_1 = require("../types");
|
|
11
11
|
const config_1 = require("./config");
|
|
12
12
|
const projectPath_1 = require("../../projectPath");
|
|
13
|
+
const registry_1 = require("../registry");
|
|
14
|
+
const env_1 = require("../env");
|
|
13
15
|
async function start(options) {
|
|
14
16
|
var _a;
|
|
15
17
|
const hostname = constants_1.DEFAULT_HOST;
|
|
@@ -29,7 +31,7 @@ async function serve(port, startCommand, backendRelativeDir) {
|
|
|
29
31
|
for (const env of apphostingLocalConfig.environmentVariables) {
|
|
30
32
|
environmentVariablesAsRecord[env.variable] = env.value;
|
|
31
33
|
}
|
|
32
|
-
const environmentVariablesToInject = Object.assign(Object.assign({}, environmentVariablesAsRecord), { PORT: port.toString() });
|
|
34
|
+
const environmentVariablesToInject = Object.assign(Object.assign(Object.assign({}, getEmulatorEnvs()), environmentVariablesAsRecord), { PORT: port.toString() });
|
|
33
35
|
if (startCommand) {
|
|
34
36
|
developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `running custom start command: '${startCommand}'`);
|
|
35
37
|
await (0, spawn_1.spawnWithCommandString)(startCommand, backendRoot, environmentVariablesToInject);
|
|
@@ -46,3 +48,9 @@ function availablePort(host, port) {
|
|
|
46
48
|
family: (0, net_1.isIPv4)(host) ? "IPv4" : "IPv6",
|
|
47
49
|
});
|
|
48
50
|
}
|
|
51
|
+
function getEmulatorEnvs() {
|
|
52
|
+
const envs = {};
|
|
53
|
+
const emulatorInfos = registry_1.EmulatorRegistry.listRunningWithInfo();
|
|
54
|
+
(0, env_1.setEnvVarsForEmulators)(envs, emulatorInfos);
|
|
55
|
+
return envs;
|
|
56
|
+
}
|
|
@@ -572,6 +572,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
572
572
|
postgresListen: listenForEmulator["dataconnect.postgres"],
|
|
573
573
|
enable_output_generated_sdk: true,
|
|
574
574
|
enable_output_schema_extensions: true,
|
|
575
|
+
debug: options.debug,
|
|
575
576
|
};
|
|
576
577
|
if (exportMetadata.dataconnect) {
|
|
577
578
|
utils.assertIsString(options.import);
|
|
@@ -588,7 +589,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
588
589
|
default: false,
|
|
589
590
|
}))) {
|
|
590
591
|
await cleanShutdown();
|
|
591
|
-
|
|
592
|
+
throw new error_1.FirebaseError("Command aborted");
|
|
592
593
|
}
|
|
593
594
|
}
|
|
594
595
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).logLabeled("BULLET", "dataconnect", `Importing data from ${exportMetadataFilePath}`);
|
|
@@ -30,13 +30,14 @@ const logger_1 = require("../../logger");
|
|
|
30
30
|
const error_1 = require("../../error");
|
|
31
31
|
exports.TRUNCATE_TABLES_SQL = `
|
|
32
32
|
DO $do$
|
|
33
|
+
DECLARE _clear text;
|
|
33
34
|
BEGIN
|
|
34
|
-
|
|
35
|
-
(SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
|
|
35
|
+
SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
|
|
36
36
|
FROM pg_class
|
|
37
37
|
WHERE relkind = 'r'
|
|
38
38
|
AND relnamespace = 'public'::regnamespace
|
|
39
|
-
|
|
39
|
+
INTO _clear;
|
|
40
|
+
EXECUTE COALESCE(_clear, 'select now()');
|
|
40
41
|
END
|
|
41
42
|
$do$;`;
|
|
42
43
|
class PostgresServer {
|
|
@@ -77,8 +78,6 @@ class PostgresServer {
|
|
|
77
78
|
const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
|
|
78
79
|
const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
|
|
79
80
|
const pgliteArgs = {
|
|
80
|
-
username: this.username,
|
|
81
|
-
database: this.database,
|
|
82
81
|
debug: this.debug,
|
|
83
82
|
extensions: {
|
|
84
83
|
vector,
|
|
@@ -133,8 +132,6 @@ class PostgresServer {
|
|
|
133
132
|
constructor(args) {
|
|
134
133
|
this.db = undefined;
|
|
135
134
|
this.server = undefined;
|
|
136
|
-
this.username = args.username;
|
|
137
|
-
this.database = args.database;
|
|
138
135
|
this.dataDirectory = args.dataDirectory;
|
|
139
136
|
this.importPath = args.importPath;
|
|
140
137
|
this.debug = args.debug ? 5 : 0;
|
|
@@ -65,13 +65,14 @@ class DataConnectEmulator {
|
|
|
65
65
|
this.logger.logLabeled("INFO", "dataconnect", `FIREBASE_DATACONNECT_POSTGRESQL_STRING is set to ${clc.bold(connStr)} - using that instead of starting a new database`);
|
|
66
66
|
}
|
|
67
67
|
else if (pgHost && pgPort) {
|
|
68
|
-
|
|
68
|
+
let dataDirectory = this.args.config.get("emulators.dataconnect.dataDir");
|
|
69
|
+
if (dataDirectory) {
|
|
70
|
+
dataDirectory = this.args.config.path(dataDirectory);
|
|
71
|
+
}
|
|
69
72
|
const postgresDumpPath = this.args.importPath
|
|
70
|
-
? path.join(this.args.importPath, "postgres.tar.gz")
|
|
73
|
+
? path.join(this.args.config.path(this.args.importPath), "postgres.tar.gz")
|
|
71
74
|
: undefined;
|
|
72
75
|
this.postgresServer = new pgliteServer_1.PostgresServer({
|
|
73
|
-
database: dbId,
|
|
74
|
-
username: "fdc",
|
|
75
76
|
dataDirectory,
|
|
76
77
|
importPath: postgresDumpPath,
|
|
77
78
|
debug: this.args.debug,
|
|
@@ -137,7 +138,7 @@ class DataConnectEmulator {
|
|
|
137
138
|
}
|
|
138
139
|
async exportData(exportPath) {
|
|
139
140
|
if (this.postgresServer) {
|
|
140
|
-
await this.postgresServer.exportData(path.join(exportPath, "postgres.tar.gz"));
|
|
141
|
+
await this.postgresServer.exportData(path.join(this.args.config.path(exportPath), "postgres.tar.gz"));
|
|
141
142
|
}
|
|
142
143
|
else {
|
|
143
144
|
throw new error_1.FirebaseError("The Data Connect emulator is currently connected to a separate Postgres instance. Export is not supported.");
|
|
@@ -176,6 +177,9 @@ class DataConnectEmulator {
|
|
|
176
177
|
var _a;
|
|
177
178
|
const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
|
|
178
179
|
const cmd = ["--logtostderr", "-v=2", "build", `--config_dir=${args.configDir}`];
|
|
180
|
+
if (args.projectId) {
|
|
181
|
+
cmd.push(`--project_id=${args.projectId}`);
|
|
182
|
+
}
|
|
179
183
|
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8" });
|
|
180
184
|
if ((0, downloadableEmulators_1.isIncomaptibleArchError)(res.error)) {
|
|
181
185
|
throw new error_1.FirebaseError(`Unkown system error when running the Data Connect toolkit. ` +
|
|
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
48
48
|
},
|
|
49
49
|
dataconnect: process.platform === "darwin"
|
|
50
50
|
? {
|
|
51
|
-
version: "1.7.
|
|
52
|
-
expectedSize:
|
|
53
|
-
expectedChecksum: "
|
|
51
|
+
version: "1.7.6",
|
|
52
|
+
expectedSize: 25322240,
|
|
53
|
+
expectedChecksum: "2dda7394330fd1aba37605466941eef0",
|
|
54
54
|
}
|
|
55
55
|
: process.platform === "win32"
|
|
56
56
|
? {
|
|
57
|
-
version: "1.7.
|
|
58
|
-
expectedSize:
|
|
59
|
-
expectedChecksum: "
|
|
57
|
+
version: "1.7.6",
|
|
58
|
+
expectedSize: 25752064,
|
|
59
|
+
expectedChecksum: "283c11e28a0072b596531b79462a8e94",
|
|
60
60
|
}
|
|
61
61
|
: {
|
|
62
|
-
version: "1.7.
|
|
63
|
-
expectedSize:
|
|
64
|
-
expectedChecksum: "
|
|
62
|
+
version: "1.7.6",
|
|
63
|
+
expectedSize: 25235608,
|
|
64
|
+
expectedChecksum: "f66e24b3726df57cd1f1685a64a87904",
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
exports.DownloadDetails = {
|
package/lib/emulator/hub.js
CHANGED
|
@@ -38,6 +38,11 @@ class EmulatorHubClient {
|
|
|
38
38
|
const res = await this.tryOrigins((client) => client.get(hub_1.EmulatorHub.PATH_EMULATORS));
|
|
39
39
|
return res.body;
|
|
40
40
|
}
|
|
41
|
+
async clearDataConnectData() {
|
|
42
|
+
const origin = await this.getStatus();
|
|
43
|
+
const apiClient = new apiv2_1.Client({ urlPrefix: origin, auth: false });
|
|
44
|
+
await apiClient.post(hub_1.EmulatorHub.PATH_CLEAR_DATA_CONNECT);
|
|
45
|
+
}
|
|
41
46
|
async postExport(options) {
|
|
42
47
|
const origin = await this.getStatus();
|
|
43
48
|
const apiClient = new apiv2_1.Client({ urlPrefix: origin, auth: false });
|
package/lib/experiments.js
CHANGED
|
@@ -106,6 +106,12 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
106
106
|
default: true,
|
|
107
107
|
public: false,
|
|
108
108
|
},
|
|
109
|
+
fdcconnectorevolution: {
|
|
110
|
+
shortDescription: "Enable Data Connect connector evolution warnings.",
|
|
111
|
+
fullDescription: "Enable Data Connect connector evolution warnings.",
|
|
112
|
+
default: false,
|
|
113
|
+
public: false,
|
|
114
|
+
},
|
|
109
115
|
});
|
|
110
116
|
function isValidExperiment(name) {
|
|
111
117
|
return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
|
|
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getErrorMessage = exports.getValidator = void 0;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const ajv_1 = require("ajv");
|
|
7
|
+
const ajv_formats_1 = require("ajv-formats");
|
|
8
|
+
const ajv = new ajv_1.Ajv({ allowUnionTypes: true });
|
|
9
|
+
(0, ajv_formats_1.default)(ajv);
|
|
8
10
|
let _VALIDATOR = undefined;
|
|
9
11
|
function getValidator() {
|
|
10
12
|
if (!_VALIDATOR) {
|
|
@@ -17,13 +19,13 @@ function getValidator() {
|
|
|
17
19
|
exports.getValidator = getValidator;
|
|
18
20
|
function getErrorMessage(e) {
|
|
19
21
|
if (e.keyword === "additionalProperties") {
|
|
20
|
-
return `Object "${e.
|
|
22
|
+
return `Object "${e.instancePath}" in "firebase.json" has unknown property: ${JSON.stringify(e.params)}`;
|
|
21
23
|
}
|
|
22
24
|
else if (e.keyword === "required") {
|
|
23
|
-
return `Object "${e.
|
|
25
|
+
return `Object "${e.instancePath}" in "firebase.json" is missing required property: ${JSON.stringify(e.params)}`;
|
|
24
26
|
}
|
|
25
27
|
else {
|
|
26
|
-
return `Field "${e.
|
|
28
|
+
return `Field "${e.instancePath}" in "firebase.json" is possibly invalid: ${e.message}`;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
exports.getErrorMessage = getErrorMessage;
|
|
@@ -259,9 +259,6 @@ function functionFromEndpoint(endpoint) {
|
|
|
259
259
|
}
|
|
260
260
|
else if (backend.isCallableTriggered(endpoint)) {
|
|
261
261
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
|
|
262
|
-
if (endpoint.callableTrigger.genkitAction) {
|
|
263
|
-
gcfFunction.labels["genkit-action"] = endpoint.callableTrigger.genkitAction;
|
|
264
|
-
}
|
|
265
262
|
}
|
|
266
263
|
else if (backend.isBlockingTriggered(endpoint)) {
|
|
267
264
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.BLOCKING_LABEL]: constants_1.BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
|
|
@@ -297,9 +294,6 @@ function endpointFromFunction(gcfFunction) {
|
|
|
297
294
|
trigger = {
|
|
298
295
|
callableTrigger: {},
|
|
299
296
|
};
|
|
300
|
-
if (gcfFunction.labels["genkit-action"]) {
|
|
301
|
-
trigger.callableTrigger.genkitAction = gcfFunction.labels["genkit-action"];
|
|
302
|
-
}
|
|
303
297
|
}
|
|
304
298
|
else if ((_d = gcfFunction.labels) === null || _d === void 0 ? void 0 : _d[constants_1.BLOCKING_LABEL]) {
|
|
305
299
|
trigger = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.actuate = exports.doSetup = void 0;
|
|
3
|
+
exports.toDNSCompatibleId = exports.actuate = exports.doSetup = void 0;
|
|
4
4
|
const path_1 = require("path");
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const fs = require("fs-extra");
|
|
@@ -100,7 +100,8 @@ async function askQuestions(setup, isBillingEnabled) {
|
|
|
100
100
|
})));
|
|
101
101
|
}
|
|
102
102
|
else {
|
|
103
|
-
|
|
103
|
+
const defaultServiceId = toDNSCompatibleId((0, path_1.basename)(process.cwd()));
|
|
104
|
+
info.serviceId = info.serviceId || defaultServiceId;
|
|
104
105
|
info.cloudSqlInstanceId = info.cloudSqlInstanceId || `${info.serviceId || "app"}-fdc`;
|
|
105
106
|
info.locationId = info.locationId || `us-central1`;
|
|
106
107
|
info.cloudSqlDatabase = info.cloudSqlDatabase || `fdcdb`;
|
|
@@ -339,3 +340,17 @@ async function locationChoices(setup) {
|
|
|
339
340
|
];
|
|
340
341
|
}
|
|
341
342
|
}
|
|
343
|
+
function toDNSCompatibleId(id) {
|
|
344
|
+
let defaultServiceId = (0, path_1.basename)(id)
|
|
345
|
+
.toLowerCase()
|
|
346
|
+
.replaceAll(/[^a-z0-9-]/g, "")
|
|
347
|
+
.slice(0, 63);
|
|
348
|
+
while (defaultServiceId.endsWith("-") && defaultServiceId.length) {
|
|
349
|
+
defaultServiceId = defaultServiceId.slice(0, defaultServiceId.length - 1);
|
|
350
|
+
}
|
|
351
|
+
while (defaultServiceId.startsWith("-") && defaultServiceId.length) {
|
|
352
|
+
defaultServiceId = defaultServiceId.slice(1, defaultServiceId.length);
|
|
353
|
+
}
|
|
354
|
+
return defaultServiceId || "app";
|
|
355
|
+
}
|
|
356
|
+
exports.toDNSCompatibleId = toDNSCompatibleId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "13.29.
|
|
3
|
+
"version": "13.29.2",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
"@google-cloud/cloud-sql-connector": "^1.3.3",
|
|
65
65
|
"@google-cloud/pubsub": "^4.5.0",
|
|
66
66
|
"abort-controller": "^3.0.0",
|
|
67
|
-
"ajv": "^
|
|
67
|
+
"ajv": "^8.17.1",
|
|
68
|
+
"ajv-formats": "3.0.1",
|
|
68
69
|
"archiver": "^7.0.0",
|
|
69
70
|
"async-lock": "1.4.1",
|
|
70
71
|
"body-parser": "^1.19.0",
|
|
@@ -75,8 +76,8 @@
|
|
|
75
76
|
"commander": "^5.1.0",
|
|
76
77
|
"configstore": "^5.0.1",
|
|
77
78
|
"cors": "^2.8.5",
|
|
78
|
-
"cross-env": "^
|
|
79
|
-
"cross-spawn": "^7.0.
|
|
79
|
+
"cross-env": "^7.0.3",
|
|
80
|
+
"cross-spawn": "^7.0.5",
|
|
80
81
|
"csv-parse": "^5.0.4",
|
|
81
82
|
"deep-equal-in-any-order": "^2.0.6",
|
|
82
83
|
"exegesis": "^4.2.0",
|