@mantlejs/memory
In-memory Repository<T> implementation for Mantle JS. Backed by a Map, it supports all QueryParams operators with the same semantics as KnexRepository. Designed for unit testing services without a database — swap it in for any Repository<T>.
Installation
Section titled “Installation”npm install @mantlejs/memoryConcepts
Section titled “Concepts”MemoryRepository
Section titled “MemoryRepository”MemoryRepository<T> implements the Repository<T> interface from @mantlejs/mantle. It stores records in a Map keyed by ID, auto-generates UUIDs, and manages createdAt/updatedAt timestamps — the same behaviour your production KnexRepository provides.
Test helpers
Section titled “Test helpers”Three helpers make it easy to set up and inspect state in tests:
repo.seed(records)— pre-populate the store with known records (bypasses auto-ID ifidis provided)repo.clear()— empty the store between testsrepo.store— read-onlyMap<Id, T>for assertions on internal state
Both seed() and clear() return this for chaining.
Quick start
Section titled “Quick start”import { MemoryRepository } from "@mantlejs/memory";
interface User { id: string; name: string; email: string;}
// In your testconst repo = new MemoryRepository<User>();
// Seed known staterepo.seed([ { id: "1", name: "Alice", email: "alice@example.com" }, { id: "2", name: "Bob", email: "bob@example.com" },]);
const user = await repo.findById("1");// → { id: "1", name: "Alice", email: "alice@example.com", createdAt: "...", updatedAt: "..." }
const active = await repo.findAll({ where: { name: { $like: "A%" } } });// → [{ id: "1", name: "Alice", ... }]Injecting into a service
Section titled “Injecting into a service”import { mantle } from "@mantlejs/mantle";import { MemoryRepository } from "@mantlejs/memory";import { UserService } from "../src/user.service.js";
const app = mantle();const repo = new MemoryRepository<User>();app.use("users", new UserService(repo));
repo.seed([{ id: "1", name: "Alice", email: "alice@example.com" }]);
const users = await app.service("users").find();// → [{ id: "1", name: "Alice", ... }]new MemoryRepository<T>(options?)
Section titled “new MemoryRepository<T>(options?)”const repo = new MemoryRepository<User>({ idField: "id", // default — primary key field name autoId: true, // default — auto-generate UUID when id is absent on save timestamps: true, // default — manage createdAt / updatedAt});MemoryRepositoryOptions
Section titled “MemoryRepositoryOptions”| Field | Type | Default | Description |
|---|---|---|---|
idField |
string |
"id" |
Primary key field name |
autoId |
boolean |
true |
Auto-generate crypto.randomUUID() when id is absent |
timestamps |
boolean |
true |
Set createdAt on save, update updatedAt on patch/update |
Repository methods
Section titled “Repository methods”All methods mirror the Repository<T> interface and return Promise<T> or Promise<T[]>.
| Method | Description |
|---|---|
findAll(params?) |
Return all records matching QueryParams |
findById(id) |
Return a single record or null |
save(data) |
Insert a new record; throws Conflict if id already exists |
saveAll(data[]) |
Insert multiple records |
updateById(id, data) |
Full replace (all fields); throws NotFound if absent |
patchById(id, data) |
Partial update (merge); throws NotFound if absent |
deleteById(id) |
Remove and return the record; throws NotFound if absent |
count(params?) |
Count records matching QueryParams.where |
Test helpers
Section titled “Test helpers”| Member | Description |
|---|---|
seed(records) |
Pre-populate the store; returns this |
clear() |
Empty the store; returns this |
store |
Read-only Map<Id, T> — direct access for assertions |
QueryParams operators
Section titled “QueryParams operators”findAll() and count() accept a QueryParams object. All operators match the behaviour of KnexRepository:
| Operator | Example | SQL equivalent |
|---|---|---|
| Equality | { field: value } |
field = value |
| Null | { field: null } |
IS NULL |
$lt / $lte / $gt / $gte |
{ age: { $gt: 18 } } |
age > 18 |
$ne |
{ status: { $ne: "inactive" } } |
status != 'inactive' |
$in / $nin |
{ role: { $in: ["admin", "mod"] } } |
role IN (...) |
$or / $and |
{ $or: [{ a: 1 }, { b: 2 }] } |
(a = 1 OR b = 2) |
$like / $notlike |
{ name: { $like: "A%" } } |
name LIKE 'A%' |
$ilike |
{ name: { $ilike: "alice" } } |
Case-insensitive match |
$contains |
{ tags: { $contains: "blue" } } |
tags @> '["blue"]' (jsonb containment) |
Where-clause keys may be dot-paths into nested objects: { "metadata.owner.name": "alice" }.
$contains follows PostgreSQL jsonb @> semantics — array operand: field contains every
element; scalar operand: field contains that element; object operand: field is a recursive
superset. This adapter is the executable reference for the shared nested-path/$contains
conformance fixture exported from @mantlejs/mantle (NESTED_QUERY_RECORDS /
NESTED_QUERY_CASES).
import type { MemoryRepositoryOptions } from "@mantlejs/memory";| Type | Description |
|---|---|
MemoryRepositoryOptions |
Constructor options for MemoryRepository |
Development
Section titled “Development”npx nx build memory # compilenpx nx test memory # run testsnpx nx lint memory # lintPublishing
Section titled “Publishing”Build before publishing:
npx nx build memoryFirst publish (scoped packages require --access public):
cd packages/memorynpm publish --access publicSubsequent releases — bump version in packages/memory/package.json, then:
cd packages/memorynpm publish