UNPKG

@flystorage/google-cloud-storage

Version:

<img src="https://raw.githubusercontent.com/duna-oss/flystorage/main/flystorage.svg" width="50px" height="50px" />

193 lines (192 loc) 7.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GoogleCloudFileStorage = exports.GoogleCloudStorageAdapter = void 0; const file_storage_1 = require("@flystorage/file-storage"); const stream_mime_type_1 = require("@flystorage/stream-mime-type"); const promises_1 = require("node:stream/promises"); const visibility_handling_js_1 = require("./visibility-handling.js"); class GoogleCloudStorageAdapter { bucket; options; visibilityHandling; prefixer; constructor(bucket, options = {}, visibilityHandling = new visibility_handling_js_1.UniformBucketLevelAccessVisibilityHandling()) { this.bucket = bucket; this.options = options; this.visibilityHandling = visibilityHandling; this.prefixer = new file_storage_1.PathPrefixer(options.prefix ?? ''); } async write(path, contents, options) { let mimeType = options.mimeType; if (mimeType === undefined) { [mimeType, contents] = await (0, stream_mime_type_1.resolveMimeType)(path, contents); } const writeStream = this.bucket.file(this.prefixer.prefixFilePath(path)) .createWriteStream({ contentType: mimeType, predefinedAcl: options.visibility ? this.visibilityHandling.visibilityToPredefinedAcl(options.visibility) : undefined, metadata: options.cacheControl ? { cacheControl: options.cacheControl } : undefined, }); await (0, promises_1.pipeline)(contents, writeStream); } async read(path) { const readStream = this.bucket.file(this.prefixer.prefixFilePath(path)).createReadStream(); // force retrieval of the head to ensure the http call is made // this ensures the error from the HTTP call is caught at the // abstraction level const [_, outStream] = await (0, stream_mime_type_1.streamHead)(readStream, 10); return outStream; } async deleteFile(path) { await this.bucket.file(this.prefixer.prefixFilePath(path)).delete({ ignoreNotFound: true, }); } async createDirectory(path, options) { await this.bucket.file(this.prefixer.prefixDirectoryPath(path)).save(''); } async copyFile(from, to, options) { await this.bucket.file(this.prefixer.prefixFilePath(from)).copy(this.bucket.file(this.prefixer.prefixFilePath(to))); } async moveFile(from, to, options) { await this.copyFile(from, to, {}); await this.deleteFile(from); } async stat(path) { const [metadata] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getMetadata(); return this.mapToStatEntry(metadata); } async *list(path, options) { let response; let query = { autoPaginate: false, delimiter: options.deep ? undefined : '/', includeTrailingDelimiter: options.deep ? undefined : true, prefix: this.prefixer.prefixDirectoryPath(path), }; while (query !== null) { [response, query] = await this.bucket.getFiles(query); for (const item of response) { yield this.mapToStatEntry(item.metadata); } } } mapToStatEntry(file) { if (file.name.endsWith('/')) { return { type: 'directory', isFile: false, isDirectory: true, path: this.prefixer.stripDirectoryPath(file.name), }; } return { type: 'file', isFile: true, isDirectory: false, path: this.prefixer.stripFilePath(file.name), lastModifiedMs: file.updated ? new Date(file.updated).getTime() : undefined, mimeType: file.contentType, }; } async changeVisibility(path, visibility) { await this.visibilityHandling.changeVisibility(this.bucket.file(this.prefixer.prefixFilePath(path)), visibility); } async visibility(path) { return await this.visibilityHandling.determineVisibility(this.bucket.file(this.prefixer.prefixFilePath(path))); } async deleteDirectory(path) { const prefix = this.prefixer.prefixDirectoryPath(path); await this.bucket.deleteFiles({ prefix, }); await this.bucket.file(prefix).delete({ ignoreNotFound: true, }); } async fileExists(path) { const [exists] = await this.bucket.file(this.prefixer.prefixFilePath(path)).exists(); return exists; } async directoryExists(path) { const [exists] = await this.bucket.file(this.prefixer.prefixDirectoryPath(path)).exists(); if (exists) { return true; } const [response] = await this.bucket.getFiles({ autoPaginate: false, maxResults: 1, prefix: this.prefixer.prefixDirectoryPath(path), }); return response.length > 0; } async publicUrl(path, options) { return this.bucket.file(this.prefixer.prefixFilePath(path)).publicUrl(); } async temporaryUrl(path, options) { const [response] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getSignedUrl({ action: 'read', expires: options.expiresAt, }); return response; } async prepareUpload(path, options) { const headers = {}; const config = { action: 'write', expires: options.expiresAt, }; const contentType = options['Content-Type'] ?? options.contentType; if (typeof contentType === 'string') { config.contentType = contentType; headers['Content-Type'] = contentType; } const [url] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getSignedUrl(config); return { url, headers, method: 'PUT', provider: 'google-cloud-storage', }; } async checksum(path, options) { const algo = options.algo ?? 'md5'; if (algo !== 'md5' && algo !== 'crc32c') { throw file_storage_1.ChecksumIsNotAvailable.checksumNotSupported(algo); } const [metadata] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getMetadata(); return algo === 'crc32c' ? metadata.crc32c : metadata.md5Hash; } async mimeType(path, options) { const stat = await this.stat(path); if (stat.type !== 'file' || stat.mimeType === undefined) { throw new Error('Unable to resolve mime-type, not available in stat entry.'); } return stat.mimeType; } async lastModified(path) { const stat = await this.stat(path); if (stat.type !== 'file' || stat.lastModifiedMs === undefined) { throw new Error('Unable to resolve last modified time, not available in stat entry.'); } return stat.lastModifiedMs; } async fileSize(path) { const stat = await this.stat(path); if (stat.type !== 'file' || stat.size === undefined) { throw new Error('Unable to resolve file size, not available in stat entry.'); } return stat.size; } } exports.GoogleCloudStorageAdapter = GoogleCloudStorageAdapter; /** * BC export * * @deprecated */ class GoogleCloudFileStorage extends GoogleCloudStorageAdapter { } exports.GoogleCloudFileStorage = GoogleCloudFileStorage;