firebase-tools 10.4.0 → 10.5.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.
Files changed (45) hide show
  1. package/lib/accountExporter.js +95 -84
  2. package/lib/bin/firebase.js +1 -1
  3. package/lib/commands/emulators-start.js +6 -1
  4. package/lib/commands/ext-configure.js +4 -4
  5. package/lib/commands/ext-dev-emulators-start.js +5 -1
  6. package/lib/commands/ext-install.js +5 -4
  7. package/lib/commands/ext-update.js +2 -1
  8. package/lib/commands/functions-config-export.js +3 -1
  9. package/lib/config.js +11 -4
  10. package/lib/deploy/functions/checkIam.js +44 -1
  11. package/lib/deploy/functions/deploy.js +3 -7
  12. package/lib/deploy/functions/prepare.js +7 -5
  13. package/lib/deploy/functions/prepareFunctionsUpload.js +7 -13
  14. package/lib/deploy/functions/release/fabricator.js +13 -1
  15. package/lib/deploy/functions/release/index.js +1 -1
  16. package/lib/deploy/functions/services/firebaseAlerts.js +1 -17
  17. package/lib/deploy/functions/services/index.js +2 -1
  18. package/lib/deploy/hosting/deploy.js +10 -0
  19. package/lib/emulator/auth/operations.js +21 -20
  20. package/lib/emulator/auth/state.js +79 -43
  21. package/lib/emulator/commandUtils.js +72 -2
  22. package/lib/emulator/controller.js +23 -8
  23. package/lib/emulator/downloadableEmulators.js +18 -11
  24. package/lib/emulator/functionsEmulator.js +8 -18
  25. package/lib/emulator/functionsEmulatorShared.js +27 -1
  26. package/lib/emulator/shared/request.js +19 -0
  27. package/lib/emulator/storage/apis/firebase.js +20 -28
  28. package/lib/emulator/storage/apis/gcloud.js +69 -57
  29. package/lib/emulator/storage/files.js +42 -49
  30. package/lib/emulator/storage/index.js +14 -2
  31. package/lib/emulator/storage/rules/utils.js +11 -3
  32. package/lib/extensions/askUserForParam.js +43 -15
  33. package/lib/extensions/extensionsHelper.js +11 -2
  34. package/lib/extensions/paramHelper.js +7 -3
  35. package/lib/functions/projectConfig.js +34 -0
  36. package/lib/init/features/functions/index.js +4 -2
  37. package/lib/init/features/hosting/index.js +32 -41
  38. package/lib/init/features/index.js +22 -12
  39. package/lib/init/index.js +28 -11
  40. package/lib/requirePermissions.js +4 -1
  41. package/lib/serve/functions.js +5 -5
  42. package/npm-shrinkwrap.json +2 -2
  43. package/package.json +1 -1
  44. package/schema/firebase-config.json +93 -36
  45. package/lib/emulator/storage/list.js +0 -18
@@ -9,12 +9,10 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.StorageLayer = exports.StoredFile = void 0;
11
11
  const fs_1 = require("fs");
12
- const list_1 = require("./list");
13
12
  const metadata_1 = require("./metadata");
14
13
  const errors_1 = require("./errors");
15
14
  const path = require("path");
16
15
  const fse = require("fs-extra");
17
- const cloudFunctions_1 = require("./cloudFunctions");
18
16
  const logger_1 = require("../../logger");
19
17
  const adminSdkConfig_1 = require("../adminSdkConfig");
20
18
  const types_1 = require("./rules/types");
@@ -39,17 +37,14 @@ class StoredFile {
39
37
  }
40
38
  exports.StoredFile = StoredFile;
