Skip to content

Conversation

@kkerti
Copy link
Contributor

@kkerti kkerti commented Nov 27, 2024

Note: this PR has been remade, as it was originally branched off from master and made against master.

Description

As briefly described in #3218, this PR adds new feature to the core BraintreePlugin, extending the plugin options with an optional object named merchantAccountIds.

  1. Usage of Braintree Merchant Account Ids are optional, as without them, the Braintree account's default currency (and Merchant Id) is used for authorising transactions.
// src/braintree/types.ts
export interface BraintreePluginOptions {
  //...other defs
  merchantAccountIds?: BraintreeMerchantAccountIds;
}

// e2e/braintree-dev-server.ts
// Plugin initialisation with currency-merchantAccountId key and value pairs.
BraintreePlugin.init({
    //...other config
    merchantAccountIds: {
        USD: process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_USD,
        EUR: process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_EUR,
    },
}),
  1. Two of braintree's nodejs API use merchantAccountId, the gateway.clientToken.generate() and gateway.transaction.sale(). Both of these methods accept the merchantAccountId with type string | undefined. To look up the correct merchantAccountId to use, we are using the active order.currencyCode and the plugin options. I did not implement heavy input error checks as the plugin option validity is evaluated runtime thanks to typescript.
// src/braintree/braintree.handler.ts
// used in clientToken generation and processPayment
const merchantAccountId = lookupMerchantAccountIdByCurrency(
    options.merchantAccountIds,
    order.currencyCode,
);

// src/braintree/braintree-common.ts
export function lookupMerchantAccountIdByCurrency(
    merchantAccountIds: BraintreeMerchantAccountIds | undefined,
    currencyCode: CurrencyCode,
): string | undefined {
    if (!merchantAccountIds || !currencyCode) {
        return undefined;
    }
    const merchantAccountIdForCurrency = merchantAccountIds[currencyCode];
    return merchantAccountIdForCurrency;
}
  1. There was no development server setup in the Braintree plugin, so I took inspiration from the Mollie and Stripe examples. The heavy lifting is done here by using simply a custom payment handler, clientToken generation and relying on the braintree-drop-in UI client side. The braintree-dev-server sets up an Order, logs info for the developer and exposes the shopClient and generated clientToken for a test plugin found under /fixtures/braintree-checkout-test.plugin.ts.
  2. The additional test plugin exposes a controller with the checkout UI, similar to the Stripe fixture plugin. Results of the attempted payments are dumped in <div id="result"/>.
image
  1. I added a BraintreeTestPlugin, but I did not add any automated tests, as the nonce generated by the client drop-in UI doesn't have fake-currency-mismatch-nonce test value. The drop-in UI in the browser has to be manually completed and the value sent to the vendure server. I think the development server introduced here helps a lot in this, but I'm open for suggestions to make complete e2e here.

Possible improvement

I think it's not optimal to add the merchantAccountId values during plugin initialisation, this would be better off as args for the payment handler itself. But I would rather wait for the new struct type to create the currency-merchantAccountId pairs as handler arguments to make it bullet-proof.

Breaking changes

As far as I'm concerned, there are none.

Checklist

📌 Always:

  • I have set a clear title
  • My PR is small and contains a single feature
  • I have checked my own PR

👍 Most of the time:

  • I have added or updated test cases - added braintree-dev-server for manual testing
  • I have updated the README if needed

Summary by CodeRabbit

  • New Features

    • Added support for configuring Braintree merchant account IDs by currency, enabling multi-currency payments.
    • Introduced a development server and test checkout page for Braintree, allowing local testing with real sandbox credentials.
    • Added a script to easily start the Braintree development server.
  • Documentation

    • Updated documentation with detailed instructions for local Braintree plugin development and testing.
    • Added comprehensive guides for extending the dashboard with custom form components, navigation, action bar items, page blocks, and custom strategies in plugins.
    • Added new GitHub Actions workflows for publishing and stale issue management.
    • Improved contributing guidelines and README with clearer setup and development instructions.
  • Style

    • Minor formatting improvements for code consistency.

@vercel
Copy link

vercel bot commented Nov 27, 2024

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs Ready Ready Preview Nov 4, 2025 8:00pm
vendure-storybook Ready Ready Preview Comment Nov 4, 2025 8:00pm

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
7.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@kkerti kkerti changed the title Feat/braintree plugin multi currency feat(payments-plugin): Add multi currency support for braintree plugin Nov 27, 2024
@michaelbromley
Copy link
Member

Hi!

Thanks for this contribution. I'll need to find some time to properly review & test this - unfortunately it's unlikely in the next week due to workload.

@michaelbromley
Copy link
Member

Thanks for the nudge :)

This is an amazing PR - great work on all the supporting parts for testing 👏

OK so it's now May 2025 and we have the struct type for custom fields - you mention above that you'd like to use that rather than hard-coded config. Do you want to go ahead with that?

You could add a flag to the init options, like multipleMerchantAccountSupport: boolean so that existing users don't need to do a migration for the custom field. Then you can check the value of that in the plugin's configuration function:

configuration: config => {
  if (BraintreePlugin.options.multipleMerchantAccountSupport === true) {
    config.customfields.PaymentMethod.push(...)
  }
  return config;
}

@michaelbromley michaelbromley added this to the v3.4.0 milestone May 16, 2025
@michaelbromley michaelbromley moved this from 👀 Under consideration to 📦 Backlog in Vendure OS Roadmap May 16, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 30, 2025

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

💤 Files selected but had no reviewable changes (1)
  • packages/payments-plugin/src/braintree/braintree-common.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes introduce a local development and testing setup for the Braintree payment plugin, including documentation, dev server scripts, and a test checkout UI. The Braintree plugin is enhanced to support multi-currency merchant account IDs, allowing merchant account selection based on order currency. Supporting types and utility functions are added, and related test infrastructure is updated.

Changes

File(s) Change Summary
README.md, package.json Added Braintree local development instructions and a dev server npm script.
e2e/braintree-dev-server.ts, e2e/fixtures/braintree-checkout-test.plugin.ts Added dev server and test plugin/controller for Braintree, enabling local checkout UI and payment flow testing.
e2e/payment-helpers.ts Added GraphQL query constant for generating Braintree client tokens.
e2e/stripe-dev-server.ts, e2e/fixtures/stripe-checkout-test.plugin.ts Updated import paths and fixed minor HTML structure in Stripe test files.
src/braintree/types.ts Added types for merchant account ID mapping by currency and updated plugin options interface.
src/braintree/braintree-common.ts Added utility function to look up merchant account IDs by currency code.
src/braintree/braintree.handler.ts, src/braintree/braintree.resolver.ts Integrated merchant account ID lookup by currency into payment processing and client token generation.
src/braintree/braintree.plugin.ts Added a blank line before the plugin decorator (no functional change).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant BraintreeTestCheckoutController
    participant BraintreeDevServer
    participant ShopGraphQLClient
    participant BraintreePlugin

    User->>BraintreeTestCheckoutController: GET /checkout
    BraintreeTestCheckoutController->>BraintreeDevServer: Get clientToken
    BraintreeDevServer->>ShopGraphQLClient: Query generateBraintreeClientToken
    ShopGraphQLClient->>BraintreePlugin: generateBraintreeClientToken (currency)
    BraintreePlugin->>BraintreePlugin: lookupMerchantAccountIdByCurrency
    BraintreePlugin-->>ShopGraphQLClient: Return clientToken
    ShopGraphQLClient-->>BraintreeDevServer: Return clientToken
    BraintreeDevServer-->>BraintreeTestCheckoutController: Return clientToken
    BraintreeTestCheckoutController-->>User: Serve checkout HTML with clientToken

    User->>BraintreeTestCheckoutController: POST /checkout (payment payload)
    BraintreeTestCheckoutController->>ShopGraphQLClient: Mutate addPaymentToOrder
    ShopGraphQLClient->>BraintreePlugin: processPayment (currency, merchantAccountIds)
    BraintreePlugin->>BraintreePlugin: lookupMerchantAccountIdByCurrency
    BraintreePlugin-->>ShopGraphQLClient: Return payment result
    ShopGraphQLClient-->>BraintreeTestCheckoutController: Return payment result
    BraintreeTestCheckoutController-->>User: Return payment result
Loading
sequenceDiagram
    participant Admin
    participant BraintreeDevServer
    participant AdminGraphQLClient
    participant BraintreePlugin

    Admin->>BraintreeDevServer: Start dev server
    BraintreeDevServer->>AdminGraphQLClient: Mutate createPaymentMethod (with merchantAccountIds)
    AdminGraphQLClient->>BraintreePlugin: Register payment method
    BraintreePlugin-->>AdminGraphQLClient: Acknowledge registration
    AdminGraphQLClient-->>BraintreeDevServer: Payment method created
    BraintreeDevServer-->>Admin: Dev server ready, checkout URL logged
Loading

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (7)
packages/payments-plugin/e2e/fixtures/stripe-checkout-test.plugin.ts (1)

22-22: HTML structure is still malformed

Placing <html> after the <head> tag violates the DOM hierarchy. For a minimal fix:

-<head>
+<html>
+<head>-</head>
-
-<html>
+</head>
+<body>+</body>
 </html>

A correct structure avoids unpredictable rendering in some browsers.

packages/payments-plugin/README.md (1)

46-46: Format the URL as a proper markdown link.

For consistency with other documentation, wrap the bare URL in markdown link syntax.

