#
tokens: 49406/50000 19/825 files (page 12/43)
lines: off (toggle) GitHub
raw markdown copy
This is page 12 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=false&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

--------------------------------------------------------------------------------
/tests/best-practices-handler.test.ts:
--------------------------------------------------------------------------------

```typescript
import { BestPracticesToolHandler } from '../src/core/handlers/best-practices-handler.js';
import { HandlerContext } from '../src/core/handlers/base-handler.js';
import { Logger } from '../src/utils/logger.js';

// Mock the SFCCBestPracticesClient
const mockBestPracticesClient = {
  getAvailableGuides: jest.fn(),
  getBestPracticeGuide: jest.fn(),
  searchBestPractices: jest.fn(),
  getHookReference: jest.fn(),
};

jest.mock('../src/clients/best-practices-client.js', () => ({
  SFCCBestPracticesClient: jest.fn(() => mockBestPracticesClient),
}));

describe('BestPracticesToolHandler', () => {
  let mockLogger: jest.Mocked<Logger>;
  let mockClient: typeof mockBestPracticesClient;
  let context: HandlerContext;
  let handler: BestPracticesToolHandler;

  beforeEach(() => {
    mockLogger = {
      debug: jest.fn(),
      log: jest.fn(),
      error: jest.fn(),
      timing: jest.fn(),
      methodEntry: jest.fn(),
      methodExit: jest.fn(),
    } as any;

    // Reset mocks
    jest.clearAllMocks();

    // Use the mock client directly
    mockClient = mockBestPracticesClient;

    jest.spyOn(Logger, 'getChildLogger').mockReturnValue(mockLogger);

    context = {
      logger: mockLogger,
      config: null as any,
      capabilities: { canAccessLogs: false, canAccessOCAPI: false },
    };

    handler = new BestPracticesToolHandler(context, 'BestPractices');
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  // Helper function to initialize handler for tests that need it
  const initializeHandler = async () => {
    await (handler as any).initialize();
  };

  describe('canHandle', () => {
    it('should handle best practices tools', () => {
      expect(handler.canHandle('get_available_best_practice_guides')).toBe(true);
      expect(handler.canHandle('get_best_practice_guide')).toBe(true);
      expect(handler.canHandle('search_best_practices')).toBe(true);
      expect(handler.canHandle('get_hook_reference')).toBe(true);
    });

    it('should not handle non-best-practices tools', () => {
      expect(handler.canHandle('get_latest_error')).toBe(false);
      expect(handler.canHandle('unknown_tool')).toBe(false);
    });
  });

  describe('initialization', () => {
    it('should initialize best practices client', async () => {
      await initializeHandler();

      const MockedConstructor = jest.requireMock('../src/clients/best-practices-client.js').SFCCBestPracticesClient;
      expect(MockedConstructor).toHaveBeenCalled();
      expect(mockLogger.debug).toHaveBeenCalledWith('Best practices client initialized');
    });
  });

  describe('disposal', () => {
    it('should dispose best practices client properly', async () => {
      await initializeHandler();
      await (handler as any).dispose();

      expect(mockLogger.debug).toHaveBeenCalledWith('Best practices client disposed');
    });
  });

  describe('get_available_best_practice_guides tool', () => {
    beforeEach(async () => {
      await initializeHandler();
      mockClient.getAvailableGuides.mockResolvedValue([
        { name: 'cartridge_creation', title: 'Cartridge Creation', description: 'Best practices for cartridge creation' },
        { name: 'isml_templates', title: 'ISML Templates', description: 'Best practices for ISML templates' },
      ]);
    });

    it('should handle get_available_best_practice_guides', async () => {
      const result = await handler.handle('get_available_best_practice_guides', {}, Date.now());

      expect(mockClient.getAvailableGuides).toHaveBeenCalled();
      expect(result.content[0].text).toContain('cartridge_creation');
      expect(result.content[0].text).toContain('isml_templates');
    });
  });

  describe('get_best_practice_guide tool', () => {
    beforeEach(async () => {
      await initializeHandler();
      mockClient.getBestPracticeGuide.mockResolvedValue({
        title: 'Cartridge Creation Best Practices',
        description: 'Complete guide for creating custom cartridges',
        sections: ['Overview', 'Setup', 'Configuration'],
        content: 'Detailed content about cartridge creation...',
      });
    });

    it('should handle get_best_practice_guide with guideName', async () => {
      const args = { guideName: 'cartridge_creation' };
      const result = await handler.handle('get_best_practice_guide', args, Date.now());

      expect(mockClient.getBestPracticeGuide).toHaveBeenCalledWith('cartridge_creation');
      expect(result.content[0].text).toContain('Cartridge Creation Best Practices');
    });

    it('should throw error when guideName is missing', async () => {
      const result = await handler.handle('get_best_practice_guide', {}, Date.now());
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('guideName must be a non-empty string');
    });
  });

  describe('search_best_practices tool', () => {
    beforeEach(async () => {
      await initializeHandler();
      mockClient.searchBestPractices.mockResolvedValue([
        { guide: 'cartridge_creation', section: 'Setup', content: 'Cartridge setup instructions...' },
        { guide: 'security', section: 'Authentication', content: 'Security best practices...' },
      ]);
    });

    it('should handle search_best_practices with query', async () => {
      const args = { query: 'validation' };
      const result = await handler.handle('search_best_practices', args, Date.now());

      expect(mockClient.searchBestPractices).toHaveBeenCalledWith('validation');
      expect(result.content[0].text).toContain('cartridge_creation');
      expect(result.content[0].text).toContain('security');
    });

    it('should throw error when query is missing', async () => {
      const result = await handler.handle('search_best_practices', {}, Date.now());
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('query must be a non-empty string');
    });

    it('should throw error when query is empty', async () => {
      const result = await handler.handle('search_best_practices', { query: '' }, Date.now());
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('query must be a non-empty string');
    });
  });

  describe('get_hook_reference tool', () => {
    beforeEach(async () => {
      await initializeHandler();
      mockClient.getHookReference.mockResolvedValue({
        type: 'OCAPI Hooks',
        hooks: [
          { endpoint: 'customers.post', description: 'Customer creation hook' },
          { endpoint: 'orders.get', description: 'Order retrieval hook' },
        ],
      });
    });

    it('should handle get_hook_reference with guideName', async () => {
      const args = { guideName: 'ocapi_hooks' };
      const result = await handler.handle('get_hook_reference', args, Date.now());

      expect(mockClient.getHookReference).toHaveBeenCalledWith('ocapi_hooks');
      expect(result.content[0].text).toContain('OCAPI Hooks');
      expect(result.content[0].text).toContain('customers.post');
    });

    it('should throw error when guideName is missing', async () => {
      const result = await handler.handle('get_hook_reference', {}, Date.now());
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('guideName must be a non-empty string');
    });
  });

  describe('error handling', () => {
    beforeEach(async () => {
      await initializeHandler();
    });

    it('should handle client errors gracefully', async () => {
      mockClient.getBestPracticeGuide.mockRejectedValue(new Error('Guide not found'));

      const result = await handler.handle('get_best_practice_guide', { guideName: 'unknown_guide' }, Date.now());
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Guide not found');
    });

    it('should throw error for unsupported tools', async () => {
      await expect(handler.handle('unsupported_tool', {}, Date.now()))
        .rejects.toThrow('Unsupported tool');
    });
  });

  describe('timing and logging', () => {
    beforeEach(async () => {
      await initializeHandler();
      mockClient.getAvailableGuides.mockResolvedValue([]);
    });

    it('should log timing information', async () => {
      const startTime = Date.now();
      await handler.handle('get_available_best_practice_guides', {}, startTime);

      expect(mockLogger.timing).toHaveBeenCalledWith('get_available_best_practice_guides', startTime);
    });

    it('should log execution details', async () => {
      await handler.handle('get_available_best_practice_guides', {}, Date.now());

      expect(mockLogger.debug).toHaveBeenCalledWith(
        'get_available_best_practice_guides completed successfully',
        expect.any(Object),
      );
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/mock-data/ocapi/site-preferences-ccv.json:
--------------------------------------------------------------------------------

```json
{
  "_v": "23.2",
  "_type": "preference_value_search_result",
  "count": 5,
  "hits": [
    {
      "_type": "preference_value",
      "attribute_definition": {
        "_type": "object_attribute_definition",
        "_resource_state": "250db50d76a5e4bf869062a6ed5aab18d77aed015a880e4f0f5b8e86cc8e071d",
        "creation_date": "2025-03-06T07:14:22.000Z",
        "description": {
          "default": "Enable or disable CCV card authorization"
        },
        "display_name": {
          "default": "CCV Cards Authorise Enabled"
        },
        "effective_id": "c_ccvCardsAuthoriseEnabled",
        "externally_defined": false,
        "externally_managed": false,
        "id": "ccvCardsAuthoriseEnabled",
        "key": false,
        "last_modified": "2025-03-06T07:14:22.000Z",
        "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/ccvCardsAuthoriseEnabled",
        "localizable": false,
        "mandatory": false,
        "multi_value_type": false,
        "order_required": false,
        "queryable": false,
        "read_only": false,
        "requires_encoding": false,
        "searchable": false,
        "set_value_type": false,
        "site_specific": false,
        "system": false,
        "value_type": "boolean",
        "visible": true
      },
      "description": {
        "default": "Enable or disable CCV card authorization"
      },
      "display_name": {
        "default": "CCV Cards Authorise Enabled"
      },
      "id": "ccvCardsAuthoriseEnabled",
      "site_values": {
        "RefArch": true,
        "RefArchGlobal": true,
        "pxl_1": null,
        "pxl_2": null,
        "pxl_3": null,
        "pxl_4": null,
        "pxl_5": null,
        "pxl_6": null
      },
      "value_type": "boolean"
    },
    {
      "_type": "preference_value",
      "attribute_definition": {
        "_type": "object_attribute_definition",
        "_resource_state": "34dce900d8eb50d95d97fcc0fd74449e78720a0da5a6804e561f347d8fdb5227",
        "creation_date": "2025-03-06T07:14:22.000Z",
        "description": {
          "default": "Enable storing cards in vault for CCV"
        },
        "display_name": {
          "default": "CCV Store Cards In Vault Enabled"
        },
        "effective_id": "c_ccvStoreCardsInVaultEnabled",
        "externally_defined": false,
        "externally_managed": false,
        "id": "ccvStoreCardsInVaultEnabled",
        "key": false,
        "last_modified": "2025-03-06T07:14:22.000Z",
        "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/ccvStoreCardsInVaultEnabled",
        "localizable": false,
        "mandatory": false,
        "multi_value_type": false,
        "order_required": false,
        "queryable": false,
        "read_only": false,
        "requires_encoding": false,
        "searchable": false,
        "set_value_type": false,
        "site_specific": false,
        "system": false,
        "value_type": "boolean",
        "visible": true
      },
      "description": {
        "default": "Enable storing cards in vault for CCV"
      },
      "display_name": {
        "default": "CCV Store Cards In Vault Enabled"
      },
      "id": "ccvStoreCardsInVaultEnabled",
      "site_values": {
        "RefArch": false,
        "RefArchGlobal": true,
        "pxl_1": null,
        "pxl_2": null,
        "pxl_3": null,
        "pxl_4": null,
        "pxl_5": null,
        "pxl_6": null
      },
      "value_type": "boolean"
    },
    {
      "_type": "preference_value",
      "attribute_definition": {
        "_type": "object_attribute_definition",
        "_resource_state": "bbcd6304068fcf391c849bb37952598bebed1bd55bafbe211e2a6539b4b522a9",
        "creation_date": "2025-03-06T07:14:22.000Z",
        "description": {
          "default": "Enable automatic refunds for CCV payments"
        },
        "display_name": {
          "default": "CCV Auto Refund Enabled"
        },
        "effective_id": "c_ccvAutoRefundEnabled",
        "externally_defined": false,
        "externally_managed": false,
        "id": "ccvAutoRefundEnabled",
        "key": false,
        "last_modified": "2025-03-06T07:14:22.000Z",
        "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/ccvAutoRefundEnabled",
        "localizable": false,
        "mandatory": false,
        "multi_value_type": false,
        "order_required": false,
        "queryable": false,
        "read_only": false,
        "requires_encoding": false,
        "searchable": false,
        "set_value_type": false,
        "site_specific": false,
        "system": false,
        "value_type": "boolean",
        "visible": true
      },
      "description": {
        "default": "Enable automatic refunds for CCV payments"
      },
      "display_name": {
        "default": "CCV Auto Refund Enabled"
      },
      "id": "ccvAutoRefundEnabled",
      "site_values": {
        "RefArch": false,
        "RefArchGlobal": false,
        "pxl_1": null,
        "pxl_2": null,
        "pxl_3": null,
        "pxl_4": null,
        "pxl_5": null,
        "pxl_6": null
      },
      "value_type": "boolean"
    },
    {
      "_type": "preference_value",
      "attribute_definition": {
        "_type": "object_attribute_definition",
        "_resource_state": "efb6bd1843b9484576c8ffaf9447bc7d0bdee5bb6b0e9231d54804dbff5a11fa",
        "creation_date": "2025-03-06T07:14:22.000Z",
        "description": {
          "default": "Enable SCA ready processing for CCV"
        },
        "display_name": {
          "default": "CCV SCA Ready Enabled"
        },
        "effective_id": "c_ccvScaReadyEnabled",
        "externally_defined": false,
        "externally_managed": false,
        "id": "ccvScaReadyEnabled",
        "key": false,
        "last_modified": "2025-03-06T07:14:22.000Z",
        "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/ccvScaReadyEnabled",
        "localizable": false,
        "mandatory": false,
        "multi_value_type": false,
        "order_required": false,
        "queryable": false,
        "read_only": false,
        "requires_encoding": false,
        "searchable": false,
        "set_value_type": false,
        "site_specific": false,
        "system": false,
        "value_type": "boolean",
        "visible": true
      },
      "description": {
        "default": "Enable SCA ready processing for CCV"
      },
      "display_name": {
        "default": "CCV SCA Ready Enabled"
      },
      "id": "ccvScaReadyEnabled",
      "site_values": {
        "RefArch": true,
        "RefArchGlobal": true,
        "pxl_1": null,
        "pxl_2": null,
        "pxl_3": null,
        "pxl_4": null,
        "pxl_5": null,
        "pxl_6": null
      },
      "value_type": "boolean"
    },
    {
      "_type": "preference_value",
      "attribute_definition": {
        "_type": "object_attribute_definition",
        "_resource_state": "ed156d5f2de1fbbea7fa8350da7b2424feb7e54ac4e338d50fd6fec992df2c6f",
        "creation_date": "2025-03-06T07:14:22.000Z",
        "description": {
          "default": "Configuration for CCV 3DS exemption handling"
        },
        "display_name": {
          "default": "CCV 3DS Exemption"
        },
        "effective_id": "c_ccv3DSExemption",
        "externally_defined": false,
        "externally_managed": false,
        "id": "ccv3DSExemption",
        "key": false,
        "last_modified": "2025-03-06T07:14:22.000Z",
        "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/SitePreferences/attribute_definitions/ccv3DSExemption",
        "localizable": false,
        "mandatory": false,
        "multi_value_type": false,
        "order_required": false,
        "queryable": false,
        "read_only": false,
        "requires_encoding": false,
        "searchable": false,
        "set_value_type": false,
        "site_specific": false,
        "system": false,
        "value_type": "string",
        "visible": true
      },
      "description": {
        "default": "Configuration for CCV 3DS exemption handling"
      },
      "display_name": {
        "default": "CCV 3DS Exemption"
      },
      "id": "ccv3DSExemption",
      "site_values": {
        "RefArch": "none",
        "RefArchGlobal": "low_value",
        "pxl_1": null,
        "pxl_2": null,
        "pxl_3": null,
        "pxl_4": null,
        "pxl_5": null,
        "pxl_6": null
      },
      "value_type": "string"
    }
  ],
  "query": {
    "match_all_query": {
      "_type": "match_all_query"
    }
  },
  "select": "(**)",
  "start": 0,
  "total": 5
}