41
39
  class StorageLayer {
42
- constructor(_projectId, _rulesValidator, _adminCredsValidator, _persistence) {
40
+ constructor(_projectId, _files, _buckets, _rulesValidator, _adminCredsValidator, _persistence, _cloudFunctions) {
43
41
  this._projectId = _projectId;
42
+ this._files = _files;
43
+ this._buckets = _buckets;
44
44
  this._rulesValidator = _rulesValidator;
45
45
  this._adminCredsValidator = _adminCredsValidator;
46
46
  this._persistence = _persistence;
47
- this.reset();
48
- this._cloudFunctions = new cloudFunctions_1.StorageCloudFunctions(this._projectId);
49
- }
50
- reset() {
51
- this._files = new Map();
52
- this._buckets = new Map();
47
+ this._cloudFunctions = _cloudFunctions;
53
48
  }
54
49
  createBucket(id) {
55
50
  if (!this._buckets.has(id)) {
@@ -66,11 +61,11 @@ class StorageLayer {
66
61
  }
67
62
  return [...this._buckets.values()];
68
63
  }
69
- async handleGetObject(request, skipAuth = false) {
64
+ async getObject(request) {
70
65
  var _a;
71
66
  const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
72
67
  const hasValidDownloadToken = ((metadata === null || metadata === void 0 ? void 0 : metadata.downloadTokens) || []).includes((_a = request.downloadToken) !== null && _a !== void 0 ? _a : "");
73
- let authorized = skipAuth || hasValidDownloadToken;
68
+ let authorized = hasValidDownloadToken;
74
69
  if (!authorized) {
75
70
  authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.GET, { before: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, request.authorization);
76
71
  }
@@ -99,10 +94,9 @@ class StorageLayer {
99
94
  }
100
95
  return undefined;
101
96
  }
102
- async handleDeleteObject(request, skipAuth = false) {
97
+ async deleteObject(request) {
103
98
  const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
104
- const authorized = skipAuth ||
105
- (await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, request.authorization));
99
+ const authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, request.authorization);
106
100
  if (!authorized) {
107
101
  throw new errors_1.ForbiddenError();
108
102
  }
@@ -131,13 +125,12 @@ class StorageLayer {
131
125
  return true;
132
126
  }
133
127
  }
134
- async handleUpdateObjectMetadata(request, skipAuth = false) {
128
+ async updateObjectMetadata(request) {
135
129
  const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
136
- const authorized = skipAuth ||
137
- (await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.UPDATE, {
138
- before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
139
- after: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(request.metadata),
140
- }, request.authorization));
130
+ const authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), request.bucketId, types_1.RulesetOperationMethod.UPDATE, {
131
+ before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
132
+ after: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(request.metadata),
133
+ }, request.authorization);
141
134
  if (!authorized) {
142
135
  throw new errors_1.ForbiddenError();
143
136
  }
@@ -147,7 +140,7 @@ class StorageLayer {
147
140
  storedMetadata.update(request.metadata);
148
141
  return storedMetadata;
149
142
  }
150
- async handleUploadObject(upload, skipAuth = false) {
143
+ async uploadObject(upload) {
151
144
  if (upload.status !== upload_1.UploadStatus.FINISHED) {
152
145
  throw new Error(`Unexpected upload status encountered: ${upload.status}.`);
153
146
  }
@@ -162,8 +155,7 @@ class StorageLayer {
162
155
  cacheControl: upload.metadata.cacheControl,
163
156
  customMetadata: upload.metadata.metadata,
164
157
  }, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
165
- const authorized = skipAuth ||
166
- (await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization));
158
+ const authorized = await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization);
167
159
  if (!authorized) {
168
160
  this._persistence.deleteFile(upload.path);
169
161
  throw new errors_1.ForbiddenError();
@@ -174,17 +166,24 @@ class StorageLayer {
174
166
  this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(metadata));
175
167
  return metadata;
176
168
  }
177
- copyFile(sourceFile, destinationBucket, destinationObject, incomingMetadata) {
178
- const filePath = this.path(destinationBucket, destinationObject);
179
- this._persistence.deleteFile(filePath, true);
180
- const bytes = this.getBytes(sourceFile.bucket, sourceFile.name);
181
- this._persistence.appendBytes(filePath, bytes);
182
- const newMetadata = Object.assign(Object.assign(Object.assign({}, sourceFile), { metadata: sourceFile.customMetadata }), incomingMetadata);
183
- if (sourceFile.downloadTokens.length &&
169
+ copyObject({ sourceBucket, sourceObject, destinationBucket, destinationObject, incomingMetadata, authorization, }) {
170
+ if (!this._adminCredsValidator.validate(authorization)) {
171
+ throw new errors_1.ForbiddenError();
172
+ }
173
+ const sourceMetadata = this.getMetadata(sourceBucket, sourceObject);
174
+ if (!sourceMetadata) {
175
+ throw new errors_1.NotFoundError();
176
+ }
177
+ const sourceBytes = this.getBytes(sourceBucket, sourceObject);
178
+ const destinationFilePath = this.path(destinationBucket, destinationObject);
179
+ this._persistence.deleteFile(destinationFilePath, true);
180
+ this._persistence.appendBytes(destinationFilePath, sourceBytes);
181
+ const newMetadata = Object.assign(Object.assign(Object.assign({}, sourceMetadata), { metadata: sourceMetadata.customMetadata }), incomingMetadata);
182
+ if (sourceMetadata.downloadTokens.length &&
184
183
  !((incomingMetadata === null || incomingMetadata === void 0 ? void 0 : incomingMetadata.metadata) && Object.keys(incomingMetadata === null || incomingMetadata === void 0 ? void 0 : incomingMetadata.metadata).length)) {
185
184
  if (!newMetadata.metadata)
186
185
  newMetadata.metadata = {};
187
- newMetadata.metadata.firebaseStorageDownloadTokens = sourceFile.downloadTokens.join(",");
186
+ newMetadata.metadata.firebaseStorageDownloadTokens = sourceMetadata.downloadTokens.join(",");
188
187
  }
189
188
  if (newMetadata.metadata) {
190
189
  for (const [k, v] of Object.entries(newMetadata.metadata)) {
@@ -201,27 +200,23 @@ class StorageLayer {
201
200
  contentLanguage: newMetadata.contentLanguage,
202
201
  cacheControl: newMetadata.cacheControl,
203
202
  customMetadata: newMetadata.metadata,
204
- }, this._cloudFunctions, bytes, incomingMetadata);
205
- const file = new StoredFile(copiedFileMetadata, this._persistence.getDiskPath(filePath));
206
- this._files.set(filePath, file);
203
+ }, this._cloudFunctions, sourceBytes, incomingMetadata);
204
+ const file = new StoredFile(copiedFileMetadata, this._persistence.getDiskPath(destinationFilePath));
205
+ this._files.set(destinationFilePath, file);
207
206
  this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
208
207
  return file.metadata;
209
208
  }
210
- async handleListObjects(request, skipAuth = false) {
211
- var _a, _b, _c;
212
- const authorized = skipAuth ||
213
- (await this._rulesValidator.validate(["b", request.bucketId, "o", request.prefix].join("/"), request.bucketId, types_1.RulesetOperationMethod.LIST, {}, request.authorization));
209
+ async listObjects(request) {
210
+ var _a;
211
+ const { bucketId, prefix, delimiter, pageToken, authorization } = request;
212
+ const authorized = await this._rulesValidator.validate(["b", bucketId, "o", prefix].join("/"), bucketId, types_1.RulesetOperationMethod.LIST, {}, authorization);
214
213
  if (!authorized) {
215
214
  throw new errors_1.ForbiddenError();
216
215
  }
217
- const itemsResults = this.listItems(request.bucketId, request.prefix, request.delimiter, request.pageToken, request.maxResults);
218
- return new list_1.ListResponse((_a = itemsResults.prefixes) !== null && _a !== void 0 ? _a : [], (_c = (_b = itemsResults.items) === null || _b === void 0 ? void 0 : _b.map((i) => new list_1.ListItem(i.name, i.bucket))) !== null && _c !== void 0 ? _c : [], itemsResults.nextPageToken);
219
- }
220
- listItems(bucket, prefix, delimiter, pageToken, maxResults) {
221
216
  let items = [];
222
217
  const prefixes = new Set();
223
218
  for (const [, file] of this._files) {
224
- if (file.metadata.bucket !== bucket) {
219
+ if (file.metadata.bucket !== bucketId) {
225
220
  continue;
226
221
  }
227
222
  const name = file.metadata.name;
@@ -258,9 +253,7 @@ class StorageLayer {
258
253
  items = items.slice(idx);
259
254
  }
260
255
  }
261
- if (!maxResults) {
262
- maxResults = 1000;
263
- }
256
+ const maxResults = (_a = request.maxResults) !== null && _a !== void 0 ? _a : 1000;
264
257
  let nextPageToken = undefined;
265
258
  if (items.length > maxResults) {
266
259
  nextPageToken = items[maxResults].name;
@@ -269,10 +262,10 @@ class StorageLayer {
269
262
  return {
270
263
  nextPageToken,
271
264
  prefixes: prefixes.size > 0 ? [...prefixes].sort() : undefined,
272
- items: items.length > 0 ? items.map((item) => new metadata_1.CloudStorageObjectMetadata(item)) : undefined,
265
+ items: items.length > 0 ? items : undefined,
273
266
  };
274
267
  }
275
- handleCreateDownloadToken(request) {
268
+ createDownloadToken(request) {
276
269
  if (!this._adminCredsValidator.validate(request.authorization)) {
277
270
  throw new errors_1.ForbiddenError();
278
271
  }
@@ -283,7 +276,7 @@ class StorageLayer {
283
276
  metadata.addDownloadToken();
284
277
  return metadata;
285
278
  }
286
- handleDeleteDownloadToken(request) {
279
+ deleteDownloadToken(request) {
287
280
  if (!this._adminCredsValidator.validate(request.authorization)) {
288
281
  throw new errors_1.ForbiddenError();
289
282
  }
@@ -13,19 +13,30 @@ const runtime_1 = require("./rules/runtime");
13
13
  const utils_1 = require("./rules/utils");
14
14
  const persistence_1 = require("./persistence");
15
15
  const upload_1 = require("./upload");
16
+ const cloudFunctions_1 = require("./cloudFunctions");
16
17
  class StorageEmulator {
17
18
  constructor(args) {
18
19
  this.args = args;
19
20
  this._logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE);
21
+ this._files = new Map();
22
+ this._buckets = new Map();
20
23
  this._rulesRuntime = new runtime_1.StorageRulesRuntime();
21
24
  this._rulesManager = (0, manager_1.createStorageRulesManager)(this.args.rules, this._rulesRuntime);
25
+ this._cloudFunctions = new cloudFunctions_1.StorageCloudFunctions(args.projectId);
22
26
  this._persistence = new persistence_1.Persistence(this.getPersistenceTmpDir());
23
- this._storageLayer = new files_1.StorageLayer(args.projectId, (0, utils_1.getRulesValidator)((resource) => this._rulesManager.getRuleset(resource)), (0, utils_1.getAdminCredentialValidator)(), this._persistence);
24
27
  this._uploadService = new upload_1.UploadService(this._persistence);
28
+ const createStorageLayer = (rulesValidator) => {
29
+ return new files_1.StorageLayer(args.projectId, this._files, this._buckets, rulesValidator, (0, utils_1.getAdminCredentialValidator)(), this._persistence, this._cloudFunctions);
30
+ };
31
+ this._storageLayer = createStorageLayer((0, utils_1.getFirebaseRulesValidator)((resource) => this._rulesManager.getRuleset(resource)));
32
+ this._adminStorageLayer = createStorageLayer((0, utils_1.getAdminOnlyFirebaseRulesValidator)());
25
33
  }
26
34
  get storageLayer() {
27
35
  return this._storageLayer;
28
36
  }
37
+ get adminStorageLayer() {
38
+ return this._adminStorageLayer;
39
+ }
29
40
  get uploadService() {
30
41
  return this._uploadService;
31
42
  }
@@ -36,7 +47,8 @@ class StorageEmulator {
36
47
  return this._logger;
37
48
  }
38
49
  reset() {
39
- this._storageLayer.reset();
50
+ this._files.clear();
51
+ this._buckets.clear();
40
52
  this._persistence.reset(this.getPersistenceTmpDir());
41
53
  this._uploadService.reset();
42
54
  }
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isPermitted = exports.getAdminCredentialValidator = exports.getRulesValidator = void 0;
3
+ exports.isPermitted = exports.getAdminCredentialValidator = exports.getAdminOnlyFirebaseRulesValidator = exports.getFirebaseRulesValidator = void 0;
4
4
  const emulatorLogger_1 = require("../../emulatorLogger");
5
5
  const types_1 = require("../../types");
6
- function getRulesValidator(rulesetProvider) {
6
+ function getFirebaseRulesValidator(rulesetProvider) {
7
7
  return {
8
8
  validate: async (path, bucketId, method, variableOverrides, authorization) => {
9
9
  return await isPermitted({
@@ -16,7 +16,15 @@ function getRulesValidator(rulesetProvider) {
16
16
  },
17
17
  };
18
18
  }
19
- exports.getRulesValidator = getRulesValidator;
19
+ exports.getFirebaseRulesValidator = getFirebaseRulesValidator;
20
+ function getAdminOnlyFirebaseRulesValidator() {
21
+ return {
22
+ validate: (_path, _bucketId, _method, _variableOverrides, _authorization) => {
23
+ return Promise.resolve(true);
24
+ },
25
+ };
26
+ }
27
+ exports.getAdminOnlyFirebaseRulesValidator = getAdminOnlyFirebaseRulesValidator;
20
28
  function getAdminCredentialValidator() {
21
29
  return { validate: isValidAdminCredentials };
22
30
  }
@@ -12,6 +12,7 @@ const utils_1 = require("./utils");
12
12
  const logger_1 = require("../logger");
13
13
  const prompt_1 = require("../prompt");
14
14
  const utils = require("../utils");
15
+ const projectUtils_1 = require("../projectUtils");
15
16
  var SecretLocation;
16
17
  (function (SecretLocation) {
17
18
  SecretLocation[SecretLocation["CLOUD"] = 1] = "CLOUD";
@@ -60,21 +61,21 @@ function checkResponse(response, spec) {
60
61
  return valid;
61
62
  }
62
63
  exports.checkResponse = checkResponse;
63
- async function ask(projectId, instanceId, paramSpecs, firebaseProjectParams, reconfiguring) {
64
- if (_.isEmpty(paramSpecs)) {
64
+ async function ask(args) {
65
+ if (_.isEmpty(args.paramSpecs)) {
65
66
  logger_1.logger.debug("No params were specified for this extension.");
66
67
  return {};
67
68
  }
68
69
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, "answer the questions below to configure your extension:");
69
- const substituted = (0, extensionsHelper_1.substituteParams)(paramSpecs, firebaseProjectParams);
70
+ const substituted = (0, extensionsHelper_1.substituteParams)(args.paramSpecs, args.firebaseProjectParams);
70
71
  const result = {};
71
72
  const promises = _.map(substituted, (paramSpec) => {
72
73
  return async () => {
73
74
  result[paramSpec.param] = await askForParam({
74
- projectId,
75
- instanceId,
76
- paramSpec,
77
- reconfiguring,
75
+ projectId: args.projectId,
76
+ instanceId: args.instanceId,
77
+ paramSpec: paramSpec,
78
+ reconfiguring: args.reconfiguring,
78
79
  });
79
80
  };
80
81
  });
@@ -129,13 +130,14 @@ async function askForParam(args) {
129
130
  valid = checkResponse(response, paramSpec);
130
131
  break;
131
132
  case extensionsApi_1.ParamType.SECRET:
132
- while (!secretLocations.length) {
133
- secretLocations = await promptSecretLocations();
134
- }
133
+ do {
134
+ secretLocations = await promptSecretLocations(paramSpec);
135
+ } while (!isValidSecretLocations(secretLocations, paramSpec));
135
136
  if (secretLocations.includes(SecretLocation.CLOUD.toString())) {
137
+ const projectId = (0, projectUtils_1.needProjectId)({ projectId: args.projectId });
136
138
  response = args.reconfiguring
137
- ? await promptReconfigureSecret(args.projectId, args.instanceId, paramSpec)
138
- : await promptCreateSecret(args.projectId, args.instanceId, paramSpec);
139
+ ? await promptReconfigureSecret(projectId, args.instanceId, paramSpec)
140
+ : await promptCreateSecret(projectId, args.instanceId, paramSpec);
139
141
  }
140
142
  if (secretLocations.includes(SecretLocation.LOCAL.toString())) {
141
143
  responseForLocal = await promptLocalSecret(args.instanceId, paramSpec);
@@ -155,14 +157,40 @@ async function askForParam(args) {
155
157
  return Object.assign({ baseValue: response }, (responseForLocal ? { local: responseForLocal } : {}));
156
158
  }
157
159
  exports.askForParam = askForParam;
158
- async function promptSecretLocations() {
160
+ function isValidSecretLocations(secretLocations, paramSpec) {
161
+ if (paramSpec.required) {
162
+ return !!secretLocations.length;
163
+ }
164
+ return true;
165
+ }
166
+ async function promptSecretLocations(paramSpec) {
167
+ if (paramSpec.required) {
168
+ return await (0, prompt_1.promptOnce)({
169
+ name: "input",
170
+ type: "checkbox",
171
+ message: "Where would you like to store your secrets? You must select at least one value",
172
+ choices: [
173
+ {
174
+ checked: true,
175
+ name: "Google Cloud Secret Manager",
176
+ value: SecretLocation.CLOUD.toString(),
177
+ },
178
+ {
179
+ checked: false,
180
+ name: "Local file (Only used by Firebase Emulator)",
181
+ value: SecretLocation.LOCAL.toString(),
182
+ },
183
+ ],
184
+ });
185
+ }
159
186
  return await (0, prompt_1.promptOnce)({
160
187
  name: "input",
161
188
  type: "checkbox",
162
- message: "Where would you like to store your secrets? You must select at least one value",
189
+ message: "Where would you like to store your secrets? " +
190
+ "If you don't want to set this optional secret, leave both options unselected to skip it",
163
191
  choices: [
164
192
  {
165
- checked: true,
193
+ checked: false,
166
194
  name: "Google Cloud Secret Manager",
167
195
  value: SecretLocation.CLOUD.toString(),
168
196
  },
@@ -80,6 +80,9 @@ function getDBInstanceFromURL(databaseUrl = "") {
80
80
  exports.getDBInstanceFromURL = getDBInstanceFromURL;
81
81
  async function getFirebaseProjectParams(projectId, emulatorMode = false) {
82
82
  var _a, _b;
83
+ if (!projectId) {
84
+ return {};
85
+ }
83
86
  const body = emulatorMode
84
87
  ? await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(projectId)
85
88
  : await (0, functionsConfig_1.getFirebaseConfig)({ project: projectId });
@@ -263,7 +266,10 @@ async function promptForValidInstanceId(instanceId) {
263
266
  }
264
267
  exports.promptForValidInstanceId = promptForValidInstanceId;
265
268
  async function ensureExtensionsApiEnabled(options) {
266
- const projectId = (0, projectUtils_1.needProjectId)(options);
269
+ const projectId = (0, projectUtils_1.getProjectId)(options);
270
+ if (!projectId) {
271
+ return;
272
+ }
267
273
  return await (0, ensureApiEnabled_1.ensure)(projectId, "firebaseextensions.googleapis.com", "extensions", options.markdown);
268
274
  }
269
275
  exports.ensureExtensionsApiEnabled = ensureExtensionsApiEnabled;
@@ -519,7 +525,10 @@ async function confirm(args) {
519
525
  }
520
526
  exports.confirm = confirm;
521
527
  async function diagnoseAndFixProject(options) {
522
- const projectId = (0, projectUtils_1.needProjectId)(options);
528
+ const projectId = (0, projectUtils_1.getProjectId)(options);
529
+ if (!projectId) {
530
+ return;
531
+ }
523
532
  const ok = await (0, diagnose_1.diagnose)(projectId);
524
533
  if (!ok) {
525
534
  throw new error_1.FirebaseError("Unable to proceed until all issues are resolved.");
@@ -57,14 +57,19 @@ async function getParams(args) {
57
57
  }
58
58
  else if (args.paramsEnvPath) {
59
59
  params = getParamsFromFile({
60
- projectId: args.projectId,
61
60
  paramSpecs: args.paramSpecs,
62
61
  paramsEnvPath: args.paramsEnvPath,
63
62
  });
64
63
  }
65
64
  else {
66
65
  const firebaseProjectParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
67
- params = await askUserForParam.ask(args.projectId, args.instanceId, args.paramSpecs, firebaseProjectParams, !!args.reconfiguring);
66
+ params = await askUserForParam.ask({
67
+ projectId: args.projectId,
68
+ instanceId: args.instanceId,
69
+ paramSpecs: args.paramSpecs,
70
+ firebaseProjectParams,
71
+ reconfiguring: !!args.reconfiguring,
72
+ });
68
73
  }
69
74
  void track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
70
75
  return params;
@@ -85,7 +90,6 @@ async function getParamsForUpdate(args) {
85
90
  }
86
91
  else if (args.paramsEnvPath) {
87
92
  params = getParamsFromFile({
88
- projectId: args.projectId,
89
93
  paramSpecs: args.newSpec.params,
90
94
  paramsEnvPath: args.paramsEnvPath,
91
95
  });
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeAndValidate = exports.validate = exports.normalize = void 0;
4
+ const error_1 = require("../error");
5
+ function normalize(config) {
6
+ if (!config) {
7
+ throw new error_1.FirebaseError("No valid functions configuration detected in firebase.json");
8
+ }
9
+ if (Array.isArray(config)) {
10
+ if (config.length < 1) {
11
+ throw new error_1.FirebaseError("Requires at least one functions.source in firebase.json.");
12
+ }
13
+ return config;
14
+ }
15
+ return [config];
16
+ }
17
+ exports.normalize = normalize;
18
+ function validateSingle(config) {
19
+ if (!config.source) {
20
+ throw new error_1.FirebaseError("functions.source must be specified");
21
+ }
22
+ return Object.assign(Object.assign({}, config), { source: config.source });
23
+ }
24
+ function validate(config) {
25
+ if (config.length > 1) {
26
+ throw new error_1.FirebaseError("More than one functions.source detected in firebase.json.");
27
+ }
28
+ return [validateSingle(config[0])];
29
+ }
30
+ exports.validate = validate;
31
+ function normalizeAndValidate(config) {
32
+ return validate(normalize(config));
33
+ }
34
+ exports.normalizeAndValidate = normalizeAndValidate;
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doSetup = void 0;
3
4
  const clc = require("cli-color");
4
5
  const logger_1 = require("../../../logger");
5
6
  const prompt_1 = require("../../../prompt");
6
7
  const requirePermissions_1 = require("../../../requirePermissions");
7
8
  const previews_1 = require("../../../previews");
8
9
  const ensureApiEnabled_1 = require("../../../ensureApiEnabled");
9
- module.exports = async function (setup, config, options) {
10
+ async function doSetup(setup, config, options) {
10
11
  var _a, _b;
11
12
  logger_1.logger.info();
12
13
  logger_1.logger.info("A " + clc.bold("functions") + " directory will be created in your project with sample code");
@@ -44,4 +45,5 @@ module.exports = async function (setup, config, options) {
44
45
  choices,
45
46
  });
46
47
  return require("./" + language)(setup, config);
47
- };
48
+ }
49
+ exports.doSetup = doSetup;
@@ -1,23 +1,23 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doSetup = void 0;
2
4
  const clc = require("cli-color");
3
5
  const fs = require("fs");
4
- const { Client } = require("../../../apiv2");
5
- const { initGitHub } = require("./github");
6
- const { prompt } = require("../../../prompt");
7
- const { logger } = require("../../../logger");
6
+ const apiv2_1 = require("../../../apiv2");
7
+ const github_1 = require("./github");
8
+ const prompt_1 = require("../../../prompt");
9
+ const logger_1 = require("../../../logger");
8
10
  const INDEX_TEMPLATE = fs.readFileSync(__dirname + "/../../../../templates/init/hosting/index.html", "utf8");
9
11
  const MISSING_TEMPLATE = fs.readFileSync(__dirname + "/../../../../templates/init/hosting/404.html", "utf8");
10
12
  const DEFAULT_IGNORES = ["firebase.json", "**/.*", "**/node_modules/**"];
11
- module.exports = function (setup, config, options) {
13
+ async function doSetup(setup, config, options) {
12
14
  setup.hosting = {};
13
- logger.info();
14
- logger.info("Your " +
15
- clc.bold("public") +
16
- " directory is the folder (relative to your project directory) that");
17
- logger.info("will contain Hosting assets to be uploaded with " + clc.bold("firebase deploy") + ". If you");
18
- logger.info("have a build process for your assets, use your build's output directory.");
19
- logger.info();
20
- return prompt(setup.hosting, [
15
+ logger_1.logger.info();
16
+ logger_1.logger.info(`Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`);
17
+ logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
18
+ logger_1.logger.info("have a build process for your assets, use your build's output directory.");
19
+ logger_1.logger.info();
20
+ await (0, prompt_1.prompt)(setup.hosting, [
21
21
  {
22
22
  name: "public",
23
23
  type: "input",
@@ -36,31 +36,22 @@ module.exports = function (setup, config, options) {
36
36
  default: false,
37
37
  message: "Set up automatic builds and deploys with GitHub?",
38
38
  },
39
- ]).then(function () {
40
- setup.config.hosting = {
41
- public: setup.hosting.public,
42
- ignore: DEFAULT_IGNORES,
43
- };
44
- let next;
45
- if (setup.hosting.spa) {
46
- setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
47
- next = Promise.resolve();
48
- }
49
- else {
50
- next = config.askWriteProjectFile(setup.hosting.public + "/404.html", MISSING_TEMPLATE);
51
- }
52
- return next
53
- .then(() => {
54
- const c = new Client({ urlPrefix: "https://www.gstatic.com", auth: false });
55
- return c.get("/firebasejs/releases.json");
56
- })
57
- .then((response) => {
58
- return config.askWriteProjectFile(setup.hosting.public + "/index.html", INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version));
59
- })
60
- .then(() => {
61
- if (setup.hosting.github) {
62
- return initGitHub(setup, config, options);
63
- }
64
- });
65
- });
66
- };
39
+ ]);
40
+ setup.config.hosting = {
41
+ public: setup.hosting.public,
42
+ ignore: DEFAULT_IGNORES,
43
+ };
44
+ if (setup.hosting.spa) {
45
+ setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
46
+ }
47
+ else {
48
+ await config.askWriteProjectFile(`${setup.hosting.public}/404.html`, MISSING_TEMPLATE);
49
+ }
50
+ const c = new apiv2_1.Client({ urlPrefix: "https://www.gstatic.com", auth: false });
51
+ const response = await c.get("/firebasejs/releases.json");
52
+ await config.askWriteProjectFile(`${setup.hosting.public}/index.html`, INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version));
53
+ if (setup.hosting.github) {
54
+ return (0, github_1.initGitHub)(setup, config, options);
55
+ }
56
+ }
57
+ exports.doSetup = doSetup;
@@ -1,13 +1,23 @@
1
1
  "use strict";
2
- module.exports = {
3
- account: require("./account").doSetup,
4
- database: require("./database").doSetup,
5
- firestore: require("./firestore").doSetup,
6
- functions: require("./functions"),
7
- hosting: require("./hosting"),
8
- storage: require("./storage").doSetup,
9
- emulators: require("./emulators").doSetup,
10
- project: require("./project").doSetup,
11
- remoteconfig: require("./remoteconfig").doSetup,
12
- "hosting:github": require("./hosting/github").initGitHub,
13
- };
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hostingGithub = exports.remoteconfig = exports.project = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = exports.account = void 0;
4
+ var account_1 = require("./account");
5
+ Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
6
+ var database_1 = require("./database");
7
+ Object.defineProperty(exports, "database", { enumerable: true, get: function () { return database_1.doSetup; } });
8
+ var firestore_1 = require("./firestore");
9
+ Object.defineProperty(exports, "firestore", { enumerable: true, get: function () { return firestore_1.doSetup; } });
10
+ var functions_1 = require("./functions");
11
+ Object.defineProperty(exports, "functions", { enumerable: true, get: function () { return functions_1.doSetup; } });
12
+ var hosting_1 = require("./hosting");
13
+ Object.defineProperty(exports, "hosting", { enumerable: true, get: function () { return hosting_1.doSetup; } });
14
+ var storage_1 = require("./storage");
15
+ Object.defineProperty(exports, "storage", { enumerable: true, get: function () { return storage_1.doSetup; } });
16
+ var emulators_1 = require("./emulators");
17
+ Object.defineProperty(exports, "emulators", { enumerable: true, get: function () { return emulators_1.doSetup; } });
18
+ var project_1 = require("./project");
19
+ Object.defineProperty(exports, "project", { enumerable: true, get: function () { return project_1.doSetup; } });
20
+ var remoteconfig_1 = require("./remoteconfig");
21
+ Object.defineProperty(exports, "remoteconfig", { enumerable: true, get: function () { return remoteconfig_1.doSetup; } });
22
+ var github_1 = require("./hosting/github");
23
+ Object.defineProperty(exports, "hostingGithub", { enumerable: true, get: function () { return github_1.initGitHub; } });