firebase-tools 11.5.0 → 11.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/lib/command.js +33 -7
  2. package/lib/commands/crashlytics-mappingfile-generateid.js +26 -0
  3. package/lib/commands/crashlytics-mappingfile-upload.js +46 -0
  4. package/lib/commands/crashlytics-symbols-upload.js +18 -87
  5. package/lib/commands/emulators-exec.js +4 -1
  6. package/lib/commands/emulators-export.js +5 -2
  7. package/lib/commands/emulators-start.js +23 -17
  8. package/lib/commands/ext-dev-publish.js +3 -0
  9. package/lib/commands/functions-delete.js +2 -0
  10. package/lib/commands/functions-secrets-get.js +2 -0
  11. package/lib/commands/index.js +3 -0
  12. package/lib/commands/login.js +2 -2
  13. package/lib/crashlytics/buildToolsJarHelper.js +51 -0
  14. package/lib/deploy/functions/backend.js +4 -4
  15. package/lib/deploy/functions/build.js +76 -9
  16. package/lib/deploy/functions/checkIam.js +6 -5
  17. package/lib/deploy/functions/params.js +22 -16
  18. package/lib/deploy/functions/prepare.js +1 -1
  19. package/lib/deploy/functions/release/fabricator.js +22 -5
  20. package/lib/deploy/functions/release/index.js +2 -0
  21. package/lib/deploy/functions/runtimes/discovery/index.js +1 -16
  22. package/lib/deploy/functions/runtimes/discovery/parsing.js +16 -0
  23. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +59 -131
  24. package/lib/deploy/functions/runtimes/node/parseTriggers.js +1 -1
  25. package/lib/emulator/auth/index.js +7 -2
  26. package/lib/emulator/auth/operations.js +10 -10
  27. package/lib/emulator/commandUtils.js +32 -15
  28. package/lib/emulator/constants.js +14 -6
  29. package/lib/emulator/controller.js +49 -17
  30. package/lib/emulator/downloadableEmulators.js +7 -7
  31. package/lib/emulator/eventarcEmulator.js +148 -0
  32. package/lib/emulator/extensionsEmulator.js +3 -1
  33. package/lib/emulator/functionsEmulator.js +44 -4
  34. package/lib/emulator/functionsEmulatorRuntime.js +12 -23
  35. package/lib/emulator/functionsEmulatorShared.js +6 -1
  36. package/lib/emulator/hub.js +7 -3
  37. package/lib/emulator/hubClient.js +2 -2
  38. package/lib/emulator/hubExport.js +22 -2
  39. package/lib/emulator/registry.js +1 -0
  40. package/lib/emulator/storage/apis/firebase.js +145 -129
  41. package/lib/emulator/storage/apis/gcloud.js +102 -42
  42. package/lib/emulator/storage/files.js +39 -17
  43. package/lib/emulator/storage/metadata.js +76 -55
  44. package/lib/emulator/storage/multipart.js +2 -2
  45. package/lib/emulator/storage/rules/runtime.js +12 -4
  46. package/lib/emulator/storage/server.js +2 -1
  47. package/lib/emulator/storage/upload.js +46 -9
  48. package/lib/emulator/types.js +3 -0
  49. package/lib/emulator/ui.js +7 -2
  50. package/lib/extensions/extensionsApi.js +2 -1
  51. package/lib/extensions/extensionsHelper.js +29 -1
  52. package/lib/functions/constants.js +14 -0
  53. package/lib/functions/env.js +9 -9
  54. package/lib/gcp/cloudfunctions.js +15 -18
  55. package/lib/gcp/cloudfunctionsv2.js +15 -18
  56. package/lib/gcp/cloudscheduler.js +32 -14
  57. package/lib/serve/index.js +15 -0
  58. package/lib/track.js +122 -3
  59. package/lib/utils.js +14 -1
  60. package/npm-shrinkwrap.json +542 -9
  61. package/package.json +5 -4
  62. package/schema/firebase-config.json +12 -0
  63. package/templates/extensions/CHANGELOG.md +1 -7
@@ -7,19 +7,19 @@ const registry_1 = require("../registry");
7
7
  const types_1 = require("../types");
8
8
  const crc_1 = require("./crc");
