#
tokens: 48755/50000 5/825 files (page 35/43)
lines: off (toggle) GitHub
raw markdown copy
This is page 35 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?page={x} to view the full context.

# Directory Structure

```
├── .DS_Store
├── .github
│   ├── dependabot.yml
│   ├── instructions
│   │   ├── mcp-node-tests.instructions.md
│   │   └── mcp-yml-tests.instructions.md
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── PULL_REQUEST_TEMPLATE
│   │   ├── bug_fix.md
│   │   ├── documentation.md
│   │   └── new_tool.md
│   ├── pull_request_template.md
│   └── workflows
│       ├── ci.yml
│       ├── deploy-pages.yml
│       ├── publish.yml
│       └── update-docs.yml
├── .gitignore
├── .husky
│   └── pre-commit
├── aegis.config.docs-only.json
├── aegis.config.json
├── aegis.config.with-dw.json
├── AGENTS.md
├── ai-instructions
│   ├── claude-desktop
│   │   └── claude_custom_instructions.md
│   ├── cursor
│   │   └── .cursor
│   │       └── rules
│   │           ├── debugging-workflows.mdc
│   │           ├── hooks-development.mdc
│   │           ├── isml-templates.mdc
│   │           ├── job-framework.mdc
│   │           ├── performance-optimization.mdc
│   │           ├── scapi-endpoints.mdc
│   │           ├── security-patterns.mdc
│   │           ├── sfcc-development.mdc
│   │           ├── sfra-controllers.mdc
│   │           ├── sfra-models.mdc
│   │           ├── system-objects.mdc
│   │           └── testing-patterns.mdc
│   └── github-copilot
│       └── copilot-instructions.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── docs
│   ├── best-practices
│   │   ├── cartridge_creation.md
│   │   ├── isml_templates.md
│   │   ├── job_framework.md
│   │   ├── localserviceregistry.md
│   │   ├── ocapi_hooks.md
│   │   ├── performance.md
│   │   ├── scapi_custom_endpoint.md
│   │   ├── scapi_hooks.md
│   │   ├── security.md
│   │   ├── sfra_client_side_js.md
│   │   ├── sfra_controllers.md
│   │   ├── sfra_models.md
│   │   └── sfra_scss.md
│   ├── dw_campaign
│   │   ├── ABTest.md
│   │   ├── ABTestMgr.md
│   │   ├── ABTestSegment.md
│   │   ├── AmountDiscount.md
│   │   ├── ApproachingDiscount.md
│   │   ├── BonusChoiceDiscount.md
│   │   ├── BonusDiscount.md
│   │   ├── Campaign.md
│   │   ├── CampaignMgr.md
│   │   ├── CampaignStatusCodes.md
│   │   ├── Coupon.md
│   │   ├── CouponMgr.md
│   │   ├── CouponRedemption.md
│   │   ├── CouponStatusCodes.md
│   │   ├── Discount.md
│   │   ├── DiscountPlan.md
│   │   ├── FixedPriceDiscount.md
│   │   ├── FixedPriceShippingDiscount.md
│   │   ├── FreeDiscount.md
│   │   ├── FreeShippingDiscount.md
│   │   ├── PercentageDiscount.md
│   │   ├── PercentageOptionDiscount.md
│   │   ├── PriceBookPriceDiscount.md
│   │   ├── Promotion.md
│   │   ├── PromotionMgr.md
│   │   ├── PromotionPlan.md
│   │   ├── SlotContent.md
│   │   ├── SourceCodeGroup.md
│   │   ├── SourceCodeInfo.md
│   │   ├── SourceCodeStatusCodes.md
│   │   └── TotalFixedPriceDiscount.md
│   ├── dw_catalog
│   │   ├── Catalog.md
│   │   ├── CatalogMgr.md
│   │   ├── Category.md
│   │   ├── CategoryAssignment.md
│   │   ├── CategoryLink.md
│   │   ├── PriceBook.md
│   │   ├── PriceBookMgr.md
│   │   ├── Product.md
│   │   ├── ProductActiveData.md
│   │   ├── ProductAttributeModel.md
│   │   ├── ProductAvailabilityLevels.md
│   │   ├── ProductAvailabilityModel.md
│   │   ├── ProductInventoryList.md
│   │   ├── ProductInventoryMgr.md
│   │   ├── ProductInventoryRecord.md
│   │   ├── ProductLink.md
│   │   ├── ProductMgr.md
│   │   ├── ProductOption.md
│   │   ├── ProductOptionModel.md
│   │   ├── ProductOptionValue.md
│   │   ├── ProductPriceInfo.md
│   │   ├── ProductPriceModel.md
│   │   ├── ProductPriceTable.md
│   │   ├── ProductSearchHit.md
│   │   ├── ProductSearchModel.md
│   │   ├── ProductSearchRefinementDefinition.md
│   │   ├── ProductSearchRefinements.md
│   │   ├── ProductSearchRefinementValue.md
│   │   ├── ProductVariationAttribute.md
│   │   ├── ProductVariationAttributeValue.md
│   │   ├── ProductVariationModel.md
│   │   ├── Recommendation.md
│   │   ├── SearchModel.md
│   │   ├── SearchRefinementDefinition.md
│   │   ├── SearchRefinements.md
│   │   ├── SearchRefinementValue.md
│   │   ├── SortingOption.md
│   │   ├── SortingRule.md
│   │   ├── Store.md
│   │   ├── StoreGroup.md
│   │   ├── StoreInventoryFilter.md
│   │   ├── StoreInventoryFilterValue.md
│   │   ├── StoreMgr.md
│   │   ├── Variant.md
│   │   └── VariationGroup.md
│   ├── dw_content
│   │   ├── Content.md
│   │   ├── ContentMgr.md
│   │   ├── ContentSearchModel.md
│   │   ├── ContentSearchRefinementDefinition.md
│   │   ├── ContentSearchRefinements.md
│   │   ├── ContentSearchRefinementValue.md
│   │   ├── Folder.md
│   │   ├── Library.md
│   │   ├── MarkupText.md
│   │   └── MediaFile.md
│   ├── dw_crypto
│   │   ├── CertificateRef.md
│   │   ├── CertificateUtils.md
│   │   ├── Cipher.md
│   │   ├── Encoding.md
│   │   ├── JWE.md
│   │   ├── JWEHeader.md
│   │   ├── JWS.md
│   │   ├── JWSHeader.md
│   │   ├── KeyRef.md
│   │   ├── Mac.md
│   │   ├── MessageDigest.md
│   │   ├── SecureRandom.md
│   │   ├── Signature.md
│   │   ├── WeakCipher.md
│   │   ├── WeakMac.md
│   │   ├── WeakMessageDigest.md
│   │   ├── WeakSignature.md
│   │   └── X509Certificate.md
│   ├── dw_customer
│   │   ├── AddressBook.md
│   │   ├── AgentUserMgr.md
│   │   ├── AgentUserStatusCodes.md
│   │   ├── AuthenticationStatus.md
│   │   ├── Credentials.md
│   │   ├── Customer.md
│   │   ├── CustomerActiveData.md
│   │   ├── CustomerAddress.md
│   │   ├── CustomerCDPData.md
│   │   ├── CustomerContextMgr.md
│   │   ├── CustomerGroup.md
│   │   ├── CustomerList.md
│   │   ├── CustomerMgr.md
│   │   ├── CustomerPasswordConstraints.md
│   │   ├── CustomerPaymentInstrument.md
│   │   ├── CustomerStatusCodes.md
│   │   ├── EncryptedObject.md
│   │   ├── ExternalProfile.md
│   │   ├── OrderHistory.md
│   │   ├── ProductList.md
│   │   ├── ProductListItem.md
│   │   ├── ProductListItemPurchase.md
│   │   ├── ProductListMgr.md
│   │   ├── ProductListRegistrant.md
│   │   ├── Profile.md
│   │   └── Wallet.md
│   ├── dw_extensions.applepay
│   │   ├── ApplePayHookResult.md
│   │   └── ApplePayHooks.md
│   ├── dw_extensions.facebook
│   │   ├── FacebookFeedHooks.md
│   │   └── FacebookProduct.md
│   ├── dw_extensions.paymentrequest
│   │   ├── PaymentRequestHookResult.md
│   │   └── PaymentRequestHooks.md
│   ├── dw_extensions.payments
│   │   ├── SalesforceBancontactPaymentDetails.md
│   │   ├── SalesforceCardPaymentDetails.md
│   │   ├── SalesforceEpsPaymentDetails.md
│   │   ├── SalesforceIdealPaymentDetails.md
│   │   ├── SalesforceKlarnaPaymentDetails.md
│   │   ├── SalesforcePaymentDetails.md
│   │   ├── SalesforcePaymentIntent.md
│   │   ├── SalesforcePaymentMethod.md
│   │   ├── SalesforcePaymentRequest.md
│   │   ├── SalesforcePaymentsHooks.md
│   │   ├── SalesforcePaymentsMgr.md
│   │   ├── SalesforcePaymentsSiteConfiguration.md
│   │   ├── SalesforcePayPalOrder.md
│   │   ├── SalesforcePayPalOrderAddress.md
│   │   ├── SalesforcePayPalOrderPayer.md
│   │   ├── SalesforcePayPalPaymentDetails.md
│   │   ├── SalesforceSepaDebitPaymentDetails.md
│   │   └── SalesforceVenmoPaymentDetails.md
│   ├── dw_extensions.pinterest
│   │   ├── PinterestAvailability.md
│   │   ├── PinterestFeedHooks.md
│   │   ├── PinterestOrder.md
│   │   ├── PinterestOrderHooks.md
│   │   └── PinterestProduct.md
│   ├── dw_io
│   │   ├── CSVStreamReader.md
│   │   ├── CSVStreamWriter.md
│   │   ├── File.md
│   │   ├── FileReader.md
│   │   ├── FileWriter.md
│   │   ├── InputStream.md
│   │   ├── OutputStream.md
│   │   ├── PrintWriter.md
│   │   ├── RandomAccessFileReader.md
│   │   ├── Reader.md
│   │   ├── StringWriter.md
│   │   ├── Writer.md
│   │   ├── XMLIndentingStreamWriter.md
│   │   ├── XMLStreamConstants.md
│   │   ├── XMLStreamReader.md
│   │   └── XMLStreamWriter.md
│   ├── dw_job
│   │   ├── JobExecution.md
│   │   └── JobStepExecution.md
│   ├── dw_net
│   │   ├── FTPClient.md
│   │   ├── FTPFileInfo.md
│   │   ├── HTTPClient.md
│   │   ├── HTTPRequestPart.md
│   │   ├── Mail.md
│   │   ├── SFTPClient.md
│   │   ├── SFTPFileInfo.md
│   │   ├── WebDAVClient.md
│   │   └── WebDAVFileInfo.md
│   ├── dw_object
│   │   ├── ActiveData.md
│   │   ├── CustomAttributes.md
│   │   ├── CustomObject.md
│   │   ├── CustomObjectMgr.md
│   │   ├── Extensible.md
│   │   ├── ExtensibleObject.md
│   │   ├── Note.md
│   │   ├── ObjectAttributeDefinition.md
│   │   ├── ObjectAttributeGroup.md
│   │   ├── ObjectAttributeValueDefinition.md
│   │   ├── ObjectTypeDefinition.md
│   │   ├── PersistentObject.md
│   │   ├── SimpleExtensible.md
│   │   └── SystemObjectMgr.md
│   ├── dw_order
│   │   ├── AbstractItem.md
│   │   ├── AbstractItemCtnr.md
│   │   ├── Appeasement.md
│   │   ├── AppeasementItem.md
│   │   ├── Basket.md
│   │   ├── BasketMgr.md
│   │   ├── BonusDiscountLineItem.md
│   │   ├── CouponLineItem.md
│   │   ├── CreateAgentBasketLimitExceededException.md
│   │   ├── CreateBasketFromOrderException.md
│   │   ├── CreateCouponLineItemException.md
│   │   ├── CreateOrderException.md
│   │   ├── CreateTemporaryBasketLimitExceededException.md
│   │   ├── GiftCertificate.md
│   │   ├── GiftCertificateLineItem.md
│   │   ├── GiftCertificateMgr.md
│   │   ├── GiftCertificateStatusCodes.md
│   │   ├── Invoice.md
│   │   ├── InvoiceItem.md
│   │   ├── LineItem.md
│   │   ├── LineItemCtnr.md
│   │   ├── Order.md
│   │   ├── OrderAddress.md
│   │   ├── OrderItem.md
│   │   ├── OrderMgr.md
│   │   ├── OrderPaymentInstrument.md
│   │   ├── OrderProcessStatusCodes.md
│   │   ├── PaymentCard.md
│   │   ├── PaymentInstrument.md
│   │   ├── PaymentMethod.md
│   │   ├── PaymentMgr.md
│   │   ├── PaymentProcessor.md
│   │   ├── PaymentStatusCodes.md
│   │   ├── PaymentTransaction.md
│   │   ├── PriceAdjustment.md
│   │   ├── PriceAdjustmentLimitTypes.md
│   │   ├── ProductLineItem.md
│   │   ├── ProductShippingCost.md
│   │   ├── ProductShippingLineItem.md
│   │   ├── ProductShippingModel.md
│   │   ├── Return.md
│   │   ├── ReturnCase.md
│   │   ├── ReturnCaseItem.md
│   │   ├── ReturnItem.md
│   │   ├── Shipment.md
│   │   ├── ShipmentShippingCost.md
│   │   ├── ShipmentShippingModel.md
│   │   ├── ShippingLineItem.md
│   │   ├── ShippingLocation.md
│   │   ├── ShippingMethod.md
│   │   ├── ShippingMgr.md
│   │   ├── ShippingOrder.md
│   │   ├── ShippingOrderItem.md
│   │   ├── SumItem.md
│   │   ├── TaxGroup.md
│   │   ├── TaxItem.md
│   │   ├── TaxMgr.md
│   │   ├── TrackingInfo.md
│   │   └── TrackingRef.md
│   ├── dw_order.hooks
│   │   ├── CalculateHooks.md
│   │   ├── OrderHooks.md
│   │   ├── PaymentHooks.md
│   │   ├── ReturnHooks.md
│   │   └── ShippingOrderHooks.md
│   ├── dw_rpc
│   │   ├── SOAPUtil.md
│   │   ├── Stub.md
│   │   └── WebReference.md
│   ├── dw_suggest
│   │   ├── BrandSuggestions.md
│   │   ├── CategorySuggestions.md
│   │   ├── ContentSuggestions.md
│   │   ├── CustomSuggestions.md
│   │   ├── ProductSuggestions.md
│   │   ├── SearchPhraseSuggestions.md
│   │   ├── SuggestedCategory.md
│   │   ├── SuggestedContent.md
│   │   ├── SuggestedPhrase.md
│   │   ├── SuggestedProduct.md
│   │   ├── SuggestedTerm.md
│   │   ├── SuggestedTerms.md
│   │   ├── Suggestions.md
│   │   └── SuggestModel.md
│   ├── dw_svc
│   │   ├── FTPService.md
│   │   ├── FTPServiceDefinition.md
│   │   ├── HTTPFormService.md
│   │   ├── HTTPFormServiceDefinition.md
│   │   ├── HTTPService.md
│   │   ├── HTTPServiceDefinition.md
│   │   ├── LocalServiceRegistry.md
│   │   ├── Result.md
│   │   ├── Service.md
│   │   ├── ServiceCallback.md
│   │   ├── ServiceConfig.md
│   │   ├── ServiceCredential.md
│   │   ├── ServiceDefinition.md
│   │   ├── ServiceProfile.md
│   │   ├── ServiceRegistry.md
│   │   ├── SOAPService.md
│   │   └── SOAPServiceDefinition.md
│   ├── dw_system
│   │   ├── AgentUserStatusCodes.md
│   │   ├── Cache.md
│   │   ├── CacheMgr.md
│   │   ├── HookMgr.md
│   │   ├── InternalObject.md
│   │   ├── JobProcessMonitor.md
│   │   ├── Log.md
│   │   ├── Logger.md
│   │   ├── LogNDC.md
│   │   ├── OrganizationPreferences.md
│   │   ├── Pipeline.md
│   │   ├── PipelineDictionary.md
│   │   ├── RemoteInclude.md
│   │   ├── Request.md
│   │   ├── RequestHooks.md
│   │   ├── Response.md
│   │   ├── RESTErrorResponse.md
│   │   ├── RESTResponseMgr.md
│   │   ├── RESTSuccessResponse.md
│   │   ├── SearchStatus.md
│   │   ├── Session.md
│   │   ├── Site.md
│   │   ├── SitePreferences.md
│   │   ├── Status.md
│   │   ├── StatusItem.md
│   │   ├── System.md
│   │   └── Transaction.md
│   ├── dw_util
│   │   ├── ArrayList.md
│   │   ├── Assert.md
│   │   ├── BigInteger.md
│   │   ├── Bytes.md
│   │   ├── Calendar.md
│   │   ├── Collection.md
│   │   ├── Currency.md
│   │   ├── DateUtils.md
│   │   ├── Decimal.md
│   │   ├── FilteringCollection.md
│   │   ├── Geolocation.md
│   │   ├── HashMap.md
│   │   ├── HashSet.md
│   │   ├── Iterator.md
│   │   ├── LinkedHashMap.md
│   │   ├── LinkedHashSet.md
│   │   ├── List.md
│   │   ├── Locale.md
│   │   ├── Map.md
│   │   ├── MapEntry.md
│   │   ├── MappingKey.md
│   │   ├── MappingMgr.md
│   │   ├── PropertyComparator.md
│   │   ├── SecureEncoder.md
│   │   ├── SecureFilter.md
│   │   ├── SeekableIterator.md
│   │   ├── Set.md
│   │   ├── SortedMap.md
│   │   ├── SortedSet.md
│   │   ├── StringUtils.md
│   │   ├── Template.md
│   │   └── UUIDUtils.md
│   ├── dw_value
│   │   ├── EnumValue.md
│   │   ├── MimeEncodedText.md
│   │   ├── Money.md
│   │   └── Quantity.md
│   ├── dw_web
│   │   ├── ClickStream.md
│   │   ├── ClickStreamEntry.md
│   │   ├── Cookie.md
│   │   ├── Cookies.md
│   │   ├── CSRFProtection.md
│   │   ├── Form.md
│   │   ├── FormAction.md
│   │   ├── FormElement.md
│   │   ├── FormElementValidationResult.md
│   │   ├── FormField.md
│   │   ├── FormFieldOption.md
│   │   ├── FormFieldOptions.md
│   │   ├── FormGroup.md
│   │   ├── FormList.md
│   │   ├── FormListItem.md
│   │   ├── Forms.md
│   │   ├── HttpParameter.md
│   │   ├── HttpParameterMap.md
│   │   ├── LoopIterator.md
│   │   ├── PageMetaData.md
│   │   ├── PageMetaTag.md
│   │   ├── PagingModel.md
│   │   ├── Resource.md
│   │   ├── URL.md
│   │   ├── URLAction.md
│   │   ├── URLParameter.md
│   │   ├── URLRedirect.md
│   │   ├── URLRedirectMgr.md
│   │   └── URLUtils.md
│   ├── sfra
│   │   ├── account.md
│   │   ├── address.md
│   │   ├── billing.md
│   │   ├── cart.md
│   │   ├── categories.md
│   │   ├── content.md
│   │   ├── locale.md
│   │   ├── order.md
│   │   ├── payment.md
│   │   ├── price-default.md
│   │   ├── price-range.md
│   │   ├── price-tiered.md
│   │   ├── product-bundle.md
│   │   ├── product-full.md
│   │   ├── product-line-items.md
│   │   ├── product-search.md
│   │   ├── product-tile.md
│   │   ├── querystring.md
│   │   ├── render.md
│   │   ├── request.md
│   │   ├── response.md
│   │   ├── server.md
│   │   ├── shipping.md
│   │   ├── store.md
│   │   ├── stores.md
│   │   └── totals.md
│   └── TopLevel
│       ├── APIException.md
│       ├── arguments.md
│       ├── Array.md
│       ├── ArrayBuffer.md
│       ├── BigInt.md
│       ├── Boolean.md
│       ├── ConversionError.md
│       ├── DataView.md
│       ├── Date.md
│       ├── Error.md
│       ├── ES6Iterator.md
│       ├── EvalError.md
│       ├── Fault.md
│       ├── Float32Array.md
│       ├── Float64Array.md
│       ├── Function.md
│       ├── Generator.md
│       ├── global.md
│       ├── Int16Array.md
│       ├── Int32Array.md
│       ├── Int8Array.md
│       ├── InternalError.md
│       ├── IOError.md
│       ├── Iterable.md
│       ├── Iterator.md
│       ├── JSON.md
│       ├── Map.md
│       ├── Math.md
│       ├── Module.md
│       ├── Namespace.md
│       ├── Number.md
│       ├── Object.md
│       ├── QName.md
│       ├── RangeError.md
│       ├── ReferenceError.md
│       ├── RegExp.md
│       ├── Set.md
│       ├── StopIteration.md
│       ├── String.md
│       ├── Symbol.md
│       ├── SyntaxError.md
│       ├── SystemError.md
│       ├── TypeError.md
│       ├── Uint16Array.md
│       ├── Uint32Array.md
│       ├── Uint8Array.md
│       ├── Uint8ClampedArray.md
│       ├── URIError.md
│       ├── WeakMap.md
│       ├── WeakSet.md
│       ├── XML.md
│       ├── XMLList.md
│       └── XMLStreamError.md
├── docs-site
│   ├── .gitignore
│   ├── App.tsx
│   ├── components
│   │   ├── Badge.tsx
│   │   ├── BreadcrumbSchema.tsx
│   │   ├── CodeBlock.tsx
│   │   ├── Collapsible.tsx
│   │   ├── ConfigBuilder.tsx
│   │   ├── ConfigHero.tsx
│   │   ├── ConfigModeTabs.tsx
│   │   ├── icons.tsx
│   │   ├── Layout.tsx
│   │   ├── LightCodeContainer.tsx
│   │   ├── NewcomerCTA.tsx
│   │   ├── NextStepsStrip.tsx
│   │   ├── OnThisPage.tsx
│   │   ├── Search.tsx
│   │   ├── SEO.tsx
│   │   ├── Sidebar.tsx
│   │   ├── StructuredData.tsx
│   │   ├── ToolCard.tsx
│   │   ├── ToolFilters.tsx
│   │   ├── Typography.tsx
│   │   └── VersionBadge.tsx
│   ├── constants.tsx
│   ├── index.html
│   ├── main.tsx
│   ├── metadata.json
│   ├── package-lock.json
│   ├── package.json
│   ├── pages
│   │   ├── AIInterfacesPage.tsx
│   │   ├── ConfigurationPage.tsx
│   │   ├── DevelopmentPage.tsx
│   │   ├── ExamplesPage.tsx
│   │   ├── FeaturesPage.tsx
│   │   ├── HomePage.tsx
│   │   ├── SecurityPage.tsx
│   │   ├── ToolsPage.tsx
│   │   └── TroubleshootingPage.tsx
│   ├── postcss.config.js
│   ├── public
│   │   ├── .well-known
│   │   │   └── security.txt
│   │   ├── 404.html
│   │   ├── android-chrome-192x192.png
│   │   ├── android-chrome-512x512.png
│   │   ├── apple-touch-icon.png
│   │   ├── explain-product-pricing-methods-no-mcp.png
│   │   ├── explain-product-pricing-methods.png
│   │   ├── favicon-16x16.png
│   │   ├── favicon-32x32.png
│   │   ├── favicon.ico
│   │   ├── llms.txt
│   │   ├── robots.txt
│   │   ├── site.webmanifest
│   │   └── sitemap.xml
│   ├── README.md
│   ├── scripts
│   │   ├── generate-search-index.js
│   │   ├── generate-sitemap.js
│   │   └── search-dev.js
│   ├── src
│   │   └── styles
│   │       ├── input.css
│   │       └── prism-theme.css
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   ├── types.ts
│   ├── utils
│   │   ├── search.ts
│   │   └── toolsData.ts
│   └── vite.config.ts
├── eslint.config.js
├── jest.config.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   └── convert-docs.js
├── SECURITY.md
├── server.json
├── src
│   ├── clients
│   │   ├── base
│   │   │   ├── http-client.ts
│   │   │   ├── oauth-token.ts
│   │   │   └── ocapi-auth-client.ts
│   │   ├── best-practices-client.ts
│   │   ├── cartridge-generation-client.ts
│   │   ├── docs
│   │   │   ├── class-content-parser.ts
│   │   │   ├── class-name-resolver.ts
│   │   │   ├── documentation-scanner.ts
│   │   │   ├── index.ts
│   │   │   └── referenced-types-extractor.ts
│   │   ├── docs-client.ts
│   │   ├── log-client.ts
│   │   ├── logs
│   │   │   ├── index.ts
│   │   │   ├── log-analyzer.ts
│   │   │   ├── log-client.ts
│   │   │   ├── log-constants.ts
│   │   │   ├── log-file-discovery.ts
│   │   │   ├── log-file-reader.ts
│   │   │   ├── log-formatter.ts
│   │   │   ├── log-processor.ts
│   │   │   ├── log-types.ts
│   │   │   └── webdav-client-manager.ts
│   │   ├── ocapi
│   │   │   ├── code-versions-client.ts
│   │   │   ├── site-preferences-client.ts
│   │   │   └── system-objects-client.ts
│   │   ├── ocapi-client.ts
│   │   └── sfra-client.ts
│   ├── config
│   │   ├── configuration-factory.ts
│   │   └── dw-json-loader.ts
│   ├── core
│   │   ├── handlers
│   │   │   ├── abstract-log-tool-handler.ts
│   │   │   ├── base-handler.ts
│   │   │   ├── best-practices-handler.ts
│   │   │   ├── cartridge-handler.ts
│   │   │   ├── client-factory.ts
│   │   │   ├── code-version-handler.ts
│   │   │   ├── docs-handler.ts
│   │   │   ├── job-log-handler.ts
│   │   │   ├── job-log-tool-config.ts
│   │   │   ├── log-handler.ts
│   │   │   ├── log-tool-config.ts
│   │   │   ├── sfra-handler.ts
│   │   │   ├── system-object-handler.ts
│   │   │   └── validation-helpers.ts
│   │   ├── server.ts
│   │   └── tool-definitions.ts
│   ├── index.ts
│   ├── main.ts
│   ├── services
│   │   ├── file-system-service.ts
│   │   ├── index.ts
│   │   └── path-service.ts
│   ├── tool-configs
│   │   ├── best-practices-tool-config.ts
│   │   ├── cartridge-tool-config.ts
│   │   ├── code-version-tool-config.ts
│   │   ├── docs-tool-config.ts
│   │   ├── job-log-tool-config.ts
│   │   ├── log-tool-config.ts
│   │   ├── sfra-tool-config.ts
│   │   └── system-object-tool-config.ts
│   ├── types
│   │   └── types.ts
│   └── utils
│       ├── cache.ts
│       ├── job-log-tool-config.ts
│       ├── job-log-utils.ts
│       ├── log-cache.ts
│       ├── log-tool-config.ts
│       ├── log-tool-constants.ts
│       ├── log-tool-utils.ts
│       ├── logger.ts
│       ├── ocapi-url-builder.ts
│       ├── path-resolver.ts
│       ├── query-builder.ts
│       ├── utils.ts
│       └── validator.ts
├── tests
│   ├── __mocks__
│   │   ├── docs-client.ts
│   │   ├── src
│   │   │   └── clients
│   │   │       └── base
│   │   │           └── http-client.js
│   │   └── webdav.js
│   ├── base-handler.test.ts
│   ├── base-http-client.test.ts
│   ├── best-practices-handler.test.ts
│   ├── cache.test.ts
│   ├── cartridge-handler.test.ts
│   ├── class-content-parser.test.ts
│   ├── class-name-resolver.test.ts
│   ├── client-factory.test.ts
│   ├── code-version-handler.test.ts
│   ├── code-versions-client.test.ts
│   ├── config.test.ts
│   ├── configuration-factory.test.ts
│   ├── docs-handler.test.ts
│   ├── documentation-scanner.test.ts
│   ├── file-system-service.test.ts
│   ├── job-log-handler.test.ts
│   ├── job-log-utils.test.ts
│   ├── log-client.test.ts
│   ├── log-handler.test.ts
│   ├── log-processor.test.ts
│   ├── logger.test.ts
│   ├── mcp
│   │   ├── AGENTS.md
│   │   ├── node
│   │   │   ├── activate-code-version-advanced.full-mode.programmatic.test.js
│   │   │   ├── code-versions.full-mode.programmatic.test.js
│   │   │   ├── generate-cartridge-structure.docs-only.programmatic.test.js
│   │   │   ├── get-available-best-practice-guides.docs-only.programmatic.test.js
│   │   │   ├── get-available-sfra-documents.programmatic.test.js
│   │   │   ├── get-best-practice-guide.docs-only.programmatic.test.js
│   │   │   ├── get-hook-reference.docs-only.programmatic.test.js
│   │   │   ├── get-job-execution-summary.full-mode.programmatic.test.js
│   │   │   ├── get-job-log-entries.full-mode.programmatic.test.js
│   │   │   ├── get-latest-debug.full-mode.programmatic.test.js
│   │   │   ├── get-latest-error.full-mode.programmatic.test.js
│   │   │   ├── get-latest-info.full-mode.programmatic.test.js
│   │   │   ├── get-latest-job-log-files.full-mode.programmatic.test.js
│   │   │   ├── get-latest-warn.full-mode.programmatic.test.js
│   │   │   ├── get-log-file-contents.full-mode.programmatic.test.js
│   │   │   ├── get-sfcc-class-documentation.docs-only.programmatic.test.js
│   │   │   ├── get-sfcc-class-info.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-categories.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-document.programmatic.test.js
│   │   │   ├── get-sfra-documents-by-category.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definition.full-mode.programmatic.test.js
│   │   │   ├── get-system-object-definitions.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definitions.full-mode.programmatic.test.js
│   │   │   ├── list-log-files.full-mode.programmatic.test.js
│   │   │   ├── list-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-best-practices.docs-only.programmatic.test.js
│   │   │   ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs-by-name.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs.full-mode.programmatic.test.js
│   │   │   ├── search-logs.full-mode.programmatic.test.js
│   │   │   ├── search-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-sfcc-methods.docs-only.programmatic.test.js
│   │   │   ├── search-sfra-documentation.docs-only.programmatic.test.js
│   │   │   ├── search-site-preferences.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-groups.full-mode.programmatic.test.js
│   │   │   ├── summarize-logs.full-mode.programmatic.test.js
│   │   │   ├── tools.docs-only.programmatic.test.js
│   │   │   └── tools.full-mode.programmatic.test.js
│   │   ├── README.md
│   │   ├── test-fixtures
│   │   │   └── dw.json
│   │   └── yaml
│   │       ├── activate-code-version.docs-only.test.mcp.yml
│   │       ├── activate-code-version.full-mode.test.mcp.yml
│   │       ├── get_latest_error.test.mcp.yml
│   │       ├── get-available-best-practice-guides.docs-only.test.mcp.yml
│   │       ├── get-available-best-practice-guides.full-mode.test.mcp.yml
│   │       ├── get-available-sfra-documents.docs-only.test.mcp.yml
│   │       ├── get-available-sfra-documents.full-mode.test.mcp.yml
│   │       ├── get-best-practice-guide.docs-only.test.mcp.yml
│   │       ├── get-best-practice-guide.full-mode.test.mcp.yml
│   │       ├── get-code-versions.docs-only.test.mcp.yml
│   │       ├── get-code-versions.full-mode.test.mcp.yml
│   │       ├── get-hook-reference.docs-only.test.mcp.yml
│   │       ├── get-hook-reference.full-mode.test.mcp.yml
│   │       ├── get-job-execution-summary.full-mode.test.mcp.yml
│   │       ├── get-job-log-entries.full-mode.test.mcp.yml
│   │       ├── get-latest-debug.full-mode.test.mcp.yml
│   │       ├── get-latest-error.full-mode.test.mcp.yml
│   │       ├── get-latest-info.full-mode.test.mcp.yml
│   │       ├── get-latest-job-log-files.full-mode.test.mcp.yml
│   │       ├── get-latest-warn.full-mode.test.mcp.yml
│   │       ├── get-log-file-contents.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-info.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-info.full-mode.test.mcp.yml
│   │       ├── get-sfra-categories.docs-only.test.mcp.yml
│   │       ├── get-sfra-categories.full-mode.test.mcp.yml
│   │       ├── get-sfra-document.docs-only.test.mcp.yml
│   │       ├── get-sfra-document.full-mode.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.docs-only.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.full-mode.test.mcp.yml
│   │       ├── get-system-object-definition.docs-only.test.mcp.yml
│   │       ├── get-system-object-definition.full-mode.test.mcp.yml
│   │       ├── get-system-object-definitions.docs-only.test.mcp.yml
│   │       ├── get-system-object-definitions.full-mode.test.mcp.yml
│   │       ├── list-log-files.full-mode.test.mcp.yml
│   │       ├── list-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── list-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-best-practices.docs-only.test.mcp.yml
│   │       ├── search-best-practices.full-mode.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.test.mcp.yml
│   │       ├── search-job-logs-by-name.full-mode.test.mcp.yml
│   │       ├── search-job-logs.full-mode.test.mcp.yml
│   │       ├── search-logs.full-mode.test.mcp.yml
│   │       ├── search-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── search-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-sfcc-methods.docs-only.test.mcp.yml
│   │       ├── search-sfcc-methods.full-mode.test.mcp.yml
│   │       ├── search-sfra-documentation.docs-only.test.mcp.yml
│   │       ├── search-sfra-documentation.full-mode.test.mcp.yml
│   │       ├── search-site-preferences.docs-only.test.mcp.yml
│   │       ├── search-site-preferences.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.full-mode.test.mcp.yml
│   │       ├── summarize-logs.full-mode.test.mcp.yml
│   │       ├── tools.docs-only.test.mcp.yml
│   │       └── tools.full-mode.test.mcp.yml
│   ├── oauth-token.test.ts
│   ├── ocapi-auth-client.test.ts
│   ├── ocapi-client.test.ts
│   ├── path-service.test.ts
│   ├── query-builder.test.ts
│   ├── referenced-types-extractor.test.ts
│   ├── servers
│   │   ├── sfcc-mock-server
│   │   │   ├── mock-data
│   │   │   │   └── ocapi
│   │   │   │       ├── code-versions.json
│   │   │   │       ├── custom-object-attributes-customapi.json
│   │   │   │       ├── custom-object-attributes-globalsettings.json
│   │   │   │       ├── custom-object-attributes-versionhistory.json
│   │   │   │       ├── site-preferences-ccv.json
│   │   │   │       ├── site-preferences-fastforward.json
│   │   │   │       ├── site-preferences-sfra.json
│   │   │   │       ├── site-preferences-storefront.json
│   │   │   │       ├── site-preferences-system.json
│   │   │   │       ├── system-object-attribute-groups-campaign.json
│   │   │   │       ├── system-object-attribute-groups-category.json
│   │   │   │       ├── system-object-attribute-groups-order.json
│   │   │   │       ├── system-object-attribute-groups-product.json
│   │   │   │       ├── system-object-attribute-groups-sitepreferences.json
│   │   │   │       ├── system-object-attributes-customeraddress.json
│   │   │   │       ├── system-object-attributes-product-expanded.json
│   │   │   │       ├── system-object-attributes-product.json
│   │   │   │       ├── system-object-definition-category.json
│   │   │   │       ├── system-object-definition-customer.json
│   │   │   │       ├── system-object-definition-customeraddress.json
│   │   │   │       ├── system-object-definition-order.json
│   │   │   │       ├── system-object-definition-product.json
│   │   │   │       ├── system-object-definitions-old.json
│   │   │   │       └── system-object-definitions.json
│   │   │   ├── package-lock.json
│   │   │   ├── package.json
│   │   │   ├── README.md
│   │   │   ├── scripts
│   │   │   │   └── setup-logs.js
│   │   │   ├── server.js
│   │   │   └── src
│   │   │       ├── app.js
│   │   │       ├── config
│   │   │       │   └── server-config.js
│   │   │       ├── middleware
│   │   │       │   ├── auth.js
│   │   │       │   ├── cors.js
│   │   │       │   └── logging.js
│   │   │       ├── routes
│   │   │       │   ├── ocapi
│   │   │       │   │   ├── code-versions-handler.js
│   │   │       │   │   ├── oauth-handler.js
│   │   │       │   │   ├── ocapi-error-utils.js
│   │   │       │   │   ├── ocapi-utils.js
│   │   │       │   │   ├── site-preferences-handler.js
│   │   │       │   │   └── system-objects-handler.js
│   │   │       │   ├── ocapi.js
│   │   │       │   └── webdav.js
│   │   │       └── utils
│   │   │           ├── mock-data-loader.js
│   │   │           └── webdav-xml.js
│   │   └── sfcc-mock-server-manager.ts
│   ├── sfcc-mock-server.test.ts
│   ├── site-preferences-client.test.ts
│   ├── system-objects-client.test.ts
│   ├── utils.test.ts
│   ├── validation-helpers.test.ts
│   └── validator.test.ts
├── tsconfig.json
└── tsconfig.test.json
```

