-
Notifications
You must be signed in to change notification settings - Fork 491
Rename catalog to product line #1107
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: dev
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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. 📝 WalkthroughWalkthroughThis PR renames payments "catalog" concepts to "product line" across backend, frontend, schema, seeds, and tests: Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 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
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (4)**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/stack-shared/src/config/schema.ts📄 CodeRabbit inference engine (AGENTS.md)
Files:
{.env*,**/*.{ts,tsx,js,jsx}}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-01-13T18:14:29.974ZApplied to files:
🧬 Code graph analysis (1)packages/stack-shared/src/config/schema.ts (2)
⏰ 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). (14)
🔇 Additional comments (4)
✏️ Tip: You can disable this entire section by setting 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 |
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 renames the "catalog" terminology to "product line" throughout the codebase to better reflect the business concept. The changes include schema updates, backend logic, API endpoints, frontend UI, and comprehensive test coverage.
Changes:
- Renamed
catalogs/catalogIdtoproductLines/productLineIdacross schema, backend, and frontend - Added
customerTypefield to product lines with validation ensuring products match their product line's customer type - Included backward compatibility migration logic for old config properties
- Updated all UI components, error messages, and documentation to use "product line" terminology
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/stack-shared/src/schema-fields.ts | Updated product schema to use productLineId instead of catalogId |
| packages/stack-shared/src/config/schema.ts | Renamed config schema from catalogs to productLines, added customerType field and validation, added migration functions |
| packages/stack-shared/src/utils/jwt.tsx | Added debug console.log statement (unrelated to PR) |
| packages/stack-shared/src/config/schema-fuzzer.test.ts | Updated test fuzzer config for new schema |
| apps/e2e/tests/backend/endpoints/api/v1/payments/*.test.ts | Updated E2E tests to use new terminology |
| apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/*.test.ts | Added backward compatibility tests |
| apps/backend/src/lib/payments.tsx | Updated all payment logic to use productLineId instead of catalogId |
| apps/backend/src/lib/payments.test.tsx | Updated unit tests |
| apps/backend/src/app/api/latest/payments/**/*.ts | Updated API endpoints to use new terminology |
| apps/backend/prisma/seed.ts | Updated seed data |
| apps/dashboard/src/lib/apps-frontend.tsx | Updated navigation to "Product Lines" |
| apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/**/*.tsx | Renamed all UI components and dialogs from catalog to product line |
| apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/catalogs/layout.tsx | Deleted old catalogs layout file |
Comments suppressed due to low confidence (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:2448
- The
onCreateProductLinefunction only setsdisplayNamebut the schema now requirescustomerTypefor product lines. This will cause validation errors. The function should passcustomerTypefrom thenewProductLineCustomerTypestate variable. Update line 2446 to:{ displayName, customerType: newProductLineCustomerType }
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:2161 - Button text should have proper spacing: 'Create Product Line' instead of 'Create ProductLine'.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:1504 - The function signature for
onCreateProductLineis missing thecustomerTypeparameter, which is now required by the schema. Update the signature to:onCreateProductLine: (productLineId: string, displayName: string, customerType: 'user' | 'team' | 'custom') => Promise<void>
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:2139 - The call to
onCreateProductLineis missing the requiredcustomerTypeargument. Update to:await onCreateProductLine(id, displayName, newProductLineCustomerType);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
Greptile SummaryThis PR renames "catalog" to "product line" throughout the codebase, introducing a clearer naming convention for the mutual exclusivity grouping concept in the payments system. The refactoring includes schema updates, API logic changes, UI component updates, and comprehensive backward compatibility migrations. Key Changes:
Critical Issues Found:
Test Coverage: Comprehensive - old config formats are tested to verify they work after migration, though the migration bug may cause those tests to fail in practice. Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Customer
participant App as App Client
participant API as Backend API
participant Config as Config System
participant Stripe
Customer->>App: Select product
App->>API: POST /create-purchase-url
API->>Config: Load payments config
Config->>Config: Migrate old catalogs→productLines
Config->>Config: Validate productLineId.customerType matches product.customerType
API->>API: getSubscriptions()<br/>Check for product line conflicts
API->>API: validatePurchaseSession()<br/>Ensure mutual exclusivity
API->>Stripe: Create/update subscription
Stripe-->>API: subscription_id
API-->>App: Purchase code
App-->>Customer: Payment link
Note over Config: ISSUE: Migration doesn't<br/>infer customerType,<br/>old configs will fail validation
|
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.
30 files reviewed, 2 comments
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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/backend/src/lib/payments.test.tsx (2)
1206-1206: Update error message assertion to match production code.The test expects
"Multiple include-by-default products configured in the same catalog"but the production code throws"Multiple include-by-default products configured in the same product line". Changecatalogtoproduct linein the assertion on line 1206.
879-879: Update error message assertions to match production code.Two test assertions use incorrect error message strings:
- Line 879: expects
"...in this product catalog"but production code throws"...in this product line"- Line 1206: expects
"...in the same catalog"but production code throws"...in the same product line"Change both occurrences from "product catalog" to "product line" to match the actual error messages in
payments.tsx.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
329-358: Replace try/catch + destructive toast withrunAsynchronouslyWithAlert(and avoid toast for blocking errors).
Thecatchblock uses a destructive toast (Line 354-355). This is a blocking failure (product wasn’t created) and should be an alert /runAsynchronouslyWithAlertpath per guidelines.Based on learnings and as per coding guidelines, prefer `runAsynchronouslyWithAlert` for async submit handlers and avoid error toasts.Proposed refactor (keep your loading state; let the alert wrapper surface errors)
- const handleSave = async () => { + const handleSave = () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } - setIsSaving(true); - try { + runAsynchronouslyWithAlert(async () => { + setIsSaving(true); + try { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { - setIsSaving(false); - } + } finally { + setIsSaving(false); + } + }); };Also applies to: 341-357
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2044-2166: Replace blocking validation error toasts with alerts (and consider loading state).
Creating a product line can fail validation (invalid ID / duplicate ID), and those failures block the action. Usingtoast(..., variant: "destructive")here is easy to miss.As per coding guidelines, blocking alerts/errors shouldn’t use toast. Also, this is an async handler without a loading/disabled state; consider disabling the button while the request is in-flight.
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx:
- Around line 564-565: Replace the internal camelCase “productLine” with
user-facing “product line” in the UI copy: update the DialogDescription text
(and the other corresponding user-facing strings near the same block) so phrases
like “in the same productLine” and “same customer type and productLine” read “in
the same product line” and “same customer type and product line”; locate these
strings in the page-client.tsx DialogDescription/related JSX and change only the
displayed text.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx:
- Around line 762-767: existingProductsList currently maps over
paymentsConfig.products assuming each product exists, which can crash if some
entries are null/undefined; update the creation of existingProductsList to
mirror the falsy-product guard used for groupedProducts by filtering
Object.entries(paymentsConfig.products) to only include entries where product is
truthy (or by conditionally mapping and skipping nulls) before constructing
objects with id, displayName, productLineId, and customerType so no access on
null/undefined occurs.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:
- Around line 2437-2443: The onSaveProductWithGroup handler is overwriting the
whole payments.productLines.{productLineId} with an empty object which wipes
properties like displayName created by onCreateProductLine; update
onSaveProductWithGroup to stop setting
[`payments.productLines.${productLineId}`] to {} (remove that key from the
update payload) or instead set it to preserve the existing displayName (e.g.,
[`payments.productLines.${productLineId}`]: { displayName: existingDisplayName
}) while still writing [`payments.products.${productId}`]: product and calling
toast.
In `@packages/stack-shared/src/utils/jwt.tsx`:
- Line 66: Remove the leftover debug log "console.log({ options })" from
packages/stack-shared/src/utils/jwt.tsx so the raw JWT/token in the options
object is not emitted to logs; search the jwt utility for any other ad-hoc
console.log/debug prints and delete them or replace with safe, non-sensitive
logging via the existing logger if needed, then run tests/lint to ensure no
unrelated changes were introduced.
🧹 Nitpick comments (9)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsx (1)
97-97: Align hover transition with coding guidelines and file consistency.The className uses
transition-colorswithouthover:transition-none, which creates both hover-enter and hover-exit transitions. Per coding guidelines, hover transitions should only occur on hover-exit. The form variant in this same file (lines 163, 239) correctly implements this pattern withtransition-colors duration-150 hover:transition-none.♻️ Proposed fix to align with hover transition pattern
- className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors" + className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors duration-150 hover:transition-none"As per coding guidelines: "When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions".
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
8-14: Consider usingsanitizeUserSpecifiedIdfor consistency.The
toIdFormathelper lowercases the input and strips non-alphanumeric characters, butsanitizeUserSpecifiedId(used on line 116 for manual edits) preserves case. This creates inconsistent behavior:
- Auto-generated ID:
"Pricing Tiers"→"pricing-tiers"(lowercase)- Manual edit:
"Pricing-Tiers"→"Pricing-Tiers"(case preserved)Consider applying
.toLowerCase()to the manual edit path as well, or documenting this as intentional behavior.♻️ Optional: Apply consistent lowercase transformation
onChange={(e) => { - const value = sanitizeUserSpecifiedId(e.target.value); + const value = sanitizeUserSpecifiedId(e.target.value).toLowerCase(); setProductLineId(value); setHasManuallyEditedId(true); setErrors(prev => ({ ...prev, id: undefined })); }}apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts (1)
1260-1260: Test title still references "catalog" instead of "product line".The test title says "same catalog" but should say "same product line" to be consistent with the rename throughout the codebase.
Suggested fix
-it("should immediately cancel existing subscriptions when granting a product of same catalog", async ({ expect }) => { +it("should immediately cancel existing subscriptions when granting a product of same product line", async ({ expect }) => {apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
670-670: Minor: transition class may not work as expected.The
transition-colorsclass is applied but there's no hover state on this div. The opacity transition on the buttons inside (line 678) usesgroup-hover, but the parent div doesn't have thegroupclass.Suggested fix
<div key={itemId} - className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors" + className="px-3 py-3 hover:bg-muted/50 flex items-center justify-between transition-colors group" >apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (1)
633-645: Minor: consider normalizing/typingproductLineIdand avoiding!where easy.
Ifproduct.productLineIdcan ever benull/""(orproductcan be falsy), normalizing toundefined(and/or tightening types forgroups/paymentsGroups) will reduce edge-case drift. As per coding guidelines, prefer defensive patterns over non-null assertions when reasonable.Also applies to: 680-685, 708-710
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
314-320: Product line + add-on selection should be constrained bycustomerType(and ideally auto-filtered).
Right now, product lines are reset on customer type change (Line 280-282), but the Select still lists all product lines (Line 915-919). Also, the add-on parent picker isn’t filtered by customer type, only validated by productLine (Line 314-319). This can let users pick invalid combinations that later UIs prevent.As per coding guidelines, prefer preventing invalid user choices over surfacing toasts/errors later.
Also applies to: 895-925
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (3)
60-66:toIdFormathelper is fine, but it’s duplicated across multiple files.
Consider centralizing to avoid subtle divergence over time (you now have at least 3 copies).
2167-2204: Async edit/delete product line flows: consider error surfacing + loading.
These handlers call async callbacks and then toast success, but failures will currently surface poorly (or via a global boundary).As per coding guidelines, prefer
runAsynchronouslyWithAlert(or equivalent) for async button actions that can fail.Also applies to: 2206-2225
2455-2478: Deletion-by-move looks correct; confirmundefinedis the right “unset” value for persisted config.
You setproductLineId: undefinedwhen moving products to “No product line”. If the config layer expectsnullto clear fields (as in other parts of the codebase), you may want to normalize tonullor omit the key entirely. The inline comment explaining theas anyis good.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (30)
apps/backend/prisma/seed.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/catalogs/layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/config/schema.tspackages/stack-shared/src/schema-fields.tspackages/stack-shared/src/utils/jwt.tsxpackages/template/src/components-page/account-settings/payments/payments-panel.tsx
💤 Files with no reviewable changes (1)
- apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/catalogs/layout.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxpackages/stack-shared/src/config/schema.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tspackages/template/src/components-page/account-settings/payments/payments-panel.tsxpackages/stack-shared/src/schema-fields.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tspackages/stack-shared/src/utils/jwt.tsxapps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxpackages/stack-shared/src/config/schema.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tspackages/template/src/components-page/account-settings/payments/payments-panel.tsxpackages/stack-shared/src/schema-fields.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tspackages/stack-shared/src/utils/jwt.tsxapps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/pricing-section.tsxpackages/stack-shared/src/config/schema.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/lib/payments.test.tsxapps/backend/src/lib/payments.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tspackages/template/src/components-page/account-settings/payments/payments-panel.tsxpackages/stack-shared/src/schema-fields.tsapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.tspackages/stack-shared/src/utils/jwt.tsxapps/backend/prisma/seed.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
{**/apps-frontend.tsx,**/apps-config.ts}
📄 CodeRabbit inference engine (AGENTS.md)
To update the list of available apps, edit
apps-frontend.tsxandapps-config.ts. Check existing apps for inspiration when implementing new apps or pages
Files:
apps/dashboard/src/lib/apps-frontend.tsx
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Use.toMatchInlineSnapshotover other selectors in tests when possible, and check/modify snapshot-serializer.ts to understand how snapshots are formatted
Files:
apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tspackages/stack-shared/src/config/schema-fuzzer.test.tsapps/backend/src/lib/payments.test.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
packages/stack-shared/src/config/schema.ts
📄 CodeRabbit inference engine (AGENTS.md)
Whenever making backwards-incompatible changes to the config schema, update the migration functions in
packages/stack-shared/src/config/schema.ts
Files:
packages/stack-shared/src/config/schema.ts
🧠 Learnings (5)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to {**/apps-frontend.tsx,**/apps-config.ts} : To update the list of available apps, edit `apps-frontend.tsx` and `apps-config.ts`. Check existing apps for inspiration when implementing new apps or pages
Applied to files:
apps/dashboard/src/lib/apps-frontend.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Applied to files:
apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/products.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.tsapps/backend/src/lib/payments.test.tsxapps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.tsapps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`
Applied to files:
packages/stack-shared/src/config/schema.tspackages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/schema-fields.tsapps/backend/prisma/seed.ts
📚 Learning: 2026-01-11T06:44:57.542Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 1069
File: packages/template/src/components-page/onboarding.tsx:59-61
Timestamp: 2026-01-11T06:44:57.542Z
Learning: In the stack-auth codebase, Button components from stackframe/stack-ui (including those used via MessageCard props like primaryAction and secondaryAction) can accept async functions directly and automatically handle loading states with indicators. No need to wrap these with runAsynchronouslyWithAlert.
Applied to files:
packages/template/src/components-page/account-settings/payments/payments-panel.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
packages/template/src/components-page/account-settings/payments/payments-panel.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
🧬 Code graph analysis (10)
apps/backend/src/app/api/latest/payments/purchases/validate-code/route.ts (1)
apps/backend/src/lib/payments.tsx (1)
isActiveSubscription(308-310)
apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx (1)
apps/backend/src/lib/payments.tsx (1)
validatePurchaseSession(598-682)
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts (1)
packages/stack-shared/src/utils/errors.tsx (1)
StatusError(152-261)
apps/backend/src/lib/payments.tsx (2)
packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)typedKeys(304-306)packages/stack-shared/src/schema-fields.ts (1)
productSchema(624-647)
apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts (1)
apps/e2e/tests/helpers.ts (1)
it(12-12)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (2)
packages/stack-shared/src/schema-fields.ts (3)
isValidUserSpecifiedId(453-455)getUserSpecifiedIdErrorMessage(460-462)sanitizeUserSpecifiedId(469-477)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (8)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useProjectId(46-53)packages/stack-shared/src/utils/strings.tsx (1)
stringCompare(61-65)packages/stack-shared/src/schema-fields.ts (2)
isValidUserSpecifiedId(453-455)getUserSpecifiedIdErrorMessage(460-462)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsx (1)
PageClient(68-108)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (1)
PageClient(72-95)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (1)
PageClient(598-944)packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)typedFromEntries(281-283)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (15)
- GitHub Check: CodeQL analysis (javascript-typescript)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: all-good
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: docker
- GitHub Check: lint_and_build (latest)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
🔇 Additional comments (68)
packages/template/src/components-page/account-settings/payments/payments-panel.tsx (2)
5-8: LGTM!The
Resultimport is properly added and correctly used withResult.fromThrowingAsync()on line 389 for error handling in the plan-switching flow. Import ordering is clean.
380-380: LGTM!The terminology update from "catalog" to "product line" aligns with the PR objective. The text is properly internationalized via
t().packages/stack-shared/src/schema-fields.ts (1)
624-647: Schema field rename looks correct and is properly supported by migrations.The
productLineIdfield definition follows the established pattern with proper validation viauserSpecifiedIdSchemaand includes a clear OpenAPI description explaining the mutual exclusivity semantics.The migration function at lines 380-382 of
packages/stack-shared/src/config/schema.tscorrectly handles the backwards-incompatiblecatalogId→productLineIdrename for existing data, using therenamePropertyutility to update the config path.apps/dashboard/src/lib/apps-frontend.tsx (1)
150-167: LGTM! Navigation and description updates are consistent with the route structure.The changes correctly update both the navigation item (displayName to "Product Lines" and href to "./product-lines") and the store description text. The corresponding product-lines directory exists in the route structure, and no remaining "catalogs" references were found in the payments section.
packages/stack-shared/src/config/schema-fuzzer.test.ts (2)
67-82: LGTM! Good backward compatibility testing for migration paths.The fuzzer config correctly includes both the new
productLinesstructure withcustomerTypeand the legacycatalogs/groupsproperties to ensure migration functions work correctly. This is well-aligned with the schema migration strategy.
96-98: Migration coverage is appropriate.Including
catalogIdandgroupIdalongsideproductLineIdin the fuzzer ensures backward compatibility during the migration period.apps/backend/src/app/api/latest/payments/purchases/validate-code/route.ts (1)
73-91: LGTM! Product line conflict detection logic is correct.The refactored code properly:
- Looks up product lines from the tenancy config
- Filters conflicts by matching
productLineId- Preserves the API response contract with
conflicting_productsThe guard at line 76 (
if (productLineId)) correctly handles the case when a product has no product line assigned.apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--validate-code.test.ts (2)
1-63: Excellent backward compatibility test coverage.This test correctly validates that the migration layer properly handles legacy
catalogsandcatalogIdconfig properties. The test structure is clear with good comments explaining the purpose.Based on learnings, adding E2E tests for API changes is critical in this industry, and this test appropriately covers the backward compatibility path.
65-167: Comprehensive conflict detection test with legacy config.The test properly validates the complete flow:
- Setup with legacy
catalogs/catalogIdproperties- Purchase one product
- Validate that the second product correctly reports conflicts
This ensures the migration layer maintains functional correctness for conflict detection.
apps/backend/src/lib/payments.test.tsx (3)
61-62: LGTM! Manual change tests updated correctly.The test fixtures properly use
productLines: {}for scenarios without product line grouping.Also applies to: 104-106
146-159: LGTM! Subscription tests properly updated.The test fixtures correctly use
productLinesin the tenancy config andproductLineIdin product definitions.
913-914: LGTM! Response property assertions updated correctly.The tests properly verify the renamed response properties:
res.productLineId(wasres.catalogId)res.conflictingProductLineSubscriptions(wasres.conflictingCatalogSubscriptions)Also applies to: 951-952, 1064-1065
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts (2)
64-66: LGTM! Product line validation correctly updated.The validation properly checks
productLineIdand the error message is updated to use "product line" terminology consistently.
93-99: LGTM! One-time purchase check updated consistently.The variable rename to
hasOneTimeInProductLineand the updated error message maintain consistency with the broader nomenclature change.apps/backend/prisma/seed.ts (3)
115-119: Consistent rename fromcatalogstoproductLinesin internal project config.The payments configuration correctly migrates from
catalogstoproductLinesterminology, with all product definitions updated to useproductLineIdinstead ofcatalogId. The structure and semantics remain unchanged.Also applies to: 122-122, 143-143, 164-164, 179-179
817-817: Dummy payments setup correctly updated for productLine terminology.The
buildDummyPaymentsSetupfunction properly renames:
- Product definitions now use
productLineId(e.g.,'workspace','add_ons')- Top-level config key renamed from
catalogstoproductLinesConsistent with the broader refactor.
Also applies to: 845-845, 881-881, 908-915
1234-1239: Inline product seed data updated to useproductLineId.The inline product definitions in
seedDummyTransactions(legacy enterprise pilot and design audit pass) correctly useproductLineIdinstead ofcatalogId.Also applies to: 1409-1418
apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/route.ts (2)
49-70: Switch options grouping correctly migrated toproductLineId.The Map-based grouping logic properly renamed from
switchOptionsByCatalogIdtoswitchOptionsByProductLineId. The guard on line 55 and subsequent Map operations consistently useproduct.productLineId. Logic remains functionally identical.
76-80: Product mapping correctly referencesproductLineIdfor switch options.The final product mapping extracts
productLineIdand uses it to look up switch options from the grouped Map. Consistent with the upstream changes.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (2)
22-56: Component and state management correctly updated for Product Line.The component properly:
- Renames from
CreateCatalogDialogtoCreateProductLineDialog- Updates
onCreatecallback signature to useproductLineobject- Adds
hasManuallyEditedIdstate for tracking user intent- Validates using
productLineIdwith appropriate error messagesForm reset logic correctly clears all new state fields.
66-142: UI terminology and auto-ID generation properly implemented.The dialog correctly:
- Updates all labels and descriptions to "Product Line" terminology
- Auto-generates ID from display name when user hasn't manually edited
- Uses
runAsynchronouslyfor asynconCreatehandler per coding guidelinesapps/backend/src/lib/payments.tsx (5)
86-88: Inline product correctly returnsproductLineId: undefined.When creating an inline product (not from config),
productLineIdis correctly set toundefinedsince inline products don't belong to any product line.
318-375: Subscription retrieval logic correctly migrated toproductLines.The
getSubscriptionsfunction properly:
- Reads from
tenancy.config.payments.productLines- Tracks which product lines have DB subscriptions via
productLinesWithDbSubscriptionsSet- Iterates product lines to find and add default "include-by-default" products
- Maintains correct error message for multiple defaults in the same product line (line 356)
Logic remains functionally identical to the previous catalog-based implementation.
377-393: Ungrouped default detection correctly checksproductLineId === undefined.Products without a
productLineIdthat are "include-by-default" are correctly identified and added as default subscriptions.
609-681:validatePurchaseSessionreturn type and conflict detection updated.The function correctly:
- Returns
productLineIdandconflictingProductLineSubscriptionsinstead of catalog equivalents- Looks up product line from
tenancy.config.payments.productLines- Blocks purchases when one-time purchase exists in same product line
- Filters conflicting subscriptions based on
product.productLineIdAll terminology and logic consistent with the rename.
721-755:grantProductToCustomercorrectly uses renamed conflict subscriptions.The destructuring and conflict handling logic properly uses
conflictingProductLineSubscriptionsto cancel conflicting subscriptions in the same product line.apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx (1)
61-73: Purchase session handler correctly uses renamed conflict subscriptions.The destructuring and conflict handling logic properly uses
conflictingProductLineSubscriptionsfrom the updatedvalidatePurchaseSessionreturn type. Logic remains unchanged.apps/e2e/tests/backend/endpoints/api/v1/payments/products.test.ts (1)
1270-1316: LGTM!The product configuration correctly uses the new
productLinesandproductLineIdproperties. The test validates the expected behavior of products within the same product line.packages/stack-shared/src/config/schema.ts (4)
151-157: LGTM!The schema correctly renames
catalogstoproductLinesandcatalogIdtoproductLineId. The addition ofcustomerTypeto product lines with corresponding cross-field validation is a good enhancement.
169-191: LGTM!The cross-field validation ensures data integrity by:
- Verifying that referenced product lines exist
- Enforcing that product
customerTypematches its product line'scustomerTypeThe error messages are descriptive and helpful for debugging.
374-384: LGTM!The migration functions correctly handle the backward-incompatible schema changes:
- Renames
payments.catalogstopayments.productLines- Renames
payments.products.*.catalogIdtopayments.products.*.productLineIdThis follows the established migration pattern and ensures existing configurations continue to work. Based on learnings, this is the required approach for backwards-incompatible changes to the config schema.
585-591: LGTM!The default values are correctly updated to use the new
productLinesandproductLineIdnaming.apps/e2e/tests/backend/endpoints/api/v1/payments/before-catalog-to-product-line-rename/outdated--purchase-session.test.ts (3)
1-60: LGTM! Excellent backward compatibility coverage.This test correctly validates that the old
catalogsandcatalogIdconfiguration properties continue to work after the rename. The migration functions inschema.tsshould automatically convert these toproductLinesandproductLineId.
62-130: LGTM!This test validates that:
- Old
catalogs/catalogIdconfig is properly migrated- Product line grouping logic works correctly after migration
- Error messages use the new "product line" terminology
132-240: LGTM!The subscription switching test validates that the group-based switching logic continues to work correctly with the old configuration format after migration.
apps/e2e/tests/backend/endpoints/api/v1/payments/before-offer-to-product-rename/outdated--purchase-session.test.ts (1)
880-880: LGTM!The updated assertion correctly expects the new "product line" terminology in the error message, even when the test uses the old configuration format. This validates that the migration layer works correctly end-to-end.
apps/e2e/tests/backend/endpoints/api/v1/payments/switch-plans.test.ts (3)
4-13: LGTM!The
setupProductsfunction signature and implementation are correctly updated to useproductLinesinstead ofcatalogs.
16-63: LGTM!The test is correctly updated:
- Title reflects new "product line" terminology
- Configuration uses
productLineIdinstead ofcatalogId- Expected error message uses new terminology
65-106: LGTM!The test correctly uses
productLineIdfor both products, maintaining consistency with the rename.apps/e2e/tests/backend/endpoints/api/v1/payments/validate-code.test.ts (1)
157-176: LGTM! Consistent rename from catalog to product line terminology.The test configuration correctly updates
catalogs→productLinesandcatalogId→productLineIdfor bothproductAandproductB. Test logic and assertions remain unchanged.apps/e2e/tests/backend/endpoints/api/v1/payments/purchase-session.test.ts (5)
169-189: LGTM! Product line terminology correctly applied to group subscription test.The test configuration properly uses
productLinesandproductLineIdfor testing one-time purchases with conflicting group subscriptions.
641-675: LGTM! Consistent rename in subscription switching test.The product configuration correctly uses
productLinesandproductLineIdfor bothproductAandproductBwhen testing subscription updates within a group.
748-781: LGTM! DB-only subscription cancellation test updated correctly.Configuration and product definitions properly reflect the product line terminology.
911-932: LGTM! One-time purchase blocking test updated.The test correctly uses
productLinesandproductLineIdfor testing purchase blocking within the same group.
969-969: LGTM! Error message assertion updated to reflect product line terminology.The assertion correctly checks for the updated error message containing "one-time purchase in this product line".
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/product-dialog.tsx (7)
9-9: LGTM! Import updated to match renamed dialog component.
25-26: LGTM! Props interface updated with product line terminology.Both
existingProductsitem type andexistingProductLinesprop correctly reflect the rename.
65-65: LGTM! State variables renamed consistently.
productLineIdandshowProductLineDialogstates properly renamed from their catalog equivalents.Also applies to: 76-76
135-140: LGTM! Validation logic correctly updated for product line consistency.The add-on validation properly checks that all selected products share the same
productLineIdand shows an appropriate error message.
466-499: LGTM! UI labels and selection logic updated for product lines.The select component correctly uses "Product Line" terminology in labels, placeholders, and options.
568-572: LGTM! Add-on product display shows product line name.The display logic correctly looks up product line names from
existingProductLines.
770-778: LGTM! CreateProductLineDialog integration updated correctly.The dialog integration properly uses the renamed component and callback.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page.tsx (1)
3-5: LGTM! Page metadata title updated to reflect product line terminology.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/product-lines/page-client.tsx (3)
9-9: LGTM! Import updated to renamed view component.
17-17: LGTM! WelcomeScreen title and description updated with product line terminology.The descriptions clearly explain the product line concept and its relationship to products.
Also applies to: 51-56
100-105: LGTM! PageLayout and view component properly renamed.The title, description, and component reference consistently use product line terminology.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx (2)
357-370: Product grouping/rendering rename looks consistent (productLineId end-to-end).
The filter + render paths now consistently key byproductLineIdand fall back to “Other” for ungrouped.Also applies to: 388-436
818-829: WiringpaymentsConfig.productLines+existingProductLinesinto child components looks correct.
Prop renames are consistent and the page now passes product-line groups to both desktop and mobile layouts.Also applies to: 880-890, 913-927
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (2)
193-285: ResettingproductLineIdon customer type change is a good guardrail.
This keeps productLine selection consistent with the “product lines are customer-type-specific” constraint.
287-292: No changes required—the code pattern is correct. TheCreateProductLineDialog'sonCreatecontract explicitly accepts(productLine: {...}) => void | Promise<void>, andhandleCreateProductLinecorrectly returnsvoid. The dialog wraps the result withrunAsynchronously(), which handles bothvoidandPromise<void>return types uniformly via its signaturepromiseOrFunc: void | Promise<unknown> | (...) | undefined. This pattern is intentional and working as designed.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (3)
105-107:productLineNameresolution + header display look good.
The fallback to ID when displayName is missing is consistent and keeps the header stable.Also applies to: 216-221
259-283: Product line selection/create flow is coherent (incl.__none__sentinel).
Nice touch using__none__to avoid empty string Select values; handlers correctly map it tonullin config updates.Also applies to: 338-357, 417-429, 627-632
202-204: No action needed: route is already correct.The actual code at line 202 uses
/payments/product-lines#product-${productId}(with hyphens), which matches the existing directory structure. The review comment's snippet is inaccurate; the navigation route is not broken.Likely an incorrect or invalid review comment.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (5)
571-573: ProductLine metadata propagation (prompt + add-on candidate filtering) looks correct.
This keeps generated docs and add-on constraints aligned with the productLine model.Also applies to: 850-852
1512-1689: Regrouping by(productLineId, customerType)is a solid model for “type-specific” product lines.
The keying and sorting logic is clear and should keep the UI deterministic across customer types.
1716-1753: Edit/delete affordances for product lines are implemented cleanly (hover-revealed actions).
The empty product line rendering + controls is also a nice completeness touch.Also applies to: 1990-2016
2386-2418: Double-check semantics: deleting the last product also deletes its product line.
Given this file now explicitly supports empty product lines (including an empty-product-line section + edit/delete), auto-deleting the product line on last product deletion may be surprising. If intentional, it might be worth a comment in code / UI copy explaining it.
2502-2505:existingProductLinesmapping intoProductDialoglooks consistent with the new naming.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
.../src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-list-view.tsx
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
331-360: Replace manual try-catch withrunAsynchronouslyWithAlertfor consistent error handling.The
handleSavefunction uses a try-catch block withtoastfor error notification. As per coding guidelines, you should never try-catch-all and never usetoastfor blocking alerts and errors. Instead, userunAsynchronouslyWithAlertwhich provides consistent error handling.♻️ Suggested refactor
const handleSave = async () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setIsSaving(true); - try { + await runAsynchronouslyWithAlert(async () => { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { - setIsSaving(false); - } + }).finally(() => { + setIsSaving(false); + }); };
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx:
- Around line 202-203: Change the menu item label text to use a human-readable
form: update the DropdownMenuItem that calls
router.push(`/projects/${projectId}/payments/product-lines#product-${productId}`)
so its display string is "View in Product Lines" (two words) instead of "View in
ProductLines"; keep the router.push call and URL unchanged and only modify the
visible text inside the DropdownMenuItem component.
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx:
- Line 2162: Replace the inconsistent label "Create ProductLine" with "Create
Product Line" in the component that renders the create button in
page-client-product-lines-view (search for the literal "Create ProductLine" in
page-client-product-lines-view.tsx and update the JSX/label or translation key
used by the button to the spaced form); if the label comes from a translation
key, update the corresponding copy/value instead of the inline string so it
remains consistent across the UI.
♻️ Duplicate comments (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (2)
2019-2021: Query parameterproductLineIdmay not be read by the target page.This link passes
productLineIdas a query parameter, but based on prior review comments, the receiving page (/payments/products/new/page-client.tsx) may not be reading this parameter. Please verify the target page usesuseSearchParamsto read and apply this value for pre-selecting the product line.#!/bin/bash # Check if the new product page reads the productLineId query parameter rg -n "productLineId|useSearchParams" "apps/dashboard/src/app/(main)/(protected)/projects/\[projectId\]/payments/products/new/" -A 3 -B 1
2438-2443: Product line displayName will be wiped when saving product with group.As noted in a prior review, the
overridefunction replaces the entire object at a given key path rather than merging. Settingpayments.productLines.${productLineId}to{}will wipe thedisplayNameproperty that was set during product line creation.Either remove this line entirely if the product line already exists, or preserve the displayName:
Proposed fix
onSaveProductWithGroup={async (productLineId, productId, product) => { + // Get existing productLine data to preserve displayName + const existingProductLine = paymentsConfig.productLines[productLineId]; await project.updateConfig({ - [`payments.productLines.${productLineId}`]: {}, + [`payments.productLines.${productLineId}`]: existingProductLine ?? {}, [`payments.products.${productId}`]: product, }); toast({ title: "Product created" }); }}Or if the product line should already exist before this is called, simply remove the productLines update:
onSaveProductWithGroup={async (productLineId, productId, product) => { await project.updateConfig({ - [`payments.productLines.${productLineId}`]: {}, [`payments.products.${productId}`]: product, }); toast({ title: "Product created" }); }}apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (2)
564-565: Minor UI copy: Use "product line" (two words) in dialog description.The user-facing text should read "in the same product line" instead of "in the same productLine".
589-589: Minor UI copy: Use "product line" (two words) in empty state message.Should read "same customer type and product line" instead of "same customer type and productLine".
🧹 Nitpick comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
382-384: Navigation change from router towindow.history.back()is intentional but loses type safety.Using
window.history.back()means the user could navigate to an external page if they entered this page directly. Consider using the router with a fallback:💡 Optional: Add fallback for direct page access
const handleCancel = () => { - window.history.back(); + if (window.history.length > 1) { + window.history.back(); + } else { + router.push(`/projects/${projectId}/payments/products`); + } };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
AGENTS.mdapps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Use.toMatchInlineSnapshotover other selectors in tests when possible, and check/modify snapshot-serializer.ts to understand how snapshots are formatted
Files:
apps/backend/src/lib/payments.test.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
apps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
apps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
apps/backend/src/lib/payments.test.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
🧠 Learnings (7)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Applied to files:
apps/backend/src/lib/payments.test.tsx
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to {**/apps-frontend.tsx,**/apps-config.ts} : To update the list of available apps, edit `apps-frontend.tsx` and `apps-config.ts`. Check existing apps for inspiration when implementing new apps or pages
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Code defensively using `?? throwErr(...)` instead of non-null assertions, with good error messages explicitly stating violated assumptions
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use ES6 maps instead of records wherever possible
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead of general try-catch error handling
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't use `Date.now()` for measuring elapsed (real) time; instead use `performance.now()`
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Try to avoid the `any` type. When using `any`, leave a comment explaining why and how the type system fails or how errors would still be caught
Applied to files:
AGENTS.md
🧬 Code graph analysis (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (6)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useProjectId(46-53)packages/stack-shared/src/utils/strings.tsx (1)
stringCompare(61-65)packages/stack-shared/src/utils/urls.tsx (1)
urlString(314-316)packages/stack-shared/src/schema-fields.ts (2)
isValidUserSpecifiedId(453-455)getUserSpecifiedIdErrorMessage(460-462)packages/stack-shared/src/utils/objects.tsx (2)
typedEntries(263-265)typedFromEntries(281-283)
⏰ 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). (12)
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: setup-tests
- GitHub Check: lint_and_build (latest)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
🔇 Additional comments (28)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (4)
289-294: LGTM!The
handleCreateProductLinefunction correctly usesrunAsynchronouslyWithAlertfor async error handling, and the rename fromhandleCreateCatalogtohandleCreateProductLinewith the updated config pathpayments.productLines.{id}is consistent with the PR objectives.
315-321: LGTM!The validation logic correctly checks that all selected add-on products belong to the same product line, with the error message properly updated to reference "product line" instead of "catalog".
897-927: LGTM!The product line dropdown is correctly implemented with:
- Proper state binding to
productLineId- Sentinel value
'no-product-line'for the empty state- Iteration over
paymentsConfig.productLines- Create new option triggering the dialog
The rename from catalog terminology is complete and consistent.
991-995: LGTM!The
CreateProductLineDialogcomponent is correctly wired up with the renamed state and handler props, consistent with the external snippet showing the dialog's expected interface.apps/backend/src/lib/payments.test.tsx (6)
61-62: Consistent terminology update across test fixtures.The rename from
catalogs→productLinesandcatalogId→productLineIdis applied consistently across the manual changes and subscription test cases. The mock tenancy configurations correctly use the new property names.Also applies to: 105-106, 150-153, 188-191, 227-230
913-914: Property accessors correctly updated.The assertions accessing
res.productLineIdandres.conflictingProductLineSubscriptionsreflect the renamed return type properties fromvalidatePurchaseSession.Also applies to: 951-952, 1064-1065
817-846: Test correctly validates duplicate one-time purchase blocking.The mock data at line 820 correctly uses
productLineId: undefinedfor the existing purchase, and thecodeData.productat line 834 also usesproductLineId: undefined. The test logic is sound for validating that duplicate purchases for the sameproductIdare blocked.
849-880: Test validates one-time purchase blocking within the same product line.The mock at line 853 correctly sets
productLineId: 'g1'for an existing purchase, and the test product at line 867 also usesproductLineId: 'g1'. This correctly tests the scenario where a purchase should be blocked when another exists in the same product line.
879-879: Error message correctly matches implementation.The test assertion expects
'Customer already has a one-time purchase in this product line', which matches the implementation inpayments.tsxat line 665. The fix is correct.
1167-1167: Test description and error message match implementation.The test name at line 1167 correctly describes the scenario as "same line", and the error message at line 1206 expects
'Multiple include-by-default products configured in the same product line', which matches the error thrown in the implementation at line 356 of payments.tsx.AGENTS.md (1)
89-89: Good addition for URL safety best practice.The
urlStringtagged template literal utility is a sensible addition to the coding guidelines, as it automatically encodes interpolated values usingencodeURIComponent(), preventing URL encoding issues and potential injection vulnerabilities. Recommending its use (or plainencodeURIComponent()as a fallback) for URL construction promotes both safety and consistency across the codebase.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (8)
61-67: LGTM - Helper function for ID generationThe
toIdFormatfunction correctly handles basic slug generation. It converts to lowercase, replaces non-alphanumeric sequences with hyphens, and trims leading/trailing hyphens.
572-574: LGTM - Consistent rename in prompt generationThe prompt generation correctly uses
productLineIdinstead of the oldcatalogId.
851-851: LGTM - Filter updated to use productLineIdThe add-on filter logic correctly uses
productLineIdfor determining which products could be add-ons.
1496-1521: LGTM - Type definitions updated consistentlyThe type renames and new callback props for CRUD operations on product lines are well-structured.
1527-1536: LGTM - State variables for product line managementThe new state variables for managing product line editing and deletion are well-organized.
2168-2226: LGTM - Product line edit and delete dialogsThe dialogs for editing and deleting product lines are well-implemented with proper state management and clear user messaging about the consequences of deletion.
2455-2479: LGTM - Product line deletion with product migrationThe deletion logic correctly moves products to "No product line" before removing the product line. The
as anycast is justified by the comment explaining the dynamic nature of the config update.
2231-2237: LGTM - PageClient component setupThe component correctly uses the renamed types and props for the product line view.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx (9)
56-56: LGTM!Import correctly updated to use the renamed
CreateProductLineDialogcomponent.
105-106: LGTM!The product line derivation correctly handles the lookup with proper null checks and fallback behavior.
135-141: LGTM!Props and function signature correctly updated to use
productLineNamewith proper nullable typing.
216-219: LGTM!Product line display correctly uses human-readable "Product Line:" label and properly handles the null case.
259-282: LGTM!The
productLineOptionsderivation correctly:
- Determines customer type compatibility from existing products in each product line
- Disables incompatible options with clear explanatory text
- Uses
__none__sentinel value for "No product line" option- Has complete dependency array
304-316: LGTM!The filter correctly uses
productLineIdfor matching products in the same product line, with proper dependencies.
338-357: LGTM!Both handlers correctly implement the product line operations:
handleProductLineUpdateproperly converts the__none__sentinel tonullhandleCreateProductLinesequentially creates the product line and assigns the product
417-429: LGTM!Grid item correctly configured with proper user-facing labels, tooltip, and the "Create new product line" action.
627-632: LGTM!
CreateProductLineDialogcorrectly integrated with proper state management and callbacks.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...rc/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
Outdated
Show resolved
Hide resolved
...(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
Outdated
Show resolved
Hide resolved
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
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx:
- Around line 387-389: The handleCancel function uses window.history.back()
which fails when the page is opened directly; update handleCancel to check
history length and fall back to a deterministic navigation to the products list
(e.g., if (window.history.length > 1) window.history.back(); else
router.push(`/projects/${projectId}/payments/products`)). Use the existing
projectId value and your router/navigation helper (e.g., next/navigation's
useRouter or a navigate function) to implement the fallback so cancel always
returns the user to the products list.
In `@packages/stack-shared/src/config/schema-fuzzer.test.ts`:
- Around line 97-99: productLineId (and legacy catalogId/groupId) values include
"some-other-product-line-id" but the productLines fuzzer config only defines
"some-product-line-id", causing potential invalid references during cross-field
validation; update the productLines fuzzer configuration to also include
"some-other-product-line-id" (or alternatively change the
productLineId/catalogId/groupId arrays to only reference existing IDs) so
generated products always reference defined product lines; locate symbols
productLineId, catalogId, groupId and the productLines fuzzer entries in
schema-fuzzer.test.ts to apply the fix.
In `@packages/stack-shared/src/config/schema.ts`:
- Around line 388-411: The current inference loop treats res as flat
dot-notation keys and thus never finds keys like
"payments.products.{id}.customerType"; replace this logic with the same nested
traversal utility used elsewhere (e.g., use mapProperty/renameProperty) to
iterate payments.products and collect productLineId -> customerType, or
explicitly traverse res.payments.products and res.payments.productLines objects
to build productLineCustomerTypes and then set missing
payments.productLines.{id}.customerType; target symbols: res,
productLineCustomerTypes, mapProperty/renameProperty, payments.products,
payments.productLines.
♻️ Duplicate comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2162-2162: Inconsistent button text casing.The button text "Create ProductLine" should be "Create Product Line" (with a space) for consistency with the rest of the UI copy.
Proposed fix
- Create ProductLine + Create Product Linepackages/stack-shared/src/config/schema.ts (1)
152-158: Fix backwards compatibility for empty productLines in migration.The
customerTypefield is required in the schema but will remainundefinedafter migration for product lines without associated products. The migration at lines 388-410 only inferscustomerTypefrom products that reference the product line, leaving empty productLines unable to satisfy schema validation.Consider either:
- Detecting old configs with empty catalogs and assigning them a default
customerType, or- Making
customerTypeoptional in the schema to gracefully handle this edge case.
🧹 Nitpick comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (1)
2130-2132: Consider user-friendly error message.The
getUserSpecifiedIdErrorMessage("productLineId")may produce a technical error message. Consider whether this message will be clear to dashboard users.#!/bin/bash # Check what error message is generated for productLineId rg -n "getUserSpecifiedIdErrorMessage" --type ts -A 3 | head -40apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
336-365: Replace try-catch-all withrunAsynchronouslyWithAlertfor proper error handling.Per coding guidelines, this code has two issues:
- Uses try-catch-all pattern which should be avoided
- Uses
toastfor errors (line 361) when alerts should be used for blocking errors♻️ Suggested refactor using runAsynchronouslyWithAlert
const handleSave = async () => { const validationErrors = validateForm(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setIsSaving(true); - try { + runAsynchronouslyWithAlert(async () => { const product: Product = { displayName, customerType, productLineId: productLineId || undefined, isAddOnTo: isAddOn ? Object.fromEntries(isAddOnTo.map(id => [id, true])) : false, stackable, prices: freeByDefault ? 'include-by-default' : prices, includedItems, serverOnly, freeTrial, }; await project.updateConfig({ [`payments.products.${productId}`]: product }); toast({ title: "Product created" }); router.push(`/projects/${projectId}/payments/products`); - } catch (error) { - toast({ title: "Failed to create product", variant: "destructive" }); - } finally { + }).finally(() => { setIsSaving(false); - } + }); };
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
AGENTS.mdREADME.mdapps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.tsapps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/config/schema.ts
✅ Files skipped from review due to trivial changes (1)
- README.md
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/backend/src/app/api/latest/payments/products/[customer_type]/[customer_id]/switch/route.ts
- apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/[productId]/page-client.tsx
- apps/backend/src/app/api/latest/payments/purchases/purchase-session/route.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.{ts,tsx,js,jsx}: Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Use.toMatchInlineSnapshotover other selectors in tests when possible, and check/modify snapshot-serializer.ts to understand how snapshots are formatted
Files:
packages/stack-shared/src/config/schema-fuzzer.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: For blocking alerts and errors, never usetoast, as they are easily missed by the user. Instead, use alerts
Keep hover/click transitions snappy and fast without pre-transition delays (e.g., no fade-in when hovering a button). Apply transitions after the action, like smooth fade-out when hover ends
NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. UserunAsynchronouslyorrunAsynchronouslyWithAlertinstead of general try-catch error handling
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g.,transition-colors hover:transition-none)
Don't useDate.now()for measuring elapsed (real) time; instead useperformance.now()
Use ES6 maps instead of records wherever possible
Files:
packages/stack-shared/src/config/schema-fuzzer.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., useusePathnameinstead ofawait params)
Code defensively using?? throwErr(...)instead of non-null assertions, with good error messages explicitly stating violated assumptions
Try to avoid theanytype. When usingany, leave a comment explaining why and how the type system fails or how errors would still be caught
Files:
packages/stack-shared/src/config/schema-fuzzer.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
{.env*,**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (AGENTS.md)
All environment variables should be prefixed with
STACK_(orNEXT_PUBLIC_STACK_if public) to ensure Turborepo picks up changes and improve readability
Files:
packages/stack-shared/src/config/schema-fuzzer.test.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsxpackages/stack-shared/src/config/schema.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
packages/stack-shared/src/config/schema.ts
📄 CodeRabbit inference engine (AGENTS.md)
Whenever making backwards-incompatible changes to the config schema, update the migration functions in
packages/stack-shared/src/config/schema.ts
Files:
packages/stack-shared/src/config/schema.ts
🧠 Learnings (9)
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to packages/stack-shared/src/config/schema.ts : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`
Applied to files:
packages/stack-shared/src/config/schema-fuzzer.test.tspackages/stack-shared/src/config/schema.ts
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Code defensively using `?? throwErr(...)` instead of non-null assertions, with good error messages explicitly stating violated assumptions
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use ES6 maps instead of records wherever possible
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : NEVER try-catch-all, NEVER void a promise, and NEVER .catch(console.error). Use loading indicators for async operations. Use `runAsynchronously` or `runAsynchronouslyWithAlert` instead of general try-catch error handling
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : Try to avoid the `any` type. When using `any`, leave a comment explaining why and how the type system fails or how errors would still be caught
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Don't use `Date.now()` for measuring elapsed (real) time; instead use `performance.now()`
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Always add new E2E tests when changing the API or SDK interface, erring on the side of creating too many tests due to the critical nature of the industry
Applied to files:
AGENTS.md
📚 Learning: 2026-01-13T18:14:29.974Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-13T18:14:29.974Z
Learning: Applies to **/*.{ts,tsx} : NEVER use Next.js dynamic functions if you can avoid them. Prefer using client components to keep pages static (e.g., use `usePathname` instead of `await params`)
Applied to files:
AGENTS.md
📚 Learning: 2025-12-03T07:19:44.433Z
Learnt from: madster456
Repo: stack-auth/stack-auth PR: 1040
File: packages/stack-shared/src/interface/crud/oauth-providers.ts:62-87
Timestamp: 2025-12-03T07:19:44.433Z
Learning: In packages/stack-shared/src/interface/crud/oauth-providers.ts and similar CRUD files, the tag "Oauth" (not "OAuth") is the correct capitalization format as it's used by the documentation generation system and follows OpenAPI conventions.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/utils.ts (1)
Product(9-9)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useProjectId(46-53)packages/stack-shared/src/utils/urls.tsx (1)
urlString(314-316)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
PageClient(178-1031)
packages/stack-shared/src/config/schema.ts (2)
packages/stack-shared/src/schema-fields.ts (2)
yupRecord(283-322)yupObject(247-251)packages/stack-shared/src/utils/objects.tsx (1)
getOrUndefined(545-548)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/create-product-line-dialog.tsx (1)
CreateProductLineDialog(22-143)
⏰ 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). (13)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: Cursor Bugbot
- GitHub Check: setup-tests-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests
- GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
🔇 Additional comments (21)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-product-lines-view.tsx (9)
51-67: LGTM!The
urlStringimport aligns with the coding guideline to preferurlString/encodeURIComponentfor URLs. ThetoIdFormathelper correctly converts display names to valid ID format.
1496-1521: LGTM!The
ProductLineViewPropstype correctly includescustomerTypeinonCreateProductLine, and theProductLineTypeKeytype provides proper grouping semantics.
2019-2021: LGTM!The
productLineIdquery parameter is correctly passed usingurlStringfor proper URL encoding. Per the relevant code snippets, the receiving page (new/page-client.tsx) reads this parameter viasearchParams.get("productLineId").
2438-2448: LGTM!The
onSaveProductWithGrouphandler correctly updates only the product without overwriting the product line. TheonCreateProductLinehandler properly includescustomerTypein the saved data.
2454-2478: LGTM!The
onDeleteProductLinehandler correctly moves products to "No product line" before deletion. Theas anycast includes appropriate justification per coding guidelines.
1716-1755: LGTM!The product line header with hover-to-reveal edit/delete buttons follows good UX patterns. The transition classes correctly use
hover:transition-noneper coding guidelines for snappy hover interactions.
2168-2227: LGTM!The edit and delete dialogs are well-implemented with proper state management and appropriate use of toast for success feedback.
2386-2423: LGTM!The
handleDeleteProductfunction correctly handles cascading deletion of empty product lines and uses proper immutable update patterns withtypedFromEntries/typedEntries.
1653-1666: LGTM!The
emptyProductLinescomputation correctly identifies product lines without any products for rendering empty state UI.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx (7)
33-36: LGTM! Import changes align with product line terminology.The
useSearchParamsusage is appropriate here since this is a client component, which is the preferred approach per coding guidelines for keeping pages static.
240-246: LGTM! Property mapping correctly updated.
289-299: LGTM! Handler correctly usesrunAsynchronouslyWithAlert.The config path update to
payments.productLines.${productLine.id}aligns with the backend schema changes.
320-327: LGTM! Validation logic correctly enforces product line consistency.The check ensures all selected add-on products belong to the same product line, which maintains the mutual exclusivity constraint.
428-439: LGTM! Code generation correctly reflects product line terminology.
902-932: LGTM! Product line selector UI correctly implemented.The select component properly handles the three cases: no product line, existing product lines, and creating a new one. Terminology is consistent throughout.
996-1000: LGTM! Dialog correctly integrated with product line creation flow.AGENTS.md (1)
89-91: LGTM!These new guidelines are clear, actionable, and align well with defensive coding practices. The path notation guidance (line 90) is particularly relevant for the config migration changes in this PR.
packages/stack-shared/src/config/schema.ts (3)
15-15: LGTM!The
getOrUndefinedimport is correctly added to support the new cross-field validation logic.
170-193: LGTM!The cross-field validation is well-implemented:
- Safely handles missing
productLineIdby continuing- Provides clear error messages with specific IDs for debugging
- Uses
getOrUndefineddefensively to check productLine existence before accessing properties
613-619: LGTM!The defaults are correctly updated to reflect the rename. The
customerType: undefineddefault is appropriate since this field must be explicitly set when creating new product lines.packages/stack-shared/src/config/schema-fuzzer.test.ts (1)
68-83: LGTM!Excellent test coverage for the migration paths. The fuzzer config correctly includes:
- New
productLinesstructure withcustomerType(lines 68-73)- Legacy
catalogsandgroupsstructures (lines 74-83) to ensure migrations workThis ensures the fuzzer exercises both fresh configs and migrated legacy configs.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const handleCancel = () => { | ||
| router.push(`/projects/${projectId}/payments/products`); | ||
| window.history.back(); | ||
| }; |
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.
Verify behavior when page is opened directly without history.
window.history.back() will do nothing (or navigate away from the app) if the user opened this page directly via URL or in a new tab. Consider adding a fallback to navigate to the products list.
🛠️ Suggested fix with fallback
const handleCancel = () => {
- window.history.back();
+ if (window.history.length > 1) {
+ window.history.back();
+ } else {
+ router.push(`/projects/${projectId}/payments/products`);
+ }
};🤖 Prompt for AI Agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/payments/products/new/page-client.tsx
around lines 387 - 389, The handleCancel function uses window.history.back()
which fails when the page is opened directly; update handleCancel to check
history length and fall back to a deterministic navigation to the products list
(e.g., if (window.history.length > 1) window.history.back(); else
router.push(`/projects/${projectId}/payments/products`)). Use the existing
projectId value and your router/navigation helper (e.g., next/navigation's
useRouter or a navigate function) to implement the fallback so cancel always
returns the user to the products list.
| productLineId: ["some-product-line-id", "some-other-product-line-id"], | ||
| catalogId: ["some-product-line-id", "some-other-product-line-id"], // ensure migration works | ||
| groupId: ["some-product-line-id", "some-other-product-line-id"], // ensure migration works |
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.
Potential fuzzer inconsistency: some-other-product-line-id is referenced but not defined.
The productLineId (and legacy catalogId/groupId) fields reference "some-other-product-line-id", but the productLines fuzzer config (lines 68-73) only defines "some-product-line-id". This could cause the cross-field validation to fail during fuzzing when a product references a non-existent product line.
Consider adding "some-other-product-line-id" to the productLines fuzzer config, or verify this is intentional to test error handling.
Suggested fix
productLines: [{
"some-product-line-id": [{
displayName: ["Some Product Line", "Some Other Product Line"],
customerType: ["user", "team", "custom"] as const,
}],
+ "some-other-product-line-id": [{
+ displayName: ["Another Product Line"],
+ customerType: ["user", "team", "custom"] as const,
+ }],
}],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| productLineId: ["some-product-line-id", "some-other-product-line-id"], | |
| catalogId: ["some-product-line-id", "some-other-product-line-id"], // ensure migration works | |
| groupId: ["some-product-line-id", "some-other-product-line-id"], // ensure migration works | |
| productLines: [{ | |
| "some-product-line-id": [{ | |
| displayName: ["Some Product Line", "Some Other Product Line"], | |
| customerType: ["user", "team", "custom"] as const, | |
| }], | |
| "some-other-product-line-id": [{ | |
| displayName: ["Another Product Line"], | |
| customerType: ["user", "team", "custom"] as const, | |
| }], | |
| }], |
🤖 Prompt for AI Agents
In `@packages/stack-shared/src/config/schema-fuzzer.test.ts` around lines 97 - 99,
productLineId (and legacy catalogId/groupId) values include
"some-other-product-line-id" but the productLines fuzzer config only defines
"some-product-line-id", causing potential invalid references during cross-field
validation; update the productLines fuzzer configuration to also include
"some-other-product-line-id" (or alternatively change the
productLineId/catalogId/groupId arrays to only reference existing IDs) so
generated products always reference defined product lines; locate symbols
productLineId, catalogId, groupId and the productLines fuzzer entries in
schema-fuzzer.test.ts to apply the fix.
| // BEGIN 2026-01-14: infer productLines.*.customerType from products if missing | ||
| if (isBranchOrHigher) { | ||
| // Build a map of productLineId -> customerType from products | ||
| const productLineCustomerTypes = new Map<string, string>(); | ||
| for (const [key, value] of Object.entries(res)) { | ||
| const path = key.split("."); | ||
| if (path.length === 4 && path[0] === "payments" && path[1] === "products" && path[3] === "customerType") { | ||
| const productId = path[2]; | ||
| const productLineIdKey = `payments.products.${productId}.productLineId`; | ||
| const productLineId = res[productLineIdKey]; | ||
| if (productLineId && typeof value === "string") { | ||
| productLineCustomerTypes.set(productLineId, value); | ||
| } | ||
| } | ||
| } | ||
| // Apply inferred customerType to productLines that don't have one | ||
| for (const [productLineId, customerType] of productLineCustomerTypes) { | ||
| const customerTypeKey = `payments.productLines.${productLineId}.customerType`; | ||
| if (!(customerTypeKey in res)) { | ||
| res[customerTypeKey] = customerType; | ||
| } | ||
| } | ||
| } | ||
| // END |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find migrateConfigOverride function calls and how configs are structured
rg -n "migrateConfigOverride\|flattenConfigObject\|Object\.entries" packages/stack-shared/src/config/schema.ts -B3 -A3 | head -100Repository: stack-auth/stack-auth
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Look for the migrateConfigOverride function signature and how it processes inputs
rg -n "function migrateConfigOverride\|const migrateConfigOverride" packages/stack-shared/src/config/ -A20 | head -80Repository: stack-auth/stack-auth
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Search for test files or examples showing config structure format
fd -e test.ts -e spec.ts packages/stack-shared/src/config/ | head -5Repository: stack-auth/stack-auth
Length of output: 502
🏁 Script executed:
#!/bin/bash
# Check file size
wc -l packages/stack-shared/src/config/schema.tsRepository: stack-auth/stack-auth
Length of output: 112
🏁 Script executed:
#!/bin/bash
# Read the migrateConfigOverride function and surrounding context starting from line 300
sed -n '300,420p' packages/stack-shared/src/config/schema.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 5637
🏁 Script executed:
#!/bin/bash
# Search for where migrateConfigOverride is called/exported
rg -n "migrateConfigOverride|export.*migrate" packages/stack-shared/src/config/schema.ts | head -20Repository: stack-auth/stack-auth
Length of output: 208
🏁 Script executed:
#!/bin/bash
# Read helper functions like mapProperty and renameProperty
sed -n '118,300p' packages/stack-shared/src/config/schema.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 7567
🏁 Script executed:
#!/bin/bash
# Search for function definitions related to property manipulation
rg -n "^function|^const.*=.*function|^const.*=.*=>" packages/stack-shared/src/config/schema.ts | grep -E "mapProperty|renameProperty|flattenConfigObject"Repository: stack-auth/stack-auth
Length of output: 348
🏁 Script executed:
#!/bin/bash
# Read the mapProperty function implementation
sed -n '421,470p' packages/stack-shared/src/config/schema.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 3048
🏁 Script executed:
#!/bin/bash
# Read the renameProperty function implementation
sed -n '451,520p' packages/stack-shared/src/config/schema.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 4561
🏁 Script executed:
#!/bin/bash
# Check what typedEntries does
rg -n "function typedEntries|const typedEntries" packages/stack-shared/src/config/schema.ts -A10 | head -30Repository: stack-auth/stack-auth
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Look for where migrateConfigOverride is called to see the input format
rg -n "migrateConfigOverride\(" packages/stack-shared/src/ -B3 -A3 --type=ts | head -80Repository: stack-auth/stack-auth
Length of output: 3804
🏁 Script executed:
#!/bin/bash
# Check the createFuzzerInput to see what format configs are in
rg -n "createFuzzerInput|function createFuzzerInput" packages/stack-shared/src/config/schema-fuzzer.test.ts -B5 -A15 | head -60Repository: stack-auth/stack-auth
Length of output: 2770
🏁 Script executed:
#!/bin/bash
# Check the config override types to understand their structure
rg -n "NormalizedOverride|ConfigNormalizedOverride" packages/stack-shared/src/config/schema.ts | head -20Repository: stack-auth/stack-auth
Length of output: 1689
🏁 Script executed:
#!/bin/bash
# Look for examples of how config objects are passed/used in migrations
rg -n "typedEntries" packages/stack-shared/src/ -A5 --type=ts | head -40Repository: stack-auth/stack-auth
Length of output: 3522
🏁 Script executed:
#!/bin/bash
# Check if normalize/flatten happens to configs before migration
rg -n "normalize\|flatten" packages/stack-shared/src/config/format.ts -B2 -A5 | head -80Repository: stack-auth/stack-auth
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Look at the fuzzer test more carefully to understand config structure
sed -n '236,275p' packages/stack-shared/src/config/schema-fuzzer.test.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 2086
🏁 Script executed:
#!/bin/bash
# Inspect what format.ts defines for config structure
head -100 packages/stack-shared/src/config/format.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 4705
🏁 Script executed:
#!/bin/bash
# Check the set function to understand how keys are stored
rg -n "^function set\|^const set" packages/stack-shared/src/utils/objects.ts -A15 | head -50Repository: stack-auth/stack-auth
Length of output: 147
🏁 Script executed:
#!/bin/bash
# Look at the actual structure of object utilities
sed -n '1,80p' packages/stack-shared/src/utils/objects.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 150
🏁 Script executed:
#!/bin/bash
# Find where objects utilities are
find packages/stack-shared -name "*objects*" -type fRepository: stack-auth/stack-auth
Length of output: 108
🏁 Script executed:
#!/bin/bash
# Search for the set function definition
rg -n "^function set\|^const set" packages/stack-shared -A10 | head -50Repository: stack-auth/stack-auth
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Read the objects utilities file
head -150 packages/stack-shared/src/utils/objects.tsx | cat -nRepository: stack-auth/stack-auth
Length of output: 7697
🏁 Script executed:
#!/bin/bash
# Search for the set function in the tsx file
rg -n "function set|export.*set" packages/stack-shared/src/utils/objects.tsx -A10 | head -80Repository: stack-auth/stack-auth
Length of output: 848
🏁 Script executed:
#!/bin/bash
# Look at the test more carefully to see what structure is actually passed
sed -n '50,100p' packages/stack-shared/src/config/schema-fuzzer.test.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 2294
🏁 Script executed:
#!/bin/bash
# Check if there's any specific migration test for the customerType inference
rg -n "customerType\|productLines" packages/stack-shared/src/config/schema.ts | grep -A5 -B5 "infer\|migration"Repository: stack-auth/stack-auth
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Read the part of mapProperty and renameProperty that shows they flatten nested objects
sed -n '420,450p' packages/stack-shared/src/config/schema.ts | cat -nRepository: stack-auth/stack-auth
Length of output: 1873
The customerType inference logic will not work because it assumes flat dot-notation keys but receives nested objects.
After all prior migrations using mapProperty and renameProperty, the config object res maintains its nested structure (e.g., { payments: { products: { "prod-1": { customerType: "user" } } } }). The new inference code iterates Object.entries(res) expecting flat keys like "payments.products.prod-1.customerType", but it only receives top-level keys (["payments"]). The path length check at line 95 requiring path.length === 4 will never be satisfied, so no customer types will be inferred.
Use mapProperty or renameProperty instead to correctly traverse the nested structure, similar to the earlier migrations in this function.
🤖 Prompt for AI Agents
In `@packages/stack-shared/src/config/schema.ts` around lines 388 - 411, The
current inference loop treats res as flat dot-notation keys and thus never finds
keys like "payments.products.{id}.customerType"; replace this logic with the
same nested traversal utility used elsewhere (e.g., use
mapProperty/renameProperty) to iterate payments.products and collect
productLineId -> customerType, or explicitly traverse res.payments.products and
res.payments.productLines objects to build productLineCustomerTypes and then set
missing payments.productLines.{id}.customerType; target symbols: res,
productLineCustomerTypes, mapProperty/renameProperty, payments.products,
payments.productLines.
Note
Refactors the payments domain to use product lines instead of catalogs throughout backend and dashboard.
catalogId->productLineId,catalogs->productLinesin seed, config, payments lib, and API routes (switching, product listing, purchase session/validation); update error messages and in-memory maps accordinglycatalog(s)config still works via schema migrationWritten by Cursor Bugbot for commit d68cfd3. This will update automatically on new commits. Configure here.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.