UNPKG

@replit/object-storage

Version:

The TypeScript library for Replit Object Storage ## Development

1 lines 24.1 kB
{"version":3,"sources":["../src/client.ts","../src/config.ts","../src/gcsApi.ts","../src/result.ts","../src/sidecar.ts","../src/index.ts"],"sourcesContent":["import { PassThrough, type Readable } from 'stream';\nimport {\n Bucket,\n Storage,\n UploadOptions as GoogleUploadOptions,\n} from '@google-cloud/storage';\n\nimport { RequestError, StorageObject, StreamRequestError } from './';\nimport { REPLIT_ADC } from './config';\nimport { errFromGoogleErr, parseFile } from './gcsApi';\nimport { getDefaultBucketId } from './sidecar';\nimport { Ok, Result } from './result';\n\n/**\n * Configuration options for client creation.\n * @public\n */\nexport interface ClientOptions {\n /**\n * The ID of the bucket the client will interact with.\n * If none is specified, the default bucket will be used.\n * @public\n */\n bucketId?: string;\n}\n\n/**\n * Configuration options for object deletion.\n * @public\n */\nexport interface DeleteOptions {\n /**\n * If specified, no error will be raised if the specified object does not exist.\n * False by default.\n */\n ignoreNotFound?: boolean;\n}\n\n/**\n * Configuration options for object download.\n * @public\n */\nexport interface DownloadOptions {\n /**\n * Whether the object should be decompressed, if uploaded using the `compress` flag.\n * True by default.\n */\n decompress?: boolean;\n}\n\n/**\n * Configuration options for listing objects in a bucket.\n * @public\n */\nexport interface ListOptions {\n /**\n * Filter results to objects whose names are\n * lexicographically before endOffset. If startOffset is also set, the objects\n * listed have names between startOffset (inclusive) and endOffset (exclusive).\n */\n endOffset?: string;\n /**\n * Glob pattern used to filter results, for example foo*bar.\n */\n matchGlob?: string;\n /**\n * The maximum number of results that can be returned in the response.\n */\n maxResults?: number;\n /**\n * Filter results to objects who names have the specified prefix.\n */\n prefix?: string;\n /**\n * Filter results to objects whose names are\n * lexicographically equal to or after startOffset. If endOffset is also set,\n * the objects listed have names between startOffset (inclusive) and endOffset (exclusive).\n */\n startOffset?: string;\n}\n\n/**\n * Configuration options for object upload.\n * @public\n */\nexport interface UploadOptions {\n /**\n * Whether the object should be compressed upon receipt of data.\n * This reduces at-rest storage cost but does not impact data transfer.\n * True by default.\n */\n compress?: boolean;\n}\n\nenum ClientStatus {\n Error = 'error',\n Initializing = 'initializing',\n Ready = 'ready',\n}\n\ntype State =\n | { status: ClientStatus.Initializing; promise: Promise<Bucket> }\n | { status: ClientStatus.Ready; bucket: Bucket }\n | { status: ClientStatus.Error; error: string };\n\n/**\n * Class representing a client to communicate with Object Storage from Replit.\n * @public\n */\nexport class Client {\n /**\n * @hidden\n */\n private state: State;\n\n /**\n * Creates a new client.\n * @param options - configurations to setup the client.\n */\n constructor(options?: ClientOptions) {\n this.state = {\n status: ClientStatus.Initializing,\n promise: this.init(options?.bucketId),\n };\n }\n\n private async init(bucketId?: string) {\n try {\n const gcsClient = new Storage({\n credentials: REPLIT_ADC,\n projectId: '',\n });\n\n const bucket = gcsClient.bucket(bucketId ?? (await getDefaultBucketId()));\n\n this.state = {\n status: ClientStatus.Ready,\n bucket,\n };\n\n return bucket;\n } catch (e) {\n this.state = {\n status: ClientStatus.Error,\n error:\n e instanceof Error\n ? `Error during client initialization: ${e.message}`\n : 'Unknown error',\n };\n\n throw e;\n }\n }\n\n private async getBucket(): Promise<Bucket> {\n if (this.state.status === ClientStatus.Initializing) {\n return this.state.promise;\n }\n\n if (this.state.status === ClientStatus.Error) {\n throw new Error(this.state.error);\n }\n\n return this.state.bucket;\n }\n\n private mapUploadOptions(\n options?: UploadOptions,\n ): GoogleUploadOptions | undefined {\n if (!options) {\n return;\n }\n\n const gzip = options.compress;\n return { gzip };\n }\n\n /**\n * Copies the specified object within the same bucket.\n * If an object exists in the same location, it will be overwritten.\n * @param objectName - The full path of the object to copy.\n * @param destObjectName - The full path to copy the object to.\n */\n async copy(\n objectName: string,\n destObjectName: string,\n ): Promise<Result<null, RequestError>> {\n const bucket = await this.getBucket();\n try {\n await bucket.file(objectName).copy(destObjectName);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n return Ok(null);\n }\n\n /**\n * Deletes the specified object.\n * @param objectName - The full path of the object to delete.\n * @param options - Configurations for the delete operation.\n */\n async delete(\n objectName: string,\n options?: DeleteOptions,\n ): Promise<Result<null, RequestError>> {\n const bucket = await this.getBucket();\n try {\n await bucket.file(objectName).delete(options);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n return Ok(null);\n }\n\n /**\n * Downloads an object as a buffer containing the object's raw contents.\n * @param objectName - The full path of the object to download.\n * @param options - Configurations for the download operation.\n */\n async downloadAsBytes(\n objectName: string,\n options?: DownloadOptions,\n ): Promise<Result<[Buffer], RequestError>> {\n const bucket = await this.getBucket();\n try {\n const buffer = await bucket.file(objectName).download(options);\n return Ok(buffer);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n }\n\n /**\n * Downloads a object to a string and returns the string.\n * @param objectName - The full path of the object to download.\n * @param options - Configurations for the download operation.\n */\n async downloadAsText(\n objectName: string,\n options?: DownloadOptions,\n ): Promise<Result<string, RequestError>> {\n const bucket = await this.getBucket();\n try {\n const buffer = await bucket.file(objectName).download(options);\n const text = buffer.toString();\n return Ok(text);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n }\n\n /**\n * Downloads an object to the local filesystem.\n * @param objectName - The full path of the object to download.\n * @param destFilename - The path on the local filesystem to write the downloaded object to.\n * @param options - Configurations for the download operation.\n */\n async downloadToFilename(\n objectName: string,\n destFilename: string,\n options?: DownloadOptions,\n ): Promise<Result<null, RequestError>> {\n const bucket = await this.getBucket();\n\n bucket.file(objectName).createReadStream();\n try {\n await bucket.file(objectName).download({\n ...options,\n destination: destFilename,\n });\n } catch (error) {\n return errFromGoogleErr(error);\n }\n return Ok(null);\n }\n\n /**\n * Opens a new stream and streams the object's contents.\n * If an error is encountered, it will be emitted through the stream.\n * @param objectName - The full path of the object to download.\n * @param options - Configurations for the download operation.\n */\n downloadAsStream(objectName: string, options?: DownloadOptions): Readable {\n const passThrough = new PassThrough();\n\n this.getBucket()\n .then((bucket) => {\n bucket\n .file(objectName)\n .createReadStream(options)\n .on('error', (err) => {\n const { error: reqErr } = errFromGoogleErr(err);\n passThrough.emit('error', new StreamRequestError(reqErr));\n })\n .pipe(passThrough);\n })\n .catch((err) => {\n const { error: reqErr } = errFromGoogleErr(err);\n passThrough.emit('error', new StreamRequestError(reqErr));\n });\n\n return passThrough;\n }\n\n /**\n * Checks whether the given object exists.\n * @param objectName - The full path of the object to check.\n */\n async exists(objectName: string): Promise<Result<boolean, RequestError>> {\n const bucket = await this.getBucket();\n try {\n const response = await bucket.file(objectName).exists();\n return Ok(response[0]);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n }\n\n /**\n * Lists objects in the bucket.\n * @param options - Configurations for the list operation.\n */\n async list(\n options?: ListOptions,\n ): Promise<Result<Array<StorageObject>, RequestError>> {\n const bucket = await this.getBucket();\n try {\n const [googleFiles] = await bucket.getFiles({\n ...options,\n autoPaginate: true,\n versions: false,\n });\n const objects = googleFiles.map(parseFile);\n return Ok(objects);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n }\n\n /**\n * Uploads an object from its in-memory byte representation.\n * If an object already exists with the specified name it will be overwritten.\n * @param objectName - The full destination path of the object.\n * @param contents - The raw contents of the object in byte form.\n * @param options - Configurations for the upload operation.\n */\n async uploadFromBytes(\n objectName: string,\n contents: Buffer,\n options?: UploadOptions,\n ): Promise<Result<null, RequestError>> {\n const bucket = await this.getBucket();\n const mappedOptions = this.mapUploadOptions(options);\n try {\n await bucket.file(objectName).save(contents, mappedOptions);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n return Ok(null);\n }\n\n /**\n * Uploads an object from its in-memory text representation.\n * If an object already exists with the specified name it will be overwritten.\n * @param objectName - The full destination path of the object.\n * @param contents - The contents of the object in text form.\n * @param options - Configurations for the upload operation.\n */\n async uploadFromText(\n objectName: string,\n contents: string,\n options?: UploadOptions,\n ): Promise<Result<null, RequestError>> {\n const bucket = await this.getBucket();\n const mappedOptions = this.mapUploadOptions(options);\n try {\n await bucket.file(objectName).save(contents, mappedOptions);\n } catch (error) {\n return errFromGoogleErr(error);\n }\n return Ok(null);\n }\n\n /**\n * Uploads an object from a file on the local filesystem.\n * If an object already exists with the specified name it will be overwritten.\n * @param objectName - The full destination path of the object.\n * @param srcFilename - The path of the file on the local filesystem to upload.\n * @param options - Configurations for the upload operation.\n */\n async uploadFromFilename(\n objectName: string,\n srcFilename: string,\n options?: UploadOptions,\n ): Promise<Result<null, RequestError>> {\n const bucket = await this.getBucket();\n const mappedOptions = this.mapUploadOptions(options);\n try {\n await bucket.upload(srcFilename, {\n ...mappedOptions,\n destination: objectName,\n });\n } catch (error) {\n return errFromGoogleErr(error);\n }\n return Ok(null);\n }\n\n /**\n * Uploads an object by streaming its contents from the provided stream.\n * 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.\n * @param objectName - The full destination path of the object.\n * @param stream - A writeable stream the object will be written from.\n * @param options - Configurations for the upload operation.\n */\n async uploadFromStream(\n objectName: string,\n stream: Readable,\n options?: UploadOptions,\n ): Promise<void> {\n const bucket = await this.getBucket();\n const mappedOptions = this.mapUploadOptions(options);\n return new Promise((resolve, reject) => {\n stream\n .pipe(\n bucket\n .file(objectName)\n .createWriteStream({ ...mappedOptions, resumable: false }),\n )\n .on('error', (err) => {\n const { error: reqErr } = errFromGoogleErr(err);\n reject(new StreamRequestError(reqErr));\n })\n .on('finish', () => {\n resolve();\n });\n });\n }\n}\n","import { ExternalAccountClientOptions } from 'google-auth-library';\n\nexport const REPLIT_SIDECAR_ENDPOINT = 'http://127.0.0.1:1106';\nexport const REPLIT_DEFAULT_BUCKET_URL =\n REPLIT_SIDECAR_ENDPOINT + '/object-storage/default-bucket';\n\nconst REPLIT_CREDENTIAL_URL = REPLIT_SIDECAR_ENDPOINT + '/credential';\nconst REPLIT_TOKEN_URL = REPLIT_SIDECAR_ENDPOINT + '/token';\n\nexport const REPLIT_ADC: ExternalAccountClientOptions = {\n audience: 'replit',\n subject_token_type: 'access_token',\n token_url: REPLIT_TOKEN_URL,\n type: 'external_account',\n credential_source: {\n url: REPLIT_CREDENTIAL_URL,\n format: {\n type: 'json',\n subject_token_field_name: 'access_token',\n },\n },\n universe_domain: 'googleapis.com',\n};\n","import { ApiError, File as GoogleFile } from '@google-cloud/storage';\n\nimport { RequestError, StorageObject } from './';\nimport { Err, ErrResult } from './result';\n\nexport function errFromGoogleErr(error: unknown): ErrResult<RequestError> {\n if (error instanceof ApiError) {\n return Err({\n message: error.message,\n statusCode: error.code,\n });\n } else if (error instanceof Error) {\n return Err({\n message: error.toString(),\n });\n }\n return Err({\n message: 'An unknown error occurred.',\n });\n}\n\nexport function parseFile(file: GoogleFile): StorageObject {\n return {\n name: file.name,\n };\n}\n","/**\n * A Result type that can be used to represent a successful value or an error.\n * It forces the consumer to check whether the returned type is an error or not,\n * `result.ok` acts as a discriminant between success and failure\n * @public\n * @typeParam T - The type of the result's value.\n * @typeParam E - The type of the result's error.\n * @typeParam ErrorExtras - The type of additional error info, if any will be returned.\n */\nexport type Result<T, E = Error | string, ErrorExtras = unknown> =\n | OkResult<T>\n | ErrResult<E, ErrorExtras>;\n\n/**\n * Represents a successful result\n * @public\n * @typeParam T - The type of the result's value.\n */\nexport interface OkResult<T> {\n /**\n * Indicates that the request was successful.\n */\n ok: true;\n /**\n * The value returned by the request.\n */\n value: T;\n /**\n * Always undefined when the request was successful.\n */\n error?: undefined;\n}\n\n/**\n * Represents a failure result\n * @public\n * @typeParam E - The type of the error.\n * @typeParam ErrorExtras - The type of any additional information on the error, if provided.\n */\nexport interface ErrResult<E, ErrorExtras = unknown> {\n /**\n * Indicates that the request was unsuccessful.\n */\n ok: false;\n /**\n * The error that occurred.\n */\n error: E;\n /**\n * Always undefined when the request was successful.\n */\n value?: undefined;\n /**\n * Additional information on the error, if applicable.\n */\n errorExtras?: ErrorExtras;\n}\n\n/**\n * A helper function to create an error Result type\n */\nexport function Err<E, ErrorExtras>(\n error: E,\n errorExtras?: ErrorExtras,\n): ErrResult<E, ErrorExtras> {\n return { ok: false, error, errorExtras };\n}\n\n/**\n * A helper function to create a successful Result type\n **/\nexport function Ok<T>(value: T): OkResult<T> {\n return { ok: true, value };\n}\n","import { REPLIT_DEFAULT_BUCKET_URL } from './config';\n\nexport async function getDefaultBucketId(): Promise<string> {\n const response = await fetch(REPLIT_DEFAULT_BUCKET_URL);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch default bucket, errorcode: ${response.status}, make sure you're running on Replit`,\n );\n }\n\n const defaultBucketResponse = await response.json();\n\n if (\n typeof defaultBucketResponse !== 'object' ||\n !defaultBucketResponse ||\n !('bucketId' in defaultBucketResponse) ||\n typeof defaultBucketResponse.bucketId !== 'string'\n ) {\n throw new Error(\n \"Failed to fetch default bucket, make sure you're running on Replit\",\n );\n }\n\n return defaultBucketResponse.bucketId;\n}\n","import {\n Client,\n ClientOptions,\n DeleteOptions,\n DownloadOptions,\n ListOptions,\n UploadOptions,\n} from './client';\nimport type { ErrResult, OkResult, Result } from './result';\n\n/**\n * Metadata for an object.\n * @public\n */\nexport interface StorageObject {\n /**\n * The name of the object, including its full path.\n */\n name: string;\n}\n\n/**\n * An object that represents an error with a request\n * @public\n */\nexport interface RequestError {\n message: string;\n statusCode?: number;\n}\n\n/**\n * An error that may be surfaced when using a stream.\n * @public\n */\nexport class StreamRequestError extends Error {\n private requestError: RequestError;\n\n constructor(err: RequestError) {\n if (err.statusCode) {\n super(`${err.statusCode}: ${err.message}`);\n } else {\n super(err.message);\n }\n\n this.requestError = err;\n }\n\n getRequestError(): RequestError {\n return this.requestError;\n }\n}\n\nexport { Client, ErrResult, OkResult, Result };\nexport type {\n ClientOptions,\n DeleteOptions,\n DownloadOptions,\n ListOptions,\n UploadOptions,\n};\n"],"mappings":"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","names":[]}