9
9
  class StoredFileMetadata {
10
- constructor(opts, _cloudFunctions, bytes, incomingMetadata) {
10
+ constructor(opts, _cloudFunctions, bytes) {
11
11
  this._cloudFunctions = _cloudFunctions;
12
12
  this.name = opts.name;
13
13
  this.bucket = opts.bucket;
14
- this.contentType = opts.contentType;
15
14
  this.metageneration = opts.metageneration || 1;
16
15
  this.generation = opts.generation || Date.now();
16
+ this.contentType = opts.contentType || "application/octet-stream";
17
17
  this.storageClass = opts.storageClass || "STANDARD";
18
- this.contentDisposition = opts.contentDisposition || "inline";
19
- this.cacheControl = opts.cacheControl || "public, max-age=3600";
18
+ this.contentDisposition = opts.contentDisposition;
19
+ this.cacheControl = opts.cacheControl;
20
20
  this.contentLanguage = opts.contentLanguage;
21
21
  this.customTime = opts.customTime;
22
- this.contentEncoding = opts.contentEncoding || "identity";
22
+ this.contentEncoding = opts.contentEncoding;
23
23
  this.customMetadata = opts.customMetadata;
24
24
  this.downloadTokens = opts.downloadTokens || [];
25
25
  if (opts.etag) {
@@ -43,42 +43,54 @@ class StoredFileMetadata {
43
43
  else {
44
44
  throw new Error("Must pass bytes array or opts object with size, md5hash, and crc32c");
45
45
  }
46
- if (incomingMetadata) {
47
- this.update(incomingMetadata, false);
48
- }
49
46
  this.deleteFieldsSetAsNull();
50
47
  this.setDownloadTokensFromCustomMetadata();
51
48
  }
52
- asRulesResource(proposedChanges) {
53
- let rulesResource = {
49
+ clone() {
50
+ const clone = new StoredFileMetadata({
54
51
  name: this.name,
55
52
  bucket: this.bucket,
56
53
  generation: this.generation,
57
54
  metageneration: this.metageneration,
55
+ contentType: this.contentType,
56
+ storageClass: this.storageClass,
58
57
  size: this.size,
59
- timeCreated: this.timeCreated,
60
- updated: this.updated,
61
58
  md5Hash: this.md5Hash,
59
+ contentEncoding: this.contentEncoding,
60
+ contentDisposition: this.contentDisposition,
61
+ contentLanguage: this.contentLanguage,
62
+ cacheControl: this.cacheControl,
63
+ customTime: this.customTime,
62
64
  crc32c: this.crc32c,
63
65
  etag: this.etag,
64
- contentDisposition: this.contentDisposition,
65
- contentEncoding: this.contentEncoding,
66
- contentType: this.contentType,
67
- metadata: this.customMetadata || {},
68
- };
66
+ downloadTokens: this.downloadTokens,
67
+ customMetadata: this.customMetadata,
68
+ }, this._cloudFunctions);
69
+ clone.timeCreated = this.timeCreated;
70
+ clone.updated = this.updated;
71
+ return clone;
72
+ }
73
+ asRulesResource(proposedChanges) {
74
+ const proposedMetadata = this.clone();
69
75
  if (proposedChanges) {
70
- if (proposedChanges.md5Hash !== rulesResource.md5Hash) {
71
- rulesResource.generation = Date.now();
72
- rulesResource.metageneration = 1;
73
- rulesResource.timeCreated = new Date();
74
- rulesResource.updated = rulesResource.timeCreated;
75
- }
76
- else {
77
- rulesResource.metageneration++;
78
- }
79
- rulesResource = Object.assign(Object.assign({}, rulesResource), proposedChanges);
76
+ proposedMetadata.update(proposedChanges, false);
80
77
  }
81
- return rulesResource;
78
+ return {
79
+ name: proposedMetadata.name,
80
+ bucket: proposedMetadata.bucket,
81
+ generation: proposedMetadata.generation,
82
+ metageneration: proposedMetadata.metageneration,
83
+ size: proposedMetadata.size,
84
+ timeCreated: proposedMetadata.timeCreated,
85
+ updated: proposedMetadata.updated,
86
+ md5Hash: proposedMetadata.md5Hash,
87
+ crc32c: proposedMetadata.crc32c,
88
+ etag: proposedMetadata.etag,
89
+ contentDisposition: proposedMetadata.contentDisposition,
90
+ contentEncoding: proposedMetadata.contentEncoding,
91
+ contentType: proposedMetadata.contentType,
92
+ metadata: proposedMetadata.customMetadata || {},
93
+ };
82
94
  }
83
95
  setDownloadTokensFromCustomMetadata() {
84
96
  if (!this.customMetadata) {
@@ -118,43 +130,52 @@ class StoredFileMetadata {
118
130
  }
119
131
  }
120
132
  update(incoming, shouldTrigger = true) {
121
- if (incoming.contentDisposition) {
122
- this.contentDisposition = incoming.contentDisposition;
133
+ if (incoming.contentDisposition !== undefined) {
134
+ this.contentDisposition =
135
+ incoming.contentDisposition === null ? undefined : incoming.contentDisposition;
123
136
  }
124
- if (incoming.contentType) {
125
- this.contentType = incoming.contentType;
137
+ if (incoming.contentType !== undefined) {
138
+ this.contentType = incoming.contentType === null ? undefined : incoming.contentType;
126
139
  }
127
- if (incoming.metadata) {
128
- this.customMetadata = this.customMetadata ? Object.assign({}, this.customMetadata) : {};
129
- for (const [k, v] of Object.entries(incoming.metadata)) {
130
- this.customMetadata[k] = v === null ? null : String(v);
131
- }
140
+ if (incoming.contentLanguage !== undefined) {
141
+ this.contentLanguage =
142
+ incoming.contentLanguage === null ? undefined : incoming.contentLanguage;
132
143
  }
133
- if (incoming.contentLanguage) {
134
- this.contentLanguage = incoming.contentLanguage;
144
+ if (incoming.contentEncoding !== undefined) {
145
+ this.contentEncoding =
146
+ incoming.contentEncoding === null ? undefined : incoming.contentEncoding;
135
147
  }
136
- if (incoming.contentEncoding) {
137
- this.contentEncoding = incoming.contentEncoding;
148
+ if (incoming.cacheControl !== undefined) {
149
+ this.cacheControl = incoming.cacheControl === null ? undefined : incoming.cacheControl;
138
150
  }
139
- if (this.generation) {
140
- this.generation++;
151
+ if (incoming.metadata !== undefined) {
152
+ if (incoming.metadata === null) {
153
+ this.customMetadata = undefined;
154
+ }
155
+ else {
156
+ this.customMetadata = this.customMetadata || {};
157
+ for (const [k, v] of Object.entries(incoming.metadata)) {
158
+ if (v === null) {
159
+ delete this.customMetadata[k];
160
+ }
161
+ else {
162
+ this.customMetadata[k] = String(v);
163
+ }
164
+ }
165
+ if (Object.keys(this.customMetadata).length === 0) {
166
+ this.customMetadata = undefined;
167
+ }
168
+ }
141
169
  }
170
+ this.metageneration++;
142
171
  this.updated = new Date();
143
- if (incoming.cacheControl) {
144
- this.cacheControl = incoming.cacheControl;
145
- }
146
172
  this.setDownloadTokensFromCustomMetadata();
147
- this.deleteFieldsSetAsNull();
148
173
  if (shouldTrigger) {
149
174
  this._cloudFunctions.dispatch("metadataUpdate", new CloudStorageObjectMetadata(this));
150
175
  }
151
176
  }
152
177
  addDownloadToken(shouldTrigger = true) {
153
- if (!this.downloadTokens.length) {
154
- this.downloadTokens.push(uuid.v4());
155
- return;
156
- }
157
- this.downloadTokens = [...this.downloadTokens, uuid.v4()];
178
+ this.downloadTokens = [...(this.downloadTokens || []), uuid.v4()];
158
179
  this.update({}, shouldTrigger);
159
180
  }
160
181
  deleteDownloadToken(token) {
@@ -197,7 +218,7 @@ class OutgoingFirebaseMetadata {
197
218
  this.crc32c = metadata.crc32c;
198
219
  this.etag = metadata.etag;
199
220
  this.downloadTokens = metadata.downloadTokens.join(",");
200
- this.contentEncoding = metadata.contentEncoding;
221
+ this.contentEncoding = metadata.contentEncoding || "identity";
201
222
  this.contentDisposition = metadata.contentDisposition;
202
223
  this.metadata = metadata.customMetadata;
203
224
  this.contentLanguage = metadata.contentLanguage;
@@ -208,7 +229,7 @@ exports.OutgoingFirebaseMetadata = OutgoingFirebaseMetadata;
208
229
  class CloudStorageBucketMetadata {
209
230
  constructor(id) {
210
231
  var _a, _b;
211
- this.kind = "#storage/bucket";
232
+ this.kind = "storage#bucket";
212
233
  this.name = id;
213
234
  this.id = id;
214
235
  this.selfLink = `http://${(_a = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _a === void 0 ? void 0 : _a.host}:${(_b = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _b === void 0 ? void 0 : _b.port}/v1/b/${this.id}`;
@@ -240,7 +261,7 @@ exports.CloudStorageObjectAccessControlMetadata = CloudStorageObjectAccessContro
240
261
  class CloudStorageObjectMetadata {
241
262
  constructor(metadata) {
242
263
  var _a, _b, _c, _d;
243
- this.kind = "#storage#object";
264
+ this.kind = "storage#object";
244
265
  this.name = metadata.name;
245
266
  this.bucket = metadata.bucket;
246
267
  this.generation = metadata.generation.toString();
@@ -45,11 +45,11 @@ function parseMultipartRequestBodyPart(bodyPart) {
45
45
  }
46
46
  function parseObjectUploadMultipartRequest(contentTypeHeader, body) {
47
47
  if (!contentTypeHeader.startsWith("multipart/related")) {
48
- throw new Error(`Invalid Content-Type: ${contentTypeHeader}`);
48
+ throw new Error(`Bad content type. ${contentTypeHeader}`);
49
49
  }
50
50
  const boundaryId = contentTypeHeader.split("boundary=")[1];
51
51
  if (!boundaryId) {
52
- throw new Error(`Invalid Content-Type header: ${contentTypeHeader}`);
52
+ throw new Error(`Bad content type. ${contentTypeHeader}`);
53
53
  }
54
54
  const parsedBody = parseMultipartRequestBody(boundaryId, body);
55
55
  if (parsedBody.length !== 2) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StorageRulesRuntime = exports.StorageRulesIssues = exports.StorageRulesetInstance = void 0;
4
4
  const cross_spawn_1 = require("cross-spawn");
5
5
  const error_1 = require("../../../error");
6
+ const AsyncLock = require("async-lock");
6
7
  const types_1 = require("./types");
7
8
  const jwt = require("jsonwebtoken");
8
9
  const emulatorLogger_1 = require("../../emulatorLogger");
@@ -13,6 +14,8 @@ const constants_1 = require("../../constants");
13
14
  const download_1 = require("../../download");
14
15
  const fs = require("fs-extra");
15
16
  const downloadableEmulators_1 = require("../../downloadableEmulators");
17
+ const lock = new AsyncLock();
18
+ const synchonizationKey = "key";
16
19
  class StorageRulesetInstance {
17
20
  constructor(runtime, rulesVersion, rulesetName) {
18
21
  this.runtime = runtime;
@@ -65,12 +68,12 @@ class StorageRulesRuntime {
65
68
  get alive() {
66
69
  return this._alive;
67
70
  }
68
- async start(auto_download = true) {
71
+ async start(autoDownload = true) {
69
72
  var _a, _b;
70
73
  const downloadDetails = downloadableEmulators_1.DownloadDetails[types_2.Emulators.STORAGE];
71
74
  const hasEmulator = fs.existsSync(downloadDetails.downloadPath);
72
75
  if (!hasEmulator) {
73
- if (auto_download) {
76
+ if (autoDownload) {
74
77
  if (process.env.CI) {
75
78
  utils.logWarning(`It appears you are running in a CI environment. You can avoid downloading the ${constants_1.Constants.description(types_2.Emulators.STORAGE)} repeatedly by caching the ${downloadDetails.opts.cacheDir} directory.`);
76
79
  }
@@ -154,13 +157,18 @@ class StorageRulesRuntime {
154
157
  throw new error_1.FirebaseError("Attempted to send Cloud Storage rules request with stale id");
155
158
  }
156
159
  return new Promise((resolve) => {
157
- var _a, _b;
158
160
  this._requests[runtimeActionRequest.id] = {
159
161
  request: runtimeActionRequest,
160
162
  handler: resolve,
161
163
  };
162
164
  const serializedRequest = JSON.stringify(runtimeActionRequest);
163
- (_b = (_a = this._childprocess) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.write(serializedRequest + "\n");
165
+ lock.acquire(synchonizationKey, (done) => {
166
+ var _a, _b;
167
+ (_b = (_a = this._childprocess) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.write(serializedRequest + "\n");
168
+ setTimeout(() => {
169
+ done();
170
+ }, 15);
171
+ });
164
172
  });
165
173
  }
166
174
  async loadRuleset(source) {
@@ -36,12 +36,13 @@ function createApp(defaultProjectId, emulator) {
36
36
  type: ["application/json"],
37
37
  }));
38
38
  app.post("/internal/export", async (req, res) => {
39
+ const initiatedBy = req.body.initiatedBy || "unknown";
39
40
  const path = req.body.path;
40
41
  if (!path) {
41
42
  res.status(400).send("Export request body must include 'path'.");
42
43
  return;
43
44
  }
44
- await storageLayer.export(path);
45
+ await storageLayer.export(path, { initiatedBy });
45
46
  res.sendStatus(200);
46
47
  });
47
48
  app.put("/internal/setRules", async (req, res) => {
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UploadService = exports.NotCancellableError = exports.UploadNotActiveError = exports.UploadStatus = exports.UploadType = void 0;
3
+ exports.UploadService = exports.NotCancellableError = exports.UploadPreviouslyFinalizedError = exports.UploadNotActiveError = exports.UploadStatus = exports.UploadType = void 0;
4
4
  const uuid_1 = require("uuid");
5
5
  const errors_1 = require("./errors");
6
6
  var UploadType;
7
7
  (function (UploadType) {
8
- UploadType[UploadType["MULTIPART"] = 0] = "MULTIPART";
9
- UploadType[UploadType["RESUMABLE"] = 1] = "RESUMABLE";
8
+ UploadType[UploadType["MEDIA"] = 0] = "MEDIA";
9
+ UploadType[UploadType["MULTIPART"] = 1] = "MULTIPART";
10
+ UploadType[UploadType["RESUMABLE"] = 2] = "RESUMABLE";
10
11
  })(UploadType = exports.UploadType || (exports.UploadType = {}));
11
12
  var UploadStatus;
12
13
  (function (UploadStatus) {
@@ -17,6 +18,9 @@ var UploadStatus;
17
18
  class UploadNotActiveError extends Error {
18
19
  }
19
20
  exports.UploadNotActiveError = UploadNotActiveError;
21
+ class UploadPreviouslyFinalizedError extends Error {
22
+ }
23
+ exports.UploadPreviouslyFinalizedError = UploadPreviouslyFinalizedError;
20
24
  class NotCancellableError extends Error {
21
25
  }
22
26
  exports.NotCancellableError = NotCancellableError;
@@ -28,23 +32,42 @@ class UploadService {
28
32
  reset() {
29
33
  this._uploads = new Map();
30
34
  }
35
+ mediaUpload(request) {
36
+ const upload = this.startOneShotUpload({
37
+ bucketId: request.bucketId,
38
+ objectId: request.objectId,
39
+ uploadType: UploadType.MEDIA,
40
+ dataRaw: request.dataRaw,
41
+ authorization: request.authorization,
42
+ });
43
+ this._persistence.deleteFile(upload.path, true);
44
+ this._persistence.appendBytes(upload.path, request.dataRaw);
45
+ return upload;
46
+ }
31
47
  multipartUpload(request) {
32
- const upload = this.startMultipartUpload(request, request.dataRaw.byteLength);
48
+ const upload = this.startOneShotUpload({
49
+ bucketId: request.bucketId,
50
+ objectId: request.objectId,
51
+ uploadType: UploadType.MULTIPART,
52
+ dataRaw: request.dataRaw,
53
+ metadata: JSON.parse(request.metadataRaw),
54
+ authorization: request.authorization,
55
+ });
33
56
  this._persistence.deleteFile(upload.path, true);
34
57
  this._persistence.appendBytes(upload.path, request.dataRaw);
35
58
  return upload;
36
59
  }
37
- startMultipartUpload(request, sizeInBytes) {
60
+ startOneShotUpload(request) {
38
61
  const id = (0, uuid_1.v4)();
39
62
  const upload = {
40
- id: (0, uuid_1.v4)(),
63
+ id,
41
64
  bucketId: request.bucketId,
42
65
  objectId: request.objectId,
43
- type: UploadType.MULTIPART,
66
+ type: request.uploadType,
44
67
  path: this.getStagingFileName(id, request.bucketId, request.objectId),
45
68
  status: UploadStatus.FINISHED,
46
- metadata: JSON.parse(request.metadataRaw),
47
- size: sizeInBytes,
69
+ metadata: request.metadata,
70
+ size: request.dataRaw.byteLength,
48
71
  authorization: request.authorization,
49
72
  };
50
73
  this._uploads.set(upload.id, upload);
@@ -65,6 +88,7 @@ class UploadService {
65
88
  };
66
89
  this._uploads.set(upload.id, upload);
67
90
  this._persistence.deleteFile(upload.path, true);
91
+ this._persistence.appendBytes(upload.path, Buffer.alloc(0));
68
92
  return upload;
69
93
  }
70
94
  continueResumableUpload(uploadId, dataRaw) {
@@ -93,12 +117,25 @@ class UploadService {
93
117
  }
94
118
  finalizeResumableUpload(uploadId) {
95
119
  const upload = this.getResumableUpload(uploadId);
120
+ if (upload.status === UploadStatus.FINISHED) {
121
+ throw new UploadPreviouslyFinalizedError();
122
+ }
96
123
  if (upload.status === UploadStatus.CANCELLED) {
97
124
  throw new UploadNotActiveError();
98
125
  }
99
126
  upload.status = UploadStatus.FINISHED;
100
127
  return upload;
101
128
  }
129
+ setResponseCode(uploadId, code) {
130
+ const upload = this._uploads.get(uploadId);
131
+ if (upload) {
132
+ upload.prevResponseCode = code;
133
+ }
134
+ }
135
+ getPreviousResponseCode(uploadId) {
136
+ var _a;
137
+ return ((_a = this._uploads.get(uploadId)) === null || _a === void 0 ? void 0 : _a.prevResponseCode) || 200;
138
+ }
102
139
  getStagingFileName(uploadId, bucketId, objectId) {
103
140
  return encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
104
141
  }
@@ -14,6 +14,7 @@ var Emulators;
14
14
  Emulators["LOGGING"] = "logging";
15
15
  Emulators["STORAGE"] = "storage";
16
16
  Emulators["EXTENSIONS"] = "extensions";
17
+ Emulators["EVENTARC"] = "eventarc";
17
18
  })(Emulators = exports.Emulators || (exports.Emulators = {}));
18
19
  exports.DOWNLOADABLE_EMULATORS = [
19
20
  Emulators.FIRESTORE,
@@ -36,12 +37,14 @@ exports.ALL_SERVICE_EMULATORS = [
36
37
  Emulators.HOSTING,
37
38
  Emulators.PUBSUB,
38
39
  Emulators.STORAGE,
40
+ Emulators.EVENTARC,
39
41
  ].filter((v) => v);
40
42
  exports.EMULATORS_SUPPORTED_BY_FUNCTIONS = [
41
43
  Emulators.FIRESTORE,
42
44
  Emulators.DATABASE,
43
45
  Emulators.PUBSUB,
44
46
  Emulators.STORAGE,
47
+ Emulators.EVENTARC,
45
48
  ];
46
49
  exports.EMULATORS_SUPPORTED_BY_UI = [
47
50
  Emulators.AUTH,
@@ -6,6 +6,7 @@ const downloadableEmulators = require("./downloadableEmulators");
6
6
  const registry_1 = require("./registry");
7
7
  const error_1 = require("../error");
8
8
  const constants_1 = require("./constants");
9
+ const track_1 = require("../track");
9
10
  class EmulatorUI {
10
11
  constructor(args) {
11
12
  this.args = args;
@@ -15,14 +16,18 @@ class EmulatorUI {
15
16
  throw new error_1.FirebaseError(`Cannot start ${constants_1.Constants.description(types_1.Emulators.UI)} without ${constants_1.Constants.description(types_1.Emulators.HUB)}!`);
16
17
  }
17
18
  const hubInfo = registry_1.EmulatorRegistry.get(types_1.Emulators.HUB).getInfo();
18
- const { auto_download, host, port, projectId } = this.args;
19
+ const { auto_download: autoDownload, host, port, projectId } = this.args;
19
20
  const env = {
20
21
  HOST: host.toString(),
21
22
  PORT: port.toString(),
22
23
  GCLOUD_PROJECT: projectId,
23
24
  [constants_1.Constants.FIREBASE_EMULATOR_HUB]: registry_1.EmulatorRegistry.getInfoHostString(hubInfo),
24
25
  };
25
- return downloadableEmulators.start(types_1.Emulators.UI, { auto_download }, env);
26
+ const session = (0, track_1.emulatorSession)();
27
+ if (session) {
28
+ env[constants_1.Constants.FIREBASE_GA_SESSION] = JSON.stringify(session);
29
+ }
30
+ return downloadableEmulators.start(types_1.Emulators.UI, { auto_download: autoDownload }, env);
26
31
  }
27
32
  connect() {
28
33
  return Promise.resolve();
@@ -291,13 +291,14 @@ async function listExtensions(publisherId) {
291
291
  return extensions;
292
292
  }
293
293
  exports.listExtensions = listExtensions;
294
- async function listExtensionVersions(ref, filter = "") {
294
+ async function listExtensionVersions(ref, filter = "", showPrereleases = false) {
295
295
  const { publisherId, extensionId } = refs.parse(ref);
296
296
  const extensionVersions = [];
297
297
  const getNextPage = async (pageToken = "") => {
298
298
  const res = await apiClient.get(`/publishers/${publisherId}/extensions/${extensionId}/versions`, {
299
299
  queryParams: {
300
300
  filter,
301
+ showPrereleases: String(showPrereleases),
301
302
  pageSize: PAGE_SIZE_MAX,
302
303
  pageToken,
303
304
  },
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
3
+ exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
4
4
  const clc = require("colorette");
5
5
  const ora = require("ora");
6
6
  const semver = require("semver");
@@ -282,6 +282,33 @@ async function archiveAndUploadSource(extPath, bucketName) {
282
282
  const res = await (0, storage_1.uploadObject)(zippedSource, bucketName);
283
283
  return `/${res.bucket}/${res.object}`;
284
284
  }
285
+ async function incrementPrereleaseVersion(ref, extensionVersion, stage) {
286
+ var _a;
287
+ const stageOptions = ["stable", "alpha", "beta", "rc"];
288
+ if (!stageOptions.includes(stage)) {
289
+ throw new error_1.FirebaseError(`--stage flag only supports the following values: ${stageOptions}`);
290
+ }
291
+ if (stage !== "stable") {
292
+ const version = semver.parse(extensionVersion);
293
+ if (version.prerelease.length > 0 || version.build.length > 0) {
294
+ throw new error_1.FirebaseError(`Cannot combine the --stage flag with a version with a prerelease annotation in extension.yaml.`);
295
+ }
296
+ let extensionVersions = [];
297
+ try {
298
+ extensionVersions = await (0, extensionsApi_1.listExtensionVersions)(ref, `id="${version.version}"`, true);
299
+ }
300
+ catch (e) {
301
+ }
302
+ const latestVersion = (_a = extensionVersions
303
+ .map((version) => semver.parse(version.spec.version))
304
+ .filter((version) => version.prerelease.length > 0 && version.prerelease[0] === stage)
305
+ .sort((v1, v2) => semver.compare(v1, v2))
306
+ .pop()) !== null && _a !== void 0 ? _a : `${version}-${stage}`;
307
+ return semver.inc(latestVersion, "prerelease", undefined, stage);
308
+ }
309
+ return extensionVersion;
310
+ }
311
+ exports.incrementPrereleaseVersion = incrementPrereleaseVersion;
285
312
  async function publishExtensionVersionFromLocalSource(args) {
286
313
  const extensionSpec = await (0, localHelper_1.getLocalExtensionSpec)(args.rootDirectory);
287
314
  if (extensionSpec.name !== args.extensionId) {
@@ -290,6 +317,7 @@ async function publishExtensionVersionFromLocalSource(args) {
290
317
  const subbedSpec = JSON.parse(JSON.stringify(extensionSpec));
291
318
  subbedSpec.params = substituteParams(extensionSpec.params || [], exports.AUTOPOULATED_PARAM_PLACEHOLDERS);
292
319
  validateSpec(subbedSpec);
320
+ extensionSpec.version = await incrementPrereleaseVersion(`${args.publisherId}/${args.extensionId}`, extensionSpec.version, args.stage);
293
321
  let extension;
294
322
  try {
295
323
  extension = await (0, extensionsApi_1.getExtension)(`${args.publisherId}/${args.extensionId}`);
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BLOCKING_EVENT_TO_LABEL_KEY = exports.BLOCKING_LABEL_KEY_TO_EVENT = exports.BLOCKING_LABEL = exports.HASH_LABEL = exports.CODEBASE_LABEL = void 0;
4
+ exports.CODEBASE_LABEL = "firebase-functions-codebase";
5
+ exports.HASH_LABEL = "firebase-functions-hash";
6
+ exports.BLOCKING_LABEL = "deployment-blocking";
7
+ exports.BLOCKING_LABEL_KEY_TO_EVENT = {
8
+ "before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
9
+ "before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
10
+ };
11
+ exports.BLOCKING_EVENT_TO_LABEL_KEY = {
12
+ "providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
13
+ "providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
14
+ };
@@ -161,9 +161,11 @@ function writeUserEnvs(toWrite, envOpts) {
161
161
  return;
162
162
  }
163
163
  const { functionsSource, projectId, projectAlias, isEmulator } = envOpts;
164
- let envFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
165
- if (envFiles.length === 0) {
166
- envFiles = [createEnvFile(envOpts)];
164
+ const envFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
165
+ const projectScopedFileName = `.env.${projectId}`;
166
+ const projectScopedFileExists = envFiles.includes(projectScopedFileName);
167
+ if (!projectScopedFileExists) {
168
+ createEnvFile(envOpts);
167
169
  }
168
170
  const currentEnvs = loadUserEnvs(envOpts);
169
171
  for (const k of Object.keys(toWrite)) {
@@ -172,17 +174,15 @@ function writeUserEnvs(toWrite, envOpts) {
172
174
  throw new error_1.FirebaseError(`Attempted to write param-defined key ${k} to .env files, but it was already defined.`);
173
175
  }
174
176
  }
175
- const mostSpecificEnv = path.join(functionsSource, envFiles[envFiles.length - 1]);
176
- (0, utils_1.logBullet)(clc.cyan(clc.bold("functions: ")) + `Writing new parameter values to disk: ${mostSpecificEnv}`);
177
+ (0, utils_1.logBullet)(clc.cyan(clc.bold("functions: ")) +
178
+ `Writing new parameter values to disk: ${projectScopedFileName}`);
177
179
  for (const k of Object.keys(toWrite)) {
178
- fs.appendFileSync(mostSpecificEnv, formatUserEnvForWrite(k, toWrite[k]));
180
+ fs.appendFileSync(path.join(functionsSource, projectScopedFileName), formatUserEnvForWrite(k, toWrite[k]));
179
181
  }
180
182
  }
181
183
  exports.writeUserEnvs = writeUserEnvs;
182
184
  function createEnvFile(envOpts) {
183
- const fileToWrite = envOpts.isEmulator
184
- ? FUNCTIONS_EMULATOR_DOTENV
185
- : `.env.${envOpts.projectAlias || envOpts.projectId}`;
185
+ const fileToWrite = envOpts.isEmulator ? FUNCTIONS_EMULATOR_DOTENV : `.env.${envOpts.projectId}`;
186
186
  logger_1.logger.debug(`Creating ${fileToWrite}...`);
187
187
  fs.writeFileSync(path.join(envOpts.functionsSource, fileToWrite), "", { flag: "wx" });
188
188
  return fileToWrite;