Documenting my work - part 1
- Published
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.
Open source means each problem only has to be solved once.
— Naval (@naval) October 2, 2021
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.