```

--------------------------------------------------------------------------------
/tests/base-http-client.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for BaseHttpClient
 * Tests the foundation HTTP client functionality
 */

import { BaseHttpClient } from '../src/clients/base/http-client.js';
import { Logger } from '../src/utils/logger.js';

// Mock fetch globally
global.fetch = jest.fn();

// Mock Logger
jest.mock('../src/utils/logger.js', () => ({
  Logger: {
    initialize: jest.fn(),
    getInstance: jest.fn(() => ({
      methodEntry: jest.fn(),
      methodExit: jest.fn(),
      debug: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      timing: jest.fn(),
      log: jest.fn(),
      info: jest.fn(),
    })),
    getChildLogger: jest.fn(() => ({
      methodEntry: jest.fn(),
      methodExit: jest.fn(),
      debug: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      timing: jest.fn(),
      log: jest.fn(),
      info: jest.fn(),
    })),
  },
}));

// Concrete implementation for testing abstract class
class TestHttpClient extends BaseHttpClient {
  private authHeaders: Record<string, string> = {};
  private shouldFailAuth = false;

  constructor(baseUrl: string = 'https://test-api.example.com') {
    super(baseUrl, 'TestHttpClient');
  }

  // Implementation of abstract method
  protected async getAuthHeaders(): Promise<Record<string, string>> {
    if (this.shouldFailAuth) {
      throw new Error('Auth failed');
    }
    return this.authHeaders;
  }

  // Test helpers
  setAuthHeaders(headers: Record<string, string>) {
    this.authHeaders = headers;
  }

  setAuthFailure(shouldFail: boolean) {
    this.shouldFailAuth = shouldFail;
  }

  // Expose protected methods for testing
  public async testMakeRequest<T>(endpoint: string, options?: any): Promise<T> {
    return this.makeRequest<T>(endpoint, options);
  }

  public async testGet<T>(endpoint: string): Promise<T> {
    return this.get<T>(endpoint);
  }

  public async testPost<T>(endpoint: string, data?: any): Promise<T> {
    return this.post<T>(endpoint, data);
  }

  public async testPut<T>(endpoint: string, data?: any): Promise<T> {
    return this.put<T>(endpoint, data);
  }

  public async testPatch<T>(endpoint: string, data?: any): Promise<T> {
    return this.patch<T>(endpoint, data);
  }

  public async testDelete<T>(endpoint: string): Promise<T> {
    return this.delete<T>(endpoint);
  }
}

describe('BaseHttpClient', () => {
  let client: TestHttpClient;
  let mockFetch: jest.MockedFunction<typeof fetch>;

  beforeEach(() => {
    jest.clearAllMocks();
    mockFetch = fetch as jest.MockedFunction<typeof fetch>;
    client = new TestHttpClient();
  });

  describe('constructor', () => {
    it('should initialize with base URL and logger context', () => {
      const customClient = new TestHttpClient('https://custom.api.com');
      expect(customClient).toBeInstanceOf(BaseHttpClient);
      expect(Logger.getChildLogger).toHaveBeenCalledWith('TestHttpClient');
    });
  });

  describe('makeRequest', () => {
    it('should make successful GET request with auth headers', async () => {
      const mockResponse = { data: 'test-data' };
      client.setAuthHeaders({ 'Authorization': 'Bearer token123' });

      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => mockResponse,
      } as Response);

      const result = await client.testMakeRequest('/test-endpoint');

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test-endpoint',
        {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer token123',
          },
        },
      );
      expect(result).toEqual(mockResponse);
    });

    it('should handle 401 errors with retry logic', async () => {
      const mockResponse = { data: 'success-after-retry' };
      client.setAuthHeaders({ 'Authorization': 'Bearer old-token' });

      // First call returns 401
      mockFetch
        .mockResolvedValueOnce({
          ok: false,
          status: 401,
        } as Response)
        .mockResolvedValueOnce({
          ok: true,
          json: async () => mockResponse,
        } as Response);

      const result = await client.testMakeRequest('/test-endpoint');

      expect(mockFetch).toHaveBeenCalledTimes(2);
      expect(result).toEqual(mockResponse);
    });

    it('should throw error for non-401 HTTP errors', async () => {
      mockFetch.mockResolvedValue({
        ok: false,
        status: 500,
        statusText: 'Internal Server Error',
        text: async () => 'Server error details',
      } as Response);

      await expect(client.testMakeRequest('/test-endpoint')).rejects.toThrow(
        'Request failed: 500 Internal Server Error - Server error details',
      );
    });

    it('should handle network errors', async () => {
      mockFetch.mockRejectedValue(new Error('Network error'));

      await expect(client.testMakeRequest('/test-endpoint')).rejects.toThrow(
        'Network error',
      );
    });

    it('should handle auth header failures', async () => {
      client.setAuthFailure(true);

      await expect(client.testMakeRequest('/test-endpoint')).rejects.toThrow(
        'Auth failed',
      );
    });

    it('should merge custom headers with auth headers', async () => {
      const mockResponse = { data: 'test' };
      client.setAuthHeaders({ 'Authorization': 'Bearer token' });

      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => mockResponse,
      } as Response);

      await client.testMakeRequest('/test', {
        headers: { 'Custom-Header': 'custom-value' },
      });

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test',
        {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer token',
            'Custom-Header': 'custom-value',
          },
        },
      );
    });
  });

  describe('HTTP method wrappers', () => {
    beforeEach(() => {
      client.setAuthHeaders({ 'Authorization': 'Bearer token' });
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ success: true }),
      } as Response);
    });

    it('should make GET request', async () => {
      await client.testGet('/test');

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test',
        expect.objectContaining({ method: 'GET' }), // GET method is explicitly set
      );
    });

    it('should make POST request with data', async () => {
      const postData = { name: 'test' };
      await client.testPost('/test', postData);

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test',
        expect.objectContaining({
          method: 'POST',
          body: JSON.stringify(postData),
        }),
      );
    });

    it('should make POST request without data', async () => {
      await client.testPost('/test');

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test',
        expect.objectContaining({
          method: 'POST',
        }),
      );
      expect(mockFetch.mock.calls[0][1]).not.toHaveProperty('body');
    });

    it('should make PUT request with data', async () => {
      const putData = { id: 1, name: 'updated' };
      await client.testPut('/test/1', putData);

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test/1',
        expect.objectContaining({
          method: 'PUT',
          body: JSON.stringify(putData),
        }),
      );
    });

    it('should make PATCH request with data', async () => {
      const patchData = { name: 'patched' };
      await client.testPatch('/test/1', patchData);

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test/1',
        expect.objectContaining({
          method: 'PATCH',
          body: JSON.stringify(patchData),
        }),
      );
    });

    it('should make DELETE request', async () => {
      await client.testDelete('/test/1');

      expect(mockFetch).toHaveBeenCalledWith(
        'https://test-api.example.com/test/1',
        expect.objectContaining({
          method: 'DELETE',
        }),
      );
    });
  });

  describe('error handling during retry', () => {
    it('should throw error if retry also fails', async () => {
      client.setAuthHeaders({ 'Authorization': 'Bearer token' });

      mockFetch
        .mockResolvedValueOnce({
          ok: false,
          status: 401,
        } as Response)
        .mockResolvedValueOnce({
          ok: false,
          status: 500,
          statusText: 'Internal Server Error',
          text: async () => 'Retry failed',
        } as Response);

      await expect(client.testMakeRequest('/test')).rejects.toThrow(
        'Request failed after retry: 500 Internal Server Error - Retry failed',
      );
    });
  });
});

```

--------------------------------------------------------------------------------
/docs/dw_web/PagingModel.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.web

# Class PagingModel

## Inheritance Hierarchy

- Object
  - dw.web.PagingModel

## Description

A page model is a helper class to apply a pages to a collection of elements or an iterator of elements and supports creating URLs for continued paging through the elements. The page model is intended to be initialized with the collection or iterator, than the paging position is applyed and than the elements are extracted with getPageElements(). In case the page model is initialized with a collection the page model can be reused multiple times.

## Constants

### DEFAULT_PAGE_SIZE

**Type:** Number = 10

The default page size.

### MAX_PAGE_SIZE

**Type:** Number = 2000

The maximum supported page size.

### PAGING_SIZE_PARAMETER

**Type:** String = "sz"

The URL Parameter used for the page size.

### PAGING_START_PARAMETER

**Type:** String = "start"

The URL parameter used for the start position.

## Properties

### count

**Type:** Number (Read Only)

The count of the number of items in the model.

### currentPage

**Type:** Number (Read Only)

The index number of the current page. The page
 counting starts with 0. The method also works with a miss-aligned
 start. In that case the start is always treated as the start of
 a page.

### empty

**Type:** boolean (Read Only)

Identifies if the model is empty.

### end

**Type:** Number (Read Only)

The index of the last element on the current page.

### maxPage

**Type:** Number (Read Only)

The maximum possible page number. Counting for pages starts
 with 0.  The method also works with a miss-aligned start. In that case
 the returned number might be higher than ((count-1) / pageSize).

### pageCount

**Type:** Number (Read Only)

The total page count. The method also works
 with a miss-aligned start. In that case the returned number might
 be higher than (count / pageSize).

### pageElements

**Type:** Iterator (Read Only)

An iterator that can be used to iterate through the elements of
 the current page.

 In case of a collection as the page models source, the method can be
 called multiple times. Each time a fresh iterator is returned.

 In case of an iterator as the page models source, the method must be
 called only once. The method will always return the same iterator,
 which means the method amy return an exhausted iterator.

### pageSize

**Type:** Number

The size of the page.

### start

**Type:** Number

The current start position from which iteration will start.

## Constructor Summary

PagingModel(elements : Iterator, count : Number) Constructs the PagingModel using the specified iterator and count value.

PagingModel(elements : Collection) Constructs the PagingModel using the specified collection.

## Method Summary

### appendPageSize

**Signature:** `static appendPageSize(url : URL, pageSize : Number) : URL`

Returns an URL containing the page size parameter appended to the specified url.

### appendPaging

**Signature:** `appendPaging(url : URL) : URL`

Returns an URL by appending the current page start position and the current page size to the URL.

### appendPaging

**Signature:** `appendPaging(url : URL, position : Number) : URL`

Returns an URL by appending the paging parameters for a desired page start position and the current page size to the specified url.

### getCount

**Signature:** `getCount() : Number`

Returns the count of the number of items in the model.

### getCurrentPage

**Signature:** `getCurrentPage() : Number`

Returns the index number of the current page.

### getEnd

**Signature:** `getEnd() : Number`

Returns the index of the last element on the current page.

### getMaxPage

**Signature:** `getMaxPage() : Number`

Returns the maximum possible page number.

### getPageCount

**Signature:** `getPageCount() : Number`

Returns the total page count.

### getPageElements

**Signature:** `getPageElements() : Iterator`

Returns an iterator that can be used to iterate through the elements of the current page.

### getPageSize

**Signature:** `getPageSize() : Number`

Returns the size of the page.

### getStart

**Signature:** `getStart() : Number`

Returns the current start position from which iteration will start.

### isEmpty

**Signature:** `isEmpty() : boolean`

Identifies if the model is empty.

### setPageSize

**Signature:** `setPageSize(pageSize : Number) : void`

Sets the size of the page.

### setStart

**Signature:** `setStart(start : Number) : void`

Sets the current start position from which iteration will start.

## Constructor Detail

## Method Detail

## Method Details

### appendPageSize

**Signature:** `static appendPageSize(url : URL, pageSize : Number) : URL`

**Description:** Returns an URL containing the page size parameter appended to the specified url. The name of the page size parameter is 'sz' (see PAGE_SIZE_PARAMETER). The start position parameter is not appended to the returned URL.

**Parameters:**

- `url`: the URL to append the page size parameter to.
- `pageSize`: the page size

**Returns:**

an URL that contains the page size parameter.

---

### appendPaging

**Signature:** `appendPaging(url : URL) : URL`

**Description:** Returns an URL by appending the current page start position and the current page size to the URL.

**Parameters:**

- `url`: the URL to append the current paging position to.

**Returns:**

an URL containing the current paging position.

---

### appendPaging

**Signature:** `appendPaging(url : URL, position : Number) : URL`

**Description:** Returns an URL by appending the paging parameters for a desired page start position and the current page size to the specified url. The name of the page start position parameter is 'start' (see PAGING_START_PARAMETER) and the page size parameter is 'sz' (see PAGE_SIZE_PARAMETER).

**Parameters:**

- `url`: the URL to append the paging parameter to.
- `position`: the start position.

**Returns:**

an URL that contains the paging parameters.

---

### getCount

**Signature:** `getCount() : Number`

**Description:** Returns the count of the number of items in the model.

**Returns:**

the count of the number of items in the model.

---

### getCurrentPage

**Signature:** `getCurrentPage() : Number`

**Description:** Returns the index number of the current page. The page counting starts with 0. The method also works with a miss-aligned start. In that case the start is always treated as the start of a page.

**Returns:**

the index number of the current page.

---

### getEnd

**Signature:** `getEnd() : Number`

**Description:** Returns the index of the last element on the current page.

**Returns:**

the index of the last element on the current page.

---

### getMaxPage

**Signature:** `getMaxPage() : Number`

**Description:** Returns the maximum possible page number. Counting for pages starts with 0. The method also works with a miss-aligned start. In that case the returned number might be higher than ((count-1) / pageSize).

**Returns:**

the maximum possible page number.

---

### getPageCount

**Signature:** `getPageCount() : Number`

**Description:** Returns the total page count. The method also works with a miss-aligned start. In that case the returned number might be higher than (count / pageSize).

**Returns:**

the total page count.

---

### getPageElements

**Signature:** `getPageElements() : Iterator`

**Description:** Returns an iterator that can be used to iterate through the elements of the current page. In case of a collection as the page models source, the method can be called multiple times. Each time a fresh iterator is returned. In case of an iterator as the page models source, the method must be called only once. The method will always return the same iterator, which means the method amy return an exhausted iterator.

**Returns:**

an iterator that you use to iterate through the elements of the current page.

---

### getPageSize

**Signature:** `getPageSize() : Number`

**Description:** Returns the size of the page.

**Returns:**

the size of the page.

---

### getStart

**Signature:** `getStart() : Number`

**Description:** Returns the current start position from which iteration will start.

**Returns:**

the current start position from which iteration will start.

---

### isEmpty

**Signature:** `isEmpty() : boolean`

**Description:** Identifies if the model is empty.

**Returns:**

true if the model is empty, false otherwise.

---

### setPageSize

**Signature:** `setPageSize(pageSize : Number) : void`

**Description:** Sets the size of the page. The page size must be greater or equal to 1.

**Parameters:**

- `pageSize`: the size of the page.

---

### setStart

**Signature:** `setStart(start : Number) : void`

**Description:** Sets the current start position from which iteration will start.

**Parameters:**

- `start`: the current start position from which iteration will start.

---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-hook-reference.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
description: "get_hook_reference docs-only tests"

# Chosen as next untested docs-only tool (no existing YAML test file). Covers success (ocapi_hooks, scapi_hooks), empty result (invalid guideName), structure validation, field extraction, and performance.

tests:
  - it: "should list tools include get_hook_reference"
    request:
      jsonrpc: "2.0"
      id: "list-hooks-tool"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "list-hooks-tool"
        result:
          tools: "match:type:array"
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_hook_reference"
      stderr: "toBeEmpty"

  - it: "should retrieve OCAPI hook reference with categories"
    request:
      jsonrpc: "2.0"
      id: "ocapi-hooks-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-hooks-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:Shop API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should retrieve SCAPI hook reference including signatures"
    request:
      jsonrpc: "2.0"
      id: "scapi-hooks-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-hooks-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:Shopper Baskets API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should return empty array content for invalid guideName without error"
    request:
      jsonrpc: "2.0"
      id: "invalid-hooks-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "invalid_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "invalid-hooks-1"
        result:
          content:
            - type: "text"
              text: "match:regex:^\\[\\s*\\]$"
          isError: false
      performance:
        maxResponseTime: "800ms"
      stderr: "toBeEmpty"

  - it: "should contain at least one hook endpoint pattern in OCAPI response"
    request:
      jsonrpc: "2.0"
      id: "ocapi-hooks-endpoint-pattern"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-hooks-endpoint-pattern"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:[\\s\\S]*GET /products/\\{id\\}[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should contain at least one SCAPI signature pattern in response"
    request:
      jsonrpc: "2.0"
      id: "scapi-hooks-signature-pattern"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-hooks-signature-pattern"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:modifyPOSTResponse(basket : dw.order.Basket"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should reject missing guideName with error flag and message"
    request:
      jsonrpc: "2.0"
      id: "missing-param-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "missing-param-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:guideName must be a non-empty string"
          isError: true
      performance:
        maxResponseTime: "600ms"
      stderr: "toBeEmpty"

  - it: "should reject empty guideName with same validation error"
    request:
      jsonrpc: "2.0"
      id: "empty-param-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: ""
    expect:
      response:
        jsonrpc: "2.0"
        id: "empty-param-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:guideName must be a non-empty string"
          isError: true
      performance:
        maxResponseTime: "600ms"
      stderr: "toBeEmpty"

  - it: "OCAPI response should include multiple categories"
    request:
      jsonrpc: "2.0"
      id: "ocapi-categories-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-categories-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:Data API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "SCAPI response should include multiple categories"
    request:
      jsonrpc: "2.0"
      id: "scapi-categories-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-categories-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:Shopper Orders API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "SCAPI response should contain multiple distinct hook signatures"
    request:
      jsonrpc: "2.0"
      id: "scapi-signatures-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-signatures-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:[\\s\\S]*beforePOST\\(basket : dw.order.Basket[\\s\\S]*afterPOST\\(basket : dw.order.Basket[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "1500ms"
      stderr: "toBeEmpty"

  - it: "OCAPI response should have multiple hookPoints for basket POST"
    request:
      jsonrpc: "2.0"
      id: "ocapi-multiple-hookpoints-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-multiple-hookpoints-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:[\\s\\S]*POST /baskets[\\s\\S]*modifyPOSTResponse[\\s\\S]*validateBasket[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "1500ms"
      stderr: "toBeEmpty"

  - it: "SCAPI response should NOT contain unrelated error text"
    request:
      jsonrpc: "2.0"
      id: "scapi-negative-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-negative-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:not:contains:guideName must be a non-empty string"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "SCAPI content payload should be reasonably large (length > 500 chars)"
    request:
      jsonrpc: "2.0"
      id: "scapi-size-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-size-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:^[\\s\\S]{500,}$"
          isError: false
      performance:
        maxResponseTime: "1500ms"
      stderr: "toBeEmpty"

