firebase-tools 14.7.0 → 14.8.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/commands/functions-delete.js +0 -2
- package/lib/deploy/functions/checkIam.js +2 -2
- package/lib/emulator/dataconnect/pgliteServer.js +124 -102
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/ensureApiEnabled.js +22 -0
- package/lib/fsutils.js +16 -1
- package/lib/gcp/serviceusage.js +2 -2
- package/lib/operation-poller.js +3 -1
- package/package.json +4 -2
- package/templates/init/functions/javascript/index.js +14 -1
- package/templates/init/functions/python/main.py +8 -0
- package/templates/init/functions/typescript/index.ts +14 -1
|
@@ -16,7 +16,6 @@ const planner = require("../deploy/functions/release/planner");
|
|
|
16
16
|
const fabricator = require("../deploy/functions/release/fabricator");
|
|
17
17
|
const executor = require("../deploy/functions/release/executor");
|
|
18
18
|
const reporter = require("../deploy/functions/release/reporter");
|
|
19
|
-
const containerCleaner = require("../deploy/functions/containerCleaner");
|
|
20
19
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
21
20
|
exports.command = new command_1.Command("functions:delete [filters...]")
|
|
22
21
|
.description("delete one or more Cloud Functions by name or group name.")
|
|
@@ -91,5 +90,4 @@ exports.command = new command_1.Command("functions:delete [filters...]")
|
|
|
91
90
|
exit: 1,
|
|
92
91
|
});
|
|
93
92
|
}
|
|
94
|
-
await containerCleaner.cleanupBuildImages([], allEpToDelete);
|
|
95
93
|
});
|
|
@@ -120,10 +120,10 @@ async function ensureGenkitMonitoringRoles(projectId, projectNumber, want, have,
|
|
|
120
120
|
.map((endpoint) => endpoint.serviceAccount || "")
|
|
121
121
|
.filter((value, index, self) => self.indexOf(value) === index);
|
|
122
122
|
const defaultServiceAccountIndex = serviceAccounts.indexOf("");
|
|
123
|
-
if (defaultServiceAccountIndex) {
|
|
123
|
+
if (defaultServiceAccountIndex !== -1) {
|
|
124
124
|
serviceAccounts[defaultServiceAccountIndex] = await gce.getDefaultServiceAccount(projectNumber);
|
|
125
125
|
}
|
|
126
|
-
const members = serviceAccounts.map((sa) => `serviceAccount:${sa}`);
|
|
126
|
+
const members = serviceAccounts.filter((sa) => !!sa).map((sa) => `serviceAccount:${sa}`);
|
|
127
127
|
const requiredBindings = [];
|
|
128
128
|
for (const monitoringRole of exports.GENKIT_MONITORING_ROLES) {
|
|
129
129
|
requiredBindings.push({
|
|
@@ -19,16 +19,17 @@ 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.fromNodeSocket = exports.
|
|
22
|
+
exports.fromNodeSocket = 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
26
|
const node_stream_1 = require("node:stream");
|
|
27
27
|
const fs = require("fs");
|
|
28
|
+
const path = require("node:path");
|
|
28
29
|
const pg_gateway_1 = require("pg-gateway");
|
|
29
30
|
const logger_1 = require("../../logger");
|
|
30
31
|
const error_1 = require("../../error");
|
|
31
|
-
const
|
|
32
|
+
const fsutils_1 = require("../../fsutils");
|
|
32
33
|
exports.TRUNCATE_TABLES_SQL = `
|
|
33
34
|
DO $do$
|
|
34
35
|
DECLARE _clear text;
|
|
@@ -45,22 +46,43 @@ class PostgresServer {
|
|
|
45
46
|
async createPGServer(host = "127.0.0.1", port) {
|
|
46
47
|
const getDb = this.getDb.bind(this);
|
|
47
48
|
const server = net.createServer(async (socket) => {
|
|
48
|
-
|
|
49
|
-
serverVersion: "
|
|
49
|
+
await fromNodeSocket(socket, {
|
|
50
|
+
serverVersion: "17.4 (PGlite 0.3.3)",
|
|
50
51
|
auth: { method: "trust" },
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
onMessage(data, { isAuthenticated }) {
|
|
53
|
+
return __asyncGenerator(this, arguments, function* onMessage_1() {
|
|
54
|
+
var _a, e_1, _b, _c;
|
|
55
|
+
if (!isAuthenticated) {
|
|
56
|
+
return yield __await(void 0);
|
|
57
|
+
}
|
|
58
|
+
const db = yield __await(getDb());
|
|
59
|
+
if (data[0] === pg_gateway_1.FrontendMessageCode.Terminate) {
|
|
60
|
+
yield __await(db.query("DEALLOCATE ALL"));
|
|
61
|
+
}
|
|
62
|
+
const response = yield __await(db.execProtocolRaw(data));
|
|
63
|
+
try {
|
|
64
|
+
for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
|
|
65
|
+
_c = _f.value;
|
|
66
|
+
_d = false;
|
|
67
|
+
try {
|
|
68
|
+
const message = _c;
|
|
69
|
+
yield yield __await(message);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
_d = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
77
|
+
finally {
|
|
78
|
+
try {
|
|
79
|
+
if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
|
|
80
|
+
}
|
|
81
|
+
finally { if (e_1) throw e_1.error; }
|
|
82
|
+
}
|
|
83
|
+
});
|
|
61
84
|
},
|
|
62
85
|
});
|
|
63
|
-
const extendedQueryPatch = new PGliteExtendedQueryPatch(connection);
|
|
64
86
|
socket.on("end", () => {
|
|
65
87
|
logger_1.logger.debug("Postgres client disconnected");
|
|
66
88
|
});
|
|
@@ -79,30 +101,15 @@ class PostgresServer {
|
|
|
79
101
|
}
|
|
80
102
|
async getDb() {
|
|
81
103
|
if (!this.db) {
|
|
82
|
-
|
|
83
|
-
fs.mkdirSync(this.dataDirectory, { recursive: true });
|
|
84
|
-
}
|
|
85
|
-
const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
|
|
86
|
-
const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
|
|
87
|
-
const pgliteArgs = {
|
|
88
|
-
debug: this.debug,
|
|
89
|
-
extensions: {
|
|
90
|
-
vector,
|
|
91
|
-
uuidOssp,
|
|
92
|
-
},
|
|
93
|
-
dataDir: this.dataDirectory,
|
|
94
|
-
};
|
|
95
|
-
if (this.importPath) {
|
|
96
|
-
logger_1.logger.debug(`Importing from ${this.importPath}`);
|
|
97
|
-
const rf = fs.readFileSync(this.importPath);
|
|
98
|
-
const file = new File([rf], this.importPath);
|
|
99
|
-
pgliteArgs.loadDataDir = file;
|
|
100
|
-
}
|
|
101
|
-
this.db = await this.forceCreateDB(pgliteArgs);
|
|
102
|
-
await this.db.waitReady;
|
|
104
|
+
this.db = await this.forceCreateDB();
|
|
103
105
|
}
|
|
104
106
|
return this.db;
|
|
105
107
|
}
|
|
108
|
+
async getExtensions() {
|
|
109
|
+
const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
|
|
110
|
+
const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
|
|
111
|
+
return { vector, uuidOssp };
|
|
112
|
+
}
|
|
106
113
|
async clearDb() {
|
|
107
114
|
const db = await this.getDb();
|
|
108
115
|
await db.query(exports.TRUNCATE_TABLES_SQL);
|
|
@@ -113,18 +120,92 @@ class PostgresServer {
|
|
|
113
120
|
const arrayBuff = await dump.arrayBuffer();
|
|
114
121
|
fs.writeFileSync(exportPath, new Uint8Array(arrayBuff));
|
|
115
122
|
}
|
|
116
|
-
async
|
|
123
|
+
async migrateDb(pgliteArgs) {
|
|
124
|
+
if (!this.baseDataDirectory) {
|
|
125
|
+
throw new error_1.FirebaseError("Cannot migrate database without a data directory.");
|
|
126
|
+
}
|
|
127
|
+
const { PGlite: PGlite02 } = await dynamicImport("pglite-2");
|
|
128
|
+
const pgDump = (await dynamicImport("@electric-sql/pglite-tools/pg_dump")).pgDump;
|
|
129
|
+
logger_1.logger.info("Opening database with Postgres 16...");
|
|
130
|
+
const extensions = await this.getExtensions();
|
|
131
|
+
const dataDir = this.baseDataDirectory;
|
|
132
|
+
const oldDb = new PGlite02(Object.assign(Object.assign({}, pgliteArgs), { dataDir }));
|
|
133
|
+
await oldDb.waitReady;
|
|
134
|
+
const oldVersion = await oldDb.query("SELECT version();");
|
|
135
|
+
logger_1.logger.debug(`Old database version: ${oldVersion.rows[0].version}`);
|
|
136
|
+
if (!oldVersion.rows[0].version.includes("PostgreSQL 16")) {
|
|
137
|
+
await oldDb.close();
|
|
138
|
+
throw new error_1.FirebaseError("Migration started, but DB version is not PostgreSQL 16.");
|
|
139
|
+
}
|
|
140
|
+
logger_1.logger.info("Dumping data from old database...");
|
|
141
|
+
const dumpDir = await oldDb.dumpDataDir("none");
|
|
142
|
+
const tempOldDb = await PGlite02.create({
|
|
143
|
+
loadDataDir: dumpDir,
|
|
144
|
+
extensions,
|
|
145
|
+
});
|
|
146
|
+
const dumpResult = await pgDump({ pg: tempOldDb, args: ["--verbose", "--verbose"] });
|
|
147
|
+
await tempOldDb.close();
|
|
148
|
+
await oldDb.close();
|
|
149
|
+
logger_1.logger.info(`Moving old database directory to ${this.baseDataDirectory}/pg16...`);
|
|
150
|
+
const pg16Dir = this.getVersionedDataDir(16);
|
|
151
|
+
(0, fsutils_1.moveAll)(this.baseDataDirectory, pg16Dir);
|
|
152
|
+
logger_1.logger.info("If you need to use an older version of the Firebase CLI, you can restore from that directory.");
|
|
153
|
+
logger_1.logger.info("Creating new database with Postgres 17...");
|
|
154
|
+
const pg17Dir = this.getVersionedDataDir(17);
|
|
155
|
+
const newDb = new pglite_1.PGlite(Object.assign(Object.assign({}, pgliteArgs), { dataDir: pg17Dir }));
|
|
156
|
+
await newDb.waitReady;
|
|
157
|
+
logger_1.logger.info("Importing data into new database...");
|
|
158
|
+
const dumpText = await dumpResult.text();
|
|
159
|
+
await newDb.exec(dumpText);
|
|
160
|
+
await newDb.exec("SET SEARCH_PATH = public;");
|
|
161
|
+
logger_1.logger.info("Postgres database migration successful.");
|
|
162
|
+
return newDb;
|
|
163
|
+
}
|
|
164
|
+
getVersionedDataDir(version) {
|
|
165
|
+
if (!this.baseDataDirectory) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
return path.join(this.baseDataDirectory, `pg${version}`);
|
|
169
|
+
}
|
|
170
|
+
async forceCreateDB() {
|
|
171
|
+
const baseArgs = {
|
|
172
|
+
debug: this.debug,
|
|
173
|
+
extensions: await this.getExtensions(),
|
|
174
|
+
};
|
|
175
|
+
const pg17Dir = this.getVersionedDataDir(17);
|
|
176
|
+
if (pg17Dir && !fs.existsSync(pg17Dir)) {
|
|
177
|
+
fs.mkdirSync(pg17Dir, { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
if (this.importPath) {
|
|
180
|
+
logger_1.logger.debug(`Importing from ${this.importPath}`);
|
|
181
|
+
const rf = fs.readFileSync(this.importPath);
|
|
182
|
+
const file = new File([rf], this.importPath);
|
|
183
|
+
baseArgs.loadDataDir = file;
|
|
184
|
+
}
|
|
185
|
+
if (this.baseDataDirectory && fs.existsSync(this.baseDataDirectory)) {
|
|
186
|
+
const versionFilePath = path.join(this.baseDataDirectory, "PG_VERSION");
|
|
187
|
+
if (fs.existsSync(versionFilePath)) {
|
|
188
|
+
const version = fs.readFileSync(versionFilePath, "utf-8").trim();
|
|
189
|
+
logger_1.logger.debug(`Found Postgres version file with version: ${version}`);
|
|
190
|
+
if (version === "16") {
|
|
191
|
+
logger_1.logger.info("Detected a Postgres 16 data directory from an older version of firebase-tools. Migrating to Postgres 17...");
|
|
192
|
+
return this.migrateDb(baseArgs);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
117
196
|
try {
|
|
118
|
-
const db =
|
|
197
|
+
const db = new pglite_1.PGlite(Object.assign(Object.assign({}, baseArgs), { dataDir: pg17Dir }));
|
|
198
|
+
await db.waitReady;
|
|
119
199
|
return db;
|
|
120
200
|
}
|
|
121
201
|
catch (err) {
|
|
122
|
-
if (
|
|
123
|
-
fs.rmSync(
|
|
124
|
-
const db =
|
|
202
|
+
if (pg17Dir && (0, error_1.hasMessage)(err) && /Database already exists/.test(err.message)) {
|
|
203
|
+
fs.rmSync(pg17Dir, { force: true, recursive: true });
|
|
204
|
+
const db = new pglite_1.PGlite(Object.assign(Object.assign({}, baseArgs), { dataDir: pg17Dir }));
|
|
205
|
+
await db.waitReady;
|
|
125
206
|
return db;
|
|
126
207
|
}
|
|
127
|
-
logger_1.logger.
|
|
208
|
+
logger_1.logger.warn(`Error from pglite: ${err}`);
|
|
128
209
|
throw new error_1.FirebaseError("Unexpected error starting up Postgres.");
|
|
129
210
|
}
|
|
130
211
|
}
|
|
@@ -140,71 +221,12 @@ class PostgresServer {
|
|
|
140
221
|
constructor(args) {
|
|
141
222
|
this.db = undefined;
|
|
142
223
|
this.server = undefined;
|
|
143
|
-
this.
|
|
224
|
+
this.baseDataDirectory = args.dataDirectory;
|
|
144
225
|
this.importPath = args.importPath;
|
|
145
226
|
this.debug = args.debug ? 5 : 0;
|
|
146
227
|
}
|
|
147
228
|
}
|
|
148
229
|
exports.PostgresServer = PostgresServer;
|
|
149
|
-
class PGliteExtendedQueryPatch {
|
|
150
|
-
constructor(connection) {
|
|
151
|
-
this.connection = connection;
|
|
152
|
-
this.isExtendedQuery = false;
|
|
153
|
-
this.eqpErrored = false;
|
|
154
|
-
}
|
|
155
|
-
filterResponse(message, response) {
|
|
156
|
-
return __asyncGenerator(this, arguments, function* filterResponse_1() {
|
|
157
|
-
var _a, e_1, _b, _c;
|
|
158
|
-
const pipelineStartMessages = [
|
|
159
|
-
pg_gateway_1.FrontendMessageCode.Parse,
|
|
160
|
-
pg_gateway_1.FrontendMessageCode.Bind,
|
|
161
|
-
pg_gateway_1.FrontendMessageCode.Close,
|
|
162
|
-
];
|
|
163
|
-
const decoder = new node_string_decoder_1.StringDecoder();
|
|
164
|
-
const decoded = decoder.write(message);
|
|
165
|
-
logger_1.logger.debug(decoded);
|
|
166
|
-
if (pipelineStartMessages.includes(message[0])) {
|
|
167
|
-
this.isExtendedQuery = true;
|
|
168
|
-
}
|
|
169
|
-
if (message[0] === pg_gateway_1.FrontendMessageCode.Sync) {
|
|
170
|
-
this.isExtendedQuery = false;
|
|
171
|
-
this.eqpErrored = false;
|
|
172
|
-
return yield __await(this.connection.createReadyForQuery());
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
|
|
176
|
-
_c = _f.value;
|
|
177
|
-
_d = false;
|
|
178
|
-
try {
|
|
179
|
-
const message = _c;
|
|
180
|
-
if (this.eqpErrored) {
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (this.isExtendedQuery && message[0] === pg_gateway_1.BackendMessageCode.ErrorMessage) {
|
|
184
|
-
this.eqpErrored = true;
|
|
185
|
-
}
|
|
186
|
-
if (this.isExtendedQuery && message[0] === pg_gateway_1.BackendMessageCode.ReadyForQuery) {
|
|
187
|
-
logger_1.logger.debug("Filtered out a ReadyForQuery.");
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
yield yield __await(message);
|
|
191
|
-
}
|
|
192
|
-
finally {
|
|
193
|
-
_d = true;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
198
|
-
finally {
|
|
199
|
-
try {
|
|
200
|
-
if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
|
|
201
|
-
}
|
|
202
|
-
finally { if (e_1) throw e_1.error; }
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
exports.PGliteExtendedQueryPatch = PGliteExtendedQueryPatch;
|
|
208
230
|
async function fromNodeSocket(socket, options) {
|
|
209
231
|
const rs = node_stream_1.Readable.toWeb(socket);
|
|
210
232
|
const ws = node_stream_1.Writable.toWeb(socket);
|
|
@@ -54,28 +54,28 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.7.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.7.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.
|
|
57
|
+
"version": "2.7.1",
|
|
58
|
+
"expectedSize": 27607808,
|
|
59
|
+
"expectedChecksum": "3cf0b4dcb96639f1b7ddf3e2052ea28f",
|
|
60
|
+
"expectedChecksumSHA256": "76b6db1f87e14e3c39035301ae199bec0458a2e7534a1db962276a92fa99c215",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.7.1",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1"
|
|
63
63
|
},
|
|
64
64
|
"win32": {
|
|
65
|
-
"version": "2.7.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.7.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.
|
|
65
|
+
"version": "2.7.1",
|
|
66
|
+
"expectedSize": 28067328,
|
|
67
|
+
"expectedChecksum": "8dacedaecac242199dfcf3f6c023f89a",
|
|
68
|
+
"expectedChecksumSHA256": "fc098efa9aed9b1ac7d35b90da47608190f3a5562f37444012785a9793d743a0",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.7.1",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1.exe"
|
|
71
71
|
},
|
|
72
72
|
"linux": {
|
|
73
|
-
"version": "2.7.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.7.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.
|
|
73
|
+
"version": "2.7.1",
|
|
74
|
+
"expectedSize": 27521176,
|
|
75
|
+
"expectedChecksum": "3c03d85c2d1de80a2f2ba13fe3b5a3ff",
|
|
76
|
+
"expectedChecksumSHA256": "94c35ab64bd8d206e7843513848e7375cd8f9b6a06d013b1ec541ac831cda8a8",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.7.1",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -8,6 +8,7 @@ const apiv2_1 = require("./apiv2");
|
|
|
8
8
|
const utils = require("./utils");
|
|
9
9
|
const error_1 = require("./error");
|
|
10
10
|
const logger_1 = require("./logger");
|
|
11
|
+
const configstore_1 = require("./configstore");
|
|
11
12
|
exports.POLL_SETTINGS = {
|
|
12
13
|
pollInterval: 10000,
|
|
13
14
|
pollsBeforeRetry: 12,
|
|
@@ -18,6 +19,9 @@ const apiClient = new apiv2_1.Client({
|
|
|
18
19
|
});
|
|
19
20
|
async function check(projectId, apiUri, prefix, silent = false) {
|
|
20
21
|
const apiName = apiUri.startsWith("http") ? new URL(apiUri).hostname : apiUri;
|
|
22
|
+
if (checkAPIEnablementCache(projectId, apiName)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
21
25
|
const res = await apiClient.get(`/projects/${projectId}/services/${apiName}`, {
|
|
22
26
|
headers: { "x-goog-quota-user": `projects/${projectId}` },
|
|
23
27
|
skipLog: { resBody: true },
|
|
@@ -26,6 +30,9 @@ async function check(projectId, apiUri, prefix, silent = false) {
|
|
|
26
30
|
if (isEnabled && !silent) {
|
|
27
31
|
utils.logLabeledSuccess(prefix, `required API ${(0, colorette_1.bold)(apiName)} is enabled`);
|
|
28
32
|
}
|
|
33
|
+
if (isEnabled) {
|
|
34
|
+
cacheEnabledAPI(projectId, apiName);
|
|
35
|
+
}
|
|
29
36
|
return isEnabled;
|
|
30
37
|
}
|
|
31
38
|
exports.check = check;
|
|
@@ -39,6 +46,7 @@ async function enable(projectId, apiName) {
|
|
|
39
46
|
headers: { "x-goog-quota-user": `projects/${projectId}` },
|
|
40
47
|
skipLog: { resBody: true },
|
|
41
48
|
});
|
|
49
|
+
cacheEnabledAPI(projectId, apiName);
|
|
42
50
|
}
|
|
43
51
|
catch (err) {
|
|
44
52
|
if ((0, error_1.isBillingError)(err)) {
|
|
@@ -120,3 +128,17 @@ function enableApiURI(projectId, apiName) {
|
|
|
120
128
|
return `https://console.cloud.google.com/apis/library/${apiName}?project=${projectId}`;
|
|
121
129
|
}
|
|
122
130
|
exports.enableApiURI = enableApiURI;
|
|
131
|
+
const API_ENABLEMENT_CACHE_KEY = "apiEnablementCache";
|
|
132
|
+
function checkAPIEnablementCache(projectId, apiName) {
|
|
133
|
+
var _a;
|
|
134
|
+
const cache = configstore_1.configstore.get(API_ENABLEMENT_CACHE_KEY);
|
|
135
|
+
return !!((_a = cache === null || cache === void 0 ? void 0 : cache[projectId]) === null || _a === void 0 ? void 0 : _a[apiName]);
|
|
136
|
+
}
|
|
137
|
+
function cacheEnabledAPI(projectId, apiName) {
|
|
138
|
+
const cache = (configstore_1.configstore.get(API_ENABLEMENT_CACHE_KEY) || {});
|
|
139
|
+
if (!cache[projectId]) {
|
|
140
|
+
cache[projectId] = {};
|
|
141
|
+
}
|
|
142
|
+
cache[projectId][apiName] = true;
|
|
143
|
+
configstore_1.configstore.set(API_ENABLEMENT_CACHE_KEY, cache);
|
|
144
|
+
}
|
package/lib/fsutils.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listFiles = exports.readFile = exports.dirExistsSync = exports.fileExistsSync = void 0;
|
|
3
|
+
exports.moveAll = exports.listFiles = exports.readFile = exports.dirExistsSync = exports.fileExistsSync = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
|
+
const path = require("path");
|
|
5
6
|
const error_1 = require("./error");
|
|
7
|
+
const fs_extra_1 = require("fs-extra");
|
|
6
8
|
function fileExistsSync(path) {
|
|
7
9
|
try {
|
|
8
10
|
return (0, fs_1.statSync)(path).isFile();
|
|
@@ -45,3 +47,16 @@ function listFiles(path) {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
exports.listFiles = listFiles;
|
|
50
|
+
function moveAll(srcDir, destDir) {
|
|
51
|
+
if (!(0, fs_1.existsSync)(destDir)) {
|
|
52
|
+
(0, fs_1.mkdirSync)(destDir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
const files = listFiles(srcDir);
|
|
55
|
+
for (const f of files) {
|
|
56
|
+
const srcPath = path.join(srcDir, f);
|
|
57
|
+
if (srcPath === destDir)
|
|
58
|
+
continue;
|
|
59
|
+
(0, fs_extra_1.moveSync)(srcPath, path.join(destDir, f));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.moveAll = moveAll;
|
package/lib/gcp/serviceusage.js
CHANGED
|
@@ -20,7 +20,7 @@ const serviceUsagePollerOptions = {
|
|
|
20
20
|
async function generateServiceIdentity(projectNumber, service, prefix) {
|
|
21
21
|
utils.logLabeledBullet(prefix, `generating the service identity for ${(0, colorette_1.bold)(service)}...`);
|
|
22
22
|
try {
|
|
23
|
-
const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`);
|
|
23
|
+
const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`, {}, { headers: { "x-goog-quota-user": `projects/${projectNumber}` } });
|
|
24
24
|
return res.body;
|
|
25
25
|
}
|
|
26
26
|
catch (err) {
|
|
@@ -35,6 +35,6 @@ async function generateServiceIdentityAndPoll(projectNumber, service, prefix) {
|
|
|
35
35
|
if (op.done) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
await poller.pollOperation(Object.assign(Object.assign({}, serviceUsagePollerOptions), { operationResourceName: op.name }));
|
|
38
|
+
await poller.pollOperation(Object.assign(Object.assign({}, serviceUsagePollerOptions), { operationResourceName: op.name, headers: { "x-goog-quota-user": `projects/${projectNumber}` } }));
|
|
39
39
|
}
|
|
40
40
|
exports.generateServiceIdentityAndPoll = generateServiceIdentityAndPoll;
|
package/lib/operation-poller.js
CHANGED
|
@@ -34,7 +34,9 @@ class OperationPoller {
|
|
|
34
34
|
return async () => {
|
|
35
35
|
let res;
|
|
36
36
|
try {
|
|
37
|
-
res = await apiClient.get(options.operationResourceName
|
|
37
|
+
res = await apiClient.get(options.operationResourceName, {
|
|
38
|
+
headers: options.headers,
|
|
39
|
+
});
|
|
38
40
|
}
|
|
39
41
|
catch (err) {
|
|
40
42
|
if (err.status === 500 || err.status === 503) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.8.0",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
]
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@electric-sql/pglite": "^0.
|
|
63
|
+
"@electric-sql/pglite": "^0.3.3",
|
|
64
|
+
"@electric-sql/pglite-tools": "^0.2.8",
|
|
64
65
|
"@google-cloud/cloud-sql-connector": "^1.3.3",
|
|
65
66
|
"@google-cloud/pubsub": "^4.5.0",
|
|
66
67
|
"@inquirer/prompts": "^7.4.0",
|
|
@@ -110,6 +111,7 @@
|
|
|
110
111
|
"p-limit": "^3.0.1",
|
|
111
112
|
"pg": "^8.11.3",
|
|
112
113
|
"pg-gateway": "^0.3.0-beta.4",
|
|
114
|
+
"pglite-2": "npm:@electric-sql/pglite@0.2.17",
|
|
113
115
|
"portfinder": "^1.0.32",
|
|
114
116
|
"progress": "^2.0.3",
|
|
115
117
|
"proxy-agent": "^6.3.0",
|
|
@@ -7,9 +7,22 @@
|
|
|
7
7
|
* See a full list of supported triggers at https://firebase.google.com/docs/functions
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const {
|
|
10
|
+
const {setGlobalOptions} = require("firebase-functions");
|
|
11
|
+
const {onRequest} = require("firebase-functions/https");
|
|
11
12
|
const logger = require("firebase-functions/logger");
|
|
12
13
|
|
|
14
|
+
// For cost control, you can set the maximum number of containers that can be
|
|
15
|
+
// running at the same time. This helps mitigate the impact of unexpected
|
|
16
|
+
// traffic spikes by instead downgrading performance. This limit is a
|
|
17
|
+
// per-function limit. You can override the limit for each function using the
|
|
18
|
+
// `maxInstances` option in the function's options, e.g.
|
|
19
|
+
// `onRequest({ maxInstances: 5 }, (req, res) => { ... })`.
|
|
20
|
+
// NOTE: setGlobalOptions does not apply to functions using the v1 API. V1
|
|
21
|
+
// functions should each use functions.runWith({ maxInstances: 10 }) instead.
|
|
22
|
+
// In the v1 API, each function can only serve one request per container, so
|
|
23
|
+
// this will be the maximum concurrent request count.
|
|
24
|
+
setGlobalOptions({ maxInstances: 10 });
|
|
25
|
+
|
|
13
26
|
// Create and deploy your first functions
|
|
14
27
|
// https://firebase.google.com/docs/functions/get-started
|
|
15
28
|
|
|
@@ -3,8 +3,16 @@
|
|
|
3
3
|
# Deploy with `firebase deploy`
|
|
4
4
|
|
|
5
5
|
from firebase_functions import https_fn
|
|
6
|
+
from firebase_functions.options import set_global_options
|
|
6
7
|
from firebase_admin import initialize_app
|
|
7
8
|
|
|
9
|
+
# For cost control, you can set the maximum number of containers that can be
|
|
10
|
+
# running at the same time. This helps mitigate the impact of unexpected
|
|
11
|
+
# traffic spikes by instead downgrading performance. This limit is a per-function
|
|
12
|
+
# limit. You can override the limit for each function using the max_instances
|
|
13
|
+
# parameter in the decorator, e.g. @https_fn.on_request(max_instances=5).
|
|
14
|
+
set_global_options(max_instances=10)
|
|
15
|
+
|
|
8
16
|
# initialize_app()
|
|
9
17
|
#
|
|
10
18
|
#
|
|
@@ -7,12 +7,25 @@
|
|
|
7
7
|
* See a full list of supported triggers at https://firebase.google.com/docs/functions
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {setGlobalOptions} from "firebase-functions";
|
|
11
|
+
import {onRequest} from "firebase-functions/https";
|
|
11
12
|
import * as logger from "firebase-functions/logger";
|
|
12
13
|
|
|
13
14
|
// Start writing functions
|
|
14
15
|
// https://firebase.google.com/docs/functions/typescript
|
|
15
16
|
|
|
17
|
+
// For cost control, you can set the maximum number of containers that can be
|
|
18
|
+
// running at the same time. This helps mitigate the impact of unexpected
|
|
19
|
+
// traffic spikes by instead downgrading performance. This limit is a
|
|
20
|
+
// per-function limit. You can override the limit for each function using the
|
|
21
|
+
// `maxInstances` option in the function's options, e.g.
|
|
22
|
+
// `onRequest({ maxInstances: 5 }, (req, res) => { ... })`.
|
|
23
|
+
// NOTE: setGlobalOptions does not apply to functions using the v1 API. V1
|
|
24
|
+
// functions should each use functions.runWith({ maxInstances: 10 }) instead.
|
|
25
|
+
// In the v1 API, each function can only serve one request per container, so
|
|
26
|
+
// this will be the maximum concurrent request count.
|
|
27
|
+
setGlobalOptions({ maxInstances: 10 });
|
|
28
|
+
|
|
16
29
|
// export const helloWorld = onRequest((request, response) => {
|
|
17
30
|
// logger.info("Hello logs!", {structuredData: true});
|
|
18
31
|
// response.send("Hello from Firebase!");
|