firebase-tools 11.28.0 → 11.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.
- package/lib/commands/database-import.js +12 -1
- package/lib/commands/ext-configure.js +2 -2
- package/lib/commands/ext-install.js +2 -2
- package/lib/database/import.js +4 -6
- package/lib/deploy/functions/params.js +2 -2
- package/lib/deploy/functions/validate.js +4 -15
- package/lib/emulator/downloadableEmulators.js +3 -3
- package/lib/extensions/extensionsApi.js +9 -6
- package/lib/frameworks/index.js +12 -5
- package/lib/functions/secrets.js +4 -4
- package/lib/gcp/eventarc.js +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -16,12 +16,17 @@ const database_1 = require("../management/database");
|
|
|
16
16
|
const api_1 = require("../database/api");
|
|
17
17
|
const requireDatabaseInstance_1 = require("../requireDatabaseInstance");
|
|
18
18
|
const requirePermissions_1 = require("../requirePermissions");
|
|
19
|
+
const MAX_CHUNK_SIZE_MB = 10;
|
|
20
|
+
const MAX_PAYLOAD_SIZE_MB = 256;
|
|
21
|
+
const CONCURRENCY_LIMIT = 5;
|
|
19
22
|
exports.command = new command_1.Command("database:import <path> [infile]")
|
|
20
23
|
.description("non-atomically import the contents of a JSON file to the specified path in Realtime Database")
|
|
21
24
|
.withForce()
|
|
22
25
|
.option("--instance <instance>", "use the database <instance>.firebaseio.com (if omitted, use default database instance)")
|
|
23
26
|
.option("--disable-triggers", "suppress any Cloud functions triggered by this operation, default to true", true)
|
|
24
27
|
.option("--filter <dataPath>", "import only data at this path in the JSON file (if omitted, import entire file)")
|
|
28
|
+
.option("--chunk-size <mb>", "max chunk size in megabytes, default to 10 MB")
|
|
29
|
+
.option("--concurrency <val>", "concurrency limit, default to 5")
|
|
25
30
|
.before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.update"])
|
|
26
31
|
.before(requireDatabaseInstance_1.requireDatabaseInstance)
|
|
27
32
|
.before(database_1.populateInstanceDetails)
|
|
@@ -33,6 +38,10 @@ exports.command = new command_1.Command("database:import <path> [infile]")
|
|
|
33
38
|
if (!infile) {
|
|
34
39
|
throw new error_1.FirebaseError("No file supplied");
|
|
35
40
|
}
|
|
41
|
+
const chunkMegabytes = options.chunkSize ? parseInt(options.chunkSize, 10) : MAX_CHUNK_SIZE_MB;
|
|
42
|
+
if (chunkMegabytes > MAX_PAYLOAD_SIZE_MB) {
|
|
43
|
+
throw new error_1.FirebaseError("Max chunk size cannot exceed 256 MB");
|
|
44
|
+
}
|
|
36
45
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
37
46
|
const origin = (0, api_1.realtimeOriginOrEmulatorOrCustomUrl)(options.instanceDetails.databaseUrl);
|
|
38
47
|
const dbPath = utils.getDatabaseUrl(origin, options.instance, path);
|
|
@@ -51,7 +60,9 @@ exports.command = new command_1.Command("database:import <path> [infile]")
|
|
|
51
60
|
}
|
|
52
61
|
const inStream = fs.createReadStream(infile);
|
|
53
62
|
const dataPath = options.filter || "";
|
|
54
|
-
const
|
|
63
|
+
const chunkBytes = chunkMegabytes * 1024 * 1024;
|
|
64
|
+
const concurrency = options.concurrency ? parseInt(options.concurrency, 10) : CONCURRENCY_LIMIT;
|
|
65
|
+
const importer = new import_1.default(dbUrl, inStream, dataPath, chunkBytes, concurrency);
|
|
55
66
|
let responses;
|
|
56
67
|
try {
|
|
57
68
|
responses = await importer.execute();
|
|
@@ -32,7 +32,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
32
32
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
33
33
|
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
34
34
|
.action(async (instanceId, options) => {
|
|
35
|
-
var _a;
|
|
35
|
+
var _a, _b;
|
|
36
36
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
37
37
|
if (options.nonInteractive) {
|
|
38
38
|
throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead. ` +
|
|
@@ -57,7 +57,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
57
57
|
instanceId,
|
|
58
58
|
projectDir: config.projectDir,
|
|
59
59
|
});
|
|
60
|
-
const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params.concat((
|
|
60
|
+
const [immutableParams, tbdParams] = (0, functional_1.partition)(((_a = spec.params) !== null && _a !== void 0 ? _a : []).concat((_b = spec.systemParams) !== null && _b !== void 0 ? _b : []), (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
|
|
61
61
|
infoImmutableParams(immutableParams, oldParamValues);
|
|
62
62
|
paramHelper.setNewDefaults(tbdParams, oldParamValues);
|
|
63
63
|
const mutableParamsBindingOptions = await paramHelper.getParams({
|
|
@@ -121,7 +121,7 @@ async function infoExtensionVersion(args) {
|
|
|
121
121
|
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, args.extensionVersion);
|
|
122
122
|
}
|
|
123
123
|
async function installToManifest(options) {
|
|
124
|
-
var _a, _b;
|
|
124
|
+
var _a, _b, _c;
|
|
125
125
|
const { projectId, extensionName, extVersion, source, paramsEnvPath, nonInteractive, force } = options;
|
|
126
126
|
const isLocalSource = (0, extensionsHelper_1.isLocalPath)(extensionName);
|
|
127
127
|
const spec = (_a = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec) !== null && _a !== void 0 ? _a : source === null || source === void 0 ? void 0 : source.spec;
|
|
@@ -138,7 +138,7 @@ async function installToManifest(options) {
|
|
|
138
138
|
}
|
|
139
139
|
const paramBindingOptions = await paramHelper.getParams({
|
|
140
140
|
projectId,
|
|
141
|
-
paramSpecs: spec.params.concat((
|
|
141
|
+
paramSpecs: ((_b = spec.params) !== null && _b !== void 0 ? _b : []).concat((_c = spec.systemParams) !== null && _c !== void 0 ? _c : []),
|
|
142
142
|
nonInteractive,
|
|
143
143
|
paramsEnvPath,
|
|
144
144
|
instanceId,
|
package/lib/database/import.js
CHANGED
|
@@ -8,16 +8,14 @@ const StreamObject = require("stream-json/streamers/StreamObject");
|
|
|
8
8
|
const apiv2_1 = require("../apiv2");
|
|
9
9
|
const error_1 = require("../error");
|
|
10
10
|
const pLimit = require("p-limit");
|
|
11
|
-
const MAX_CHUNK_SIZE = 1024 * 1024 * 10;
|
|
12
|
-
const CONCURRENCY_LIMIT = 5;
|
|
13
11
|
class DatabaseImporter {
|
|
14
|
-
constructor(dbUrl, inStream, dataPath,
|
|
12
|
+
constructor(dbUrl, inStream, dataPath, chunkBytes, concurrency) {
|
|
15
13
|
this.dbUrl = dbUrl;
|
|
16
14
|
this.inStream = inStream;
|
|
17
15
|
this.dataPath = dataPath;
|
|
18
|
-
this.
|
|
19
|
-
this.limit = pLimit(CONCURRENCY_LIMIT);
|
|
16
|
+
this.chunkBytes = chunkBytes;
|
|
20
17
|
this.client = new apiv2_1.Client({ urlPrefix: dbUrl.origin, auth: true });
|
|
18
|
+
this.limit = pLimit(concurrency);
|
|
21
19
|
}
|
|
22
20
|
async execute() {
|
|
23
21
|
await this.checkLocationIsEmpty();
|
|
@@ -107,7 +105,7 @@ class DatabaseImporter {
|
|
|
107
105
|
chunks.push(child);
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
|
-
if (hasChunkedChild || size >= this.
|
|
108
|
+
if (hasChunkedChild || size >= this.chunkBytes) {
|
|
111
109
|
return { chunks, size };
|
|
112
110
|
}
|
|
113
111
|
else {
|
|
@@ -8,6 +8,7 @@ const functional_1 = require("../../functional");
|
|
|
8
8
|
const secretManager = require("../../gcp/secretManager");
|
|
9
9
|
const storage_1 = require("../../gcp/storage");
|
|
10
10
|
const cel_1 = require("./cel");
|
|
11
|
+
const secrets_1 = require("../../functions/secrets");
|
|
11
12
|
function dependenciesCEL(expr) {
|
|
12
13
|
const deps = [];
|
|
13
14
|
const paramCapture = /{{ params\.(\w+) }}/g;
|
|
@@ -221,8 +222,7 @@ async function handleSecret(secretParam, projectId) {
|
|
|
221
222
|
type: "password",
|
|
222
223
|
message: `This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as ${secretParam.name}. Enter a value for ${secretParam.label || secretParam.name}:`,
|
|
223
224
|
});
|
|
224
|
-
|
|
225
|
-
await secretManager.createSecret(projectId, secretParam.name, secretLabel);
|
|
225
|
+
await secretManager.createSecret(projectId, secretParam.name, (0, secrets_1.labels)());
|
|
226
226
|
await secretManager.addVersion(projectId, secretParam.name, secretValue);
|
|
227
227
|
return secretValue;
|
|
228
228
|
}
|
|
@@ -136,24 +136,13 @@ function functionsDirectoryExists(sourceDir, projectDir) {
|
|
|
136
136
|
}
|
|
137
137
|
exports.functionsDirectoryExists = functionsDirectoryExists;
|
|
138
138
|
function functionIdsAreValid(functions) {
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (invalidV1Ids.length !== 0) {
|
|
144
|
-
const msg = `${invalidV1Ids.map((f) => f.id).join(", ")} function name(s) can only contain letters, ` +
|
|
139
|
+
const functionName = /^[a-zA-Z][a-zA-Z0-9_-]{0,62}$/;
|
|
140
|
+
const invalidIds = functions.filter((fn) => !functionName.test(fn.id));
|
|
141
|
+
if (invalidIds.length !== 0) {
|
|
142
|
+
const msg = `${invalidIds.map((f) => f.id).join(", ")} function name(s) can only contain letters, ` +
|
|
145
143
|
`numbers, hyphens, and not exceed 62 characters in length`;
|
|
146
144
|
throw new error_1.FirebaseError(msg);
|
|
147
145
|
}
|
|
148
|
-
const v2FunctionName = /^[a-z][a-z0-9-]{0,62}$/;
|
|
149
|
-
const invalidV2Ids = functions.filter((fn) => {
|
|
150
|
-
return fn.platform === "gcfv2" && !v2FunctionName.test(fn.id);
|
|
151
|
-
});
|
|
152
|
-
if (invalidV2Ids.length !== 0) {
|
|
153
|
-
const msg = `${invalidV2Ids.map((f) => f.id).join(", ")} v2 function name(s) can only contain lower ` +
|
|
154
|
-
`case letters, numbers, hyphens, and not exceed 62 characters in length`;
|
|
155
|
-
throw new error_1.FirebaseError(msg);
|
|
156
|
-
}
|
|
157
146
|
}
|
|
158
147
|
exports.functionIdsAreValid = functionIdsAreValid;
|
|
159
148
|
async function secretsAreValid(projectId, wantBackend) {
|
|
@@ -23,9 +23,9 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
23
23
|
expectedChecksum: "311609538bd65666eb724ef47c2e6466",
|
|
24
24
|
},
|
|
25
25
|
firestore: {
|
|
26
|
-
version: "1.
|
|
27
|
-
expectedSize:
|
|
28
|
-
expectedChecksum: "
|
|
26
|
+
version: "1.17.2",
|
|
27
|
+
expectedSize: 64907894,
|
|
28
|
+
expectedChecksum: "bc56ccf2419be3242e7b53def0a51566",
|
|
29
29
|
},
|
|
30
30
|
storage: {
|
|
31
31
|
version: "1.1.3",
|
|
@@ -222,9 +222,10 @@ async function patchInstance(args) {
|
|
|
222
222
|
});
|
|
223
223
|
return pollRes;
|
|
224
224
|
}
|
|
225
|
-
function
|
|
225
|
+
function populateSpec(spec) {
|
|
226
|
+
var _a, _b;
|
|
226
227
|
if (spec) {
|
|
227
|
-
spec.resources
|
|
228
|
+
for (const r of spec.resources) {
|
|
228
229
|
try {
|
|
229
230
|
if (r.propertiesYaml) {
|
|
230
231
|
r.properties = yaml.safeLoad(r.propertiesYaml);
|
|
@@ -233,7 +234,9 @@ function populateResourceProperties(spec) {
|
|
|
233
234
|
catch (err) {
|
|
234
235
|
logger_1.logger.debug(`[ext] failed to parse resource properties yaml: ${err}`);
|
|
235
236
|
}
|
|
236
|
-
}
|
|
237
|
+
}
|
|
238
|
+
spec.params = (_a = spec.params) !== null && _a !== void 0 ? _a : [];
|
|
239
|
+
spec.systemParams = (_b = spec.systemParams) !== null && _b !== void 0 ? _b : [];
|
|
237
240
|
}
|
|
238
241
|
}
|
|
239
242
|
async function createSource(projectId, packageUri, extensionRoot) {
|
|
@@ -248,7 +251,7 @@ async function createSource(projectId, packageUri, extensionRoot) {
|
|
|
248
251
|
masterTimeout: 600000,
|
|
249
252
|
});
|
|
250
253
|
if (pollRes.spec) {
|
|
251
|
-
|
|
254
|
+
populateSpec(pollRes.spec);
|
|
252
255
|
}
|
|
253
256
|
return pollRes;
|
|
254
257
|
}
|
|
@@ -256,7 +259,7 @@ exports.createSource = createSource;
|
|
|
256
259
|
async function getSource(sourceName) {
|
|
257
260
|
const res = await apiClient.get(`/${sourceName}`);
|
|
258
261
|
if (res.body.spec) {
|
|
259
|
-
|
|
262
|
+
populateSpec(res.body.spec);
|
|
260
263
|
}
|
|
261
264
|
return res.body;
|
|
262
265
|
}
|
|
@@ -269,7 +272,7 @@ async function getExtensionVersion(extensionVersionRef) {
|
|
|
269
272
|
try {
|
|
270
273
|
const res = await apiClient.get(`/${refs.toExtensionVersionName(ref)}`);
|
|
271
274
|
if (res.body.spec) {
|
|
272
|
-
|
|
275
|
+
populateSpec(res.body.spec);
|
|
273
276
|
}
|
|
274
277
|
return res.body;
|
|
275
278
|
}
|
package/lib/frameworks/index.js
CHANGED
|
@@ -52,8 +52,15 @@ const DEFAULT_FIND_DEP_OPTIONS = {
|
|
|
52
52
|
};
|
|
53
53
|
exports.WebFrameworks = Object.fromEntries((0, fs_1.readdirSync)(__dirname)
|
|
54
54
|
.filter((path) => (0, fs_1.statSync)((0, path_1.join)(__dirname, path)).isDirectory())
|
|
55
|
-
.map((path) =>
|
|
56
|
-
|
|
55
|
+
.map((path) => {
|
|
56
|
+
try {
|
|
57
|
+
return [path, require((0, path_1.join)(__dirname, path))];
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.filter(([, obj]) => obj && obj.name && obj.discover && obj.build && obj.type !== undefined && obj.support));
|
|
57
64
|
function relativeRequire(dir, mod) {
|
|
58
65
|
try {
|
|
59
66
|
const path = require.resolve(mod, { paths: [dir] });
|
|
@@ -113,11 +120,11 @@ function scanDependencyTree(searchingFor, dependencies = {}) {
|
|
|
113
120
|
function getNodeModuleBin(name, cwd) {
|
|
114
121
|
var _a;
|
|
115
122
|
const cantFindExecutable = new error_1.FirebaseError(`Could not find the ${name} executable.`);
|
|
116
|
-
const
|
|
117
|
-
if (!
|
|
123
|
+
const npmRoot = (_a = (0, cross_spawn_1.sync)("npm", ["root"], { cwd }).stdout) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
124
|
+
if (!npmRoot) {
|
|
118
125
|
throw cantFindExecutable;
|
|
119
126
|
}
|
|
120
|
-
const path = (0, path_1.join)(
|
|
127
|
+
const path = (0, path_1.join)(npmRoot, ".bin", name);
|
|
121
128
|
if (!(0, fsutils_1.fileExistsSync)(path)) {
|
|
122
129
|
throw cantFindExecutable;
|
|
123
130
|
}
|
package/lib/functions/secrets.js
CHANGED
|
@@ -12,13 +12,13 @@ const env_1 = require("./env");
|
|
|
12
12
|
const logger_1 = require("../logger");
|
|
13
13
|
const api_1 = require("../api");
|
|
14
14
|
const functional_1 = require("../functional");
|
|
15
|
-
const
|
|
15
|
+
const FIREBASE_MANAGED = "firebase-managed";
|
|
16
16
|
function isFirebaseManaged(secret) {
|
|
17
|
-
return Object.keys(secret.labels || []).includes(
|
|
17
|
+
return Object.keys(secret.labels || []).includes(FIREBASE_MANAGED);
|
|
18
18
|
}
|
|
19
19
|
exports.isFirebaseManaged = isFirebaseManaged;
|
|
20
20
|
function labels() {
|
|
21
|
-
return { [
|
|
21
|
+
return { [FIREBASE_MANAGED]: "true" };
|
|
22
22
|
}
|
|
23
23
|
exports.labels = labels;
|
|
24
24
|
function toUpperSnakeCase(key) {
|
|
@@ -107,7 +107,7 @@ async function pruneSecrets(projectInfo, endpoints) {
|
|
|
107
107
|
const { projectId, projectNumber } = projectInfo;
|
|
108
108
|
const pruneKey = (name, version) => `${name}@${version}`;
|
|
109
109
|
const prunedSecrets = new Set();
|
|
110
|
-
const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${
|
|
110
|
+
const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${FIREBASE_MANAGED}=true`);
|
|
111
111
|
for (const secret of haveSecrets) {
|
|
112
112
|
const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `NOT state: DESTROYED`);
|
|
113
113
|
for (const version of versions) {
|
package/lib/gcp/eventarc.js
CHANGED
|
@@ -12,7 +12,7 @@ const client = new apiv2_1.Client({
|
|
|
12
12
|
apiVersion: exports.API_VERSION,
|
|
13
13
|
});
|
|
14
14
|
async function getChannel(name) {
|
|
15
|
-
const res = await client.get(name);
|
|
15
|
+
const res = await client.get(name, { resolveOnHTTPError: true });
|
|
16
16
|
if (res.status === 404) {
|
|
17
17
|
return undefined;
|
|
18
18
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.29.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "firebase-tools",
|
|
9
|
-
"version": "11.
|
|
9
|
+
"version": "11.29.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@google-cloud/pubsub": "^3.0.1",
|