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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/mock-data/ocapi/system-object-definitions.json:
--------------------------------------------------------------------------------

```json
{
  "_v": "23.2",
  "_type": "object_type_definitions",
  "count": 27,
  "data": [
    {
      "_type": "object_type_definition",
      "_resource_state": "50ddc22db56ece8a16a546bb710d894b8a3d7a90d453c7d3e68c18f1957528b4",
      "object_type": "Appeasement",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Appeasement"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "690b6ebf6a292ebabff1bceee5700e1d23756cf795295590a6d676d80e76752c",
      "object_type": "AppeasementItem",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/AppeasementItem"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "8428af5200166cf3149f948ea843f38832a2f77214699a5b0e26605c6098c93b",
      "object_type": "Basket",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Basket"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "5c583fb95335bb7c08a021f15cec35d972040fd1259d1497fe70e9b0861f0b39",
      "object_type": "BonusDiscountLineItem",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/BonusDiscountLineItem"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "509b6af7dc05ecc7c74beef901e37d6c48803b02922b782a5f13fd67f2855f61",
      "object_type": "Campaign",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Campaign"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "3d5d0265c55926e15bcc829ded6977db8bcf0cd276a02a10823adc0e57a1e795",
      "object_type": "Catalog",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Catalog"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "c02990e0c2dd64cecf2dd7f89b857718ddbf19c1607a30b2a964945f7d653343",
      "object_type": "Category",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Category"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "fddeadc4d54ecd507c9eeb74388b6e51147d42f45108bb56e569316e06c0c900",
      "object_type": "CategoryAssignment",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CategoryAssignment"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "bbd46a905ab7393d2ccbc0ffd111eee82c3c331302161308950f40c6eb23396f",
      "object_type": "Content",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Content"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "c41b2f924e0cf0cb793bf0bd2d50e53e66308a4137d1fed613df584d7ef08a46",
      "object_type": "Coupon",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Coupon"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "861081f499e48eb616129cd71eb4edad71914636b4f722ec5fd035139c4025ab",
      "object_type": "CouponLineItem",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CouponLineItem"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "969b2870f867b14ec145ae39ddc0370ea64951112cde3275ff2bf063f045bf96",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "f6b5e0f6be3e55d750b49d7ebda7ce7e60548ab426deb9c38f847437b83ebc05",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "0f001293cee7e2041cb11c9ac75df963e00e87992c3fae1294f68a520de26a66",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "c551d79ad5d03e43f3818b62deb9bc022bf63ab65a25d20a5baace4814479b93",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "c791af0e68594272ed567adb2a0cd6f317649d268d4a130eaea156453ef93191",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "e0ee4be30313bff780fafbe624a7b5fa12dcd1fe612e07c8253cabeb89673eef",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "37dae93b96f46692eefa0198ebb0343f39b9087a01dc0866ac9f68d607fee347",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "3f00a07beb99dff8bd23a260e39706aef06a972abee152acbf99cb23f393a874",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "ca9c5999b32a237368365b3a6d9282112b318e83a87fa44196887ee5e27ad722",
      "object_type": "CustomObject",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "eefce3a725c68b0396b5fe04bfcfdd3308b7e6c2b959cdf1dc4cf6585aefb40e",
      "object_type": "CustomerActiveData",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerActiveData"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "41677a8c9dc8a3d7bff48395880f3c80eac4d5627d82d03581f7e6ae8e36c0dd",
      "object_type": "CustomerAddress",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerAddress"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "725f55adb3c725776cc9041735b49bad2c4986f6992fdd36c4c7e730df024acf",
      "object_type": "CustomerCDPData",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerCDPData"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "fdcad9f0ccbe08c5620d33558d88872465bc4899fa2e1b1d5d662cf2a20e2333",
      "object_type": "CustomerGroup",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerGroup"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "e81f19ba694f61c8c9773a14d2e1ccde997f33033948e5840ff4fa4ef44381a8",
      "object_type": "CustomerPaymentInstrument",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerPaymentInstrument"
    },
    {
      "_type": "object_type_definition",
      "_resource_state": "f92c6870f867b14ec145ae39ddc0370ea64951112cde3275ff2bf063f045bf97",
      "object_type": "Product",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product"
    },
    {
      "_type": "object_type_definition", 
      "_resource_state": "a93d7870f867b14ec145ae39ddc0370ea64951112cde3275ff2bf063f045bf98",
      "object_type": "Order",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Order"
    }
  ],
  "next": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions?start=27&count=25",
  "start": 0,
  "total": 27
}
```

--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Logger class for standardized logging across the SFCC MCP application.
 * Provides consistent logging with timestamps and log levels.
 * Always logs to files for consistent debugging and to avoid interfering with stdio.
 *
 * ## Log Directory Location
 *
 * The logger uses the operating system's temporary directory via Node.js `os.tmpdir()`:
 * - **macOS**: `/var/folders/{user-specific-path}/T/sfcc-mcp-logs/`
 * - **Linux**: `/tmp/sfcc-mcp-logs/` (typically)
 * - **Windows**: `%TEMP%\sfcc-mcp-logs\` (typically `C:\Users\{user}\AppData\Local\Temp\`)
 *
 * This approach provides:
 * - User-specific isolation (more secure than system-wide `/tmp`)
 * - Automatic cleanup by the OS
 * - Platform-appropriate temporary storage
 * - Proper permissions handling
 *
 * To find your log directory, use `Logger.getInstance().getLogDirectory()` or check
 * the debug logs which show the directory path during initialization.
 */

import { appendFileSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

export class Logger {
  private context: string;
  private enableTimestamp: boolean;
  private debugEnabled: boolean;
  private logDir: string;
  private static instance: Logger | null = null;

  /**
   * Create a new Logger instance
   * @param context The context/component name for this logger
   * @param enableTimestamp Whether to include timestamps in log messages (default: true)
   * @param debugEnabled Whether to enable debug logging (default: false)
   * @param customLogDir Custom log directory for testing purposes
   */
  constructor(context: string = 'SFCC-MCP', enableTimestamp: boolean = true, debugEnabled: boolean = false, customLogDir?: string) {
    this.context = context;
    this.enableTimestamp = enableTimestamp;
    this.debugEnabled = debugEnabled;

    // Set up log directory - use custom directory for testing or default for production
    this.logDir = customLogDir ?? join(tmpdir(), 'sfcc-mcp-logs');
    if (!existsSync(this.logDir)) {
      mkdirSync(this.logDir, { recursive: true });
    }
  }

  /**
   * Initialize the global logger instance with specific settings
   * This should be called once at application startup
   */
  public static initialize(context: string = 'SFCC-MCP', enableTimestamp: boolean = true, debugEnabled: boolean = false, customLogDir?: string): void {
    Logger.instance = new Logger(context, enableTimestamp, debugEnabled, customLogDir);
  }

  /**
   * Get the global logger instance
   * If not initialized, creates a default instance
   */
  public static getInstance(): Logger {
    Logger.instance ??= new Logger();
    return Logger.instance;
  }

  /**
   * Create a child logger with a new context but inheriting other settings from the global instance
   * @param subContext The sub-context to append to the current context
   * @returns A new Logger instance with the combined context
   */
  public static getChildLogger(subContext: string): Logger {
    const globalLogger = Logger.getInstance();
    return new Logger(`${globalLogger.context}:${subContext}`, globalLogger.enableTimestamp, globalLogger.debugEnabled, globalLogger.logDir);
  }

  /**
   * Format a log message with optional timestamp and context
   * @param message The message to format
   * @returns Formatted message string
   */
  private formatMessage(message: string): string {
    const timestamp = this.enableTimestamp ? `[${new Date().toISOString()}] ` : '';
    return `${timestamp}[${this.context}] ${message}`;
  }

  /**
   * Write log message to appropriate log file
   */
  private writeLog(level: 'info' | 'warn' | 'error' | 'debug', message: string, ...args: any[]): void {
    const formattedMessage = this.formatMessage(message);
    const fullMessage = args.length > 0 ? `${formattedMessage} ${args.map(arg =>
      typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg),
    ).join(' ')}` : formattedMessage;

    // Always write to log files
    const logFile = join(this.logDir, `sfcc-mcp-${level}.log`);
    const logEntry = `${fullMessage}\n`;

    try {
      appendFileSync(logFile, logEntry, 'utf8');
    } catch (error) {
      // Fallback: if file logging fails, try stderr for critical errors only
      if (level === 'error') {
        process.stderr.write(`[LOGGER ERROR] Could not write to log file: ${error}\n`);
        process.stderr.write(`${logEntry}`);
      }
    }
  }

  /**
   * Log an informational message
   * @param message The message to log
   * @param args Optional arguments to include
   */
  public log(message: string, ...args: any[]): void {
    this.writeLog('info', message, ...args);
  }

  /**
   * Log an informational message (alias for log)
   * @param message The message to log
   * @param args Optional arguments to include
   */
  public info(message: string, ...args: any[]): void {
    this.writeLog('info', message, ...args);
  }

  /**
   * Log a warning message
   * @param message The warning message to log
   * @param args Optional arguments to include
   */
  public warn(message: string, ...args: any[]): void {
    this.writeLog('warn', message, ...args);
  }

  /**
   * Log an error message
   * @param message The error message to log
   * @param args Optional arguments to include
   */
  public error(message: string, ...args: any[]): void {
    this.writeLog('error', message, ...args);
  }

  /**
   * Log a debug message (only if debug is enabled)
   * @param message The debug message to log
   * @param args Optional arguments to include
   */
  public debug(message: string, ...args: any[]): void {
    if (this.debugEnabled) {
      this.writeLog('debug', `[DEBUG] ${message}`, ...args);
    }
  }

  /**
   * Log method entry with parameters
   * @param methodName The name of the method being entered
   * @param params Optional parameters being passed to the method
   */
  public methodEntry(methodName: string, params?: any): void {
    if (this.debugEnabled) {
      const paramStr = params ? ` with params: ${JSON.stringify(params)}` : '';
      this.debug(`Entering method: ${methodName}${paramStr}`);
    }
  }

  /**
   * Log method exit with optional result
   * @param methodName The name of the method being exited
   * @param result Optional result being returned from the method
   */
  public methodExit(methodName: string, result?: any): void {
    if (this.debugEnabled) {
      const resultStr = result !== undefined ? ` with result: ${typeof result === 'object' ? JSON.stringify(result) : result}` : '';
      this.debug(`Exiting method: ${methodName}${resultStr}`);
    }
  }

  /**
   * Log performance timing information
   * @param operation The operation being timed
   * @param startTime The start time (from performance.now() or Date.now())
   */
  public timing(operation: string, startTime: number): void {
    if (this.debugEnabled) {
      const duration = Date.now() - startTime;
      this.debug(`Performance: ${operation} took ${duration}ms`);
    }
  }

  /**
   * Create a child logger with a new context but inheriting other settings
   * @param subContext The sub-context to append to the current context
   * @returns A new Logger instance with the combined context
   */
  public createChildLogger(subContext: string): Logger {
    return new Logger(`${this.context}:${subContext}`, this.enableTimestamp, this.debugEnabled, this.logDir);
  }

  /**
   * Enable or disable debug logging
   * @param enabled Whether debug logging should be enabled
   */
  public setDebugEnabled(enabled: boolean): void {
    this.debugEnabled = enabled;
  }

  /**
   * Get the current log directory
   */
  public getLogDirectory(): string {
    return this.logDir;
  }
}

// Export the singleton instance getter for convenience
export const getLogger = Logger.getInstance;

```

--------------------------------------------------------------------------------
/tests/mcp/node/tools.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

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

describe('SFCC Development MCP Server - Full Mode with Credentials Tests (Tool Presence Only)', () => {
  let client;
  let toolsCache;

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

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

  beforeEach(() => {
    // CRITICAL: Clear all buffers to prevent leaking into next tests
    client.clearAllBuffers(); // Recommended - comprehensive protection
  });

  test('should successfully connect to server with credentials', async () => {
    assert.ok(client.connected, 'Client should be connected');
  });

  test('should list all available tools in full mode', async () => {
    const tools = await client.listTools();
    toolsCache = tools; // Cache for other tests
    
    assert.ok(Array.isArray(tools), 'Tools should be an array');
    assert.ok(tools.length > 15, 'Should have more tools than documentation-only mode');
  });

  test('should have all documentation tools available', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // SFCC Documentation Tools
    assert.ok(toolNames.includes('get_sfcc_class_info'), 'Should have SFCC class info tool');
    assert.ok(toolNames.includes('search_sfcc_classes'), 'Should have SFCC class search tool');
    assert.ok(toolNames.includes('list_sfcc_classes'), 'Should have SFCC class list tool');
    
    // Best Practices Tools  
    assert.ok(toolNames.includes('get_available_best_practice_guides'), 'Should have best practices list tool');
    assert.ok(toolNames.includes('get_best_practice_guide'), 'Should have best practice guide tool');
    
    // SFRA Documentation Tools
    assert.ok(toolNames.includes('get_available_sfra_documents'), 'Should have SFRA documents list tool');
    assert.ok(toolNames.includes('get_sfra_document'), 'Should have SFRA document tool');
    
    // Cartridge Generation Tools
    assert.ok(toolNames.includes('generate_cartridge_structure'), 'Should have cartridge generation tool');
  });

  test('should have log analysis tools in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // Log analysis tools should be available with credentials
    assert.ok(toolNames.includes('get_latest_error'), 'Should have log error tool');
    assert.ok(toolNames.includes('get_latest_info'), 'Should have log info tool');
    assert.ok(toolNames.includes('get_latest_warn'), 'Should have log warn tool');
    assert.ok(toolNames.includes('get_latest_debug'), 'Should have log debug tool');
    assert.ok(toolNames.includes('summarize_logs'), 'Should have log summary tool');
    assert.ok(toolNames.includes('search_logs'), 'Should have log search tool');
    assert.ok(toolNames.includes('list_log_files'), 'Should have log file listing tool');
  });

  test('should have OCAPI system object tools in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // System object tools should be available with credentials
    assert.ok(toolNames.includes('get_system_object_definitions'), 'Should have system object definitions tool');
    assert.ok(toolNames.includes('get_system_object_definition'), 'Should have system object definition tool');
    assert.ok(toolNames.includes('search_system_object_attribute_definitions'), 'Should have system object attribute search tool');
    assert.ok(toolNames.includes('search_system_object_attribute_groups'), 'Should have system object attribute groups search tool');
    assert.ok(toolNames.includes('search_site_preferences'), 'Should have site preferences search tool');
  });

  test('should have code version tools in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // Code version tools should be available with credentials
    assert.ok(toolNames.includes('get_code_versions'), 'Should have code versions tool');
    assert.ok(toolNames.includes('activate_code_version'), 'Should have code version activation tool');
  });

  test('should have job log tools in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // Job log tools should be available with credentials
    assert.ok(toolNames.includes('get_latest_job_log_files'), 'Should have job log files tool');
    assert.ok(toolNames.includes('get_job_log_entries'), 'Should have job log entries tool');
    assert.ok(toolNames.includes('search_job_logs'), 'Should have job log search tool');
    assert.ok(toolNames.includes('search_job_logs_by_name'), 'Should have job log search by name tool');
    assert.ok(toolNames.includes('get_job_execution_summary'), 'Should have job execution summary tool');
  });

  // NOTE: We intentionally do NOT test tool execution in full mode because:
  // 1. The test credentials are not real SFCC instances
  // 2. Tools would fail with authentication/connection errors  
  // 3. This test suite is designed to verify tool PRESENCE, not functionality
  // 4. Tool functionality testing should be done against real SFCC development instances

  test('should have exactly 36 tools available in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    
    // Full mode should have exactly 36 tools (same count as YAML test)
    assert.equal(tools.length, 36, `Should have exactly 36 tools, got ${tools.length}`);
  });

  test('should have WebDAV-dependent tools (log analysis)', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // WebDAV-dependent tools should be available
    assert.ok(toolNames.includes('summarize_logs'), 'Should have summarize_logs tool');
    assert.ok(toolNames.includes('get_log_file_contents'), 'Should have get_log_file_contents tool');
  });

  test('should have OCAPI-dependent tools (system objects)', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // OCAPI-dependent tools should be available
    assert.ok(toolNames.includes('search_site_preferences'), 'Should have search_site_preferences tool');
    assert.ok(toolNames.includes('search_custom_object_attribute_definitions'), 'Should have search_custom_object_attribute_definitions tool');
  });

  test('should include all docs-only tools in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    const toolNames = tools.map(tool => tool.name);
    
    // Verify that docs-only tools are NOT missing in full mode
    assert.ok(toolNames.includes('search_sfra_documentation'), 'Should have search_sfra_documentation tool');
    assert.ok(toolNames.includes('get_best_practice_guide'), 'Should have get_best_practice_guide tool');
    assert.ok(toolNames.includes('get_sfcc_class_info'), 'Should have get_sfcc_class_info tool');
  });

  test('should validate all tool schemas in full mode', async () => {
    const tools = toolsCache || await client.listTools();
    
    for (const tool of tools) {
      assert.ok(tool.name, `Tool should have a name: ${JSON.stringify(tool, null, 2)}`);
      assert.ok(tool.description, `Tool ${tool.name} should have a description`);
      assert.ok(tool.inputSchema, `Tool ${tool.name} should have an input schema`);
      assert.equal(tool.inputSchema.type, 'object', `Tool ${tool.name} input schema should be object type`);
    }
  });
});

