firebase-tools 11.4.1 → 11.6.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 (203) hide show
  1. package/lib/accountImporter.js +1 -1
  2. package/lib/auth.js +3 -4
  3. package/lib/bin/firebase.js +4 -4
  4. package/lib/command.js +35 -10
  5. package/lib/commands/apps-android-sha-create.js +1 -1
  6. package/lib/commands/apps-android-sha-delete.js +1 -1
  7. package/lib/commands/apps-create.js +1 -1
  8. package/lib/commands/apps-list.js +1 -1
  9. package/lib/commands/auth-export.js +1 -1
  10. package/lib/commands/auth-import.js +1 -1
  11. package/lib/commands/database-instances-list.js +1 -1
  12. package/lib/commands/database-push.js +1 -1
  13. package/lib/commands/database-remove.js +1 -1
  14. package/lib/commands/database-set.js +1 -1
  15. package/lib/commands/database-update.js +1 -1
  16. package/lib/commands/emulators-exec.js +4 -1
  17. package/lib/commands/emulators-export.js +5 -2
  18. package/lib/commands/emulators-start.js +24 -18
  19. package/lib/commands/ext-dev-deprecate.js +1 -1
  20. package/lib/commands/ext-dev-emulators-exec.js +1 -1
  21. package/lib/commands/ext-dev-emulators-start.js +1 -1
  22. package/lib/commands/ext-dev-extension-delete.js +1 -1
  23. package/lib/commands/ext-dev-list.js +1 -1
  24. package/lib/commands/ext-dev-publish.js +4 -1
  25. package/lib/commands/ext-dev-register.js +1 -1
  26. package/lib/commands/ext-dev-undeprecate.js +1 -1
  27. package/lib/commands/ext-dev-unpublish.js +1 -1
  28. package/lib/commands/ext-dev-usage.js +1 -1
  29. package/lib/commands/ext-info.js +1 -1
  30. package/lib/commands/ext-install.js +2 -2
  31. package/lib/commands/ext-update.js +1 -1
  32. package/lib/commands/ext.js +1 -1
  33. package/lib/commands/firestore-delete.js +2 -2
  34. package/lib/commands/firestore-indexes-list.js +3 -3
  35. package/lib/commands/functions-config-clone.js +1 -1
  36. package/lib/commands/functions-config-export.js +1 -1
  37. package/lib/commands/functions-config-set.js +1 -1
  38. package/lib/commands/functions-config-unset.js +1 -1
  39. package/lib/commands/functions-delete.js +1 -1
  40. package/lib/commands/functions-secrets-set.js +1 -1
  41. package/lib/commands/help.js +1 -1
  42. package/lib/commands/hosting-channel-create.js +5 -5
  43. package/lib/commands/hosting-channel-delete.js +3 -3
  44. package/lib/commands/hosting-channel-deploy.js +6 -6
  45. package/lib/commands/hosting-channel-list.js +2 -2
  46. package/lib/commands/hosting-channel-open.js +2 -2
  47. package/lib/commands/hosting-clone.js +8 -8
  48. package/lib/commands/hosting-disable.js +1 -1
  49. package/lib/commands/hosting-sites-create.js +4 -4
  50. package/lib/commands/hosting-sites-delete.js +4 -4
  51. package/lib/commands/hosting-sites-list.js +2 -2
  52. package/lib/commands/init.js +5 -5
  53. package/lib/commands/login-add.js +1 -1
  54. package/lib/commands/login-ci.js +2 -2
  55. package/lib/commands/login-list.js +1 -1
  56. package/lib/commands/login-use.js +1 -1
  57. package/lib/commands/login.js +3 -3
  58. package/lib/commands/logout.js +1 -1
  59. package/lib/commands/open.js +3 -3
  60. package/lib/commands/projects-list.js +2 -2
  61. package/lib/commands/serve.js +1 -1
  62. package/lib/commands/target-apply.js +1 -1
  63. package/lib/commands/target-clear.js +1 -1
  64. package/lib/commands/target-remove.js +1 -1
  65. package/lib/commands/target.js +1 -1
  66. package/lib/commands/use.js +7 -7
  67. package/lib/config.js +1 -1
  68. package/lib/deploy/database/prepare.js +3 -3
  69. package/lib/deploy/database/release.js +3 -3
  70. package/lib/deploy/extensions/deploymentSummary.js +1 -1
  71. package/lib/deploy/extensions/errors.js +1 -1
  72. package/lib/deploy/extensions/secrets.js +1 -1
  73. package/lib/deploy/extensions/tasks.js +2 -2
  74. package/lib/deploy/firestore/deploy.js +2 -2
  75. package/lib/deploy/firestore/prepare.js +2 -2
  76. package/lib/deploy/functions/backend.js +7 -5
  77. package/lib/deploy/functions/build.js +110 -95
  78. package/lib/deploy/functions/checkIam.js +3 -3
  79. package/lib/deploy/functions/containerCleaner.js +2 -2
  80. package/lib/deploy/functions/deploy.js +2 -2
  81. package/lib/deploy/functions/ensure.js +2 -2
  82. package/lib/deploy/functions/params.js +5 -2
  83. package/lib/deploy/functions/prepare.js +4 -4
  84. package/lib/deploy/functions/prepareFunctionsUpload.js +2 -2
  85. package/lib/deploy/functions/pricing.js +3 -2
  86. package/lib/deploy/functions/prompts.js +2 -2
  87. package/lib/deploy/functions/release/fabricator.js +10 -9
  88. package/lib/deploy/functions/release/index.js +1 -1
  89. package/lib/deploy/functions/release/reporter.js +1 -1
  90. package/lib/deploy/functions/runtimes/discovery/parsing.js +19 -8
  91. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +112 -107
  92. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +1 -1
  93. package/lib/deploy/functions/runtimes/node/parseTriggers.js +54 -26
  94. package/lib/deploy/functions/runtimes/node/versioning.js +4 -4
  95. package/lib/deploy/functions/services/storage.js +6 -0
  96. package/lib/deploy/functions/validate.js +1 -1
  97. package/lib/deploy/hosting/convertConfig.js +8 -1
  98. package/lib/deploy/hosting/deploy.js +10 -9
  99. package/lib/deploy/hosting/uploader.js +2 -2
  100. package/lib/deploy/hosting/validate.js +2 -2
  101. package/lib/deploy/index.js +7 -7
  102. package/lib/deploy/lifecycleHooks.js +5 -2
  103. package/lib/deploy/storage/prepare.js +5 -3
  104. package/lib/deploy/storage/release.js +7 -6
  105. package/lib/emulator/auth/index.js +7 -2
  106. package/lib/emulator/auth/operations.js +10 -10
  107. package/lib/emulator/commandUtils.js +33 -16
  108. package/lib/emulator/constants.js +14 -6
  109. package/lib/emulator/controller.js +55 -18
  110. package/lib/emulator/databaseEmulator.js +1 -1
  111. package/lib/emulator/downloadableEmulators.js +8 -8
  112. package/lib/emulator/emulatorLogger.js +1 -1
  113. package/lib/emulator/eventarcEmulator.js +148 -0
  114. package/lib/emulator/extensionsEmulator.js +5 -3
  115. package/lib/emulator/firestoreEmulator.js +1 -1
  116. package/lib/emulator/functionsEmulator.js +66 -14
  117. package/lib/emulator/functionsEmulatorShared.js +12 -12
  118. package/lib/emulator/hub.js +7 -3
  119. package/lib/emulator/hubClient.js +2 -2
  120. package/lib/emulator/hubExport.js +22 -2
  121. package/lib/emulator/loggingEmulator.js +2 -2
  122. package/lib/emulator/registry.js +1 -0
  123. package/lib/emulator/storage/apis/firebase.js +13 -1
  124. package/lib/emulator/storage/apis/gcloud.js +15 -8
  125. package/lib/emulator/storage/files.js +25 -4
  126. package/lib/emulator/storage/metadata.js +6 -6
  127. package/lib/emulator/storage/multipart.js +4 -3
  128. package/lib/emulator/storage/rules/runtime.js +3 -3
  129. package/lib/emulator/storage/rules/utils.js +4 -2
  130. package/lib/emulator/storage/server.js +2 -1
  131. package/lib/emulator/storage/upload.js +1 -0
  132. package/lib/emulator/types.js +4 -0
  133. package/lib/emulator/ui.js +7 -2
  134. package/lib/ensureApiEnabled.js +7 -7
  135. package/lib/extensions/askUserForConsent.js +1 -1
  136. package/lib/extensions/askUserForEventsConfig.js +1 -1
  137. package/lib/extensions/askUserForParam.js +1 -1
  138. package/lib/extensions/changelog.js +2 -2
  139. package/lib/extensions/checkProjectBilling.js +2 -2
  140. package/lib/extensions/displayExtensionInfo.js +2 -102
  141. package/lib/extensions/emulator/triggerHelper.js +2 -2
  142. package/lib/extensions/extensionsApi.js +3 -2
  143. package/lib/extensions/extensionsHelper.js +30 -2
  144. package/lib/extensions/listExtensions.js +1 -1
  145. package/lib/extensions/manifest.js +1 -1
  146. package/lib/extensions/metricsUtils.js +1 -1
  147. package/lib/extensions/paramHelper.js +1 -1
  148. package/lib/extensions/updateHelper.js +3 -9
  149. package/lib/extensions/warnings.js +2 -2
  150. package/lib/fetchMOTD.js +1 -1
  151. package/lib/firestore/delete.js +1 -1
  152. package/lib/firestore/indexes.js +2 -2
  153. package/lib/firestore/validator.js +1 -1
  154. package/lib/functional.js +16 -1
  155. package/lib/functions/env.js +3 -3
  156. package/lib/functions/runtimeConfigExport.js +1 -1
  157. package/lib/functionsConfig.js +1 -1
  158. package/lib/functionsConfigClone.js +1 -1
  159. package/lib/functionsShellCommandAction.js +1 -1
  160. package/lib/gcp/cloudfunctions.js +24 -11
  161. package/lib/gcp/cloudfunctionsv2.js +48 -24
  162. package/lib/gcp/cloudscheduler.js +58 -22
  163. package/lib/gcp/cloudtasks.js +21 -4
  164. package/lib/gcp/proto.js +18 -6
  165. package/lib/gcp/resourceManager.js +25 -3
  166. package/lib/gcp/serviceusage.js +2 -2
  167. package/lib/handlePreviewToggles.js +4 -4
  168. package/lib/hosting/implicitInit.js +1 -1
  169. package/lib/hosting/normalizedHostingConfigs.js +3 -3
  170. package/lib/index.js +2 -2
  171. package/lib/init/features/database.js +1 -1
  172. package/lib/init/features/emulators.js +1 -1
  173. package/lib/init/features/firestore/index.js +2 -2
  174. package/lib/init/features/firestore/indexes.js +1 -1
  175. package/lib/init/features/firestore/rules.js +1 -1
  176. package/lib/init/features/functions/golang.js +1 -1
  177. package/lib/init/features/functions/index.js +8 -1
  178. package/lib/init/features/hosting/github.js +9 -9
  179. package/lib/init/features/hosting/index.js +1 -1
  180. package/lib/init/features/project.js +1 -1
  181. package/lib/init/features/remoteconfig.js +1 -1
  182. package/lib/init/features/storage.js +1 -1
  183. package/lib/init/index.js +1 -1
  184. package/lib/logError.js +3 -3
  185. package/lib/management/projects.js +1 -1
  186. package/lib/parseBoltRules.js +1 -1
  187. package/lib/previews.js +1 -1
  188. package/lib/profileReport.js +2 -2
  189. package/lib/projectUtils.js +1 -1
  190. package/lib/rc.js +1 -1
  191. package/lib/requireAuth.js +1 -1
  192. package/lib/requireDatabaseInstance.js +2 -2
  193. package/lib/requirePermissions.js +2 -2
  194. package/lib/rulesDeploy.js +49 -13
  195. package/lib/serve/hosting.js +2 -1
  196. package/lib/serve/index.js +15 -0
  197. package/lib/track.js +119 -3
  198. package/lib/utils.js +24 -11
  199. package/npm-shrinkwrap.json +14 -294
  200. package/package.json +5 -4
  201. package/schema/firebase-config.json +12 -0
  202. package/templates/extensions/CHANGELOG.md +1 -7
  203. package/templates/hosting/init.js +6 -2
