Skip to content

feat: enhance typing with interfaces and lesser coupling - #700

Merged
tada5hi merged 3 commits into
masterfrom
typing
Feb 3, 2026
Merged

feat: enhance typing with interfaces and lesser coupling#700
tada5hi merged 3 commits into
masterfrom
typing

Conversation

@tada5hi

@tada5hi tada5hi commented Feb 3, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Refactor

    • Converted many public types to new I*-style interfaces across query, fields, filters, pagination, relations, and sorts; parser, visitor, encoder/decoder signatures and return types updated accordingly while preserving runtime behavior.
    • Simplified several parsing flows and aligned public APIs to interface contracts.
  • Chores

    • Removed legacy helper re-exports and cleaned related public surface.
    • Removed an unused transitive dependency from a package manifest.

@coderabbitai

coderabbitai Bot commented Feb 3, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Refactors public types to I-prefixed interfaces across core, parsers, codecs, and SQL visitor modules; classes now implement I* interfaces and public method signatures/return types switched from concrete classes to I* interfaces. Some filter helper utilities were removed and a new isFilters type-guard added.

Changes

Cohort / File(s) Summary
Core parameter interfaces & types
packages/core/src/parameter/types.ts, packages/core/src/parameter/{fields,filters,relations,sorts,pagination}/**/types.ts
Added I*-prefixed interfaces (IField, IFields, IFilter, IFilters, IRelation, IRelations, ISort, ISorts, IPagination, IQuery), renamed QueryOptions→QueryContext, and updated visitor signatures to use I* types.
Core implementations (classes)
packages/core/src/parameter/{fields,filters,relations,sorts,pagination}/**/module.ts, packages/core/src/parameter/module.ts, packages/core/src/parameter/filters/condition.ts
Runtime classes now implement corresponding I* interfaces; public properties/constructors updated to use I* types; Condition converted to plain class with ICondition interface.
Filters collection helpers & check
packages/core/src/parameter/filters/helpers/module.ts (removed), packages/core/src/parameter/filters/helpers/index.ts, packages/core/src/parameter/filters/collection/check.ts, packages/core/src/parameter/filters/collection/index.ts
Removed legacy helper module and its re-export; added isFilters type-guard and re-exported it; Filters collection now relies on isFilters for checks.
Parser bases & concrete parsers
packages/core/src/parser/parameter/{fields,filters,pagination,relations,sort}/base.ts, packages/parser-expression/src/module.ts, packages/parser-expression/src/parameter/filters/module.ts, packages/parser-simple/src/module.ts, packages/parser-simple/src/parameter/{fields,filters}/module.ts
BaseParser generics and parser method signatures updated to I* interfaces; ExpressionParser/SimpleParser parse/parseXxx now return I* types; tests adjusted (parseExact usage).
Codec: URL decoder
packages/codec-url-simple/src/decoder/module.ts
URLDecoder.decode and decode* helpers now return IQuery/IFields/IFilters/IPagination/IRelations/ISorts (interfaces) instead of concrete Query/Fields/Filters/etc.; internal return logic simplified to return parsed I* types.
Codec: URL encoder & visitors & types
packages/codec-url-simple/src/encoder/module.ts, packages/codec-url-simple/src/encoder/visitors/module.ts, packages/codec-url-simple/src/types.ts
URLEncoder, visitors, and encoder types updated to accept/return I*-prefixed interfaces (IQuery, IFields, IFilter, IFilters, IPagination, IRelations, ISorts, etc.) in public signatures and imports.
Schema & parameter filter types
packages/core/src/schema/parameter/filters/{schema,types}.ts, packages/core/src/parameter/filters/collection/module.ts, packages/core/src/parameter/filters/collection/types.ts, packages/core/src/parameter/filters/record/{module,types}.ts
Schema validators and filters collection/record types switched to ICondition/IFilter/IFilters; Filters collection redesigned to implement IFilters with add/clear/flatten and use isFilters.
Sorts / Relations / Fields modules & visitors
packages/core/src/parameter/{relations,sorts,fields}/**/*, packages/sql/src/visitor/fields.ts
Collection and record classes updated to implement I* interfaces and use I*-typed values; visitor signatures changed to accept I* types; visitFields now returns adapter instance.
Parser-simple adjustments
packages/parser-simple/src/parameter/fields/module.ts, packages/parser-simple/src/parameter/filters/module.ts
Public parse methods now return I* types; small internal changes to avoid mutating nested Field names and to align filter parsing output types with IFilter/IFilters.
Package manifests
packages/core/package.json, packages/sql/package.json
Removed @ucast/core from packages/core and packages/sql manifests (dev/peer removal in sql).