```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-hook-reference.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
description: "get_hook_reference full-mode tests"

# Chosen as next untested full-mode tool (no existing YAML test file). Covers success (ocapi_hooks, scapi_hooks), empty result (invalid guideName), structure validation, field extraction, and performance.

tests:
  - it: "should list tools include get_hook_reference"
    request:
      jsonrpc: "2.0"
      id: "list-hooks-tool"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "list-hooks-tool"
        result:
          tools: "match:type:array"
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_hook_reference"
      stderr: "toBeEmpty"

  - it: "should retrieve OCAPI hook reference with categories"
    request:
      jsonrpc: "2.0"
      id: "ocapi-hooks-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-hooks-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:Shop API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should retrieve SCAPI hook reference including signatures"
    request:
      jsonrpc: "2.0"
      id: "scapi-hooks-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-hooks-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:Shopper Baskets API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should return empty array content for invalid guideName without error"
    request:
      jsonrpc: "2.0"
      id: "invalid-hooks-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "invalid_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "invalid-hooks-1"
        result:
          content:
            - type: "text"
              text: "match:regex:^\\[\\s*\\]$"
          isError: false
      performance:
        maxResponseTime: "800ms"
      stderr: "toBeEmpty"

  - it: "should contain at least one hook endpoint pattern in OCAPI response"
    request:
      jsonrpc: "2.0"
      id: "ocapi-hooks-endpoint-pattern"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-hooks-endpoint-pattern"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:[\\s\\S]*GET /products/\\{id\\}[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should contain at least one SCAPI signature pattern in response"
    request:
      jsonrpc: "2.0"
      id: "scapi-hooks-signature-pattern"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-hooks-signature-pattern"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:modifyPOSTResponse(basket : dw.order.Basket"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "should reject missing guideName with error flag and message"
    request:
      jsonrpc: "2.0"
      id: "missing-param-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "missing-param-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:guideName must be a non-empty string"
          isError: true
      performance:
        maxResponseTime: "600ms"
      stderr: "toBeEmpty"

  - it: "should reject empty guideName with same validation error"
    request:
      jsonrpc: "2.0"
      id: "empty-param-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: ""
    expect:
      response:
        jsonrpc: "2.0"
        id: "empty-param-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:guideName must be a non-empty string"
          isError: true
      performance:
        maxResponseTime: "600ms"
      stderr: "toBeEmpty"

  - it: "OCAPI response should include multiple categories"
    request:
      jsonrpc: "2.0"
      id: "ocapi-categories-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-categories-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:Data API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "SCAPI response should include multiple categories"
    request:
      jsonrpc: "2.0"
      id: "scapi-categories-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-categories-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:contains:Shopper Orders API Hooks"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "SCAPI response should contain multiple distinct hook signatures"
    request:
      jsonrpc: "2.0"
      id: "scapi-signatures-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-signatures-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:[\\s\\S]*beforePOST\\(basket : dw.order.Basket[\\s\\S]*afterPOST\\(basket : dw.order.Basket[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "1500ms"
      stderr: "toBeEmpty"

  - it: "OCAPI response should have multiple hookPoints for basket POST"
    request:
      jsonrpc: "2.0"
      id: "ocapi-multiple-hookpoints-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "ocapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "ocapi-multiple-hookpoints-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:[\\s\\S]*POST /baskets[\\s\\S]*modifyPOSTResponse[\\s\\S]*validateBasket[\\s\\S]*"
          isError: false
      performance:
        maxResponseTime: "1500ms"
      stderr: "toBeEmpty"

  - it: "SCAPI response should NOT contain unrelated error text"
    request:
      jsonrpc: "2.0"
      id: "scapi-negative-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-negative-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:not:contains:guideName must be a non-empty string"
          isError: false
      performance:
        maxResponseTime: "1200ms"
      stderr: "toBeEmpty"

  - it: "SCAPI content payload should be reasonably large (length > 500 chars)"
    request:
      jsonrpc: "2.0"
      id: "scapi-size-1"
      method: "tools/call"
      params:
        name: "get_hook_reference"
        arguments:
          guideName: "scapi_hooks"
    expect:
      response:
        jsonrpc: "2.0"
        id: "scapi-size-1"
        result:
          content:
            match:arrayElements:
              match:partial:
                text: "match:regex:^[\\s\\S]{500,}$"
          isError: false
      performance:
        maxResponseTime: "1500ms"
      stderr: "toBeEmpty"

```

--------------------------------------------------------------------------------
/docs/TopLevel/DataView.md:
--------------------------------------------------------------------------------

```markdown
## Package: TopLevel

# Class DataView

## Inheritance Hierarchy

- Object
  - DataView

## Description

The DataView provides low level access to ArrayBuffer.

## Properties

### buffer

**Type:** ArrayBuffer

The array buffer referenced by this view.

### byteLength

**Type:** Number

The number of bytes in the array buffer used by this view.

### byteOffset

**Type:** Number

The start offset for this view within the array buffer.

## Constructor Summary

DataView(buffer : ArrayBuffer, byteOffset : Number, byteLength : Number) Creates a data view on the given ArrayBuffer.

## Method Summary

### getFloat32

**Signature:** `getFloat32(byteOffset : Number, littleEndian : boolean) : Number`

Returns the 32-bit floating point number at the given offset.

### getFloat64

**Signature:** `getFloat64(byteOffset : Number, littleEndian : boolean) : Number`

Returns the 64-bit floating point number at the given offset.

### getInt16

**Signature:** `getInt16(byteOffset : Number, littleEndian : boolean) : Number`

Returns the 16-bit signed integer number at the given offset.

### getInt32

**Signature:** `getInt32(byteOffset : Number, littleEndian : boolean) : Number`

Returns the 32-bit signed integer number at the given offset.

### getInt8

**Signature:** `getInt8(byteOffset : Number) : Number`

Returns the 8-bit signed integer number at the given offset.

### getUint16

**Signature:** `getUint16(byteOffset : Number, littleEndian : boolean) : Number`

Returns the 16-bit unsigned integer number at the given offset.

### getUint32

**Signature:** `getUint32(byteOffset : Number, littleEndian : boolean) : Number`

Returns the 32-bit unsigned integer number at the given offset.

### getUint8

**Signature:** `getUint8(byteOffset : Number) : Number`

Returns the 8-bit unsigned integer number at the given offset.

### setFloat32

**Signature:** `setFloat32(byteOffset : Number, value : Number, littleEndian : boolean) : void`

Writes a 32-bit floating point number into the byte array at the given offset.

### setFloat64

**Signature:** `setFloat64(byteOffset : Number, value : Number, littleEndian : boolean) : void`

Writes a 64-bit floating point number into the byte array at the given offset.

### setInt16

**Signature:** `setInt16(byteOffset : Number, value : Number, littleEndian : boolean) : void`

Writes a 16-bit signed integer number into the byte array at the given offset.

### setInt32

**Signature:** `setInt32(byteOffset : Number, value : Number, littleEndian : boolean) : void`

Writes a 32-bit signed integer number into the byte array at the given offset.

### setInt8

**Signature:** `setInt8(byteOffset : Number, value : Number) : void`

Writes an 8-bit signed integer number into the byte array at the given offset.

### setUint16

**Signature:** `setUint16(byteOffset : Number, value : Number, littleEndian : boolean) : void`

Writes a 16-bit unsigned integer number into the byte array at the given offset.

### setUint32

**Signature:** `setUint32(byteOffset : Number, value : Number, littleEndian : boolean) : void`

Writes a 32-bit unsigned integer number into the byte array at the given offset.

### setUint8

**Signature:** `setUint8(byteOffset : Number, value : Number) : void`

Writes an 8-bit unsigned integer number into the byte array at the given offset.

## Constructor Detail

## Method Detail

## Method Details

### getFloat32

**Signature:** `getFloat32(byteOffset : Number, littleEndian : boolean) : Number`

**Description:** Returns the 32-bit floating point number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `littleEndian`: Optional. Default is false. Use true if the number is stored in little-endian format.

---

### getFloat64

**Signature:** `getFloat64(byteOffset : Number, littleEndian : boolean) : Number`

**Description:** Returns the 64-bit floating point number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `littleEndian`: Optional. Default is false. Use true if the number is stored in little-endian format.

---

### getInt16

**Signature:** `getInt16(byteOffset : Number, littleEndian : boolean) : Number`

**Description:** Returns the 16-bit signed integer number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `littleEndian`: Optional. Default is false. Use true if the number is stored in little-endian format.

---

### getInt32

**Signature:** `getInt32(byteOffset : Number, littleEndian : boolean) : Number`

**Description:** Returns the 32-bit signed integer number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `littleEndian`: Optional. Default is false. Use true if the number is stored in little-endian format.

---

### getInt8

**Signature:** `getInt8(byteOffset : Number) : Number`

**Description:** Returns the 8-bit signed integer number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.

---

### getUint16

**Signature:** `getUint16(byteOffset : Number, littleEndian : boolean) : Number`

**Description:** Returns the 16-bit unsigned integer number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `littleEndian`: Optional. Default is false. Use true if the number is stored in little-endian format.

---

### getUint32

**Signature:** `getUint32(byteOffset : Number, littleEndian : boolean) : Number`

**Description:** Returns the 32-bit unsigned integer number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `littleEndian`: Optional. Default is false. Use true if the number is stored in little-endian format.

---

### getUint8

**Signature:** `getUint8(byteOffset : Number) : Number`

**Description:** Returns the 8-bit unsigned integer number at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.

---

### setFloat32

**Signature:** `setFloat32(byteOffset : Number, value : Number, littleEndian : boolean) : void`

**Description:** Writes a 32-bit floating point number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.
- `littleEndian`: Optional. Default is false. Use true if the little-endian format is to be used.

---

### setFloat64

**Signature:** `setFloat64(byteOffset : Number, value : Number, littleEndian : boolean) : void`

**Description:** Writes a 64-bit floating point number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.
- `littleEndian`: Optional. Default is false. Use true if the little-endian format is to be used.

---

### setInt16

**Signature:** `setInt16(byteOffset : Number, value : Number, littleEndian : boolean) : void`

**Description:** Writes a 16-bit signed integer number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.
- `littleEndian`: Optional. Default is false. Use true if the little-endian format is to be used.

---

### setInt32

**Signature:** `setInt32(byteOffset : Number, value : Number, littleEndian : boolean) : void`

**Description:** Writes a 32-bit signed integer number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.
- `littleEndian`: Optional. Default is false. Use true if the little-endian format is to be used.

---

### setInt8

**Signature:** `setInt8(byteOffset : Number, value : Number) : void`

**Description:** Writes an 8-bit signed integer number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.

---

### setUint16

**Signature:** `setUint16(byteOffset : Number, value : Number, littleEndian : boolean) : void`

**Description:** Writes a 16-bit unsigned integer number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.
- `littleEndian`: Optional. Default is false. Use true if the little-endian format is to be used.

---

### setUint32

**Signature:** `setUint32(byteOffset : Number, value : Number, littleEndian : boolean) : void`

**Description:** Writes a 32-bit unsigned integer number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.
- `littleEndian`: Optional. Default is false. Use true if the little-endian format is to be used.

---

### setUint8

**Signature:** `setUint8(byteOffset : Number, value : Number) : void`

**Description:** Writes an 8-bit unsigned integer number into the byte array at the given offset.

**Parameters:**

- `byteOffset`: The offset within the view.
- `value`: The value to be written.

---
```

--------------------------------------------------------------------------------
/tests/client-factory.test.ts:
--------------------------------------------------------------------------------

