#
tokens: 47807/50000 10/825 files (page 23/61)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 23 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&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/mcp/yaml/get-best-practice-guide.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
  1 | description: "Test get_best_practice_guide tool in docs-only mode"
  2 | tests:
  3 |   # Test successful guide retrieval with cartridge_creation guide
  4 |   - it: "should return cartridge creation guide with complete structure"
  5 |     request:
  6 |       jsonrpc: "2.0"
  7 |       id: "cartridge-guide-1"
  8 |       method: "tools/call"
  9 |       params:
 10 |         name: "get_best_practice_guide"
 11 |         arguments:
 12 |           guideName: "cartridge_creation"
 13 |     expect:
 14 |       response:
 15 |         jsonrpc: "2.0"
 16 |         id: "cartridge-guide-1"
 17 |         result:
 18 |           content:
 19 |             - type: "text"
 20 |               text: "match:contains:Instructions for Creating a Salesforce B2C Commerce"
 21 |           isError: false
 22 |       stderr: "toBeEmpty"
 23 | 
 24 |   # Test that the response contains proper JSON structure
 25 |   - it: "should return valid JSON with title, description, sections, and content"
 26 |     request:
 27 |       jsonrpc: "2.0"
 28 |       id: "cartridge-structure-1"
 29 |       method: "tools/call"
 30 |       params:
 31 |         name: "get_best_practice_guide"
 32 |         arguments:
 33 |           guideName: "cartridge_creation"
 34 |     expect:
 35 |       response:
 36 |         jsonrpc: "2.0"
 37 |         id: "cartridge-structure-1"
 38 |         result:
 39 |           content:
 40 |             - type: "text"
 41 |               text: "match:regex:\\{[\\s\\S]*\"title\"[\\s\\S]*\"description\"[\\s\\S]*\"sections\"[\\s\\S]*\"content\"[\\s\\S]*\\}"
 42 |           isError: false
 43 |       stderr: "toBeEmpty"
 44 | 
 45 |   # Test security guide retrieval
 46 |   - it: "should return security best practices guide"
 47 |     request:
 48 |       jsonrpc: "2.0"
 49 |       id: "security-guide-1"
 50 |       method: "tools/call"
 51 |       params:
 52 |         name: "get_best_practice_guide"
 53 |         arguments:
 54 |           guideName: "security"
 55 |     expect:
 56 |       response:
 57 |         jsonrpc: "2.0"
 58 |         id: "security-guide-1"
 59 |         result:
 60 |           content:
 61 |             - type: "text"
 62 |               text: "match:contains:Secure Coding Best Practices"
 63 |           isError: false
 64 |       stderr: "toBeEmpty"
 65 | 
 66 |   # Test performance guide
 67 |   - it: "should return performance optimization guide"
 68 |     request:
 69 |       jsonrpc: "2.0"
 70 |       id: "performance-guide-1"
 71 |       method: "tools/call"
 72 |       params:
 73 |         name: "get_best_practice_guide"
 74 |         arguments:
 75 |           guideName: "performance"
 76 |     expect:
 77 |       response:
 78 |         jsonrpc: "2.0"
 79 |         id: "performance-guide-1"
 80 |         result:
 81 |           content:
 82 |             - type: "text"
 83 |               text: "match:contains:performance"
 84 |           isError: false
 85 |       stderr: "toBeEmpty"
 86 | 
 87 |   # Test SFRA controllers guide
 88 |   - it: "should return SFRA controllers guide"
 89 |     request:
 90 |       jsonrpc: "2.0"
 91 |       id: "sfra-controllers-1"
 92 |       method: "tools/call"
 93 |       params:
 94 |         name: "get_best_practice_guide"
 95 |         arguments:
 96 |           guideName: "sfra_controllers"
 97 |     expect:
 98 |       response:
 99 |         jsonrpc: "2.0"
100 |         id: "sfra-controllers-1"
101 |         result:
102 |           content:
103 |             - type: "text"
104 |               text: "match:contains:SFRA"
105 |           isError: false
106 |       stderr: "toBeEmpty"
107 | 
108 |   # Test SFRA models guide
109 |   - it: "should return SFRA models guide"
110 |     request:
111 |       jsonrpc: "2.0"
112 |       id: "sfra-models-1"
113 |       method: "tools/call"
114 |       params:
115 |         name: "get_best_practice_guide"
116 |         arguments:
117 |           guideName: "sfra_models"
118 |     expect:
119 |       response:
120 |         jsonrpc: "2.0"
121 |         id: "sfra-models-1"
122 |         result:
123 |           content:
124 |             - type: "text"
125 |               text: "match:contains:SFRA"
126 |           isError: false
127 |       stderr: "toBeEmpty"
128 | 
129 |   - it: "should return SFRA client-side JavaScript guide"
130 |     request:
131 |       jsonrpc: "2.0"
132 |       id: "sfra-client-js-1"
133 |       method: "tools/call"
134 |       params:
135 |         name: "get_best_practice_guide"
136 |         arguments:
137 |           guideName: "sfra_client_side_js"
138 |     expect:
139 |       response:
140 |         jsonrpc: "2.0"
141 |         id: "sfra-client-js-1"
142 |         result:
143 |           content:
144 |             - type: "text"
145 |               text: "match:contains:Client-Side JavaScript"
146 |           isError: false
147 |       stderr: "toBeEmpty"
148 | 
149 |   - it: "should return SFRA SCSS guide"
150 |     request:
151 |       jsonrpc: "2.0"
152 |       id: "sfra-scss-1"
153 |       method: "tools/call"
154 |       params:
155 |         name: "get_best_practice_guide"
156 |         arguments:
157 |           guideName: "sfra_scss"
158 |     expect:
159 |       response:
160 |         jsonrpc: "2.0"
161 |         id: "sfra-scss-1"
162 |         result:
163 |           content:
164 |             - type: "text"
165 |               text: "match:contains:SFRA SCSS"
166 |           isError: false
167 |       stderr: "toBeEmpty"
168 | 
169 |   # Test OCAPI hooks guide
170 |   - it: "should return OCAPI hooks guide"
171 |     request:
172 |       jsonrpc: "2.0"
173 |       id: "ocapi-hooks-1"
174 |       method: "tools/call"
175 |       params:
176 |         name: "get_best_practice_guide"
177 |         arguments:
178 |           guideName: "ocapi_hooks"
179 |     expect:
180 |       response:
181 |         jsonrpc: "2.0"
182 |         id: "ocapi-hooks-1"
183 |         result:
184 |           content:
185 |             - type: "text"
186 |               text: "match:contains:OCAPI"
187 |           isError: false
188 |       stderr: "toBeEmpty"
189 | 
190 |   # Test SCAPI hooks guide
191 |   - it: "should return SCAPI hooks guide"
192 |     request:
193 |       jsonrpc: "2.0"
194 |       id: "scapi-hooks-1"
195 |       method: "tools/call"
196 |       params:
197 |         name: "get_best_practice_guide"
198 |         arguments:
199 |           guideName: "scapi_hooks"
200 |     expect:
201 |       response:
202 |         jsonrpc: "2.0"
203 |         id: "scapi-hooks-1"
204 |         result:
205 |           content:
206 |             - type: "text"
207 |               text: "match:contains:SCAPI"
208 |           isError: false
209 |       stderr: "toBeEmpty"
210 | 
211 |   # Test SCAPI custom endpoint guide
212 |   - it: "should return SCAPI custom endpoint guide"
213 |     request:
214 |       jsonrpc: "2.0"
215 |       id: "scapi-endpoint-1"
216 |       method: "tools/call"
217 |       params:
218 |         name: "get_best_practice_guide"
219 |         arguments:
220 |           guideName: "scapi_custom_endpoint"
221 |     expect:
222 |       response:
223 |         jsonrpc: "2.0"
224 |         id: "scapi-endpoint-1"
225 |         result:
226 |           content:
227 |             - type: "text"
228 |               text: "match:contains:SCAPI"
229 |           isError: false
230 |       stderr: "toBeEmpty"
231 | 
232 |   # Test ISML templates guide
233 |   - it: "should return ISML templates guide"
234 |     request:
235 |       jsonrpc: "2.0"
236 |       id: "isml-templates-1"
237 |       method: "tools/call"
238 |       params:
239 |         name: "get_best_practice_guide"
240 |         arguments:
241 |           guideName: "isml_templates"
242 |     expect:
243 |       response:
244 |         jsonrpc: "2.0"
245 |         id: "isml-templates-1"
246 |         result:
247 |           content:
248 |             - type: "text"
249 |               text: "match:contains:ISML"
250 |           isError: false
251 |       stderr: "toBeEmpty"
252 | 
253 |   # Test job framework guide
254 |   - it: "should return job framework guide"
255 |     request:
256 |       jsonrpc: "2.0"
257 |       id: "job-framework-1"
258 |       method: "tools/call"
259 |       params:
260 |         name: "get_best_practice_guide"
261 |         arguments:
262 |           guideName: "job_framework"
263 |     expect:
264 |       response:
265 |         jsonrpc: "2.0"
266 |         id: "job-framework-1"
267 |         result:
268 |           content:
269 |             - type: "text"
270 |               text: "match:contains:job"
271 |           isError: false
272 |       stderr: "toBeEmpty"
273 | 
274 |   # Test LocalServiceRegistry guide
275 |   - it: "should return LocalServiceRegistry guide"
276 |     request:
277 |       jsonrpc: "2.0"
278 |       id: "localservice-1"
279 |       method: "tools/call"
280 |       params:
281 |         name: "get_best_practice_guide"
282 |         arguments:
283 |           guideName: "localserviceregistry"
284 |     expect:
285 |       response:
286 |         jsonrpc: "2.0"
287 |         id: "localservice-1"
288 |         result:
289 |           content:
290 |             - type: "text"
291 |               text: "match:contains:LocalServiceRegistry"
292 |           isError: false
293 |       stderr: "toBeEmpty"
294 | 
295 |   # Test response time performance
296 |   - it: "should respond within reasonable time for guide retrieval"
297 |     request:
298 |       jsonrpc: "2.0"
299 |       id: "perf-guide-1"
300 |       method: "tools/call"
301 |       params:
302 |         name: "get_best_practice_guide"
303 |         arguments:
304 |           guideName: "cartridge_creation"
305 |     expect:
306 |       response:
307 |         jsonrpc: "2.0"
308 |         id: "perf-guide-1"
309 |         result:
310 |           content:
311 |             - type: "text"
312 |               text: "match:type:string"
313 |           isError: false
314 |       performance:
315 |         maxResponseTime: "2000ms"
316 |       stderr: "toBeEmpty"
317 | 
318 |   # Test invalid guide name handling
319 |   - it: "should handle invalid guide name gracefully"
320 |     request:
321 |       jsonrpc: "2.0"
322 |       id: "invalid-guide-1"
323 |       method: "tools/call"
324 |       params:
325 |         name: "get_best_practice_guide"
326 |         arguments:
327 |           guideName: "nonexistent_guide"
328 |     expect:
329 |       response:
330 |         jsonrpc: "2.0"
331 |         id: "invalid-guide-1"
332 |         result:
333 |           content:
334 |             - type: "text"
335 |               text: "null"
336 |           isError: false
337 |       stderr: "toBeEmpty"
338 | 
339 |   # Test empty guide name handling
340 |   - it: "should handle empty guide name"
341 |     request:
342 |       jsonrpc: "2.0"
343 |       id: "empty-guide-1"
344 |       method: "tools/call"
345 |       params:
346 |         name: "get_best_practice_guide"
347 |         arguments:
348 |           guideName: ""
349 |     expect:
350 |       response:
351 |         jsonrpc: "2.0"
352 |         id: "empty-guide-1"
353 |         result:
354 |           content:
355 |             - type: "text"
356 |               text: "Error: guideName must be a non-empty string"
357 |           isError: true
358 |       stderr: "toBeEmpty"
359 | 
360 |   # Test that guide contains essential structural elements
361 |   - it: "should contain essential sections in cartridge creation guide"
362 |     request:
363 |       jsonrpc: "2.0"
364 |       id: "structure-test-1"
365 |       method: "tools/call"
366 |       params:
367 |         name: "get_best_practice_guide"
368 |         arguments:
369 |           guideName: "cartridge_creation"
370 |     expect:
371 |       response:
372 |         jsonrpc: "2.0"
373 |         id: "structure-test-1"
374 |         result:
375 |           content:
376 |             - type: "text"
377 |               text: "match:contains:Core Principles"
378 |           isError: false
379 |       stderr: "toBeEmpty"
380 | 
381 |   # Test security guide contains security-specific content
382 |   - it: "should contain security-specific content in security guide"
383 |     request:
384 |       jsonrpc: "2.0"
385 |       id: "security-content-1"
386 |       method: "tools/call"
387 |       params:
388 |         name: "get_best_practice_guide"
389 |         arguments:
390 |           guideName: "security"
391 |     expect:
392 |       response:
393 |         jsonrpc: "2.0"
394 |         id: "security-content-1"
395 |         result:
396 |           content:
397 |             - type: "text"
398 |               text: "match:regex:[\\s\\S]*(CSRF|XSS|authentication|authorization)[\\s\\S]*"
399 |           isError: false
400 |       stderr: "toBeEmpty"
401 | 
402 |   # Test that guide content is substantial (not just placeholder)
403 |   - it: "should return substantial content for cartridge creation guide"
404 |     request:
405 |       jsonrpc: "2.0"
406 |       id: "content-length-1"
407 |       method: "tools/call"
408 |       params:
409 |         name: "get_best_practice_guide"
410 |         arguments:
411 |           guideName: "cartridge_creation"
412 |     expect:
413 |       response:
414 |         jsonrpc: "2.0"
415 |         id: "content-length-1"
416 |         result:
417 |           content:
418 |             - type: "text"
419 |               text: "match:regex:.{1000,}"  # At least 1000 characters
420 |           isError: false
421 |       stderr: "toBeEmpty"
422 | 
423 |   # Test that guide contains code examples for practical guides
424 |   - it: "should contain code examples in cartridge creation guide"
425 |     request:
426 |       jsonrpc: "2.0"
427 |       id: "code-examples-1"
428 |       method: "tools/call"
429 |       params:
430 |         name: "get_best_practice_guide"
431 |         arguments:
432 |           guideName: "cartridge_creation"
433 |     expect:
434 |       response:
435 |         jsonrpc: "2.0"
436 |         id: "code-examples-1"
437 |         result:
438 |           content:
439 |             - type: "text"
440 |               text: "match:regex:[\\s\\S]*(```|javascript|module\\.exports)[\\s\\S]*"
441 |           isError: false
442 |       stderr: "toBeEmpty"
443 | 
444 |   # Test missing required parameter handling
445 |   - it: "should handle missing guideName parameter"
446 |     request:
447 |       jsonrpc: "2.0"
448 |       id: "missing-param-1"
449 |       method: "tools/call"
450 |       params:
451 |         name: "get_best_practice_guide"
452 |         arguments: {}
453 |     expect:
454 |       response:
455 |         jsonrpc: "2.0"
456 |         id: "missing-param-1"
457 |         result:
458 |           content:
459 |             - type: "text"
460 |               text: "Error: guideName must be a non-empty string"
461 |           isError: true
462 |       stderr: "toBeEmpty"
463 | 
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-best-practice-guide.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
  1 | description: "Test get_best_practice_guide tool in full-mode mode"
  2 | tests:
  3 |   # Test successful guide retrieval with cartridge_creation guide
  4 |   - it: "should return cartridge creation guide with complete structure"
  5 |     request:
  6 |       jsonrpc: "2.0"
  7 |       id: "cartridge-guide-1"
  8 |       method: "tools/call"
  9 |       params:
 10 |         name: "get_best_practice_guide"
 11 |         arguments:
 12 |           guideName: "cartridge_creation"
 13 |     expect:
 14 |       response:
 15 |         jsonrpc: "2.0"
 16 |         id: "cartridge-guide-1"
 17 |         result:
 18 |           content:
 19 |             - type: "text"
 20 |               text: "match:contains:Instructions for Creating a Salesforce B2C Commerce"
 21 |           isError: false
 22 |       stderr: "toBeEmpty"
 23 | 
 24 |   # Test that the response contains proper JSON structure
 25 |   - it: "should return valid JSON with title, description, sections, and content"
 26 |     request:
 27 |       jsonrpc: "2.0"
 28 |       id: "cartridge-structure-1"
 29 |       method: "tools/call"
 30 |       params:
 31 |         name: "get_best_practice_guide"
 32 |         arguments:
 33 |           guideName: "cartridge_creation"
 34 |     expect:
 35 |       response:
 36 |         jsonrpc: "2.0"
 37 |         id: "cartridge-structure-1"
 38 |         result:
 39 |           content:
 40 |             - type: "text"
 41 |               text: "match:regex:\\{[\\s\\S]*\"title\"[\\s\\S]*\"description\"[\\s\\S]*\"sections\"[\\s\\S]*\"content\"[\\s\\S]*\\}"
 42 |           isError: false
 43 |       stderr: "toBeEmpty"
 44 | 
 45 |   # Test security guide retrieval
 46 |   - it: "should return security best practices guide"
 47 |     request:
 48 |       jsonrpc: "2.0"
 49 |       id: "security-guide-1"
 50 |       method: "tools/call"
 51 |       params:
 52 |         name: "get_best_practice_guide"
 53 |         arguments:
 54 |           guideName: "security"
 55 |     expect:
 56 |       response:
 57 |         jsonrpc: "2.0"
 58 |         id: "security-guide-1"
 59 |         result:
 60 |           content:
 61 |             - type: "text"
 62 |               text: "match:contains:Secure Coding Best Practices"
 63 |           isError: false
 64 |       stderr: "toBeEmpty"
 65 | 
 66 |   # Test performance guide
 67 |   - it: "should return performance optimization guide"
 68 |     request:
 69 |       jsonrpc: "2.0"
 70 |       id: "performance-guide-1"
 71 |       method: "tools/call"
 72 |       params:
 73 |         name: "get_best_practice_guide"
 74 |         arguments:
 75 |           guideName: "performance"
 76 |     expect:
 77 |       response:
 78 |         jsonrpc: "2.0"
 79 |         id: "performance-guide-1"
 80 |         result:
 81 |           content:
 82 |             - type: "text"
 83 |               text: "match:contains:performance"
 84 |           isError: false
 85 |       stderr: "toBeEmpty"
 86 | 
 87 |   # Test SFRA controllers guide
 88 |   - it: "should return SFRA controllers guide"
 89 |     request:
 90 |       jsonrpc: "2.0"
 91 |       id: "sfra-controllers-1"
 92 |       method: "tools/call"
 93 |       params:
 94 |         name: "get_best_practice_guide"
 95 |         arguments:
 96 |           guideName: "sfra_controllers"
 97 |     expect:
 98 |       response:
 99 |         jsonrpc: "2.0"
