Changelog - 2026-07-11
Connectors Consistency Hardening
Breaking Change Bug Fix EnhancementIn one line. A full review of the connector layer (Postgres, Typesense, Meilisearch) closed a batch of inconsistencies between engines, tightened the public API, and fixed several correctness bugs.
What changed
- Strict
find()/findOne()everywhere.optsandfilterare both required on every connector now; the find-all form is the explicitfind({ filter: {} }). - Engine tuning moved to
engineParams. Eleven Typesense-only fields were removed from the neutral search input; engine-specific tuning now travels throughengineParams, keyed by the engine's own wire names. - Memory connector removed.
MemoryDataSource/MemoryRepositoryhad no known consumers and were deleted entirely, including the@venizia/ignis/memoryexport. - Internal barrels are no longer public. The search, Typesense, and Meilisearch
internal/modules are no longer exported from the connector barrels. SwaggerComponentrenamed toApiReferenceComponent. Swagger UI is one pluggable UI provider among several, so the component now has a vendor-neutral name.- Dead exports removed. The
node:test-based structured testing module (TestPlan,TestCase,TestDescribe,BaseTestPlan) and four unused helper utilities (transformValueOrPromise,getNumberValue,toStringDecimal,parseArrayToRecordWithKey). - New operators on Postgres.
exists,notExists, andnotnow work (previously threwInvalid query operator), including on JSON-path fields. - Filter fix.
mergeFilterno longer corrupts filters when combining a caller'swherewith a@modeldefault filter (see Details). - Cross-engine alignment. Typesense and Meilisearch now return the same
409/404behavior for duplicate and missing document IDs. - Search fixes.
multiSearchno longer leaks hidden fields;updateAll/deleteAllon the search connectors are now count-only and no longer silently cap atdefaultLimit. - Performance. Relation resolution is memoized,
WHEREis built once per call, and Meilisearch write polling backs off exponentially instead of polling at a fixed interval.
Who is affected
- Every application using search repositories (
find()/findOne()). Barefind()no longer compiles - see Breaking change 1. - Any caller passing Typesense-only tuning fields directly on the search input (
numTypos,preset, etc.). Move them intoengineParams- see Breaking change 2. - Anyone using the memory connector. It is gone - see Breaking change 3.
- Anyone importing an engine
internalbarrel. No longer resolvable - see Breaking change 4. - Anyone importing
SwaggerComponent. Still works via a deprecated alias, but new code should useApiReferenceComponent- see Breaking change 6. - Everyone else. No action needed; the bug fixes and performance work apply automatically.
Breaking changes
1. Strict find() / findOne() signatures
opts and filter are both required on every connector - Postgres and the search family. Bare find() and find({}) are now compile errors. This only changes the search repositories in practice; Postgres already required a filter.
Before
// worked on the search repositories only
const all = await productSearchRepository.find();After
// the find-all form is explicit on every connector
const all = await productSearchRepository.find({ filter: {} });2. Engine-specific search knobs moved to engineParams
Eleven Typesense-only tuning fields were removed from the neutral search input: numTypos, prefix, infix, useCache, cacheTtl, exhaustiveSearch, pinnedHits, hiddenHits, prioritizeExactMatch, dropTokensThreshold, and preset. They now travel through engineParams, keyed by the engine's own wire name (snake_case).
Before
await productSearchRepository.search({
mode: 'keyword',
query: 'wireless',
queryBy: ['name'],
numTypos: 1, // camelCase neutral field
preset: 'listing',
});After
await productSearchRepository.search({
mode: 'keyword',
query: 'wireless',
queryBy: ['name'],
engineParams: { num_typos: 1, preset: 'listing' }, // engine wire names, passed verbatim
});3. Memory connector removed
MemoryDataSource / MemoryRepository and the @venizia/ignis/memory export are gone - code, tests, and all.
What to do: use BasePostgresDataSource / DefaultCRUDRepository against a disposable database for prototyping and tests, or use the search connectors' fakes if your use case was search-shaped.
4. Engine internal/ barrels no longer exported
SearchConnectorInternal, TypesenseInternal, and MeilisearchInternal are no longer part of the public API.
Before
import { TypesenseInternal } from '@venizia/ignis/typesense';After
// Gone from the public surface - there is no specifier that resolves to it.
// These were engine-internal helpers with no compatibility guarantee.5. SwaggerComponent renamed to ApiReferenceComponent
Before
import { ISwaggerOptions, SwaggerBindingKeys, SwaggerComponent } from '@venizia/ignis';
this.bind({ key: SwaggerBindingKeys.SWAGGER_OPTIONS }).toValue(options);
this.component(SwaggerComponent);After
import { ApiReferenceBindingKeys, ApiReferenceComponent, IApiReferenceOptions } from '@venizia/ignis';
this.bind({ key: ApiReferenceBindingKeys.API_REFERENCE_OPTIONS }).toValue(options);
this.component(ApiReferenceComponent);Not urgent - old names still work
The old names remain as deprecated aliases, so existing applications compile and run unchanged.
Details
Bug fixes
mergeFilterno longer corrupts filters. The default-filter merge previously combined arrays index-wise, so a user{ id: { inq: [4] } }merged onto a default{ id: { inq: [1, 2, 3] } }producedinq: [4, 2, 3], and twoorarrays could collapse into one AND-of-both - either could silently widen a default tenant scope. The merge is now top-key level: a user-supplied key replaces the default's value wholesale, and a user value ofundefinednever overrides a defined default.- Cross-engine
409/404alignment. TypesensecreateDocumenton a duplicate id now throws409(core.search_engine.already_exists) instead of a misleading503. MeilisearchupdateByIdon a missing id now throws404instead of silently creating a new record. - Meilisearch primary-key authority. Update payloads now write the path id last and authoritative, so a patch body carrying the primary key can no longer retarget a write onto a different row.
multiSearchno longer leaks hidden fields. Each collection's@modelhiddenPropertiesare now injected into that entry'sexcludeFields, matching what single-collectionsearch()already did.updateAll/deleteAllon search are count-only. They now return{ count, data: null }and never issue an extra read - the previous behavior silently capped the returned data atdefaultLimitwhile reporting a highercount.- Meilisearch
updateByIdrespectsdefaultFilter. A document excluded by a@modeldefault filter now reports the same404as a genuinely missing one, instead of being updated through the filter. - Numeric consistency on Postgres. JSON-path numeric operators now cast the extracted value consistently, fixing a
text = integerSQL error;neqcorrectly never matches aNULLrow.
Performance
| Scenario | Improvement |
|---|---|
Reads without include | Relation resolution runs only when include is present, and is memoized per schema |
update / delete | The WHERE condition is built once and reused, not built twice per call |
| Meilisearch writes | Task polling backs off exponentially (50ms doubling, 1s cap) instead of a fixed 50ms interval |
| Typesense wire mapping | camelCase-to-wire key mapping is O(1) |
Files changed
| File | Changes |
|---|---|
src/connectors/search/repositories/common/constants.ts | 11 engine knobs removed from TSearchInput; engineParams added |
src/connectors/search/repositories/core/readable.ts | Strict find / findOne; search dispatch |
src/connectors/search/repositories/core/persistable.ts | updateAll/deleteAll count-only contract; updateById default-filter guard |
src/connectors/search/datasources/base.ts | multiSearch injects hidden fields into excludeFields per entry |
src/connectors/search/index.ts | Stop re-exporting ./internal |
src/connectors/postgres/repositories/dialect/filter.ts | mergeFilter top-key merge; not operator; JSON numeric cast; memoized relations |
src/connectors/postgres/repositories/dialect/query.ts | exists / notExists operators; NULL-aware neq / ne |
src/connectors/postgres/repositories/core/readable.ts | Strict find / findOne |
src/connectors/postgres/repositories/core/persistable.ts | Build the WHERE once and reuse it |
src/connectors/memory/** | Removed entirely |
src/connectors/meilisearch/connector.ts | Missing-id 404; primary-key authority; exponential poll backoff; batch-atomic import |
src/connectors/typesense/connector.ts | Duplicate-id 409 already_exists (was a misleading 503) |
src/connectors/typesense/compiler.ts | Clear error for a defaultSort naming a nonexistent field |