firebase-tools 11.6.0 → 11.8.1

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 (52) hide show
  1. package/lib/auth.js +1 -1
  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/functions-delete.js +2 -0
  6. package/lib/commands/functions-secrets-get.js +2 -0
  7. package/lib/commands/index.js +3 -0
  8. package/lib/crashlytics/buildToolsJarHelper.js +51 -0
  9. package/lib/deploy/functions/backend.js +4 -4
  10. package/lib/deploy/functions/build.js +98 -17
  11. package/lib/deploy/functions/cache/applyHash.js +29 -0
  12. package/lib/deploy/functions/cache/hash.js +30 -0
  13. package/lib/deploy/functions/cel.js +249 -0
  14. package/lib/deploy/functions/checkIam.js +6 -5
  15. package/lib/deploy/functions/functionsDeployHelper.js +12 -1
  16. package/lib/deploy/functions/params.js +262 -105
  17. package/lib/deploy/functions/prepare.js +34 -4
  18. package/lib/deploy/functions/prepareFunctionsUpload.js +12 -4
  19. package/lib/deploy/functions/release/fabricator.js +39 -6
  20. package/lib/deploy/functions/release/index.js +2 -0
  21. package/lib/deploy/functions/release/planner.js +17 -0
  22. package/lib/deploy/functions/runtimes/discovery/index.js +1 -16
  23. package/lib/deploy/functions/runtimes/discovery/parsing.js +16 -0
  24. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +59 -131
  25. package/lib/deploy/functions/runtimes/node/parseTriggers.js +10 -1
  26. package/lib/emulator/constants.js +1 -1
  27. package/lib/emulator/controller.js +6 -11
  28. package/lib/emulator/extensionsEmulator.js +1 -0
  29. package/lib/emulator/functionsEmulator.js +18 -59
  30. package/lib/emulator/functionsEmulatorRuntime.js +12 -23
  31. package/lib/emulator/functionsRuntimeWorker.js +38 -7
  32. package/lib/emulator/storage/apis/firebase.js +145 -129
  33. package/lib/emulator/storage/apis/gcloud.js +102 -42
  34. package/lib/emulator/storage/files.js +25 -15
  35. package/lib/emulator/storage/metadata.js +86 -56
  36. package/lib/emulator/storage/multipart.js +2 -2
  37. package/lib/emulator/storage/rules/runtime.js +10 -2
  38. package/lib/emulator/storage/upload.js +45 -9
  39. package/lib/extensions/extensionsHelper.js +1 -1
  40. package/lib/functions/constants.js +14 -0
  41. package/lib/functions/env.js +9 -9
  42. package/lib/functions/secrets.js +8 -1
  43. package/lib/gcp/cloudfunctions.js +15 -18
  44. package/lib/gcp/cloudfunctionsv2.js +15 -18
  45. package/lib/gcp/cloudscheduler.js +32 -14
  46. package/lib/gcp/secretManager.js +15 -1
  47. package/lib/gcp/storage.js +15 -1
  48. package/lib/previews.js +1 -1
  49. package/lib/track.js +3 -0
  50. package/npm-shrinkwrap.json +563 -30
  51. package/package.json +7 -5
  52. package/templates/init/storage/storage.rules +1 -1
@@ -54,7 +54,11 @@ class FunctionsEmulator {
54
54
  const mode = this.args.debugPort
55
55
  ? types_1.FunctionsExecutionMode.SEQUENTIAL
56
56
  : types_1.FunctionsExecutionMode.AUTO;
57
- this.workerPool = new functionsRuntimeWorker_1.RuntimeWorkerPool(mode);
57
+ this.workerPools = {};
58
+ for (const backend of this.args.emulatableBackends) {
59
+ const pool = new functionsRuntimeWorker_1.RuntimeWorkerPool(mode);
60
+ this.workerPools[backend.codebase] = pool;
61
+ }
58
62
  this.workQueue = new workQueue_1.WorkQueue(mode);
59
63
  }