100 |         id: "sfra-controllers-1"
101 |         result:
102 |           content:
103 |             - type: "text"
104 |               text: "match:contains:SFRA"
105 |           isError: false
106 |       stderr: "toBeEmpty"
107 | 
108 |   # Test SFRA models guide
109 |   - it: "should return SFRA models guide"
110 |     request:
111 |       jsonrpc: "2.0"
112 |       id: "sfra-models-1"
113 |       method: "tools/call"
114 |       params:
115 |         name: "get_best_practice_guide"
116 |         arguments:
117 |           guideName: "sfra_models"
118 |     expect:
119 |       response:
120 |         jsonrpc: "2.0"
121 |         id: "sfra-models-1"
122 |         result:
123 |           content:
124 |             - type: "text"
125 |               text: "match:contains:SFRA"
126 |           isError: false
127 |       stderr: "toBeEmpty"
128 | 
129 |   - it: "should return SFRA client-side JavaScript guide"
130 |     request:
131 |       jsonrpc: "2.0"
132 |       id: "sfra-client-js-1"
133 |       method: "tools/call"
134 |       params:
135 |         name: "get_best_practice_guide"
136 |         arguments:
137 |           guideName: "sfra_client_side_js"
138 |     expect:
139 |       response:
140 |         jsonrpc: "2.0"
141 |         id: "sfra-client-js-1"
142 |         result:
143 |           content:
144 |             - type: "text"
145 |               text: "match:contains:Client-Side JavaScript"
146 |           isError: false
147 |       stderr: "toBeEmpty"
148 | 
149 |   - it: "should return SFRA SCSS guide"
150 |     request:
151 |       jsonrpc: "2.0"
152 |       id: "sfra-scss-1"
153 |       method: "tools/call"
154 |       params:
155 |         name: "get_best_practice_guide"
156 |         arguments:
157 |           guideName: "sfra_scss"
158 |     expect:
159 |       response:
160 |         jsonrpc: "2.0"
161 |         id: "sfra-scss-1"
162 |         result:
163 |           content:
164 |             - type: "text"
165 |               text: "match:contains:SFRA SCSS"
166 |           isError: false
167 |       stderr: "toBeEmpty"
168 | 
169 |   # Test OCAPI hooks guide
170 |   - it: "should return OCAPI hooks guide"
171 |     request:
172 |       jsonrpc: "2.0"
173 |       id: "ocapi-hooks-1"
174 |       method: "tools/call"
175 |       params:
176 |         name: "get_best_practice_guide"
177 |         arguments:
178 |           guideName: "ocapi_hooks"
179 |     expect:
180 |       response:
181 |         jsonrpc: "2.0"
182 |         id: "ocapi-hooks-1"
183 |         result:
184 |           content:
185 |             - type: "text"
186 |               text: "match:contains:OCAPI"
187 |           isError: false
188 |       stderr: "toBeEmpty"
189 | 
190 |   # Test SCAPI hooks guide
191 |   - it: "should return SCAPI hooks guide"
192 |     request:
193 |       jsonrpc: "2.0"
194 |       id: "scapi-hooks-1"
195 |       method: "tools/call"
196 |       params:
197 |         name: "get_best_practice_guide"
198 |         arguments:
199 |           guideName: "scapi_hooks"
200 |     expect:
201 |       response:
202 |         jsonrpc: "2.0"
203 |         id: "scapi-hooks-1"
204 |         result:
205 |           content:
206 |             - type: "text"
207 |               text: "match:contains:SCAPI"
208 |           isError: false
209 |       stderr: "toBeEmpty"
210 | 
211 |   # Test SCAPI custom endpoint guide
212 |   - it: "should return SCAPI custom endpoint guide"
213 |     request:
214 |       jsonrpc: "2.0"
215 |       id: "scapi-endpoint-1"
216 |       method: "tools/call"
217 |       params:
218 |         name: "get_best_practice_guide"
219 |         arguments:
220 |           guideName: "scapi_custom_endpoint"
221 |     expect:
222 |       response:
223 |         jsonrpc: "2.0"
224 |         id: "scapi-endpoint-1"
225 |         result:
226 |           content:
227 |             - type: "text"
228 |               text: "match:contains:SCAPI"
229 |           isError: false
230 |       stderr: "toBeEmpty"
231 | 
232 |   # Test ISML templates guide
233 |   - it: "should return ISML templates guide"
234 |     request:
235 |       jsonrpc: "2.0"
236 |       id: "isml-templates-1"
237 |       method: "tools/call"
238 |       params:
239 |         name: "get_best_practice_guide"
240 |         arguments:
241 |           guideName: "isml_templates"
242 |     expect:
243 |       response:
244 |         jsonrpc: "2.0"
245 |         id: "isml-templates-1"
246 |         result:
247 |           content:
248 |             - type: "text"
249 |               text: "match:contains:ISML"
250 |           isError: false
251 |       stderr: "toBeEmpty"
252 | 
253 |   # Test job framework guide
254 |   - it: "should return job framework guide"
255 |     request:
256 |       jsonrpc: "2.0"
257 |       id: "job-framework-1"
258 |       method: "tools/call"
259 |       params:
260 |         name: "get_best_practice_guide"
261 |         arguments:
262 |           guideName: "job_framework"
263 |     expect:
264 |       response:
265 |         jsonrpc: "2.0"
266 |         id: "job-framework-1"
267 |         result:
268 |           content:
269 |             - type: "text"
270 |               text: "match:contains:job"
271 |           isError: false
272 |       stderr: "toBeEmpty"
273 | 
274 |   # Test LocalServiceRegistry guide
275 |   - it: "should return LocalServiceRegistry guide"
276 |     request:
277 |       jsonrpc: "2.0"
278 |       id: "localservice-1"
279 |       method: "tools/call"
280 |       params:
281 |         name: "get_best_practice_guide"
282 |         arguments:
283 |           guideName: "localserviceregistry"
284 |     expect:
285 |       response:
286 |         jsonrpc: "2.0"
287 |         id: "localservice-1"
288 |         result:
289 |           content:
290 |             - type: "text"
291 |               text: "match:contains:LocalServiceRegistry"
292 |           isError: false
293 |       stderr: "toBeEmpty"
294 | 
295 |   # Test response time performance
296 |   - it: "should respond within reasonable time for guide retrieval"
297 |     request:
298 |       jsonrpc: "2.0"
299 |       id: "perf-guide-1"
300 |       method: "tools/call"
301 |       params:
302 |         name: "get_best_practice_guide"
303 |         arguments:
304 |           guideName: "cartridge_creation"
305 |     expect:
306 |       response:
307 |         jsonrpc: "2.0"
308 |         id: "perf-guide-1"
309 |         result:
310 |           content:
311 |             - type: "text"
312 |               text: "match:type:string"
313 |           isError: false
314 |       performance:
315 |         maxResponseTime: "2000ms"
316 |       stderr: "toBeEmpty"
317 | 
318 |   # Test invalid guide name handling
319 |   - it: "should handle invalid guide name gracefully"
320 |     request:
321 |       jsonrpc: "2.0"
322 |       id: "invalid-guide-1"
323 |       method: "tools/call"
324 |       params:
325 |         name: "get_best_practice_guide"
326 |         arguments:
327 |           guideName: "nonexistent_guide"
328 |     expect:
329 |       response:
330 |         jsonrpc: "2.0"
331 |         id: "invalid-guide-1"
332 |         result:
333 |           content:
334 |             - type: "text"
335 |               text: "null"
336 |           isError: false
337 |       stderr: "toBeEmpty"
338 | 
339 |   # Test empty guide name handling
340 |   - it: "should handle empty guide name"
341 |     request:
342 |       jsonrpc: "2.0"
343 |       id: "empty-guide-1"
344 |       method: "tools/call"
345 |       params:
346 |         name: "get_best_practice_guide"
347 |         arguments:
348 |           guideName: ""
349 |     expect:
350 |       response:
351 |         jsonrpc: "2.0"
352 |         id: "empty-guide-1"
353 |         result:
354 |           content:
355 |             - type: "text"
356 |               text: "Error: guideName must be a non-empty string"
357 |           isError: true
358 |       stderr: "toBeEmpty"
359 | 
360 |   # Test that guide contains essential structural elements
361 |   - it: "should contain essential sections in cartridge creation guide"
362 |     request:
363 |       jsonrpc: "2.0"
364 |       id: "structure-test-1"
365 |       method: "tools/call"
366 |       params:
367 |         name: "get_best_practice_guide"
368 |         arguments:
369 |           guideName: "cartridge_creation"
370 |     expect:
371 |       response:
372 |         jsonrpc: "2.0"
373 |         id: "structure-test-1"
374 |         result:
375 |           content:
376 |             - type: "text"
377 |               text: "match:contains:Core Principles"
378 |           isError: false
379 |       stderr: "toBeEmpty"
380 | 
381 |   # Test security guide contains security-specific content
382 |   - it: "should contain security-specific content in security guide"
383 |     request:
384 |       jsonrpc: "2.0"
385 |       id: "security-content-1"
386 |       method: "tools/call"
387 |       params:
388 |         name: "get_best_practice_guide"
389 |         arguments:
390 |           guideName: "security"
391 |     expect:
392 |       response:
393 |         jsonrpc: "2.0"
394 |         id: "security-content-1"
395 |         result:
396 |           content:
397 |             - type: "text"
398 |               text: "match:regex:[\\s\\S]*(CSRF|XSS|authentication|authorization)[\\s\\S]*"
399 |           isError: false
400 |       stderr: "toBeEmpty"
401 | 
402 |   # Test that guide content is substantial (not just placeholder)
403 |   - it: "should return substantial content for cartridge creation guide"
404 |     request:
405 |       jsonrpc: "2.0"
406 |       id: "content-length-1"
407 |       method: "tools/call"
408 |       params:
409 |         name: "get_best_practice_guide"
410 |         arguments:
411 |           guideName: "cartridge_creation"
412 |     expect:
413 |       response:
414 |         jsonrpc: "2.0"
415 |         id: "content-length-1"
416 |         result:
417 |           content:
418 |             - type: "text"
419 |               text: "match:regex:.{1000,}"  # At least 1000 characters
420 |           isError: false
421 |       stderr: "toBeEmpty"
422 | 
423 |   # Test that guide contains code examples for practical guides
424 |   - it: "should contain code examples in cartridge creation guide"
425 |     request:
426 |       jsonrpc: "2.0"
427 |       id: "code-examples-1"
428 |       method: "tools/call"
429 |       params:
430 |         name: "get_best_practice_guide"
431 |         arguments:
432 |           guideName: "cartridge_creation"
433 |     expect:
434 |       response:
435 |         jsonrpc: "2.0"
436 |         id: "code-examples-1"
437 |         result:
438 |           content:
439 |             - type: "text"
440 |               text: "match:regex:[\\s\\S]*(```|javascript|module\\.exports)[\\s\\S]*"
441 |           isError: false
442 |       stderr: "toBeEmpty"
443 | 
444 |   # Test missing required parameter handling
445 |   - it: "should handle missing guideName parameter"
446 |     request:
447 |       jsonrpc: "2.0"
448 |       id: "missing-param-1"
449 |       method: "tools/call"
450 |       params:
451 |         name: "get_best_practice_guide"
452 |         arguments: {}
453 |     expect:
454 |       response:
455 |         jsonrpc: "2.0"
456 |         id: "missing-param-1"
457 |         result:
458 |           content:
459 |             - type: "text"
460 |               text: "Error: guideName must be a non-empty string"
461 |           isError: true
462 |       stderr: "toBeEmpty"
463 | 
```

--------------------------------------------------------------------------------
/tests/validator.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Tests for Validator utility
  3 |  * Tests input validation functionality
  4 |  */
  5 | 
  6 | import { Validator, ValidationError } from '../src/utils/validator.js';
  7 | 
  8 | describe('Validator', () => {
  9 |   describe('ValidationError', () => {
 10 |     it('should create validation error with message', () => {
 11 |       const error = new ValidationError('Test error message');
 12 |       expect(error).toBeInstanceOf(Error);
 13 |       expect(error.name).toBe('ValidationError');
 14 |       expect(error.message).toBe('Test error message');
 15 |     });
 16 |   });
 17 | 
 18 |   describe('validateRequired', () => {
 19 |     it('should pass when all required fields are present', () => {
 20 |       const params = {
 21 |         field1: 'value1',
 22 |         field2: 'value2',
 23 |         field3: 123,
 24 |       };
 25 | 
 26 |       expect(() => {
 27 |         Validator.validateRequired(params, ['field1', 'field2']);
 28 |       }).not.toThrow();
 29 |     });
 30 | 
 31 |     it('should throw error when required field is missing', () => {
 32 |       const params = {
 33 |         field1: 'value1',
 34 |       };
 35 | 
 36 |       expect(() => {
 37 |         Validator.validateRequired(params, ['field1', 'field2']);
 38 |       }).toThrow(ValidationError);
 39 |       expect(() => {
 40 |         Validator.validateRequired(params, ['field1', 'field2']);
 41 |       }).toThrow('Required fields are missing or empty: field2');
 42 |     });
 43 | 
 44 |     it('should throw error when required field is empty string', () => {
 45 |       const params = {
 46 |         field1: 'value1',
 47 |         field2: '',
 48 |       };
 49 | 
 50 |       expect(() => {
 51 |         Validator.validateRequired(params, ['field1', 'field2']);
 52 |       }).toThrow('Required fields are missing or empty: field2');
 53 |     });
 54 | 
 55 |     it('should throw error when required field is whitespace only', () => {
 56 |       const params = {
 57 |         field1: 'value1',
 58 |         field2: '   ',
 59 |       };
 60 | 
 61 |       expect(() => {
 62 |         Validator.validateRequired(params, ['field1', 'field2']);
 63 |       }).toThrow('Required fields are missing or empty: field2');
 64 |     });
 65 | 
 66 |     it('should throw error for multiple missing fields', () => {
 67 |       const params = {
 68 |         field1: 'value1',
 69 |       };
 70 | 
 71 |       expect(() => {
 72 |         Validator.validateRequired(params, ['field2', 'field3', 'field4']);
 73 |       }).toThrow('Required fields are missing or empty: field2, field3, field4');
 74 |     });
 75 | 
 76 |     it('should handle non-string values correctly', () => {
 77 |       const params = {
 78 |         number: 0,
 79 |         boolean: false,
 80 |         object: {},
 81 |         array: [],
 82 |       };
 83 | 
 84 |       expect(() => {
 85 |         Validator.validateRequired(params, ['number', 'boolean', 'object', 'array']);
 86 |       }).not.toThrow();
 87 |     });
 88 |   });
 89 | 
 90 |   describe('validateInstanceType', () => {
 91 |     it('should accept valid instance types', () => {
 92 |       expect(Validator.validateInstanceType('staging')).toBe('staging');
 93 |       expect(Validator.validateInstanceType('development')).toBe('development');
 94 |       expect(Validator.validateInstanceType('sandbox')).toBe('sandbox');
 95 |       expect(Validator.validateInstanceType('production')).toBe('production');
 96 |     });
 97 | 
 98 |     it('should throw error for invalid instance type', () => {
 99 |       expect(() => {
100 |         Validator.validateInstanceType('invalid');
101 |       }).toThrow(ValidationError);
102 |       expect(() => {
103 |         Validator.validateInstanceType('invalid');
104 |       }).toThrow('Invalid instance type \'invalid\'. Must be one of: staging, development, sandbox, production');
105 |     });
106 | 
107 |     it('should throw error for empty string', () => {
108 |       expect(() => {
109 |         Validator.validateInstanceType('');
110 |       }).toThrow('Invalid instance type \'\'. Must be one of: staging, development, sandbox, production');
111 |     });
112 | 
113 |     it('should throw error for case-sensitive mismatch', () => {
114 |       expect(() => {
115 |         Validator.validateInstanceType('STAGING');
116 |       }).toThrow('Invalid instance type \'STAGING\'. Must be one of: staging, development, sandbox, production');
117 |     });
118 |   });
119 | 
120 |   describe('validateNotEmpty', () => {
121 |     it('should pass for non-empty string', () => {
122 |       expect(() => {
123 |         Validator.validateNotEmpty('valid value', 'testField');
124 |       }).not.toThrow();
125 |     });
126 | 
127 |     it('should throw error for empty string', () => {
128 |       expect(() => {
129 |         Validator.validateNotEmpty('', 'testField');
130 |       }).toThrow(ValidationError);
131 |       expect(() => {
132 |         Validator.validateNotEmpty('', 'testField');
133 |       }).toThrow('testField cannot be empty');
134 |     });
135 | 
136 |     it('should throw error for whitespace-only string', () => {
137 |       expect(() => {
138 |         Validator.validateNotEmpty('   ', 'testField');
139 |       }).toThrow('testField cannot be empty');
140 |     });
141 |   });
142 | 
143 |   describe('validatePositiveNumber', () => {
144 |     it('should pass for positive numbers', () => {
145 |       expect(() => {
146 |         Validator.validatePositiveNumber(1, 'testField');
147 |         Validator.validatePositiveNumber(100, 'testField');
148 |         Validator.validatePositiveNumber(0.5, 'testField');
149 |       }).not.toThrow();
150 |     });
151 | 
152 |     it('should pass for zero', () => {
153 |       expect(() => {
154 |         Validator.validatePositiveNumber(0, 'testField');
155 |       }).not.toThrow();
156 |     });
157 | 
158 |     it('should throw error for negative numbers', () => {
159 |       expect(() => {
160 |         Validator.validatePositiveNumber(-1, 'testField');
161 |       }).toThrow(ValidationError);
162 |       expect(() => {
163 |         Validator.validatePositiveNumber(-1, 'testField');
164 |       }).toThrow('testField must be a positive number');
165 |     });
166 |   });
167 | 
168 |   describe('validateObjectType', () => {
169 |     it('should pass for valid object types', () => {
170 |       expect(() => {
171 |         Validator.validateObjectType('Product');
172 |         Validator.validateObjectType('Customer');
173 |         Validator.validateObjectType('SitePreferences');
174 |         Validator.validateObjectType('Custom_Object');
175 |         Validator.validateObjectType('MyObject123');
176 |       }).not.toThrow();
177 |     });
178 | 
179 |     it('should throw error for empty object type', () => {
180 |       expect(() => {
181 |         Validator.validateObjectType('');
182 |       }).toThrow('objectType cannot be empty');
183 |     });
184 | 
185 |     it('should throw error for object type starting with number', () => {
186 |       expect(() => {
187 |         Validator.validateObjectType('123Object');
188 |       }).toThrow('Invalid object type \'123Object\'. Must start with a letter and contain only letters, numbers, and underscores.');
189 |     });
190 | 
191 |     it('should throw error for object type with special characters', () => {
192 |       expect(() => {
193 |         Validator.validateObjectType('Product-Type');
194 |       }).toThrow('Invalid object type \'Product-Type\'. Must start with a letter and contain only letters, numbers, and underscores.');
195 |     });
196 | 
197 |     it('should throw error for object type with spaces', () => {
198 |       expect(() => {
199 |         Validator.validateObjectType('Product Type');
200 |       }).toThrow('Invalid object type \'Product Type\'. Must start with a letter and contain only letters, numbers, and underscores.');
201 |     });
202 |   });
203 | 
204 |   describe('validateSearchRequest', () => {
205 |     it('should pass for valid search request with text_query', () => {
206 |       const searchRequest = {
207 |         query: {
208 |           text_query: {
209 |             fields: ['id', 'display_name'],
210 |             search_phrase: 'test',
211 |           },
212 |         },
213 |       };
214 | 
215 |       expect(() => {
216 |         Validator.validateSearchRequest(searchRequest);
217 |       }).not.toThrow();
218 |     });
219 | 
220 |     it('should pass for valid search request with term_query', () => {
221 |       const searchRequest = {
222 |         query: {
223 |           term_query: {
224 |             fields: ['value_type'],
225 |             operator: 'is',
226 |             values: ['string'],
227 |           },
228 |         },
229 |       };
230 | 
231 |       expect(() => {
232 |         Validator.validateSearchRequest(searchRequest);
233 |       }).not.toThrow();
234 |     });
235 | 
236 |     it('should pass for valid search request with match_all_query', () => {
237 |       const searchRequest = {
238 |         query: {
239 |           match_all_query: {},
240 |         },
241 |       };
242 | 
243 |       expect(() => {
244 |         Validator.validateSearchRequest(searchRequest);
245 |       }).not.toThrow();
246 |     });
247 | 
248 |     it('should pass for search request with sorts', () => {
249 |       const searchRequest = {
250 |         query: {
251 |           match_all_query: {},
252 |         },
253 |         sorts: [
254 |           { field: 'id', sort_order: 'asc' },
255 |           { field: 'display_name' },
256 |         ],
257 |       };
258 | 
259 |       expect(() => {
260 |         Validator.validateSearchRequest(searchRequest);
261 |       }).not.toThrow();
262 |     });
263 | 
264 |     it('should pass for search request with pagination', () => {
265 |       const searchRequest = {
266 |         query: {
267 |           match_all_query: {},
268 |         },
269 |         start: 0,
270 |         count: 25,
271 |       };
272 | 
273 |       expect(() => {
274 |         Validator.validateSearchRequest(searchRequest);
275 |       }).not.toThrow();
276 |     });
277 | 
278 |     it('should throw error for non-object search request', () => {
279 |       expect(() => {
280 |         Validator.validateSearchRequest('invalid');
281 |       }).toThrow('Search request must be a valid object');
282 |     });
283 | 
284 |     it('should throw error for null search request', () => {
285 |       expect(() => {
286 |         Validator.validateSearchRequest(null);
287 |       }).toThrow('Search request must be a valid object');
288 |     });
289 | 
290 |     it('should throw error for search request with no valid query types', () => {
291 |       const searchRequest = {
292 |         query: {
293 |           invalid_query: {},
294 |         },
295 |       };
296 | 
297 |       expect(() => {
298 |         Validator.validateSearchRequest(searchRequest);
299 |       }).toThrow('Search query must contain at least one of: text_query, term_query, filtered_query, bool_query, match_all_query');
300 |     });
301 | 
302 |     it('should throw error for text_query with empty fields', () => {
303 |       const searchRequest = {
304 |         query: {
305 |           text_query: {
306 |             fields: [],
307 |             search_phrase: 'test',
308 |           },
309 |         },
310 |       };
311 | 
312 |       expect(() => {
313 |         Validator.validateSearchRequest(searchRequest);
314 |       }).toThrow('text_query.fields must be a non-empty array');
315 |     });
316 | 
317 |     it('should throw error for text_query with missing search_phrase', () => {
318 |       const searchRequest = {
319 |         query: {
320 |           text_query: {
321 |             fields: ['id'],
322 |           },
323 |         },
324 |       };
325 | 
326 |       expect(() => {
327 |         Validator.validateSearchRequest(searchRequest);
328 |       }).toThrow('text_query.search_phrase must be a non-empty string');
329 |     });
330 | 
331 |     it('should throw error for term_query with invalid structure', () => {
332 |       const searchRequest = {
333 |         query: {
334 |           term_query: {
335 |             fields: [],
336 |             operator: 'is',
337 |             values: ['test'],
338 |           },
339 |         },
340 |       };
341 | 
342 |       expect(() => {
343 |         Validator.validateSearchRequest(searchRequest);
344 |       }).toThrow('term_query.fields must be a non-empty array');
345 |     });
346 | 
347 |     it('should throw error for invalid sorts structure', () => {
348 |       const searchRequest = {
349 |         query: {
350 |           match_all_query: {},
351 |         },
352 |         sorts: 'invalid',
353 |       };
354 | 
355 |       expect(() => {
356 |         Validator.validateSearchRequest(searchRequest);
357 |       }).toThrow('sorts must be an array');
358 |     });
359 | 
360 |     it('should throw error for sort with missing field', () => {
361 |       const searchRequest = {
362 |         query: {
363 |           match_all_query: {},
364 |         },
365 |         sorts: [
366 |           { sort_order: 'asc' },
367 |         ],
368 |       };
369 | 
370 |       expect(() => {
371 |         Validator.validateSearchRequest(searchRequest);
372 |       }).toThrow('sorts[0].field must be a non-empty string');
373 |     });
374 | 
375 |     it('should throw error for sort with invalid sort_order', () => {
376 |       const searchRequest = {
377 |         query: {
378 |           match_all_query: {},
379 |         },
380 |         sorts: [
381 |           { field: 'id', sort_order: 'invalid' },
382 |         ],
383 |       };
384 | 
385 |       expect(() => {
386 |         Validator.validateSearchRequest(searchRequest);
387 |       }).toThrow('sorts[0].sort_order must be either \'asc\' or \'desc\'');
388 |     });
389 | 
390 |     it('should throw error for negative start value', () => {
391 |       const searchRequest = {
392 |         query: {
393 |           match_all_query: {},
394 |         },
395 |         start: -1,
396 |       };
397 | 
398 |       expect(() => {
399 |         Validator.validateSearchRequest(searchRequest);
400 |       }).toThrow('start must be a positive number');
401 |     });
402 | 
403 |     it('should throw error for negative count value', () => {
404 |       const searchRequest = {
405 |         query: {
406 |           match_all_query: {},
407 |         },
408 |         count: -5,
409 |       };
410 | 
411 |       expect(() => {
412 |         Validator.validateSearchRequest(searchRequest);
413 |       }).toThrow('count must be a positive number');
414 |     });
415 |   });
416 | });
417 | 
```

