firebase-tools 10.2.1 → 10.3.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 (125) hide show
  1. package/lib/appdistribution/options-parser-util.js +1 -1
  2. package/lib/auth.js +3 -3
  3. package/lib/command.js +1 -1
  4. package/lib/commands/apps-android-sha-create.js +2 -2
  5. package/lib/commands/apps-sdkconfig.js +1 -1
  6. package/lib/commands/database-rules-list.js +2 -2
  7. package/lib/commands/emulators-start.js +1 -1
  8. package/lib/commands/ext-configure.js +67 -7
  9. package/lib/commands/ext-dev-init.js +49 -49
  10. package/lib/commands/ext-export.js +7 -2
  11. package/lib/commands/ext-install.js +173 -109
  12. package/lib/commands/ext-uninstall.js +17 -8
  13. package/lib/commands/ext-update.js +67 -12
  14. package/lib/commands/functions-config-export.js +1 -1
  15. package/lib/commands/hosting-clone.js +3 -3
  16. package/lib/commands/remoteconfig-get.js +1 -1
  17. package/lib/config.js +6 -3
  18. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  19. package/lib/deploy/extensions/planner.js +7 -6
  20. package/lib/deploy/extensions/tasks.js +1 -1
  21. package/lib/deploy/functions/backend.js +21 -5
  22. package/lib/deploy/functions/checkIam.js +5 -5
  23. package/lib/deploy/functions/containerCleaner.js +3 -3
  24. package/lib/deploy/functions/ensure.js +3 -3
  25. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  26. package/lib/deploy/functions/prepare.js +5 -3
  27. package/lib/deploy/functions/pricing.js +1 -1
  28. package/lib/deploy/functions/prompts.js +2 -2
  29. package/lib/deploy/functions/release/fabricator.js +7 -7
  30. package/lib/deploy/functions/release/index.js +1 -1
  31. package/lib/deploy/functions/release/planner.js +43 -26
  32. package/lib/deploy/functions/release/reporter.js +3 -0
  33. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  34. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  35. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
  36. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  37. package/lib/deploy/functions/runtimes/node/index.js +53 -0
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +64 -20
  40. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  41. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  42. package/lib/deploy/functions/services/index.js +9 -1
  43. package/lib/deploy/functions/services/storage.js +10 -4
  44. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  45. package/lib/deploy/functions/validate.js +3 -3
  46. package/lib/deploy/hosting/deploy.js +2 -2
  47. package/lib/deploy/hosting/hashcache.js +21 -19
  48. package/lib/deploy/hosting/uploader.js +5 -5
  49. package/lib/deploy/remoteconfig/functions.js +2 -2
  50. package/lib/emulator/auth/cloudFunctions.js +1 -1
  51. package/lib/emulator/auth/operations.js +1 -1
  52. package/lib/emulator/constants.js +4 -0
  53. package/lib/emulator/controller.js +54 -24
  54. package/lib/emulator/download.js +18 -1
  55. package/lib/emulator/downloadableEmulators.js +1 -1
  56. package/lib/emulator/emulatorLogger.js +12 -1
  57. package/lib/emulator/extensions/validation.js +70 -0
  58. package/lib/emulator/extensionsEmulator.js +175 -0
  59. package/lib/emulator/functionsEmulator.js +95 -43
  60. package/lib/emulator/functionsEmulatorRuntime.js +44 -36
  61. package/lib/emulator/functionsEmulatorShared.js +30 -11
  62. package/lib/emulator/functionsEmulatorShell.js +1 -1
  63. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  64. package/lib/emulator/functionsRuntimeWorker.js +2 -2
  65. package/lib/emulator/hub.js +4 -3
  66. package/lib/emulator/loggingEmulator.js +1 -1
  67. package/lib/emulator/pubsubEmulator.js +1 -1
  68. package/lib/emulator/registry.js +10 -2
  69. package/lib/emulator/storage/apis/firebase.js +314 -332
  70. package/lib/emulator/storage/apis/gcloud.js +241 -121
  71. package/lib/emulator/storage/crc.js +5 -1
  72. package/lib/emulator/storage/errors.js +9 -0
  73. package/lib/emulator/storage/files.js +159 -300
  74. package/lib/emulator/storage/index.js +27 -73
  75. package/lib/emulator/storage/metadata.js +65 -51
  76. package/lib/emulator/storage/multipart.js +62 -0
  77. package/lib/emulator/storage/persistence.js +78 -0
  78. package/lib/emulator/storage/rules/config.js +33 -0
  79. package/lib/emulator/storage/rules/manager.js +81 -0
  80. package/lib/emulator/storage/rules/runtime.js +8 -7
  81. package/lib/emulator/storage/rules/utils.js +48 -0
  82. package/lib/emulator/storage/server.js +2 -2
  83. package/lib/emulator/storage/upload.js +106 -0
  84. package/lib/emulator/types.js +3 -0
  85. package/lib/ensureApiEnabled.js +5 -1
  86. package/lib/error.js +1 -1
  87. package/lib/extensions/askUserForParam.js +31 -25
  88. package/lib/extensions/changelog.js +3 -1
  89. package/lib/extensions/checkProjectBilling.js +1 -1
  90. package/lib/extensions/displayExtensionInfo.js +1 -1
  91. package/lib/extensions/emulator/optionsHelper.js +56 -8
  92. package/lib/extensions/emulator/specHelper.js +10 -23
  93. package/lib/extensions/export.js +1 -51
  94. package/lib/extensions/extensionsApi.js +1 -1
  95. package/lib/extensions/extensionsHelper.js +32 -19
  96. package/lib/extensions/manifest.js +144 -0
  97. package/lib/extensions/metricsUtils.js +4 -4
  98. package/lib/extensions/paramHelper.js +34 -12
  99. package/lib/extensions/refs.js +1 -1
  100. package/lib/extensions/secretsUtils.js +3 -3
  101. package/lib/functional.js +1 -1
  102. package/lib/functions/env.js +6 -7
  103. package/lib/functions/events/v2.js +11 -0
  104. package/lib/gcp/cloudfunctions.js +43 -11
  105. package/lib/gcp/cloudfunctionsv2.js +48 -17
  106. package/lib/gcp/cloudtasks.js +1 -1
  107. package/lib/gcp/docker.js +2 -2
  108. package/lib/gcp/resourceManager.js +4 -4
  109. package/lib/gcp/run.js +2 -2
  110. package/lib/hosting/api.js +1 -1
  111. package/lib/hosting/proxy.js +2 -2
  112. package/lib/init/features/account.js +1 -1
  113. package/lib/management/database.js +1 -1
  114. package/lib/previews.js +1 -1
  115. package/lib/serve/functions.js +2 -1
  116. package/lib/utils.js +15 -2
  117. package/npm-shrinkwrap.json +786 -393
  118. package/package.json +1 -1
  119. package/schema/firebase-config.json +5 -0
  120. package/templates/init/functions/javascript/package.lint.json +3 -3
  121. package/templates/init/functions/javascript/package.nolint.json +2 -2
  122. package/templates/init/functions/typescript/package.lint.json +7 -7
  123. package/templates/init/functions/typescript/package.nolint.json +3 -3
  124. package/lib/deploy/extensions/params.js +0 -39
  125. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -7,19 +7,18 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
