firebase-tools 12.4.3 → 12.4.5
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/apiv2.js +4 -7
- package/lib/commands/database-instances-list.js +23 -46
- package/lib/commands/functions-secrets-set.js +18 -2
- package/lib/deploy/firestore/prepare.js +4 -4
- package/lib/deploy/functions/prepare.js +1 -1
- package/lib/deploy/functions/release/fabricator.js +6 -6
- package/lib/emulator/auth/operations.js +2 -2
- package/lib/emulator/functionsRuntimeWorker.js +6 -2
- package/lib/ensureApiEnabled.js +22 -1
- package/lib/firestore/fsConfig.js +8 -1
- package/lib/frameworks/frameworks.js +2 -0
- package/lib/frameworks/next/index.js +4 -9
- package/lib/frameworks/next/utils.js +57 -9
- package/lib/functions/secrets.js +36 -16
- package/lib/functionsShellCommandAction.js +7 -6
- package/lib/gcp/cloudfunctionsv2.js +7 -7
- package/lib/localFunction.js +183 -170
- package/lib/requireAuth.js +9 -0
- package/lib/utils.js +1 -9
- package/package.json +3 -3
- package/standalone/package.json +1 -1
- package/lib/gcp/firedata.js +0 -27
package/lib/apiv2.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Client = exports.setAccessToken = exports.setRefreshToken = void 0;
|
|
4
4
|
const url_1 = require("url");
|
|
5
5
|
const stream_1 = require("stream");
|
|
6
|
-
const
|
|
6
|
+
const proxy_agent_1 = require("proxy-agent");
|
|
7
7
|
const retry = require("retry");
|
|
8
8
|
const abort_controller_1 = require("abort-controller");
|
|
9
9
|
const node_fetch_1 = require("node-fetch");
|
|
@@ -179,12 +179,8 @@ class Client {
|
|
|
179
179
|
redirect: options.redirect,
|
|
180
180
|
compress: options.compress,
|
|
181
181
|
};
|
|
182
|
-
if (
|
|
183
|
-
fetchOptions.agent = new ProxyAgent(
|
|
184
|
-
}
|
|
185
|
-
const envProxy = proxyURIFromEnv();
|
|
186
|
-
if (envProxy) {
|
|
187
|
-
fetchOptions.agent = new ProxyAgent(envProxy);
|
|
182
|
+
if (proxyURIFromEnv()) {
|
|
183
|
+
fetchOptions.agent = new proxy_agent_1.ProxyAgent();
|
|
188
184
|
}
|
|
189
185
|
if (options.signal) {
|
|
190
186
|
fetchOptions.signal = options.signal;
|
|
@@ -233,6 +229,7 @@ class Client {
|
|
|
233
229
|
}
|
|
234
230
|
catch (thrown) {
|
|
235
231
|
const err = thrown instanceof Error ? thrown : new Error(thrown);
|
|
232
|
+
logger_1.logger.debug(`*** [apiv2] error from fetch(${fetchURL}, ${JSON.stringify(fetchOptions)}): ${err}`);
|
|
236
233
|
const isAbortError = err.name.includes("AbortError");
|
|
237
234
|
if (isAbortError) {
|
|
238
235
|
throw new error_1.FirebaseError(`Timeout reached making request to ${fetchURL}`, {
|
|
@@ -1,76 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.command = void 0;
|
|
4
|
-
const command_1 = require("../command");
|
|
5
4
|
const Table = require("cli-table");
|
|
5
|
+
const command_1 = require("../command");
|
|
6
6
|
const clc = require("colorette");
|
|
7
7
|
const ora = require("ora");
|
|
8
8
|
const logger_1 = require("../logger");
|
|
9
9
|
const requirePermissions_1 = require("../requirePermissions");
|
|
10
|
-
const projectUtils_1 = require("../projectUtils");
|
|
11
|
-
const firedata = require("../gcp/firedata");
|
|
12
10
|
const types_1 = require("../emulator/types");
|
|
13
11
|
const commandUtils_1 = require("../emulator/commandUtils");
|
|
14
12
|
const experiments = require("../experiments");
|
|
15
|
-
const
|
|
13
|
+
const projectUtils_1 = require("../projectUtils");
|
|
16
14
|
const database_1 = require("../management/database");
|
|
17
|
-
function logInstances(instances) {
|
|
18
|
-
if (instances.length === 0) {
|
|
19
|
-
logger_1.logger.info(clc.bold("No database instances found."));
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const tableHead = ["Database Instance Name", "Location", "Type", "State"];
|
|
23
|
-
const table = new Table({ head: tableHead, style: { head: ["green"] } });
|
|
24
|
-
instances.forEach((db) => {
|
|
25
|
-
table.push([db.name, db.location, db.type, db.state]);
|
|
26
|
-
});
|
|
27
|
-
logger_1.logger.info(table.toString());
|
|
28
|
-
}
|
|
29
|
-
function logInstancesCount(count = 0) {
|
|
30
|
-
if (count === 0) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
logger_1.logger.info("");
|
|
34
|
-
logger_1.logger.info(`${count} database instance(s) total.`);
|
|
35
|
-
}
|
|
36
15
|
exports.command = new command_1.Command("database:instances:list")
|
|
37
16
|
.description("list realtime database instances, optionally filtered by a specified location")
|
|
38
17
|
.before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.list"])
|
|
18
|
+
.option("-l, --location <location>", "(optional) location for the database instance, defaults to all regions")
|
|
39
19
|
.before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.DATABASE)
|
|
40
20
|
.action(async (options) => {
|
|
41
21
|
const location = (0, database_1.parseDatabaseLocation)(options.location, database_1.DatabaseLocation.ANY);
|
|
42
22
|
const spinner = ora("Preparing the list of your Firebase Realtime Database instances" +
|
|
43
23
|
`${location === database_1.DatabaseLocation.ANY ? "" : ` for location: ${location}`}`).start();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const projectId = (0, projectUtils_2.needProjectId)(options);
|
|
47
|
-
try {
|
|
48
|
-
instances = await (0, database_1.listDatabaseInstances)(projectId, location);
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
spinner.fail();
|
|
52
|
-
throw err;
|
|
53
|
-
}
|
|
54
|
-
spinner.succeed();
|
|
55
|
-
logInstances(instances);
|
|
56
|
-
logInstancesCount(instances.length);
|
|
57
|
-
return instances;
|
|
58
|
-
}
|
|
59
|
-
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
24
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
25
|
+
let instances = [];
|
|
60
26
|
try {
|
|
61
|
-
instances = await
|
|
27
|
+
instances = await (0, database_1.listDatabaseInstances)(projectId, location);
|
|
62
28
|
}
|
|
63
29
|
catch (err) {
|
|
64
30
|
spinner.fail();
|
|
65
31
|
throw err;
|
|
66
32
|
}
|
|
67
33
|
spinner.succeed();
|
|
68
|
-
|
|
69
|
-
logger_1.logger.info(
|
|
34
|
+
if (instances.length === 0) {
|
|
35
|
+
logger_1.logger.info(clc.bold("No database instances found."));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!experiments.isEnabled("rtdbmanagement")) {
|
|
39
|
+
for (const instance of instances) {
|
|
40
|
+
logger_1.logger.info(instance.name);
|
|
41
|
+
}
|
|
42
|
+
logger_1.logger.info(`Project ${options.project} has ${instances.length} database instances`);
|
|
43
|
+
return instances;
|
|
70
44
|
}
|
|
71
|
-
|
|
45
|
+
const tableHead = ["Database Instance Name", "Location", "Type", "State"];
|
|
46
|
+
const table = new Table({ head: tableHead, style: { head: ["green"] } });
|
|
47
|
+
for (const db of instances) {
|
|
48
|
+
table.push([db.name, db.location, db.type, db.state]);
|
|
49
|
+
}
|
|
50
|
+
logger_1.logger.info(table.toString());
|
|
51
|
+
logger_1.logger.info(`${instances.length} database instance(s) total.`);
|
|
72
52
|
return instances;
|
|
73
53
|
});
|
|
74
|
-
if (experiments.isEnabled("rtdbmanagement")) {
|
|
75
|
-
exports.command = exports.command.option("-l, --location <location>", "(optional) location for the database instance, defaults to us-central1");
|
|
76
|
-
}
|
|
@@ -12,9 +12,9 @@ const prompt_1 = require("../prompt");
|
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
const projectUtils_1 = require("../projectUtils");
|
|
14
14
|
const secretManager_1 = require("../gcp/secretManager");
|
|
15
|
+
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
15
16
|
const secrets = require("../functions/secrets");
|
|
16
17
|
const backend = require("../deploy/functions/backend");
|
|
17
|
-
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
18
18
|
exports.command = new command_1.Command("functions:secrets:set <KEY>")
|
|
19
19
|
.description("Create or update a secret for use in Cloud Functions for Firebase.")
|
|
20
20
|
.withForce("Automatically updates functions to use the new secret.")
|
|
@@ -58,7 +58,7 @@ exports.command = new command_1.Command("functions:secrets:set <KEY>")
|
|
|
58
58
|
logger_1.logger.debug("Customer set secrets before enabling functions. Exiting");
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
let haveBackend = await backend.existingBackend({ projectId });
|
|
62
62
|
const endpointsToUpdate = backend
|
|
63
63
|
.allEndpoints(haveBackend)
|
|
64
64
|
.filter((e) => secrets.inUse({ projectId, projectNumber }, secret, e));
|
|
@@ -87,4 +87,20 @@ exports.command = new command_1.Command("functions:secrets:set <KEY>")
|
|
|
87
87
|
return updated;
|
|
88
88
|
});
|
|
89
89
|
await Promise.all(updateOps);
|
|
90
|
+
haveBackend = await backend.existingBackend({ projectId }, true);
|
|
91
|
+
const staleEndpoints = backend.allEndpoints(backend.matchingBackend(haveBackend, (e) => {
|
|
92
|
+
const pInfo = { projectId, projectNumber };
|
|
93
|
+
return secrets.inUse(pInfo, secret, e) && !secrets.versionInUse(pInfo, secretVersion, e);
|
|
94
|
+
}));
|
|
95
|
+
if (staleEndpoints.length !== 0) {
|
|
96
|
+
(0, utils_1.logWarning)(`${staleEndpoints.length} functions are unexpectedly using old version of secret ${secret.name} still:\n\t` +
|
|
97
|
+
staleEndpoints.map((e) => `${e.id}(${e.region})`).join("\n\t"));
|
|
98
|
+
(0, utils_1.logBullet)("Please deploy your functions manually for the change to take effect by running:\n\t" +
|
|
99
|
+
clc.bold("firebase deploy --only functions"));
|
|
100
|
+
}
|
|
101
|
+
const secretsToPrune = (await secrets.pruneSecrets({ projectId, projectNumber }, backend.allEndpoints(haveBackend))).filter((sv) => sv.key === key);
|
|
102
|
+
(0, utils_1.logBullet)(`Removing secret versions: ${secretsToPrune
|
|
103
|
+
.map((sv) => sv.key + "[" + sv.version + "]")
|
|
104
|
+
.join(", ")}`);
|
|
105
|
+
await Promise.all(secretsToPrune.map((sv) => (0, secretManager_1.destroySecretVersion)(projectId, sv.secret, sv.version)));
|
|
90
106
|
});
|
|
@@ -25,11 +25,11 @@ function prepareIndexes(context, options, databaseId, indexesFileName) {
|
|
|
25
25
|
async function default_1(context, options) {
|
|
26
26
|
if (options.only) {
|
|
27
27
|
const targets = options.only.split(",");
|
|
28
|
-
const
|
|
29
|
-
const
|
|
28
|
+
const excludeRules = targets.indexOf("firestore:indexes") >= 0;
|
|
29
|
+
const excludeIndexes = targets.indexOf("firestore:rules") >= 0;
|
|
30
30
|
const onlyFirestore = targets.indexOf("firestore") >= 0;
|
|
31
|
-
context.firestoreIndexes =
|
|
32
|
-
context.firestoreRules =
|
|
31
|
+
context.firestoreIndexes = !excludeIndexes || onlyFirestore;
|
|
32
|
+
context.firestoreRules = !excludeRules || onlyFirestore;
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
35
|
context.firestoreIndexes = true;
|
|
@@ -295,7 +295,7 @@ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, fil
|
|
|
295
295
|
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
296
296
|
await runtimeDelegate.build();
|
|
297
297
|
const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
|
|
298
|
-
(0, utils_1.logLabeledBullet)("functions", `Loading and
|
|
298
|
+
(0, utils_1.logLabeledBullet)("functions", `Loading and analyzing source code for codebase ${codebase} to determine what to deploy`);
|
|
299
299
|
wantBuilds[codebase] = await runtimeDelegate.discoverBuild(runtimeConfig, Object.assign(Object.assign({}, firebaseEnvs), { GOOGLE_CLOUD_QUOTA_PROJECT: projectId }));
|
|
300
300
|
wantBuilds[codebase].runtime = codebaseConfig.runtime;
|
|
301
301
|
}
|
|
@@ -223,12 +223,12 @@ class Fabricator {
|
|
|
223
223
|
}
|
|
224
224
|
async createV2Function(endpoint) {
|
|
225
225
|
var _a, _b, _c, _d, _e;
|
|
226
|
-
const
|
|
227
|
-
if (!
|
|
226
|
+
const storageSource = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
227
|
+
if (!storageSource) {
|
|
228
228
|
logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
|
|
229
229
|
throw new Error("Precondition failed");
|
|
230
230
|
}
|
|
231
|
-
const apiFunction = gcfV2.functionFromEndpoint(endpoint,
|
|
231
|
+
const apiFunction = gcfV2.functionFromEndpoint(Object.assign(Object.assign({}, endpoint), { source: { storageSource } }));
|
|
232
232
|
const topic = (_b = apiFunction.eventTrigger) === null || _b === void 0 ? void 0 : _b.pubsubTopic;
|
|
233
233
|
if (topic) {
|
|
234
234
|
await this.executor
|
|
@@ -368,12 +368,12 @@ class Fabricator {
|
|
|
368
368
|
}
|
|
369
369
|
async updateV2Function(endpoint) {
|
|
370
370
|
var _a, _b, _c, _d;
|
|
371
|
-
const
|
|
372
|
-
if (!
|
|
371
|
+
const storageSource = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
372
|
+
if (!storageSource) {
|
|
373
373
|
logger_1.logger.debug("Precondition failed. Cannot update a GCFv2 function without storage");
|
|
374
374
|
throw new Error("Precondition failed");
|
|
375
375
|
}
|
|
376
|
-
const apiFunction = gcfV2.functionFromEndpoint(endpoint,
|
|
376
|
+
const apiFunction = gcfV2.functionFromEndpoint(Object.assign(Object.assign({}, endpoint), { source: { storageSource } }));
|
|
377
377
|
if ((_b = apiFunction.eventTrigger) === null || _b === void 0 ? void 0 : _b.pubsubTopic) {
|
|
378
378
|
delete apiFunction.eventTrigger.pubsubTopic;
|
|
379
379
|
}
|
|
@@ -146,13 +146,13 @@ async function signUp(state, reqBody, ctx) {
|
|
|
146
146
|
(0, errors_1.assert)(state.enableAnonymousUser, "ADMIN_ONLY_OPERATION");
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
-
if (
|
|
149
|
+
if (reqBody.email || (reqBody.email === "" && provider)) {
|
|
150
150
|
(0, errors_1.assert)((0, utils_1.isValidEmailAddress)(reqBody.email), "INVALID_EMAIL");
|
|
151
151
|
const email = (0, utils_1.canonicalizeEmailAddress)(reqBody.email);
|
|
152
152
|
(0, errors_1.assert)(!state.getUserByEmail(email), "EMAIL_EXISTS");
|
|
153
153
|
updates.email = email;
|
|
154
154
|
}
|
|
155
|
-
if (
|
|
155
|
+
if (reqBody.password) {
|
|
156
156
|
(0, errors_1.assert)(reqBody.password.length >= PASSWORD_MIN_LENGTH, `WEAK_PASSWORD : Password should be at least ${PASSWORD_MIN_LENGTH} characters`);
|
|
157
157
|
updates.salt = "fakeSalt" + (0, utils_1.randomId)(20);
|
|
158
158
|
updates.passwordHash = hashPassword(reqBody.password, updates.salt);
|
|
@@ -84,7 +84,7 @@ class RuntimeWorker {
|
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
request(req, resp, body) {
|
|
87
|
+
request(req, resp, body, debug) {
|
|
88
88
|
if (this.triggerKey !== FREE_WORKER_KEY) {
|
|
89
89
|
this.logInfo(`Beginning execution of "${this.triggerKey}"`);
|
|
90
90
|
}
|
|
@@ -124,6 +124,10 @@ class RuntimeWorker {
|
|
|
124
124
|
const piped = _resp.pipe(resp);
|
|
125
125
|
piped.on("finish", () => finishReq("finish"));
|
|
126
126
|
});
|
|
127
|
+
if (debug) {
|
|
128
|
+
proxy.setSocketKeepAlive(false);
|
|
129
|
+
proxy.setTimeout(0);
|
|
130
|
+
}
|
|
127
131
|
proxy.on("timeout", () => {
|
|
128
132
|
this.logger.log("ERROR", `Your function timed out after ~${this.timeoutSeconds}s. To configure this timeout, see
|
|
129
133
|
https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.`);
|
|
@@ -260,7 +264,7 @@ class RuntimeWorkerPool {
|
|
|
260
264
|
if (debug) {
|
|
261
265
|
await worker.sendDebugMsg(debug);
|
|
262
266
|
}
|
|
263
|
-
return worker.request(req, resp, body);
|
|
267
|
+
return worker.request(req, resp, body, !!debug);
|
|
264
268
|
}
|
|
265
269
|
getIdleWorker(triggerId) {
|
|
266
270
|
this.cleanUpWorkers();
|
package/lib/ensureApiEnabled.js
CHANGED
|
@@ -27,6 +27,10 @@ async function check(projectId, apiName, prefix, silent = false) {
|
|
|
27
27
|
return isEnabled;
|
|
28
28
|
}
|
|
29
29
|
exports.check = check;
|
|
30
|
+
function isPermissionError(e) {
|
|
31
|
+
var _a, _b, _c;
|
|
32
|
+
return ((_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.status) === "PERMISSION_DENIED";
|
|
33
|
+
}
|
|
30
34
|
async function enable(projectId, apiName) {
|
|
31
35
|
try {
|
|
32
36
|
await apiClient.post(`/projects/${projectId}/services/${apiName}:enable`, undefined, {
|
|
@@ -40,7 +44,24 @@ async function enable(projectId, apiName) {
|
|
|
40
44
|
|
|
41
45
|
https://console.firebase.google.com/project/${projectId}/usage/details`);
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
else if (isPermissionError(err)) {
|
|
48
|
+
const apiPermissionDeniedRegex = new RegExp(/Permission denied to enable service \[([.a-zA-Z]+)\]/);
|
|
49
|
+
const permissionsError = apiPermissionDeniedRegex.exec(err.message);
|
|
50
|
+
if (permissionsError && permissionsError[1]) {
|
|
51
|
+
const serviceUrl = permissionsError[1];
|
|
52
|
+
err.message = `Permissions denied enabling ${serviceUrl}.
|
|
53
|
+
Please ask a project owner to visit the following URL to enable this service:
|
|
54
|
+
|
|
55
|
+
https://console.cloud.google.com/apis/library/${serviceUrl}?project=${projectId}`;
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
44
65
|
}
|
|
45
66
|
}
|
|
46
67
|
async function pollCheckEnabled(projectId, apiName, prefix, silent, enablementRetries, pollRetries = 0) {
|
|
@@ -49,14 +49,21 @@ function getFirestoreConfig(projectId, options) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
else if (database) {
|
|
52
|
-
if (allDatabases) {
|
|
52
|
+
if (allDatabases || onlyDatabases.has(database)) {
|
|
53
53
|
results.push(c);
|
|
54
|
+
onlyDatabases.delete(database);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
else {
|
|
57
58
|
throw new error_1.FirebaseError('Must supply either "target" or "databaseId" in firestore config');
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
if (onlyDatabases.has("rules")) {
|
|
62
|
+
onlyDatabases.delete("rules");
|
|
63
|
+
}
|
|
64
|
+
if (onlyDatabases.has("indexes")) {
|
|
65
|
+
onlyDatabases.delete("indexes");
|
|
66
|
+
}
|
|
60
67
|
if (!allDatabases && onlyDatabases.size !== 0) {
|
|
61
68
|
throw new error_1.FirebaseError(`Could not find configurations in firebase.json for the following database targets: ${[
|
|
62
69
|
...onlyDatabases,
|
|
@@ -13,6 +13,7 @@ const svelte = require("./svelte");
|
|
|
13
13
|
const svelekit = require("./sveltekit");
|
|
14
14
|
const react = require("./react");
|
|
15
15
|
const vite = require("./vite");
|
|
16
|
+
const flutter = require("./flutter");
|
|
16
17
|
exports.WebFrameworks = {
|
|
17
18
|
angular,
|
|
18
19
|
astro,
|
|
@@ -26,4 +27,5 @@ exports.WebFrameworks = {
|
|
|
26
27
|
svelekit,
|
|
27
28
|
react,
|
|
28
29
|
vite,
|
|
30
|
+
flutter,
|
|
29
31
|
};
|
|
@@ -30,10 +30,6 @@ exports.support = "preview";
|
|
|
30
30
|
exports.type = 2;
|
|
31
31
|
exports.docsUrl = "https://firebase.google.com/docs/hosting/frameworks/nextjs";
|
|
32
32
|
const DEFAULT_NUMBER_OF_REASONS_TO_LIST = 5;
|
|
33
|
-
function getNextVersion(cwd) {
|
|
34
|
-
var _a;
|
|
35
|
-
return (_a = (0, utils_1.findDependency)("next", { cwd, depth: 0, omitDev: false })) === null || _a === void 0 ? void 0 : _a.version;
|
|
36
|
-
}
|
|
37
33
|
function getReactVersion(cwd) {
|
|
38
34
|
var _a;
|
|
39
35
|
return (_a = (0, utils_1.findDependency)("react-dom", { cwd, omitDev: false })) === null || _a === void 0 ? void 0 : _a.version;
|
|
@@ -41,7 +37,7 @@ function getReactVersion(cwd) {
|
|
|
41
37
|
async function discover(dir) {
|
|
42
38
|
if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
|
|
43
39
|
return;
|
|
44
|
-
if (!(await (0, fs_extra_1.pathExists)("next.config.js")) && !getNextVersion(dir))
|
|
40
|
+
if (!(await (0, fs_extra_1.pathExists)("next.config.js")) && !(0, utils_2.getNextVersion)(dir))
|
|
45
41
|
return;
|
|
46
42
|
return { mayWantBackend: true, publicDirectory: (0, path_1.join)(dir, PUBLIC_DIR) };
|
|
47
43
|
}
|
|
@@ -72,7 +68,7 @@ async function build(dir) {
|
|
|
72
68
|
if (await (0, utils_2.isUsingMiddleware)((0, path_1.join)(dir, distDir), false)) {
|
|
73
69
|
reasonsForBackend.add("middleware");
|
|
74
70
|
}
|
|
75
|
-
if (await (0, utils_2.isUsingImageOptimization)(
|
|
71
|
+
if (await (0, utils_2.isUsingImageOptimization)(dir, distDir)) {
|
|
76
72
|
reasonsForBackend.add(`Image Optimization`);
|
|
77
73
|
}
|
|
78
74
|
const prerenderManifest = await (0, utils_1.readJSON)((0, path_1.join)(dir, distDir, constants_2.PRERENDER_MANIFEST));
|
|
@@ -349,8 +345,7 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir) {
|
|
|
349
345
|
await (0, promises_1.mkdir)((0, path_1.join)(destDir, "public"));
|
|
350
346
|
await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, "public"), (0, path_1.join)(destDir, "public"));
|
|
351
347
|
}
|
|
352
|
-
if (
|
|
353
|
-
((0, utils_2.usesAppDirRouter)(sourceDir) || (await (0, utils_2.usesNextImage)(sourceDir, distDir)))) {
|
|
348
|
+
if (await (0, utils_2.isUsingImageOptimization)(sourceDir, distDir)) {
|
|
354
349
|
packageJson.dependencies["sharp"] = constants_1.SHARP_VERSION;
|
|
355
350
|
}
|
|
356
351
|
await (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir));
|
|
@@ -386,7 +381,7 @@ async function getConfig(dir) {
|
|
|
386
381
|
var _b;
|
|
387
382
|
let config = {};
|
|
388
383
|
if ((0, fs_1.existsSync)((0, path_1.join)(dir, "next.config.js"))) {
|
|
389
|
-
const version = getNextVersion(dir);
|
|
384
|
+
const version = (0, utils_2.getNextVersion)(dir);
|
|
390
385
|
if (!version)
|
|
391
386
|
throw new Error("Unable to find the next dep, try NPM installing?");
|
|
392
387
|
if ((0, semver_1.gte)(version, "12.0.0")) {
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
3
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
4
|
+
var m = o[Symbol.asyncIterator], i;
|
|
5
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
6
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
7
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
8
|
+
};
|
|
2
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
|
|
10
|
+
exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
|
|
4
11
|
const fs_1 = require("fs");
|
|
5
12
|
const fs_extra_1 = require("fs-extra");
|
|
6
13
|
const path_1 = require("path");
|
|
14
|
+
const promises_1 = require("fs/promises");
|
|
15
|
+
const glob_1 = require("glob");
|
|
16
|
+
const semver_1 = require("semver");
|
|
7
17
|
const utils_1 = require("../utils");
|
|
8
18
|
const constants_1 = require("./constants");
|
|
9
19
|
const fsutils_1 = require("../../fsutils");
|
|
10
|
-
const promises_1 = require("fs/promises");
|
|
11
20
|
exports.I18N_SOURCE = /\/:nextInternalLocale(\([^\)]+\))?/;
|
|
12
21
|
function cleanEscapedChars(path) {
|
|
13
22
|
return path.replace(/\\([(){}:+?*])/g, (a, b) => b);
|
|
@@ -84,20 +93,49 @@ async function isUsingMiddleware(dir, isDevMode) {
|
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
exports.isUsingMiddleware = isUsingMiddleware;
|
|
87
|
-
async function isUsingImageOptimization(
|
|
88
|
-
let
|
|
89
|
-
if (!isNextImageImported && isUsingAppDirectory(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
async function isUsingImageOptimization(projectDir, distDir) {
|
|
97
|
+
let isNextImageImported = await usesNextImage(projectDir, distDir);
|
|
98
|
+
if (!isNextImageImported && isUsingAppDirectory((0, path_1.join)(projectDir, distDir))) {
|
|
99
|
+
if (await isUsingNextImageInAppDirectory(projectDir, distDir)) {
|
|
100
|
+
isNextImageImported = true;
|
|
101
|
+
}
|
|
93
102
|
}
|
|
94
103
|
if (isNextImageImported) {
|
|
95
|
-
const imagesManifest = await (0, utils_1.readJSON)((0, path_1.join)(
|
|
104
|
+
const imagesManifest = await (0, utils_1.readJSON)((0, path_1.join)(projectDir, distDir, constants_1.IMAGES_MANIFEST));
|
|
96
105
|
return !imagesManifest.images.unoptimized;
|
|
97
106
|
}
|
|
98
107
|
return false;
|
|
99
108
|
}
|
|
100
109
|
exports.isUsingImageOptimization = isUsingImageOptimization;
|
|
110
|
+
async function isUsingNextImageInAppDirectory(projectDir, nextDir) {
|
|
111
|
+
var _a, e_1, _b, _c;
|
|
112
|
+
const { found: files } = new glob_1.Glob((0, path_1.join)(projectDir, nextDir, "server", "**", "*client-reference-manifest.js"));
|
|
113
|
+
try {
|
|
114
|
+
for (var _d = true, files_1 = __asyncValues(files), files_1_1; files_1_1 = await files_1.next(), _a = files_1_1.done, !_a;) {
|
|
115
|
+
_c = files_1_1.value;
|
|
116
|
+
_d = false;
|
|
117
|
+
try {
|
|
118
|
+
const filepath = _c;
|
|
119
|
+
const fileContents = await (0, promises_1.readFile)(filepath);
|
|
120
|
+
if (fileContents.includes("node_modules/next/dist/client/image")) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
_d = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
130
|
+
finally {
|
|
131
|
+
try {
|
|
132
|
+
if (!_d && !_a && (_b = files_1.return)) await _b.call(files_1);
|
|
133
|
+
}
|
|
134
|
+
finally { if (e_1) throw e_1.error; }
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
exports.isUsingNextImageInAppDirectory = isUsingNextImageInAppDirectory;
|
|
101
139
|
function isUsingAppDirectory(dir) {
|
|
102
140
|
const appPathRoutesManifestPath = (0, path_1.join)(dir, constants_1.APP_PATH_ROUTES_MANIFEST);
|
|
103
141
|
return (0, fsutils_1.fileExistsSync)(appPathRoutesManifestPath);
|
|
@@ -172,3 +210,13 @@ async function getBuildId(distDir) {
|
|
|
172
210
|
return buildId.toString();
|
|
173
211
|
}
|
|
174
212
|
exports.getBuildId = getBuildId;
|
|
213
|
+
function getNextVersion(cwd) {
|
|
214
|
+
const dependency = (0, utils_1.findDependency)("next", { cwd, depth: 0, omitDev: false });
|
|
215
|
+
if (!dependency)
|
|
216
|
+
return undefined;
|
|
217
|
+
const nextVersionSemver = (0, semver_1.coerce)(dependency.version);
|
|
218
|
+
if (!nextVersionSemver)
|
|
219
|
+
return dependency.version;
|
|
220
|
+
return nextVersionSemver.toString();
|
|
221
|
+
}
|
|
222
|
+
exports.getNextVersion = getNextVersion;
|
package/lib/functions/secrets.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.updateEndpointSecret = exports.pruneAndDestroySecrets = exports.pruneSecrets = exports.inUse = exports.getSecretVersions = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.ensureApi = exports.labels = exports.isFirebaseManaged = void 0;
|
|
3
|
+
exports.updateEndpointSecret = exports.pruneAndDestroySecrets = exports.pruneSecrets = exports.versionInUse = exports.inUse = exports.getSecretVersions = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.ensureApi = exports.labels = exports.isFirebaseManaged = void 0;
|
|
4
4
|
const utils = require("../utils");
|
|
5
5
|
const poller = require("../operation-poller");
|
|
6
|
-
const
|
|
6
|
+
const gcfV1 = require("../gcp/cloudfunctions");
|
|
7
|
+
const gcfV2 = require("../gcp/cloudfunctionsv2");
|
|
7
8
|
const ensureApiEnabled = require("../ensureApiEnabled");
|
|
9
|
+
const api_1 = require("../api");
|
|
8
10
|
const secretManager_1 = require("../gcp/secretManager");
|
|
9
11
|
const error_1 = require("../error");
|
|
10
12
|
const utils_1 = require("../utils");
|
|
11
13
|
const prompt_1 = require("../prompt");
|
|
12
14
|
const env_1 = require("./env");
|
|
13
15
|
const logger_1 = require("../logger");
|
|
14
|
-
const api_1 = require("../api");
|
|
15
16
|
const functional_1 = require("../functional");
|
|
16
17
|
const projectUtils_1 = require("../projectUtils");
|
|
17
18
|
const FIREBASE_MANAGED = "firebase-managed";
|
|
19
|
+
const gcfV1PollerOptions = {
|
|
20
|
+
apiOrigin: api_1.functionsOrigin,
|
|
21
|
+
apiVersion: "v1",
|
|
22
|
+
masterTimeout: 25 * 60 * 1000,
|
|
23
|
+
maxBackoff: 10000,
|
|
24
|
+
};
|
|
25
|
+
const gcfV2PollerOptions = {
|
|
26
|
+
apiOrigin: api_1.functionsV2Origin,
|
|
27
|
+
apiVersion: "v2",
|
|
28
|
+
masterTimeout: 25 * 60 * 1000,
|
|
29
|
+
maxBackoff: 10000,
|
|
30
|
+
};
|
|
18
31
|
function isFirebaseManaged(secret) {
|
|
19
32
|
return Object.keys(secret.labels || []).includes(FIREBASE_MANAGED);
|
|
20
33
|
}
|
|
@@ -110,6 +123,18 @@ function inUse(projectInfo, secret, endpoint) {
|
|
|
110
123
|
return false;
|
|
111
124
|
}
|
|
112
125
|
exports.inUse = inUse;
|
|
126
|
+
function versionInUse(projectInfo, sv, endpoint) {
|
|
127
|
+
const { projectId, projectNumber } = projectInfo;
|
|
128
|
+
for (const sev of of([endpoint])) {
|
|
129
|
+
if ((sev.projectId === projectId || sev.projectId === projectNumber) &&
|
|
130
|
+
sev.secret === sv.secret.name &&
|
|
131
|
+
sev.version === sv.versionId) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
exports.versionInUse = versionInUse;
|
|
113
138
|
async function pruneSecrets(projectInfo, endpoints) {
|
|
114
139
|
const { projectId, projectNumber } = projectInfo;
|
|
115
140
|
const pruneKey = (name, version) => `${name}@${version}`;
|
|
@@ -191,26 +216,21 @@ async function updateEndpointSecret(projectInfo, secretVersion, endpoint) {
|
|
|
191
216
|
updatedSevs.push(updatedSev);
|
|
192
217
|
}
|
|
193
218
|
if (endpoint.platform === "gcfv1") {
|
|
194
|
-
const fn =
|
|
195
|
-
const op = await
|
|
219
|
+
const fn = gcfV1.functionFromEndpoint(endpoint, "");
|
|
220
|
+
const op = await gcfV1.updateFunction({
|
|
196
221
|
name: fn.name,
|
|
197
222
|
runtime: fn.runtime,
|
|
198
223
|
entryPoint: fn.entryPoint,
|
|
199
224
|
secretEnvironmentVariables: updatedSevs,
|
|
200
225
|
});
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
apiVersion: gcf.API_VERSION,
|
|
204
|
-
masterTimeout: 25 * 60 * 1000,
|
|
205
|
-
maxBackoff: 10000,
|
|
206
|
-
pollerName: `update-${endpoint.region}-${endpoint.id}`,
|
|
207
|
-
operationResourceName: op.name,
|
|
208
|
-
};
|
|
209
|
-
const cfn = await poller.pollOperation(gcfV1PollerOptions);
|
|
210
|
-
return gcf.endpointFromFunction(cfn);
|
|
226
|
+
const cfn = await poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { operationResourceName: op.name }));
|
|
227
|
+
return gcfV1.endpointFromFunction(cfn);
|
|
211
228
|
}
|
|
212
229
|
else if (endpoint.platform === "gcfv2") {
|
|
213
|
-
|
|
230
|
+
const fn = gcfV2.functionFromEndpoint(endpoint);
|
|
231
|
+
const op = await gcfV2.updateFunction(Object.assign(Object.assign({}, fn), { serviceConfig: Object.assign(Object.assign({}, fn.serviceConfig), { secretEnvironmentVariables: updatedSevs }) }));
|
|
232
|
+
const cfn = await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { operationResourceName: op.name }));
|
|
233
|
+
return gcfV2.endpointFromFunction(cfn);
|
|
214
234
|
}
|
|
215
235
|
else {
|
|
216
236
|
(0, functional_1.assertExhaustive)(endpoint.platform);
|
|
@@ -6,16 +6,17 @@ const repl = require("repl");
|
|
|
6
6
|
const _ = require("lodash");
|
|
7
7
|
const request = require("request");
|
|
8
8
|
const util = require("util");
|
|
9
|
+
const shell = require("./emulator/functionsEmulatorShell");
|
|
10
|
+
const commandUtils = require("./emulator/commandUtils");
|
|
9
11
|
const functions_1 = require("./serve/functions");
|
|
10
|
-
const
|
|
12
|
+
const localFunction_1 = require("./localFunction");
|
|
11
13
|
const utils = require("./utils");
|
|
12
14
|
const logger_1 = require("./logger");
|
|
13
|
-
const shell = require("./emulator/functionsEmulatorShell");
|
|
14
|
-
const commandUtils = require("./emulator/commandUtils");
|
|
15
15
|
const types_1 = require("./emulator/types");
|
|
16
16
|
const hubClient_1 = require("./emulator/hubClient");
|
|
17
17
|
const portUtils_1 = require("./emulator/portUtils");
|
|
18
18
|
const constants_1 = require("./emulator/constants");
|
|
19
|
+
const projectUtils_1 = require("./projectUtils");
|
|
19
20
|
const serveFunctions = new functions_1.FunctionsServer();
|
|
20
21
|
const actionFunction = async (options) => {
|
|
21
22
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -26,7 +27,7 @@ const actionFunction = async (options) => {
|
|
|
26
27
|
if (options.inspectFunctions) {
|
|
27
28
|
debugPort = commandUtils.parseInspectionPort(options);
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
(0, projectUtils_1.needProjectId)(options);
|
|
30
31
|
const hubClient = new hubClient_1.EmulatorHubClient(options.project);
|
|
31
32
|
let remoteEmulators = {};
|
|
32
33
|
if (hubClient.foundHub()) {
|
|
@@ -73,9 +74,9 @@ const actionFunction = async (options) => {
|
|
|
73
74
|
const initializeContext = (context) => {
|
|
74
75
|
for (const trigger of emulator.triggers) {
|
|
75
76
|
if (emulator.emulatedFunctions.includes(trigger.id)) {
|
|
76
|
-
const localFunction = new
|
|
77
|
+
const localFunction = new localFunction_1.default(trigger, emulator.urls, emulator);
|
|
77
78
|
const triggerNameDotNotation = trigger.name.replace(/-/g, ".");
|
|
78
|
-
_.set(context, triggerNameDotNotation, localFunction.
|
|
79
|
+
_.set(context, triggerNameDotNotation, localFunction.makeFn());
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
context.help =
|
|
@@ -168,8 +168,8 @@ async function deleteFunction(cloudFunction) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
exports.deleteFunction = deleteFunction;
|
|
171
|
-
function functionFromEndpoint(endpoint
|
|
172
|
-
var _a, _b;
|
|
171
|
+
function functionFromEndpoint(endpoint) {
|
|
172
|
+
var _a, _b, _c;
|
|
173
173
|
if (endpoint.platform !== "gcfv2") {
|
|
174
174
|
throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
|
|
175
175
|
}
|
|
@@ -183,7 +183,7 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
183
183
|
runtime: endpoint.runtime,
|
|
184
184
|
entryPoint: endpoint.entryPoint,
|
|
185
185
|
source: {
|
|
186
|
-
storageSource: source,
|
|
186
|
+
storageSource: (_a = endpoint.source) === null || _a === void 0 ? void 0 : _a.storageSource,
|
|
187
187
|
},
|
|
188
188
|
environmentVariables: {},
|
|
189
189
|
},
|
|
@@ -213,7 +213,7 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
213
213
|
eventType: endpoint.eventTrigger.eventType,
|
|
214
214
|
};
|
|
215
215
|
if (gcfFunction.eventTrigger.eventType === v2_1.PUBSUB_PUBLISH_EVENT) {
|
|
216
|
-
if (!((
|
|
216
|
+
if (!((_b = endpoint.eventTrigger.eventFilters) === null || _b === void 0 ? void 0 : _b.topic)) {
|
|
217
217
|
throw new error_1.FirebaseError("Error: Pub/Sub event trigger is missing topic: " +
|
|
218
218
|
JSON.stringify(endpoint.eventTrigger, null, 2));
|
|
219
219
|
}
|
|
@@ -262,7 +262,7 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
262
262
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.CODEBASE_LABEL]: codebase });
|
|
263
263
|
}
|
|
264
264
|
else {
|
|
265
|
-
(
|
|
265
|
+
(_c = gcfFunction.labels) === null || _c === void 0 ? true : delete _c[constants_1.CODEBASE_LABEL];
|
|
266
266
|
}
|
|
267
267
|
if (endpoint.hash) {
|
|
268
268
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.HASH_LABEL]: endpoint.hash });
|
|
@@ -335,7 +335,7 @@ function endpointFromFunction(gcfFunction) {
|
|
|
335
335
|
}
|
|
336
336
|
const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
|
|
337
337
|
project,
|
|
338
|
-
region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime });
|
|
338
|
+
region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, source: gcfFunction.buildConfig.source });
|
|
339
339
|
if (gcfFunction.serviceConfig) {
|
|
340
340
|
proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "ingressSettings", "environmentVariables", "secretEnvironmentVariables", "timeoutSeconds", "uri");
|
|
341
341
|
proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccount", "serviceAccountEmail");
|
|
@@ -346,7 +346,7 @@ function endpointFromFunction(gcfFunction) {
|
|
|
346
346
|
}
|
|
347
347
|
const mem = mebibytes(prod);
|
|
348
348
|
if (!backend.isValidMemoryOption(mem)) {
|
|
349
|
-
logger_1.logger.
|
|
349
|
+
logger_1.logger.debug("Converting a function to an endpoint with an invalid memory option", mem);
|
|
350
350
|
}
|
|
351
351
|
return mem;
|
|
352
352
|
});
|
package/lib/localFunction.js
CHANGED
|
@@ -1,191 +1,204 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
this.controller = controller;
|
|
13
|
-
this.url = _.get(urls, this.id);
|
|
14
|
-
if (this.httpsTrigger) {
|
|
15
|
-
if (isCallable == "true") {
|
|
16
|
-
this.call = this._constructCallableFunc.bind(this);
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
this.call = request.defaults({
|
|
20
|
-
callback: this._requestCallBack,
|
|
21
|
-
baseUrl: this.url,
|
|
22
|
-
uri: "",
|
|
23
|
-
});
|
|
24
|
-
}
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const request = require("request");
|
|
4
|
+
const utils = require("./utils");
|
|
5
|
+
const encodeFirestoreValue_1 = require("./firestore/encodeFirestoreValue");
|
|
6
|
+
class LocalFunction {
|
|
7
|
+
constructor(trigger, urls, controller) {
|
|
8
|
+
this.trigger = trigger;
|
|
9
|
+
this.controller = controller;
|
|
10
|
+
this.paramWildcardRegex = new RegExp("{[^/{}]*}", "g");
|
|
11
|
+
this.url = urls[trigger.id];
|
|
25
12
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
};
|
|
36
|
-
LocalFunction.prototype._substituteParams = function (resource, params) {
|
|
37
|
-
var wildcardRegex = new RegExp("{[^/{}]*}", "g");
|
|
38
|
-
return resource.replace(wildcardRegex, function (wildcard) {
|
|
39
|
-
var wildcardNoBraces = wildcard.slice(1, -1);
|
|
40
|
-
var sub = _.get(params, wildcardNoBraces);
|
|
41
|
-
return sub || wildcardNoBraces + utils.randomInt(1, 9);
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
LocalFunction.prototype._constructCallableFunc = function (data, opts) {
|
|
45
|
-
opts = opts || {};
|
|
46
|
-
var headers = {};
|
|
47
|
-
if (opts.instanceIdToken) {
|
|
48
|
-
headers["Firebase-Instance-ID-Token"] = opts.instanceIdToken;
|
|
13
|
+
substituteParams(resource, params) {
|
|
14
|
+
if (!params) {
|
|
15
|
+
return resource;
|
|
16
|
+
}
|
|
17
|
+
return resource.replace(this.paramWildcardRegex, (wildcard) => {
|
|
18
|
+
const wildcardNoBraces = wildcard.slice(1, -1);
|
|
19
|
+
const sub = params === null || params === void 0 ? void 0 : params[wildcardNoBraces];
|
|
20
|
+
return sub || `${wildcardNoBraces}${utils.randomInt(1, 9)}`;
|
|
21
|
+
});
|
|
49
22
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
23
|
+
constructCallableFunc(data, opts) {
|
|
24
|
+
opts = opts || {};
|
|
25
|
+
const headers = {};
|
|
26
|
+
if (opts.instanceIdToken) {
|
|
27
|
+
headers["Firebase-Instance-ID-Token"] = opts.instanceIdToken;
|
|
28
|
+
}
|
|
29
|
+
return request.post({
|
|
30
|
+
callback: (...args) => this.requestCallBack(...args),
|
|
31
|
+
baseUrl: this.url,
|
|
32
|
+
uri: "",
|
|
33
|
+
body: { data },
|
|
34
|
+
json: true,
|
|
35
|
+
headers: headers,
|
|
36
|
+
});
|
|
62
37
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
case "ADMIN":
|
|
73
|
-
if (_.get(auth, "uid") || _.get(auth, "token")) {
|
|
74
|
-
throw new Error("authType and auth are incompatible.");
|
|
75
|
-
}
|
|
76
|
-
return { admin: true };
|
|
77
|
-
case "UNAUTHENTICATED":
|
|
78
|
-
if (_.get(auth, "uid") || _.get(auth, "token")) {
|
|
79
|
-
throw new Error("authType and auth are incompatible.");
|
|
80
|
-
}
|
|
81
|
-
return { admin: false };
|
|
82
|
-
default:
|
|
83
|
-
throw new Error("Unrecognized authType, valid values are: " + "ADMIN, USER, and UNAUTHENTICATED");
|
|
38
|
+
constructAuth(auth, authType) {
|
|
39
|
+
var _a, _b, _c;
|
|
40
|
+
if ((auth === null || auth === void 0 ? void 0 : auth.admin) || (auth === null || auth === void 0 ? void 0 : auth.variable)) {
|
|
41
|
+
return {
|
|
42
|
+
admin: auth.admin || false,
|
|
43
|
+
variable: auth.variable,
|
|
44
|
+
};
|
|
84
45
|
}
|
|
46
|
+
if (authType) {
|
|
47
|
+
switch (authType) {
|
|
48
|
+
case "USER":
|
|
49
|
+
return {
|
|
50
|
+
admin: false,
|
|
51
|
+
variable: {
|
|
52
|
+
uid: (_a = auth === null || auth === void 0 ? void 0 : auth.uid) !== null && _a !== void 0 ? _a : "",
|
|
53
|
+
token: (_b = auth === null || auth === void 0 ? void 0 : auth.token) !== null && _b !== void 0 ? _b : {},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
case "ADMIN":
|
|
57
|
+
if ((auth === null || auth === void 0 ? void 0 : auth.uid) || (auth === null || auth === void 0 ? void 0 : auth.token)) {
|
|
58
|
+
throw new Error("authType and auth are incompatible.");
|
|
59
|
+
}
|
|
60
|
+
return { admin: true };
|
|
61
|
+
case "UNAUTHENTICATED":
|
|
62
|
+
if ((auth === null || auth === void 0 ? void 0 : auth.uid) || (auth === null || auth === void 0 ? void 0 : auth.token)) {
|
|
63
|
+
throw new Error("authType and auth are incompatible.");
|
|
64
|
+
}
|
|
65
|
+
return { admin: false };
|
|
66
|
+
default:
|
|
67
|
+
throw new Error("Unrecognized authType, valid values are: " + "ADMIN, USER, and UNAUTHENTICATED");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (auth) {
|
|
71
|
+
return {
|
|
72
|
+
admin: false,
|
|
73
|
+
variable: {
|
|
74
|
+
uid: (_c = auth.uid) !== null && _c !== void 0 ? _c : "",
|
|
75
|
+
token: auth.token || {},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { admin: true };
|
|
85
80
|
}
|
|
86
|
-
|
|
81
|
+
makeFirestoreValue(input) {
|
|
82
|
+
if (typeof input === "undefined" ||
|
|
83
|
+
input === null ||
|
|
84
|
+
(typeof input === "object" && Object.keys(input).length === 0)) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
if (typeof input !== "object") {
|
|
88
|
+
throw new Error("Firestore data must be key-value pairs.");
|
|
89
|
+
}
|
|
90
|
+
const currentTime = new Date().toISOString();
|
|
87
91
|
return {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
+
fields: (0, encodeFirestoreValue_1.encodeFirestoreValue)(input),
|
|
93
|
+
createTime: currentTime,
|
|
94
|
+
updateTime: currentTime,
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (typeof input === "undefined" || _.isEmpty(input)) {
|
|
98
|
-
return {};
|
|
99
|
-
}
|
|
100
|
-
if (typeof input !== "object") {
|
|
101
|
-
throw new Error("Firestore data must be key-value pairs.");
|
|
102
|
-
}
|
|
103
|
-
var currentTime = new Date().toISOString();
|
|
104
|
-
return {
|
|
105
|
-
fields: encodeFirestoreValue(input),
|
|
106
|
-
createTime: currentTime,
|
|
107
|
-
updateTime: currentTime,
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
LocalFunction.prototype._requestCallBack = function (err, response, body) {
|
|
111
|
-
if (err) {
|
|
112
|
-
return console.warn("\nERROR SENDING REQUEST: " + err);
|
|
113
|
-
}
|
|
114
|
-
var status = response ? response.statusCode + ", " : "";
|
|
115
|
-
var bodyString = body;
|
|
116
|
-
if (typeof body === "string") {
|
|
117
|
-
try {
|
|
118
|
-
bodyString = JSON.stringify(JSON.parse(bodyString), null, 2);
|
|
97
|
+
requestCallBack(err, response, body) {
|
|
98
|
+
if (err) {
|
|
99
|
+
return console.warn("\nERROR SENDING REQUEST: " + err);
|
|
119
100
|
}
|
|
120
|
-
|
|
101
|
+
const status = response ? response.statusCode + ", " : "";
|
|
102
|
+
let bodyString = body;
|
|
103
|
+
if (typeof bodyString === "string") {
|
|
104
|
+
try {
|
|
105
|
+
bodyString = JSON.stringify(JSON.parse(bodyString), null, 2);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
bodyString = JSON.stringify(body, null, 2);
|
|
121
112
|
}
|
|
113
|
+
return console.log("\nRESPONSE RECEIVED FROM FUNCTION: " + status + bodyString);
|
|
122
114
|
}
|
|
123
|
-
|
|
124
|
-
|
|
115
|
+
isDatabaseFn(eventTrigger) {
|
|
116
|
+
return utils.getFunctionsEventProvider(eventTrigger.eventType) === "Database";
|
|
125
117
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
LocalFunction.prototype._call = function (data, opts) {
|
|
129
|
-
opts = opts || {};
|
|
130
|
-
var operationType;
|
|
131
|
-
var dataPayload;
|
|
132
|
-
if (this.httpsTrigger) {
|
|
133
|
-
this.controller.call(this.name, data || {});
|
|
118
|
+
isFirestoreFunc(eventTrigger) {
|
|
119
|
+
return utils.getFunctionsEventProvider(eventTrigger.eventType) === "Firestore";
|
|
134
120
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
121
|
+
triggerEvent(data, opts) {
|
|
122
|
+
opts = opts || {};
|
|
123
|
+
let operationType;
|
|
124
|
+
let dataPayload;
|
|
125
|
+
if (this.trigger.httpsTrigger) {
|
|
126
|
+
this.controller.call(this.trigger.name, data || {}, opts);
|
|
127
|
+
}
|
|
128
|
+
else if (this.trigger.eventTrigger) {
|
|
129
|
+
if (this.isDatabaseFn(this.trigger.eventTrigger)) {
|
|
130
|
+
operationType = utils.last(this.trigger.eventTrigger.eventType.split("."));
|
|
131
|
+
switch (operationType) {
|
|
132
|
+
case "create":
|
|
133
|
+
dataPayload = {
|
|
134
|
+
data: null,
|
|
135
|
+
delta: data,
|
|
136
|
+
};
|
|
137
|
+
break;
|
|
138
|
+
case "delete":
|
|
139
|
+
dataPayload = {
|
|
140
|
+
data: data,
|
|
141
|
+
delta: null,
|
|
142
|
+
};
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
dataPayload = {
|
|
146
|
+
data: data.before,
|
|
147
|
+
delta: data.after,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
opts.resource = this.substituteParams(this.trigger.eventTrigger.resource, opts.params);
|
|
151
|
+
opts.auth = this.constructAuth(opts.auth, opts.authType);
|
|
152
|
+
this.controller.call(this.trigger.name, dataPayload, opts);
|
|
153
|
+
}
|
|
154
|
+
else if (this.isFirestoreFunc(this.trigger.eventTrigger)) {
|
|
155
|
+
operationType = utils.last(this.trigger.eventTrigger.eventType.split("."));
|
|
156
|
+
switch (operationType) {
|
|
157
|
+
case "create":
|
|
158
|
+
dataPayload = {
|
|
159
|
+
value: this.makeFirestoreValue(data),
|
|
160
|
+
oldValue: {},
|
|
161
|
+
};
|
|
162
|
+
break;
|
|
163
|
+
case "delete":
|
|
164
|
+
dataPayload = {
|
|
165
|
+
value: {},
|
|
166
|
+
oldValue: this.makeFirestoreValue(data),
|
|
167
|
+
};
|
|
168
|
+
break;
|
|
169
|
+
default:
|
|
170
|
+
dataPayload = {
|
|
171
|
+
value: this.makeFirestoreValue(data.after),
|
|
172
|
+
oldValue: this.makeFirestoreValue(data.before),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
opts.resource = this.substituteParams(this.trigger.eventTrigger.resource, opts.params);
|
|
176
|
+
this.controller.call(this.trigger.name, dataPayload, opts);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
this.controller.call(this.trigger.name, data || {}, opts);
|
|
156
180
|
}
|
|
157
|
-
opts.resource = this._substituteParams(this.eventTrigger.resource, opts.params);
|
|
158
|
-
opts.auth = this._constructAuth(opts.auth, opts.authType);
|
|
159
|
-
this.controller.call(this.name, dataPayload, opts);
|
|
160
181
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
default:
|
|
177
|
-
dataPayload = {
|
|
178
|
-
value: this._makeFirestoreValue(data.after),
|
|
179
|
-
oldValue: this._makeFirestoreValue(data.before),
|
|
180
|
-
};
|
|
182
|
+
return console.log("Successfully invoked function.");
|
|
183
|
+
}
|
|
184
|
+
makeFn() {
|
|
185
|
+
var _a;
|
|
186
|
+
if (this.trigger.httpsTrigger) {
|
|
187
|
+
const isCallable = !!((_a = this.trigger.labels) === null || _a === void 0 ? void 0 : _a["deployment-callable"]);
|
|
188
|
+
if (isCallable) {
|
|
189
|
+
return (data, opt) => this.constructCallableFunc(data, opt);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
return request.defaults({
|
|
193
|
+
callback: (...args) => this.requestCallBack(...args),
|
|
194
|
+
baseUrl: this.url,
|
|
195
|
+
uri: "",
|
|
196
|
+
});
|
|
181
197
|
}
|
|
182
|
-
opts.resource = this._substituteParams(this.eventTrigger.resource, opts.params);
|
|
183
|
-
this.controller.call(this.name, dataPayload, opts);
|
|
184
198
|
}
|
|
185
199
|
else {
|
|
186
|
-
|
|
200
|
+
return (data, opt) => this.triggerEvent(data, opt);
|
|
187
201
|
}
|
|
188
202
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
module.exports = LocalFunction;
|
|
203
|
+
}
|
|
204
|
+
exports.default = LocalFunction;
|
package/lib/requireAuth.js
CHANGED
|
@@ -24,6 +24,14 @@ async function autoAuth(options, authScopes) {
|
|
|
24
24
|
const client = getAuthClient({ scopes: authScopes, projectId: options.project });
|
|
25
25
|
const token = await client.getAccessToken();
|
|
26
26
|
token !== null ? apiv2.setAccessToken(token) : false;
|
|
27
|
+
let clientEmail;
|
|
28
|
+
try {
|
|
29
|
+
const credentials = await client.getCredentials();
|
|
30
|
+
clientEmail = credentials.client_email;
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
logger_1.logger.debug(`Error getting account credentials.`);
|
|
34
|
+
}
|
|
27
35
|
if (!options.isVSCE && (0, monospace_1.isMonospaceEnv)()) {
|
|
28
36
|
await (0, monospace_1.selectProjectInMonospace)({
|
|
29
37
|
projectRoot: options.config.projectDir,
|
|
@@ -31,6 +39,7 @@ async function autoAuth(options, authScopes) {
|
|
|
31
39
|
isVSCE: options.isVSCE,
|
|
32
40
|
});
|
|
33
41
|
}
|
|
42
|
+
return clientEmail;
|
|
34
43
|
}
|
|
35
44
|
async function requireAuth(options) {
|
|
36
45
|
api.setScopes([scopes.CLOUD_PLATFORM, scopes.FIREBASE_PLATFORM]);
|
package/lib/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.
|
|
3
|
+
exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const url = require("url");
|
|
6
6
|
const clc = require("colorette");
|
|
@@ -375,14 +375,6 @@ function thirtyDaysFromNow() {
|
|
|
375
375
|
return new Date(Date.now() + THIRTY_DAYS_IN_MILLISECONDS);
|
|
376
376
|
}
|
|
377
377
|
exports.thirtyDaysFromNow = thirtyDaysFromNow;
|
|
378
|
-
function assertDefined(val, message) {
|
|
379
|
-
if (val === undefined || val === null) {
|
|
380
|
-
throw new assert_1.AssertionError({
|
|
381
|
-
message: message || `expected value to be defined but got "${val}"`,
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
exports.assertDefined = assertDefined;
|
|
386
378
|
function assertIsString(val, message) {
|
|
387
379
|
if (typeof val !== "string") {
|
|
388
380
|
throw new assert_1.AssertionError({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "12.4.
|
|
3
|
+
"version": "12.4.5",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -99,11 +99,11 @@
|
|
|
99
99
|
"p-limit": "^3.0.1",
|
|
100
100
|
"portfinder": "^1.0.32",
|
|
101
101
|
"progress": "^2.0.3",
|
|
102
|
-
"proxy-agent": "^
|
|
102
|
+
"proxy-agent": "^6.3.0",
|
|
103
103
|
"request": "^2.87.0",
|
|
104
104
|
"retry": "^0.13.1",
|
|
105
105
|
"rimraf": "^3.0.0",
|
|
106
|
-
"semver": "^5.
|
|
106
|
+
"semver": "^7.5.2",
|
|
107
107
|
"stream-chain": "^2.2.4",
|
|
108
108
|
"stream-json": "^1.7.3",
|
|
109
109
|
"strip-ansi": "^6.0.1",
|
package/standalone/package.json
CHANGED
package/lib/gcp/firedata.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listDatabaseInstances = void 0;
|
|
4
|
-
const api_1 = require("../api");
|
|
5
|
-
const apiv2_1 = require("../apiv2");
|
|
6
|
-
const logger_1 = require("../logger");
|
|
7
|
-
const utils = require("../utils");
|
|
8
|
-
function _handleErrorResponse(response) {
|
|
9
|
-
if (response.body && response.body.error) {
|
|
10
|
-
return utils.reject(response.body.error, { code: 2 });
|
|
11
|
-
}
|
|
12
|
-
logger_1.logger.debug("[firedata] error:", response.status, response.body);
|
|
13
|
-
return utils.reject("Unexpected error encountered with FireData.", {
|
|
14
|
-
code: 2,
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
async function listDatabaseInstances(projectNumber) {
|
|
18
|
-
const client = new apiv2_1.Client({ urlPrefix: api_1.firedataOrigin, apiVersion: "v1" });
|
|
19
|
-
const response = await client.get(`/projects/${projectNumber}/databases`, {
|
|
20
|
-
resolveOnHTTPError: true,
|
|
21
|
-
});
|
|
22
|
-
if (response.status === 200) {
|
|
23
|
-
return response.body.instance;
|
|
24
|
-
}
|
|
25
|
-
return _handleErrorResponse(response);
|
|
26
|
-
}
|
|
27
|
-
exports.listDatabaseInstances = listDatabaseInstances;
|