--------------------------------------------------------------------------------
/docs/dw_web/HttpParameter.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.web
  2 | 
  3 | # Class HttpParameter
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.web.HttpParameter
  9 | 
 10 | ## Description
 11 | 
 12 | Represents an HTTP parameter.
 13 | 
 14 | ## Properties
 15 | 
 16 | ### booleanValue
 17 | 
 18 | **Type:** boolean (Read Only)
 19 | 
 20 | The value of the current HttpParameter attribute as a boolean. If
 21 |  there is more than one value defined, only the first one is returned. For an
 22 |  undefined attribute it returns null.
 23 | 
 24 | ### dateValue
 25 | 
 26 | **Type:** Date (Read Only)
 27 | 
 28 | The value of the current HttpParameter attribute as a date. If
 29 |  there is more than one value defined, only the first one is returned. For
 30 |  an undefined attribute and if attribute is not a date it return null.
 31 | 
 32 | ### doubleValue
 33 | 
 34 | **Type:** Number (Read Only)
 35 | 
 36 | The value of the current HttpParameter attribute as a number. If
 37 |  there is more than one value defined, only the first one is returned. For
 38 |  an undefined attribute it returns 0.0.
 39 | 
 40 | ### empty
 41 | 
 42 | **Type:** boolean (Read Only)
 43 | 
 44 | Identifies if there is a value for the http parameter attribute
 45 |  and whether the value is empty.
 46 |  A value is treated as empty if it's not blank.
 47 | 
 48 | ### intValue
 49 | 
 50 | **Type:** Number (Read Only)
 51 | 
 52 | The value of the current HttpParameter attribute as int. If there
 53 |  is more than one value defined, only the first one is returned. For an
 54 |  undefined attribute it returns null.
 55 | 
 56 | ### rawValue
 57 | 
 58 | **Type:** String (Read Only)
 59 | 
 60 | The raw value for this HttpParameter instance.
 61 |  The raw value is the not trimmed String value of this HTTP parameter.
 62 |  If there is more than one value defined, only the first one is returned. For an
 63 |  undefined attribute the method returns a null.
 64 | 
 65 | ### rawValues
 66 | 
 67 | **Type:** Collection (Read Only)
 68 | 
 69 | A Collection of all raw values for this HTTP parameter.
 70 |  The raw value is the not trimmed String value of this HTTP parameter.
 71 | 
 72 | ### stringValue
 73 | 
 74 | **Type:** String (Read Only)
 75 | 
 76 | The value of the current HttpParameter attribute. If there is
 77 |  more than one value defined, only the first one is returned. For an
 78 |  undefined attribute the method returns a null.
 79 | 
 80 | ### stringValues
 81 | 
 82 | **Type:** Collection (Read Only)
 83 | 
 84 | A Collection of all defined values for this HTTP parameter.
 85 | 
 86 | ### submitted
 87 | 
 88 | **Type:** boolean (Read Only)
 89 | 
 90 | Identifies if the parameter was submitted. This is equivalent to the
 91 |  check, whether the parameter has a value.
 92 | 
 93 | ### value
 94 | 
 95 | **Type:** String (Read Only)
 96 | 
 97 | The value of the current HttpParameter attribute. If there is
 98 |  more than one value defined, only the first one is returned. For an
 99 |  undefined attribute the method returns null.
