firebase-tools 10.2.2 → 10.4.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 (76) hide show
  1. package/lib/commands/deploy.js +1 -1
  2. package/lib/commands/experimental-functions-shell.js +1 -1
  3. package/lib/commands/ext-configure.js +68 -7
  4. package/lib/commands/ext-export.js +10 -9
  5. package/lib/commands/ext-install.js +73 -9
  6. package/lib/commands/ext-uninstall.js +9 -0
  7. package/lib/commands/ext-update.js +58 -3
  8. package/lib/commands/functions-config-export.js +2 -2
  9. package/lib/commands/functions-shell.js +1 -1
  10. package/lib/commands/hosting-channel-create.js +2 -2
  11. package/lib/commands/hosting-channel-delete.js +2 -2
  12. package/lib/commands/hosting-channel-deploy.js +2 -2
  13. package/lib/commands/hosting-channel-list.js +2 -2
  14. package/lib/commands/hosting-channel-open.js +2 -2
  15. package/lib/commands/hosting-sites-delete.js +2 -2
  16. package/lib/commands/serve.js +1 -1
  17. package/lib/commands/target-apply.js +2 -2
  18. package/lib/commands/target-clear.js +2 -2
  19. package/lib/commands/target-remove.js +2 -2
  20. package/lib/commands/target.js +2 -2
  21. package/lib/config.js +9 -3
  22. package/lib/deploy/extensions/planner.js +15 -9
  23. package/lib/deploy/functions/backend.js +10 -1
  24. package/lib/deploy/functions/checkIam.js +4 -4
  25. package/lib/deploy/functions/prepare.js +2 -1
  26. package/lib/deploy/functions/release/fabricator.js +4 -4
  27. package/lib/deploy/functions/release/planner.js +34 -20
  28. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +6 -1
  29. package/lib/deploy/functions/runtimes/node/index.js +27 -0
  30. package/lib/deploy/functions/runtimes/node/parseTriggers.js +36 -13
  31. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  32. package/lib/deploy/functions/services/index.js +9 -1
  33. package/lib/deploy/functions/services/storage.js +10 -4
  34. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  35. package/lib/emulator/auth/apiSpec.js +37 -0
  36. package/lib/emulator/commandUtils.js +2 -2
  37. package/lib/emulator/constants.js +1 -0
  38. package/lib/emulator/controller.js +9 -7
  39. package/lib/emulator/extensions/validation.js +37 -2
  40. package/lib/emulator/extensionsEmulator.js +47 -9
  41. package/lib/emulator/functionsEmulator.js +17 -12
  42. package/lib/emulator/functionsEmulatorShared.js +34 -11
  43. package/lib/emulator/storage/apis/firebase.js +316 -341
  44. package/lib/emulator/storage/apis/gcloud.js +238 -113
  45. package/lib/emulator/storage/crc.js +5 -1
  46. package/lib/emulator/storage/errors.js +9 -0
  47. package/lib/emulator/storage/files.js +161 -304
  48. package/lib/emulator/storage/index.js +25 -74
  49. package/lib/emulator/storage/metadata.js +63 -49
  50. package/lib/emulator/storage/multipart.js +62 -0
  51. package/lib/emulator/storage/persistence.js +78 -0
  52. package/lib/emulator/storage/rules/config.js +34 -0
  53. package/lib/emulator/storage/rules/manager.js +98 -0
  54. package/lib/emulator/storage/rules/runtime.js +4 -0
  55. package/lib/emulator/storage/rules/utils.js +48 -0
  56. package/lib/emulator/storage/server.js +2 -2
  57. package/lib/emulator/storage/upload.js +106 -0
  58. package/lib/extensions/askUserForParam.js +77 -28
  59. package/lib/extensions/emulator/optionsHelper.js +35 -3
  60. package/lib/extensions/extensionsHelper.js +19 -10
  61. package/lib/extensions/manifest.js +142 -14
  62. package/lib/extensions/paramHelper.js +32 -9
  63. package/lib/fsutils.js +14 -1
  64. package/lib/functions/env.js +4 -6
  65. package/lib/functions/events/v2.js +11 -0
  66. package/lib/gcp/cloudfunctions.js +20 -7
  67. package/lib/gcp/cloudfunctionsv2.js +30 -12
  68. package/lib/gcp/resourceManager.js +4 -4
  69. package/lib/requireConfig.js +11 -9
  70. package/lib/serve/functions.js +2 -1
  71. package/lib/utils.js +14 -1
  72. package/npm-shrinkwrap.json +2 -2
  73. package/package.json +1 -1
  74. package/lib/deploy/extensions/params.js +0 -42
  75. package/lib/deploy/functions/eventTypes.js +0 -10
  76. package/lib/prepareUpload.js +0 -44