# Files

--------------------------------------------------------------------------------
/docs/dw_catalog/Category.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.catalog

# Class Category

## Inheritance Hierarchy

- Object
  - dw.object.PersistentObject
  - dw.object.ExtensibleObject
    - dw.catalog.Category

## Description

Represents a category in a product catalog.

## Constants

### DISPLAY_MODE_INDIVIDUAL

**Type:** Number = 0

Constant representing the Variation Group Display Mode individual setting.

### DISPLAY_MODE_MERGED

**Type:** Number = 1

Constant representing the Variation Group Display Mode merged setting.

## Properties

### allRecommendations

**Type:** Collection (Read Only)

All outgoing recommendations for this category.  The
 recommendations are sorted by their explicitly set order.

### categoryAssignments

**Type:** Collection (Read Only)

A collection of category assignments of the category.

### defaultSortingRule

**Type:** SortingRule (Read Only)

The default sorting rule configured for this category,
 or null if there is no default rule to be applied for it.

 This method returns the default rule for the parent category if this
 category inherits one.  The parent category may inherit its default
 rule from its parent, and so on, up to the root category.

 This method returns null if no ancestor category for this
 category has a default rule.

### description

**Type:** String (Read Only)

The description of the catalog category for the current locale.

### displayMode

**Type:** Number

The Variation Groups Display Mode of the category or null if no display mode is defined.

### displayName

**Type:** String (Read Only)

The display name of the of the catalog category for the current locale.

 This value is intended to be used as the
 external visible name of the catalog category.

### ID

**Type:** String (Read Only)

The id of the category.

### image

**Type:** MediaFile (Read Only)

The image reference of this catalog category.

### incomingCategoryLinks

**Type:** Collection (Read Only)

The collection of CategoryLink objects for which this category
 is the target.  If the source category of a link belongs to a different
 catalog than the catalog owning this category, it is not returned.

### online

**Type:** boolean (Read Only)

The value indicating whether the catalog category is "currently
 online".  A category is currently online if its online flag equals true
 and the current site date is within the date range defined by the
 onlineFrom and onlineTo attributes.

### onlineCategoryAssignments

**Type:** Collection (Read Only)

A collection of category assignments of the category where the
 referenced product is currently online. When checking the online status
 of the product, the online flag and the online from & to dates are taken
 into account. Online flag, online from & to dates set for the current site
 takes precedence over the default values.

### onlineFlag

**Type:** boolean (Read Only)

The online status flag of the category.

### onlineFrom

**Type:** Date (Read Only)

The date from which the category is online or valid.

### onlineIncomingCategoryLinks

**Type:** Collection (Read Only)

The collection of CategoryLink objects for
 which this category is the target. If the source category of a link
 belongs to a different catalog than the catalog owning this category, it
 is not returned. Additionally, this method will only return a link if the
 source category is currently online. A category is currently online if
 its online flag equals true and the current site date is within the date
 range defined by the onlineFrom and onlineTo attributes.

### onlineOutgoingCategoryLinks

**Type:** Collection (Read Only)

The collection of CategoryLink objects for
 which this category is the source. If the target category of a link
 belongs to a different catalog than the catalog owning this category, it
 is not returned. Additionally, this method will only return a link if the
 target category is currently online. A category is currently online if
 its online flag equals true and the current site date is within the date
 range defined by the onlineFrom and onlineTo attributes.

### onlineProducts

**Type:** Collection (Read Only)

Returns online products assigned to this category.
 Offline products are not included in the returned collection.
 When checking the online status of the product,
 the online flag and the online from & to dates are taken into account.
 Online flag, online from & to dates set for the current site takes precedence
 over the default values. 

 The order of products in the returned collection corresponds to the
 defined explicit sorting of products in this category.

### onlineSubCategories

**Type:** Collection (Read Only)

A sorted collection of currently online subcategories of this
 catalog category.
 
  
   A category is currently online if its online flag
   equals true and the current site date is within the date range defined by
   the onlineFrom and onlineTo attributes.
  
  
   The returned collection is sorted by position. Subcategories marked as
   "unsorted" always appear after those marked as "sorted" but are otherwise
   not in any guaranteed order.
  
  
    The returned collection contains direct subcategories only.

### onlineTo

**Type:** Date (Read Only)

The date until which the category is online or valid.

### orderableRecommendations

**Type:** Collection (Read Only)

A list of outgoing recommendations for this category. This method
 behaves similarly to getRecommendations() but additionally filters out
 recommendations for which the target product is unorderable according to
 its product availability model.

### outgoingCategoryLinks

**Type:** Collection (Read Only)

The collection of CategoryLink objects for which this category
 is the source.  If the target category of a link belongs to a different
 catalog than the catalog owning this category, it is not returned.
 The collection of links is sorted by the explicitly defined order
 for this category with unsorted links appearing at the end.

### pageDescription

**Type:** String (Read Only)

The page description of this category for the default locale or null if not defined.

### pageKeywords

**Type:** String (Read Only)

The page keywords of this category for the default locale or null if not defined.

### pageTitle

**Type:** String (Read Only)

The page title of this category for the default locale or null if not defined.

### pageURL

**Type:** String (Read Only)

The page URL property of this category or null if not defined.

### parent

**Type:** Category (Read Only)

The parent of this category.

### productAttributeModel

**Type:** ProductAttributeModel (Read Only)

Returns this category's ProductAttributeModel, which makes access to the
 category's attribute information convenient. The model is calculated
 based on the attribute definitions assigned to this category and the
 global attribute definitions for the object type 'Product'.

### products

**Type:** Collection (Read Only)

All products assigned to this category.
 The order of products in the returned collection corresponds to the
 defined explicit sorting of products in this category.

### recommendations

**Type:** Collection (Read Only)

The outgoing recommendations for this category.  If this category
 is not in the site catalog, or there is no site catalog, an empty
 collection is returned.  Only recommendations for which the target
 product exists and is assigned to the site catalog are returned.  The
 recommendations are sorted by their explicitly set order.

### root

**Type:** boolean (Read Only)

Identifies if the category is the root category of its catalog.

### searchPlacement

**Type:** Number

The search placement of the category or null of no search placement is defined.

### searchRank

**Type:** Number

The search rank of the category or null of no search rank is defined.

### siteMapChangeFrequency

**Type:** String (Read Only)

The category's sitemap change frequency.

### siteMapIncluded

**Type:** Number (Read Only)

The category's sitemap inclusion.

### siteMapPriority

**Type:** Number (Read Only)

The category's sitemap priority.

### subCategories

**Type:** Collection (Read Only)

A sorted collection of the subcategories of this catalog category,
 including both online and offline subcategories.
 
  
   The returned collection is sorted by position. Subcategories marked as
   "unsorted" always appear after those marked as "sorted" but are otherwise
   not in any guaranteed order.
  
  
   The returned collection contains direct subcategories only.

### template

**Type:** String (Read Only)

The template property value , which is the file name of the template
 used to display the catalog category.

### thumbnail

**Type:** MediaFile (Read Only)

The thumbnail image reference of this catalog category.

### topLevel

**Type:** boolean (Read Only)

Returns true if the category is a top level category, but not the root
 category.

## Constructor Summary

## Method Summary

### getAllRecommendations

**Signature:** `getAllRecommendations() : Collection`

Returns all outgoing recommendations for this category.

### getAllRecommendations

**Signature:** `getAllRecommendations(type : Number) : Collection`

Returns all outgoing recommendations for this category which are of the specified type.

### getCategoryAssignments

**Signature:** `getCategoryAssignments() : Collection`

Returns a collection of category assignments of the category.

### getDefaultSortingRule

**Signature:** `getDefaultSortingRule() : SortingRule`

Returns the default sorting rule configured for this category, or null if there is no default rule to be applied for it.

### getDescription

**Signature:** `getDescription() : String`

Returns the description of the catalog category for the current locale.

### getDisplayMode

**Signature:** `getDisplayMode() : Number`

Returns the Variation Groups Display Mode of the category or null if no display mode is defined.

### getDisplayName

**Signature:** `getDisplayName() : String`

Returns the display name of the of the catalog category for the current locale.

### getID

**Signature:** `getID() : String`

Returns the id of the category.

### getImage

**Signature:** `getImage() : MediaFile`

Returns the image reference of this catalog category.

### getIncomingCategoryLinks

**Signature:** `getIncomingCategoryLinks() : Collection`

Returns the collection of CategoryLink objects for which this category is the target.

### getIncomingCategoryLinks

**Signature:** `getIncomingCategoryLinks(type : Number) : Collection`

Returns the collection of CategoryLink objects for which this category is the target and which are of the specified type.

### getOnlineCategoryAssignments

**Signature:** `getOnlineCategoryAssignments() : Collection`

Returns a collection of category assignments of the category where the referenced product is currently online.

### getOnlineFlag

**Signature:** `getOnlineFlag() : boolean`

Returns the online status flag of the category.

### getOnlineFrom

**Signature:** `getOnlineFrom() : Date`

Returns the date from which the category is online or valid.

### getOnlineIncomingCategoryLinks

**Signature:** `getOnlineIncomingCategoryLinks() : Collection`

Returns the collection of CategoryLink objects for which this category is the target.

### getOnlineOutgoingCategoryLinks

**Signature:** `getOnlineOutgoingCategoryLinks() : Collection`

Returns the collection of CategoryLink objects for which this category is the source.

### getOnlineProducts

**Signature:** `getOnlineProducts() : Collection`

Returns online products assigned to this category.

### getOnlineSubCategories

**Signature:** `getOnlineSubCategories() : Collection`

Returns a sorted collection of currently online subcategories of this catalog category.

### getOnlineTo

**Signature:** `getOnlineTo() : Date`

Returns the date until which the category is online or valid.

### getOrderableRecommendations

**Signature:** `getOrderableRecommendations() : Collection`

Returns a list of outgoing recommendations for this category.

### getOrderableRecommendations

**Signature:** `getOrderableRecommendations(type : Number) : Collection`

Returns a list of outgoing recommendations for this category.

### getOutgoingCategoryLinks

**Signature:** `getOutgoingCategoryLinks() : Collection`

Returns the collection of CategoryLink objects for which this category is the source.

### getOutgoingCategoryLinks

**Signature:** `getOutgoingCategoryLinks(type : Number) : Collection`

Returns the collection of CategoryLink objects for which this category is the source and which are of the specified type.

### getPageDescription

**Signature:** `getPageDescription() : String`

Returns the page description of this category for the default locale or null if not defined.

### getPageKeywords

**Signature:** `getPageKeywords() : String`

Returns the page keywords of this category for the default locale or null if not defined.

### getPageTitle

**Signature:** `getPageTitle() : String`

Returns the page title of this category for the default locale or null if not defined.

### getPageURL

**Signature:** `getPageURL() : String`

Returns the page URL property of this category or null if not defined.

### getParent

**Signature:** `getParent() : Category`

Returns the parent of this category.

### getProductAttributeModel

**Signature:** `getProductAttributeModel() : ProductAttributeModel`

Returns this category's ProductAttributeModel, which makes access to the category's attribute information convenient.

### getProducts

**Signature:** `getProducts() : Collection`

Returns all products assigned to this category.

### getRecommendations

**Signature:** `getRecommendations() : Collection`

Returns the outgoing recommendations for this category.

### getRecommendations

**Signature:** `getRecommendations(type : Number) : Collection`

Returns the outgoing recommendations for this category which are of the specified type.

### getSearchPlacement

**Signature:** `getSearchPlacement() : Number`

Returns the search placement of the category or null of no search placement is defined.

### getSearchRank

**Signature:** `getSearchRank() : Number`

Returns the search rank of the category or null of no search rank is defined.

### getSiteMapChangeFrequency

**Signature:** `getSiteMapChangeFrequency() : String`

Returns the category's sitemap change frequency.

### getSiteMapIncluded

**Signature:** `getSiteMapIncluded() : Number`

Returns the category's sitemap inclusion.

### getSiteMapPriority

**Signature:** `getSiteMapPriority() : Number`

Returns the category's sitemap priority.

### getSubCategories

**Signature:** `getSubCategories() : Collection`

Returns a sorted collection of the subcategories of this catalog category, including both online and offline subcategories.

### getTemplate

**Signature:** `getTemplate() : String`

Returns the template property value , which is the file name of the template used to display the catalog category.

### getThumbnail

**Signature:** `getThumbnail() : MediaFile`

Returns the thumbnail image reference of this catalog category.

### hasOnlineProducts

**Signature:** `hasOnlineProducts() : boolean`

Returns true if this catalog category has any online products assigned.

### hasOnlineSubCategories

**Signature:** `hasOnlineSubCategories() : boolean`

Returns true if this catalog category has any online subcategories.

### isDirectSubCategoryOf

**Signature:** `isDirectSubCategoryOf(parent : Category) : boolean`

Returns true if this category is a direct sub-category of the provided category.

### isOnline

**Signature:** `isOnline() : boolean`

Returns the value indicating whether the catalog category is "currently online".

### isRoot

**Signature:** `isRoot() : boolean`

Identifies if the category is the root category of its catalog.

### isSubCategoryOf

**Signature:** `isSubCategoryOf(ancestor : Category) : boolean`

Returns true if this category is a sub-category of the provided category.

### isTopLevel

**Signature:** `isTopLevel() : boolean`

Returns true if the category is a top level category, but not the root category.

### setDisplayMode

**Signature:** `setDisplayMode(displayMode : Number) : void`

Set the category's Variation Groups Display Mode.

### setSearchPlacement

**Signature:** `setSearchPlacement(placement : Number) : void`

Set the category's search placement.

### setSearchRank

**Signature:** `setSearchRank(rank : Number) : void`

Set the category's search rank.

## Method Detail

## Method Details

### getAllRecommendations

**Signature:** `getAllRecommendations() : Collection`

**Description:** Returns all outgoing recommendations for this category. The recommendations are sorted by their explicitly set order.

**Returns:**

the sorted collection of recommendations, never null but possibly empty.

---

### getAllRecommendations

**Signature:** `getAllRecommendations(type : Number) : Collection`

**Description:** Returns all outgoing recommendations for this category which are of the specified type. The recommendations are sorted by their explicitly set order.

**Parameters:**

- `type`: the recommendation type.

**Returns:**

the sorted collection of recommendations, never null but possibly empty.

---

### getCategoryAssignments

**Signature:** `getCategoryAssignments() : Collection`

**Description:** Returns a collection of category assignments of the category.

**Returns:**

Collection of category assignments of the category.

---

### getDefaultSortingRule

**Signature:** `getDefaultSortingRule() : SortingRule`

**Description:** Returns the default sorting rule configured for this category, or null if there is no default rule to be applied for it. This method returns the default rule for the parent category if this category inherits one. The parent category may inherit its default rule from its parent, and so on, up to the root category. This method returns null if no ancestor category for this category has a default rule.

**Returns:**

the default SortingRule or null.

---

### getDescription

**Signature:** `getDescription() : String`

**Description:** Returns the description of the catalog category for the current locale.

**Returns:**

The value of the property for the current locale, or null if it wasn't found.

---

### getDisplayMode

**Signature:** `getDisplayMode() : Number`

**Description:** Returns the Variation Groups Display Mode of the category or null if no display mode is defined.

**Returns:**

the value of the attribute 'displayMode' which is either DISPLAY_MODE_MERGED or DISPLAY_MODE_INDIVIDUAL or null if category is set to inherit the display mode.

---

### getDisplayName

**Signature:** `getDisplayName() : String`

**Description:** Returns the display name of the of the catalog category for the current locale. This value is intended to be used as the external visible name of the catalog category.

**Returns:**

The value of the property for the current locale, or null if it wasn't found.

---

### getID

**Signature:** `getID() : String`

**Description:** Returns the id of the category.

**Returns:**

the id of the category.

---

### getImage

**Signature:** `getImage() : MediaFile`

**Description:** Returns the image reference of this catalog category.

**Returns:**

the image reference for this category.

---

### getIncomingCategoryLinks

**Signature:** `getIncomingCategoryLinks() : Collection`

**Description:** Returns the collection of CategoryLink objects for which this category is the target. If the source category of a link belongs to a different catalog than the catalog owning this category, it is not returned.

**Returns:**

a collection of CategoryLink objects, possibly empty but not null.

---

### getIncomingCategoryLinks

**Signature:** `getIncomingCategoryLinks(type : Number) : Collection`

**Description:** Returns the collection of CategoryLink objects for which this category is the target and which are of the specified type. If the source category of a link belongs to a different catalog than the catalog owning this category, it is not returned.

**Parameters:**

- `type`: the link type type.

**Returns:**

a collection of CategoryLink objects, possibly empty but not null.

---

### getOnlineCategoryAssignments

**Signature:** `getOnlineCategoryAssignments() : Collection`

**Description:** Returns a collection of category assignments of the category where the referenced product is currently online. When checking the online status of the product, the online flag and the online from & to dates are taken into account. Online flag, online from & to dates set for the current site takes precedence over the default values.

**Returns:**

Collection of online category assignments of the category.

---

### getOnlineFlag

**Signature:** `getOnlineFlag() : boolean`

**Description:** Returns the online status flag of the category.

**Returns:**

the online status flag of the category.

---

### getOnlineFrom

**Signature:** `getOnlineFrom() : Date`

**Description:** Returns the date from which the category is online or valid.

**Returns:**

the date from which the category is online or valid.

---

### getOnlineIncomingCategoryLinks

**Signature:** `getOnlineIncomingCategoryLinks() : Collection`

**Description:** Returns the collection of CategoryLink objects for which this category is the target. If the source category of a link belongs to a different catalog than the catalog owning this category, it is not returned. Additionally, this method will only return a link if the source category is currently online. A category is currently online if its online flag equals true and the current site date is within the date range defined by the onlineFrom and onlineTo attributes.

**Returns:**

a collection of CategoryLink objects, possibly empty but not null.

---

### getOnlineOutgoingCategoryLinks

**Signature:** `getOnlineOutgoingCategoryLinks() : Collection`

**Description:** Returns the collection of CategoryLink objects for which this category is the source. If the target category of a link belongs to a different catalog than the catalog owning this category, it is not returned. Additionally, this method will only return a link if the target category is currently online. A category is currently online if its online flag equals true and the current site date is within the date range defined by the onlineFrom and onlineTo attributes.

**Returns:**

a collection of CategoryLink objects, possibly empty but not null.

---

### getOnlineProducts

**Signature:** `getOnlineProducts() : Collection`

**Description:** Returns online products assigned to this category. Offline products are not included in the returned collection. When checking the online status of the product, the online flag and the online from & to dates are taken into account. Online flag, online from & to dates set for the current site takes precedence over the default values. The order of products in the returned collection corresponds to the defined explicit sorting of products in this category.

**Returns:**

a sorted collection of online products of this category.

**See Also:**

hasOnlineProducts()

---

### getOnlineSubCategories

**Signature:** `getOnlineSubCategories() : Collection`

**Description:** Returns a sorted collection of currently online subcategories of this catalog category. A category is currently online if its online flag equals true and the current site date is within the date range defined by the onlineFrom and onlineTo attributes. The returned collection is sorted by position. Subcategories marked as "unsorted" always appear after those marked as "sorted" but are otherwise not in any guaranteed order. The returned collection contains direct subcategories only.

**Returns:**

a sorted collection of currently online subcategories.

**See Also:**

hasOnlineSubCategories()

---

### getOnlineTo

**Signature:** `getOnlineTo() : Date`

**Description:** Returns the date until which the category is online or valid.

**Returns:**

the date until which the category is online or valid.

---

### getOrderableRecommendations

**Signature:** `getOrderableRecommendations() : Collection`

**Description:** Returns a list of outgoing recommendations for this category. This method behaves similarly to getRecommendations() but additionally filters out recommendations for which the target product is unorderable according to its product availability model.

**Returns:**

the sorted collection of recommendations, never null but possibly empty.

**See Also:**

ProductAvailabilityModel.isOrderable()

---

### getOrderableRecommendations

**Signature:** `getOrderableRecommendations(type : Number) : Collection`

**Description:** Returns a list of outgoing recommendations for this category. This method behaves similarly to getRecommendations(Number) but additionally filters out recommendations for which the target product is unorderable according to its product availability model.

**Parameters:**

- `type`: the recommendation type.

**Returns:**

the sorted collection of recommendations, never null but possibly empty.

**See Also:**

ProductAvailabilityModel.isOrderable()

---

### getOutgoingCategoryLinks

**Signature:** `getOutgoingCategoryLinks() : Collection`

**Description:** Returns the collection of CategoryLink objects for which this category is the source. If the target category of a link belongs to a different catalog than the catalog owning this category, it is not returned. The collection of links is sorted by the explicitly defined order for this category with unsorted links appearing at the end.

**Returns:**

a collection of CategoryLink objects, possibly empty but not null.

---

### getOutgoingCategoryLinks

**Signature:** `getOutgoingCategoryLinks(type : Number) : Collection`

**Description:** Returns the collection of CategoryLink objects for which this category is the source and which are of the specified type. If the target category of a link belongs to a different catalog than the catalog owning this category, it is not returned. The collection of links is sorted by the explicitly defined order for this category with unsorted links appearing at the end.

**Parameters:**

- `type`: the link type type.

**Returns:**

a collection of CategoryLink objects, possibly empty but not null.

---

### getPageDescription

**Signature:** `getPageDescription() : String`

**Description:** Returns the page description of this category for the default locale or null if not defined.

**Returns:**

the value of the attribute 'pageDescription'.

---

### getPageKeywords

**Signature:** `getPageKeywords() : String`

**Description:** Returns the page keywords of this category for the default locale or null if not defined.

**Returns:**

the value of the attribute 'pageKeywords'.

---

### getPageTitle

**Signature:** `getPageTitle() : String`

**Description:** Returns the page title of this category for the default locale or null if not defined.

**Returns:**

the value of the attribute 'pageTitle'.

---

### getPageURL

**Signature:** `getPageURL() : String`

**Description:** Returns the page URL property of this category or null if not defined.

**Returns:**

the value of the attribute 'pageURL'.

---

### getParent

**Signature:** `getParent() : Category`

**Description:** Returns the parent of this category.

**Returns:**

a CatalogCategory instance representing the parent of this CatalogCategory or null.

---

### getProductAttributeModel

**Signature:** `getProductAttributeModel() : ProductAttributeModel`

**Description:** Returns this category's ProductAttributeModel, which makes access to the category's attribute information convenient. The model is calculated based on the attribute definitions assigned to this category and the global attribute definitions for the object type 'Product'.

**Returns:**

the ProductAttributeModel for this category.

---

### getProducts

**Signature:** `getProducts() : Collection`

**Description:** Returns all products assigned to this category. The order of products in the returned collection corresponds to the defined explicit sorting of products in this category.

**Returns:**

a sorted collection of all products of this category.

**See Also:**

getOnlineProducts()

---

### getRecommendations

**Signature:** `getRecommendations() : Collection`

**Description:** Returns the outgoing recommendations for this category. If this category is not in the site catalog, or there is no site catalog, an empty collection is returned. Only recommendations for which the target product exists and is assigned to the site catalog are returned. The recommendations are sorted by their explicitly set order.

**Returns:**

the sorted collection of recommendations, never null but possibly empty.

---

### getRecommendations

**Signature:** `getRecommendations(type : Number) : Collection`

**Description:** Returns the outgoing recommendations for this category which are of the specified type. Behaves the same as getRecommendations() but additionally filters by recommendation type.

**Parameters:**

- `type`: the recommendation type.

**Returns:**

the sorted collection of recommendations, never null but possibly empty.

---

### getSearchPlacement

**Signature:** `getSearchPlacement() : Number`

**Description:** Returns the search placement of the category or null of no search placement is defined.

**Returns:**

the value of the attribute 'searchPlacement'.

---

### getSearchRank

**Signature:** `getSearchRank() : Number`

**Description:** Returns the search rank of the category or null of no search rank is defined.

**Returns:**

the value of the attribute 'searchRank'.

---

### getSiteMapChangeFrequency

**Signature:** `getSiteMapChangeFrequency() : String`

**Description:** Returns the category's sitemap change frequency.

**Returns:**

the value of the attribute 'siteMapChangeFrequency'.

---

### getSiteMapIncluded

**Signature:** `getSiteMapIncluded() : Number`

**Description:** Returns the category's sitemap inclusion.

**Returns:**

the value of the attribute 'siteMapIncluded'.

---

### getSiteMapPriority

**Signature:** `getSiteMapPriority() : Number`

**Description:** Returns the category's sitemap priority.

**Returns:**

the value of the attribute 'siteMapPriority'.

---

### getSubCategories

**Signature:** `getSubCategories() : Collection`

**Description:** Returns a sorted collection of the subcategories of this catalog category, including both online and offline subcategories. The returned collection is sorted by position. Subcategories marked as "unsorted" always appear after those marked as "sorted" but are otherwise not in any guaranteed order. The returned collection contains direct subcategories only.

**Returns:**

a sorted collection of the subcategories.

**See Also:**

getOnlineSubCategories()

---

### getTemplate

**Signature:** `getTemplate() : String`

**Description:** Returns the template property value , which is the file name of the template used to display the catalog category.

**Returns:**

the value of the property 'template'.

---

### getThumbnail

**Signature:** `getThumbnail() : MediaFile`

**Description:** Returns the thumbnail image reference of this catalog category.

**Returns:**

the thumbnail image reference for this category.

---

### hasOnlineProducts

**Signature:** `hasOnlineProducts() : boolean`

**Description:** Returns true if this catalog category has any online products assigned. When checking the online status of the product, the online flag and the online from & to dates are taken into account. Online flag, online from & to dates set for the current site takes precedence over the default values.

**Returns:**

true, if this category has at least one online product assigned, false otherwise.

**See Also:**

getOnlineProducts()

---

### hasOnlineSubCategories

**Signature:** `hasOnlineSubCategories() : boolean`

**Description:** Returns true if this catalog category has any online subcategories. A category is currently online if its online flag equals true and the current site date is within the date range defined by the onlineFrom and onlineTo attributes. Only direct subcategories are considered.

**Returns:**

true, if this category has at least one online subcategory, false otherwise.

**See Also:**

getOnlineSubCategories()

---

### isDirectSubCategoryOf

**Signature:** `isDirectSubCategoryOf(parent : Category) : boolean`

**Description:** Returns true if this category is a direct sub-category of the provided category.

**Parameters:**

- `parent`: The parent category, must not be null.

**Returns:**

True if this category is a direct sub-category of parent, false otherwise.

---

### isOnline

**Signature:** `isOnline() : boolean`

**Description:** Returns the value indicating whether the catalog category is "currently online". A category is currently online if its online flag equals true and the current site date is within the date range defined by the onlineFrom and onlineTo attributes.

**Returns:**

true if the category is currently online, false otherwise.

---

### isRoot

**Signature:** `isRoot() : boolean`

**Description:** Identifies if the category is the root category of its catalog.

**Returns:**

'true' if the category is the root category of its catalog, 'false' otherwise.

---

### isSubCategoryOf

**Signature:** `isSubCategoryOf(ancestor : Category) : boolean`

**Description:** Returns true if this category is a sub-category of the provided category. This can be either a direct or an indirect sub-category.

**Parameters:**

- `ancestor`: The ancestor category, must not be null.

**Returns:**

true if this category is a sub-category of ancestor, false otherwise.

---

### isTopLevel

**Signature:** `isTopLevel() : boolean`

**Description:** Returns true if the category is a top level category, but not the root category.

**Returns:**

True if the category is a direct sub-category of the root category, false otherwise.

---

### setDisplayMode

**Signature:** `setDisplayMode(displayMode : Number) : void`

**Description:** Set the category's Variation Groups Display Mode.

**Parameters:**

- `displayMode`: The category's variation groups display mode which is either DISPLAY_MODE_MERGED or DISPLAY_MODE_INDIVIDUAL or null if category is set to inherit the display mode.

---

### setSearchPlacement

**Signature:** `setSearchPlacement(placement : Number) : void`

**Description:** Set the category's search placement.

**Parameters:**

- `placement`: The category's search placement.

---

### setSearchRank

**Signature:** `setSearchRank(rank : Number) : void`

**Description:** Set the category's search rank.

**Parameters:**

- `rank`: The category's search rank.

---
```

--------------------------------------------------------------------------------
/docs-site/pages/AIInterfacesPage.tsx:
--------------------------------------------------------------------------------

```typescript
import React from 'react';
import { NavLink } from 'react-router-dom';
import SEO from '../components/SEO';
import BreadcrumbSchema from '../components/BreadcrumbSchema';
import StructuredData from '../components/StructuredData';
import { H1, H2, H3, PageSubtitle } from '../components/Typography';
import CodeBlock, { InlineCode } from '../components/CodeBlock';
import LightCodeContainer from '../components/LightCodeContainer';
import NewcomerCTA from '../components/NewcomerCTA';
import { SITE_DATES } from '../constants';

// Small reusable bullet list with icon
const Check: React.FC<{ color?: string; children: React.ReactNode }> = ({ color = 'text-green-500', children }) => (
  <li className="flex items-start gap-2 text-sm text-gray-700">
    <span className={`${color} mt-0.5`}>✅</span>
    <span>{children}</span>
  </li>
);

const SectionCard: React.FC<React.PropsWithChildren<{ className?: string }>> = ({ className = '', children }) => (
  <div className={`rounded-2xl p-6 md:p-8 md:pt-0 border shadow-sm bg-white/90 backdrop-blur-sm ${className}`}>{children}</div>
);

const AIInterfacesPage: React.FC = () => {
  const aiInterfacesStructuredData = {
    "@context": "https://schema.org",
    "@type": "TechArticle",
    "headline": "AI Interface Integration - SFCC Development MCP Server",
    "description": "Integration guide for AI assistants (Claude Desktop, GitHub Copilot, Cursor, generic MCP clients) with SFCC Development MCP Server.",
    "author": {
      "@type": "Person",
      "name": "Thomas Theunen"
    },
    "publisher": {
      "@type": "Person",
      "name": "Thomas Theunen"
    },
    "datePublished": SITE_DATES.PUBLISHED,
    "dateModified": SITE_DATES.MODIFIED,
    "url": "https://sfcc-mcp-dev.rhino-inquisitor.com/ai-interfaces/",
    "about": {
      "@type": "SoftwareApplication",
      "name": "SFCC Development MCP Server",
      "applicationCategory": "DeveloperApplication",
      "operatingSystem": "Node.js",
      "offers": {
        "@type": "Offer",
        "price": "0",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock"
      }
    }
  };

  return (
    <div className="max-w-6xl mx-auto px-6 py-8">
      <SEO 
        title="AI Interface Integration"
        description="Integration guide for AI assistants (Claude Desktop, GitHub Copilot, Cursor, generic MCP clients) with SFCC Development MCP Server."
        keywords="Claude Desktop MCP, GitHub Copilot, Cursor AI, MCP server integration, SFCC AI tools"
        canonical="/ai-interfaces/"
        ogType="article"
      />
      <BreadcrumbSchema items={[
        { name: "Home", url: "/" },
        { name: "AI Interfaces", url: "/ai-interfaces/" }
      ]} />
      <StructuredData structuredData={aiInterfacesStructuredData} />
      
      {/* Hero */}
      <div className="text-center">
        <div className="inline-flex items-center gap-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white px-4 py-2 rounded-full text-sm font-medium mb-6">
          <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clipRule="evenodd" /></svg>
          AI Interface Integration
        </div>
        <H1 id="ai-interface-setup" className="text-5xl md:text-6xl font-extrabold bg-gradient-to-r from-gray-900 via-blue-900 to-purple-900 bg-clip-text text-transparent mb-6">Connect Your AI Assistants</H1>
        <PageSubtitle className="text-xl md:text-2xl text-gray-600 max-w-4xl mx-auto leading-relaxed">
          Unified setup guide for Claude Desktop, GitHub Copilot, Cursor and any MCP-compatible client. Start in
          documentation-only mode then unlock real-time log & job log analysis, system & custom object exploration,
          and code version management with credentials.
        </PageSubtitle>
      </div>

      {/* Newcomer CTA */}
      <NewcomerCTA className="mb-20" />

      {/* Quick Start (Tabbed) */}
      <div id="quick-start" className="mb-20 bg-gradient-to-r from-blue-50 via-indigo-50 to-purple-50 rounded-2xl shadow-xl border border-blue-100 p-8">
          <div className="text-center mb-10">
            <H2 id="environment-modes" className="text-3xl font-bold mb-4">🚀 Environment Modes</H2>
            <p className="text-lg text-gray-700 max-w-3xl mx-auto">Switch between zero-config documentation mode and full analytics mode with a single flag.</p>
          </div>
          {/* Tabs */}
          <ModeTabs />
      </div>

 

      {/* Configuration Options */}
      <div id="configuration" className="mb-20 bg-gradient-to-r from-gray-50 to-blue-50 rounded-2xl border border-gray-200 p-8">
          <div className="text-center mb-10">
            <H2 id="configuration-flags" className="text-3xl font-bold mb-4">⚙️ Configuration Flags</H2>
            <p className="text-lg text-gray-600 max-w-3xl mx-auto">Minimal surface area – just two flags control operating mode & diagnostics.</p>
          </div>
          <div className="grid md:grid-cols-2 gap-6">
            <div className="bg-white rounded-xl p-6 border border-gray-200">
              <div className="flex items-center gap-3 mb-3">
                <div className="bg-blue-100 rounded-full p-2"><svg className="w-4 h-4 text-blue-600" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" /></svg></div>
                <span className="font-mono text-sm bg-gray-100 px-2 py-1 rounded">--dw-json &lt;path&gt;</span>
              </div>
              <p className="text-gray-700 text-sm">Add credentials for real-time log & job log analysis, system & custom object exploration, site preference searches & code version management.</p>
            </div>
            <div className="bg-white rounded-xl p-6 border border-gray-200">
              <div className="flex items-center gap-3 mb-3">
                <div className="bg-green-100 rounded-full p-2"><svg className="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" /></svg></div>
                <span className="font-mono text-sm bg-gray-100 px-2 py-1 rounded">--debug &lt;true|false&gt;</span>
              </div>
              <p className="text-gray-700 text-sm">Enable verbose internal logging for troubleshooting & development.</p>
            </div>
          </div>
      </div>

      {/* Assistant Tabs Section */}
      <div id="assistants" className="mb-24">
        <div className="text-center mb-10">
          <H2 id="ai-assistant-setup" className="text-3xl font-bold mb-4">AI Assistant Setup</H2>
          <p className="text-gray-600 max-w-3xl mx-auto">Choose your interface – configuration, strengths & verification prompts in one place.</p>
        </div>
        <SectionCard className="border-blue-200">
          <AssistantTabs />
        </SectionCard>
      </div>

      {/* Anchors preserved for deep links */}
      <div id="claude" className="hidden" aria-hidden="true" />
      <div id="copilot" className="hidden" aria-hidden="true" />
      <div id="cursor" className="hidden" aria-hidden="true" />

      {/* AI Instruction Files Section */}
      <div id="instruction-files" className="mb-24">
        <div className="text-center mb-10">
          <H2 id="ai-instruction-files" className="text-3xl font-bold mb-4">📋 AI Instruction Files</H2>
          <p className="text-gray-600 max-w-3xl mx-auto">
            Enhance your AI assistant's SFCC expertise with specialized instruction files that provide context about development patterns, security practices, and MCP tool usage.
          </p>
        </div>
        <SectionCard className="border-amber-200">
          <InstructionFilesTabs />
        </SectionCard>
      </div>

      {/* Universal Config */}
      <div id="universal" className="mb-24">
        <div className="text-center mb-8">
          <H2 id="universal-mcp-configuration" className="text-3xl font-bold mb-4">Universal MCP Configuration</H2>
          <p className="text-gray-600 max-w-3xl mx-auto">Works across any MCP-compatible AI (desktop, IDE plugin, CLI agent).</p>
        </div>
        <SectionCard>
          <CodeBlock language="json" code={`{\n  "mcpServers": {\n    "sfcc-dev": {\n      "command": "npx",\n      "args": ["sfcc-dev-mcp", "--dw-json", "/Users/username/sfcc-project/dw.json", "--debug", "false"]\n    }\n  }\n}`} />
          <ul className="list-disc pl-5 text-sm text-gray-700 mt-6 space-y-1">
            <li><InlineCode>--dw-json</InlineCode> optional for docs-only mode</li>
            <li><InlineCode>--debug</InlineCode> set true when diagnosing tool behavior</li>
          </ul>
        </SectionCard>
      </div>

      {/* Next Steps - HomePage style */}
      <div id="next" className="mb-16 bg-gradient-to-r from-blue-50 via-indigo-50 to-purple-50 rounded-2xl p-10 shadow-xl border border-blue-100">
              <div className="text-center mb-8">
                <div className="inline-flex items-center gap-2 bg-blue-100 text-blue-800 px-4 py-2 rounded-full text-sm font-medium mb-4">
                  <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" /></svg>
                  You're Set Up
                </div>
                <p className="text-lg text-gray-700 max-w-3xl mx-auto">Pick your next path—refine configuration or explore full capability surface.</p>
                <div className="mt-6 flex flex-col sm:flex-row gap-4 justify-center">
                  <NavLink to="/configuration/" className="inline-flex items-center justify-center px-6 py-3 rounded-full text-sm font-semibold bg-blue-600 text-white shadow hover:bg-blue-700 transition group no-underline hover:no-underline focus:no-underline">
                    Configuration Guide
                    <span className="ml-2 transition group-hover:translate-x-0.5">→</span>
                  </NavLink>
                  <NavLink to="/features/" className="inline-flex items-center justify-center px-6 py-3 rounded-full text-sm font-semibold bg-white text-gray-800 border border-gray-300 hover:border-gray-400 hover:bg-gray-50 transition group no-underline hover:no-underline focus:no-underline">
                    Explore Features
                    <span className="ml-2 transition group-hover:translate-x-0.5">→</span>
                  </NavLink>
                </div>
              </div>
      </div>
    </div>
  );
};

export default AIInterfacesPage;

// --- Inline Tab Component (kept at bottom for clarity) ---
const ModeTabs: React.FC = () => {
  const [active, setActive] = React.useState<'docs' | 'full'>('docs');
  const tabBase = 'px-5 py-2 rounded-full text-sm font-medium transition border';
  return (
    <div>
      <div role="tablist" aria-label="Mode selection" className="flex flex-wrap gap-3 mb-8 justify-center">
        <button
          role="tab"
          aria-selected={active === 'docs'}
          className={`${tabBase} ${active === 'docs' ? 'bg-blue-600 text-white border-blue-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-blue-400 hover:text-blue-600'}`}
          onClick={() => setActive('docs')}
        >Docs Mode</button>
        <button
          role="tab"
          aria-selected={active === 'full'}
          className={`${tabBase} ${active === 'full' ? 'bg-purple-600 text-white border-purple-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-purple-400 hover:text-purple-600'}`}
          onClick={() => setActive('full')}
        >Full Mode</button>
      </div>
      {/* Panels */}
      {active === 'docs' && (
        <div role="tabpanel" aria-label="Documentation Mode" className="space-y-8 animate-fade-in">
          <div>
            <h3 className="text-lg font-semibold text-gray-900 mb-4">Configuration (Docs Mode)</h3>
            <LightCodeContainer>
              <CodeBlock language="json" code={`{\n  "mcpServers": {\n    "sfcc-dev": {\n      "command": "npx",\n      "args": ["sfcc-dev-mcp"]\n    }\n  }\n}`} />
            </LightCodeContainer>
          </div>
          <div>
            <h4 className="font-semibold text-gray-900 mb-3">Benefits</h4>
            <div className="grid md:grid-cols-3 gap-4">
              <BenefitCard title="Zero Setup" color="blue" text="Immediate access to SFCC API docs, SFRA docs & best practices." />
              <BenefitCard title="Fast Onboarding" color="green" text="Great for new devs or quick reference sessions." />
              <BenefitCard title="Safe & Local" color="purple" text="No credentials needed (no logs / objects)." />
            </div>
          </div>
          <p className="text-xs text-gray-500">Upgrade any time by adding <InlineCode>--dw-json</InlineCode>.</p>
        </div>
      )}
      {active === 'full' && (
        <div role="tabpanel" aria-label="Full Mode" className="space-y-8 animate-fade-in">
          <div>
            <h3 className="text-lg font-semibold text-gray-900 mb-4">Configuration (Full Mode)</h3>
            <LightCodeContainer className="mb-4">
              <CodeBlock language="json" code={`{\n  "mcpServers": {\n    "sfcc-dev": {\n      "command": "npx",\n      "args": [\n         "sfcc-dev-mcp", \n         "--dw-json", \n         "/Users/username/sfcc-project/dw.json", \n         "--debug", \n         "false"\n      ]\n    }\n  }\n}`} />
            </LightCodeContainer>
            <p className="text-sm text-gray-600">Set <InlineCode>--debug true</InlineCode> temporarily when diagnosing tool responses.</p>
          </div>
          <div>
            <h4 className="font-semibold text-gray-900 mb-3">Additional Benefits</h4>
            <div className="grid md:grid-cols-3 gap-4">
              <BenefitCard title="Live Logs" color="blue" text="Real-time error, warn, info & debug analysis." />
              <BenefitCard title="Job Insights" color="green" text="Job log discovery, execution summaries & step health." />
              <BenefitCard title="Data Model" color="purple" text="System & custom objects, attributes & site preferences." />
              <BenefitCard title="Code Versions" color="orange" text="List & activate versions for deployment fixes." />
              <BenefitCard title="Deeper Reasoning" color="indigo" text="More context = better AI architectural guidance." />
              <BenefitCard title="Unified Workflow" color="rose" text="Docs + analysis in one consistent interface." />
            </div>
          </div>
          <p className="text-xs text-gray-500">Credentials never leave your machine—this MCP server runs locally and only calls your sandbox APIs.</p>
        </div>
      )}
    </div>
  );
};

const BenefitCard: React.FC<{ title: string; text: string; color: string }> = ({ title, text, color }) => {
  const colorMap: Record<string, string> = {
    blue: 'bg-blue-50 text-blue-700 border-blue-200',
    green: 'bg-green-50 text-green-700 border-green-200',
    purple: 'bg-purple-50 text-purple-700 border-purple-200',
    orange: 'bg-orange-50 text-orange-700 border-orange-200',
    indigo: 'bg-indigo-50 text-indigo-700 border-indigo-200',
    rose: 'bg-rose-50 text-rose-700 border-rose-200'
  };
  return (
    <div className={`rounded-xl border p-4 ${colorMap[color] || 'bg-gray-50 text-gray-700 border-gray-200'}`}>
      <h5 className="font-semibold mb-1 text-sm">{title}</h5>
      <p className="text-xs leading-relaxed">{text}</p>
    </div>
  );
};

// AssistantTabs component for Claude / Copilot / Cursor (refined layout)
const AssistantTabs: React.FC = () => {
  type Assistant = 'claude' | 'copilot' | 'cursor';
  const [active, setActive] = React.useState<Assistant>('copilot');
  const tabBase = 'px-5 py-2 rounded-full text-sm font-medium transition border';
  // Unified configuration: keep identical parameters for all assistants for clarity
  const unifiedSnippet = `{"mcpServers":{"sfcc-dev":{"command":"npx","args":["sfcc-dev-mcp","--dw-json","/Users/username/sfcc-project/dw.json","--debug","false"]}}}`;
  const configSnippets: Record<Assistant, string> = {
    claude: unifiedSnippet,
    copilot: unifiedSnippet,
    cursor: unifiedSnippet
  };
  const benefits: Record<Assistant, Array<{ title: string; text: string; color: string }>> = {
    claude: [
      { title: 'Deep Reasoning', text: 'Great for architecture & multi-step planning.', color: 'blue' },
      { title: 'Debug Sessions', text: 'Explain logs & identify root causes.', color: 'green' },
      { title: 'Exploration', text: 'Modeling & refactor strategy guidance.', color: 'purple' }
    ],
    copilot: [
      { title: 'Inline Speed', text: 'Rapid completions & edits in VS Code.', color: 'green' },
      { title: 'Scaffolding', text: 'Generate controllers, models & tests.', color: 'blue' },
      { title: 'Everyday Flow', text: 'Low-friction iteration for daily work.', color: 'purple' }
    ],
    cursor: [
      { title: 'Rule Packs', text: 'Context-aware security & performance rules.', color: 'purple' },
      { title: 'Large Changes', text: 'Safely coordinate multi-file refactors.', color: 'blue' },
      { title: 'Consistency', text: 'Standardize patterns across the codebase.', color: 'green' }
    ]
  };
  const prompts: Record<Assistant, string[]> = {
    claude: [
      'List available SFCC documentation tools',
      'Analyze recent error logs then summarize likely root cause',
      'Generate cartridge structure named demo_cartridge',
      'Show hook reference entries for SCAPI extension points'
    ],
    copilot: [
      'Show methods on dw.catalog.Product',
      'Create SFRA controller for Product-Show',
      'Search job logs for failed step exceptions',
      'List custom attributes on Product object'
    ],
    cursor: [
      'Suggest performance improvements for this controller',
      'Apply security patterns to this hook',
      'Search SFRA docs for middleware guidance',
      'Summarize latest job execution health'
    ]
  };

  const renderSnippet = (assistant: Assistant) => {
    const json = configSnippets[assistant];
    const pretty = JSON.stringify(JSON.parse(json), null, 2);
    return <CodeBlock language="json" code={pretty} />;
  };

  return (
    <div>
      <div role="tablist" aria-label="Assistant selection" className="flex flex-wrap gap-3 mb-10 mt-10 justify-center">
        <button
          role="tab"
          aria-selected={active === 'copilot'}
          className={`${tabBase} ${active === 'copilot' ? 'bg-green-600 text-white border-green-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-green-400 hover:text-green-600'}`}
          onClick={() => setActive('copilot')}
        >GitHub Copilot</button>
        <button
          role="tab"
          aria-selected={active === 'claude'}
          className={`${tabBase} ${active === 'claude' ? 'bg-blue-600 text-white border-blue-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-blue-400 hover:text-blue-600'}`}
          onClick={() => setActive('claude')}
        >Claude Desktop</button>
        <button
          role="tab"
          aria-selected={active === 'cursor'}
          className={`${tabBase} ${active === 'cursor' ? 'bg-purple-600 text-white border-purple-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-purple-400 hover:text-purple-600'}`}
          onClick={() => setActive('cursor')}
        >Cursor</button>
      </div>
      <div className="space-y-10 animate-fade-in" role="tabpanel">
        <div>
          <h3 className="text-lg font-semibold text-gray-900 mb-4">Configuration</h3>
          <LightCodeContainer className="mb-6">
            {renderSnippet(active)}
          </LightCodeContainer>
          
          <div className="flex justify-center mb-6">
            <div className="flex flex-wrap gap-3 justify-center">
              {active === 'copilot' && (
                <>
                  <a 
                    href="https://docs.github.com/en/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp" 
                    target="_blank" 
                    rel="noopener noreferrer"
                    className="inline-flex items-center gap-2 px-6 py-3 bg-green-100 hover:bg-green-200 text-green-700 hover:text-green-800 rounded-lg font-medium transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg no-underline hover:no-underline focus:no-underline"
                  >
                    📖 Copilot MCP Setup
                    <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                      <path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
                    </svg>
                  </a>
                  <a 
                    href="https://www.youtube.com/watch?v=ZlrQJQV14xQ&t=215s" 
                    target="_blank" 
                    rel="noopener noreferrer"
                    className="inline-flex items-center gap-2 px-6 py-3 bg-green-100 hover:bg-green-200 text-green-700 hover:text-green-800 rounded-lg font-medium transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg no-underline hover:no-underline focus:no-underline"
                  >
                    🎥 Video Guide
                    <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                      <path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
                    </svg>
                  </a>
                </>
              )}
              {active === 'claude' && (
                <a 
                  href="https://support.anthropic.com/en/articles/10949351-getting-started-with-local-mcp-servers-on-claude-desktop" 
                  target="_blank" 
                  rel="noopener noreferrer"
                  className="inline-flex items-center gap-2 px-6 py-3 bg-blue-100 hover:bg-blue-200 text-blue-700 hover:text-blue-800 rounded-lg font-medium transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg no-underline hover:no-underline focus:no-underline"
                >
                  📖 Claude MCP Setup
                  <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                    <path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
                  </svg>
                </a>
              )}
              {active === 'cursor' && (
                <a 
                  href="https://docs.cursor.com/advanced/model-context-protocol" 
                  target="_blank" 
                  rel="noopener noreferrer"
                  className="inline-flex items-center gap-2 px-6 py-3 bg-purple-100 hover:bg-purple-200 text-purple-700 hover:text-purple-800 rounded-lg font-medium transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg no-underline hover:no-underline focus:no-underline"
                >
                  📖 Cursor MCP Setup
                  <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                    <path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
                  </svg>
                </a>
              )}
            </div>
          </div>
          
          <p className="text-xs text-gray-500 text-center">Docs-only mode: omit <InlineCode>--dw-json</InlineCode>. Enable verbose logging temporarily with <InlineCode>--debug true</InlineCode>.</p>
        </div>
        <div>
          <h4 className="font-semibold text-gray-900 mb-3">Key Strengths</h4>
          <div className="grid md:grid-cols-3 gap-4">
            {benefits[active].map(b => <BenefitCard key={b.title} title={b.title} text={b.text} color={b.color} />)}
          </div>
        </div>
        <div>
          <h4 className="font-semibold text-gray-900 mb-2">Verification Prompts</h4>
          <ul className="list-disc pl-5 text-xs text-gray-700 space-y-1">
            {prompts[active].map(p => <li key={p}>{p}</li>)}
          </ul>
        </div>
      </div>
    </div>
  );
};

