Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

flydrive

Package Overview
Dependencies
Maintainers
1
Versions
10
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 0.0.1-4 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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc