feat(core): add provider defaults, strengthen WebDAV validation, and modernize tests and package metadata

This commit is contained in:
2026-05-01 22:31:47 +00:00
parent 157f97cbd7
commit 623788ae25
14 changed files with 8148 additions and 4210 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/smartexpose',
version: '1.0.6',
description: 'a package to expose things to the internet'
version: '1.1.0',
description: 'A TypeScript package for temporarily exposing files through WebDAV-backed public URLs.'
}
+14 -13
View File
@@ -2,8 +2,8 @@ import * as plugins from './plugins.js';
/**
* An expose provider provides a standardized interface for exposing data
* Note: SmartExpose comes with some default implmentations of ExposeProvider
*
* Note: SmartExpose comes with some default implementations of ExposeProvider
*
* notably:
* - WebDavExposeProvider
*/
@@ -13,15 +13,15 @@ export abstract class ExposeProvider {
* Should take care of any housekeeping required to keep things in a healthy state.
*/
public abstract houseKeeping(): Promise<void>;
/**
* should return a url with info about how to access the file
* @param optionsArg
* @param optionsArg
*/
public abstract exposeFile(optionsArg: {
smartFile: plugins.smartfile.SmartFile,
deleteAfterMillis?: number,
privateUrl?: boolean, // wether the returned url should be private by design
smartFile: plugins.smartfile.SmartFile;
deleteAfterMillis?: number;
privateUrl?: boolean; // whether the returned url should be private by design
}): Promise<{
url: string;
id: string;
@@ -29,9 +29,9 @@ export abstract class ExposeProvider {
}>;
public abstract exposeFileArray(optionsArg: {
smartFiles: plugins.smartfile.SmartFile[],
deleteAfterMillis?: number,
privateUrl?: boolean,
smartFiles: plugins.smartfile.SmartFile[];
deleteAfterMillis?: number;
privateUrl?: boolean;
}): Promise<{
url: string;
id: string;
@@ -45,9 +45,10 @@ export abstract class ExposeProvider {
public abstract wipeAll(): Promise<{
id: string;
status: 'deleted' | 'failed' | 'notfound';
}[]>
}[]>;
public abstract deleteFileById(idArg: string): Promise<{
id: string;
status: 'deleted' | 'failed' | 'notfound';
}>
}
}>;
}
+25 -13
View File
@@ -9,7 +9,7 @@ export interface IWebdavExposeProviderOptions {
export class WebDavExposeProvider extends ExposeProvider {
public smartExposeRef: SmartExpose;
public webdavClient: plugins.smartwebdav.WebdavClient;
public webdavClient!: plugins.smartwebdav.WebdavClient;
public options: IWebdavExposeProviderOptions;
constructor(smartexposeRefArg: SmartExpose, optionsArg: IWebdavExposeProviderOptions) {
@@ -65,27 +65,37 @@ export class WebDavExposeProvider extends ExposeProvider {
)})`
);
const webdavFilePath = await this.getFilePathById(fileId);
const fileToUpload = await plugins.smartfile.SmartFile.fromBuffer(
webdavFilePath,
optionsArg.smartFile.contents
);
const fileToUpload = new plugins.smartfile.SmartFile({
path: webdavFilePath,
contentBuffer: optionsArg.smartFile.contents,
base: '/',
});
await this.webdavClient.uploadSmartFileArray([fileToUpload]);
console.log(`checking file presence: ${webdavFilePath}`);
const existsOnWebdav = await this.webdavClient.wdClient.exists(webdavFilePath);
if (!existsOnWebdav) {
throw new Error(`Uploaded file does not exist on WebDAV: ${webdavFilePath}`);
}
const publicUrl = plugins.smartpath.join(
this.smartExposeRef.options.exposedBaseUrl,
webdavFilePath
);
console.log(`cehcking for file at ${publicUrl}`);
const response = await plugins.smartrequest.getBinary(publicUrl);
plugins.smartexpect.expect(response.body).toEqual(fileToUpload.contents);
console.log(`checking for file at ${publicUrl}`);
const response = await plugins.smartrequest.SmartRequest.create()
.url(publicUrl)
.accept('binary')
.get();
const responseBuffer = Buffer.from(await response.arrayBuffer());
if (!responseBuffer.equals(fileToUpload.contents)) {
throw new Error(`Public URL did not return the uploaded file contents: ${publicUrl}`);
}
if (optionsArg.deleteAfterMillis) {
console.log(
`Scheduling deletion of file with id: ${fileId} in ${optionsArg.deleteAfterMillis}ms...`
);
plugins.smartdelay.delayFor(optionsArg.deleteAfterMillis).then(() => {
void plugins.smartdelay.delayFor(optionsArg.deleteAfterMillis).then(async () => {
console.log(`Deleting file with id: ${fileId}...`);
this.deleteFileById(fileId);
await this.deleteFileById(fileId);
console.log(`Deleted file with id: ${fileId}`);
});
}
@@ -96,8 +106,10 @@ export class WebDavExposeProvider extends ExposeProvider {
};
}
public async exposeFileArray(optionsArg: Parameters<ExposeProvider['exposeFileArray']>[0]) {
const returnArray = [];
public async exposeFileArray(
optionsArg: Parameters<ExposeProvider['exposeFileArray']>[0]
): ReturnType<ExposeProvider['exposeFileArray']> {
const returnArray: Awaited<ReturnType<ExposeProvider['exposeFile']>>[] = [];
for (const smartFile of optionsArg.smartFiles) {
returnArray.push(
await this.exposeFile({
@@ -125,7 +137,7 @@ export class WebDavExposeProvider extends ExposeProvider {
idArg: string
): Promise<{ id: string; status: 'deleted' | 'failed' | 'notfound' }> {
const filePath = await this.getFilePathById(idArg);
if (!this.webdavClient.wdClient.exists(filePath)) {
if (!(await this.webdavClient.wdClient.exists(filePath))) {
return {
id: idArg,
status: 'notfound',
+27 -12
View File
@@ -3,25 +3,32 @@ import { WebDavExposeProvider } from './classes.exposeprovider.webdav.js';
import * as plugins from './plugins.js';
export interface ISmartExposeOptions {
deleteAfterMillis?: number,
privateUrl?: boolean,
exposedBaseUrl: string,
deleteAfterMillis?: number;
privateUrl?: boolean;
exposedBaseUrl: string;
webdav?: {
webdavCredentials: plugins.smartwebdav.IWebdavClientOptions,
webdavSubPath: string,
}
webdavCredentials: plugins.smartwebdav.IWebdavClientOptions;
webdavSubPath: string;
};
}
export class SmartExpose {
// INSTANCE
public taskmanager: plugins.taskbuffer.TaskManager;
public provider: ExposeProvider;
public taskmanager!: plugins.taskbuffer.TaskManager;
public provider!: ExposeProvider;
public options: ISmartExposeOptions;
constructor(optionsArg: ISmartExposeOptions) {
this.options = optionsArg;
}
private getProvider(): ExposeProvider {
if (!this.provider) {
throw new Error('No expose provider has been configured.');
}
return this.provider;
}
public async start() {
this.taskmanager = new plugins.taskbuffer.TaskManager();
if (this.options.webdav) {
@@ -30,20 +37,28 @@ export class SmartExpose {
webdavSubPath: this.options.webdav.webdavSubPath,
});
}
await this.provider.start();
await this.getProvider().start();
this.taskmanager.start();
}
public async stop() {
await this.provider.stop();
await this.getProvider().stop();
this.taskmanager.stop();
}
public async exposeFile(optionsArg: Parameters<ExposeProvider['exposeFile']>[0]) {
return this.provider.exposeFile(optionsArg);
return this.getProvider().exposeFile({
...optionsArg,
deleteAfterMillis: optionsArg.deleteAfterMillis ?? this.options.deleteAfterMillis,
privateUrl: optionsArg.privateUrl ?? this.options.privateUrl,
});
}
public async exposeFileArray(optionsArg: Parameters<ExposeProvider['exposeFileArray']>[0]) {
return this.provider.exposeFileArray(optionsArg);
return this.getProvider().exposeFileArray({
...optionsArg,
deleteAfterMillis: optionsArg.deleteAfterMillis ?? this.options.deleteAfterMillis,
privateUrl: optionsArg.privateUrl ?? this.options.privateUrl,
});
}
}
+1 -5
View File
@@ -1,9 +1,7 @@
// @push.rocks scope
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartexpect from '@push.rocks/smartexpect';
import * as smartfile from '@push.rocks/smartfile';
import * as smartformat from '@push.rocks/smartformat';
import * as smartjson from '@push.rocks/smartjson';
import * as smartpath from '@push.rocks/smartpath';
import * as smartrequest from '@push.rocks/smartrequest';
import * as smartunique from '@push.rocks/smartunique';
@@ -12,13 +10,11 @@ import * as taskbuffer from '@push.rocks/taskbuffer';
export {
smartdelay,
smartexpect,
smartfile,
smartformat,
smartjson,
smartpath,
smartrequest,
smartunique,
smartwebdav,
taskbuffer,
}
};