flydrive
Version:
File storage library with unified API to manage files across multiple cloud storage providers like S3, GCS, R2 and so on
313 lines (307 loc) • 8.56 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/errors.ts
var errors_exports = {};
__export(errors_exports, {
E_CANNOT_CHECK_FILE_EXISTENCE: () => E_CANNOT_CHECK_FILE_EXISTENCE,
E_CANNOT_COPY_FILE: () => E_CANNOT_COPY_FILE,
E_CANNOT_DELETE_DIRECTORY: () => E_CANNOT_DELETE_DIRECTORY,
E_CANNOT_DELETE_FILE: () => E_CANNOT_DELETE_FILE,
E_CANNOT_GENERATE_URL: () => E_CANNOT_GENERATE_URL,
E_CANNOT_GET_METADATA: () => E_CANNOT_GET_METADATA,
E_CANNOT_MOVE_FILE: () => E_CANNOT_MOVE_FILE,
E_CANNOT_READ_FILE: () => E_CANNOT_READ_FILE,
E_CANNOT_SET_VISIBILITY: () => E_CANNOT_SET_VISIBILITY,
E_CANNOT_WRITE_FILE: () => E_CANNOT_WRITE_FILE,
E_INVALID_KEY: () => E_INVALID_KEY,
E_PATH_TRAVERSAL_DETECTED: () => E_PATH_TRAVERSAL_DETECTED,
E_UNALLOWED_CHARACTERS: () => E_UNALLOWED_CHARACTERS
});
import { createError } from "@poppinss/utils";
var E_CANNOT_WRITE_FILE = createError(
'Cannot write file at location "%s"',
"E_CANNOT_WRITE_FILE"
);
var E_CANNOT_READ_FILE = createError(
'Cannot read file from location "%s"',
"E_CANNOT_READ_FILE"
);
var E_CANNOT_DELETE_FILE = createError(
'Cannot delete file at location "%s"',
"E_CANNOT_DELETE_FILE"
);
var E_CANNOT_DELETE_DIRECTORY = createError(
'Cannot delete directory at location "%s"',
"E_CANNOT_DELETE_DIRECTORY"
);
var E_CANNOT_COPY_FILE = createError(
'Cannot copy file from "%s" to "%s"',
"E_CANNOT_COPY_FILE"
);
var E_CANNOT_MOVE_FILE = createError(
'Cannot move file from "%s" to "%s"',
"E_CANNOT_MOVE_FILE"
);
var E_CANNOT_CHECK_FILE_EXISTENCE = createError(
'Unable to check existence for file at location "%s"',
"E_CANNOT_CHECK_FILE_EXISTENCE"
);
var E_CANNOT_GET_METADATA = createError(
'Unable to retrieve metadata of file at location "%s"',
"E_CANNOT_GET_METADATA"
);
var E_CANNOT_SET_VISIBILITY = createError(
'Unable to set visibility for file at location "%s"',
"E_CANNOT_SET_VISIBILITY"
);
var E_CANNOT_GENERATE_URL = createError(
'Cannot generate URL for file at location "%s"',
"E_CANNOT_GENERATE_URL"
);
var E_UNALLOWED_CHARACTERS = createError(
'The key "%s" has unallowed characters',
"E_UNALLOWED_CHARACTERS"
);
var E_INVALID_KEY = createError(
'Invalid key "%s". After normalization results in an empty string',
"E_INVALID_KEY"
);
var E_PATH_TRAVERSAL_DETECTED = createError(
'Path traversal segment detected in key "%s"',
"E_PATH_TRAVERSAL_DETECTED"
);
// src/key_normalizer.ts
import { slash } from "@poppinss/utils";
import { normalize } from "node:path/posix";
import string from "@poppinss/utils/string";
var KeyNormalizer = class _KeyNormalizer {
/**
* The set of allowed characters. Key free to re-assign a new
* value
*/
static allowedCharacterSet = /^[A-Za-z0-9-_!\/\.\s]*$/;
/**
* Normalizes the key by condensing whitespaces, using unix
* slashes, and replacing consecutive slashes with one
* slash ("/").
*/
#preNormalize(key) {
let normalizedKey = string.condenseWhitespace(key);
return slash(normalizedKey).replace(/\/{2,}/g, "/").replace(/\.{3,}\//g, "../");
}
/**
* Validates the key to check for unallowed characters
*/
#validateCharacterSet(key, originalKey) {
if (!_KeyNormalizer.allowedCharacterSet.test(key)) {
throw new E_UNALLOWED_CHARACTERS([originalKey]);
}
}
/**
* Checks for path traversel in key
*/
#checkForPathTraversal(key, originalKey) {
const tokens = key.split("/");
for (let token of tokens) {
if (token === "..") {
throw new E_PATH_TRAVERSAL_DETECTED([originalKey]);
}
}
}
/**
* Further normalizing the key after validating it. Here we remove
* starting and ending path expressions like "." and "/" from
* the key.
*/
#postNormalize(key) {
let normalizedKey = normalize(key);
return normalizedKey.replace(/^\/|\/$/g, "").replace(/^\.|\.$/g, "");
}
/**
* Normalize the key
*/
normalize(key) {
let normalizedKey = this.#preNormalize(key);
this.#validateCharacterSet(normalizedKey, key);
this.#checkForPathTraversal(normalizedKey, key);
normalizedKey = this.#postNormalize(normalizedKey);
if (normalizedKey.trim() === "") {
throw new E_INVALID_KEY([key]);
}
return normalizedKey;
}
};
// src/driver_file.ts
import { basename } from "node:path";
var DriveFile = class {
/**
* The driver to use for performing read-only operations
*/
#driver;
/**
* Known metadata from the snapshot or from the files listing
* API
*/
#metaData;
/**
* The normalizer is used to normalize and validate keys
*/
#normalizer = new KeyNormalizer();
/**
* Reference to the normalized file key
*/
key;
/**
* The basename of the file. Extracted from the key
*/
name;
/**
* Flags to know if the object is a file or a directory
*/
isFile = true;
isDirectory = false;
constructor(key, driver, metaData) {
this.#driver = driver;
this.#metaData = metaData;
this.key = this.#normalizer.normalize(key);
this.name = basename(this.key);
}
/**
* Check if the file exists. This method cannot check existence
* of directories.
*/
async exists() {
try {
return await this.#driver.exists(this.key);
} catch (error) {
throw new E_CANNOT_CHECK_FILE_EXISTENCE([this.key], { cause: error });
}
}
/**
* Returns file contents as a UTF-8 string. Use "getArrayBuffer" method
* if you need more control over the file contents decoding.
*/
async get() {
try {
return await this.#driver.get(this.key);
} catch (error) {
throw new E_CANNOT_READ_FILE([this.key], { cause: error });
}
}
/**
* Returns file contents as a Readable stream.
*/
async getStream() {
try {
return await this.#driver.getStream(this.key);
} catch (error) {
throw new E_CANNOT_READ_FILE([this.key], { cause: error });
}
}
/**
* Returns file contents as a Uint8Array.
*/
async getBytes() {
try {
return await this.#driver.getBytes(this.key);
} catch (error) {
throw new E_CANNOT_READ_FILE([this.key], { cause: error });
}
}
/**
* @deprecated
* @see {@link DriveFile.getBytes}
*/
async getArrayBuffer() {
process.emitWarning(
'getArrayBuffer() method has been deprecated. Instead use "getBytes"',
"DeprecationWarning"
);
return this.getBytes();
}
/**
* Returns metadata of the given file.
*/
async getMetaData() {
if (this.#metaData) {
return this.#metaData;
}
try {
return await this.#driver.getMetaData(this.key);
} catch (error) {
throw new E_CANNOT_GET_METADATA([this.key], { cause: error });
}
}
/**
* Returns the visibility of the file
*/
async getVisibility() {
try {
return await this.#driver.getVisibility(this.key);
} catch (error) {
throw new E_CANNOT_GET_METADATA([this.key], { cause: error });
}
}
/**
* Returns the public URL of the file
*/
async getUrl() {
try {
return await this.#driver.getUrl(this.key);
} catch (error) {
throw new E_CANNOT_GENERATE_URL([this.key], { cause: error });
}
}
/**
* Returns a signed/temporary URL of the file
*/
async getSignedUrl(options) {
try {
return await this.#driver.getSignedUrl(this.key, options);
} catch (error) {
throw new E_CANNOT_GENERATE_URL([this.key], { cause: error });
}
}
/**
* Returns a snapshot of the file. The snapshot could be persisted
* within any database storage and later you can create a file
* instance from it using the "disk.fromSnapshot" method.
*/
async toSnapshot() {
const metaData = await this.getMetaData();
return {
key: this.key,
name: this.name,
contentLength: metaData.contentLength,
lastModified: metaData.lastModified.toString(),
etag: metaData.etag,
contentType: metaData.contentType
};
}
};
// src/drive_directory.ts
import { basename as basename2 } from "node:path";
var DriveDirectory = class {
constructor(prefix) {
this.prefix = prefix;
this.name = basename2(this.prefix);
}
isFile = false;
isDirectory = true;
name;
};
export {
E_CANNOT_WRITE_FILE,
E_CANNOT_DELETE_FILE,
E_CANNOT_DELETE_DIRECTORY,
E_CANNOT_COPY_FILE,
E_CANNOT_MOVE_FILE,
E_CANNOT_SET_VISIBILITY,
errors_exports,
KeyNormalizer,
DriveFile,
DriveDirectory
};