Conversation
📝 WalkthroughWalkthroughRefactors 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
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
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 | 🟠 MajorMutation of
readonlyproperty violates interface contract.The
IRelationsinterface declaresvalueasreadonly, butextract()mutatesthis.valueviasplice()(lines 28, 34) and pushes toremoved.value(line 33). This violates the interface contract and could cause unexpected behavior for consumers expecting immutability.Consider either:
- Making
extract()return a new collection without mutating the original- Removing
readonlyfrom 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 | 🟠 MajorFix
clear()loop condition so it actually clears.
i === 0prevents 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 inacceptWithFallback.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 returningIQueryfor consistency withExpressionParser.
ExpressionParserreturnsIQueryfrom itsparsemethod (line 69 in that file), whileSimpleParserreturns the concreteQueryclass. 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 > {
There was a problem hiding this comment.
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 | 🔴 CriticalCritical bug: Loop condition prevents clearing arrays with more than one element.
The loop condition
i === 0only evaluates totruewheniis exactly0. For an array with length > 1,istarts atlength - 1(e.g.,1for a 2-element array), the condition is immediatelyfalse, 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: PreserveOPERATORgeneric to keep type narrowing.Typing
operatorand the constructor parameter asstringerases the genericOPERATOR, soFilter<FilterFieldOperator.EQUAL>no longer narrows.operator. Consider keepingOPERATORhere; callers who need arbitrary strings can useFilter<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 implementingICondition<T>in theConditionclass.The
Conditionclass structurally matchesICondition<T>, but adding an explicitimplements 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;
| export function isFilters( | ||
| input: ICondition, | ||
| operator?: string, | ||
| ) : input is IFilters { | ||
| if (!Array.isArray(input)) return false; | ||
|
|
||
| if (operator) { | ||
| return operator === input.operator; | ||
| } | ||
|
|
||
| return true; |
There was a problem hiding this comment.
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.
| export interface IFiltersVisitor<R> { | ||
| visitFilters(expr: Filters): R; | ||
| visitFilters(expr: IFilters): R; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find and examine the clear() method in module.ts
find . -name "module.ts" -path "*/parameter/filters/collection/*" | head -5Repository: 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 2Repository: 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.
There was a problem hiding this comment.
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 | 🟡 MinorDuplicate 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> {
Summary by CodeRabbit
Refactor
Chores