firebase-tools 13.25.0 → 13.27.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/appdistribution/client.js +62 -8
- package/lib/appdistribution/distribution.js +1 -1
- package/lib/apphosting/backend.js +4 -4
- package/lib/apphosting/config.js +86 -9
- package/lib/apphosting/secrets/index.js +5 -43
- package/lib/apphosting/yaml.js +3 -0
- package/lib/archiveDirectory.js +1 -1
- package/lib/auth.js +1 -1
- package/lib/command.js +9 -1
- package/lib/commands/appdistribution-distribute.js +4 -4
- package/lib/commands/{appdistribution-group-create.js → appdistribution-groups-create.js} +2 -1
- package/lib/commands/{appdistribution-group-delete.js → appdistribution-groups-delete.js} +3 -2
- package/lib/commands/appdistribution-groups-list.js +56 -0
- package/lib/commands/appdistribution-testers-list.js +54 -0
- package/lib/commands/appdistribution-testers-remove.js +1 -1
- package/lib/commands/apphosting-backends-delete.js +3 -1
- package/lib/commands/apphosting-backends-get.js +1 -1
- package/lib/commands/apphosting-config-export.js +4 -26
- package/lib/commands/database-import.js +4 -2
- package/lib/commands/database-push.js +4 -2
- package/lib/commands/database-set.js +4 -2
- package/lib/commands/database-settings-get.js +1 -1
- package/lib/commands/database-settings-set.js +1 -1
- package/lib/commands/ext-dev-init.js +2 -2
- package/lib/commands/ext-dev-list.js +1 -1
- package/lib/commands/ext-dev-register.js +2 -2
- package/lib/commands/ext-dev-upload.js +2 -2
- package/lib/commands/ext-dev-usage.js +2 -2
- package/lib/commands/ext-install.js +2 -2
- package/lib/commands/index.js +7 -6
- package/lib/commands/use.js +1 -1
- package/lib/deploy/extensions/deploy.js +3 -1
- package/lib/deploy/extensions/deploymentSummary.js +4 -1
- package/lib/deploy/extensions/planner.js +14 -3
- package/lib/deploy/extensions/prepare.js +9 -9
- package/lib/deploy/functions/ensure.js +1 -1
- package/lib/deploy/functions/release/fabricator.js +3 -3
- package/lib/deploy/lifecycleHooks.js +2 -1
- package/lib/emulator/apphosting/config.js +13 -3
- package/lib/emulator/apphosting/developmentServer.js +32 -0
- package/lib/emulator/apphosting/index.js +5 -3
- package/lib/emulator/apphosting/serve.js +15 -12
- package/lib/emulator/commandUtils.js +2 -1
- package/lib/emulator/controller.js +27 -18
- package/lib/emulator/dataconnect/pgliteServer.js +51 -18
- package/lib/emulator/dataconnectEmulator.js +26 -2
- package/lib/emulator/downloadableEmulators.js +11 -11
- package/lib/emulator/hub.js +26 -7
- package/lib/emulator/hubExport.js +23 -0
- package/lib/emulator/initEmulators.js +49 -0
- package/lib/emulator/types.js +2 -2
- package/lib/emulator/ui.js +47 -25
- package/lib/error.js +8 -1
- package/lib/gcp/cloudfunctionsv2.js +1 -0
- package/lib/getProjectNumber.js +1 -1
- package/lib/init/features/emulators.js +8 -0
- package/lib/init/features/project.js +7 -6
- package/lib/logger.js +2 -2
- package/lib/management/projects.js +24 -4
- package/lib/projectUtils.js +1 -1
- package/lib/requireDatabaseInstance.js +1 -1
- package/lib/requirePermissions.js +1 -1
- package/lib/rulesDeploy.js +1 -1
- package/lib/templates.js +2 -2
- package/lib/utils.js +20 -8
- package/lib/vsCodeUtils.js +8 -0
- package/package.json +2 -2
- package/schema/firebase-config.json +6 -0
- package/lib/emulator/apphosting/utils.js +0 -18
|
@@ -58,7 +58,7 @@ async function exportOnExit(options) {
|
|
|
58
58
|
await exportEmulatorData(exportOnExitDir, options, "exit");
|
|
59
59
|
}
|
|
60
60
|
catch (e) {
|
|
61
|
-
utils.logWarning(e);
|
|
61
|
+
utils.logWarning(`${e}`);
|
|
62
62
|
utils.logWarning(`Automatic export to "${exportOnExitDir}" failed, going to exit now...`);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -561,19 +561,30 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
561
561
|
else if (config.length > 1) {
|
|
562
562
|
logger_1.logger.warn(`TODO: Add support for multiple services in the Data Connect emulator. Currently emulating first service ${config[0].source}`);
|
|
563
563
|
}
|
|
564
|
-
const
|
|
565
|
-
const dataConnectEmulator = new dataconnectEmulator_1.DataConnectEmulator({
|
|
564
|
+
const args = {
|
|
566
565
|
listen: listenForEmulator.dataconnect,
|
|
567
566
|
projectId,
|
|
568
567
|
auto_download: true,
|
|
569
|
-
configDir,
|
|
568
|
+
configDir: config[0].source,
|
|
570
569
|
rc: options.rc,
|
|
571
570
|
config: options.config,
|
|
572
571
|
autoconnectToPostgres: true,
|
|
573
572
|
postgresListen: listenForEmulator["dataconnect.postgres"],
|
|
574
573
|
enable_output_generated_sdk: true,
|
|
575
574
|
enable_output_schema_extensions: true,
|
|
576
|
-
}
|
|
575
|
+
};
|
|
576
|
+
if (exportMetadata.dataconnect) {
|
|
577
|
+
utils.assertIsString(options.import);
|
|
578
|
+
const importDirAbsPath = path.resolve(options.import);
|
|
579
|
+
const exportMetadataFilePath = path.resolve(importDirAbsPath, exportMetadata.dataconnect.path);
|
|
580
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).logLabeled("BULLET", "dataconnect", `Importing data from ${exportMetadataFilePath}`);
|
|
581
|
+
args.importPath = exportMetadataFilePath;
|
|
582
|
+
void (0, track_1.trackEmulator)("emulator_import", {
|
|
583
|
+
initiated_by: "start",
|
|
584
|
+
emulator_name: types_1.Emulators.DATACONNECT,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const dataConnectEmulator = new dataconnectEmulator_1.DataConnectEmulator(args);
|
|
577
588
|
await startEmulator(dataConnectEmulator);
|
|
578
589
|
}
|
|
579
590
|
if (listenForEmulator.storage) {
|
|
@@ -601,18 +612,17 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
601
612
|
});
|
|
602
613
|
await startEmulator(hostingEmulator);
|
|
603
614
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
}
|
|
615
|
+
const apphostingConfig = (_l = options.config.src.emulators) === null || _l === void 0 ? void 0 : _l[types_1.Emulators.APPHOSTING];
|
|
616
|
+
if (listenForEmulator.apphosting) {
|
|
617
|
+
const apphostingAddr = legacyGetFirstAddr(types_1.Emulators.APPHOSTING);
|
|
618
|
+
const apphostingEmulator = new apphosting_1.AppHostingEmulator({
|
|
619
|
+
host: apphostingAddr.host,
|
|
620
|
+
port: apphostingAddr.port,
|
|
621
|
+
startCommandOverride: apphostingConfig === null || apphostingConfig === void 0 ? void 0 : apphostingConfig.startCommandOverride,
|
|
622
|
+
rootDirectory: apphostingConfig === null || apphostingConfig === void 0 ? void 0 : apphostingConfig.rootDirectory,
|
|
623
|
+
options,
|
|
624
|
+
});
|
|
625
|
+
await startEmulator(apphostingEmulator);
|
|
616
626
|
}
|
|
617
627
|
if (listenForEmulator.logging) {
|
|
618
628
|
const loggingAddr = legacyGetFirstAddr(types_1.Emulators.LOGGING);
|
|
@@ -630,7 +640,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
630
640
|
if (listenForEmulator.ui) {
|
|
631
641
|
const ui = new ui_1.EmulatorUI({
|
|
632
642
|
projectId: projectId,
|
|
633
|
-
auto_download: true,
|
|
634
643
|
listen: listenForEmulator[types_1.Emulators.UI],
|
|
635
644
|
});
|
|
636
645
|
await startEmulator(ui);
|
|
@@ -19,17 +19,28 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
|
|
|
19
19
|
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.PGliteExtendedQueryPatch = exports.PostgresServer = void 0;
|
|
22
|
+
exports.PGliteExtendedQueryPatch = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
|
|
23
23
|
const pglite_1 = require("@electric-sql/pglite");
|
|
24
24
|
const { dynamicImport } = require(true && "../../dynamicImport");
|
|
25
25
|
const net = require("node:net");
|
|
26
|
+
const fs = require("fs");
|
|
26
27
|
const index_1 = require("./pg-gateway/index");
|
|
27
28
|
const node_1 = require("./pg-gateway/platforms/node");
|
|
28
29
|
const logger_1 = require("../../logger");
|
|
30
|
+
exports.TRUNCATE_TABLES_SQL = `
|
|
31
|
+
DO $do$
|
|
32
|
+
BEGIN
|
|
33
|
+
EXECUTE
|
|
34
|
+
(SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
|
|
35
|
+
FROM pg_class
|
|
36
|
+
WHERE relkind = 'r'
|
|
37
|
+
AND relnamespace = 'public'::regnamespace
|
|
38
|
+
);
|
|
39
|
+
END
|
|
40
|
+
$do$;`;
|
|
29
41
|
class PostgresServer {
|
|
30
42
|
async createPGServer(host = "127.0.0.1", port) {
|
|
31
|
-
const
|
|
32
|
-
await db.waitReady;
|
|
43
|
+
const getDb = this.getDb.bind(this);
|
|
33
44
|
const server = net.createServer(async (socket) => {
|
|
34
45
|
const connection = await (0, node_1.fromNodeSocket)(socket, {
|
|
35
46
|
serverVersion: "16.3 (PGlite 0.2.0)",
|
|
@@ -38,6 +49,7 @@ class PostgresServer {
|
|
|
38
49
|
if (!isAuthenticated) {
|
|
39
50
|
return;
|
|
40
51
|
}
|
|
52
|
+
const db = await getDb();
|
|
41
53
|
const result = await db.execProtocolRaw(data);
|
|
42
54
|
return extendedQueryPatch.filterResponse(data, result);
|
|
43
55
|
},
|
|
@@ -55,29 +67,50 @@ class PostgresServer {
|
|
|
55
67
|
resolve();
|
|
56
68
|
});
|
|
57
69
|
});
|
|
58
|
-
await db.waitReady;
|
|
59
70
|
await listeningPromise;
|
|
60
71
|
return server;
|
|
61
72
|
}
|
|
62
73
|
async getDb() {
|
|
63
|
-
if (this.db) {
|
|
64
|
-
|
|
74
|
+
if (!this.db) {
|
|
75
|
+
const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
|
|
76
|
+
const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
|
|
77
|
+
const pgliteArgs = {
|
|
78
|
+
username: this.username,
|
|
79
|
+
database: this.database,
|
|
80
|
+
debug: 0,
|
|
81
|
+
extensions: {
|
|
82
|
+
vector,
|
|
83
|
+
uuidOssp,
|
|
84
|
+
},
|
|
85
|
+
dataDir: this.dataDirectory,
|
|
86
|
+
};
|
|
87
|
+
if (this.importPath) {
|
|
88
|
+
logger_1.logger.debug(`Importing from ${this.importPath}`);
|
|
89
|
+
const rf = fs.readFileSync(this.importPath);
|
|
90
|
+
const file = new File([rf], this.importPath);
|
|
91
|
+
pgliteArgs.loadDataDir = file;
|
|
92
|
+
}
|
|
93
|
+
this.db = await pglite_1.PGlite.create(pgliteArgs);
|
|
94
|
+
await this.db.waitReady;
|
|
65
95
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
return this.db;
|
|
97
|
+
}
|
|
98
|
+
async clearDb() {
|
|
99
|
+
const db = await this.getDb();
|
|
100
|
+
await db.query(exports.TRUNCATE_TABLES_SQL);
|
|
101
|
+
}
|
|
102
|
+
async exportData(exportPath) {
|
|
103
|
+
const db = await this.getDb();
|
|
104
|
+
const dump = await db.dumpDataDir();
|
|
105
|
+
const arrayBuff = await dump.arrayBuffer();
|
|
106
|
+
fs.writeFileSync(exportPath, new Uint8Array(arrayBuff));
|
|
77
107
|
}
|
|
78
|
-
constructor(database, username) {
|
|
108
|
+
constructor(database, username, dataDirectory, importPath) {
|
|
109
|
+
this.db = undefined;
|
|
79
110
|
this.username = username;
|
|
80
111
|
this.database = database;
|
|
112
|
+
this.dataDirectory = dataDirectory;
|
|
113
|
+
this.importPath = importPath;
|
|
81
114
|
}
|
|
82
115
|
}
|
|
83
116
|
exports.PostgresServer = PostgresServer;
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DataConnectEmulatorClient = exports.DataConnectEmulator = exports.dataConnectEmulatorEvents = void 0;
|
|
4
4
|
const childProcess = require("child_process");
|
|
5
|
+
const pg = require("pg");
|
|
5
6
|
const events_1 = require("events");
|
|
6
7
|
const clc = require("colorette");
|
|
8
|
+
const path = require("path");
|
|
7
9
|
const api_1 = require("../api");
|
|
8
10
|
const constants_1 = require("./constants");
|
|
9
11
|
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
@@ -63,8 +65,12 @@ class DataConnectEmulator {
|
|
|
63
65
|
this.logger.logLabeled("INFO", "dataconnect", `FIREBASE_DATACONNECT_POSTGRESQL_STRING is set to ${clc.bold(connStr)} - using that instead of starting a new database`);
|
|
64
66
|
}
|
|
65
67
|
else if (pgHost && pgPort) {
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
+
const dataDirectory = this.args.config.get("emulators.dataconnect.dataDir");
|
|
69
|
+
const postgresDumpPath = this.args.importPath
|
|
70
|
+
? path.join(this.args.importPath, "postgres.tar.gz")
|
|
71
|
+
: undefined;
|
|
72
|
+
this.postgresServer = new pgliteServer_1.PostgresServer(dbId, "postgres", dataDirectory, postgresDumpPath);
|
|
73
|
+
const server = await this.postgresServer.createPGServer(pgHost, pgPort);
|
|
68
74
|
const connectableHost = (0, utils_1.connectableHostname)(pgHost);
|
|
69
75
|
connStr = `postgres://${connectableHost}:${pgPort}/${dbId}?sslmode=disable`;
|
|
70
76
|
server.on("error", (err) => {
|
|
@@ -110,6 +116,24 @@ class DataConnectEmulator {
|
|
|
110
116
|
getName() {
|
|
111
117
|
return types_1.Emulators.DATACONNECT;
|
|
112
118
|
}
|
|
119
|
+
async clearData() {
|
|
120
|
+
if (this.postgresServer) {
|
|
121
|
+
await this.postgresServer.clearDb();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const conn = new pg.Client((0, api_1.dataConnectLocalConnString)());
|
|
125
|
+
await conn.query(pgliteServer_1.TRUNCATE_TABLES_SQL);
|
|
126
|
+
await conn.end();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async exportData(exportPath) {
|
|
130
|
+
if (this.postgresServer) {
|
|
131
|
+
await this.postgresServer.exportData(path.join(exportPath, "postgres.tar.gz"));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new error_1.FirebaseError("The Data Connect emulator is currently connected to a separate Postgres instance. Export is not supported.");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
113
137
|
static async generate(args) {
|
|
114
138
|
const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
|
|
115
139
|
const cmd = [
|
|
@@ -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.3",
|
|
52
|
+
expectedSize: 25211648,
|
|
53
|
+
expectedChecksum: "8410794304b2ae340c3facf07d7edc16",
|
|
54
54
|
}
|
|
55
55
|
: process.platform === "win32"
|
|
56
56
|
? {
|
|
57
|
-
version: "1.7.
|
|
58
|
-
expectedSize:
|
|
59
|
-
expectedChecksum: "
|
|
57
|
+
version: "1.7.3",
|
|
58
|
+
expectedSize: 25641984,
|
|
59
|
+
expectedChecksum: "a4bd0f9d9d884528fa4494e4d7918c08",
|
|
60
60
|
}
|
|
61
61
|
: {
|
|
62
|
-
version: "1.7.
|
|
63
|
-
expectedSize:
|
|
64
|
-
expectedChecksum: "
|
|
62
|
+
version: "1.7.3",
|
|
63
|
+
expectedSize: 25125016,
|
|
64
|
+
expectedChecksum: "48660e6370aeed973f33c3420c3255fb",
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
67
|
exports.DownloadDetails = {
|
|
@@ -233,8 +233,8 @@ const Commands = {
|
|
|
233
233
|
shell: true,
|
|
234
234
|
},
|
|
235
235
|
ui: {
|
|
236
|
-
binary: "
|
|
237
|
-
args: [
|
|
236
|
+
binary: "",
|
|
237
|
+
args: [],
|
|
238
238
|
optionalArgs: [],
|
|
239
239
|
joinArgs: false,
|
|
240
240
|
shell: false,
|
package/lib/emulator/hub.js
CHANGED
|
@@ -10,7 +10,7 @@ const types_1 = require("./types");
|
|
|
10
10
|
const hubExport_1 = require("./hubExport");
|
|
11
11
|
const registry_1 = require("./registry");
|
|
12
12
|
const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
|
|
13
|
-
const
|
|
13
|
+
const vsCodeUtils_1 = require("../vsCodeUtils");
|
|
14
14
|
const pkg = require("../../package.json");
|
|
15
15
|
class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
16
16
|
static readLocatorFile(projectId) {
|
|
@@ -20,7 +20,7 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
20
20
|
}
|
|
21
21
|
const data = fs.readFileSync(locatorPath, "utf8").toString();
|
|
22
22
|
const locator = JSON.parse(data);
|
|
23
|
-
if (!
|
|
23
|
+
if (!vsCodeUtils_1.isVSCodeExtension && locator.version !== this.CLI_VERSION) {
|
|
24
24
|
logger_1.logger.debug(`Found locator with mismatched version, ignoring: ${JSON.stringify(locator)}`);
|
|
25
25
|
return undefined;
|
|
26
26
|
}
|
|
@@ -41,17 +41,20 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
41
41
|
await super.start();
|
|
42
42
|
await this.writeLocatorFile();
|
|
43
43
|
}
|
|
44
|
+
getRunningEmulatorsMapping() {
|
|
45
|
+
const emulators = {};
|
|
46
|
+
for (const info of registry_1.EmulatorRegistry.listRunningWithInfo()) {
|
|
47
|
+
emulators[info.name] = Object.assign({ listen: this.args.listenForEmulator[info.name] }, info);
|
|
48
|
+
}
|
|
49
|
+
return emulators;
|
|
50
|
+
}
|
|
44
51
|
async createExpressApp() {
|
|
45
52
|
const app = await super.createExpressApp();
|
|
46
53
|
app.get("/", (req, res) => {
|
|
47
54
|
res.json(Object.assign(Object.assign({}, this.getLocator()), { host: utils.connectableHostname(this.args.listen[0].address), port: this.args.listen[0].port }));
|
|
48
55
|
});
|
|
49
56
|
app.get(EmulatorHub.PATH_EMULATORS, (req, res) => {
|
|
50
|
-
|
|
51
|
-
for (const info of registry_1.EmulatorRegistry.listRunningWithInfo()) {
|
|
52
|
-
body[info.name] = Object.assign({ listen: this.args.listenForEmulator[info.name] }, info);
|
|
53
|
-
}
|
|
54
|
-
res.json(body);
|
|
57
|
+
res.json(this.getRunningEmulatorsMapping());
|
|
55
58
|
});
|
|
56
59
|
app.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
|
|
57
60
|
if (req.headers.origin) {
|
|
@@ -102,6 +105,21 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
102
105
|
await emu.reloadTriggers();
|
|
103
106
|
res.status(200).json({ enabled: true });
|
|
104
107
|
});
|
|
108
|
+
app.post(EmulatorHub.PATH_CLEAR_DATA_CONNECT, async (req, res) => {
|
|
109
|
+
if (req.headers.origin) {
|
|
110
|
+
res.status(403).json({
|
|
111
|
+
message: `Clear Data Connect cannot be triggered by external callers.`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
utils.logLabeledBullet("emulators", `Clearing data from Data Connect data sources.`);
|
|
115
|
+
const instance = registry_1.EmulatorRegistry.get(types_1.Emulators.DATACONNECT);
|
|
116
|
+
if (!instance) {
|
|
117
|
+
res.status(400).json({ error: "The Data Connect emulator is not running." });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
await instance.clearData();
|
|
121
|
+
res.status(200).send("Data cleared");
|
|
122
|
+
});
|
|
105
123
|
return app;
|
|
106
124
|
}
|
|
107
125
|
async stop() {
|
|
@@ -166,3 +184,4 @@ EmulatorHub.PATH_EXPORT = "/_admin/export";
|
|
|
166
184
|
EmulatorHub.PATH_DISABLE_FUNCTIONS = "/functions/disableBackgroundTriggers";
|
|
167
185
|
EmulatorHub.PATH_ENABLE_FUNCTIONS = "/functions/enableBackgroundTriggers";
|
|
168
186
|
EmulatorHub.PATH_EMULATORS = "/emulators";
|
|
187
|
+
EmulatorHub.PATH_CLEAR_DATA_CONNECT = "/dataconnect/clearData";
|
|
@@ -71,6 +71,13 @@ class HubExport {
|
|
|
71
71
|
};
|
|
72
72
|
await this.exportStorage(metadata);
|
|
73
73
|
}
|
|
74
|
+
if (shouldExport(types_1.Emulators.DATACONNECT)) {
|
|
75
|
+
metadata.dataconnect = {
|
|
76
|
+
version: hub_1.EmulatorHub.CLI_VERSION,
|
|
77
|
+
path: "dataconnect_export",
|
|
78
|
+
};
|
|
79
|
+
await this.exportDataConnect(metadata);
|
|
80
|
+
}
|
|
74
81
|
if (!fs.existsSync(this.exportPath)) {
|
|
75
82
|
fs.mkdirSync(this.exportPath);
|
|
76
83
|
}
|
|
@@ -197,6 +204,22 @@ class HubExport {
|
|
|
197
204
|
throw new error_1.FirebaseError(`Failed to export storage: ${await res.response.text()}`);
|
|
198
205
|
}
|
|
199
206
|
}
|
|
207
|
+
async exportDataConnect(metadata) {
|
|
208
|
+
void (0, track_1.trackEmulator)("emulator_export", {
|
|
209
|
+
initiated_by: this.options.initiatedBy,
|
|
210
|
+
emulator_name: types_1.Emulators.DATACONNECT,
|
|
211
|
+
});
|
|
212
|
+
const instance = registry_1.EmulatorRegistry.get(types_1.Emulators.DATACONNECT);
|
|
213
|
+
if (!instance) {
|
|
214
|
+
throw new error_1.FirebaseError("Unable to export Data Connect emulator data: the Data Connect emulator is not running.");
|
|
215
|
+
}
|
|
216
|
+
const dataconnectExportPath = path.join(this.tmpDir, metadata.dataconnect.path);
|
|
217
|
+
if (fs.existsSync(dataconnectExportPath)) {
|
|
218
|
+
fse.removeSync(dataconnectExportPath);
|
|
219
|
+
}
|
|
220
|
+
fs.mkdirSync(dataconnectExportPath);
|
|
221
|
+
await instance.exportData(dataconnectExportPath);
|
|
222
|
+
}
|
|
200
223
|
}
|
|
201
224
|
exports.HubExport = HubExport;
|
|
202
225
|
HubExport.METADATA_FILE_NAME = "firebase-export-metadata.json";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AdditionalInitFns = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const prompt_1 = require("../prompt");
|
|
6
|
+
const developmentServer_1 = require("./apphosting/developmentServer");
|
|
7
|
+
const emulatorLogger_1 = require("./emulatorLogger");
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
const config_1 = require("../apphosting/config");
|
|
10
|
+
const detectProjectRoot_1 = require("../detectProjectRoot");
|
|
11
|
+
exports.AdditionalInitFns = {
|
|
12
|
+
[types_1.Emulators.APPHOSTING]: async () => {
|
|
13
|
+
var _a;
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const additionalConfigs = new Map();
|
|
16
|
+
const logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.APPHOSTING);
|
|
17
|
+
logger.logLabeled("INFO", "Initializing Emulator");
|
|
18
|
+
const backendRelativeDir = await (0, prompt_1.promptOnce)({
|
|
19
|
+
name: "rootDir",
|
|
20
|
+
type: "input",
|
|
21
|
+
default: "./",
|
|
22
|
+
message: "Specify your app's root directory relative to your repository",
|
|
23
|
+
});
|
|
24
|
+
additionalConfigs.set("rootDirectory", backendRelativeDir);
|
|
25
|
+
const backendRoot = (0, path_1.join)(cwd, backendRelativeDir);
|
|
26
|
+
try {
|
|
27
|
+
const startCommand = await (0, developmentServer_1.detectStartCommand)(backendRoot);
|
|
28
|
+
additionalConfigs.set("startCommandOverride", startCommand);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
logger.log("WARN", "Failed to auto-detect your project's start command. Consider manually setting the start command by setting `firebase.json#emulators.apphosting.startCommandOverride`");
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const projectRoot = (_a = (0, detectProjectRoot_1.detectProjectRoot)({})) !== null && _a !== void 0 ? _a : backendRoot;
|
|
35
|
+
await (0, config_1.exportConfig)(cwd, projectRoot, backendRoot);
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
logger.log("WARN", "failed to export app hosting configs");
|
|
39
|
+
}
|
|
40
|
+
return mapToObject(additionalConfigs);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
function mapToObject(map) {
|
|
44
|
+
const newObject = {};
|
|
45
|
+
for (const [key, value] of map) {
|
|
46
|
+
newObject[key] = value;
|
|
47
|
+
}
|
|
48
|
+
return newObject;
|
|
49
|
+
}
|
package/lib/emulator/types.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Severity = exports.EmulatorLog = exports.FunctionsExecutionMode = exports.isEmulator = exports.isDownloadableEmulator = exports.ALL_EMULATORS = exports.EMULATORS_SUPPORTED_BY_USE_EMULATOR = exports.EMULATORS_SUPPORTED_BY_UI = exports.EMULATORS_SUPPORTED_BY_FUNCTIONS = exports.ALL_SERVICE_EMULATORS = exports.IMPORT_EXPORT_EMULATORS = exports.DOWNLOADABLE_EMULATORS = exports.Emulators = void 0;
|
|
4
|
-
const experiments = require("../experiments");
|
|
5
4
|
var Emulators;
|
|
6
5
|
(function (Emulators) {
|
|
7
6
|
Emulators["AUTH"] = "auth";
|
|
@@ -33,9 +32,10 @@ exports.IMPORT_EXPORT_EMULATORS = [
|
|
|
33
32
|
Emulators.DATABASE,
|
|
34
33
|
Emulators.AUTH,
|
|
35
34
|
Emulators.STORAGE,
|
|
35
|
+
Emulators.DATACONNECT,
|
|
36
36
|
];
|
|
37
37
|
exports.ALL_SERVICE_EMULATORS = [
|
|
38
|
-
|
|
38
|
+
Emulators.APPHOSTING,
|
|
39
39
|
Emulators.AUTH,
|
|
40
40
|
Emulators.FUNCTIONS,
|
|
41
41
|
Emulators.FIRESTORE,
|
package/lib/emulator/ui.js
CHANGED
|
@@ -1,52 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EmulatorUI = void 0;
|
|
4
|
+
const express = require("express");
|
|
5
|
+
const path = require("path");
|
|
4
6
|
const types_1 = require("./types");
|
|
5
7
|
const downloadableEmulators = require("./downloadableEmulators");
|
|
6
8
|
const registry_1 = require("./registry");
|
|
7
9
|
const error_1 = require("../error");
|
|
10
|
+
const emulatorLogger_1 = require("./emulatorLogger");
|
|
8
11
|
const constants_1 = require("./constants");
|
|
9
12
|
const track_1 = require("../track");
|
|
10
13
|
const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
|
|
11
14
|
const experiments_1 = require("../experiments");
|
|
12
|
-
class EmulatorUI {
|
|
15
|
+
class EmulatorUI extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
13
16
|
constructor(args) {
|
|
17
|
+
super({
|
|
18
|
+
listen: args.listen,
|
|
19
|
+
});
|
|
14
20
|
this.args = args;
|
|
15
21
|
}
|
|
16
|
-
start() {
|
|
22
|
+
async start() {
|
|
23
|
+
await super.start();
|
|
24
|
+
}
|
|
25
|
+
async createExpressApp() {
|
|
17
26
|
if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.HUB)) {
|
|
18
27
|
throw new error_1.FirebaseError(`Cannot start ${constants_1.Constants.description(types_1.Emulators.UI)} without ${constants_1.Constants.description(types_1.Emulators.HUB)}!`);
|
|
19
28
|
}
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
GCLOUD_PROJECT: projectId,
|
|
24
|
-
[constants_1.Constants.FIREBASE_EMULATOR_HUB]: registry_1.EmulatorRegistry.url(types_1.Emulators.HUB).host,
|
|
25
|
-
};
|
|
26
|
-
const session = (0, track_1.emulatorSession)();
|
|
27
|
-
if (session) {
|
|
28
|
-
env[constants_1.Constants.FIREBASE_GA_SESSION] = JSON.stringify(session);
|
|
29
|
-
}
|
|
29
|
+
const hub = registry_1.EmulatorRegistry.get(types_1.Emulators.HUB);
|
|
30
|
+
const app = await super.createExpressApp();
|
|
31
|
+
const { projectId } = this.args;
|
|
30
32
|
const enabledExperiments = Object.keys(experiments_1.ALL_EXPERIMENTS).filter((experimentName) => (0, experiments_1.isEnabled)(experimentName));
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const emulatorGaSession = (0, track_1.emulatorSession)();
|
|
34
|
+
await downloadableEmulators.downloadIfNecessary(types_1.Emulators.UI);
|
|
35
|
+
const downloadDetails = downloadableEmulators.getDownloadDetails(types_1.Emulators.UI);
|
|
36
|
+
const webDir = path.join(downloadDetails.unzipDir, "client");
|
|
37
|
+
app.get("/api/config", this.jsonHandler(() => {
|
|
38
|
+
const json = Object.assign({ projectId, experiments: [] }, hub.getRunningEmulatorsMapping());
|
|
39
|
+
if (emulatorGaSession) {
|
|
40
|
+
json.analytics = emulatorGaSession;
|
|
41
|
+
}
|
|
42
|
+
if (enabledExperiments) {
|
|
43
|
+
json.experiments = enabledExperiments;
|
|
44
|
+
}
|
|
45
|
+
return Promise.resolve(json);
|
|
46
|
+
}));
|
|
47
|
+
app.use(express.static(webDir));
|
|
48
|
+
app.get("*", (_, res) => {
|
|
49
|
+
res.sendFile(path.join(webDir, "index.html"));
|
|
50
|
+
});
|
|
51
|
+
return app;
|
|
33
52
|
}
|
|
34
53
|
connect() {
|
|
35
54
|
return Promise.resolve();
|
|
36
55
|
}
|
|
37
|
-
stop() {
|
|
38
|
-
return downloadableEmulators.stop(types_1.Emulators.UI);
|
|
39
|
-
}
|
|
40
|
-
getInfo() {
|
|
41
|
-
return {
|
|
42
|
-
name: this.getName(),
|
|
43
|
-
host: this.args.listen[0].address,
|
|
44
|
-
port: this.args.listen[0].port,
|
|
45
|
-
pid: downloadableEmulators.getPID(types_1.Emulators.UI),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
56
|
getName() {
|
|
49
57
|
return types_1.Emulators.UI;
|
|
50
58
|
}
|
|
59
|
+
jsonHandler(handler) {
|
|
60
|
+
return (req, res) => {
|
|
61
|
+
handler(req).then((body) => {
|
|
62
|
+
res.status(200).json(body);
|
|
63
|
+
}, (err) => {
|
|
64
|
+
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.UI).log("ERROR", err);
|
|
65
|
+
res.status(500).json({
|
|
66
|
+
message: err.message,
|
|
67
|
+
stack: err.stack,
|
|
68
|
+
raw: err,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
51
73
|
}
|
|
52
74
|
exports.EmulatorUI = EmulatorUI;
|
package/lib/error.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isBillingError = exports.getErrStatus = exports.getErrMsg = exports.FirebaseError = void 0;
|
|
3
|
+
exports.isBillingError = exports.getError = exports.getErrStatus = exports.getErrMsg = exports.FirebaseError = void 0;
|
|
4
4
|
const lodash_1 = require("lodash");
|
|
5
5
|
const DEFAULT_CHILDREN = [];
|
|
6
6
|
const DEFAULT_EXIT = 1;
|
|
@@ -41,6 +41,13 @@ function getErrStatus(err, defaultStatus) {
|
|
|
41
41
|
return defaultStatus || DEFAULT_STATUS;
|
|
42
42
|
}
|
|
43
43
|
exports.getErrStatus = getErrStatus;
|
|
44
|
+
function getError(err) {
|
|
45
|
+
if (err instanceof Error) {
|
|
46
|
+
return err;
|
|
47
|
+
}
|
|
48
|
+
return Error(getErrMsg(err));
|
|
49
|
+
}
|
|
50
|
+
exports.getError = getError;
|
|
44
51
|
function isBillingError(e) {
|
|
45
52
|
var _a, _b, _c, _d;
|
|
46
53
|
return !!((_d = (_c = (_b = (_a = e.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.details) === null || _d === void 0 ? void 0 : _d.find((d) => {
|
|
@@ -381,6 +381,7 @@ function endpointFromFunction(gcfFunction) {
|
|
|
381
381
|
endpoint.runServiceId = utils.last(serviceName.split("/"));
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
|
+
proto.renameIfPresent(endpoint, gcfFunction, "uri", "url");
|
|
384
385
|
endpoint.codebase = ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[constants_1.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
|
|
385
386
|
if ((_f = gcfFunction.labels) === null || _f === void 0 ? void 0 : _f[constants_1.HASH_LABEL]) {
|
|
386
387
|
endpoint.hash = gcfFunction.labels[constants_1.HASH_LABEL];
|
package/lib/getProjectNumber.js
CHANGED
|
@@ -8,7 +8,7 @@ async function getProjectNumber(options) {
|
|
|
8
8
|
return options.projectNumber;
|
|
9
9
|
}
|
|
10
10
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
11
|
-
const metadata = await (0, projects_1.
|
|
11
|
+
const metadata = await (0, projects_1.getProject)(projectId);
|
|
12
12
|
options.projectNumber = metadata.projectNumber;
|
|
13
13
|
return options.projectNumber;
|
|
14
14
|
}
|
|
@@ -7,6 +7,7 @@ const prompt_1 = require("../../prompt");
|
|
|
7
7
|
const types_1 = require("../../emulator/types");
|
|
8
8
|
const constants_1 = require("../../emulator/constants");
|
|
9
9
|
const downloadableEmulators_1 = require("../../emulator/downloadableEmulators");
|
|
10
|
+
const initEmulators_1 = require("../../emulator/initEmulators");
|
|
10
11
|
async function doSetup(setup, config) {
|
|
11
12
|
var _a, _b, _c;
|
|
12
13
|
const choices = types_1.ALL_SERVICE_EMULATORS.map((e) => {
|
|
@@ -46,6 +47,13 @@ async function doSetup(setup, config) {
|
|
|
46
47
|
},
|
|
47
48
|
]);
|
|
48
49
|
}
|
|
50
|
+
const additionalInitFn = initEmulators_1.AdditionalInitFns[selected];
|
|
51
|
+
if (additionalInitFn) {
|
|
52
|
+
const additionalOptions = await additionalInitFn();
|
|
53
|
+
if (additionalOptions) {
|
|
54
|
+
setup.config.emulators[selected] = Object.assign(Object.assign({}, setup.config.emulators[selected]), additionalOptions);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
58
|
if (selections.emulators.length) {
|
|
51
59
|
const uiDesc = constants_1.Constants.description(types_1.Emulators.UI);
|