```typescript
import { ClientFactory } from '../src/core/handlers/client-factory.js';
import { HandlerContext } from '../src/core/handlers/base-handler.js';
import { Logger } from '../src/utils/logger.js';
import { SFCCConfig } from '../src/types/types.js';

// Mock the clients
jest.mock('../src/clients/log-client.js');
jest.mock('../src/clients/ocapi-client.js');
jest.mock('../src/clients/ocapi/code-versions-client.js');
jest.mock('../src/clients/cartridge-generation-client.js');

describe('ClientFactory', () => {
  let mockLogger: jest.Mocked<Logger>;
  let factory: ClientFactory;

  beforeEach(() => {
    mockLogger = {
      debug: jest.fn(),
      info: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      timing: jest.fn(),
    } as any;
  });

  describe('createLogClient', () => {
    it('should create log client when capabilities and config are available', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: { hostname: 'test.com' } as SFCCConfig,
        capabilities: {
          canAccessLogs: true,
          canAccessOCAPI: false,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createLogClient();

      expect(client).toBeDefined();
      expect(mockLogger.debug).toHaveBeenCalledWith('Creating SFCC Log Client');
    });

    it('should return null when log access capability is missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: { hostname: 'test.com' } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: false,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createLogClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('Log client not created: missing log access capability or config');
    });

    it('should return null when config is missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: undefined as any,
        capabilities: {
          canAccessLogs: true,
          canAccessOCAPI: false,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createLogClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('Log client not created: missing log access capability or config');
    });
  });

  describe('createOCAPIClient', () => {
    it('should create OCAPI client when all credentials are available', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          hostname: 'test.com',
          clientId: 'client123',
          clientSecret: 'secret123',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: true,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createOCAPIClient();

      expect(client).toBeDefined();
      expect(mockLogger.debug).toHaveBeenCalledWith('Creating OCAPI Client');
    });

    it('should return null when OCAPI capability is missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          hostname: 'test.com',
          clientId: 'client123',
          clientSecret: 'secret123',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: false,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createOCAPIClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('OCAPI client not created: missing OCAPI credentials or capability');
    });

    it('should return null when hostname is missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          clientId: 'client123',
          clientSecret: 'secret123',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: true,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createOCAPIClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('OCAPI client not created: missing OCAPI credentials or capability');
    });

    it('should return null when clientId is missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          hostname: 'test.com',
          clientSecret: 'secret123',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: true,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createOCAPIClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('OCAPI client not created: missing OCAPI credentials or capability');
    });

    it('should return null when clientSecret is missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          hostname: 'test.com',
          clientId: 'client123',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: true,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createOCAPIClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('OCAPI client not created: missing OCAPI credentials or capability');
    });
  });

  describe('createCodeVersionsClient', () => {
    it('should create code versions client when all credentials are available', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          hostname: 'test.com',
          clientId: 'client123',
          clientSecret: 'secret123',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: true,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createCodeVersionsClient();

      expect(client).toBeDefined();
      expect(mockLogger.debug).toHaveBeenCalledWith('Creating OCAPI Code Versions Client');
    });

    it('should return null when credentials are missing', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {
          hostname: 'test.com',
        } as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: true,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createCodeVersionsClient();

      expect(client).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('Code versions client not created: missing OCAPI credentials or capability');
    });
  });

  describe('createCartridgeClient', () => {
    it('should create cartridge client with default services', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {} as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: false,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const client = factory.createCartridgeClient();

      expect(client).toBeDefined();
      expect(mockLogger.debug).toHaveBeenCalledWith('Creating Cartridge Generation Client');
    });

    it('should create cartridge client with custom services', () => {
      const context: HandlerContext = {
        logger: mockLogger,
        config: {} as SFCCConfig,
        capabilities: {
          canAccessLogs: false,
          canAccessOCAPI: false,
        },
      };
      factory = new ClientFactory(context, mockLogger);

      const mockFileSystem = { writeFile: jest.fn() } as any;
      const mockPath = { join: jest.fn() } as any;

      const client = factory.createCartridgeClient(mockFileSystem, mockPath);

      expect(client).toBeDefined();
      expect(mockLogger.debug).toHaveBeenCalledWith('Creating Cartridge Generation Client');
    });
  });

  describe('getClientRequiredError', () => {
    it('should return OCAPI error message', () => {
      const error = ClientFactory.getClientRequiredError('OCAPI');
      expect(error).toBe('OCAPI client not configured - ensure credentials are provided in full mode.');
    });

    it('should return Log error message', () => {
      const error = ClientFactory.getClientRequiredError('Log');
      expect(error).toBe('Log client not configured - ensure log access is enabled.');
    });

    it('should return default error message for unknown client type', () => {
      const error = ClientFactory.getClientRequiredError('Unknown' as any);
      expect(error).toBe('Required client not configured.');
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/mcp/node/get-hook-reference.docs-only.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Programmatic tests for get_hook_reference tool (docs-only mode)
 *
 * Response formats discovered via aegis query:
 *  Success (ocapi_hooks): { content:[{type:'text', text:'[ {"category": ... } ]'}], isError:false }
 *  Success (scapi_hooks): similar but includes signature fields in JSON text
 *  Invalid guideName: content text is "[]" (empty JSON array), isError:false
 *  Validation error (missing/empty guideName): { content:[{text:'Error: guideName must be a non-empty string'}], isError:true }
 *
 * This suite validates:
 *  - Tool presence in listTools
 *  - Successful retrieval for ocapi_hooks & scapi_hooks
 *  - Structural JSON parsing of categories & hooks
 *  - Presence of multiple hookPoints for basket endpoints
 *  - Presence of hook signatures in scapi_hooks
 *  - Validation errors for missing & empty guideName
 *  - Empty result handling for invalid guideName
 */

import { describe, test, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';


function parseHookReference(result) {
  assert.equal(result.isError, false, 'Expected non-error result');
  assert.ok(Array.isArray(result.content), 'content must be array');
  assert.ok(result.content.length > 0, 'content should have at least one item');
  const textItem = result.content.find(i => i.type === 'text');
  assert.ok(textItem, 'text content item required');
  const raw = textItem.text.trim();
  let data;
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  try { data = JSON.parse(raw); } catch (e) {
    throw new Error('Failed to JSON.parse hook reference payload: ' + raw.slice(0,200));
  }
  assert.ok(Array.isArray(data), 'Top-level parsed data must be array of categories');
  return { raw, data };
}

function findCategory(data, nameFragment) {
  return data.find(c => typeof c.category === 'string' && c.category.toLowerCase().includes(nameFragment.toLowerCase()));
}

function validateHookSchema(h) {
  assert.ok(typeof h.endpoint === 'string' && h.endpoint.length > 0, 'hook.endpoint must be non-empty string');
  assert.ok(Array.isArray(h.hookPoints), 'hook.hookPoints must be array');
  h.hookPoints.forEach(p => assert.ok(typeof p === 'string' && p.length > 0, 'hookPoint must be non-empty string'));
  if ('signature' in h) {
    assert.ok(typeof h.signature === 'string' && /\w+\(/.test(h.signature), 'signature should look like a function');
  }
}

describe('get_hook_reference.docs-only (programmatic)', () => {
  let client;

  before(async () => {
    client = await connect('./aegis.config.docs-only.json');
  });

  after(async () => {
    if (client?.connected) await client.disconnect();
  });

  beforeEach(() => {
    client.clearAllBuffers(); // Recommended - comprehensive protection
  });

  test('tool should be present in listTools', async () => {
    const tools = await client.listTools();
    const names = tools.map(t => t.name);
    assert.ok(names.includes('get_hook_reference'), 'get_hook_reference should be registered');
  });

  test('ocapi_hooks reference basic structure', async () => {
    const result = await client.callTool('get_hook_reference', { guideName: 'ocapi_hooks' });
    const { data, raw } = parseHookReference(result);
    assert.ok(data.length >= 2, 'Expect >=2 categories (Shop API Hooks, Data API Hooks)');

    const shopCat = findCategory(data, 'shop api');
    const dataCat = findCategory(data, 'data api');
    assert.ok(shopCat, 'Shop API Hooks category missing');
    assert.ok(dataCat, 'Data API Hooks category missing');

    // Validate hook objects minimally
    for (const cat of [shopCat, dataCat]) {
      assert.ok(Array.isArray(cat.hooks), 'category.hooks must be array');
      assert.ok(cat.hooks.length > 0, 'category.hooks should not be empty');
      const firstHook = cat.hooks[0];
      assert.ok(firstHook.endpoint, 'hook.endpoint required');
      assert.ok(Array.isArray(firstHook.hookPoints), 'hook.hookPoints must be array');
    }

    // Basket POST should have multiple hookPoints including modifyPOSTResponse or validateBasket
    const basketPost = shopCat.hooks.find(h => /POST \/baskets$/.test(h.endpoint));
    if (basketPost) {
      const points = basketPost.hookPoints.join(' ');
      assert.ok(/modifyPOSTResponse/.test(points) || /validateBasket/.test(points), 'Basket POST should expose modifyPOSTResponse or validateBasket');
    } else {
      // Non-fatal, but log for debugging
      console.warn('Basket POST endpoint not found in OCAPI shop hooks');
    }

    assert.ok(raw.length > 500, 'Raw OCAPI JSON text should exceed 500 chars for richness');
  });

  test('scapi_hooks reference includes signatures & structural integrity', async () => {
    const result = await client.callTool('get_hook_reference', { guideName: 'scapi_hooks' });
    const { data, raw } = parseHookReference(result);
    assert.ok(data.length >= 3, 'Expect >=3 categories for SCAPI');

    const basketCat = findCategory(data, 'baskets');
    const ordersCat = findCategory(data, 'orders');
    assert.ok(basketCat, 'Basket category missing');
    assert.ok(ordersCat, 'Orders category missing');

    // Validate full schema for first 3 hooks of basket category (or all if <3)
    basketCat.hooks.slice(0,3).forEach(validateHookSchema);

    // SCAPI version adds signature field per hook (at least some hooks)
    const signaturePresent = basketCat.hooks.some(h => 'signature' in h && /\w+\(.+\)/.test(h.signature));
    assert.ok(signaturePresent, 'Expected at least one signature field with function pattern in SCAPI hooks');

    // Validate at least one hook shows beforePOST/afterPOST pair
    const anyBefore = basketCat.hooks.some(h => h.hookPoints.some(p => /beforePOST/.test(p)));
    const anyAfter = basketCat.hooks.some(h => h.hookPoints.some(p => /afterPOST/.test(p)));
    assert.ok(anyBefore && anyAfter, 'Expect beforePOST and afterPOST patterns in SCAPI baskets');

    // Endpoint uniqueness across all categories
    const allEndpoints = data.flatMap(c => c.hooks.map(h => h.endpoint));
    const uniqueCount = new Set(allEndpoints).size;
    // Relaxed: just ensure we have a reasonable number of endpoints and log uniqueness ratio for diagnostic purposes
    assert.ok(allEndpoints.length > 10, 'Should expose more than 10 endpoints total');
    const uniquenessRatio = uniqueCount / allEndpoints.length;
    assert.ok(uniquenessRatio > 0.2, 'Uniqueness ratio should be >20% (diagnostic sanity check)');
    // console.debug(`Endpoint uniqueness ratio: ${(uniquenessRatio*100).toFixed(1)}%`);

    assert.ok(raw.length > 800, 'Raw SCAPI JSON text should exceed 800 chars for richness');
  });

  // Additional targeted signature regex test
  test('SCAPI signatures follow expected function pattern', async () => {
    const result = await client.callTool('get_hook_reference', { guideName: 'scapi_hooks' });
    const { data } = parseHookReference(result);
    const signatures = data.flatMap(c => c.hooks.filter(h => h.signature).map(h => h.signature));
    assert.ok(signatures.length > 5, 'Expect multiple signatures');
    // All signatures should have pattern name(args) : returnType
    const invalid = signatures.filter(sig => !/^\w+\([^)]*\)\s*:\s*\w+\.?\w*/.test(sig));
    assert.ok(invalid.length === 0, 'All signatures should match basic function signature pattern');
  });

  test('OCAPI hook objects basic schema validation', async () => {
    const result = await client.callTool('get_hook_reference', { guideName: 'ocapi_hooks' });
    const { data } = parseHookReference(result);
    data.forEach(cat => {
      cat.hooks.slice(0,5).forEach(h => {
        validateHookSchema(h);
        assert.ok(!('signature' in h) || typeof h.signature === 'string', 'signature optional but must be string if present');
      });
    });
  });

  test('invalid guideName returns empty array string (non-error)', async () => {
    const result = await client.callTool('get_hook_reference', { guideName: 'invalid_hooks' });
    assert.equal(result.isError, false, 'invalid guide should not set isError');
    const textItem = result.content.find(i => i.type === 'text');
    assert.ok(textItem, 'text item required');
    assert.equal(textItem.text.trim(), '[]', 'Expected empty JSON array payload');
  });

  test('missing guideName validation error', async () => {
    const result = await client.callTool('get_hook_reference', {}); // missing param
    assert.equal(result.isError, true, 'Should be error');
    const msg = result.content[0].text;
    assert.ok(/guideName must be a non-empty string/.test(msg), 'Validation message missing');
  });

  test('empty guideName validation error', async () => {
    const result = await client.callTool('get_hook_reference', { guideName: '' });
    assert.equal(result.isError, true, 'Should be error');
    const msg = result.content[0].text;
    assert.ok(/guideName must be a non-empty string/.test(msg), 'Validation message missing');
  });

});

```

--------------------------------------------------------------------------------
/docs/dw_extensions.paymentrequest/PaymentRequestHooks.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.extensions.paymentrequest

# Class PaymentRequestHooks

## Inheritance Hierarchy

- dw.extensions.paymentrequest.PaymentRequestHooks

## Description

PaymentRequestHooks interface containing extension points for customizing Payment Requests. These hooks are executed in a transaction. The extension points (hook names), and the functions that are called by each extension point. A function must be defined inside a JavaScript source and must be exported. The script with the exported hook function must be located inside a site cartridge. Inside the site cartridge a 'package.json' file with a 'hooks' entry must exist. "hooks": "./hooks.json" The hooks entry links to a json file, relative to the 'package.json' file. This file lists all registered hooks inside the hooks property: "hooks": [ {"name": "dw.extensions.paymentrequest.getPaymentRequest", "script": "./paymentrequest.ds"} {"name": "dw.extensions.paymentrequest.shippingAddressChange", "script": "./paymentrequest.ds"} ] A hook entry has a 'name' and a 'script' property. The 'name' contains the extension point, the hook name. The 'script' contains the script relative to the hooks file, with the exported hook function.

## Constants

## Properties

## Constructor Summary

## Method Summary

### abort

**Signature:** `abort(basket : Basket) : PaymentRequestHookResult`

Called after the Payment Request user interface was canceled.

### authorizeOrderPayment

**Signature:** `authorizeOrderPayment(order : Order, response : Object) : Status`

Called after the shopper accepts the Payment Request payment for the given order.

### getPaymentRequest

**Signature:** `getPaymentRequest(basket : Basket, parameters : Object) : PaymentRequestHookResult`

Called to get the PaymentRequest constructor parameters for the given basket.

### placeOrder

**Signature:** `placeOrder(order : Order) : PaymentRequestHookResult`

Called after payment has been authorized and the given Payment Request order is ready to be placed.

### shippingAddressChange

**Signature:** `shippingAddressChange(basket : Basket, details : Object) : PaymentRequestHookResult`

Called after handling the Payment Request shippingaddresschange event for the given basket.

### shippingOptionChange

**Signature:** `shippingOptionChange(basket : Basket, shippingMethod : ShippingMethod, details : Object) : PaymentRequestHookResult`

Called after handling the Payment Request shippingoptionchange event for the given basket.

## Method Detail

## Method Details

### abort

**Signature:** `abort(basket : Basket) : PaymentRequestHookResult`

**Description:** Called after the Payment Request user interface was canceled. The given basket is the one that was passed to other hooks earlier in the Payment Request checkout process. It is not guaranteed that this hook will be executed for all Payment Request user interfaces canceled by shoppers or otherwise ended without a successful order. Calls to this hook are provided on a best-effort basis. If the returned result includes a redirect URL, the shopper browser will be navigated to that URL if possible. It is not guaranteed that the response with the hook result will be handled in the shopper browser in all cases.

**Parameters:**

- `basket`: the basket that was being checked out using Payment Request

**Returns:**

a non-null result ends the hook execution

---

### authorizeOrderPayment

**Signature:** `authorizeOrderPayment(order : Order, response : Object) : Status`

**Description:** Called after the shopper accepts the Payment Request payment for the given order. Basket customer information, billing address, and/or shipping address for the default shipment will have already been updated to reflect the available contact information provided by Payment Request. Any preexisting payment instruments on the basket will have been removed, and a single DW_ANDROID_PAY payment instrument added for the total amount. The given order will have been created from this updated basket. The purpose of this hook is to authorize the Payment Request payment for the order. If a non-error status is returned that means that you have successfully authorized the payment with your payment service provider. Your hook implementation must set the necessary payment status and transaction identifier data on the order as returned by the provider. Return an error status to indicate a problem, including unsuccessful authorization. See the Payment Request API for more information.

**Parameters:**

- `order`: the order paid using Payment Request
- `response`: response to the accepted PaymentRequest

**Returns:**

a non-null status ends the hook execution

---

### getPaymentRequest

**Signature:** `getPaymentRequest(basket : Basket, parameters : Object) : PaymentRequestHookResult`

**Description:** Called to get the PaymentRequest constructor parameters for the given basket. You can set properties in the given parameters object to extend or override default properties set automatically based on the Google Pay configuration for your site. The parameters object will contain the following properties by default: methodData - array containing payment methods the web site accepts details - information about the transaction that the user is being asked to complete options - information about what options the web page wishes to use from the payment request system Return a result with an error status to indicate a problem. If the returned result includes a redirect URL, the shopper browser will be navigated to that URL if the Payment Request user interaction is canceled. See the Payment Request API for more information.

**Parameters:**

- `basket`: the basket for the Payment Request request
- `parameters`: object containing PaymentRequest constructor parameters

**Returns:**

a non-null result ends the hook execution

---

### placeOrder

**Signature:** `placeOrder(order : Order) : PaymentRequestHookResult`

**Description:** Called after payment has been authorized and the given Payment Request order is ready to be placed. The purpose of this hook is to place the order, or return a redirect URL that results in the order being placed when the shopper browser is navigated to it. The default implementation of this hook returns a redirect to COPlaceOrder-Submit with URL parameters order_id set to Order.getOrderNo() and order_token set to Order.getOrderToken() which corresponds to SiteGenesis-based implementations. Your hook implementation should return a result with a different redirect URL as necessary to place the order and show an order confirmation. Alternatively, your hook implementation itself can place the order and return a result with a redirect URL to an order confirmation page that does not place the order. This is inconsistent with SiteGenesis-based implementations so is not the default. Return an error status to indicate a problem. If the returned result includes a redirect URL, the shopper browser will be navigated to that URL if the Payment Request user interface is canceled.

**Parameters:**

- `order`: the order paid using PaymentRequest

**Returns:**

a non-null result ends the hook execution

---

### shippingAddressChange

**Signature:** `shippingAddressChange(basket : Basket, details : Object) : PaymentRequestHookResult`

**Description:** Called after handling the Payment Request shippingaddresschange event for the given basket. Basket customer information and/or shipping address for the default shipment will have already been updated to reflect the available shipping address information provided by Payment Request. The basket will have already been calculated before this hook is called. Return a result with an error status to indicate a problem. If the returned result includes a redirect URL, the shopper browser will be navigated to that URL if the Payment Request user interface is canceled. See the Payment Request API for more information.

**Parameters:**

- `basket`: the basket being checked out using Payment Request
- `details`: updated PaymentRequest object details

**Returns:**

a non-null result ends the hook execution

---

### shippingOptionChange

**Signature:** `shippingOptionChange(basket : Basket, shippingMethod : ShippingMethod, details : Object) : PaymentRequestHookResult`

**Description:** Called after handling the Payment Request shippingoptionchange event for the given basket. The given shipping method will have already been set on the basket. The basket will have already been calculated before this hook is called. Return a result with an error status to indicate a problem. If the returned result includes a redirect URL, the shopper browser will be navigated to that URL if the Payment Request user interface is canceled. See the Payment Request API for more information.

**Parameters:**

- `basket`: the basket being checked out using Payment Request
- `shippingMethod`: the shipping method that was selected
- `details`: updated PaymentRequest object details

**Returns:**

a non-null result ends the hook execution

