🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

flydrive

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

flydrive - npm Package Compare versions

Comparing version

to
1.0.0

build/chunk-FRUCTVD5.js

10

build/drivers/fs/driver.d.ts

@@ -16,5 +16,9 @@ /// <reference types="node" resolution-mode="require"/>

/**
* Synchronously check if a file exists
*/
existsSync(key: string): boolean;
/**
* Returns a boolean indicating if the file exists or not.
*/
exist(key: string): Promise<boolean>;
exists(key: string): Promise<boolean>;
/**

@@ -98,2 +102,6 @@ * Returns the contents of the file as a UTF-8 string. An

/**
* Synchronously delete all files from the root location
*/
clearSync(): void;
/**
* Returns a list of files. The pagination properties are ignored

@@ -100,0 +108,0 @@ * by the fs driver, since it does not support pagination.

279

build/drivers/fs/main.js
import {
DriveDirectory
} from "../../chunk-Y2S65K27.js";
import {
DriveFile
} from "../../chunk-2GUJ7FO2.js";
// drivers/fs/driver.ts
import etag from "etag";
import mimeTypes from "mime-types";
import { slash } from "@poppinss/utils";
import * as fsp from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { createReadStream } from "node:fs";
import { Retrier } from "@humanwhocodes/retry";
import { RuntimeException } from "@poppinss/utils";
import { dirname, join, relative } from "node:path";
// drivers/fs/debug.ts
import { debuglog } from "node:util";
var debug_default = debuglog("flydrive:fs");
// drivers/fs/driver.ts
var RETRY_ERROR_CODES = /* @__PURE__ */ new Set(["ENFILE", "EMFILE"]);
var FSDriver = class {
constructor(options) {
this.options = options;
this.#rootUrl = typeof options.location === "string" ? options.location : fileURLToPath(options.location);
debug_default("driver config %O", options);
}
/**
* The root directory for the driver
*/
#rootUrl;
/**
* Retrier is used to retry file system operations
* when certain errors are raised.
*/
#retrier = new Retrier(
(error) => error.code && RETRY_ERROR_CODES.has(error.code)
);
/**
* Reads the file for the provided path
*/
#read(key) {
const location = join(this.#rootUrl, key);
return this.#retrier.retry(() => fsp.readFile(location));
}
/**
* Generic implementation to write a file
*/
#write(key, contents, options) {
const location = join(this.#rootUrl, key);
return this.#retrier.retry(async () => {
await fsp.mkdir(dirname(location), { recursive: true });
await fsp.writeFile(location, contents, options);
});
}
/**
* Returns a boolean indicating if the file exists or not.
*/
async exist(key) {
debug_default("checking if file exists %s:%s", this.#rootUrl, key);
const location = join(this.#rootUrl, key);
try {
const object = await fsp.stat(location);
return object.isFile();
} catch (error) {
if (error.code === "ENOENT") {
return false;
}
throw error;
}
}
/**
* Returns the contents of the file as a UTF-8 string. An
* exception is thrown when the file is missing.
*/
async get(key) {
debug_default("reading file contents %s:%s", this.#rootUrl, key);
return this.#read(key).then((value) => value.toString("utf-8"));
}
/**
* Returns the contents of the file as a stream. An
* exception is thrown when the file is missing.
*/
async getStream(key) {
debug_default("reading file contents as a stream %s:%s", this.#rootUrl, key);
const location = join(this.#rootUrl, key);
return createReadStream(location);
}
/**
* Returns the contents of the file as an Uint8Array. An
* exception is thrown when the file is missing.
*/
async getArrayBuffer(key) {
debug_default("reading file contents as array buffer %s:%s", this.#rootUrl, key);
return this.#read(key).then((value) => new Uint8Array(value.buffer));
}
/**
* Returns the metadata of a file.
*/
async getMetaData(key) {
debug_default("fetching file metadata %s:%s", this.#rootUrl, key);
const location = join(this.#rootUrl, key);
const stats = await fsp.stat(location);
if (stats.isDirectory()) {
throw new RuntimeException(`Cannot get metadata of a directory "${key}"`);
}
return {
contentLength: stats.size,
contentType: mimeTypes.lookup(key) || void 0,
etag: etag(stats),
lastModified: stats.mtime
};
}
/**
* Returns the file visibility from the pre-defined config
* value
*/
async getVisibility(_) {
return this.options.visibility;
}
/**
* Returns the public URL of the file. This method does not check
* if the file exists or not.
*/
async getUrl(key) {
const location = join(this.#rootUrl, key);
const generateURL = this.options.urlBuilder?.generateURL;
if (generateURL) {
debug_default("generating public URL %s:%s", this.#rootUrl, key);
return generateURL(key, location);
}
throw new RuntimeException('Cannot generate URL. The "fs" driver does not support it');
}
/**
* Returns the signed/temporary URL of the file. By default, the signed URLs
* expire in 30mins, but a custom expiry can be defined using
* "options.expiresIn" property.
*/
async getSignedUrl(key, options) {
const location = join(this.#rootUrl, key);
const normalizedOptions = Object.assign(
{
expiresIn: "30 mins"
},
options
);
const generateSignedURL = this.options.urlBuilder?.generateSignedURL;
if (generateSignedURL) {
debug_default("generating signed URL %s:%s", this.#rootUrl, key);
return generateSignedURL(key, location, normalizedOptions);
}
throw new RuntimeException('Cannot generate signed URL. The "fs" driver does not support it');
}
/**
* Results in noop, since the local filesystem cannot have per
* object visibility.
*/
async setVisibility(_, __) {
}
/**
* Writes a file to the destination with the provided contents.
*
* - Missing directories will be created recursively.
* - Existing file will be overwritten.
*/
put(key, contents, options) {
debug_default("creating/updating file %s:%s", this.#rootUrl, key);
return this.#write(key, contents, { signal: options?.signal });
}
/**
* Writes a file to the destination with the provided contents
* as a readable stream.
*
* - Missing directories will be created recursively.
* - Existing file will be overwritten.
*/
putStream(key, contents, options) {
debug_default("creating/updating file using readable stream %s:%s", this.#rootUrl, key);
return new Promise((resolve, reject) => {
contents.once("error", (error) => reject(error));
return this.#write(key, contents, { signal: options?.signal }).then(resolve).catch(reject);
});
}
/**
* Copies the source file to the destination. Both paths must
* be within the root location.
*/
copy(source, destination) {
debug_default("copying file from %s to %s", source, destination);
const sourceLocation = join(this.#rootUrl, source);
const destinationLocation = join(this.#rootUrl, destination);
return this.#retrier.retry(async () => {
await fsp.mkdir(dirname(destinationLocation), { recursive: true });
await fsp.copyFile(sourceLocation, destinationLocation);
});
}
/**
* Moves the source file to the destination. Both paths must
* be within the root location.
*/
move(source, destination) {
debug_default("moving file from %s to %s", source, destination);
const sourceLocation = join(this.#rootUrl, source);
const destinationLocation = join(this.#rootUrl, destination);
return this.#retrier.retry(async () => {
await fsp.mkdir(dirname(destinationLocation), { recursive: true });
await fsp.copyFile(sourceLocation, destinationLocation);
await fsp.unlink(sourceLocation);
});
}
/**
* Deletes a file within the root location of the filesystem.
* Attempting to delete a non-existing file will result in
* a noop.
*/
delete(key) {
debug_default("deleting file %s:%s", this.#rootUrl, key);
const location = join(this.#rootUrl, key);
return this.#retrier.retry(async () => {
try {
await fsp.unlink(location);
} catch (error) {
if (error.code !== "ENOENT") {
throw error;
}
}
});
}
/**
* Deletes the files and directories matching the provided
* prefix. The method is same as running "rm -rf" unix
* command
*/
deleteAll(prefix) {
debug_default("deleting all files in folder %s:%s", this.#rootUrl, prefix);
const location = join(this.#rootUrl, prefix);
return this.#retrier.retry(async () => {
return fsp.rm(location, { recursive: true, force: true });
});
}
/**
* Returns a list of files. The pagination properties are ignored
* by the fs driver, since it does not support pagination.
*/
async listAll(prefix, options) {
const self = this;
const location = join(this.#rootUrl, prefix);
const { recursive } = Object.assign({ recursive: false }, options);
debug_default("listing files from folder %s:%s %O", this.#rootUrl, prefix, options);
const files = await fsp.readdir(location, {
recursive,
withFileTypes: true
});
function* filesGenerator() {
for (const file of files) {
const relativeName = slash(
// @ts-expect-error "Dirent.parentPath" is the new property, but missing on types
relative(self.#rootUrl, join(file.parentPath || file.path, file.name))
);
if (file.isFile()) {
yield new DriveFile(relativeName, self);
} else if (!recursive) {
yield new DriveDirectory(relativeName);
}
}
}
return {
paginationToken: void 0,
objects: {
[Symbol.iterator]: filesGenerator
}
};
}
};
FSDriver
} from "../../chunk-PNY64WQT.js";
import "../../chunk-FRUCTVD5.js";
export {

@@ -279,0 +6,0 @@ FSDriver

@@ -19,3 +19,3 @@ /// <reference types="node" resolution-mode="require"/>

*/
exist(key: string): Promise<boolean>;
exists(key: string): Promise<boolean>;
/**

@@ -22,0 +22,0 @@ * Returns the contents of a file as a UTF-8 string. An

import {
DriveDirectory
} from "../../chunk-Y2S65K27.js";
import {
DriveDirectory,
DriveFile
} from "../../chunk-2GUJ7FO2.js";
} from "../../chunk-FRUCTVD5.js";

@@ -124,3 +122,3 @@ // drivers/gcs/driver.ts

*/
async exist(key) {
async exists(key) {
debug_default("checking if file exists %s:%s", this.options.bucket, key);

@@ -321,2 +319,3 @@ const bucket = this.#storage.bucket(this.options.bucket);

}
debug_default("listing all files matching prefix %s:%s", this.options.bucket, prefix);
const response = await this.#getGCSObjects({

@@ -323,0 +322,0 @@ autoPaginate: false,

@@ -1,2 +0,2 @@

import type { S3Client, S3ClientConfig } from '@aws-sdk/client-s3';
import type { GetObjectAclCommandInput, S3Client, S3ClientConfig } from '@aws-sdk/client-s3';
import type { ObjectVisibility } from '../../src/types.js';

@@ -16,2 +16,31 @@ /**

visibility: ObjectVisibility;
/**
* Does service supports ACL?
*
* When set to "false", the ACL related commands uses visibility
* defined within the config without any API call.
*
* Defaults to "true". However, when you are using Cloudflare R2, you
* must set it to "false".
*/
supportsACL?: boolean;
/**
* An optional CDN URL to use for public URLs. Otherwise the endpoint
* will be used
*/
cdnUrl?: string;
/**
* Configure a custom URL builder for creating public and
* temporary URLs
*/
urlBuilder?: {
/**
* Custom implementation for creating public URLs
*/
generateURL?(key: string, bucket: string, client: S3Client): Promise<string>;
/**
* Custom implementation for creating signed/temporary URLs
*/
generateSignedURL?(key: string, options: GetObjectAclCommandInput, client: S3Client): Promise<string>;
};
};

@@ -18,0 +47,0 @@ /**

export { Disk } from './src/disk.js';
export * as errors from './src/errors.js';
export { DriveFile } from './src/driver_file.js';
export { DriveManager } from './src/drive_manager.js';
export { KeyNormalizer } from './src/key_normalizer.js';
export { DriveDirectory } from './src/drive_directory.js';
import {
FSDriver
} from "./chunk-PNY64WQT.js";
import {
DriveDirectory,
DriveFile,

@@ -11,3 +15,3 @@ E_CANNOT_COPY_FILE,

errors_exports
} from "./chunk-2GUJ7FO2.js";
} from "./chunk-FRUCTVD5.js";

@@ -191,3 +195,3 @@ // src/disk.ts

async deleteAll(prefix) {
prefix = this.#normalizer.normalize(prefix);
prefix = prefix && prefix !== "/" ? this.#normalizer.normalize(prefix) : "/";
try {

@@ -204,7 +208,141 @@ return await this.driver.deleteAll(prefix);

listAll(prefix, options) {
prefix = prefix && prefix !== "/" ? this.#normalizer.normalize(prefix) : "/";
return this.driver.listAll(prefix, options);
}
};
// src/drive_manager.ts
import { RuntimeException } from "@poppinss/utils";
// src/debug.ts
import { debuglog } from "node:util";
var debug_default = debuglog("flydrive:core");
// src/fake_disk.ts
import { join } from "node:path";
import { AssertionError } from "node:assert";
var FakeDisk = class extends Disk {
constructor(disk, fakesConfig) {
super(
new FSDriver({
location: typeof fakesConfig.location === "string" ? join(fakesConfig.location, disk) : new URL(disk, fakesConfig.location),
visibility: "public",
urlBuilder: fakesConfig.urlBuilder
})
);
this.disk = disk;
}
/**
* Assert the expected file(s) exists. Otherwise an assertion
* error is thrown
*/
assertExists(paths) {
const pathsToVerify = Array.isArray(paths) ? paths : [paths];
for (let filePath of pathsToVerify) {
if (!this.driver.existsSync(filePath)) {
throw new AssertionError({
message: `Expected "${filePath}" to exist, but file not found.`
});
}
}
}
/**
* Assert the expected file(s) to not exist. Otherwise an assertion
* error is thrown
*/
assertMissing(paths) {
const pathsToVerify = Array.isArray(paths) ? paths : [paths];
for (let filePath of pathsToVerify) {
if (this.driver.existsSync(filePath)) {
throw new AssertionError({
message: `Expected "${filePath}" to be missing, but file exists`
});
}
}
}
/**
* Clear storage
*/
clear() {
this.driver.clearSync();
}
};
// src/drive_manager.ts
var DriveManager = class {
/**
* Registered config
*/
#config;
/**
* A collection of cached service. We re-use disk instances for a
* service, since there isn't any need to reconstruct them
* everytime.
*/
#cachedServices = /* @__PURE__ */ new Map();
/**
* A collection of fakes created for the services.
*/
#fakes = /* @__PURE__ */ new Map();
constructor(config) {
this.#config = config;
debug_default("driver manager config %O", config);
}
/**
* Returns an instance of a Disk for the given service. By default
* use the "default" service from config
*/
use(service) {
const serviceToUse = service || this.#config.default;
const fake = this.#fakes.get(serviceToUse);
if (fake) {
debug_default("returning fake for service %s", serviceToUse);
return fake;
}
const cachedDisk = this.#cachedServices.get(serviceToUse);
if (cachedDisk) {
debug_default("use cached disk instance for service %s", serviceToUse);
return cachedDisk;
}
const disk = new Disk(this.#config.services[serviceToUse]());
debug_default("creating disk instance for service %s", serviceToUse);
this.#cachedServices.set(serviceToUse, disk);
return disk;
}
/**
* Deploy fake for a given service. The "use" method for the same service
* will now return an instance of the "FakeDisk" class and not the
* real implementation.
*/
fake(service) {
const serviceToUse = service || this.#config.default;
if (!this.#config.fakes) {
throw new RuntimeException(
'Cannot use "drive.fake". Make sure to define fakes configuration when creating DriveManager instance'
);
}
this.restore(serviceToUse);
debug_default("creating fake for service %s", serviceToUse);
const fake = new FakeDisk(serviceToUse, this.#config.fakes);
this.#fakes.set(serviceToUse, fake);
return fake;
}
/**
* Restore fake for a given service
*/
restore(service) {
const serviceToUse = service || this.#config.default;
const fake = this.#fakes.get(serviceToUse);
if (fake) {
debug_default("restoring fake for service %s", serviceToUse);
fake.clear();
this.#fakes.delete(serviceToUse);
}
}
};
export {
Disk,
DriveDirectory,
DriveFile,
DriveManager,
KeyNormalizer,

@@ -211,0 +349,0 @@ errors_exports as errors

@@ -6,3 +6,3 @@ /// <reference types="node" resolution-mode="require"/>

import { DriveDirectory } from './drive_directory.js';
import type { DriverContract, FileSnapshot, ObjectMetaData, ObjectVisibility, SignedURLOptions, WriteOptions } from './types.js';
import type { WriteOptions, FileSnapshot, ObjectMetaData, DriverContract, ObjectVisibility, SignedURLOptions } from './types.js';
/**

@@ -106,3 +106,3 @@ * Disk offers a unified API for working with different drivers

*/
deleteAll(prefix: string): Promise<void>;
deleteAll(prefix?: string): Promise<void>;
/**

@@ -112,3 +112,3 @@ * Returns a list of objects which includes and files and directories.

*/
listAll(prefix: string, options?: {
listAll(prefix?: string, options?: {
recursive?: boolean;

@@ -115,0 +115,0 @@ paginationToken?: string;

/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
import { Readable } from 'node:stream';

@@ -62,3 +63,3 @@ import { DriveFile } from './driver_file.js';

*/
exist(key: string): Promise<boolean>;
exists(key: string): Promise<boolean>;
/**

@@ -145,1 +146,38 @@ * Return contents of a object for the given key as a UTF-8 string.

}
/**
* Configuration accepted by DriveManager
*/
export interface DriveManagerOptions<Services extends Record<string, () => DriverContract>> {
/**
* The default service to use for file system operations
*/
default: keyof Services;
/**
* Configured services
*/
services: Services;
/**
* Fakes configuration. Only needed when using fakes from the
* DriveManager
*/
fakes?: {
/**
* The location for persisting files during fake mode
*/
location: URL | string;
/**
* Configure a custom URL builder for creating public and
* temporary URLs in fake mode
*/
urlBuilder?: {
/**
* Custom implementation for creating public URLs
*/
generateURL?(key: string, filePath: string): Promise<string>;
/**
* Custom implementation for creating signed/temporary URLs
*/
generateSignedURL?(key: string, filePath: string, options: SignedURLOptions): Promise<string>;
};
};
}
{
"name": "flydrive",
"description": "File storage library with unified API to manage files across multiple cloud storage providers like S3, GCS, R2 and so on",
"version": "0.0.1-4",
"version": "1.0.0",
"engines": {

@@ -21,16 +21,16 @@ "node": ">=20.6.0"

"./drivers/gcs": "./build/drivers/gcs/main.js",
"./drivers/gcs/types": "./build/drivers/gcs/types.js"
"./drivers/gcs/types": "./build/drivers/gcs/types.js",
"./drivers/s3": "./build/drivers/s3/main.js",
"./drivers/s3/types": "./build/drivers/s3/types.js"
},
"scripts": {
"clean": "del-cli build",
"copy:templates": "copyfiles \"stubs/**/*.stub\" build",
"typecheck": "tsc --noEmit",
"lint": "eslint . --ext=.ts",
"format": "prettier --write .",
"quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
"quick:test": "cross-env NODE_DEBUG=flydrive:* node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
"pretest": "npm run lint",
"test": "cross-env NODE_DEBUG=flydrive:* c8 npm run quick:test",
"test": "c8 npm run quick:test",
"prebuild": "npm run lint && npm run clean",
"build": "tsup-node && tsc --emitDeclarationOnly --declaration",
"postbuild": "npm run copy:templates",
"release": "np",

@@ -49,2 +49,8 @@ "version": "npm run build",

"license": "MIT",
"dependencies": {
"@humanwhocodes/retry": "^0.3.0",
"@poppinss/utils": "^6.7.3",
"etag": "^1.8.1",
"mime-types": "^2.1.35"
},
"devDependencies": {

@@ -55,3 +61,4 @@ "@adonisjs/env": "^6.0.1",

"@adonisjs/tsconfig": "^1.3.0",
"@aws-sdk/client-s3": "^3.564.0",
"@aws-sdk/client-s3": "^3.577.0",
"@aws-sdk/s3-request-presigner": "^3.577.0",
"@google-cloud/storage": "^7.10.2",

@@ -61,6 +68,7 @@ "@japa/assert": "^3.0.0",

"@japa/runner": "^3.1.1",
"@swc/core": "^1.4.17",
"@swc/core": "^1.5.7",
"@types/etag": "^1.8.3",
"@types/mime-types": "^2.1.4",
"@types/node": "^20.12.7",
"@types/node": "^20.12.12",
"@types/sinon": "^17.0.3",
"c8": "^9.1.0",

@@ -74,2 +82,3 @@ "copyfiles": "^2.4.1",

"prettier": "^3.2.5",
"sinon": "^18.0.0",
"ts-node": "^10.9.2",

@@ -79,2 +88,18 @@ "tsup": "^8.0.2",

},
"peerDependencies": {
"@aws-sdk/client-s3": "^3.577.0",
"@aws-sdk/s3-request-presigner": "^3.577.0",
"@google-cloud/storage": "^7.10.2"
},
"peerDependenciesMeta": {
"@aws-sdk/client-s3": {
"optional": true
},
"@aws-sdk/s3-request-presigner": {
"optional": true
},
"@google-cloud/storage": {
"optional": true
}
},
"publishConfig": {

@@ -103,8 +128,2 @@ "access": "public",

"prettier": "@adonisjs/prettier-config",
"dependencies": {
"@humanwhocodes/retry": "^0.2.3",
"@poppinss/utils": "^6.7.3",
"etag": "^1.8.1",
"mime-types": "^2.1.35"
},
"tsup": {

@@ -117,3 +136,5 @@ "entry": [

"./drivers/gcs/main.ts",
"./drivers/gcs/types.ts"
"./drivers/gcs/types.ts",
"./drivers/s3/main.ts",
"./drivers/s3/types.ts"
],

@@ -120,0 +141,0 @@ "outDir": "./build",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet