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

@tussle/core

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tussle/core - npm Package Compare versions

Comparing version 0.0.5 to 0.1.0

lib/handlers/index.d.ts

32

lib/core.d.ts

@@ -1,30 +0,26 @@

import type { Observable } from 'rxjs';
import type { TussleIncomingRequest } from '@tussle/spec/interface/request';
import type { TussleStorageService } from '@tussle/spec/interface/storage';
import type { TusProtocolExtension } from '@tussle/spec/interface/tus';
import type { TussleIncomingRequest } from '@tussle/spec/interface/request';
import type { TussleStorageService, TussleStorageCreateFileResponse, TussleStorageCreateFileParams } from '@tussle/spec/interface/storage';
import { TussleCoreHooks } from './hooks';
import { Observable } from 'rxjs';
export interface TussleConfig {
maxSizeBytes?: number;
storage: TussleStorageService | Record<'default' | string, TussleStorageService>;
hooks?: Partial<TussleCoreHooks>;
storage: TussleStorageService | (<Req>(ctx: TussleIncomingRequest<Req>) => Promise<TussleStorageService>);
handlers?: Partial<RequestHandler>;
}
declare type IncomingRequestMethod = TussleIncomingRequest<unknown>['request']['method'];
declare type IncomingRequestHandler = <T>(core: Tussle, ctx: TussleIncomingRequest<T>) => Observable<TussleIncomingRequest<T>>;
declare type RequestHandler = Record<IncomingRequestMethod, IncomingRequestHandler>;
export declare class Tussle {
private readonly cfg;
readonly handlers: Partial<Record<IncomingRequestMethod, IncomingRequestHandler>>;
constructor(cfg: TussleConfig);
readonly handlers: Partial<RequestHandler>;
readonly extensions: Partial<Record<TusProtocolExtension, boolean>>;
readonly storage: Partial<Record<'default' | string, TussleStorageService>>;
readonly hooks: Partial<TussleCoreHooks>;
constructor(cfg: TussleConfig);
handle<T>(ctx: TussleIncomingRequest<T>): Observable<TussleIncomingRequest<T>>;
private chooseProtocolVersion;
private readonly processRequestHeaders;
private chooseProtocolVersion;
private readonly process;
private readonly postProcess;
setHandler(method: IncomingRequestMethod, handler: IncomingRequestHandler): void;
getStorage(name?: string): TussleStorageService;
hook<K extends keyof TussleCoreHooks, H extends TussleCoreHooks[K]>(which: K, ctx: TussleIncomingRequest<unknown>, params: Parameters<H>[2]): ReturnType<H> | Observable<Parameters<H>[2]>;
create(params: TussleStorageCreateFileParams, storeName?: string): Observable<TussleStorageCreateFileResponse>;
private readonly selectStorageService;
private readonly processRequest;
private readonly postProcessRequest;
readonly handle: <T>(source: Observable<TussleIncomingRequest<T>>) => Observable<TussleIncomingRequest<T>>;
getStorage<R>(ctx: TussleIncomingRequest<R>): Promise<TussleStorageService>;
}
export {};
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -9,6 +6,3 @@ exports.Tussle = void 0;

