
Product
Introducing Webhook Events for Alert Changes
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.
@decaf-ts/core
Advanced tools
Decaf Core provides the foundational building blocks for the Decaf TypeScript ecosystem: strongly-typed models, repository pattern, pluggable persistence adapters, a composable query DSL, and pagination/observer utilities. With decorators and an injectable registry, it wires models to repositories and adapters so you can build data access that is framework-agnostic yet fully typed.
Documentation here, Test results here and Coverage here
Minimal size: 13.8 KB kb gzipped
The Decaf Core package provides a cohesive set of primitives for building strongly-typed data-access layers in TypeScript. It centers around:
Below is an overview of the main modules and their public APIs exposed by core.
Design intent
Below are practical, focused examples for the public APIs exposed by the Core package. Each example includes a short description and valid TypeScript code. Examples are inspired by and aligned with the unit tests under core/tests.
Prerequisites used across examples:
import { Model, model } from "@decaf-ts/decorator-validation";
import type { ModelArg } from "@decaf-ts/decorator-validation";
import {
Adapter,
OrderDirection,
Paginator,
Repository,
repository,
uses,
pk,
column,
table,
} from "@decaf-ts/core";
import { RamAdapter, RamRepository } from "@decaf-ts/core/ram";
@table("tst_user")
@model()
class User extends Model {
@pk() id!: string;
@column("tst_name") name!: string;
@column("tst_nif") nif!: string;
constructor(arg?: ModelArg<User>) { super(arg); }
}
import { NotFoundError } from "@decaf-ts/db-decorators";
async function crudExample() {
const adapter = new RamAdapter();
const repo: RamRepository<User> = new Repository(adapter, User);
// CREATE
const created = await repo.create(
new User({ id: Date.now().toString(), name: "Alice", nif: "123456789" })
);
// READ
const read = await repo.read(created.id);
console.log(read.equals(created)); // true (same data, different instance)
// UPDATE
const updated = await repo.update(Object.assign(read, {name: "Alice 2" }));
// DELETE
const deleted = await repo.delete(created.id);
console.log(deleted.equals(updated)); // true
}
@model()
class Managed extends Model { constructor(arg?: ModelArg<Managed>) { super(arg); } }
@repository(Managed)
@uses("ram")
class ManagedRepository extends Repository<Managed> {
// Concrete adapter-backed methods would be provided by adapter implementation
// For quick test or demo, use a RamAdapter
}
async function adapterRegistryExample() {
const adapter = new RamAdapter();
Adapter.setCurrent("ram"); // set current flavour
console.log(Adapter.current === Adapter.get("ram")); // true
// Models managed by current or specific adapter flavour
const managed = Adapter.models("ram");
console.log(Array.isArray(managed));
}
async function queryExample() {
const adapter = new RamAdapter();
const repo: RamRepository<User> = new Repository(adapter, User);
// Seed data
await repo.createAll(
Array.from({ length: 5 }).map((_, i) =>
new User({ id: (i + 1).toString(), name: `u${i + 1}`, nif: "123456789" })
)
);
const results = await repo
.select()
.orderBy(["id", OrderDirection.ASC])
.execute();
console.log(results.map((u) => u.id)); // ["1","2","3","4","5"]
}
async function paginationExample() {
const adapter = new RamAdapter();
const repo: RamRepository<User> = new Repository(adapter, User);
// Seed data
const size = 25;
await repo.createAll(
Array.from({ length: size }).map((_, i) =>
new User({ id: (i + 1).toString(), name: `u${i + 1}`, nif: "123456789" })
)
);
const paginator: Paginator<User> = await repo
.select()
.orderBy(["id", OrderDirection.DSC])
.paginate(10);
const page1 = await paginator.page(); // first page by default
const page2 = await paginator.next();
const page3 = await paginator.next();
console.log(page1.length, page2.length, page3.length); // 10, 10, 5
}
import { Condition } from "@decaf-ts/core";
async function conditionExample() {
const adapter = new RamAdapter();
const repo: RamRepository<User> = new Repository(adapter, User);
await repo.createAll([
new User({ id: "1", name: "Alice", nif: "111111111" }),
new User({ id: "2", name: "Bob", nif: "222222222" }),
]);
const cond = Condition.attr<User>("name")
.eq("Alice")
.build();
const results = await repo.select().where(cond).execute();
console.log(results.length); // 1
}
async function mappingExample() {
const adapter = new RamAdapter();
const repo: RamRepository<User> = new Repository(adapter, User);
const toCreate = new User({ id: "abc", name: "Test", nif: "123456789" });
// prepare: model -> record
const pk = "id"; // infer with findPrimaryKey(toCreate).id if available
const { record, id } = adapter.prepare(toCreate, pk);
console.log(id === toCreate.id); // true
// revert: record -> model instance
const model = adapter.revert(record, User, pk, id) as User;
console.log(model instanceof User); // true
}
import { Injectables } from "@decaf-ts/injectable-decorators";
import { InjectablesRegistry } from "@decaf-ts/core";
async function injectablesExample() {
// Register current adapter so repositories can be created
new RamAdapter();
Adapter.setCurrent("ram");
// Resolve by constructor
const userRepo = Injectables.get<Repository<User>>(User);
if (userRepo) {
const u = await userRepo.create(
new User({ id: "1", name: "A", nif: "123456789" })
);
console.log(!!u);
}
}
If you have bug reports, questions or suggestions please create a new issue.
I am grateful for any contributions made to this project. Please read this to get started.
The first and easiest way you can support it is by Contributing. Even just finding a typo in the documentation is important.
Financial support is always welcome and helps keep both me and the project alive and healthy.
So if you can, if this project in any way. either by learning something or simply by helping you save precious time, please consider donating.
This project is released under the Mozilla Public License 2.0.
By developers, for developers...
FAQs
Core persistence module for the decaf framework
The npm package @decaf-ts/core receives a total of 237 weekly downloads. As such, @decaf-ts/core popularity was classified as not popular.
We found that @decaf-ts/core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers 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.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.

Product
Socket now scans OpenVSX extensions, giving teams early detection of risky behaviors, hidden capabilities, and supply chain threats in developer tools.