Repositories
@di-framework/repo provides a coherent abstraction of repositories and storage adapters, allowing you to decouple your business logic from the underlying storage technology. It integrates seamlessly with @di-framework/core.
Key Concepts
Storage Adapter
A StorageAdapter is a minimal protocol that every storage backend must implement. It keeps the repository layer agnostic to whether you are using SQL, NoSQL, In-Memory, or an external API.
Repository
The Repository layer uses a StorageAdapter to perform data operations. It can add business logic, caching, validation, or event dispatching.
BaseRepository<E, ID>: The foundational repository class.EntityRepository<E, ID>: A standard entity-aware repository.SoftDeleteRepository<E, ID>: AddssoftDelete,restore, and filtering for active/deleted records.
Installation
Important: Scoped imports
Always import from the scoped package @di-framework/core/* to ensure a single global container instance. Mixing different import IDs (e.g., di-framework/* or relative paths to sources) can load a second copy of the library and create a second global container instance.
Correct:
Avoid:
Usage with @di-framework/core
Define a model with Spring/JPA-style @Model, @Id, and optional @GeneratedValue (the class is the type), then register a repository with @Repository.
IdKind covers multi-context identity (Primary, Public, External, Legacy, Tenant, Version). Multiple Primary fields express a composite primary key. @GeneratedValue stacks with @Id like JPA; GenerationType matches Jakarta (Auto, Identity, Sequence, Table, UUID), and UUID means UUIDv7 in this framework.
Plain interfaces still work if you do not need model metadata. Read metadata with getModelMetadata/getIdentities/getPrimaryId/isModel. Foreign keys to other models are not @Id kinds.
Injecting Repositories
Once registered, you can inject your repository into any other container-managed class:
Built-in In-Memory Repository
For prototyping, testing, or simple local state, use InMemoryRepository:
Custom Adapters
You can implement your own adapter to connect to any data source:
Database migrations
@di-framework/repo discovers and applies SQLite schema migrations from decorated classes, SQL files, and a JSON manifest. The three sources merge; they are not mutually exclusive.
Decorator
@Migration requires version and description. binding defaults to 'default'. Registration happens at import time. The runner constructs new target() with no arguments — constructor DI is not used. up is required at execution; execute/run are accepted aliases. down is stored on the definition but MigrationRunner.execute() never calls it. There is no rollback CLI.
SQL files and JSON manifests
SQL discovery (discoverSqlMigrations(dir)): *.sql except *.down.sql. Version and description come from header comments or the filename (001_create_users.sql, V2__add_index.sql). Split bodies with -- migrate:up/-- migrate:down.
Manifest discovery (discoverManifestMigrations) reads JSON only (not TOML):
entry.file is resolved relative to the manifest directory. Optional directory merges SQL files; the same (version, binding) from the directory is skipped.
Runner
History lives in _migrations keyed by (version, binding). A lock table _migrations_lock serializes runners (60s steal timeout). Each successful up runs inside db.transaction(...); a thrown up rolls back and does not write history.
Versions: integers compare numerically; strings with ., _, or - compare by dotted segments (1.2 < 1.10). Duplicate versions throw MigrationIntegrityError. A pending version lower than the latest applied throws MigrationOrderError.
Checksums: SQL/manifest use sha256(upSql); decorator checksums use Class.toString() (native vs minified Wasm output can disagree).
autoApply() applies pending migrations only when NODE_ENV is development or test, unless enabled: true. Nothing in application bootstrap calls it except tests. Actor SQLite databases reuse this runner on activation with binding equal to the actor type. See Actors.
CLI
Flag | Behavior |
|---|---|
| SQLite path. Default: |
| SQL directory (default |
| JSON manifest (default |
| Default |
| Repeatable; import |
| Execute only: max pending migrations |
| Execute only: plan, do not apply |
JSON data: status { binding, isUpToDate, applied[], pending[] }; execute { binding, applied[], pending[], dryRun, durationMs }. Binding mismatch exits 2. Connect or runner failures exit 1.
Limitations
downis parsed and stored; it is never executed by the runner or CLISQLite cannot fully roll back some DDL even inside a transaction
No baseline API, no repeatable migrations, no constructor-injected
DatabasewasmCloud does not scan
@Migrationclasses at build time; actor migrations run in-guest before activation
wasmCloud PostgreSQL values
@di-framework/repo/postgres encodes and decodes wasmcloud:postgres pg-value variants. The host binds parameters in binary, so tag values before query. A text payload in a uuid or boolean column is rejected. This module is not a MigrationRunner backend: Postgres schema history stays in the guest, and the runner's placeholders and transaction handle stay SQLite.
Without a tag, pgValue infers one: null and undefined become null, a UUID-shaped string becomes uuid, other strings become text, booleans become bool, integers in the int4 range become int4, other safe integers and bigint values become int8, other finite numbers become numeric, Date becomes timestamp-tz, Uint8Array becomes bytea, and any other value becomes jsonb. Pass a second argument ('text', 'uuid', 'bool', 'int4', 'int8', 'float8', 'numeric', 'timestamp-tz', 'bytea', or 'jsonb'), or use text, uuid, int8, or float8, when the column type differs. int8 keeps a bigint so values above 2^53 are not rounded.
pgScalar turns one result cell into a JSON value. readRows reads the column list and row stream from a query result and throws on an err variant. assertBatch throws when a batch result is an err variant. postgresError normalizes a binding failure into an Error whose message starts with PostgreSQL. isUniqueViolation is true for Postgres 23505 and for an error message that contains duplicate key.
The same functions are re-exported by the portable wasmCloud build, which is what the wasmcloud export condition on @di-framework/repo selects. Import @di-framework/repo/postgres from application code. Shipped in @di-framework/repo 6.0.4.