UNPKG

@storybooker/gcp

Version:

StoryBooker Adapter for interacting with GCP services.

1 lines 6.81 kB
{"version":3,"file":"storage.cjs","names":["#client","Readable"],"sources":["../src/storage.ts"],"sourcesContent":["import { Readable } from \"node:stream\";\nimport type streamWeb from \"node:stream/web\";\nimport type { File, Storage } from \"@google-cloud/storage\";\nimport type { StorageService } from \"@storybooker/core/types\";\n\nexport class GcpGcsStorageService implements StorageService {\n #client: Storage;\n\n constructor(client: Storage) {\n this.#client = client;\n }\n\n createContainer: StorageService[\"createContainer\"] = async (\n containerId,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n await this.#client.createBucket(bucketName, {});\n };\n\n deleteContainer: StorageService[\"deleteContainer\"] = async (\n containerId,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n await this.#client.bucket(bucketName).delete();\n };\n\n hasContainer: StorageService[\"hasContainer\"] = async (\n containerId,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n const [exists] = await this.#client.bucket(bucketName).exists();\n return exists;\n };\n\n listContainers: StorageService[\"listContainers\"] = async (_options) => {\n const [buckets] = await this.#client.getBuckets();\n return buckets.map((bucket) => bucket.name);\n };\n\n deleteFiles: StorageService[\"deleteFiles\"] = async (\n containerId,\n filePathsOrPrefix,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n const bucket = this.#client.bucket(bucketName);\n\n if (typeof filePathsOrPrefix === \"string\") {\n // Delete all files with the prefix\n await bucket.deleteFiles({ prefix: filePathsOrPrefix });\n } else {\n // Delete specific files\n await Promise.all(\n filePathsOrPrefix.map(\n async (filepath) =>\n await bucket.file(filepath).delete({ ignoreNotFound: true }),\n ),\n );\n }\n };\n\n uploadFiles: StorageService[\"uploadFiles\"] = async (\n containerId,\n files,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n const bucket = this.#client.bucket(bucketName);\n\n await Promise.allSettled(\n files.map(async ({ content, path, mimeType }) => {\n await uploadFileToGcs(bucket.file(path), content, mimeType);\n }),\n );\n };\n\n hasFile: StorageService[\"hasFile\"] = async (\n containerId,\n filepath,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n const file = this.#client.bucket(bucketName).file(filepath);\n const [exists] = await file.exists();\n return exists;\n };\n\n downloadFile: StorageService[\"downloadFile\"] = async (\n containerId,\n filepath,\n _options,\n ) => {\n const bucketName = genBucketNameFromContainerId(containerId);\n const file = this.#client.bucket(bucketName).file(filepath);\n\n const [exists] = await file.exists();\n if (!exists) {\n throw new Error(\n `File '${filepath}' not found in bucket '${containerId}'.`,\n );\n }\n\n const [metadata] = await file.getMetadata();\n const mimeType = metadata.contentType;\n\n const readable = file.createReadStream();\n const content = Readable.toWeb(readable);\n\n return {\n content: content as ReadableStream,\n mimeType,\n path: filepath,\n };\n };\n}\n\nfunction genBucketNameFromContainerId(containerId: string): string {\n // GCS bucket names: lowercase, numbers, dashes, dots, 3-63 chars\n return containerId\n .replaceAll(/[^\\w.-]+/g, \"-\")\n .replaceAll(/^-+|-+$/g, \"\")\n .toLowerCase()\n .slice(0, 63);\n}\n\nasync function uploadFileToGcs(\n file: File,\n data: Blob | string | ReadableStream,\n mimeType: string,\n): Promise<void> {\n if (typeof data === \"string\" || data instanceof Buffer) {\n await file.save(data, { contentType: mimeType });\n return;\n }\n\n if (data instanceof Blob) {\n const buffer = Buffer.from(await data.arrayBuffer());\n await file.save(buffer, { contentType: mimeType });\n return;\n }\n\n const readable =\n data instanceof ReadableStream\n ? Readable.fromWeb(data as streamWeb.ReadableStream)\n : data;\n\n if (readable instanceof Readable) {\n // Node.js Readable stream\n await new Promise<void>((resolve, reject) => {\n const writeStream = file.createWriteStream({ contentType: mimeType });\n readable.pipe(writeStream).on(\"finish\", resolve).on(\"error\", reject);\n });\n return;\n }\n\n throw new Error(`Unknown file type`);\n}\n"],"mappings":"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"}