---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/search-system-object-attribute-groups.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
# ==================================================================================
# SFCC MCP Server - search_system_object_attribute_groups Tool YAML Tests (Docs-Only Mode)
# Tests that system object attribute group tools are NOT available in docs-only mode
# This tool requires SFCC credentials and should not be available without them
# However, the tool can still be called and should return authentication error
# 
# Quick Test Commands:
# aegis "tests/mcp/yaml/search-system-object-attribute-groups.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose
# aegis "tests/mcp/yaml/search-system-object-attribute-groups.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --debug --timing
# aegis query --config "aegis.config.docs-only.json"
# aegis query search_system_object_attribute_groups '{"objectType": "Product", "searchRequest": {"query": {"match_all_query": {}}, "count": 3}}' --config "aegis.config.docs-only.json"
# ==================================================================================

description: "search_system_object_attribute_groups tool tests - Docs-only mode tool availability and authentication errors"

# ==================================================================================
# TOOL UNAVAILABILITY IN DOCS-ONLY MODE
# ==================================================================================
tests:
  - it: "should NOT list search_system_object_attribute_groups tool in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "tool-not-available-docs"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "tool-not-available-docs"
        result:
          match:extractField: "tools.*.name"
          value: "match:not:arrayContains:search_system_object_attribute_groups"
      stderr: "toBeEmpty"

# ==================================================================================
# AUTHENTICATION ERROR TESTS (Tool Can Be Called But Returns Error)
# ==================================================================================

  - it: "should return authentication error when calling search_system_object_attribute_groups in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "auth-error-product"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: "Product"
          searchRequest:
            query:
              match_all_query: {}
            count: 3
    expect:
      response:
        jsonrpc: "2.0"
        id: "auth-error-product"
        result:
          content:
            - type: "text"
              text: "match:contains:OCAPI client not configured"
          isError: true
      performance:
        maxResponseTime: "500ms"
      stderr: "toBeEmpty"

  - it: "should return authentication error for SitePreferences object type"
    request:
      jsonrpc: "2.0"
      id: "auth-error-siteprefs"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: "SitePreferences"
          searchRequest:
            query:
              match_all_query: {}
            count: 2
    expect:
      response:
        jsonrpc: "2.0"
        id: "auth-error-siteprefs"
        result:
          content:
            - type: "text"
              text: "match:contains:credentials are provided"
          isError: true
      stderr: "toBeEmpty"

  - it: "should return authentication error for Customer object type"
    request:
      jsonrpc: "2.0"
      id: "auth-error-customer"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: "Customer"
          searchRequest:
            query:
              text_query:
                fields: ["id"]
                search_phrase: "PersonalInfo"
            count: 5
    expect:
      response:
        jsonrpc: "2.0"
        id: "auth-error-customer"
        result:
          content:
            - type: "text"
              text: "match:contains:full mode"
          isError: true
      stderr: "toBeEmpty"

  - it: "should return authentication error for any object type with complex query"
    request:
      jsonrpc: "2.0"
      id: "auth-error-complex"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: "Order"
          searchRequest:
            query:
              bool_query:
                must:
                  - text_query:
                      fields: ["id", "display_name"]
                      search_phrase: "Billing"
            sorts:
              - field: "id"
                sort_order: "asc"
            count: 3
    expect:
      response:
        jsonrpc: "2.0"
        id: "auth-error-complex"
        result:
          content:
            - type: "text"
              text: "match:contains:OCAPI client not configured"
          isError: true
      stderr: "toBeEmpty"

  # ==================================================================================
  # BEHAVIOR VALIDATION IN DOCS-ONLY MODE
  # ==================================================================================

  - it: "should return authentication error even for missing objectType parameter in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "validation-missing-object-type"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          searchRequest:
            query:
              match_all_query: {}
            count: 3
    expect:
      response:
        jsonrpc: "2.0"
        id: "validation-missing-object-type"
        result:
          content:
            - type: "text"
              text: "match:contains:OCAPI client not configured"
          isError: true
      performance:
        maxResponseTime: "500ms"
      stderr: "toBeEmpty"

  - it: "should return authentication error even for empty objectType parameter in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "validation-empty-object-type"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: ""
          searchRequest:
            query:
              match_all_query: {}
            count: 2
    expect:
      response:
        jsonrpc: "2.0"
        id: "validation-empty-object-type"
        result:
          content:
            - type: "text"
              text: "match:contains:credentials are provided"
          isError: true
      performance:
        maxResponseTime: "500ms"
      stderr: "toBeEmpty"

  - it: "should return authentication error even for missing searchRequest parameter in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "validation-missing-search-request"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: "Product"
    expect:
      response:
        jsonrpc: "2.0"
        id: "validation-missing-search-request"
        result:
          content:
            - type: "text"
              text: "match:contains:full mode"
          isError: true
      performance:
        maxResponseTime: "500ms"
      stderr: "toBeEmpty"

  # ==================================================================================
  # PERFORMANCE VALIDATION IN DOCS-ONLY MODE
  # ==================================================================================

  - it: "should return authentication errors quickly in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "performance-auth-error"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          objectType: "Product"
          searchRequest:
            query:
              match_all_query: {}
            count: 10
    expect:
      response:
        jsonrpc: "2.0"
        id: "performance-auth-error"
        result:
          content:
            - type: "text"
              text: "match:contains:OCAPI client not configured"
          isError: true
      performance:
        maxResponseTime: "400ms"
      stderr: "toBeEmpty"

  - it: "should handle authentication errors quickly even with missing parameters in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "performance-validation"
      method: "tools/call"
      params:
        name: "search_system_object_attribute_groups"
        arguments:
          searchRequest:
            query:
              text_query:
                fields: ["id"]
                search_phrase: "test"
            count: 5
    expect:
      response:
        jsonrpc: "2.0"
        id: "performance-validation"
        result:
          content:
            - type: "text"
              text: "match:contains:OCAPI client not configured"
          isError: true
      performance:
        maxResponseTime: "300ms"
      stderr: "toBeEmpty"
```

--------------------------------------------------------------------------------
/docs-site/components/Search.tsx:
--------------------------------------------------------------------------------

```typescript
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { searchDocs, SearchResult } from '../utils/search';
import { SearchIcon } from './icons';

const Highlight: React.FC<{ text: string; query: string }> = ({ text, query }) => {
    if (!query) return <>{text}</>;
    const parts = text.split(new RegExp(`(${query})`, 'gi'));
    return (
        <>
            {parts.map((part, i) =>
                part.toLowerCase() === query.toLowerCase() ? (
                    <mark key={i} className="bg-orange-200 text-orange-800 font-semibold rounded px-0.5">
                        {part}
                    </mark>
                ) : (
                    part
                )
            )}
        </>
    );
};


const Search: React.FC = () => {
    const [query, setQuery] = useState('');
    const [results, setResults] = useState<SearchResult[]>([]);
    const [isOpen, setIsOpen] = useState(false);
    const [activeIndex, setActiveIndex] = useState(-1);
    const inputRef = useRef<HTMLInputElement>(null);
    const resultsRef = useRef<HTMLUListElement>(null);
    const navigate = useNavigate();
    const location = useLocation();

    const openSearch = useCallback(() => {
        setIsOpen(true);
    }, []);

    const closeSearch = useCallback(() => {
        setIsOpen(false);
        setQuery('');
        setResults([]);
        setActiveIndex(-1);
    }, []);

    useEffect(() => {
        // Only run on client side
        if (typeof window === 'undefined') return;
        
        const handleKeyDown = (e: KeyboardEvent) => {
            if (e.metaKey && e.key === 'k') {
                e.preventDefault();
                if (isOpen) {
                    closeSearch();
                } else {
                    openSearch();
                }
            }

            if (isOpen) {
                if (e.key === 'Escape') {
                    closeSearch();
                } else if (e.key === 'ArrowDown') {
                    e.preventDefault();
                    setActiveIndex(prev => prev < results.length - 1 ? prev + 1 : prev);
                } else if (e.key === 'ArrowUp') {
                    e.preventDefault();
                    setActiveIndex(prev => prev > 0 ? prev - 1 : prev);
                } else if (e.key === 'Enter' && activeIndex >= 0) {
                    const result = results[activeIndex];
                    handleNavigation(result.path, result.heading, result.headingId);
                }
            }
        };

        window.addEventListener('keydown', handleKeyDown);
        return () => window.removeEventListener('keydown', handleKeyDown);
    }, [isOpen, results.length, activeIndex, openSearch, closeSearch]);

    useEffect(() => {
        if (isOpen && inputRef.current) {
            inputRef.current.focus();
        }
    }, [isOpen]);

    useEffect(() => {
      closeSearch();
    }, [location.pathname, closeSearch]);

    useEffect(() => {
        if (activeIndex >= 0 && resultsRef.current) {
            const activeElement = resultsRef.current.children[activeIndex] as HTMLLIElement;
            if (activeElement) {
                activeElement.scrollIntoView({ block: 'nearest' });
            }
        }
    }, [activeIndex]);

    const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
        const newQuery = e.target.value;
        setQuery(newQuery);
        if (newQuery.length > 1) {
            setResults(searchDocs(newQuery));
        } else {
            setResults([]);
        }
        setActiveIndex(0);
    };
    
    const handleNavigation = (path: string, heading?: string, headingId?: string) => {
        // Use the actual headingId if available, otherwise generate one from the heading
        let targetPath = path;
        let hashFragment = '';
        
        if (headingId) {
            hashFragment = headingId;
        } else if (heading && heading !== 'Introduction' && heading !== path.split('/').pop()) {
            // Convert heading to a URL-safe ID as fallback
            const generatedId = heading
                .toLowerCase()
                .replace(/[^a-z0-9\s-]/g, '') // Remove special characters except spaces and hyphens
                .replace(/\s+/g, '-') // Replace spaces with hyphens
                .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
                .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
            
            if (generatedId) {
                hashFragment = generatedId;
            }
        }
        
        // Navigate to the path first, then handle hash navigation
        if (hashFragment) {
              navigate(targetPath + '#' + hashFragment);
        } else {
            navigate(targetPath);
        }
        
        closeSearch();
    };

    return (
        <>
            <div className="relative">
                <SearchIcon className="absolute top-1/2 left-3 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
                <button
                    type="button"
                    onClick={openSearch}
                    className="w-full bg-slate-100 border border-slate-200 rounded-lg py-2 pl-9 pr-12 sm:pr-3 text-sm text-left text-slate-500 hover:border-slate-300 transition-colors focus:outline-none focus:ring-2 focus:ring-orange-400"
                >
                    Search...
                </button>
                <div className="absolute top-1/2 right-3 -translate-y-1/2 text-xs text-slate-400 border border-slate-300 rounded-md px-1.5 py-0.5 pointer-events-none hidden sm:block">
                    ⌘K
                </div>
            </div>

            {isOpen && (
                 <div className="fixed inset-0 z-50 flex justify-center items-start pt-4 sm:pt-20 p-4" aria-modal="true">
                    <div className="fixed inset-0 bg-slate-900/50 backdrop-blur-sm" onClick={closeSearch}></div>
                    <div className="relative bg-white w-full max-w-2xl rounded-lg shadow-lg max-h-[90vh] flex flex-col">
                        <div className="relative flex-shrink-0">
                            <SearchIcon className="absolute top-1/2 left-4 -translate-y-1/2 w-5 h-5 text-slate-400" />
                            <input
                                ref={inputRef}
                                type="text"
                                value={query}
                                onChange={handleSearch}
                                placeholder="Search documentation..."
                                className="w-full text-base sm:text-lg py-3 sm:py-4 pl-12 pr-4 border-b border-slate-200 focus:outline-none"
                            />
                        </div>
                        {query.length > 1 && (
                             <div className="flex-1 overflow-y-auto min-h-0">
                                {results.length > 0 ? (
                                    <ul ref={resultsRef} className="p-3 sm:p-4 space-y-2">
                                        {results.map((result, index) => (
                                            <li key={`${result.path}-${result.heading}`}>
                                                <button
                                                    onClick={() => handleNavigation(result.path, result.heading, result.headingId)}
                                                    className={`w-full text-left p-3 rounded-md transition-colors ${activeIndex === index ? 'bg-orange-100' : 'hover:bg-slate-100'}`}
                                                >
                                                    <div className="font-semibold text-slate-800 text-sm sm:text-base">
                                                        <Highlight text={result.pageTitle} query={query} />
                                                    </div>
                                                    <div className="text-xs sm:text-sm text-slate-600 mb-1">
                                                        <Highlight text={result.heading} query={query} />
                                                    </div>
                                                    <p className="text-xs sm:text-sm text-slate-500 line-clamp-2">
                                                        <Highlight text={result.snippet} query={query} />
                                                    </p>
                                                </button>
                                            </li>
                                        ))}
                                    </ul>
                                ) : (
                                    <p className="p-6 sm:p-8 text-center text-slate-500 text-sm sm:text-base">No results found for "{query}"</p>
                                )}
                            </div>
                        )}
                    </div>
                </div>
            )}
        </>
    );
};

export default Search;
```

--------------------------------------------------------------------------------
/docs/sfra/response.md:
--------------------------------------------------------------------------------

```markdown
# Class Response

## Inheritance Hierarchy

- Object
    - sfra.models.Response

## Description

The SFRA Response object is a local wrapper around the global response object that provides enhanced functionality for SFRA (Storefront Reference Architecture) applications. This class serves as a centralized interface for managing response data, rendering templates, handling redirects, and controlling HTTP response behavior. The Response object maintains state for template rendering, view data, redirect URLs, logging messages, and HTTP headers while providing a consistent API for different types of responses (ISML templates, JSON, XML, Page Designer pages). It includes built-in support for caching, content type management, and response status codes, making it the primary interface for controller response handling in SFRA applications.

## Properties

### view

**Type:** String

The template name/path to be rendered.

### viewData

**Type:** Object

Data object containing all variables to be passed to the template during rendering.

### redirectUrl

**Type:** String

URL to redirect to when a redirect response is triggered.

### redirectStatus

**Type:** String

HTTP status code for redirect responses (e.g., "301", "302").

### messageLog

**Type:** Array

Collection of log messages for debugging and error output.

### base

**Type:** dw.system.Response

Reference to the original global response object.

### cachePeriod

**Type:** Number

Cache expiration period value.

### cachePeriodUnit

**Type:** String

Unit for cache period (typically hours).

### personalized

**Type:** Boolean

Indicates whether the response contains personalized content.

### renderings

**Type:** Array

Collection of rendering steps to be executed in order.

### isJson

**Type:** Boolean

Flag indicating if the response should be rendered as JSON.

### isXml

**Type:** Boolean

Flag indicating if the response should be rendered as XML.

## Constructor Summary

### Response

**Signature:** `Response(response)`

Creates a new SFRA Response object from the global response object.

**Parameters:**
- `response` (Object) - Global response object

## Method Summary

### render

**Signature:** `render(name, data) : void`

Stores template name and data for ISML template rendering.

### json

**Signature:** `json(data) : void`

Configures response to render data as JSON.

### xml

**Signature:** `xml(xmlString) : void`

Configures response to render data as XML.

### page

**Signature:** `page(page, data, aspectAttributes) : void`

Configures response to render a Page Designer page.

### redirect

**Signature:** `redirect(url) : void`

Sets up URL redirection for the response.

### setRedirectStatus

**Signature:** `setRedirectStatus(redirectStatus) : void`

Sets the HTTP status code for redirects.

### getViewData

**Signature:** `getViewData() : Object`

Retrieves the current view data object.

### setViewData

**Signature:** `setViewData(data) : void`

Updates the view data with new data.

### log

**Signature:** `log(...arguments) : void`

Logs messages for debugging and error output.

### setContentType

**Signature:** `setContentType(type) : void`

Sets the HTTP content type for the response.

### setStatusCode

**Signature:** `setStatusCode(code) : void`

Sets the HTTP status code for the response.

### print

**Signature:** `print(message) : void`

Adds a print step to the rendering pipeline.

### cacheExpiration

**Signature:** `cacheExpiration(period) : void`

Sets cache expiration period in hours.

### setHttpHeader

**Signature:** `setHttpHeader(name, value) : void`

Adds a custom HTTP header to the response.

## Method Detail

### render

**Signature:** `render(name, data) : void`

**Description:** Stores template name and data for rendering an ISML template at execution time. The data is merged with existing view data, and a render step is added to the renderings pipeline.

**Parameters:**
- `name` (String) - Path to the ISML template file
- `data` (Object) - Data object to be passed to the template

**Returns:**
void

### json

**Signature:** `json(data) : void`

**Description:** Configures the response to render the provided data as JSON. Sets the isJson flag and merges data with existing view data.

**Parameters:**
- `data` (Object) - Data object to be serialized as JSON

**Returns:**
void

### xml