100 | 
101 | ### values
102 | 
103 | **Type:** Collection (Read Only)
104 | 
105 | A Collection of all defined values for this current HTTP parameter.
106 | 
107 | ## Constructor Summary
108 | 
109 | ## Method Summary
110 | 
111 | ### containsStringValue
112 | 
113 | **Signature:** `containsStringValue(value : String) : boolean`
114 | 
115 | Identifies if the given value is part of the actual values.
116 | 
117 | ### getBooleanValue
118 | 
119 | **Signature:** `getBooleanValue() : boolean`
120 | 
121 | Returns the value of the current HttpParameter attribute as a boolean.
122 | 
123 | ### getBooleanValue
124 | 
125 | **Signature:** `getBooleanValue(defaultValue : boolean) : boolean`
126 | 
127 | Returns the value of the current HttpParameter attribute as a boolean.
128 | 
129 | ### getDateValue
130 | 
131 | **Signature:** `getDateValue() : Date`
132 | 
133 | Returns the value of the current HttpParameter attribute as a date.
134 | 
135 | ### getDateValue
136 | 
137 | **Signature:** `getDateValue(defaultValue : Date) : Date`
138 | 
139 | Returns the value of the current HttpParameter attribute as a date.
140 | 
141 | ### getDoubleValue
142 | 
143 | **Signature:** `getDoubleValue() : Number`
144 | 
145 | Returns the value of the current HttpParameter attribute as a number.
146 | 
147 | ### getDoubleValue
148 | 
149 | **Signature:** `getDoubleValue(defaultValue : Number) : Number`
150 | 
151 | Returns the value of the current HttpParameter attribute as a number.
152 | 
153 | ### getIntValue
154 | 
155 | **Signature:** `getIntValue() : Number`
156 | 
157 | Returns the value of the current HttpParameter attribute as int.
158 | 
159 | ### getIntValue
160 | 
161 | **Signature:** `getIntValue(defaultValue : Number) : Number`
162 | 
163 | Returns the value of the current HttpParameter attribute as an integer.
164 | 
165 | ### getRawValue
166 | 
167 | **Signature:** `getRawValue() : String`
168 | 
169 | Returns the raw value for this HttpParameter instance.
170 | 
171 | ### getRawValues
172 | 
173 | **Signature:** `getRawValues() : Collection`
174 | 
175 | Returns a Collection of all raw values for this HTTP parameter.
176 | 
177 | ### getStringValue
178 | 
179 | **Signature:** `getStringValue() : String`
180 | 
181 | Returns the value of the current HttpParameter attribute.
182 | 
183 | ### getStringValue
184 | 
185 | **Signature:** `getStringValue(defaultValue : String) : String`
186 | 
187 | Returns the value of the current HttpParameter attribute.
188 | 
189 | ### getStringValues
190 | 
191 | **Signature:** `getStringValues() : Collection`
192 | 
193 | Returns a Collection of all defined values for this HTTP parameter.
194 | 
195 | ### getValue
196 | 
197 | **Signature:** `getValue() : String`
198 | 
199 | Returns the value of the current HttpParameter attribute.
200 | 
201 | ### getValues
202 | 
203 | **Signature:** `getValues() : Collection`
204 | 
205 | Returns a Collection of all defined values for this current HTTP parameter.
206 | 
207 | ### isChecked
208 | 
209 | **Signature:** `isChecked(value : String) : boolean`
210 | 
211 | Identifies if the given String is an actual value of this http parameter.
212 | 
213 | ### isEmpty
214 | 
215 | **Signature:** `isEmpty() : boolean`
216 | 
217 | Identifies if there is a value for the http parameter attribute and whether the value is empty.
218 | 
219 | ### isSubmitted
220 | 
221 | **Signature:** `isSubmitted() : boolean`
222 | 
223 | Identifies if the parameter was submitted.
224 | 
225 | ### toString
226 | 
227 | **Signature:** `toString() : String`
228 | 
229 | Returns the value of the current HttpParameter attribute.
230 | 
231 | ## Method Detail
232 | 
233 | ## Method Details
234 | 
235 | ### containsStringValue
236 | 
237 | **Signature:** `containsStringValue(value : String) : boolean`
238 | 
239 | **Description:** Identifies if the given value is part of the actual values.
240 | 
241 | **Parameters:**
242 | 
243 | - `value`: the value to check.
244 | 
245 | **Returns:**
246 | 
247 | true if the value is among the actual values, false otherwise.
248 | 
249 | ---
250 | 
251 | ### getBooleanValue
252 | 
253 | **Signature:** `getBooleanValue() : boolean`
254 | 
255 | **Description:** Returns the value of the current HttpParameter attribute as a boolean. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns null.
256 | 
257 | **Returns:**
258 | 
259 | the actual value as a boolean or null of no value is available.
260 | 
261 | ---
262 | 
263 | ### getBooleanValue
264 | 
265 | **Signature:** `getBooleanValue(defaultValue : boolean) : boolean`
266 | 
267 | **Description:** Returns the value of the current HttpParameter attribute as a boolean. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns the given default value.
268 | 
269 | **Parameters:**
270 | 
271 | - `defaultValue`: the default value to use.
272 | 
273 | **Returns:**
274 | 
275 | the value of the parameter or the default value if empty.
276 | 
277 | ---
278 | 
279 | ### getDateValue
280 | 
281 | **Signature:** `getDateValue() : Date`
282 | 
283 | **Description:** Returns the value of the current HttpParameter attribute as a date. If there is more than one value defined, only the first one is returned. For an undefined attribute and if attribute is not a date it return null.
284 | 
285 | **Returns:**
286 | 
287 | the actual value as date or null if empty.
288 | 
289 | ---
290 | 
291 | ### getDateValue
292 | 
293 | **Signature:** `getDateValue(defaultValue : Date) : Date`
294 | 
295 | **Description:** Returns the value of the current HttpParameter attribute as a date. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns the given default value and if the attributes is not a date it returns null.
296 | 
297 | **Parameters:**
298 | 
299 | - `defaultValue`: the default value to use.
300 | 
301 | **Returns:**
302 | 
303 | the data value of the attribute or the default value if empty
304 | 
305 | ---
306 | 
307 | ### getDoubleValue
308 | 
309 | **Signature:** `getDoubleValue() : Number`
310 | 
311 | **Description:** Returns the value of the current HttpParameter attribute as a number. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns 0.0.
312 | 
313 | **Returns:**
314 | 
315 | the actual value as double or null if the parameter has no value.
316 | 
317 | ---
318 | 
319 | ### getDoubleValue
320 | 
321 | **Signature:** `getDoubleValue(defaultValue : Number) : Number`
322 | 
323 | **Description:** Returns the value of the current HttpParameter attribute as a number. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns the given default value.
324 | 
325 | **Parameters:**
326 | 
327 | - `defaultValue`: the default value to use.
328 | 
329 | **Returns:**
330 | 
331 | the actual value as double or the default value if empty.
332 | 
333 | ---
334 | 
335 | ### getIntValue
336 | 
337 | **Signature:** `getIntValue() : Number`
338 | 
339 | **Description:** Returns the value of the current HttpParameter attribute as int. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns null.
340 | 
341 | **Returns:**
342 | 
343 | the actual value as an integer or null of no value is available.
344 | 
345 | ---
346 | 
347 | ### getIntValue
348 | 
349 | **Signature:** `getIntValue(defaultValue : Number) : Number`
350 | 
351 | **Description:** Returns the value of the current HttpParameter attribute as an integer. If there is more than one value defined, only the first one is returned. For an undefined attribute it returns the given default value.
352 | 
353 | **Parameters:**
354 | 
355 | - `defaultValue`: the default value to use.
356 | 
357 | **Returns:**
358 | 
359 | the value of the parameter or the default value if empty.
360 | 
361 | ---
362 | 
363 | ### getRawValue
364 | 
365 | **Signature:** `getRawValue() : String`
366 | 
367 | **Description:** Returns the raw value for this HttpParameter instance. The raw value is the not trimmed String value of this HTTP parameter. If there is more than one value defined, only the first one is returned. For an undefined attribute the method returns a null.
368 | 
369 | **Returns:**
370 | 
371 | the actual value or null.
372 | 
373 | **See Also:**
374 | 
375 | getStringValue()
376 | 
377 | ---
378 | 
379 | ### getRawValues
380 | 
381 | **Signature:** `getRawValues() : Collection`
382 | 
383 | **Description:** Returns a Collection of all raw values for this HTTP parameter. The raw value is the not trimmed String value of this HTTP parameter.
384 | 
385 | **Returns:**
386 | 
387 | the raw values as a Collection of String, might be empty
388 | 
389 | **See Also:**
390 | 
391 | getStringValues()
392 | 
393 | ---
394 | 
395 | ### getStringValue
396 | 
397 | **Signature:** `getStringValue() : String`
398 | 
399 | **Description:** Returns the value of the current HttpParameter attribute. If there is more than one value defined, only the first one is returned. For an undefined attribute the method returns a null.
400 | 
401 | **Returns:**
402 | 
403 | the actual value or null.
404 | 
405 | ---
406 | 
407 | ### getStringValue
408 | 
409 | **Signature:** `getStringValue(defaultValue : String) : String`
410 | 
411 | **Description:** Returns the value of the current HttpParameter attribute. If there is more than one value defined, only the first one is returned. For an undefined attribute the method returns the given default value.
412 | 
413 | **Parameters:**
414 | 
415 | - `defaultValue`: the default value to use.
416 | 
417 | **Returns:**
418 | 
419 | the actual value or the default value.
420 | 
421 | ---
422 | 
423 | ### getStringValues
424 | 
425 | **Signature:** `getStringValues() : Collection`
426 | 
427 | **Description:** Returns a Collection of all defined values for this HTTP parameter.
428 | 
429 | **Returns:**
430 | 
431 | the actual values as Collection.
432 | 
433 | ---
434 | 
435 | ### getValue
436 | 
437 | **Signature:** `getValue() : String`
438 | 
439 | **Description:** Returns the value of the current HttpParameter attribute. If there is more than one value defined, only the first one is returned. For an undefined attribute the method returns null.
440 | 
441 | **Returns:**
442 | 
443 | the actual value or null.
444 | 
445 | ---
446 | 
447 | ### getValues
448 | 
449 | **Signature:** `getValues() : Collection`
450 | 
451 | **Description:** Returns a Collection of all defined values for this current HTTP parameter.
452 | 
453 | **Returns:**
454 | 
455 | the actual values as Collection.
456 | 
457 | **See Also:**
458 | 
459 | getStringValues()
460 | 
461 | ---
462 | 
463 | ### isChecked
464 | 
465 | **Signature:** `isChecked(value : String) : boolean`
466 | 
467 | **Description:** Identifies if the given String is an actual value of this http parameter.
468 | 
469 | **Parameters:**
470 | 
471 | - `value`: the value to check.
472 | 
473 | **Returns:**
474 | 
475 | true if the value is among the actual values, false otherwise.
476 | 
477 | ---
478 | 
479 | ### isEmpty
480 | 
481 | **Signature:** `isEmpty() : boolean`
482 | 
483 | **Description:** Identifies if there is a value for the http parameter attribute and whether the value is empty. A value is treated as empty if it's not blank.
484 | 
485 | **Returns:**
486 | 
487 | true if a value is empty, false otherwise.
488 | 
489 | ---
490 | 
491 | ### isSubmitted
492 | 
493 | **Signature:** `isSubmitted() : boolean`
494 | 
495 | **Description:** Identifies if the parameter was submitted. This is equivalent to the check, whether the parameter has a value.
496 | 
497 | **Returns:**
498 | 
499 | true if a value is there, false otherwise.
500 | 
501 | ---
502 | 
503 | ### toString
504 | 
505 | **Signature:** `toString() : String`
506 | 
507 | **Description:** Returns the value of the current HttpParameter attribute. If there is more than one value defined, only the first one is returned. For an undefined attribute the method returns an empty string.
508 | 
509 | **Returns:**
510 | 
511 | the actual value or an empty String.
512 | 
513 | ---
```

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/src/routes/ocapi/site-preferences-handler.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Site Preferences Handler
  3 |  * 
  4 |  * Handles site preferences search operations for OCAPI endpoints.
  5 |  */
  6 | 
  7 | const express = require('express');
  8 | 
  9 | class SitePreferencesHandler {
 10 |     constructor(config, dataLoader) {
 11 |         this.config = config;
 12 |         this.ocapiConfig = config.getOcapiConfig();
 13 |         this.dataLoader = dataLoader;
 14 |         this.router = express.Router();
 15 |         this.setupRoutes();
 16 |     }
 17 | 
 18 |     setupRoutes() {
 19 |         // Site Preferences Search - proper SFCC route pattern
 20 |         this.router.post(`/s/-/dw/data/${this.ocapiConfig.version}/site_preferences/preference_groups/:groupId/:instanceType/preference_search`, 
 21 |             this.handleSearchSitePreferences.bind(this)
 22 |         );
 23 |     }
 24 | 
 25 |     /**
 26 |      * Handle site preferences search
 27 |      */
 28 |     async handleSearchSitePreferences(req, res) {
 29 |         const { groupId, instanceType } = req.params;
 30 |         const searchRequest = req.body;
 31 |         
 32 |         try {
 33 |             // Validate input parameters
 34 |             const validationError = this.validateSearchRequest(searchRequest, groupId, instanceType);
 35 |             if (validationError) {
 36 |                 return res.status(validationError.status).json(validationError.body);
 37 |             }
 38 |             
 39 |             // Try to load specific site preferences
 40 |             let mockData = this.dataLoader.loadOcapiData(`site-preferences-${groupId.toLowerCase()}.json`);
 41 |             
 42 |             // Check if group exists (simulate 404 for unknown groups that aren't in our known list)
 43 |             const knownGroups = ['ccv', 'fastforward', 'system', 'storefront', 'sfra', 'integration'];
 44 |             if (!mockData && !knownGroups.includes(groupId.toLowerCase())) {
 45 |                 return res.status(404).json({
 46 |                     "_v": "23.2",
 47 |                     "fault": {
 48 |                         "arguments": {"preferenceGroupId": groupId},
 49 |                         "type": "CustomPreferenceGroupNotFoundException",
 50 |                         "message": `No preference group with ID '${groupId}' could be found.`
 51 |                     }
 52 |                 });
 53 |             }
 54 |             
 55 |             if (!mockData) {
 56 |                 // Create fallback data with proper SFCC format (matching real API)
 57 |                 mockData = {
 58 |                     "_v": "23.2",
 59 |                     "_type": "preference_value_search_result",
 60 |                     "count": 0,
 61 |                     "hits": [],
 62 |                     "query": searchRequest.query || {"match_all_query": {"_type": "match_all_query"}},
 63 |                     "select": searchRequest.select || "(**)",
 64 |                     "start": 0,
 65 |                     "total": 0
 66 |                 };
 67 |             }
 68 | 
 69 |             // Apply search and pagination
 70 |             let results = mockData.hits || [];
 71 |             
 72 |             // Apply search filtering
 73 |             results = this.applySearchFiltering(results, searchRequest.query);
 74 |             
 75 |             // Apply pagination
 76 |             const start = searchRequest.start || 0;
 77 |             const count = searchRequest.count || 200;
 78 |             const paginatedResults = results.slice(start, start + count);
 79 | 
 80 |             // Build response with proper SFCC format
 81 |             const response = {
 82 |                 "_v": mockData._v || "23.2",
 83 |                 "_type": "preference_value_search_result",
 84 |                 "count": paginatedResults.length,
 85 |                 "hits": paginatedResults,
 86 |                 "query": this.enhanceQueryWithTypes(searchRequest.query || {"match_all_query": {"_type": "match_all_query"}}),
 87 |                 "select": searchRequest.select || "(**)",
 88 |                 "start": start,
 89 |                 "total": results.length
 90 |             };
 91 | 
 92 |             res.json(response);
 93 |         } catch (error) {
 94 |             // Handle unexpected errors
 95 |             res.status(500).json({
 96 |                 "_v": "23.2",
 97 |                 "fault": {
 98 |                     "type": "InternalServerError",
 99 |                     "message": "An internal server error occurred while processing your request."
100 |                 }
101 |             });
102 |         }
103 |     }
104 | 
105 |     /**
106 |      * Validate search request parameters
107 |      */
108 |     validateSearchRequest(searchRequest, groupId, instanceType) {
109 |         // Validate pagination parameters
110 |         if (searchRequest.start && searchRequest.start < 0) {
111 |             return {
112 |                 status: 400,
113 |                 body: {
114 |                     "_v": "23.2",
115 |                     "fault": {
116 |                         "arguments": {"path": "$.start", "document": "search_request"},
117 |                         "type": "PropertyConstraintViolationException",
118 |                         "message": "An error occurred while decoding the request. There's a value constraint violation of property '$.start' in document 'search_request'."
119 |                     }
120 |                 }
121 |             };
122 |         }
123 | 
124 |         if (searchRequest.count && (searchRequest.count < 0 || searchRequest.count > 200)) {
125 |             return {
126 |                 status: 400,
127 |                 body: {
128 |                     "_v": "23.2",
129 |                     "fault": {
130 |                         "arguments": {"path": "$.count", "document": "search_request"},
131 |                         "type": "PropertyConstraintViolationException",
132 |                         "message": "An error occurred while decoding the request. There's a value constraint violation of property '$.count' in document 'search_request'."
133 |                     }
134 |                 }
135 |             };
136 |         }
137 | 
138 |         // Validate query structure
139 |         if (searchRequest.query && !this.isValidQueryStructure(searchRequest.query)) {
140 |             return {
141 |                 status: 400,
142 |                 body: {
143 |                     "_v": "23.2",
144 |                     "fault": {
145 |                         "type": "InvalidQueryException",
146 |                         "message": "Search query must contain at least one of: text_query, term_query, filtered_query, bool_query, match_all_query"
147 |                     }
148 |                 }
149 |             };
150 |         }
151 | 
152 |         // Validate text query parameters
153 |         if (searchRequest.query?.text_query) {
154 |             const textQuery = searchRequest.query.text_query;
155 |             if (!textQuery.search_phrase || textQuery.search_phrase.trim() === '') {
156 |                 return {
157 |                     status: 400,
158 |                     body: {
159 |                         "_v": "23.2",
160 |                         "fault": {
161 |                             "type": "ValidationException",
162 |                             "message": "text_query.search_phrase must be a non-empty string"
163 |                         }
164 |                     }
165 |                 };
166 |             }
167 |         }
168 | 
169 |         return null; // No validation errors
170 |     }
171 | 
172 |     /**
173 |      * Check if query structure is valid
174 |      */
175 |     isValidQueryStructure(query) {
176 |         const validQueryTypes = ['text_query', 'term_query', 'filtered_query', 'bool_query', 'match_all_query'];
177 |         return validQueryTypes.some(type => query.hasOwnProperty(type));
178 |     }
179 | 
180 |     /**
181 |      * Apply search filtering based on query
182 |      */
183 |     applySearchFiltering(results, query) {
184 |         if (!query) return results;
185 | 
186 |         if (query.text_query) {
187 |             return this.applyTextQuery(results, query.text_query);
188 |         }
189 | 
190 |         if (query.bool_query) {
191 |             return this.applyBoolQuery(results, query.bool_query);
192 |         }
193 | 
194 |         if (query.term_query) {
195 |             return this.applyTermQuery(results, query.term_query);
196 |         }
197 | 
198 |         if (query.match_all_query) {
199 |             return results; // Match all returns everything
200 |         }
201 | 
202 |         return results;
203 |     }
204 | 
205 |     /**
206 |      * Apply text query filtering
207 |      */
208 |     applyTextQuery(results, textQuery) {
209 |         const searchPhrase = textQuery.search_phrase?.toLowerCase();
210 |         const searchFields = textQuery.fields || ['id', 'display_name'];
211 |         
212 |         if (!searchPhrase) return results;
213 |         
214 |         return results.filter(item => {
215 |             return searchFields.some(field => {
216 |                 const fieldValue = field === 'display_name' 
217 |                     ? item.display_name?.default || item.attribute_definition?.display_name?.default
218 |                     : item[field];
219 |                 return fieldValue?.toLowerCase().includes(searchPhrase);
220 |             });
221 |         });
222 |     }
223 | 
224 |     /**
225 |      * Apply boolean query filtering (simplified implementation)
226 |      */
227 |     applyBoolQuery(results, boolQuery) {
228 |         let filteredResults = results;
229 | 
230 |         // Apply must conditions (AND)
231 |         if (boolQuery.must && boolQuery.must.length > 0) {
232 |             for (const condition of boolQuery.must) {
233 |                 filteredResults = this.applySearchFiltering(filteredResults, condition);
234 |             }
235 |         }
236 | 
237 |         // Apply must_not conditions (exclude)
238 |         if (boolQuery.must_not && boolQuery.must_not.length > 0) {
239 |             for (const condition of boolQuery.must_not) {
240 |                 const excludeResults = this.applySearchFiltering(results, condition);
241 |                 const excludeIds = new Set(excludeResults.map(item => item.id));
242 |                 filteredResults = filteredResults.filter(item => !excludeIds.has(item.id));
243 |             }
244 |         }
245 | 
246 |         // Apply should conditions (OR) - for now, just return if any match
247 |         if (boolQuery.should && boolQuery.should.length > 0) {
248 |             const shouldResults = [];
249 |             for (const condition of boolQuery.should) {
250 |                 const conditionResults = this.applySearchFiltering(results, condition);
251 |                 shouldResults.push(...conditionResults);
252 |             }
253 |             // Remove duplicates and combine with must results
254 |             const shouldIds = new Set(shouldResults.map(item => item.id));
255 |             if (boolQuery.must && boolQuery.must.length > 0) {
256 |                 // Intersect with must results
257 |                 filteredResults = filteredResults.filter(item => shouldIds.has(item.id));
258 |             } else {
259 |                 // Use should results if no must conditions
260 |                 filteredResults = shouldResults.filter((item, index, self) => 
261 |                     self.findIndex(i => i.id === item.id) === index
262 |                 );
263 |             }
264 |         }
265 | 
266 |         return filteredResults;
267 |     }
268 | 
269 |     /**
270 |      * Apply term query filtering
271 |      */
272 |     applyTermQuery(results, termQuery) {
273 |         const { fields, operator, values } = termQuery;
274 |         
275 |         return results.filter(item => {
276 |             return fields.some(field => {
277 |                 const fieldValue = item[field] || item.attribute_definition?.[field];
278 |                 
279 |                 switch (operator) {
280 |                     case 'is':
281 |                         return values.includes(fieldValue);
282 |                     case 'one_of':
283 |                         return values.some(value => fieldValue === value);
284 |                     case 'contains':
285 |                         return values.some(value => fieldValue?.includes(value));
286 |                     default:
287 |                         return false;
288 |                 }
289 |             });
290 |         });
291 |     }
292 | 
293 |     /**
294 |      * Enhance query object with proper _type fields for response
295 |      */
296 |     enhanceQueryWithTypes(query) {
297 |         const enhanced = { ...query };
298 |         
299 |         if (enhanced.text_query) {
300 |             enhanced.text_query._type = 'text_query';
301 |         }
302 |         if (enhanced.bool_query) {
303 |             enhanced.bool_query._type = 'bool_query';
304 |             if (enhanced.bool_query.must) {
305 |                 enhanced.bool_query.must = enhanced.bool_query.must.map(q => this.enhanceQueryWithTypes(q));
306 |             }
307 |             if (enhanced.bool_query.must_not) {
308 |                 enhanced.bool_query.must_not = enhanced.bool_query.must_not.map(q => this.enhanceQueryWithTypes(q));
309 |             }
310 |             if (enhanced.bool_query.should) {
311 |                 enhanced.bool_query.should = enhanced.bool_query.should.map(q => this.enhanceQueryWithTypes(q));
312 |             }
313 |         }
314 |         if (enhanced.term_query) {
315 |             enhanced.term_query._type = 'term_query';
316 |         }
317 |         if (enhanced.match_all_query) {
318 |             enhanced.match_all_query._type = 'match_all_query';
319 |         }
320 |         
321 |         return enhanced;
322 |     }
323 | 
324 |     /**
325 |      * Get the configured router
326 |      */
327 |     getRouter() {
328 |         return this.router;
329 |     }
330 | }
331 | 
332 | module.exports = SitePreferencesHandler;
```

