firebase-tools 13.10.1 → 13.11.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.
@@ -17,7 +17,7 @@ exports.command = new command_1.Command("dataconnect:services:list")
17
17
  "dataconnect.connectors.list",
18
18
  ])
19
19
  .action(async (options) => {
20
- var _a, _b, _c, _d, _e, _f;
20
+ var _a, _b, _c, _d, _e, _f, _g;
21
21
  const projectId = (0, projectUtils_1.needProjectId)(options);
22
22
  await (0, ensureApis_1.ensureApis)(projectId);
23
23
  const services = await client.listAllServices(projectId);
@@ -34,28 +34,22 @@ exports.command = new command_1.Command("dataconnect:services:list")
34
34
  });
35
35
  const jsonOutput = { services: [] };
36
36
  for (const service of services) {
37
- let schema = {
37
+ const schema = (_a = (await client.getSchema(service.name))) !== null && _a !== void 0 ? _a : {
38
38
  name: "",
39
39
  primaryDatasource: {},
40
40
  source: { files: [] },
41
41
  };
42
- try {
43
- schema = await client.getSchema(service.name);
44
- }
45
- catch (err) {
46
- logger_1.logger.debug(`Error fetching schema: ${err}`);
47
- }
48
42
  const connectors = await client.listConnectors(service.name);
49
43
  const serviceName = names.parseServiceName(service.name);
50
- const instanceName = (_b = (_a = schema === null || schema === void 0 ? void 0 : schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) !== null && _b !== void 0 ? _b : "";
44
+ const instanceName = (_c = (_b = schema === null || schema === void 0 ? void 0 : schema.primaryDatasource.postgresql) === null || _b === void 0 ? void 0 : _b.cloudSql.instance) !== null && _c !== void 0 ? _c : "";
51
45
  const instanceId = instanceName.split("/").pop();
52
- const dbId = (_d = (_c = schema === null || schema === void 0 ? void 0 : schema.primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
46
+ const dbId = (_e = (_d = schema === null || schema === void 0 ? void 0 : schema.primaryDatasource.postgresql) === null || _d === void 0 ? void 0 : _d.database) !== null && _e !== void 0 ? _e : "";
53
47
  const dbName = `CloudSQL Instance: ${instanceId}\nDatabase:${dbId}`;
54
48
  table.push([
55
49
  serviceName.serviceId,
56
50
  serviceName.location,
57
51
  dbName,
58
- (_e = schema === null || schema === void 0 ? void 0 : schema.updateTime) !== null && _e !== void 0 ? _e : "",
52
+ (_f = schema === null || schema === void 0 ? void 0 : schema.updateTime) !== null && _f !== void 0 ? _f : "",
59
53
  "",
60
54
  "",
61
55
  ]);
@@ -71,7 +65,7 @@ exports.command = new command_1.Command("dataconnect:services:list")
71
65
  table.push(["", "", "", "", connectorName.connectorId, conn.updateTime]);
72
66
  serviceJson.connectors.push({
73
67
  connectorId: connectorName.connectorId,
74
- connectorLastUpdated: (_f = conn.updateTime) !== null && _f !== void 0 ? _f : "",
68
+ connectorLastUpdated: (_g = conn.updateTime) !== null && _g !== void 0 ? _g : "",
75
69
  });
76
70
  }
77
71
  jsonOutput.services.push(serviceJson);
@@ -12,10 +12,11 @@ const rimraf = require("rimraf");
12
12
  const utils = require("../utils");
13
13
  const JAR_CACHE_DIR = process.env.FIREBASE_CRASHLYTICS_BUILDTOOLS_PATH ||
14
14
  path.join(os.homedir(), ".cache", "firebase", "crashlytics", "buildtools");
15
- const JAR_VERSION = "2.9.2";
15
+ const JAR_VERSION = "3.0.0";
16
16
  const JAR_URL = `https://dl.google.com/android/maven2/com/google/firebase/firebase-crashlytics-buildtools/${JAR_VERSION}/firebase-crashlytics-buildtools-${JAR_VERSION}.jar`;
17
17
  async function fetchBuildtoolsJar() {
18
18
  if (process.env.CRASHLYTICS_LOCAL_JAR) {
19
+ logger_1.logger.debug(`Using local Crashlytics Jar override at ${process.env.CRASHLYTICS_LOCAL_JAR}`);
19
20
  return process.env.CRASHLYTICS_LOCAL_JAR;
20
21
  }
21
22
  const jarPath = path.join(JAR_CACHE_DIR, `crashlytics-buildtools-${JAR_VERSION}.jar`);
@@ -66,8 +66,16 @@ async function deleteService(projectId, locationId, serviceId) {
66
66
  }
67
67
  exports.deleteService = deleteService;
68
68
  async function getSchema(serviceName) {
69
- const res = await dataconnectClient().get(`${serviceName}/schemas/${types.SCHEMA_ID}`);
70
- return res.body;
69
+ try {
70
+ const res = await dataconnectClient().get(`${serviceName}/schemas/${types.SCHEMA_ID}`);
71
+ return res.body;
72
+ }
73
+ catch (err) {
74
+ if (err.status !== 404) {
75
+ throw err;
76
+ }
77
+ return undefined;
78
+ }
71
79
  }
72
80
  exports.getSchema = getSchema;
73
81
  async function upsertSchema(schema, validateOnly = false) {
@@ -2,11 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.prettify = void 0;
4
4
  function prettify(err) {
5
- var _a;
5
+ var _a, _b, _c, _d;
6
6
  const message = err.message;
7
- let header = (_a = err.extensions.file) !== null && _a !== void 0 ? _a : "";
8
- if (err.locations) {
9
- header += `:${err.locations[0].line}`;
7
+ let header = (_b = (_a = err.extensions) === null || _a === void 0 ? void 0 : _a.file) !== null && _b !== void 0 ? _b : "";
8
+ if (err.locations && err.locations.length) {
9
+ const line = (_d = (_c = err.locations[0]) === null || _c === void 0 ? void 0 : _c.line) !== null && _d !== void 0 ? _d : "";
10
+ if (line) {
11
+ header += `:${line}`;
12
+ }
10
13
  }
11
14
  return header.length ? `${header}: ${message}` : message;
12
15
  }
@@ -205,14 +205,8 @@ function displayInvalidConnectors(invalidConnectors) {
205
205
  (0, utils_1.logLabeledWarning)("dataconnect", `This is a ${clc.red("breaking")} change and may break existing apps.`);
206
206
  }
207
207
  async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, databaseId, linkIfNotConnected) {
208
- let currentSchema;
209
- try {
210
- currentSchema = await (0, client_1.getSchema)(serviceName);
211
- }
212
- catch (err) {
213
- if (err.status !== 404) {
214
- throw err;
215
- }
208
+ let currentSchema = await (0, client_1.getSchema)(serviceName);
209
+ if (!currentSchema) {
216
210
  if (!linkIfNotConnected) {
217
211
  (0, utils_1.logLabeledWarning)("dataconnect", `Not yet linked to the Cloud SQL instance.`);
218
212
  return;
@@ -290,7 +290,7 @@ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, fil
290
290
  const firebaseJsonRuntime = codebaseConfig.runtime;
291
291
  if (firebaseJsonRuntime && !supported.isRuntime(firebaseJsonRuntime)) {
292
292
  throw new error_1.FirebaseError(`Functions codebase ${codebase} has invalid runtime ` +
293
- `${firebaseJsonRuntime} specified in firebase.json. Valid values are: ` +
293
+ `${firebaseJsonRuntime} specified in firebase.json. Valid values are: \n` +
294
294
  Object.keys(supported.RUNTIMES)
295
295
  .map((s) => `- ${s}`)
296
296
  .join("\n"));
@@ -53,6 +53,12 @@ exports.RUNTIMES = runtimes({
53
53
  deprecationDate: "2026-04-30",
54
54
  decommissionDate: "2026-10-31",
55
55
  },
56
+ nodejs22: {
57
+ friendly: "Node.js 22",
58
+ status: "beta",
59
+ deprecationDate: "2027-04-30",
60
+ decommissionDate: "2027-10-31",
61
+ },
56
62
  python310: {
57
63
  friendly: "Python 3.10",
58
64
  status: "GA",
@@ -539,6 +539,9 @@ async function startAll(options, showUI = true, runningTestScript = false) {
539
539
  rc: options.rc,
540
540
  });
541
541
  await startEmulator(dataConnectEmulator);
542
+ if (!utils.isVSCodeExtension()) {
543
+ dataConnectEmulator.connectToPostgres();
544
+ }
542
545
  }
543
546
  if (listenForEmulator.storage) {
544
547
  const storageAddr = legacyGetFirstAddr(types_1.Emulators.STORAGE);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DataConnectEmulator = void 0;
3
+ exports.DataConnectEmulatorClient = exports.DataConnectEmulator = void 0;
4
4
  const childProcess = require("child_process");
5
5
  const api_1 = require("../api");
6
6
  const constants_1 = require("./constants");
@@ -10,23 +10,35 @@ const error_1 = require("../error");
10
10
  const emulatorLogger_1 = require("./emulatorLogger");
11
11
  const types_2 = require("../dataconnect/types");
12
12
  const portUtils_1 = require("./portUtils");
13
+ const registry_1 = require("./registry");
13
14
  class DataConnectEmulator {
14
15
  constructor(args) {
15
16
  this.args = args;
16
17
  this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT);
18
+ this.emulatorClient = new DataConnectEmulatorClient();
17
19
  }
18
20
  async start() {
19
- this.logger.log("DEBUG", `Using Postgres connection string: ${this.getLocalConectionString()}`);
20
- const info = await DataConnectEmulator.build({ configDir: this.args.configDir });
21
- if ((0, types_2.requiresVector)(info.metadata)) {
22
- if (constants_1.Constants.isDemoProject(this.args.projectId)) {
23
- this.logger.logLabeled("WARN", "Data Connect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
24
- }
25
- else {
26
- this.logger.logLabeled("WARN", "Data Connect", "Operations that use vector_embed will make calls to production Vertex AI");
21
+ try {
22
+ const info = await DataConnectEmulator.build({ configDir: this.args.configDir });
23
+ if ((0, types_2.requiresVector)(info.metadata)) {
24
+ if (constants_1.Constants.isDemoProject(this.args.projectId)) {
25
+ this.logger.logLabeled("WARN", "Data Connect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
26
+ }
27
+ else {
28
+ this.logger.logLabeled("WARN", "Data Connect", "Operations that use vector_embed will make calls to production Vertex AI");
29
+ }
27
30
  }
28
31
  }
29
- return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, Object.assign(Object.assign({}, this.args), { listen: (0, portUtils_1.listenSpecsToString)(this.args.listen), config_dir: this.args.configDir, local_connection_string: this.getLocalConectionString(), project_id: this.args.projectId, service_location: this.args.locationId }));
32
+ catch (err) {
33
+ this.logger.log("DEBUG", `'fdc build' failed with error: ${err.message}`);
34
+ }
35
+ return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, {
36
+ auto_download: this.args.auto_download,
37
+ listen: (0, portUtils_1.listenSpecsToString)(this.args.listen),
38
+ config_dir: this.args.configDir,
39
+ project_id: this.args.projectId,
40
+ service_location: this.args.locationId,
41
+ });
30
42
  }
31
43
  connect() {
32
44
  return Promise.resolve();
@@ -73,8 +85,11 @@ class DataConnectEmulator {
73
85
  original: res.error,
74
86
  });
75
87
  }
88
+ if (res.status !== 0) {
89
+ throw new error_1.FirebaseError(`Unable to build your Data Connect schema and connectors (exit code ${res.status}): ${res.stderr}`);
90
+ }
76
91
  if (res.stderr) {
77
- throw new error_1.FirebaseError(`Unable to build your Data Connect schema and connectors: ${res.stderr}`);
92
+ emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).log("DEBUG", res.stderr);
78
93
  }
79
94
  try {
80
95
  return JSON.parse(res.stdout);
@@ -90,5 +105,24 @@ class DataConnectEmulator {
90
105
  }
91
106
  return (_b = (_a = this.args.rc.getDataconnect()) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString;
92
107
  }
108
+ async connectToPostgres(localConnectionString, database, serviceId) {
109
+ const connectionString = localConnectionString !== null && localConnectionString !== void 0 ? localConnectionString : this.getLocalConectionString();
110
+ if (!connectionString) {
111
+ this.logger.log("DEBUG", "No Postgres connection string found, not connecting to Postgres");
112
+ return false;
113
+ }
114
+ await this.emulatorClient.configureEmulator({ connectionString, database, serviceId });
115
+ return true;
116
+ }
93
117
  }
94
118
  exports.DataConnectEmulator = DataConnectEmulator;
119
+ class DataConnectEmulatorClient {
120
+ constructor() {
121
+ this.client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATACONNECT);
122
+ }
123
+ async configureEmulator(body) {
124
+ const res = await this.client.post("emulator/configure", body);
125
+ return res;
126
+ }
127
+ }
128
+ exports.DataConnectEmulatorClient = DataConnectEmulatorClient;
@@ -13,6 +13,10 @@ const downloadUtils = require("../downloadUtils");
13
13
  tmp.setGracefulCleanup();
14
14
  async function downloadEmulator(name) {
15
15
  const emulator = downloadableEmulators.getDownloadDetails(name);
16
+ if (emulator.localOnly) {
17
+ emulatorLogger_1.EmulatorLogger.forEmulator(name).logLabeled("WARN", name, `Env variable override detected, skipping download. Using ${emulator} emulator at ${emulator.binaryPath}`);
18
+ return;
19
+ }
16
20
  emulatorLogger_1.EmulatorLogger.forEmulator(name).logLabeled("BULLET", name, `downloading ${path.basename(emulator.downloadPath)}...`);
17
21
  fs.ensureDirSync(emulator.opts.cacheDir);
18
22
  const tmpfile = await downloadUtils.downloadToTmp(emulator.opts.remoteUrl, !!emulator.opts.auth);
@@ -23,9 +23,9 @@ const EMULATOR_UPDATE_DETAILS = {
23
23
  expectedChecksum: "2fd771101c0e1f7898c04c9204f2ce63",
24
24
  },
25
25
  firestore: {
26
- version: "1.19.6",
27
- expectedSize: 66349770,
28
- expectedChecksum: "2eaabbe3cdb4867df585b7ec5505bad7",
26
+ version: "1.19.7",
27
+ expectedSize: 66438992,
28
+ expectedChecksum: "aec233bea95c5cfab03881574ec16d6c",
29
29
  },
30
30
  storage: {
31
31
  version: "1.1.3",
@@ -40,21 +40,27 @@ const EMULATOR_UPDATE_DETAILS = {
40
40
  expectedChecksum: "a7f4398a00e5ca22abdcd78dc3877d00",
41
41
  },
42
42
  pubsub: {
43
- version: "0.8.2",
44
- expectedSize: 65611398,
45
- expectedChecksum: "70bb840321423e6ae621a3ae2f314903",
43
+ version: "0.8.14",
44
+ expectedSize: 66786933,
45
+ expectedChecksum: "a9025b3e53fdeafd2969ccb3ba1e1d38",
46
46
  },
47
47
  dataconnect: process.platform === "darwin"
48
48
  ? {
49
- version: "1.1.18",
50
- expectedSize: 25836736,
51
- expectedChecksum: "28a760826968c86cff7e2137b7a487cf",
49
+ version: "1.2.0",
50
+ expectedSize: 23954240,
51
+ expectedChecksum: "0f250761959519bb5a28fed76ceab2cb",
52
52
  }
53
- : {
54
- version: "1.1.17",
55
- expectedSize: 23247120,
56
- expectedChecksum: "ff2d10655fd0ad16c0fd07e1ca6dac3e",
57
- },
53
+ : process.platform === "win32"
54
+ ? {
55
+ version: "1.2.0",
56
+ expectedSize: 24360960,
57
+ expectedChecksum: "168ce32c742e1d26037c52bdbb7d871c",
58
+ }
59
+ : {
60
+ version: "1.2.0",
61
+ expectedSize: 23970052,
62
+ expectedChecksum: "2ca17e4009a9ebae0f7c983bafff2ee6",
63
+ },
58
64
  };
59
65
  exports.DownloadDetails = {
60
66
  database: {
@@ -119,14 +125,16 @@ exports.DownloadDetails = {
119
125
  },
120
126
  },
121
127
  dataconnect: {
122
- downloadPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}`),
128
+ downloadPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}${process.platform === "win32" ? ".exe" : ""}`),
123
129
  version: EMULATOR_UPDATE_DETAILS.dataconnect.version,
124
- binaryPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}`),
130
+ binaryPath: path.join(CACHE_DIR, `dataconnect-emulator-${EMULATOR_UPDATE_DETAILS.dataconnect.version}${process.platform === "win32" ? ".exe" : ""}`),
125
131
  opts: {
126
132
  cacheDir: CACHE_DIR,
127
133
  remoteUrl: process.platform === "darwin"
128
134
  ? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`
129
- : `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`,
135
+ : process.platform === "win32"
136
+ ? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`
137
+ : `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v${EMULATOR_UPDATE_DETAILS.dataconnect.version}`,
130
138
  expectedSize: EMULATOR_UPDATE_DETAILS.dataconnect.expectedSize,
131
139
  expectedChecksum: EMULATOR_UPDATE_DETAILS.dataconnect.expectedChecksum,
132
140
  skipChecksumAndSize: false,
@@ -235,12 +243,15 @@ const Commands = {
235
243
  optionalArgs: [
236
244
  "listen",
237
245
  "config_dir",
238
- "local_connection_string",
239
246
  "project_id",
240
247
  "service_location",
248
+ "disable_sdk_generation",
249
+ "resolvers_emulator",
250
+ "vertex_location",
251
+ "rpc_retry_count",
241
252
  ],
242
253
  joinArgs: true,
243
- shell: true,
254
+ shell: false,
244
255
  },
245
256
  };
246
257
  function getExecPath(name) {
@@ -372,7 +383,17 @@ async function _runBinary(emulator, command, extraEnv) {
372
383
  });
373
384
  }
374
385
  function getDownloadDetails(emulator) {
375
- return exports.DownloadDetails[emulator];
386
+ const details = exports.DownloadDetails[emulator];
387
+ const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`];
388
+ if (pathOverride) {
389
+ const logger = emulatorLogger_1.EmulatorLogger.forEmulator(emulator);
390
+ logger.logLabeled("WARN", emulator, `Env variable override detected. Using ${emulator} emulator at ${pathOverride}`);
391
+ details.downloadPath = pathOverride;
392
+ details.binaryPath = pathOverride;
393
+ details.localOnly = true;
394
+ fs.chmodSync(pathOverride, 0o755);
395
+ }
396
+ return details;
376
397
  }
377
398
  exports.getDownloadDetails = getDownloadDetails;
378
399
  function get(emulator) {
@@ -416,7 +437,7 @@ async function downloadIfNecessary(targetName) {
416
437
  }
417
438
  exports.downloadIfNecessary = downloadIfNecessary;
418
439
  async function start(targetName, args, extraEnv = {}) {
419
- const downloadDetails = exports.DownloadDetails[targetName];
440
+ const downloadDetails = getDownloadDetails(targetName);
420
441
  const emulator = get(targetName);
421
442
  const hasEmulator = fs.existsSync(getExecPath(targetName));
422
443
  const logger = emulatorLogger_1.EmulatorLogger.forEmulator(targetName);
@@ -74,7 +74,7 @@ class StorageRulesRuntime {
74
74
  if (this.alive) {
75
75
  return;
76
76
  }
77
- const downloadDetails = downloadableEmulators_1.DownloadDetails[types_2.Emulators.STORAGE];
77
+ const downloadDetails = (0, downloadableEmulators_1.getDownloadDetails)(types_2.Emulators.STORAGE);
78
78
  const hasEmulator = fs.existsSync(downloadDetails.downloadPath);
79
79
  if (!hasEmulator) {
80
80
  if (autoDownload) {
@@ -310,6 +310,8 @@ async function getContext(dir, targetOrConfiguration) {
310
310
  continue;
311
311
  if (target === buildTarget && builder === "@angular-devkit/build-angular:application")
312
312
  continue;
313
+ if (target === buildTarget && builder === "@angular-devkit/build-angular:browser")
314
+ continue;
313
315
  if (target === browserTarget && builder === "@angular-devkit/build-angular:browser-esbuild")
314
316
  continue;
315
317
  if (target === browserTarget && builder === "@angular-devkit/build-angular:browser")
@@ -364,10 +366,13 @@ async function getBrowserConfig(sourceDir, configuration) {
364
366
  if (!buildOrBrowserTarget) {
365
367
  throw new assert_1.AssertionError({ message: "expected build or browser target defined" });
366
368
  }
367
- const { locales, defaultLocale } = await localesForTarget(sourceDir, architectHost, buildOrBrowserTarget, workspaceProject);
368
- const targetOptions = await architectHost.getOptionsForTarget(buildOrBrowserTarget);
369
+ const [{ locales, defaultLocale }, targetOptions, builderName] = await Promise.all([
370
+ localesForTarget(sourceDir, architectHost, buildOrBrowserTarget, workspaceProject),
371
+ architectHost.getOptionsForTarget(buildOrBrowserTarget),
372
+ architectHost.getBuilderNameForTarget(buildOrBrowserTarget),
373
+ ]);
369
374
  (0, utils_2.assertIsString)(targetOptions === null || targetOptions === void 0 ? void 0 : targetOptions.outputPath);
370
- const outputPath = (0, path_1.join)(targetOptions.outputPath, buildTarget ? "browser" : "");
375
+ const outputPath = (0, path_1.join)(targetOptions.outputPath, buildTarget && builderName === "@angular-devkit/build-angular:application" ? "browser" : "");
371
376
  return { locales, baseHref, outputPath, defaultLocale };
372
377
  }
373
378
  exports.getBrowserConfig = getBrowserConfig;
@@ -10,6 +10,7 @@ const cloudsql = require("../../../gcp/cloudsql/cloudsqladmin");
10
10
  const ensureApis_1 = require("../../../dataconnect/ensureApis");
11
11
  const client_1 = require("../../../dataconnect/client");
12
12
  const emulators_1 = require("../emulators");
13
+ const names_1 = require("../../../dataconnect/names");
13
14
  const TEMPLATE_ROOT = (0, path_1.resolve)(__dirname, "../../../../templates/init/dataconnect/");
14
15
  const DATACONNECT_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "dataconnect.yaml"), "utf8");
15
16
  const CONNECTOR_YAML_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "connector.yaml"), "utf8");
@@ -18,42 +19,164 @@ const QUERIES_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT,
18
19
  const MUTATIONS_TEMPLATE = (0, fs_1.readFileSync)((0, path_1.join)(TEMPLATE_ROOT, "mutations.gql"), "utf8");
19
20
  async function doSetup(setup, config) {
20
21
  var _a, _b, _c;
21
- if (setup.projectId) {
22
- await (0, ensureApis_1.ensureApis)(setup.projectId);
22
+ let info = {
23
+ serviceId: "",
24
+ locationId: "",
25
+ cloudSqlInstanceId: "",
26
+ isNewInstance: false,
27
+ cloudSqlDatabase: "",
28
+ isNewDatabase: false,
29
+ connectorId: "default-connector",
30
+ };
31
+ info = await promptForService(setup, info);
32
+ if (info.cloudSqlInstanceId === "") {
33
+ info = await promptForCloudSQLInstance(setup, info);
23
34
  }
24
- const serviceId = await (0, prompt_1.promptOnce)({
25
- message: "What ID would you like to use for this service?",
26
- type: "input",
27
- default: "dataconnect",
28
- });
29
- const connectorId = await (0, prompt_1.promptOnce)({
30
- message: "What ID would you like to use for your connector?",
35
+ if (info.cloudSqlDatabase === "") {
36
+ info = await promptForDatabase(setup, config, info);
37
+ }
38
+ const defaultConnectionString = (_c = (_b = (_a = setup.rcfile.dataconnectEmulatorConfig) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString) !== null && _c !== void 0 ? _c : emulators_1.DEFAULT_POSTGRES_CONNECTION;
39
+ const localConnectionString = await (0, prompt_1.promptOnce)({
31
40
  type: "input",
32
- default: "my-connector",
41
+ name: "localConnectionString",
42
+ message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
43
+ default: defaultConnectionString,
33
44
  });
34
- let cloudSqlInstanceId = "";
35
- let newInstance = false;
36
- let locationId = "";
45
+ setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
46
+ const dir = config.get("dataconnect.source") || "dataconnect";
47
+ const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, info);
48
+ const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, info);
49
+ await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
50
+ await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
51
+ await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "connector.yaml"), subbedConnectorYaml);
52
+ await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "queries.gql"), QUERIES_TEMPLATE);
53
+ await config.askWriteProjectFile((0, path_1.join)(dir, info.connectorId, "mutations.gql"), MUTATIONS_TEMPLATE);
54
+ if (setup.projectId &&
55
+ (info.isNewInstance || info.isNewDatabase) &&
56
+ (await (0, prompt_1.confirm)({
57
+ message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
58
+ default: true,
59
+ }))) {
60
+ await (0, provisionCloudSql_1.provisionCloudSql)({
61
+ projectId: setup.projectId,
62
+ locationId: info.locationId,
63
+ instanceId: info.cloudSqlInstanceId,
64
+ databaseId: info.cloudSqlDatabase,
65
+ enableGoogleMlIntegration: false,
66
+ });
67
+ }
68
+ }
69
+ exports.doSetup = doSetup;
70
+ function subValues(template, replacementValues) {
71
+ const replacements = {
72
+ serviceId: "__serviceId__",
73
+ cloudSqlDatabase: "__cloudSqlDatabase__",
74
+ cloudSqlInstanceId: "__cloudSqlInstanceId__",
75
+ connectorId: "__connectorId__",
76
+ };
77
+ let replaced = template;
78
+ for (const [k, v] of Object.entries(replacementValues)) {
79
+ replaced = replaced.replace(replacements[k], v);
80
+ }
81
+ return replaced;
82
+ }
83
+ async function promptForService(setup, info) {
84
+ var _a, _b, _c, _d;
85
+ if (setup.projectId) {
86
+ await (0, ensureApis_1.ensureApis)(setup.projectId);
87
+ const existingServices = await (0, client_1.listAllServices)(setup.projectId);
88
+ const existingServicesAndSchemas = await Promise.all(existingServices.map(async (s) => {
89
+ return {
90
+ service: s,
91
+ schema: await (0, client_1.getSchema)(s.name),
92
+ };
93
+ }));
94
+ const existingFreshServicesAndSchemas = existingServicesAndSchemas.filter((s) => {
95
+ var _a, _b;
96
+ return !((_b = (_a = s.schema) === null || _a === void 0 ? void 0 : _a.source.files) === null || _b === void 0 ? void 0 : _b.length);
97
+ });
98
+ if (existingFreshServicesAndSchemas.length) {
99
+ const choices = existingFreshServicesAndSchemas.map((s) => {
100
+ const serviceName = (0, names_1.parseServiceName)(s.service.name);
101
+ return {
102
+ name: `${serviceName.location}/${serviceName.serviceId}`,
103
+ value: s,
104
+ };
105
+ });
106
+ choices.push({ name: "Create a new service", value: undefined });
107
+ const choice = await (0, prompt_1.promptOnce)({
108
+ message: "Your project already has existing services. Which would you like to set up local files for?",
109
+ type: "list",
110
+ choices,
111
+ });
112
+ if (choice) {
113
+ const serviceName = (0, names_1.parseServiceName)(choice.service.name);
114
+ info.serviceId = serviceName.serviceId;
115
+ info.locationId = serviceName.location;
116
+ if (choice.schema) {
117
+ info.cloudSqlInstanceId =
118
+ (_b = (_a = choice.schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.cloudSql.instance) !== null && _b !== void 0 ? _b : "";
119
+ info.cloudSqlDatabase = (_d = (_c = choice.schema.primaryDatasource.postgresql) === null || _c === void 0 ? void 0 : _c.database) !== null && _d !== void 0 ? _d : "";
120
+ }
121
+ }
122
+ }
123
+ }
124
+ if (info.serviceId === "") {
125
+ info.serviceId = await (0, prompt_1.promptOnce)({
126
+ message: "What ID would you like to use for this service?",
127
+ type: "input",
128
+ default: "my-service",
129
+ });
130
+ }
131
+ return info;
132
+ }
133
+ async function promptForCloudSQLInstance(setup, info) {
37
134
  if (setup.projectId) {
38
135
  const instances = await cloudsql.listInstances(setup.projectId);
39
- const choices = instances.map((i) => {
136
+ let choices = instances.map((i) => {
40
137
  return { name: i.name, value: i.name, location: i.region };
41
138
  });
42
- const freeTrialInstanceId = await (0, freeTrial_1.checkForFreeTrialInstance)(setup.projectId);
43
- if (!freeTrialInstanceId) {
44
- choices.push({ name: "Create a new instance", value: "", location: "" });
45
- }
46
- if (instances.length) {
47
- cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
139
+ choices = choices.filter((c) => info.locationId === "" || info.locationId === c.location);
140
+ if (choices.length) {
141
+ const freeTrialInstanceId = await (0, freeTrial_1.checkForFreeTrialInstance)(setup.projectId);
142
+ if (!freeTrialInstanceId) {
143
+ choices.push({ name: "Create a new instance", value: "", location: "" });
144
+ }
145
+ info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
48
146
  message: `Which CloudSQL instance would you like to use?`,
49
147
  type: "list",
50
148
  choices,
51
149
  });
150
+ info.locationId = choices.find((c) => c.value === info.cloudSqlInstanceId).location;
52
151
  }
53
- locationId = choices.find((c) => c.value === cloudSqlInstanceId).location;
54
152
  }
55
- if (cloudSqlInstanceId === "") {
56
- let locationOptions = [
153
+ if (info.cloudSqlInstanceId === "") {
154
+ info.isNewInstance = true;
155
+ info.cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
156
+ message: `What ID would you like to use for your new CloudSQL instance?`,
157
+ type: "input",
158
+ default: `fdc-sql`,
159
+ });
160
+ }
161
+ if (info.locationId === "") {
162
+ const choices = await locationChoices(setup);
163
+ info.locationId = await (0, prompt_1.promptOnce)({
164
+ message: "What location would like to use?",
165
+ type: "list",
166
+ choices,
167
+ });
168
+ }
169
+ return info;
170
+ }
171
+ async function locationChoices(setup) {
172
+ if (setup.projectId) {
173
+ const locations = await (0, client_1.listLocations)(setup.projectId);
174
+ return locations.map((l) => {
175
+ return { name: l, value: l };
176
+ });
177
+ }
178
+ else {
179
+ return [
57
180
  { name: "us-central1", value: "us-central1" },
58
181
  { name: "europe-north1", value: "europe-north1" },
59
182
  { name: "europe-central2", value: "europe-central2" },
@@ -63,104 +186,35 @@ async function doSetup(setup, config) {
63
186
  { name: "us-west1", value: "us-west1" },
64
187
  { name: "asia-southeast1", value: "asia-southeast1" },
65
188
  ];
66
- if (setup.projectId) {
67
- const locations = await (0, client_1.listLocations)(setup.projectId);
68
- locationOptions = locations.map((l) => {
69
- return { name: l, value: l };
70
- });
71
- }
72
- newInstance = true;
73
- cloudSqlInstanceId = await (0, prompt_1.promptOnce)({
74
- message: `What ID would you like to use for your new CloudSQL instance?`,
75
- type: "input",
76
- default: `dataconnect-test`,
77
- });
78
- locationId = await (0, prompt_1.promptOnce)({
79
- message: "What location would you use for this instance?",
80
- type: "list",
81
- choices: locationOptions,
82
- });
83
189
  }
190
+ }
191
+ async function promptForDatabase(setup, config, info) {
84
192
  const dir = config.get("dataconnect.source") || "dataconnect";
85
193
  if (!config.has("dataconnect")) {
86
194
  config.set("dataconnect.source", dir);
87
- config.set("dataconnect.location", locationId);
195
+ config.set("dataconnect.location", info.locationId);
88
196
  }
89
- let cloudSqlDatabase = "";
90
- let newDB = false;
91
- if (!newInstance && setup.projectId) {
92
- const dbs = await cloudsql.listDatabases(setup.projectId, cloudSqlInstanceId);
197
+ if (!info.isNewInstance && setup.projectId) {
198
+ const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
93
199
  const choices = dbs.map((d) => {
94
200
  return { name: d.name, value: d.name };
95
201
  });
96
202
  choices.push({ name: "Create a new database", value: "" });
97
203
  if (dbs.length) {
98
- cloudSqlDatabase = await (0, prompt_1.promptOnce)({
99
- message: `Which database in ${cloudSqlInstanceId} would you like to use?`,
204
+ info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
205
+ message: `Which database in ${info.cloudSqlInstanceId} would you like to use?`,
100
206
  type: "list",
101
207
  choices,
102
208
  });
103
209
  }
104
210
  }
105
- if (cloudSqlDatabase === "") {
106
- newDB = true;
107
- cloudSqlDatabase = await (0, prompt_1.promptOnce)({
108
- message: `What ID would you like to use for your new database in ${cloudSqlInstanceId}?`,
211
+ if (info.cloudSqlDatabase === "") {
212
+ info.isNewDatabase = true;
213
+ info.cloudSqlDatabase = await (0, prompt_1.promptOnce)({
214
+ message: `What ID would you like to use for your new database in ${info.cloudSqlInstanceId}?`,
109
215
  type: "input",
110
- default: `dataconnect`,
111
- });
112
- }
113
- const defaultConnectionString = (_c = (_b = (_a = setup.rcfile.dataconnectEmulatorConfig) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString) !== null && _c !== void 0 ? _c : emulators_1.DEFAULT_POSTGRES_CONNECTION;
114
- const localConnectionString = await (0, prompt_1.promptOnce)({
115
- type: "input",
116
- name: "localConnectionString",
117
- message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
118
- default: defaultConnectionString,
119
- });
120
- setup.rcfile.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
121
- const subbedDataconnectYaml = subValues(DATACONNECT_YAML_TEMPLATE, {
122
- serviceId,
123
- cloudSqlInstanceId,
124
- cloudSqlDatabase,
125
- connectorId,
126
- });
127
- const subbedConnectorYaml = subValues(CONNECTOR_YAML_TEMPLATE, {
128
- serviceId,
129
- cloudSqlInstanceId,
130
- cloudSqlDatabase,
131
- connectorId,
132
- });
133
- await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
134
- await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "connector.yaml"), subbedConnectorYaml);
135
- await config.askWriteProjectFile((0, path_1.join)(dir, "schema", "schema.gql"), SCHEMA_TEMPLATE);
136
- await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "queries.gql"), QUERIES_TEMPLATE);
137
- await config.askWriteProjectFile((0, path_1.join)(dir, "connector", "mutations.gql"), MUTATIONS_TEMPLATE);
138
- if (setup.projectId &&
139
- (newInstance || newDB) &&
140
- (await (0, prompt_1.confirm)({
141
- message: "Would you like to provision your CloudSQL instance and database now? This will take a few minutes.",
142
- default: true,
143
- }))) {
144
- await (0, provisionCloudSql_1.provisionCloudSql)({
145
- projectId: setup.projectId,
146
- locationId,
147
- instanceId: cloudSqlInstanceId,
148
- databaseId: cloudSqlDatabase,
149
- enableGoogleMlIntegration: false,
216
+ default: `fdcdb`,
150
217
  });
151
218
  }
152
- }
153
- exports.doSetup = doSetup;
154
- function subValues(template, replacementValues) {
155
- const replacements = {
156
- serviceId: "__serviceId__",
157
- cloudSqlDatabase: "__cloudSqlDatabase__",
158
- cloudSqlInstanceId: "__cloudSqlInstanceId__",
159
- connectorId: "__connectorId__",
160
- };
161
- let replaced = template;
162
- for (const [k, v] of Object.entries(replacementValues)) {
163
- replaced = replaced.replace(replacements[k], v);
164
- }
165
- return replaced;
219
+ return info;
166
220
  }
package/lib/rc.js CHANGED
@@ -157,9 +157,7 @@ class RC {
157
157
  return (_a = this.data.dataconnectEmulatorConfig) !== null && _a !== void 0 ? _a : {};
158
158
  }
159
159
  setDataconnect(localConnectionString) {
160
- if (!this.data.dataconnectEmulatorConfig) {
161
- this.data.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
162
- }
160
+ this.data.dataconnectEmulatorConfig = { postgres: { localConnectionString } };
163
161
  }
164
162
  save() {
165
163
  if (this.path) {
package/lib/track.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.track = exports.cliSession = exports.emulatorSession = exports.trackEmulator = exports.trackGA4 = exports.usageEnabled = exports.GA4_PROPERTIES = void 0;
3
+ exports.track = exports.cliSession = exports.vscodeSession = exports.emulatorSession = exports.trackVSCode = exports.trackEmulator = exports.trackGA4 = exports.usageEnabled = exports.GA4_PROPERTIES = void 0;
4
4
  const node_fetch_1 = require("node-fetch");
5
5
  const ua = require("universal-analytics");
6
6
  const uuid_1 = require("uuid");
@@ -19,6 +19,11 @@ exports.GA4_PROPERTIES = {
19
19
  apiSecret: process.env.FIREBASE_EMULATOR_GA4_API_SECRET || "2V_zBYc4TdeoppzDaIu0zw",
20
20
  clientIdKey: "emulator-analytics-clientId",
21
21
  },
22
+ vscode: {
23
+ measurementId: process.env.FIREBASE_VSCODE_GA4_MEASUREMENT_ID || "G-FYJ489XM2T",
24
+ apiSecret: process.env.FIREBASE_VSCODE_GA4_API_SECRET || "XAEWKHe7RM-ygCK44N52Ww",
25
+ clientIdKey: "vscode-analytics-clientId",
26
+ },
22
27
  };
23
28
  function usageEnabled() {
24
29
  return !!process.env.IS_FIREBASE_CLI && !!configstore_1.configstore.get("usage");
@@ -69,6 +74,24 @@ async function trackEmulator(eventName, params) {
69
74
  });
70
75
  }
71
76
  exports.trackEmulator = trackEmulator;
77
+ async function trackVSCode(eventName, params) {
78
+ const session = vscodeSession();
79
+ if (!session) {
80
+ return;
81
+ }
82
+ session.debugMode = process.env.VSCODE_DEBUG_MODE === "true";
83
+ const oldTotalEngagementSeconds = session.totalEngagementSeconds;
84
+ session.totalEngagementSeconds = process.uptime();
85
+ const duration = session.totalEngagementSeconds - oldTotalEngagementSeconds;
86
+ return _ga4Track({
87
+ session,
88
+ apiSecret: exports.GA4_PROPERTIES.vscode.apiSecret,
89
+ eventName,
90
+ params,
91
+ duration,
92
+ });
93
+ }
94
+ exports.trackVSCode = trackVSCode;
72
95
  async function _ga4Track(args) {
73
96
  const { session, apiSecret, eventName, params, duration } = args;
74
97
  session.commandName = (params === null || params === void 0 ? void 0 : params.command_name) || session.commandName;
@@ -119,13 +142,17 @@ function emulatorSession() {
119
142
  return session("emulator");
120
143
  }
121
144
  exports.emulatorSession = emulatorSession;
145
+ function vscodeSession() {
146
+ return session("vscode");
147
+ }
148
+ exports.vscodeSession = vscodeSession;
122
149
  function cliSession() {
123
150
  return session("cli");
124
151
  }
125
152
  exports.cliSession = cliSession;
126
153
  function session(propertyName) {
127
154
  const validateOnly = !!process.env.FIREBASE_CLI_MP_VALIDATE;
128
- if (!usageEnabled()) {
155
+ if (!usageEnabled() && propertyName !== "vscode") {
129
156
  if (validateOnly) {
130
157
  logger_1.logger.warn("Google Analytics is DISABLED. To enable, (re)login and opt in to collection.");
131
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.10.1",
3
+ "version": "13.11.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -726,6 +726,7 @@
726
726
  "nodejs16",
727
727
  "nodejs18",
728
728
  "nodejs20",
729
+ "nodejs22",
729
730
  "nodejs6",
730
731
  "nodejs8",
731
732
  "python310",
@@ -787,6 +788,7 @@
787
788
  "nodejs16",
788
789
  "nodejs18",
789
790
  "nodejs20",
791
+ "nodejs22",
790
792
  "nodejs6",
791
793
  "nodejs8",
792
794
  "python310",
@@ -7,4 +7,4 @@ schema:
7
7
  database: "__cloudSqlDatabase__"
8
8
  cloudSql:
9
9
  instanceId: "__cloudSqlInstanceId__"
10
- connectorDirs: ["./connector"]
10
+ connectorDirs: ["./__connectorId__"]
@@ -10,7 +10,7 @@
10
10
  # }
11
11
  # mutation CreateEmail($content: String, $subject: String, $fromUid: String) @auth(level: PUBLIC) {
12
12
  # email_insert(data: {
13
- # text: $content,
13
+ # text: $content, # The request variable name doesn't have to match the field name.
14
14
  # subject: $subject,
15
15
  # fromUid: $fromUid,
16
16
  ## Server values let your service populate data for you
@@ -13,12 +13,12 @@
13
13
  ## where allows you to filter lists
14
14
  ## Here, we use it to filter to only emails where this user is one of the recipients.
15
15
  # emails(where: {
16
- # users_via_Recipient: {
16
+ # users_via_Recipient: {
17
17
  # exist: { uid: { eq: $uid }
18
18
  # }}
19
19
  # }) {
20
- # id subject sent
21
- # content: text
20
+ # id subject sent
21
+ # content: text # Select the `text` field but alias it as `content` in the response.
22
22
  # sender: from { name email: address uid }
23
23
 
24
24
  ## <field>_on_<foreign_key_field> makes it easy to grab info from another table
@@ -38,11 +38,11 @@
38
38
  # query ListSent(
39
39
  # $uid: String
40
40
  # ) @auth(level: PUBLIC) {
41
- # emails(where: {
41
+ # emails(where: {
42
42
  # fromUid: { eq: $uid }
43
43
  # }) {
44
- # id subject sent
45
- # content: text
44
+ # id subject sent
45
+ # content: text
46
46
  # sender: from { name email: address uid }
47
47
  # to: recipients_on_email {
48
48
  # user { name email: address uid }