Sequence Diagram(s)

(omitted — changes are primarily type/interface surface updates and small helper removals, not new multi-component control flows)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through types and made them neat,
I-prefixed whispers now tidy and sweet.
Parsers nod, visitors take their cues,
Guards and interfaces guide the views. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: introducing interface-based typing (I* prefixed types) across the codebase and reducing coupling by removing direct class dependencies in favor of interface contracts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch typing

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/parameter/relations/collection/module.ts (1)

23-39: ⚠️ Potential issue | 🟠 Major

Mutation of readonly property violates interface contract.

The IRelations interface declares value as readonly, but extract() mutates this.value via splice() (lines 28, 34) and pushes to removed.value (line 33). This violates the interface contract and could cause unexpected behavior for consumers expecting immutability.

Consider either:

  1. Making extract() return a new collection without mutating the original
  2. Removing readonly from the interface if mutation is intentional
♻️ Proposed immutable implementation
     extract(root: string): IRelations {
-        const removed: Relations = new Relations();
+        const remaining: IRelation[] = [];
+        const extracted: IRelation[] = [];

-        for (let i = this.value.length - 1; i >= 0; i--) {
-            if (this.value[i].name === root) {
-                this.value.splice(i, 1);
+        for (let i = 0; i < this.value.length; i++) {
+            const item = this.value[i];
+            if (item.name === root) {
                 continue;
             }

-            if (this.value[i].name.substring(0, root.length) === root) {
-                removed.value.push(new Relation(this.value[i].name.substring(root.length + 1)));
-                this.value.splice(i, 1);
+            if (item.name.startsWith(root + '.')) {
+                extracted.push(new Relation(item.name.substring(root.length + 1)));
+            } else {
+                remaining.push(item);
             }
         }

-        return removed;
+        // Note: If mutation is intended, consider a different API design
+        return new Relations(extracted);
     }
packages/core/src/parameter/filters/collection/module.ts (1)

24-27: ⚠️ Potential issue | 🟠 Major

Fix clear() loop condition so it actually clears.

i === 0 prevents the loop from running for arrays longer than 1, leaving items uncleared.

🛠️ Proposed fix
-    for (let i = this.value.length - 1; i === 0; i--) {
+    for (let i = this.value.length - 1; i >= 0; i--) {
         this.value.splice(i, 1);
     }
🤖 Fix all issues with AI agents
In `@packages/core/src/parameter/types.ts`:
- Around line 34-36: The visitor interface IQueryVisitor currently accepts the
concrete Query class; change its method signature to accept the interface type
IQuery instead (i.e., update visitQuery to visitQuery(expr: IQuery): R) so
visitors depend on the abstraction not the concrete class, and update any
import/usage of IQueryVisitor or its implementors to use IQuery accordingly
(look for IQueryVisitor, visitQuery, Query, and IQuery in the diff to update
types and imports).
🧹 Nitpick comments (3)
packages/core/src/parameter/fields/record/types.ts (1)

8-20: Align IFieldVisitor to the interface surface.

IField is introduced, but IFieldVisitor still depends on the concrete Field type, which undercuts the interface-based decoupling goal. Consider switching the visitor to IField to keep the public surface fully interface-driven. This aligns with the parallel IFieldsVisitor pattern, which already uses IFields as its parameter type.

♻️ Suggested change
-import type { Field } from './module';
-
 export interface IFieldVisitor<R> {
-    visitField(expr: Field): R;
+    visitField(expr: IField): R;
 }
packages/core/src/parameter/filters/record/module.ts (1)

101-110: Consider type safety improvement in acceptWithFallback.

The cast this as Filter<any, any> at line 106 bypasses type checking. While this works, you could improve type safety by using the interface type instead.

♻️ Optional: Use interface type in cast
     private acceptWithFallback<R, P extends keyof IFilterVisitor<R>>(
         visitor: IFilterVisitor<R>,
         property: P,
     ) : R {
         if (visitor[property]) {
-            return visitor[property](this as Filter<any, any>);
+            return visitor[property](this as IFilter<any, any>);
         }

         return visitor.visitFilter(this);
     }
packages/parser-simple/src/module.ts (1)

32-35: Consider returning IQuery for consistency with ExpressionParser.

ExpressionParser returns IQuery from its parse method (line 69 in that file), while SimpleParser returns the concrete Query class. For API consistency across parsers, consider updating the generic parameter and return type.

♻️ Optional refactor for consistency
 export class SimpleParser extends BaseParser<
 ParseOptions,
-Query
+IQuery
 > {

Comment thread packages/core/src/parameter/types.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/parameter/filters/collection/module.ts (1)

32-36: ⚠️ Potential issue | 🔴 Critical

Critical bug: Loop condition prevents clearing arrays with more than one element.

The loop condition i === 0 only evaluates to true when i is exactly 0. For an array with length > 1, i starts at length - 1 (e.g., 1 for a 2-element array), the condition is immediately false, and the loop never executes.

🐛 Proposed fix
 clear() {
-    for (let i = this.value.length - 1; i === 0; i--) {
+    for (let i = this.value.length - 1; i >= 0; i--) {
         this.value.splice(i, 1);
     }
 }

Alternatively, for simplicity:

clear() {
    this.value.length = 0;
}
🤖 Fix all issues with AI agents
In `@packages/core/src/parameter/filters/collection/check.ts`:
- Around line 11-21: The type-guard isFilters currently (in function isFilters)
wrongly checks Array.isArray(input); instead detect IFilters by verifying input
is an object with a value that is an array (e.g., ensure input && typeof input
=== 'object' && 'value' in input && Array.isArray((input as any).value)); keep
the existing operator comparison (operator === input.operator) when operator is
provided; update the function's initial checks to safely access input.value so
the type predicate (input is IFilters) works correctly.

In `@packages/core/src/parameter/filters/collection/types.ts`:
- Around line 10-12: The clear() method in the collection module is using the
wrong loop condition so it never iterates through all elements; in the clear()
implementation (method name: clear on the collection class in
packages/core/src/parameter/filters/collection/module.ts) change the for-loop
condition from i === 0 to i >= 0 so the loop decrements from this.value.length -
1 down to 0 and splices each element, ensuring multi-element arrays are fully
cleared.
🧹 Nitpick comments (2)
packages/core/src/parameter/filters/record/module.ts (1)

11-24: Preserve OPERATOR generic to keep type narrowing.

Typing operator and the constructor parameter as string erases the generic OPERATOR, so Filter<FilterFieldOperator.EQUAL> no longer narrows .operator. Consider keeping OPERATOR here; callers who need arbitrary strings can use Filter<string>.

♻️ Proposed refactor
-    readonly operator: string;
+    readonly operator: OPERATOR;
@@
-    constructor(operator: string, field: string, value: VALUE) {
+    constructor(operator: OPERATOR, field: string, value: VALUE) {
packages/core/src/parameter/filters/condition.ts (1)

15-26: Consider explicitly implementing ICondition<T> in the Condition class.

The Condition class structurally matches ICondition<T>, but adding an explicit implements ICondition<T> clause provides compile-time verification and clearer intent.

♻️ Suggested change
 export class Condition<
     T = unknown,
-> {
+> implements ICondition<T> {
     readonly operator: string;

     readonly value: T;

Comment on lines +11 to +21
export function isFilters(
input: ICondition,
operator?: string,
) : input is IFilters {
if (!Array.isArray(input)) return false;

if (operator) {
return operator === input.operator;
}

return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical bug: Array.isArray(input) always returns false for ICondition/IFilters objects.

The ICondition and IFilters interfaces are objects with operator and value properties—they are not arrays themselves. The check Array.isArray(input) will always fail, making this type guard non-functional.

To distinguish IFilters from a plain ICondition or IFilter, consider checking for the presence of IFilters-specific properties (like whether value is an array) or methods.

🐛 Proposed fix
 export function isFilters(
     input: ICondition,
     operator?: string,
 ) : input is IFilters {
-    if (!Array.isArray(input)) return false;
+    if (!Array.isArray(input.value)) return false;

     if (operator) {
         return operator === input.operator;
     }

     return true;
 }

Note: This fix assumes IFilters is the only condition type with an array value. If IFilter or other condition types can also have array values, additional discrimination logic may be needed (e.g., checking for 'accept' in input or a dedicated type discriminator property).

🤖 Prompt for AI Agents
In `@packages/core/src/parameter/filters/collection/check.ts` around lines 11 -
21, The type-guard isFilters currently (in function isFilters) wrongly checks
Array.isArray(input); instead detect IFilters by verifying input is an object
with a value that is an array (e.g., ensure input && typeof input === 'object'
&& 'value' in input && Array.isArray((input as any).value)); keep the existing
operator comparison (operator === input.operator) when operator is provided;
update the function's initial checks to safely access input.value so the type
predicate (input is IFilters) works correctly.

Comment on lines 10 to +12
export interface IFiltersVisitor<R> {
visitFilters(expr: Filters): R;
visitFilters(expr: IFilters): R;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the clear() method in module.ts
find . -name "module.ts" -path "*/parameter/filters/collection/*" | head -5

Repository: tada5hi/rapiq

Length of output: 115


🏁 Script executed:

#!/bin/bash
# Search for the clear() method in the filters collection directory
rg "clear\s*\(" packages/core/src/parameter/filters/collection/ -A 5 -B 2

Repository: tada5hi/rapiq

Length of output: 1076


The clear() method in packages/core/src/parameter/filters/collection/module.ts has a critical bug in its loop condition.

The loop condition i === 0 should be i >= 0. The current code prevents the loop body from executing when the array has more than one element, making the method ineffective at clearing multi-element arrays.

Buggy code
clear() {
    for (let i = this.value.length - 1; i === 0; i--) {
        this.value.splice(i, 1);
    }
}

Change the condition to i >= 0 to properly iterate backward through all elements.

🤖 Prompt for AI Agents
In `@packages/core/src/parameter/filters/collection/types.ts` around lines 10 -
12, The clear() method in the collection module is using the wrong loop
condition so it never iterates through all elements; in the clear()
implementation (method name: clear on the collection class in
packages/core/src/parameter/filters/collection/module.ts) change the for-loop
condition from i === 0 to i >= 0 so the loop decrements from this.value.length -
1 down to 0 and splices each element, ensuring multi-element arrays are fully
cleared.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/codec-url-simple/src/encoder/visitors/module.ts (1)

38-48: ⚠️ Potential issue | 🟡 Minor

Duplicate interface implementation: IFilterVisitor<RecordSerializer> appears twice.

Line 42 and Line 43 both declare IFilterVisitor<RecordSerializer>. This appears to be a copy-paste error.

Proposed fix
 export class QueryVisitor implements IQueryVisitor<QuerySerializer>,
     IFieldsVisitor<RecordArraySerializer>,
     IFieldVisitor<RecordArraySerializer>,
     IFiltersVisitor<RecordSerializer>,
     IFilterVisitor<RecordSerializer>,
-    IFilterVisitor<RecordSerializer>,
     IPaginationVisitor<RecordSerializer>,
     IRelationsVisitor<ArraySerializer>,
     IRelationVisitor<ArraySerializer>,
     ISortsVisitor<ArraySerializer>,
     ISortVisitor<ArraySerializer> {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant