@brainerce/mcp-server 2.1.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/http.js CHANGED
@@ -314,6 +314,20 @@ function CheckoutPage() {
314
314
  if (!paymentData) return <div>Unable to initialize payment</div>;
315
315
 
316
316
  // Render payment UI based on provider
317
+ if (paymentData.provider === 'sandbox') {
318
+ return (
319
+ <div className="text-center p-6 bg-amber-50 border border-amber-200 rounded-lg">
320
+ <h3 className="font-semibold mb-2">Test Mode</h3>
321
+ <p className="text-sm text-gray-600 mb-4">No real payment will be charged.</p>
322
+ <button onClick={async () => {
323
+ await client.completeGuestCheckout(checkoutId);
324
+ window.location.href = \\\`/order-confirmation?checkout_id=\\\${checkoutId}\\\`;
325
+ }} className="bg-amber-500 text-white px-6 py-2 rounded">
326
+ Complete Test Order
327
+ </button>
328
+ </div>
329
+ );
330
+ }
317
331
  if (paymentData.provider === 'grow') {
318
332
  return <GrowPaymentForm paymentUrl={paymentData.clientSecret} checkoutId={paymentData.checkoutId} />;
319
333
  }
@@ -432,11 +446,12 @@ const growProvider = providers.find(p => p.provider === 'grow');
432
446
  const paypalProvider = providers.find(p => p.provider === 'paypal');
433
447
  \`\`\`
434
448
 
435
- Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
449
+ Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'|'sandbox'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
436
450
 
437
451
  - **Stripe:** \`npm install @stripe/stripe-js @stripe/react-stripe-js\` \u2014 \`loadStripe(publicKey, { stripeAccount })\`
438
452
  - **Grow:** No SDK \u2014 uses iframe with payment URL. Supports credit cards, Bit, Apple Pay, Google Pay.
439
- - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\``;
453
+ - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\`
454
+ - **Sandbox:** No SDK needed \u2014 when \`sandboxPaymentsEnabled\` is true, \`getPaymentProviders()\` includes a sandbox provider with \`renderType: 'sandbox'\`. Show a "Complete Test Order" button. Call \`completeGuestCheckout(checkoutId)\` to finalize. Orders are marked \`isTestOrder: true\`.`;
440
455
  }
441
456
  function getProductsSection(_currency) {
442
457
  return `## Products & Variants
@@ -3157,7 +3172,12 @@ export default function CartPage() {
3157
3172
  <div className="mt-6 space-y-3">
3158
3173
  <h3 className="text-foreground text-sm font-semibold">{t('bundleOffers')}</h3>
3159
3174
  {bundles.bundles.map((offer) => (
3160
- <CartBundleOfferCard key={offer.id} offer={offer} onAdd={refreshCart} />
3175
+ <CartBundleOfferCard
3176
+ key={offer.id}
3177
+ offer={offer}
3178
+ cartId={cart.id}
3179
+ onAdd={refreshCart}
3180
+ />
3161
3181
  ))}
3162
3182
  </div>
3163
3183
  )}
@@ -7654,8 +7674,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7654
7674
  })
7655
7675
  .catch(() => null);
7656
7676
 
7657
- // B) Load + init SDK as early as possible
7677
+ // B) Load + init SDK as early as possible (skip for sandbox)
7658
7678
  providerPromise.then((providerSdk) => {
7679
+ if (providerSdk?.renderType === 'sandbox') return;
7659
7680
  if (providerSdk?.renderType === 'sdk-widget' && providerSdk.scriptUrl) {
7660
7681
  currentSdk = providerSdk;
7661
7682
  loadScript(providerSdk);
@@ -7682,6 +7703,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7682
7703
  const sdk = resolveClientSdk(intent, providerSdk);
7683
7704
  currentSdk = sdk;
7684
7705
 
7706
+ // Sandbox mode \u2014 no SDK to load, UI handles it
7707
+ if (sdk.renderType === 'sandbox') return;
7708
+
7685
7709
  if (sdk.renderType === 'redirect') {
7686
7710
  window.location.href = intent.clientSecret;
7687
7711
  return;
@@ -7776,6 +7800,48 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7776
7800
 
7777
7801
  const sdk = resolveClientSdk(paymentIntent, preloadedSdk);
7778
7802
 
7803
+ if (sdk.renderType === 'sandbox') {
7804
+ const handleCompleteSandbox = async () => {
7805
+ setLoading(true);
7806
+ try {
7807
+ const client = getClient();
7808
+ await client.completeGuestCheckout(checkoutId);
7809
+ window.location.href = \`/order-confirmation?checkout_id=\${checkoutId}\`;
7810
+ } catch (err) {
7811
+ setError(err instanceof Error ? err.message : t('paymentError'));
7812
+ setLoading(false);
7813
+ }
7814
+ };
7815
+
7816
+ return (
7817
+ <div className={cn('py-8 text-center', className)}>
7818
+ <div className="mx-auto max-w-md rounded-lg border border-amber-200 bg-amber-50 p-6">
7819
+ <svg
7820
+ className="mx-auto mb-3 h-10 w-10 text-amber-500"
7821
+ fill="none"
7822
+ viewBox="0 0 24 24"
7823
+ stroke="currentColor"
7824
+ >
7825
+ <path
7826
+ strokeLinecap="round"
7827
+ strokeLinejoin="round"
7828
+ strokeWidth={1.5}
7829
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"
7830
+ />
7831
+ </svg>
7832
+ <h3 className="text-foreground mb-1 text-lg font-semibold">{t('sandboxTitle')}</h3>
7833
+ <p className="text-muted-foreground mb-4 text-sm">{t('sandboxDescription')}</p>
7834
+ <button
7835
+ onClick={handleCompleteSandbox}
7836
+ className="inline-flex items-center rounded-md bg-amber-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-amber-600"
7837
+ >
7838
+ {t('completeTestOrder')}
7839
+ </button>
7840
+ </div>
7841
+ </div>
7842
+ );
7843
+ }
7844
+
7779
7845
  if (sdk.renderType === 'sdk-widget') {
7780
7846
  const containerId =
7781
7847
  sdk.containerId || \`\${paymentIntent.provider || 'payment'}-payment-container\`;
@@ -9933,18 +9999,18 @@ import { useState } from 'react';
9933
9999
  import Image from 'next/image';
9934
10000
  import type { CartBundleOffer as CartBundleOfferType } from 'brainerce';
9935
10001
  import { formatPrice } from 'brainerce';
9936
- import { getClient } from '@/lib/brainerce';
9937
10002
  import { useStoreInfo } from '@/providers/store-provider';
9938
10003
  import { useTranslations } from '@/lib/translations';
9939
10004
  import { cn } from '@/lib/utils';
9940
10005
 
9941
10006
  interface CartBundleOfferCardProps {
9942
10007
  offer: CartBundleOfferType;
10008
+ cartId: string;
9943
10009
  onAdd: () => void;
9944
10010
  className?: string;
9945
10011
  }
9946
10012
 
9947
- export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOfferCardProps) {
10013
+ export function CartBundleOfferCard({ offer, cartId, onAdd, className }: CartBundleOfferCardProps) {
9948
10014
  const { storeInfo } = useStoreInfo();
9949
10015
  const t = useTranslations('cart');
9950
10016
  const currency = storeInfo?.currency || 'USD';
@@ -9968,12 +10034,9 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9968
10034
  if (adding) return;
9969
10035
  try {
9970
10036
  setAdding(true);
10037
+ const { getClient } = await import('@/lib/brainerce');
9971
10038
  const client = getClient();
9972
- await client.smartAddToCart({
9973
- productId: product.id,
9974
- variantId: offer.bundleVariantId || undefined,
9975
- quantity: 1,
9976
- });
10039
+ await client.addBundleToCart(cartId, offer.id);
9977
10040
  onAdd();
9978
10041
  } catch (err) {
9979
10042
  console.error('Failed to add bundle item:', err);
@@ -10485,7 +10548,8 @@ var DETAILED = `# Required Pages \u2014 Detailed Guide
10485
10548
  - Step 1: Customer info (email, name)
10486
10549
  - Step 2: Shipping address form
10487
10550
  - Step 3: Shipping method selection (from rates returned by \`setShippingAddress()\`)
10488
- - Step 4: Payment (auto-detects Stripe/Grow/PayPal via \`getPaymentProviders()\`)
10551
+ - Step 4: Payment (auto-detects Stripe/Grow/PayPal/Sandbox via \`getPaymentProviders()\`)
10552
+ - If \`sandboxPaymentsEnabled\` is on, a sandbox provider with \`renderType: 'sandbox'\` is included \u2014 show a "Complete Test Order" button instead of real payment UI. Call \`completeGuestCheckout(checkoutId)\` to finalize.
10489
10553
  - Order summary sidebar showing \`checkout.lineItems\` (NOT cart.items!)
10490
10554
  - Tax display after address entry
10491
10555
  - Reservation countdown
@@ -10727,6 +10791,9 @@ function formatCapabilities(caps) {
10727
10791
  lines.push(
10728
10792
  `- Email verification: ${caps.connection.requireEmailVerification ? "required" : "not required"}`
10729
10793
  );
10794
+ lines.push(
10795
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "enabled (test orders without real payment)" : "disabled"}`
10796
+ );
10730
10797
  lines.push(
10731
10798
  `- Inventory reservation: ${caps.connection.reservationStrategy} (${caps.connection.reservationTimeout} min timeout)`
10732
10799
  );
@@ -10766,6 +10833,11 @@ function formatCapabilities(caps) {
10766
10833
  'Email verification is required \u2014 ALWAYS build the verify-email page. Use get-code-example("verify-email").'
10767
10834
  );
10768
10835
  }
10836
+ if (caps.connection.sandboxPaymentsEnabled) {
10837
+ suggestions.push(
10838
+ `Sandbox payments are enabled \u2014 the PaymentStep component shows a "Complete Test Order" button when renderType is 'sandbox'. No real charges. Test orders are marked isTestOrder: true.`
10839
+ );
10840
+ }
10769
10841
  if (caps.connection.reservationStrategy !== "ON_PAYMENT") {
10770
10842
  suggestions.push(
10771
10843
  'Inventory reservation is active \u2014 show a countdown timer. Use get-code-example("reservation-countdown").'
@@ -10919,6 +10991,9 @@ function formatCapabilitiesSummary(caps) {
10919
10991
  lines.push(
10920
10992
  `- Guest checkout tracking: ${caps.connection.guestCheckoutTracking ? "enabled" : "disabled"}`
10921
10993
  );
10994
+ lines.push(
10995
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "ENABLED \u2014 payment step shows test order button (no real charges)" : "disabled"}`
10996
+ );
10922
10997
  return lines.join("\n");
10923
10998
  }
10924
10999
  function buildStoreBundle(options) {
package/dist/bin/stdio.js CHANGED
@@ -312,6 +312,20 @@ function CheckoutPage() {
312
312
  if (!paymentData) return <div>Unable to initialize payment</div>;
313
313
 
314
314
  // Render payment UI based on provider
315
+ if (paymentData.provider === 'sandbox') {
316
+ return (
317
+ <div className="text-center p-6 bg-amber-50 border border-amber-200 rounded-lg">
318
+ <h3 className="font-semibold mb-2">Test Mode</h3>
319
+ <p className="text-sm text-gray-600 mb-4">No real payment will be charged.</p>
320
+ <button onClick={async () => {
321
+ await client.completeGuestCheckout(checkoutId);
322
+ window.location.href = \\\`/order-confirmation?checkout_id=\\\${checkoutId}\\\`;
323
+ }} className="bg-amber-500 text-white px-6 py-2 rounded">
324
+ Complete Test Order
325
+ </button>
326
+ </div>
327
+ );
328
+ }
315
329
  if (paymentData.provider === 'grow') {
316
330
  return <GrowPaymentForm paymentUrl={paymentData.clientSecret} checkoutId={paymentData.checkoutId} />;
317
331
  }
@@ -430,11 +444,12 @@ const growProvider = providers.find(p => p.provider === 'grow');
430
444
  const paypalProvider = providers.find(p => p.provider === 'paypal');
431
445
  \`\`\`
432
446
 
433
- Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
447
+ Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'|'sandbox'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
434
448
 
435
449
  - **Stripe:** \`npm install @stripe/stripe-js @stripe/react-stripe-js\` \u2014 \`loadStripe(publicKey, { stripeAccount })\`
436
450
  - **Grow:** No SDK \u2014 uses iframe with payment URL. Supports credit cards, Bit, Apple Pay, Google Pay.
437
- - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\``;
451
+ - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\`
452
+ - **Sandbox:** No SDK needed \u2014 when \`sandboxPaymentsEnabled\` is true, \`getPaymentProviders()\` includes a sandbox provider with \`renderType: 'sandbox'\`. Show a "Complete Test Order" button. Call \`completeGuestCheckout(checkoutId)\` to finalize. Orders are marked \`isTestOrder: true\`.`;
438
453
  }
439
454
  function getProductsSection(_currency) {
440
455
  return `## Products & Variants
@@ -3155,7 +3170,12 @@ export default function CartPage() {
3155
3170
  <div className="mt-6 space-y-3">
3156
3171
  <h3 className="text-foreground text-sm font-semibold">{t('bundleOffers')}</h3>
3157
3172
  {bundles.bundles.map((offer) => (
3158
- <CartBundleOfferCard key={offer.id} offer={offer} onAdd={refreshCart} />
3173
+ <CartBundleOfferCard
3174
+ key={offer.id}
3175
+ offer={offer}
3176
+ cartId={cart.id}
3177
+ onAdd={refreshCart}
3178
+ />
3159
3179
  ))}
3160
3180
  </div>
3161
3181
  )}
@@ -7652,8 +7672,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7652
7672
  })
7653
7673
  .catch(() => null);
7654
7674
 
7655
- // B) Load + init SDK as early as possible
7675
+ // B) Load + init SDK as early as possible (skip for sandbox)
7656
7676
  providerPromise.then((providerSdk) => {
7677
+ if (providerSdk?.renderType === 'sandbox') return;
7657
7678
  if (providerSdk?.renderType === 'sdk-widget' && providerSdk.scriptUrl) {
7658
7679
  currentSdk = providerSdk;
7659
7680
  loadScript(providerSdk);
@@ -7680,6 +7701,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7680
7701
  const sdk = resolveClientSdk(intent, providerSdk);
7681
7702
  currentSdk = sdk;
7682
7703
 
7704
+ // Sandbox mode \u2014 no SDK to load, UI handles it
7705
+ if (sdk.renderType === 'sandbox') return;
7706
+
7683
7707
  if (sdk.renderType === 'redirect') {
7684
7708
  window.location.href = intent.clientSecret;
7685
7709
  return;
@@ -7774,6 +7798,48 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7774
7798
 
7775
7799
  const sdk = resolveClientSdk(paymentIntent, preloadedSdk);
7776
7800
 
7801
+ if (sdk.renderType === 'sandbox') {
7802
+ const handleCompleteSandbox = async () => {
7803
+ setLoading(true);
7804
+ try {
7805
+ const client = getClient();
7806
+ await client.completeGuestCheckout(checkoutId);
7807
+ window.location.href = \`/order-confirmation?checkout_id=\${checkoutId}\`;
7808
+ } catch (err) {
7809
+ setError(err instanceof Error ? err.message : t('paymentError'));
7810
+ setLoading(false);
7811
+ }
7812
+ };
7813
+
7814
+ return (
7815
+ <div className={cn('py-8 text-center', className)}>
7816
+ <div className="mx-auto max-w-md rounded-lg border border-amber-200 bg-amber-50 p-6">
7817
+ <svg
7818
+ className="mx-auto mb-3 h-10 w-10 text-amber-500"
7819
+ fill="none"
7820
+ viewBox="0 0 24 24"
7821
+ stroke="currentColor"
7822
+ >
7823
+ <path
7824
+ strokeLinecap="round"
7825
+ strokeLinejoin="round"
7826
+ strokeWidth={1.5}
7827
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"
7828
+ />
7829
+ </svg>
7830
+ <h3 className="text-foreground mb-1 text-lg font-semibold">{t('sandboxTitle')}</h3>
7831
+ <p className="text-muted-foreground mb-4 text-sm">{t('sandboxDescription')}</p>
7832
+ <button
7833
+ onClick={handleCompleteSandbox}
7834
+ className="inline-flex items-center rounded-md bg-amber-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-amber-600"
7835
+ >
7836
+ {t('completeTestOrder')}
7837
+ </button>
7838
+ </div>
7839
+ </div>
7840
+ );
7841
+ }
7842
+
7777
7843
  if (sdk.renderType === 'sdk-widget') {
7778
7844
  const containerId =
7779
7845
  sdk.containerId || \`\${paymentIntent.provider || 'payment'}-payment-container\`;
@@ -9931,18 +9997,18 @@ import { useState } from 'react';
9931
9997
  import Image from 'next/image';
9932
9998
  import type { CartBundleOffer as CartBundleOfferType } from 'brainerce';
9933
9999
  import { formatPrice } from 'brainerce';
9934
- import { getClient } from '@/lib/brainerce';
9935
10000
  import { useStoreInfo } from '@/providers/store-provider';
9936
10001
  import { useTranslations } from '@/lib/translations';
9937
10002
  import { cn } from '@/lib/utils';
9938
10003
 
9939
10004
  interface CartBundleOfferCardProps {
9940
10005
  offer: CartBundleOfferType;
10006
+ cartId: string;
9941
10007
  onAdd: () => void;
9942
10008
  className?: string;
9943
10009
  }
9944
10010
 
9945
- export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOfferCardProps) {
10011
+ export function CartBundleOfferCard({ offer, cartId, onAdd, className }: CartBundleOfferCardProps) {
9946
10012
  const { storeInfo } = useStoreInfo();
9947
10013
  const t = useTranslations('cart');
9948
10014
  const currency = storeInfo?.currency || 'USD';
@@ -9966,12 +10032,9 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9966
10032
  if (adding) return;
9967
10033
  try {
9968
10034
  setAdding(true);
10035
+ const { getClient } = await import('@/lib/brainerce');
9969
10036
  const client = getClient();
9970
- await client.smartAddToCart({
9971
- productId: product.id,
9972
- variantId: offer.bundleVariantId || undefined,
9973
- quantity: 1,
9974
- });
10037
+ await client.addBundleToCart(cartId, offer.id);
9975
10038
  onAdd();
9976
10039
  } catch (err) {
9977
10040
  console.error('Failed to add bundle item:', err);
@@ -10483,7 +10546,8 @@ var DETAILED = `# Required Pages \u2014 Detailed Guide
10483
10546
  - Step 1: Customer info (email, name)
10484
10547
  - Step 2: Shipping address form
10485
10548
  - Step 3: Shipping method selection (from rates returned by \`setShippingAddress()\`)
10486
- - Step 4: Payment (auto-detects Stripe/Grow/PayPal via \`getPaymentProviders()\`)
10549
+ - Step 4: Payment (auto-detects Stripe/Grow/PayPal/Sandbox via \`getPaymentProviders()\`)
10550
+ - If \`sandboxPaymentsEnabled\` is on, a sandbox provider with \`renderType: 'sandbox'\` is included \u2014 show a "Complete Test Order" button instead of real payment UI. Call \`completeGuestCheckout(checkoutId)\` to finalize.
10487
10551
  - Order summary sidebar showing \`checkout.lineItems\` (NOT cart.items!)
10488
10552
  - Tax display after address entry
10489
10553
  - Reservation countdown
@@ -10725,6 +10789,9 @@ function formatCapabilities(caps) {
10725
10789
  lines.push(
10726
10790
  `- Email verification: ${caps.connection.requireEmailVerification ? "required" : "not required"}`
10727
10791
  );
10792
+ lines.push(
10793
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "enabled (test orders without real payment)" : "disabled"}`
10794
+ );
10728
10795
  lines.push(
10729
10796
  `- Inventory reservation: ${caps.connection.reservationStrategy} (${caps.connection.reservationTimeout} min timeout)`
10730
10797
  );
@@ -10764,6 +10831,11 @@ function formatCapabilities(caps) {
10764
10831
  'Email verification is required \u2014 ALWAYS build the verify-email page. Use get-code-example("verify-email").'
10765
10832
  );
10766
10833
  }
10834
+ if (caps.connection.sandboxPaymentsEnabled) {
10835
+ suggestions.push(
10836
+ `Sandbox payments are enabled \u2014 the PaymentStep component shows a "Complete Test Order" button when renderType is 'sandbox'. No real charges. Test orders are marked isTestOrder: true.`
10837
+ );
10838
+ }
10767
10839
  if (caps.connection.reservationStrategy !== "ON_PAYMENT") {
10768
10840
  suggestions.push(
10769
10841
  'Inventory reservation is active \u2014 show a countdown timer. Use get-code-example("reservation-countdown").'
@@ -10917,6 +10989,9 @@ function formatCapabilitiesSummary(caps) {
10917
10989
  lines.push(
10918
10990
  `- Guest checkout tracking: ${caps.connection.guestCheckoutTracking ? "enabled" : "disabled"}`
10919
10991
  );
10992
+ lines.push(
10993
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "ENABLED \u2014 payment step shows test order button (no real charges)" : "disabled"}`
10994
+ );
10920
10995
  return lines.join("\n");
10921
10996
  }
10922
10997
  function buildStoreBundle(options) {
package/dist/index.d.mts CHANGED
@@ -50,6 +50,7 @@ interface StoreCapabilities {
50
50
  ordersWriteEnabled: boolean;
51
51
  guestCheckoutTracking: boolean;
52
52
  requireEmailVerification: boolean;
53
+ sandboxPaymentsEnabled: boolean;
53
54
  reservationStrategy: string;
54
55
  reservationTimeout: number;
55
56
  lowStockWarning: boolean;
package/dist/index.d.ts CHANGED
@@ -50,6 +50,7 @@ interface StoreCapabilities {
50
50
  ordersWriteEnabled: boolean;
51
51
  guestCheckoutTracking: boolean;
52
52
  requireEmailVerification: boolean;
53
+ sandboxPaymentsEnabled: boolean;
53
54
  reservationStrategy: string;
54
55
  reservationTimeout: number;
55
56
  lowStockWarning: boolean;
package/dist/index.js CHANGED
@@ -329,6 +329,20 @@ function CheckoutPage() {
329
329
  if (!paymentData) return <div>Unable to initialize payment</div>;
330
330
 
331
331
  // Render payment UI based on provider
332
+ if (paymentData.provider === 'sandbox') {
333
+ return (
334
+ <div className="text-center p-6 bg-amber-50 border border-amber-200 rounded-lg">
335
+ <h3 className="font-semibold mb-2">Test Mode</h3>
336
+ <p className="text-sm text-gray-600 mb-4">No real payment will be charged.</p>
337
+ <button onClick={async () => {
338
+ await client.completeGuestCheckout(checkoutId);
339
+ window.location.href = \\\`/order-confirmation?checkout_id=\\\${checkoutId}\\\`;
340
+ }} className="bg-amber-500 text-white px-6 py-2 rounded">
341
+ Complete Test Order
342
+ </button>
343
+ </div>
344
+ );
345
+ }
332
346
  if (paymentData.provider === 'grow') {
333
347
  return <GrowPaymentForm paymentUrl={paymentData.clientSecret} checkoutId={paymentData.checkoutId} />;
334
348
  }
@@ -447,11 +461,12 @@ const growProvider = providers.find(p => p.provider === 'grow');
447
461
  const paypalProvider = providers.find(p => p.provider === 'paypal');
448
462
  \`\`\`
449
463
 
450
- Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
464
+ Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'|'sandbox'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
451
465
 
452
466
  - **Stripe:** \`npm install @stripe/stripe-js @stripe/react-stripe-js\` \u2014 \`loadStripe(publicKey, { stripeAccount })\`
453
467
  - **Grow:** No SDK \u2014 uses iframe with payment URL. Supports credit cards, Bit, Apple Pay, Google Pay.
454
- - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\``;
468
+ - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\`
469
+ - **Sandbox:** No SDK needed \u2014 when \`sandboxPaymentsEnabled\` is true, \`getPaymentProviders()\` includes a sandbox provider with \`renderType: 'sandbox'\`. Show a "Complete Test Order" button. Call \`completeGuestCheckout(checkoutId)\` to finalize. Orders are marked \`isTestOrder: true\`.`;
455
470
  }
456
471
  function getProductsSection(_currency) {
457
472
  return `## Products & Variants
@@ -3172,7 +3187,12 @@ export default function CartPage() {
3172
3187
  <div className="mt-6 space-y-3">
3173
3188
  <h3 className="text-foreground text-sm font-semibold">{t('bundleOffers')}</h3>
3174
3189
  {bundles.bundles.map((offer) => (
3175
- <CartBundleOfferCard key={offer.id} offer={offer} onAdd={refreshCart} />
3190
+ <CartBundleOfferCard
3191
+ key={offer.id}
3192
+ offer={offer}
3193
+ cartId={cart.id}
3194
+ onAdd={refreshCart}
3195
+ />
3176
3196
  ))}
3177
3197
  </div>
3178
3198
  )}
@@ -7669,8 +7689,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7669
7689
  })
7670
7690
  .catch(() => null);
7671
7691
 
7672
- // B) Load + init SDK as early as possible
7692
+ // B) Load + init SDK as early as possible (skip for sandbox)
7673
7693
  providerPromise.then((providerSdk) => {
7694
+ if (providerSdk?.renderType === 'sandbox') return;
7674
7695
  if (providerSdk?.renderType === 'sdk-widget' && providerSdk.scriptUrl) {
7675
7696
  currentSdk = providerSdk;
7676
7697
  loadScript(providerSdk);
@@ -7697,6 +7718,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7697
7718
  const sdk = resolveClientSdk(intent, providerSdk);
7698
7719
  currentSdk = sdk;
7699
7720
 
7721
+ // Sandbox mode \u2014 no SDK to load, UI handles it
7722
+ if (sdk.renderType === 'sandbox') return;
7723
+
7700
7724
  if (sdk.renderType === 'redirect') {
7701
7725
  window.location.href = intent.clientSecret;
7702
7726
  return;
@@ -7791,6 +7815,48 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7791
7815
 
7792
7816
  const sdk = resolveClientSdk(paymentIntent, preloadedSdk);
7793
7817
 
7818
+ if (sdk.renderType === 'sandbox') {
7819
+ const handleCompleteSandbox = async () => {
7820
+ setLoading(true);
7821
+ try {
7822
+ const client = getClient();
7823
+ await client.completeGuestCheckout(checkoutId);
7824
+ window.location.href = \`/order-confirmation?checkout_id=\${checkoutId}\`;
7825
+ } catch (err) {
7826
+ setError(err instanceof Error ? err.message : t('paymentError'));
7827
+ setLoading(false);
7828
+ }
7829
+ };
7830
+
7831
+ return (
7832
+ <div className={cn('py-8 text-center', className)}>
7833
+ <div className="mx-auto max-w-md rounded-lg border border-amber-200 bg-amber-50 p-6">
7834
+ <svg
7835
+ className="mx-auto mb-3 h-10 w-10 text-amber-500"
7836
+ fill="none"
7837
+ viewBox="0 0 24 24"
7838
+ stroke="currentColor"
7839
+ >
7840
+ <path
7841
+ strokeLinecap="round"
7842
+ strokeLinejoin="round"
7843
+ strokeWidth={1.5}
7844
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"
7845
+ />
7846
+ </svg>
7847
+ <h3 className="text-foreground mb-1 text-lg font-semibold">{t('sandboxTitle')}</h3>
7848
+ <p className="text-muted-foreground mb-4 text-sm">{t('sandboxDescription')}</p>
7849
+ <button
7850
+ onClick={handleCompleteSandbox}
7851
+ className="inline-flex items-center rounded-md bg-amber-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-amber-600"
7852
+ >
7853
+ {t('completeTestOrder')}
7854
+ </button>
7855
+ </div>
7856
+ </div>
7857
+ );
7858
+ }
7859
+
7794
7860
  if (sdk.renderType === 'sdk-widget') {
7795
7861
  const containerId =
7796
7862
  sdk.containerId || \`\${paymentIntent.provider || 'payment'}-payment-container\`;
@@ -9948,18 +10014,18 @@ import { useState } from 'react';
9948
10014
  import Image from 'next/image';
9949
10015
  import type { CartBundleOffer as CartBundleOfferType } from 'brainerce';
9950
10016
  import { formatPrice } from 'brainerce';
9951
- import { getClient } from '@/lib/brainerce';
9952
10017
  import { useStoreInfo } from '@/providers/store-provider';
9953
10018
  import { useTranslations } from '@/lib/translations';
9954
10019
  import { cn } from '@/lib/utils';
9955
10020
 
9956
10021
  interface CartBundleOfferCardProps {
9957
10022
  offer: CartBundleOfferType;
10023
+ cartId: string;
9958
10024
  onAdd: () => void;
9959
10025
  className?: string;
9960
10026
  }
9961
10027
 
9962
- export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOfferCardProps) {
10028
+ export function CartBundleOfferCard({ offer, cartId, onAdd, className }: CartBundleOfferCardProps) {
9963
10029
  const { storeInfo } = useStoreInfo();
9964
10030
  const t = useTranslations('cart');
9965
10031
  const currency = storeInfo?.currency || 'USD';
@@ -9983,12 +10049,9 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9983
10049
  if (adding) return;
9984
10050
  try {
9985
10051
  setAdding(true);
10052
+ const { getClient } = await import('@/lib/brainerce');
9986
10053
  const client = getClient();
9987
- await client.smartAddToCart({
9988
- productId: product.id,
9989
- variantId: offer.bundleVariantId || undefined,
9990
- quantity: 1,
9991
- });
10054
+ await client.addBundleToCart(cartId, offer.id);
9992
10055
  onAdd();
9993
10056
  } catch (err) {
9994
10057
  console.error('Failed to add bundle item:', err);
@@ -10500,7 +10563,8 @@ var DETAILED = `# Required Pages \u2014 Detailed Guide
10500
10563
  - Step 1: Customer info (email, name)
10501
10564
  - Step 2: Shipping address form
10502
10565
  - Step 3: Shipping method selection (from rates returned by \`setShippingAddress()\`)
10503
- - Step 4: Payment (auto-detects Stripe/Grow/PayPal via \`getPaymentProviders()\`)
10566
+ - Step 4: Payment (auto-detects Stripe/Grow/PayPal/Sandbox via \`getPaymentProviders()\`)
10567
+ - If \`sandboxPaymentsEnabled\` is on, a sandbox provider with \`renderType: 'sandbox'\` is included \u2014 show a "Complete Test Order" button instead of real payment UI. Call \`completeGuestCheckout(checkoutId)\` to finalize.
10504
10568
  - Order summary sidebar showing \`checkout.lineItems\` (NOT cart.items!)
10505
10569
  - Tax display after address entry
10506
10570
  - Reservation countdown
@@ -10742,6 +10806,9 @@ function formatCapabilities(caps) {
10742
10806
  lines.push(
10743
10807
  `- Email verification: ${caps.connection.requireEmailVerification ? "required" : "not required"}`
10744
10808
  );
10809
+ lines.push(
10810
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "enabled (test orders without real payment)" : "disabled"}`
10811
+ );
10745
10812
  lines.push(
10746
10813
  `- Inventory reservation: ${caps.connection.reservationStrategy} (${caps.connection.reservationTimeout} min timeout)`
10747
10814
  );
@@ -10781,6 +10848,11 @@ function formatCapabilities(caps) {
10781
10848
  'Email verification is required \u2014 ALWAYS build the verify-email page. Use get-code-example("verify-email").'
10782
10849
  );
10783
10850
  }
10851
+ if (caps.connection.sandboxPaymentsEnabled) {
10852
+ suggestions.push(
10853
+ `Sandbox payments are enabled \u2014 the PaymentStep component shows a "Complete Test Order" button when renderType is 'sandbox'. No real charges. Test orders are marked isTestOrder: true.`
10854
+ );
10855
+ }
10784
10856
  if (caps.connection.reservationStrategy !== "ON_PAYMENT") {
10785
10857
  suggestions.push(
10786
10858
  'Inventory reservation is active \u2014 show a countdown timer. Use get-code-example("reservation-countdown").'
@@ -10934,6 +11006,9 @@ function formatCapabilitiesSummary(caps) {
10934
11006
  lines.push(
10935
11007
  `- Guest checkout tracking: ${caps.connection.guestCheckoutTracking ? "enabled" : "disabled"}`
10936
11008
  );
11009
+ lines.push(
11010
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "ENABLED \u2014 payment step shows test order button (no real charges)" : "disabled"}`
11011
+ );
10937
11012
  return lines.join("\n");
10938
11013
  }
10939
11014
  function buildStoreBundle(options) {
package/dist/index.mjs CHANGED
@@ -284,6 +284,20 @@ function CheckoutPage() {
284
284
  if (!paymentData) return <div>Unable to initialize payment</div>;
285
285
 
286
286
  // Render payment UI based on provider
287
+ if (paymentData.provider === 'sandbox') {
288
+ return (
289
+ <div className="text-center p-6 bg-amber-50 border border-amber-200 rounded-lg">
290
+ <h3 className="font-semibold mb-2">Test Mode</h3>
291
+ <p className="text-sm text-gray-600 mb-4">No real payment will be charged.</p>
292
+ <button onClick={async () => {
293
+ await client.completeGuestCheckout(checkoutId);
294
+ window.location.href = \\\`/order-confirmation?checkout_id=\\\${checkoutId}\\\`;
295
+ }} className="bg-amber-500 text-white px-6 py-2 rounded">
296
+ Complete Test Order
297
+ </button>
298
+ </div>
299
+ );
300
+ }
287
301
  if (paymentData.provider === 'grow') {
288
302
  return <GrowPaymentForm paymentUrl={paymentData.clientSecret} checkoutId={paymentData.checkoutId} />;
289
303
  }
@@ -402,11 +416,12 @@ const growProvider = providers.find(p => p.provider === 'grow');
402
416
  const paypalProvider = providers.find(p => p.provider === 'paypal');
403
417
  \`\`\`
404
418
 
405
- Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
419
+ Each provider has: \`id\`, \`provider\` ('stripe'|'grow'|'paypal'|'sandbox'), \`name\`, \`publicKey\`, \`stripeAccountId\` (Stripe only), \`supportedMethods\`, \`testMode\`, \`isDefault\`.
406
420
 
407
421
  - **Stripe:** \`npm install @stripe/stripe-js @stripe/react-stripe-js\` \u2014 \`loadStripe(publicKey, { stripeAccount })\`
408
422
  - **Grow:** No SDK \u2014 uses iframe with payment URL. Supports credit cards, Bit, Apple Pay, Google Pay.
409
- - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\``;
423
+ - **PayPal:** \`npm install @paypal/react-paypal-js\` \u2014 \`PayPalScriptProvider\` + \`PayPalButtons\`
424
+ - **Sandbox:** No SDK needed \u2014 when \`sandboxPaymentsEnabled\` is true, \`getPaymentProviders()\` includes a sandbox provider with \`renderType: 'sandbox'\`. Show a "Complete Test Order" button. Call \`completeGuestCheckout(checkoutId)\` to finalize. Orders are marked \`isTestOrder: true\`.`;
410
425
  }
411
426
  function getProductsSection(_currency) {
412
427
  return `## Products & Variants
@@ -3127,7 +3142,12 @@ export default function CartPage() {
3127
3142
  <div className="mt-6 space-y-3">
3128
3143
  <h3 className="text-foreground text-sm font-semibold">{t('bundleOffers')}</h3>
3129
3144
  {bundles.bundles.map((offer) => (
3130
- <CartBundleOfferCard key={offer.id} offer={offer} onAdd={refreshCart} />
3145
+ <CartBundleOfferCard
3146
+ key={offer.id}
3147
+ offer={offer}
3148
+ cartId={cart.id}
3149
+ onAdd={refreshCart}
3150
+ />
3131
3151
  ))}
3132
3152
  </div>
3133
3153
  )}
@@ -7624,8 +7644,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7624
7644
  })
7625
7645
  .catch(() => null);
7626
7646
 
7627
- // B) Load + init SDK as early as possible
7647
+ // B) Load + init SDK as early as possible (skip for sandbox)
7628
7648
  providerPromise.then((providerSdk) => {
7649
+ if (providerSdk?.renderType === 'sandbox') return;
7629
7650
  if (providerSdk?.renderType === 'sdk-widget' && providerSdk.scriptUrl) {
7630
7651
  currentSdk = providerSdk;
7631
7652
  loadScript(providerSdk);
@@ -7652,6 +7673,9 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7652
7673
  const sdk = resolveClientSdk(intent, providerSdk);
7653
7674
  currentSdk = sdk;
7654
7675
 
7676
+ // Sandbox mode \u2014 no SDK to load, UI handles it
7677
+ if (sdk.renderType === 'sandbox') return;
7678
+
7655
7679
  if (sdk.renderType === 'redirect') {
7656
7680
  window.location.href = intent.clientSecret;
7657
7681
  return;
@@ -7746,6 +7770,48 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
7746
7770
 
7747
7771
  const sdk = resolveClientSdk(paymentIntent, preloadedSdk);
7748
7772
 
7773
+ if (sdk.renderType === 'sandbox') {
7774
+ const handleCompleteSandbox = async () => {
7775
+ setLoading(true);
7776
+ try {
7777
+ const client = getClient();
7778
+ await client.completeGuestCheckout(checkoutId);
7779
+ window.location.href = \`/order-confirmation?checkout_id=\${checkoutId}\`;
7780
+ } catch (err) {
7781
+ setError(err instanceof Error ? err.message : t('paymentError'));
7782
+ setLoading(false);
7783
+ }
7784
+ };
7785
+
7786
+ return (
7787
+ <div className={cn('py-8 text-center', className)}>
7788
+ <div className="mx-auto max-w-md rounded-lg border border-amber-200 bg-amber-50 p-6">
7789
+ <svg
7790
+ className="mx-auto mb-3 h-10 w-10 text-amber-500"
7791
+ fill="none"
7792
+ viewBox="0 0 24 24"
7793
+ stroke="currentColor"
7794
+ >
7795
+ <path
7796
+ strokeLinecap="round"
7797
+ strokeLinejoin="round"
7798
+ strokeWidth={1.5}
7799
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"
7800
+ />
7801
+ </svg>
7802
+ <h3 className="text-foreground mb-1 text-lg font-semibold">{t('sandboxTitle')}</h3>
7803
+ <p className="text-muted-foreground mb-4 text-sm">{t('sandboxDescription')}</p>
7804
+ <button
7805
+ onClick={handleCompleteSandbox}
7806
+ className="inline-flex items-center rounded-md bg-amber-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-amber-600"
7807
+ >
7808
+ {t('completeTestOrder')}
7809
+ </button>
7810
+ </div>
7811
+ </div>
7812
+ );
7813
+ }
7814
+
7749
7815
  if (sdk.renderType === 'sdk-widget') {
7750
7816
  const containerId =
7751
7817
  sdk.containerId || \`\${paymentIntent.provider || 'payment'}-payment-container\`;
@@ -9903,18 +9969,18 @@ import { useState } from 'react';
9903
9969
  import Image from 'next/image';
9904
9970
  import type { CartBundleOffer as CartBundleOfferType } from 'brainerce';
9905
9971
  import { formatPrice } from 'brainerce';
9906
- import { getClient } from '@/lib/brainerce';
9907
9972
  import { useStoreInfo } from '@/providers/store-provider';
9908
9973
  import { useTranslations } from '@/lib/translations';
9909
9974
  import { cn } from '@/lib/utils';
9910
9975
 
9911
9976
  interface CartBundleOfferCardProps {
9912
9977
  offer: CartBundleOfferType;
9978
+ cartId: string;
9913
9979
  onAdd: () => void;
9914
9980
  className?: string;
9915
9981
  }
9916
9982
 
9917
- export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOfferCardProps) {
9983
+ export function CartBundleOfferCard({ offer, cartId, onAdd, className }: CartBundleOfferCardProps) {
9918
9984
  const { storeInfo } = useStoreInfo();
9919
9985
  const t = useTranslations('cart');
9920
9986
  const currency = storeInfo?.currency || 'USD';
@@ -9938,12 +10004,9 @@ export function CartBundleOfferCard({ offer, onAdd, className }: CartBundleOffer
9938
10004
  if (adding) return;
9939
10005
  try {
9940
10006
  setAdding(true);
10007
+ const { getClient } = await import('@/lib/brainerce');
9941
10008
  const client = getClient();
9942
- await client.smartAddToCart({
9943
- productId: product.id,
9944
- variantId: offer.bundleVariantId || undefined,
9945
- quantity: 1,
9946
- });
10009
+ await client.addBundleToCart(cartId, offer.id);
9947
10010
  onAdd();
9948
10011
  } catch (err) {
9949
10012
  console.error('Failed to add bundle item:', err);
@@ -10455,7 +10518,8 @@ var DETAILED = `# Required Pages \u2014 Detailed Guide
10455
10518
  - Step 1: Customer info (email, name)
10456
10519
  - Step 2: Shipping address form
10457
10520
  - Step 3: Shipping method selection (from rates returned by \`setShippingAddress()\`)
10458
- - Step 4: Payment (auto-detects Stripe/Grow/PayPal via \`getPaymentProviders()\`)
10521
+ - Step 4: Payment (auto-detects Stripe/Grow/PayPal/Sandbox via \`getPaymentProviders()\`)
10522
+ - If \`sandboxPaymentsEnabled\` is on, a sandbox provider with \`renderType: 'sandbox'\` is included \u2014 show a "Complete Test Order" button instead of real payment UI. Call \`completeGuestCheckout(checkoutId)\` to finalize.
10459
10523
  - Order summary sidebar showing \`checkout.lineItems\` (NOT cart.items!)
10460
10524
  - Tax display after address entry
10461
10525
  - Reservation countdown
@@ -10697,6 +10761,9 @@ function formatCapabilities(caps) {
10697
10761
  lines.push(
10698
10762
  `- Email verification: ${caps.connection.requireEmailVerification ? "required" : "not required"}`
10699
10763
  );
10764
+ lines.push(
10765
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "enabled (test orders without real payment)" : "disabled"}`
10766
+ );
10700
10767
  lines.push(
10701
10768
  `- Inventory reservation: ${caps.connection.reservationStrategy} (${caps.connection.reservationTimeout} min timeout)`
10702
10769
  );
@@ -10736,6 +10803,11 @@ function formatCapabilities(caps) {
10736
10803
  'Email verification is required \u2014 ALWAYS build the verify-email page. Use get-code-example("verify-email").'
10737
10804
  );
10738
10805
  }
10806
+ if (caps.connection.sandboxPaymentsEnabled) {
10807
+ suggestions.push(
10808
+ `Sandbox payments are enabled \u2014 the PaymentStep component shows a "Complete Test Order" button when renderType is 'sandbox'. No real charges. Test orders are marked isTestOrder: true.`
10809
+ );
10810
+ }
10739
10811
  if (caps.connection.reservationStrategy !== "ON_PAYMENT") {
10740
10812
  suggestions.push(
10741
10813
  'Inventory reservation is active \u2014 show a countdown timer. Use get-code-example("reservation-countdown").'
@@ -10889,6 +10961,9 @@ function formatCapabilitiesSummary(caps) {
10889
10961
  lines.push(
10890
10962
  `- Guest checkout tracking: ${caps.connection.guestCheckoutTracking ? "enabled" : "disabled"}`
10891
10963
  );
10964
+ lines.push(
10965
+ `- Sandbox payments: ${caps.connection.sandboxPaymentsEnabled ? "ENABLED \u2014 payment step shows test order button (no real charges)" : "disabled"}`
10966
+ );
10892
10967
  return lines.join("\n");
10893
10968
  }
10894
10969
  function buildStoreBundle(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainerce/mcp-server",
3
- "version": "2.1.1",
3
+ "version": "2.4.0",
4
4
  "description": "MCP server for AI-powered Brainerce store building. Provides SDK docs, types, and code examples to AI tools like Lovable, Cursor, and Claude Code.",
5
5
  "bin": {
6
6
  "brainerce-mcp": "dist/bin/stdio.js"