--------------------------------------------------------------------------------
/tests/site-preferences-client.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Tests for OCAPISitePreferencesClient
  3 |  * Tests site preferences operations
  4 |  */
  5 | 
  6 | import { OCAPISitePreferencesClient } from '../src/clients/ocapi/site-preferences-client.js';
  7 | import { OCAPIConfig } from '../src/types/types.js';
  8 | import { QueryBuilder } from '../src/utils/query-builder.js';
  9 | import { Validator } from '../src/utils/validator.js';
 10 | 
 11 | // Mock dependencies
 12 | jest.mock('../src/clients/base/ocapi-auth-client.js');
 13 | jest.mock('../src/utils/query-builder.js');
 14 | jest.mock('../src/utils/validator.js');
 15 | 
 16 | describe('OCAPISitePreferencesClient', () => {
 17 |   let client: OCAPISitePreferencesClient;
 18 |   let mockValidateRequired: jest.MockedFunction<typeof Validator.validateRequired>;
 19 |   let mockValidateInstanceType: jest.MockedFunction<typeof Validator.validateInstanceType>;
 20 |   let mockValidateSearchRequest: jest.MockedFunction<typeof Validator.validateSearchRequest>;
 21 |   let mockQueryBuilderFromObject: jest.MockedFunction<typeof QueryBuilder.fromObject>;
 22 | 
 23 |   const mockConfig: OCAPIConfig = {
 24 |     hostname: 'test-instance.demandware.net',
 25 |     clientId: 'test-client-id',
 26 |     clientSecret: 'test-client-secret',
 27 |     version: 'v21_3',
 28 |   };
 29 | 
 30 |   beforeEach(() => {
 31 |     jest.clearAllMocks();
 32 | 
 33 |     // Mock Validator methods
 34 |     mockValidateRequired = Validator.validateRequired as jest.MockedFunction<typeof Validator.validateRequired>;
 35 |     mockValidateInstanceType = Validator.validateInstanceType as jest.MockedFunction<
 36 |         typeof Validator.validateInstanceType
 37 |     >;
 38 |     mockValidateSearchRequest = Validator.validateSearchRequest as jest.MockedFunction<
 39 |         typeof Validator.validateSearchRequest
 40 |     >;
 41 | 
 42 |     // Reset mock implementations to default behavior
 43 |     mockValidateRequired.mockImplementation(() => {});
 44 |     mockValidateInstanceType.mockImplementation(() => 'sandbox'); // Return a valid instance type
 45 |     mockValidateSearchRequest.mockImplementation(() => {});
 46 | 
 47 |     // Mock QueryBuilder
 48 |     mockQueryBuilderFromObject = QueryBuilder.fromObject as jest.MockedFunction<typeof QueryBuilder.fromObject>;
 49 | 
 50 |     client = new OCAPISitePreferencesClient(mockConfig);
 51 | 
 52 |     // Mock the inherited methods by adding them as properties - avoid protected access
 53 |     (client as any).post = jest.fn().mockResolvedValue({ data: 'mocked' });
 54 |   });
 55 | 
 56 |   describe('constructor', () => {
 57 |     it('should initialize with correct base URL', () => {
 58 |       expect(client).toBeInstanceOf(OCAPISitePreferencesClient);
 59 |     });
 60 | 
 61 |     it('should use default version when not provided', () => {
 62 |       const configWithoutVersion = {
 63 |         hostname: 'test.demandware.net',
 64 |         clientId: 'client-id',
 65 |         clientSecret: 'client-secret',
 66 |       };
 67 | 
 68 |       const clientWithDefaults = new OCAPISitePreferencesClient(configWithoutVersion);
 69 |       expect(clientWithDefaults).toBeInstanceOf(OCAPISitePreferencesClient);
 70 |     });
 71 |   });
 72 | 
 73 |   describe('searchSitePreferences', () => {
 74 |     const groupId = 'SiteGeneral';
 75 |     const instanceType = 'sandbox';
 76 |     const searchRequest = {
 77 |       query: { match_all_query: {} },
 78 |     };
 79 | 
 80 |     beforeEach(() => {
 81 |       mockValidateInstanceType.mockReturnValue('sandbox');
 82 |     });
 83 | 
 84 |     it('should validate all required parameters', async () => {
 85 |       await client.searchSitePreferences(groupId, instanceType, searchRequest);
 86 | 
 87 |       expect(Validator.validateRequired).toHaveBeenCalledWith(
 88 |         { groupId, instanceType },
 89 |         ['groupId', 'instanceType'],
 90 |       );
 91 |       expect(Validator.validateInstanceType).toHaveBeenCalledWith(instanceType);
 92 |       expect(Validator.validateSearchRequest).toHaveBeenCalledWith(searchRequest);
 93 |     });
 94 | 
 95 |     it('should make POST request to site preferences search endpoint', async () => {
 96 |       await client.searchSitePreferences(groupId, instanceType, searchRequest);
 97 | 
 98 |       expect((client as any).post).toHaveBeenCalledWith(
 99 |         '/site_preferences/preference_groups/SiteGeneral/sandbox/preference_search',
100 |         searchRequest,
101 |       );
102 |     });
103 | 
104 |     it('should encode group ID in URL', async () => {
105 |       const encodedGroupId = 'Site General Settings';
106 | 
107 |       await client.searchSitePreferences(encodedGroupId, instanceType, searchRequest);
108 | 
109 |       expect((client as any).post).toHaveBeenCalledWith(
110 |         '/site_preferences/preference_groups/Site%20General%20Settings/sandbox/preference_search',
111 |         searchRequest,
112 |       );
113 |     });
114 | 
115 |     it('should handle different instance types', async () => {
116 |       const testCases = [
117 |         { type: 'staging', expected: 'staging' },
118 |         { type: 'development', expected: 'development' },
119 |         { type: 'production', expected: 'production' },
120 |       ];
121 | 
122 |       for (const testCase of testCases) {
123 |         mockValidateInstanceType.mockReturnValue(testCase.expected as any);
124 | 
125 |         await client.searchSitePreferences(groupId, testCase.type, searchRequest);
126 | 
127 |         expect((client as any).post).toHaveBeenCalledWith(
128 |           `/site_preferences/preference_groups/${groupId}/${testCase.expected}/preference_search`,
129 |           searchRequest,
130 |         );
131 |       }
132 |     });
133 | 
134 |     it('should include options in query string when provided', async () => {
135 |       const options = {
136 |         maskPasswords: true,
137 |         expand: 'value',
138 |       };
139 |       mockQueryBuilderFromObject.mockReturnValue('maskPasswords=true&expand=value');
140 | 
141 |       await client.searchSitePreferences(groupId, instanceType, searchRequest, options);
142 | 
143 |       expect(QueryBuilder.fromObject).toHaveBeenCalledWith(options);
144 |       expect((client as any).post).toHaveBeenCalledWith(
145 |         '/site_preferences/preference_groups/SiteGeneral/sandbox/preference_search?maskPasswords=true&expand=value',
146 |         searchRequest,
147 |       );
148 |     });
149 | 
150 |     it('should not include query string when options are empty', async () => {
151 |       const options = {};
152 |       mockQueryBuilderFromObject.mockReturnValue('');
153 | 
154 |       await client.searchSitePreferences(groupId, instanceType, searchRequest, options);
155 | 
156 |       expect((client as any).post).toHaveBeenCalledWith(
157 |         '/site_preferences/preference_groups/SiteGeneral/sandbox/preference_search',
158 |         searchRequest,
159 |       );
160 |     });
161 | 
162 |     it('should handle complex search request', async () => {
163 |       const complexSearchRequest = {
164 |         query: {
165 |           text_query: {
166 |             fields: ['id', 'display_name', 'description'],
167 |             search_phrase: 'email',
168 |           },
169 |         },
170 |         sorts: [
171 |           { field: 'display_name', sort_order: 'asc' as const },
172 |           { field: 'id' },
173 |         ],
174 |         start: 0,
175 |         count: 25,
176 |         select: '(**)',
177 |       };
178 | 
179 |       await client.searchSitePreferences(groupId, instanceType, complexSearchRequest);
180 | 
181 |       expect(Validator.validateSearchRequest).toHaveBeenCalledWith(complexSearchRequest);
182 |       expect((client as any).post).toHaveBeenCalledWith(
183 |         '/site_preferences/preference_groups/SiteGeneral/sandbox/preference_search',
184 |         complexSearchRequest,
185 |       );
186 |     });
187 | 
188 |     it('should handle term query for value types', async () => {
189 |       const termSearchRequest = {
190 |         query: {
191 |           term_query: {
192 |             fields: ['value_type'],
193 |             operator: 'one_of',
194 |             values: ['string', 'boolean', 'int'],
195 |           },
196 |         },
197 |       };
198 | 
199 |       await client.searchSitePreferences(groupId, instanceType, termSearchRequest);
200 | 
201 |       expect((client as any).post).toHaveBeenCalledWith(
202 |         '/site_preferences/preference_groups/SiteGeneral/sandbox/preference_search',
203 |         termSearchRequest,
204 |       );
205 |     });
206 | 
207 |     it('should handle boolean query combinations', async () => {
208 |       const boolSearchRequest = {
209 |         query: {
210 |           bool_query: {
211 |             must: [
212 |               {
213 |                 text_query: {
214 |                   fields: ['display_name'],
215 |                   search_phrase: 'payment',
216 |                 },
217 |               },
218 |             ],
219 |             must_not: [
220 |               {
221 |                 term_query: {
222 |                   fields: ['value_type'],
223 |                   operator: 'is',
224 |                   values: ['password'],
225 |                 },
226 |               },
227 |             ],
228 |           },
229 |         },
230 |       };
231 | 
232 |       await client.searchSitePreferences(groupId, instanceType, boolSearchRequest);
233 | 
234 |       expect((client as any).post).toHaveBeenCalledWith(
235 |         '/site_preferences/preference_groups/SiteGeneral/sandbox/preference_search',
236 |         boolSearchRequest,
237 |       );
238 |     });
239 |   });
240 | 
241 |   describe('error handling', () => {
242 |     it('should propagate validation errors for required fields', async () => {
243 |       const validationError = new Error('Required field missing');
244 |       mockValidateRequired.mockImplementation(() => {
245 |         throw validationError;
246 |       });
247 | 
248 |       await expect(
249 |         client.searchSitePreferences('groupId', 'sandbox', { query: {} }),
250 |       ).rejects.toThrow(validationError);
251 |     });
252 | 
253 |     it('should propagate instance type validation errors', async () => {
254 |       const instanceTypeError = new Error('Invalid instance type');
255 |       mockValidateInstanceType.mockImplementation(() => {
256 |         throw instanceTypeError;
257 |       });
258 | 
259 |       await expect(
260 |         client.searchSitePreferences('groupId', 'invalid', { query: {} }),
261 |       ).rejects.toThrow(instanceTypeError);
262 |     });
263 | 
264 |     it('should propagate search request validation errors', async () => {
265 |       const searchValidationError = new Error('Invalid search request');
266 |       mockValidateSearchRequest.mockImplementation(() => {
267 |         throw searchValidationError;
268 |       });
269 | 
270 |       await expect(
271 |         client.searchSitePreferences('groupId', 'sandbox', { query: {} }),
272 |       ).rejects.toThrow(searchValidationError);
273 |     });
274 | 
275 |     it('should propagate HTTP errors from base client', async () => {
276 |       const httpError = new Error('HTTP request failed');
277 |       (client as any).post = jest.fn().mockRejectedValue(httpError);
278 | 
279 |       await expect(
280 |         client.searchSitePreferences('groupId', 'sandbox', { query: { match_all_query: {} } }),
281 |       ).rejects.toThrow(httpError);
282 |     });
283 |   });
284 | 
285 |   describe('integration scenarios', () => {
286 |     it('should handle complete site preferences search workflow', async () => {
287 |       const groupId = 'CustomPreferences';
288 |       const instanceType = 'development';
289 |       const searchRequest = {
290 |         query: {
291 |           bool_query: {
292 |             must: [
293 |               {
294 |                 text_query: {
295 |                   fields: ['id', 'display_name'],
296 |                   search_phrase: 'api',
297 |                 },
298 |               },
299 |               {
300 |                 term_query: {
301 |                   fields: ['value_type'],
302 |                   operator: 'one_of',
303 |                   values: ['string', 'text'],
304 |                 },
305 |               },
306 |             ],
307 |           },
308 |         },
309 |         sorts: [
310 |           { field: 'display_name', sort_order: 'asc' as const },
311 |         ],
312 |         start: 0,
313 |         count: 50,
314 |       };
315 |       const options = {
316 |         maskPasswords: false,
317 |         expand: 'value',
318 |       };
319 | 
320 |       mockValidateInstanceType.mockReturnValue('development');
321 |       mockQueryBuilderFromObject.mockReturnValue('maskPasswords=false&expand=value');
322 | 
323 |       await client.searchSitePreferences(groupId, instanceType, searchRequest, options);
324 | 
325 |       // Verify all validations were called
326 |       expect(Validator.validateRequired).toHaveBeenCalledWith(
327 |         { groupId, instanceType },
328 |         ['groupId', 'instanceType'],
329 |       );
330 |       expect(Validator.validateInstanceType).toHaveBeenCalledWith(instanceType);
331 |       expect(Validator.validateSearchRequest).toHaveBeenCalledWith(searchRequest);
332 | 
333 |       // Verify query string building
334 |       expect(QueryBuilder.fromObject).toHaveBeenCalledWith(options);
335 | 
336 |       // Verify the final API call
337 |       expect((client as any).post).toHaveBeenCalledWith(
338 |         '/site_preferences/preference_groups/CustomPreferences/development/preference_search?maskPasswords=false&expand=value',
339 |         searchRequest,
340 |       );
341 |     });
342 | 
343 |     it('should handle minimal search request', async () => {
344 |       const minimalRequest = {
345 |         query: { match_all_query: {} },
346 |       };
347 | 
348 |       mockValidateInstanceType.mockReturnValue('sandbox');
349 | 
350 |       await client.searchSitePreferences('Basic', 'sandbox', minimalRequest);
351 | 
352 |       expect((client as any).post).toHaveBeenCalledWith(
353 |         '/site_preferences/preference_groups/Basic/sandbox/preference_search',
354 |         minimalRequest,
355 |       );
356 |     });
357 |   });
358 | });
359 | 
```

--------------------------------------------------------------------------------
/docs/dw_util/List.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.util
  2 | 
  3 | # Class List
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.util.Collection
  9 |   - dw.util.List
 10 | 
 11 | ## Description
 12 | 
 13 | An ordered collection of objects. The user of a List has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list. Lists are zero based similar to arrays. Unlike sets, lists allow duplicate elements.
 14 | 
 15 | ## Properties
 16 | 
 17 | ## Constructor Summary
 18 | 
 19 | ## Method Summary
 20 | 
 21 | ### addAt
 22 | 
 23 | **Signature:** `addAt(index : Number, value : Object) : void`
 24 | 
 25 | Adds the specified object into the list at the specified index.
 26 | 
 27 | ### concat
 28 | 
 29 | **Signature:** `concat(values : Object...) : List`
 30 | 
 31 | Creates and returns a new List that is the result of concatenating this list with each of the specified values.
 32 | 
 33 | ### fill
 34 | 
 35 | **Signature:** `fill(obj : Object) : void`
 36 | 
 37 | Replaces all of the elements in the list with the given object.
 38 | 
 39 | ### get
 40 | 
 41 | **Signature:** `get(index : Number) : Object`
 42 | 
 43 | Returns the object at the specified index.
 44 | 
 45 | ### indexOf
 46 | 
 47 | **Signature:** `indexOf(value : Object) : Number`
 48 | 
 49 | Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
 50 | 
 51 | ### join
 52 | 
 53 | **Signature:** `join() : String`
 54 | 
 55 | Converts all elements of the list to a string by calling the toString() method and then concatenates them together, with a comma between elements.
 56 | 
 57 | ### join
 58 | 
 59 | **Signature:** `join(separator : String) : String`
 60 | 
 61 | Converts all elements of the list to a string by calling the toString() method and then concatenates them together, with the separator string between elements.
 62 | 
 63 | ### lastIndexOf
 64 | 
 65 | **Signature:** `lastIndexOf(value : Object) : Number`
 66 | 
 67 | Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
 68 | 
 69 | ### pop
 70 | 
 71 | **Signature:** `pop() : Object`
 72 | 
 73 | Removes and returns the last element from the list.
 74 | 
 75 | ### push
 76 | 
 77 | **Signature:** `push(values : Object...) : Number`
 78 | 
 79 | Appends the specified values to the end of the list in order.
 80 | 
 81 | ### removeAt
 82 | 
 83 | **Signature:** `removeAt(index : Number) : Object`
 84 | 
 85 | Removes the object at the specified index.
 86 | 
 87 | ### replaceAll
 88 | 
 89 | **Signature:** `replaceAll(oldValue : Object, newValue : Object) : boolean`
 90 | 
 91 | Replaces all occurrences of oldValue with newValue.
 92 | 
 93 | ### reverse
 94 | 
 95 | **Signature:** `reverse() : void`
 96 | 
 97 | Reverses the order of the elements in the list.
 98 | 
 99 | ### rotate
100 | 
101 | **Signature:** `rotate(distance : Number) : void`
102 | 
103 | Rotates the elements in the list by the specified distance.
104 | 
105 | ### set
106 | 
107 | **Signature:** `set(index : Number, value : Object) : Object`
108 | 
109 | Replaces the object at the specified index in this list with the specified object.
110 | 
111 | ### shift
112 | 
113 | **Signature:** `shift() : Object`
114 | 
115 | Removes and returns the first element of the list.
116 | 
117 | ### shuffle
118 | 
119 | **Signature:** `shuffle() : void`
120 | 
121 | Randomly permutes the elements in the list.
122 | 
123 | ### size
124 | 
125 | **Signature:** `size() : Number`
126 | 
127 | Returns the size of this list.
128 | 
129 | ### slice
130 | 
131 | **Signature:** `slice(from : Number) : List`
132 | 
133 | Returns a slice, or sublist, of this list.
134 | 
135 | ### slice
136 | 
137 | **Signature:** `slice(from : Number, to : Number) : List`
138 | 
139 | Returns a slice, or sublist, of this list.
140 | 
141 | ### sort
142 | 
143 | **Signature:** `sort() : void`
144 | 
145 | Sorts the elements of the list based on their natural order.
146 | 
147 | ### sort
148 | 
149 | **Signature:** `sort(comparator : Object) : void`
150 | 
151 | Sorts the elements of a list.
152 | 
153 | ### subList
154 | 
155 | **Signature:** `subList(from : Number, to : Number) : List`
156 | 
157 | Returns a list containing the elements in this list identified by the specified arguments.
158 | 
159 | ### swap
160 | 
161 | **Signature:** `swap(i : Number, j : Number) : void`
162 | 
163 | Swaps the elements at the specified positions in the list.
164 | 
165 | ### unshift
166 | 
167 | **Signature:** `unshift(values : Object...) : Number`
168 | 
169 | Inserts values at the beginning of the list.
170 | 
171 | ## Method Detail
172 | 
173 | ## Method Details
174 | 
175 | ### addAt
176 | 
177 | **Signature:** `addAt(index : Number, value : Object) : void`
178 | 
179 | **Description:** Adds the specified object into the list at the specified index.
180 | 
181 | **Parameters:**
182 | 
183 | - `index`: the index to use.
184 | - `value`: the object to insert.
185 | 
186 | ---
187 | 
188 | ### concat
189 | 
190 | **Signature:** `concat(values : Object...) : List`
191 | 
192 | **Description:** Creates and returns a new List that is the result of concatenating this list with each of the specified values. This list itself is unmodified. If any of the specified values is itself an array or a Collection, then the elements of that Collection or array are appended to the new list rather than the object itself.
193 | 
194 | **Parameters:**
195 | 
196 | - `values`: one or more objects to concatenate.
197 | 
198 | **Returns:**
199 | 
200 | a new List that is the result of concatenating this list with each of the specified values.
201 | 
202 | ---
203 | 
204 | ### fill
205 | 
206 | **Signature:** `fill(obj : Object) : void`
207 | 
208 | **Description:** Replaces all of the elements in the list with the given object.
209 | 
210 | **Parameters:**
211 | 
212 | - `obj`: the object to use during replacement.
213 | 
214 | ---
215 | 
216 | ### get
217 | 
218 | **Signature:** `get(index : Number) : Object`
219 | 
220 | **Description:** Returns the object at the specified index.
221 | 
222 | **Parameters:**
223 | 
224 | - `index`: the index to use.
225 | 
226 | **Returns:**
227 | 
228 | the object at the specified index.
229 | 
230 | ---
231 | 
232 | ### indexOf
233 | 
234 | **Signature:** `indexOf(value : Object) : Number`
235 | 
236 | **Description:** Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
237 | 
238 | **Parameters:**
239 | 
240 | - `value`: the element to use.
241 | 
242 | **Returns:**
243 | 
244 | the index of the specified object or -1 if the passed object is not found in the list.
245 | 
246 | ---
247 | 
248 | ### join
249 | 
250 | **Signature:** `join() : String`
251 | 
252 | **Description:** Converts all elements of the list to a string by calling the toString() method and then concatenates them together, with a comma between elements.
253 | 
254 | **Returns:**
255 | 
256 | The string that results from converting each element of the list to a string and then concatenating them together, with a comma between elements.
257 | 
258 | ---
259 | 
260 | ### join
261 | 
262 | **Signature:** `join(separator : String) : String`
263 | 
264 | **Description:** Converts all elements of the list to a string by calling the toString() method and then concatenates them together, with the separator string between elements. If null is passed, then the comma character is used as a separator.
265 | 
266 | **Parameters:**
267 | 
268 | - `separator`: The separator string. May be null in which case the comma character is used.
269 | 
270 | **Returns:**
271 | 
272 | The string that results from converting each element of the list to a string and then concatenating them together, with the separator string between elements.
273 | 
274 | ---
275 | 
276 | ### lastIndexOf
277 | 
278 | **Signature:** `lastIndexOf(value : Object) : Number`
279 | 
280 | **Description:** Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
281 | 
282 | **Parameters:**
283 | 
284 | - `value`: the element to use.
285 | 
286 | **Returns:**
287 | 
288 | the last index of the specified object or -1 if the passed object is not found in the list.
289 | 
290 | ---
291 | 
292 | ### pop
293 | 
294 | **Signature:** `pop() : Object`
295 | 
296 | **Description:** Removes and returns the last element from the list.
297 | 
298 | **Returns:**
299 | 
300 | The last element of the list or null if the list is already empty.
301 | 
302 | ---
303 | 
304 | ### push
305 | 
306 | **Signature:** `push(values : Object...) : Number`
307 | 
308 | **Description:** Appends the specified values to the end of the list in order.
309 | 
310 | **Parameters:**
311 | 
312 | - `values`: One or more values to be appended to the end of the list.
313 | 
314 | **Returns:**
315 | 
316 | The new length of the list, after the specified values are appended to it.
317 | 
318 | ---
319 | 
320 | ### removeAt
321 | 
322 | **Signature:** `removeAt(index : Number) : Object`
323 | 
324 | **Description:** Removes the object at the specified index.
325 | 
326 | **Parameters:**
327 | 
328 | - `index`: the index to use.
329 | 
330 | **Returns:**
331 | 
332 | the object that was removed.
333 | 
334 | ---
335 | 
336 | ### replaceAll
337 | 
338 | **Signature:** `replaceAll(oldValue : Object, newValue : Object) : boolean`
339 | 
340 | **Description:** Replaces all occurrences of oldValue with newValue.
341 | 
342 | **Parameters:**
343 | 
344 | - `oldValue`: the old object.
345 | - `newValue`: the new object.
346 | 
347 | **Returns:**
348 | 
349 | true if one or more elements were replaced, false otherwise.
350 | 
351 | ---
352 | 
353 | ### reverse
354 | 
355 | **Signature:** `reverse() : void`
356 | 
357 | **Description:** Reverses the order of the elements in the list.
358 | 
359 | ---
360 | 
361 | ### rotate
362 | 
363 | **Signature:** `rotate(distance : Number) : void`
364 | 
365 | **Description:** Rotates the elements in the list by the specified distance.
366 | 
367 | **Parameters:**
368 | 
369 | - `distance`: the distance to use.
370 | 
371 | ---
372 | 
373 | ### set
374 | 
375 | **Signature:** `set(index : Number, value : Object) : Object`
376 | 
377 | **Description:** Replaces the object at the specified index in this list with the specified object.
378 | 
379 | **Parameters:**
380 | 
381 | - `index`: the index to use.
382 | - `value`: the object to use when replacing the existing object.
383 | 
384 | **Returns:**
385 | 
386 | the replaced object.
387 | 
388 | ---
389 | 
390 | ### shift
391 | 
392 | **Signature:** `shift() : Object`
393 | 
394 | **Description:** Removes and returns the first element of the list. If the list is already empty, this method simply returns null.
395 | 
396 | **Returns:**
397 | 
398 | The former first element of the list, or null is list is already empty.
399 | 
400 | ---
401 | 
402 | ### shuffle
403 | 
404 | **Signature:** `shuffle() : void`
405 | 
406 | **Description:** Randomly permutes the elements in the list.
407 | 
408 | ---
409 | 
410 | ### size
411 | 
412 | **Signature:** `size() : Number`
413 | 
414 | **Description:** Returns the size of this list.
415 | 
416 | **Returns:**
417 | 
418 | the size of this list.
419 | 
420 | ---
421 | 
422 | ### slice
423 | 
424 | **Signature:** `slice(from : Number) : List`
425 | 
426 | **Description:** Returns a slice, or sublist, of this list. The returned list contains the element specified by from and all subsequent elements up to the end of this list.
427 | 
428 | **Parameters:**
429 | 
430 | - `from`: The index at which the slice is to begin. If negative, this argument specifies a position measured from the end of this list. That, -1 indicates the last element, -2 indicates the next from the last element, and so on.
431 | 
432 | **Returns:**
433 | 
434 | A new List that contains the elements of this list from the element specified by from up to the end of this list.
435 | 
436 | ---
437 | 
438 | ### slice
439 | 
440 | **Signature:** `slice(from : Number, to : Number) : List`
441 | 
442 | **Description:** Returns a slice, or sublist, of this list. The returned list contains the element specified by from and all subsequent elements up to, but not including, the element specified by to.
443 | 
444 | **Parameters:**
445 | 
446 | - `from`: The index at which the slice is to begin. If negative, this argument specifies a position measured from the end of this list. That, -1 indicates the last element, -2 indicates the next from the last element, and so on.
447 | - `to`: The index immediately after the end of the slice. If this argument is negative, it specifies an element measured from the end of this list.
448 | 
449 | **Returns:**
450 | 
451 | A new List that contains the elements of this list from the element specified by from up to, but not including, the element specified by to.
452 | 
453 | ---
454 | 
455 | ### sort
456 | 
457 | **Signature:** `sort() : void`
458 | 
459 | **Description:** Sorts the elements of the list based on their natural order. This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
460 | 
461 | ---
462 | 
463 | ### sort
464 | 
465 | **Signature:** `sort(comparator : Object) : void`
466 | 
467 | **Description:** Sorts the elements of a list. The order of the elements is determined with a comparator (see PropertyComparator) or with the help of the given function. The function must take two parameters and return a value <0 if the first parameter is smaller than the second, a value of 0 if both are equal and a value if >0 if the first one is greater than the second parameter. This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
468 | 
469 | **Parameters:**
470 | 
471 | - `comparator`: an instance of a PropertyComparator or a comparison function
472 | 
473 | ---
474 | 
475 | ### subList
476 | 
477 | **Signature:** `subList(from : Number, to : Number) : List`
478 | 
479 | **Description:** Returns a list containing the elements in this list identified by the specified arguments.
480 | 
481 | **Parameters:**
482 | 
483 | - `from`: the beginning index of the elements to move to the new list.
484 | - `to`: the ending index of the elements to move to the new list.
485 | 
486 | **Returns:**
487 | 
488 | the new list containing the elements.
489 | 
490 | ---
491 | 
492 | ### swap
493 | 
494 | **Signature:** `swap(i : Number, j : Number) : void`
495 | 
496 | **Description:** Swaps the elements at the specified positions in the list.
497 | 
498 | **Parameters:**
499 | 
500 | - `i`: the first element to swap.
501 | - `j`: the second element to swap.
502 | 
503 | ---
504 | 
505 | ### unshift
506 | 
507 | **Signature:** `unshift(values : Object...) : Number`
508 | 
509 | **Description:** Inserts values at the beginning of the list. The first argument becomes the new element 0; the second argument becomes element 1; and so on.
510 | 
511 | **Parameters:**
512 | 
513 | - `values`: The values to insert into the list.
514 | 
515 | **Returns:**
516 | 
517 | The new length of the lest.
518 | 
519 | ---
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-system-object-definitions.docs-only.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * MCP Aegis - Programmatic Tests for get_system_object_definitions Tool (Docs-Only Mode)
  3 |  * 
  4 |  * Tests that system object tools are NOT available in docs-only mode.
  5 |  * This tool requires SFCC credentials and should not be available without them.
  6 |  * 
  7 |  * Quick Test Commands:
  8 |  * node --test tests/mcp/node/get-system-object-definitions.docs-only.programmatic.test.js
  9 |  * npm test -- --grep "get_system_object_definitions.*Docs-Only"
 10 |  */
 11 | 
 12 | import { test, describe, before, after, beforeEach } from 'node:test';
 13 | import { strict as assert } from 'node:assert';
 14 | import { connect } from 'mcp-aegis';
 15 | 
 16 | describe('get_system_object_definitions Tool - Docs-Only Mode Programmatic Tests', () => {
 17 |   let client;
 18 | 
 19 |   before(async () => {
 20 |     client = await connect('./aegis.config.docs-only.json');
 21 |   });
 22 | 
 23 |   after(async () => {
 24 |     if (client?.connected) {
 25 |       await client.disconnect();
 26 |     }
 27 |   });
 28 | 
 29 |   beforeEach(() => {
 30 |     // CRITICAL: Clear all buffers to prevent leaking into next tests
 31 |     client.clearAllBuffers();
 32 |   });
 33 | 
 34 |   // ==================================================================================
 35 |   // TOOL UNAVAILABILITY TESTS
 36 |   // ==================================================================================
 37 | 
 38 |   describe('Tool Unavailability in Docs-Only Mode', () => {
 39 |     test('should NOT list get_system_object_definitions tool in docs-only mode', async () => {
 40 |       const tools = await client.listTools();
 41 |       
 42 |       assert.ok(Array.isArray(tools), 'Tools should be an array');
 43 |       
 44 |       const toolNames = tools.map(tool => tool.name);
 45 |       assert.ok(!toolNames.includes('get_system_object_definitions'), 
 46 |         'Should NOT include get_system_object_definitions tool in docs-only mode');
 47 |     });
 48 | 
 49 |     test('should NOT list any system object tools in docs-only mode', async () => {
 50 |       const tools = await client.listTools();
 51 |       const toolNames = tools.map(tool => tool.name);
 52 |       
 53 |       const systemObjectTools = [
 54 |         'get_system_object_definitions',
 55 |         'get_system_object_definition',
 56 |         'search_system_object_attribute_definitions',
 57 |         'search_site_preferences',
 58 |         'search_system_object_attribute_groups',
 59 |         'search_custom_object_attribute_definitions'
 60 |       ];
 61 | 
 62 |       systemObjectTools.forEach(toolName => {
 63 |         assert.ok(!toolNames.includes(toolName), 
 64 |           `Should NOT include ${toolName} tool in docs-only mode`);
 65 |       });
 66 |     });
 67 | 
 68 |     test('should have expected number of tools in docs-only mode', async () => {
 69 |       const tools = await client.listTools();
 70 |       
 71 |       // Should have documentation, SFRA, best practices, and cartridge tools
 72 |       // but NOT system object, log, or code version tools
 73 |       assert.ok(tools.length >= 10, 'Should have at least 10 tools available');
 74 |       assert.ok(tools.length <= 20, 'Should not have too many tools (likely around 15)');
 75 |     });
 76 |   });
 77 | 
 78 | 
 79 | 
 80 |   // ==================================================================================
 81 |   // TOOL CALL BEHAVIOR - SHOULD RETURN ERROR
 82 |   // ==================================================================================
 83 | 
 84 |   describe('Tool Call Behavior - Configuration Error', () => {
 85 |     test('should return configuration error when calling unlisted tool', async () => {
 86 |       const result = await client.callTool('get_system_object_definitions', {});
 87 |       
 88 |       assert.equal(result.isError, true, 'Should return error result');
 89 |       assert.ok(result.content, 'Should have content');
 90 |       assert.ok(Array.isArray(result.content), 'Content should be array');
 91 |       assert.equal(result.content[0].type, 'text', 'Content should be text type');
 92 |       
 93 |       const errorText = result.content[0].text;
 94 |       assert.ok(errorText.includes('OCAPI client not configured') || 
 95 |                 errorText.includes('not configured') ||
 96 |                 errorText.includes('credentials'), 
 97 |         'Error should mention configuration/credentials issue');
 98 |     });
 99 | 
100 |     test('should return proper error result structure for unlisted tool', async () => {
101 |       const result = await client.callTool('get_system_object_definitions', {});
102 |       
103 |       assert.equal(result.isError, true, 'Should be error result');
104 |       assert.ok(Array.isArray(result.content), 'Content should be array');
105 |       assert.ok(result.content.length > 0, 'Should have error content');
106 |       assert.equal(result.content[0].type, 'text', 'Error content should be text');
107 |     });
108 | 
109 |     test('should fail fast when calling unlisted tool with parameters', async () => {
110 |       const startTime = Date.now();
111 |       
112 |       const result = await client.callTool('get_system_object_definitions', {
113 |         start: 0,
114 |         count: 10,
115 |         select: '(**)'
116 |       });
117 |       
118 |       const duration = Date.now() - startTime;
119 |       
120 |       assert.equal(result.isError, true, 'Should return error');
121 |       assert.ok(duration < 1000, 'Should fail quickly (under 1 second)');
122 |       
123 |       const errorText = result.content[0].text;
124 |       assert.ok(errorText.includes('OCAPI client not configured') || 
125 |                 errorText.includes('not configured') ||
126 |                 errorText.includes('credentials'), 
127 |         'Error should mention configuration issue');
128 |     });
129 | 
130 |     test('should handle invalid parameters gracefully even in error mode', async () => {
131 |       const result = await client.callTool('get_system_object_definitions', {
132 |         invalidParam: 'test',
133 |         anotherInvalid: 123
134 |       });
135 |       
136 |       assert.equal(result.isError, true, 'Should return error');
137 |       
138 |       const errorText = result.content[0].text;
139 |       // Should mention configuration, not parameter validation
140 |       assert.ok(errorText.includes('OCAPI client not configured') || 
141 |                 errorText.includes('not configured') ||
142 |                 errorText.includes('credentials'), 
143 |         'Should prioritize configuration error over parameter validation');
144 |     });
145 |   });
146 | 
147 |   // ==================================================================================
148 |   // OTHER SYSTEM OBJECT TOOLS VERIFICATION
149 |   // ==================================================================================
150 | 
151 |   describe('Other System Object Tools Unavailability', () => {
152 |     test('should NOT have get_system_object_definition tool', async () => {
153 |       const tools = await client.listTools();
154 |       const toolNames = tools.map(tool => tool.name);
155 |       
156 |       assert.ok(!toolNames.includes('get_system_object_definition'), 
157 |         'Should NOT include get_system_object_definition tool');
158 |       
159 |       // Verify calling it also returns error
160 |       const result = await client.callTool('get_system_object_definition', {
161 |         objectType: 'Product'
162 |       });
163 |       
164 |       assert.equal(result.isError, true, 'Should return configuration error');
165 |     });
166 | 
167 |     test('should NOT have search_system_object_attribute_definitions tool', async () => {
168 |       const tools = await client.listTools();
169 |       const toolNames = tools.map(tool => tool.name);
170 |       
171 |       assert.ok(!toolNames.includes('search_system_object_attribute_definitions'), 
172 |         'Should NOT include search_system_object_attribute_definitions tool');
173 |       
174 |       // Verify calling it also returns error
175 |       const result = await client.callTool('search_system_object_attribute_definitions', {
176 |         objectType: 'Product',
177 |         searchRequest: {
178 |           query: { match_all_query: {} }
179 |         }
180 |       });
181 |       
182 |       assert.equal(result.isError, true, 'Should return configuration error');
183 |     });
184 | 
185 |     test('should NOT have site preferences tools', async () => {
186 |       const tools = await client.listTools();
187 |       const toolNames = tools.map(tool => tool.name);
188 |       
189 |       assert.ok(!toolNames.includes('search_site_preferences'), 
190 |         'Should NOT include search_site_preferences tool');
191 |       
192 |       // Verify calling it also returns error
193 |       const result = await client.callTool('search_site_preferences', {
194 |         groupId: 'SitePreferences',
195 |         instanceType: 'sandbox',
196 |         searchRequest: {
197 |           query: { match_all_query: {} }
198 |         }
199 |       });
200 |       
201 |       assert.equal(result.isError, true, 'Should return configuration error');
202 |     });
203 |   });
204 | 
205 |   // ==================================================================================
206 |   // CONSISTENCY TESTS
207 |   // ==================================================================================
208 | 
209 |   describe('Consistency Tests', () => {
210 |     test('should consistently exclude system object tools across multiple calls', async () => {
211 |       const tools1 = await client.listTools();
212 |       const tools2 = await client.listTools();
213 |       
214 |       const toolNames1 = tools1.map(tool => tool.name);
215 |       const toolNames2 = tools2.map(tool => tool.name);
216 |       
217 |       assert.ok(!toolNames1.includes('get_system_object_definitions'), 
218 |         'First call should exclude system object tools');
219 |       assert.ok(!toolNames2.includes('get_system_object_definitions'), 
220 |         'Second call should exclude system object tools');
221 |     });
222 | 
223 |     test('should maintain consistent tool exclusion on second call', async () => {
224 |       const tools = await client.listTools();
225 |       const toolNames = tools.map(tool => tool.name);
226 |       
227 |       assert.ok(!toolNames.includes('get_system_object_definitions'), 
228 |         'Should consistently exclude get_system_object_definitions');
229 |     });
230 | 
231 |     test('should consistently return configuration error across multiple attempts', async () => {
232 |       const result1 = await client.callTool('get_system_object_definitions', {});
233 |       const result2 = await client.callTool('get_system_object_definitions', {});
234 |       
235 |       assert.equal(result1.isError, true, 'First call should return error');
236 |       assert.equal(result2.isError, true, 'Second call should return error');
237 |       
238 |       const error1 = result1.content[0].text;
239 |       const error2 = result2.content[0].text;
240 |       
241 |       assert.ok(error1.includes('OCAPI client not configured') || 
242 |                 error1.includes('not configured'), 
243 |         'First error should mention configuration');
244 |       assert.ok(error2.includes('OCAPI client not configured') || 
245 |                 error2.includes('not configured'), 
246 |         'Second error should mention configuration');
247 |     });
248 | 
249 |     test('should have consistent tool count in docs-only mode', async () => {
250 |       const tools1 = await client.listTools();
251 |       const tools2 = await client.listTools();
252 |       
253 |       assert.equal(tools1.length, tools2.length, 
254 |         'Tool count should be consistent between calls');
255 |       
256 |       // Should have around 15 tools in docs-only mode
257 |       assert.ok(tools1.length >= 10, 'Should have reasonable number of tools');
258 |       assert.ok(tools1.length <= 20, 'Should not have excessive tools');
259 |     });
260 |   });
261 | 
262 | 
263 | 
264 |   // ==================================================================================
265 |   // MODE IDENTIFICATION TESTS - FOCUSED ON SYSTEM OBJECT TOOLS
266 |   // ==================================================================================
267 | 
268 |   describe('System Object Tools Mode Identification', () => {
269 |     test('should clearly identify system object tools unavailability in docs-only mode', async () => {
270 |       const tools = await client.listTools();
271 |       const toolNames = tools.map(tool => tool.name);
272 |       
273 |       const hasSystemObjectTools = toolNames.includes('get_system_object_definitions');
274 |       const hasOtherSystemObjectTools = toolNames.includes('search_site_preferences');
275 |       
276 |       assert.ok(!hasSystemObjectTools, 'Should NOT have get_system_object_definitions tool');
277 |       assert.ok(!hasOtherSystemObjectTools, 'Should NOT have other system object tools');
278 |     });
279 | 
280 |     test('should exclude all system object tools in docs-only mode', async () => {
281 |       const tools = await client.listTools();
282 |       const toolNames = tools.map(tool => tool.name);
283 |       
284 |       // System object tools that should NOT be available in docs-only mode
285 |       const systemObjectTools = [
286 |         'get_system_object_definitions',
287 |         'get_system_object_definition',
288 |         'search_system_object_attribute_definitions',
289 |         'search_site_preferences',
290 |         'search_system_object_attribute_groups',
291 |         'search_custom_object_attribute_definitions'
292 |       ];
293 |       
294 |       systemObjectTools.forEach(toolName => {
295 |         assert.ok(!toolNames.includes(toolName), 
296 |           `Docs-only mode should NOT include system object tool: ${toolName}`);
297 |       });
298 |     });
299 |   });
300 | });
```