-1. Get the test keys from your Braintree dashboard, under menu settings, API section: https://sandbox.braintreegateway.com
+1. Get the test keys from your [Braintree dashboard](https://sandbox.braintreegateway.com), under menu settings, API section.
packages/payments-plugin/src/braintree/braintree-common.ts (1)

81-81: Fix the typo in the JSDoc comment.

There's a duplicated 'm' in "mmerchantAccountId" in the description.

- * Looks up a single mmerchantAccountId from `merchantAccountIds` passed through the plugin options.
+ * Looks up a single merchantAccountId from `merchantAccountIds` passed through the plugin options.
packages/payments-plugin/e2e/braintree-dev-server.ts (1)

70-73: Consider adding environment variable validation.

The merchant account IDs are configured from environment variables without validation. If these variables are undefined, they could cause issues during testing.

            merchantAccountIds: {
-                USD: process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_USD,
-                EUR: process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_EUR,
+                ...(process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_USD && { USD: process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_USD }),
+                ...(process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_EUR && { EUR: process.env.BRAINTREE_MERCHANT_ACCOUNT_ID_EUR }),
            },
packages/payments-plugin/e2e/fixtures/braintree-checkout-test.plugin.ts (3)

38-42: Consider making PayPal configuration dynamic.

The PayPal flow configuration uses hard-coded values for amount and currency. Consider making these configurable or dynamic based on the actual order.

        paypal: {
            flow: 'checkout',
-            amount: 100,
-            currency: 'GBP',
+            amount: 10.00, // Consider making this dynamic based on order total
+            currency: 'USD', // Consider making this match the order currency
        },

110-110: Fix the incorrect comment.

The comment mentions "Stripe intent checkout page" but this is a Braintree plugin.

/**
- * Test plugin for serving the Stripe intent checkout page
+ * Test plugin for serving the Braintree drop-in checkout page
 */

69-85: Add error handling to the payment submission function.

The sendPayloadToServer function could benefit from more robust error handling.

    async function sendPayloadToServer(payload) {
+        try {
            const response = await fetch('checkout', {
                method: 'POST', 
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',       
                    'Credentials': 'include',
                },
                body: JSON.stringify(payload)
            })
-            .then(res => res.json())
-            .catch(err => console.error(err))
+            .then(res => {
+                if (!res.ok) {
+                    throw new Error(`HTTP error! status: ${res.status}`);
+                }
+                return res.json();
+            });

            document.querySelector('#result').innerHTML = JSON.stringify(response)
            console.log(response)
+        } catch (err) {
+            console.error('Payment submission failed:', err);
+            document.querySelector('#result').innerHTML = `Error: ${err.message}`;
+        }
    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbf0016 and 8c68d8f.

📒 Files selected for processing (12)
  • packages/payments-plugin/README.md (1 hunks)
  • packages/payments-plugin/e2e/braintree-dev-server.ts (1 hunks)
  • packages/payments-plugin/e2e/fixtures/braintree-checkout-test.plugin.ts (1 hunks)
  • packages/payments-plugin/e2e/fixtures/stripe-checkout-test.plugin.ts (2 hunks)
  • packages/payments-plugin/e2e/payment-helpers.ts (1 hunks)
  • packages/payments-plugin/e2e/stripe-dev-server.ts (1 hunks)
  • packages/payments-plugin/package.json (2 hunks)
  • packages/payments-plugin/src/braintree/braintree-common.ts (2 hunks)
  • packages/payments-plugin/src/braintree/braintree.handler.ts (2 hunks)
  • packages/payments-plugin/src/braintree/braintree.plugin.ts (1 hunks)
  • packages/payments-plugin/src/braintree/braintree.resolver.ts (3 hunks)
  • packages/payments-plugin/src/braintree/types.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/payments-plugin/e2e/stripe-dev-server.ts (1)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: In packages/dashboard, when performing mutations with useMutation, do not pass variables at the point of declaration; instead, pass them when calling the mutate function. Type information should flow from the graphql function, so explicit typings or generics are not needed.
🧬 Code Graph Analysis (4)
packages/payments-plugin/src/braintree/braintree.handler.ts (1)
packages/payments-plugin/src/braintree/braintree-common.ts (1)
  • lookupMerchantAccountIdByCurrency (86-95)
packages/payments-plugin/src/braintree/types.ts (2)
packages/payments-plugin/src/braintree/braintree.handler.ts (1)
  • braintreePaymentMethodHandler (25-87)
packages/core/src/index.ts (1)
  • CurrencyCode (23-23)
packages/payments-plugin/src/braintree/braintree-common.ts (2)
packages/payments-plugin/src/braintree/types.ts (1)
  • BraintreeMerchantAccountIds (9-9)
packages/core/src/index.ts (1)
  • CurrencyCode (23-23)
packages/payments-plugin/src/braintree/braintree.resolver.ts (1)
packages/payments-plugin/src/braintree/braintree-common.ts (2)
  • getGateway (6-13)
  • lookupMerchantAccountIdByCurrency (86-95)
🪛 LanguageTool
packages/payments-plugin/README.md

[uncategorized] ~61-~61: Possible missing comma found.
Context: ...er:braintree3. Watch the logs for the link or go tohttp://localhost:3050/checkou...

(AI_HYDRA_LEO_MISSING_COMMA)

🪛 markdownlint-cli2 (0.17.2)
packages/payments-plugin/README.md

46-46: Bare URL used
null

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: e2e tests (22.x, sqljs)
  • GitHub Check: e2e tests (22.x, mariadb)
  • GitHub Check: e2e tests (22.x, postgres)
  • GitHub Check: e2e tests (20.x, mysql)
  • GitHub Check: e2e tests (20.x, postgres)
  • GitHub Check: e2e tests (22.x, mysql)
  • GitHub Check: e2e tests (20.x, mariadb)
  • GitHub Check: e2e tests (20.x, sqljs)
  • GitHub Check: codegen / codegen
  • GitHub Check: build (22.x)
  • GitHub Check: build (20.x)
  • GitHub Check: unit tests (22.x)
  • GitHub Check: unit tests (20.x)
  • GitHub Check: publish_install (macos-latest, 20.x)
  • GitHub Check: publish_install (macos-latest, 22.x)
  • GitHub Check: publish_install (ubuntu-latest, 20.x)
  • GitHub Check: publish_install (windows-latest, 20.x)
  • GitHub Check: publish_install (ubuntu-latest, 22.x)
  • GitHub Check: publish_install (windows-latest, 22.x)
🔇 Additional comments (18)
packages/payments-plugin/e2e/stripe-dev-server.ts (1)

29-29: Import-path adjustment looks correct

The new relative path resolves to e2e/fixtures/stripe-checkout-test.plugin.ts, matching the revised folder structure. No further action required.

packages/payments-plugin/e2e/payment-helpers.ts (1)

232-237: LGTM! GraphQL query follows established patterns.

The new GENERATE_BRAINTREE_CLIENT_TOKEN query is well-structured and consistent with the existing GraphQL queries in the file. It correctly supports the new Braintree development server infrastructure.

packages/payments-plugin/package.json (1)

20-20: LGTM! Development script follows established patterns.

The new dev-server:braintree script is consistent with the existing development server scripts for other payment providers (Mollie and Stripe), enabling streamlined local development and testing.

packages/payments-plugin/src/braintree/types.ts (2)

1-1: LGTM! Import addition is appropriate.

The CurrencyCode import from @vendure/core is correctly added to support the new multi-currency merchant account functionality.


9-9: LGTM! Type definition is well-structured.

The BraintreeMerchantAccountIds type correctly uses Partial<Record<CurrencyCode, string>> to allow optional mapping of currency codes to merchant account IDs, providing flexibility for multi-currency configurations.

packages/payments-plugin/src/braintree/braintree.plugin.ts (1)

241-241: LGTM! Minor formatting improvement.

The added blank line before the @VendurePlugin decorator improves code readability without affecting functionality.

packages/payments-plugin/src/braintree/braintree.handler.ts (3)

14-14: LGTM! Import addition supports multi-currency functionality.

The lookupMerchantAccountIdByCurrency import is correctly added to support the new multi-currency merchant account functionality.


98-102: LGTM! Merchant account ID lookup logic is well-implemented.

The merchant account ID lookup correctly uses the order's currency code to determine the appropriate merchant account. The implementation gracefully handles cases where no merchant account IDs are configured by returning undefined.


108-108: LGTM! Transaction integration is correct.

The merchantAccountId is properly passed to the Braintree transaction.sale() method, enabling multi-currency merchant account support. The Braintree SDK will handle undefined values gracefully by using the default merchant account.

packages/payments-plugin/README.md (1)

41-62: Excellent documentation for local development setup.

The Braintree local development section is well-structured and provides clear, step-by-step instructions. The inclusion of optional multi-currency configuration is particularly helpful.

packages/payments-plugin/src/braintree/braintree.resolver.ts (4)

16-16: Good addition of the utility function import.

The import correctly includes the new lookupMerchantAccountIdByCurrency function needed for multi-currency support.


48-48: Excellent defensive programming with optional chaining.

The addition of optional chaining (?.) for accessing customFields prevents potential runtime errors if customFields is undefined.


54-57: Proper integration of merchant account ID lookup.

The merchant account ID is correctly determined using the utility function with the plugin options and order currency. This enables currency-specific merchant accounts for Braintree transactions.


75-78: Consistent implementation in fallback scenario.

The merchant account ID lookup is correctly applied in the fallback case as well, ensuring consistency when the customer ID is invalid and needs to be reset.

packages/payments-plugin/src/braintree/braintree-common.ts (2)

86-95: Well-implemented utility function with proper error handling.

The function correctly handles edge cases by returning undefined when inputs are missing, and provides a clean lookup mechanism for merchant account IDs by currency code. The implementation is simple and effective.


79-85: Excellent documentation with helpful context.

The JSDoc provides clear usage examples and references the relevant Braintree documentation for setting up merchant account IDs. This will be very helpful for developers implementing multi-currency support.

packages/payments-plugin/e2e/braintree-dev-server.ts (1)

50-132: Excellent development server setup with comprehensive test environment.

The dev server provides a complete testing environment with proper initialization, order preparation, and client token generation. The integration with the test plugin and exposure of necessary variables for external use is well thought out.

packages/payments-plugin/e2e/fixtures/braintree-checkout-test.plugin.ts (1)

17-107: Well-implemented test controller with functional Braintree integration.

The controller provides a comprehensive testing interface with proper Braintree drop-in UI integration, event handling, and payment processing. The HTML template includes all necessary JavaScript for a functional checkout experience.

@@ -3,7 +3,7 @@ import { Controller, Res, Get } from '@nestjs/common';
import { PluginCommonModule, VendurePlugin } from '@vendure/core';
import { Response } from 'express';

import { clientSecret } from './stripe-dev-server';
import { clientSecret } from '../stripe-dev-server';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Circular-dependency causes clientSecret to stay undefined

Because the plugin is imported by stripe-dev-server, this destructuring import happens while clientSecret is still undefined.
With CommonJS transpilation, the value is copied, not live-bound, so the HTML will embed “undefined” instead of the real secret.

-import { clientSecret } from '../stripe-dev-server';
+// Keep a live reference to the export to avoid stale value
+import * as StripeDevServer from '../stripe-dev-server';

Then replace the template interpolation later in the file:

-  clientSecret: '${clientSecret}',
+  clientSecret: '${StripeDevServer.clientSecret}',

This keeps a single object reference and avoids the stale-value bug.

🤖 Prompt for AI Agents
In packages/payments-plugin/e2e/fixtures/stripe-checkout-test.plugin.ts at line
6, the import of clientSecret causes a circular dependency resulting in
clientSecret being undefined due to CommonJS copying the value instead of
live-binding. To fix this, import the entire module as an object instead of
destructuring clientSecret directly. Then, update the code to access
clientSecret as a property of that imported object, ensuring a live reference
and preventing the stale-value issue.

Comment on lines +100 to 102
merchantAccountIds?: BraintreeMerchantAccountIds;
extractMetadata?: (transaction: Transaction) => PaymentMetadata;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix documentation comment placement.

The documentation comment starting at line 100 appears to be documenting the extractMetadata property, but it's positioned above the merchantAccountIds property. This creates confusion about which property is being documented.

Apply this diff to fix the documentation:

+    /**
+     * @description
+     * Optional mapping of currency codes to Braintree merchant account IDs.
+     * This allows transactions to be processed using the appropriate merchant account
+     * based on the order's currency.
+     * 
+     * @example
+     * ```ts
+     * merchantAccountIds: {
+     *   USD: 'my_usd_merchant_account',
+     *   EUR: 'my_eur_merchant_account',
+     *   GBP: 'my_gbp_merchant_account'
+     * }
+     * ```
+     */
     merchantAccountIds?: BraintreeMerchantAccountIds;
+    /**
+     * @description
+     * Allows you to configure exactly what information from the Braintree
+     * [Transaction object](https://developer.paypal.com/braintree/docs/reference/response/transaction#result-object) (which is returned by the
+     * `transaction.sale()` method of the SDK) should be persisted to the resulting Payment entity metadata.
+     *
+     * By default, the built-in extraction function will return a metadata object that looks like this:
+     *
+     * @example
+     * ```ts
+     * const metadata = {
+     *   "status": "settling",
+     *   "currencyIsoCode": "GBP",
+     *   "merchantAccountId": "my_account_id",
+     *   "cvvCheck": "Not Applicable",
+     *   "avsPostCodeCheck": "Not Applicable",
+     *   "avsStreetAddressCheck": "Not Applicable",
+     *   "processorAuthorizationCode": null,
+     *   "processorResponseText": "Approved",
+     *   // for Paypal payments
+     *   "paymentMethod": "paypal_account",
+     *   "paypalData": {
+     *     "payerEmail": "[email protected]",
+     *     "paymentId": "PAYID-MLCXYNI74301746XK8807043",
+     *     "authorizationId": "3BU93594D85624939",
+     *     "payerStatus": "VERIFIED",
+     *     "sellerProtectionStatus": "ELIGIBLE",
+     *     "transactionFeeAmount": "0.54"
+     *   },
+     *   // for credit card payments
+     *   "paymentMethod": "credit_card",
+     *   "cardData": {
+     *     "cardType": "MasterCard",
+     *     "last4": "5454",
+     *     "expirationDate": "02/2023"
+     *   }
+     *   // publicly-available metadata that will be
+     *   // readable from the Shop API
+     *   "public": {
+     *     "cardData": {
+     *       "cardType": "MasterCard",
+     *       "last4": "5454",
+     *       "expirationDate": "02/2023"
+     *     },
+     *     "paypalData": {
+     *       "authorizationId": "3BU93594D85624939",
+     *     }
+     *   }
+     * }
+     * ```
+     *
+     * @since 1.7.0
+     */
-    merchantAccountIds?: BraintreeMerchantAccountIds;
     extractMetadata?: (transaction: Transaction) => PaymentMetadata;
🤖 Prompt for AI Agents
In packages/payments-plugin/src/braintree/types.ts around lines 100 to 102, the
documentation comment is incorrectly placed above the merchantAccountIds
property but describes the extractMetadata property. Move the existing comment
block so that it directly precedes the extractMetadata property, and add a
proper documentation comment for merchantAccountIds above it as shown in the
suggested diff. This will clarify which property each comment documents and
improve code readability.

Comment on lines +104 to +107
{ name: 'privateKey', value: process.env.BRAINTREE_PRIVATE_KEY! },
{ name: 'publicKey', value: process.env.BRAINTREE_PUBLIC_KEY! },
{ name: 'merchantId', value: process.env.BRAINTREE_MERCHANT_ID! },
],
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for required environment variables.

