Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
1a71c76da3 | |||
e924511147 | |||
645ebbdd4d | |||
168148b2c9 | |||
1293fc4ca6 | |||
b040120813 |
@ -8,7 +8,7 @@
|
||||
"githost": "code.foss.global",
|
||||
"gitscope": "push.rocks",
|
||||
"gitrepo": "smartbucket",
|
||||
"description": "A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.",
|
||||
"description": "A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.",
|
||||
"npmPackagename": "@push.rocks/smartbucket",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -26,7 +26,12 @@
|
||||
"unified storage",
|
||||
"buffer handling",
|
||||
"access key",
|
||||
"secret key"
|
||||
"secret key",
|
||||
"metadata",
|
||||
"file locking",
|
||||
"file streaming",
|
||||
"directory listing",
|
||||
"cloud agnostic"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "3.0.4",
|
||||
"version": "3.0.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "3.0.4",
|
||||
"version": "3.0.7",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@push.rocks/smartpath": "^5.0.18",
|
||||
|
11
package.json
11
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "3.0.4",
|
||||
"description": "A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.",
|
||||
"version": "3.0.7",
|
||||
"description": "A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"type": "module",
|
||||
@ -58,6 +58,11 @@
|
||||
"unified storage",
|
||||
"buffer handling",
|
||||
"access key",
|
||||
"secret key"
|
||||
"secret key",
|
||||
"metadata",
|
||||
"file locking",
|
||||
"file streaming",
|
||||
"directory listing",
|
||||
"cloud agnostic"
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
# @push.rocks/smartbucket
|
||||
A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.
|
||||
|
||||
A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.
|
||||
|
||||
## Install
|
||||
|
||||
@ -199,12 +200,15 @@ writeFileStream("exampleBucket", "path/to/streamedObject.txt", readable);
|
||||
|
||||
`@push.rocks/smartbucket` abstracts directories within buckets for easier object management. You can create, list, and delete directories using the `Directory` class.
|
||||
|
||||
Here's how to list the contents of a directory:
|
||||
|
||||
```typescript
|
||||
async function listDirectoryContents(bucketName: string, directoryPath: string) {
|
||||
const myBucket: Bucket = await mySmartBucket.getBucketByName(bucketName);
|
||||
if (myBucket) {
|
||||
const baseDirectory: Directory = await myBucket.getBaseDirectory();
|
||||
const targetDirectory: Directory = await baseDirectory.getSubDirectoryByName(directoryPath);
|
||||
|
||||
console.log('Listing directories:');
|
||||
const directories = await targetDirectory.listDirectories();
|
||||
directories.forEach(dir => {
|
||||
@ -304,6 +308,8 @@ Remember, each cloud provider has specific features and limitations. `@push.rock
|
||||
|
||||
This guide covers the basic to advanced scenarios of using `@push.rocks/smartbucket`. For further details, refer to the API documentation and examples.
|
||||
|
||||
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
|
@ -79,7 +79,7 @@ tap.test('prepare for directory style tests', async () => {
|
||||
contents: 'dir3/dir4/file1.txt content',
|
||||
});
|
||||
await myBucket.fastPut({
|
||||
path: 'file1.txt',
|
||||
path: '/file1.txt',
|
||||
contents: 'file1 content',
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartbucket',
|
||||
version: '3.0.4',
|
||||
description: 'A TypeScript library that offers simple, cloud-independent object storage with features like bucket creation, file management, and directory management.'
|
||||
version: '3.0.7',
|
||||
description: 'A TypeScript library for cloud-independent object storage, providing features like bucket creation, file and directory management, and data streaming.'
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as helpers from './helpers.js';
|
||||
import * as interfaces from './interfaces.js';
|
||||
import { SmartBucket } from './classes.smartbucket.js';
|
||||
import { Directory } from './classes.directory.js';
|
||||
import { File } from './classes.file.js';
|
||||
|
||||
export class Bucket {
|
||||
public static async getBucketByName(smartbucketRef: SmartBucket, bucketNameArg: string) {
|
||||
@ -38,10 +41,19 @@ export class Bucket {
|
||||
/**
|
||||
* gets the base directory of the bucket
|
||||
*/
|
||||
public async getBaseDirectory() {
|
||||
public async getBaseDirectory(): Promise<Directory> {
|
||||
return new Directory(this, null, '');
|
||||
}
|
||||
|
||||
public async getDirectoryFromPath(pathDescriptorArg: interfaces.IPathDecriptor): Promise<Directory> {
|
||||
if (!pathDescriptorArg.path && !pathDescriptorArg.directory) {
|
||||
return this.getBaseDirectory();
|
||||
}
|
||||
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptorArg);
|
||||
const baseDirectory = await this.getBaseDirectory();
|
||||
return await baseDirectory.getSubDirectoryByName(checkPath);
|
||||
}
|
||||
|
||||
// ===============
|
||||
// Fast Operations
|
||||
// ===============
|
||||
@ -53,28 +65,38 @@ export class Bucket {
|
||||
path: string;
|
||||
contents: string | Buffer;
|
||||
overwrite?: boolean;
|
||||
}): Promise<void> {
|
||||
}): Promise<File> {
|
||||
try {
|
||||
const reducedPath = await helpers.reducePathDescriptorToPath({
|
||||
path: optionsArg.path,
|
||||
})
|
||||
// Check if the object already exists
|
||||
const exists = await this.fastExists({ path: optionsArg.path });
|
||||
const exists = await this.fastExists({ path: reducedPath });
|
||||
|
||||
if (exists && !optionsArg.overwrite) {
|
||||
console.error(`Object already exists at path '${optionsArg.path}' in bucket '${this.name}'.`);
|
||||
console.error(`Object already exists at path '${reducedPath}' in bucket '${this.name}'.`);
|
||||
return;
|
||||
} else if (exists && optionsArg.overwrite) {
|
||||
console.log(`Overwriting existing object at path '${optionsArg.path}' in bucket '${this.name}'.`);
|
||||
console.log(`Overwriting existing object at path '${reducedPath}' in bucket '${this.name}'.`);
|
||||
} else {
|
||||
console.log(`Creating new object at path '${optionsArg.path}' in bucket '${this.name}'.`);
|
||||
console.log(`Creating new object at path '${reducedPath}' in bucket '${this.name}'.`);
|
||||
}
|
||||
|
||||
// Proceed with putting the object
|
||||
const streamIntake = new plugins.smartstream.StreamIntake();
|
||||
const putPromise = this.smartbucketRef.minioClient.putObject(this.name, optionsArg.path, streamIntake);
|
||||
const putPromise = this.smartbucketRef.minioClient.putObject(this.name, reducedPath, streamIntake);
|
||||
streamIntake.pushData(optionsArg.contents);
|
||||
streamIntake.signalEnd();
|
||||
await putPromise;
|
||||
|
||||
console.log(`Object '${optionsArg.path}' has been successfully stored in bucket '${this.name}'.`);
|
||||
console.log(`Object '${reducedPath}' has been successfully stored in bucket '${this.name}'.`);
|
||||
const parsedPath = plugins.path.parse(reducedPath);
|
||||
return new File({
|
||||
directoryRefArg: await this.getDirectoryFromPath({
|
||||
path: parsedPath.dir,
|
||||
}),
|
||||
fileName: parsedPath.base,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error storing object at path '${optionsArg.path}' in bucket '${this.name}':`, error);
|
||||
throw error;
|
||||
@ -183,19 +205,10 @@ export class Bucket {
|
||||
}
|
||||
|
||||
|
||||
public async copyObject(optionsArg: {
|
||||
/**
|
||||
* the
|
||||
*/
|
||||
objectKey: string;
|
||||
/**
|
||||
* in case you want to copy to another bucket specify it here
|
||||
*/
|
||||
public async fastCopy(optionsArg: {
|
||||
sourcePath: string;
|
||||
destinationPath?: string;
|
||||
targetBucket?: Bucket;
|
||||
targetBucketKey?: string;
|
||||
/**
|
||||
* metadata will be merged with existing metadata
|
||||
*/
|
||||
nativeMetadata?: { [key: string]: string };
|
||||
deleteExistingNativeMetadata?: boolean;
|
||||
}): Promise<void> {
|
||||
@ -205,7 +218,7 @@ export class Bucket {
|
||||
// Retrieve current object information to use in copy conditions
|
||||
const currentObjInfo = await this.smartbucketRef.minioClient.statObject(
|
||||
targetBucketName,
|
||||
optionsArg.objectKey
|
||||
optionsArg.sourcePath
|
||||
);
|
||||
|
||||
// Setting up copy conditions
|
||||
@ -221,8 +234,8 @@ export class Bucket {
|
||||
// TODO: check on issue here: https://github.com/minio/minio-js/issues/1286
|
||||
await this.smartbucketRef.minioClient.copyObject(
|
||||
this.name,
|
||||
optionsArg.objectKey,
|
||||
`/${targetBucketName}/${optionsArg.objectKey}`,
|
||||
optionsArg.sourcePath,
|
||||
`/${targetBucketName}/${optionsArg.destinationPath || optionsArg.sourcePath}`,
|
||||
copyConditions
|
||||
);
|
||||
} catch (err) {
|
||||
@ -231,6 +244,43 @@ export class Bucket {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move object from one path to another within the same bucket or to another bucket
|
||||
*/
|
||||
public async fastMove(optionsArg: {
|
||||
sourcePath: string;
|
||||
destinationPath: string;
|
||||
targetBucket?: Bucket;
|
||||
overwrite?: boolean;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
// Check if the destination object already exists
|
||||
const destinationBucket = optionsArg.targetBucket || this;
|
||||
const exists = await destinationBucket.fastExists({ path: optionsArg.destinationPath });
|
||||
|
||||
if (exists && !optionsArg.overwrite) {
|
||||
console.error(`Object already exists at destination path '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`);
|
||||
return;
|
||||
} else if (exists && optionsArg.overwrite) {
|
||||
console.log(`Overwriting existing object at destination path '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`);
|
||||
} else {
|
||||
console.log(`Moving object to path '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`);
|
||||
}
|
||||
|
||||
// Proceed with copying the object to the new path
|
||||
await this.fastCopy(optionsArg);
|
||||
|
||||
// Remove the original object after successful copy
|
||||
await this.fastRemove({ path: optionsArg.sourcePath });
|
||||
|
||||
console.log(`Object '${optionsArg.sourcePath}' has been successfully moved to '${optionsArg.destinationPath}' in bucket '${destinationBucket.name}'.`);
|
||||
} catch (error) {
|
||||
console.error(`Error moving object from '${optionsArg.sourcePath}' to '${optionsArg.destinationPath}':`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* removeObject
|
||||
*/
|
||||
@ -263,9 +313,56 @@ export class Bucket {
|
||||
}
|
||||
}
|
||||
|
||||
public async fastStat(optionsArg: {
|
||||
path: string;
|
||||
}) {
|
||||
return this.smartbucketRef.minioClient.statObject(this.name, optionsArg.path);
|
||||
public async fastStat(pathDescriptor: interfaces.IPathDecriptor) {
|
||||
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor);
|
||||
return this.smartbucketRef.minioClient.statObject(this.name, checkPath);
|
||||
}
|
||||
|
||||
public async isDirectory(pathDescriptor: interfaces.IPathDecriptor): Promise<boolean> {
|
||||
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor);
|
||||
|
||||
// lets check if the checkPath is a directory
|
||||
const stream = this.smartbucketRef.minioClient.listObjectsV2(this.name, checkPath, true);
|
||||
const done = plugins.smartpromise.defer<boolean>();
|
||||
stream.on('data', (dataArg) => {
|
||||
stream.destroy(); // Stop the stream early if we find at least one object
|
||||
if (dataArg.prefix.startsWith(checkPath + '/')) {
|
||||
done.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
done.resolve(false);
|
||||
});
|
||||
|
||||
stream.on('error', (err) => {
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
return done.promise;
|
||||
};
|
||||
|
||||
public async isFile(pathDescriptor: interfaces.IPathDecriptor): Promise<boolean> {
|
||||
let checkPath = await helpers.reducePathDescriptorToPath(pathDescriptor);
|
||||
|
||||
// lets check if the checkPath is a directory
|
||||
const stream = this.smartbucketRef.minioClient.listObjectsV2(this.name, checkPath, true);
|
||||
const done = plugins.smartpromise.defer<boolean>();
|
||||
stream.on('data', (dataArg) => {
|
||||
stream.destroy(); // Stop the stream early if we find at least one object
|
||||
if (dataArg.prefix === checkPath) {
|
||||
done.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
done.resolve(false);
|
||||
});
|
||||
|
||||
stream.on('error', (err) => {
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
return done.promise;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as helpers from './helpers.js';
|
||||
import * as interfaces from './interfaces.js';
|
||||
import { Directory } from './classes.directory.js';
|
||||
import { MetaData } from './classes.metadata.js';
|
||||
|
||||
@ -102,7 +104,7 @@ export class File {
|
||||
const metadata = await this.getMetaData();
|
||||
await metadata.setLock({
|
||||
lock: 'locked',
|
||||
expires: new Date(Date.now() + (optionsArg?.timeoutMillis || 1000)),
|
||||
expires: Date.now() + (optionsArg?.timeoutMillis || 1000),
|
||||
});
|
||||
}
|
||||
|
||||
@ -116,7 +118,10 @@ export class File {
|
||||
*/
|
||||
force?: boolean;
|
||||
}) {
|
||||
|
||||
const metadata = await this.getMetaData();
|
||||
await metadata.removeLock({
|
||||
force: optionsArg?.force,
|
||||
});
|
||||
}
|
||||
|
||||
public async updateWithContents(optionsArg: {
|
||||
@ -141,14 +146,55 @@ export class File {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* moves the file to another directory
|
||||
*/
|
||||
public async move(pathDescriptorArg: interfaces.IPathDecriptor) {
|
||||
let moveToPath = '';
|
||||
const isDirectory = await this.parentDirectoryRef.bucketRef.isDirectory(pathDescriptorArg);
|
||||
if (isDirectory) {
|
||||
moveToPath = await helpers.reducePathDescriptorToPath({
|
||||
...pathDescriptorArg,
|
||||
path: plugins.path.join(pathDescriptorArg.path, this.name),
|
||||
});
|
||||
}
|
||||
// lets move the file
|
||||
await this.parentDirectoryRef.bucketRef.fastMove({
|
||||
sourcePath: this.getBasePath(),
|
||||
destinationPath: moveToPath,
|
||||
});
|
||||
|
||||
// lets move the metadatafile
|
||||
const metadata = await this.getMetaData();
|
||||
await metadata.metadataFile.move(pathDescriptorArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* allows updating the metadata of a file
|
||||
* @param updatedMetadata
|
||||
*/
|
||||
public async getMetaData() {
|
||||
if (this.name.endsWith('.metadata')) {
|
||||
throw new Error('metadata files cannot have metadata');
|
||||
}
|
||||
const metadata = await MetaData.createForFile({
|
||||
file: this,
|
||||
});
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the contents as json
|
||||
*/
|
||||
public async getJsonData() {
|
||||
const json = await this.getContentsAsString();
|
||||
const parsed = await JSON.parse(json);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
public async writeJsonData(dataArg: any) {
|
||||
await this.updateWithContents({
|
||||
contents: JSON.stringify(dataArg),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,7 @@ import { File } from './classes.file.js';
|
||||
|
||||
export class MetaData {
|
||||
// static
|
||||
public static async createForFile(optionsArg: {
|
||||
file: File;
|
||||
}) {
|
||||
public static async createForFile(optionsArg: { file: File }) {
|
||||
const metaData = new MetaData();
|
||||
metaData.fileRef = optionsArg.file;
|
||||
|
||||
@ -29,16 +27,16 @@ export class MetaData {
|
||||
* the file that the metadata is for
|
||||
*/
|
||||
fileRef: File;
|
||||
|
||||
|
||||
public async getFileType(optionsArg?: {
|
||||
useFileExtension?: boolean;
|
||||
useMagicBytes?: boolean;
|
||||
}): Promise<string> {
|
||||
if (optionsArg && optionsArg.useFileExtension || optionsArg.useFileExtension === undefined) {
|
||||
if ((optionsArg && optionsArg.useFileExtension) || optionsArg.useFileExtension === undefined) {
|
||||
return plugins.path.extname(this.fileRef.name);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the size of the fileRef
|
||||
*/
|
||||
@ -47,59 +45,56 @@ export class MetaData {
|
||||
path: this.fileRef.getBasePath(),
|
||||
});
|
||||
return stat.size;
|
||||
};
|
||||
}
|
||||
|
||||
private prefixCustomMetaData = 'custom_';
|
||||
|
||||
public async storeCustomMetaData<T = any>(optionsArg: {
|
||||
key: string;
|
||||
value: T;
|
||||
}) {
|
||||
const json = await this.metadataFile.getContentsAsString();
|
||||
const parsed = await JSON.parse(json);
|
||||
parsed[this.prefixCustomMetaData + optionsArg.key] = optionsArg.value;
|
||||
await this.metadataFile.updateWithContents({
|
||||
contents: JSON.stringify(parsed),
|
||||
});
|
||||
public async storeCustomMetaData<T = any>(optionsArg: { key: string; value: T }) {
|
||||
const data = await this.metadataFile.getContentsAsString();
|
||||
data[this.prefixCustomMetaData + optionsArg.key] = optionsArg.value;
|
||||
await this.metadataFile.writeJsonData(data);
|
||||
}
|
||||
|
||||
public async getCustomMetaData<T = any>(optionsArg: {
|
||||
key: string;
|
||||
}): Promise<T> {
|
||||
const json = await this.metadataFile.getContentsAsString();
|
||||
const parsed = await JSON.parse(json);
|
||||
return parsed[this.prefixCustomMetaData + optionsArg.key];
|
||||
public async getCustomMetaData<T = any>(optionsArg: { key: string }): Promise<T> {
|
||||
const data = await this.metadataFile.getJsonData();
|
||||
return data[this.prefixCustomMetaData + optionsArg.key];
|
||||
}
|
||||
|
||||
public async deleteCustomMetaData(optionsArg: {
|
||||
key: string;
|
||||
}) {
|
||||
const json = await this.metadataFile.getContentsAsString();
|
||||
const parsed = await JSON.parse(json);
|
||||
delete parsed[this.prefixCustomMetaData + optionsArg.key];
|
||||
await this.metadataFile.updateWithContents({
|
||||
contents: JSON.stringify(parsed),
|
||||
});
|
||||
public async deleteCustomMetaData(optionsArg: { key: string }) {
|
||||
const data = await this.metadataFile.getJsonData();
|
||||
delete data[this.prefixCustomMetaData + optionsArg.key];
|
||||
await this.metadataFile.writeJsonData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* set a lock on the ref file
|
||||
* @param optionsArg
|
||||
*/
|
||||
public async setLock(optionsArg: {
|
||||
lock: string;
|
||||
expires: Date;
|
||||
}) {
|
||||
|
||||
public async setLock(optionsArg: { lock: string; expires: number }) {
|
||||
const data = await this.metadataFile.getJsonData();
|
||||
data.lock = optionsArg.lock;
|
||||
data.lockExpires = optionsArg.expires;
|
||||
await this.metadataFile.writeJsonData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove the lock on the ref file
|
||||
* @param optionsArg
|
||||
* @param optionsArg
|
||||
*/
|
||||
public async removeLock(optionsArg: {
|
||||
force: boolean;
|
||||
}) {
|
||||
|
||||
public async removeLock(optionsArg: { force: boolean }) {
|
||||
const data = await this.metadataFile.getJsonData();
|
||||
delete data.lock;
|
||||
delete data.lockExpires;
|
||||
await this.metadataFile.writeJsonData(data);
|
||||
}
|
||||
}
|
||||
|
||||
public async checkLocked(): Promise<boolean> {
|
||||
const data = await this.metadataFile.getJsonData();
|
||||
return data.lock && data.lockExpires > Date.now();
|
||||
}
|
||||
|
||||
public async getLockInfo(): Promise<{ lock: string; expires: number }> {
|
||||
const data = await this.metadataFile.getJsonData();
|
||||
return { lock: data.lock, expires: data.lockExpires };
|
||||
}
|
||||
}
|
||||
|
22
ts/helpers.ts
Normal file
22
ts/helpers.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as interfaces from './interfaces.js';
|
||||
|
||||
export const reducePathDescriptorToPath = async (pathDescriptorArg: interfaces.IPathDecriptor): Promise<string> => {
|
||||
let returnPath = ``
|
||||
if (pathDescriptorArg.directory) {
|
||||
if (pathDescriptorArg.path && plugins.path.isAbsolute(pathDescriptorArg.path)) {
|
||||
console.warn('Directory is being ignored when path is absolute.');
|
||||
returnPath = pathDescriptorArg.path;
|
||||
} else if (pathDescriptorArg.path) {
|
||||
returnPath = plugins.path.join(pathDescriptorArg.directory.getBasePath(), pathDescriptorArg.path);
|
||||
}
|
||||
} else if (pathDescriptorArg.path) {
|
||||
returnPath = pathDescriptorArg.path;
|
||||
} else {
|
||||
throw new Error('You must specify either a path or a directory.');
|
||||
}
|
||||
if (returnPath.startsWith('/')) {
|
||||
returnPath = returnPath.substring(1);
|
||||
}
|
||||
return returnPath;
|
||||
}
|
6
ts/interfaces.ts
Normal file
6
ts/interfaces.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import type { Directory } from "./classes.directory.js";
|
||||
|
||||
export interface IPathDecriptor {
|
||||
path?: string;
|
||||
directory?: Directory;
|
||||
}
|
Reference in New Issue
Block a user