--------------------------------------------------------------------------------
/docs/TopLevel/global.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: TopLevel
  2 | 
  3 | # Class global
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - global
  9 | 
 10 | ## Description
 11 | 
 12 | The global object is a pre-defined object that serves as a placeholder for the global properties and functions of JavaScript. All other predefined objects, functions, and properties are accessible through the global object.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### PIPELET_ERROR
 17 | 
 18 | **Type:** Number
 19 | 
 20 | represents an error during pipelet execution
 21 | 
 22 | ### PIPELET_NEXT
 23 | 
 24 | **Type:** Number
 25 | 
 26 | represents the next pipelet to fire
 27 | 
 28 | ## Properties
 29 | 
 30 | ## Constructor Summary
 31 | 
 32 | ## Method Summary
 33 | 
 34 | ### decodeURI
 35 | 
 36 | **Signature:** `static decodeURI(uri : String) : String`
 37 | 
 38 | Unescapes characters in a URI component.
 39 | 
 40 | ### decodeURIComponent
 41 | 
 42 | **Signature:** `static decodeURIComponent(uriComponent : String) : String`
 43 | 
 44 | Unescapes characters in a URI component.
 45 | 
 46 | ### empty
 47 | 
 48 | **Signature:** `static empty(obj : Object) : boolean`
 49 | 
 50 | The method tests, whether the given object is empty.
 51 | 
 52 | ### encodeURI
 53 | 
 54 | **Signature:** `static encodeURI(uri : String) : String`
 55 | 
 56 | Escapes characters in a URI.
 57 | 
 58 | ### encodeURIComponent
 59 | 
 60 | **Signature:** `static encodeURIComponent(uriComponent : String) : String`
 61 | 
 62 | Escapes characters in a URI component.
 63 | 
 64 | ### escape
 65 | 
 66 | **Signature:** `static escape(s : String) : String`
 67 | 
 68 | Encodes a String.
 69 | 
 70 | ### eval
 71 | 
 72 | **Signature:** `static eval(code : String) : Object`
 73 | 
 74 | Execute JavaScript code from a String.
 75 | 
 76 | ### importClass
 77 | 
 78 | **Signature:** `static importClass(classPath : Object) : void`
 79 | 
 80 | Import the specified class and make it available at the top level.
 81 | 
 82 | ### importPackage
 83 | 
 84 | **Signature:** `static importPackage(packagePath : Object) : void`
 85 | 
 86 | Import all the classes in the specified package available at the top level.
 87 | 
 88 | ### importScript
 89 | 
 90 | **Signature:** `static importScript(scriptPath : String) : void`
 91 | 
 92 | Imports all functions from the specified script.
 93 | 
 94 | ### isFinite
 95 | 
 96 | **Signature:** `static isFinite(number : Number) : boolean`
 97 | 
 98 | Returns true if the specified Number is finite.
 99 | 
100 | ### isNaN
101 | 
102 | **Signature:** `static isNaN(object : Object) : boolean`
103 | 
104 | Test the specified value to determine if it is not a Number.
105 | 
106 | ### isXMLName
107 | 
108 | **Signature:** `static isXMLName(name : String) : boolean`
109 | 
110 | Determines whether the specified string is a valid name for an XML element or attribute.
111 | 
112 | ### parseFloat
113 | 
114 | **Signature:** `static parseFloat(s : String) : Number`
115 | 
116 | Parses a String into an float Number.
117 | 
118 | ### parseInt
119 | 
120 | **Signature:** `static parseInt(s : String, radix : Number) : Number`
121 | 
122 | Parses a String into an integer Number using the specified radix.
123 | 
124 | ### parseInt
125 | 
126 | **Signature:** `static parseInt(s : String) : Number`
127 | 
128 | Parses a String into an integer Number. This function is a short form for the call to parseInt(String, Number) with automatic determination of the radix.
129 | 
130 | ### parseInt
131 | 
132 | **Signature:** `static parseInt(s : String) : Number`
133 | 
134 | Parses a String into an integer Number.
135 | 
136 | ### require
137 | 
138 | **Signature:** `static require(path : String) : Module`
139 | 
140 | The require() function supports loading of modules in JavaScript.
141 | 
142 | ### trace
143 | 
144 | **Signature:** `static trace(msg : String, params : Object...) : void`
145 | 
146 | Formats and prints the message using the specified params and returns the formatted message.
147 | 
148 | ### unescape
149 | 
150 | **Signature:** `static unescape(string : String) : String`
151 | 
152 | Decode an escaped String.
153 | 
154 | ## Method Detail
155 | 
156 | ## Method Details
157 | 
158 | ### decodeURI
159 | 
160 | **Signature:** `static decodeURI(uri : String) : String`
161 | 
162 | **Description:** Unescapes characters in a URI component.
163 | 
164 | **Parameters:**
165 | 
166 | - `uri`: a string that contains an encoded URI or other text to be decoded.
167 | 
168 | **Returns:**
169 | 
170 | A copy of uri with any hexadecimal escape sequences replaced with the characters they represent
171 | 
172 | ---
173 | 
174 | ### decodeURIComponent
175 | 
176 | **Signature:** `static decodeURIComponent(uriComponent : String) : String`
177 | 
178 | **Description:** Unescapes characters in a URI component.
179 | 
180 | **Parameters:**
181 | 
182 | - `uriComponent`: a string that contains an encoded URI component or other text to be decoded.
183 | 
184 | **Returns:**
185 | 
186 | A copy of uriComponent with any hexadecimal escape sequences replaced with the characters they represent
187 | 
188 | ---
189 | 
190 | ### empty
191 | 
192 | **Signature:** `static empty(obj : Object) : boolean`
193 | 
194 | **Description:** The method tests, whether the given object is empty. The interpretation of empty is the following. - null is always empty - undefined is always empty - a string with zero length is empty - an array with no elements is empty - a collection with no elements is empty
195 | 
196 | **Parameters:**
197 | 
198 | - `obj`: the object to be thested
199 | 
200 | **Returns:**
201 | 
202 | true if the object is interpreted as being empty
203 | 
204 | ---
205 | 
206 | ### encodeURI
207 | 
208 | **Signature:** `static encodeURI(uri : String) : String`
209 | 
210 | **Description:** Escapes characters in a URI.
211 | 
212 | **Parameters:**
213 | 
214 | - `uri`: a String that contains the URI or other text to be encoded.
215 | 
216 | **Returns:**
217 | 
218 | a copy of uri with certain characters replaced by hexadecimal escape sequences.
219 | 
220 | ---
221 | 
222 | ### encodeURIComponent
223 | 
224 | **Signature:** `static encodeURIComponent(uriComponent : String) : String`
225 | 
226 | **Description:** Escapes characters in a URI component.
227 | 
228 | **Parameters:**
229 | 
230 | - `uriComponent`: a String that contains a portion of a URI or other text to be encoded.
231 | 
232 | **Returns:**
233 | 
234 | a copy of uriComponent with certain characters replaced by hexadecimal escape sequences.
235 | 
236 | ---
237 | 
238 | ### escape
239 | 
240 | **Signature:** `static escape(s : String) : String`
241 | 
242 | **Description:** Encodes a String.
243 | 
244 | **Parameters:**
245 | 
246 | - `s`: the String to be encoded.
247 | 
248 | **Returns:**
249 | 
250 | a copy of s where characters have been replace by hexadecimal escape sequences.
251 | 
252 | ---
253 | 
254 | ### eval
255 | 
256 | **Signature:** `static eval(code : String) : Object`
257 | 
258 | **Description:** Execute JavaScript code from a String.
259 | 
260 | **Deprecated:**
261 | 
262 | The eval() function is deprecated, because it's potential security risk for server side code injection.
263 | 
264 | **Parameters:**
265 | 
266 | - `code`: a String that contains the JavaScript expression to be evaluated or the statements to be executed.
267 | 
268 | **Returns:**
269 | 
270 | the value of the executed call or null.
271 | 
272 | ---
273 | 
274 | ### importClass
275 | 
276 | **Signature:** `static importClass(classPath : Object) : void`
277 | 
278 | **Description:** Import the specified class and make it available at the top level. It's equivalent in effect to the Java import declaration.
279 | 
280 | **Parameters:**
281 | 
282 | - `classPath`: the fully qualified class path.
283 | 
284 | ---
285 | 
286 | ### importPackage
287 | 
288 | **Signature:** `static importPackage(packagePath : Object) : void`
289 | 
290 | **Description:** Import all the classes in the specified package available at the top level. It's equivalent in effect to the Java import declaration.
291 | 
292 | **Parameters:**
293 | 
294 | - `packagePath`: the fully qualified package path.
295 | 
296 | ---
297 | 
298 | ### importScript
299 | 
300 | **Signature:** `static importScript(scriptPath : String) : void`
301 | 
302 | **Description:** Imports all functions from the specified script. Variables are not imported from the script and must be accessed through helper functions. The script path has the following syntax: [cartridgename:]scriptname, where cartridgename identifies a cartridge where the script file is located. If cartridgename is omitted the script file is loaded from the same cartridge in which the importing component is located. Examples: importScript( 'example.ds' ) imports the script file example.ds from the same cartridge importScript( 'abc:example.ds' ) imports the script file example.ds from the cartridge 'abc'
303 | 
304 | **Parameters:**
305 | 
306 | - `scriptPath`: the path to the script.
307 | 
308 | ---
309 | 
310 | ### isFinite
311 | 
312 | **Signature:** `static isFinite(number : Number) : boolean`
313 | 
314 | **Description:** Returns true if the specified Number is finite.
315 | 
316 | **Parameters:**
317 | 
318 | - `number`: the Number to test.
319 | 
320 | **Returns:**
321 | 
322 | true if the specified Number is finite, false otherwise.
323 | 
324 | ---
325 | 
326 | ### isNaN
327 | 
328 | **Signature:** `static isNaN(object : Object) : boolean`
329 | 
330 | **Description:** Test the specified value to determine if it is not a Number.
331 | 
332 | **Parameters:**
333 | 
334 | - `object`: the Object to be tested as a number
335 | 
336 | **Returns:**
337 | 
338 | True if the object is not a number
339 | 
340 | ---
341 | 
342 | ### isXMLName
343 | 
344 | **Signature:** `static isXMLName(name : String) : boolean`
345 | 
346 | **Description:** Determines whether the specified string is a valid name for an XML element or attribute.
347 | 
348 | **Parameters:**
349 | 
350 | - `name`: the String specified
351 | 
352 | **Returns:**
353 | 
354 | True if the string is a valid name
355 | 
356 | ---
357 | 
358 | ### parseFloat
359 | 
360 | **Signature:** `static parseFloat(s : String) : Number`
361 | 
362 | **Description:** Parses a String into an float Number.
363 | 
364 | **Parameters:**
365 | 
366 | - `s`: the String to parse.
367 | 
368 | **Returns:**
369 | 
370 | Returns the float as a Number.
371 | 
372 | ---
373 | 
374 | ### parseInt
375 | 
376 | **Signature:** `static parseInt(s : String, radix : Number) : Number`
377 | 
378 | **Description:** Parses a String into an integer Number using the specified radix.
379 | 
380 | **Parameters:**
381 | 
382 | - `s`: the String to parse.
383 | - `radix`: the radix to use.
384 | 
385 | **Returns:**
386 | 
387 | Returns the integer as a Number.
388 | 
389 | ---
390 | 
391 | ### parseInt
392 | 
393 | **Signature:** `static parseInt(s : String) : Number`
394 | 
395 | **Description:** Parses a String into an integer Number. This function is a short form for the call to parseInt(String, Number) with automatic determination of the radix. If the string starts with "0x" or "0X" then the radix is 16. If the string starts with "0" the the radix is 8. In all other cases the radix is 10.
396 | 
397 | **API Versioned:**
398 | 
399 | No longer available as of version 16.1.
400 | 
401 | **Parameters:**
402 | 
403 | - `s`: the String to parse.
404 | 
405 | **Returns:**
406 | 
407 | Returns the integer as a Number.
408 | 
409 | ---
410 | 
411 | ### parseInt
412 | 
413 | **Signature:** `static parseInt(s : String) : Number`
414 | 
415 | **Description:** Parses a String into an integer Number. This function is a short form for the call to parseInt(String, Number) with automatic determination of the radix. If the string starts with "0x" or "0X" then the radix is 16. In all other cases the radix is 10.
416 | 
417 | **API Versioned:**
418 | 
419 | From version 16.1. ECMAScript 5 compliance: removed support for octal numbers.
420 | 
421 | **Parameters:**
422 | 
423 | - `s`: the String to parse.
424 | 
425 | **Returns:**
426 | 
427 | Returns the integer as a Number.
428 | 
429 | ---
430 | 
431 | ### require
432 | 
433 | **Signature:** `static require(path : String) : Module`
434 | 
435 | **Description:** The require() function supports loading of modules in JavaScript. The function works similar to the require() function in CommonJS. The general module loading works the same way, but the exact path lookup is slightly different and better fits into Demandware concepts. Here are the details for the lookup: If the module name starts with "./" or "../" then load it relative to the current module. The module can be a file or a directory. A file extension is acknowledged, but not required. If it's a directory a 'package.json' or a 'main' file is expected. If the 'package.json' does not contain a main entry, then default to main file in the directory. Access to parent files can't go beyond the cartridges directory. Access to other cartridges is explicitly allowed. If the module name starts with "~/" it's a path relative to the current cartridge. If the module name starts with "*/" try to find the module in all cartridges that are assigned to the current site. A module with the name "dw" or which starts with "dw/" references Demandware built-in functions and classes. For example var u = require( 'dw/util' ); loads the classes in the util package, which can be then used like var h = new u.HashMap(); A module, which doesn't start with "./" or "../" is resolved as top level module. The module name is used to find a folder in the top cartridge directory, typically a cartridge itself, but it can also be a simple folder. If nothing is found, the module name is used to look into a special folder called "modules" in the top cartridge directory. That folder can be used by a developer to manage different modules. For example a developer could drop a module like "less.js" just into that folder. If the require function is used to reference a file then an optional file extension is used to determine the content of the file. Currently supported are the extensions ordered by priority: js - JavaScript file ds - Demandware Script file json - JSON file
436 | 
437 | **Parameters:**
438 | 
439 | - `path`: the path to the JavaScript module.
440 | 
441 | **Returns:**
442 | 
443 | an object with the exported functions and properties.
444 | 
445 | ---
446 | 
447 | ### trace
448 | 
449 | **Signature:** `static trace(msg : String, params : Object...) : void`
450 | 
451 | **Description:** Formats and prints the message using the specified params and returns the formatted message. The format message is a Java MessageFormat expression. Printing happens in the script log output.
452 | 
453 | **Parameters:**
454 | 
455 | - `msg`: the message to format.
456 | - `params`: one, or multiple parameters that are used to format the message.
457 | 
458 | ---
459 | 
460 | ### unescape
461 | 
462 | **Signature:** `static unescape(string : String) : String`
463 | 
464 | **Description:** Decode an escaped String.
465 | 
466 | **Parameters:**
467 | 
468 | - `string`: the String to decode.
469 | 
470 | **Returns:**
471 | 
472 | a copy of the String where hexadecimal character sequences are replace by Unicode characters.
473 | 
474 | ---
```

--------------------------------------------------------------------------------
/docs/dw_system/Site.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.system
  2 | 
  3 | # Class Site
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.system.Site
  9 | 
 10 | ## Description
 11 | 
 12 | This class represents a site in Commerce Cloud Digital and provides access to several site-level configuration values which are managed from within the Business Manager. It is only possible to get a reference to the current site as determined by the current request. The static method getCurrent() returns a reference to the current site.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### SITE_STATUS_MAINTENANCE
 17 | 
 18 | **Type:** Number = 3
 19 | 
 20 | Constant that represents the Site under maintenance/offline
 21 | 
 22 | ### SITE_STATUS_ONLINE
 23 | 
 24 | **Type:** Number = 1
 25 | 
 26 | Constant that represents the Site is Online
 27 | 
 28 | ### SITE_STATUS_PROTECTED
 29 | 
 30 | **Type:** Number = 5
 31 | 
 32 | Constant that represents the Site is in preview mode or online/password (protected)
 33 | 
 34 | ## Properties
 35 | 
 36 | ### allowedCurrencies
 37 | 
 38 | **Type:** List (Read Only)
 39 | 
 40 | The allowed currencies of the current site as a collection of
 41 |  currency codes.
 42 | 
 43 | ### allowedLocales
 44 | 
 45 | **Type:** List (Read Only)
 46 | 
 47 | The allowed locales of the current site as a collection of
 48 |  locale ID's.
 49 | 
 50 | ### allSites
 51 | 
 52 | **Type:** List (Read Only)
 53 | 
 54 | All sites.
 55 | 
 56 | ### calendar
 57 | 
 58 | **Type:** Calendar (Read Only)
 59 | 
 60 | A new Calendar object in the time zone of the
 61 |  current site.
 62 | 
 63 | ### currencyCode
 64 | 
 65 | **Type:** String (Read Only)
 66 | 
 67 | The default currency code for the current site.
 68 | 
 69 | ### current
 70 | 
 71 | **Type:** Site (Read Only)
 72 | 
 73 | The current site.
 74 | 
 75 | ### defaultCurrency
 76 | 
 77 | **Type:** String (Read Only)
 78 | 
 79 | The default currency code for the current site.
 80 | 
 81 | ### defaultLocale
 82 | 
 83 | **Type:** String (Read Only)
 84 | 
 85 | Return default locale for the site.
 86 | 
 87 | ### einsteinSiteID
 88 | 
 89 | **Type:** String (Read Only)
 90 | 
 91 | The Einstein site Id. Typically this is a concatenation of the realm, underscore character and the site id.
 92 |  It can be overwritten by support users to help with realm moves to continue using the Einstein data from the old realm.
 93 |  Used when making calls to the Einstein APIs.
 94 | 
 95 | ### httpHostName
 96 | 
 97 | **Type:** String (Read Only)
 98 | 
 99 | The configured HTTP host name. If no host name
100 |  is configured the method returns the instance hostname.
101 | 
102 | ### httpsHostName
103 | 
104 | **Type:** String (Read Only)
105 | 
106 | The configured HTTPS host name. If no host name
107 |  is configured the method returns the HTTP host name or the instance hostname, if
108 |  that is not configured as well.
109 | 
110 | ### ID
111 | 
112 | **Type:** String (Read Only)
113 | 
114 | The ID of the site.
115 | 
116 | ### name
117 | 
118 | **Type:** String (Read Only)
119 | 
120 | A descriptive name for the site.
121 | 
122 | ### OMSEnabled
123 | 
124 | **Type:** boolean (Read Only)
125 | 
126 | Whether oms is active in the current site. This depends on a general
127 |  property which states whether oms is active for the server,
128 |  and a site-dependent preference whether oms is available for the current site.
129 | 
130 | ### pageMetaTags
131 | 
132 | **Type:** Array (Read Only)
133 | 
134 | All page meta tags, defined for this instance for which content can be generated.
135 | 
136 |  The meta tag content is generated based on the home page meta tag context and rules.
137 |  The rules are obtained from the current repository domain.
138 | 
139 | ### preferences
140 | 
141 | **Type:** SitePreferences (Read Only)
142 | 
143 | This method returns a container of all site preferences of this site.
144 | 
145 | ### status
146 | 
147 | **Type:** Number (Read Only)
148 | 
149 | The status of this site.
150 |  
151 |  Possible values are SITE_STATUS_ONLINE, SITE_STATUS_MAINTENANCE, SITE_STATUS_PROTECTED
152 | 
153 | ### timezone
154 | 
155 | **Type:** String (Read Only)
156 | 
157 | The code for the time zone in which the storefront is
158 |  running.
159 | 
160 | ### timezoneOffset
161 | 
162 | **Type:** Number (Read Only)
163 | 
164 | Returns time zone offset in which the storefront is running.
165 | 
166 | ## Constructor Summary
167 | 
168 | ## Method Summary
169 | 
170 | ### getAllowedCurrencies
171 | 
172 | **Signature:** `getAllowedCurrencies() : List`
173 | 
174 | Returns the allowed currencies of the current site as a collection of currency codes.
175 | 
176 | ### getAllowedLocales
177 | 
178 | **Signature:** `getAllowedLocales() : List`
179 | 
180 | Returns the allowed locales of the current site as a collection of locale ID's.
181 | 
182 | ### getAllSites
183 | 
184 | **Signature:** `static getAllSites() : List`
185 | 
186 | Returns all sites.
187 | 
188 | ### getCalendar
189 | 
190 | **Signature:** `static getCalendar() : Calendar`
191 | 
192 | Returns a new Calendar object in the time zone of the current site.
193 | 
194 | ### getCurrencyCode
195 | 
196 | **Signature:** `getCurrencyCode() : String`
197 | 
198 | Returns the default currency code for the current site.
199 | 
200 | ### getCurrent
201 | 
202 | **Signature:** `static getCurrent() : Site`
203 | 
204 | Returns the current site.
205 | 
206 | ### getCustomPreferenceValue
207 | 
208 | **Signature:** `getCustomPreferenceValue(name : String) : Object`
209 | 
210 | Returns a custom preference value.
211 | 
212 | ### getDefaultCurrency
213 | 
214 | **Signature:** `getDefaultCurrency() : String`
215 | 
216 | Returns the default currency code for the current site.
217 | 
218 | ### getDefaultLocale
219 | 
220 | **Signature:** `getDefaultLocale() : String`
221 | 
222 | Return default locale for the site.
223 | 
224 | ### getEinsteinSiteID
225 | 
226 | **Signature:** `getEinsteinSiteID() : String`
227 | 
228 | Returns the Einstein site Id.
229 | 
230 | ### getHttpHostName
231 | 
232 | **Signature:** `getHttpHostName() : String`
233 | 
234 | Returns the configured HTTP host name.
235 | 
236 | ### getHttpsHostName
237 | 
238 | **Signature:** `getHttpsHostName() : String`
239 | 
240 | Returns the configured HTTPS host name.
241 | 
242 | ### getID
243 | 
244 | **Signature:** `getID() : String`
245 | 
246 | Returns the ID of the site.
247 | 
248 | ### getName
249 | 
250 | **Signature:** `getName() : String`
251 | 
252 | Returns a descriptive name for the site.
253 | 
254 | ### getPageMetaTag
255 | 
256 | **Signature:** `getPageMetaTag(id : String) : PageMetaTag`
257 | 
258 | Returns the page meta tag for the specified id.
259 | 
260 | ### getPageMetaTags
261 | 
262 | **Signature:** `getPageMetaTags() : Array`
263 | 
264 | Returns all page meta tags, defined for this instance for which content can be generated.
265 | 
266 | ### getPreferences
267 | 
268 | **Signature:** `getPreferences() : SitePreferences`
269 | 
270 | This method returns a container of all site preferences of this site.
271 | 
272 | ### getStatus
273 | 
274 | **Signature:** `getStatus() : Number`
275 | 
276 | Returns the status of this site.
277 | 
278 | ### getTimezone
279 | 
280 | **Signature:** `getTimezone() : String`
281 | 
282 | Returns the code for the time zone in which the storefront is running.
283 | 
284 | ### getTimezoneOffset
285 | 
286 | **Signature:** `getTimezoneOffset() : Number`
287 | 
288 | Returns time zone offset in which the storefront is running.
289 | 
290 | ### isOMSEnabled
291 | 
292 | **Signature:** `isOMSEnabled() : boolean`
293 | 
294 | Whether oms is active in the current site.
295 | 
296 | ### setCustomPreferenceValue
297 | 
298 | **Signature:** `setCustomPreferenceValue(name : String, value : Object) : void`
299 | 
300 | The method sets a value for a custom preference.
301 | 
302 | ## Method Detail
303 | 
304 | ## Method Details
305 | 
306 | ### getAllowedCurrencies
307 | 
308 | **Signature:** `getAllowedCurrencies() : List`
309 | 
310 | **Description:** Returns the allowed currencies of the current site as a collection of currency codes.
311 | 
312 | **Returns:**
313 | 
314 | Collection of allowed site currencies
315 | 
316 | ---
317 | 
318 | ### getAllowedLocales
319 | 
320 | **Signature:** `getAllowedLocales() : List`
321 | 
322 | **Description:** Returns the allowed locales of the current site as a collection of locale ID's.
323 | 
324 | **Returns:**
325 | 
326 | Collection if allowed site locales
327 | 
328 | ---
329 | 
330 | ### getAllSites
331 | 
332 | **Signature:** `static getAllSites() : List`
333 | 
334 | **Description:** Returns all sites.
335 | 
336 | **Returns:**
337 | 
338 | all sites for the current instance
339 | 
340 | ---
341 | 
342 | ### getCalendar
343 | 
344 | **Signature:** `static getCalendar() : Calendar`
345 | 
346 | **Description:** Returns a new Calendar object in the time zone of the current site.
347 | 
348 | **Returns:**
349 | 
350 | the Calendar object in the time zone of the current site.
351 | 
352 | ---
353 | 
354 | ### getCurrencyCode
355 | 
356 | **Signature:** `getCurrencyCode() : String`
357 | 
358 | **Description:** Returns the default currency code for the current site.
359 | 
360 | **Deprecated:**
361 | 
362 | Use getDefaultCurrency() method instead,
363 | 
364 | **Returns:**
365 | 
366 | the default currency code for the current site.
367 | 
368 | ---
369 | 
370 | ### getCurrent
371 | 
372 | **Signature:** `static getCurrent() : Site`
373 | 
374 | **Description:** Returns the current site.
375 | 
376 | **Returns:**
377 | 
378 | the current site.
379 | 
380 | ---
381 | 
382 | ### getCustomPreferenceValue
383 | 
384 | **Signature:** `getCustomPreferenceValue(name : String) : Object`
385 | 
386 | **Description:** Returns a custom preference value. If the preference does not exist the method returns null. This method is simply a shortcut method for accessing the value for a custom attribute defined on the SitePreferences object. // Method #1 var mySitePrefValue1 : String = dw.system.Site.getCurrent(). getCustomPreferenceValue("mySitePref"); // Method #2 var sitePrefs : SitePreferences = dw.system.Site.getCurrent().getPreferences(); var mySitePrefValue2 : String = sitePrefs.getCustom()["mySitePref"];
387 | 
388 | **Parameters:**
389 | 
390 | - `name`: preference name.
391 | 
392 | **Returns:**
393 | 
394 | the preference value, or null if there is no preference with the given name.
395 | 
396 | ---
397 | 
398 | ### getDefaultCurrency
399 | 
400 | **Signature:** `getDefaultCurrency() : String`
401 | 
402 | **Description:** Returns the default currency code for the current site.
403 | 
404 | **Returns:**
405 | 
406 | the default currency code for the current site.
407 | 
408 | ---
409 | 
410 | ### getDefaultLocale
411 | 
412 | **Signature:** `getDefaultLocale() : String`
413 | 
414 | **Description:** Return default locale for the site.
415 | 
416 | **Returns:**
417 | 
418 | default locale for the site.
419 | 
420 | ---
421 | 
422 | ### getEinsteinSiteID
423 | 
424 | **Signature:** `getEinsteinSiteID() : String`
425 | 
426 | **Description:** Returns the Einstein site Id. Typically this is a concatenation of the realm, underscore character and the site id. It can be overwritten by support users to help with realm moves to continue using the Einstein data from the old realm. Used when making calls to the Einstein APIs.
427 | 
428 | **Returns:**
429 | 
430 | the Einstein site Id
431 | 
432 | ---
433 | 
434 | ### getHttpHostName
435 | 
436 | **Signature:** `getHttpHostName() : String`
437 | 
438 | **Description:** Returns the configured HTTP host name. If no host name is configured the method returns the instance hostname.
439 | 
440 | **Returns:**
441 | 
442 | the configured HTTP host name or if it is not set the instance hostname.
443 | 
444 | ---
445 | 
446 | ### getHttpsHostName
447 | 
448 | **Signature:** `getHttpsHostName() : String`
449 | 
450 | **Description:** Returns the configured HTTPS host name. If no host name is configured the method returns the HTTP host name or the instance hostname, if that is not configured as well.
451 | 
452 | **Returns:**
453 | 
454 | the configured HTTPS host name or HTTP host name or the instance hostname.
455 | 
456 | ---
457 | 
458 | ### getID
459 | 
460 | **Signature:** `getID() : String`
461 | 
462 | **Description:** Returns the ID of the site.
463 | 
464 | **Returns:**
465 | 
466 | the ID of the site.
467 | 
468 | ---
469 | 
470 | ### getName
471 | 
472 | **Signature:** `getName() : String`
473 | 
474 | **Description:** Returns a descriptive name for the site.
475 | 
476 | **Returns:**
477 | 
478 | a descriptive name for the site.
479 | 
480 | ---
481 | 
482 | ### getPageMetaTag
483 | 
484 | **Signature:** `getPageMetaTag(id : String) : PageMetaTag`
485 | 
486 | **Description:** Returns the page meta tag for the specified id. The meta tag content is generated based on the home page meta tag context and rule. The rule is obtained from the current repository domain. Null will be returned if the meta tag is undefined on the current instance, or if no rule can be found for the current context, or if the rule resolves to an empty string.
487 | 
488 | **Parameters:**
489 | 
490 | - `id`: the ID to get the page meta tag for
491 | 
492 | **Returns:**
493 | 
494 | page meta tag containing content generated based on rules
495 | 
496 | ---
497 | 
498 | ### getPageMetaTags
499 | 
500 | **Signature:** `getPageMetaTags() : Array`
501 | 
502 | **Description:** Returns all page meta tags, defined for this instance for which content can be generated. The meta tag content is generated based on the home page meta tag context and rules. The rules are obtained from the current repository domain.
503 | 
504 | **Returns:**
505 | 
506 | page meta tags defined for this instance, containing content generated based on rules
507 | 
508 | ---
509 | 
510 | ### getPreferences
511 | 
512 | **Signature:** `getPreferences() : SitePreferences`
513 | 
514 | **Description:** This method returns a container of all site preferences of this site.
515 | 
516 | **Returns:**
517 | 
518 | a preferences object containing all system and custom site preferences of this site
519 | 
520 | ---
521 | 
522 | ### getStatus
523 | 
524 | **Signature:** `getStatus() : Number`
525 | 
526 | **Description:** Returns the status of this site. Possible values are SITE_STATUS_ONLINE, SITE_STATUS_MAINTENANCE, SITE_STATUS_PROTECTED
527 | 
528 | **Returns:**
529 | 
530 | Status of the this site.
531 | 
532 | ---
533 | 
534 | ### getTimezone
535 | 
536 | **Signature:** `getTimezone() : String`
537 | 
538 | **Description:** Returns the code for the time zone in which the storefront is running.
539 | 
540 | **Returns:**
541 | 
542 | time zone code in which the storefront is running.
543 | 
544 | ---
545 | 
546 | ### getTimezoneOffset
547 | 
548 | **Signature:** `getTimezoneOffset() : Number`
549 | 
550 | **Description:** Returns time zone offset in which the storefront is running.
551 | 
552 | **Returns:**
553 | 
554 | time zone offset in which the storefront is running.
555 | 
556 | ---
557 | 
558 | ### isOMSEnabled
559 | 
560 | **Signature:** `isOMSEnabled() : boolean`
561 | 
562 | **Description:** Whether oms is active in the current site. This depends on a general property which states whether oms is active for the server, and a site-dependent preference whether oms is available for the current site.
563 | 
564 | **Deprecated:**
565 | 
566 | This item is deprecated.
567 | 
568 | **Returns:**
569 | 
570 | whether oms is active in the site
571 | 
572 | ---
573 | 
574 | ### setCustomPreferenceValue
575 | 
576 | **Signature:** `setCustomPreferenceValue(name : String, value : Object) : void`
577 | 
578 | **Description:** The method sets a value for a custom preference. The type of the value must match with the declared type of the preference definition.
579 | 
580 | **Parameters:**
581 | 
582 | - `name`: name of the preference
583 | - `value`: new value for the preference
584 | 
585 | ---
```
Page 23/61FirstPrevNextLast