**Signature:** `xml(xmlString) : void`

**Description:** Configures the response to render the provided XML string. Sets the isXml flag and stores the XML content in view data.

**Parameters:**
- `xmlString` (String) - Valid XML string to be rendered

**Returns:**
void

### page

**Signature:** `page(page, data, aspectAttributes) : void`

**Description:** Configures the response to render a Page Designer page with optional aspect attributes for advanced page management.

**Parameters:**
- `page` (String) - ID of the Page Designer page to render
- `data` (Object) - Data object to be passed to the page
- `aspectAttributes` (dw.util.HashMap) - Optional aspect attributes for PageMgr

**Returns:**
void

### redirect

**Signature:** `redirect(url) : void`

**Description:** Sets up URL redirection for the response. The redirect will be executed during response processing.

**Parameters:**
- `url` (String) - Target URL for redirection

**Returns:**
void

### setRedirectStatus

**Signature:** `setRedirectStatus(redirectStatus) : void`

**Description:** Sets the HTTP status code for redirect responses. Common values are "301" for permanent redirects and "302" for temporary redirects.

**Parameters:**
- `redirectStatus` (String) - HTTP status code for the redirect

**Returns:**
void

### getViewData

**Signature:** `getViewData() : Object`

**Description:** Retrieves the current view data object containing all variables that will be passed to the template during rendering.

**Returns:**
Object containing all view data variables.

### setViewData

**Signature:** `setViewData(data) : void`

**Description:** Updates the view data by merging the provided data object with existing view data. Existing properties with the same keys will be overwritten.

**Parameters:**
- `data` (Object) - Data object to merge with existing view data

**Returns:**
void

### log

**Signature:** `log(...arguments) : void`

**Description:** Logs multiple arguments for debugging and error output. Objects and arrays are automatically JSON.stringified, while other types are converted to strings.

**Parameters:**
- `...arguments` - Variable number of arguments to log

**Returns:**
void

### setContentType

**Signature:** `setContentType(type) : void`

**Description:** Sets the HTTP content type header for the response (e.g., "application/json", "text/xml", "text/html").

**Parameters:**
- `type` (String) - MIME type for the response content

**Returns:**
void

### setStatusCode

**Signature:** `setStatusCode(code) : void`

**Description:** Sets the HTTP status code for the response (e.g., 200, 404, 500).

**Parameters:**
- `code` (Number) - Valid HTTP status code

**Returns:**
void

### print

**Signature:** `print(message) : void`

**Description:** Adds a print step to the rendering pipeline that will output the message directly to the response stream.

**Parameters:**
- `message` (String) - Message to be printed to the response

**Returns:**
void

### cacheExpiration

**Signature:** `cacheExpiration(period) : void`

**Description:** Sets the cache expiration period for the current page response in hours from the current time.

**Parameters:**
- `period` (Number) - Number of hours from current time for cache expiration

**Returns:**
void

### setHttpHeader

**Signature:** `setHttpHeader(name, value) : void`

**Description:** Adds a custom HTTP header to the response with the specified name and value.

**Parameters:**
- `name` (String) - Header name
- `value` (String) - Header value

**Returns:**
void

## Property Details

### viewData

**Type:** Object

**Description:** Central data object that accumulates all variables to be passed to templates during rendering. Data is merged using the assign utility, allowing for incremental data building throughout controller execution.

### renderings

**Type:** Array

**Description:** Ordered collection of rendering steps that define how the response should be processed. Each step contains:

**For Render Steps:**
- `type` - Always "render"
- `subType` - Type of rendering ("isml", "json", "xml", "page")
- `view` - Template name (for ISML)
- `page` - Page ID (for Page Designer)
- `aspectAttributes` - Page attributes (for Page Designer)

**For Print Steps:**
- `type` - Always "print"
- `message` - Message to output

### messageLog

**Type:** Array

**Description:** Collection of debug and error messages logged during controller execution. Messages are automatically formatted, with objects and arrays converted to JSON strings.

### base

**Type:** dw.system.Response

**Description:** Reference to the original global response object, providing access to core response functionality like setting HTTP headers, content types, and status codes.

---

```

--------------------------------------------------------------------------------
/docs/dw_order/Appeasement.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.order

# Class Appeasement

## Inheritance Hierarchy

- Object
  - dw.object.Extensible
  - dw.order.AbstractItemCtnr
    - dw.order.Appeasement

## Description

The Appeasement represents a shopper request for an order credit. Example: The buyer finds any problem with the products but he agrees to preserve them, if he would be compensated, rather than return them. The Appeasement contains 1..n appeasement items. Each appeasement item is associated with one OrderItem usually representing an Order ProductLineItem. An Appeasement can have one of these status values: OPEN - the appeasement is open and appeasement items could be added to it COMPLETED - the appeasement is complete and it is not allowed to add new items to it, this is a precondition for refunding the customer for an appeasement. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.

## Constants

### ORDERBY_ITEMID

**Type:** Object

Sorting by item id. Use with method getItems() as an argument to method FilteringCollection.sort(Object).

### ORDERBY_ITEMPOSITION

**Type:** Object

Sorting by the position of the related order item. Use with method getItems() as an argument to method FilteringCollection.sort(Object).

### ORDERBY_UNSORTED

**Type:** Object

Unsorted, as it is. Use with method getItems() as an argument to method FilteringCollection.sort(Object).

### QUALIFIER_PRODUCTITEMS

**Type:** Object

Selects the product items. Use with method getItems() as an argument to method FilteringCollection.select(Object).

### QUALIFIER_SERVICEITEMS

**Type:** Object

Selects the service items. Use with method getItems() as an argument to method FilteringCollection.select(Object).

### STATUS_COMPLETED

**Type:** String = "COMPLETED"

Constant for Appeasement Status COMPLETED

### STATUS_OPEN

**Type:** String = "OPEN"

Constant for Appeasement Status OPEN

## Properties

### appeasementNumber

**Type:** String (Read Only)

The appeasement number.

### invoice

**Type:** Invoice (Read Only)

Returns null or the previously created Invoice.

### invoiceNumber

**Type:** String (Read Only)

Returns null or the invoice-number.

### items

**Type:** FilteringCollection (Read Only)

A filtering collection of the appeasement items belonging to the appeasement.
 
 This FilteringCollection could be sorted / filtered using:
 
 FilteringCollection.sort(Object) with ORDERBY_ITEMID
 FilteringCollection.sort(Object) with ORDERBY_ITEMPOSITION
 FilteringCollection.sort(Object) with ORDERBY_UNSORTED
 FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS
 FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS

### reasonCode

**Type:** EnumValue

The reason code for the appeasement. The list of reason codes can be updated
 by updating meta-data for Appeasement.

### reasonNote

**Type:** String

The reason note for the appeasement.

### status

**Type:** EnumValue

Gets the status of this appeasement.
 The possible values are STATUS_OPEN, STATUS_COMPLETED.

## Constructor Summary

## Method Summary

### addItems

**Signature:** `addItems(totalAmount : Money, orderItems : List) : void`

Creates appeasement items corresponding to certain order items and adds them to the appeasement.

### createInvoice

**Signature:** `createInvoice() : Invoice`

Creates a new Invoice based on this Appeasement.

### createInvoice

**Signature:** `createInvoice(invoiceNumber : String) : Invoice`

Creates a new Invoice based on this Appeasement.

### getAppeasementNumber

**Signature:** `getAppeasementNumber() : String`

Returns the appeasement number.

### getInvoice

**Signature:** `getInvoice() : Invoice`

Returns null or the previously created Invoice.

### getInvoiceNumber

**Signature:** `getInvoiceNumber() : String`

Returns null or the invoice-number.

### getItems

**Signature:** `getItems() : FilteringCollection`

Returns a filtering collection of the appeasement items belonging to the appeasement.

### getReasonCode

**Signature:** `getReasonCode() : EnumValue`

Returns the reason code for the appeasement.

### getReasonNote

**Signature:** `getReasonNote() : String`

Returns the reason note for the appeasement.

### getStatus

**Signature:** `getStatus() : EnumValue`

Gets the status of this appeasement. The possible values are STATUS_OPEN, STATUS_COMPLETED.

### setReasonCode

**Signature:** `setReasonCode(reasonCode : String) : void`

Set the reason code for the appeasement.

### setReasonNote

**Signature:** `setReasonNote(reasonNote : String) : void`

Sets the reason note for the appeasement.

### setStatus

**Signature:** `setStatus(appeasementStatus : String) : void`

Sets the appeasement status.

## Method Detail

## Method Details

### addItems

**Signature:** `addItems(totalAmount : Money, orderItems : List) : void`

**Description:** Creates appeasement items corresponding to certain order items and adds them to the appeasement.

**Parameters:**

- `totalAmount`: the appeasement amount corresponding to the provided order items; this amount is the net price when the order is net based and respectively - gross price when the order is gross based
- `orderItems`: the order items for which appeasement items should be created

---

### createInvoice

**Signature:** `createInvoice() : Invoice`

**Description:** Creates a new Invoice based on this Appeasement. The appeasement-number will be used as the invoice-number. The method must not be called more than once for an Appeasement, nor may 2 invoices exist with the same invoice-number. The new Invoice is a credit-invoice with a Invoice.STATUS_NOT_PAID status, and should be passed to the refund payment-hook in a separate database transaction for processing.

**Returns:**

the created invoice

---

### createInvoice

**Signature:** `createInvoice(invoiceNumber : String) : Invoice`

**Description:** Creates a new Invoice based on this Appeasement. The invoice-number must be specified as an argument. The method must not be called more than once for an Appeasement, nor may 2 invoices exist with the same invoice-number. The new Invoice is a credit-invoice with a Invoice.STATUS_NOT_PAID status, and should be passed to the refund payment-hook in a separate database transaction for processing.

**Parameters:**

- `invoiceNumber`: the invoice-number to be used in the appeasement creation process

**Returns:**

the created invoice

---

### getAppeasementNumber

**Signature:** `getAppeasementNumber() : String`

**Description:** Returns the appeasement number.

**Returns:**

the appeasement number

---

### getInvoice

**Signature:** `getInvoice() : Invoice`

**Description:** Returns null or the previously created Invoice.

**Returns:**

null or the previously created invoice

**See Also:**

createInvoice(String)

---

### getInvoiceNumber

**Signature:** `getInvoiceNumber() : String`

**Description:** Returns null or the invoice-number.

**Returns:**

null or the number of the previously created invoice

**See Also:**

createInvoice(String)

---

### getItems

**Signature:** `getItems() : FilteringCollection`

**Description:** Returns a filtering collection of the appeasement items belonging to the appeasement. This FilteringCollection could be sorted / filtered using: FilteringCollection.sort(Object) with ORDERBY_ITEMID FilteringCollection.sort(Object) with ORDERBY_ITEMPOSITION FilteringCollection.sort(Object) with ORDERBY_UNSORTED FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS

**Returns:**

the filtering collection of the appeasement items

---

### getReasonCode

**Signature:** `getReasonCode() : EnumValue`

**Description:** Returns the reason code for the appeasement. The list of reason codes can be updated by updating meta-data for Appeasement.

**Returns:**

the appeasement reason code

---

### getReasonNote

**Signature:** `getReasonNote() : String`

**Description:** Returns the reason note for the appeasement.

**Returns:**

the reason note or null

---

### getStatus

**Signature:** `getStatus() : EnumValue`

**Description:** Gets the status of this appeasement. The possible values are STATUS_OPEN, STATUS_COMPLETED.

**Returns:**

the status

---

### setReasonCode

**Signature:** `setReasonCode(reasonCode : String) : void`

**Description:** Set the reason code for the appeasement. The list of reason codes can be updated by updating meta-data for Appeasement.

**Parameters:**

- `reasonCode`: the reason code to set

---

### setReasonNote

**Signature:** `setReasonNote(reasonNote : String) : void`

**Description:** Sets the reason note for the appeasement.

**Parameters:**

- `reasonNote`: the reason note for the appeasement to set

---

### setStatus

**Signature:** `setStatus(appeasementStatus : String) : void`

**Description:** Sets the appeasement status. The possible values are STATUS_OPEN, STATUS_COMPLETED. When set to status COMPLETED, only the the custom attributes of its appeasement items can be changed.

**Parameters:**

- `appeasementStatus`: the appeasement status to set.

---
```

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/mock-data/ocapi/code-versions.json:
--------------------------------------------------------------------------------

```json
{
  "_v": "23.2",
  "_type": "code_version_result",
  "count": 19,
  "data": [
    {
      "_type": "code_version",
      "activation_time": "2025-09-22T10:00:00.000Z",
      "active": true,
      "cartridges": ["reset_cartridge"],
      "compatibility_mode": "22.7",
      "id": "reset_version",
      "last_modification_time": "2025-09-22T10:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reset_version"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": [
        "app_storefront_base",
        "bm_app_storefront_base",
        "bm_fastforward_backinstock",
        "bm_fastforward_base",
        "bm_fastforward_configurator",
        "bm_fastforward_configurator_pd",
        "bm_fastforward_customer_insights",
        "bm_fastforward_google",
        "bm_fastforward_gpt",
        "bm_fastforward_insights",
        "bm_fastforward_managed_runtime",
        "bm_fastforward_reviews",
        "bm_fastforward_starter_kit",
        "bm_fastforward_starter_kit_pd",
        "bm_fastforward_store_manager",
        "bm_fastforward_tools",
        "chance",
        "date-fns",
        "fast-xml-parser",
        "google",
        "jsPDF",
        "lodash",
        "modules",
        "moment",
        "openai",
        "plugin_backinstock",
        "plugin_demo_mcp",
        "plugin_demo_mcp_2",
        "plugin_fastforward_configurator",
        "plugin_fastforward_starter_kit",
        "plugin_mcp_example",
        "plugin_openai",
        "plugin_testlibraries",
        "ramda",
        "rhinodb",
        "salesforce-managed-runtime"
      ],
      "compatibility_mode": "22.7",
      "id": "SFRA_AP_01_24_2024",
      "last_modification_time": "2025-09-18T13:18:21Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/SFRA_AP_01_24_2024"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": [
        "bm_fastforward_base",
        "bm_fastforward_configurator",
        "bm_fastforward_configurator_pd",
        "plugin_fastforward_configurator"
      ],
      "compatibility_mode": "22.7",
      "id": "SFRA_AP_01_24_2024 ",
      "last_modification_time": "2025-03-05T14:55:22Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/SFRA_AP_01_24_2024 "
    },
    {
      "_type": "code_version",
      "activation_time": "2025-09-18T13:41:07Z",
      "active": false,
      "cartridges": [
        "bm_fastforward_base",
        "bm_fastforward_configurator", 
        "bm_fastforward_configurator_pd",
        "plugin_fastforward_configurator"
      ],
      "compatibility_mode": "22.7",
      "id": "version1",
      "last_modification_time": "2025-03-05T14:12:00Z",
      "rollback": true,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/version1"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["test_cartridge"],
      "compatibility_mode": "22.7",
      "id": "test_activation",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/test_activation"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["simple_cartridge"],
      "compatibility_mode": "22.7",
      "id": "simple_id",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/simple_id"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["version_cartridge"],
      "compatibility_mode": "22.7",
      "id": "version-with-dashes",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/version-with-dashes"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["version_cartridge"],
      "compatibility_mode": "22.7",
      "id": "version_with_underscores",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/version_with_underscores"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["version_cartridge"],
      "compatibility_mode": "22.7",
      "id": "Version123",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/Version123"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["test_cartridge"],
      "compatibility_mode": "22.7",
      "id": "seq_test_1",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/seq_test_1"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["test_cartridge"],
      "compatibility_mode": "22.7",
      "id": "seq_test_2",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/seq_test_2"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["test_cartridge"],
      "compatibility_mode": "22.7",
      "id": "seq_test_3",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/seq_test_3"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["test_cartridge"],
      "compatibility_mode": "22.7",
      "id": "workflow_test_version",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/workflow_test_version"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["recovery_cartridge"],
      "compatibility_mode": "22.7",
      "id": "recovery_test",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/recovery_test"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["reliability_cartridge"],
      "compatibility_mode": "22.7",
      "id": "reliability_test_0",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reliability_test_0"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["reliability_cartridge"],
      "compatibility_mode": "22.7",
      "id": "reliability_test_1",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reliability_test_1"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["reliability_cartridge"],
      "compatibility_mode": "22.7",
      "id": "reliability_test_2",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reliability_test_2"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["deploy_cartridge"],
      "compatibility_mode": "22.7",
      "id": "deployment_v1.2.3",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/deployment_v1.2.3"
    },
    {
      "_type": "code_version",
      "active": false,
      "cartridges": ["deploy_cartridge"],
      "compatibility_mode": "22.7",
      "id": "deployment_simulation_v1",
      "last_modification_time": "2025-09-22T12:00:00Z",
      "rollback": false,
      "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/deployment_simulation_v1"
    }
  ],
  "total": 19
}

```

