Releases: arkstack-hq/arkormx
2.0.4
This release introduces configurable inferred model table casing, with a new snake_case default.
Highlights
- Added user-configurable table name casing for inferred model tables.
- Changed the default inferred table casing from camelCase to snake_case.
- Limited case options to modes supported by the string helper pipeline for consistent behavior.
Added
- New runtime config option under naming:
- modelTableCase
Supported values:
- snake
- camel
- kebab
- studly
Changed
- Model.getTable now resolves inferred table names using the configured naming.modelTableCase.
- If no explicit table is set on a model, inferred names now default to snake plural form.
Compatibility Notes
- Models with an explicit table value are unchanged.
- Existing projects that relied on inferred camelCase table names should set naming.modelTableCase to camel to preserve previous behavior.
Example Config
export default defineConfig({
naming: {
modelTableCase: 'camel',
},
})Validation
- Added regression coverage for:
- snake_case default inference
- camel override via runtime config
- Targeted runtime configuration test suite passes.
Full Changelog: 2.0.2...2.0.4
2.0.2
This patch release adds first-class direct raw query support through the DB facade, enabling SQL execution without creating a table-scoped query builder first.
Highlights
- Added DB.raw for direct raw SQL queries from the DB facade
- Added transaction-scoped raw query support through db.raw inside DB.transaction
- Added adapter-level rawQuery contract for consistent raw execution across adapters
- Added Kysely adapter rawQuery implementation with structured error context and inspection metadata
- Added regression tests for success, transaction routing, and unsupported-adapter behavior
Added
- Static DB.raw(sql, bindings?) returning an ArkormCollection
- Instance db.raw(sql, bindings?) for transaction-scoped use
- RawQuerySpec in adapter types to standardize raw SQL + bindings payload
- Optional adapter rawQuery capability hook in the DatabaseAdapter interface
Behavior
- Raw queries now work directly via DB.raw once an adapter is configured
- Raw queries inside DB.transaction correctly execute on the transaction adapter
- If no adapter is configured, DB.raw throws an adapter-not-configured error
- If the active adapter does not implement rawQuery, DB.raw throws an unsupported feature error
Adapter Support
- KyselyDatabaseAdapter now supports rawQuery execution and returns row arrays
- Raw query failures in Kysely are wrapped in QueryExecutionException with adapter inspection details
Tests
- Added base DB facade regression coverage for:
- Direct DB.raw success path
- Transaction-scoped db.raw routing
- Unsupported adapter feature rejection
Notes
- This is a backward-compatible patch release focused on raw-query ergonomics and adapter parity.
- No breaking changes introduced in v2.0.2.
Full Changelog: 2.0.0...2.0.2
2.0.0
Arkorm 2.0.0 marks the completion of the adapter-first migration (Phase 1 - Phase 7).
The runtime is now fully adapter-first by default, with Prisma support retained as an explicit compatibility path for the 2.x line.
Highlights
- Completed the full migration from delegate-shaped internals to adapter-first execution.
- Established Arkorm-owned query and relation planning as the primary runtime model.
- Added and matured SQL-backed execution through the Kysely adapter.
- Kept Prisma available as a compatibility adapter during 2.x, with clear capability boundaries.
- Finalized docs, tests, and merge-readiness criteria for the 2.0 baseline.
What Is New In 2.0.0
Adapter-first architecture is now baseline
- Model and query execution now run through Arkorm adapter contracts.
- Core internals no longer rely on Prisma delegate semantics in normal runtime paths.
- Adapter capabilities define supported advanced features explicitly.
Query and typing surface modernization
- Core typing now uses neutral query-schema contracts.
- Delegate-shaped helper names remain only as compatibility aliases in 2.x.
- Arkorm-owned specs now drive relation filtering, aggregation, and eager loading behavior.
Relation execution and eager loading
- Set-based eager loading is fully integrated for core relation types.
- Constrained eager loading is compiled into Arkorm relation load plans.
- Model.load and QueryBuilder.with share the same adapter-first relation planning path.
Kysely/Postgres execution maturity
- Core CRUD, pagination, relation filters, and relation aggregates are SQL-backed on Kysely.
- Postgres conflict-handling and RETURNING-aware mutation paths are supported.
- Query-shape and parity regressions were expanded to protect adapter-first behavior.
Prisma compatibility in 2.x
- Prisma remains supported as a compatibility adapter.
- Unsupported compatibility edges now fail explicitly instead of silently degrading.
- Delegate-first APIs are deprecated compatibility surfaces, with removal targeted for 3.0.
Migration Notes
If you are upgrading from 1.x
- Move runtime bootstrap to adapter-first configuration.
- Prefer one top-level adapter for application runtime.
- Keep Prisma only where compatibility behavior is intentionally required.
- Treat deprecated delegate-first APIs as transitional and avoid them in new code.
If you are already on 2.0.0-next.x
- 2.0.0 consolidates and stabilizes the completed Phase 1 - Phase 7 migration work.
- Adapter-first behavior should now be considered the default contract for new development.
Compatibility and Deprecations
- Prisma compatibility adapter remains supported through the 2.x line.
- Deprecated delegate-first APIs and delegate-shaped aliases remain compatibility-only in 2.x.
- Removal target for those deprecated compatibility surfaces is Arkorm 3.0.
Validation and Quality
- Regression and parity coverage now verify adapter-first behavior across Prisma compatibility and Kysely paths.
- Relation execution, eager loading, relation filters, and aggregates are covered through focused integration scenarios.
- Full-suite validation has been maintained as part of the migration closeout.
Thank You
Thank you to everyone who tested migration paths, reported edge cases, and helped validate the adapter-first architecture across real workflows.
Full Changelog: 1.3.2...2.0.0
2.0.0-next.27
This release advances the Phase 7 adapter-first migration across items #4 through #11. The core runtime and type system now treat adapters and neutral query-schema contracts as the primary model, while Prisma-shaped behavior is kept only as an explicit compatibility layer during the 2.x transition window.
What's Changed
- Replaced Prisma-shaped core generic constraints with adapter-native query schema contracts across
Model,QueryBuilder,ModelStatic,DB, and the shared core/model type helpers. - Introduced neutral
QuerySchema*andAttributeQuerySchemahelpers as the primary public typing surface, while keeping older delegate-shaped names available as deprecated compatibility aliases. - Moved transaction typing and runtime flow to adapter-first contracts, including neutral
TransactionContext,TransactionOptions, and runtime client handling instead of Prisma-specific callback assumptions. - Updated transaction execution so adapter-backed transactions propagate through runtime storage and keep
Model.query()andDB.table()inside the active transaction scope. - Centralized compatibility fallback behavior in the runtime compatibility layer instead of rebuilding delegate resolution paths in multiple runtime entry points.
- Removed the remaining runtime dependency on
Model.getDelegate()for relation execution, eager loading fallback paths, relation filters, relation aggregates, andModel.load(...). - Added adapter-owned relation loading for the Kysely path, including nested eager-load execution through
adapter.loadRelations(...)without reintroducing delegate-based runtime behavior. - Isolated the remaining Prisma compatibility gaps behind explicit capability flags and targeted unsupported-feature errors instead of allowing them to leak as implicit runtime behavior.
- Unified constrained eager loading, relation aggregates, and relation filters around Arkorm-owned relation specs by compiling
with({...})callbacks intoRelationLoadPlanstructures and reusing the same path inModel.load(...). - Renamed remaining non-compatibility delegate-oriented internals to
tableterminology in model generation, schema sync helpers, and bundled stubs, while preserving legacy delegate parsing only where compatibility still requires it.
Documentation
- Updated the migration plan, roadmap, README, and versioned guides to reflect the adapter-first runtime as the real primary path.
- Updated the upgrade guidance so normal model overrides now use
tableterminology instead of teachingdelegateas the default pattern. - Clarified that
Model.setClient(...),Model.getDelegate(...), direct delegate-map helpers, and the staticdelegatealias remain compatibility-only during the 2.x window.
Compatibility Notes
- Prisma compatibility remains available, but it is now more explicitly scoped.
- Direct Prisma include-style relation plans still work through the compatibility adapter where supported.
- Adapter-owned relation batch loading, raw SQL predicate paths, and similar unsupported compatibility surfaces now fail explicitly instead of falling back silently.
- Deprecated delegate-shaped aliases such as
PrismaDelegateLikeremain temporarily available so existing integrations can continue compiling during the migration window.
Testing
- Added and expanded regression coverage for adapter-first relation execution, Kysely-owned eager loading, constrained eager-load planning, Prisma compatibility boundaries, and CLI/model-generation terminology updates.
- Current repo validation remains green under the full test suite via
pnpm test:all.
Migration Impact
If you are already on the next line, this release continues the same migration direction:
- Prefer adapter-backed setup and execution paths.
- Treat delegate-first APIs as compatibility-only.
- Use neutral query-schema and table terminology in new code.
- Expect unsupported Prisma-compatibility edge cases to fail explicitly rather than degrade silently.
Full Changelog: 2.0.0-next.26...2.0.0-next.27
2.0.0-next.26
Highlights
- Moved Arkorm core further from Prisma-shaped internals to adapter-native contracts
- Made transaction handling adapter-first, including transaction-scoped adapter propagation
- Isolated Prisma compatibility behavior behind dedicated helper layers instead of spreading it through core runtime classes
- Reduced delegate-first Model APIs to compatibility-only transition members
- Renamed table-backed relation internals away from delegate terminology where runtime behavior already uses tables
- Proved normal runtime queries no longer depend on Model.getDelegate
What’s Changed
- Added adapter-native ModelQuerySchemaLike as the primary query-shape contract in the core type system
- Switched Model, QueryBuilder, ModelStatic, DB, and model typing helpers to compile against adapter-native schema types instead of PrismaDelegateLike-first generics
- Introduced neutral transaction contracts with RuntimeClientLike, TransactionContext, and TransactionOptions
- Updated Model.transaction to prefer adapter-backed transactions and keep Model.query and DB.table inside the active transaction scope
- Centralized Prisma compatibility adapter and query-schema fallback in runtime-compatibility helpers
- Made Model.getDelegate a thin deprecated compatibility wrapper instead of a runtime control path
- Marked Model.setClient, Model.getDelegate, Model.delegate, and direct delegate-map bootstrapping as compatibility-only migration APIs rather than supported primary runtime setup
- Renamed throughDelegate to throughTable across through and pivot relation code where the runtime contract is already table-backed
- Added regression coverage proving adapter-backed runtime queries do not go through Model.getDelegate
- Updated the migration plan and roadmap to reflect the completed early Phase 7 items
Compatibility Notes
- Prisma compatibility remains supported during the 2.x transition window
- Deprecated delegate-first APIs still exist for migration support, but they no longer shape the primary runtime surface
Full Changelog: 2.0.0-next.25...2.0.0-next.26
2.0.0-next.25
What's Changed
Fix query delete behavior when no rows match
QueryBuilder.delete() no longer throws when a constrained delete finds no matching row. It now returns null, which fixes cases like User.query().whereNot({ id: demoUser.id }).delete() behaving as an unexpected failure instead of a no-op.
Add explicit throwing delete variant
Added QueryBuilder.deleteOrFail() for callers that need the previous fail-fast behavior. This preserves a strict path for workflows that expect a deleted model instance or an error.
Keep model instance deletes strict
Model.delete() and Model.forceDelete() now delegate to deleteOrFail(), so deleting a concrete model instance still throws if the underlying row cannot be found. The nullable behavior is limited to query-builder deletes.
Add regression coverage
Added base and Postgres query-builder tests covering:
- non-unique constrained deletes that match no rows now returning
null - row-count stability when the delete is a no-op
deleteOrFail()continuing to throwRecord not found for delete operation.
Full Changelog: 2.0.0-next.23...2.0.0-next.25
2.0.0-next.23
What's Changed
Added nested eager loading support
Arkorm eager loading now supports dotted child relation paths such as with(['requester', 'pocket', 'consents', 'consents.user']) and load(['consents.user']). Parent relations are loaded first, then child relations are eager loaded onto the resolved related models.
Eager loading now fails fast on invalid relations
Invalid eager-loaded relation names are no longer silently ignored. Arkorm now throws RelationResolutionException for both direct missing relations and nested missing relation paths during with(...) and load(...).
Added regression coverage
Base relationship tests now cover nested eager loading through both query-time and instance-time APIs, along with failure cases for undefined eager-loaded relationships.
Full Changelog: 2.0.0-next.21...2.0.0-next.23
2.0.0-next.21
What's Changed
BelongsToMany write helpers
BelongsToManyRelation now supports storage-oriented helpers for many-to-many relationships, including make(), create(), save(), attach(), detach(), and sync(). These helpers work through the adapter layer rather than assuming Prisma-only behavior, so non-Prisma adapters can manage pivot rows directly as part of normal relationship workflows.
Relationship typing fixes
The model attribute typing work was tightened so relationship-aware getAttribute() and setAttribute() keep strong inference without breaking Model.query() usage or causing circular type expansion in relation-heavy models and tests. This restores stable typing for eager-loaded relationships and declared model properties while preserving the newer relationship payload inference.
Pivot reconciliation improvements
Many-to-many pivot operations now support:
- attaching multiple related records
- detaching one, many, or all related records
- syncing relationship membership in one operation
- updating pivot attributes during sync
- transaction-backed reconciliation through the active adapter
This closes the gap between read-side belongs-to-many ergonomics and write-side behavior.
Non-Prisma adapter coverage
PostgreSQL integration coverage was expanded for the Kysely adapter path to verify belongs-to-many write helpers end to end against a non-Prisma adapter. This includes create/save/attach/detach/sync behavior on real pivot tables, not just the Prisma-compatible runtime path.
Test and fixture cleanup
The base relationship fixtures and specs were updated to match the new typing and write-helper behavior, including fixes for prior core-fixtures and relationships.spec errors. Additional regression coverage now verifies pivot payload handling and membership changes after detach/sync operations.
Fixes
- Fixed missing storage methods on
BelongsToManyRelation. - Fixed relation typing regressions that caused circular references and broken
query()inference. - Fixed adapter-backed pivot deletion behavior for composite-key pivot tables in the Kysely/Postgres path.
Tests
- Added base relationship coverage for belongs-to-many write helpers.
- Added base coverage for
detach()andsync(). - Added PostgreSQL Kysely integration coverage for many-to-many write helpers on the non-Prisma adapter path.
Full Changelog: 2.0.0-next.20...2.0.0-next.21
2.0.0-next.20
What’s Changed
Typed getAttribute() / setAttribute() inference
- Improved
getAttribute()so it now infers the return type from declared model properties, not just schema/generic attributes. - Improved
setAttribute()so writes against declared model properties are type-checked consistently withgetAttribute()reads.
Declared property support
- Models with explicit declarations like
declare name: stringnow get strongly typed attribute access:user.getAttribute('name')resolves tostringuser.setAttribute('name', value)enforcesstring
Relationship-aware attribute access
- Extended attribute typing to relationship method keys when the relationship has been loaded onto the model.
getAttribute()now resolves the expected relationship payload type from the relationship method’sgetResults()contract:- single-result relations resolve to
RelatedModel | null - multi-result relations resolve to
ArkormCollection<RelatedModel>
- single-result relations resolve to
setAttribute()mirrors that behavior for assigning eager-loaded relationship payloads.
Type utilities
- Added internal model relationship typing helpers to derive:
- which model method names represent relationships
- what resolved payload type each relationship exposes
Test coverage
- Added type-level regression coverage for:
- generic attribute inference
- declared property inference
- relationship payload inference for
hasOneandhasMany - typed
setAttribute()parity withgetAttribute()
Developer Impact
- Better IDE autocomplete and stronger compile-time guarantees when reading or assigning model attributes.
- Less need for manual casts around eager-loaded relationships and declared model fields.
Full Changelog: 2.0.0-next.17...2.0.0-next.20
2.0.0-next.17
What’s Changed
PostgreSQL enum naming
- Fixed generated PostgreSQL enum type names for non-Prisma migration flows to include the table name, so a users.status enum now becomes users_status_enum instead of the globally-colliding status_enum.
- Added regression coverage to ensure multiple tables can declare same-named enum columns without type-name conflicts.
Many-to-many relation ergonomics
- Added Laravel-style pivot helpers to belongs-to-many relations, including withPivot, withTimestamps, as, using, wherePivot, wherePivotNotIn, wherePivotBetween, wherePivotNotBetween, wherePivotNull, and wherePivotNotNull.
- Extended relation metadata and eager-loading behavior so pivot fields and aliases are preserved consistently across relation loading.
- Improved pivot hydration so executing terminal operations from relation queries still keeps pivot payloads attached.
Query debugging and inspection
- Added query inspection support for adapters that can expose compiled queries.
- Added QueryBuilder inspection support for select, single-record select, count, and exists flows.
- Added structured query execution error wrapping through QueryExecutionException so adapter failures surface consistent Arkorm error context.
- Included compiled SQL and bound parameters in wrapped Kysely execution errors when available.
- Added runtime debug configuration with support for either a boolean flag or a custom callback handler.
- Enabled structured debug events around database activity, including before, after, and error phases for adapter-backed operations.
Adapter improvements
- Enhanced the Kysely adapter to centralize compiled-query handling for inspection, debug logging, and execution error reporting.
- Enhanced the Prisma adapter to emit structured debug events and wrap raw delegate execution failures in Arkorm exceptions.
- Preserved the current Prisma boundary where SQL inspection is unavailable rather than returning guessed or partial SQL.
What's Changed
- Add 'next' branch to CI workflow triggers by @3m1n3nc3 in #2
- Update deploy-docs.yml by @3m1n3nc3 in #3
Full Changelog: 2.0.0-next.2...2.0.0-next.17