const operators_1 = require("rxjs/operators");
const create_1 = __importDefault(require("./handlers/create"));
const patch_1 = __importDefault(require("./handlers/patch"));
const head_1 = __importDefault(require("./handlers/head"));
const options_1 = __importDefault(require("./handlers/options"));
const handlers_1 = require("./handlers");
const supportedVersions = [

@@ -20,7 +14,5 @@ '1.0.0',

this.cfg = cfg;
this.handlers = {};
this.handlers = Object.assign(Object.assign({}, handlers_1.defaultHandlers), this.cfg.handlers);
this.extensions = {};
this.hooks = {};
this.processRequestHeaders = () => operators_1.map((ctx) => {
// Ensure meta property exists on incoming request context
this.processRequestHeaders = (0, rxjs_1.pipe)((0, operators_1.map)((ctx) => {
// Verify that the requested Tus protocol version is supported.

@@ -32,3 +24,3 @@ if (ctx.request.method !== 'OPTIONS') {

}
// Set the negotiated protocol version in the context metadata
// Set the negotiated protocol version in the context metadata.
ctx.meta.tusVersion = version;

@@ -38,6 +30,19 @@ }

return ctx;
});
this.process = () => operators_1.mergeMap((ctx) => {
// only if no response was already attached by preprocessing
if (!ctx.response) {
}));
this.selectStorageService = (0, rxjs_1.pipe)((0, operators_1.mergeMap)((ctx) => {
if (isStorageService(this.cfg.storage)) {
ctx.cfg.storage = this.cfg.storage;
return (0, rxjs_1.of)(ctx);
}
else {
return (0, rxjs_1.from)(this.cfg.storage(ctx)).pipe((0, operators_1.map)((storage) => {
ctx.cfg.storage = storage;
return ctx;
}));
}
}));
this.processRequest = (0, rxjs_1.pipe)((0, operators_1.mergeMap)((ctx) => {
// only if no response was already attached by preprocessing and a
// storage service has been linked to the incoming request.
if (!ctx.response && ctx.cfg.storage) {
const handler = this.handlers[ctx.request.method];

@@ -48,5 +53,5 @@ if (handler) {

}
return rxjs_1.of(ctx); // pass through
});
this.postProcess = () => operators_1.map((ctx) => {
return (0, rxjs_1.of)(ctx); // pass through
}));
this.postProcessRequest = (0, rxjs_1.pipe)((0, operators_1.map)((ctx) => {
// Add any remaining response headers

@@ -63,3 +68,3 @@ const extraHeaders = {};

// Include required Tus-Extension
const supportedExtensions = 'creation,termination,checksum'; // TODO -- generate this
const supportedExtensions = 'creation,checksum'; // TODO -- generate this
if (supportedExtensions) {

@@ -76,13 +81,5 @@ extraHeaders['Tus-Extension'] = supportedExtensions;

return ctx;
});
this.setHandler('POST', create_1.default);
this.setHandler('PATCH', patch_1.default);
this.setHandler('HEAD', head_1.default);
this.setHandler('OPTIONS', options_1.default);
this.storage = isStorageService(cfg.storage) ? { default: cfg.storage } : cfg.storage;
this.hooks = cfg.hooks || {};
}));
this.handle = (0, rxjs_1.pipe)(this.selectStorageService, this.processRequestHeaders, this.processRequest, this.postProcessRequest);
}
handle(ctx) {
return rxjs_1.of(ctx).pipe(this.processRequestHeaders(), this.process(), this.postProcess());
}
chooseProtocolVersion(ctx) {

@@ -95,23 +92,7 @@ const clientVersion = ctx.request.getHeader('tus-resumable');

}
setHandler(method, handler) {
this.handlers[method] = handler.bind(this);
getStorage(ctx) {
return isStorageService(this.cfg.storage) ?
Promise.resolve(this.cfg.storage) :
this.cfg.storage(ctx);
}
getStorage(name = 'default') {
const storage = this.storage[name];
if (!storage) {
throw new Error('Unable to find storage: ' + name);
}
return storage;
}
hook(which, ctx, params) {
const hook = this.hooks[which];
if (hook) {
return hook(this, ctx, params);
}
return rxjs_1.of(params);
}
create(params, storeName = 'default') {
const store = this.getStorage(storeName);
return store.createFile(params);
}
}

@@ -118,0 +99,0 @@ exports.Tussle = Tussle;

@@ -1,6 +0,6 @@

import type { Observable } from 'rxjs';
import type { TussleIncomingRequest } from '@tussle/spec/interface/request';
import type { Tussle } from '../core';
export default function handleCreate<T>(core: Tussle, ctx: TussleIncomingRequest<T>): Observable<TussleIncomingRequest<T>>;
declare const extractCreationHeaders: <T>(ctx: TussleIncomingRequest<T>) => {
import { Observable } from 'rxjs';
export default function handleCreate<R>(core: Tussle, ctx: Readonly<TussleIncomingRequest<R>>): Observable<TussleIncomingRequest<R>>;
export interface TussleCreationParams {
id: string;

@@ -12,4 +12,5 @@ path: string;

uploadConcat: string | null;
};
}
declare const extractCreationHeaders: <T>(ctx: TussleIncomingRequest<T>) => TussleCreationParams;
export declare type ExtractedCreateHeaders = ReturnType<typeof extractCreationHeaders>;
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const js_base64_1 = require("js-base64");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const js_base64_1 = require("js-base64");
function defaultPath(path, filename) {

@@ -12,15 +13,22 @@ return [

}
// If the before-create hook didn't set the location, for the file being
// created, then generate a default based on the request path and metadata
// filename.
const ensureFilePath = (originalPath) => (0, operators_1.map)(params => {
if (params.path === originalPath) {
params.path = defaultPath(params.path, params.uploadMetadata.filename);
}
return params;
});
function handleCreate(core, ctx) {
const params = extractCreationHeaders(ctx);
const originalPath = params.path;
const params$ = core.hook('before-create', ctx, params).pipe(operators_1.map((params) => {
// If the before-create hook didn't set the location,
// for the file being created, then generate a default
// based on the request path and metadata filename.
if (params.path === originalPath) {
params.path = defaultPath(params.path, params.uploadMetadata.filename);
}
return params;
}));
return params$.pipe(operators_1.switchMap((params) => core.create(params).pipe(operators_1.mergeMap((createdFile) => core.hook('after-create', ctx, createdFile)), operators_1.map((createdFile) => toResponse(ctx, createdFile)))));
const store = ctx.cfg.storage;
if (!store) {
return (0, rxjs_1.throwError)('no storage service selected');
}
else {
const params$ = ctx.source.hook('before-create', ctx, params).pipe(ensureFilePath(originalPath));
return params$.pipe((0, operators_1.switchMap)((params) => store.createFile(params)), (0, operators_1.switchMap)((createdFile) => ctx.source.hook('after-create', ctx, createdFile)), (0, operators_1.map)((createdFile) => toResponse(ctx, createdFile)));
}
}

@@ -38,3 +46,3 @@ exports.default = handleCreate;

.map((value) => value.split(' '))
.map(([key, value]) => [key, value ? js_base64_1.decode(value) : value])
.map(([key, value]) => [key, value ? (0, js_base64_1.decode)(value) : value])
.reduce((acc, [key, value]) => {

@@ -46,7 +54,2 @@ acc[key] = value;

const uploadConcat = header('upload-concat') || null;
// provide a default file location (this can be altered during
// the 'before-create' hook, and then possibly altered further
// by the current storage component.
// const location = [path, encodeURIComponent(uploadMetadata.filename)].join('/');
// console.log('creation location is', location);
return {

@@ -66,3 +69,3 @@ id,

status: 201,
headers: Object.assign({ 'Location': createdFile.location, 'Tussle-Storage': 'b2' }, (_a = ctx.response) === null || _a === void 0 ? void 0 : _a.headers),
headers: Object.assign({ 'Location': createdFile.location }, (_a = ctx.response) === null || _a === void 0 ? void 0 : _a.headers),
};

@@ -72,3 +75,3 @@ }

ctx.response = {
status: 400,
status: 400, // TODO - check this
};

@@ -75,0 +78,0 @@ }

@@ -1,5 +0,5 @@

import type { Observable } from "rxjs";
import { Observable } from "rxjs";
import type { TussleIncomingRequest } from '@tussle/spec/interface/request';
import type { Tussle } from '../core';
export default function handleHead<T>(core: Tussle, ctx: TussleIncomingRequest<T>): Observable<TussleIncomingRequest<T>>;
export default function handleHead<Req>(core: Tussle, ctx: TussleIncomingRequest<Req>): Observable<TussleIncomingRequest<Req>>;
declare const extractParamsFromHeaders: <T>(ctx: TussleIncomingRequest<T>) => {

@@ -6,0 +6,0 @@ location: string;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
function handleHead(core, ctx) {
const store = core.getStorage('default');
const params = extractParamsFromHeaders(ctx);
const params$ = core.hook('before-head', ctx, params);
return params$.pipe(operators_1.switchMap((params) => store.getFileInfo(params).pipe(operators_1.map((fileInfo) => toResponse(ctx, fileInfo)))));
const store = ctx.cfg.storage;
if (!store) {
return (0, rxjs_1.throwError)('no storage service selected');
}
else {
const params$ = ctx.source.hook('before-head', ctx, params);
return params$.pipe((0, operators_1.switchMap)((params) => store.getFileInfo(params)), (0, operators_1.map)(fileInfo => toResponse(ctx, fileInfo)));
}
}

@@ -18,8 +24,13 @@ exports.default = handleHead;

const toResponse = (ctx, fileInfo) => {
if (fileInfo.info) {
const { info } = fileInfo;
if (info) {
const headers = {
'Upload-Offset': (info.currentOffset || 0).toString(),
};
if (typeof info.uploadLength === 'number') {
headers['Upload-Length'] = info.uploadLength.toString();
}
ctx.response = {
status: 200,
headers: {
'Upload-Offset': fileInfo.info.currentOffset.toString(),
},
headers,
};

@@ -29,3 +40,3 @@ }

ctx.response = {
status: 410,
status: 410, // GONE
};

@@ -32,0 +43,0 @@ }

import type { Observable } from 'rxjs';
import type { TussleIncomingRequest } from '@tussle/spec/interface/request';
import type { Tussle } from '../core';
export default function handleOptions<T>(core: Tussle, ctx: TussleIncomingRequest<T>): Observable<TussleIncomingRequest<T>>;
declare const defaultResponse: {
status: number;
headers: {
'access-control-allow-headers': string;
'access-control-expose-headers': string;
};
};
export default function handleOptions<Req>(core: Tussle, ctx: TussleIncomingRequest<Req>): Observable<TussleIncomingRequest<Req>>;
export declare type OptionsDefaultResponse = typeof defaultResponse;
export {};

@@ -14,5 +14,5 @@ "use strict";

function handleOptions(core, ctx) {
const response$ = core.hook('before-options', ctx, Object.assign({}, defaultResponse)).pipe(operators_1.map((response) => (Object.assign(Object.assign({}, ctx), { response }))));
const response$ = ctx.source.hook('before-options', ctx, Object.assign({}, defaultResponse)).pipe((0, operators_1.map)((response) => (Object.assign(Object.assign({}, ctx), { response }))));
return response$;
}
exports.default = handleOptions;
/// <reference types="node" />
import type { Observable } from 'rxjs';
import { Observable } from 'rxjs';
import type { Tussle } from '../core';
import type { TussleIncomingRequest } from '@tussle/spec/interface/request';
export default function handlePatch<T>(core: Tussle, ctx: TussleIncomingRequest<T>): Observable<TussleIncomingRequest<T>>;
declare const extractPatchHeaders: (ctx: TussleIncomingRequest<unknown>) => {
declare const extractPatchHeaders: <Req>(ctx: TussleIncomingRequest<Req>) => {
contentType: string;
getReadable: () => import("stream").Readable;
getReadable: () => import("stream").Readable | ReadableStream<Uint8Array>;
length: number;
location: string;
offset: number;
request: TussleIncomingRequest<unknown>;
request: TussleIncomingRequest<Req>;
};
export declare type ExtractedPatchHeaders = ReturnType<typeof extractPatchHeaders>;
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const rxjs_1 = require("rxjs");
const rxjs_2 = require("rxjs");
const operators_1 = require("rxjs/operators");
function handlePatch(core, ctx) {
const store = core.getStorage('default');
const params = extractPatchHeaders(ctx);
const store = ctx.cfg.storage;
if (!store) {
return (0, rxjs_1.throwError)('no storage service selected');
}
// PATCH requests MUST use Content-Type: application/offset+octet-stream

@@ -14,6 +18,6 @@ if (params.contentType !== 'application/offset+octet-stream') {

};
return rxjs_1.of(ctx);
return (0, rxjs_2.of)(ctx);
}
const params$ = core.hook('before-patch', ctx, params);
return params$.pipe(operators_1.switchMap((params) => store.patchFile(params).pipe(operators_1.mergeMap((patchedFile) => callOptionalHooks(core, ctx, patchedFile)), operators_1.map((patchedFile) => toResponse(ctx, patchedFile)))));
const params$ = ctx.source.hook('before-patch', ctx, params);
return params$.pipe((0, operators_1.switchMap)((params) => store.patchFile(params)), (0, operators_1.switchMap)((patchedFile) => callOptionalHooks(ctx, patchedFile)), (0, operators_1.map)((patchedFile) => toResponse(ctx, patchedFile)));
}

@@ -24,8 +28,8 @@ exports.default = handlePatch;

}
const callOptionalHooks = (core, ctx, patchedFile) => {
const callOptionalHooks = (ctx, patchedFile) => {
ctx.meta.storage = patchedFile.details;
if (isComplete(patchedFile)) {
return core.hook('after-complete', ctx, patchedFile);
return ctx.source.hook('after-complete', ctx, patchedFile);
}
return rxjs_1.of(patchedFile);
return (0, rxjs_2.of)(patchedFile);
};

@@ -32,0 +36,0 @@ const extractPatchHeaders = (ctx) => {

@@ -7,2 +7,3 @@ import { Tussle, TussleConfig } from './core';

import { TTLCache } from './util/ttlcache';
export { Tussle, TusProtocolExtension, TussleConfig, TussleIncomingRequest, TussleOutgoingRequest, TussleOutgoingResponse, TussleRequestService, TussleStateNamespace, TussleStorageService, TTLCache, };
import { TussleBaseMiddleware } from './middleware';
export { Tussle, TusProtocolExtension, TussleConfig, TussleIncomingRequest, TussleOutgoingRequest, TussleOutgoingResponse, TussleRequestService, TussleStateNamespace, TussleStorageService, TussleBaseMiddleware, TTLCache, };
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TTLCache = exports.TussleStateNamespace = exports.Tussle = void 0;
exports.TTLCache = exports.TussleBaseMiddleware = exports.TussleStateNamespace = exports.Tussle = void 0;
const core_1 = require("./core");

@@ -10,1 +10,3 @@ Object.defineProperty(exports, "Tussle", { enumerable: true, get: function () { return core_1.Tussle; } });

Object.defineProperty(exports, "TTLCache", { enumerable: true, get: function () { return ttlcache_1.TTLCache; } });
const middleware_1 = require("./middleware");
Object.defineProperty(exports, "TussleBaseMiddleware", { enumerable: true, get: function () { return middleware_1.TussleBaseMiddleware; } });

@@ -16,7 +16,2 @@ "use strict";

const item = this.state.getItem(key);
item.then((item) => {
if (!item) {
console.error('MISSING', key);
}
});
return item;

@@ -23,0 +18,0 @@ }

@@ -13,2 +13,3 @@ export declare type TTLCacheType<T> = T extends TTLCache<infer U> ? U : never;

constructor(ttl?: number, garbageCollectionInterval?: number, cache?: Record<string, TTLCacheItem<T>>, now?: () => number);
onRelease(_key: string, _data: T): void;
getOrCreate(key: string, create: () => Promise<T>): Promise<T>;

@@ -18,5 +19,8 @@ getItem(key: string): T | null;

setItem(key: string, data: null): null;
removeItem(key: string): T | null;
key(nth: number): string | null;
private garbageCollect;
private release;
private asyncGarbageCollect;
}
export {};

@@ -25,2 +25,3 @@ "use strict";

}
onRelease(_key, _data) { }
getOrCreate(key, create) {

@@ -58,6 +59,16 @@ return __awaiter(this, void 0, void 0, function* () {

else {
delete this.cache[key];
this.release(key);
}
return data;
}
removeItem(key) {
const hit = this.cache[key];
if (hit) {
delete this.cache[key];
}
return hit.data;
}
key(nth) {
return Object.keys(this.cache)[nth] || null;
}
garbageCollect() {

@@ -68,6 +79,15 @@ const now = this.now();

if (isExpired(now, cache[key].atime, ttl)) {
delete cache[key];
this.release(key);
}
}
}
release(key) {
const item = this.cache[key];
if (item) {
if (this.onRelease) {
this.onRelease(key, item.data);
}
delete this.cache[key];
}
}
asyncGarbageCollect(now, force = false) {

@@ -74,0 +94,0 @@ if (force || isExpired(now, this.lastGarbageCollection, this.garbageCollectionInterval)) {

{
"name": "@tussle/core",
"version": "0.0.5",
"version": "0.1.0",
"description": "Tussle tus daemon core module",

@@ -19,7 +19,7 @@ "main": "lib/index.js",

"dependencies": {
"js-base64": "^3.6.0",
"rxjs": "^6.6.3"
"js-base64": "^3.7.2",
"rxjs": "^6.6.7"
},
"devDependencies": {
"jest": "^26.6.3",
"jest": "^27.5.1",
"npm-run-all": "^4.1.5",

@@ -35,3 +35,3 @@ "rimraf": "^3.0.2"

},
"gitHead": "5c214317d3148878710064b8a175960d350368a2"
"gitHead": "3b2ef13249ca16b6c4db1f3c761ee3eb92053445"
}
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