--------------------------------------------------------------------------------
/docs/dw_util/Decimal.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.util

# Class Decimal

## Inheritance Hierarchy

- Object
  - dw.util.Decimal

## Description

The Decimal class is a helper class to perform decimal arithmetic in scripts and to represent a decimal number with arbitrary length. The decimal class avoids arithmetic errors, which are typical for calculating with floating numbers, that are based on a binary mantissa. The class is designed in a way that it can be used very similar to a desktop calculator. var d = new Decimal( 10.0 ); var result = d.add( 2.0 ).sub( 3.0 ).get(); The above code will return 9 as result.

## Constructor Summary

Decimal() Constructs a new Decimal with the value 0.

Decimal(value : Number) Constructs a new decimal using the specified Number value.

Decimal(value : BigInt) Constructs a new decimal using the specified BigInt value.

Decimal(value : String) Constructs a new Decimal using the specified string representation of a number.

## Method Summary

### abs

**Signature:** `abs() : Decimal`

Returns a new Decimal with the absolute value of this Decimal.

### add

**Signature:** `add(value : Number) : Decimal`

Adds a Number value to this Decimal and returns the new Decimal.

### add

**Signature:** `add(value : Decimal) : Decimal`

Adds a Decimal value to this Decimal and returns the new Decimal.

### addPercent

**Signature:** `addPercent(value : Number) : Decimal`

Adds a percentage value to the current value of the decimal.

### addPercent

**Signature:** `addPercent(value : Decimal) : Decimal`

Adds a percentage value to the current value of the decimal.

### divide

**Signature:** `divide(value : Number) : Decimal`

Divides the specified Number value with this decimal and returns the new decimal.

### divide

**Signature:** `divide(value : Decimal) : Decimal`

Divides the specified Decimal value with this decimal and returns the new decimal.

### equals

**Signature:** `equals(other : Object) : boolean`

Compares two decimal values whether they are equivalent.

### get

**Signature:** `get() : Number`

Returns the value of the Decimal as a Number.

### hashCode

**Signature:** `hashCode() : Number`

Calculates the hash code for this decimal;

### multiply

**Signature:** `multiply(value : Number) : Decimal`

Multiples the specified Number value with this Decimal and returns the new Decimal.

### multiply

**Signature:** `multiply(value : Decimal) : Decimal`

Multiples the specified Decimal value with this Decimal and returns the new Decimal.

### negate

**Signature:** `negate() : Decimal`

Returns a new Decimal with the negated value of this Decimal.

### round

**Signature:** `round(decimals : Number) : Decimal`

Rounds the current value of the decimal using the specified number of decimals.

### subtract

**Signature:** `subtract(value : Number) : Decimal`

Subtracts the specified Number value from this Decimal and returns the new Decimal.

### subtract

**Signature:** `subtract(value : Decimal) : Decimal`

Subtracts the specified Decimal value from this Decimal and returns the new Decimal.

### subtractPercent

**Signature:** `subtractPercent(value : Number) : Decimal`

Subtracts a percentage value from the current value of the decimal.

### subtractPercent

**Signature:** `subtractPercent(value : Decimal) : Decimal`

Subtracts a percentage value from the current value of the decimal.

### toString

**Signature:** `toString() : String`

Returns a string representation of this object.

### valueOf

**Signature:** `valueOf() : Object`

The valueOf() method is called by the ECMAScript interpret to return the "natural" value of an object.

## Constructor Detail

## Method Detail

## Method Details

### abs

**Signature:** `abs() : Decimal`

**Description:** Returns a new Decimal with the absolute value of this Decimal.

**Returns:**

the new Decimal

---

### add

**Signature:** `add(value : Number) : Decimal`

**Description:** Adds a Number value to this Decimal and returns the new Decimal.

**Parameters:**

- `value`: the value to add to this decimal.

**Returns:**

the new decimal with the value added.

---

### add

**Signature:** `add(value : Decimal) : Decimal`

**Description:** Adds a Decimal value to this Decimal and returns the new Decimal.

**Parameters:**

- `value`: the value to add to this decimal.

**Returns:**

the new decimal with the value added.

---

### addPercent

**Signature:** `addPercent(value : Number) : Decimal`

**Description:** Adds a percentage value to the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.

**Parameters:**

- `value`: the value to add.

**Returns:**

a new decimal with the added percentage value.

---

### addPercent

**Signature:** `addPercent(value : Decimal) : Decimal`

**Description:** Adds a percentage value to the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.

**Parameters:**

- `value`: the value to add.

**Returns:**

a new decimal with the added percentage value.

---

### divide

**Signature:** `divide(value : Number) : Decimal`

**Description:** Divides the specified Number value with this decimal and returns the new decimal. When performing the division, 34 digits precision and a rounding mode of HALF_EVEN is used to prevent quotients with nonterminating decimal expansions.

**Parameters:**

- `value`: the value to use to divide this decimal.

**Returns:**

the new decimal.

---

### divide

**Signature:** `divide(value : Decimal) : Decimal`

**Description:** Divides the specified Decimal value with this decimal and returns the new decimal. When performing the division, 34 digits precision and a rounding mode of HALF_EVEN is used to prevent quotients with nonterminating decimal expansions.

**Parameters:**

- `value`: the value to use to divide this decimal.

**Returns:**

the new decimal.

---

### equals

**Signature:** `equals(other : Object) : boolean`

**Description:** Compares two decimal values whether they are equivalent.

**Parameters:**

- `other`: the object to comapre against this decimal.

---

### get

**Signature:** `get() : Number`

**Description:** Returns the value of the Decimal as a Number.

**Returns:**

the value of the Decimal.

---

### hashCode

**Signature:** `hashCode() : Number`

**Description:** Calculates the hash code for this decimal;

---

### multiply

**Signature:** `multiply(value : Number) : Decimal`

**Description:** Multiples the specified Number value with this Decimal and returns the new Decimal.

**Parameters:**

- `value`: the value to multiply with this decimal.

**Returns:**

the new decimal.

---

### multiply

**Signature:** `multiply(value : Decimal) : Decimal`

**Description:** Multiples the specified Decimal value with this Decimal and returns the new Decimal.

**Parameters:**

- `value`: the value to multiply with this decimal.

**Returns:**

the new decimal.

---

### negate

**Signature:** `negate() : Decimal`

**Description:** Returns a new Decimal with the negated value of this Decimal.

**Returns:**

the new Decimal

---

### round

**Signature:** `round(decimals : Number) : Decimal`

**Description:** Rounds the current value of the decimal using the specified number of decimals. The parameter specifies the number of digest after the decimal point.

**Parameters:**

- `decimals`: the number of decimals to use.

**Returns:**

the decimal that has been rounded.

---

### subtract

**Signature:** `subtract(value : Number) : Decimal`

**Description:** Subtracts the specified Number value from this Decimal and returns the new Decimal.

**Parameters:**

- `value`: the value to add to this decimal.

**Returns:**

the new decimal with the value subtraced.

---

### subtract

**Signature:** `subtract(value : Decimal) : Decimal`

**Description:** Subtracts the specified Decimal value from this Decimal and returns the new Decimal.

**Parameters:**

- `value`: the value to add to this decimal.

**Returns:**

the new decimal with the value subtraced.

---

### subtractPercent

**Signature:** `subtractPercent(value : Number) : Decimal`

**Description:** Subtracts a percentage value from the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.

**Parameters:**

- `value`: the value to subtract.

**Returns:**

a new decimal with the subtracted percentage value.

---

### subtractPercent

**Signature:** `subtractPercent(value : Decimal) : Decimal`

**Description:** Subtracts a percentage value from the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.

**Parameters:**

- `value`: the value to subtract.

**Returns:**

a new decimal with the subtracted percentage value.

---

### toString

**Signature:** `toString() : String`

**Description:** Returns a string representation of this object.

**Returns:**

a string representation of this object.

---

### valueOf

**Signature:** `valueOf() : Object`

**Description:** The valueOf() method is called by the ECMAScript interpret to return the "natural" value of an object. The Decimal object returns its current value as number. With this behavior script snippets can be written like: var d = new Decimal( 10.0 ); var x = 1.0 + d.add( 2.0 ); where x will be at the end 13.0.

**Returns:**

the value of this object.

---
```

--------------------------------------------------------------------------------
/docs-site/scripts/generate-search-index.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node

/**
 * Search Index Generator for SFCC Development MCP Server Documentation
 *
 * This script automatically generates a search index by parsing React components
 * and extracting searchable content. It replaces the manually maintained search
 * index with an automatically generated one during the build process.
 */

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Configuration
const PAGES_DIR = path.join(__dirname, '../pages');
const OUTPUT_FILE = path.join(__dirname, '../src/generated-search-index.ts');

/**
 * Route mappings for pages to their URL paths
 */
const ROUTE_MAPPINGS = {
  'HomePage.tsx': '/',
  'ConfigurationPage.tsx': '/configuration/',
  'AIInterfacesPage.tsx': '/ai-interfaces/',
  'FeaturesPage.tsx': '/features/',
  'ToolsPage.tsx': '/tools/',
  'ExamplesPage.tsx': '/examples/',
  'SecurityPage.tsx': '/security/',
  'DevelopmentPage.tsx': '/development/',
  'TroubleshootingPage.tsx': '/troubleshooting/',
};

/**
 * Extract content from JSX strings and React elements
 */
function extractTextFromJSX(content) {
  const textContent = [];

  // Remove JSX tags but keep the text content
  const cleanContent = content
    // Remove comments
    .replace(/\/\*[\s\S]*?\*\//g, '')
    .replace(/\/\/.*$/gm, '')
    // Remove import statements
    .replace(/^import\s+.*$/gm, '')
    // Remove className and other JSX attributes
    .replace(/className=["'`][^"'`]*["'`]/g, '')
    .replace(/\w+={[^}]*}/g, '');

  // Extract text between JSX tags
  const textRegex = />([^<]+)</g;
  let match;
  while ((match = textRegex.exec(cleanContent)) !== null) {
    const text = match[1].trim();
    // Filter out empty strings, JSX expressions, and very short text
    if (text &&
        !text.startsWith('{') &&
        !text.includes('className') &&
        !text.includes('src=') &&
        text.length > 3 &&
        !/^[{\s}]*$/.test(text)) {
      textContent.push(text);
    }
  }

  // Also extract string literals that might contain content
  const stringRegex = /["'`]([^"'`{]+)["'`]/g;
  while ((match = stringRegex.exec(cleanContent)) !== null) {
    const text = match[1].trim();
    if (text &&
        text.length > 10 &&
        !text.includes('className') &&
        !text.includes('src=') &&
        !text.includes('href=') &&
        !text.startsWith('http') &&
        (!text.includes('.') || text.includes(' '))) { // Allow sentences with periods
      textContent.push(text);
    }
  }

  // Clean up the extracted text
  return textContent
    .map(text => text
      .replace(/\s+/g, ' ') // Normalize whitespace
      .replace(/[{}]/g, '') // Remove remaining braces
      .trim())
    .filter(text => text.length > 5)
    .join(' ');
}

/**
 * Extract headings and sections from a React component
 */
function parseReactComponent(filePath, content) {
  const results = [];

  // Get the page title from the route mapping
  const relativePath = path.relative(PAGES_DIR, filePath);
  const routePath = ROUTE_MAPPINGS[relativePath];

  if (!routePath) {
    console.warn(`Warning: No route mapping found for ${relativePath}`);
    return results;
  }

  // Extract page title from component name or H1 tags
  let pageTitle = 'Unknown Page';
  const titleMatch = content.match(/pageTitle\s*[:=]\s*["'`]([^"'`]+)["'`]/);
  if (titleMatch) {
    pageTitle = titleMatch[1];
  } else {
    // Fallback: extract from H1 tags
    const h1Match = content.match(/<h1[^>]*>([^<]+)<\/h1>/i);
    if (h1Match) {
      pageTitle = h1Match[1].replace(/\{[^}]*\}/g, '').trim();
    }
  }

  // Extract sections based on headings (H1, H2, H3 components and regular h1-h3 tags)
  const jsxComponentPattern = /<H([123])[^>]*id=["']([^"']+)["'][^>]*>([^<]+)<\/H[123]>/gi;
  const regularHeadingPattern = /<(h[1-3])[^>]*(?:id=["']([^"']+)["'])?[^>]*>([^<]+)<\/(h[1-3])>/gi;
  const sections = [];
  let match;

  // First, extract JSX component headings (H1, H2, H3)
  while ((match = jsxComponentPattern.exec(content)) !== null) {
    const heading = match[3].trim();
    const headingId = match[2];
    const level = parseInt(match[1]);

    if (heading && heading.length > 1) {
      sections.push({
        heading,
        headingId,
        level,
        position: match.index,
      });
    }
  }

  // Reset regex for next pass
  regularHeadingPattern.lastIndex = 0;

  // Then, extract regular HTML headings
  while ((match = regularHeadingPattern.exec(content)) !== null) {
    const heading = match[3].replace(/\{[^}]*\}/g, '').trim();
    const headingId = match[2];
    const level = parseInt(match[1].charAt(1));

    if (heading && heading.length > 1 && !heading.includes('className')) {
      sections.push({
        heading,
        headingId,
        level,
        position: match.index,
      });
    }
  }

  // Sort sections by their position in the document
  sections.sort((a, b) => a.position - b.position);

  // Filter out duplicate headings at the same position, preferring those with a headingId
  const filteredSections = [];
  for (let i = 0; i < sections.length; i++) {
    const curr = sections[i];
    const next = sections[i + 1];
    if (
      next &&
      curr.position === next.position &&
      curr.heading === next.heading
    ) {
      // Prefer the one with headingId
      if (curr.headingId) {
        filteredSections.push(curr);
      } else if (next.headingId) {
        filteredSections.push(next);
      }
      i++; // Skip the next one
    } else {
      filteredSections.push(curr);
    }
  }

  if (filteredSections.length === 0) {
    // If no headings found, create a single entry for the whole page
    const textContent = extractTextFromJSX(content);
    results.push({
      path: routePath,
      pageTitle,
      heading: pageTitle,
      headingId: null, // No heading ID for fallback case
      content: textContent.substring(0, 500), // Limit content length
    });
    return results;
  }

  // Create search entries for each section
  filteredSections.forEach((section, index) => {
    const nextSection = filteredSections[index + 1];
    const sectionStart = section.position;
    const sectionEnd = nextSection ? nextSection.position : content.length;

    const sectionContent = content.substring(sectionStart, sectionEnd);
    const textContent = extractTextFromJSX(sectionContent);

    if (textContent.trim().length > 10) { // Only include sections with meaningful content
      results.push({
        path: routePath,
        pageTitle,
        heading: section.heading,
        headingId: section.headingId || null, // Ensure the property is always present
        content: textContent.substring(0, 300), // Limit content length per section
      });
    }
  });

  return results;
}

/**
 * Recursively find all React component files
 */
function findReactFiles(dir) {
  const files = [];

  function traverse(currentDir) {
    const entries = fs.readdirSync(currentDir, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = path.join(currentDir, entry.name);

      if (entry.isDirectory()) {
        traverse(fullPath);
      } else if (entry.isFile() && entry.name.endsWith('.tsx')) {
        files.push(fullPath);
      }
    }
  }

  traverse(dir);
  return files;
}

/**
 * Generate the search index
 */
function generateSearchIndex() {
  console.log('🔍 Generating search index...');

  const reactFiles = findReactFiles(PAGES_DIR);
  const searchIndex = [];

  console.log(`Found ${reactFiles.length} React files to process`);

  for (const filePath of reactFiles) {
    try {
      const content = fs.readFileSync(filePath, 'utf-8');
      const entries = parseReactComponent(filePath, content);
      searchIndex.push(...entries);

      console.log(`  ✓ Processed ${path.relative(PAGES_DIR, filePath)} (${entries.length} entries)`);
    } catch (error) {
      console.error(`  ✗ Failed to process ${filePath}:`, error.message);
    }
  }

  console.log(`Generated ${searchIndex.length} search entries`);

  return searchIndex;
}

/**
 * Write the generated index to a TypeScript file
 */
function writeSearchIndex(searchIndex) {
  const outputDir = path.dirname(OUTPUT_FILE);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  const tsContent = `// This file is auto-generated by scripts/generate-search-index.js
// Do not edit manually - changes will be overwritten during build

export interface SearchableItem {
  path: string;
  pageTitle: string;
  heading: string;
  headingId?: string;
  content: string;
}

export const GENERATED_SEARCH_INDEX: SearchableItem[] = ${JSON.stringify(searchIndex, null, 2)};
`;

  fs.writeFileSync(OUTPUT_FILE, tsContent, 'utf-8');
  console.log(`✅ Search index written to ${path.relative(process.cwd(), OUTPUT_FILE)}`);
}