7
7
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
8
8
  };
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.Persistence = exports.StorageLayer = exports.UploadStatus = exports.ResumableUpload = exports.StoredFile = void 0;
10
+ exports.StorageLayer = exports.StoredFile = void 0;
11
11
  const fs_1 = require("fs");
12
- const os_1 = require("os");
13
- const uuid_1 = require("uuid");
14
12
  const list_1 = require("./list");
15
13
  const metadata_1 = require("./metadata");
14
+ const errors_1 = require("./errors");
16
15
  const path = require("path");
17
- const fs = require("fs");
18
16
  const fse = require("fs-extra");
19
- const rimraf = require("rimraf");
20
17
  const cloudFunctions_1 = require("./cloudFunctions");
21
18
  const logger_1 = require("../../logger");
22
19
  const adminSdkConfig_1 = require("../adminSdkConfig");
20
+ const types_1 = require("./rules/types");
21
+ const upload_1 = require("./upload");
23
22
  class StoredFile {
24
23
  constructor(metadata, path) {
25
24
  this.metadata = metadata;
@@ -39,69 +38,17 @@ class StoredFile {
39
38
  }
40
39
  }
41
40
  exports.StoredFile = StoredFile;
42
- class ResumableUpload {
43
- constructor(bucketId, objectId, uploadId, contentType, metadata) {
44
- this._currentBytesUploaded = 0;
45
- this._status = UploadStatus.ACTIVE;
46
- this._bucketId = bucketId;
47
- this._objectId = objectId;
48
- this._uploadId = uploadId;
49
- this._contentType = contentType;
50
- this._metadata = metadata;
51
- this._fileLocation = encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
52
- this._currentBytesUploaded = 0;
53
- }
54
- get uploadId() {
55
- return this._uploadId;
56
- }
57
- get metadata() {
58
- return this._metadata;
59
- }
60
- get bucketId() {
61
- return this._bucketId;
62
- }
63
- get objectId() {
64
- return this._objectId;
65
- }
66
- get contentType() {
67
- return this._contentType;
68
- }
69
- set contentType(contentType) {
70
- this._contentType = contentType;
71
- }
72
- get currentBytesUploaded() {
73
- return this._currentBytesUploaded;
74
- }
75
- set currentBytesUploaded(value) {
76
- this._currentBytesUploaded = value;
77
- }
78
- set status(status) {
79
- this._status = status;
80
- }
81
- get status() {
82
- return this._status;
83
- }
84
- get fileLocation() {
85
- return this._fileLocation;
86
- }
87
- }
88
- exports.ResumableUpload = ResumableUpload;
89
- var UploadStatus;
90
- (function (UploadStatus) {
91
- UploadStatus[UploadStatus["ACTIVE"] = 0] = "ACTIVE";
92
- UploadStatus[UploadStatus["CANCELLED"] = 1] = "CANCELLED";
93
- UploadStatus[UploadStatus["FINISHED"] = 2] = "FINISHED";
94
- })(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {}));
95
41
  class StorageLayer {
96
- constructor(_projectId) {
42
+ constructor(_projectId, _rulesValidator, _adminCredsValidator, _persistence) {
97
43
  this._projectId = _projectId;
44
+ this._rulesValidator = _rulesValidator;
45
+ this._adminCredsValidator = _adminCredsValidator;
46
+ this._persistence = _persistence;
98
47
  this.reset();
99
48
  this._cloudFunctions = new cloudFunctions_1.StorageCloudFunctions(this._projectId);
100
49
  }
101
50
  reset() {
102
51
  this._files = new Map();
103
- this._persistence = new Persistence(`${(0, os_1.tmpdir)()}/firebase/storage/blobs`);
104
- this._uploads = new Map();
105
52
  this._buckets = new Map();
106
53
  }
107
54
  createBucket(id) {
@@ -110,7 +57,7 @@ class StorageLayer {
110
57
  }
111
58
  }
112
59
  async listBuckets() {
113
- if (this._buckets.size == 0) {
60
+ if (this._buckets.size === 0) {
114
61
  let adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this._projectId);
115
62
  if (!adminSdkConfig) {
116
63
  adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this._projectId);
@@ -119,6 +66,22 @@ class StorageLayer {
119
66
  }
120
67
  return [...this._buckets.values()];
121
68
  }
69
+ async handleGetObject(request, skipAuth = false) {
70
+ var _a;
71
+ const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
72
+ const hasValidDownloadToken = ((metadata === null || metadata === void 0 ? void 0 : metadata.downloadTokens) || []).includes((_a = request.downloadToken) !== null && _a !== void 0 ? _a : "");
73
+ let authorized = skipAuth || hasValidDownloadToken;
74
+ if (!authorized) {
75
+ authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.GET, { before: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, request.authorization);
76
+ }
77
+ if (!authorized) {
78
+ throw new errors_1.ForbiddenError("Failed auth");
79
+ }
80
+ if (!metadata) {
81
+ throw new errors_1.NotFoundError("File not found");
82
+ }
83
+ return { metadata: metadata, data: this.getBytes(request.bucketId, request.decodedObjectId) };
84
+ }
122
85
  getMetadata(bucket, object) {
123
86
  const key = this.path(bucket, object);
124
87
  const val = this._files.get(key);
@@ -136,34 +99,17 @@ class StorageLayer {
136
99
  }
137
100
  return undefined;
138
101
  }
139
- public(value) {
140
- this._files = value;
141
- }
142
- startUpload(bucket, object, contentType, metadata) {
143
- const uploadId = (0, uuid_1.v4)();
144
- const upload = new ResumableUpload(bucket, object, uploadId, contentType, metadata);
145
- this._uploads.set(uploadId, upload);
146
- return upload;
147
- }
148
- queryUpload(uploadId) {
149
- return this._uploads.get(uploadId);
150
- }
151
- cancelUpload(uploadId) {
152
- const upload = this._uploads.get(uploadId);
153
- if (!upload) {
154
- return undefined;
102
+ async handleDeleteObject(request, skipAuth = false) {
103
+ const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
104
+ const authorized = skipAuth ||
105
+ (await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, request.authorization));
106
+ if (!authorized) {
107
+ throw new errors_1.ForbiddenError();
155
108
  }
156
- upload.status = UploadStatus.CANCELLED;
157
- this._persistence.deleteFile(upload.fileLocation);
158
- }
159
- uploadBytes(uploadId, bytes) {
160
- const upload = this._uploads.get(uploadId);
161
- if (!upload) {
162
- return undefined;
163
- }
164
- this._persistence.appendBytes(upload.fileLocation, bytes, upload.currentBytesUploaded);
165
- upload.currentBytesUploaded += bytes.byteLength;
166
- return upload;
109
+ if (!storedMetadata) {
110
+ throw new errors_1.NotFoundError();
111
+ }
112
+ this.deleteFile(request.bucketId, request.decodedObjectId);
167
113
  }
168
114
  deleteFile(bucketId, objectId) {
169
115
  const isFolder = objectId.toLowerCase().endsWith("%2f");
@@ -175,7 +121,7 @@ class StorageLayer {
175
121
  filePath += "%2F";
176
122
  }
177
123
  const file = this._files.get(filePath);
178
- if (file == undefined) {
124
+ if (file === undefined) {
179
125
  return false;
180
126
  }
181
127
  else {
@@ -185,89 +131,130 @@ class StorageLayer {
185
131
  return true;
186
132
  }
187
133
  }
188
- async deleteAll() {
189
- return this._persistence.deleteAll();
134
+ async handleUpdateObjectMetadata(request, skipAuth = false) {
135
+ const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
136
+ const authorized = skipAuth ||
137
+ (await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.UPDATE, {
138
+ before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
139
+ after: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(request.metadata),
140
+ }, request.authorization));
141
+ if (!authorized) {
142
+ throw new errors_1.ForbiddenError();
143
+ }
144
+ if (!storedMetadata) {
145
+ throw new errors_1.NotFoundError();
146
+ }
147
+ storedMetadata.update(request.metadata);
148
+ return storedMetadata;
190
149
  }
191
- finalizeUpload(uploadId) {
192
- const upload = this._uploads.get(uploadId);
193
- if (!upload) {
194
- return undefined;
150
+ async handleUploadObject(upload, skipAuth = false) {
151
+ if (upload.status !== upload_1.UploadStatus.FINISHED) {
152
+ throw new Error(`Unexpected upload status encountered: ${upload.status}.`);
195
153
  }
196
- upload.status = UploadStatus.FINISHED;
197
154
  const filePath = this.path(upload.bucketId, upload.objectId);
198
- const bytes = this._persistence.readBytes(upload.fileLocation, upload.currentBytesUploaded);
199
- const finalMetadata = new metadata_1.StoredFileMetadata({
155
+ const metadata = new metadata_1.StoredFileMetadata({
200
156
  name: upload.objectId,
201
157
  bucket: upload.bucketId,
202
- contentType: "",
158
+ contentType: upload.metadata.contentType || "application/octet-stream",
159
+ contentDisposition: upload.metadata.contentDisposition,
203
160
  contentEncoding: upload.metadata.contentEncoding,
161
+ contentLanguage: upload.metadata.contentLanguage,
162
+ cacheControl: upload.metadata.cacheControl,
204
163
  customMetadata: upload.metadata.metadata,
205
- }, this._cloudFunctions, bytes, upload.metadata);
206
- const file = new StoredFile(finalMetadata, filePath);
207
- this._files.set(filePath, file);
164
+ }, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
165
+ const authorized = skipAuth ||
166
+ (await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization));
167
+ if (!authorized) {
168
+ this._persistence.deleteFile(upload.path);
169
+ throw new errors_1.ForbiddenError();
170
+ }
208
171
  this._persistence.deleteFile(filePath, true);
209
- this._persistence.renameFile(upload.fileLocation, filePath);
210
- this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
211
- return { upload: upload, file: file };
172
+ this._persistence.renameFile(upload.path, filePath);
173
+ this._files.set(filePath, new StoredFile(metadata, this._persistence.getDiskPath(filePath)));
174
+ this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(metadata));
175
+ return metadata;
212
176
  }
213
- oneShotUpload(bucket, object, contentType, incomingMetadata, bytes) {
214
- const filePath = this.path(bucket, object);
177
+ copyFile(sourceFile, destinationBucket, destinationObject, incomingMetadata) {
178
+ const filePath = this.path(destinationBucket, destinationObject);
215
179
  this._persistence.deleteFile(filePath, true);
180
+ const bytes = this.getBytes(sourceFile.bucket, sourceFile.name);
216
181
  this._persistence.appendBytes(filePath, bytes);
217
- const md = new metadata_1.StoredFileMetadata({
218
- name: object,
219
- bucket: bucket,
220
- contentType: incomingMetadata.contentType || "application/octet-stream",
221
- contentEncoding: incomingMetadata.contentEncoding,
222
- customMetadata: incomingMetadata.metadata,
182
+ const newMetadata = Object.assign(Object.assign(Object.assign({}, sourceFile), { metadata: sourceFile.customMetadata }), incomingMetadata);
183
+ if (sourceFile.downloadTokens.length &&
184
+ !((incomingMetadata === null || incomingMetadata === void 0 ? void 0 : incomingMetadata.metadata) && Object.keys(incomingMetadata === null || incomingMetadata === void 0 ? void 0 : incomingMetadata.metadata).length)) {
185
+ if (!newMetadata.metadata)
186
+ newMetadata.metadata = {};
187
+ newMetadata.metadata.firebaseStorageDownloadTokens = sourceFile.downloadTokens.join(",");
188
+ }
189
+ if (newMetadata.metadata) {
190
+ for (const [k, v] of Object.entries(newMetadata.metadata)) {
191
+ if (v === null)
192
+ newMetadata.metadata[k] = "";
193
+ }
194
+ }
195
+ const copiedFileMetadata = new metadata_1.StoredFileMetadata({
196
+ name: destinationObject,
197
+ bucket: destinationBucket,
198
+ contentType: newMetadata.contentType || "application/octet-stream",
199
+ contentDisposition: newMetadata.contentDisposition,
200
+ contentEncoding: newMetadata.contentEncoding,
201
+ contentLanguage: newMetadata.contentLanguage,
202
+ cacheControl: newMetadata.cacheControl,
203
+ customMetadata: newMetadata.metadata,
223
204
  }, this._cloudFunctions, bytes, incomingMetadata);
224
- const file = new StoredFile(md, this._persistence.getDiskPath(filePath));
205
+ const file = new StoredFile(copiedFileMetadata, this._persistence.getDiskPath(filePath));
225
206
  this._files.set(filePath, file);
226
207
  this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
227
208
  return file.metadata;
228
209
  }
229
- listItemsAndPrefixes(bucket, prefix, delimiter, pageToken, maxResults) {
230
- if (!delimiter) {
231
- delimiter = "/";
232
- }
233
- if (!prefix) {
234
- prefix = "";
235
- }
236
- if (!prefix.endsWith(delimiter)) {
237
- prefix += delimiter;
238
- }
239
- if (!prefix.startsWith(delimiter)) {
240
- prefix = delimiter + prefix;
210
+ async handleListObjects(request, skipAuth = false) {
211
+ var _a, _b, _c;
212
+ const authorized = skipAuth ||
213
+ (await this._rulesValidator.validate(["b", request.bucketId, "o", request.prefix].join("/"), types_1.RulesetOperationMethod.LIST, {}, request.authorization));
214
+ if (!authorized) {
215
+ throw new errors_1.ForbiddenError();
241
216
  }
217
+ const itemsResults = this.listItems(request.bucketId, request.prefix, request.delimiter, request.pageToken, request.maxResults);
218
+ return new list_1.ListResponse((_a = itemsResults.prefixes) !== null && _a !== void 0 ? _a : [], (_c = (_b = itemsResults.items) === null || _b === void 0 ? void 0 : _b.map((i) => new list_1.ListItem(i.name, i.bucket))) !== null && _c !== void 0 ? _c : [], itemsResults.nextPageToken);
219
+ }
220
+ listItems(bucket, prefix, delimiter, pageToken, maxResults) {
242
221
  let items = [];
243
222
  const prefixes = new Set();
244
223
  for (const [, file] of this._files) {
245
- if (file.metadata.bucket != bucket) {
224
+ if (file.metadata.bucket !== bucket) {
246
225
  continue;
247
226
  }
248
- let name = `${delimiter}${file.metadata.name}`;
227
+ const name = file.metadata.name;
249
228
  if (!name.startsWith(prefix)) {
250
229
  continue;
251
230
  }
252
- name = name.substring(prefix.length);
253
- if (name.startsWith(delimiter)) {
254
- name = name.substring(prefix.length);
255
- }
256
- const startAtIndex = name.indexOf(delimiter);
257
- if (startAtIndex == -1) {
258
- if (!file.metadata.name.endsWith("/")) {
259
- items.push(file.metadata.name);
231
+ let includeMetadata = true;
232
+ if (delimiter) {
233
+ const delimiterIdx = name.indexOf(delimiter);
234
+ const delimiterAfterPrefixIdx = name.indexOf(delimiter, prefix.length);
235
+ includeMetadata = delimiterIdx === -1 || delimiterAfterPrefixIdx === -1;
236
+ if (delimiterAfterPrefixIdx !== -1) {
237
+ prefixes.add(name.slice(0, delimiterAfterPrefixIdx + delimiter.length));
260
238
  }
261
239
  }
262
- else {
263
- const prefixPath = prefix + name.substring(0, startAtIndex + 1);
264
- prefixes.add(prefixPath);
240
+ if (includeMetadata) {
241
+ items.push(file.metadata);
265
242
  }
266
243
  }
267
- items.sort();
244
+ items.sort((a, b) => {
245
+ if (a.name === b.name) {
246
+ return 0;
247
+ }
248
+ else if (a.name < b.name) {
249
+ return -1;
250
+ }
251
+ else {
252
+ return 1;
253
+ }
254
+ });
268
255
  if (pageToken) {
269
- const idx = items.findIndex((v) => v == pageToken);
270
- if (idx != -1) {
256
+ const idx = items.findIndex((v) => v.name === pageToken);
257
+ if (idx !== -1) {
271
258
  items = items.slice(idx);
272
259
  }
273
260
  }
@@ -276,81 +263,39 @@ class StorageLayer {
276
263
  }
277
264
  let nextPageToken = undefined;
278
265
  if (items.length > maxResults) {
279
- nextPageToken = items[maxResults];
266
+ nextPageToken = items[maxResults].name;
280
267
  items = items.slice(0, maxResults);
281
268
  }
282
- return new list_1.ListResponse([...prefixes].sort(), items.map((i) => new list_1.ListItem(i, bucket)), nextPageToken);
283
- }
284
- listItems(bucket, prefix, delimiter, pageToken, maxResults) {
285
- if (!delimiter) {
286
- delimiter = "/";
287
- }
288
- if (!prefix) {
289
- prefix = "";
290
- }
291
- if (!prefix.endsWith(delimiter)) {
292
- prefix += delimiter;
293
- }
294
- let items = [];
295
- for (const [, file] of this._files) {
296
- if (file.metadata.bucket != bucket) {
297
- continue;
298
- }
299
- let name = file.metadata.name;
300
- if (!name.startsWith(prefix)) {
301
- continue;
302
- }
303
- name = name.substring(prefix.length);
304
- if (name.startsWith(delimiter)) {
305
- name = name.substring(prefix.length);
306
- }
307
- items.push(this.path(file.metadata.bucket, file.metadata.name));
308
- }
309
- items.sort();
310
- if (pageToken) {
311
- const idx = items.findIndex((v) => v == pageToken);
312
- if (idx != -1) {
313
- items = items.slice(idx);
314
- }
315
- }
316
- if (!maxResults) {
317
- maxResults = 1000;
318
- }
319
269
  return {
320
- kind: "#storage/objects",
321
- items: items.map((item) => {
322
- const storedFile = this._files.get(item);
323
- if (!storedFile) {
324
- return console.warn(`No file ${item}`);
325
- }
326
- return new metadata_1.CloudStorageObjectMetadata(storedFile.metadata);
327
- }),
270
+ nextPageToken,
271
+ prefixes: prefixes.size > 0 ? [...prefixes].sort() : undefined,
272
+ items: items.length > 0 ? items.map((item) => new metadata_1.CloudStorageObjectMetadata(item)) : undefined,
328
273
  };
329
274
  }
330
- addDownloadToken(bucket, object) {
331
- const key = this.path(bucket, object);
332
- const val = this._files.get(key);
333
- if (!val) {
334
- return undefined;
275
+ handleCreateDownloadToken(request) {
276
+ if (!this._adminCredsValidator.validate(request.authorization)) {
277
+ throw new errors_1.ForbiddenError();
278
+ }
279
+ const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
280
+ if (!metadata) {
281
+ throw new errors_1.NotFoundError();
335
282
  }
336
- const md = val.metadata;
337
- md.addDownloadToken();
338
- return md;
283
+ metadata.addDownloadToken();
284
+ return metadata;
339
285
  }
340
- deleteDownloadToken(bucket, object, token) {
341
- const key = this.path(bucket, object);
342
- const val = this._files.get(key);
343
- if (!val) {
344
- return undefined;
286
+ handleDeleteDownloadToken(request) {
287
+ if (!this._adminCredsValidator.validate(request.authorization)) {
288
+ throw new errors_1.ForbiddenError();
289
+ }
290
+ const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
291
+ if (!metadata) {
292
+ throw new errors_1.NotFoundError();
345
293
  }
346
- const md = val.metadata;
347
- md.deleteDownloadToken(token);
348
- return md;
294
+ metadata.deleteDownloadToken(request.token);
295
+ return metadata;
349
296
  }
350
297
  path(bucket, object) {
351
- const directory = path.dirname(object);
352
- const filename = path.basename(object) + (object.endsWith("/") ? "/" : "");
353
- return path.join(bucket, directory, encodeURIComponent(filename));
298
+ return path.join(bucket, object);
354
299
  }
355
300
  get dirPath() {
356
301
  return this._persistence.dirPath;
@@ -373,9 +318,7 @@ class StorageLayer {
373
318
  try {
374
319
  for (var _b = __asyncValues(this._files.entries()), _c; _c = await _b.next(), !_c.done;) {
375
320
  const [p, file] = _c.value;
376
- const metadataExportPath = path.join(metadataDirPath, p) + ".json";
377
- const metadataExportDirPath = path.dirname(metadataExportPath);
378
- await fse.ensureDir(metadataExportDirPath);
321
+ const metadataExportPath = path.join(metadataDirPath, encodeURIComponent(p)) + ".json";
379
322
  await fse.writeFile(metadataExportPath, metadata_1.StoredFileMetadata.toJSON(file.metadata));
380
323
  }
381
324
  }
@@ -389,7 +332,7 @@ class StorageLayer {
389
332
  }
390
333
  import(storageExportPath) {
391
334
  const bucketsFile = path.join(storageExportPath, "buckets.json");
392
- const bucketsList = JSON.parse(fs.readFileSync(bucketsFile, "utf-8"));
335
+ const bucketsList = JSON.parse((0, fs_1.readFileSync)(bucketsFile, "utf-8"));
393
336
  for (const b of bucketsList.buckets) {
394
337
  const bucketMetadata = new metadata_1.CloudStorageBucketMetadata(b.id);
395
338
  this._buckets.set(b.id, bucketMetadata);
@@ -403,11 +346,11 @@ class StorageLayer {
403
346
  logger_1.logger.debug(`Skipping unexpected storage metadata file: ${f}`);
404
347
  continue;
405
348
  }
406
- const metadata = metadata_1.StoredFileMetadata.fromJSON(fs.readFileSync(f, "utf-8"), this._cloudFunctions);
349
+ const metadata = metadata_1.StoredFileMetadata.fromJSON((0, fs_1.readFileSync)(f, "utf-8"), this._cloudFunctions);
407
350
  const metadataRelPath = path.relative(metadataDir, f);
408
351
  const blobPath = metadataRelPath.substring(0, metadataRelPath.length - dotJson.length);
409
352
  const blobAbsPath = path.join(blobsDir, blobPath);
410
- if (!fs.existsSync(blobAbsPath)) {
353
+ if (!(0, fs_1.existsSync)(blobAbsPath)) {
411
354
  logger_1.logger.warn(`Could not find file "${blobPath}" in storage export.`);
412
355
  continue;
413
356
  }
@@ -417,10 +360,10 @@ class StorageLayer {
417
360
  fse.copySync(blobsDir, this.dirPath);
418
361
  }
419
362
  *walkDirSync(dir) {
420
- const files = fs.readdirSync(dir);
363
+ const files = (0, fs_1.readdirSync)(dir);
421
364
  for (const file of files) {
422
365
  const p = path.join(dir, file);
423
- if (fs.statSync(p).isDirectory()) {
366
+ if ((0, fs_1.statSync)(p).isDirectory()) {
424
367
  yield* this.walkDirSync(p);
425
368
  }
426
369
  else {
@@ -430,87 +373,3 @@ class StorageLayer {
430
373
  }
431
374
  }
432
375
  exports.StorageLayer = StorageLayer;
433
- class Persistence {
434
- constructor(dirPath) {
435
- this._dirPath = dirPath;
436
- if (!(0, fs_1.existsSync)(dirPath)) {
437
- (0, fs_1.mkdirSync)(dirPath, {
438
- recursive: true,
439
- });
440
- }
441
- }
442
- get dirPath() {
443
- return this._dirPath;
444
- }
445
- appendBytes(fileName, bytes, fileOffset) {
446
- const filepath = this.getDiskPath(fileName);
447
- const encodedSlashIndex = filepath.toLowerCase().lastIndexOf("%2f");
448
- const dirPath = encodedSlashIndex >= 0 ? filepath.substring(0, encodedSlashIndex) : path.dirname(filepath);
449
- if (!(0, fs_1.existsSync)(dirPath)) {
450
- (0, fs_1.mkdirSync)(dirPath, {
451
- recursive: true,
452
- });
453
- }
454
- let fd;
455
- try {
456
- fs.appendFileSync(filepath, bytes);
457
- return filepath;
458
- }
459
- finally {
460
- if (fd) {
461
- (0, fs_1.closeSync)(fd);
462
- }
463
- }
464
- }
465
- readBytes(fileName, size, fileOffset) {
466
- const path = this.getDiskPath(fileName);
467
- let fd;
468
- try {
469
- fd = (0, fs_1.openSync)(path, "r");
470
- const buf = Buffer.alloc(size);
471
- const offset = fileOffset && fileOffset > 0 ? fileOffset : 0;
472
- (0, fs_1.readSync)(fd, buf, 0, size, offset);
473
- return buf;
474
- }
475
- finally {
476
- if (fd) {
477
- (0, fs_1.closeSync)(fd);
478
- }
479
- }
480
- }
481
- deleteFile(fileName, failSilently = false) {
482
- try {
483
- (0, fs_1.unlinkSync)(this.getDiskPath(fileName));
484
- }
485
- catch (err) {
486
- if (!failSilently) {
487
- throw err;
488
- }
489
- }
490
- }
491
- deleteAll() {
492
- return new Promise((resolve, reject) => {
493
- rimraf(this._dirPath, (err) => {
494
- if (err) {
495
- reject(err);
496
- }
497
- else {
498
- resolve();
499
- }
500
- });
501
- });
502
- }
503
- renameFile(oldName, newName) {
504
- const dirPath = this.getDiskPath(path.dirname(newName));
505
- if (!(0, fs_1.existsSync)(dirPath)) {
506
- (0, fs_1.mkdirSync)(dirPath, {
507
- recursive: true,
508
- });
509
- }
510
- (0, fs_1.renameSync)(this.getDiskPath(oldName), this.getDiskPath(newName));
511
- }
512
- getDiskPath(fileName) {
513
- return path.join(this._dirPath, fileName);
514
- }
515
- }
516
- exports.Persistence = Persistence;