-
Notifications
You must be signed in to change notification settings - Fork 173
【⚠️ Do Not Merge】fix(schema): support complex Drizzle method chaining patterns #3614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/github-folder-url-support
Are you sure you want to change the base?
【⚠️ Do Not Merge】fix(schema): support complex Drizzle method chaining patterns #3614
Conversation
Enhance the Drizzle PostgreSQL parser to correctly handle complex method chaining patterns like `.enableRLS().$comment()`. Previously, only simple chaining patterns were supported. Key changes: - Add extractPgTableFromChain() function to recursively traverse method chains - Enhance parsePgTableWithComment() to handle nested method calls - Refactor mainParser for better separation of concerns - Add comprehensive tests for .enableRLS() and .$comment() method chaining - Add test for complex chaining combining multiple methods - Make isPgTableCall private as it's only used internally This fixes GitHub folder URL parsing for Drizzle schemas that use multiple method chaining patterns, ensuring all tables are correctly parsed and displayed in ERD visualizations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
🦋 Changeset detectedLatest commit: f7cc245 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds support for parsing chained pgTable calls (e.g., .enableRLS(), .$comment()) and schema/comment/enum pathways by refactoring the Postgres main parser into modular handlers, introducing chain extraction utilities, updating table parsing to use chain-aware logic, and adding tests covering enableRLS and $comment chaining. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Source File
participant MP as mainParser
participant H as Handlers
participant AU as astUtils
participant TP as tableParser
participant State as Parser State
Dev->>MP: visitVariableDeclarator(node)
MP->>H: dispatch(callExpr)
alt pgTable direct or chained
H->>AU: extractPgTableFromChain(callExpr)
AU-->>H: basePgTableCall | null
alt basePgTableCall
H->>TP: parsePgTableCall(basePgTableCall)
TP-->>H: DrizzleTableDefinition
H->>State: save table + var→table map
else not pgTable
H-->>MP: pass
end
else schema-qualified table
H->>TP: parseSchemaTableCall(callExpr)
TP-->>H: DrizzleTableDefinition
H->>State: save table + map
else comment-based definition
H->>TP: parsePgTableWithComment(memberCall)
TP-->>H: DrizzleTableDefinition
H->>State: save/update table
else pgEnum
H->>TP: parsePgEnumCall(callExpr)
TP-->>H: DrizzleEnumDefinition
H->>State: save enum
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Updates to Preview Branch (feature/github-drizzle-method-chaining-support) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
🤖 Agent Deep Modeling ExecutionProcessing time: 9m 16s Command Output
RUN v3.2.4 /home/runner/work/liam/liam/frontend/internal-packages/agent (node:7679) ExperimentalWarning: WASI is an experimental feature and might change at any time ✅ [INFO] 2025-09-29T11:46:04.228Z ⏺ (pm): Planned actions:
🔧 saveRequirementsToArtifactTool(businessRequirement: "Deliver a simple,...", functionalRequirements: {12 keys}) ⎿ unknown: Requirements saved successfully to artifact 🔧 schemaDesignTool(operations: [19 items]) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) 🔧 saveTestcase(testcaseWithDml: {6 keys}) ⏺ (qa): Success. The test case has been generated and saved. Here is the test case created for the requirement. Test case details:
DML (produces a self-contained test in PostgreSQL using a PL/pgSQL DO block): -- Create user -- Create 5 password history records: hist_hash_5 is current INSERT INTO password_history (id, source, user_id, created_at, password_hash, is_active_current) INSERT INTO password_history (id, source, user_id, created_at, password_hash, is_active_current) INSERT INTO password_history (id, source, user_id, created_at, password_hash, is_active_current) INSERT INTO password_history (id, source, user_id, created_at, password_hash, is_active_current) -- Build last 5 hashes -- Capture current active row for revert -- Generate a good candidate not in last 5 -- Positive test: ensure candidate_good not in last 5 -- Revert positive test changes -- Negative test: attempt to reuse hist_hash_3 (present in last 5) END Notes:
🔧 saveTestcase(testcaseWithDml: {6 keys}) ⎿ unknown: 30/46 test cases passed, 16 failed ❌ Test Case: End-user profile view/edit with email re-verification1. Error:
|
…q/liam into feature/github-drizzle-method-chaining-support
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (3)
frontend/packages/schema/src/parser/drizzle/postgres/__tests__/index.test.ts (2)
61-101
: Good coverage for pgTable(...).enableRLS() chains; please add schema.table(...) variants.Add a case for auth.table('...').enableRLS() to prevent regressions for multi‑schema projects.
Apply a test like:
+ it('should parse schema.table() with .enableRLS() chaining', async () => { + const { value } = await processor(` + import { pgSchema, serial, text } from 'drizzle-orm/pg-core'; + const auth = pgSchema('auth'); + export const users = auth.table('users', { + id: serial('id').primaryKey(), + email: text('email').notNull(), + }).enableRLS(); + `) + expect(Object.keys(value.tables)).toHaveLength(1) + expect(value.tables['users']).toBeDefined() + expect(value.tables['users']?.columns['email']?.type).toBe('text') + })
103-140
: Capture comment ordering edge cases in tests.You cover enableRLS().$comment(); also test $comment().enableRLS() and schema.table(...).$comment().
Proposed additions:
+ it('should parse tables when $comment() is not the last chained call', async () => { + const { value } = await processor(` + import { pgTable, serial, text } from 'drizzle-orm/pg-core'; + export const t = pgTable('t', { id: serial('id'), name: text('name') }).$comment('T').enableRLS(); + `) + expect(value.tables['t']?.comment).toBe('T') + }) + + it('should parse schema.table().$comment()', async () => { + const { value } = await processor(` + import { pgSchema, serial } from 'drizzle-orm/pg-core'; + const auth = pgSchema('auth'); + export const users = auth.table('users', { id: serial('id').primaryKey() }).$comment('Auth users'); + `) + expect(value.tables['users']?.comment).toBe('Auth users') + })frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts (1)
5-9
: Import helpers to support generalized chaining and comment extraction.You’ll need parseMethodChain/getArgumentExpression/isStringLiteral and the generalized extractor.
Apply:
-import type { CallExpression, Module, VariableDeclarator } from '@swc/core' +import type { CallExpression, Module, VariableDeclarator } from '@swc/core' import { parseSync } from '@swc/core' import type { Processor, ProcessResult } from '../../types.js' -import { extractPgTableFromChain, isSchemaTableCall } from './astUtils.js' +import { + extractTableCallFromChain, + isSchemaTableCall, + parseMethodChain, + getArgumentExpression, + isStringLiteral, +} from './astUtils.js'
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/packages/schema/src/parser/drizzle/postgres/__tests__/index.test.ts
(1 hunks)frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
(1 hunks)frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts
(2 hunks)frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
(2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: FunamaYukina
PR: liam-hq/liam#2299
File: frontend/packages/db-structure/src/parser/drizzle/tableParser.ts:0-0
Timestamp: 2025-07-07T06:36:07.240Z
Learning: In the Drizzle ORM parser (frontend/packages/db-structure/src/parser/drizzle/tableParser.ts), the parseIndexDefinition function properly supports parsing index types from .using() method chains like index('name').on(...).using('btree'), extracting the type from the first argument of the .using() call and handling various PostgreSQL index types such as 'btree', 'gin', 'gist', etc.
Learnt from: FunamaYukina
PR: liam-hq/liam#2299
File: frontend/packages/db-structure/src/parser/drizzle/tableParser.ts:0-0
Timestamp: 2025-07-07T06:36:07.240Z
Learning: In the Drizzle ORM parser (frontend/packages/db-structure/src/parser/drizzle/tableParser.ts), the parseIndexDefinition function properly supports parsing index types from .using() method chains like index('name').on(...).using('btree'), extracting the type from the first argument of the .using() call and handling various PostgreSQL index types such as 'btree', 'gin', 'gist', etc.
📚 Learning: 2025-07-14T08:31:33.458Z
Learnt from: FunamaYukina
PR: liam-hq/liam#2556
File: frontend/packages/db-structure/src/parser/drizzle/postgres/mainParser.ts:7-7
Timestamp: 2025-07-14T08:31:33.458Z
Learning: In the drizzle parser refactor, the import path `../../types.js` in `frontend/packages/db-structure/src/parser/drizzle/postgres/mainParser.ts` correctly references `src/parser/types.ts` which exports the `Processor` and `ProcessResult` types. The build succeeds without issues as confirmed by FunamaYukina.
Applied to files:
frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/__tests__/index.test.ts
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
📚 Learning: 2025-07-14T08:31:33.458Z
Learnt from: FunamaYukina
PR: liam-hq/liam#2556
File: frontend/packages/db-structure/src/parser/drizzle/postgres/mainParser.ts:7-7
Timestamp: 2025-07-14T08:31:33.458Z
Learning: In the drizzle parser refactor (frontend/packages/db-structure/src/parser/drizzle/postgres/mainParser.ts), the import path `../../types.js` correctly references `src/parser/types.ts` from the postgres subdirectory. TypeScript imports use `.js` extensions even when importing `.ts` files, and the build succeeds without issues as confirmed by FunamaYukina.
Applied to files:
frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/__tests__/index.test.ts
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
📚 Learning: 2025-07-14T08:31:33.458Z
Learnt from: FunamaYukina
PR: liam-hq/liam#2556
File: frontend/packages/db-structure/src/parser/drizzle/postgres/mainParser.ts:7-7
Timestamp: 2025-07-14T08:31:33.458Z
Learning: In the drizzle parser refactor (frontend/packages/db-structure/src/parser/drizzle/postgres/mainParser.ts), the import path `../../types.js` correctly references `src/parser/types.js` from the postgres subdirectory, and the build succeeds without issues as confirmed by FunamaYukina.
Applied to files:
frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/__tests__/index.test.ts
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
📚 Learning: 2025-07-07T06:36:07.240Z
Learnt from: FunamaYukina
PR: liam-hq/liam#2299
File: frontend/packages/db-structure/src/parser/drizzle/tableParser.ts:0-0
Timestamp: 2025-07-07T06:36:07.240Z
Learning: In the Drizzle ORM parser (frontend/packages/db-structure/src/parser/drizzle/tableParser.ts), the parseIndexDefinition function properly supports parsing index types from .using() method chains like index('name').on(...).using('btree'), extracting the type from the first argument of the .using() call and handling various PostgreSQL index types such as 'btree', 'gin', 'gist', etc.
Applied to files:
frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/__tests__/index.test.ts
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
📚 Learning: 2025-06-26T05:50:27.762Z
Learnt from: MH4GF
PR: liam-hq/liam#2225
File: frontend/packages/db-structure/src/deparser/postgresql/schemaDeparser.ts:14-14
Timestamp: 2025-06-26T05:50:27.762Z
Learning: In the PostgreSQL schema deparser (frontend/packages/db-structure/src/deparser/postgresql/schemaDeparser.ts), type assertions like `as Table[]` and `as Index[]` are acceptable when used with Object.values() because the schema structure is guaranteed to be correct upstream, making the assertions safe.
Applied to files:
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Supabase Preview
- GitHub Check: Supabase Preview
- GitHub Check: Supabase Preview
🔇 Additional comments (1)
frontend/packages/schema/src/parser/drizzle/postgres/mainParser.ts (1)
175-197
: Dispatch order is fine; no change needed.The isCommentCall fast path remains valid after the generalized handler; it will still parse and set comments. The handler above covers non‑tail $comment() and schema.table chains.
Please run the new tests suggested in the test file to confirm both orderings and schema.table chains parse as expected.
const isPgTableCall = (callExpr: CallExpression): boolean => { | ||
return isIdentifierWithName(callExpr.callee, 'pgTable') | ||
} | ||
/** | ||
* Extract the base pgTable call from method chaining patterns | ||
* Handles patterns like: pgTable(...).enableRLS(), pgTable(...).comment(...), etc. | ||
*/ | ||
export const extractPgTableFromChain = ( | ||
callExpr: CallExpression, | ||
): CallExpression | null => { | ||
// If it's already a direct pgTable call, return it | ||
if (isPgTableCall(callExpr)) { | ||
return callExpr | ||
} | ||
|
||
// If it's a method call on another expression, check the object | ||
if (callExpr.callee.type === 'MemberExpression') { | ||
const baseCall = callExpr.callee.object | ||
if (baseCall.type === 'CallExpression') { | ||
// Recursively check if the base call is or contains a pgTable call | ||
return extractPgTableFromChain(baseCall) | ||
} | ||
} | ||
|
||
return null | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Broaden chain unwrapping utility to include schema.table(...).
extractPgTableFromChain only finds pgTable; callers miss schema.table chains. Provide a general extractor that returns either pgTable(...) or schema.table(...).
Apply (place after isSchemaTableCall to avoid TDZ):
+/**
+ * Extract the base table call (pgTable(...) or schema.table(...)) from a chain.
+ */
+export const extractTableCallFromChain = (
+ callExpr: CallExpression,
+): CallExpression | null => {
+ if (isPgTableCall(callExpr) || isSchemaTableCall(callExpr)) {
+ return callExpr
+ }
+ if (callExpr.callee.type === 'MemberExpression') {
+ const base = callExpr.callee.object
+ if (base.type === 'CallExpression') {
+ return extractTableCallFromChain(base)
+ }
+ }
+ return null
+}
Then use this in mainParser/tableParser (see suggested patches there). This keeps isPgTableCall private while enabling broader support. Based on learnings.
🤖 Prompt for AI Agents
In frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts around lines
114 to 139, the extractPgTableFromChain only recognizes pgTable(...) calls and
misses schema.table(...) chains; add a new extractor (placed immediately after
isSchemaTableCall to avoid TDZ) that unwraps chained CallExpressions and returns
either a pgTable(...) CallExpression or a schema.table(...) CallExpression
(i.e., check both isPgTableCall and isSchemaTableCall when unwrapping), keep
isPgTableCall private, and update callers (mainParser/tableParser) to use this
broader extractor so schema.table(...) chains are recognized the same way as
pgTable(...).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai
Supporting schema.table() requires fundamental schema type restructuring and should be addressed separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR enhances the Drizzle PostgreSQL parser to correctly handle complex method chaining patterns like .enableRLS().$comment()
that were previously causing tables to be ignored during parsing. The fix ensures all tables from Drizzle schemas are properly displayed in ERD visualizations from GitHub folder URLs.
Key Changes:
- Added recursive method chain traversal to find underlying
pgTable
calls - Refactored parser flow for better modularity and handling of different Drizzle construct types
- Added comprehensive test coverage for chained method patterns
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
astUtils.ts | Added extractPgTableFromChain function to recursively traverse method chains and find base pgTable calls |
tableParser.ts | Updated comment parsing to use new chain extraction logic for complex method patterns |
mainParser.ts | Refactored variable declarator parsing into modular functions for different call expression types |
index.test.ts | Added comprehensive tests for .enableRLS() and .$comment() method chaining patterns |
whole-mirrors-throw.md | Added changeset entry documenting the bug fix |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
// Store enum by its actual name (e.g., 'user_role') | ||
enums[enumDef.name] = enumDef | ||
// Also store by variable name (e.g., 'userRoleEnum') for lookup | ||
enums[declarator.id.value] = enumDef |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storing enums under both their actual name and variable name could lead to conflicts if an enum's actual name matches another enum's variable name. Consider using a more structured approach to avoid potential overwrites.
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously stored enums under both actual name and variable name, which could cause overwrites when an enum's actual name matched another enum's variable name. Now only stores by variable name to ensure unique keys and prevent data loss. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Reduced test complexity by using only 1 table per test case instead of 3. Added separate test for complex chaining pattern (.enableRLS().$comment()). Tests are now more focused and easier to maintain. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Please do not merge this PR yet, as the base PR has not been merged.
Issue
Summary
Enhance the Drizzle PostgreSQL parser to correctly handle complex method chaining patterns like
.enableRLS().$comment()
. This fixes GitHub folder URL parsing for Drizzle schemas that use multiple method chaining patterns.Issues Fixed
.enableRLS().$comment()
were not being parsed correctlyTechnical Details
AST Structure Analysis
The complex chaining pattern
.enableRLS().$comment()
creates this AST structure:$comment(...)
enableRLS()
callpgTable(...)
call (the base)Solution Implementation
The fix involves recursively traversing the method chain to find the underlying
pgTable
call, regardless of how many intermediate method calls are chained.Testing
before
in cli
after
https://liam-app-git-feature-github-drizzle-method-chaini-3328d9-liambx.vercel.app/erd/p/github.com/kanbn/kan/tree/main/packages/db/src/schema
Impact
This change ensures that ERD visualizations from GitHub folder URLs correctly display all tables from Drizzle schemas, regardless of the method chaining patterns used. Users can now use complex patterns like
.enableRLS().$comment()
without losing tables in their ERD diagrams.🤖 Generated with Claude Code
Summary by CodeRabbit