```

--------------------------------------------------------------------------------
/docs/dw_order/ReturnCaseItem.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.order

# Class ReturnCaseItem

## Inheritance Hierarchy

- Object
  - dw.object.Extensible
  - dw.order.AbstractItem
    - dw.order.ReturnCaseItem

## Description

An item of a ReturnCase, created using method ReturnCase.createItem(String). Initially the ReturnCaseItem is NEW. No Return can be created at this point. From NEW the item transitions in CONFIRMED state. Now Return can be created. Next transition is either to PARTIAL_RETURNED or to CANCELLED. At the end the item can be RETURNED (no other Returns can be created. The custom code implementing the ReturnHooks is responsible to provide the logic for the transitions. Please refer to the documentation of ReturnHooks for further information. When the related ReturnCase were confirmed, only the the custom attributes of the return case item can be changed. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.

## Constants

### STATUS_CANCELLED

**Type:** String = "CANCELLED"

constant for ReturnCase Status CANCELLED

### STATUS_CONFIRMED

**Type:** String = "CONFIRMED"

constant for ReturnCase Status CONFIRMED

### STATUS_NEW

**Type:** String = "NEW"

constant for ReturnCase Status NEW

### STATUS_PARTIAL_RETURNED

**Type:** String = "PARTIAL_RETURNED"

constant for ReturnCase Status PARTIAL RETURNED

### STATUS_RETURNED

**Type:** String = "RETURNED"

constant for ReturnCase Status RETURNED

## Properties

### authorizedQuantity

**Type:** Quantity

Return the Quantity authorized for this ReturnCaseItem, may be N/A.

### basePrice

**Type:** Money (Read Only)

Price of a single unit before discount application.

### note

**Type:** String

Return the note for this return case item.

### parentItem

**Type:** ReturnCaseItem

Returns null or the parent item.

### reasonCode

**Type:** EnumValue

The reason code for return case item.

### returnCaseNumber

**Type:** String (Read Only)

Mandatory number of ReturnCase to which this item belongs

### returnItems

**Type:** Collection (Read Only)

Unsorted collection of ReturnItems associated with this ReturnCaseItem.

### status

**Type:** EnumValue

Gets the return case item status.
 
 The possible values are STATUS_NEW,STATUS_CONFIRMED,
 STATUS_PARTIAL_RETURNED, STATUS_RETURNED,
 STATUS_CANCELLED.

## Constructor Summary

## Method Summary

### createReturnItem

**Signature:** `createReturnItem(returnNumber : String) : ReturnItem`

Create a new ReturnItem for this ReturnCaseItem and assign it to the given Return.

### getAuthorizedQuantity

**Signature:** `getAuthorizedQuantity() : Quantity`

Return the Quantity authorized for this ReturnCaseItem, may be N/A.

### getBasePrice

**Signature:** `getBasePrice() : Money`

Price of a single unit before discount application.

### getNote

**Signature:** `getNote() : String`

Return the note for this return case item.

### getParentItem

**Signature:** `getParentItem() : ReturnCaseItem`

Returns null or the parent item.

### getReasonCode

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

Returns the reason code for return case item.

### getReturnCaseNumber

**Signature:** `getReturnCaseNumber() : String`

Mandatory number of ReturnCase to which this item belongs

### getReturnItems

**Signature:** `getReturnItems() : Collection`

Unsorted collection of ReturnItems associated with this ReturnCaseItem.

### getStatus

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

Gets the return case item status.

### setAuthorizedQuantity

**Signature:** `setAuthorizedQuantity(authorizedQuantity : Quantity) : void`

Set the optional authorized Quantity for this item.

### setNote

**Signature:** `setNote(note : String) : void`

Sets a note for this return case item.

### setParentItem

**Signature:** `setParentItem(parentItem : ReturnCaseItem) : void`

Set a parent item.

### setReasonCode

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

Changes the reason code.

### setStatus

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

Sets the status.

## Method Detail

## Method Details

### createReturnItem

**Signature:** `createReturnItem(returnNumber : String) : ReturnItem`

**Description:** Create a new ReturnItem for this ReturnCaseItem and assign it to the given Return.

**Parameters:**

- `returnNumber`: number of Return to which new item is assigned.

**Returns:**

new ReturnItem

---

### getAuthorizedQuantity

**Signature:** `getAuthorizedQuantity() : Quantity`

**Description:** Return the Quantity authorized for this ReturnCaseItem, may be N/A.

**Returns:**

the authorized quantity or N/A

---

### getBasePrice

**Signature:** `getBasePrice() : Money`

**Description:** Price of a single unit before discount application.

**Returns:**

Price of a single unit before discount application.

---

### getNote

**Signature:** `getNote() : String`

**Description:** Return the note for this return case item.

**Returns:**

the note or null

---

### getParentItem

**Signature:** `getParentItem() : ReturnCaseItem`

**Description:** Returns null or the parent item.

**Returns:**

null or the parent item.

---

### getReasonCode

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

**Description:** Returns the reason code for return case item.

**Returns:**

the return reason code

---

### getReturnCaseNumber

**Signature:** `getReturnCaseNumber() : String`

**Description:** Mandatory number of ReturnCase to which this item belongs

**Returns:**

number of ReturnCase to which this item belongs

---

### getReturnItems

**Signature:** `getReturnItems() : Collection`

**Description:** Unsorted collection of ReturnItems associated with this ReturnCaseItem.

**Returns:**

unsorted collection of ReturnItems associated with this ReturnCaseItem

**See Also:**

createReturnItem(String)

---

### getStatus

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

**Description:** Gets the return case item status. The possible values are STATUS_NEW,STATUS_CONFIRMED, STATUS_PARTIAL_RETURNED, STATUS_RETURNED, STATUS_CANCELLED.

**Returns:**

the status

---

### setAuthorizedQuantity

**Signature:** `setAuthorizedQuantity(authorizedQuantity : Quantity) : void`

**Description:** Set the optional authorized Quantity for this item. Passing null will result in an N/A Quantity being set.

**Parameters:**

- `authorizedQuantity`: null or the quantity

---

### setNote

**Signature:** `setNote(note : String) : void`

**Description:** Sets a note for this return case item.

**Parameters:**

- `note`: the note for this return case item to set

---

### setParentItem

**Signature:** `setParentItem(parentItem : ReturnCaseItem) : void`

**Description:** Set a parent item. The parent item must belong to the same ReturnCase. An infinite parent-child loop is disallowed as is a parent-child depth greater than 10. Setting a parent item indicates a dependency of the child item on the parent item, and can be used to form a parallel structure to that accessed using ProductLineItem.getParent().

**Parameters:**

- `parentItem`: The parent item, null is allowed

---

### setReasonCode

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

**Description:** Changes the reason code. Initially the reason code is set on return case item creation.

**Parameters:**

- `reasonCode`: the reason code to set

---

### setStatus

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

**Description:** Sets the status. The possible values are STATUS_NEW,STATUS_CONFIRMED, STATUS_PARTIAL_RETURNED, STATUS_RETURNED, STATUS_CANCELLED.

**Parameters:**

- `statusString`: the status

**Throws:**

NullPointerException - if status is null
IllegalArgumentException - if the status transition to the status is not allowed

---
```

--------------------------------------------------------------------------------
/tests/ocapi-auth-client.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for OCAPIAuthClient
 * Tests OAuth authentication functionality for OCAPI
 */

import { OCAPIAuthClient } from '../src/clients/base/ocapi-auth-client.js';
import { TokenManager } from '../src/clients/base/oauth-token.js';
import { OCAPIConfig, OAuthTokenResponse } from '../src/types/types.js';

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

// Mock TokenManager
jest.mock('../src/clients/base/oauth-token.js');

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

// Mock BaseHttpClient
jest.mock('../src/clients/base/http-client.js');