@@ -7,9 +7,13 @@ const types_1 = require("../../types");
7
7
  const metadata_1 = require("../metadata");
8
8
  const registry_1 = require("../../registry");
9
9
  const emulatorLogger_1 = require("../../emulatorLogger");
10
+ const crc_1 = require("../crc");
11
+ const multipart_1 = require("../multipart");
12
+ const upload_1 = require("../upload");
13
+ const errors_1 = require("../errors");
10
14
  function createCloudEndpoints(emulator) {
11
15
  const gcloudStorageAPI = (0, express_1.Router)();
12
- const { storageLayer } = emulator;
16
+ const { storageLayer, uploadService } = emulator;
13
17
  gcloudStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
14
18
  storageLayer.createBucket(req.params[0]);
15
19
  next();
@@ -20,98 +24,158 @@ function createCloudEndpoints(emulator) {
20
24
  items: await storageLayer.listBuckets(),
21
25
  });
22
26
  });
23
- gcloudStorageAPI.get(["/b/:bucketId/o/:objectId", "/download/storage/v1/b/:bucketId/o/:objectId"], (req, res) => {
24
- const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
25
- if (!md) {
26
- res.sendStatus(404);
27
- return;
27
+ gcloudStorageAPI.get(["/b/:bucketId/o/:objectId", "/download/storage/v1/b/:bucketId/o/:objectId"], async (req, res) => {
28
+ let getObjectResponse;
29
+ try {
30
+ getObjectResponse = await storageLayer.handleGetObject({
31
+ bucketId: req.params.bucketId,
32
+ decodedObjectId: req.params.objectId,
33
+ }, true);
34
+ }
35
+ catch (err) {
36
+ if (err instanceof errors_1.NotFoundError) {
37
+ return sendObjectNotFound(req, res);
38
+ }
39
+ if (err instanceof errors_1.ForbiddenError) {
40
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
41
+ }
42
+ throw err;
28
43
  }
29
44
  if (req.query.alt === "media") {
30
- return sendFileBytes(md, storageLayer, req, res);
45
+ return sendFileBytes(getObjectResponse.metadata, getObjectResponse.data, req, res);
31
46
  }
32
- const outgoingMd = new metadata_1.CloudStorageObjectMetadata(md);
33
- res.json(outgoingMd).status(200).send();
34
- return;
47
+ return res.json(new metadata_1.CloudStorageObjectMetadata(getObjectResponse.metadata));
35
48
  });
36
- gcloudStorageAPI.patch("/b/:bucketId/o/:objectId", (req, res) => {
37
- const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
38
- if (!md) {
39
- res.sendStatus(404);
40
- return;
49
+ gcloudStorageAPI.patch("/b/:bucketId/o/:objectId", async (req, res) => {
50
+ let updatedMetadata;
51
+ try {
52
+ updatedMetadata = await storageLayer.handleUpdateObjectMetadata({
53
+ bucketId: req.params.bucketId,
54
+ decodedObjectId: req.params.objectId,
55
+ metadata: req.body,
56
+ }, true);
57
+ }
58
+ catch (err) {
59
+ if (err instanceof errors_1.NotFoundError) {
60
+ return sendObjectNotFound(req, res);
61
+ }
62
+ if (err instanceof errors_1.ForbiddenError) {
63
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
64
+ }
65
+ throw err;
41
66
  }
42
- md.update(req.body);
43
- const outgoingMetadata = new metadata_1.CloudStorageObjectMetadata(md);
44
- res.json(outgoingMetadata).status(200).send();
45
- return;
67
+ return res.json(new metadata_1.CloudStorageObjectMetadata(updatedMetadata));
46
68
  });
47
69
  gcloudStorageAPI.get("/b/:bucketId/o", (req, res) => {
48
70
  let maxRes = undefined;
49
71
  if (req.query.maxResults) {
50
72
  maxRes = +req.query.maxResults.toString();
51
73
  }
52
- const delimiter = req.query.delimiter ? req.query.delimiter.toString() : "/";
74
+ const delimiter = req.query.delimiter ? req.query.delimiter.toString() : "";
53
75
  const pageToken = req.query.pageToken ? req.query.pageToken.toString() : undefined;
54
76
  const prefix = req.query.prefix ? req.query.prefix.toString() : "";
55
77
  const listResult = storageLayer.listItems(req.params.bucketId, prefix, delimiter, pageToken, maxRes);
56
- res.json(listResult);
78
+ res.json(Object.assign(Object.assign({}, listResult), { kind: "#storage/objects" }));
57
79
  });
58
- gcloudStorageAPI.delete("/b/:bucketId/o/:objectId", (req, res) => {
59
- const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
60
- if (!md) {
61
- res.sendStatus(404);
62
- return;
80
+ gcloudStorageAPI.delete("/b/:bucketId/o/:objectId", async (req, res) => {
81
+ try {
82
+ await storageLayer.handleDeleteObject({
83
+ bucketId: req.params.bucketId,
84
+ decodedObjectId: req.params.objectId,
85
+ }, true);
63
86
  }
64
- storageLayer.deleteFile(req.params.bucketId, req.params.objectId);
65
- res.status(200).send();
87
+ catch (err) {
88
+ if (err instanceof errors_1.NotFoundError) {
89
+ return sendObjectNotFound(req, res);
90
+ }
91
+ if (err instanceof errors_1.ForbiddenError) {
92
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
93
+ }
94
+ throw err;
95
+ }
96
+ return res.sendStatus(204);
66
97
  });
67
- gcloudStorageAPI.put("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
68
- if (!req.query.upload_id) {
69
- res.sendStatus(400);
70
- return;
98
+ const reqBodyToBuffer = async (req) => {
99
+ if (req.body instanceof Buffer) {
100
+ return Buffer.from(req.body);
71
101
  }
72
- const uploadId = req.query.upload_id.toString();
73
102
  const bufs = [];
74
103
  req.on("data", (data) => {
75
104
  bufs.push(data);
76
105
  });
77
106
  await new Promise((resolve) => {
78
107
  req.on("end", () => {
79
- req.body = Buffer.concat(bufs);
80
108
  resolve();
81
109
  });
82
110
  });
83
- const upload = storageLayer.uploadBytes(uploadId, req.body);
84
- if (!upload) {
111
+ return Buffer.concat(bufs);
112
+ };
113
+ gcloudStorageAPI.put("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
114
+ if (!req.query.upload_id) {
85
115
  res.sendStatus(400);
86
116
  return;
87
117
  }
88
- const uploadedFile = storageLayer.finalizeUpload(upload);
89
- res.status(200).json(new metadata_1.CloudStorageObjectMetadata(uploadedFile.metadata)).send();
118
+ const uploadId = req.query.upload_id.toString();
119
+ let upload;
120
+ try {
121
+ uploadService.continueResumableUpload(uploadId, await reqBodyToBuffer(req));
122
+ upload = uploadService.finalizeResumableUpload(uploadId);
123
+ }
124
+ catch (err) {
125
+ if (err instanceof errors_1.NotFoundError) {
126
+ return res.sendStatus(404);
127
+ }
128
+ else if (err instanceof upload_1.UploadNotActiveError) {
129
+ return res.sendStatus(400);
130
+ }
131
+ throw err;
132
+ }
133
+ let metadata;
134
+ try {
135
+ metadata = await storageLayer.handleUploadObject(upload, true);
136
+ }
137
+ catch (err) {
138
+ if (err instanceof errors_1.ForbiddenError) {
139
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
140
+ }
141
+ throw err;
142
+ }
143
+ return res.json(new metadata_1.CloudStorageObjectMetadata(metadata));
90
144
  });
91
- gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", (req, res) => {
145
+ gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", async (req, res) => {
92
146
  var _a, _b;
93
147
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE).log("WARN_ONCE", "Cloud Storage ACLs are not supported in the Storage Emulator. All related methods will succeed, but have no effect.");
94
- const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
95
- if (!md) {
96
- res.sendStatus(404);
97
- return;
148
+ let getObjectResponse;
149
+ try {
150
+ getObjectResponse = await storageLayer.handleGetObject({
151
+ bucketId: req.params.bucketId,
152
+ decodedObjectId: req.params.objectId,
153
+ }, true);
154
+ }
155
+ catch (err) {
156
+ if (err instanceof errors_1.NotFoundError) {
157
+ return sendObjectNotFound(req, res);
158
+ }
159
+ if (err instanceof errors_1.ForbiddenError) {
160
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
161
+ }
162
+ throw err;
98
163
  }
99
- md.update({});
100
- res
101
- .json({
164
+ const { metadata } = getObjectResponse;
165
+ metadata.update({});
166
+ return res.json({
102
167
  kind: "storage#objectAccessControl",
103
- object: md.name,
104
- id: `${req.params.bucketId}/${md.name}/${md.generation}/allUsers`,
105
- 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}/storage/v1/b/${md.bucket}/o/${encodeURIComponent(md.name)}/acl/allUsers`,
106
- bucket: md.bucket,
168
+ object: metadata.name,
169
+ id: `${req.params.bucketId}/${metadata.name}/${metadata.generation}/allUsers`,
170
+ 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}/storage/v1/b/${metadata.bucket}/o/${encodeURIComponent(metadata.name)}/acl/allUsers`,
171
+ bucket: metadata.bucket,
107
172
  entity: req.body.entity,
108
173
  role: req.body.role,
109
174
  etag: "someEtag",
110
- generation: md.generation.toString(),
111
- })
112
- .status(200);
175
+ generation: metadata.generation.toString(),
176
+ });
113
177
  });
114
- gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", (req, res) => {
178
+ gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
115
179
  if (!req.query.name) {
116
180
  res.sendStatus(400);
117
181
  return;
@@ -120,69 +184,112 @@ function createCloudEndpoints(emulator) {
120
184
  if (name.startsWith("/")) {
121
185
  name = name.slice(1);
122
186
  }
123
- const contentType = req.header("content-type") || req.header("x-upload-content-type");
124
- if (!contentType) {
125
- res.sendStatus(400);
126
- return;
187
+ const contentTypeHeader = req.header("content-type") || req.header("x-upload-content-type");
188
+ if (!contentTypeHeader) {
189
+ return res.sendStatus(400);
127
190
  }
128
191
  if (req.query.uploadType === "resumable") {
129
- const upload = storageLayer.startUpload(req.params.bucketId, name, contentType, req.body);
130
192
  const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
131
193
  if (emulatorInfo === undefined) {
132
- res.sendStatus(500);
133
- return;
194
+ return res.sendStatus(500);
134
195
  }
196
+ const upload = uploadService.startResumableUpload({
197
+ bucketId: req.params.bucketId,
198
+ objectId: name,
199
+ metadataRaw: JSON.stringify(req.body),
200
+ authorization: req.header("authorization"),
201
+ });
135
202
  const { host, port } = emulatorInfo;
136
- const uploadUrl = `http://${host}:${port}/upload/storage/v1/b/${upload.bucketId}/o?name=${upload.fileLocation}&uploadType=resumable&upload_id=${upload.uploadId}`;
137
- res.header("location", uploadUrl).status(200).send();
138
- return;
203
+ const uploadUrl = `http://${host}:${port}/upload/storage/v1/b/${req.params.bucketId}/o?name=${name}&uploadType=resumable&upload_id=${upload.id}`;
204
+ return res.header("location", uploadUrl).sendStatus(200);
139
205
  }
140
- if (!contentType.startsWith("multipart/related")) {
141
- res.sendStatus(400);
142
- return;
206
+ let metadataRaw;
207
+ let dataRaw;
208
+ try {
209
+ ({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await reqBodyToBuffer(req)));
143
210
  }
144
- const boundary = `--${contentType.split("boundary=")[1]}`;
145
- const bodyString = req.body.toString();
146
- const bodyStringParts = bodyString.split(boundary).filter((v) => v);
147
- const metadataString = bodyStringParts[0].split(/\r?\n/)[3];
148
- const blobParts = bodyStringParts[1].split(/\r?\n/);
149
- const blobContentTypeString = blobParts[1];
150
- if (!blobContentTypeString || !blobContentTypeString.startsWith("Content-Type: ")) {
151
- res.sendStatus(400);
152
- return;
211
+ catch (err) {
212
+ return res.status(400).json({
213
+ error: {
214
+ code: 400,
215
+ message: err,
216
+ },
217
+ });
153
218
  }
154
- const blobContentType = blobContentTypeString.slice("Content-Type: ".length);
155
- const bodyBuffer = req.body;
156
- const metadataSegment = `${boundary}${bodyString.split(boundary)[1]}`;
157
- const dataSegment = `${boundary}${bodyString.split(boundary).slice(2)[0]}`;
158
- const dataSegmentHeader = (dataSegment.match(/.+Content-Type:.+?\r?\n\r?\n/s) || [])[0];
159
- if (!dataSegmentHeader) {
160
- res.sendStatus(400);
161
- return;
219
+ const upload = uploadService.multipartUpload({
220
+ bucketId: req.params.bucketId,
221
+ objectId: name,
222
+ metadataRaw: metadataRaw,
223
+ dataRaw: dataRaw,
224
+ authorization: req.header("authorization"),
225
+ });
226
+ let metadata;
227
+ try {
228
+ metadata = await storageLayer.handleUploadObject(upload, true);
162
229
  }
163
- const bufferOffset = metadataSegment.length + dataSegmentHeader.length;
164
- const blobBytes = Buffer.from(bodyBuffer.slice(bufferOffset, -`\r\n${boundary}--`.length));
165
- const metadata = storageLayer.oneShotUpload(req.params.bucketId, name, blobContentType, JSON.parse(metadataString), blobBytes);
166
- if (!metadata) {
167
- res.sendStatus(400);
168
- return;
230
+ catch (err) {
231
+ if (err instanceof errors_1.ForbiddenError) {
232
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
233
+ }
234
+ throw err;
235
+ }
236
+ return res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata));
237
+ });
238
+ gcloudStorageAPI.get("/:bucketId/:objectId(**)", async (req, res) => {
239
+ let getObjectResponse;
240
+ try {
241
+ getObjectResponse = await storageLayer.handleGetObject({
242
+ bucketId: req.params.bucketId,
243
+ decodedObjectId: req.params.objectId,
244
+ }, true);
169
245
  }
170
- res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata)).send();
171
- return;
246
+ catch (err) {
247
+ if (err instanceof errors_1.NotFoundError) {
248
+ return sendObjectNotFound(req, res);
249
+ }
250
+ if (err instanceof errors_1.ForbiddenError) {
251
+ throw new Error("Request failed unexpectedly due to Firebase Rules.");
252
+ }
253
+ throw err;
254
+ }
255
+ return sendFileBytes(getObjectResponse.metadata, getObjectResponse.data, req, res);
172
256
  });
173
- gcloudStorageAPI.get("/:bucketId/:objectId(**)", (req, res) => {
257
+ gcloudStorageAPI.post("/b/:bucketId/o/:objectId/:method(rewriteTo|copyTo)/b/:destBucketId/o/:destObjectId", (req, res, next) => {
174
258
  const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
175
259
  if (!md) {
176
- res.sendStatus(404);
260
+ return sendObjectNotFound(req, res);
261
+ }
262
+ if (req.params.method === "rewriteTo" && req.query.rewriteToken) {
263
+ return next();
264
+ }
265
+ const metadata = storageLayer.copyFile(md, req.params.destBucketId, req.params.destObjectId, req.body);
266
+ if (!metadata) {
267
+ res.sendStatus(400);
177
268
  return;
178
269
  }
179
- return sendFileBytes(md, storageLayer, req, res);
270
+ const resource = new metadata_1.CloudStorageObjectMetadata(metadata);
271
+ res.status(200);
272
+ if (req.params.method === "copyTo") {
273
+ return res.json(resource);
274
+ }
275
+ else if (req.params.method === "rewriteTo") {
276
+ return res.json({
277
+ kind: "storage#rewriteResponse",
278
+ totalBytesRewritten: String(metadata.size),
279
+ objectSize: String(metadata.size),
280
+ done: true,
281
+ resource,
282
+ });
283
+ }
284
+ else {
285
+ return next();
286
+ }
180
287
  });
181
288
  gcloudStorageAPI.all("/**", (req, res) => {
182
289
  if (process.env.STORAGE_EMULATOR_DEBUG) {
183
290
  console.table(req.headers);
184
291
  console.log(req.method, req.url);
185
- res.json("endpoint not implemented");
292
+ res.status(501).json("endpoint not implemented");
186
293
  }
187
294
  else {
188
295
  res.sendStatus(501);
@@ -191,12 +298,7 @@ function createCloudEndpoints(emulator) {
191
298
  return gcloudStorageAPI;
192
299
  }
193
300
  exports.createCloudEndpoints = createCloudEndpoints;
194
- function sendFileBytes(md, storageLayer, req, res) {
195
- let data = storageLayer.getBytes(req.params.bucketId, req.params.objectId);
196
- if (!data) {
197
- res.sendStatus(404);
198
- return;
199
- }
301
+ function sendFileBytes(md, data, req, res) {
200
302
  const isGZipped = md.contentEncoding === "gzip";
201
303
  if (isGZipped) {
202
304
  data = (0, zlib_1.gunzipSync)(data);
@@ -204,19 +306,42 @@ function sendFileBytes(md, storageLayer, req, res) {
204
306
  res.setHeader("Accept-Ranges", "bytes");
205
307
  res.setHeader("Content-Type", md.contentType);
206
308
  res.setHeader("Content-Disposition", md.contentDisposition);
207
- res.setHeader("Content-Encoding", "identity");
208
- const byteRange = [...(req.header("range") || "").split("bytes="), "", ""];
209
- const [rangeStart, rangeEnd] = byteRange[1].split("-");
210
- if (rangeStart) {
211
- const range = {
212
- start: parseInt(rangeStart),
213
- end: rangeEnd ? parseInt(rangeEnd) : data.byteLength,
214
- };
215
- res.setHeader("Content-Range", `bytes ${range.start}-${range.end - 1}/${data.byteLength}`);
216
- res.status(206).end(data.slice(range.start, range.end));
309
+ res.setHeader("Content-Encoding", md.contentEncoding);
310
+ res.setHeader("ETag", md.etag);
311
+ res.setHeader("Cache-Control", md.cacheControl);
312
+ res.setHeader("x-goog-generation", `${md.generation}`);
313
+ res.setHeader("x-goog-metadatageneration", `${md.metageneration}`);
314
+ res.setHeader("x-goog-storage-class", md.storageClass);
315
+ res.setHeader("x-goog-hash", `crc32c=${(0, crc_1.crc32cToString)(md.crc32c)},md5=${md.md5Hash}`);
316
+ const byteRange = req.range(data.byteLength, { combine: true });
317
+ if (Array.isArray(byteRange) && byteRange.type === "bytes" && byteRange.length > 0) {
318
+ const range = byteRange[0];
319
+ res.setHeader("Content-Range", `${byteRange.type} ${range.start}-${range.end}/${data.byteLength}`);
320
+ res.status(206).end(data.slice(range.start, range.end + 1));
217
321
  }
218
322
  else {
219
323
  res.end(data);
220
324
  }
221
- return;
325
+ }
326
+ function sendObjectNotFound(req, res) {
327
+ res.status(404);
328
+ const message = `No such object: ${req.params.bucketId}/${req.params.objectId}`;
329
+ if (req.method === "GET" && req.query.alt === "media") {
330
+ res.send(message);
331
+ }
332
+ else {
333
+ res.json({
334
+ error: {
335
+ code: 404,
336
+ message,
337
+ errors: [
338
+ {
339
+ message,
340
+ domain: "global",
341
+ reason: "notFound",
342
+ },
343
+ ],
344
+ },
345
+ });
346
+ }
222
347
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.crc32c = void 0;
3
+ exports.crc32cToString = exports.crc32c = void 0;
4
4
  function makeCRCTable(poly) {
5
5
  let c;
6
6
  const crcTable = [];
@@ -24,3 +24,7 @@ function crc32c(bytes) {
24
24
  return (crc ^ -1) >>> 0;
25
25
  }
26
26
  exports.crc32c = crc32c;
27
+ function crc32cToString(crc32cValue) {
28
+ return "----" + Buffer.from([crc32cValue]).toString("base64");
29
+ }
30
+ exports.crc32cToString = crc32cToString;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ForbiddenError = exports.NotFoundError = void 0;
4
+ class NotFoundError extends Error {
5
+ }
6
+ exports.NotFoundError = NotFoundError;
7
+ class ForbiddenError extends Error {
8
+ }
9
+ exports.ForbiddenError = ForbiddenError;