/**
 * Main execution
 */
function main() {
  try {
    const searchIndex = generateSearchIndex();
    writeSearchIndex(searchIndex);
    console.log('🎉 Search index generation complete!');
  } catch (error) {
    console.error('❌ Failed to generate search index:', error);
    process.exit(1);
  }
}

// Run if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
  main();
}

export { generateSearchIndex, writeSearchIndex };

```

--------------------------------------------------------------------------------
/docs/dw_util/Assert.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.util

# Class Assert

## Inheritance Hierarchy

- Object
  - dw.util.Assert

## Description

The Assert class provides utility methods for assertion events.

## Constructor Summary

## Method Summary

### areEqual

**Signature:** `static areEqual(arg1 : Object, arg2 : Object) : void`

Propagates an assertion if the specified objects are not equal.

### areEqual

**Signature:** `static areEqual(arg1 : Object, arg2 : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified objects are not equal.

### areSame

**Signature:** `static areSame(arg1 : Object, arg2 : Object) : void`

Propagates an assertion if the specified objects are not the same.

### areSame

**Signature:** `static areSame(arg1 : Object, arg2 : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified objects are not the same.

### fail

**Signature:** `static fail() : void`

Propagates a failure assertion.

### fail

**Signature:** `static fail(msg : String) : void`

Propagates a failure assertion using the specified message.

### isEmpty

**Signature:** `static isEmpty(arg : Object) : void`

Propagates an assertion if the specified check does not evaluate to an empty object.

### isEmpty

**Signature:** `static isEmpty(arg : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified check does not evaluate to an empty object.

### isFalse

**Signature:** `static isFalse(check : boolean) : void`

Propagates an assertion if the specified check does not evaluate to false.

### isFalse

**Signature:** `static isFalse(check : boolean, msg : String) : void`

Propagates an assertion using the specified message if the specified check does not evaluate to false.

### isInstanceOf

**Signature:** `static isInstanceOf(clazz : Object, arg : Object) : void`

Propagates an assertion if the specified object 'arg' is not an instance of the specified class 'clazz'.

### isInstanceOf

**Signature:** `static isInstanceOf(clazz : Object, arg : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified object is not an instance of the specified class.

### isNotEmpty

**Signature:** `static isNotEmpty(arg : Object) : void`

Propagates an assertion if the specified object is empty.

### isNotEmpty

**Signature:** `static isNotEmpty(arg : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified object is empty.

### isNotNull

**Signature:** `static isNotNull(arg : Object) : void`

Propagates an assertion if the specified object is null.

### isNotNull

**Signature:** `static isNotNull(arg : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified object is null.

### isNull

**Signature:** `static isNull(arg : Object) : void`

Propagates an assertion if the specified object is not null.

### isNull

**Signature:** `static isNull(arg : Object, msg : String) : void`

Propagates an assertion using the specified message if the specified object is not null.

### isTrue

**Signature:** `static isTrue(check : boolean) : void`

Propagates an assertion if the specified check does not evaluate to true.

### isTrue

**Signature:** `static isTrue(check : boolean, msg : String) : void`

Propagates an assertion using the specified message if the specified check does not evaluate to true.

## Method Detail

## Method Details

### areEqual

**Signature:** `static areEqual(arg1 : Object, arg2 : Object) : void`

**Description:** Propagates an assertion if the specified objects are not equal.

**Parameters:**

- `arg1`: the first object to check.
- `arg2`: the second object to check.

---

### areEqual

**Signature:** `static areEqual(arg1 : Object, arg2 : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified objects are not equal.

**Parameters:**

- `arg1`: the first object to check.
- `arg2`: the second object to check.
- `msg`: the assertion message.

---

### areSame

**Signature:** `static areSame(arg1 : Object, arg2 : Object) : void`

**Description:** Propagates an assertion if the specified objects are not the same.

**Parameters:**

- `arg1`: the first object to check.
- `arg2`: the second object to check.

---

### areSame

**Signature:** `static areSame(arg1 : Object, arg2 : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified objects are not the same.

**Parameters:**

- `arg1`: the first object to check.
- `arg2`: the second object to check.
- `msg`: the assertion message.

---

### fail

**Signature:** `static fail() : void`

**Description:** Propagates a failure assertion.

---

### fail

**Signature:** `static fail(msg : String) : void`

**Description:** Propagates a failure assertion using the specified message.

**Parameters:**

- `msg`: the assertion message.

---

### isEmpty

**Signature:** `static isEmpty(arg : Object) : void`

**Description:** Propagates an assertion if the specified check does not evaluate to an empty object.

**Parameters:**

- `arg`: the object to check.

---

### isEmpty

**Signature:** `static isEmpty(arg : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified check does not evaluate to an empty object.

**Parameters:**

- `arg`: the object to check.
- `msg`: the assertion message.

---

### isFalse

**Signature:** `static isFalse(check : boolean) : void`

**Description:** Propagates an assertion if the specified check does not evaluate to false.

**Parameters:**

- `check`: the condition to check.

---

### isFalse

**Signature:** `static isFalse(check : boolean, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified check does not evaluate to false.

**Parameters:**

- `check`: the condition to check.
- `msg`: the assertion message.

---

### isInstanceOf

**Signature:** `static isInstanceOf(clazz : Object, arg : Object) : void`

**Description:** Propagates an assertion if the specified object 'arg' is not an instance of the specified class 'clazz'. For example, the following call does not propagate an assertion: var test = new dw.util.HashMap(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); But the following call will propagate an assertion: var test = new dw.util.Set(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); Note that 'clazz' can only be a Demandware API Scripting class.

**Parameters:**

- `clazz`: the scripting class to use to check the object.
- `arg`: the object to check.

---

### isInstanceOf

**Signature:** `static isInstanceOf(clazz : Object, arg : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified object is not an instance of the specified class. For example, the following call does not propagate an assertion: var test = new dw.util.HashMap(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); But the following call will propagate an assertion: var test = new dw.util.Set(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); Note that 'clazz' can only be a Demandware API Scripting class.

**Parameters:**

- `clazz`: the scripting class to use to check the object.
- `arg`: the object to check.
- `msg`: the assertion message.

---

### isNotEmpty

**Signature:** `static isNotEmpty(arg : Object) : void`

**Description:** Propagates an assertion if the specified object is empty.

**Parameters:**

- `arg`: the object to check.

---

### isNotEmpty

**Signature:** `static isNotEmpty(arg : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified object is empty.

**Parameters:**

- `arg`: the object to check.
- `msg`: the assertion message.

---

### isNotNull

**Signature:** `static isNotNull(arg : Object) : void`

**Description:** Propagates an assertion if the specified object is null.

**Parameters:**

- `arg`: the object to check.

---

### isNotNull

**Signature:** `static isNotNull(arg : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified object is null.

**Parameters:**

- `arg`: the object to check.
- `msg`: the assertion message.

---

### isNull

**Signature:** `static isNull(arg : Object) : void`

**Description:** Propagates an assertion if the specified object is not null.

**Parameters:**

- `arg`: the object to check.

---

### isNull

**Signature:** `static isNull(arg : Object, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified object is not null.

**Parameters:**

- `arg`: the object to check.
- `msg`: the assertion message.

---

### isTrue

**Signature:** `static isTrue(check : boolean) : void`

**Description:** Propagates an assertion if the specified check does not evaluate to true.

**Parameters:**

- `check`: the condition to check.

---

### isTrue

**Signature:** `static isTrue(check : boolean, msg : String) : void`

**Description:** Propagates an assertion using the specified message if the specified check does not evaluate to true.

**Parameters:**

- `check`: the condition to check.
- `msg`: the assertion message.

---
```

--------------------------------------------------------------------------------
/docs/dw_object/ObjectAttributeDefinition.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.object

# Class ObjectAttributeDefinition

## Inheritance Hierarchy

- Object
  - dw.object.ObjectAttributeDefinition

## Description

Represents the definition of an object's attribute.

## Constants

### VALUE_TYPE_BOOLEAN

**Type:** Number = 8

Boolean value type.

### VALUE_TYPE_DATE

**Type:** Number = 6

Date value type.

### VALUE_TYPE_DATETIME

**Type:** Number = 11

Date and Time value type.

### VALUE_TYPE_EMAIL

**Type:** Number = 12

Email value type.

### VALUE_TYPE_ENUM_OF_INT

**Type:** Number = 31

Enum of int value type.

### VALUE_TYPE_ENUM_OF_STRING

**Type:** Number = 33

Enum of String value type.

### VALUE_TYPE_HTML

**Type:** Number = 5

HTML value type.

### VALUE_TYPE_IMAGE

**Type:** Number = 7

Image value type.

### VALUE_TYPE_INT

**Type:** Number = 1

int value type.

### VALUE_TYPE_MONEY

**Type:** Number = 9

Money value type.

### VALUE_TYPE_NUMBER

**Type:** Number = 2

Number value type.

### VALUE_TYPE_PASSWORD

**Type:** Number = 13

Password value type.

### VALUE_TYPE_QUANTITY

**Type:** Number = 10

Quantity value type.

### VALUE_TYPE_SET_OF_INT

**Type:** Number = 21

Set of int value type.

### VALUE_TYPE_SET_OF_NUMBER

**Type:** Number = 22

Set of Number value type.

### VALUE_TYPE_SET_OF_STRING

**Type:** Number = 23

Set of String value type.

### VALUE_TYPE_STRING

**Type:** Number = 3

String value type.

### VALUE_TYPE_TEXT

**Type:** Number = 4

Text value type.

## Properties

### attributeGroups

**Type:** Collection (Read Only)

All attribute groups the attribute is assigned to.

### defaultValue

**Type:** ObjectAttributeValueDefinition (Read Only)

Return the default value for the attribute or null if none is defined.

### displayName

**Type:** String (Read Only)

The display name for the attribute, which can be used in the
 user interface.

### ID

**Type:** String (Read Only)

The ID of the attribute definition.

### key

**Type:** boolean (Read Only)

Identifies if the attribute represents the primary key of the object.

### mandatory

**Type:** boolean (Read Only)

Checks if this attribute is mandatory.

### multiValueType

**Type:** VALUE_TYPE_SET_OF_INT (Read Only)

Returns true if the attribute can have multiple values.
 Attributes of the following types are multi-value capable:
 
 VALUE_TYPE_SET_OF_INT
 VALUE_TYPE_SET_OF_NUMBER
 VALUE_TYPE_SET_OF_STRING
 
 Additionally, attributes of the following types can be multi-value
 enabled:
 
 VALUE_TYPE_ENUM_OF_INT
 VALUE_TYPE_ENUM_OF_STRING

### objectTypeDefinition

**Type:** ObjectTypeDefinition (Read Only)

The object type definition in which this attribute is defined.

### setValueType

**Type:** boolean (Read Only)

Returns true if the attribute is of type 'Set of'.

### system

**Type:** boolean (Read Only)

Indicates if the attribute is a pre-defined system attribute
 or a custom attribute.

### unit

**Type:** String (Read Only)

The attribute's unit representation such as
 inches for length or pounds for weight. The value returned by
 this method is based on the attribute itself.

### values

**Type:** Collection (Read Only)

The list of attribute values. In the user interface only the
 values specified in this list should be offered as valid input values.

 The collection contains instances of ObjectAttributeValueDefinition.

### valueTypeCode

**Type:** Number (Read Only)

A code for the data type stored in the attribute. See constants
 defined in this class.

## Constructor Summary

## Method Summary

### getAttributeGroups

**Signature:** `getAttributeGroups() : Collection`

Returns all attribute groups the attribute is assigned to.

### getDefaultValue

**Signature:** `getDefaultValue() : ObjectAttributeValueDefinition`

Return the default value for the attribute or null if none is defined.

### getDisplayName

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

Returns the display name for the attribute, which can be used in the user interface.

### getID

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

Returns the ID of the attribute definition.

### getObjectTypeDefinition

**Signature:** `getObjectTypeDefinition() : ObjectTypeDefinition`

Returns the object type definition in which this attribute is defined.

### getUnit

**Signature:** `getUnit() : String`

Returns the attribute's unit representation such as inches for length or pounds for weight.

### getValues

**Signature:** `getValues() : Collection`

Returns the list of attribute values.

### getValueTypeCode

**Signature:** `getValueTypeCode() : Number`

Returns a code for the data type stored in the attribute.

### isKey

**Signature:** `isKey() : boolean`

Identifies if the attribute represents the primary key of the object.

### isMandatory

**Signature:** `isMandatory() : boolean`

Checks if this attribute is mandatory.

### isMultiValueType

**Signature:** `isMultiValueType() : boolean`

Returns true if the attribute can have multiple values.

### isSetValueType

**Signature:** `isSetValueType() : boolean`

Returns true if the attribute is of type 'Set of'.

### isSystem

**Signature:** `isSystem() : boolean`

Indicates if the attribute is a pre-defined system attribute or a custom attribute.

### requiresEncoding

**Signature:** `requiresEncoding() : boolean`

Returns a boolean flag indicating whether or not values of this attribute definition should be encoded using the encoding="off" flag in ISML templates.

## Method Detail

## Method Details

### getAttributeGroups

**Signature:** `getAttributeGroups() : Collection`

**Description:** Returns all attribute groups the attribute is assigned to.

**Returns:**

all attribute groups the attribute is assigned to.

---

### getDefaultValue

**Signature:** `getDefaultValue() : ObjectAttributeValueDefinition`

**Description:** Return the default value for the attribute or null if none is defined.

**Returns:**

the default value for the attribute or null if none is defined.

---

### getDisplayName

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

**Description:** Returns the display name for the attribute, which can be used in the user interface.

**Returns:**

the display name for the attribute, which can be used in the user interface.

---

### getID

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

**Description:** Returns the ID of the attribute definition.

**Returns:**

the ID of the attribute definition.

---

### getObjectTypeDefinition

**Signature:** `getObjectTypeDefinition() : ObjectTypeDefinition`

**Description:** Returns the object type definition in which this attribute is defined.

**Returns:**

the object type definition in which this attribute is defined.

---

### getUnit

**Signature:** `getUnit() : String`

**Description:** Returns the attribute's unit representation such as inches for length or pounds for weight. The value returned by this method is based on the attribute itself.

**Returns:**

the attribute's unit representation such as inches for length or pounds for weight.

---

### getValues

**Signature:** `getValues() : Collection`

**Description:** Returns the list of attribute values. In the user interface only the values specified in this list should be offered as valid input values. The collection contains instances of ObjectAttributeValueDefinition.

**Returns:**

a collection of ObjectAttributeValueDefinition instances representing the list of attribute values, or null if no values are specified.

---

### getValueTypeCode

**Signature:** `getValueTypeCode() : Number`

**Description:** Returns a code for the data type stored in the attribute. See constants defined in this class.

**Returns:**

a code for the data type stored in the attribute. See constants defined in this class.

---

### isKey

**Signature:** `isKey() : boolean`

**Description:** Identifies if the attribute represents the primary key of the object.

**Returns:**

true if the attribute represents the primary key, false otherwise.

---

### isMandatory

**Signature:** `isMandatory() : boolean`

**Description:** Checks if this attribute is mandatory.

**Returns:**

true, if this attribute is mandatory

---

### isMultiValueType

**Signature:** `isMultiValueType() : boolean`

**Description:** Returns true if the attribute can have multiple values. Attributes of the following types are multi-value capable: VALUE_TYPE_SET_OF_INT VALUE_TYPE_SET_OF_NUMBER VALUE_TYPE_SET_OF_STRING Additionally, attributes of the following types can be multi-value enabled: VALUE_TYPE_ENUM_OF_INT VALUE_TYPE_ENUM_OF_STRING

**Returns:**

true if attributes can have multiple values, otherwise false

---

### isSetValueType

**Signature:** `isSetValueType() : boolean`

**Description:** Returns true if the attribute is of type 'Set of'.

**Deprecated:**

Use isMultiValueType() instead.

---

### isSystem

**Signature:** `isSystem() : boolean`

**Description:** Indicates if the attribute is a pre-defined system attribute or a custom attribute.

**Returns:**

true if the the attribute is a pre-defined system attribute, false if it is a custom attribute.

---

### requiresEncoding

**Signature:** `requiresEncoding() : boolean`

**Description:** Returns a boolean flag indicating whether or not values of this attribute definition should be encoded using the encoding="off" flag in ISML templates.

**Returns:**

a boolean flag indicating whether or not values of this attribute definition should be encoded using the encoding="off" flag in ISML templates.

---
```
Page 12/43FirstPrevNextLast