describe('OCAPIAuthClient', () => {
  let client: OCAPIAuthClient;
  let mockTokenManager: jest.Mocked<TokenManager>;
  let mockFetch: jest.MockedFunction<typeof fetch>;

  const mockConfig: OCAPIConfig = {
    hostname: 'test-instance.demandware.net',
    clientId: 'test-client-id',
    clientSecret: 'test-client-secret',
    version: 'v21_3',
  };

  beforeEach(() => {
    jest.clearAllMocks();

    mockFetch = fetch as jest.MockedFunction<typeof fetch>;

    // Setup TokenManager mock
    mockTokenManager = {
      getValidToken: jest.fn(),
      storeToken: jest.fn(),
      clearToken: jest.fn(),
      getTokenExpiration: jest.fn(),
      isTokenValid: jest.fn(),
      clearAllTokens: jest.fn(),
    } as any;

    (TokenManager.getInstance as jest.Mock).mockReturnValue(mockTokenManager);

    client = new OCAPIAuthClient(mockConfig);

    // Manually set up the logger mock since BaseHttpClient mock isn't working as expected
    (client as any).logger = {
      debug: jest.fn(),
      info: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
    };
  });

  describe('constructor', () => {
    it('should initialize with config', () => {
      expect(client).toBeInstanceOf(OCAPIAuthClient);
      expect(TokenManager.getInstance).toHaveBeenCalled();
    });
  });

  describe('getAuthHeaders', () => {
    it('should return Bearer token in auth headers', async () => {
      const mockToken = 'mock-access-token';
      mockTokenManager.getValidToken.mockReturnValue(mockToken);

      const headers = await (client as any).getAuthHeaders();

      expect(headers).toEqual({
        'Authorization': 'Bearer mock-access-token',
      });
      expect(mockTokenManager.getValidToken).toHaveBeenCalledWith(
        mockConfig.hostname,
        mockConfig.clientId,
      );
    });

    it('should request new token when no valid token exists', async () => {
      const newToken = 'new-access-token';
      const tokenResponse: OAuthTokenResponse = {
        access_token: newToken,
        token_type: 'bearer',
        expires_in: 3600,
      };

      mockTokenManager.getValidToken.mockReturnValue(null);
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => tokenResponse,
      } as Response);

      const headers = await (client as any).getAuthHeaders();

      expect(headers).toEqual({
        'Authorization': 'Bearer new-access-token',
      });
      expect(mockFetch).toHaveBeenCalledWith(
        'https://account.demandware.com/dwsso/oauth2/access_token',
        {
          method: 'POST',
          headers: {
            'Authorization': `Basic ${Buffer.from(`${mockConfig.clientId}:${mockConfig.clientSecret}`).toString('base64')}`,
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: 'grant_type=client_credentials',
        },
      );
      expect(mockTokenManager.storeToken).toHaveBeenCalledWith(
        mockConfig.hostname,
        mockConfig.clientId,
        tokenResponse,
      );
    });

    it('should handle OAuth request failure', async () => {
      mockTokenManager.getValidToken.mockReturnValue(null);
      mockFetch.mockResolvedValue({
        ok: false,
        status: 401,
        statusText: 'Unauthorized',
        text: async () => 'Invalid credentials',
      } as Response);

      await expect((client as any).getAuthHeaders()).rejects.toThrow(
        'Failed to get access token: Error: OAuth authentication failed: 401 Unauthorized - Invalid credentials',
      );
    });

    it('should handle network errors during token request', async () => {
      mockTokenManager.getValidToken.mockReturnValue(null);
      mockFetch.mockRejectedValue(new Error('Network error'));

      await expect((client as any).getAuthHeaders()).rejects.toThrow(
        'Failed to get access token: Error: Network error',
      );
    });
  });

  describe('handleAuthError', () => {
    it('should clear token when handling auth error', async () => {
      await (client as any).handleAuthError();

      expect(mockTokenManager.clearToken).toHaveBeenCalledWith(
        mockConfig.hostname,
        mockConfig.clientId,
      );
    });
  });

  describe('getTokenExpiration', () => {
    it('should return token expiration from TokenManager', () => {
      const mockExpiration = new Date('2025-12-31T23:59:59Z');
      mockTokenManager.getTokenExpiration.mockReturnValue(mockExpiration);

      const result = client.getTokenExpiration();

      expect(result).toBe(mockExpiration);
      expect(mockTokenManager.getTokenExpiration).toHaveBeenCalledWith(
        mockConfig.hostname,
        mockConfig.clientId,
      );
    });

    it('should return null when no token exists', () => {
      mockTokenManager.getTokenExpiration.mockReturnValue(null);

      const result = client.getTokenExpiration();

      expect(result).toBeNull();
    });
  });

  describe('refreshToken', () => {
    it('should clear token and request new one', async () => {
      const newToken = 'refreshed-token';
      const tokenResponse: OAuthTokenResponse = {
        access_token: newToken,
        token_type: 'bearer',
        expires_in: 3600,
      };

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

      await client.refreshToken();

      expect(mockTokenManager.clearToken).toHaveBeenCalledWith(
        mockConfig.hostname,
        mockConfig.clientId,
      );
      expect(mockFetch).toHaveBeenCalled();
    });

    it('should handle refresh token failure', async () => {
      mockFetch.mockResolvedValue({
        ok: false,
        status: 401,
        statusText: 'Unauthorized',
        text: async () => 'Invalid refresh',
      } as Response);

      await expect(client.refreshToken()).rejects.toThrow(
        'Failed to get access token',
      );
    });
  });

  describe('token management flow', () => {
    it('should use existing token when valid', async () => {
      const existingToken = 'existing-valid-token';
      mockTokenManager.getValidToken.mockReturnValue(existingToken);

      const headers = await (client as any).getAuthHeaders();

      expect(headers).toEqual({
        'Authorization': 'Bearer existing-valid-token',
      });
      expect(mockFetch).not.toHaveBeenCalled();
    });

    it('should store new token after successful request', async () => {
      const tokenResponse: OAuthTokenResponse = {
        access_token: 'new-token',
        token_type: 'bearer',
        expires_in: 7200,
      };

      mockTokenManager.getValidToken.mockReturnValue(null);
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => tokenResponse,
      } as Response);

      await (client as any).getAuthHeaders();

      expect(mockTokenManager.storeToken).toHaveBeenCalledWith(
        mockConfig.hostname,
        mockConfig.clientId,
        tokenResponse,
      );
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-code-versions.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
# ==================================================================================
# SFCC MCP Server - get_code_versions Tool YAML Tests (Full Mode)
# Tests code version retrieval functionality with SFCC credentials
# This tool provides code version management for deployment troubleshooting
# 
# Quick Test Commands:
# aegis "tests/mcp/yaml/get-code-versions.full-mode.test.mcp.yml" --config "aegis.config.with-dw.json" --verbose
# aegis query get_code_versions '{}' --config "aegis.config.with-dw.json"
# ==================================================================================

description: "get_code_versions tool full mode tests - Core functionality validation"

tests:
  # ==================================================================================
  # TOOL AVAILABILITY TESTS
  # ==================================================================================
  - it: "should list get_code_versions tool in full mode"
    request:
      jsonrpc: "2.0"
      id: "tool-availability-full"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "tool-availability-full"
        result:
          tools:
            match:arrayElements:
              match:partial:
                name: "match:type:string"
                description: "match:type:string"
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_code_versions"
      stderr: "toBeEmpty"

  - it: "should have correct tool definition in full mode"
    request:
      jsonrpc: "2.0"
      id: "tool-definition-full"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "tool-definition-full"
        result:
          tools: "match:arrayContains:name:get_code_versions"
      stderr: "toBeEmpty"

  # ==================================================================================
  # BASIC FUNCTIONALITY TESTS
  # ==================================================================================
  - it: "should retrieve code versions with default parameters"
    request:
      jsonrpc: "2.0"
      id: "code-versions-default"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "code-versions-default"
        result:
          content:
            match:arrayElements:
              type: "text"
              text: "match:regex:[\\s\\S]*code_version_result[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"
    performance:
      maxResponseTime: "2000ms"

  # ==================================================================================
  # PARAMETER VALIDATION TESTS
  # ==================================================================================
  - it: "should accept empty arguments object"
    request:
      jsonrpc: "2.0"
      id: "empty-args"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "empty-args"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
          isError: false
      stderr: "toBeEmpty"
    performance:
      maxResponseTime: "2000ms"

  - it: "should ignore extra parameters gracefully"
    request:
      jsonrpc: "2.0"
      id: "extra-params"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments:
          extraParam: "should be ignored"
          anotherParam: 123
    expect:
      response:
        jsonrpc: "2.0"
        id: "extra-params"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
          isError: false
      stderr: "toBeEmpty"
    performance:
      maxResponseTime: "2000ms"

  # ==================================================================================
  # RESPONSE CONTENT VALIDATION
  # ==================================================================================
  - it: "should return properly formatted JSON content"
    request:
      jsonrpc: "2.0"
      id: "json-format"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "json-format"
        result:
          content:
            match:arrayElements:
              type: "text"
              text: "match:regex:[\\s\\S]*\\{[\\s\\S]*\\}[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  - it: "should include essential code version fields"
    request:
      jsonrpc: "2.0"
      id: "essential-fields"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "essential-fields"
        result:
          content:
            match:arrayElements:
              type: "text"
              text: "match:regex:[\\s\\S]*_type[\\s\\S]*code_version_result[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  - it: "should include data array with code version objects"
    request:
      jsonrpc: "2.0"
      id: "data-array"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "data-array"
        result:
          content:
            match:arrayElements:
              type: "text"
              text: "match:regex:[\\s\\S]*data[\\s\\S]*\\[[\\s\\S]*\\][\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  - it: "should include code version metadata"
    request:
      jsonrpc: "2.0"
      id: "version-metadata"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "version-metadata"
        result:
          content:
            match:arrayElements:
              type: "text"
              text: "match:regex:[\\s\\S]*(?:id)[\\s\\S]*(?:active)[\\s\\S]*"
          isError: false
      stderr: "toBeEmpty"

  # ==================================================================================
  # PERFORMANCE TESTS
  # ==================================================================================
  - it: "should respond within acceptable time limit"
    request:
      jsonrpc: "2.0"
      id: "performance-test"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "performance-test"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
          isError: false
      stderr: "toBeEmpty"
    performance:
      maxResponseTime: "2000ms"

  - it: "should maintain consistent performance on repeat calls"
    request:
      jsonrpc: "2.0"
      id: "performance-consistency"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "performance-consistency"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
          isError: false
      stderr: "toBeEmpty"
    performance:
      maxResponseTime: "2000ms"

  # ==================================================================================
  # ERROR HANDLING TESTS (Edge Cases)
  # ==================================================================================
  - it: "should return validation error for null arguments"
    request:
      jsonrpc: "2.0"
      id: "null-args"
      method: "tools/call"
      params:
        name: "get_code_versions"
        arguments: null
    expect:
      response:
        jsonrpc: "2.0"
        id: "null-args"
        error:
          code: "match:type:number"
          message: "match:type:string"
      stderr: "toBeEmpty"
```

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/src/routes/webdav.js:
--------------------------------------------------------------------------------

```javascript
/**
 * WebDAV Route Handler
 * 
 * Handles WebDAV requests including PROPFIND for directory listings 
 * and GET requests for file content with range support.
 * Modular implementation following single responsibility principle.
 */

const express = require('express');
const fs = require('fs');
const path = require('path');
const { URL } = require('url');
const {
    generateFileXmlResponse,
    generateDirectoryXmlResponse,
    generateErrorXmlResponse,
    parseDepthHeader
} = require('../utils/webdav-xml');

class WebDAVRouteHandler {
    constructor(config) {
        this.config = config;
        this.webdavConfig = config.getWebdavConfig();
        this.router = express.Router();
        this.setupRoutes();
    }

    setupRoutes() {
        // Handle PROPFIND method (custom method support)
        this.router.use((req, res, next) => {
            if (req.method === 'PROPFIND') {
                this.handlePropfind(req, res);
            } else {
                next();
            }
        });

        // WebDAV base path routes
        this.router.get(`${this.webdavConfig.basePath}/Logs/*`, (req, res) => {
            this.handleGet(req, res);
        });

        // Direct logs path routes (for backward compatibility)
        this.router.get('/Logs/*', (req, res) => {
            this.handleGet(req, res);
        });

        // Root logs directory
        this.router.get('/Logs', (req, res) => {
            this.handleGet(req, res);
        });
    }

    /**
     * Map URL path to file system path
     */
    mapUrlToFilePath(urlPath) {
        // Handle SFCC WebDAV path structure
        if (urlPath.startsWith(this.webdavConfig.basePath)) {
            // Remove the WebDAV base path and map to our mock structure
            let relativePath = urlPath.replace(this.webdavConfig.basePath, '');
            // Convert uppercase Logs to lowercase logs for file system, but remove the leading /Logs since it's already in logsPath
            relativePath = relativePath.replace(/^\/Logs/, '');
            return path.join(this.webdavConfig.logsPath, relativePath);
        }
        
        // Fallback: treat as direct path to logs (for backward compatibility)
        let relativePath = urlPath.replace(/^\/+/, '');
        // Convert uppercase Logs to lowercase logs for file system, but remove the leading Logs since it's already in logsPath
        relativePath = relativePath.replace(/^Logs/, '');
        return path.join(this.webdavConfig.logsPath, relativePath);
    }

    /**
     * Handle GET requests for file content
     */
    handleGet(req, res) {
        try {
            const urlPath = decodeURIComponent(req.path);
            const fsPath = this.mapUrlToFilePath(urlPath);

            if (this.config.isDevMode) {
                console.log(`[WebDAV] GET ${urlPath} -> ${fsPath}`);
            }

            if (!fs.existsSync(fsPath)) {
                return res.status(404).send('Not Found');
            }

            const stats = fs.statSync(fsPath);
            if (stats.isDirectory()) {
                return res.status(404).send('Not Found');
            }

            // Handle range requests for partial content
            const rangeHeader = req.headers.range;
            if (rangeHeader) {
                return this.handleRangeRequest(req, res, fsPath, stats);
            }

            // Normal full file request
            res.set({
                'Content-Type': 'text/plain',
                'Content-Length': stats.size,
                'Accept-Ranges': 'bytes'
            });
            
            const stream = fs.createReadStream(fsPath);
            stream.pipe(res);

        } catch (error) {
            console.error('[WebDAV] Error handling GET request:', error);
            res.status(500).send('Internal Server Error');
        }
    }

    /**
     * Handle range requests for partial content
     */
    handleRangeRequest(req, res, fsPath, stats) {
        try {
            const rangeHeader = req.headers.range;
            const fileSize = stats.size;
            
            if (this.config.isDevMode) {
                console.log(`[WebDAV] Range request: ${rangeHeader} for file ${fsPath} (size: ${fileSize})`);
            }

            // Parse range header (e.g., "bytes=0-499" or "bytes=500-999")
            const rangeMatch = rangeHeader.match(/bytes=(\d+)-(\d*)/);
            if (!rangeMatch) {
                res.set('Content-Range', `bytes */${fileSize}`);
                return res.status(416).send('Range Not Satisfiable');
            }

            const start = parseInt(rangeMatch[1], 10);
            const end = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : fileSize - 1;

            // Validate range
            if (start >= fileSize || end >= fileSize || start > end) {
                res.set('Content-Range', `bytes */${fileSize}`);
                return res.status(416).send('Range Not Satisfiable');
            }

            const contentLength = end - start + 1;

            if (this.config.isDevMode) {
                console.log(`[WebDAV] Range: ${start}-${end}, Content-Length: ${contentLength}`);
            }

            // Send partial content response
            res.set({
                'Content-Type': 'text/plain',
                'Content-Length': contentLength,
                'Content-Range': `bytes ${start}-${end}/${fileSize}`,
                'Accept-Ranges': 'bytes'
            });

            res.status(206); // Partial Content
            const stream = fs.createReadStream(fsPath, { start, end });
            stream.pipe(res);

        } catch (error) {
            console.error('[WebDAV] Error handling range request:', error);
            res.status(500).send('Internal Server Error');
        }
    }

    /**
     * Handle PROPFIND requests for directory listings
     */
    handlePropfind(req, res) {
        try {
            const urlPath = decodeURIComponent(req.path);
            const fsPath = this.mapUrlToFilePath(urlPath);

            if (this.config.isDevMode) {
                console.log(`[WebDAV] PROPFIND ${urlPath} -> ${fsPath}`);
            }

            if (!fs.existsSync(fsPath)) {
                if (this.config.isDevMode) {
                    console.log(`[WebDAV] File not found: ${fsPath}`);
                }
                return res.status(404).send('Not Found');
            }

            const stats = fs.statSync(fsPath);
            
            if (this.config.isDevMode) {
                console.log(`[WebDAV] File stats: size=${stats.size}, isFile=${stats.isFile()}, isDirectory=${stats.isDirectory()}`);
            }
            
            // Handle PROPFIND for individual files
            if (stats.isFile()) {
                const xml = generateFileXmlResponse(urlPath, stats);

                if (this.config.isDevMode) {
                    console.log(`[WebDAV] Returning file PROPFIND response for ${urlPath}`);
                }

                res.set('Content-Type', 'application/xml');
                return res.status(207).send(xml);
            }

            // Handle PROPFIND for directories
            if (!stats.isDirectory()) {
                return res.status(404).send('Not Found');
            }

            // Read directory contents
            const itemNames = fs.readdirSync(fsPath);
            const items = itemNames.map(name => {
                const itemPath = path.join(fsPath, name);
                const itemStats = fs.statSync(itemPath);
                return { name, stats: itemStats };
            });

            const xml = generateDirectoryXmlResponse(urlPath, items, stats);

            res.set('Content-Type', 'application/xml');
            res.status(207).send(xml);

        } catch (error) {
            console.error(`[WebDAV] Error handling PROPFIND request for ${req.path}:`, error);
            res.status(500).send('Internal Server Error');
        }
    }

    /**
     * Get the configured router
     */
    getRouter() {
        return this.router;
    }
}

module.exports = WebDAVRouteHandler;
```

--------------------------------------------------------------------------------
/src/clients/logs/log-file-reader.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Log file reading operations with range request support
 */

import type { WebDAVClient } from 'webdav';
import { Logger } from '../../utils/logger.js';
import { LOG_CONSTANTS } from './log-constants.js';
import type { FileReadOptions } from './log-types.js';

export class LogFileReader {
  private logger: Logger;
  private webdavClient: WebDAVClient;

  constructor(webdavClient: WebDAVClient, logger: Logger) {
    this.webdavClient = webdavClient;
    this.logger = logger;
  }

  /**
   * Get the last portion of a file using range requests to avoid loading huge files
   *
   * @param filename - The file path to read
   * @param options - Read options including maxBytes
   * @returns Promise<string> - The file content as a string
   */
  async getFileContentsTail(
    filename: string,
    options: FileReadOptions = {},
  ): Promise<string> {
    const { maxBytes = LOG_CONSTANTS.DEFAULT_TAIL_BYTES } = options;

    this.logger.debug(`Reading file tail: ${filename} (maxBytes: ${maxBytes})`);

    try {
      // First, try to get file info to determine the file size
      this.logger.debug(`Attempting stat for file: ${filename}`);
      const stat = await this.webdavClient.stat(filename);
      this.logger.debug(`Stat successful for ${filename}:`, stat);
      const fileSize = (stat as any).size;

      if (!fileSize || fileSize <= maxBytes) {
        // File is small enough or size unknown, just get the whole file
        this.logger.debug(`File ${filename} is small (${fileSize} bytes), reading full content`);
        return await this.getFullFileContents(filename);
      }

      // File is large, get only the last portion using range request
      this.logger.debug(`File ${filename} is large (${fileSize} bytes), using range request for last ${maxBytes} bytes`);
      const startByte = fileSize - maxBytes;
      return await this.getRangeFileContents(filename, startByte, fileSize - 1);

    } catch (statError) {
      const error = statError as Error;
      this.logger.warn(`Failed to get file stats for ${filename}, falling back to full file. Error:`, {
        message: error.message,
        name: error.name,
        code: (error as any).code,
        status: (error as any).status,
      });
      return await this.getFullFileContents(filename);
    }
  }

  /**
   * Read the full contents of a file
   */
  private async getFullFileContents(filename: string): Promise<string> {
    const content = await this.webdavClient.getFileContents(filename, { format: 'text' });
    return content as string;
  }

  /**
   * Read from the beginning of a file with optional size limit
   *
   * @param filename - The file path to read
   * @param maxBytes - Maximum number of bytes to read from the beginning
   * @returns Promise<string> - The file content as a string (truncated if needed)
   */
  async getFileContentsHead(
    filename: string,
    maxBytes?: number,
  ): Promise<string> {
    this.logger.debug(`Reading file head: ${filename} (maxBytes: ${maxBytes ?? 'unlimited'})`);

    if (!maxBytes) {
      // No size limit, read full file
      return await this.getFullFileContents(filename);
    }

    try {
      // First, try to get file info to determine the file size
      this.logger.debug(`Attempting stat for file: ${filename}`);
      const stat = await this.webdavClient.stat(filename);
      this.logger.debug(`Stat successful for ${filename}:`, stat);
      const fileSize = (stat as any).size;

      if (!fileSize || fileSize <= maxBytes) {
        // File is small enough or size unknown, just get the whole file
        this.logger.debug(`File ${filename} is small (${fileSize} bytes), reading full content`);
        return await this.getFullFileContents(filename);
      }

      // File is large, get only the first portion using range request
      this.logger.debug(`File ${filename} is large (${fileSize} bytes), using range request for first ${maxBytes} bytes`);
      return await this.getRangeFileContents(filename, 0, maxBytes - 1);

    } catch (statError) {
      const error = statError as Error;
      this.logger.warn(`Failed to get file stats for ${filename}, falling back to full file with truncation. Error:`, {
        message: error.message,
        name: error.name,
        code: (error as any).code,
        status: (error as any).status,
      });
      // Fallback to reading full file and truncating
      const content = await this.getFullFileContents(filename);
      if (content.length > maxBytes) {
        this.logger.debug(`Truncating full file content from ${content.length} to ${maxBytes} characters`);
        return content.substring(0, maxBytes);
      }
      return content;
    }
  }

  /**
   * Read a range of bytes from a file using streaming
   *
   * @param filename - The file path to read
   * @param start - Start byte position (0-based)
   * @param end - End byte position (inclusive)
   * @returns Promise<string> - The file content as a string
   */
  private async getRangeFileContents(
    filename: string,
    start: number,
    end: number,
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      try {
        const stream = this.webdavClient.createReadStream(filename, {
          range: { start, end },
        });

        const chunks: Buffer[] = [];

        stream.on('data', (chunk: Buffer) => {
          chunks.push(chunk);
        });

        stream.on('end', () => {
          const content = Buffer.concat(chunks).toString('utf-8');
          this.logger.debug(`Successfully read ${content.length} characters from range request (${start}-${end})`);
          resolve(content);
        });

        stream.on('error', (error: any) => {
          this.logger.warn(`Failed to read range ${start}-${end} for ${filename}, falling back to full file:`, error);
          // Fallback to getting the full file
          this.getFullFileContents(filename)
            .then((content: string) => {
              // Truncate to the requested range if possible
              const requestedLength = end - start + 1;
              if (start === 0 && content.length > requestedLength) {
                // Reading from start, truncate
                resolve(content.substring(0, requestedLength));
              } else {
                // Reading from end or full file is smaller, return what we have
                resolve(content);
              }
            })
            .catch((fallbackError: any) => reject(fallbackError));
        });
      } catch (error) {
        // If createReadStream fails, fall back to full file
        this.logger.warn(`Failed to create read stream for ${filename}, falling back to full file:`, error);
        this.getFullFileContents(filename)
          .then((content: string) => {
            const requestedLength = end - start + 1;
            if (start === 0 && content.length > requestedLength) {
              resolve(content.substring(0, requestedLength));
            } else {
              resolve(content);
            }
          })
          .catch((fallbackError: any) => reject(fallbackError));
      }
    });
  }

  /**
   * Read multiple files with tail optimization
   */
  async readMultipleFiles(
    filenames: string[],
    options: FileReadOptions = {},
  ): Promise<Map<string, string>> {
    const results = new Map<string, string>();

    for (const filename of filenames) {
      try {
        const content = await this.getFileContentsTail(filename, options);
        results.set(filename, content);
      } catch (error) {
        this.logger.error(`Error reading file ${filename}:`, error);
        // Continue processing other files even if one fails
      }
    }

    return results;
  }

  /**
   * Check if a file exists and get its basic metadata
   */
  async getFileInfo(filename: string): Promise<{ exists: boolean; size?: number; lastmod?: string }> {
    try {
      const stat = await this.webdavClient.stat(filename);
      return {
        exists: true,
        size: (stat as any).size,
        lastmod: (stat as any).lastmod,
      };
    } catch {
      return { exists: false };
    }
  }
}

```

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

```markdown
# Instructions for Creating a Salesforce B2C Commerce (SFRA) Cartridge

This document provides instructions to create, configure, and deploy a new custom cartridge for Salesforce B2C Commerce using the Storefront Reference Architecture (SFRA).

**NOTE**: When doing this, also request best practices for controller creation from this MCP server. Additionally, consult the **Performance and Stability Best Practices** guide from this MCP server to ensure your cartridge follows performance optimization strategies and coding standards.

## 1. Core Principles

**Cartridge:** A cartridge is a self-contained module for code and data. It is the fundamental unit for extending functionality.

**Cartridge Path:** A colon-separated list of cartridge names that dictates the order of code execution. The path is searched from left to right, and the first resource found is used.

**Override Mechanism:** To customize functionality, create a new cartridge (e.g., app_custom_mybrand) and place it at the beginning of the cartridge path. This allows your custom files to override the base functionality without modifying the core app_storefront_base cartridge.

**Example Path:** `app_custom_mybrand:app_storefront_base`

## 2. Prerequisites

- **Git Client:** Installed and configured.
- **Node.js:** Version 18 is recommended for compatibility.
- **SFCC Sandbox:** Access to a sandbox instance, including Business Manager credentials.
- **GitHub Access:** Ability to clone SalesforceCommerceCloud repositories.

## 3. Environment Setup

Create a parent project directory.

Clone the following repositories as siblings inside the parent directory:

Note: This step is not necessary unless specifically asked for. You should only clone these if the user requests it, for most
projects, there is only a need for the new cartridge that is being built - but not the entire storefront reference architecture.

```bash
# Contains the base cartridge (app_storefront_base)
git clone [email protected]:SalesforceCommerceCloud/storefront-reference-architecture.git

# Contains build and deployment scripts
git clone [email protected]:SalesforceCommerceCloud/sgmf-scripts.git
```

Install dependencies:

```bash
cd storefront-reference-architecture
npm install
```

## 4. Cartridge File Structure

A new cartridge should be created using the provided scaffolding tool. The core structure is as follows:

```
package.json
.eslintrc.json
.eslintignore
.stylelintrc.json
.gitignore
README.md
dw.json    
webpack.config.js           
cartridges/
└── plugin_my_custom_cartridge/
    └── cartridge/
        ├── client/             
        │   └── default/
        │       ├── js/
        │       └── scss/
        ├── controllers/        
        ├── models/             
        ├── scripts/            
        └── templates/       
            └── default/
```

Optional but common directories:

- `cartridge/forms/default/`: XML form definitions.
- `cartridge/services/`: Definitions for external web service integrations.
- `cartridge/scripts/jobs/`: Scripts for automated tasks scheduled in Business Manager.
- `cartridge/properties/`: Localization string files (.properties).

## 5. Creating a New Cartridge

### Step 1: Generate the Cartridge Structure

!IMPORTANT!: Always do this step, don't attempt to create the cartridge structure manually. The MCP server ensures all necessary files and configurations are created correctly.

**Using MCP Server (Recommended)**
Use the `generate_cartridge_structure` tool from this MCP server to automatically create the cartridge structure:

```json
{
  "cartridgeName": "plugin_my_custom_cartridge",
  "targetPath": "/path/to/your/project",
  "fullProjectSetup": true
}
```

This tool will:
- Create all necessary configuration files (package.json, webpack.config.js, etc.)
- Set up the complete cartridge structure with proper organization
- Ensure the cartridge is created exactly where needed in your project structure
- Generate all required directories and files for a fully functional cartridge

## 6. "Hello, World" Example

### Step 1: Navigate to Your Project

After creating the cartridge structure, navigate to your project directory:

```bash
# If you kept the subdirectory structure:
cd plugin_my_custom_cartridge

# If you moved files to root level, you're already in the right place
```

### Step 2: Create the Controller

**File:** `cartridges/plugin_my_custom_cartridge/cartridge/controllers/Hello.js`

```javascript
'use strict';

var server = require('server');

// URL: /Hello-Show
server.get('Show', function (req, res, next) {
    res.render('hello/helloTemplate', {
        message: 'Hello from a custom cartridge!'
    });
    next();
});

module.exports = server.exports();
```

### Step 3: Create the ISML Template

**File:** `cartridges/plugin_my_custom_cartridge/cartridge/templates/default/hello/helloTemplate.isml`

```html
<iscontent type="text/html" charset="UTF-8" compact="true"/>
<isdecorate template="common/layout/page">
    <isreplace name="main">
        <div class="container">
            <h1>Custom Page</h1>
            <p>${pdict.message}</p>
        </div>
    </isreplace>
</isdecorate>
```

### Step 4: Update Deployment Configuration

Ensure your `dw.json` file has the correct sandbox credentials (this file is automatically generated but needs your specific sandbox details).

## 7. Deployment and Registration

### Step 1: Upload the Cartridge

From the project root, run:

```bash
npm run uploadCartridge plugin_my_custom_cartridge
```

For continuous development, use `npm run watch`.

## 8. Naming Conventions & Best Practices

**Cartridge Naming:** Use standard prefixes.

- `app_custom_*`: Site-specific customizations.
- `int_*`: Third-party integrations.
- `bm_*`: Business Manager extensions.
- `plugin_*`: Reusable SFRA feature extensions.

**File Naming:** Controllers use PascalCase.js. Other JS files use camelCase.js.

**NEVER modify app_storefront_base or other base/plugin cartridges directly.**

**Extend, Don't Replace:** Use server.append() or server.prepend() to extend controllers. Avoid server.replace().

**Logic-less Templates:** Keep business logic out of ISML files. Use models to prepare data.

**Security:** Protect all state-changing POST requests with CSRF tokens. Properly encode all user-provided output to prevent XSS.

**Localization:** Use Resource.msg() in templates to fetch text from .properties files.

## 9. MCP Integration Workflow

When using this MCP server for cartridge development, follow this enhanced workflow:

1. **Generate Cartridge Structure**: Use the `generate_cartridge_structure` tool to create the initial cartridge with all necessary files and configurations
2. **Get Best Practices**: Use `get_best_practice_guide` with "sfra_controllers" for controller development patterns
3. **Search Documentation**: Use `search_sfcc_classes` and `get_sfcc_class_info` for API reference
4. **Validate Implementation**: Use `search_best_practices` to ensure your code follows security and performance guidelines
5. **Debug Issues**: Use log analysis tools to troubleshoot deployment or runtime issues
6. **Handle Deployment Issues**: If new cartridge features (jobs, SCAPI endpoints, hooks) don't appear after upload:
   - **Check Code Versions**: Use `get_code_versions` tool to see available versions
   - **Activate Version**: Use `activate_code_version` tool to ensure proper registration
   - **Alternative**: Manually switch code versions in Business Manager (Administration > Site Development > Code Deployment)

### Common Deployment Troubleshooting

**Issue**: Custom jobs or SCAPI endpoints not visible after cartridge deployment
**Solution**: 
1. Use MCP `get_code_versions` tool to check available code versions
2. Use MCP `activate_code_version` tool to switch to the uploaded version
3. Verify registration in logs using log analysis tools

**Issue**: Hooks not taking effect after deployment
**Solution**: Follow the same code version activation process above

This integrated approach ensures your cartridge follows all best practices and leverages the full power of the SFCC development ecosystem with direct file generation capabilities.

```

--------------------------------------------------------------------------------
/docs/TopLevel/Number.md:
--------------------------------------------------------------------------------

```markdown
## Package: TopLevel

# Class Number

## Inheritance Hierarchy

- Object
  - Number

## Description

A Number object represents any numerical value, whether it is an integer or floating-point number. Generally, you do not need to worry about a Number object because a numerical value automatically becomes a Number object instance when you use a numerical value or assign it to a variable.

## Constants

### EPSILON

**Type:** Number

EPSILON is the Number value for the magnitude of the difference between 1 and the smallest value greater than 1 that is representable as a Number value, which is approximately 2.2204460492503130808472633361816 × 10-16.

### MAX_SAFE_INTEGER

**Type:** Number

The maximum safe integer in JavaScript.

### MAX_VALUE

**Type:** Number

The largest representable Number.

### MIN_SAFE_INTEGER

**Type:** Number

The minimum safe integer in JavaScript.

### MIN_VALUE

**Type:** Number

The smallest representable Number.

### NEGATIVE_INFINITY

**Type:** Number

Negative infinite value; returned on overflow;

### POSITIVE_INFINITY

**Type:** Number

Negative infinite value; returned on overflow;

## Properties

## Constructor Summary

Number() Constructs a Number with value 0

Number(num : Number) Constructs a new Number using the specified Number.

Number(value : String) Constructs a Number using the specified value.

## Method Summary

### isFinite

**Signature:** `static isFinite(value : Object) : boolean`

Determines whether the passed value is a finite number.

### isInteger

**Signature:** `static isInteger(value : Object) : boolean`

Determines whether the passed value is an integer number.

### isNaN

**Signature:** `static isNaN(value : Object) : boolean`

Determines whether the passed value is NaN.

### isSafeInteger

**Signature:** `static isSafeInteger(value : Object) : boolean`

Determines whether the passed value is a safe integer number.

### parseFloat

**Signature:** `static parseFloat(s : String) : Number`

Parses a String into an float Number.

### parseInt

**Signature:** `static parseInt(s : String) : Number`

Parses a String into an integer Number.

### parseInt

**Signature:** `static parseInt(s : String, radix : Number) : Number`

Parses a String into an integer Number using the specified radix.

### toExponential

**Signature:** `toExponential() : String`

Converts this Number to a String using exponential notation.

### toExponential

**Signature:** `toExponential(digits : Number) : String`

Converts this Number to a String using exponential notation with the specified number of digits after the decimal place.

### toFixed

**Signature:** `toFixed() : String`

Converts a Number to a String that contains a no fractional part.

### toFixed

**Signature:** `toFixed(digits : Number) : String`

Converts a Number to a String that contains a specified number of digits after the decimal place.

### toLocaleString

**Signature:** `toLocaleString() : String`

Converts this Number to a String using local number formatting conventions.

### toPrecision

**Signature:** `toPrecision(precision : Number) : String`

Converts a Number to a String using the specified number of significant digits.

### toString

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

A String representation of this Number.

### toString

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

Converts the number into a string using the specified radix (base).

## Constructor Detail

## Method Detail

## Method Details

### isFinite

**Signature:** `static isFinite(value : Object) : boolean`

**Description:** Determines whether the passed value is a finite number.

**API Versioned:**

From version 21.2.

**Parameters:**

- `value`: The value to check.

**Returns:**

true if the passed value is a finite number, else false.

---

### isInteger

**Signature:** `static isInteger(value : Object) : boolean`

**Description:** Determines whether the passed value is an integer number.

**API Versioned:**

From version 21.2.

**Parameters:**

- `value`: The value to check.

**Returns:**

true if the passed value is a finite integer number, else false.

---

### isNaN

**Signature:** `static isNaN(value : Object) : boolean`

**Description:** Determines whether the passed value is NaN. Unlike the global function, the passed parameter is not converted to number before doing the check.

**API Versioned:**

From version 21.2.

**Parameters:**

- `value`: The value to check.

**Returns:**

true if the passed value is the NaN number value, else false.

---

### isSafeInteger

**Signature:** `static isSafeInteger(value : Object) : boolean`

**Description:** Determines whether the passed value is a safe integer number.

**API Versioned:**

From version 21.2.

**Parameters:**

- `value`: The value to check.

**Returns:**

true if the passed value is a safe integer number, else false.

---

### parseFloat

**Signature:** `static parseFloat(s : String) : Number`

**Description:** Parses a String into an float Number.

**API Versioned:**

From version 21.2.

**Parameters:**

- `s`: the String to parse.

**Returns:**

Returns the float as a Number.

---

### parseInt

**Signature:** `static parseInt(s : String) : Number`

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

**API Versioned:**

From version 21.2.

**Parameters:**

- `s`: the String to parse.

**Returns:**

Returns the integer as a Number.

---

### parseInt

**Signature:** `static parseInt(s : String, radix : Number) : Number`

**Description:** Parses a String into an integer Number using the specified radix.

**API Versioned:**

From version 21.2.

**Parameters:**

- `s`: the String to parse.
- `radix`: the radix to use.

**Returns:**

Returns the integer as a Number.

---

### toExponential

**Signature:** `toExponential() : String`

**Description:** Converts this Number to a String using exponential notation.

**Returns:**

a String using exponential notation.

---

### toExponential

**Signature:** `toExponential(digits : Number) : String`

**Description:** Converts this Number to a String using exponential notation with the specified number of digits after the decimal place.

**Parameters:**

- `digits`: the number of digits after the decimal place.

**Returns:**

a String using exponential notation with the specified number of digits after the decimal place.

---

### toFixed

**Signature:** `toFixed() : String`

**Description:** Converts a Number to a String that contains a no fractional part.

**Returns:**

a String representation of the number

---

### toFixed

**Signature:** `toFixed(digits : Number) : String`

**Description:** Converts a Number to a String that contains a specified number of digits after the decimal place.

**Parameters:**

- `digits`: the number of digits after the decimal place.

**Returns:**

a String that contains a specified number of digits after the decimal place.

---

### toLocaleString

**Signature:** `toLocaleString() : String`

**Description:** Converts this Number to a String using local number formatting conventions. The current implementation actually only returns the same as toString().

**Returns:**

a String using local number formatting conventions.

---

### toPrecision

**Signature:** `toPrecision(precision : Number) : String`

**Description:** Converts a Number to a String using the specified number of significant digits. Uses exponential or fixed point notation depending on the size of the number and the number of significant digits specified.

**Parameters:**

- `precision`: the precision to use when converting the Number to a String.

**Returns:**

a String using the specified number of significant digits.

---

### toString

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

**Description:** A String representation of this Number.

**Returns:**

a String representation of this Number.

---

### toString

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

**Description:** Converts the number into a string using the specified radix (base).

**Parameters:**

- `radix`: the radix to use.

**Returns:**

a String representation of this Number.

---
```

--------------------------------------------------------------------------------
/docs/dw_svc/HTTPService.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.svc

# Class HTTPService

## Inheritance Hierarchy

- Object
  - dw.svc.Service
  - dw.svc.HTTPService

## Description

Represents an HTTP Service. The HTTP Service will use the return value of the createRequest callback as the request body (if supported by the HTTP method). If this is an array of non-null HTTPRequestPart objects, then a multi-part request will be formed. Otherwise the object is converted to a String and used. See also XML.toXMLString() and JSON.stringify(Object), which must be explicitly called if needed.

## Properties

### authentication

**Type:** String

The authentication type.

### cachingTTL

**Type:** Number

The caching time to live value.

### client

**Type:** HTTPClient (Read Only)

The underlying HTTP client object.

### encoding

**Type:** String

The request body encoding to declare.

### hostNameVerification

**Type:** boolean

Determines whether host name verification is enabled.

### identity

**Type:** KeyRef

Gets the identity used for mutual TLS (mTLS).

### outFile

**Type:** File

The output file, or null if there is none.

### requestMethod

**Type:** String

The request method.

## Constructor Summary

## Method Summary

### addHeader

**Signature:** `addHeader(name : String, val : String) : HTTPService`

Adds an HTTP Header.

### addParam

**Signature:** `addParam(name : String, val : String) : HTTPService`

Adds a query parameter that will be appended to the URL.

### getAuthentication

**Signature:** `getAuthentication() : String`

Returns the authentication type.

### getCachingTTL

**Signature:** `getCachingTTL() : Number`

Returns the caching time to live value.

### getClient

**Signature:** `getClient() : HTTPClient`

Returns the underlying HTTP client object.

### getEncoding

**Signature:** `getEncoding() : String`

Returns the request body encoding to declare.

### getHostNameVerification

**Signature:** `getHostNameVerification() : boolean`

Determines whether host name verification is enabled.

### getIdentity

**Signature:** `getIdentity() : KeyRef`

Gets the identity used for mutual TLS (mTLS).

### getOutFile

**Signature:** `getOutFile() : File`

Returns the output file, or null if there is none.

### getRequestMethod

**Signature:** `getRequestMethod() : String`

Returns the request method.

### setAuthentication

**Signature:** `setAuthentication(authentication : String) : HTTPService`

Sets the type of authentication.

### setCachingTTL

**Signature:** `setCachingTTL(ttl : Number) : HTTPService`

Enables caching for GET requests.

### setEncoding

**Signature:** `setEncoding(encoding : String) : HTTPService`

Sets the encoding of the request body (if any).

### setHostNameVerification

**Signature:** `setHostNameVerification(enable : boolean) : HTTPService`

Sets whether certificate host name verification is enabled.

### setIdentity

**Signature:** `setIdentity(keyRef : KeyRef) : HTTPService`

Sets the identity (private key) to use when mutual TLS (mTLS) is configured.

### setOutFile

**Signature:** `setOutFile(outFile : File) : HTTPService`

Sets the output file in which to write the HTTP response body.

### setRequestMethod

**Signature:** `setRequestMethod(requestMethod : String) : HTTPService`

Sets the HTTP request method.

## Method Detail

## Method Details

### addHeader

**Signature:** `addHeader(name : String, val : String) : HTTPService`

**Description:** Adds an HTTP Header.

**Parameters:**

- `name`: Header name.
- `val`: Header value.

**Returns:**

this HTTP Service.

---

### addParam

**Signature:** `addParam(name : String, val : String) : HTTPService`

**Description:** Adds a query parameter that will be appended to the URL.

**Parameters:**

- `name`: Parameter name.
- `val`: Parameter value.

**Returns:**

this HTTP Service.

---

### getAuthentication

**Signature:** `getAuthentication() : String`

**Description:** Returns the authentication type.

**Returns:**

Authentication type.

---

### getCachingTTL

**Signature:** `getCachingTTL() : Number`

**Description:** Returns the caching time to live value.

**Returns:**

The caching time to live value in seconds.

**See Also:**

setCachingTTL(Number)

---

### getClient

**Signature:** `getClient() : HTTPClient`

**Description:** Returns the underlying HTTP client object.

**Returns:**

HTTP client object.

---

### getEncoding

**Signature:** `getEncoding() : String`

**Description:** Returns the request body encoding to declare.

**Returns:**

Request encoding.

---

### getHostNameVerification

**Signature:** `getHostNameVerification() : boolean`

**Description:** Determines whether host name verification is enabled.

**Returns:**

true if verification is enabled, false otherwise

---

### getIdentity

**Signature:** `getIdentity() : KeyRef`

**Description:** Gets the identity used for mutual TLS (mTLS).

**Returns:**

Reference to the private key, or null if not configured

---

### getOutFile

**Signature:** `getOutFile() : File`

**Description:** Returns the output file, or null if there is none.

**Returns:**

Output file or null.

---

### getRequestMethod

**Signature:** `getRequestMethod() : String`

**Description:** Returns the request method.

**Returns:**

HTTP Request method.

---

### setAuthentication

**Signature:** `setAuthentication(authentication : String) : HTTPService`

**Description:** Sets the type of authentication. Valid values include "BASIC" and "NONE". The default value is BASIC.

**Parameters:**

- `authentication`: Type of authentication.

**Returns:**

this HTTP Service.

---

### setCachingTTL

**Signature:** `setCachingTTL(ttl : Number) : HTTPService`

**Description:** Enables caching for GET requests. This only caches status codes 2xx with a content length and size of less than 50k that are not immediately written to file. The URL and the user name are used as cache keys. The total size of cacheable content and the number of cached items is limited and automatically managed by the system. Cache control information sent by the remote server is ignored. Caching HTTP responses should be done very carefully. It is important to ensure that the response really depends only on the URL and doesn't contain any remote state information or time information which is independent of the URL. It is also important to verify that the application sends exactly the same URL multiple times.

**Parameters:**

- `ttl`: The time to live for the cached content in seconds. A value of 0 disables caching.

**See Also:**

HTTPClient.enableCaching(Number)

---

### setEncoding

**Signature:** `setEncoding(encoding : String) : HTTPService`

**Description:** Sets the encoding of the request body (if any). The default value is UTF-8.

**Parameters:**

- `encoding`: Encoding of the request body.

**Returns:**

this HTTP Service.

---

### setHostNameVerification

**Signature:** `setHostNameVerification(enable : boolean) : HTTPService`

**Description:** Sets whether certificate host name verification is enabled. The default value is true. Set it to false to disable host name verification.

**Parameters:**

- `enable`: true to enable host name verification or false to disable it.

**Returns:**

this HTTP Service.

---

### setIdentity

**Signature:** `setIdentity(keyRef : KeyRef) : HTTPService`

**Description:** Sets the identity (private key) to use when mutual TLS (mTLS) is configured. If this is not set and mTLS is used then the private key will be chosen from the key store based on the host name. If this is set to a reference named "__NONE__" then no private key will be used even if one is requested by the remote server.

**Parameters:**

- `keyRef`: Reference to the private key

---

### setOutFile

**Signature:** `setOutFile(outFile : File) : HTTPService`

**Description:** Sets the output file in which to write the HTTP response body. The default behavior is to not write a file.

**Parameters:**

- `outFile`: Output file, or null to disable.

**Returns:**

this HTTP Service.

---

### setRequestMethod

**Signature:** `setRequestMethod(requestMethod : String) : HTTPService`

**Description:** Sets the HTTP request method. Valid values include GET, PUT, POST, and DELETE. The default value is POST.

**Parameters:**

- `requestMethod`: HTTP request method.

**Returns:**

this HTTP Service.

---
```

--------------------------------------------------------------------------------
/docs/dw_order/Return.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.order

# Class Return

## Inheritance Hierarchy

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

## Description

The Return represents a physical customer return, and contains 1..n ReturnItems. The Return is associated with one ReturnCase, and each ReturnItem is associated with one ReturnCaseItem and (via the ReturnCaseItem) a single OrderItem usually representing an Order ProductLineItem. The ReturnItem records the quantity returned. The Return can have one of these status values: NEW - the return is new, i.e. needs to undergo a check before it can be marked as COMPLETED COMPLETED - the return is complete, this is a precondition for refunding the customer for a return. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.

## Constants

### ORDERBY_ITEMID

**Type:** Object

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

### ORDERBY_ITEMPOSITION

**Type:** Object

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

### ORDERBY_UNSORTED

**Type:** Object

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

### QUALIFIER_PRODUCTITEMS

**Type:** Object

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

### QUALIFIER_SERVICEITEMS

**Type:** Object

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

### STATUS_COMPLETED

**Type:** String = "COMPLETED"

Constant for Return Status COMPLETED

### STATUS_NEW

**Type:** String = "NEW"

Constant for Return Status NEW

## Properties

### invoice

**Type:** Invoice (Read Only)

Returns null or the previously created Invoice.

### invoiceNumber

**Type:** String (Read Only)

Returns null or the invoice-number.

### items

**Type:** FilteringCollection (Read Only)

The ReturnItems contained in the Return, created with method
 createItem(String).

 
 This FilteringCollection can be sorted / filtered using:
 
 FilteringCollection.sort(Object) with ORDERBY_ITEMID
 FilteringCollection.sort(Object) with
 ORDERBY_ITEMPOSITION
 FilteringCollection.sort(Object) with ORDERBY_UNSORTED
 FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS
 FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS

### note

**Type:** String

A note for the return.

### returnCase

**Type:** ReturnCase (Read Only)

The ReturnCase with which this Return is associated. The ReturnCase
 may represent an RMA (return merchandise authorization).

### returnNumber

**Type:** String (Read Only)

The return number identifying this return.

### status

**Type:** EnumValue

Gets the return status.
 
 Possible values are STATUS_NEW, STATUS_COMPLETED.

## Constructor Summary

## Method Summary

### createInvoice

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

Creates a new Invoice based on this Return.

### createInvoice

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

Creates a new Invoice based on this Return.

### createItem

**Signature:** `createItem(returnCaseItemID : String) : ReturnItem`

Create a ReturnItem based on a ReturnCaseItem.

### getInvoice

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

Returns null or the previously created Invoice.

### getInvoiceNumber

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

Returns null or the invoice-number.

### getItems

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

Returns the ReturnItems contained in the Return, created with method createItem(String).

### getNote

**Signature:** `getNote() : String`

A note for the return.

### getReturnCase

**Signature:** `getReturnCase() : ReturnCase`

Returns the ReturnCase with which this Return is associated.

### getReturnNumber

**Signature:** `getReturnNumber() : String`

The return number identifying this return.

### getStatus

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

Gets the return status.

### setNote

**Signature:** `setNote(note : String) : void`

Sets a note for the return.

### setStatus

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

Sets the return status.

## Method Detail

## Method Details

### createInvoice

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

**Description:** Creates a new Invoice based on this Return. The return-number will be used as the invoice-number. The Invoice can then be accessed using getInvoice() or its number using getInvoiceNumber(). The method must not be called more than once for a Return, nor may 2 Invoices exist with the same invoice-number. The new Invoice is a credit-invoice with a Invoice.STATUS_NOT_PAID status, and will be passed to the refund payment-hook in a separate database transaction for processing.

**Returns:**

new invoice

---

### createInvoice

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

**Description:** Creates a new Invoice based on this Return. The invoice-number must be specified as an argument. The Invoice can then be accessed using getInvoice() or its number using getInvoiceNumber(). The method must not be called more than once for a Return, nor may 2 Invoices exist with the same invoice-number. The new Invoice is a credit-invoice with a Invoice.STATUS_NOT_PAID status, and will be passed to the refund payment-hook in a separate database transaction for processing.

**Parameters:**

- `invoiceNumber`: the invoice-number to use

**Returns:**

the new invoice

---

### createItem

**Signature:** `createItem(returnCaseItemID : String) : ReturnItem`

**Description:** Create a ReturnItem based on a ReturnCaseItem.

**Parameters:**

- `returnCaseItemID`: the id of the return case item

**Returns:**

the created return item

---

### getInvoice

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

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

**Returns:**

null or the previously created invoice.

**See Also:**

createInvoice(String)

---

### getInvoiceNumber

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

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

**Returns:**

null or the previously created invoice.

**See Also:**

createInvoice(String)

---

### getItems

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

**Description:** Returns the ReturnItems contained in the Return, created with method createItem(String). This FilteringCollection can be sorted / filtered using: FilteringCollection.sort(Object) with ORDERBY_ITEMID FilteringCollection.sort(Object) with ORDERBY_ITEMPOSITION FilteringCollection.sort(Object) with ORDERBY_UNSORTED FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS

**Returns:**

the return items

**See Also:**

ReturnItem

---

### getNote

**Signature:** `getNote() : String`

**Description:** A note for the return.

**Returns:**

the note or null

---

### getReturnCase

**Signature:** `getReturnCase() : ReturnCase`

**Description:** Returns the ReturnCase with which this Return is associated. The ReturnCase may represent an RMA (return merchandise authorization).

**Returns:**

the return case

---

### getReturnNumber

**Signature:** `getReturnNumber() : String`

**Description:** The return number identifying this return.

**Returns:**

the return number

---

### getStatus

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

**Description:** Gets the return status. Possible values are STATUS_NEW, STATUS_COMPLETED.

**Returns:**

the status

---

### setNote

**Signature:** `setNote(note : String) : void`

**Description:** Sets a note for the return.

**Parameters:**

- `note`: the note

---

### setStatus

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

**Description:** Sets the return status. Possible values are STATUS_NEW, STATUS_COMPLETED When set to status COMPLETED, only the the custom attributes of the return itself and its return items can be changed.

**Parameters:**

- `statusName`: the status

---
```

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/mock-data/ocapi/custom-object-attributes-versionhistory.json:
--------------------------------------------------------------------------------

```json
{
  "_v": "23.2",
  "_type": "object_attribute_definition_search_result",
  "count": 8,
  "hits": [
    {
      "_type": "object_attribute_definition",
      "_resource_state": "896b31874723fb22d8e7dca243b9b1c5c2de3a2a73645562d9af221a91318612",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "effective_id": "c_ID",
      "externally_defined": false,
      "externally_managed": false,
      "id": "ID",
      "key": true,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/ID",
      "localizable": false,
      "mandatory": true,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": false,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": false,
      "value_type": "string",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "69bff66e9e1774188891baa67e28861a73c716636b7a84ae2582c7b4b649c0bd",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "display_name": {
        "default": "UUID"
      },
      "effective_id": "UUID",
      "externally_defined": false,
      "externally_managed": false,
      "field_length": 28,
      "id": "UUID",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/UUID",
      "localizable": false,
      "mandatory": true,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": true,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": true,
      "value_type": "string",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "7606845698773be81fe494069711f52bfb6092e0909e95948c944611fb259572",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "description": {
        "default": "The ID of the component the history is of."
      },
      "display_name": {
        "default": "Component ID"
      },
      "effective_id": "c_componentId",
      "externally_defined": false,
      "externally_managed": false,
      "id": "componentId",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/componentId",
      "localizable": false,
      "mandatory": false,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": false,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": false,
      "value_type": "string",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "66393d94b5177d0c307d20e948fd3e18687767d72bdcb9913ecde08a312c6275",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "display_name": {
        "default": "Creation Date"
      },
      "effective_id": "creationDate",
      "externally_defined": false,
      "externally_managed": false,
      "id": "creationDate",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/creationDate",
      "localizable": false,
      "mandatory": true,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": true,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": true,
      "value_type": "datetime",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "8476a3b0562d6aa0900126bab96ccc90e27d0508d604ed432d6757515594a098",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "display_name": {
        "default": "Last Modified"
      },
      "effective_id": "lastModified",
      "externally_defined": false,
      "externally_managed": false,
      "id": "lastModified",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/lastModified",
      "localizable": false,
      "mandatory": true,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": true,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": true,
      "value_type": "datetime",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "3081b438888efbc03c8bbcddfc033b9097ff6644a59023c047ab88ad6d46a1c3",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "description": {
        "default": "The locale of the value."
      },
      "display_name": {
        "default": "Locale"
      },
      "effective_id": "c_locale",
      "externally_defined": false,
      "externally_managed": false,
      "id": "locale",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/locale",
      "localizable": false,
      "mandatory": false,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": false,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": false,
      "value_type": "string",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "383d2b5957098f09e256dfc26926790784b382c1d87a14426e1ef7a7e81271f4",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "description": {
        "default": "The user who has made the modification"
      },
      "display_name": {
        "default": "User"
      },
      "effective_id": "c_user",
      "externally_defined": false,
      "externally_managed": false,
      "id": "user",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/user",
      "localizable": false,
      "mandatory": false,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": false,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": false,
      "value_type": "string",
      "visible": false
    },
    {
      "_type": "object_attribute_definition",
      "_resource_state": "ee9469bc639a2cb37bb6d2419f8123397853f0ff40e69d211b665088ae322957",
      "creation_date": "2024-02-26T19:35:57.000Z",
      "description": {
        "default": "The value"
      },
      "display_name": {
        "default": "Value"
      },
      "effective_id": "c_value",
      "externally_defined": false,
      "externally_managed": false,
      "id": "value",
      "key": false,
      "last_modified": "2024-02-26T19:35:57.000Z",
      "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/value",
      "localizable": false,
      "mandatory": false,
      "min_length": 0,
      "multi_value_type": false,
      "order_required": false,
      "queryable": true,
      "read_only": false,
      "requires_encoding": false,
      "searchable": false,
      "set_value_type": false,
      "site_specific": false,
      "system": false,
      "value_type": "string",
      "visible": false
    }
  ],
  "query": {
    "match_all_query": {
      "_type": "match_all_query"
    }
  },
  "start": 0,
  "total": 8
}
```

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

```typescript
import { ConfigurationFactory } from '../src/config/configuration-factory';
import { SFCCConfig } from '../src/types/types';
import { writeFileSync, unlinkSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

describe('ConfigurationFactory', () => {
  const testDir = join(tmpdir(), 'sfcc-config-factory-tests');

  beforeAll(() => {
    if (!existsSync(testDir)) {
      mkdirSync(testDir, { recursive: true });
    }
  });

  afterEach(() => {
    // Clean up test files
    try {
      const testFiles = ['valid-dw.json', 'invalid-dw.json', 'missing-dw.json'];
      testFiles.forEach(file => {
        const filePath = join(testDir, file);
        if (existsSync(filePath)) {
          unlinkSync(filePath);
        }
      });
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  describe('create', () => {
    it('should create configuration from dw.json file', () => {
      const dwJsonContent = {
        hostname: 'test-instance.demandware.net',
        username: 'testuser',
        password: 'testpass',
        'client-id': 'test-client-id',
        'client-secret': 'test-client-secret',
      };

      const testFile = join(testDir, 'valid-dw.json');
      writeFileSync(testFile, JSON.stringify(dwJsonContent, null, 2));

      const config = ConfigurationFactory.create({ dwJsonPath: testFile });

      expect(config.hostname).toBe('test-instance.demandware.net');
      expect(config.username).toBe('testuser');
      expect(config.password).toBe('testpass');
      expect(config.clientId).toBe('test-client-id');
      expect(config.clientSecret).toBe('test-client-secret');
    });

    it('should throw an error if dw.json file is invalid', () => {
      const testFile = join(testDir, 'invalid-dw.json');
      writeFileSync(testFile, '{ invalid json }');

      expect(() => {
        ConfigurationFactory.create({ dwJsonPath: testFile });
      }).toThrow(/Invalid JSON in configuration file:/);
    });

    it('should throw an error if dw.json file is missing', () => {
      const testFile = join(testDir, 'missing-dw.json');

      expect(() => {
        ConfigurationFactory.create({ dwJsonPath: testFile });
      }).toThrow(/dw\.json file not found at:/);
    });

    it('should create configuration from provided options', () => {
      const config = ConfigurationFactory.create({
        hostname: 'test-instance.demandware.net',
        username: 'testuser',
        password: 'testpass',
        clientId: 'test-client-id',
        clientSecret: 'test-client-secret',
        siteId: 'test-site',
      });

      expect(config.hostname).toBe('test-instance.demandware.net');
      expect(config.username).toBe('testuser');
      expect(config.password).toBe('testpass');
      expect(config.clientId).toBe('test-client-id');
      expect(config.clientSecret).toBe('test-client-secret');
      expect(config.siteId).toBe('test-site');
    });

    it('should allow configuration without credentials for local mode', () => {
      const config = ConfigurationFactory.create({});
      expect(config.hostname).toBe('');
      expect(config.username).toBeUndefined();
      expect(config.password).toBeUndefined();
      expect(config.clientId).toBeUndefined();
      expect(config.clientSecret).toBeUndefined();
    });

    it('should validate configuration and throw error for hostname without credentials', () => {
      expect(() => {
        ConfigurationFactory.create({ hostname: 'test-instance.demandware.net' });
      }).toThrow('When hostname is provided, either username/password or OAuth credentials (clientId/clientSecret) must be provided');
    });

    it('should validate configuration and throw error for invalid hostname format', () => {
      expect(() => {
        ConfigurationFactory.create({
          hostname: 'invalid@hostname!',
          username: 'testuser',
          password: 'testpass',
        });
      }).toThrow('Invalid hostname format in configuration');
    });

    it('should validate configuration and allow hostname with basic auth', () => {
      const config = ConfigurationFactory.create({
        hostname: 'test-instance.demandware.net',
        username: 'testuser',
        password: 'testpass',
      });
      expect(config.hostname).toBe('test-instance.demandware.net');
      expect(config.username).toBe('testuser');
      expect(config.password).toBe('testpass');
    });

    it('should validate configuration and allow hostname with OAuth', () => {
      const config = ConfigurationFactory.create({
        hostname: 'test-instance.demandware.net',
        clientId: 'test-client-id',
        clientSecret: 'test-client-secret',
      });
      expect(config.hostname).toBe('test-instance.demandware.net');
      expect(config.clientId).toBe('test-client-id');
      expect(config.clientSecret).toBe('test-client-secret');
    });

    it('should validate configuration and allow localhost with port', () => {
      const config = ConfigurationFactory.create({
        hostname: 'localhost:3000',
        username: 'testuser',
        password: 'testpass',
      });
      expect(config.hostname).toBe('localhost:3000');
      expect(config.username).toBe('testuser');
      expect(config.password).toBe('testpass');
    });

    it('should validate configuration and allow hostname with custom port', () => {
      const config = ConfigurationFactory.create({
        hostname: 'test-instance.demandware.net:8080',
        clientId: 'test-client-id',
        clientSecret: 'test-client-secret',
      });
      expect(config.hostname).toBe('test-instance.demandware.net:8080');
      expect(config.clientId).toBe('test-client-id');
      expect(config.clientSecret).toBe('test-client-secret');
    });
  });

  describe('getCapabilities', () => {
    it('should return correct capabilities for basic auth', () => {
      const config: SFCCConfig = {
        hostname: 'test-instance.demandware.net',
        username: 'testuser',
        password: 'testpass',
      };

      const capabilities = ConfigurationFactory.getCapabilities(config);

      expect(capabilities.canAccessLogs).toBe(true);
      expect(capabilities.canAccessOCAPI).toBe(false);
      expect(capabilities.canAccessWebDAV).toBe(true);
      expect(capabilities.isLocalMode).toBe(false);
    });

    it('should return correct capabilities for OAuth', () => {
      const config: SFCCConfig = {
        hostname: 'test-instance.demandware.net',
        clientId: 'test-client-id',
        clientSecret: 'test-client-secret',
      };

      const capabilities = ConfigurationFactory.getCapabilities(config);

      expect(capabilities.canAccessLogs).toBe(true);
      expect(capabilities.canAccessOCAPI).toBe(true);
      expect(capabilities.canAccessWebDAV).toBe(true);
      expect(capabilities.isLocalMode).toBe(false);
    });

    it('should return correct capabilities for both basic auth and OAuth', () => {
      const config: SFCCConfig = {
        hostname: 'test-instance.demandware.net',
        username: 'testuser',
        password: 'testpass',
        clientId: 'test-client-id',
        clientSecret: 'test-client-secret',
      };

      const capabilities = ConfigurationFactory.getCapabilities(config);

      expect(capabilities.canAccessLogs).toBe(true);
      expect(capabilities.canAccessOCAPI).toBe(true);
      expect(capabilities.canAccessWebDAV).toBe(true);
      expect(capabilities.isLocalMode).toBe(false);
    });

    it('should return local mode capabilities for empty config', () => {
      const config: SFCCConfig = {};

      const capabilities = ConfigurationFactory.getCapabilities(config);

      expect(capabilities.canAccessLogs).toBe(false);
      expect(capabilities.canAccessOCAPI).toBe(false);
      expect(capabilities.canAccessWebDAV).toBe(false);
      expect(capabilities.isLocalMode).toBe(true);
    });

    it('should return local mode capabilities for config without hostname', () => {
      const config: SFCCConfig = {
        hostname: '',
        username: undefined,
        password: undefined,
      };

      const capabilities = ConfigurationFactory.getCapabilities(config);

      expect(capabilities.canAccessLogs).toBe(false);
      expect(capabilities.canAccessOCAPI).toBe(false);
      expect(capabilities.canAccessWebDAV).toBe(false);
      expect(capabilities.isLocalMode).toBe(true);
    });
  });
});

```

--------------------------------------------------------------------------------
/docs/dw_order/ReturnItem.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.order

# Class ReturnItem

## Inheritance Hierarchy

- Object
  - dw.object.Extensible
  - dw.order.AbstractItem
    - dw.order.ReturnItem

## Description

An item of a Return, created using Return.createItem(String). Represents a physically returned order line item. Please refer to the documentation of ReturnHooks for further information. When the related Return were set to status COMPLETED, only the the custom attributes of the return item can be changed. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.

## Properties

### basePrice

**Type:** Money (Read Only)

Price of a single unit before discount application.

### note

**Type:** String

Return the note for this return item.

### parentItem

**Type:** ReturnItem

Returns null or the parent item.

### reasonCode

**Type:** EnumValue

The reason code for return item. The list of reason codes can be updated
 by updating meta-data for ReturnItem.

### returnCaseItem

**Type:** ReturnCaseItem (Read Only)

The return case item related to this item. Should never return null.

### returnedQuantity

**Type:** Quantity

The Quantity returned. This may return an N/A quantity.

### returnNumber

**Type:** String (Read Only)

The mandatory returnNumber of the Return to which this item belongs.

## Constructor Summary

## Method Summary

### addTaxItem

**Signature:** `addTaxItem(amount : Decimal, taxGroup : TaxGroup) : TaxItem`

Create a new tax-item and add to this item.

### applyPriceRate

**Signature:** `applyPriceRate(factor : Decimal, divisor : Decimal, roundUp : boolean) : void`

Apply a rate of (factor / divisor) to the prices in this item, with the option to half round up or half round down to the nearest cent if necessary.

### getBasePrice

**Signature:** `getBasePrice() : Money`

Price of a single unit before discount application.

### getNote

**Signature:** `getNote() : String`

Return the note for this return item.

### getParentItem

**Signature:** `getParentItem() : ReturnItem`

Returns null or the parent item.

### getReasonCode

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

Returns the reason code for return item.

### getReturnCaseItem

**Signature:** `getReturnCaseItem() : ReturnCaseItem`

Returns the return case item related to this item.

### getReturnedQuantity

**Signature:** `getReturnedQuantity() : Quantity`

The Quantity returned.

### getReturnNumber

**Signature:** `getReturnNumber() : String`

The mandatory returnNumber of the Return to which this item belongs.

### setNote

**Signature:** `setNote(note : String) : void`

Sets a note for this return item.

### setParentItem

**Signature:** `setParentItem(parentItem : ReturnItem) : void`

Set a parent item.

### setReasonCode

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

Set the reason code.

### setReturnedQuantity

**Signature:** `setReturnedQuantity(quantity : Quantity) : void`

Set the Quantity returned.

### setTaxBasis

**Signature:** `setTaxBasis(taxBasis : Money) : void`

Set the tax-basis price for this item.

### setTaxItems

**Signature:** `setTaxItems(taxItems : Collection) : void`

Set the tax-items for this item.

## Method Detail

## Method Details

### addTaxItem

**Signature:** `addTaxItem(amount : Decimal, taxGroup : TaxGroup) : TaxItem`

**Description:** Create a new tax-item and add to this item.

**Parameters:**

- `amount`: amount to assign to the tax-item
- `taxGroup`: the TaxGroup to which the item belongs

**Returns:**

the new tax-item

---

### applyPriceRate

**Signature:** `applyPriceRate(factor : Decimal, divisor : Decimal, roundUp : boolean) : void`

**Description:** Apply a rate of (factor / divisor) to the prices in this item, with the option to half round up or half round down to the nearest cent if necessary. Examples: TaxBasis beforefactordivisorroundupCalculationTaxBasis after $10.0012true10*1/2=$5.00 $10.00910true10*9/10=$9.00 $10.0013true10*1/3=3.3333=$3.33 $2.4712true2.47*1/2=1.235=$1.24 $2.4712false2.47*1/2=1.235=$1.23 Which prices are updated?: The rate described above is applied to tax-basis and tax then the net-price and gross-price are recalculated by adding / subtracting depending on whether the order is based on net price. Example (order based on net price) New TaxBasis:$10.00, Tax:$1.00, NetPrice=TaxBasis=$10.00, GrossPrice=TaxBasis+Tax=$11.00 Example (order based on gross price) New TaxBasis:$10.00, Tax:$1.00, NetPrice=TaxBasis-tax=$9.00, GrossPrice=TaxBasis=$10.00

**Parameters:**

- `factor`: factor used to calculate rate
- `divisor`: divisor used to calculate rate
- `roundUp`: whether to round up or down on 0.5

**See Also:**

AbstractItem.getTaxBasis()
AbstractItem.getTax()
AbstractItem.getNetPrice()
AbstractItem.getGrossPrice()
TaxMgr.getTaxationPolicy()

---

### getBasePrice

**Signature:** `getBasePrice() : Money`

**Description:** Price of a single unit before discount application.

**Returns:**

Price of a single unit before discount application.

---

### getNote

**Signature:** `getNote() : String`

**Description:** Return the note for this return item.

**Returns:**

the note or null

---

### getParentItem

**Signature:** `getParentItem() : ReturnItem`

**Description:** Returns null or the parent item.

**Returns:**

null or the parent item.

---

### getReasonCode

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

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

**Returns:**

the return reason code

---

### getReturnCaseItem

**Signature:** `getReturnCaseItem() : ReturnCaseItem`

**Description:** Returns the return case item related to this item. Should never return null.

**Returns:**

the return case item related to this item

---

### getReturnedQuantity

**Signature:** `getReturnedQuantity() : Quantity`

**Description:** The Quantity returned. This may return an N/A quantity.

**Returns:**

the quantity returned, may be N/A

---

### getReturnNumber

**Signature:** `getReturnNumber() : String`

**Description:** The mandatory returnNumber of the Return to which this item belongs.

**Returns:**

the returnNumber of the Return to which this item belongs

---

### setNote

**Signature:** `setNote(note : String) : void`

**Description:** Sets a note for this return item.

**Parameters:**

- `note`: the note for this return item to set

---

### setParentItem

**Signature:** `setParentItem(parentItem : ReturnItem) : void`

**Description:** Set a parent item. The parent item must belong to the same Return. An infinite parent-child loop is disallowed as is a parent-child depth greater than 10. Setting a parent item indicates a dependency of the child item on the parent item, and can be used to form a parallel structure to that accessed using ProductLineItem.getParent().

**Parameters:**

- `parentItem`: The parent item, null is allowed

---

### setReasonCode

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

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

**Parameters:**

- `reasonCode`: the reason code to set

---

### setReturnedQuantity

**Signature:** `setReturnedQuantity(quantity : Quantity) : void`

**Description:** Set the Quantity returned. Passing null results in an exception being thrown. The quantity must be higher than zero and not be higher than the remaining quantity to return. The item prices are recalculated in this method as described in applyPriceRate(Decimal, Decimal, Boolean) with the quantity argument as the factor, and ordered quantity as divisor and true as the roundup parameter.

**Parameters:**

- `quantity`: the quantity returned, null not allowed

**See Also:**

OrderItem.getReturnedQuantity()
ProductLineItem.getQuantity()

---

### setTaxBasis

**Signature:** `setTaxBasis(taxBasis : Money) : void`

**Description:** Set the tax-basis price for this item.

**Parameters:**

- `taxBasis`: the tax basis value.

---

### setTaxItems

**Signature:** `setTaxItems(taxItems : Collection) : void`

**Description:** Set the tax-items for this item.

**Parameters:**

- `taxItems`: items

**See Also:**

addTaxItem(Decimal, TaxGroup)
TaxGroup.create(String, String, String, Decimal)

---
```

--------------------------------------------------------------------------------
/docs/dw_order/ShippingLineItem.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.order

# Class ShippingLineItem

## Inheritance Hierarchy

- Object
  - dw.object.PersistentObject
  - dw.object.ExtensibleObject
    - dw.order.LineItem
      - dw.order.ShippingLineItem

## Description

Represents a specific line item in a shipment. The ShippingLineItem defines the general shipping costs of a shipment.

## Constants

### STANDARD_SHIPPING_ID

**Type:** String = "STANDARD_SHIPPING"

Constant used to get the standard shipping line item.

## Properties

### adjustedGrossPrice

**Type:** Money (Read Only)

The price of this shipping line item including tax after
 shipping adjustments have been applied.

### adjustedNetPrice

**Type:** Money (Read Only)

The price of this shipping line item, excluding tax after
 shipping adjustments have been applied.

### adjustedPrice

**Type:** Money (Read Only)

The adjusted price of this shipping line item. If the line item
 container is based on net pricing, the adjusted net price is returned. If
 the line item container is based on gross pricing, the adjusted gross
 price is returned.

### adjustedTax

**Type:** Money (Read Only)

The tax of this shipping line item after shipping adjustments
 have been applied.

### ID

**Type:** String (Read Only)

The ID of this ShippingLineItem.

### orderItem

**Type:** OrderItem (Read Only)

The order-item extension for this item, or null.
 An order-item extension will only exist for a ShippingLineItem which
 belongs to an Order.
 
 Order post-processing APIs (gillian) are now inactive by default and will throw
 an exception if accessed. Activation needs preliminary approval by Product Management.
 Please contact support in this case. Existing customers using these APIs are not
 affected by this change and can use the APIs until further notice.

### shippingPriceAdjustments

**Type:** Collection (Read Only)

The collection of shipping price adjustments that have been
 applied to this shipping line item.

## Constructor Summary

## Method Summary

### createShippingPriceAdjustment

**Signature:** `createShippingPriceAdjustment(promotionID : String) : PriceAdjustment`

Creates a shipping price adjustment to be applied to the shipping line item. The promotion ID is mandatory and must not be the ID of any actual promotion defined in B2C Commerce. If there already exists a shipping price adjustment on this shipping line item referring to the specified promotion ID, an exception is thrown.

### createShippingPriceAdjustment

**Signature:** `createShippingPriceAdjustment(promotionID : String, discount : Discount) : PriceAdjustment`

Creates a shipping price adjustment to be applied to the shipping line item. The promotion ID is mandatory and must not be the ID of any actual promotion defined in B2C Commerce.

### getAdjustedGrossPrice

**Signature:** `getAdjustedGrossPrice() : Money`

Returns the price of this shipping line item including tax after shipping adjustments have been applied.

### getAdjustedNetPrice

**Signature:** `getAdjustedNetPrice() : Money`

Returns the price of this shipping line item, excluding tax after shipping adjustments have been applied.

### getAdjustedPrice

**Signature:** `getAdjustedPrice() : Money`

Returns the adjusted price of this shipping line item.

### getAdjustedTax

**Signature:** `getAdjustedTax() : Money`

Returns the tax of this shipping line item after shipping adjustments have been applied.

### getID

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

Returns the ID of this ShippingLineItem.

### getOrderItem

**Signature:** `getOrderItem() : OrderItem`

Returns the order-item extension for this item, or null.

### getShippingPriceAdjustments

**Signature:** `getShippingPriceAdjustments() : Collection`

Returns the collection of shipping price adjustments that have been applied to this shipping line item.

### removeShippingPriceAdjustment

**Signature:** `removeShippingPriceAdjustment(priceAdjustment : PriceAdjustment) : void`

Removes the specified shipping price adjustment from this shipping line item.

## Method Detail

## Method Details

### createShippingPriceAdjustment

**Signature:** `createShippingPriceAdjustment(promotionID : String) : PriceAdjustment`

**Description:** Creates a shipping price adjustment to be applied to the shipping line item. The promotion ID is mandatory and must not be the ID of any actual promotion defined in B2C Commerce. If there already exists a shipping price adjustment on this shipping line item referring to the specified promotion ID, an exception is thrown.

**Parameters:**

- `promotionID`: Promotion ID

**Returns:**

The new price adjustment line item.

---

### createShippingPriceAdjustment

**Signature:** `createShippingPriceAdjustment(promotionID : String, discount : Discount) : PriceAdjustment`

**Description:** Creates a shipping price adjustment to be applied to the shipping line item. The promotion ID is mandatory and must not be the ID of any actual promotion defined in B2C Commerce. If a shipping price adjustment on this shipping line item referring to the specified promotion ID already exists, an exception is thrown. The possible values for discount are PercentageDiscount, AmountDiscount, FixedPriceShippingDiscount. Examples: var myShippingItem : dw.order.ShippingLineItem; // assume known var paFixedShippingPrice12 : dw.order.PriceAdjustment = myShippingItem.createPriceAdjustment("myPromotionID1", new FixedPriceShippingDiscount(12)); var paTenPercent : dw.order.PriceAdjustment = myShippingItem.createPriceAdjustment("myPromotionID2", new PercentageDiscount(10)); var paReduceBy2 : dw.order.PriceAdjustment = myShippingItem.createPriceAdjustment("myPromotionID3", new AmountDiscount(2));

**Parameters:**

- `promotionID`: Promotion ID
- `discount`: The desired discount, not null

**Returns:**

The new price adjustment line item.

---

### getAdjustedGrossPrice

**Signature:** `getAdjustedGrossPrice() : Money`

**Description:** Returns the price of this shipping line item including tax after shipping adjustments have been applied.

**Returns:**

the price of this shipping line item, including tax after shipping adjustments have been applied.

---

### getAdjustedNetPrice

**Signature:** `getAdjustedNetPrice() : Money`

**Description:** Returns the price of this shipping line item, excluding tax after shipping adjustments have been applied.

**Returns:**

the price of this shipping line item, excluding tax after shipping adjustments have been applied.

---

### getAdjustedPrice

**Signature:** `getAdjustedPrice() : Money`

**Description:** Returns the adjusted price of this shipping line item. If the line item container is based on net pricing, the adjusted net price is returned. If the line item container is based on gross pricing, the adjusted gross price is returned.

**Returns:**

either the adjusted net or gross price of this shipping line item.

---

### getAdjustedTax

**Signature:** `getAdjustedTax() : Money`

**Description:** Returns the tax of this shipping line item after shipping adjustments have been applied.

**Returns:**

the tax of this shipping line item after shipping adjustments have been applied.

---

### getID

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

**Description:** Returns the ID of this ShippingLineItem.

**Returns:**

ID of this ShippingLineItem

---

### getOrderItem

**Signature:** `getOrderItem() : OrderItem`

**Description:** Returns the order-item extension for this item, or null. An order-item extension will only exist for a ShippingLineItem which belongs to an Order. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.

**Returns:**

null or the order-item

---

### getShippingPriceAdjustments

**Signature:** `getShippingPriceAdjustments() : Collection`

**Description:** Returns the collection of shipping price adjustments that have been applied to this shipping line item.

**Returns:**

the collection of shipping price adjustments that have been applied to this shipping line item.

---

### removeShippingPriceAdjustment

**Signature:** `removeShippingPriceAdjustment(priceAdjustment : PriceAdjustment) : void`

**Description:** Removes the specified shipping price adjustment from this shipping line item.

**Parameters:**

- `priceAdjustment`: The price adjustment line item to remove

---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-system-object-definitions.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
# ==================================================================================
# SFCC MCP Server - get_system_object_definitions Tool YAML Tests (Docs-Only Mode)
# Tests that system object tools are NOT available in docs-only mode
# This tool requires SFCC credentials and should not be available without them
# 
# Quick Test Commands:
# aegis "tests/mcp/yaml/get-system-object-definitions.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose
# aegis "tests/mcp/yaml/get-system-object-definitions.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --debug --timing
# aegis query --config "aegis.config.docs-only.json"
# ==================================================================================

description: "get_system_object_definitions tool tests - Docs-only mode tool availability"

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

  - it: "should NOT list any system object tools in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "no-system-object-tools"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "no-system-object-tools"
        result:
          match:extractField: "tools.*.name"
          value: "match:not:arrayContains:get_system_object_definition"
      stderr: "toBeEmpty"

  - it: "should NOT list site preferences tools in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "no-site-prefs-tools"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "no-site-prefs-tools"
        result:
          match:extractField: "tools.*.name"
          value: "match:not:arrayContains:search_site_preferences"
      stderr: "toBeEmpty"

  - it: "should NOT list custom object attribute tools in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "no-custom-object-tools"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "no-custom-object-tools"
        result:
          match:extractField: "tools.*.name"
          value: "match:not:arrayContains:search_custom_object_attribute_definitions"
      stderr: "toBeEmpty"

# ==================================================================================
# TOOL CALL BEHAVIOR - AVAILABLE BUT RETURNS ERROR  
# ==================================================================================

  - it: "should return configuration error when calling unlisted tool"
    request:
      jsonrpc: "2.0"
      id: "config-error"
      method: "tools/call"
      params:
        name: "get_system_object_definitions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "config-error"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:OCAPI client not configured"
          isError: true
      performance:
        maxResponseTime: "500ms"  # Error should be fast

  - it: "should return proper error result structure for unlisted tool"
    request:
      jsonrpc: "2.0"
      id: "error-result-structure"
      method: "tools/call"
      params:
        name: "get_system_object_definitions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "error-result-structure"
        result:
          content: "match:type:array"
          isError: true

  - it: "should fail fast when calling unlisted tool with parameters"
    request:
      jsonrpc: "2.0"
      id: "fail-fast-with-params"
      method: "tools/call"
      params:
        name: "get_system_object_definitions"
        arguments:
          start: 0
          count: 10
    expect:
      response:
        jsonrpc: "2.0"
        id: "fail-fast-with-params"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:OCAPI client not configured"
          isError: true
      performance:
        maxResponseTime: "500ms"  # Should fail quickly

# ==================================================================================
# DOCS-ONLY MODE TOOL AVAILABILITY VERIFICATION
# ==================================================================================

  - it: "should have SFCC documentation tools available"
    request:
      jsonrpc: "2.0"
      id: "docs-tools-available"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "docs-tools-available"
        result:
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_sfcc_class_info"
      stderr: "toBeEmpty"

  - it: "should have SFRA documentation tools available"
    request:
      jsonrpc: "2.0"
      id: "sfra-tools-available"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "sfra-tools-available"
        result:
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_sfra_document"
      stderr: "toBeEmpty"

  - it: "should have best practices tools available"
    request:
      jsonrpc: "2.0"
      id: "best-practices-available"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "best-practices-available"
        result:
          match:extractField: "tools.*.name"
          value: "match:arrayContains:get_best_practice_guide"
      stderr: "toBeEmpty"

  - it: "should have cartridge generation tools available"
    request:
      jsonrpc: "2.0"
      id: "cartridge-tools-available"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "cartridge-tools-available"
        result:
          match:extractField: "tools.*.name"
          value: "match:arrayContains:generate_cartridge_structure"
      stderr: "toBeEmpty"

  - it: "should have expected number of tools in docs-only mode"
    request:
      jsonrpc: "2.0"
      id: "expected-tool-count"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "expected-tool-count"
        result:
          tools: "match:arrayLength:15"  # Based on actual count from aegis query
      stderr: "toBeEmpty"

# ==================================================================================
# CONSISTENCY TESTING
# ==================================================================================

  - it: "should consistently exclude system object tools across multiple calls"
    request:
      jsonrpc: "2.0"
      id: "consistent-exclusion-1"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "consistent-exclusion-1"
        result:
          match:extractField: "tools.*.name"
          value: "match:not:arrayContains:get_system_object_definitions"

  - it: "should maintain consistent tool exclusion on second call"
    request:
      jsonrpc: "2.0"
      id: "consistent-exclusion-2"
      method: "tools/list"
      params: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "consistent-exclusion-2"
        result:
          match:extractField: "tools.*.name"
          value: "match:not:arrayContains:get_system_object_definitions"

  - it: "should consistently return configuration error across multiple attempts"
    request:
      jsonrpc: "2.0"
      id: "consistent-config-error"
      method: "tools/call"
      params:
        name: "get_system_object_definitions"
        arguments: {}
    expect:
      response:
        jsonrpc: "2.0"
        id: "consistent-config-error"
        result:
          content:
            match:arrayElements:
              match:partial:
                type: "text"
                text: "match:contains:OCAPI client not configured"
          isError: true
```

--------------------------------------------------------------------------------
/docs/dw_catalog/Store.md:
--------------------------------------------------------------------------------

```markdown
## Package: dw.catalog

# Class Store

## Inheritance Hierarchy

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

## Description

Represents a store in Commerce Cloud Digital.

## Properties

### address1

**Type:** String (Read Only)

The address1 of the store.

### address2

**Type:** String (Read Only)

The address2 of the store.

### city

**Type:** String (Read Only)

The city of the store.

### countryCode

**Type:** EnumValue (Read Only)

The countryCode of the store.

### demandwarePosEnabled

**Type:** boolean (Read Only)

The demandwarePosEnabled flag for the store.
 Indicates that this store uses Commerce Cloud Store for point-of-sale.

### email

**Type:** String (Read Only)

The email of the store.

### fax

**Type:** String (Read Only)

The fax of the store.

### ID

**Type:** String (Read Only)

The ID of the store.

### image

**Type:** MediaFile (Read Only)

The store image.

### inventoryList

**Type:** ProductInventoryList (Read Only)

The inventory list the store is associated with. If the
 store is not associated with a inventory list, or the inventory list does not
 exist, the method returns null.

### inventoryListID

**Type:** String (Read Only)

The inventory list id the store is associated with. If the
 store is not associated with a inventory list, or the inventory list does not
 exist, the method returns null.

### latitude

**Type:** Number (Read Only)

The latitude of the store.

### longitude

**Type:** Number (Read Only)

The longitude of the store.

### name

**Type:** String (Read Only)

The name of the store.

### phone

**Type:** String (Read Only)

The phone of the store.

### posEnabled

**Type:** boolean (Read Only)

The posEnabled flag for the Store.
 Indicates that this store uses Commerce Cloud Store for point-of-sale.

### postalCode

**Type:** String (Read Only)

The postalCode of the store.

### stateCode

**Type:** String (Read Only)

The stateCode of the store.

### storeEvents

**Type:** MarkupText (Read Only)

The storeEvents of the store.

### storeGroups

**Type:** Collection (Read Only)

All the store groups this store belongs to.

### storeHours

**Type:** MarkupText (Read Only)

The storeHours of the store.

### storeLocatorEnabled

**Type:** boolean (Read Only)

The storeLocatorEnabled flag for the store.

## Constructor Summary

## Method Summary

### getAddress1

**Signature:** `getAddress1() : String`

Returns the address1 of the store.

### getAddress2

**Signature:** `getAddress2() : String`

Returns the address2 of the store.

### getCity

**Signature:** `getCity() : String`

Returns the city of the store.

### getCountryCode

**Signature:** `getCountryCode() : EnumValue`

Returns the countryCode of the store.

### getEmail

**Signature:** `getEmail() : String`

Returns the email of the store.

### getFax

**Signature:** `getFax() : String`

Returns the fax of the store.

### getID

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

Returns the ID of the store.

### getImage

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

Returns the store image.

### getInventoryList

**Signature:** `getInventoryList() : ProductInventoryList`

Returns the inventory list the store is associated with.

### getInventoryListID

**Signature:** `getInventoryListID() : String`

Returns the inventory list id the store is associated with.

### getLatitude

**Signature:** `getLatitude() : Number`

Returns the latitude of the store.

### getLongitude

**Signature:** `getLongitude() : Number`

Returns the longitude of the store.

### getName

**Signature:** `getName() : String`

Returns the name of the store.

### getPhone

**Signature:** `getPhone() : String`

Returns the phone of the store.

### getPostalCode

**Signature:** `getPostalCode() : String`

Returns the postalCode of the store.

### getStateCode

**Signature:** `getStateCode() : String`

Returns the stateCode of the store.

### getStoreEvents

**Signature:** `getStoreEvents() : MarkupText`

Returns the storeEvents of the store.

### getStoreGroups

**Signature:** `getStoreGroups() : Collection`

Returns all the store groups this store belongs to.

### getStoreHours

**Signature:** `getStoreHours() : MarkupText`

Returns the storeHours of the store.

### isDemandwarePosEnabled

**Signature:** `isDemandwarePosEnabled() : boolean`

Returns the demandwarePosEnabled flag for the store.

### isPosEnabled

**Signature:** `isPosEnabled() : boolean`

Returns the posEnabled flag for the Store.

### isStoreLocatorEnabled

**Signature:** `isStoreLocatorEnabled() : boolean`

Returns the storeLocatorEnabled flag for the store.

## Method Detail

## Method Details

### getAddress1

**Signature:** `getAddress1() : String`

**Description:** Returns the address1 of the store.

**Returns:**

address1 of the store

---

### getAddress2

**Signature:** `getAddress2() : String`

**Description:** Returns the address2 of the store.

**Returns:**

address2 of the store

---

### getCity

**Signature:** `getCity() : String`

**Description:** Returns the city of the store.

**Returns:**

city of the store

---

### getCountryCode

**Signature:** `getCountryCode() : EnumValue`

**Description:** Returns the countryCode of the store.

**Returns:**

countryCode of the store

---

### getEmail

**Signature:** `getEmail() : String`

**Description:** Returns the email of the store.

**Returns:**

email of the store

---

### getFax

**Signature:** `getFax() : String`

**Description:** Returns the fax of the store.

**Returns:**

fax of the store

---

### getID

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

**Description:** Returns the ID of the store.

**Returns:**

ID of the store

---

### getImage

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

**Description:** Returns the store image.

**Returns:**

the store image.

---

### getInventoryList

**Signature:** `getInventoryList() : ProductInventoryList`

**Description:** Returns the inventory list the store is associated with. If the store is not associated with a inventory list, or the inventory list does not exist, the method returns null.

**Returns:**

ProductInventoryList or null

---

### getInventoryListID

**Signature:** `getInventoryListID() : String`

**Description:** Returns the inventory list id the store is associated with. If the store is not associated with a inventory list, or the inventory list does not exist, the method returns null.

**Returns:**

the inventory list id

---

### getLatitude

**Signature:** `getLatitude() : Number`

**Description:** Returns the latitude of the store.

**Returns:**

latitude of the store

---

### getLongitude

**Signature:** `getLongitude() : Number`

**Description:** Returns the longitude of the store.

**Returns:**

longitude of the store

---

### getName

**Signature:** `getName() : String`

**Description:** Returns the name of the store.

**Returns:**

name of the store

---

### getPhone

**Signature:** `getPhone() : String`

**Description:** Returns the phone of the store.

**Returns:**

phone of the store

---

### getPostalCode

**Signature:** `getPostalCode() : String`

**Description:** Returns the postalCode of the store.

**Returns:**

postalCode of the store

---

### getStateCode

**Signature:** `getStateCode() : String`

**Description:** Returns the stateCode of the store.

**Returns:**

stateCode of the store

---

### getStoreEvents

**Signature:** `getStoreEvents() : MarkupText`

**Description:** Returns the storeEvents of the store.

**Returns:**

storeEvents of the store

---

### getStoreGroups

**Signature:** `getStoreGroups() : Collection`

**Description:** Returns all the store groups this store belongs to.

**Returns:**

collection of store groups

---

### getStoreHours

**Signature:** `getStoreHours() : MarkupText`

**Description:** Returns the storeHours of the store.

**Returns:**

storeHours of the store

---

### isDemandwarePosEnabled

**Signature:** `isDemandwarePosEnabled() : boolean`

**Description:** Returns the demandwarePosEnabled flag for the store. Indicates that this store uses Commerce Cloud Store for point-of-sale.

**Deprecated:**

Use isPosEnabled() instead

**Returns:**

the demandwarePosEnabled flag

---

### isPosEnabled

**Signature:** `isPosEnabled() : boolean`

**Description:** Returns the posEnabled flag for the Store. Indicates that this store uses Commerce Cloud Store for point-of-sale.

**Returns:**

the posEnabled flag

---

### isStoreLocatorEnabled

**Signature:** `isStoreLocatorEnabled() : boolean`

**Description:** Returns the storeLocatorEnabled flag for the store.

**Returns:**

the storeLocatorEnabled flag

---
```

--------------------------------------------------------------------------------
/tests/query-builder.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for QueryBuilder utility
 * Tests URL parameter construction and array handling
 */

import { QueryBuilder } from '../src/utils/query-builder.js';

describe('QueryBuilder', () => {
  let builder: QueryBuilder;

  beforeEach(() => {
    builder = new QueryBuilder();
  });

  describe('constructor', () => {
    it('should initialize with empty parameters', () => {
      expect(builder).toBeInstanceOf(QueryBuilder);
    });
  });

  describe('add method', () => {
    it('should add string parameter', () => {
      const result = builder.add('key', 'value').build();
      expect(result).toBe('key=value');
    });

    it('should add number parameter', () => {
      const result = builder.add('count', 10).build();
      expect(result).toBe('count=10');
    });

    it('should add boolean parameter', () => {
      const result = builder.add('active', true).build();
      expect(result).toBe('active=true');
    });

    it('should skip undefined values', () => {
      const result = builder.add('key', undefined as any).build();
      expect(result).toBe('');
    });

    it('should skip null values', () => {
      const result = builder.add('key', null as any).build();
      expect(result).toBe('');
    });

    it('should chain multiple adds', () => {
      const result = builder
        .add('first', 'value1')
        .add('second', 'value2')
        .build();
      expect(result).toBe('first=value1&second=value2');
    });
  });

  describe('addArray method', () => {
    it('should handle regular arrays with comma separation', () => {
      const result = builder.addArray('ids', ['1', '2', '3']).build();
      expect(result).toBe('ids=1%2C2%2C3');
    });

    it('should handle refine parameters with multiple entries', () => {
      const result = builder.addArray('refine', ['category=shirts', 'color=blue']).build();
      expect(result).toBe('refine=category%3Dshirts&refine=color%3Dblue');
    });

    it('should handle mixed string and number arrays', () => {
      const result = builder.addArray('values', ['string', 123]).build();
      expect(result).toBe('values=string%2C123');
    });

    it('should skip empty arrays', () => {
      const result = builder.addArray('empty', []).build();
      expect(result).toBe('');
    });

    it('should skip non-arrays', () => {
      const result = builder.addArray('invalid', null as any).build();
      expect(result).toBe('');
    });

    it('should chain with other methods', () => {
      const result = builder
        .add('single', 'value')
        .addArray('multiple', ['a', 'b'])
        .build();
      expect(result).toBe('single=value&multiple=a%2Cb');
    });
  });

  describe('addFromObject method', () => {
    it('should add simple object properties', () => {
      const params = {
        name: 'test',
        count: 5,
        active: true,
      };
      const result = builder.addFromObject(params).build();
      expect(result).toBe('name=test&count=5&active=true');
    });

    it('should handle arrays in object', () => {
      const params = {
        ids: ['1', '2'],
        expand: ['details', 'variations'],
      };
      const result = builder.addFromObject(params).build();
      expect(result).toBe('ids=1%2C2&expand=details%2Cvariations');
    });

    it('should handle refine arrays specially', () => {
      const params = {
        q: 'shirt',
        refine: ['category=clothing', 'size=large'],
      };
      const result = builder.addFromObject(params).build();
      expect(result).toBe('q=shirt&refine=category%3Dclothing&refine=size%3Dlarge');
    });

    it('should skip undefined and null values', () => {
      const params = {
        defined: 'value',
        undefined,
        null: null,
        empty: '',
        zero: 0,
      };
      const result = builder.addFromObject(params).build();
      expect(result).toBe('defined=value&empty=&zero=0');
    });

    it('should handle complex object with mixed types', () => {
      const params = {
        q: 'search term',
        count: 20,
        start: 0,
        expand: ['images', 'prices'],
        refine: ['brand=nike', 'color=red'],
        active: true,
        skip: undefined,
      };
      const result = builder.addFromObject(params).build();
      expect(result).toBe(
        'q=search+term&count=20&start=0&expand=images%2Cprices&refine=brand%3Dnike&refine=color%3Dred&active=true',
      );
    });

    it('should chain with other methods', () => {
      const result = builder
        .add('manual', 'value')
        .addFromObject({ auto: 'generated' })
        .add('final', 'last')
        .build();
      expect(result).toBe('manual=value&auto=generated&final=last');
    });
  });

  describe('build method', () => {
    it('should return empty string for no parameters', () => {
      const result = builder.build();
      expect(result).toBe('');
    });

    it('should properly encode special characters', () => {
      const result = builder.add('special', 'value with spaces & symbols!').build();
      expect(result).toBe('special=value+with+spaces+%26+symbols%21');
    });

    it('should handle multiple calls to build', () => {
      builder.add('key', 'value');
      const first = builder.build();
      const second = builder.build();
      expect(first).toBe(second);
      expect(first).toBe('key=value');
    });
  });

  describe('reset method', () => {
    it('should clear all parameters', () => {
      const result = builder
        .add('first', 'value')
        .add('second', 'value')
        .reset()
        .build();
      expect(result).toBe('');
    });

    it('should return QueryBuilder instance for chaining', () => {
      const result = builder.reset();
      expect(result).toBeInstanceOf(QueryBuilder);
      expect(result).toBe(builder);
    });

    it('should allow rebuilding after reset', () => {
      const result = builder
        .add('old', 'value')
        .reset()
        .add('new', 'value')
        .build();
      expect(result).toBe('new=value');
    });
  });

  describe('static fromObject method', () => {
    it('should create query string from object', () => {
      const params = {
        search: 'test',
        count: 10,
        active: true,
      };
      const result = QueryBuilder.fromObject(params);
      expect(result).toBe('search=test&count=10&active=true');
    });

    it('should handle arrays correctly', () => {
      const params = {
        ids: ['1', '2', '3'],
        refine: ['category=shirts', 'size=large'],
      };
      const result = QueryBuilder.fromObject(params);
      expect(result).toBe('ids=1%2C2%2C3&refine=category%3Dshirts&refine=size%3Dlarge');
    });

    it('should handle empty object', () => {
      const result = QueryBuilder.fromObject({});
      expect(result).toBe('');
    });

    it('should handle complex OCAPI-style parameters', () => {
      const params = {
        q: 'mens shoes',
        count: 25,
        start: 50,
        expand: ['images', 'variations', 'prices'],
        refine: ['category=footwear', 'brand=nike', 'size=10'],
        sort: 'price-asc',
        currency: 'USD',
        locale: 'en_US',
      };
      const result = QueryBuilder.fromObject(params);

      expect(result).toContain('q=mens+shoes');
      expect(result).toContain('count=25');
      expect(result).toContain('start=50');
      expect(result).toContain('expand=images%2Cvariations%2Cprices');
      expect(result).toContain('refine=category%3Dfootwear');
      expect(result).toContain('refine=brand%3Dnike');
      expect(result).toContain('refine=size%3D10');
      expect(result).toContain('sort=price-asc');
      expect(result).toContain('currency=USD');
      expect(result).toContain('locale=en_US');
    });
  });

  describe('edge cases', () => {
    it('should handle empty strings', () => {
      const result = builder.add('empty', '').build();
      expect(result).toBe('empty=');
    });

    it('should handle zero values', () => {
      const result = builder.add('zero', 0).build();
      expect(result).toBe('zero=0');
    });

    it('should handle false values', () => {
      const result = builder.add('false', false).build();
      expect(result).toBe('false=false');
    });

    it('should handle arrays with empty strings', () => {
      const result = builder.addArray('mixed', ['value', '', 'another']).build();
      expect(result).toBe('mixed=value%2C%2Canother');
    });

    it('should handle Unicode characters', () => {
      const result = builder.add('unicode', 'café ñoño 中文').build();
      expect(result).toBe('unicode=caf%C3%A9+%C3%B1o%C3%B1o+%E4%B8%AD%E6%96%87');
    });
  });
});

```

--------------------------------------------------------------------------------
/src/clients/logs/log-analyzer.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Log analysis, summarization, and pattern detection
 */

import { Logger } from '../../utils/logger.js';
import { LogProcessor } from './log-processor.js';
import { LogFormatter } from './log-formatter.js';
import type { LogSummary, LogFileMetadata, ProcessedLogEntry } from './log-types.js';

export class LogAnalyzer {
  private logger: Logger;
  private processor: LogProcessor;

  constructor(logger: Logger) {
    this.logger = logger;
    this.processor = new LogProcessor(logger);
  }

  /**
   * Analyze log files and generate comprehensive summary
   */
  async analyzeLogs(
    files: LogFileMetadata[],
    fileContents: Map<string, string>,
    date: string,
  ): Promise<LogSummary> {
    const summary: LogSummary = {
      date,
      errorCount: 0,
      warningCount: 0,
      infoCount: 0,
      debugCount: 0,
      keyIssues: [],
      files: files.map((f: LogFileMetadata) => f.filename),
    };

    // Analyze each log file for counts and patterns
    for (const file of files) {
      const content = fileContents.get(file.filename);
      if (!content) {
        this.logger.warn(`No content found for analysis: ${file.filename}`);
        continue;
      }

      try {
        // Count different log levels
        const counts = this.processor.countLogLevels(content);
        summary.errorCount += counts.errorCount;
        summary.warningCount += counts.warningCount;
        summary.infoCount += counts.infoCount;
        summary.debugCount += counts.debugCount;

        // Extract key issues from error files
        if (this.isErrorFile(file.filename)) {
          const issues = this.processor.extractKeyIssues(content);
          summary.keyIssues.push(...issues);
        }
      } catch (error) {
        this.logger.error(`Error analyzing file ${file.filename}:`, error);
      }
    }

    // Remove duplicate key issues
    summary.keyIssues = [...new Set(summary.keyIssues)];

    return summary;
  }

  /**
   * Detect patterns and anomalies in logs
   */
  detectPatterns(entries: ProcessedLogEntry[]): {
    frequentErrors: Map<string, number>;
    timePatterns: Map<string, number>;
    sourcePatterns: Map<string, number>;
  } {
    const frequentErrors = new Map<string, number>();
    const timePatterns = new Map<string, number>();
    const sourcePatterns = new Map<string, number>();

    for (const entry of entries) {
      // Count error patterns
      if (entry.level === 'error') {
        const errorPattern = this.extractErrorPattern(entry.content);
        frequentErrors.set(errorPattern, (frequentErrors.get(errorPattern) ?? 0) + 1);
      }

      // Count time patterns (hour-based)
      if (entry.timestamp) {
        const hour = new Date(entry.timestamp).getHours();
        const hourKey = `${hour}:00-${hour + 1}:00`;
        timePatterns.set(hourKey, (timePatterns.get(hourKey) ?? 0) + 1);
      }

      // Count source patterns
      if (entry.source) {
        sourcePatterns.set(entry.source, (sourcePatterns.get(entry.source) ?? 0) + 1);
      }
    }

    return {
      frequentErrors,
      timePatterns,
      sourcePatterns,
    };
  }

  /**
   * Generate health score based on log analysis
   */
  calculateHealthScore(summary: LogSummary): {
    score: number;
    level: 'excellent' | 'good' | 'warning' | 'critical';
    factors: string[];
  } {
    const factors: string[] = [];
    let score = 100;

    // Deduct points for errors
    if (summary.errorCount > 0) {
      const errorPenalty = Math.min(summary.errorCount * 2, 30);
      score -= errorPenalty;
      factors.push(`Errors detected: -${errorPenalty} points`);
    }

    // Deduct points for warnings
    if (summary.warningCount > 10) {
      const warningPenalty = Math.min((summary.warningCount - 10) * 0.5, 15);
      score -= warningPenalty;
      factors.push(`High warning count: -${warningPenalty} points`);
    }

    // Deduct points for key issues
    if (summary.keyIssues.length > 0) {
      const issuePenalty = Math.min(summary.keyIssues.length * 5, 25);
      score -= issuePenalty;
      factors.push(`Key issues: -${issuePenalty} points`);
    }

    // Determine level
    let level: 'excellent' | 'good' | 'warning' | 'critical';
    if (score >= 90) {
      level = 'excellent';
    } else if (score >= 75) {
      level = 'good';
    } else if (score >= 50) {
      level = 'warning';
    } else {
      level = 'critical';
    }

    return { score: Math.max(0, score), level, factors };
  }

  /**
   * Find trending issues across time periods
   */
  findTrendingIssues(
    currentSummary: LogSummary,
    previousSummaries: LogSummary[],
  ): {
    increasing: string[];
    decreasing: string[];
    new: string[];
  } {
    const trending = {
      increasing: [] as string[],
      decreasing: [] as string[],
      new: [] as string[],
    };

    // Compare current issues with previous periods
    const previousIssues = new Set(
      previousSummaries.flatMap(summary => summary.keyIssues),
    );

    for (const issue of currentSummary.keyIssues) {
      if (!previousIssues.has(issue)) {
        trending.new.push(issue);
      }
    }

    // For increasing/decreasing, we'd need more sophisticated tracking
    // This is a simplified version
    const currentErrorCount = currentSummary.errorCount;
    const avgPreviousErrors = previousSummaries.length > 0
      ? previousSummaries.reduce((sum, s) => sum + s.errorCount, 0) / previousSummaries.length
      : 0;

    if (currentErrorCount > avgPreviousErrors * 1.5) {
      trending.increasing.push('Overall error rate');
    } else if (currentErrorCount < avgPreviousErrors * 0.5) {
      trending.decreasing.push('Overall error rate');
    }

    return trending;
  }

  /**
   * Generate recommendations based on analysis
   */
  generateRecommendations(summary: LogSummary, patterns: ReturnType<typeof this.detectPatterns>): string[] {
    const recommendations: string[] = [];

    if (summary.errorCount > 10) {
      recommendations.push('High error count detected. Review error logs for critical issues.');
    }

    if (summary.warningCount > 50) {
      recommendations.push('High warning count. Consider addressing warnings to prevent future errors.');
    }

    if (patterns.frequentErrors.size > 0) {
      const topError = Array.from(patterns.frequentErrors.entries())
        .sort((a, b) => b[1] - a[1])[0];
      recommendations.push(`Most frequent error: "${topError[0]}" (${topError[1]} occurrences)`);
    }

    if (patterns.timePatterns.size > 0) {
      const peakHour = Array.from(patterns.timePatterns.entries())
        .sort((a, b) => b[1] - a[1])[0];
      recommendations.push(`Peak activity time: ${peakHour[0]} (${peakHour[1]} events)`);
    }

    if (summary.keyIssues.length === 0 && summary.errorCount === 0) {
      recommendations.push('System appears to be running smoothly with no critical issues detected.');
    }

    return recommendations;
  }

  /**
   * Extract error pattern for categorization
   */
  private extractErrorPattern(errorContent: string): string {
    // Extract the core error message, removing dynamic parts
    const patterns = [
      /Exception: (.+?)(\s+at\s|$)/,
      /Error: (.+?)(\s+at\s|$)/,
      /Failed to (.+?)(\s+\(|$)/,
      /Cannot (.+?)(\s+\(|$)/,
    ];

    for (const pattern of patterns) {
      const match = errorContent.match(pattern);
      if (match) {
        return match[1].trim();
      }
    }

    // Fallback: use first 50 characters
    return LogFormatter.truncateText(errorContent, 50);
  }

  /**
   * Check if filename indicates an error log file
   */
  private isErrorFile(filename: string): boolean {
    const normalizedName = filename.toLowerCase();
    return normalizedName.includes('error-') || normalizedName.includes('customerror-');
  }

  /**
   * Format analysis results for display
   */
  formatAnalysisResults(
    summary: LogSummary,
    patterns: ReturnType<typeof this.detectPatterns>,
    healthScore: ReturnType<typeof this.calculateHealthScore>,
    recommendations: string[],
  ): string {
    const sections = [
      LogFormatter.formatLogSummary(summary),
      '',
      `🏥 Health Score: ${healthScore.score}/100 (${healthScore.level})`,
      healthScore.factors.length > 0 ? `Factors: ${healthScore.factors.join(', ')}` : '',
      '',
      '🔍 Pattern Analysis:',
      `- Unique error patterns: ${patterns.frequentErrors.size}`,
      `- Active time periods: ${patterns.timePatterns.size}`,
      `- Different sources: ${patterns.sourcePatterns.size}`,
      '',
      '💡 Recommendations:',
      ...recommendations.map(rec => `- ${rec}`),
    ].filter(Boolean);

    return sections.join('\n');
  }
}

```
Page 11/43FirstPrevNextLast