The required Braintree credentials are used without validation. Consider adding checks to ensure they're properly configured.

                    arguments: [
-                        { name: 'privateKey', value: process.env.BRAINTREE_PRIVATE_KEY! },
-                        { name: 'publicKey', value: process.env.BRAINTREE_PUBLIC_KEY! },
-                        { name: 'merchantId', value: process.env.BRAINTREE_MERCHANT_ID! },
+                        { name: 'privateKey', value: process.env.BRAINTREE_PRIVATE_KEY || (() => { throw new Error('BRAINTREE_PRIVATE_KEY is required'); })() },
+                        { name: 'publicKey', value: process.env.BRAINTREE_PUBLIC_KEY || (() => { throw new Error('BRAINTREE_PUBLIC_KEY is required'); })() },
+                        { name: 'merchantId', value: process.env.BRAINTREE_MERCHANT_ID || (() => { throw new Error('BRAINTREE_MERCHANT_ID is required'); })() },
                    ],
🤖 Prompt for AI Agents
In packages/payments-plugin/e2e/braintree-dev-server.ts around lines 104 to 107,
the Braintree environment variables are used without validation. Add checks
before this code to verify that process.env.BRAINTREE_PRIVATE_KEY,
process.env.BRAINTREE_PUBLIC_KEY, and process.env.BRAINTREE_MERCHANT_ID are
defined and not empty. If any are missing, throw an error or log a clear message
to prevent proceeding with invalid configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (23)
docs/docs/reference/typescript-api/services/product-option-group-service.md (1)

22-23: Document the semantics of the newly-added includeSoftDeleted flag

The generated signature now exposes a findOneOptions?: { includeSoftDeleted: boolean } parameter, but the surrounding documentation does not describe what happens when the flag is set (e.g. whether it defaults to false, how it behaves with hard-deleted entities, performance impact, etc.).
Please add a short JSDoc comment on the findOne method in the source (product-option-group.service.ts). The docs generator will then surface that explanation here.

docs/docs/reference/dashboard/components/detail-page.md (2)

41-49: entityName description may under-specify edge-cases

The docs state that the name “will be inferred from the query document” when omitted.
In practice, inference fails when the query:

  • includes an alias (order: orderByCode(...))
  • selects multiple root fields (order, customer, …)
  • uses fragments with renamed types

A terse hint such as “Only omit when the query root field unambiguously matches the entity name” prevents surprises.

-If not provided, it will be inferred from the query document.
+If omitted, the name is inferred from the root field of the query **only when that field uniquely and unambiguously matches the entity type**.

54-59: Mark optional prop consistently

The property table lists entityName without the trailing “?” that denotes optionality, whereas the signature above correctly shows entityName?: string. Add the “?” for consistency.

-### entityName
+### entityName?
docs/docs/reference/core-plugins/graphiql-plugin/index.md (3)

24-25: Confirm the exported symbol’s casing

The example now imports GraphiqlPlugin (lower-case “q”) whereas earlier docs used GraphiQLPlugin.
If the public export in @vendure/graphiql-plugin is still GraphiQLPlugin, this sample will not compile on case-sensitive filesystems.

-import { GraphiqlPlugin } from '@vendure/graphiql-plugin';
+import { GraphiQLPlugin } from '@vendure/graphiql-plugin';