60
64
  static getHttpFunctionUrl(host, port, projectId, name, region) {
@@ -241,7 +245,9 @@ class FunctionsEmulator {
241
245
  this.logger.logLabeled("WARN", "functions", "Functions emulator work queue did not empty before stopping");
242
246
  }
243
247
  this.workQueue.stop();
244
- this.workerPool.exit();
248
+ for (const pool of Object.values(this.workerPools)) {
249
+ pool.exit();
250
+ }
245
251
  if (this.destroyServer) {
246
252
  await this.destroyServer();
247
253
  }
@@ -273,7 +279,8 @@ class FunctionsEmulator {
273
279
  projectAlias: this.args.projectAlias,
274
280
  };
275
281
  const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
276
- const discoveredBackend = await (0, build_1.resolveBackend)(discoveredBuild, userEnvOpt, environment);
282
+ const resolution = await (0, build_1.resolveBackend)(discoveredBuild, userEnvOpt, environment);
283
+ const discoveredBackend = resolution.backend;
277
284
  const endpoints = backend.allEndpoints(discoveredBackend);
278
285
  (0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
279
286
  for (const e of endpoints) {
@@ -297,7 +304,7 @@ class FunctionsEmulator {
297
304
  this.logger.logLabeled("ERROR", "functions", `Failed to load function definition from source: ${e}`);
298
305
  return;
299
306
  }
300
- this.workerPool.refresh();
307
+ this.workerPools[emulatableBackend.codebase].refresh();
301
308
  this.blockingFunctionsConfig = {};
302
309
  const toSetup = triggerDefinitions.filter((definition) => {
303
310
  if (force) {
@@ -380,7 +387,7 @@ class FunctionsEmulator {
380
387
  }
381
388
  }
382
389
  if (this.args.debugPort) {
383
- this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
390
+ await this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
384
391
  }
385
392
  }
386
393
  addEventarcTrigger(projectId, key, eventTrigger) {
@@ -802,10 +809,11 @@ class FunctionsEmulator {
802
809
  return secretEnvs;
803
810
  }
804
811
  async invokeRuntime(backend, trigger, frb, opts) {
805
- if (!this.workerPool.readyForWork(trigger.id)) {
812
+ const pool = this.workerPools[backend.codebase];
813
+ if (!pool.readyForWork(trigger.id)) {
806
814
  await this.startRuntime(backend, opts, trigger);
807
815
  }
808
- return this.workerPool.submitWork(trigger.id, frb, opts);
816
+ return pool.submitWork(trigger.id, frb, opts);
809
817
  }
810
818
  async startRuntime(backend, opts, trigger) {
811
819
  var _a;
@@ -838,52 +846,18 @@ class FunctionsEmulator {
838
846
  env: Object.assign(Object.assign(Object.assign(Object.assign({ node: opts.nodeBinary }, process.env), runtimeEnv), secretEnvs), { PORT: socketPath }),
839
847
  stdio: ["pipe", "pipe", "pipe", "ipc"],
840
848
  });
841
- if (!childProcess.stderr) {
842
- throw new error_1.FirebaseError(`childProcess.stderr is undefined.`);
843
- }
844
- if (!childProcess.stdout) {
845
- throw new error_1.FirebaseError(`childProcess.stdout is undefined.`);
846
- }
847
- const buffers = {
848
- stderr: { pipe: childProcess.stderr, value: "" },
849
- stdout: { pipe: childProcess.stdout, value: "" },
850
- };
851
- const ipcBuffer = { value: "" };
852
- childProcess.on("message", (message) => {
853
- this.onData(childProcess, emitter, ipcBuffer, message);
854
- });
855
- for (const id in buffers) {
856
- if (buffers.hasOwnProperty(id)) {
857
- const buffer = buffers[id];
858
- buffer.pipe.on("data", (buf) => {
859
- this.onData(childProcess, emitter, buffer, buf);
860
- });
861
- }
862
- }
863
849
  const runtime = {
864
- pid: childProcess.pid,
865
- exit: new Promise((resolve) => {
866
- childProcess.on("exit", resolve);
867
- }),
850
+ process: childProcess,
868
851
  events: emitter,
869
852
  cwd: backend.functionsDir,
870
853
  socketPath,
871
- shutdown: () => {
872
- childProcess.kill();
873
- },
874
- kill: (signal) => {
875
- childProcess.kill(signal);
876
- emitter.emit("log", new types_1.EmulatorLog("SYSTEM", "runtime-status", "killed"));
877
- },
878
- send: (args) => {
879
- return childProcess.send(JSON.stringify(args));
880
- },
881
854
  };
882
855
  const extensionLogInfo = {
883
856
  instanceId: backend.extensionInstanceId,
884
857
  ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
885
858
  };
886
- this.workerPool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
859
+ const pool = this.workerPools[backend.codebase];
860
+ pool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
887
861
  return;
888
862
  }
889
863
  async disableBackgroundTriggers() {
@@ -1057,20 +1031,5 @@ class FunctionsEmulator {
1057
1031
  });
1058
1032
  await worker.waitForDone();
1059
1033
  }
1060
- onData(runtime, emitter, buffer, buf) {
1061
- buffer.value += buf.toString();
1062
- const lines = buffer.value.split("\n");
1063
- if (lines.length > 1) {
1064
- lines.slice(0, -1).forEach((line) => {
1065
- const log = types_1.EmulatorLog.fromJSON(line);
1066
- emitter.emit("log", log);
1067
- if (log.level === "FATAL") {
1068
- emitter.emit("log", new types_1.EmulatorLog("SYSTEM", "runtime-status", "killed"));
1069
- runtime.kill();
1070
- }
1071
- });
1072
- }
1073
- buffer.value = lines[lines.length - 1];
1074
- }
1075
1034
  }
1076
1035
  exports.FunctionsEmulator = FunctionsEmulator;
@@ -675,24 +675,19 @@ async function initializeRuntime() {
675
675
  await initializeFirebaseFunctionsStubs();
676
676
  await initializeFirebaseAdminStubs();
677
677
  }
678
- async function loadTriggers(serializedFunctionTrigger) {
678
+ async function loadTriggers() {
679
679
  let triggerModule;
680
- if (serializedFunctionTrigger) {
681
- triggerModule = eval(serializedFunctionTrigger)();
680
+ try {
681
+ triggerModule = require(process.cwd());
682
682
  }
683
- else {
684
- try {
685
- triggerModule = require(process.cwd());
686
- }
687
- catch (err) {
688
- if (err.code !== "ERR_REQUIRE_ESM") {
689
- await moduleResolutionDetective(err);
690
- throw err;
691
- }
692
- const modulePath = require.resolve(process.cwd());
693
- const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
694
- triggerModule = await dynamicImport(moduleURL);
683
+ catch (err) {
684
+ if (err.code !== "ERR_REQUIRE_ESM") {
685
+ await moduleResolutionDetective(err);
686
+ throw err;
695
687
  }
688
+ const modulePath = require.resolve(process.cwd());
689
+ const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
690
+ triggerModule = await dynamicImport(moduleURL);
696
691
  }
697
692
  return triggerModule;
698
693
  }
@@ -716,8 +711,7 @@ async function handleMessage(message) {
716
711
  }
717
712
  if (!functionModule) {
718
713
  try {
719
- const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
720
- functionModule = await loadTriggers(serializedTriggers);
714
+ functionModule = await loadTriggers();
721
715
  }
722
716
  catch (e) {
723
717
  logDebug(e);
@@ -739,12 +733,7 @@ async function handleMessage(message) {
739
733
  logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
740
734
  try {
741
735
  await invokeTrigger(trigger, runtimeArgs.frb);
742
- if (runtimeArgs.opts && runtimeArgs.opts.serializedTriggers) {
743
- await flushAndExit(0);
744
- }
745
- else {
746
- await goIdle();
747
- }
736
+ await goIdle();
748
737
  }
749
738
  catch (err) {
750
739
  new types_1.EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log();
@@ -30,21 +30,52 @@ class RuntimeWorker {
30
30
  }
31
31
  else if (this.state === RuntimeWorkerState.FINISHING) {
32
32
  this.log(`IDLE --> FINISHING`);
33
- this.runtime.shutdown();
33
+ this.runtime.process.kill();
34
34
  }
35
35
  }
36
36
  }
37
37
  });
38
- this.runtime.exit.then(() => {
38
+ const childProc = this.runtime.process;
39
+ let msgBuffer = "";
40
+ childProc.on("message", (msg) => {
41
+ msgBuffer = this.processStream(msg, msgBuffer);
42
+ });
43
+ let stdBuffer = "";
44
+ if (childProc.stdout) {
45
+ childProc.stdout.on("data", (data) => {
46
+ stdBuffer = this.processStream(data, stdBuffer);
47
+ });
48
+ }
49
+ if (childProc.stderr) {
50
+ childProc.stderr.on("data", (data) => {
51
+ stdBuffer = this.processStream(data, stdBuffer);
52
+ });
53
+ }
54
+ childProc.on("exit", () => {
39
55
  this.log("exited");
40
56
  this.state = RuntimeWorkerState.FINISHED;
41
57
  });
42
58
  }
59
+ processStream(s, buf) {
60
+ buf += s.toString();
61
+ const lines = buf.split("\n");
62
+ if (lines.length > 1) {
63
+ lines.slice(0, -1).forEach((line) => {
64
+ const log = types_1.EmulatorLog.fromJSON(line);
65
+ this.runtime.events.emit("log", log);
66
+ if (log.level === "FATAL") {
67
+ this.runtime.events.emit("log", new types_1.EmulatorLog("SYSTEM", "runtime-status", "killed"));
68
+ this.runtime.process.kill();
69
+ }
70
+ });
71
+ }
72
+ return lines[lines.length - 1];
73
+ }
43
74
  execute(frb, opts) {
44
75
  const execFrb = Object.assign({}, frb);
45
76
  const args = { frb: execFrb, opts };
46
77
  this.state = RuntimeWorkerState.BUSY;
47
- this.runtime.send(args);
78
+ this.runtime.process.send(JSON.stringify(args));
48
79
  }
49
80
  get state() {
50
81
  return this._state;
@@ -102,7 +133,7 @@ class RuntimeWorker {
102
133
  const timeout = new Promise((resolve, reject) => {
103
134
  setTimeout(() => {
104
135
  reject(new error_1.FirebaseError("Failed to load function."));
105
- }, 7000);
136
+ }, 30000);
106
137
  });
107
138
  while (true) {
108
139
  try {
@@ -142,7 +173,7 @@ class RuntimeWorkerPool {
142
173
  if (w.state === RuntimeWorkerState.IDLE) {
143
174
  this.log(`Shutting down IDLE worker (${w.key})`);
144
175
  w.state = RuntimeWorkerState.FINISHING;
145
- w.runtime.shutdown();
176
+ w.runtime.process.kill();
146
177
  }
147
178
  else if (w.state === RuntimeWorkerState.BUSY) {
148
179
  this.log(`Marking BUSY worker to finish (${w.key})`);
@@ -155,10 +186,10 @@ class RuntimeWorkerPool {
155
186
  for (const arr of this.workers.values()) {
156
187
  arr.forEach((w) => {
157
188
  if (w.state === RuntimeWorkerState.IDLE) {
158
- w.runtime.shutdown();
189
+ w.runtime.process.kill();
159
190
  }
160
191
  else {
161
- w.runtime.kill();
192
+ w.runtime.process.kill();
162
193
  }
163
194
  });
164
195
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createFirebaseEndpoints = void 0;
4
4
  const emulatorLogger_1 = require("../../emulatorLogger");
5
5
  const types_1 = require("../../types");
6
+ const uuid = require("uuid");
6
7
  const zlib_1 = require("zlib");
7
8
  const metadata_1 = require("../metadata");
8
9
  const express_1 = require("express");
@@ -93,8 +94,8 @@ function createFirebaseEndpoints(emulator) {
93
94
  }
94
95
  throw err;
95
96
  }
96
- if (!metadata.downloadTokens.length) {
97
- metadata.addDownloadToken();
97
+ if (metadata.downloadTokens.length === 0) {
98
+ metadata.addDownloadToken(true);
98
99
  }
99
100
  if (req.query.alt === "media") {
100
101
  const isGZipped = metadata.contentEncoding === "gzip";
@@ -102,7 +103,8 @@ function createFirebaseEndpoints(emulator) {
102
103
  data = (0, zlib_1.gunzipSync)(data);
103
104
  }
104
105
  res.setHeader("Accept-Ranges", "bytes");
105
- res.setHeader("Content-Type", metadata.contentType);
106
+ res.setHeader("Content-Type", metadata.contentType || "application/octet-stream");
107
+ res.setHeader("Content-Disposition", metadata.contentDisposition || "inline");
106
108
  setObjectHeaders(res, metadata, { "Content-Encoding": isGZipped ? "identity" : undefined });
107
109
  const byteRange = req.range(data.byteLength, { combine: true });
108
110
  if (Array.isArray(byteRange) && byteRange.type === "bytes" && byteRange.length > 0) {
@@ -165,47 +167,26 @@ function createFirebaseEndpoints(emulator) {
165
167
  });
166
168
  });
167
169
  const handleUpload = async (req, res) => {
168
- if (!req.query.name) {
169
- res.sendStatus(400);
170
- return;
171
- }
170
+ var _a, _b;
172
171
  const bucketId = req.params.bucketId;
173
- const objectId = req.query.name.toString();
174
- const uploadType = req.header("x-goog-upload-protocol");
175
- if (uploadType === "multipart") {
176
- const contentTypeHeader = req.header("content-type");
177
- if (!contentTypeHeader) {
178
- return res.sendStatus(400);
172
+ const objectId = req.params.objectId
173
+ ? decodeURIComponent(req.params.objectId)
174
+ : ((_a = req.query.name) === null || _a === void 0 ? void 0 : _a.toString()) || null;
175
+ const uploadType = (_b = req.header("x-goog-upload-protocol")) === null || _b === void 0 ? void 0 : _b.toString();
176
+ async function finalizeOneShotUpload(upload) {
177
+ var _a, _b, _c;
178
+ if (!((_b = (_a = upload.metadata) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.firebaseStorageDownloadTokens)) {
179
+ const customMetadata = Object.assign(Object.assign({}, (((_c = upload.metadata) === null || _c === void 0 ? void 0 : _c.metadata) || {})), { firebaseStorageDownloadTokens: uuid.v4() });
180
+ upload.metadata = Object.assign(Object.assign({}, (upload.metadata || {})), { metadata: customMetadata });
179
181
  }
180
- let metadataRaw;
181
- let dataRaw;
182
- try {
183
- ({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await (0, request_1.reqBodyToBuffer)(req)));
184
- }
185
- catch (err) {
186
- if (err instanceof Error) {
187
- return res.status(400).json({
188
- error: {
189
- code: 400,
190
- message: err.message,
191
- },
192
- });
193
- }
194
- throw err;
195
- }
196
- const upload = uploadService.multipartUpload({
197
- bucketId,
198
- objectId,
199
- metadataRaw,
200
- dataRaw: dataRaw,
201
- authorization: req.header("authorization"),
202
- });
203
182
  let metadata;
204
183
  try {
205
184
  metadata = await storageLayer.uploadObject(upload);
206
185
  }
207
186
  catch (err) {
208
187
  if (err instanceof errors_1.ForbiddenError) {
188
+ res.header("x-goog-upload-status", "final");
189
+ uploadService.setResponseCode(upload.id, 403);
209
190
  return res.status(403).json({
210
191
  error: {
211
192
  code: 403,
@@ -215,120 +196,153 @@ function createFirebaseEndpoints(emulator) {
215
196
  }
216
197
  throw err;
217
198
  }
218
- metadata.addDownloadToken(false);
199
+ if (!metadata.contentDisposition) {
200
+ metadata.contentDisposition = "inline";
201
+ }
219
202
  return res.status(200).json(new metadata_1.OutgoingFirebaseMetadata(metadata));
220
203
  }
221
- const uploadCommand = req.header("x-goog-upload-command");
222
- if (!uploadCommand) {
223
- res.sendStatus(400);
224
- return;
225
- }
226
- if (uploadCommand === "start") {
227
- const upload = uploadService.startResumableUpload({
228
- bucketId,
229
- objectId,
230
- metadataRaw: JSON.stringify(req.body),
231
- authorization: req.header("authorization"),
232
- });
233
- res.header("x-goog-upload-chunk-granularity", "10000");
234
- res.header("x-goog-upload-control-url", "");
235
- res.header("x-goog-upload-status", "active");
236
- res.header("x-gupload-uploadid", upload.id);
237
- const uploadUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE, req);
238
- uploadUrl.pathname = `/v0/b/${bucketId}/o`;
239
- uploadUrl.searchParams.set("name", objectId);
240
- uploadUrl.searchParams.set("upload_id", upload.id);
241
- uploadUrl.searchParams.set("upload_protocol", "resumable");
242
- res.header("x-goog-upload-url", uploadUrl.toString());
243
- return res.sendStatus(200);
244
- }
245
- if (!req.query.upload_id) {
246
- return res.sendStatus(400);
247
- }
248
- const uploadId = req.query.upload_id.toString();
249
- if (uploadCommand === "query") {
250
- let upload;
251
- try {
252
- upload = uploadService.getResumableUpload(uploadId);
253
- }
254
- catch (err) {
255
- if (err instanceof errors_1.NotFoundError) {
256
- return res.sendStatus(404);
204
+ if (uploadType === "resumable") {
205
+ const uploadCommand = req.header("x-goog-upload-command");
206
+ if (!uploadCommand) {
207
+ res.sendStatus(400);
208
+ return;
209
+ }
210
+ if (uploadCommand === "start") {
211
+ if (!objectId) {
212
+ res.sendStatus(400);
213
+ return;
257
214
  }
258
- throw err;
215
+ const upload = uploadService.startResumableUpload({
216
+ bucketId,
217
+ objectId,
218
+ metadataRaw: JSON.stringify(req.body),
219
+ authorization: req.header("authorization"),
220
+ });
221
+ res.header("x-goog-upload-chunk-granularity", "10000");
222
+ res.header("x-goog-upload-control-url", "");
223
+ res.header("x-goog-upload-status", "active");
224
+ res.header("x-gupload-uploadid", upload.id);
225
+ const uploadUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE, req);
226
+ uploadUrl.pathname = `/v0/b/${bucketId}/o`;
227
+ uploadUrl.searchParams.set("name", objectId);
228
+ uploadUrl.searchParams.set("upload_id", upload.id);
229
+ uploadUrl.searchParams.set("upload_protocol", "resumable");
230
+ res.header("x-goog-upload-url", uploadUrl.toString());
231
+ return res.sendStatus(200);
259
232
  }
260
- res.header("X-Goog-Upload-Size-Received", upload.size.toString());
261
- return res.sendStatus(200);
262
- }
263
- if (uploadCommand === "cancel") {
264
- try {
265
- uploadService.cancelResumableUpload(uploadId);
233
+ if (!req.query.upload_id) {
234
+ return res.sendStatus(400);
266
235
  }
267
- catch (err) {
268
- if (err instanceof errors_1.NotFoundError) {
269
- return res.sendStatus(404);
236
+ const uploadId = req.query.upload_id.toString();
237
+ if (uploadCommand === "query") {
238
+ let upload;
239
+ try {
240
+ upload = uploadService.getResumableUpload(uploadId);
270
241
  }
271
- else if (err instanceof upload_1.NotCancellableError) {
272
- return res.sendStatus(400);
242
+ catch (err) {
243
+ if (err instanceof errors_1.NotFoundError) {
244
+ return res.sendStatus(404);
245
+ }
246
+ throw err;
273
247
  }
274
- throw err;
275
- }
276
- return res.sendStatus(200);
277
- }
278
- if (uploadCommand.includes("upload")) {
279
- let upload;
280
- try {
281
- upload = uploadService.continueResumableUpload(uploadId, await (0, request_1.reqBodyToBuffer)(req));
248
+ res.header("X-Goog-Upload-Size-Received", upload.size.toString());
249
+ return res.sendStatus(200);
282
250
  }
283
- catch (err) {
284
- if (err instanceof errors_1.NotFoundError) {
285
- return res.sendStatus(404);
251
+ if (uploadCommand === "cancel") {
252
+ try {
253
+ uploadService.cancelResumableUpload(uploadId);
286
254
  }
287
- else if (err instanceof upload_1.UploadNotActiveError) {
288
- return res.sendStatus(400);
255
+ catch (err) {
256
+ if (err instanceof errors_1.NotFoundError) {
257
+ return res.sendStatus(404);
258
+ }
259
+ else if (err instanceof upload_1.NotCancellableError) {
260
+ return res.sendStatus(400);
261
+ }
262
+ throw err;
289
263
  }
290
- throw err;
291
- }
292
- if (!uploadCommand.includes("finalize")) {
293
- res.header("x-goog-upload-status", "active");
294
- res.header("x-gupload-uploadid", upload.id);
295
264
  return res.sendStatus(200);
296
265
  }
297
- }
298
- if (uploadCommand.includes("finalize")) {
299
- let upload;
300
- try {
301
- upload = uploadService.finalizeResumableUpload(uploadId);
266
+ if (uploadCommand.includes("upload")) {
267
+ let upload;
268
+ try {
269
+ upload = uploadService.continueResumableUpload(uploadId, await (0, request_1.reqBodyToBuffer)(req));
270
+ }
271
+ catch (err) {
272
+ if (err instanceof errors_1.NotFoundError) {
273
+ return res.sendStatus(404);
274
+ }
275
+ else if (err instanceof upload_1.UploadNotActiveError) {
276
+ return res.sendStatus(400);
277
+ }
278
+ throw err;
279
+ }
280
+ if (!uploadCommand.includes("finalize")) {
281
+ res.header("x-goog-upload-status", "active");
282
+ res.header("x-gupload-uploadid", upload.id);
283
+ return res.sendStatus(200);
284
+ }
302
285
  }
303
- catch (err) {
304
- if (err instanceof errors_1.NotFoundError) {
305
- return res.sendStatus(404);
286
+ if (uploadCommand.includes("finalize")) {
287
+ let upload;
288
+ try {
289
+ upload = uploadService.finalizeResumableUpload(uploadId);
306
290
  }
307
- else if (err instanceof upload_1.UploadNotActiveError) {
308
- return res.sendStatus(400);
291
+ catch (err) {
292
+ if (err instanceof errors_1.NotFoundError) {
293
+ uploadService.setResponseCode(uploadId, 404);
294
+ return res.sendStatus(404);
295
+ }
296
+ else if (err instanceof upload_1.UploadNotActiveError) {
297
+ uploadService.setResponseCode(uploadId, 400);
298
+ return res.sendStatus(400);
299
+ }
300
+ else if (err instanceof upload_1.UploadPreviouslyFinalizedError) {
301
+ res.header("x-goog-upload-status", "final");
302
+ return res.sendStatus(uploadService.getPreviousResponseCode(uploadId));
303
+ }
304
+ throw err;
309
305
  }
310
- throw err;
306
+ res.header("x-goog-upload-status", "final");
307
+ return await finalizeOneShotUpload(upload);
308
+ }
309
+ }
310
+ if (!objectId) {
311
+ res.sendStatus(400);
312
+ return;
313
+ }
314
+ if (uploadType === "multipart") {
315
+ const contentTypeHeader = req.header("content-type");
316
+ if (!contentTypeHeader) {
317
+ return res.sendStatus(400);
311
318
  }
312
- let storedMetadata;
319
+ let metadataRaw;
320
+ let dataRaw;
313
321
  try {
314
- storedMetadata = await storageLayer.uploadObject(upload);
322
+ ({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await (0, request_1.reqBodyToBuffer)(req)));
315
323
  }
316
324
  catch (err) {
317
- if (err instanceof errors_1.ForbiddenError) {
318
- return res.status(403).json({
319
- error: {
320
- code: 403,
321
- message: `Permission denied. No WRITE permission.`,
322
- },
323
- });
325
+ if (err instanceof Error) {
326
+ return res.status(400).send(err.message);
324
327
  }
325
328
  throw err;
326
329
  }
327
- res.header("x-goog-upload-status", "final");
328
- storedMetadata.addDownloadToken(false);
329
- return res.status(200).json(new metadata_1.OutgoingFirebaseMetadata(storedMetadata));
330
+ const upload = uploadService.multipartUpload({
331
+ bucketId,
332
+ objectId,
333
+ metadataRaw,
334
+ dataRaw: dataRaw,
335
+ authorization: req.header("authorization"),
336
+ });
337
+ return await finalizeOneShotUpload(upload);
330
338
  }
331
- return res.sendStatus(400);
339
+ const upload = uploadService.mediaUpload({
340
+ bucketId: req.params.bucketId,
341
+ objectId: objectId,
342
+ dataRaw: await (0, request_1.reqBodyToBuffer)(req),
343
+ authorization: req.header("authorization"),
344
+ });
345
+ return await finalizeOneShotUpload(upload);
332
346
  };
333
347
  const handleTokenRequest = (req, res) => {
334
348
  var _a, _b;
@@ -467,11 +481,13 @@ function createFirebaseEndpoints(emulator) {
467
481
  }
468
482
  exports.createFirebaseEndpoints = createFirebaseEndpoints;
469
483
  function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding": undefined }) {
470
- res.setHeader("Content-Disposition", metadata.contentDisposition);
484
+ if (metadata.contentDisposition) {
485
+ res.setHeader("Content-Disposition", metadata.contentDisposition);
486
+ }
471
487
  if (headerOverride["Content-Encoding"]) {
472
488
  res.setHeader("Content-Encoding", headerOverride["Content-Encoding"]);
473
489
  }
474
- else {
490
+ else if (metadata.contentEncoding) {
475
491
  res.setHeader("Content-Encoding", metadata.contentEncoding);
476
492
  }
477
493
  if (metadata.cacheControl) {