// InstructionFilesTabs component for AI instruction file setup
const InstructionFilesTabs: React.FC = () => {
  type InstructionTarget = 'copilot' | 'claude' | 'cursor';
  const [active, setActive] = React.useState<InstructionTarget>('copilot');
  const tabBase = 'px-5 py-2 rounded-full text-sm font-medium transition border';

  const instructionData: Record<InstructionTarget, {
    setupUrl: string;
    files: Array<{ name: string; path: string; description: string }>;
    instructions: string[];
  }> = {
    copilot: {
      setupUrl: 'https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions',
      files: [
        {
          name: 'copilot-instructions.md',
          path: '.github/copilot-instructions.md',
          description: 'Main instruction file for GitHub Copilot with SFCC expertise and MCP tool usage patterns'
        }
      ],
      instructions: [
        'Copy the instruction file from the repository to your project',
        'Place it in the .github folder of your repository',
        'Copilot will automatically detect and use these instructions when working in your repo',
        'The file includes SFCC development patterns, security guidelines, and MCP tool usage'
      ]
    },
    claude: {
      setupUrl: 'https://support.anthropic.com/en/articles/10185728-understanding-claude-s-personalization-features',
      files: [
        {
          name: 'claude_custom_instructions.md',
          path: 'ai-instructions/claude-desktop/claude_custom_instructions.md',
          description: 'Claude Desktop-optimized instructions with conversational development patterns and MCP integration'
        }
      ],
      instructions: [
        'Copy the instruction content from the repository file',
        'Click your initials in the lower left corner of Claude',
        'Select "Settings" from the menu',
        'Under "What preferences should Claude consider in responses?", paste the content',
        'These profile preferences will apply to all your conversations with Claude'
      ]
    },
    cursor: {
      setupUrl: 'https://docs.cursor.com/en/context/rules',
      files: [
        {
          name: 'SFCC Rule Pack',
          path: 'ai-instructions/cursor/.cursor/rules/',
          description: 'Complete rule pack with 12 specialized files covering all SFCC development areas'
        }
      ],
      instructions: [
        'Copy the entire .cursor folder from the repository to your project root',
        'This includes 12 specialized rule files covering different SFCC development areas',
        'Cursor automatically applies rules based on file context and development patterns',
        'Rules cover cartridge development, SFRA patterns, security, performance, and debugging'
      ]
    }
  };

  return (
    <div>
      <div role="tablist" aria-label="Instruction files selection" className="flex flex-wrap gap-3 mb-10 mt-10 justify-center">
        <button
          role="tab"
          aria-selected={active === 'copilot'}
          className={`${tabBase} ${active === 'copilot' ? 'bg-green-600 text-white border-green-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-green-400 hover:text-green-600'}`}
          onClick={() => setActive('copilot')}
        >GitHub Copilot</button>
        <button
          role="tab"
          aria-selected={active === 'claude'}
          className={`${tabBase} ${active === 'claude' ? 'bg-blue-600 text-white border-blue-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-blue-400 hover:text-blue-600'}`}
          onClick={() => setActive('claude')}
        >Claude Desktop</button>
        <button
          role="tab"
          aria-selected={active === 'cursor'}
          className={`${tabBase} ${active === 'cursor' ? 'bg-purple-600 text-white border-purple-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-purple-400 hover:text-purple-600'}`}
          onClick={() => setActive('cursor')}
        >Cursor</button>
      </div>
      
      <div className="space-y-8 animate-fade-in" role="tabpanel">
        <div>
          <h3 className="text-lg font-semibold text-gray-900 mb-4">Available Instruction Files</h3>
          <div className="space-y-4">
            {instructionData[active].files.map((file, index) => (
              <div key={index} className="bg-white rounded-xl p-6 border border-gray-200">
                <div className="flex items-start gap-4">
                  <div className="bg-amber-100 rounded-full p-2 flex-shrink-0">
                    <svg className="w-5 h-5 text-amber-600" fill="currentColor" viewBox="0 0 20 20">
                      <path fillRule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clipRule="evenodd" />
                    </svg>
                  </div>
                  <div className="flex-grow">
                    <h4 className="font-semibold text-gray-900 mb-2">{file.name}</h4>
                    <p className="text-sm text-gray-600 mb-3">{file.description}</p>
                    <div className="flex flex-wrap gap-3">
                      <a 
                        href={`https://github.com/taurgis/sfcc-dev-mcp/tree/main/ai-instructions/cursor/.cursor/rules`}
                        target="_blank" 
                        rel="noopener noreferrer"
                        className="inline-flex items-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-800 rounded-lg text-sm font-medium transition-all duration-200 no-underline hover:no-underline focus:no-underline"
                      >
                        📁 View on GitHub
                        <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                          <path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
                        </svg>
                      </a>
                      <span className="inline-flex items-center gap-2 px-3 py-1 bg-gray-50 text-gray-600 rounded-lg text-xs font-mono">
                        {file.path}
                      </span>
                    </div>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>

        <div>
          <h4 className="font-semibold text-gray-900 mb-4">Setup Instructions</h4>
          <div className="bg-gradient-to-r from-amber-50 to-yellow-50 rounded-xl p-6 border border-amber-200">
            <ol className="list-decimal list-inside space-y-3 text-sm text-gray-700">
              {instructionData[active].instructions.map((instruction, index) => (
                <li key={index} className="leading-relaxed">{instruction}</li>
              ))}
            </ol>
          </div>
          
          <div className="flex justify-center mt-6">
            <a 
              href={instructionData[active].setupUrl}
              target="_blank" 
              rel="noopener noreferrer"
              className="inline-flex items-center gap-2 px-6 py-3 bg-amber-100 hover:bg-amber-200 text-amber-700 hover:text-amber-800 rounded-lg font-medium transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg no-underline hover:no-underline focus:no-underline"
            >
              📖 Official Setup Guide
              <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
                <path fillRule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clipRule="evenodd" />
              </svg>
            </a>
          </div>
        </div>

        <div>
          <h4 className="font-semibold text-gray-900 mb-3">Benefits</h4>
          <div className="grid md:grid-cols-2 gap-4">
            <div className="bg-green-50 border border-green-200 rounded-xl p-4">
              <h5 className="font-semibold text-green-700 mb-2 text-sm">🎯 Enhanced Expertise</h5>
              <p className="text-xs text-green-600 leading-relaxed">Pre-loaded SFCC knowledge, development patterns, and security best practices</p>
            </div>
            <div className="bg-blue-50 border border-blue-200 rounded-xl p-4">
              <h5 className="font-semibold text-blue-700 mb-2 text-sm">🔧 MCP Tool Awareness</h5>
              <p className="text-xs text-blue-600 leading-relaxed">Built-in knowledge of 36+ MCP tools (docs, SFRA, best practices, logs, job logs, objects, code versions)</p>
            </div>
            <div className="bg-purple-50 border border-purple-200 rounded-xl p-4">
              <h5 className="font-semibold text-purple-700 mb-2 text-sm">⚡ Faster Development</h5>
              <p className="text-xs text-purple-600 leading-relaxed">Immediate access to SFCC-specific patterns without requiring context</p>
            </div>
            <div className="bg-amber-50 border border-amber-200 rounded-xl p-4">
              <h5 className="font-semibold text-amber-700 mb-2 text-sm">🔒 Security-First</h5>
              <p className="text-xs text-amber-600 leading-relaxed">Built-in security patterns and validation practices for SFCC development</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

```

--------------------------------------------------------------------------------
/docs/dw_catalog/ProductPriceModel.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.catalog

# Class ProductPriceModel

## Inheritance Hierarchy

- Object
  - dw.catalog.ProductPriceModel

## Description

ProductPriceModel provides methods to access all the PriceBook information of a product. A ProductPriceModel instance is retrieved by calling Product.getPriceModel() or Product.getPriceModel(ProductOptionModel) for a specific product. The latter method will return a model which also includes the additional option prices of an option product. When the current price of a product is accessed in the storefront via its price model, a price lookup is performed. The high-level steps of this price lookup are: Get all price books applicable in the context of the current site, time, session, customer, source code. Identify all prices in the applicable price books and for a requested quantity. Calculate the best-price of all identified prices. The best-price is the lowest price. In more detail: Identify applicable price books If any price books are explicitly registered in the session (see pipelet SetApplicablePriceBooks), use these price books and their direct parents for price lookup. Ignore all inactive price books, price books not valid at the current time, and price books with a currency other than the session currency. Otherwise: If a valid source code is registered with the current session, get all price books assigned to the source code and their parent price books. Ignore all inactive price books, price books not valid at the current time, and price books with a currency other than the session currency. Get all price books assigned to site and their parent price books. Ignore all inactive price books, price books not valid at the current time, and price books with a currency other than the session currency. Identify all prices: Get all price definitions for the product from all applicable price books. Ignore price definitions not valid at the current time. Convert any percentage price definition into a monetary amount. As the base price for this calculation, the minimum product price for the minimum order quantity of the product, including product options, is used. Compare all prices and identify the lowest (= best) price. Calculate best price for each defined price cut in the price table and return price table. Variation Price Fallback: If no applicable pricebooks for a variant is found, the price lookup gets the price books from the variant's master product A price books is also not applicable of the price definition for the variant in the price book is not valid at the current time. Typically, in order to do a standard price lookup, it is only necessary to call Product.getPriceModel().getPrice(). However, Commerce Cloud Digital also supports tiered prices, meaning that higher quantities receive a lower price. In this case, the merchant typically wants to display a table of price points on product detail pages. Therefore, the ProductPriceModel provides the method getPriceTable() to retrieve a table of these prices. If a merchant wants to know not only what the price of a given product is, but what price book the price was derived from, this class provides the method getPriceInfo(). This class also provides methods to lookup product prices in specific price books by name and quantity. See getPriceBookPrice(String).

## Properties

### basePriceQuantity

**Type:** Quantity (Read Only)

The quantity for which the base price is defined. This
 is typically 1.0.

### maxPrice

**Type:** Money (Read Only)

Calculates and returns the maximum price-book price of all variants (for
 master products) or set-products (for product sets) for base quantity
 1.00. This value can be used to display a range of prices in storefront.
 If the product represented by this model is not a master product or
 product set, then this method behaves the same as getPrice().
 Only online products are considered. If the "orderable products only"
 search preference is enabled for the current site, then only orderable
 products are considered. For master products, only variants with all
 variation attributes configured are considered.

 Warning:  If the product represented by this model is a master
 product with numerous variants, this method can be very expensive and
 should be avoided.

### maxPricePerUnit

**Type:** Money (Read Only)

Calculates and returns the maximum price-book price per unit of all variants (for
 master products) or set-products (for product sets) for base quantity
 1.00. This value can be used to display a range of prices in storefront.
 If the product represented by this model is not a master product or
 product set, then this method behaves the same as getPricePerUnit().
 Only online products are considered. If the "orderable products only"
 search preference is enabled for the current site, then only orderable
 products are considered. For master products, only variants with all
 variation attributes configured are considered.

 e.g.
 suppose one master product mp (price = $6, unitQuantity = 2), it has 2 variants:
 v1(price = $5, unitQuantity = 5), v2(price = $10, unitQuantity = 20).
 The max price per unit of mp will be max($6/2, $5/5, $10/20) = $3

### minPrice

**Type:** Money (Read Only)

Calculates and returns the minimum price-book price of all variants (for
 master products) or set-products (for product sets) for base quantity
 1.00. This value can be used to display a range of prices in storefront.
 If the product represented by this model is not a master product or
 product set, then this method behaves the same as getPrice().
 Only online products are considered. If the "orderable products only"
 search preference is enabled for the current site, then only orderable
 products are considered. For master products, only variants with all
 variation attributes configured are considered.

 Warning:  If the product represented by this model is a master
 product with numerous variants, this method can be very expensive and
 should be avoided.

### minPricePerUnit

**Type:** Money (Read Only)

Calculates and returns the minimum price-book price per unit of all variants (for
 master products) or set-products (for product sets) for base quantity
 1.00. This value can be used to display a range of prices in storefront.
 If the product represented by this model is not a master product or
 product set, then this method behaves the same as getPricePerUnit().
 Only online products are considered. If the "orderable products only"
 search preference is enabled for the current site, then only orderable
 products are considered. For master products, only variants with all
 variation attributes configured are considered.

 e.g.
 suppose one master product mp (price = $6, unitQuantity = 2), it has 2 variants:
 v1(price = $5, unitQuantity = 5), v2(price = $10, unitQuantity = 20).
 The min price per unit of mp will be min($6/2, $5/5, $10/20) = $0.5

### price

**Type:** Money (Read Only)

The active price of a product, calculated based on base price quantity
 1.00. The price is returned for the currency of the current session.
 
 The price lookup is based on the configuration of price books. It depends
 on various settings, such as which price books are active, or explicitly
 set as applicable in the current session.
 
 If the product represented by this model is an option product, option
 prices will be added to the price book price if the price model was
 initialized with an option model.
 
 If no price could be found, MONEY.NOT_AVAILABLE is returned.

### priceInfo

**Type:** ProductPriceInfo (Read Only)

The active price info of a product, calculated based on base price
 quantity 1.00. The price is returned for the currency of the current
 session.
 
 This method is similar to getPrice() but instead of just
 returning the price value, it returns a ProductPriceInfo
 which contains additional information such as the PriceBook which defined
 the price and the percentage discount this price point represents.
 
 If the product represented by this model is an option product, option
 prices will be added to the price book price if the price model was
 initialized with an option model.
 
 If no price info could be found, null is returned.

### priceInfos

**Type:** Collection (Read Only)

All the eligible ProductPriceInfo(s), calculated based
 on base price quantity 1.00. This will return an empty list if getPriceInfo() would return null, and if there is
 only one price info in the collection it will be the same price info as getPriceInfo(). Two or more price infos
 indicate that there are that many price books that meet the criteria for returning the price shown in the
 storefront.

### pricePerUnit

**Type:** Money (Read Only)

The sales price per unit of a product, calculated based on base price
 quantity 1.00.
 
 The product sales price per unit is returned for the current session currency.
 Hence, the using this method is only useful in storefront processes.
 
 The price lookup is based on the configuration of price books. It depends
 on various settings, such as which price books are active, or explicitly
 set as applicable in the current session.
 
 If no price could be found, MONEY.N_A is returned.

### priceRange

**Type:** boolean (Read Only)

Returns true if this product is a master product (or product set) and the
 collection of online variants (or set products respectively) contains
 products of different prices.

 Warning:  If the product represented by this model is a master
 product with numerous variants, this method can be very expensive and
 should be avoided.

### priceTable

**Type:** ProductPriceTable (Read Only)

The product price table object. The price table represents a map
 between order quantities and prices, and also provides % off information
 to be shown to storefront customers. The price is returned for the
 currency of the current session.
 
 Usually, the product price table is printed on product detail pages in
 the storefront.
 
 If the product represented by this model is an option product, option
 prices will be added to the price book price if the price model was
 initialized with an option model.
 
 All other methods of this class are based on the information in the
 product price table.

## Constructor Summary

## Method Summary

### getBasePriceQuantity

**Signature:** `getBasePriceQuantity() : Quantity`

Returns the quantity for which the base price is defined.

### getMaxPrice

**Signature:** `getMaxPrice() : Money`

Calculates and returns the maximum price-book price of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMaxPriceBookPrice

**Signature:** `getMaxPriceBookPrice(priceBookID : String) : Money`

Calculates and returns the maximum price in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMaxPriceBookPricePerUnit

**Signature:** `getMaxPriceBookPricePerUnit(priceBookID : String) : Money`

Calculates and returns the maximum price per unit in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMaxPricePerUnit

**Signature:** `getMaxPricePerUnit() : Money`

Calculates and returns the maximum price-book price per unit of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMinPrice

**Signature:** `getMinPrice() : Money`

Calculates and returns the minimum price-book price of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMinPriceBookPrice

**Signature:** `getMinPriceBookPrice(priceBookID : String) : Money`

Calculates and returns the minimum price in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMinPriceBookPricePerUnit

**Signature:** `getMinPriceBookPricePerUnit(priceBookID : String) : Money`

Calculates and returns the minimum price per unit in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getMinPricePerUnit

**Signature:** `getMinPricePerUnit() : Money`

Calculates and returns the minimum price-book price per unit of all variants (for master products) or set-products (for product sets) for base quantity 1.00.

### getPrice

**Signature:** `getPrice() : Money`

Returns the active price of a product, calculated based on base price quantity 1.00.

### getPrice

**Signature:** `getPrice(quantity : Quantity) : Money`

Returns the active price of a product, calculated based on the passed order quantity.

### getPriceBookPrice

**Signature:** `getPriceBookPrice(priceBookID : String) : Money`

Returns the active price of the product in the specified price book for quantity 1.00.

### getPriceBookPrice

**Signature:** `getPriceBookPrice(priceBookID : String, quantity : Quantity) : Money`

Returns the active price of the product in the specified price book for the specified quantity.

### getPriceBookPriceInfo

**Signature:** `getPriceBookPriceInfo(priceBookID : String) : ProductPriceInfo`

This method acts similarly to getPriceBookPrice(String) but returns a ProductPriceInfo object wrapping the actual price with additional information.

### getPriceBookPriceInfo

**Signature:** `getPriceBookPriceInfo(priceBookID : String, quantity : Quantity) : ProductPriceInfo`

This method acts similarly to getPriceBookPrice(String, Quantity) but returns a ProductPriceInfo object wrapping the actual price with additional information.

### getPriceBookPricePerUnit

**Signature:** `getPriceBookPricePerUnit(priceBookID : String) : Money`

Returns the active price per unit of the product in the specified price book for quantity 1.00.

### getPriceBookPricePerUnit

**Signature:** `getPriceBookPricePerUnit(priceBookID : String, quantity : Quantity) : Money`

Returns the active price per unit of the product in the specified price book for the specified quantity.

### getPriceInfo

**Signature:** `getPriceInfo() : ProductPriceInfo`

Returns the active price info of a product, calculated based on base price quantity 1.00.

### getPriceInfo

**Signature:** `getPriceInfo(quantity : Quantity) : ProductPriceInfo`

Returns the active price info of a product, calculated based on the passed order quantity.

### getPriceInfos

**Signature:** `getPriceInfos() : Collection`

Returns all the eligible ProductPriceInfo(s), calculated based on base price quantity 1.00.

### getPricePercentage

**Signature:** `getPricePercentage(basePrice : Money, comparePrice : Money) : Number`

Calculates and returns the percentage off amount of the passed comparePrice to the passed basePrice.

### getPricePerUnit

**Signature:** `getPricePerUnit() : Money`

Returns the sales price per unit of a product, calculated based on base price quantity 1.00.

### getPricePerUnit

**Signature:** `getPricePerUnit(quantity : Quantity) : Money`

Returns the sales price per unit of a product, calculated based on the passed order quantity.

### getPriceTable

**Signature:** `getPriceTable() : ProductPriceTable`

Returns the product price table object.

### isPriceRange

**Signature:** `isPriceRange() : boolean`

Returns true if this product is a master product (or product set) and the collection of online variants (or set products respectively) contains products of different prices.

### isPriceRange

**Signature:** `isPriceRange(priceBookID : String) : boolean`

Returns true if this product is a master product (or product set) and the collection of online variants (or set products respectively) contains products of different prices in the specified price book.

## Method Detail

## Method Details

### getBasePriceQuantity

**Signature:** `getBasePriceQuantity() : Quantity`

**Description:** Returns the quantity for which the base price is defined. This is typically 1.0.

**Returns:**

the quantity for which the base price is defined.

---

### getMaxPrice

**Signature:** `getMaxPrice() : Money`

**Description:** Calculates and returns the maximum price-book price of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of prices in storefront. If the product represented by this model is not a master product or product set, then this method behaves the same as getPrice(). Only online products are considered. If the "orderable products only" search preference is enabled for the current site, then only orderable products are considered. For master products, only variants with all variation attributes configured are considered. Warning: If the product represented by this model is a master product with numerous variants, this method can be very expensive and should be avoided.

**Returns:**

Maximum price of all online variants or set-products.

---

### getMaxPriceBookPrice

**Signature:** `getMaxPriceBookPrice(priceBookID : String) : Money`

**Description:** Calculates and returns the maximum price in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of prices in storefront. This method follows the same rules as getPriceBookPrice(String) in determining the price book price for each variant or set-product. If the product represented by this model is not a master product or product set, then this method behaves the same as getPriceBookPrice(String).

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.

**Returns:**

The maximum price across all subproducts in the specified price book.

---

### getMaxPriceBookPricePerUnit

**Signature:** `getMaxPriceBookPricePerUnit(priceBookID : String) : Money`

**Description:** Calculates and returns the maximum price per unit in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of price per units in storefront. This method follows the same rules as getPriceBookPricePerUnit(String) in determining the price book price for each variant or set-product. If the product represented by this model is not a master product or product set, then this method behaves the same as getPriceBookPricePerUnit(String).

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.

**Returns:**

The maximum price per unit across all sub-products in the specified price book.

---

### getMaxPricePerUnit

**Signature:** `getMaxPricePerUnit() : Money`

**Description:** Calculates and returns the maximum price-book price per unit of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of prices in storefront. If the product represented by this model is not a master product or product set, then this method behaves the same as getPricePerUnit(). Only online products are considered. If the "orderable products only" search preference is enabled for the current site, then only orderable products are considered. For master products, only variants with all variation attributes configured are considered. e.g. suppose one master product mp (price = $6, unitQuantity = 2), it has 2 variants: v1(price = $5, unitQuantity = 5), v2(price = $10, unitQuantity = 20). The max price per unit of mp will be max($6/2, $5/5, $10/20) = $3

**Returns:**

Maximum price per unit of all online variants or set-products.

---

### getMinPrice

**Signature:** `getMinPrice() : Money`

**Description:** Calculates and returns the minimum price-book price of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of prices in storefront. If the product represented by this model is not a master product or product set, then this method behaves the same as getPrice(). Only online products are considered. If the "orderable products only" search preference is enabled for the current site, then only orderable products are considered. For master products, only variants with all variation attributes configured are considered. Warning: If the product represented by this model is a master product with numerous variants, this method can be very expensive and should be avoided.

**Returns:**

Minimum price of all online variants or set-products.

---

### getMinPriceBookPrice

**Signature:** `getMinPriceBookPrice(priceBookID : String) : Money`

**Description:** Calculates and returns the minimum price in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of prices in storefront. This method follows the same rules as getPriceBookPrice(String) in determining the price book price for each variant or set-product. If the product represented by this model is not a master product or product set, then this method behaves the same as getPriceBookPrice(String).

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.

**Returns:**

The minimum price across all subproducts in the specified price book.

---

### getMinPriceBookPricePerUnit

**Signature:** `getMinPriceBookPricePerUnit(priceBookID : String) : Money`

**Description:** Calculates and returns the minimum price per unit in a given price book of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of price per units in storefront. This method follows the same rules as getPriceBookPricePerUnit(String) in determining the price book price for each variant or set-product. If the product represented by this model is not a master product or product set, then this method behaves the same as getPriceBookPricePerUnit(String).

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.

**Returns:**

The minimum price per unit across all sub-products in the specified price book.

---

### getMinPricePerUnit

**Signature:** `getMinPricePerUnit() : Money`

**Description:** Calculates and returns the minimum price-book price per unit of all variants (for master products) or set-products (for product sets) for base quantity 1.00. This value can be used to display a range of prices in storefront. If the product represented by this model is not a master product or product set, then this method behaves the same as getPricePerUnit(). Only online products are considered. If the "orderable products only" search preference is enabled for the current site, then only orderable products are considered. For master products, only variants with all variation attributes configured are considered. e.g. suppose one master product mp (price = $6, unitQuantity = 2), it has 2 variants: v1(price = $5, unitQuantity = 5), v2(price = $10, unitQuantity = 20). The min price per unit of mp will be min($6/2, $5/5, $10/20) = $0.5

**Returns:**

Minimum price of all online variants or set-products.

---

### getPrice

**Signature:** `getPrice() : Money`

**Description:** Returns the active price of a product, calculated based on base price quantity 1.00. The price is returned for the currency of the current session. The price lookup is based on the configuration of price books. It depends on various settings, such as which price books are active, or explicitly set as applicable in the current session. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. If no price could be found, MONEY.NOT_AVAILABLE is returned.

**Returns:**

the product price.

---

### getPrice

**Signature:** `getPrice(quantity : Quantity) : Money`

**Description:** Returns the active price of a product, calculated based on the passed order quantity. The price is returned for the currency of the current session. The price lookup is based on the configuration of price books. It depends on various settings, such as which price books are active, or explicitly set as applicable in the current session. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. If passed order quantity < 1 (and greater than zero), price for quantity 1 is returned. If no price could be found, MONEY.NOT_AVAILABLE is returned.

**Parameters:**

- `quantity`: Quantity price is requested for

**Returns:**

the product price.

---

### getPriceBookPrice

**Signature:** `getPriceBookPrice(priceBookID : String) : Money`

**Description:** Returns the active price of the product in the specified price book for quantity 1.00. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. Money.NOT_AVAILABLE will be returned in any of the following cases: priceBookID is null or does not identify a valid price book. The price book has no price for the product. None of the prices for the product in the price book is currently active. The currently active price entry is a percentage.

**Parameters:**

- `priceBookID`: ID of price book the price is requested for.

**Returns:**

the price of the product in the specified price book.

---

### getPriceBookPrice

**Signature:** `getPriceBookPrice(priceBookID : String, quantity : Quantity) : Money`

**Description:** Returns the active price of the product in the specified price book for the specified quantity. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. Money.NOT_AVAILABLE will be returned in any of the following cases: priceBookID is null or does not identify a valid price book. quantity is null. The price book has no price for the product. None of the prices for the product in the price book is currently active. The currently active price entry is a percentage.

**Parameters:**

- `priceBookID`: ID of price book the price is requested for.
- `quantity`: the specified quantity to find the price for.

**Returns:**

the price of the product in the specified price book.

---

### getPriceBookPriceInfo

**Signature:** `getPriceBookPriceInfo(priceBookID : String) : ProductPriceInfo`

**Description:** This method acts similarly to getPriceBookPrice(String) but returns a ProductPriceInfo object wrapping the actual price with additional information.

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.

**Returns:**

the product price info, or null if not found.

---

### getPriceBookPriceInfo

**Signature:** `getPriceBookPriceInfo(priceBookID : String, quantity : Quantity) : ProductPriceInfo`

**Description:** This method acts similarly to getPriceBookPrice(String, Quantity) but returns a ProductPriceInfo object wrapping the actual price with additional information.

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.
- `quantity`: Quantity price is requested for.

**Returns:**

the product price info, or null if not found.

---

### getPriceBookPricePerUnit

**Signature:** `getPriceBookPricePerUnit(priceBookID : String) : Money`

**Description:** Returns the active price per unit of the product in the specified price book for quantity 1.00. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. Money.NOT_AVAILABLE will be returned in any of the following cases: The priceBookID does not identify a valid price book. The price book has no price for the product. None of the prices for the product in the price book is currently active. The currently active price entry is a percentage.

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.

**Returns:**

the price per unit of the product in the specified price book.

---

### getPriceBookPricePerUnit

**Signature:** `getPriceBookPricePerUnit(priceBookID : String, quantity : Quantity) : Money`

**Description:** Returns the active price per unit of the product in the specified price book for the specified quantity. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. Money.NOT_AVAILABLE will be returned in any of the following cases: The priceBookID does not identify a valid price book. The price book has no price for the product. None of the prices for the product in the price book is currently active. The currently active price entry is a percentage.

**Parameters:**

- `priceBookID`: ID of price book the price is requested for, must not be null.
- `quantity`: the specified quantity to find the price for, must not be null.

**Returns:**

the price per unit of the product in the specified price book for the specific quantity.

---

### getPriceInfo

**Signature:** `getPriceInfo() : ProductPriceInfo`

**Description:** Returns the active price info of a product, calculated based on base price quantity 1.00. The price is returned for the currency of the current session. This method is similar to getPrice() but instead of just returning the price value, it returns a ProductPriceInfo which contains additional information such as the PriceBook which defined the price and the percentage discount this price point represents. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. If no price info could be found, null is returned.

**Returns:**

the product price info, or null if not found.

**See Also:**

getPrice()
getPriceInfo(Quantity)

---

### getPriceInfo

**Signature:** `getPriceInfo(quantity : Quantity) : ProductPriceInfo`

**Description:** Returns the active price info of a product, calculated based on the passed order quantity. The price is returned for the currency of the current session. This method is similar to getPrice(Quantity) but instead of just returning the price value, it returns a ProductPriceInfo which contains additional information such as the PriceBook which defined the price and the percentage discount this price point represents. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. If no price info could be found, null is returned.

**Parameters:**

- `quantity`: the quantity to use.

**Returns:**

the product price info, or null if not found.

**See Also:**

getPrice(Quantity)

---

### getPriceInfos

**Signature:** `getPriceInfos() : Collection`

**Description:** Returns all the eligible ProductPriceInfo(s), calculated based on base price quantity 1.00. This will return an empty list if getPriceInfo() would return null, and if there is only one price info in the collection it will be the same price info as getPriceInfo(). Two or more price infos indicate that there are that many price books that meet the criteria for returning the price shown in the storefront.

**Returns:**

any product price info that could be responsible for the storefront price, or empty collection if there were no product price infos this price model.

**See Also:**

getPriceInfo()

---

### getPricePercentage

**Signature:** `getPricePercentage(basePrice : Money, comparePrice : Money) : Number`

**Description:** Calculates and returns the percentage off amount of the passed comparePrice to the passed basePrice.

**Deprecated:**

Use Money.percentLessThan(Money)

**Parameters:**

- `basePrice`: The assumed 100% price amount
- `comparePrice`: The price to compare to the basePrice

**Returns:**

The percentage between comparePrice and basePrice (e.g. 90%).

---

### getPricePerUnit

**Signature:** `getPricePerUnit() : Money`

**Description:** Returns the sales price per unit of a product, calculated based on base price quantity 1.00. The product sales price per unit is returned for the current session currency. Hence, the using this method is only useful in storefront processes. The price lookup is based on the configuration of price books. It depends on various settings, such as which price books are active, or explicitly set as applicable in the current session. If no price could be found, MONEY.N_A is returned.

**Returns:**

product sales price per unit

---

### getPricePerUnit

**Signature:** `getPricePerUnit(quantity : Quantity) : Money`

**Description:** Returns the sales price per unit of a product, calculated based on the passed order quantity. The product sales price per unit is returned for the current session currency. Hence, the using this method is only useful in storefront processes. The price lookup is based on the configuration of price books. It depends on various settings, such as which price books are active, or explicitely set as applicable in the current session. If no price could be found, MONEY.N_A is returned.

**Parameters:**

- `quantity`: Quantity price is requested for

**Returns:**

product sales price per unit

---

### getPriceTable

**Signature:** `getPriceTable() : ProductPriceTable`

**Description:** Returns the product price table object. The price table represents a map between order quantities and prices, and also provides % off information to be shown to storefront customers. The price is returned for the currency of the current session. Usually, the product price table is printed on product detail pages in the storefront. If the product represented by this model is an option product, option prices will be added to the price book price if the price model was initialized with an option model. All other methods of this class are based on the information in the product price table.

**Returns:**

the Product price table.

---

### isPriceRange

**Signature:** `isPriceRange() : boolean`

**Description:** Returns true if this product is a master product (or product set) and the collection of online variants (or set products respectively) contains products of different prices. Warning: If the product represented by this model is a master product with numerous variants, this method can be very expensive and should be avoided.

**Returns:**

True if this product has a range of prices, false otherwise.

---

### isPriceRange

**Signature:** `isPriceRange(priceBookID : String) : boolean`

**Description:** Returns true if this product is a master product (or product set) and the collection of online variants (or set products respectively) contains products of different prices in the specified price book.

**Parameters:**

- `priceBookID`: The ID of the price book.

**Returns:**

True if this product has a range of prices, false otherwise.

---
```

--------------------------------------------------------------------------------
/docs/best-practices/scapi_custom_endpoint.md:
--------------------------------------------------------------------------------

```markdown
# Salesforce B2C Commerce: Custom SCAPI Endpoint Best Practices

This guide provides a concise overview of best practices and examples for creating custom Salesforce Commerce API (SCAPI) endpoints in B2C Commerce Cloud.

**IMPORTANT**: Before implementing custom SCAPI endpoints, consult the **Performance and Stability Best Practices** guide from this MCP server. Pay special attention to the external system integration guidelines, timeout requirements, and caching strategies for optimal endpoint performance.

## 1. Authentication Methodologies for Custom SCAPI Endpoints

Custom SCAPI endpoints leverage the Shopper Login and API Access Service (SLAS) for authentication and authorization. Understanding SLAS authentication flows is critical for secure endpoint implementation.

### 1.1 Client Architecture Decision: Public vs. Private

The most critical architectural decision is determining your client type, which dictates the entire authentication flow:

#### Private Clients
- **Capability**: Can securely store a `client_secret` on a server-side component
- **Use Cases**: Backend-for-Frontend (BFF), full-stack web applications, server-to-server integrations
- **Security Model**: Uses `client_secret` in Basic Authorization header for client authentication
- **OAuth Grant Types**: 
  - `client_credentials` (guest users, system operations)
  - `authorization_code` (registered user authentication)

#### Public Clients
- **Capability**: Cannot securely store secrets (operates in untrusted environments)
- **Use Cases**: PWA Kit storefronts, Single-Page Applications (SPAs), native mobile apps
- **Security Model**: Uses Proof Key for Code Exchange (PKCE) for dynamic security
- **OAuth Grant Types**: 
  - `authorization_code_pkce` (all authentication scenarios)

### 1.2 Security Best Practices

#### Scope-Based Authorization in Custom Endpoints

Always implement fine-grained authorization checks in your endpoint scripts:

```javascript
// In your custom endpoint script
exports.getLoyaltyInfo = function () {
    var customerId = request.getHttpParameterMap().get('c_customer_id').getStringValue();
    
    // CRITICAL: Verify the authenticated user can access this customer's data
    if (request.user && request.user.profile) {
        var authenticatedCustomerId = request.user.profile.customerNo;
        
        // Prevent privilege escalation
        if (authenticatedCustomerId !== customerId) {
            RESTResponseMgr.createError(403, "insufficient-privileges", 
                "Access Denied", "Cannot access another customer's data").render();
            return;
        }
    }
    
    // Continue with business logic...
};
```

### 1.3 Client Configuration and Scope Management

#### SLAS Client Configuration

Configure clients using the SLAS Admin API or UI:

```javascript
// Example API call to create/update a SLAS client
const clientConfig = {
    "clientId": "your-client-id",
    "name": "My Custom API Client",
    "isPrivateClient": true, // or false for public clients
    "secret": "secure-client-secret", // Only for private clients
    "channels": ["your-site-id"],
    "scopes": [
        "sfcc.shopper-baskets-orders.rw",
        "sfcc.shopper-myaccount.rw", 
        "sfcc.shopper-products",
        "c_read_loyalty", // Your custom scope
        "c_write_loyalty"
    ],
    "redirectUri": [
        "https://your-app.com/callback"
    ]
};
```

#### Custom Object Scopes

For accessing custom objects via SCAPI, configure both the object and scope:

1. **Define Custom Object** in Business Manager: `Administration > Site Development > Custom Object Types`
2. **Add Scope** to SLAS client: `sfcc.shopper-custom-objects.{object-type-id}`

```yaml
# In your schema.yaml
security:
  - ShopperToken: [sfcc.shopper-custom-objects.StoreReview]
```

### 1.4 Authentication Flow Examples & Testing

#### Private Client: Guest Token Flow (Client Credentials)

For server-side applications using private clients:

```bash
# Get Guest Token
wget --post-data="grant_type=client_credentials" \
     --header="Authorization: Basic $(echo -n 'your-private-client-id:your-client-secret' | base64)" \
     --header="Content-Type: application/x-www-form-urlencoded" \
     -O token_response.json \
     "https://your-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/f_ecom_your_org/oauth2/token"

# Extract Token and Test API Call
TOKEN=$(cat token_response.json | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
wget --header="Authorization: Bearer $TOKEN" \
     -O categories.json \
     "https://your-shortcode.api.commercecloud.salesforce.com/product/shopper-products/v1/organizations/f_ecom_your_org/categories/root?siteId=your-site-id"
```

#### Public Client: PKCE Authentication Flow

For browser-based applications using PKCE:

```bash
# Generate PKCE Challenge
VERIFIER=$(openssl rand -base64 96 | tr -d '\n' | tr '/+' '_-' | tr -d '=')
CHALLENGE=$(echo -n $VERIFIER | openssl dgst -binary -sha256 | openssl base64 -A | tr '/' '_' | tr '+' '-' | tr -d '=')

# Get Authorization Code (returns redirect with usid and code)
wget --server-response --spider \
     "https://your-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/f_ecom_your_org/oauth2/authorize?redirect_uri=http://localhost:3000/callback&response_type=code&hint=guest&client_id=your-public-client-id&code_challenge=$CHALLENGE" \
     2>&1 | grep -i location

# Exchange Code for Token (extract usid and code from Location header)
wget --post-data="client_id=your-public-client-id&channel_id=your-site-id&code_verifier=$VERIFIER&usid=your-usid&code=your-code&grant_type=authorization_code_pkce&redirect_uri=http://localhost:3000/callback" \
     --header="Content-Type: application/x-www-form-urlencoded" \
     -O pkce_token_response.json \
     "https://your-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/f_ecom_your_org/oauth2/token"
```

### 1.5 Error Handling and Troubleshooting

#### Common Authentication Errors

| Error Code | Cause | Solution |
|------------|-------|----------|
| 401 Unauthorized | Invalid or expired token | Refresh token or re-authenticate |
| 403 Forbidden | Valid token, missing scope | Check client scope configuration |
| 400 Bad Request | Invalid PKCE parameters | Verify code_verifier/code_challenge |
| 404 Not Found | Incorrect endpoint URL | Verify API registration and URL structure |

#### Token Validation in Scripts

```javascript
// Validate token and extract user information
function validateAndExtractUser() {
    if (!request.user) {
        RESTResponseMgr.createError(401, "unauthorized", 
            "Authentication Required", "Valid token required").render();
        return null;
    }
    
    // For registered users
    if (request.user.profile && request.user.profile.customerNo) {
        return {
            type: 'registered',
            customerId: request.user.profile.customerNo,
            email: request.user.profile.email
        };
    }
    
    // For guest users
    return {
        type: 'guest',
        customerId: request.user.ID // Guest customer ID
    };
}
```

#### Accessing Customer Context in Headless APIs

Even though SCAPI endpoints use JWT-based authentication without traditional sessions, you can still access the authenticated customer:

```javascript
// Access the current customer (works for both registered and guest users)
exports.getCustomerInfo = function () {
    var session = request.getSession();
    var customer = session.getCustomer();
    
    if (!customer) {
        RESTResponseMgr.createError(401, "unauthorized", 
            "No Customer", "No authenticated customer found").render();
        return;
    }
    
    var customerData = {
        isRegistered: customer.isRegistered(),
        isAuthenticated: customer.isAuthenticated()
    };
    
    // For registered customers, access profile information
    if (customer.isRegistered() && customer.getProfile()) {
        var profile = customer.getProfile();
        customerData.customerId = profile.getCustomerNo();
        customerData.email = profile.getEmail();
        customerData.firstName = profile.getFirstName();
        customerData.lastName = profile.getLastName();
    } else {
        // For guest customers
        customerData.customerId = customer.getID();
        customerData.isGuest = true;
    }
    
    RESTResponseMgr.createSuccess(customerData).render();
};

exports.getCustomerInfo.public = true;
```

## 2. URL Structure and Endpoint Mapping

Understanding how your OpenAPI schema translates to actual SCAPI endpoint URLs is crucial for both development and client integration.

### 2.1 SCAPI URL Structure

All custom SCAPI endpoints follow this standardized URL pattern:

```
https://{shortcode}.api.commercecloud.salesforce.com/custom/{api-name}/v{major-version}/organizations/{organization-id}{path}?{query-parameters}
```

**URL Components:**
- **`{shortcode}`**: Your SFCC instance shortcode (e.g., `zzrf-001`, `my-company-dev`)
- **`{api-name}`**: The directory name under `cartridge/rest-apis/` in your cartridge
- **`v{major-version}`**: Automatically derived from `info.version` in your `schema.yaml` (e.g., "1.0.0" → "v1", "2.3.1" → "v2")
- **`{organization-id}`**: Your SFCC organization ID (typically starts with `f_ecom_`)
- **`{path}`**: The path defined in your OpenAPI schema
- **`{query-parameters}`**: Query string parameters from your schema

### 2.2 Schema-to-URL Mapping Examples

#### Example 1: Simple Query Parameters

**OpenAPI Schema Definition:**
```yaml
# File: int_product_api/cartridge/rest-apis/product-info/schema.yaml
openapi: 3.0.0
info:
  title: Product Information API
  version: "1.0.0"
paths:
  /products/{sku}:
    get:
      summary: Retrieves basic product information by SKU
      operationId: getProductInfo
      parameters:
        - name: siteId
          in: query
          required: true
          schema:
            type: string
            minLength: 1
        - name: sku
          in: path
          required: true
          schema:
            type: string
            minLength: 1
```

**Resulting Endpoint URL:**
```
https://{{shortcode}}.api.commercecloud.salesforce.com/custom/product-info/v1/organizations/{{organizationId}}/products/682875540326M?siteId=RefArchGlobal
```

**Complete Example:**
```
https://zzrf-001.api.commercecloud.salesforce.com/custom/product-info/v1/organizations/f_ecom_zzrf_001/products/682875540326M?siteId=RefArchGlobal
```

#### Example 2: Complex Query Parameters with Custom Attributes

**OpenAPI Schema Definition:**
```yaml
# File: int_pricing_api/cartridge/rest-apis/pricing-api/schema.yaml
openapi: 3.0.0
info:
  title: Advanced Pricing API
  version: "1.2.0"  # Results in v1 URL
paths:
  /product-pricing/{productId}:
    get:
      summary: Retrieves comprehensive pricing information for a product SKU
      description: |
        Returns detailed pricing information for a given product including:
        - Base price and promotional prices
        - Available coupon-based promotions (without requiring coupon codes)
        - Price ranges for master products
        - Quantity-based pricing tiers
      operationId: getProductPricing
      parameters:
        - name: siteId
          in: query
          required: true
          description: Site identifier for the pricing context
          schema:
            type: string
            minLength: 1
        - name: productId
          in: path
          required: true
          description: Product ID or SKU to get pricing for
          schema:
            type: string
            minLength: 1
        - name: c_quantity
          in: query
          required: false
          description: Quantity for tiered pricing calculation (defaults to 1)
          schema:
            type: number
            minimum: 1
            default: 1
        - name: c_currency
          in: query
          required: false
          description: Currency code for price display (defaults to session currency)
          schema:
            type: string
            pattern: '^[A-Z]{3}$'
        - name: c_include_upcoming_promotions
          in: query
          required: false
          description: Include upcoming promotions within next 24 hours
          schema:
            type: boolean
            default: false
```

**Resulting Endpoint URL:**
```
https://{{shortcode}}.api.commercecloud.salesforce.com/custom/pricing-api/v1/organizations/{{organizationId}}/product-pricing/product-123?siteId=your-site-id&c_quantity=2&c_include_upcoming_promotions=true
```

**Complete Example:**
```
https://your-shortcode.api.commercecloud.salesforce.com/custom/pricing-api/v1/organizations/your-org-id/product-pricing/product-123?siteId=your-site-id&c_quantity=2&c_include_upcoming_promotions=true
```

#### Example 3: Nested Resource Paths

**OpenAPI Schema Definition:**
```yaml
# File: int_customer_api/cartridge/rest-apis/customer-management/schema.yaml
openapi: 3.0.0
info:
  title: Customer Management API
  version: "2.0.0"  # Results in v2 URL
paths:
  /customers/{customerId}/orders/{orderId}/tracking:
    get:
      summary: Get order tracking information
      operationId: getOrderTracking
      parameters:
        - name: siteId
          in: query
          required: true
          schema:
            type: string
        - name: customerId
          in: path
          required: true
          schema:
            type: string
        - name: orderId
          in: path
          required: true
          schema:
            type: string
        - name: c_include_delivery_details
          in: query
          required: false
          schema:
            type: boolean
            default: false
```

**Resulting Endpoint URL:**
```
https://{{shortcode}}.api.commercecloud.salesforce.com/custom/customer-management/v2/organizations/{{organizationId}}/customers/12345/orders/ORD-001234/tracking?siteId=SiteGenesis&c_include_delivery_details=true
```

### 2.3 Parameter Access in Scripts

Understanding how to access different parameter types in your script implementation:

```javascript
'use strict';

var RESTResponseMgr = require('dw/system/RESTResponseMgr');

exports.getProductPricing = function () {
    // Path parameters: use getSCAPIPathParameters()
    var productId = request.getSCAPIPathParameters().get('productId');
    
    // Query parameters: use getHttpParameterMap()
    var siteId = request.getHttpParameterMap().get('siteId').getStringValue();
    var quantity = request.getHttpParameterMap().get('c_quantity').getIntValue() || 1;
    var currency = request.getHttpParameterMap().get('c_currency').getStringValue();
    var includeUpcoming = request.getHttpParameterMap().get('c_include_upcoming_promotions').getBooleanValue();
    
    // Parameter validation
    if (!productId) {
        RESTResponseMgr.createError(400, "missing-product-id", 
            "Missing Product ID", "Product ID is required in the path").render();
        return;
    }
    
    // Business logic implementation...
    var pricingData = {
        productId: productId,
        basePrice: 29.99,
        salePrice: 24.99,
        currency: currency || "USD",
        quantity: quantity,
        upcomingPromotions: includeUpcoming ? [] : undefined
    };
    
    RESTResponseMgr.createSuccess(pricingData).render();
};

exports.getProductPricing.public = true;
```

### 2.4 Version Management in URLs

The version segment in your URL is automatically determined by the major version number in your OpenAPI schema:

| Schema Version | URL Version | Example URL |
|----------------|-------------|-------------|
| "1.0.0" | v1 | `/custom/my-api/v1/organizations/...` |
| "1.2.5" | v1 | `/custom/my-api/v1/organizations/...` |
| "2.0.0" | v2 | `/custom/my-api/v2/organizations/...` |
| "2.1.3" | v2 | `/custom/my-api/v2/organizations/...` |
| "3.0.0-beta" | v3 | `/custom/my-api/v3/organizations/...` |

**Best Practice**: Use semantic versioning in your schema and introduce breaking changes only in new major versions to maintain backward compatibility.

### 2.5 Testing Your URL Mappings

#### Using Postman/cURL

```bash
# Test the product pricing endpoint
curl -X GET \
  'https://your-shortcode.api.commercecloud.salesforce.com/custom/pricing-api/v1/organizations/your-org-id/product-pricing/product-123?siteId=your-site-id&c_quantity=2&c_include_upcoming_promotions=true' \
  -H 'Authorization: Bearer YOUR_SLAS_TOKEN' \
  -H 'Content-Type: application/json'
```

### 2.6 Common Issues and Solutions

| Issue | Symptom | Solution |
|-------|---------|----------|
| 404 Not Found | Endpoint not accessible | Check cartridge registration, API directory name, and schema syntax |
| Version mismatch | Wrong version in URL | Verify `info.version` in schema.yaml matches expected URL |
| Parameter not found | Script can't access parameters | Ensure parameter names match schema exactly, use correct access method |
| Invalid path structure | URL doesn't match expected pattern | Verify path definition in schema matches your intended URL structure |

### 2.7 Documentation for Client Developers

When documenting your custom endpoints for client developers, always provide:

1. **Complete URL Examples**: Show the full URL with real values
2. **Parameter Descriptions**: Explain what each parameter does and its format requirements
3. **Authentication Requirements**: Specify required scopes and token types
4. **Response Examples**: Include sample JSON responses
5. **Error Scenarios**: Document common error codes and their meanings

**Example Documentation:**

```markdown
## Get Product Pricing

Retrieves comprehensive pricing information for a product.

### Endpoint
`GET /custom/pricing-api/v1/organizations/{organizationId}/product-pricing/{productId}`

### Full URL Example
```
https://your-shortcode.api.commercecloud.salesforce.com/custom/pricing-api/v1/organizations/f_ecom_your_org/product-pricing/ABC123?siteId=RefArchGlobal&c_quantity=2
```

### Parameters
- `productId` (path, required): Product SKU or ID
- `siteId` (query, required): Site identifier
- `c_quantity` (query, optional): Quantity for tiered pricing (default: 1)
- `c_currency` (query, optional): Currency code (default: session currency)

### Authentication
Requires SLAS Shopper Token with scope: `c_read_pricing`
```

This URL structure understanding is essential for both endpoint development and client integration, ensuring your custom SCAPI endpoints are accessible and properly documented.

## 3. Securing Endpoints

Endpoints must be associated with a security scheme in the API contract. SCAPI supports two primary security schemes for custom endpoints:

- **ShopperToken**: For Shopper APIs and storefront use cases. Use Shopper Login and API Access to obtain tokens.
- **AmOAuth2**: For SCAPI Admin APIs to be used with backoffice apps. Use Account Manager to obtain tokens.

Association is either per operation or global, with the operation scheme taking precedence. The schemes are the same as those used for non-custom B2C Commerce APIs (SCAPI).

The scheme must set exactly one custom scope.

### 3.1 Security Scheme Configuration

Schemes used in the contract must be defined in the components section, for example:

```yaml
components:
  securitySchemes:
    ShopperToken: {}
    AmOAuth2:
      type: oauth2

# 👇 A global scheme applies to all operations in this file.
security:
  - AmOAuth2: ["c_loyalty_rw"]

paths:
  /customer:
    get:
      operationId: getLoyaltyInfo
      parameters:
        - in: query
          name: siteId
          required: true
          schema:
            type: string
            minLength: 1
      responses:
        200:
          description: OK
      # 👇 An operation scheme applies only to this operation and takes precedence over the global scheme.
      security:
        - ShopperToken: ["c_loyalty"]
```

### 3.2 Custom Scope Requirements

Custom scopes must meet the following requirements:

- The name must begin with `c_`.
- The name must not contain characters other than alphanumeric, period, hyphen and underscore.
- The name must not be more than 25 characters.

Endpoints without correct scheme or scope assignments are not registered.

### 3.3 Requesting Endpoints

#### ShopperLogin

Scopes for ShopperLogin can be assigned to a client in the SLAS Admin UI. For example:

> Custom API SLAS scope configuration and permissions

To obtain a SLAS token, follow the steps in the Shopper Login overview.

#### AmOAuth2

Custom scopes for AmOAuth2 can be assigned to a client in Account Manager. For example:

> Account Manager OAuth2 scopes configuration for Custom APIs

To obtain an Account Manager token follow the steps in the Authorization for Admin APIs guide.

Unauthorized requests receive a 401 Unauthorized response.

### 3.4 Complete Security Scheme Definition

While endpoint registration does not require the complete definition of the security schemes, it can be useful to have a complete and valid scheme to enable the creation of documentation, tests and code stubs, and mock requests. The following code provides a complete example:

```yaml
components:
  securitySchemes:
    ShopperToken:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://my-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{organizationId}/oauth2/authorize
          tokenUrl: https://my-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{organizationId}/oauth2/token
          scopes:
            c_my-scopes: description of my-scopes
        clientCredentials:
          tokenUrl: https://my-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{organizationId}/oauth2/token
          scopes:
            c_my-scopes: description of my-scopes
    AmOAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://account.demandware.com/dwsso/oauth2/authorize
          tokenUrl: https://account.demandware.com/dwsso/oauth2/access_token
          scopes:
            c_my-scopes: description of my-scopes
        clientCredentials:
          tokenUrl: https://account.demandware.com/dwsso/oauth2/access_token
          scopes:
            c_my-scopes: description of my-scopes
```

## 4. Core Concept: The Three Pillars of a Custom API

Every Custom SCAPI Endpoint is built from three mandatory files, located within a dedicated cartridge directory: `your_cartridge/cartridge/rest-apis/{api-name}/`.

- **`schema.yaml` (The Contract)**: An OpenAPI 3.0 specification that defines the endpoint's URL path, HTTP method, parameters, security requirements, and response models. SCAPI uses this for automated request validation.

- **`script.js` (The Logic)**: A server-side B2C Commerce script (`dw.*` packages) that contains the business logic. It must export a public function with a name that exactly matches the `operationId` in the schema.

- **`api.json` (The Mapping)**: A simple JSON file that links the `operationId` from the schema to the correct implementation script.

## 5. Quick Start Example: A "Loyalty Info" Endpoint

Here is a complete example for a custom Shopper API endpoint `GET /custom/loyalty-api/v1/.../loyalty-info?c_customer_id={id}`.

### Directory Structure

```
int_loyalty_api/
└── cartridge/
    └── rest-apis/
        └── loyalty-api/
            ├── schema.yaml
            ├── loyalty.js
            └── api.json
```

### `schema.yaml`

```yaml
openapi: 3.0.0
info:
  title: Loyalty Information API
  version: "1.0.0" # Becomes /v1/ in the URL
paths:
  /loyalty-info:
    get:
      summary: Retrieves loyalty points for a customer.
      operationId: getLoyaltyInfo # Must match the exported function name
      parameters:
        - name: siteId # Required for Shopper APIs
          in: query
          required: true
          schema:
            type: string
            minLength: 1
        - name: c_customer_id # Custom parameters must be prefixed with 'c_'
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful retrieval of loyalty information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoyaltyInfo'
        '404':
          description: Customer not found.
      security:
        - ShopperToken: [c_read_loyalty] # Apply security scheme and custom scope
components:
  schemas:
    LoyaltyInfo:
      type: object
      properties:
        tier:
          type: string
        points:
          type: integer
  securitySchemes:
    ShopperToken: # Define the security scheme for Shopper APIs
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://my-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/my-org-id/oauth2/token
          scopes:
            c_read_loyalty: "Read access to loyalty data." # Define custom scope
```

### `loyalty.js`

```javascript
'use strict';

var RESTResponseMgr = require('dw/system/RESTResponseMgr');
var CustomerMgr = require('dw/customer/CustomerMgr');

/**
 * Implements the getLoyaltyInfo operationId.
 */
exports.getLoyaltyInfo = function () {
    var customerId = request.getHttpParameterMap().get('c_customer_id').getStringValue();
    var customer = CustomerMgr.getCustomerByCustomerNumber(customerId);

    // IMPORTANT: Add fine-grained authorization check here.
    // e.g., verify request.user.profile.customerNo === customerId

    if (customer) {
        var loyaltyData = {
            tier: 'Gold',
            points: 25800
        };
        RESTResponseMgr.createSuccess(loyaltyData).render();
    } else {
        RESTResponseMgr.createError(404, "customer-not-found", "Customer Not Found", "Customer ID is unknown.").render();
    }
};

// Function must be public to be exposed as an endpoint
exports.getLoyaltyInfo.public = true;
```

### `api.json`

```json
{
  "endpoints": [
    {
      "endpoint": "getLoyaltyInfo",
      "schema": "schema.yaml",
      "implementation": "loyalty"
    }
  ]
}
```

## 5.1 Working with Path Parameters

When your OpenAPI schema defines parameters with `in: path`, you must use the `getSCAPIPathParameters()` method to access them in your script implementation.

### Path Parameter Example

**schema.yaml with path parameter:**
```yaml
openapi: 3.0.0
info:
  title: Product Reviews API
  version: "1.0.0"
paths:
  /products/{productId}/reviews:
    get:
      summary: Get reviews for a specific product
      operationId: getProductReviews
      parameters:
        - name: productId
          in: path  # This is a path parameter
          required: true
          schema:
            type: string
        - name: siteId
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Product reviews retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Review'
components:
  schemas:
    Review:
      type: object
      properties:
        id:
          type: string
        rating:
          type: integer
        comment:
          type: string
```

**script.js accessing path parameter:**
```javascript
'use strict';

var RESTResponseMgr = require('dw/system/RESTResponseMgr');
var ProductMgr = require('dw/catalog/ProductMgr');

/**
 * Implements the getProductReviews operationId.
 */
exports.getProductReviews = function () {
    // Access path parameters using getSCAPIPathParameters()
    var productId = request.getSCAPIPathParameters().get('productId');
    
    // Access query parameters using getHttpParameterMap()
    var siteId = request.getHttpParameterMap().get('siteId').getStringValue();
    
    // Validate required path parameter
    if (!productId) {
        RESTResponseMgr.createError(400, "missing-product-id", 
            "Missing Product ID", "Product ID is required in the path").render();
        return;
    }
    
    var product = ProductMgr.getProduct(productId);
    if (!product) {
        RESTResponseMgr.createError(404, "product-not-found", 
            "Product Not Found", "Product with ID " + productId + " was not found").render();
        return;
    }
    
    // Business logic to fetch reviews
    var reviews = [
        {
            id: "review-1",
            rating: 5,
            comment: "Excellent product!"
        },
        {
            id: "review-2", 
            rating: 4,
            comment: "Good quality, fast shipping"
        }
    ];
    
    RESTResponseMgr.createSuccess(reviews).render();
};

exports.getProductReviews.public = true;
```

### Key Points for Path Parameters

1. **Path Parameter Access**: Always use `request.getSCAPIPathParameters().get('parameterName')` for parameters defined with `in: path` in your OpenAPI schema.

2. **Query Parameter Access**: Continue using `request.getHttpParameterMap().get('parameterName')` for parameters defined with `in: query`.

3. **Parameter Validation**: Path parameters are automatically validated by SCAPI based on your schema, but you should still add business logic validation in your script.

4. **URL Structure**: Path parameters become part of the URL structure. For example, `/products/{productId}/reviews` with `productId: "ABC123"` becomes `/products/ABC123/reviews`.

5. **Multiple Path Parameters**: You can have multiple path parameters in a single endpoint:
   ```yaml
   paths:
     /customers/{customerId}/orders/{orderId}:
       get:
         parameters:
           - name: customerId
             in: path
             required: true
           - name: orderId
             in: path
             required: true
   ```

   Access them individually:
   ```javascript
   var customerId = request.getSCAPIPathParameters().get('customerId');
   var orderId = request.getSCAPIPathParameters().get('orderId');
   ```


## 5.2 Caching Strategies for Custom Endpoints

### Platform Web-Tier Behavior

- Commerce Cloud's application layer performs web-tier caching for **GET** requests across SCAPI API families. Custom endpoints participate automatically once a request reaches the platform.
- Client-side caches (browser storage, SPA state, CDN/edge caches) continue to operate independently. Model your architecture with layered caches in mind so one layer's miss does not surprise you at runtime.

### Personalized Cache Variants

When personalization is enabled, the cache key is expanded with:

- The complete set of active promotions
- Active product sorting rules
- Applicable price books
- All active AB test groups

That means two shoppers requesting the same URL can receive different cache entries if they fall into different promotional contexts (for example, promotion X vs. promotion Y). This is valuable for targeted experiences but increases cache cardinality. Use personalization only for well-sized shopper cohorts and monitor cache pressure.

`response.setVaryBy('price_promotion')` is the only supported personalization flag. Product detail responses that expand prices or promotions—and product search responses with the `prices` expand—are already personalized by default.

### Programmatic Cache Controls

- `response.setExpires(Date.now() + ttlInMs)`: Sets the cache expiration timestamp. The TTL must be at least **1 second (1,000 ms)** and no more than **86,400 seconds (86,400,000 ms)**.
- `response.setVaryBy('price_promotion')`: Opts into price/promotion-aware personalization for the response variant.

```javascript
// Example snippet within your endpoint script
exports.getLoyaltyInfo = function (params) {
  var loyaltyData = { id: params.c_customer_id, points: 1234 };

  // Cache for five minutes and personalize by price/promotion eligibility when needed
  response.setExpires(Date.now() + 5 * 60 * 1000);
  // response.setVaryBy('price_promotion');

  RESTResponseMgr.createSuccess(loyaltyData).render();
};
exports.getLoyaltyInfo.public = true;
```

### Cache-Key Design Tips

- Introduce explicit query parameters (for example, `c_view=light`) when you need multiple cacheable variants from the same controller logic.
- Avoid combining expansions with drastically different TTLs. The response inherits the **shortest** TTL from the set of requested expands, so pairing a 24-hour asset expand with a 60-second availability expand collapses the lifespan to 60 seconds.
- Pair web-tier caching with application-tier caching (`CacheMgr.getCache().get(key, loader)`) to shield downstream services. See the Performance Best Practices guide for the full two-tier pattern.

## 6. Core Best Practices

### Design & Architecture

**Shopper vs. Admin APIs**: Choose the correct type for your use case. This choice is critical and dictates security, required parameters, and performance limits.

- **Shopper API**: For customer-facing applications. Requires `siteId` parameter. Secured by ShopperToken (SLAS). 10-second timeout.
- **Admin API**: For merchant tools. Must not have `siteId`. Secured by AmOAuth2 (Account Manager). 60-second timeout.

**BFF Pattern**: Create endpoints that aggregate data for specific UI components to reduce the number of client-side calls and improve performance.

### Security

- **Custom Scopes**: Every endpoint must be secured by exactly one custom scope defined in the `schema.yaml`. Scope names must start with `c_` (e.g., `c_read_loyalty`).
- **Client Permissions**: The API client in SLAS (for Shopper) or Account Manager (for Admin) must be granted permission to use your custom scope.
- **In-Script Authorization**: The platform validates the token and scope. You must validate that the authenticated user has permission to access the specific data they are requesting. This prevents privilege escalation attacks.

### Performance

- **Timeouts**: Respect the hard timeouts: 10 seconds for Shopper APIs and 60 seconds for Admin APIs.
- **Efficient Scripting**: Avoid expensive API calls (e.g., `ProductMgr.getProduct()`) inside loops. Fetch data in bulk where possible.
- **External Calls**: Use the Service Framework (`dw.svc.*`) for any third-party callouts. Set aggressive timeouts and enable the circuit breaker to prevent cascading failures.
- **Caching**: Use Custom Caches (`dw.system.CacheMgr`) for data that is expensive to compute but doesn't change often.

### Versioning

- The URL version (e.g., `/v1/`) is automatically derived from the major version number in your `schema.yaml`'s `info.version` field (e.g., "1.0.0" or "1.2.5" both map to v1).
- Introduce breaking changes (e.g., removing a field, changing a data type) in a new major version (e.g., 2.0.0 -> `/v2/`) to avoid disrupting existing clients.

### Troubleshooting

- **404 Not Found**: This almost always means the API failed to register. Systematically check your cartridge structure, file names, `operationId` matching, and `api.json` syntax.
- **403 Forbidden**: The client's token is valid but is missing the required custom scope. Check the scope assignments in SLAS or Account Manager.
- **504 Gateway Timeout**: Your script exceeded the performance limit. Use the Code Profiler to find and optimize the bottleneck in your code.


## 7. Custom APIs vs. Hooks

This is a critical architectural decision.

- **Use Hooks to...** modify or augment the behavior of an existing, out-of-the-box SCAPI endpoint. For example, adding a custom attribute to the standard `/baskets` response.

- **Use Custom APIs to...** create entirely new functionality that has no OOTB equivalent. For example, a store locator, a loyalty points service, or a newsletter signup endpoint.

> **Note**: Choosing the wrong tool leads to technical debt. Do not use hooks to create net-new functionality.

---

```

--------------------------------------------------------------------------------
/docs/dw_catalog/ProductVariationModel.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.catalog

# Class ProductVariationModel

## Inheritance Hierarchy

- Object
  - dw.catalog.ProductVariationModel

## Description

Class representing the complete variation information for a master product in the system. An instance of this class provides methods to access the following information: The variation attributes of the master product (e.g. size and color). Use getProductVariationAttributes(). The variation attribute values (e.g. size=Small, Medium, Large and color=Red, Blue). Use getAllValues(ProductVariationAttribute). The variation groups which may represent a subset of variants by defining a subset of the variation attribute values (e.g. color=Red, size=empty). Use getVariationGroups(). The variants themselves (e.g. the products representing Small Red, Large Red, Small Blue, Large Blue, etc). Use getVariants(). The variation attribute values of those variants. Use getVariationValue(Product, ProductVariationAttribute). This model only considers variants which are complete (i.e. the variant has a value for each variation attribute), and currently online. Incomplete or offline variants will not be returned by any method that returns Variants, and their attribute values will not be considered in any method that returns values. In addition to providing access to this meta information, ProductVariationModel maintains a collection of selected variation attribute values, representing the selections that a customer makes in the storefront. If this model was constructed for a master product, then none of the attributes will have pre-selected values. If this model was constructed for a variant product, then all the attribute values of that variant will be pre-selected. It is possible to query the current selections by calling getSelectedValue(ProductVariationAttribute) or isSelectedAttributeValue(ProductVariationAttribute, ProductVariationAttributeValue). The method setSelectedAttributeValue(String, String) can be used to modify the selected values. Depending on the product type, it's possible to select or modify variation attribute values: If this model was constructed for a master product, it's possible to select and modify all variation attributes. If this model was constructed for a variation group, it's possible to select and modify all variation attributes that are not defined by the variation group. If this model was constructed for a variation product, it's not possible to select or modify any of the variation attributes. Furthermore, the model provides helper methods to generate URLs for selecting and unselecting variation attribute values. See those methods for details. If this model was constructed for a product that is neither a master nor a variant, then none of the methods will return useful values at all. The methods in this class which access the currently selected variation attribute values can be used on product detail pages to render information about which combinations are available or unavailable. The methods getFilteredValues(ProductVariationAttribute) and hasOrderableVariants(ProductVariationAttribute, ProductVariationAttributeValue) can be used to determine this type of situation and render the appropriate message in the storefront. NOTE: Several methods in this class have a version taking a ProductVariationAttribute parameter, and another deprecated version accepting a ObjectAttributeDefinition parameter instead. The former should be strictly favored. The latter are historical leftovers from when object attributes were used directly as the basis for variation, and the value lists were stored directly on the ObjectAttributeDefinition. Every ProductVariationAttribute corresponds with exactly one ObjectAttributeDefinition, but values are now stored on the ProductVariationAttribute and not the ObjectAttributeDefinition.

## Properties

### attributeDefinitions

**Type:** Collection (Read Only)

The object attribute definitions corresponding with the product
 variation attributes of the master product.

### defaultVariant

**Type:** Variant (Read Only)

Return the default variant of this model's master product. If no default
 variant has been defined, return an arbitrary variant.

### master

**Type:** Product (Read Only)

The master of the product variation.

### productVariationAttributes

**Type:** Collection (Read Only)

A collection of product variation attributes of the variation.

### selectedVariant

**Type:** Variant (Read Only)

The variant currently selected for this variation model.
 Returns null if no variant is selected.

### selectedVariants

**Type:** Collection (Read Only)

The variants currently selected for this variation model.
 Returns an empty collection if no variant is selected.

### variants

**Type:** Collection (Read Only)

The collection of product variants of this variation model.
 This collection only includes online variants. Offline variants are
 filtered out. If all variation products are required, consider using
 Product.getVariants().

 The product variants are returned in no particular order.

### variationGroups

**Type:** Collection (Read Only)

The collection of variation groups of this variation model.
 This collection only includes online variation groups. Offline variation
 groups are filtered out. If all variation group products are required,
 consider using Product.getVariationGroups().

 The variation groups are returned in no particular order.

## Constructor Summary

## Method Summary

### getAllValues

**Signature:** `getAllValues(attribute : ObjectAttributeDefinition) : Collection`

Returns the value definitions for the specified attribute.

### getAllValues

**Signature:** `getAllValues(attribute : ProductVariationAttribute) : Collection`

Returns the values for the specified attribute.

### getAttributeDefinitions

**Signature:** `getAttributeDefinitions() : Collection`

Returns the object attribute definitions corresponding with the product variation attributes of the master product.

### getDefaultVariant

**Signature:** `getDefaultVariant() : Variant`

Return the default variant of this model's master product.

### getFilteredValues

**Signature:** `getFilteredValues(attribute : ObjectAttributeDefinition) : Collection`

Returns a collection of the value definitions defined for the specified attribute, filtered based on currently selected values.

### getFilteredValues

**Signature:** `getFilteredValues(attribute : ProductVariationAttribute) : Collection`

Returns a collection of the value definitions defined for the specified attribute, filtered based on currently selected values.

### getHtmlName

**Signature:** `getHtmlName(attribute : ObjectAttributeDefinition) : String`

Returns an HTML representation of the variation attribute id.

### getHtmlName

**Signature:** `getHtmlName(prefix : String, attribute : ObjectAttributeDefinition) : String`

Returns an HTML representation of the variation attribute id with the custom prefix.

### getHtmlName

**Signature:** `getHtmlName(attribute : ProductVariationAttribute) : String`

Returns an HTML representation of the product variation attribute id.

### getHtmlName

**Signature:** `getHtmlName(prefix : String, attribute : ProductVariationAttribute) : String`

Returns an HTML representation of the product variation attribute id with the custom prefix.

### getImage

**Signature:** `getImage(viewtype : String, attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : MediaFile`

The method returns the first image appropriate for the currently selected attribute values.

### getImage

**Signature:** `getImage(viewtype : String, index : Number) : MediaFile`

The method returns an image appropriate for the current selected variation values with the specific index.

### getImage

**Signature:** `getImage(viewtype : String) : MediaFile`

The method returns the first image appropriate for the current selected variation values with the specific index.

### getImages

**Signature:** `getImages(viewtype : String) : List`

The method returns the image appropriate for the currently selected attribute values.

### getMaster

**Signature:** `getMaster() : Product`

Returns the master of the product variation.

### getProductVariationAttribute

**Signature:** `getProductVariationAttribute(id : String) : ProductVariationAttribute`

Returns the product variation attribute for the specific id, or null if there is no product variation attribute for that id.

### getProductVariationAttributes

**Signature:** `getProductVariationAttributes() : Collection`

Returns a collection of product variation attributes of the variation.

### getSelectedValue

**Signature:** `getSelectedValue(attribute : ObjectAttributeDefinition) : ObjectAttributeValueDefinition`

Returns the selected value for the specified attribute.

### getSelectedValue

**Signature:** `getSelectedValue(attribute : ProductVariationAttribute) : ProductVariationAttributeValue`

Returns the selected value for the specified product variation attribute.

### getSelectedVariant

**Signature:** `getSelectedVariant() : Variant`

Returns the variant currently selected for this variation model.

### getSelectedVariants

**Signature:** `getSelectedVariants() : Collection`

Returns the variants currently selected for this variation model.

### getVariants

**Signature:** `getVariants() : Collection`

Returns the collection of product variants of this variation model.

### getVariants

**Signature:** `getVariants(filter : HashMap) : Collection`

Returns the variants that match the specified filter conditions.

### getVariationGroups

**Signature:** `getVariationGroups() : Collection`

Returns the collection of variation groups of this variation model.

### getVariationValue

**Signature:** `getVariationValue(variantOrVariationGroup : Product, attribute : ProductVariationAttribute) : ProductVariationAttributeValue`

Returns the value for the specified variant or variation group product and variation attribute.

### hasOrderableVariants

**Signature:** `hasOrderableVariants(attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : boolean`

Returns true if any variant is available with the specified value of the specified variation attribute.

### isSelectedAttributeValue

**Signature:** `isSelectedAttributeValue(attribute : ObjectAttributeDefinition, value : ObjectAttributeValueDefinition) : boolean`

Identifies if the specified variation value is the one currently selected.

### isSelectedAttributeValue

**Signature:** `isSelectedAttributeValue(attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : boolean`

Identifies if the specified product variation attribute value is the one currently selected.

### setSelectedAttributeValue

**Signature:** `setSelectedAttributeValue(variationAttributeID : String, variationAttributeValueID : String) : void`

Applies a selected attribute value to this model instance.

### url

**Signature:** `url(action : String, varAttrAndValues : Object...) : URL`

Constructs a URL to select a set of variation attribute values.

### urlSelectVariationValue

**Signature:** `urlSelectVariationValue(action : String, attribute : ObjectAttributeDefinition, value : ObjectAttributeValueDefinition) : String`

Constructs an URL to select the specified value of the specified variation attribute.

### urlSelectVariationValue

**Signature:** `urlSelectVariationValue(action : String, attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : String`

Generates a URL for selecting a value for a given variation attribute.

### urlUnselectVariationValue

**Signature:** `urlUnselectVariationValue(action : String, attribute : ObjectAttributeDefinition) : String`

Constructs an URL to unselect the value of the specified variation attribute.

### urlUnselectVariationValue

**Signature:** `urlUnselectVariationValue(action : String, attribute : ProductVariationAttribute) : String`

Generates a URL for unselecting a value for a given variation attribute.

## Method Detail

## Method Details

### getAllValues

**Signature:** `getAllValues(attribute : ObjectAttributeDefinition) : Collection`

**Description:** Returns the value definitions for the specified attribute. Only values that actually exist for at least one of the master product's currently online and complete variants are returned. Returns an empty collection if the passed attribute is not even a variation attribute of the master product.

**Deprecated:**

This method is deprecated since custom code should work with ProductVariationAttributes and not directly with their corresponding ObjectAttributeDefinitions. Use getAllValues(ProductVariationAttribute) to get the collection of ProductVariationAttributeValue instances instead.

**Parameters:**

- `attribute`: the attribute whose values will be returned.

**Returns:**

the sorted collection of ObjectAttributeValueDefinition instances representing the value definitions defined for the specified attribute. The collection is sorted by the explicit sort order defined for the values.

---

### getAllValues

**Signature:** `getAllValues(attribute : ProductVariationAttribute) : Collection`

**Description:** Returns the values for the specified attribute. Only values that actually exist for at least one of the master product's currently online and complete variants are returned. Returns an empty collection if the passed attribute is not even a variation attribute of the master product.

**Parameters:**

- `attribute`: the variation attribute whose values will be returned.

**Returns:**

the sorted collection of ProductVariationAttributeValue instances representing the values defined for the specified attribute. The collection is sorted by the explicit sort order defined for the values.

---

### getAttributeDefinitions

**Signature:** `getAttributeDefinitions() : Collection`

**Description:** Returns the object attribute definitions corresponding with the product variation attributes of the master product.

**Deprecated:**

This method is deprecated since custom code should work with ProductVariationAttributes and not directly with their corresponding ObjectAttributeDefinitions. Use getProductVariationAttributes() to get the product variation attributes.

**Returns:**

the collection of ObjectAttributeDefinition instances corresponding with the variation attributes of the master product, sorted by explicit position.

---

### getDefaultVariant

**Signature:** `getDefaultVariant() : Variant`

**Description:** Return the default variant of this model's master product. If no default variant has been defined, return an arbitrary variant.

**Returns:**

the default value of this model's master product, an arbitrary variant if no default is defined, or null if this model does not represent a master product with variants.

---

### getFilteredValues

**Signature:** `getFilteredValues(attribute : ObjectAttributeDefinition) : Collection`

**Description:** Returns a collection of the value definitions defined for the specified attribute, filtered based on currently selected values. A value is only returned if it at least one variant has the value and also possesses the selected values for all previous attributes. It is important to know that the filter is applied in a certain order. The method looks at all ObjectAttributeDefinition instances that appear before the passed one in the sorted collection returned by getAttributeDefinitions(). If the passed attribute is the first in this collection, then this method simply returns all its values. If an earlier attribute does not have a selected value, this method returns an empty list. Otherwise, the filter looks at all variants matching the selected value for all previous attributes, and considers the range of values possessed by these variants for the passed attribute. The idea behind this method is that customers in the storefront select variation attribute values one by one in the order the variation attributes are defined and displayed. After each selection, customer only wants to see values that he can possibly order for the remaining attributes. Returns an empty collection if the passed attribute is not even a variation attribute of the master product.

**Deprecated:**

Use getFilteredValues(ProductVariationAttribute) to get the sorted and calculated collection of product variation attribute values.

**Parameters:**

- `attribute`: the attribute whose values are returned by this method.

**Returns:**

a sorted collection of ObjectAttributeDefinitionValue instances calculated based on the currently selected variation values.

---

### getFilteredValues

**Signature:** `getFilteredValues(attribute : ProductVariationAttribute) : Collection`

**Description:** Returns a collection of the value definitions defined for the specified attribute, filtered based on currently selected values. A value is only returned if it at least one variant has the value and also possesses the selected values for all previous attributes. It is important to know that the filter is applied in a certain order. The method looks at all ProductVariationAttribute instances that appear before the passed one in the sorted collection returned by getProductVariationAttributes(). If the passed attribute is the first in this collection, then this method simply returns all its values. If an earlier attribute does not have a selected value, this method returns an empty list. Otherwise, the filter looks at all variants matching the selected value for all previous attributes, and considers the range of values possessed by these variants for the passed attribute. The idea behind this method is that customers in the storefront select variation attribute values one by one in the order the variation attributes are defined and displayed. After each selection, customer only wants to see values that he can possibly order for the remaining attributes. Returns an empty collection if the passed attribute is not even a variation attribute of the master product.

**Parameters:**

- `attribute`: the product variation attribute whose values are to be returned.

**Returns:**

a sorted and filtered collection of product variation attribute values. The collection is sorted by the explicit sort order defined for the values.

---

### getHtmlName

**Signature:** `getHtmlName(attribute : ObjectAttributeDefinition) : String`

**Description:** Returns an HTML representation of the variation attribute id. This method is deprecated. You should use getHtmlName(ProductVariationAttribute) instead.

**Deprecated:**

Use getHtmlName(ProductVariationAttribute) to get the HTML representation of the product variation attribute id.

**Parameters:**

- `attribute`: the attribute whose ID is returned.

**Returns:**

an HTML representation of the attribute id.

---

### getHtmlName

**Signature:** `getHtmlName(prefix : String, attribute : ObjectAttributeDefinition) : String`

**Description:** Returns an HTML representation of the variation attribute id with the custom prefix. This method is deprecated. You should use getHtmlName(String, ProductVariationAttribute) instead.

**Deprecated:**

Use getHtmlName(String, ProductVariationAttribute) to get the HTML representation of the product variation attribute id with the custom prefix.

**Parameters:**

- `prefix`: a custom prefix.
- `attribute`: the attribute whose ID is returned.

**Returns:**

an HTML representation of the attribute id.

---

### getHtmlName

**Signature:** `getHtmlName(attribute : ProductVariationAttribute) : String`

**Description:** Returns an HTML representation of the product variation attribute id.

**Parameters:**

- `attribute`: the product variation attribute whose ID is returned.

**Returns:**

an HTML representation of the product variation attribute id.

---

### getHtmlName

**Signature:** `getHtmlName(prefix : String, attribute : ProductVariationAttribute) : String`

**Description:** Returns an HTML representation of the product variation attribute id with the custom prefix.

**Parameters:**

- `prefix`: a custom prefix.
- `attribute`: the product variation attribute whose ID is returned.

**Returns:**

an HTML representation of the product variation attribute id.

---

### getImage

**Signature:** `getImage(viewtype : String, attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : MediaFile`

**Description:** The method returns the first image appropriate for the currently selected attribute values. The method first considers the most specific combination of attribute values (e.g "Red leather") and if that is not found, more general (e.g "Red"). If no image group is found for the attributes, returns null The view type parameter is required, otherwise a exception is thrown.

**Parameters:**

- `viewtype`: the view type annotated to image
- `attribute`: the variation attribute
- `value`: the the variation attribute value

**Returns:**

the first image, or null if not found

---

### getImage

**Signature:** `getImage(viewtype : String, index : Number) : MediaFile`

**Description:** The method returns an image appropriate for the current selected variation values with the specific index. If images are defined for this view type and variants, but not for specified index, the method returns null. If no images are defined for all variants and specified view type, the image at the specified index of the master product images is returned. If no master product image for specified index is defined, the method returns null. The view type parameter is required, otherwise a exception is thrown.

**Parameters:**

- `viewtype`: the view type annotated to image
- `index`: the index number of the image within image list

**Returns:**

the MediaFile or null

---

### getImage

**Signature:** `getImage(viewtype : String) : MediaFile`

**Description:** The method returns the first image appropriate for the current selected variation values with the specific index. If images are defined for this view type and variants, but not for specified index, the method returns null. If no images are defined for all variants and specified view type, the image at the specified index of the master product images is returned. If no master product image for specified index is defined, the method returns null. The view type parameter is required, otherwise a exception is thrown.

**Parameters:**

- `viewtype`: the view type annotated to image

**Returns:**

the MediaFile or null

---

### getImages

**Signature:** `getImages(viewtype : String) : List`

**Description:** The method returns the image appropriate for the currently selected attribute values. The method first considers the most specific combination of attribute values (e.g "Red leather") and if that is not found, more general (e.g "Red"). If no image group is found for the attributes, returns null The view type parameter is required, otherwise a exception is thrown.

**Parameters:**

- `viewtype`: the view type annotated to image

**Returns:**

an array of images

---

### getMaster

**Signature:** `getMaster() : Product`

**Description:** Returns the master of the product variation.

**Returns:**

the master of the product variation.

---

### getProductVariationAttribute

**Signature:** `getProductVariationAttribute(id : String) : ProductVariationAttribute`

**Description:** Returns the product variation attribute for the specific id, or null if there is no product variation attribute for that id.

**Parameters:**

- `id`: the id of the product variation attribute

**Returns:**

the product variation attribute, or null.

---

### getProductVariationAttributes

**Signature:** `getProductVariationAttributes() : Collection`

**Description:** Returns a collection of product variation attributes of the variation.

**Returns:**

a collection of product variation attributes of the variation.

---

### getSelectedValue

**Signature:** `getSelectedValue(attribute : ObjectAttributeDefinition) : ObjectAttributeValueDefinition`

**Description:** Returns the selected value for the specified attribute. If no value is selected, null is returned.

**Deprecated:**

Use getSelectedValue(ProductVariationAttribute) to get the selected product variation attribute value for the specified attribute.

**Parameters:**

- `attribute`: the attribute whose value will be returned.

**Returns:**

the selected value for the specified attribute or null.

---

### getSelectedValue

**Signature:** `getSelectedValue(attribute : ProductVariationAttribute) : ProductVariationAttributeValue`

**Description:** Returns the selected value for the specified product variation attribute. If no value is selected, null is returned.

**Parameters:**

- `attribute`: the product variation attribute whose value will be returned.

**Returns:**

the selected product variation attribute value for the specified attribute or null.

---

### getSelectedVariant

**Signature:** `getSelectedVariant() : Variant`

**Description:** Returns the variant currently selected for this variation model. Returns null if no variant is selected.

**Returns:**

selected variant or null.

---

### getSelectedVariants

**Signature:** `getSelectedVariants() : Collection`

**Description:** Returns the variants currently selected for this variation model. Returns an empty collection if no variant is selected.

**Returns:**

selected variants, might be empty if no valid variant was selected by the given attribute values

---

### getVariants

**Signature:** `getVariants() : Collection`

**Description:** Returns the collection of product variants of this variation model. This collection only includes online variants. Offline variants are filtered out. If all variation products are required, consider using Product.getVariants(). The product variants are returned in no particular order.

**Returns:**

a collection of all the product variants of the variation model.

---

### getVariants

**Signature:** `getVariants(filter : HashMap) : Collection`

**Description:** Returns the variants that match the specified filter conditions. The filter conditions are specified as a hash map of <attribute_id> - <value_id>. This method does not consider the currently selected attribute values.

**Parameters:**

- `filter`: the filters to apply when collecting the variants.

**Returns:**

the collection of variants that match the specified filter conditions.

---

### getVariationGroups

**Signature:** `getVariationGroups() : Collection`

**Description:** Returns the collection of variation groups of this variation model. This collection only includes online variation groups. Offline variation groups are filtered out. If all variation group products are required, consider using Product.getVariationGroups(). The variation groups are returned in no particular order.

**Returns:**

a collection of all the variation groups of the variation model.

---

### getVariationValue

**Signature:** `getVariationValue(variantOrVariationGroup : Product, attribute : ProductVariationAttribute) : ProductVariationAttributeValue`

**Description:** Returns the value for the specified variant or variation group product and variation attribute. The specified product should be a Variant returned by getVariants() or a VariationGroup returned by getVariationGroups(). The variation attribute should be one returned by getProductVariationAttributes(). If an invalid product or attribute is passed, null is returned. If null is passed for either argument, an exception is thrown.

**Parameters:**

- `variantOrVariationGroup`: the variant or variation group product to retrieve a value for, must not be null.
- `attribute`: the product variation attribute to get the value for, must not be null.

**Returns:**

the attribute value for the specified variant or variation group and attribute, or null if an invalid variant, variation group or attribute is passed or the variation group define no value for the variation attribute.

---

### hasOrderableVariants

**Signature:** `hasOrderableVariants(attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : boolean`

**Description:** Returns true if any variant is available with the specified value of the specified variation attribute. Available means that the variant is orderable according to the variant's availability model. This method takes currently selected attribute values into consideration. The specific rules are as follows: If no variation value is currently selected, the method returns true if any variant with the specified value is available, else false. if one or more variation values are selected, the method returns true if any variant with a combination of the specified value and the selected value is available, else false. if all variation values are selected, the method returns true of the variant that is represented by the current selection is available, else false.

**Parameters:**

- `attribute`: The product variation attribute whose values are to be tested for orderable variants.
- `value`: The specific attribute value to test for orderable variants.

**Returns:**

true if any variant is available with the specified value of the specified variation attribute based on the currently selected attribute values, false otherwise.

---

### isSelectedAttributeValue

**Signature:** `isSelectedAttributeValue(attribute : ObjectAttributeDefinition, value : ObjectAttributeValueDefinition) : boolean`

**Description:** Identifies if the specified variation value is the one currently selected.

**Deprecated:**

Use isSelectedAttributeValue(ProductVariationAttribute, ProductVariationAttributeValue) to identify if the specified product variation attribute value is the one currently selected.

**Parameters:**

- `attribute`: the attribute to check.
- `value`: the value to check for selection.

**Returns:**

true if the specified variation value is currently selected, false otherwise.

---

### isSelectedAttributeValue

**Signature:** `isSelectedAttributeValue(attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : boolean`

**Description:** Identifies if the specified product variation attribute value is the one currently selected.

**Parameters:**

- `attribute`: the attribute to check.
- `value`: the value to check for selection.

**Returns:**

true if the specified variation attribute value is currently selected, false otherwise.

---

### setSelectedAttributeValue

**Signature:** `setSelectedAttributeValue(variationAttributeID : String, variationAttributeValueID : String) : void`

**Description:** Applies a selected attribute value to this model instance. Usually this method is used to set the model state corresponding to the variation attribute values specified by a URL. The URLs can be obtained by using one of the models URL methods, like urlSelectVariationValue(String, ProductVariationAttribute, ProductVariationAttributeValue) and urlUnselectVariationValue(String, ProductVariationAttribute). Anyway, there are some limitations to keep in mind when selecting variation attribute values. A Variation Model created for a Variation Group or Variant Product is bound to an initial state. Example: A Variation Model created for Variation Group A can't be switched to Variation Group B. A Variation Model created for Variant A can't be switched to Variant B. The state of a Variation Model for a Variation Group that defines color = red can't be changed to color = black. The state of a Variation Model for a Variant that defines color = red / size = L can't be changed to color = black / size = S. However, the state of a Variation Model created for a Variation Group that defines color = red can be changed to a more specific state by adding another selected value, e.g. size = L. The state of a Variation Model created for a Variation Master can be changed in any possible way because the initial state involves all variation values and Variants.

**Parameters:**

- `variationAttributeID`: the ID of an product variation attribute, must not be null, otherwise a exception is thrown
- `variationAttributeValueID`: the ID of the product variation attribute value to apply, this value must not be part of the initial model state (e.g. the variant or group that the model has been created for), otherwise a exception is thrown

---

### url

**Signature:** `url(action : String, varAttrAndValues : Object...) : URL`

**Description:** Constructs a URL to select a set of variation attribute values. The optional varAttrAndValues argument can be empty, or can contain one or more variation attribute / value pairs. This variable list should be even in length, with attributes and values alternating. Attributes can be specified as instances of ProductVariationAttribute, or String variation attribute ID. (Note: ObjectAttributeDefinition IDs are not supported.) Values can be specified as instances of ProductVariationAttributeValue or String or Integer depending on the data type of the variation attribute. If a parameter type is invalid, or does not reference a valid ProductVariationAttribute or ProductVariationAttributeValue, then the parameter pair is not included in the generated URL. The returned URL will contain variation attributes and values already selected in the product variation model, as well as attributes and values specified as method parameters. Sample usage: master.variationModel.url("Product-Show", "color", "red", "size", "XL"); master.variationModel.url("Product-Show", colorVarAttr, colorValue, sizeVarAttr, sizeValue); // --> on/demandware.store/Sites-SiteGenesis-Site/default/Product-Show?pid=master_id&dwvar_color=red&dwvar_size=XL

**Parameters:**

- `action`: The pipeline action.
- `varAttrAndValues`: Variable length list of attributes and corresponding values to select.

**Returns:**

The constructed URL.

---

### urlSelectVariationValue

**Signature:** `urlSelectVariationValue(action : String, attribute : ObjectAttributeDefinition, value : ObjectAttributeValueDefinition) : String`

**Description:** Constructs an URL to select the specified value of the specified variation attribute. The generated URL will be an absolute URL which uses the protocol of the current request.

**Deprecated:**

Use urlSelectVariationValue(String, ProductVariationAttribute, ProductVariationAttributeValue) to construct an URL to select the specified product variation attribute value of the specified product variation attribute.

**Parameters:**

- `action`: the pipeline action, e.g. "Product-Show".
- `attribute`: the attribute to select a value for.
- `value`: the attribute definition value portion of the variation.

**Returns:**

the generated URL, an absolute URL which uses the protocol of the current request.

---

### urlSelectVariationValue

**Signature:** `urlSelectVariationValue(action : String, attribute : ProductVariationAttribute, value : ProductVariationAttributeValue) : String`

**Description:** Generates a URL for selecting a value for a given variation attribute. This URL is intended to be used on dynamic product detail pages. When a customer selects which value he wants for one of the variation attributes, the product detail page can issue a request to the passed URL which in turn can invoke the UpdateProductVariationSelections pipelet. That pipelet reads the querystring parameters and returns an updated variation model with the desired attribute value selected. The generated URL will be an absolute URL which uses the protocol of the current request.

**Parameters:**

- `action`: the pipeline action, e.g. "Product-Show".
- `attribute`: the product variation attribute to select a value for.
- `value`: the product variation attribute value to select.

**Returns:**

the generated URL, an absolute URL which uses the protocol of the current request.

---

### urlUnselectVariationValue

**Signature:** `urlUnselectVariationValue(action : String, attribute : ObjectAttributeDefinition) : String`

**Description:** Constructs an URL to unselect the value of the specified variation attribute. The generated URL will be an absolute URL which uses the protocol of the current request.

**Deprecated:**

Use urlUnselectVariationValue(String, ProductVariationAttribute) to unselect the product variation attribute value of the specified product variation attribute.

**Parameters:**

- `action`: the pipeline action, e.g. "Product-Show".
- `attribute`: the attribute to unselect.

**Returns:**

the generated URL, an absolute URL which uses the protocol of the current request.

---

### urlUnselectVariationValue

**Signature:** `urlUnselectVariationValue(action : String, attribute : ProductVariationAttribute) : String`

**Description:** Generates a URL for unselecting a value for a given variation attribute. This URL is intended to be used on dynamic product detail pages. When a customer deselects a value for one of the variation attributes, the product detail page can issue a request to the passed URL which in turn can invoke the UpdateProductVariationSelections pipelet. That pipelet reads the querystring parameters and returns an updated variation model with the desired attribute value unselected. The generated URL will be an absolute URL which uses the protocol of the current request.

**Parameters:**

- `action`: the pipeline action, e.g. "Product-Show".
- `attribute`: the product variation attribute to unselect.

**Returns:**

the generated URL, an absolute URL which uses the protocol of the current request.

---
```
Page 35/43FirstPrevNextLast