Please verify the actual export name and keep the docs consistent.


36-46: Example contradicts the heading “Custom API paths”

The section promises instructions on overriding the Admin/Shop API paths, yet the snippet only sets a custom route.
Consider extending the snippet to demonstrate the expected options (e.g. adminApiPath, shopApiPath) or clarify that the plugin now autodetects these and that overriding is no longer needed.

 GraphiQLPlugin.init({
     route: 'my-custom-route', // defaults to `graphiql`
+    // adminApiPath: 'my-admin-api',
+    // shopApiPath: 'my-shop-api',
 });

52-55: Minor punctuation tweak for readability

A comma after “applications” improves flow.

-`?embeddedMode=true` - This renders the editor in embedded mode, which hides the header and
-   the API switcher. This is useful for embedding GraphiQL in other applications such as documentation.
+`?embeddedMode=true` - This renders the editor in embedded mode, which hides the header and
+   the API switcher. This is useful for embedding GraphiQL in other applications, such as documentation.
docs/docs/guides/getting-started/installation/index.md (4)

10-10: Tighten wording around Node.js version support

The sentence is grammatically correct but slightly verbose. Replacing the second clause with a shorter parenthetical improves readability.

-* [Node.js](https://nodejs.org/en/) **v20** or above, with support for **even-numbered Node.js versions**. (Odd-numbered versions should still work but are not officially supported.)
+* [Node.js](https://nodejs.org/en/) **v20** or above, preferably an **even-numbered version**. (Odd-numbered releases usually work, but they are not officially supported.)

15-16: Reduce repetition in the Docker Desktop note

“Docker Desktop installed” appears twice in one sentence. Removing the duplication and tightening the wording keeps the bullet concise.

-* [Docker Desktop](https://www.docker.com/products/docker-desktop/): If you want to use the quick start with Postgres, you must have Docker Desktop installed. If you do not have Docker Desktop installed, then SQLite will be used for your database.
+* [Docker Desktop](https://www.docker.com/products/docker-desktop/): Required only when using the quick-start option with Postgres. If Docker Desktop is unavailable, the installer falls back to SQLite.

42-42: Drop the unnecessary hyphen in “fully functional”

“Fully” is an adverb ending in “-ly”, so the compound modifier doesn’t need a hyphen.

-And that's it! After a minute or two, you'll have a **fully-functional Vendure server** installed locally.
+And that's it! After a minute or two, you'll have a **fully functional Vendure server** installed locally.

65-73: Use “who” for the database user and standardise list punctuation

Minor grammar tweak plus terminal punctuation to make each sub-bullet consistent.

-3. **Have database credentials**: You need the username and password for a database user that has full permissions (CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT) on the database you created.
+3. **Have database credentials**: You need the username and password for a database user who has full permissions (CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT) on the database you created.
docs/docs/reference/core-plugins/job-queue-plugin/pub-sub-job-queue-strategy.md (1)

44-64: Remove trailing blanks inside spans

markdownlint (MD038) flags the four <MemberInfo … type={\… `}` lines because there’s a space inside the back-ticked string.
Trimming those spaces avoids the lint warning without altering the generated output:

-<MemberInfo kind="method" type={`(injector: Injector) => `}   />
+<MemberInfo kind="method" type={`(injector: Injector) =>`} />

Apply the same change to the other 3 occurrences (lines 49, 54, 59/64).

docs/docs/guides/getting-started/try-the-api/index.mdx (2)

59-71: Consider giving each example a unique operation name.

All three snippets use query GetProducts, which is fine in isolation, but when a reader pastes multiple snippets into the same GraphiQL tab it will trigger “duplicate operation name” errors. Using something like GetProductsBasic, GetProductsDetails, GetProductsFiltered would avoid that friction.

Also applies to: 88-96


208-226: // is not a valid GraphQL comment delimiter – switch to #

If a reader copies the snippet directly into GraphiQL, the lines starting with // highlight-… will break parsing because GraphQL only recognises # for single-line comments.

-    // highlight-next-line
+    # highlight-next-line
...
-      // highlight-start
+      # highlight-start
...
-      // highlight-end
+      # highlight-end

The MDX highlighting plugin should still work with #, and the snippet becomes copy-paste-safe.

CHANGELOG.md (1)

3-3: Fix heading level increments to follow markdown conventions.

The changelog violates markdown heading conventions by jumping from h2 (##) directly to h4 (####). Heading levels should only increment by one level at a time.

Change the #### Fixes and #### Features headings to ### Fixes and ### Features throughout the changelog:

-#### Fixes
+### Fixes

-#### Features  
+### Features

Also applies to: 35-35, 59-59, 84-84

.github/workflows/build_and_test_pr.yml (1)

66-66: Same version-pin caveat applies here

See the comment on the master workflow – the rationale and risk profile are identical.

README.md (1)

150-150: Consider improving the preposition usage.

The static analysis tool suggests using "independent of" instead of "independent from" for better grammar.

-If you are making changes to the Admin UI, you need to start the Admin UI independent from the dev-server:
+If you are making changes to the Admin UI, you need to start the Admin UI independent of the dev-server:
docs/docs/guides/extending-the-dashboard/action-bar-items/index.md (2)

37-42: Minor punctuation glitch in bullet list.
LanguageTool flags “Loose punctuation mark”.
Trim the space before the dash to avoid rendering an extra paragraph.

-The `context` prop provides access to:
-
-- **`entity`**: …
+- The `context` prop provides access to:
+  - **`entity`**: …

58-70: Clean up temporary anchor element in export example.

The snippet creates an <a> element but never removes it, which can leak DOM
nodes in copy-pasted code.

-    const a = document.createElement('a');
-    a.href = url;
-    a.download = `product-${product.id}.json`;
-    a.click();
-    URL.revokeObjectURL(url);
+    const a = document.createElement('a');
+    a.href = url;
+    a.download = `product-${product.id}.json`;
+    a.click();
+    a.remove();
+    URL.revokeObjectURL(url);
docs/docs/guides/extending-the-dashboard/page-blocks/index.md (1)

36-40: Add null-safety to example to avoid runtime errors in copies.

context.entity can be undefined until data loads.
A guarded render avoids “Cannot read property ‘name’ of undefined”.

- return <div className="text-sm">Articles related to {context.entity.name}</div>;
+ return (
+   <div className="text-sm">
+     {context.entity ? `Articles related to ${context.entity.name}` : 'Loading…'}
+   </div>
+ );
docs/docs/guides/extending-the-dashboard/navigation/index.md (1)

128-146: Add missing icon imports in the code sample

BarChartIcon and PlugIcon are referenced but never imported in this snippet, which will throw an undefined identifier error if copied verbatim.

+import { defineDashboardExtension } from '@vendure/dashboard';
+import { BarChartIcon, PlugIcon } from 'lucide-react';
 
 export default defineDashboardExtension({
     navSections: [
docs/docs/guides/extending-the-dashboard/cms-tutorial/index.md (2)

28-32: Typo: replace “you” with “your”

-You now have you `CmsPlugin` created with a new `Article` entity.
+You now have your `CmsPlugin` created with a new `Article` entity.

8-12: Minor wording – drop “in order to”

The phrase “in order to” is verbose. Prefer a direct “to” for tighter prose.

-order to demonstrate how to extend the dashboard.
+to demonstrate how to extend the dashboard.
docs/docs/guides/extending-the-dashboard/custom-form-components/index.md (1)

24-65: Consider wrapping field controls with FormFieldWrapper

Internally, FormFieldWrapper standardises labels, error display, and disabled states. Using it inside custom components aligns with established dashboard patterns and reduces boilerplate.

Example refactor for ColorPickerComponent (simplified):

-<div className="space-y-2">
-
-    {fieldState.error && <p className="text-sm text-destructive">{fieldState.error.message}</p>}
-</div>
+<FormFieldWrapper control={field.control} name={field.name} render={() => (
+    <>
+        … /* existing picker controls */
+    </>
+)} />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c68d8f6dde511f5e3f3eb8feaabe3b3c0bc05ce and f3b1fe63598796221ea54b8a88a161eb0ff321e3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (63)
  • .cursor/rules/dashboard.mdc (1 hunks)
  • .github/workflows/build_and_test_branches.yml (1 hunks)
  • .github/workflows/build_and_test_master.yml (2 hunks)
  • .github/workflows/build_and_test_pr.yml (2 hunks)
  • .github/workflows/publish_master_to_npm.yml (1 hunks)
  • .github/workflows/stale_bot.yml (1 hunks)
  • .gitignore (0 hunks)
  • CHANGELOG.md (5 hunks)
  • CONTRIBUTING.md (3 hunks)
  • README.md (5 hunks)
  • docker-compose.yml (0 hunks)
  • docs/docs/guides/developer-guide/custom-strategies-in-plugins/index.mdx (1 hunks)
  • docs/docs/guides/developer-guide/scheduled-tasks/index.md (1 hunks)
  • docs/docs/guides/extending-the-admin-ui/getting-started/index.md (17 hunks)
  • docs/docs/guides/extending-the-dashboard/action-bar-items/index.md (1 hunks)
  • docs/docs/guides/extending-the-dashboard/cms-tutorial/index.md (1 hunks)
  • docs/docs/guides/extending-the-dashboard/custom-form-components/index.md (1 hunks)
  • docs/docs/guides/extending-the-dashboard/getting-started/index.md (5 hunks)
  • docs/docs/guides/extending-the-dashboard/navigation/index.md (1 hunks)
  • docs/docs/guides/extending-the-dashboard/page-blocks/index.md (1 hunks)
  • docs/docs/guides/extending-the-dashboard/tech-stack/index.md (1 hunks)
  • docs/docs/guides/getting-started/graphql-intro/index.mdx (2 hunks)
  • docs/docs/guides/getting-started/installation/index.md (2 hunks)
  • docs/docs/guides/getting-started/try-the-api/index.mdx (7 hunks)
  • docs/docs/reference/core-plugins/graphiql-plugin/index.md (2 hunks)
  • docs/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin.md (2 hunks)
  • docs/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-strategy.md (1 hunks)
  • docs/docs/reference/core-plugins/job-queue-plugin/pub-sub-job-queue-strategy.md (1 hunks)
  • docs/docs/reference/core-plugins/job-queue-plugin/pub-sub-plugin.md (1 hunks)
  • docs/docs/reference/dashboard/components/detail-page.md (3 hunks)
  • docs/docs/reference/dashboard/components/list-page.md (5 hunks)
  • docs/docs/reference/dashboard/components/page-action-bar.md (3 hunks)
  • docs/docs/reference/dashboard/components/page-block.md (4 hunks)
  • docs/docs/reference/dashboard/components/page-layout.md (2 hunks)
  • docs/docs/reference/dashboard/components/page-title.md (1 hunks)
  • docs/docs/reference/dashboard/extensions/dashboard-action-bar-item.md (1 hunks)
  • docs/docs/reference/dashboard/extensions/dashboard-custom-form-component.md (1 hunks)
  • docs/docs/reference/dashboard/extensions/dashboard-extension.md (4 hunks)
  • docs/docs/reference/dashboard/extensions/dashboard-page-block-definition.md (1 hunks)
  • docs/docs/reference/dashboard/extensions/define-dashboard-extension.md (1 hunks)
  • docs/docs/reference/dashboard/extensions/page-block-location.md (1 hunks)
  • docs/docs/reference/dashboard/hooks/use-auth.md (1 hunks)
  • docs/docs/reference/dashboard/hooks/use-detail-page.md (5 hunks)
  • docs/docs/reference/dashboard/hooks/use-permissions.md (1 hunks)
  • docs/docs/reference/typescript-api/cache/redis-cache-plugin.md (1 hunks)
  • docs/docs/reference/typescript-api/cache/redis-cache-strategy.md (1 hunks)
  • docs/docs/reference/typescript-api/common/middleware.md (1 hunks)
  • docs/docs/reference/typescript-api/scheduled-tasks/clean-sessions-task.md (1 hunks)
  • docs/docs/reference/typescript-api/services/order-service.md (1 hunks)
  • docs/docs/reference/typescript-api/services/product-option-group-service.md (2 hunks)
  • docs/docs/reference/typescript-api/services/product-option-service.md (1 hunks)
  • docs/docs/reference/typescript-api/tax/address-based-tax-zone-strategy.md (1 hunks)
  • docs/docs/reference/typescript-api/telemetry/instrument.md (1 hunks)
  • docs/sidebars.js (3 hunks)
  • docs/src/components/Playground/index.tsx (1 hunks)
  • docs/src/pages/index.tsx (1 hunks)
  • lerna.json (1 hunks)
  • license/signatures/version1/cla.json (1 hunks)
  • packages/admin-ui-plugin/package.json (2 hunks)
  • packages/admin-ui/i18n-coverage.json (1 hunks)
  • packages/admin-ui/package.json (2 hunks)
  • packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.ts (2 hunks)
  • packages/admin-ui/src/lib/core/src/common/version.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • docker-compose.yml
  • .gitignore
✅ Files skipped from review due to trivial changes (35)
  • docs/docs/reference/dashboard/extensions/page-block-location.md
  • docs/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-strategy.md
  • docs/docs/reference/typescript-api/tax/address-based-tax-zone-strategy.md
  • docs/docs/reference/dashboard/components/page-layout.md
  • license/signatures/version1/cla.json
  • docs/docs/reference/typescript-api/cache/redis-cache-plugin.md
  • packages/admin-ui-plugin/package.json
  • packages/admin-ui/package.json
  • docs/docs/reference/dashboard/extensions/define-dashboard-extension.md
  • lerna.json
  • docs/docs/reference/typescript-api/telemetry/instrument.md
  • docs/docs/reference/dashboard/components/page-title.md
  • docs/docs/reference/dashboard/extensions/dashboard-action-bar-item.md
  • docs/docs/reference/dashboard/hooks/use-auth.md
  • docs/docs/reference/typescript-api/services/product-option-service.md
  • docs/docs/reference/dashboard/extensions/dashboard-page-block-definition.md
  • docs/docs/reference/dashboard/hooks/use-permissions.md
  • docs/src/pages/index.tsx
  • docs/docs/reference/typescript-api/scheduled-tasks/clean-sessions-task.md
  • .github/workflows/build_and_test_branches.yml
  • packages/admin-ui/src/lib/core/src/common/version.ts
  • docs/docs/reference/dashboard/components/page-block.md
  • docs/docs/reference/typescript-api/cache/redis-cache-strategy.md
  • docs/docs/reference/dashboard/components/page-action-bar.md
  • docs/docs/guides/developer-guide/scheduled-tasks/index.md
  • docs/docs/reference/dashboard/extensions/dashboard-custom-form-component.md
  • docs/docs/reference/typescript-api/services/order-service.md
  • docs/docs/guides/getting-started/graphql-intro/index.mdx
  • packages/admin-ui/i18n-coverage.json
  • docs/src/components/Playground/index.tsx
  • docs/docs/reference/core-plugins/job-queue-plugin/pub-sub-plugin.md
  • docs/docs/reference/dashboard/hooks/use-detail-page.md
  • .cursor/rules/dashboard.mdc
  • .github/workflows/stale_bot.yml
  • docs/docs/reference/dashboard/components/list-page.md
🧰 Additional context used
🧠 Learnings (6)
docs/docs/guides/getting-started/try-the-api/index.mdx (3)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: In packages/dashboard, when performing mutations with useMutation, do not pass variables at the point of declaration; instead, pass them when calling the mutate function. Type information should flow from the graphql function, so explicit typings or generics are not needed.
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: When using TanStack Query in packages/dashboard, always use the useQuery or useMutation hooks with the graphql and api utilities, following the provided pattern to ensure type safety and consistent API interaction.
Learnt from: dlhck
PR: vendure-ecommerce/vendure#3615
File: packages/dashboard/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx:161-214
Timestamp: 2025-06-25T11:20:48.133Z
Learning: Using `useMutation` with multiple concurrent `mutate` calls (e.g., in a `map` operation) causes race conditions and unpredictable behavior. This pattern has caused issues in the Vendure dashboard codebase. Instead, use `mutateAsync` with sequential processing (for loop with await) or proper chaining to ensure only one mutation runs at a time.
docs/docs/reference/dashboard/extensions/dashboard-extension.md (1)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: For forms in packages/dashboard, prefer using the FormFieldWrapper component (a wrapper around Shadcn react-hook-form components) to make form controls more concise, instead of using raw Shadcn components directly.
docs/docs/reference/core-plugins/graphiql-plugin/index.md (1)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: In packages/dashboard, when performing mutations with useMutation, do not pass variables at the point of declaration; instead, pass them when calling the mutate function. Type information should flow from the graphql function, so explicit typings or generics are not needed.
docs/docs/guides/extending-the-dashboard/getting-started/index.md (3)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: All user-facing labels and messages in packages/dashboard should use localization via the Trans component or the useLingui hook, as shown in the provided example, to ensure proper internationalization.
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: For forms in packages/dashboard, prefer using the FormFieldWrapper component (a wrapper around Shadcn react-hook-form components) to make form controls more concise, instead of using raw Shadcn components directly.
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: When using TanStack Query in packages/dashboard, always use the useQuery or useMutation hooks with the graphql and api utilities, following the provided pattern to ensure type safety and consistent API interaction.
docs/docs/guides/extending-the-dashboard/custom-form-components/index.md (2)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: For forms in packages/dashboard, prefer using the FormFieldWrapper component (a wrapper around Shadcn react-hook-form components) to make form controls more concise, instead of using raw Shadcn components directly.
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: All user-facing labels and messages in packages/dashboard should use localization via the Trans component or the useLingui hook, as shown in the provided example, to ensure proper internationalization.
docs/docs/guides/extending-the-dashboard/tech-stack/index.md (1)
Learnt from: CR
PR: vendure-ecommerce/vendure#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-06-23T13:16:30.177Z
Learning: When using TanStack Query in packages/dashboard, always use the useQuery or useMutation hooks with the graphql and api utilities, following the provided pattern to ensure type safety and consistent API interaction.
🧬 Code Graph Analysis (1)
docs/sidebars.js (2)
docs/src/theme/DocSidebarItem/Link/index.js (1)
  • icon (6-6)
docs/src/theme/DocSidebarItem/Category/index.js (1)
  • icon (6-6)
🪛 LanguageTool
CONTRIBUTING.md

[uncategorized] ~139-~139: Do not mix variants of the same word (‘synchronize’ and ‘synchronise’) within a single text.
Context: ...e three main ways to keep your fork synchronized: #### Option 1: Using GitHub's Web Int...

(EN_WORD_COHERENCY)


[uncategorized] ~272-~272: Possible missing comma found.
Context: ...merged. This is done via an automation bot which will prompt you to sign the CLA w...

(AI_HYDRA_LEO_MISSING_COMMA)

docs/docs/reference/core-plugins/graphiql-plugin/index.md

[uncategorized] ~54-~54: Possible missing comma found.
Context: ... useful for embedding GraphiQL in other applications such as documentation. In this mode,...

(AI_HYDRA_LEO_MISSING_COMMA)

docs/docs/guides/extending-the-dashboard/cms-tutorial/index.md

[style] ~10-~10: Consider a more concise word here.
Context: ...ent management system (CMS) for Vendure in order to demonstrate how to extend the dashboard...

(IN_ORDER_TO_PREMIUM)


[uncategorized] ~30-~30: “you” seems less likely than “your” (belonging to you).
Context: ...*[Finish] No, I'm done!** You now have you CmsPlugin created with a new `Article...

(AI_HYDRA_LEO_CP_YOU_YOUR)


[uncategorized] ~62-~62: Possible missing comma found.
Context: ...stomFields: ArticleCustomFields; } ``` Now let's create a new ArticleService to ...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~92-~92: Although a hyphen is possible, it is not necessary in a compound modifier in which the first word is an adverb that ends in ‘ly’.
Context: ...t step is to create a migration for our newly-created entity: ```bash npx vendure migrate ``...

(HYPHENATED_LY_ADVERB_ADJECTIVE)


[uncategorized] ~153-~153: Possible missing comma found.
Context: ... a list page for our Article entity. First we'll create a new article-list.tsx f...

(AI_HYDRA_LEO_MISSING_COMMA)


[style] ~532-~532: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ts to structure the page into blocks. - Used custom form components (such as the `Ri...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

docs/docs/guides/extending-the-admin-ui/getting-started/index.md

[style] ~476-~476: ‘Prior to’ might be wordy. Consider a shorter alternative.
Context: ...ing correctly. ## Legacy API < v2.1.0 Prior to Vendure v2.1.0, the API for extending t...

(EN_WORDINESS_PREMIUM_PRIOR_TO)

docs/docs/guides/developer-guide/custom-strategies-in-plugins/index.mdx

[uncategorized] ~401-~401: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... Always provide a default implementation so users can use your plugin out-of-the-bo...

(COMMA_COMPOUND_SENTENCE_2)

CHANGELOG.md

[uncategorized] ~203-~203: Possible missing comma found.
Context: ...At` column. This will only affect cache records so no prod data will be affected by i...

(AI_HYDRA_LEO_MISSING_COMMA)

README.md

[style] ~54-~54: The wording of this phrase can be improved.
Context: ...welcome%22). Thank you for considering making Vendure better! ## Development > [!IMPORTANT] > The ...

(MAKE_STYLE_BETTER)


[style] ~150-~150: Consider shortening or rephrasing this to strengthen your wording.
Context: ...ng Admin UI changes locally If you are making changes to the Admin UI, you need to start the Adm...

(MAKE_CHANGES)


[grammar] ~150-~150: The usual collocation for “independent” is “of”, not “from”. Did you mean “independent of”?
Context: ...dmin UI, you need to start the Admin UI independent from the dev-server: > [!NOTE] > You don't...

(INDEPENDENTLY_FROM_OF)


[misspelling] ~162-~162: Did you mean the verb “log in” instead of the noun ‘login’?
Context: ...-ui on "http://localhost:4200", you can login with the default credentials: Username...

(LOG_IN)


[uncategorized] ~167-~167: It appears that a hyphen is missing (if ‘auto’ is not used in the context of ‘cars’).
Context: ...dminPassword:superadmin` This will auto restart when you make changes to the Admin UI. ...

(AUTO_HYPHEN)


[style] ~167-~167: Consider shortening or rephrasing this to strengthen your wording.
Context: ...admin` This will auto restart when you make changes to the Admin UI. ### Testing your change...

(MAKE_CHANGES)


[uncategorized] ~211-~211: Possible missing comma found.
Context: ...ugging To debug the dev server with VS Code use the included [launch.json](/.vscode...

(AI_HYDRA_LEO_MISSING_COMMA)


[grammar] ~259-~259: Did you mean the noun “publishing”?
Context: ...it then invalidates the npm OTP and the publish will fail. So the solution is to publis...

(PREPOSITION_VERB)

docs/docs/guides/extending-the-dashboard/action-bar-items/index.md

[uncategorized] ~39-~39: Loose punctuation mark.
Context: ...prop provides access to: - entity: The current entity being viewed/edited ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~40-~40: Loose punctuation mark.
Context: ...., Product, Customer, etc.) - form: The React Hook Form instance for the cu...

(UNLIKELY_OPENING_PUNCTUATION)

docs/docs/guides/extending-the-dashboard/navigation/index.md

[uncategorized] ~97-~97: Loose punctuation mark.
Context: ...ies ### Required Properties - id: Unique identifier for the section - **`...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~102-~102: Loose punctuation mark.
Context: ...n ### Optional Properties - icon: Lucide React icon component to display ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~110-~110: Loose punctuation mark.
Context: ...# Required Properties - sectionId: ID of the section where this item shoul...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~116-~116: Loose punctuation mark.
Context: ...em ### Optional Properties - url: Custom URL if different from the route ...

(UNLIKELY_OPENING_PUNCTUATION)


[style] ~174-~174: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...rea, you might use order: 250. If you want to add a section before Settings in the bo...

(REP_WANT_TO_VB)

docs/docs/guides/extending-the-dashboard/page-blocks/index.md

[uncategorized] ~91-~91: Loose punctuation mark.
Context: ... be placed in two columns: - main: The main content area (wider column on ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~98-~98: Loose punctuation mark.
Context: ...prop provides access to: - entity: The current entity being viewed/edited ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~99-~99: Loose punctuation mark.
Context: ...., Product, Customer, etc.) - form: The React Hook Form instance for the cu...

(UNLIKELY_OPENING_PUNCTUATION)

docs/docs/guides/extending-the-dashboard/tech-stack/index.md

[style] ~11-~11: Try using a synonym here to strengthen your writing.
Context: ... The dashboard is built with React 19, giving you access to all the latest React features...

(GIVE_PROVIDE)


[uncategorized] ~11-~11: Possible missing comma found.
Context: ...ving you access to all the latest React features including: - React Compiler optimizati...

(AI_HYDRA_LEO_MISSING_COMMA)


[misspelling] ~66-~66: This word is normally spelled as one.
Context: ... ecosystem - Environment API for better multi-environment support ## UI Framework ### Tailwind ...

(EN_COMPOUNDS_MULTI_ENVIRONMENT)

docs/docs/guides/getting-started/installation/index.md

[uncategorized] ~11-~11: Possible missing comma found.
Context: ...(Odd-numbered versions should still work but are not officially supported.) ### Opt...

(AI_HYDRA_LEO_MISSING_COMMA)


[style] ~16-~16: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ill be used for your database. * If you want to use an existing MySQL, MariaDB, or Post...

(REP_WANT_TO_VB)


[uncategorized] ~42-~42: Although a hyphen is possible, it is not necessary in a compound modifier in which the first word is an adverb that ends in ‘ly’.
Context: ... After a minute or two, you'll have a fully-functional Vendure server installed locally. On...

(HYPHENATED_LY_ADVERB_ADJECTIVE)


[style] ~71-~71: Consider using “who” when you are referring to a person instead of an object.
Context: ...ername and password for a database user that has full permissions (CREATE, ALTER, DR...

(THAT_WHO)

🪛 markdownlint-cli2 (0.17.2)
CONTRIBUTING.md

113-113: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


117-117: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


185-185: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


216-216: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


224-224: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


246-246: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

docs/docs/reference/dashboard/components/detail-page.md

37-37: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

CHANGELOG.md

3-3: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)


35-35: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)


59-59: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)


84-84: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)

README.md

157-157: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


162-162: Bare URL used
null

(MD034, no-bare-urls)


193-193: Blank line inside blockquote
null

(MD028, no-blanks-blockquote)

docs/docs/reference/core-plugins/job-queue-plugin/pub-sub-job-queue-strategy.md

44-44: Spaces inside code span elements
null

(MD038, no-space-in-code)


49-49: Spaces inside code span elements
null

(MD038, no-space-in-code)


59-59: Spaces inside code span elements
null

(MD038, no-space-in-code)


64-64: Spaces inside code span elements
null

(MD038, no-space-in-code)

⏰ Context from checks skipped due to timeout of 90000ms (18)
  • GitHub Check: e2e tests (20.x, mariadb)
  • GitHub Check: e2e tests (22.x, postgres)
  • GitHub Check: e2e tests (22.x, mysql)
  • GitHub Check: e2e tests (22.x, sqljs)
  • GitHub Check: e2e tests (22.x, mariadb)
  • GitHub Check: e2e tests (20.x, postgres)
  • GitHub Check: e2e tests (20.x, mysql)
  • GitHub Check: e2e tests (20.x, sqljs)
  • GitHub Check: unit tests (20.x)
  • GitHub Check: unit tests (22.x)
  • GitHub Check: build (20.x)
  • GitHub Check: build (22.x)
  • GitHub Check: publish_install (windows-latest, 22.x)
  • GitHub Check: publish_install (ubuntu-latest, 22.x)
  • GitHub Check: publish_install (windows-latest, 20.x)
  • GitHub Check: publish_install (macos-latest, 22.x)
  • GitHub Check: publish_install (ubuntu-latest, 20.x)
  • GitHub Check: publish_install (macos-latest, 20.x)
🔇 Additional comments (20)
docs/docs/reference/dashboard/components/detail-page.md (1)

14-15: Verify sourceLine reference is still accurate

The GenerationInfo tag now points to line 124 in detail-page.tsx. Given the surrounding source code churn, please re-run the docs build after merging or double-check that line numbers still match the latest commit; otherwise the “view source” links in the docs will break.

docs/docs/guides/getting-started/try-the-api/index.mdx (1)

39-46: Good improvement – operation name added.

Adding the GetProducts operation name is a best-practice for readability & tooling support.

packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.ts (3)

5-5: LGTM! Type correction improves semantic accuracy.

The change from FacetWithValuesFragment to FacetValueFragment is semantically correct since this component handles individual facet values rather than complete facets with their value collections.

Also applies to: 47-48, 54-54


45-45: Good formatting improvement.

Adding the trailing comma follows best practices and makes future diffs cleaner.


58-61: Constructor formatting improvement enhances readability.

The multi-line parameter formatting follows TypeScript conventions and improves maintainability.

.github/workflows/build_and_test_master.yml (1)

64-66: Pinning to 11.5 avoids the snapshot-isolation issue but may leave us on an old major release

Good call-out about 11.6.2+, but freezing the image at 11.5 places the CI on an EOL (or soon-to-be-EOL) minor.
Consider instead:

  1. Start the container with 11.6 (or later) and set
    MARIADB_EXTRA_FLAGS: --innodb-snapshot-isolation=0
    to retain the old behaviour; or
  2. At least add a follow-up ticket reminding us to bump once the test-suite is fixed.

This keeps security patches flowing while still unblocking the pipeline.

README.md (3)

46-55: Excellent addition to encourage community contributions.

The new Contributing section provides clear guidance and appropriately links to detailed contribution guidelines. This will help onboard new contributors effectively.


105-128: Great improvement to PostgreSQL setup instructions.

The addition of multiple configuration methods (.env file and CLI) with clear examples makes the database setup much more accessible for developers.


174-208: Excellent reorganization of the development workflow.

The clearer terminal setup instructions and step-by-step workflow summary will significantly improve the developer experience when testing changes locally.

CONTRIBUTING.md (4)

7-20: Excellent addition of Table of Contents.

The comprehensive Table of Contents makes this long document much more navigable and user-friendly for contributors.


111-231: Outstanding detailed contribution workflow guidance.

The step-by-step workflow with multiple options (GitHub CLI, web interface, git commands) significantly lowers the barrier to entry for new contributors. The inclusion of different synchronization methods accommodates various developer preferences and skill levels.


232-268: Valuable addition of documentation and help guidance.

The documentation section provides clear instructions for working with Docusaurus, and the help section appropriately directs contributors to community resources. The note about auto-generated reference docs is particularly helpful.


274-300: Excellent addition of issue triage guidelines for maintainers.

The structured approach with checklists and clear actions will help maintainers handle issues consistently and efficiently. This is valuable for project management.

docs/docs/guides/developer-guide/custom-strategies-in-plugins/index.mdx (1)

1-468: Outstanding comprehensive guide on custom strategies in plugins.

This is an exceptional addition to the developer documentation that:

  • Provides a complete learning path from basic concepts to advanced usage
  • Includes practical, working code examples throughout
  • Covers essential aspects like dependency injection and lifecycle management
  • Addresses both plugin author and user perspectives
  • Follows Vendure's established patterns and conventions
  • Includes valuable best practices and documentation guidelines

The guide will significantly help developers create extensible and maintainable plugins. The progression from simple examples to complex scenarios with multiple strategies is particularly well done.

docs/sidebars.js (2)

103-103: Appropriate placement of custom strategies guide.

Adding the custom strategies guide to the Advanced Topics section is well-placed, as it covers advanced plugin development concepts.


136-186: Excellent reorganization of Admin UI documentation.

The clear separation between "React Admin Dashboard" and "Angular Admin UI" categories with appropriate icons will help users navigate to the correct documentation for their use case. This reorganization aligns well with the project's strategic direction toward the React-based dashboard.

docs/docs/reference/dashboard/extensions/dashboard-extension.md (1)

23-23: Appropriate extensions to DashboardExtension interface.

The addition of navSections and customFormComponents properties enhances the dashboard extension API by enabling:

  1. Custom navigation section definitions for sidebar customization
  2. Custom form component registration for enhanced form handling

These additions align well with the broader dashboard extension capabilities being introduced and provide valuable customization options for developers.

Also applies to: 28-28, 39-43, 65-69

docs/docs/guides/extending-the-dashboard/getting-started/index.md (2)

9-10: Clear & helpful beta warning – thumbs-up.
No issues spotted; the timeline information is useful to readers.


127-135: Double-check new “What’s Next” links render correctly in Docusaurus.
Using absolute /guides/... URLs bypasses the site base-url; if the docs site is
served from a sub-path (e.g. /docs), links will 404.

Consider:

- - [CMS Tutorial](/guides/extending-the-dashboard/cms-tutorial/)
+ - [CMS Tutorial](../cms-tutorial/)

Same for the other items.

docs/docs/guides/extending-the-admin-ui/getting-started/index.md (1)

8-14: Deprecation notice looks good, but validate the cross-link.

Ensure /guides/extending-the-dashboard/getting-started/ exists in the built
site, otherwise readers will hit a 404.

middleware: [{
handler: json({ limit: '10mb' }),
route: '*',
route: '*splat',
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

'*splat' does not behave as a catch-all route – likely a typo

In Express, the pattern '*splat' matches any path that ends with the literal text splat, not every route.
If the intention is to apply the JSON body-parser to all routes (as the original example with '*' did), revert the change:

-      route: '*splat',
+      route: '*',

Otherwise, clarify in the docs why only routes terminating with “splat” should be affected to avoid misleading users.

📝 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.

Suggested change
route: '*splat',
route: '*',
🤖 Prompt for AI Agents
In docs/docs/reference/typescript-api/common/middleware.md at line 36, the route
pattern '*splat' is incorrectly used as a catch-all route but it only matches
paths ending with the literal 'splat'. To fix this, replace '*splat' with '*' to
correctly apply the middleware to all routes, or update the documentation to
clearly explain why only routes ending with 'splat' are targeted if that is the
intended behavior.

Comment on lines 35 to 41
Note: To use this plugin, you need to manually install the `bullmq` package:

or

`npm install @vendure/job-queue-plugin bullmq`
```shell
npm install bullmq@^5.4.2
```

**Note:** The v1.x version of this plugin is designed to work with bullmq v1.x, etc.
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Confusing version guidance – v5 in install command but v1 in note

The install snippet pins bullmq at ^5.4.2, yet the follow-up note still references “v1.x version of this plugin is designed to work with bullmq v1.x”.
Readers may not know which combo is correct. Either update the note to reflect the current (v3.x) plugin using BullMQ v5, or change the install snippet if v1 is still expected.

Suggested wording:

-Note: To use this plugin, you need to manually install the `bullmq` package:
+Note: To use this plugin, you need to manually install the `bullmq` package.
+Vendure ≥ v3 is tested with **bullmq v5**, so install a compatible v5 release.  
+For older Vendure v1 installations, stick to **bullmq v1** instead.

This removes ambiguity and helps users avoid an immediate “invalid peer-dependency” headache.

🤖 Prompt for AI Agents
In docs/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin.md
around lines 35 to 41, the install command specifies bullmq version 5.4.2, but
the note below references compatibility with bullmq v1.x, causing confusion.
Update the note to correctly reflect that the current plugin version (v3.x) is
designed to work with bullmq v5.x, ensuring consistency between the install
command and the compatibility note to avoid user confusion and dependency
issues.

Comment on lines 19 to 24
Note: To use this strategy, you need to manually install the `@google-cloud/pubsub` package:

```shell
npm install

```ts title="Signature"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix broken code-fence and show the correct install command

The opening ```shell fence is never closed, so everything that follows is rendered as part of the shell block, breaking the whole page.
At the same time the snippet omits the actual package name that the preceding paragraph tells the reader to install.

 ```shell
-npm install
-
-```ts title="Signature"
+npm install @google-cloud/pubsub
+```
+
+```ts title="Signature"

This closes the first fence and includes the required package, restoring proper Markdown rendering.

🤖 Prompt for AI Agents
In
docs/docs/reference/core-plugins/job-queue-plugin/pub-sub-job-queue-strategy.md
around lines 19 to 24, the shell code fence for the npm install command is not
closed and the install command is incomplete. Fix this by closing the shell code
fence properly and updating the command to "npm install @google-cloud/pubsub" to
match the described package installation. Then reopen the next code fence for
the TypeScript signature as intended.

Comment on lines 78 to 80
- name: Publish to NPM
if: ${{ steps.commit_check.outputs.should_publish == 'true' }}
run: npx lerna publish from-package --yes --dist-tag master --no-git-reset
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Packages are published without an explicit build – rely on incidental scripts

lerna publish from-package assumes artefacts are already built.
If any package lacks a "prepare"/"prepublishOnly" script the publish will push unbuilt sources.

Add an explicit build before publishing:

- name: Build workspace
  if: ${{ steps.commit_check.outputs.should_publish == 'true' }}
  run: npm run build
🤖 Prompt for AI Agents
In .github/workflows/publish_master_to_npm.yml around lines 78 to 80, the
publish step uses 'lerna publish from-package' without an explicit build step,
risking publishing unbuilt source code if packages lack prepare or
prepublishOnly scripts. To fix this, add a separate build step before the
publish step that runs 'npm run build' conditioned on the same publish trigger,
ensuring all packages are properly built before publishing.

Comment on lines 60 to 70
- name: Version and build packages
if: ${{ steps.commit_check.outputs.should_publish == 'true' }}
run: |
# Get current version from lerna.json
CURRENT_VERSION=$(node -p "require('./lerna.json').version")
IFS='.' read -r -a VERSION_PARTS <<< "$CURRENT_VERSION"
# Increment patch version for pre-release
PATCH=$((VERSION_PARTS[2] + 1))
# Compose new pre-release version incl. timestamp
NEW_VERSION="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${PATCH}-master-${{ steps.date.outputs.date }}"
npx lerna version $NEW_VERSION --no-git-tag-version --yes --no-push --force-publish
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

NEW_VERSION is lost after this step – commit message will be blank

NEW_VERSION is a shell variable local to this step; subsequent steps run in a fresh shell, so it is not available when we build the commit message.
This results in:
chore: Bump version for pre-release to (note the empty version).

-                  npx lerna version $NEW_VERSION --no-git-tag-version --yes --no-push --force-publish
+                  npx lerna version $NEW_VERSION --no-git-tag-version --yes --no-push --force-publish
+                  # Expose for later steps
+                  echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV

and later:

-                  git commit -m "chore: Bump version for pre-release to $NEW_VERSION"
+                  git commit -m "chore: Bump version for pre-release to ${NEW_VERSION:-UNKNOWN}"

(or use ${{ env.NEW_VERSION }} in a composite run-step).

📝 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.

Suggested change
- name: Version and build packages
if: ${{ steps.commit_check.outputs.should_publish == 'true' }}
run: |
# Get current version from lerna.json
CURRENT_VERSION=$(node -p "require('./lerna.json').version")
IFS='.' read -r -a VERSION_PARTS <<< "$CURRENT_VERSION"
# Increment patch version for pre-release
PATCH=$((VERSION_PARTS[2] + 1))
# Compose new pre-release version incl. timestamp
NEW_VERSION="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${PATCH}-master-${{ steps.date.outputs.date }}"
npx lerna version $NEW_VERSION --no-git-tag-version --yes --no-push --force-publish
- name: Version and build packages
if: ${{ steps.commit_check.outputs.should_publish == 'true' }}
run: |
# Get current version from lerna.json
CURRENT_VERSION=$(node -p "require('./lerna.json').version")
IFS='.' read -r -a VERSION_PARTS <<< "$CURRENT_VERSION"
# Increment patch version for pre-release
PATCH=$((VERSION_PARTS[2] + 1))
# Compose new pre-release version incl. timestamp
NEW_VERSION="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${PATCH}-master-${{ steps.date.outputs.date }}"
npx lerna version $NEW_VERSION --no-git-tag-version --yes --no-push --force-publish
+ # Expose for later steps
+ echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
Suggested change
- name: Version and build packages
if: ${{ steps.commit_check.outputs.should_publish == 'true' }}
run: |
# Get current version from lerna.json
CURRENT_VERSION=$(node -p "require('./lerna.json').version")
IFS='.' read -r -a VERSION_PARTS <<< "$CURRENT_VERSION"
# Increment patch version for pre-release
PATCH=$((VERSION_PARTS[2] + 1))
# Compose new pre-release version incl. timestamp
NEW_VERSION="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${PATCH}-master-${{ steps.date.outputs.date }}"
npx lerna version $NEW_VERSION --no-git-tag-version --yes --no-push --force-publish
- git commit -m "chore: Bump version for pre-release to $NEW_VERSION"
+ git commit -m "chore: Bump version for pre-release to ${NEW_VERSION:-UNKNOWN}"
🤖 Prompt for AI Agents
In .github/workflows/publish_master_to_npm.yml around lines 60 to 70, the
NEW_VERSION variable is defined in a shell step but not persisted for later
steps, causing the commit message to miss the version. To fix this, export
NEW_VERSION as a GitHub Actions environment variable by adding 'echo
"NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV' at the end of the run block, so
subsequent steps can access it via '${{ env.NEW_VERSION }}'.

Comment on lines 53 to 57
// to introspect the GraphQL schema based on any API extensions
// and custom fields that are configured.
vendureConfigPath: pathToFileURL('./src/vendure-config.ts'),
// Points to the location of your Vendure server.
// Points to the location of your Vendure server.
adminUiConfig: { apiHost: 'http://localhost', apiPort: 3000 },
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make vendureConfigPath robust across working-directories.
pathToFileURL('./src/vendure-config.ts') is resolved relative to the process cwd.
If users invoke Vite from another directory (CI, Nx, pnpm workspace etc.) the
path can be wrong. Convert to an absolute path first.

-            vendureConfigPath: pathToFileURL('./src/vendure-config.ts'),
+            vendureConfigPath: pathToFileURL(resolve(__dirname, 'src/vendure-config.ts')),
📝 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.

Suggested change
// to introspect the GraphQL schema based on any API extensions
// and custom fields that are configured.
vendureConfigPath: pathToFileURL('./src/vendure-config.ts'),
// Points to the location of your Vendure server.
// Points to the location of your Vendure server.
adminUiConfig: { apiHost: 'http://localhost', apiPort: 3000 },
// to introspect the GraphQL schema based on any API extensions
// and custom fields that are configured.
vendureConfigPath: pathToFileURL(resolve(__dirname, 'src/vendure-config.ts')),
// Points to the location of your Vendure server.
adminUiConfig: { apiHost: 'http://localhost', apiPort: 3000 },
🤖 Prompt for AI Agents
In docs/docs/guides/extending-the-dashboard/getting-started/index.md around
lines 53 to 57, the vendureConfigPath is set using a relative path which can
break if the process is run from a different working directory. Fix this by
converting the relative path './src/vendure-config.ts' to an absolute path
before passing it to pathToFileURL, ensuring it resolves correctly regardless of
the current working directory.

Comment on lines 23 to 27
function OptimisticUpdateExample() {
const [optimisticState, addOptimistic] = useOptimistic(state, (currentState, optimisticValue) => {
// Return new state with optimistic update
return [...currentState, optimisticValue];
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

useOptimistic example references undeclared state.

Readers copying this will hit a TS error. Provide an initial state variable:

-function OptimisticUpdateExample() {
-    const [optimisticState, addOptimistic] = useOptimistic(state, (currentState, optimisticValue) => {
+function OptimisticUpdateExample() {
+    const [state, setState] = useState<ItemType[]>([]);
+    const [optimisticState, addOptimistic] = useOptimistic(state, (currentState, optimisticValue) => {
         // ...
     });
📝 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.

Suggested change
function OptimisticUpdateExample() {
const [optimisticState, addOptimistic] = useOptimistic(state, (currentState, optimisticValue) => {
// Return new state with optimistic update
return [...currentState, optimisticValue];
});
function OptimisticUpdateExample() {
const [state, setState] = useState<ItemType[]>([]);
const [optimisticState, addOptimistic] = useOptimistic(state, (currentState, optimisticValue) => {
// Return new state with optimistic update
return [...currentState, optimisticValue];
});
🤖 Prompt for AI Agents
In docs/docs/guides/extending-the-dashboard/tech-stack/index.md around lines 23
to 27, the example using useOptimistic references an undeclared variable state,
causing a TypeScript error. Declare and initialize a state variable before using
it in useOptimistic, such as const state = [] or an appropriate initial value,
to fix the error and make the example complete.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
5.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@kkerti
Copy link
Contributor Author

kkerti commented Jun 30, 2025

@michaelbromley I have played around with struct, but I found the current implementation is although stricter, it feels safer through type checking, while the plugin user adds the CurrencyCode: MERCHANT_STRING pairs to plugin options. I would leave it as-is. No breaking change either.

@dlhck dlhck moved this from 📦 Backlog to 📅 Planned in Vendure OS Roadmap Aug 22, 2025
@dlhck dlhck modified the milestones: v3.4.0, v3.5.0 Aug 22, 2025
@kkerti
Copy link
Contributor Author

kkerti commented Oct 28, 2025

@michaelbromley started to patch package this feature set, do you need anything from me to get this into core payments plugins?

I get that you were working 24/7 on the dashboard - which came together fabolously -, still would love to see this merged.

@dlhck dlhck modified the milestones: v3.5.0, v3.6.0 Nov 3, 2025
@vercel
Copy link

vercel bot commented Nov 3, 2025

@dlhck is attempting to deploy a commit to the Vendure Team on Vercel.

A member of the Team first needs to authorize it.

@dlhck
Copy link
Collaborator

dlhck commented Nov 4, 2025

@kkerti I can't find the flag in the plugins options regarding the support for multiple merchant support, that Michael mentioned in his comment. Can you please take a look at that?

@kkerti
Copy link
Contributor Author

kkerti commented Nov 4, 2025

@kkerti I can't find the flag in the plugins options regarding the support for multiple merchant support, that Michael mentioned in his comment. Can you please take a look at that?

The plugin in this PR does not add any new customFields to vendure. I opted to define the merchantAccountIds object as a typed plugin option. The current implementation in my opinion doesn't need a feature flag, the new option property can be just omitted. Using undefined will use the user's braintree account default merchantAccountId.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 4, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
7.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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

Labels

None yet

Projects

Status: 📅 Planned

Development

Successfully merging this pull request may close these issues.

3 participants