Make programming easier

Introduction
Documenting my work - part 1

Documenting my work - part 1

During my career as programmer I have created a lot of tools to tackle specific problems but I've never spent enough time to advertise or document them. Inspired by Naval's quote I've decided to change it.

Below there is the list of just few projects (more in the future) that I believe are truly valuable and might help you resolve problems that were already solved by me. Some of them are not documented and require deeper explanation but I need your help to decide which one are the most valuable so I can tackle them first.

@pallad/app-env

Provides information about the environment in which app is running. It distinguish most common environment conventions like: production, test, staging, ci, development

import * as env from '@pallad/app-env';

env.isProduction
env.isTest
env.isCi
// ...etc

Additionally exposes set of helpers that allows you to produce a value (in a type-safe manner) based on environment

env.forDevelopment('dev'); // 'dev' for development, undefined for others
env.forDevelopment('dev', 'others'); // 'dev' for development, 'others' for others

For more advanced setup you can use builder

const value = build()
    .forStaging({foo: 'foo'} as const)
    .forDevelopment({bar: 'bar'} as const)
    .get();
    
// typeof undefined | {foo: 'foo'} | {bar: 'bar'}

@pallad/assert-helper

I am a huge fan of monads. Especially simplified version from monet.js

@pallad/assert-helper is a tool that helps me creating assertion functions for monads. Useful in repositories.

import {createAssertion} from '@pallad/assert-helper';
import {ID} from '@pallad/id';
import {NotFoundError} from '@pallad/common-errors';

export class ApiTokenRepository {
    assert = createAssertion((id: ID) => {
        return this.findById(id)
    }, id => {
        return NotFoundError.entity('ApiToken', {id})
    });

    findById(id: ID): Promise<Maybe<ApiToken>> {

    }
}

const repository = new ApiTokenRepository();

await repository.assert('01ARZ3NDEKTSV4RRFFQ69G5FAV'); // throws NotFoundError if entity does not exist

await repository.assert.maybe('01ARZ3NDEKTSV4RRFFQ69G5FAV'); // returns Maybe<ApiToken>
await repository.assert.validation('01ARZ3NDEKTSV4RRFFQ69G5FAV'); // returns Validation<NotFoundError, ApiToken>

@pallad/builder

When working with builder pattern it is very common to call certain builder methods conditionally which in the end might be ugly and annoying.

class ApiTokenEntityBuilder {

    withReference(reference: string): this {
        // do smth
    }

    type(type: string): this {
        // do smth
    }

    value(value: number): this {
        // do smth
    }

    get(): ApiToken {
        // do smth
    }
}


let builder = new ApiTokenEntityBuilder()
    .type('foo')

// this is annoying...
if (someCondition) {
    builder = builder.withReference('bar')
}

return builder.get()

With @pallad/builder it's more concise

import {Builder} from '@pallad/builder';
class BetterApiTokenEntityBuilder extends Builder {

}

return new BetterApiTokenEntityBuilder()
    .type('foo')
     .runIf(someCondition, b => b.withReference('bar'))
     .get();

@pallad/cascade

Resolves problem of running certain logic in cascade. First time you hear that you might ask: this is handled on database level. That is true but not all resources are stored in database and not every database engine supports that (see mongo, DynamoDB, redis).

This library allows you to define action and register set of rules that are responsible for handling certain target types.

import {Action} from './Action';

class Image {
    constructor(readonly id: number) {
    }
}

class Article {
    constructor(readonly images: Set<Image>) {
    }
}

const deleteAction = new Action()
    .registerRule({
        supports: (target) => target instanceof Image,
        run(target: Image) {
            console.log(`Deleting image: ${target.id}`);
        }
    })
    .registerRule({
        supports: (target) => target instanceof Article,
        run(target: Article) {
            console.log(`Deleting article`);
            // return other targets to remove
            return Array.from(target.images);
        }
    });


deleteAction.run(
    new Article(
        new Set([
            new Image(1),
            new Image(2),
            new Image(3)
        ])
    )
)

// Deleting article
// Deleting image: 1
// Deleting image: 2
// Deleting image: 3

@pallad/common-errors

Just set of commonly used errors in my projects.
Supports better serialization through alpha-serializer

ApplicationError

Might be extended or used directly to indicate that error is not standard one but rather triggered by application logic.

new ApplicationError('Article that is archived cannot be published');
new ApplicationError('You cannot activate user that is already active');

InternalError
Mostly used as a wrapper for other errors that suppose to be hidden

new InternalError('Something went wrong on our side', errorObjectToCover);

NotFoundError
To indicate that something cannot be found like Entity, resource, anything.

new NotFoundError('Article not found', {id: 1});
// or
NotFoundError.entity('Article', {id: 1}); // "Article not found - id: 1"

RemoteServerError
When something wrong is happening with remote server. Mostly for communication errors.

TimeoutError
Name says everything :)

This is not the end

More information about other tools coming soon. Like handling project configuration, user defined conditions, defining references to entities and more!

Ping me on twitter and say which tool seems to be most useful for you so I can start documenting it first.

Author

wookieb

Fullstack developer with 10 years of experience. Passionated about programming, computers and how things work.

View Comments
Previous Post

Improved snapshot testing in Jest with alpha-serializer