feat: paykit v2#195
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Lost in the diff? Review this PR in Change Stack to follow the change map from intent to exact ranges. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughConvert PayKit to Stripe-only: configure via options, replace provider JSON with Stripe columns, migrate schema, update core/services/webhooks/CLI, unify demo to a single paykit route/client, adjust e2e harness/tests, and rewrite docs and site content accordingly. ChangesStripe-only refactor and ecosystem updates
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
apps/web/src/components/sections/readme-code-content.ts (1)
28-40:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winStripe wiring update LGTM, but the example callback uses
awaitwithoutasync.Lines 36-38 use
await sendEmail(...)inside a non-async arrow, which is a syntax error for anyone copying this hero snippet.📝 Proposed fix
on: { - "subscription.activated": ({ customer, plan }) => { + "subscription.activated": async ({ customer, plan }) => { await sendEmail(customer.email, "Welcome to Pro!") }, }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/sections/readme-code-content.ts` around lines 28 - 40, The event handler for "subscription.activated" in the createPayKit call uses await inside a non-async arrow function causing a syntax error; update the handler for that event (the "subscription.activated" callback used in paykit/createPayKit) to be async (e.g., make the arrow async: async ({ customer, plan }) => { await sendEmail(...) }) or remove the await and return the promise from sendEmail, ensuring sendEmail is invoked correctly.packages/paykit/src/core/create-paykit.ts (1)
68-90:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAttach a rejection handler to the cached context promise to prevent unhandledRejection.
initContext()can reject (e.g., pending migrations). IncreatePayKit,contextPromise = initContext(options)is created eagerly and passed intogetApi, butwrapMethodsonlyawaits the ctx promise inside each method wrapper (andhandler()/$contextdo the same), so no immediate.catch/await is attached at context creation time. This can allow Node to emitunhandledRejectionbefore any caller invokes a method/handler.Add a rejection handler right after setting
contextPromise(e.g.,contextPromise.catch(() => {}), or equivalent) so the rejection is handled without swallowing the error.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/core/create-paykit.ts` around lines 68 - 90, The cached context promise created in createPayKit (contextPromise assigned via initContext in getContext) can reject and cause unhandledRejection because no handler is attached immediately; after you set contextPromise (contextPromise ??= initContext(options)) attach a rejection handler (e.g., contextPromise.catch(() => {})) so the rejection is handled right away while preserving the existing behavior where wrappers (handler/$context/wrapped API methods produced by getApi/wrapMethods) still await the promise; ensure you add this line in the getContext scope right after the assignment to contextPromise.packages/paykit/src/cli/commands/status.ts (1)
80-86:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winInconsistent section label: error path still says
Provider.Success output (Lines 150, 156) was renamed to
Stripe, but this failure block still printsProvider/Fix provider issues. Align for consistency.Proposed fix
- p.log.error(`Provider\n ${picocolors.red("✖")} ${providerResult.account.message}`); - p.outro("Fix provider issues before continuing"); + p.log.error(`Stripe\n ${picocolors.red("✖")} ${providerResult.account.message}`); + p.outro("Fix Stripe issues before continuing");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/cli/commands/status.ts` around lines 80 - 86, The failure block prints "Provider" and "Fix provider issues" but the success messages were renamed to "Stripe"; update the error section to match by replacing the "Provider" label and the outro text accordingly. Locate the block that checks providerResult.account.ok (uses s.stop(""), p.log.error, p.outro, await database.end(), process.exit) and change the displayed label in p.log.error from "Provider" to "Stripe" and update p.outro to "Fix Stripe issues before continuing" so the success and failure texts are consistent.packages/paykit/src/cli/commands/push.ts (1)
49-54:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winInconsistent section label: error path still says
Provider.The success path (Line 96) and config errors now use the
Stripelabel, but this failure block still printsProvider. Align it for consistent CLI output.Proposed fix
- p.log.error(`Provider\n ${picocolors.red("✖")} ${providerResult.account.message}`); + p.log.error(`Stripe\n ${picocolors.red("✖")} ${providerResult.account.message}`);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/cli/commands/push.ts` around lines 49 - 54, The error block that checks providerResult.account.ok prints the wrong section label "Provider"; update the log to use the "Stripe" label for consistency with the success and config error paths by changing the string passed to p.log.error from `Provider\n ${picocolors.red("✖")} ...` to `Stripe\n ${picocolors.red("✖")} ${providerResult.account.message}`, leaving the surrounding calls (s.stop(""), p.cancel("Push failed"), process.exit(1)) and symbols (providerResult.account.ok, s.stop, p.log.error, picocolors.red, p.cancel, process.exit) unchanged.packages/paykit/src/webhook/webhook.api.ts (1)
17-22:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winConfirm whether the legacy
PAYKIT_ALLOW_STALE_SIGNATURESenv var is still intended.The rename from
allowStaleSignatures→allowUnsignedPayloadcarried over the old env var on Line 19 alongside the newPAYKIT_ALLOW_UNSIGNED_PAYLOADS. If this is intentional backward compatibility it should be documented with a short comment; otherwise it's a dangling reference to a removed flag that silently keeps a signature-bypass path alive.🔧 If backward compat is not intended
return ( process.env.PAYKIT_ALLOW_UNSIGNED_PAYLOADS === "1" || - process.env.PAYKIT_ALLOW_STALE_SIGNATURES === "1" || process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test" );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/webhook/webhook.api.ts` around lines 17 - 22, The conditional that checks environment variables in webhook.api.ts still references the legacy PAYKIT_ALLOW_STALE_SIGNATURES alongside PAYKIT_ALLOW_UNSIGNED_PAYLOADS; either remove the legacy reference or explicitly document it: if backward compatibility is intended, add a short inline comment next to the return expression explaining that PAYKIT_ALLOW_STALE_SIGNATURES is kept for legacy support and should be removed in a future major; if not intended, delete the PAYKIT_ALLOW_STALE_SIGNATURES check from the return expression so only PAYKIT_ALLOW_UNSIGNED_PAYLOADS (and NODE_ENV checks) control unsigned-stub behavior.packages/paykit/src/stripe/stripe-provider.ts (1)
943-955:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMove the signature guard behind
allowUnsignedPayload.
allowUnsignedPayloadis checked only afterPROVIDER_SIGNATURE_MISSINGcan already be thrown, so the new flag never works for unsigned deliveries.packages/paykit/src/cli/commands/listen.tsnow relies on this path for direct tunnel replays.Suggested fix
async handleWebhook(data) { - const headerKey = Object.keys(data.headers).find( - (k) => k.toLowerCase() === "stripe-signature", - ); - const signature = headerKey ? data.headers[headerKey] : undefined; - if (!signature) { - throw PayKitError.from("BAD_REQUEST", PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING); - } - - const event = data.allowUnsignedPayload - ? (JSON.parse(data.body) as StripeSdk.Event) - : await client.webhooks.constructEventAsync(data.body, signature, options.webhookSecret); + const event = data.allowUnsignedPayload + ? (JSON.parse(data.body) as StripeSdk.Event) + : await (async () => { + const headerKey = Object.keys(data.headers).find( + (k) => k.toLowerCase() === "stripe-signature", + ); + const signature = headerKey ? data.headers[headerKey] : undefined; + if (!signature) { + throw PayKitError.from( + "BAD_REQUEST", + PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING, + ); + } + return client.webhooks.constructEventAsync( + data.body, + signature, + options.webhookSecret, + ); + })(); return [🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/stripe/stripe-provider.ts` around lines 943 - 955, In handleWebhook, the signature missing guard runs before allowUnsignedPayload is checked so unsigned deliveries are blocked; change the flow in the handleWebhook method to first branch on data.allowUnsignedPayload and, if true, parse JSON from data.body into a StripeSdk.Event, otherwise perform the existing signature lookup (headerKey/signature) and throw the PayKitError with PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING if no signature, then call client.webhooks.constructEventAsync(...) with data.body, signature and options.webhookSecret; keep all existing variable names (headerKey, signature, data.allowUnsignedPayload, PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING, client.webhooks.constructEventAsync) and return the event as before.
🧹 Nitpick comments (3)
e2e/cli/push.test.ts (1)
85-93: 💤 Low valueNit:
providerRowsname is stale.The rows no longer come from a
providerfield; considerproRowsfor clarity.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@e2e/cli/push.test.ts` around lines 85 - 93, Rename the stale variable providerRows to proRows in the select block and update its use when extracting the first row into proProduct; specifically update the identifier providerRows in the lines using ctx.database.select(...).from(product)... to proRows and adjust any subsequent references (e.g., the assignment to proProduct) so the new name is used consistently.packages/paykit/src/cli/utils/shared.ts (1)
181-186: ⚖️ Poor tradeoff
checkActiveSubscriptionsOnOtherProvideris now a no-op.It ignores both params and always returns
[], yet callers inpush.ts/status.tsstill computeproviderId = "stripe"and await it as a preflight check. In a Stripe-only world this cross-provider check is dead. Consider removing the function and its call sites in a follow-up to avoid misleading preflight semantics.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/cli/utils/shared.ts` around lines 181 - 186, The function checkActiveSubscriptionsOnOtherProvider is a no-op and causes misleading preflight behavior; remove the function and its call sites instead of leaving it returning [] — locate references to checkActiveSubscriptionsOnOtherProvider in push.ts and status.ts (where providerId is computed as "stripe"), delete the await calls and any related handling, and remove the import/export of checkActiveSubscriptionsOnOtherProvider from packages/paykit/src/cli/utils/shared.ts; ensure any leftover variables (e.g., providerId or returned values) are cleaned up or adjusted and run tests to confirm no unused-import/variable errors.packages/paykit/src/cli/commands/init.ts (1)
551-552: Open TODO forpaykitjs listen.The forwarded webhook command is hardcoded to the Stripe CLI with a TODO to swap in
paykitjs listen.Want me to open an issue to track replacing this with
paykitjs listen?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/cli/commands/init.ts` around lines 551 - 552, Replace the hardcoded webhookCommand string with a small helper that prefers the new paykit CLI: implement a getWebhookListenCommand() (and optional detectPaykitCli()) and assign webhookCommand = getWebhookListenCommand(port) so it returns "paykitjs listen --forward-to localhost:3000/paykit/webhook" when the paykit CLI is available and falls back to "stripe listen --forward-to ..." otherwise; update the TODO by removing the hardcoded string and ensuring the helper is referenced from init.ts (webhookCommand) and covered by a simple unit test for both code paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@e2e/test-utils/harness/stripe.ts`:
- Around line 91-94: The current use of cardPaymentButton.evaluate(...click())
bypasses Playwright actionability checks; keep the existing count() conditional
but replace the evaluate call with an actionable locator click: use
cardPaymentButton.first().waitFor({ state: "visible" }) (or waitFor({ state:
"enabled" }) as appropriate) and then await cardPaymentButton.first().click() so
the click uses Playwright's built-in waits and retries; update the code in the
block referencing the cardPaymentButton locator accordingly.
In `@packages/paykit/src/cli/commands/listen.ts`:
- Around line 94-106: The function getEnvStripeOptions currently returns a fake
"whsec_placeholder" when no webhook secret env var exists; change it to
explicitly represent "unknown" by returning webhookSecret as undefined (and
update the function signature to allow webhookSecret?: string | undefined)
instead of synthesizing a value, and then ensure callers (e.g.,
ensureTunnelWebhook in stripe-provider.ts and any code that prints
options.webhookSecret) treat undefined as "secret unknown" and avoid
echoing/saving a placeholder.
- Around line 682-692: The current catch around getPayKitConfig() treats every
error as "no config" and falls back to envs; change it to only fall back when
the error is a genuine "config not found" case and rethrow all other errors
(syntax/validation/missing env) so they surface. Implement a check (e.g., add or
use an isConfigNotFound(error) helper or test error.name/message/code) inside
the catch for getPayKitConfig(...) — if isConfigNotFound(error) is true then
call loadDotEnv(params.cwd) and stripeOptions = getEnvStripeOptions(), otherwise
throw the error (respecting params.configPath || params.requireConfig logic).
Ensure references: getPayKitConfig, params.configPath, params.requireConfig,
loadDotEnv, getEnvStripeOptions, basePath, stripeOptions.
In `@packages/paykit/src/database/migrations/0001_stripe_only_schema.sql`:
- Around line 65-70: The migration adds a new column paykit_payment_method.brand
but never backfills it from the existing JSON provider_data, so dropping
provider_data will lose historical brand values; update the migration (around
ALTER TABLE "paykit_payment_method" ADD COLUMN "brand" text and the related
blocks at the other ranges) to run an UPDATE that sets brand =
provider_data->>'brand' for all rows (and ensure NULL-safe handling), verify the
backfill completes before any DROP COLUMN "provider_data" statements, and apply
the same backfill logic for the other affected migration sections (lines
referenced 89-104 and 147-148) so historical brand data is preserved.
- Around line 55-60: The migration currently adds stripe_frozen_time as type
timestamp which drops the Z/UTC offset; change the ALTER TABLE that adds
stripe_frozen_time on paykit_customer to use timestamptz instead of timestamp,
and update any related backfill/CAST logic (the backfill that converts
ISO-8601/Z values into stripe_frozen_time) to cast to timestamptz so the
original instant is preserved when reading/writing from node-postgres; ensure
the column name stripe_frozen_time and the ALTER TABLE statement are the only
places modified.
In `@packages/paykit/src/payment-method/payment-method.service.ts`:
- Around line 49-54: The current lookup in
database.query.paymentMethod.findFirst excludes soft-deleted rows
(isNull(paymentMethod.deletedAt)), so after deletePaymentMethodByProviderId a
later upsert creates a duplicate; change the upsert flow to include soft-deleted
rows by removing the isNull filter (or explicitly searching both deleted and
non-deleted) when querying by stripePaymentMethodId
(paymentMethod.stripePaymentMethodId) and if a row is found with deletedAt set,
perform an update to clear deletedAt and refresh the record fields instead of
inserting a new row; keep the existing update path that clears deletedAt intact
and ensure deletePaymentMethodByProviderId still sets deletedAt only.
---
Outside diff comments:
In `@apps/web/src/components/sections/readme-code-content.ts`:
- Around line 28-40: The event handler for "subscription.activated" in the
createPayKit call uses await inside a non-async arrow function causing a syntax
error; update the handler for that event (the "subscription.activated" callback
used in paykit/createPayKit) to be async (e.g., make the arrow async: async ({
customer, plan }) => { await sendEmail(...) }) or remove the await and return
the promise from sendEmail, ensuring sendEmail is invoked correctly.
In `@packages/paykit/src/cli/commands/push.ts`:
- Around line 49-54: The error block that checks providerResult.account.ok
prints the wrong section label "Provider"; update the log to use the "Stripe"
label for consistency with the success and config error paths by changing the
string passed to p.log.error from `Provider\n ${picocolors.red("✖")} ...` to
`Stripe\n ${picocolors.red("✖")} ${providerResult.account.message}`, leaving
the surrounding calls (s.stop(""), p.cancel("Push failed"), process.exit(1)) and
symbols (providerResult.account.ok, s.stop, p.log.error, picocolors.red,
p.cancel, process.exit) unchanged.
In `@packages/paykit/src/cli/commands/status.ts`:
- Around line 80-86: The failure block prints "Provider" and "Fix provider
issues" but the success messages were renamed to "Stripe"; update the error
section to match by replacing the "Provider" label and the outro text
accordingly. Locate the block that checks providerResult.account.ok (uses
s.stop(""), p.log.error, p.outro, await database.end(), process.exit) and change
the displayed label in p.log.error from "Provider" to "Stripe" and update
p.outro to "Fix Stripe issues before continuing" so the success and failure
texts are consistent.
In `@packages/paykit/src/core/create-paykit.ts`:
- Around line 68-90: The cached context promise created in createPayKit
(contextPromise assigned via initContext in getContext) can reject and cause
unhandledRejection because no handler is attached immediately; after you set
contextPromise (contextPromise ??= initContext(options)) attach a rejection
handler (e.g., contextPromise.catch(() => {})) so the rejection is handled right
away while preserving the existing behavior where wrappers
(handler/$context/wrapped API methods produced by getApi/wrapMethods) still
await the promise; ensure you add this line in the getContext scope right after
the assignment to contextPromise.
In `@packages/paykit/src/stripe/stripe-provider.ts`:
- Around line 943-955: In handleWebhook, the signature missing guard runs before
allowUnsignedPayload is checked so unsigned deliveries are blocked; change the
flow in the handleWebhook method to first branch on data.allowUnsignedPayload
and, if true, parse JSON from data.body into a StripeSdk.Event, otherwise
perform the existing signature lookup (headerKey/signature) and throw the
PayKitError with PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING if no signature,
then call client.webhooks.constructEventAsync(...) with data.body, signature and
options.webhookSecret; keep all existing variable names (headerKey, signature,
data.allowUnsignedPayload, PAYKIT_ERROR_CODES.PROVIDER_SIGNATURE_MISSING,
client.webhooks.constructEventAsync) and return the event as before.
In `@packages/paykit/src/webhook/webhook.api.ts`:
- Around line 17-22: The conditional that checks environment variables in
webhook.api.ts still references the legacy PAYKIT_ALLOW_STALE_SIGNATURES
alongside PAYKIT_ALLOW_UNSIGNED_PAYLOADS; either remove the legacy reference or
explicitly document it: if backward compatibility is intended, add a short
inline comment next to the return expression explaining that
PAYKIT_ALLOW_STALE_SIGNATURES is kept for legacy support and should be removed
in a future major; if not intended, delete the PAYKIT_ALLOW_STALE_SIGNATURES
check from the return expression so only PAYKIT_ALLOW_UNSIGNED_PAYLOADS (and
NODE_ENV checks) control unsigned-stub behavior.
---
Nitpick comments:
In `@e2e/cli/push.test.ts`:
- Around line 85-93: Rename the stale variable providerRows to proRows in the
select block and update its use when extracting the first row into proProduct;
specifically update the identifier providerRows in the lines using
ctx.database.select(...).from(product)... to proRows and adjust any subsequent
references (e.g., the assignment to proProduct) so the new name is used
consistently.
In `@packages/paykit/src/cli/commands/init.ts`:
- Around line 551-552: Replace the hardcoded webhookCommand string with a small
helper that prefers the new paykit CLI: implement a getWebhookListenCommand()
(and optional detectPaykitCli()) and assign webhookCommand =
getWebhookListenCommand(port) so it returns "paykitjs listen --forward-to
localhost:3000/paykit/webhook" when the paykit CLI is available and falls back
to "stripe listen --forward-to ..." otherwise; update the TODO by removing the
hardcoded string and ensuring the helper is referenced from init.ts
(webhookCommand) and covered by a simple unit test for both code paths.
In `@packages/paykit/src/cli/utils/shared.ts`:
- Around line 181-186: The function checkActiveSubscriptionsOnOtherProvider is a
no-op and causes misleading preflight behavior; remove the function and its call
sites instead of leaving it returning [] — locate references to
checkActiveSubscriptionsOnOtherProvider in push.ts and status.ts (where
providerId is computed as "stripe"), delete the await calls and any related
handling, and remove the import/export of
checkActiveSubscriptionsOnOtherProvider from
packages/paykit/src/cli/utils/shared.ts; ensure any leftover variables (e.g.,
providerId or returned values) are cleaned up or adjusted and run tests to
confirm no unused-import/variable errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1059bfa6-d80b-4f37-9f95-137993970930
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (103)
README.mdapps/demo/drizzle.config.tsapps/demo/next.config.jsapps/demo/package.jsonapps/demo/paykit.config.tsapps/demo/paykit.polar.config.tsapps/demo/paykit.stripe.config.tsapps/demo/scripts/push-sandbox.tsapps/demo/scripts/sandbox.tsapps/demo/src/app/_components/checkout-page-content.tsxapps/demo/src/app/_components/features-panel.tsxapps/demo/src/app/_components/subscribe-panel.tsxapps/demo/src/app/paykit-polar/[[...slug]]/route.tsapps/demo/src/app/paykit-stripe/[[...slug]]/route.tsapps/demo/src/app/paykit/[[...slug]]/route.tsapps/demo/src/env.jsapps/demo/src/lib/paykit-client.tsapps/demo/src/lib/paykit-scenarios.tsapps/demo/src/lib/paykit.tsapps/demo/src/lib/paykit/polar.tsapps/demo/src/lib/paykit/stripe.tsapps/demo/src/lib/scenario-config.tsapps/demo/src/server/api/root.tsapps/demo/src/server/api/routers/paykit-route.tsapps/demo/src/server/db.tsapps/web/content/docs/concepts/cli.mdxapps/web/content/docs/concepts/payment-providers.mdxapps/web/content/docs/get-started/index.mdxapps/web/content/docs/get-started/installation.mdxapps/web/content/docs/providers/creem.mdxapps/web/content/docs/providers/lemonsqueezy.mdxapps/web/content/docs/providers/meta.jsonapps/web/content/docs/providers/paddle.mdxapps/web/content/docs/providers/paypal.mdxapps/web/content/docs/providers/polar.mdxapps/web/content/docs/providers/stripe.mdxapps/web/src/components/docs/docs-icons.tsxapps/web/src/components/docs/features.tsxapps/web/src/components/sections/demo/demo-types.tsxapps/web/src/components/sections/features-section.tsxapps/web/src/components/sections/readme-code-content.tse2e/cli/init.test.tse2e/cli/push.test.tse2e/cli/setup.tse2e/cli/status.test.tse2e/core/subscribe/cancel-end-of-cycle.test.tse2e/core/subscribe/renewal.test.tse2e/core/webhook/duplicate-webhook.test.tse2e/core/webhook/subscription-deleted.test.tse2e/package.jsone2e/test-utils/env.tse2e/test-utils/harness/index.tse2e/test-utils/harness/polar.tse2e/test-utils/harness/stripe.tse2e/test-utils/harness/types.tse2e/test-utils/setup.tse2e/vitest.config.tspackages/paykit/package.jsonpackages/paykit/src/api/__tests__/define-route.test.tspackages/paykit/src/api/__tests__/methods.test.tspackages/paykit/src/api/methods.tspackages/paykit/src/cli/commands/init.tspackages/paykit/src/cli/commands/listen.tspackages/paykit/src/cli/commands/push.tspackages/paykit/src/cli/commands/status.tspackages/paykit/src/cli/utils/get-config.tspackages/paykit/src/cli/utils/shared.tspackages/paykit/src/core/__tests__/context.test.tspackages/paykit/src/core/__tests__/create-paykit.test.tspackages/paykit/src/core/context.tspackages/paykit/src/core/create-paykit.tspackages/paykit/src/customer/__tests__/customer.service.test.tspackages/paykit/src/customer/customer.service.tspackages/paykit/src/database/migrations/0001_stripe_only_schema.sqlpackages/paykit/src/database/migrations/meta/0001_snapshot.jsonpackages/paykit/src/database/migrations/meta/_journal.jsonpackages/paykit/src/database/schema.tspackages/paykit/src/index.tspackages/paykit/src/invoice/invoice.service.tspackages/paykit/src/payment-method/payment-method.service.tspackages/paykit/src/payment/payment.service.tspackages/paykit/src/product/product.service.tspackages/paykit/src/providers/provider.tspackages/paykit/src/stripe/stripe-provider.tspackages/paykit/src/subscription/subscription.service.tspackages/paykit/src/testing/testing.service.tspackages/paykit/src/types/instance.tspackages/paykit/src/types/options.tspackages/paykit/src/utilities/dependencies/paykit-package-list.tspackages/paykit/src/webhook/webhook.api.tspackages/paykit/src/webhook/webhook.service.tspackages/polar/package.jsonpackages/polar/src/index.tspackages/polar/src/polar-provider.tspackages/polar/tsconfig.jsonpackages/polar/tsdown.config.tspackages/stripe/package.jsonpackages/stripe/src/__tests__/stripe-provider.test.tspackages/stripe/src/__tests__/stripe.test.tspackages/stripe/src/index.tspackages/stripe/tsconfig.jsonpackages/stripe/tsdown.config.tsscripts/publish-dist.mjs
💤 Files with no reviewable changes (27)
- apps/demo/src/lib/paykit/stripe.ts
- packages/stripe/tsconfig.json
- packages/stripe/src/tests/stripe.test.ts
- apps/web/content/docs/providers/lemonsqueezy.mdx
- apps/web/content/docs/providers/creem.mdx
- apps/web/content/docs/providers/paypal.mdx
- packages/stripe/src/index.ts
- apps/demo/src/lib/paykit/polar.ts
- apps/web/content/docs/providers/polar.mdx
- apps/demo/paykit.stripe.config.ts
- apps/demo/src/lib/paykit-scenarios.ts
- packages/stripe/package.json
- packages/polar/tsconfig.json
- apps/web/content/docs/providers/paddle.mdx
- e2e/test-utils/harness/polar.ts
- packages/polar/tsdown.config.ts
- packages/stripe/tsdown.config.ts
- packages/paykit/src/testing/testing.service.ts
- packages/stripe/src/tests/stripe-provider.test.ts
- packages/polar/src/polar-provider.ts
- e2e/package.json
- apps/demo/src/app/paykit-stripe/[[...slug]]/route.ts
- apps/demo/src/app/paykit-polar/[[...slug]]/route.ts
- packages/polar/package.json
- packages/polar/src/index.ts
- apps/demo/paykit.polar.config.ts
- apps/demo/src/lib/scenario-config.ts
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/paykit/src/database/schema.ts (1)
231-248:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winSchema defines
stripeEventIdas nullable but migration enforces NOT NULL.The schema at line 235 defines
stripeEventIdwithout.notNull(), but the migration (line 158) addsALTER TABLE ... ALTER COLUMN "stripe_event_id" SET NOT NULL. This mismatch means the Drizzle schema won't reflect the actual database constraint, potentially causing type-level confusion.🔧 Proposed fix to align schema with migration
webhookEvent = pgTable( "webhook_event", { id: text("id").primaryKey(), - stripeEventId: text("stripe_event_id").notNull(), + stripeEventId: text("stripe_event_id").notNull(), type: text("type").notNull(),Wait, looking again at line 235, it already shows
stripeEventId: text("stripe_event_id").notNull()- let me re-check... Actually line 235 shows justtext("stripe_event_id")without.notNull(). The fix should be:- stripeEventId: text("stripe_event_id"), + stripeEventId: text("stripe_event_id").notNull(),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/database/schema.ts` around lines 231 - 248, The Drizzle schema for the webhook_event table has stripeEventId defined without .notNull(), but the migration enforces NOT NULL; update the webhookEvent table definition by adding .notNull() to the stripeEventId column (symbol: stripeEventId in the webhookEvent pgTable) so the schema matches the migration and the generated types reflect the non-null constraint.apps/web/content/docs/concepts/cli.mdx (1)
27-27:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove the multi-provider reference.
The parenthetical "(or whichever provider you're using)" is inconsistent with the Stripe-only migration. This line should reference Stripe specifically to match the rest of the documentation.
📝 Proposed fix
-2. Syncs your plan definitions to the database and your payment provider, creating or updating products and prices in Stripe (or whichever provider you're using) +2. Syncs your plan definitions to the database and Stripe, creating or updating products and prices🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/content/docs/concepts/cli.mdx` at line 27, Update the sentence fragment "Syncs your plan definitions to the database and your payment provider, creating or updating products and prices in Stripe (or whichever provider you're using)" to remove the multi‑provider parenthetical and explicitly reference Stripe (e.g., end with "creating or updating products and prices in Stripe"). Locate the sentence in the CLI docs content (the line starting "Syncs your plan definitions...") and replace the parenthetical "(or whichever provider you're using)" with a Stripe‑specific phrasing so the text consistently references Stripe only.packages/paykit/src/cli/commands/status.ts (1)
48-68:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate the config here before using Stripe/database fields.
pushActionalready callsassertValidPayKitOptions(...), butstatusActionnow readsconfig.options.stripeand opens the database without the same schema check. With the new Stripe-only contract, malformed config will fall through tocreatePool()/checkProvider()and show a generic runtime failure instead of a config error.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/cli/commands/status.ts` around lines 48 - 68, The status flow currently reads config.options.stripe and calls createPool/checkProvider without validating the config schema; call the existing validation function assertValidPayKitOptions (or equivalent) on config.options near the start of statusAction (before referencing config.options.stripe or creating the DB pool) and handle validation failures by logging the config error and exiting, so malformed configs produce a clear config error instead of runtime failures in createPool/checkProvider.
🧹 Nitpick comments (2)
packages/paykit/src/stripe/stripe-provider.ts (1)
1066-1066: 💤 Low valueEmpty string default for
webhookSecretmay cause confusing runtime failures.Passing an empty string as
webhookSecrettocreateStripeProviderwill causeconstructEventAsyncto fail with a cryptic Stripe SDK error during webhook verification (whenallowUnsignedPayloadis false). This scenario can occur when the CLI'slistencommand creates an adapter without a pre-existing webhook secret.Consider either:
- Throwing early if
webhookSecretis required but missing- Documenting that the empty string case is only valid when
allowUnsignedPayloadwill always be true🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/paykit/src/stripe/stripe-provider.ts` at line 1066, The current wrapper returns createStripeProvider(client, { ...options, webhookSecret: options.webhookSecret ?? "" }) which injects an empty string and leads to cryptic failures in Stripe.constructEventAsync; remove the empty-string default and instead pass webhookSecret only if present (e.g., leave it undefined when missing) and add validation inside createStripeProvider to throw a clear error when webhookSecret is missing while allowUnsignedPayload is false (referencing createStripeProvider, webhookSecret, allowUnsignedPayload, and constructEventAsync).apps/web/src/components/docs/docs-icons.tsx (1)
125-195: 💤 Low valueConsider removing unused provider icon definitions.
The
providerPageIconsobject still contains SVG definitions forpolar,paypal,lemonsqueezy,paddle, andcreem, but onlystripeis in theenabledProvidersset. Since Polar is being removed from the project entirely and the other providers aren't currently supported, these icon definitions are unused code.🧹 Optional cleanup to remove unused icons
const providerPageIcons = { stripe: ( <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" className="docs-category-icon size-3! shrink-0 text-current" > <path fill="currentColor" d="M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409c0-.831.683-1.305 1.901-1.305c2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0C9.667 0 7.589.654 6.104 1.872C4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219c2.585.92 3.445 1.574 3.445 2.583c0 .98-.84 1.545-2.354 1.545c-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813c1.664-1.305 2.525-3.236 2.525-5.732c0-4.128-2.524-5.851-6.594-7.305z" /> </svg> ), - paypal: ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="15" - height="15" - viewBox="0 0 154.728 190.5" - className="docs-category-icon size-3! shrink-0 text-current" - > - <g transform="translate(898.192 276.071)"> - <path - d="M-837.663-237.968a5.49 5.49 0 0 0-5.423 4.633l-9.013 57.15-8.281 52.514-.005.044.01-.044 8.281-52.514c.421-2.669 2.719-4.633 5.42-4.633h26.404c26.573 0 49.127-19.387 53.246-45.658.314-1.996.482-3.973.52-5.924v-.003h-.003c-6.753-3.543-14.683-5.565-23.372-5.565z" - fill="currentColor" - /> - <path - d="M-766.506-232.402c-.037 1.951-.207 3.93-.52 5.926-4.119 26.271-26.673 45.658-53.246 45.658h-26.404c-2.701 0-4.999 1.964-5.42 4.633l-8.281 52.514-5.197 32.947a4.46 4.46 0 0 0 4.405 5.153h28.66a5.49 5.49 0 0 0 5.423-4.633l7.55-47.881c.423-2.669 2.722-4.636 5.423-4.636h16.876c26.573 0 49.124-19.386 53.243-45.655 2.924-18.649-6.46-35.614-22.511-44.026z" - fill="currentColor" - /> - <path - d="M-870.225-276.071a5.49 5.49 0 0 0-5.423 4.636l-22.489 142.608a4.46 4.46 0 0 0 4.405 5.156h33.351l8.281-52.514 9.013-57.15a5.49 5.49 0 0 1 5.423-4.633h47.782c8.691 0 16.621 2.025 23.375 5.563.46-23.917-19.275-43.666-46.412-43.666z" - fill="currentColor" - /> - </g> - </svg> - ), - polar: ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="15" - height="15" - viewBox="-0.5 -0.5 16 16" - fill="none" - className="docs-category-icon size-3! shrink-0 text-current" - > - <path - d="M7.5 14.337c-3.776 0-6.837-3.061-6.837-6.837C.663 3.724 3.724.663 7.5.663c3.776 0 6.837 3.061 6.837 6.837 0 3.776-3.061 6.837-6.837 6.837Z" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="1" - /> - <path - d="M7.5 14.337c-1.51 0-2.735-3.061-2.735-6.837C4.765 3.724 5.99.663 7.5.663c1.51 0 2.735 3.061 2.735 6.837 0 3.776-1.225 6.837-2.735 6.837Z" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="1" - /> - <path - d="M5.449 13.654c-2.051-.684-2.735-3.685-2.735-5.812 0-2.127 1.026-4.786 3.419-6.495" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="1" - /> - <path - d="M9.551 1.346c2.051.684 2.735 3.685 2.735 5.812 0 2.127-1.026 4.786-3.419 6.495" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="1" - /> - </svg> - ), - lemonsqueezy: ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="15" - height="15" - viewBox="0 0 24 24" - className="docs-category-icon size-3! shrink-0 text-current" - > - <path - fill="currentColor" - d="m7.4916 10.835 2.3748-6.5114a3.1497 3.1497 0 0 0-.065-2.3418C9.0315.183 6.9427-.398 5.2928.265 3.643.929 2.71 2.4348 3.512 4.3046l2.8197 6.5615c.219.509.97.489 1.16-.03m1.6798 1.0969 6.5334-2.7758c2.1699-.9219 2.7218-3.6907 1.022-5.2905l-.068-.063c-1.6669-1.5469-4.4217-1.002-5.3706 1.0359L8.3566 11.135c-.234.503.295 1.0199.8159.7979m.373.87 6.6454-2.5119c2.2078-.8349 4.6206.745 4.5886 3.0398l-.002.09c-.048 2.2358-2.3938 3.7376-4.5536 2.9467l-6.6724-2.4418a.595.595 0 0 1-.006-1.1229m-.386 1.9269 6.4375 2.9767a3.2997 3.2997 0 0 1 1.6658 1.6989c.769 1.7998-.283 3.6396-1.9328 4.3016-1.6499.662-3.4097.235-4.2097-1.6359l-2.8027-6.5694c-.217-.509.328-1.009.8419-.772" - /> - </svg> - ), - paddle: ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="15" - height="15" - viewBox="0 0 160 201" - className="docs-category-icon size-3! shrink-0 text-current" - > - <path - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - d="M32.154 10.694V21.113l34.575.751c33.315.724 34.94.966 44.623 6.66 32.453 19.077 29.294 71.09-5.193 85.472-5.992 2.5-15.922 3.3-40.958 3.3H32.154v41.49 41.49h11.702 11.702v-30.632-30.632l26.064-.855c22.107-.726 27.679-1.634 36.703-5.991 15.017-7.254 29.536-22.83 35.153-37.716 9.43-24.991 2.627-55.38-16.353-73.051C118.533 4.09 105.065.275 62.559.275H32.154v10.42zm0 26.143c0 1.387-1.915 6.477-4.255 11.311-4.213 8.704-16.779 18.085-24.226 18.085-2.015 0-3.434 1.757-3.434 4.255 0 2.87 1.42 4.255 4.362 4.255 11.183 0 23.508 11.673 26.394 24.999 2.155 9.95 5.853 8.496 10.734-4.219 4.59-11.957 14.662-20.78 23.719-20.78 3.585 0 5.004-1.207 5.004-4.256 0-2.498-1.42-4.255-3.434-4.255-7.447 0-20.013-9.381-24.226-18.085-2.34-4.834-4.255-9.923-4.255-11.31 0-1.386-1.436-2.52-3.191-2.52s-3.192 1.134-3.192 2.52z" - /> - </svg> - ), - creem: <CreemIcon className="docs-category-icon size-3! shrink-0 text-current" />, } as const;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/docs/docs-icons.tsx` around lines 125 - 195, providerPageIcons contains unused SVG entries (polar, paypal, lemonsqueezy, paddle, creem) while only stripe is in enabledProviders; remove the dead icon definitions or reduce providerPageIcons to only include providers present in enabledProviders (e.g., keep the stripe entry and delete polar/paypal/lemonsqueezy/paddle/creem) and ensure any imports/components referenced (like CreemIcon) are also removed if no longer used; update any consumers that iterate providerPageIcons to handle the reduced set.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/paykit/src/cli/commands/status.ts`:
- Around line 113-115: The code after createPool(...) can throw (e.g., in
loadProductDiffs or checkProviderCustomers) without calling database.end(), so
wrap the entire post-createPool section in a try/finally that always calls
database.end(); specifically, around the block that calls loadProductDiffs(...),
checkProviderCustomers(...), and mutates preflightErrors, ensure a try { /*
existing logic */ } finally { await database.end(); } so the pg pool is always
closed even on errors.
In `@packages/paykit/src/stripe/stripe-provider.ts`:
- Around line 953-960: When allowUnsignedPayload is true the code calls
JSON.parse(data.body) without validation; wrap that parse in a try-catch and
validate the resulting object shape before treating it as a StripeSdk.Event.
Specifically, in the block that sets event when data.allowUnsignedPayload is
true, catch JSON.parse errors and throw a PayKitError (e.g., BAD_REQUEST with a
PROVIDER_INVALID_PAYLOAD or similar code) and perform basic structural checks
(presence of required keys like "type" and "id" on the parsed object) before
casting to StripeSdk.Event; keep the existing signed path using
client.webhooks.constructEventAsync unchanged.
---
Outside diff comments:
In `@apps/web/content/docs/concepts/cli.mdx`:
- Line 27: Update the sentence fragment "Syncs your plan definitions to the
database and your payment provider, creating or updating products and prices in
Stripe (or whichever provider you're using)" to remove the multi‑provider
parenthetical and explicitly reference Stripe (e.g., end with "creating or
updating products and prices in Stripe"). Locate the sentence in the CLI docs
content (the line starting "Syncs your plan definitions...") and replace the
parenthetical "(or whichever provider you're using)" with a Stripe‑specific
phrasing so the text consistently references Stripe only.
In `@packages/paykit/src/cli/commands/status.ts`:
- Around line 48-68: The status flow currently reads config.options.stripe and
calls createPool/checkProvider without validating the config schema; call the
existing validation function assertValidPayKitOptions (or equivalent) on
config.options near the start of statusAction (before referencing
config.options.stripe or creating the DB pool) and handle validation failures by
logging the config error and exiting, so malformed configs produce a clear
config error instead of runtime failures in createPool/checkProvider.
In `@packages/paykit/src/database/schema.ts`:
- Around line 231-248: The Drizzle schema for the webhook_event table has
stripeEventId defined without .notNull(), but the migration enforces NOT NULL;
update the webhookEvent table definition by adding .notNull() to the
stripeEventId column (symbol: stripeEventId in the webhookEvent pgTable) so the
schema matches the migration and the generated types reflect the non-null
constraint.
---
Nitpick comments:
In `@apps/web/src/components/docs/docs-icons.tsx`:
- Around line 125-195: providerPageIcons contains unused SVG entries (polar,
paypal, lemonsqueezy, paddle, creem) while only stripe is in enabledProviders;
remove the dead icon definitions or reduce providerPageIcons to only include
providers present in enabledProviders (e.g., keep the stripe entry and delete
polar/paypal/lemonsqueezy/paddle/creem) and ensure any imports/components
referenced (like CreemIcon) are also removed if no longer used; update any
consumers that iterate providerPageIcons to handle the reduced set.
In `@packages/paykit/src/stripe/stripe-provider.ts`:
- Line 1066: The current wrapper returns createStripeProvider(client, {
...options, webhookSecret: options.webhookSecret ?? "" }) which injects an empty
string and leads to cryptic failures in Stripe.constructEventAsync; remove the
empty-string default and instead pass webhookSecret only if present (e.g., leave
it undefined when missing) and add validation inside createStripeProvider to
throw a clear error when webhookSecret is missing while allowUnsignedPayload is
false (referencing createStripeProvider, webhookSecret, allowUnsignedPayload,
and constructEventAsync).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ea71ac5a-720a-412d-bdab-f1b69a2c6b18
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (104)
README.mdapps/demo/drizzle.config.tsapps/demo/next.config.jsapps/demo/package.jsonapps/demo/paykit.config.tsapps/demo/paykit.polar.config.tsapps/demo/paykit.stripe.config.tsapps/demo/scripts/push-sandbox.tsapps/demo/scripts/sandbox.tsapps/demo/src/app/_components/checkout-page-content.tsxapps/demo/src/app/_components/features-panel.tsxapps/demo/src/app/_components/subscribe-panel.tsxapps/demo/src/app/paykit-polar/[[...slug]]/route.tsapps/demo/src/app/paykit-stripe/[[...slug]]/route.tsapps/demo/src/app/paykit/[[...slug]]/route.tsapps/demo/src/env.jsapps/demo/src/lib/paykit-client.tsapps/demo/src/lib/paykit-scenarios.tsapps/demo/src/lib/paykit.tsapps/demo/src/lib/paykit/polar.tsapps/demo/src/lib/paykit/stripe.tsapps/demo/src/lib/scenario-config.tsapps/demo/src/server/api/root.tsapps/demo/src/server/api/routers/paykit-route.tsapps/demo/src/server/db.tsapps/web/content/docs/concepts/cli.mdxapps/web/content/docs/concepts/payment-providers.mdxapps/web/content/docs/get-started/index.mdxapps/web/content/docs/get-started/installation.mdxapps/web/content/docs/providers/creem.mdxapps/web/content/docs/providers/lemonsqueezy.mdxapps/web/content/docs/providers/meta.jsonapps/web/content/docs/providers/paddle.mdxapps/web/content/docs/providers/paypal.mdxapps/web/content/docs/providers/polar.mdxapps/web/content/docs/providers/stripe.mdxapps/web/src/components/docs/docs-icons.tsxapps/web/src/components/docs/features.tsxapps/web/src/components/sections/demo/demo-types.tsxapps/web/src/components/sections/features-section.tsxapps/web/src/components/sections/readme-code-content.tse2e/cli/init.test.tse2e/cli/push.test.tse2e/cli/setup.tse2e/cli/status.test.tse2e/core/subscribe/cancel-end-of-cycle.test.tse2e/core/subscribe/renewal.test.tse2e/core/webhook/duplicate-webhook.test.tse2e/core/webhook/subscription-deleted.test.tse2e/package.jsone2e/test-utils/env.tse2e/test-utils/harness/index.tse2e/test-utils/harness/polar.tse2e/test-utils/harness/stripe.tse2e/test-utils/harness/types.tse2e/test-utils/setup.tse2e/vitest.config.tspackages/paykit/package.jsonpackages/paykit/src/api/__tests__/define-route.test.tspackages/paykit/src/api/__tests__/methods.test.tspackages/paykit/src/api/methods.tspackages/paykit/src/cli/__tests__/init.test.tspackages/paykit/src/cli/commands/init.tspackages/paykit/src/cli/commands/listen.tspackages/paykit/src/cli/commands/push.tspackages/paykit/src/cli/commands/status.tspackages/paykit/src/cli/utils/get-config.tspackages/paykit/src/cli/utils/shared.tspackages/paykit/src/core/__tests__/context.test.tspackages/paykit/src/core/__tests__/create-paykit.test.tspackages/paykit/src/core/context.tspackages/paykit/src/core/create-paykit.tspackages/paykit/src/customer/__tests__/customer.service.test.tspackages/paykit/src/customer/customer.service.tspackages/paykit/src/database/migrations/0001_stripe_only_schema.sqlpackages/paykit/src/database/migrations/meta/0001_snapshot.jsonpackages/paykit/src/database/migrations/meta/_journal.jsonpackages/paykit/src/database/schema.tspackages/paykit/src/index.tspackages/paykit/src/invoice/invoice.service.tspackages/paykit/src/payment-method/payment-method.service.tspackages/paykit/src/payment/payment.service.tspackages/paykit/src/product/product.service.tspackages/paykit/src/providers/provider.tspackages/paykit/src/stripe/stripe-provider.tspackages/paykit/src/subscription/subscription.service.tspackages/paykit/src/testing/testing.service.tspackages/paykit/src/types/instance.tspackages/paykit/src/types/options.tspackages/paykit/src/utilities/dependencies/paykit-package-list.tspackages/paykit/src/webhook/webhook.api.tspackages/paykit/src/webhook/webhook.service.tspackages/polar/package.jsonpackages/polar/src/index.tspackages/polar/src/polar-provider.tspackages/polar/tsconfig.jsonpackages/polar/tsdown.config.tspackages/stripe/package.jsonpackages/stripe/src/__tests__/stripe-provider.test.tspackages/stripe/src/__tests__/stripe.test.tspackages/stripe/src/index.tspackages/stripe/tsconfig.jsonpackages/stripe/tsdown.config.tsscripts/publish-dist.mjs
💤 Files with no reviewable changes (27)
- e2e/package.json
- packages/polar/tsconfig.json
- apps/web/content/docs/providers/paypal.mdx
- apps/web/content/docs/providers/paddle.mdx
- apps/demo/paykit.polar.config.ts
- apps/web/content/docs/providers/creem.mdx
- apps/web/content/docs/providers/polar.mdx
- packages/stripe/package.json
- apps/demo/src/lib/paykit-scenarios.ts
- apps/web/content/docs/providers/lemonsqueezy.mdx
- packages/stripe/src/tests/stripe.test.ts
- packages/stripe/tsconfig.json
- packages/stripe/src/tests/stripe-provider.test.ts
- apps/demo/src/lib/paykit/stripe.ts
- apps/demo/src/lib/paykit/polar.ts
- packages/polar/tsdown.config.ts
- packages/polar/package.json
- packages/polar/src/index.ts
- apps/demo/src/lib/scenario-config.ts
- apps/demo/src/app/paykit-polar/[[...slug]]/route.ts
- packages/paykit/src/testing/testing.service.ts
- packages/stripe/src/index.ts
- apps/demo/src/app/paykit-stripe/[[...slug]]/route.ts
- packages/stripe/tsdown.config.ts
- e2e/test-utils/harness/polar.ts
- packages/polar/src/polar-provider.ts
- apps/demo/paykit.stripe.config.ts
✅ Files skipped from review due to trivial changes (12)
- apps/web/content/docs/providers/meta.json
- packages/paykit/src/cli/tests/init.test.ts
- apps/web/src/components/sections/features-section.tsx
- apps/web/content/docs/get-started/index.mdx
- apps/web/src/components/sections/demo/demo-types.tsx
- packages/paykit/src/database/migrations/meta/_journal.json
- apps/web/content/docs/providers/stripe.mdx
- packages/paykit/src/cli/utils/get-config.ts
- apps/web/content/docs/concepts/payment-providers.mdx
- apps/demo/next.config.js
- packages/paykit/src/database/migrations/meta/0001_snapshot.json
- README.md
🚧 Files skipped from review as they are similar to previous changes (54)
- packages/paykit/src/core/tests/create-paykit.test.ts
- e2e/cli/status.test.ts
- apps/demo/drizzle.config.ts
- scripts/publish-dist.mjs
- packages/paykit/src/utilities/dependencies/paykit-package-list.ts
- packages/paykit/src/api/tests/define-route.test.ts
- e2e/core/subscribe/cancel-end-of-cycle.test.ts
- e2e/core/subscribe/renewal.test.ts
- apps/web/src/components/docs/features.tsx
- e2e/core/webhook/duplicate-webhook.test.ts
- packages/paykit/src/invoice/invoice.service.ts
- packages/paykit/src/api/tests/methods.test.ts
- e2e/cli/push.test.ts
- apps/demo/src/app/paykit/[[...slug]]/route.ts
- apps/demo/src/lib/paykit-client.ts
- apps/demo/scripts/sandbox.ts
- apps/web/src/components/sections/readme-code-content.ts
- packages/paykit/src/types/options.ts
- apps/demo/paykit.config.ts
- packages/paykit/src/index.ts
- e2e/cli/init.test.ts
- e2e/core/webhook/subscription-deleted.test.ts
- apps/demo/src/server/api/root.ts
- packages/paykit/src/core/context.ts
- apps/web/content/docs/get-started/installation.mdx
- packages/paykit/package.json
- e2e/test-utils/harness/index.ts
- apps/demo/src/server/db.ts
- apps/demo/scripts/push-sandbox.ts
- e2e/test-utils/harness/types.ts
- packages/paykit/src/core/tests/context.test.ts
- e2e/test-utils/harness/stripe.ts
- packages/paykit/src/providers/provider.ts
- apps/demo/src/server/api/routers/paykit-route.ts
- packages/paykit/src/customer/customer.service.ts
- packages/paykit/src/payment/payment.service.ts
- e2e/vitest.config.ts
- apps/demo/src/env.js
- e2e/test-utils/env.ts
- e2e/test-utils/setup.ts
- packages/paykit/src/cli/utils/shared.ts
- apps/demo/src/app/_components/subscribe-panel.tsx
- packages/paykit/src/types/instance.ts
- packages/paykit/src/product/product.service.ts
- packages/paykit/src/api/methods.ts
- e2e/cli/setup.ts
- apps/demo/src/lib/paykit.ts
- packages/paykit/src/core/create-paykit.ts
- packages/paykit/src/cli/commands/listen.ts
- packages/paykit/src/webhook/webhook.api.ts
- packages/paykit/src/payment-method/payment-method.service.ts
- packages/paykit/src/webhook/webhook.service.ts
- packages/paykit/src/cli/commands/init.ts
- packages/paykit/src/subscription/subscription.service.ts
feat: add web v2 docs and landing updates
|
@vercel deploy |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/components/layout/navigation-bar.tsx (1)
186-197:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMake the desktop “links” dropdown keyboard-operable.
Line 189 renders a button, but visibility only changes on hover (Lines 186-187). Keyboard users can’t reliably open/access dropdown items.
Suggested fix
<motion.div @@ className="relative" onMouseEnter={openLinks} onMouseLeave={closeLinks} + onFocus={openLinks} + onBlur={(e) => { + if (!e.currentTarget.contains(e.relatedTarget as Node | null)) closeLinks(); + }} > <button type="button" + aria-haspopup="menu" + aria-expanded={linksOpen} + aria-controls="desktop-links-menu" + onClick={() => setLinksOpen((prev) => !prev)} className={`${tabBase} gap-1 ${linksOpen ? "text-foreground/70" : tabInactive}`} > @@ <motion.div + id="desktop-links-menu" + role="menu"Also applies to: 199-227
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/layout/navigation-bar.tsx` around lines 186 - 197, The "links" dropdown is only hover-controlled; make it keyboard-operable by wiring focus and key events into the same state handlers: call openLinks onFocus and closeLinks onBlur (in addition to onMouseEnter/onMouseLeave), add an onKeyDown handler on the button that toggles or opens the menu for Enter/Space and closes on Escape, and ensure the button includes accessible attributes like aria-expanded={linksOpen} and aria-controls pointing to the dropdown list id; update logic in the component using openLinks, closeLinks and linksOpen (and the button rendering around RiArrowDownSLine) so keyboard users can open, navigate, and close the dropdown.
🧹 Nitpick comments (13)
apps/web/src/app/(marketing)/blog/page.tsx (3)
8-14: 💤 Low valueConsider adding JSDoc for exported metadata.
Per coding guidelines, JSDoc should be added on user-facing API. This metadata object is exported and defines the page's SEO properties.
📝 Suggested JSDoc
+/** Metadata for the blog placeholder page. */ export const metadata: Metadata = {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(marketing)/blog/page.tsx around lines 8 - 14, Add a JSDoc comment above the exported metadata object to document the user-facing SEO properties; annotate what the exported symbol "metadata: Metadata" represents (page title, description, alternates/canonical URL) and any expectations for values/format. Place the JSDoc directly above the export of metadata in page.tsx (the exported constant "metadata") so IDEs and docs pick it up, using standard JSDoc tags like `@type` and a short description.Source: Coding guidelines
1-1: ⚡ Quick winUse
import typefor type-only imports.Per coding guidelines, type-only imports should use the
import typesyntax.♻️ Refactor to use type import
-import type { Metadata } from "next"; +import type { Metadata } from "next";Wait, that's the same. Let me check... actually line 1 shows
import type { Metadata }which is already correct. Let me reconsider.Actually looking at line 1:
import type { Metadata } from "next";- this is already usingimport type. This is correct. No issue here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(marketing)/blog/page.tsx at line 1, The import already uses type-only syntax so no change is needed; leave the existing import type { Metadata } from "next" in page.tsx as-is and do not modify it.Source: Coding guidelines
16-42: 💤 Low valueConsider adding JSDoc for the page component.
Per coding guidelines, user-facing functions (including page components) should include JSDoc documentation.
📝 Suggested JSDoc
+/** Blog placeholder page with "coming soon" message. */ export default function BlogPage() {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(marketing)/blog/page.tsx around lines 16 - 42, Add a JSDoc block above the BlogPage component to document the page per coding guidelines: include a one-line description (e.g., "Marketing blog landing page placeholder"), mention that it takes no props (or list props if added later), and add an `@returns` tag like "`@returns` JSX.Element" so tooling and readers know the component's intent and return type; place the comment immediately above the export default function BlogPage() declaration.Source: Coding guidelines
apps/web/src/components/ui/sonner.tsx (2)
13-43: 💤 Low valueConsider adding JSDoc for the Toaster component.
Per coding guidelines, UI components used in many places should include JSDoc documentation.
📝 Suggested JSDoc
+/** Toast notification component wrapper for Sonner with theme integration. */ const Toaster = ({ ...props }: ToasterProps) => {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/ui/sonner.tsx` around lines 13 - 43, Add a JSDoc block above the Toaster component describing its purpose, props, and default behavior: document the Toaster component (function Toaster), its ToasterProps parameter (including that it spreads {...props}), the theme defaulting via useTheme(), and the custom icons/style/toastOptions it applies; briefly mention expected types (ToasterProps["theme"] etc.) and any side-effects or usage notes so other developers know how to use and override it.Source: Coding guidelines
11-11: ⚡ Quick winSeparate type import from value import.
Per coding guidelines, use
import typefor type-only imports. TheToasterPropstype import should be separated from the value import.♻️ Separate the imports
-import { Toaster as Sonner, type ToasterProps } from "sonner"; +import { Toaster as Sonner } from "sonner"; +import type { ToasterProps } from "sonner";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/ui/sonner.tsx` at line 11, The import mixes a value and a type; separate them by importing the value default and the type with an `import type` to follow guidelines—replace the combined import of `Toaster as Sonner, type ToasterProps` with two imports: one importing the value `Toaster as Sonner` from "sonner" and a second `import type { ToasterProps }` from "sonner" so `Sonner` is a runtime value import and `ToasterProps` is a type-only import.Source: Coding guidelines
apps/web/src/components/ui/navigation-menu.tsx (1)
7-161: ⚖️ Poor tradeoffConsider adding JSDoc for exported UI components.
Per coding guidelines, JSDoc should be added on API used in many places. These navigation menu primitives are reusable UI components that would benefit from documentation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/ui/navigation-menu.tsx` around lines 7 - 161, Add JSDoc comments for the exported navigation menu components (NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuPositioner and navigationMenuTriggerStyle): for each export, add a short JSDoc block above its declaration describing its purpose, props (noting key props like align, side, sideOffset, alignOffset), any important behavior or className expectations, and usage examples where helpful; ensure the JSDoc uses `@param/`@returns/@example tags as appropriate and stays consistent with existing project doc style.Source: Coding guidelines
apps/web/src/components/docs/toc-footer.tsx (1)
7-43: 💤 Low valueConsider adding JSDoc for the component.
Per coding guidelines, JSDoc should be added on user-facing components. This table-of-contents footer is part of the docs UI.
📝 Suggested JSDoc
+/** Footer component displaying roadmap progress link in the docs TOC. */ export function TocFooter() {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/docs/toc-footer.tsx` around lines 7 - 43, Add a JSDoc block above the TocFooter component declaration describing the component's purpose (table-of-contents footer used in docs), note that it is user-facing, document any props (none currently) and returned JSX, and include brief accessibility or interaction notes (opens roadmap in new tab, shows progressValue). Reference the TocFooter function in the comment so reviewers can quickly locate it.Source: Coding guidelines
apps/web/src/components/web/footer.tsx (1)
7-70: 💤 Low valueConsider adding JSDoc for the Footer component.
Per coding guidelines, user-facing components should include JSDoc documentation. This footer component is part of the main layout.
📝 Suggested JSDoc
+/** Main footer component with copyright, social links, and theme toggle. */ export default function Footer() {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/web/footer.tsx` around lines 7 - 70, Add a JSDoc comment block above the Footer component (export default function Footer) describing that it renders the site footer with watermark SVG, decorative grid, copyright/year and social links; include `@component`, note that it accepts no props, mention accessibility concerns (aria-hidden on decorative elements and aria-labels on links), and any relevant tags like `@example` or `@returns` for clarity.Source: Coding guidelines
apps/web/src/app/not-found.tsx (1)
12-64: 💤 Low valueConsider adding JSDoc for the page component.
Per coding guidelines, user-facing functions should include JSDoc documentation. This 404 page component is user-facing.
📝 Suggested JSDoc
+/** Custom 404 error page displayed when a route is not found. */ export default function NotFound() {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/not-found.tsx` around lines 12 - 64, Add a JSDoc block above the exported NotFound component describing the page (e.g., "404 Not Found page shown when a route is missing"), note that it returns a JSX.Element and mention there are no props; place it directly above the function declaration for NotFound so tools and other devs can pick up the docstring (include a short one-line description and an `@returns` {JSX.Element} tag).Source: Coding guidelines
apps/web/src/components/web/brand-menu.tsx (1)
25-87: 💤 Low valueConsider adding JSDoc for the BrandMenu component.
Per coding guidelines, user-facing components should include JSDoc documentation. This brand menu with context menu actions is part of the navigation UI.
📝 Suggested JSDoc
+/** + * Brand menu component with context menu for copying logo/wordmark SVG. + * `@param` wordmarkBaseClassName - Base class for wordmark sizing (defaults to "h-4") + */ export function BrandMenu({🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/web/brand-menu.tsx` around lines 25 - 87, Add a JSDoc block above the BrandMenu function describing the component's purpose (a brand/logo link with a context menu to copy SVG assets), document each prop (className, linkClassName, wordmarkBaseClassName, wordmarkClassName) with their types and optional nature, and note any important behavior such as the context menu actions and the copyAsSvg helper used to copy brandAssets ("Logo" and "Wordmark") to the clipboard and display toast notifications; reference the BrandMenu function, the copyAsSvg helper, and the brandAssets keys in the JSDoc.Source: Coding guidelines
apps/web/src/components/ui/pagination.tsx (1)
7-118: ⚖️ Poor tradeoffConsider adding JSDoc for exported pagination components.
Per coding guidelines, JSDoc should be added on API used in many places. These pagination primitives are reusable UI components that would benefit from documentation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/ui/pagination.tsx` around lines 7 - 118, Add JSDoc comments above each exported pagination primitive (Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis) describing the component purpose, accepted props and types (mention PaginationLinkProps and its isActive/size/nativeButton behavior), key accessibility behaviors (aria-label/aria-current usage on PaginationLink, aria-hidden on PaginationEllipsis), and any important className/slot data attributes (data-slot values) or default prop values; keep each block concise (one-line summary + `@param` entries for props and types + `@returns`) so consumers and IDEs can pick up documentation and prop signatures.Source: Coding guidelines
apps/web/src/components/layout/section.tsx (1)
8-8: 💤 Low valueConsider removing separator comments per coding guidelines.
Per coding guidelines: "Keep comments rare and about code logic; do not add unnecessary separator comments." The section separator comments (lines 8, 34, 57, 69) could be considered decorative rather than explanatory.
Also applies to: 34-34, 57-57, 69-69
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/layout/section.tsx` at line 8, Remove the decorative separator comments (for example the line "// ─── Shared section line ─────────────────────────────────────────────" and the similar separators at the other occurrences) from apps/web/src/components/layout/section.tsx to follow the guideline of avoiding unnecessary comments; edit the Section component file to delete those separator comment lines (the ones at the top and the other two occurrences) so only meaningful, explanatory comments remain.Source: Coding guidelines
apps/web/src/components/sections/demo/index.tsx (1)
324-324: ⚡ Quick winAvoid >100% width allocation in the demo row.
lg:w-[73%]+lg:w-[37%](plus gap) can overflow the container on large screens and cause clipping/horizontal scroll.Suggested fix
- className={`${WINDOW_HEIGHT} md:w-1/2 lg:w-[73%]`} + className={`${WINDOW_HEIGHT} md:flex-1 lg:flex-[73]`} ... - className="h-58 md:h-144 md:w-1/2 lg:w-[37%]" + className="h-58 md:h-144 md:flex-1 lg:flex-[37]"Also applies to: 336-336
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/sections/demo/index.tsx` at line 324, The two JSX containers using className={`${WINDOW_HEIGHT} md:w-1/2 lg:w-[73%]`} and the corresponding sibling using `lg:w-[37%]` can exceed 100% width (plus gap) on large screens; update these className values so their lg: widths plus gap do not exceed 100% (for example switch to safe fractions like lg:w-2/3 + lg:w-1/3, or use flex utilities such as lg:flex and lg:flex-[2] / lg:flex-[1] or lg:w-3/5 + lg:w-2/5) and apply the change to both occurrences referenced in the file to prevent horizontal overflow and clipping.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/docs/copy-markdown-button.tsx`:
- Around line 24-47: The component currently reuses the single markdown state
across prop changes, causing stale cached text for new markdownUrl; fix this by
scoping the cache to the URL—either store a map keyed by markdownUrl or simply
reset the markdown cache when markdownUrl changes (e.g. add a useEffect that
calls setMarkdown(undefined) with [markdownUrl] as dependency). Ensure the
onClick closure continues to depend on markdownUrl and setMarkdown so it
reads/writes the cache for the current URL.
In `@apps/web/src/components/docs/docs-mdx-components.tsx`:
- Around line 56-64: getHeadingId currently calls children?.toString() which
collapses rich MDX nodes into "[object Object]" causing bad/duplicate anchors;
replace that with a recursive text-extraction helper (e.g.,
extractTextFromChildren) that traverses React
nodes/arrays/fragments/numbers/strings and concatenates text from
props.children, then run the existing lowercasing/replace/trim/space-to-dash
logic on that extracted text; keep the final fallback to a stable string like
"heading" only when the extracted text is empty. Ensure you update getHeadingId
to call this helper and reference the helper in tests if present.
In `@apps/web/src/components/docs/mdx-text.tsx`:
- Around line 42-45: The Link element in mdx-text.tsx sets target based on
_blank but doesn't set rel, exposing window.opener; update the Link props (the
JSX using Link with href, target={_blank ? "_blank" : "_self"},
className={cn(...)}) to include a rel attribute when _blank is true (e.g.,
rel="noopener noreferrer")—apply this conditionally using the same _blank
boolean so external blank targets get rel="noopener noreferrer" while
internal/_self links omit or use a safe default.
In `@apps/web/src/components/docs/package-command-utils.ts`:
- Around line 31-33: The "run" case currently returns npm-run for manager ===
"npm" and otherwise builds `${manager} ${command.args}`, which yields `bun
<script>` and can invoke Bun subcommands; update the mapping in the case "run"
branch to explicitly use `bun run ${command.args}` when manager === "bun" and
fall back to `${manager} ${command.args}` for other managers (i.e., change the
ternary/branching in the case "run" handling to check manager === "bun" and
return `bun run ...`).
In `@apps/web/src/components/docs/package-command.tsx`:
- Around line 296-299: The CopyButton is only exposed via hover (className uses
group-hover:opacity-100), so keyboard users can focus it while it remains
visually hidden; update the CopyButton container className to also reveal on
keyboard focus (e.g., add focus:opacity-100 and focus-visible:opacity-100 and/or
group-focus:opacity-100) so the button becomes visible when focused, and ensure
the CopyButton itself remains keyboard-focusable (no tabIndex=-1 or aria-hidden)
so its code prop behavior continues to work.
In `@apps/web/src/components/layout/navigation-bar.tsx`:
- Line 232: Fix the invalid Tailwind utility in the NavigationBar component:
locate the JSX element that sets className to "relative z-10 flex items-center
gap-0." (the container div in the navigation-bar component) and remove the
trailing dot so the class becomes "gap-0" (or replace with an appropriate gap
value, e.g., "gap-2") to ensure the Tailwind utility is recognized.
---
Outside diff comments:
In `@apps/web/src/components/layout/navigation-bar.tsx`:
- Around line 186-197: The "links" dropdown is only hover-controlled; make it
keyboard-operable by wiring focus and key events into the same state handlers:
call openLinks onFocus and closeLinks onBlur (in addition to
onMouseEnter/onMouseLeave), add an onKeyDown handler on the button that toggles
or opens the menu for Enter/Space and closes on Escape, and ensure the button
includes accessible attributes like aria-expanded={linksOpen} and aria-controls
pointing to the dropdown list id; update logic in the component using openLinks,
closeLinks and linksOpen (and the button rendering around RiArrowDownSLine) so
keyboard users can open, navigate, and close the dropdown.
---
Nitpick comments:
In `@apps/web/src/app/`(marketing)/blog/page.tsx:
- Around line 8-14: Add a JSDoc comment above the exported metadata object to
document the user-facing SEO properties; annotate what the exported symbol
"metadata: Metadata" represents (page title, description, alternates/canonical
URL) and any expectations for values/format. Place the JSDoc directly above the
export of metadata in page.tsx (the exported constant "metadata") so IDEs and
docs pick it up, using standard JSDoc tags like `@type` and a short description.
- Line 1: The import already uses type-only syntax so no change is needed; leave
the existing import type { Metadata } from "next" in page.tsx as-is and do not
modify it.
- Around line 16-42: Add a JSDoc block above the BlogPage component to document
the page per coding guidelines: include a one-line description (e.g., "Marketing
blog landing page placeholder"), mention that it takes no props (or list props
if added later), and add an `@returns` tag like "`@returns` JSX.Element" so tooling
and readers know the component's intent and return type; place the comment
immediately above the export default function BlogPage() declaration.
In `@apps/web/src/app/not-found.tsx`:
- Around line 12-64: Add a JSDoc block above the exported NotFound component
describing the page (e.g., "404 Not Found page shown when a route is missing"),
note that it returns a JSX.Element and mention there are no props; place it
directly above the function declaration for NotFound so tools and other devs can
pick up the docstring (include a short one-line description and an `@returns`
{JSX.Element} tag).
In `@apps/web/src/components/docs/toc-footer.tsx`:
- Around line 7-43: Add a JSDoc block above the TocFooter component declaration
describing the component's purpose (table-of-contents footer used in docs), note
that it is user-facing, document any props (none currently) and returned JSX,
and include brief accessibility or interaction notes (opens roadmap in new tab,
shows progressValue). Reference the TocFooter function in the comment so
reviewers can quickly locate it.
In `@apps/web/src/components/layout/section.tsx`:
- Line 8: Remove the decorative separator comments (for example the line "// ───
Shared section line ─────────────────────────────────────────────" and the
similar separators at the other occurrences) from
apps/web/src/components/layout/section.tsx to follow the guideline of avoiding
unnecessary comments; edit the Section component file to delete those separator
comment lines (the ones at the top and the other two occurrences) so only
meaningful, explanatory comments remain.
In `@apps/web/src/components/sections/demo/index.tsx`:
- Line 324: The two JSX containers using className={`${WINDOW_HEIGHT} md:w-1/2
lg:w-[73%]`} and the corresponding sibling using `lg:w-[37%]` can exceed 100%
width (plus gap) on large screens; update these className values so their lg:
widths plus gap do not exceed 100% (for example switch to safe fractions like
lg:w-2/3 + lg:w-1/3, or use flex utilities such as lg:flex and lg:flex-[2] /
lg:flex-[1] or lg:w-3/5 + lg:w-2/5) and apply the change to both occurrences
referenced in the file to prevent horizontal overflow and clipping.
In `@apps/web/src/components/ui/navigation-menu.tsx`:
- Around line 7-161: Add JSDoc comments for the exported navigation menu
components (NavigationMenu, NavigationMenuContent, NavigationMenuIndicator,
NavigationMenuItem, NavigationMenuLink, NavigationMenuList,
NavigationMenuTrigger, NavigationMenuPositioner and navigationMenuTriggerStyle):
for each export, add a short JSDoc block above its declaration describing its
purpose, props (noting key props like align, side, sideOffset, alignOffset), any
important behavior or className expectations, and usage examples where helpful;
ensure the JSDoc uses `@param/`@returns/@example tags as appropriate and stays
consistent with existing project doc style.
In `@apps/web/src/components/ui/pagination.tsx`:
- Around line 7-118: Add JSDoc comments above each exported pagination primitive
(Pagination, PaginationContent, PaginationItem, PaginationLink,
PaginationPrevious, PaginationNext, PaginationEllipsis) describing the component
purpose, accepted props and types (mention PaginationLinkProps and its
isActive/size/nativeButton behavior), key accessibility behaviors
(aria-label/aria-current usage on PaginationLink, aria-hidden on
PaginationEllipsis), and any important className/slot data attributes (data-slot
values) or default prop values; keep each block concise (one-line summary +
`@param` entries for props and types + `@returns`) so consumers and IDEs can pick up
documentation and prop signatures.
In `@apps/web/src/components/ui/sonner.tsx`:
- Around line 13-43: Add a JSDoc block above the Toaster component describing
its purpose, props, and default behavior: document the Toaster component
(function Toaster), its ToasterProps parameter (including that it spreads
{...props}), the theme defaulting via useTheme(), and the custom
icons/style/toastOptions it applies; briefly mention expected types
(ToasterProps["theme"] etc.) and any side-effects or usage notes so other
developers know how to use and override it.
- Line 11: The import mixes a value and a type; separate them by importing the
value default and the type with an `import type` to follow guidelines—replace
the combined import of `Toaster as Sonner, type ToasterProps` with two imports:
one importing the value `Toaster as Sonner` from "sonner" and a second `import
type { ToasterProps }` from "sonner" so `Sonner` is a runtime value import and
`ToasterProps` is a type-only import.
In `@apps/web/src/components/web/brand-menu.tsx`:
- Around line 25-87: Add a JSDoc block above the BrandMenu function describing
the component's purpose (a brand/logo link with a context menu to copy SVG
assets), document each prop (className, linkClassName, wordmarkBaseClassName,
wordmarkClassName) with their types and optional nature, and note any important
behavior such as the context menu actions and the copyAsSvg helper used to copy
brandAssets ("Logo" and "Wordmark") to the clipboard and display toast
notifications; reference the BrandMenu function, the copyAsSvg helper, and the
brandAssets keys in the JSDoc.
In `@apps/web/src/components/web/footer.tsx`:
- Around line 7-70: Add a JSDoc comment block above the Footer component (export
default function Footer) describing that it renders the site footer with
watermark SVG, decorative grid, copyright/year and social links; include
`@component`, note that it accepts no props, mention accessibility concerns
(aria-hidden on decorative elements and aria-labels on links), and any relevant
tags like `@example` or `@returns` for clarity.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: add29d97-2e9b-4f2b-965a-727ff3e0b20b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (117)
AGENTS.mdapps/web/content/docs/cli.mdxapps/web/content/docs/client.mdxapps/web/content/docs/concepts/meta.jsonapps/web/content/docs/concepts/payment-providers.mdxapps/web/content/docs/customers.mdxapps/web/content/docs/dashboard.mdxapps/web/content/docs/database.mdxapps/web/content/docs/entitlements.mdxapps/web/content/docs/flows/meta.jsonapps/web/content/docs/get-started/meta.jsonapps/web/content/docs/guides/meta.jsonapps/web/content/docs/installation.mdxapps/web/content/docs/introduction.mdxapps/web/content/docs/meta.jsonapps/web/content/docs/metered-usage.mdxapps/web/content/docs/plans-and-features.mdxapps/web/content/docs/plugins.mdxapps/web/content/docs/plugins/meta.jsonapps/web/content/docs/providers/meta.jsonapps/web/content/docs/providers/stripe.mdxapps/web/content/docs/quickstart.mdxapps/web/content/docs/skills.mdxapps/web/content/docs/subscription-billing.mdxapps/web/content/docs/subscriptions.mdxapps/web/content/docs/typescript.mdxapps/web/content/docs/webhook-events.mdxapps/web/content/drafts/docs-index.mdxapps/web/mdx-components.tsxapps/web/next.config.jsapps/web/package.jsonapps/web/source.config.tsapps/web/src/app/(marketing)/blog/page.tsxapps/web/src/app/(marketing)/contact/contact-form.tsxapps/web/src/app/(marketing)/donate/page.tsxapps/web/src/app/(marketing)/layout.tsxapps/web/src/app/(marketing)/page.tsxapps/web/src/app/(marketing)/sponsor/page.tsxapps/web/src/app/api/og/[[...slug]]/route.tsxapps/web/src/app/docs/[[...slug]]/page.tsxapps/web/src/app/docs/layout.tsxapps/web/src/app/layout.tsxapps/web/src/app/llms.txt/route.tsapps/web/src/app/not-found.tsxapps/web/src/components/command-menu.tsxapps/web/src/components/docs/copy-markdown-button.tsxapps/web/src/components/docs/docs-code-surface.tsxapps/web/src/components/docs/docs-icons.tsxapps/web/src/components/docs/docs-layout.tsxapps/web/src/components/docs/docs-mdx-components.tsxapps/web/src/components/docs/docs-page.tsxapps/web/src/components/docs/features.tsxapps/web/src/components/docs/mdx-tabs.tsxapps/web/src/components/docs/mdx-text.tsxapps/web/src/components/docs/package-command-pre.tsxapps/web/src/components/docs/package-command-utils.tsapps/web/src/components/docs/package-command.tsxapps/web/src/components/docs/package-manager-state.tsapps/web/src/components/docs/react-node-text.tsapps/web/src/components/docs/sidebar-category-accordion.tsxapps/web/src/components/docs/sidebar-collapse-button.tsxapps/web/src/components/docs/toc-footer.tsxapps/web/src/components/icons/index.tsxapps/web/src/components/layout/mini-nav-bar.tsxapps/web/src/components/layout/navigation-bar.tsxapps/web/src/components/layout/section.tsxapps/web/src/components/providers.tsxapps/web/src/components/sections/cta-section.tsxapps/web/src/components/sections/demo/demo-app-window.tsxapps/web/src/components/sections/demo/demo-backend-panel.tsxapps/web/src/components/sections/demo/demo-types.tsxapps/web/src/components/sections/demo/index.tsxapps/web/src/components/sections/features-section.tsxapps/web/src/components/sections/feedback-content.tsapps/web/src/components/sections/feedback-section.tsxapps/web/src/components/sections/footer-section.tsxapps/web/src/components/sections/hero-section.tsxapps/web/src/components/sections/readme-code-content.tsapps/web/src/components/sections/testimonials-section.tsxapps/web/src/components/sidebar-content.tsxapps/web/src/components/theme-switcher.tsxapps/web/src/components/ui/accordion.tsxapps/web/src/components/ui/breadcrumb.tsxapps/web/src/components/ui/button-group.tsxapps/web/src/components/ui/button.tsxapps/web/src/components/ui/calendar.tsxapps/web/src/components/ui/carousel.tsxapps/web/src/components/ui/checkbox.tsxapps/web/src/components/ui/code-block-content.tsxapps/web/src/components/ui/code-block.tsxapps/web/src/components/ui/combobox.tsxapps/web/src/components/ui/command.tsxapps/web/src/components/ui/context-menu.tsxapps/web/src/components/ui/dialog.tsxapps/web/src/components/ui/dropdown-menu.tsxapps/web/src/components/ui/dynamic-code-block.tsxapps/web/src/components/ui/frame-corners.tsxapps/web/src/components/ui/input-otp.tsxapps/web/src/components/ui/menubar.tsxapps/web/src/components/ui/native-select.tsxapps/web/src/components/ui/navigation-menu.tsxapps/web/src/components/ui/pagination.tsxapps/web/src/components/ui/select.tsxapps/web/src/components/ui/sheet.tsxapps/web/src/components/ui/sidebar.tsxapps/web/src/components/ui/sonner.tsxapps/web/src/components/ui/spinner.tsxapps/web/src/components/web/brand-menu.tsxapps/web/src/components/web/footer.tsxapps/web/src/components/web/hero-code-block.tsxapps/web/src/components/web/hero-title.tsxapps/web/src/lib/consts.tsapps/web/src/lib/lucide-react-remix-shim.tsapps/web/src/lib/shiki-themes.tsapps/web/src/lib/shiki-themes/shiki-aura-theme.tsapps/web/src/styles/globals.cssdev/DESIGN.md
💤 Files with no reviewable changes (16)
- apps/web/content/docs/concepts/meta.json
- apps/web/content/docs/flows/meta.json
- apps/web/src/components/docs/sidebar-category-accordion.tsx
- apps/web/content/docs/introduction.mdx
- apps/web/content/docs/get-started/meta.json
- apps/web/src/app/(marketing)/donate/page.tsx
- apps/web/src/lib/consts.ts
- apps/web/content/docs/guides/meta.json
- apps/web/src/components/docs/sidebar-collapse-button.tsx
- apps/web/content/docs/concepts/payment-providers.mdx
- apps/web/src/components/command-menu.tsx
- apps/web/content/docs/plugins/meta.json
- apps/web/src/components/sections/testimonials-section.tsx
- apps/web/src/components/sections/features-section.tsx
- apps/web/content/docs/providers/meta.json
- apps/web/content/docs/providers/stripe.mdx
✅ Files skipped from review due to trivial changes (22)
- apps/web/content/docs/database.mdx
- apps/web/src/components/docs/docs-code-surface.tsx
- apps/web/src/app/llms.txt/route.ts
- apps/web/src/components/ui/breadcrumb.tsx
- apps/web/src/components/ui/button.tsx
- apps/web/src/app/(marketing)/sponsor/page.tsx
- apps/web/content/drafts/docs-index.mdx
- apps/web/content/docs/skills.mdx
- apps/web/src/components/ui/sidebar.tsx
- apps/web/src/components/ui/sheet.tsx
- apps/web/src/app/layout.tsx
- apps/web/content/docs/typescript.mdx
- apps/web/content/docs/quickstart.mdx
- apps/web/content/docs/plugins.mdx
- AGENTS.md
- apps/web/content/docs/metered-usage.mdx
- apps/web/src/app/(marketing)/contact/contact-form.tsx
- dev/DESIGN.md
- apps/web/src/components/ui/select.tsx
- apps/web/content/docs/meta.json
- apps/web/content/docs/client.mdx
- apps/web/content/docs/subscription-billing.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/src/components/sections/readme-code-content.ts
- apps/web/src/components/docs/docs-icons.tsx
Summary
Verification
Summary by CodeRabbit
Release Notes
New Features
Documentation
Style