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.
@@ -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 importer = new import_1.default(dbUrl, inStream, dataPath);
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((_a = spec.systemParams) !== null && _a !== void 0 ? _a : []), (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
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((_b = spec.systemParams) !== null && _b !== void 0 ? _b : []),
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,
@@ -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, chunkSize = MAX_CHUNK_SIZE) {
12
+ constructor(dbUrl, inStream, dataPath, chunkBytes, concurrency) {
15
13
  this.dbUrl = dbUrl;
16
14
  this.inStream = inStream;
17
15
  this.dataPath = dataPath;
18
- this.chunkSize = chunkSize;
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.chunkSize) {
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
- const secretLabel = { "firebase-hosting-managed": "yes" };
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 v1FunctionName = /^[a-zA-Z][a-zA-Z0-9_-]{0,62}$/;
140
- const invalidV1Ids = functions.filter((fn) => {
141
- return fn.platform === "gcfv1" && !v1FunctionName.test(fn.id);
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.16.2",
27
- expectedSize: 64601019,
28
- expectedChecksum: "83f379a5b3d367503a860497fea3a936",
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 populateResourceProperties(spec) {
225
+ function populateSpec(spec) {
226
+ var _a, _b;
226
227
  if (spec) {
227
- spec.resources.forEach((r) => {
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
- populateResourceProperties(pollRes.spec);
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
- populateResourceProperties(res.body.spec);
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
- populateResourceProperties(res.body.spec);
275
+ populateSpec(res.body.spec);
273
276
  }
274
277
  return res.body;
275
278
  }
@@ -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) => [path, require((0, path_1.join)(__dirname, path))])
56
- .filter(([, obj]) => obj.name && obj.discover && obj.build && obj.type !== undefined && obj.support));
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 npmBin = (_a = (0, cross_spawn_1.sync)("npm", ["bin"], { cwd }).stdout) === null || _a === void 0 ? void 0 : _a.toString().trim();
117
- if (!npmBin) {
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)(npmBin, name);
127
+ const path = (0, path_1.join)(npmRoot, ".bin", name);
121
128
  if (!(0, fsutils_1.fileExistsSync)(path)) {
122
129
  throw cantFindExecutable;
123
130
  }
@@ -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 FIREBASE_MANGED = "firebase-managed";
15
+ const FIREBASE_MANAGED = "firebase-managed";
16
16
  function isFirebaseManaged(secret) {
17
- return Object.keys(secret.labels || []).includes(FIREBASE_MANGED);
17
+ return Object.keys(secret.labels || []).includes(FIREBASE_MANAGED);
18
18
  }
19
19
  exports.isFirebaseManaged = isFirebaseManaged;
20
20
  function labels() {
21
- return { [FIREBASE_MANGED]: "true" };
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.${FIREBASE_MANGED}=true`);
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) {
@@ -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
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "11.28.0",
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.28.0",
9
+ "version": "11.29.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@google-cloud/pubsub": "^3.0.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "11.28.0",
3
+ "version": "11.29.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {