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.
Files changed (69) hide show
  1. package/lib/appdistribution/client.js +62 -8
  2. package/lib/appdistribution/distribution.js +1 -1
  3. package/lib/apphosting/backend.js +4 -4
  4. package/lib/apphosting/config.js +86 -9
  5. package/lib/apphosting/secrets/index.js +5 -43
  6. package/lib/apphosting/yaml.js +3 -0
  7. package/lib/archiveDirectory.js +1 -1
  8. package/lib/auth.js +1 -1
  9. package/lib/command.js +9 -1
  10. package/lib/commands/appdistribution-distribute.js +4 -4
  11. package/lib/commands/{appdistribution-group-create.js → appdistribution-groups-create.js} +2 -1
  12. package/lib/commands/{appdistribution-group-delete.js → appdistribution-groups-delete.js} +3 -2
  13. package/lib/commands/appdistribution-groups-list.js +56 -0
  14. package/lib/commands/appdistribution-testers-list.js +54 -0
  15. package/lib/commands/appdistribution-testers-remove.js +1 -1
  16. package/lib/commands/apphosting-backends-delete.js +3 -1
  17. package/lib/commands/apphosting-backends-get.js +1 -1
  18. package/lib/commands/apphosting-config-export.js +4 -26
  19. package/lib/commands/database-import.js +4 -2
  20. package/lib/commands/database-push.js +4 -2
  21. package/lib/commands/database-set.js +4 -2
  22. package/lib/commands/database-settings-get.js +1 -1
  23. package/lib/commands/database-settings-set.js +1 -1
  24. package/lib/commands/ext-dev-init.js +2 -2
  25. package/lib/commands/ext-dev-list.js +1 -1
  26. package/lib/commands/ext-dev-register.js +2 -2
  27. package/lib/commands/ext-dev-upload.js +2 -2
  28. package/lib/commands/ext-dev-usage.js +2 -2
  29. package/lib/commands/ext-install.js +2 -2
  30. package/lib/commands/index.js +7 -6
  31. package/lib/commands/use.js +1 -1
  32. package/lib/deploy/extensions/deploy.js +3 -1
  33. package/lib/deploy/extensions/deploymentSummary.js +4 -1
  34. package/lib/deploy/extensions/planner.js +14 -3
  35. package/lib/deploy/extensions/prepare.js +9 -9
  36. package/lib/deploy/functions/ensure.js +1 -1
  37. package/lib/deploy/functions/release/fabricator.js +3 -3
  38. package/lib/deploy/lifecycleHooks.js +2 -1
  39. package/lib/emulator/apphosting/config.js +13 -3
  40. package/lib/emulator/apphosting/developmentServer.js +32 -0
  41. package/lib/emulator/apphosting/index.js +5 -3
  42. package/lib/emulator/apphosting/serve.js +15 -12
  43. package/lib/emulator/commandUtils.js +2 -1
  44. package/lib/emulator/controller.js +27 -18
  45. package/lib/emulator/dataconnect/pgliteServer.js +51 -18
  46. package/lib/emulator/dataconnectEmulator.js +26 -2
  47. package/lib/emulator/downloadableEmulators.js +11 -11
  48. package/lib/emulator/hub.js +26 -7
  49. package/lib/emulator/hubExport.js +23 -0
  50. package/lib/emulator/initEmulators.js +49 -0
  51. package/lib/emulator/types.js +2 -2
  52. package/lib/emulator/ui.js +47 -25
  53. package/lib/error.js +8 -1
  54. package/lib/gcp/cloudfunctionsv2.js +1 -0
  55. package/lib/getProjectNumber.js +1 -1
  56. package/lib/init/features/emulators.js +8 -0
  57. package/lib/init/features/project.js +7 -6
  58. package/lib/logger.js +2 -2
  59. package/lib/management/projects.js +24 -4
  60. package/lib/projectUtils.js +1 -1
  61. package/lib/requireDatabaseInstance.js +1 -1
  62. package/lib/requirePermissions.js +1 -1
  63. package/lib/rulesDeploy.js +1 -1
  64. package/lib/templates.js +2 -2
  65. package/lib/utils.js +20 -8
  66. package/lib/vsCodeUtils.js +8 -0
  67. package/package.json +2 -2
  68. package/schema/firebase-config.json +6 -0
  69. 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 configDir = config[0].source;
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
- if (experiments.isEnabled("emulatorapphosting")) {
605
- const apphostingConfig = (_l = options.config.src.emulators) === null || _l === void 0 ? void 0 : _l[types_1.Emulators.APPHOSTING];
606
- if (listenForEmulator.apphosting) {
607
- const apphostingAddr = legacyGetFirstAddr(types_1.Emulators.APPHOSTING);
608
- const apphostingEmulator = new apphosting_1.AppHostingEmulator({
609
- host: apphostingAddr.host,
610
- port: apphostingAddr.port,
611
- startCommandOverride: apphostingConfig === null || apphostingConfig === void 0 ? void 0 : apphostingConfig.startCommandOverride,
612
- options,
613
- });
614
- await startEmulator(apphostingEmulator);
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 db = await this.getDb();
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
- return this.db;
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
- const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
67
- const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
68
- return pglite_1.PGlite.create({
69
- username: this.username,
70
- database: this.database,
71
- debug: 0,
72
- extensions: {
73
- vector,
74
- uuidOssp,
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 pgServer = new pgliteServer_1.PostgresServer(dbId, "postgres");
67
- const server = await pgServer.createPGServer(pgHost, pgPort);
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.0",
52
- expectedSize: 25350912,
53
- expectedChecksum: "1479411a52b689a990179c7f674362b6",
51
+ version: "1.7.3",
52
+ expectedSize: 25211648,
53
+ expectedChecksum: "8410794304b2ae340c3facf07d7edc16",
54
54
  }
55
55
  : process.platform === "win32"
56
56
  ? {
57
- version: "1.7.0",
58
- expectedSize: 25783808,
59
- expectedChecksum: "fef62bf25e816aa4ba250e80050f0125",
57
+ version: "1.7.3",
58
+ expectedSize: 25641984,
59
+ expectedChecksum: "a4bd0f9d9d884528fa4494e4d7918c08",
60
60
  }
61
61
  : {
62
- version: "1.7.0",
63
- expectedSize: 25272472,
64
- expectedChecksum: "24d873457787546ca6f2470fe9ec3edd",
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: "node",
237
- args: [getExecPath(types_1.Emulators.UI)],
236
+ binary: "",
237
+ args: [],
238
238
  optionalArgs: [],
239
239
  joinArgs: false,
240
240
  shell: false,
@@ -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 utils_1 = require("../utils");
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 (!utils_1.isVSCodeExtension && locator.version !== this.CLI_VERSION) {
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
- const body = {};
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
+ }
@@ -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
- ...(experiments.isEnabled("emulatorapphosting") ? [Emulators.APPHOSTING] : []),
38
+ Emulators.APPHOSTING,
39
39
  Emulators.AUTH,
40
40
  Emulators.FUNCTIONS,
41
41
  Emulators.FIRESTORE,
@@ -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 { auto_download: autoDownload, projectId } = this.args;
21
- const env = {
22
- LISTEN: JSON.stringify(ExpressBasedEmulator_1.ExpressBasedEmulator.listenOptionsFromSpecs(this.args.listen)),
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
- env[constants_1.Constants.FIREBASE_ENABLED_EXPERIMENTS] = JSON.stringify(enabledExperiments);
32
- return downloadableEmulators.start(types_1.Emulators.UI, { auto_download: autoDownload }, env);
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];
@@ -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.getFirebaseProject)(projectId);
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);