@@ -32,10 +32,14 @@ class EmulatorHub {
32
32
  res.json(body);
33
33
  });
34
34
  this.hub.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
35
- const exportPath = req.body.path;
36
- utils.logLabeledBullet("emulators", `Received export request. Exporting data to ${exportPath}.`);
35
+ const path = req.body.path;
36
+ const initiatedBy = req.body.initiatedBy || "unknown";
37
+ utils.logLabeledBullet("emulators", `Received export request. Exporting data to ${path}.`);
37
38
  try {
38
- await new hubExport_1.HubExport(this.args.projectId, exportPath).exportAll();
39
+ await new hubExport_1.HubExport(this.args.projectId, {
40
+ path,
41
+ initiatedBy,
42
+ }).exportAll();
39
43
  utils.logLabeledSuccess("emulators", "Export complete.");
40
44
  res.status(200).send({
41
45
  message: "OK",
@@ -21,9 +21,9 @@ class EmulatorHubClient {
21
21
  const res = await apiClient.get(hub_1.EmulatorHub.PATH_EMULATORS);
22
22
  return res.body;
23
23
  }
24
- async postExport(path) {
24
+ async postExport(options) {
25
25
  const apiClient = new apiv2_1.Client({ urlPrefix: this.origin, auth: false });
26
- await apiClient.post(hub_1.EmulatorHub.PATH_EXPORT, { path });
26
+ await apiClient.post(hub_1.EmulatorHub.PATH_EXPORT, options);
27
27
  }
28
28
  get origin() {
29
29
  const locator = this.assertLocator();
@@ -13,10 +13,12 @@ const hub_1 = require("./hub");
13
13
  const downloadableEmulators_1 = require("./downloadableEmulators");
14
14
  const rimraf = require("rimraf");
15
15
  const apiv2_1 = require("../apiv2");
16
+ const track_1 = require("../track");
16
17
  class HubExport {
17
- constructor(projectId, exportPath) {
18
+ constructor(projectId, options) {
18
19
  this.projectId = projectId;
19
- this.exportPath = exportPath;
20
+ this.options = options;
21
+ this.exportPath = options.path;
20
22
  this.tmpDir = fs.mkdtempSync(`firebase-export-${new Date().getTime()}`);
21
23
  }
22
24
  static readMetadata(exportPath) {
@@ -66,6 +68,10 @@ class HubExport {
66
68
  if (!fs.existsSync(this.exportPath)) {
67
69
  fs.mkdirSync(this.exportPath);
68
70
  }
71
+ void (0, track_1.trackEmulator)("emulator_export", {
72
+ initiated_by: this.options.initiatedBy,
73
+ emulator_name: types_1.Emulators.HUB,
74
+ });
69
75
  const metadataPath = path.join(this.tmpDir, HubExport.METADATA_FILE_NAME);
70
76
  fs.writeFileSync(metadataPath, JSON.stringify(metadata, undefined, 2));
71
77
  logger_1.logger.debug(`hubExport: swapping ${this.tmpDir} with ${this.exportPath}`);
@@ -73,6 +79,10 @@ class HubExport {
73
79
  fse.moveSync(this.tmpDir, this.exportPath);
74
80
  }
75
81
  async exportFirestore(metadata) {
82
+ void (0, track_1.trackEmulator)("emulator_export", {
83
+ initiated_by: this.options.initiatedBy,
84
+ emulator_name: types_1.Emulators.FIRESTORE,
85
+ });
76
86
  const firestoreInfo = registry_1.EmulatorRegistry.get(types_1.Emulators.FIRESTORE).getInfo();
77
87
  const firestoreHost = `http://${registry_1.EmulatorRegistry.getInfoHostString(firestoreInfo)}`;
78
88
  const firestoreExportBody = {
@@ -115,6 +125,11 @@ class HubExport {
115
125
  namespacesToExport.push(ns);
116
126
  }
117
127
  }
128
+ void (0, track_1.trackEmulator)("emulator_export", {
129
+ initiated_by: this.options.initiatedBy,
130
+ emulator_name: types_1.Emulators.DATABASE,
131
+ count: namespacesToExport.length,
132
+ });
118
133
  const dbExportPath = path.join(this.tmpDir, metadata.database.path);
119
134
  if (!fs.existsSync(dbExportPath)) {
120
135
  fs.mkdirSync(dbExportPath);
@@ -132,6 +147,10 @@ class HubExport {
132
147
  }
133
148
  }
134
149
  async exportAuth(metadata) {
150
+ void (0, track_1.trackEmulator)("emulator_export", {
151
+ initiated_by: this.options.initiatedBy,
152
+ emulator_name: types_1.Emulators.AUTH,
153
+ });
135
154
  const { host, port } = registry_1.EmulatorRegistry.get(types_1.Emulators.AUTH).getInfo();
136
155
  const authExportPath = path.join(this.tmpDir, metadata.auth.path);
137
156
  if (!fs.existsSync(authExportPath)) {
@@ -164,6 +183,7 @@ class HubExport {
164
183
  const storageHost = `http://${registry_1.EmulatorRegistry.getInfoHostString(storageEmulator.getInfo())}`;
165
184
  const storageExportBody = {
166
185
  path: storageExportPath,
186
+ initiatedBy: this.options.initiatedBy,
167
187
  };
168
188
  const client = new apiv2_1.Client({ urlPrefix: storageHost, auth: false });
169
189
  const res = await client.request({
@@ -7,7 +7,7 @@ const triple_beam_1 = require("triple-beam");
7
7
  const WebSocket = require("ws");
8
8
  const TransportStream = require("winston-transport");
9
9
  const logger_1 = require("../logger");
10
- const ansiStrip = require("cli-color/strip");
10
+ const stripAnsi = require("strip-ansi");
11
11
  class LoggingEmulator {
12
12
  constructor(args) {
13
13
  this.args = args;
@@ -106,7 +106,7 @@ class WebSocketTransport extends TransportStream {
106
106
  if (bundle.data && bundle.data.metadata && bundle.data.metadata.message) {
107
107
  bundle.message = bundle.data.metadata.message;
108
108
  }
109
- bundle.message = ansiStrip(bundle.message);
109
+ bundle.message = stripAnsi(bundle.message);
110
110
  this.history.push(bundle);
111
111
  this.connections.forEach((ws) => {
112
112
  ws.send(JSON.stringify(bundle));
@@ -39,6 +39,7 @@ class EmulatorRegistry {
39
39
  pubsub: 3.2,
40
40
  auth: 3.3,
41
41
  storage: 3.5,
42
+ eventarc: 3.6,
42
43
  hub: 4,
43
44
  logging: 5,
44
45
  };
@@ -121,10 +121,22 @@ function createFirebaseEndpoints(emulator) {
121
121
  var _a, _b, _c, _d;
122
122
  const maxResults = (_a = req.query.maxResults) === null || _a === void 0 ? void 0 : _a.toString();
123
123
  let listResponse;
124
+ let prefix = "";
125
+ if (req.query.prefix) {
126
+ prefix = req.query.prefix.toString();
127
+ if (prefix.charAt(prefix.length - 1) !== "/") {
128
+ return res.status(400).json({
129
+ error: {
130
+ code: 400,
131
+ message: "The prefix parameter is required to be empty or ends with a single / character.",
132
+ },
133
+ });
134
+ }
135
+ }
124
136
  try {
125
137
  listResponse = await storageLayer.listObjects({
126
138
  bucketId: req.params.bucketId,
127
- prefix: req.query.prefix ? req.query.prefix.toString() : "",
139
+ prefix: prefix,
128
140
  delimiter: req.query.delimiter ? req.query.delimiter.toString() : "",
129
141
  pageToken: (_b = req.query.pageToken) === null || _b === void 0 ? void 0 : _b.toString(),
130
142
  maxResults: maxResults ? +maxResults : undefined,
@@ -177,14 +177,6 @@ function createCloudEndpoints(emulator) {
177
177
  });
178
178
  });
179
179
  gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
180
- if (!req.query.name) {
181
- res.sendStatus(400);
182
- return;
183
- }
184
- let name = req.query.name.toString();
185
- if (name.startsWith("/")) {
186
- name = name.slice(1);
187
- }
188
180
  const contentTypeHeader = req.header("content-type") || req.header("x-upload-content-type");
189
181
  if (!contentTypeHeader) {
190
182
  return res.sendStatus(400);
@@ -194,6 +186,11 @@ function createCloudEndpoints(emulator) {
194
186
  if (emulatorInfo === undefined) {
195
187
  return res.sendStatus(500);
196
188
  }
189
+ const name = getIncomingFileNameFromRequest(req.query, req.body);
190
+ if (name === undefined) {
191
+ res.sendStatus(400);
192
+ return;
193
+ }
197
194
  const upload = uploadService.startResumableUpload({
198
195
  bucketId: req.params.bucketId,
199
196
  objectId: name,
@@ -223,6 +220,11 @@ function createCloudEndpoints(emulator) {
223
220
  }
224
221
  throw err;
225
222
  }
223
+ const name = getIncomingFileNameFromRequest(req.query, JSON.parse(metadataRaw));
224
+ if (name === undefined) {
225
+ res.sendStatus(400);
226
+ return;
227
+ }
226
228
  const upload = uploadService.multipartUpload({
227
229
  bucketId: req.params.bucketId,
228
230
  objectId: name,
@@ -363,3 +365,8 @@ function sendObjectNotFound(req, res) {
363
365
  });
364
366
  }
365
367
  }
368
+ function getIncomingFileNameFromRequest(query, metadata) {
369
+ var _a;
370
+ const name = ((_a = query === null || query === void 0 ? void 0 : query.name) === null || _a === void 0 ? void 0 : _a.toString()) || (metadata === null || metadata === void 0 ? void 0 : metadata.name);
371
+ return (name === null || name === void 0 ? void 0 : name.startsWith("/")) ? name.slice(1) : name;
372
+ }
@@ -17,6 +17,8 @@ const logger_1 = require("../../logger");
17
17
  const adminSdkConfig_1 = require("../adminSdkConfig");
18
18
  const types_1 = require("./rules/types");
19
19
  const upload_1 = require("./upload");
20
+ const track_1 = require("../../track");
21
+ const types_2 = require("../types");
20
22
  class StoredFile {
21
23
  constructor(metadata) {
22
24
  this.metadata = metadata;
@@ -29,6 +31,7 @@ class StoredFile {
29
31
  }
30
32
  }
31
33
  exports.StoredFile = StoredFile;
34
+ const TRAILING_SLASHES_PATTERN = /\/+$/;
32
35
  class StorageLayer {
33
36
  constructor(_projectId, _files, _buckets, _rulesValidator, _adminCredsValidator, _persistence, _cloudFunctions) {
34
37
  this._projectId = _projectId;
@@ -137,6 +140,7 @@ class StorageLayer {
137
140
  if (upload.status !== upload_1.UploadStatus.FINISHED) {
138
141
  throw new Error(`Unexpected upload status encountered: ${upload.status}.`);
139
142
  }
143
+ const storedMetadata = this.getMetadata(upload.bucketId, upload.objectId);
140
144
  const filePath = this.path(upload.bucketId, upload.objectId);
141
145
  const metadata = new metadata_1.StoredFileMetadata({
142
146
  name: upload.objectId,
@@ -149,7 +153,10 @@ class StorageLayer {
149
153
  customMetadata: upload.metadata.metadata,
150
154
  }, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
151
155
  metadata.update(upload.metadata, false);
152
- 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);
156
+ const authorized = await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), upload.bucketId, types_1.RulesetOperationMethod.CREATE, {
157
+ before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
158
+ after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource(),
159
+ }, upload.authorization);
153
160
  if (!authorized) {
154
161
  this._persistence.deleteFile(upload.path);
155
162
  throw new errors_1.ForbiddenError();
@@ -203,7 +210,7 @@ class StorageLayer {
203
210
  async listObjects(request) {
204
211
  var _a;
205
212
  const { bucketId, prefix, delimiter, pageToken, authorization } = request;
206
- const authorized = await this._rulesValidator.validate(["b", bucketId, "o", prefix].join("/"), bucketId, types_1.RulesetOperationMethod.LIST, {}, authorization);
213
+ const authorized = await this._rulesValidator.validate(["b", bucketId, "o", prefix.replace(TRAILING_SLASHES_PATTERN, "")].join("/"), bucketId, types_1.RulesetOperationMethod.LIST, {}, authorization, delimiter);
207
214
  if (!authorized) {
208
215
  throw new errors_1.ForbiddenError();
209
216
  }
@@ -287,7 +294,7 @@ class StorageLayer {
287
294
  get dirPath() {
288
295
  return this._persistence.dirPath;
289
296
  }
290
- async export(storageExportPath) {
297
+ async export(storageExportPath, options) {
291
298
  var e_1, _a;
292
299
  const bucketsList = {
293
300
  buckets: [],
@@ -295,6 +302,11 @@ class StorageLayer {
295
302
  for (const b of await this.listBuckets()) {
296
303
  bucketsList.buckets.push({ id: b.id });
297
304
  }
305
+ void (0, track_1.trackEmulator)("emulator_export", {
306
+ initiated_by: options.initiatedBy,
307
+ emulator_name: types_2.Emulators.STORAGE,
308
+ count: bucketsList.buckets.length,
309
+ });
298
310
  const bucketsFilePath = path.join(storageExportPath, "buckets.json");
299
311
  await fse.writeFile(bucketsFilePath, JSON.stringify(bucketsList, undefined, 2));
300
312
  const blobsDirPath = path.join(storageExportPath, "blobs");
@@ -318,15 +330,24 @@ class StorageLayer {
318
330
  finally { if (e_1) throw e_1.error; }
319
331
  }
320
332
  }
321
- import(storageExportPath) {
333
+ import(storageExportPath, options) {
322
334
  const bucketsFile = path.join(storageExportPath, "buckets.json");
323
335
  const bucketsList = JSON.parse((0, fs_1.readFileSync)(bucketsFile, "utf-8"));
336
+ void (0, track_1.trackEmulator)("emulator_import", {
337
+ initiated_by: options.initiatedBy,
338
+ emulator_name: types_2.Emulators.STORAGE,
339
+ count: bucketsList.buckets.length,
340
+ });
324
341
  for (const b of bucketsList.buckets) {
325
342
  const bucketMetadata = new metadata_1.CloudStorageBucketMetadata(b.id);
326
343
  this._buckets.set(b.id, bucketMetadata);
327
344
  }
328
345
  const metadataDir = path.join(storageExportPath, "metadata");
329
346
  const blobsDir = path.join(storageExportPath, "blobs");
347
+ if (!(0, fs_1.existsSync)(metadataDir) || !(0, fs_1.existsSync)(blobsDir)) {
348
+ logger_1.logger.warn(`Could not find metadata directory at "${metadataDir}" and/or blobs directory at "${blobsDir}".`);
349
+ return;
350
+ }
330
351
  const metadataList = this.walkDirSync(metadataDir);
331
352
  const dotJson = ".json";
332
353
  for (const f of metadataList) {
@@ -287,15 +287,15 @@ class CloudStorageObjectMetadata {
287
287
  }
288
288
  exports.CloudStorageObjectMetadata = CloudStorageObjectMetadata;
289
289
  function toSerializedDate(d) {
290
- const day = `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d
291
- .getDate()
290
+ const day = `${d.getUTCFullYear()}-${(d.getUTCMonth() + 1).toString().padStart(2, "0")}-${d
291
+ .getUTCDate()
292
292
  .toString()
293
293
  .padStart(2, "0")}`;
294
- const time = `${d.getHours().toString().padStart(2, "0")}:${d
295
- .getMinutes()
294
+ const time = `${d.getUTCHours().toString().padStart(2, "0")}:${d
295
+ .getUTCMinutes()
296
296
  .toString()
297
- .padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}.${d
298
- .getMilliseconds()
297
+ .padStart(2, "0")}:${d.getUTCSeconds().toString().padStart(2, "0")}.${d
298
+ .getUTCMilliseconds()
299
299
  .toString()
300
300
  .padStart(3, "0")}`;
301
301
  return `${day}T${time}Z`;
@@ -23,7 +23,8 @@ function splitBufferByDelimiter(buffer, delimiter, maxResults = -1) {
23
23
  return bufferParts;
24
24
  }
25
25
  function parseMultipartRequestBody(boundaryId, body) {
26
- const boundaryString = `--${boundaryId}`;
26
+ const cleanBoundaryId = boundaryId.replace(/^["'](.+(?=["']$))["']$/, "$1");
27
+ const boundaryString = `--${cleanBoundaryId}`;
27
28
  const bodyParts = splitBufferByDelimiter(body, boundaryString).map((buf) => {
28
29
  return Buffer.from(buf.slice(2));
29
30
  });
@@ -35,8 +36,8 @@ function parseMultipartRequestBody(boundaryId, body) {
35
36
  }
36
37
  function parseMultipartRequestBodyPart(bodyPart) {
37
38
  const sections = splitBufferByDelimiter(bodyPart, LINE_SEPARATOR, 3);
38
- const contentTypeRaw = sections[0].toString();
39
- if (!contentTypeRaw.startsWith("Content-Type: ")) {
39
+ const contentTypeRaw = sections[0].toString().toLowerCase();
40
+ if (!contentTypeRaw.startsWith("content-type: ")) {
40
41
  throw new Error(`Failed to parse multipart request body part. Missing content type.`);
41
42
  }
42
43
  const dataRaw = Buffer.from(sections[2]).slice(0, sections[2].byteLength - LINE_SEPARATOR.length);
@@ -65,12 +65,12 @@ class StorageRulesRuntime {
65
65
  get alive() {
66
66
  return this._alive;
67
67
  }
68
- async start(auto_download = true) {
68
+ async start(autoDownload = true) {
69
69
  var _a, _b;
70
70
  const downloadDetails = downloadableEmulators_1.DownloadDetails[types_2.Emulators.STORAGE];
71
71
  const hasEmulator = fs.existsSync(downloadDetails.downloadPath);
72
72
  if (!hasEmulator) {
73
- if (auto_download) {
73
+ if (autoDownload) {
74
74
  if (process.env.CI) {
75
75
  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
76
  }
@@ -199,6 +199,7 @@ class StorageRulesRuntime {
199
199
  service: "firebase.storage",
200
200
  path: opts.path,
201
201
  method: opts.method,
202
+ delimiter: opts.delimiter,
202
203
  variables: runtimeVariables,
203
204
  },
204
205
  };
@@ -293,7 +294,6 @@ function createRequestExpressionValue(opts) {
293
294
  segments: opts.path
294
295
  .split("/")
295
296
  .filter((s) => s)
296
- .slice(3)
297
297
  .map((simple) => ({
298
298
  simple,
299
299
  })),
@@ -5,13 +5,14 @@ const emulatorLogger_1 = require("../../emulatorLogger");
5
5
  const types_1 = require("../../types");
6
6
  function getFirebaseRulesValidator(rulesetProvider) {
7
7
  return {
8
- validate: async (path, bucketId, method, variableOverrides, authorization) => {
8
+ validate: async (path, bucketId, method, variableOverrides, authorization, delimiter) => {
9
9
  return await isPermitted({
10
10
  ruleset: rulesetProvider(bucketId),
11
11
  file: variableOverrides,
12
12
  path,
13
13
  method,
14
14
  authorization,
15
+ delimiter,
15
16
  });
16
17
  },
17
18
  };
@@ -19,7 +20,7 @@ function getFirebaseRulesValidator(rulesetProvider) {
19
20
  exports.getFirebaseRulesValidator = getFirebaseRulesValidator;
20
21
  function getAdminOnlyFirebaseRulesValidator() {
21
22
  return {
22
- validate: (_path, _bucketId, _method, _variableOverrides, _authorization) => {
23
+ validate: (_path, _bucketId, _method, _variableOverrides, _authorization, delimiter) => {
23
24
  return Promise.resolve(true);
24
25
  },
25
26
  };
@@ -42,6 +43,7 @@ async function isPermitted(opts) {
42
43
  path: opts.path,
43
44
  file: opts.file,
44
45
  token: opts.authorization ? opts.authorization.split(" ")[1] : undefined,
46
+ delimiter: opts.delimiter,
45
47
  });
46
48
  if (issues.exist()) {
47
49
  issues.all.forEach((warningOrError) => {
@@ -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) => {
@@ -65,6 +65,7 @@ class UploadService {
65
65
  };
66
66
  this._uploads.set(upload.id, upload);
67
67
  this._persistence.deleteFile(upload.path, true);
68
+ this._persistence.appendBytes(upload.path, Buffer.alloc(0));
68
69
  return upload;
69
70
  }
70
71
  continueResumableUpload(uploadId, dataRaw) {
@@ -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,
@@ -56,6 +59,7 @@ exports.EMULATORS_SUPPORTED_BY_USE_EMULATOR = [
56
59
  Emulators.DATABASE,
57
60
  Emulators.FIRESTORE,
58
61
  Emulators.FUNCTIONS,
62
+ Emulators.STORAGE,
59
63
  ];
60
64
  exports.ALL_EMULATORS = [
61
65
  Emulators.HUB,
@@ -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();
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enableApiURI = exports.ensure = exports.check = exports.POLL_SETTINGS = void 0;
4
- const cli_color_1 = require("cli-color");
4
+ const colorette_1 = require("colorette");
5
5
  const track_1 = require("./track");
6
6
  const api_1 = require("./api");
7
7
  const apiv2_1 = require("./apiv2");
@@ -21,7 +21,7 @@ async function check(projectId, apiName, prefix, silent = false) {
21
21
  });
22
22
  const isEnabled = res.body.state === "ENABLED";
23
23
  if (isEnabled && !silent) {
24
- utils.logLabeledSuccess(prefix, `required API ${(0, cli_color_1.bold)(apiName)} is enabled`);
24
+ utils.logLabeledSuccess(prefix, `required API ${(0, colorette_1.bold)(apiName)} is enabled`);
25
25
  }
26
26
  return isEnabled;
27
27
  }
@@ -34,7 +34,7 @@ async function enable(projectId, apiName) {
34
34
  }
35
35
  catch (err) {
36
36
  if ((0, error_1.isBillingError)(err)) {
37
- throw new error_1.FirebaseError(`Your project ${(0, cli_color_1.bold)(projectId)} must be on the Blaze (pay-as-you-go) plan to complete this command. Required API ${(0, cli_color_1.bold)(apiName)} can't be enabled until the upgrade is complete. To upgrade, visit the following URL:
37
+ throw new error_1.FirebaseError(`Your project ${(0, colorette_1.bold)(projectId)} must be on the Blaze (pay-as-you-go) plan to complete this command. Required API ${(0, colorette_1.bold)(apiName)} can't be enabled until the upgrade is complete. To upgrade, visit the following URL:
38
38
 
39
39
  https://console.firebase.google.com/project/${projectId}/usage/details`);
40
40
  }
@@ -54,27 +54,27 @@ async function pollCheckEnabled(projectId, apiName, prefix, silent, enablementRe
54
54
  return;
55
55
  }
56
56
  if (!silent) {
57
- utils.logLabeledBullet(prefix, `waiting for API ${(0, cli_color_1.bold)(apiName)} to activate...`);
57
+ utils.logLabeledBullet(prefix, `waiting for API ${(0, colorette_1.bold)(apiName)} to activate...`);
58
58
  }
59
59
  return pollCheckEnabled(projectId, apiName, prefix, silent, enablementRetries, pollRetries + 1);
60
60
  }
61
61
  async function enableApiWithRetries(projectId, apiName, prefix, silent, enablementRetries = 0) {
62
62
  if (enablementRetries > 1) {
63
- throw new error_1.FirebaseError(`Timed out waiting for API ${(0, cli_color_1.bold)(apiName)} to enable. Please try again in a few minutes.`);
63
+ throw new error_1.FirebaseError(`Timed out waiting for API ${(0, colorette_1.bold)(apiName)} to enable. Please try again in a few minutes.`);
64
64
  }
65
65
  await enable(projectId, apiName);
66
66
  return pollCheckEnabled(projectId, apiName, prefix, silent, enablementRetries);
67
67
  }
68
68
  async function ensure(projectId, apiName, prefix, silent = false) {
69
69
  if (!silent) {
70
- utils.logLabeledBullet(prefix, `ensuring required API ${(0, cli_color_1.bold)(apiName)} is enabled...`);
70
+ utils.logLabeledBullet(prefix, `ensuring required API ${(0, colorette_1.bold)(apiName)} is enabled...`);
71
71
  }
72
72
  const isEnabled = await check(projectId, apiName, prefix, silent);
73
73
  if (isEnabled) {
74
74
  return;
75
75
  }
76
76
  if (!silent) {
77
- utils.logLabeledWarning(prefix, `missing required API ${(0, cli_color_1.bold)(apiName)}. Enabling now...`);
77
+ utils.logLabeledWarning(prefix, `missing required API ${(0, colorette_1.bold)(apiName)}. Enabling now...`);
78
78
  }
79
79
  return enableApiWithRetries(projectId, apiName, prefix, silent);
80
80
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.promptForPublisherTOS = exports.displayApis = exports.displayRoles = exports.retrieveRoleInfo = exports.formatDescription = void 0;
4
- const clc = require("cli-color");
4
+ const clc = require("colorette");
5
5
  const { marked } = require("marked");
6
6
  const TerminalRenderer = require("marked-terminal");
7
7
  const error_1 = require("../error");
@@ -4,7 +4,7 @@ exports.askForEventArcLocation = exports.askShouldCollectEventsConfig = exports.
4
4
  const prompt_1 = require("../prompt");
5
5
  const extensionsApi = require("../extensions/extensionsApi");
6
6
  const utils = require("../utils");
7
- const clc = require("cli-color");
7
+ const clc = require("colorette");
8
8
  const logger_1 = require("../logger");
9
9
  const { marked } = require("marked");
10
10
  function checkAllowedEventTypesResponse(response, validEvents) {
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getInquirerDefault = exports.promptCreateSecret = exports.askForParam = exports.ask = exports.checkResponse = exports.SecretLocation = void 0;
4
4
  const _ = require("lodash");
5
- const clc = require("cli-color");
5
+ const clc = require("colorette");
6
6
  const { marked } = require("marked");
7
7
  const types_1 = require("./types");
8
8
  const secretManagerApi = require("../gcp/secretManager");
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseChangelog = exports.getLocalChangelog = exports.breakingChangesInUpdate = exports.displayReleaseNotes = exports.getReleaseNotesForUpdate = void 0;
4
- const clc = require("cli-color");
4
+ const clc = require("colorette");
5
5
  const { marked } = require("marked");
6
6
  const path = require("path");
7
7
  const semver = require("semver");
@@ -39,7 +39,7 @@ function displayReleaseNotes(releaseNotes, fromVersion) {
39
39
  const table = new Table({ head: ["Version", "What's New"], style: { head: ["yellow", "bold"] } });
40
40
  for (const [version, note] of Object.entries(releaseNotes)) {
41
41
  if (breakingVersions.includes(version)) {
42
- table.push([clc.yellow.bold(version), marked(note)]);
42
+ table.push([clc.yellow(clc.bold(version)), marked(note)]);
43
43
  }
44
44
  else {
45
45
  table.push([version, marked(note)]);
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enableBilling = void 0;
4
- const clc = require("cli-color");
4
+ const clc = require("colorette");
5
5
  const opn = require("open");
6
6
  const cloudbilling = require("../gcp/cloudbilling");
7
7
  const error_1 = require("../error");
@@ -59,7 +59,7 @@ async function setUpBillingAccount(projectId) {
59
59
  logger_1.logger.info();
60
60
  logger_1.logger.info(`Extension require your project to be upgraded to the Blaze plan. Please visit the following link to add a billing account:`);
61
61
  logger_1.logger.info();
62
- logger_1.logger.info(clc.bold.underline(billingURL));
62
+ logger_1.logger.info(clc.bold(clc.underline(billingURL)));
63
63
  logger_1.logger.info();
64
64
  const open = await prompt.promptOnce({
65
65
  name: "open-url",