UNPKG

@replit/object-storage

Version:

The TypeScript library for Replit Object Storage ## Development

378 lines (369 loc) 12 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { Client: () => Client, StreamRequestError: () => StreamRequestError }); module.exports = __toCommonJS(src_exports); // src/client.ts var import_stream = require("stream"); var import_storage2 = require("@google-cloud/storage"); // src/config.ts var REPLIT_SIDECAR_ENDPOINT = "http://127.0.0.1:1106"; var REPLIT_DEFAULT_BUCKET_URL = REPLIT_SIDECAR_ENDPOINT + "/object-storage/default-bucket"; var REPLIT_CREDENTIAL_URL = REPLIT_SIDECAR_ENDPOINT + "/credential"; var REPLIT_TOKEN_URL = REPLIT_SIDECAR_ENDPOINT + "/token"; var REPLIT_ADC = { audience: "replit", subject_token_type: "access_token", token_url: REPLIT_TOKEN_URL, type: "external_account", credential_source: { url: REPLIT_CREDENTIAL_URL, format: { type: "json", subject_token_field_name: "access_token" } }, universe_domain: "googleapis.com" }; // src/gcsApi.ts var import_storage = require("@google-cloud/storage"); // src/result.ts function Err(error, errorExtras) { return { ok: false, error, errorExtras }; } function Ok(value) { return { ok: true, value }; } // src/gcsApi.ts function errFromGoogleErr(error) { if (error instanceof import_storage.ApiError) { return Err({ message: error.message, statusCode: error.code }); } else if (error instanceof Error) { return Err({ message: error.toString() }); } return Err({ message: "An unknown error occurred." }); } function parseFile(file) { return { name: file.name }; } // src/sidecar.ts async function getDefaultBucketId() { const response = await fetch(REPLIT_DEFAULT_BUCKET_URL); if (!response.ok) { throw new Error( `Failed to fetch default bucket, errorcode: ${response.status}, make sure you're running on Replit` ); } const defaultBucketResponse = await response.json(); if (typeof defaultBucketResponse !== "object" || !defaultBucketResponse || !("bucketId" in defaultBucketResponse) || typeof defaultBucketResponse.bucketId !== "string") { throw new Error( "Failed to fetch default bucket, make sure you're running on Replit" ); } return defaultBucketResponse.bucketId; } // src/client.ts var Client = class { /** * @hidden */ state; /** * Creates a new client. * @param options - configurations to setup the client. */ constructor(options) { this.state = { status: "initializing" /* Initializing */, promise: this.init(options?.bucketId) }; } async init(bucketId) { try { const gcsClient = new import_storage2.Storage({ credentials: REPLIT_ADC, projectId: "" }); const bucket = gcsClient.bucket(bucketId ?? await getDefaultBucketId()); this.state = { status: "ready" /* Ready */, bucket }; return bucket; } catch (e) { this.state = { status: "error" /* Error */, error: e instanceof Error ? `Error during client initialization: ${e.message}` : "Unknown error" }; throw e; } } async getBucket() { if (this.state.status === "initializing" /* Initializing */) { return this.state.promise; } if (this.state.status === "error" /* Error */) { throw new Error(this.state.error); } return this.state.bucket; } mapUploadOptions(options) { if (!options) { return; } const gzip = options.compress; return { gzip }; } /** * Copies the specified object within the same bucket. * If an object exists in the same location, it will be overwritten. * @param objectName - The full path of the object to copy. * @param destObjectName - The full path to copy the object to. */ async copy(objectName, destObjectName) { const bucket = await this.getBucket(); try { await bucket.file(objectName).copy(destObjectName); } catch (error) { return errFromGoogleErr(error); } return Ok(null); } /** * Deletes the specified object. * @param objectName - The full path of the object to delete. * @param options - Configurations for the delete operation. */ async delete(objectName, options) { const bucket = await this.getBucket(); try { await bucket.file(objectName).delete(options); } catch (error) { return errFromGoogleErr(error); } return Ok(null); } /** * Downloads an object as a buffer containing the object's raw contents. * @param objectName - The full path of the object to download. * @param options - Configurations for the download operation. */ async downloadAsBytes(objectName, options) { const bucket = await this.getBucket(); try { const buffer = await bucket.file(objectName).download(options); return Ok(buffer); } catch (error) { return errFromGoogleErr(error); } } /** * Downloads a object to a string and returns the string. * @param objectName - The full path of the object to download. * @param options - Configurations for the download operation. */ async downloadAsText(objectName, options) { const bucket = await this.getBucket(); try { const buffer = await bucket.file(objectName).download(options); const text = buffer.toString(); return Ok(text); } catch (error) { return errFromGoogleErr(error); } } /** * Downloads an object to the local filesystem. * @param objectName - The full path of the object to download. * @param destFilename - The path on the local filesystem to write the downloaded object to. * @param options - Configurations for the download operation. */ async downloadToFilename(objectName, destFilename, options) { const bucket = await this.getBucket(); bucket.file(objectName).createReadStream(); try { await bucket.file(objectName).download({ ...options, destination: destFilename }); } catch (error) { return errFromGoogleErr(error); } return Ok(null); } /** * Opens a new stream and streams the object's contents. * If an error is encountered, it will be emitted through the stream. * @param objectName - The full path of the object to download. * @param options - Configurations for the download operation. */ downloadAsStream(objectName, options) { const passThrough = new import_stream.PassThrough(); this.getBucket().then((bucket) => { bucket.file(objectName).createReadStream(options).on("error", (err) => { const { error: reqErr } = errFromGoogleErr(err); passThrough.emit("error", new StreamRequestError(reqErr)); }).pipe(passThrough); }).catch((err) => { const { error: reqErr } = errFromGoogleErr(err); passThrough.emit("error", new StreamRequestError(reqErr)); }); return passThrough; } /** * Checks whether the given object exists. * @param objectName - The full path of the object to check. */ async exists(objectName) { const bucket = await this.getBucket(); try { const response = await bucket.file(objectName).exists(); return Ok(response[0]); } catch (error) { return errFromGoogleErr(error); } } /** * Lists objects in the bucket. * @param options - Configurations for the list operation. */ async list(options) { const bucket = await this.getBucket(); try { const [googleFiles] = await bucket.getFiles({ ...options, autoPaginate: true, versions: false }); const objects = googleFiles.map(parseFile); return Ok(objects); } catch (error) { return errFromGoogleErr(error); } } /** * Uploads an object from its in-memory byte representation. * If an object already exists with the specified name it will be overwritten. * @param objectName - The full destination path of the object. * @param contents - The raw contents of the object in byte form. * @param options - Configurations for the upload operation. */ async uploadFromBytes(objectName, contents, options) { const bucket = await this.getBucket(); const mappedOptions = this.mapUploadOptions(options); try { await bucket.file(objectName).save(contents, mappedOptions); } catch (error) { return errFromGoogleErr(error); } return Ok(null); } /** * Uploads an object from its in-memory text representation. * If an object already exists with the specified name it will be overwritten. * @param objectName - The full destination path of the object. * @param contents - The contents of the object in text form. * @param options - Configurations for the upload operation. */ async uploadFromText(objectName, contents, options) { const bucket = await this.getBucket(); const mappedOptions = this.mapUploadOptions(options); try { await bucket.file(objectName).save(contents, mappedOptions); } catch (error) { return errFromGoogleErr(error); } return Ok(null); } /** * Uploads an object from a file on the local filesystem. * If an object already exists with the specified name it will be overwritten. * @param objectName - The full destination path of the object. * @param srcFilename - The path of the file on the local filesystem to upload. * @param options - Configurations for the upload operation. */ async uploadFromFilename(objectName, srcFilename, options) { const bucket = await this.getBucket(); const mappedOptions = this.mapUploadOptions(options); try { await bucket.upload(srcFilename, { ...mappedOptions, destination: objectName }); } catch (error) { return errFromGoogleErr(error); } return Ok(null); } /** * Uploads an object by streaming its contents from the provided stream. * If an error is encountered, it will be emitted through the stream. If an object already exists with the specified name it will be overwritten. * @param objectName - The full destination path of the object. * @param stream - A writeable stream the object will be written from. * @param options - Configurations for the upload operation. */ async uploadFromStream(objectName, stream, options) { const bucket = await this.getBucket(); const mappedOptions = this.mapUploadOptions(options); return new Promise((resolve, reject) => { stream.pipe( bucket.file(objectName).createWriteStream({ ...mappedOptions, resumable: false }) ).on("error", (err) => { const { error: reqErr } = errFromGoogleErr(err); reject(new StreamRequestError(reqErr)); }).on("finish", () => { resolve(); }); }); } }; // src/index.ts var StreamRequestError = class extends Error { requestError; constructor(err) { if (err.statusCode) { super(`${err.statusCode}: ${err.message}`); } else { super(err.message); } this.requestError = err; } getRequestError() { return this.requestError; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Client, StreamRequestError }); //# sourceMappingURL=index.js.map