
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
sweet-decorators
Advanced tools
Zero dependency collection of common used typescript & javascript patterns provided in convenient format of decorators.
0
dependencies⚠️ Please, consider to read Microsoft's article about Decorators Composition ⚠️
npm
)npm i --save sweet-decorators
yarn
)yarn add sweet-decorators
@Mixin
Mixin is a pattern of assigning new methods
and static properties to an existing class. It's called composition
.
This decorator just makes it easy, by using by abstracting applyMixin
function described in typescript docs
Example
import { Mixin } from "sweet-decorators";
class Swimmable {
swim() {
console.log("🏊♂️");
}
}
class Walkable {
walk() {
console.log("🚶♂️");
}
}
class Flyable {
fly() {
console.log("🐦");
}
}
@Mixin(Swimmable, Walkable)
class Human {}
interface Human extends Swimmable, Walkable {}
@Mixin(Walkable, Flyable)
class Bird {}
interface Bird extends Walkable, Flyable {}
const human = new Human();
human.swim();
// => 🏊♂️
const bird = new Bird();
bird.fly();
// => 🐦
@Assign
and @Assign.<Key>
This decorator is used in pair with readMeta
method. Here is the example:
import { Assign, readMeta } from "sweet-decorators";
// You can use multiple syntaxes of the decorator
@Assign({ BASE_BEHAVIOR: "passive" }) // If u pass object, his props will be merged with current meta
@Assign("isTest", process.env.NODE_ENV === "test") // You can set prop directly
@Assign.ErrorClass(Error) // Or you can use @Assign.<key>(<value>) because its more beautiful alt to @Assign('<key>', <value>)
class Human {
// All these decorators also applies to methods
@Assign({ IS_ACTION: true })
@Assign.ActionType("MOVEMENT")
@Assign(Symbol("ENTITY_TOKEN"), "X_HUMAN_ENTITY")
@Assign({ IS_ACTION: false }) // will be overridden
walk() {}
}
class Child extends Human {
@Assign({ isClumsy: true })
walk() {}
}
@Assign({ isFavorite: true })
class FavoriteChild extends Human {
}
const human = new Human();
console.log(readMeta(human));
// => { BASE_BEHAVIOR: "passive", isTest: false, ErrorClass: Error }
console.log(readMeta(human.walk));
// => { IS_ACTION: true, ActionType: "MOVEMENT", Symbol(ENTITY_TOKEN): "X_HUMAN_ENTITY" }
const child = new Child();
console.log(readMeta(child));
// => { BASE_BEHAVIOR: "passive", isTest: false, ErrorClass: Error }
console.log(readMeta(child.walk));
// => { isClumsy: true }
const fav = new FavoriteChild();
console.log(readMeta(child));
// => { isFavorite: true }
console.log(readMeta(child.walk));
// => { IS_ACTION: true, ActionType: "MOVEMENT", Symbol(ENTITY_TOKEN): "X_HUMAN_ENTITY" }
@MapErrors
and @MapErrorsAsync
This decorator in used to intercept errors
to catch and display more effectively one layer up
import { MapErrorsAsync } from "sweet-decorators";
import { FetchError } from "node-fetch";
import { PaymentError } from "../errors";
const fetchErrorMapper = (error: Error) => {
if (error instanceof FetchError) {
return new PaymentError(
"Cannot connect to remote endpoint",
PaymentError.Code.NetworkError
);
}
return;
};
const refErrorMapper = (error: Error) => {
if (error instanceof ReferenceError) {
return new PaymentError(
"Internal reference error",
PaymentError.Code.InternalError
);
}
return;
};
class PaymentSystem {
// You can use multiple mappers for handle different types of errors separately
@MapErrorsAsync(fetchErrorMapper, refErrorMapper)
async finishPayment(id: string) {
/* ... */
}
}
// In some other file
const ps = new PaymentSystem();
app.post("/finish-3ds", async (req, res) => {
try {
const response = await ps.finishPayment(req.query.id);
/* ... */
} catch (error) {
console.log(error);
// => PaymentError(NETWORK_ERROR): Cannot connect to remote endpoint
}
});
@MapErrors
tips and best practicesError
class)undefined
, to pass control to next mapperDIContainer
This is simple implementation of dependency injection
pattern in typescript without assigning any metadata
.
import { DIContainer } from "sweet-decorators";
const container = new DIContainer();
const SECRET_TOKEN = Symbol("SECRET_TOKEN");
container.provide(SECRET_TOKEN, process.env.SECRET_TOKEN);
class ExternalAPIClient {
@container.Inject(SECRET_TOKEN)
private readonly token!: string;
public call(method: string, params: object) {
params = { token: this.token, ...params };
/* foreign api call logic */
}
public get secretToken() {
return this.token;
}
}
const client = new ExternalAPIClient();
console.log(client.secretToken === process.env.SECRET_TOKEN);
// => true
import { DIContainer } from "sweet-decorators";
const container = new DIContainer();
// You must give a name(token) to provided class
@container.Provide('CONFIG_SERVICE');
class ConfigService {
// BAD BAD BAD. Constructor of a provider can't have params. Because his initialization is controlled by container
constructor(public path: sting) {}
get(propertyPath: string): any { /* ... */ }
}
class Database {
@container.Inject('CONFIG_SERVICE')
private readonly config: ConfigService;
public connection = this.config.get('db.connectionString')
/* ... logic ... */
}
injectAsync
methodimport { DIContainer } from "sweet-decorators";
const container = new DIContainer();
setTimeout(
() =>
container.provide(
"DB_SERVICE",
new DB({
/* ... */
})
),
5000
);
async function main() {
const start = Date.now();
const db = await container.injectAsync("DB_SERVICE");
const time = Date.now() - start;
console.log(time);
/* logic */
}
main();
// => 5005
Common tips applicable to any realization of DI in TS.
classes
, inject interfaces
Symbols
as keysundefined
)Tips for using my realization of DI.
!
, to indicate TS that property will not be initialized in constructorasynchronously by getter
injectAsync
methodinjectAsync
method is waiting for, is not provided, it will hang execution of your code@Before
, @After
, @Around
, @BeforeAsync
, @AfterAsync
, @AroundAsync
This decorators used to call methods around other methods. Its can help make code more concise by moving similar aspects out of the method.
import { Before, After, Around } from "sweet-decorators";
function before(...args: any[]) {
console.log("Before", { args });
}
function after(result: any[], ...args: any[]) {
console.log("After", { result, args });
}
function around(fn: Function, ...args: any[]) {
console.log("Before (Around)");
fn(...args);
console.log("After (Around)");
return 43;
}
class Test {
// Order of decorators matters
@Before(before)
@After(after)
@Around(around)
example(..._args: any[]) {
console.log("Call Example");
return 42;
}
}
const result = new Test().example(1488);
console.log(result === 42);
/*
Before { args: [ 1488 ] }
Before (Around)
Call Example
After (Around)
After { result: 43, args: [ 1488 ] } // If you swap `@After` and `@Around` in function declaration, result will be 42
false
// False, because function `around` changed it
*/
import { AroundAsync, AfterAsync, BeforeAsync } from "sweet-decorators";
function checkAuthorization(this: UserService) {
if (!this.currentSession) {
throw new UserError("Unauthorized");
}
}
async function updateLastLogin(this: UserService, result: any, ...args: any[]) {
if (result.success) {
await this.db.query(/* ... */);
}
}
async function handleErrors(this: UserService, fn: Function, ...args: any[]) {
try {
return await fn(...args);
} catch (error) {
if (error instanceof DBError) {
throw new UserError("Database got wrong");
}
throw error;
}
}
class UserService {
@AroundAsync(handleErrors) // First decorator wraps all next
// If you put it ^ last. It will wrap only the function content.
// That's how decorators work
// https://www.typescriptlang.org/docs/handbook/decorators.html#decorator-composition
@AfterAsync(updateLastLogin)
@BeforeAsync(checkAuthorization)
async getPersonalData() {
/* ... */
}
/* ... */
}
Section may contain Cap's notices.
@Before
@Around
@After
@Around
, except your project is good at using either monad
@Memoize
& @MemoizeAsync
This decorator is used to easily create memoized functions.
By default, memo storage uses storage, that caches method's result only by 1st parameter
. If you want to change this behavior you can create your own storage by implementing IMemoStorage
interface.
import { Memoize } from "sweet-decorators";
import { promisify } from "util";
const sleep = promisify(setTimeout);
class Example {
@Memoize()
date() {
return new Date().toString();
}
}
const e = new Example();
async function main() {
const now = e.date();
await sleep(10);
console.log(
e.date() === now, // 10 ms passed, but result remembered
now === new Date().toString()
);
}
main();
// => true, false
@Memoize
& @MemoizeAsync
uses hooks @Around
& @Around
async under the hood. Please consider this while bundling your code.FAQs
Library of utility typescript decorators
The npm package sweet-decorators receives a total of 18 weekly downloads. As such, sweet-decorators popularity was classified as not popular.
We found that sweet-decorators demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.