firebase-tools 13.28.0 → 13.29.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.
@@ -53,6 +53,9 @@ function validateConnectorYaml(unvalidated) {
53
53
  return unvalidated;
54
54
  }
55
55
  async function readGQLFiles(sourceDir) {
56
+ if (!fs.existsSync(sourceDir)) {
57
+ return [];
58
+ }
56
59
  const files = await fs.readdir(sourceDir);
57
60
  return files
58
61
  .filter((f) => f.endsWith(".gql") || f.endsWith(".graphql"))
@@ -25,9 +25,7 @@ function endpointTriggerType(endpoint) {
25
25
  else if (isBlockingTriggered(endpoint)) {
26
26
  return endpoint.blockingTrigger.eventType;
27
27
  }
28
- else {
29
- throw new Error("Unexpected trigger type for endpoint " + JSON.stringify(endpoint));
30
- }
28
+ (0, functional_1.assertExhaustive)(endpoint);
31
29
  }
32
30
  exports.endpointTriggerType = endpointTriggerType;
33
31
  exports.AllVpcEgressSettings = ["PRIVATE_RANGES_ONLY", "ALL_TRAFFIC"];
@@ -259,7 +259,9 @@ function discoverTrigger(endpoint, region, r) {
259
259
  return { httpsTrigger };
260
260
  }
261
261
  else if (isCallableTriggered(endpoint)) {
262
- return { callableTrigger: {} };
262
+ const trigger = { callableTrigger: {} };
263
+ proto.copyIfPresent(trigger.callableTrigger, endpoint.callableTrigger, "genkitAction");
264
+ return trigger;
263
265
  }
264
266
  else if (isBlockingTriggered(endpoint)) {
265
267
  return { blockingTrigger: endpoint.blockingTrigger };
@@ -7,7 +7,6 @@ const error_1 = require("../../../error");
7
7
  const utils = require("../../../utils");
8
8
  const backend = require("../backend");
9
9
  const v2events = require("../../../functions/events/v2");
10
- const v2_1 = require("../../../functions/events/v2");
11
10
  function calculateChangesets(want, have, keyFn, deleteAll) {
12
11
  const toCreate = utils.groupBy(Object.keys(want)
13
12
  .filter((id) => !have[id])
@@ -167,9 +166,9 @@ function upgradedScheduleFromV1ToV2(want, have) {
167
166
  exports.upgradedScheduleFromV1ToV2 = upgradedScheduleFromV1ToV2;
168
167
  function checkForUnsafeUpdate(want, have) {
169
168
  return (backend.isEventTriggered(want) &&
170
- v2_1.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX.test(want.eventTrigger.eventType) &&
171
169
  backend.isEventTriggered(have) &&
172
- v2_1.FIRESTORE_EVENT_REGEX.test(have.eventTrigger.eventType));
170
+ want.eventTrigger.eventType ===
171
+ v2events.CONVERTABLE_EVENTS[have.eventTrigger.eventType]);
173
172
  }
174
173
  exports.checkForUnsafeUpdate = checkForUnsafeUpdate;
175
174
  function checkForIllegalUpdate(want, have) {
@@ -196,7 +195,8 @@ function checkForIllegalUpdate(want, have) {
196
195
  };
197
196
  const wantType = triggerType(want);
198
197
  const haveType = triggerType(have);
199
- if (wantType !== haveType) {
198
+ const upgradingHttpsFunction = backend.isHttpsTriggered(have) && backend.isCallableTriggered(want);
199
+ if (wantType !== haveType && !upgradingHttpsFunction) {
200
200
  throw new error_1.FirebaseError(`[${(0, functionsDeployHelper_1.getFunctionLabel)(want)}] Changing from ${haveType} function to ${wantType} function is not allowed. Please delete your function and create a new one instead.`);
201
201
  }
202
202
  if (want.platform === "gcfv1" && have.platform === "gcfv2") {
@@ -134,6 +134,9 @@ function assertBuildEndpoint(ep, id) {
134
134
  });
135
135
  }
136
136
  else if (build.isCallableTriggered(ep)) {
137
+ (0, parsing_1.assertKeyTypes)(prefix + ".callableTrigger", ep.callableTrigger, {
138
+ genkitAction: "string?",
139
+ });
137
140
  }
138
141
  else if (build.isScheduleTriggered(ep)) {
139
142
  (0, parsing_1.assertKeyTypes)(prefix + ".scheduleTrigger", ep.scheduleTrigger, {
@@ -216,6 +219,7 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
216
219
  }
217
220
  else if (build.isCallableTriggered(ep)) {
218
221
  triggered = { callableTrigger: {} };
222
+ (0, proto_1.copyIfPresent)(triggered.callableTrigger, ep.callableTrigger, "genkitAction");
219
223
  }
220
224
  else if (build.isScheduleTriggered(ep)) {
221
225
  const st = {
@@ -55,7 +55,7 @@ exports.RUNTIMES = runtimes({
55
55
  },
56
56
  nodejs22: {
57
57
  friendly: "Node.js 22",
58
- status: "beta",
58
+ status: "GA",
59
59
  deprecationDate: "2027-04-30",
60
60
  decommissionDate: "2027-10-31",
61
61
  },
@@ -577,6 +577,20 @@ async function startAll(options, showUI = true, runningTestScript = false) {
577
577
  utils.assertIsString(options.import);
578
578
  const importDirAbsPath = path.resolve(options.import);
579
579
  const exportMetadataFilePath = path.resolve(importDirAbsPath, exportMetadata.dataconnect.path);
580
+ const dataDirectory = options.config.get("emulators.dataconnect.dataDir");
581
+ if (exportMetadataFilePath && dataDirectory) {
582
+ emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).logLabeled("WARN", "dataconnect", "'firebase.json#emulators.dataconnect.dataDir' is set and `--import` flag was passed. " +
583
+ "This will overwrite any data saved from previous runs.");
584
+ if (!options.nonInteractive &&
585
+ !(await (0, prompt_1.promptOnce)({
586
+ type: "confirm",
587
+ message: `Do you wish to continue and overwrite data in ${dataDirectory}?`,
588
+ default: false,
589
+ }))) {
590
+ await cleanShutdown();
591
+ return { deprecationNotices: [] };
592
+ }
593
+ }
580
594
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT).logLabeled("BULLET", "dataconnect", `Importing data from ${exportMetadataFilePath}`);
581
595
  args.importPath = exportMetadataFilePath;
582
596
  void (0, track_1.trackEmulator)("emulator_import", {
@@ -27,6 +27,7 @@ const fs = require("fs");
27
27
  const index_1 = require("./pg-gateway/index");
28
28
  const node_1 = require("./pg-gateway/platforms/node");
29
29
  const logger_1 = require("../../logger");
30
+ const error_1 = require("../../error");
30
31
  exports.TRUNCATE_TABLES_SQL = `
31
32
  DO $do$
32
33
  BEGIN
@@ -62,6 +63,7 @@ class PostgresServer {
62
63
  server.emit("error", err);
63
64
  });
64
65
  });
66
+ this.server = server;
65
67
  const listeningPromise = new Promise((resolve) => {
66
68
  server.listen(port, host, () => {
67
69
  resolve();
@@ -77,7 +79,7 @@ class PostgresServer {
77
79
  const pgliteArgs = {
78
80
  username: this.username,
79
81
  database: this.database,
80
- debug: 0,
82
+ debug: this.debug,
81
83
  extensions: {
82
84
  vector,
83
85
  uuidOssp,
@@ -90,7 +92,7 @@ class PostgresServer {
90
92
  const file = new File([rf], this.importPath);
91
93
  pgliteArgs.loadDataDir = file;
92
94
  }
93
- this.db = await pglite_1.PGlite.create(pgliteArgs);
95
+ this.db = await this.forceCreateDB(pgliteArgs);
94
96
  await this.db.waitReady;
95
97
  }
96
98
  return this.db;
@@ -105,12 +107,37 @@ class PostgresServer {
105
107
  const arrayBuff = await dump.arrayBuffer();
106
108
  fs.writeFileSync(exportPath, new Uint8Array(arrayBuff));
107
109
  }
108
- constructor(database, username, dataDirectory, importPath) {
110
+ async forceCreateDB(pgliteArgs) {
111
+ try {
112
+ const db = await pglite_1.PGlite.create(pgliteArgs);
113
+ return db;
114
+ }
115
+ catch (err) {
116
+ if (pgliteArgs.dataDir && (0, error_1.hasMessage)(err) && /Database already exists/.test(err.message)) {
117
+ fs.rmSync(pgliteArgs.dataDir, { force: true, recursive: true });
118
+ const db = await pglite_1.PGlite.create(pgliteArgs);
119
+ return db;
120
+ }
121
+ throw err;
122
+ }
123
+ }
124
+ async stop() {
125
+ if (this.db) {
126
+ await this.db.close();
127
+ }
128
+ if (this.server) {
129
+ this.server.close();
130
+ }
131
+ return;
132
+ }
133
+ constructor(args) {
109
134
  this.db = undefined;
110
- this.username = username;
111
- this.database = database;
112
- this.dataDirectory = dataDirectory;
113
- this.importPath = importPath;
135
+ this.server = undefined;
136
+ this.username = args.username;
137
+ this.database = args.database;
138
+ this.dataDirectory = args.dataDirectory;
139
+ this.importPath = args.importPath;
140
+ this.debug = args.debug ? 5 : 0;
114
141
  }
115
142
  }
116
143
  exports.PostgresServer = PostgresServer;
@@ -69,7 +69,13 @@ class DataConnectEmulator {
69
69
  const postgresDumpPath = this.args.importPath
70
70
  ? path.join(this.args.importPath, "postgres.tar.gz")
71
71
  : undefined;
72
- this.postgresServer = new pgliteServer_1.PostgresServer(dbId, "postgres", dataDirectory, postgresDumpPath);
72
+ this.postgresServer = new pgliteServer_1.PostgresServer({
73
+ database: dbId,
74
+ username: "fdc",
75
+ dataDirectory,
76
+ importPath: postgresDumpPath,
77
+ debug: this.args.debug,
78
+ });
73
79
  const server = await this.postgresServer.createPGServer(pgHost, pgPort);
74
80
  const connectableHost = (0, utils_1.connectableHostname)(pgHost);
75
81
  connStr = `postgres://${connectableHost}:${pgPort}/${dbId}?sslmode=disable`;
@@ -101,6 +107,9 @@ class DataConnectEmulator {
101
107
  this.logger.logLabeled("INFO", "dataconnect", "Skipping cleanup of Data Connect emulator, as it was not started by this process.");
102
108
  return;
103
109
  }
110
+ if (this.postgresServer) {
111
+ await this.postgresServer.stop();
112
+ }
104
113
  return (0, downloadableEmulators_1.stop)(types_1.Emulators.DATACONNECT);
105
114
  }
106
115
  getInfo() {
@@ -48,20 +48,20 @@ const EMULATOR_UPDATE_DETAILS = {
48
48
  },
49
49
  dataconnect: process.platform === "darwin"
50
50
  ? {
51
- version: "1.7.4",
52
- expectedSize: 25277184,
53
- expectedChecksum: "74f6b66c79a8a903132c7ab26c644593",
51
+ version: "1.7.5",
52
+ expectedSize: 25281280,
53
+ expectedChecksum: "85d0de96b5c08b553fd8506a2bc381bb",
54
54
  }
55
55
  : process.platform === "win32"
56
56
  ? {
57
- version: "1.7.4",
58
- expectedSize: 25707520,
59
- expectedChecksum: "66eec92e2d57ae42a8b58f33b65b4184",
57
+ version: "1.7.5",
58
+ expectedSize: 25711616,
59
+ expectedChecksum: "c99d67fa8e74d41760b96122b055b8e2",
60
60
  }
61
61
  : {
62
- version: "1.7.4",
62
+ version: "1.7.5",
63
63
  expectedSize: 25190552,
64
- expectedChecksum: "acb7be487020afa6e1a597ceb8c6e862",
64
+ expectedChecksum: "61d966b781e6f2887f8b38ec271b54e2",
65
65
  },
66
66
  };
67
67
  exports.DownloadDetails = {
@@ -475,8 +475,7 @@ async function start(targetName, args, extraEnv = {}) {
475
475
  exports.start = start;
476
476
  function isIncomaptibleArchError(err) {
477
477
  var _a;
478
- const hasMessage = (e) => !!(e === null || e === void 0 ? void 0 : e.message);
479
- return (hasMessage(err) &&
478
+ return ((0, error_1.hasMessage)(err) &&
480
479
  /Unknown system error/.test((_a = err.message) !== null && _a !== void 0 ? _a : "") &&
481
480
  process.platform === "darwin");
482
481
  }
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.getError = exports.getErrStatus = exports.isObject = exports.getErrStack = exports.getErrMsg = exports.FirebaseError = void 0;
3
+ exports.hasMessage = exports.isBillingError = exports.getError = exports.getErrStatus = exports.isObject = exports.getErrStack = exports.getErrMsg = exports.FirebaseError = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  const DEFAULT_CHILDREN = [];
6
6
  const DEFAULT_EXIT = 1;
@@ -65,3 +65,5 @@ function isBillingError(e) {
65
65
  }));
66
66
  }
67
67
  exports.isBillingError = isBillingError;
68
+ const hasMessage = (e) => !!(e === null || e === void 0 ? void 0 : e.message);
69
+ exports.hasMessage = hasMessage;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIREALERTS_EVENT = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
3
+ exports.CONVERTABLE_EVENTS = exports.FIREALERTS_EVENT = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
4
4
  exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
5
5
  exports.STORAGE_EVENTS = [
6
6
  "google.cloud.storage.object.v1.finalized",
@@ -28,5 +28,9 @@ exports.FIRESTORE_EVENTS = [
28
28
  "google.cloud.firestore.document.v1.deleted.withAuthContext",
29
29
  ];
30
30
  exports.FIREALERTS_EVENT = "google.firebase.firebasealerts.alerts.v1.published";
31
- exports.FIRESTORE_EVENT_REGEX = /^google\.cloud\.firestore\.document\.v1\.[^\.]*$/;
32
- exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = /^google\.cloud\.firestore\.document\.v1\..*\.withAuthContext$/;
31
+ exports.CONVERTABLE_EVENTS = {
32
+ "google.cloud.firestore.document.v1.created": "google.cloud.firestore.document.v1.created.withAuthContext",
33
+ "google.cloud.firestore.document.v1.updated": "google.cloud.firestore.document.v1.updated.withAuthContext",
34
+ "google.cloud.firestore.document.v1.deleted": "google.cloud.firestore.document.v1.deleted.withAuthContext",
35
+ "google.cloud.firestore.document.v1.written": "google.cloud.firestore.document.v1.written.withAuthContext",
36
+ };
@@ -259,6 +259,9 @@ function functionFromEndpoint(endpoint) {
259
259
  }
260
260
  else if (backend.isCallableTriggered(endpoint)) {
261
261
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
262
+ if (endpoint.callableTrigger.genkitAction) {
263
+ gcfFunction.labels["genkit-action"] = endpoint.callableTrigger.genkitAction;
264
+ }
262
265
  }
263
266
  else if (backend.isBlockingTriggered(endpoint)) {
264
267
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.BLOCKING_LABEL]: constants_1.BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
@@ -294,6 +297,9 @@ function endpointFromFunction(gcfFunction) {
294
297
  trigger = {
295
298
  callableTrigger: {},
296
299
  };
300
+ if (gcfFunction.labels["genkit-action"]) {
301
+ trigger.callableTrigger.genkitAction = gcfFunction.labels["genkit-action"];
302
+ }
297
303
  }
298
304
  else if ((_d = gcfFunction.labels) === null || _d === void 0 ? void 0 : _d[constants_1.BLOCKING_LABEL]) {
299
305
  trigger = {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.actuate = exports.doSetup = void 0;
4
4
  const path_1 = require("path");
5
5
  const clc = require("colorette");
6
+ const fs = require("fs-extra");
6
7
  const prompt_1 = require("../../../prompt");
7
8
  const provisionCloudSql_1 = require("../../../dataconnect/provisionCloudSql");
8
9
  const freeTrial_1 = require("../../../dataconnect/freeTrial");
@@ -137,6 +138,9 @@ async function writeFiles(config, info) {
137
138
  await config.askWriteProjectFile((0, path_1.join)(dir, "schema", f.path), f.content);
138
139
  }
139
140
  }
141
+ else {
142
+ fs.ensureFileSync((0, path_1.join)(dir, "schema", "schema.gql"));
143
+ }
140
144
  for (const c of info.connectors) {
141
145
  await writeConnectorFiles(config, c);
142
146
  }
@@ -26,12 +26,34 @@ exports.PROJECTS_CREATE_QUESTIONS = [
26
26
  default: "",
27
27
  message: "Please specify a unique project id " +
28
28
  `(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
29
+ validate: (projectId) => {
30
+ if (projectId.length < 6) {
31
+ return "Project ID must be at least 6 characters long";
32
+ }
33
+ else if (projectId.length > 30) {
34
+ return "Project ID cannot be longer than 30 characters";
35
+ }
36
+ else {
37
+ return true;
38
+ }
39
+ },
29
40
  },
30
41
  {
31
42
  type: "input",
32
43
  name: "displayName",
33
- default: "",
44
+ default: (answers) => answers.projectId,
34
45
  message: "What would you like to call your project? (defaults to your project ID)",
46
+ validate: (displayName) => {
47
+ if (displayName.length < 4) {
48
+ return "Project name must be at least 4 characters long";
49
+ }
50
+ else if (displayName.length > 30) {
51
+ return "Project name cannot be longer than 30 characters";
52
+ }
53
+ else {
54
+ return true;
55
+ }
56
+ },
35
57
  },
36
58
  ];
37
59
  const firebaseAPIClient = new apiv2_1.Client({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.28.0",
3
+ "version": "13.29.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "logs": "firebase functions:log"
11
11
  },
12
12
  "engines": {
13
- "node": "18"
13
+ "node": "22"
14
14
  },
15
15
  "main": "index.js",
16
16
  "dependencies": {
@@ -9,7 +9,7 @@
9
9
  "logs": "firebase functions:log"
10
10
  },
11
11
  "engines": {
12
- "node": "18"
12
+ "node": "22"
13
13
  },
14
14
  "main": "index.js",
15
15
  "dependencies": {
@@ -11,7 +11,7 @@
11
11
  "logs": "firebase functions:log"
12
12
  },
13
13
  "engines": {
14
- "node": "18"
14
+ "node": "22"
15
15
  },
16
16
  "main": "lib/index.js",
17
17
  "dependencies": {
@@ -10,7 +10,7 @@
10
10
  "logs": "firebase functions:log"
11
11
  },
12
12
  "engines": {
13
- "node": "18"
13
+ "node": "22"
14
14
  },
15
15
  "main": "lib/index.js",
16
16
  "dependencies": {
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "module": "commonjs",
3
+ "module": "NodeNext",
4
+ "esModuleInterop": true,
5
+ "moduleResolution": "nodenext",
4
6
  "noImplicitReturns": true,
5
7
  "noUnusedLocals": true,
6
8
  "outDir": "lib",