#
tokens: 48147/50000 14/825 files (page 16/61)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 16 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&page={x} to view the full context.

# Directory Structure

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

# Files

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

```yaml
  1 | # ==================================================================================
  2 | # SFCC MCP Server - get_code_versions Tool YAML Tests (Full Mode)
  3 | # Tests code version retrieval functionality with SFCC credentials
  4 | # This tool provides code version management for deployment troubleshooting
  5 | # 
  6 | # Quick Test Commands:
  7 | # aegis "tests/mcp/yaml/get-code-versions.full-mode.test.mcp.yml" --config "aegis.config.with-dw.json" --verbose
  8 | # aegis query get_code_versions '{}' --config "aegis.config.with-dw.json"
  9 | # ==================================================================================
 10 | 
 11 | description: "get_code_versions tool full mode tests - Core functionality validation"
 12 | 
 13 | tests:
 14 |   # ==================================================================================
 15 |   # TOOL AVAILABILITY TESTS
 16 |   # ==================================================================================
 17 |   - it: "should list get_code_versions tool in full mode"
 18 |     request:
 19 |       jsonrpc: "2.0"
 20 |       id: "tool-availability-full"
 21 |       method: "tools/list"
 22 |       params: {}
 23 |     expect:
 24 |       response:
 25 |         jsonrpc: "2.0"
 26 |         id: "tool-availability-full"
 27 |         result:
 28 |           tools:
 29 |             match:arrayElements:
 30 |               match:partial:
 31 |                 name: "match:type:string"
 32 |                 description: "match:type:string"
 33 |           match:extractField: "tools.*.name"
 34 |           value: "match:arrayContains:get_code_versions"
 35 |       stderr: "toBeEmpty"
 36 | 
 37 |   - it: "should have correct tool definition in full mode"
 38 |     request:
 39 |       jsonrpc: "2.0"
 40 |       id: "tool-definition-full"
 41 |       method: "tools/list"
 42 |       params: {}
 43 |     expect:
 44 |       response:
 45 |         jsonrpc: "2.0"
 46 |         id: "tool-definition-full"
 47 |         result:
 48 |           tools: "match:arrayContains:name:get_code_versions"
 49 |       stderr: "toBeEmpty"
 50 | 
 51 |   # ==================================================================================
 52 |   # BASIC FUNCTIONALITY TESTS
 53 |   # ==================================================================================
 54 |   - it: "should retrieve code versions with default parameters"
 55 |     request:
 56 |       jsonrpc: "2.0"
 57 |       id: "code-versions-default"
 58 |       method: "tools/call"
 59 |       params:
 60 |         name: "get_code_versions"
 61 |         arguments: {}
 62 |     expect:
 63 |       response:
 64 |         jsonrpc: "2.0"
 65 |         id: "code-versions-default"
 66 |         result:
 67 |           content:
 68 |             match:arrayElements:
 69 |               type: "text"
 70 |               text: "match:regex:[\\s\\S]*code_version_result[\\s\\S]*"
 71 |           isError: false
 72 |       stderr: "toBeEmpty"
 73 |     performance:
 74 |       maxResponseTime: "2000ms"
 75 | 
 76 |   # ==================================================================================
 77 |   # PARAMETER VALIDATION TESTS
 78 |   # ==================================================================================
 79 |   - it: "should accept empty arguments object"
 80 |     request:
 81 |       jsonrpc: "2.0"
 82 |       id: "empty-args"
 83 |       method: "tools/call"
 84 |       params:
 85 |         name: "get_code_versions"
 86 |         arguments: {}
 87 |     expect:
 88 |       response:
 89 |         jsonrpc: "2.0"
 90 |         id: "empty-args"
 91 |         result:
 92 |           content:
 93 |             match:arrayElements:
 94 |               match:partial:
 95 |                 type: "text"
 96 |           isError: false
 97 |       stderr: "toBeEmpty"
 98 |     performance:
 99 |       maxResponseTime: "2000ms"
100 | 
101 |   - it: "should ignore extra parameters gracefully"
102 |     request:
103 |       jsonrpc: "2.0"
104 |       id: "extra-params"
105 |       method: "tools/call"
106 |       params:
107 |         name: "get_code_versions"
108 |         arguments:
109 |           extraParam: "should be ignored"
110 |           anotherParam: 123
111 |     expect:
112 |       response:
113 |         jsonrpc: "2.0"
114 |         id: "extra-params"
115 |         result:
116 |           content:
117 |             match:arrayElements:
118 |               match:partial:
119 |                 type: "text"
120 |           isError: false
121 |       stderr: "toBeEmpty"
122 |     performance:
123 |       maxResponseTime: "2000ms"
124 | 
125 |   # ==================================================================================
126 |   # RESPONSE CONTENT VALIDATION
127 |   # ==================================================================================
128 |   - it: "should return properly formatted JSON content"
129 |     request:
130 |       jsonrpc: "2.0"
131 |       id: "json-format"
132 |       method: "tools/call"
133 |       params:
134 |         name: "get_code_versions"
135 |         arguments: {}
136 |     expect:
137 |       response:
138 |         jsonrpc: "2.0"
139 |         id: "json-format"
140 |         result:
141 |           content:
142 |             match:arrayElements:
143 |               type: "text"
144 |               text: "match:regex:[\\s\\S]*\\{[\\s\\S]*\\}[\\s\\S]*"
145 |           isError: false
146 |       stderr: "toBeEmpty"
147 | 
148 |   - it: "should include essential code version fields"
149 |     request:
150 |       jsonrpc: "2.0"
151 |       id: "essential-fields"
152 |       method: "tools/call"
153 |       params:
154 |         name: "get_code_versions"
155 |         arguments: {}
156 |     expect:
157 |       response:
158 |         jsonrpc: "2.0"
159 |         id: "essential-fields"
160 |         result:
161 |           content:
162 |             match:arrayElements:
163 |               type: "text"
164 |               text: "match:regex:[\\s\\S]*_type[\\s\\S]*code_version_result[\\s\\S]*"
165 |           isError: false
166 |       stderr: "toBeEmpty"
167 | 
168 |   - it: "should include data array with code version objects"
169 |     request:
170 |       jsonrpc: "2.0"
171 |       id: "data-array"
172 |       method: "tools/call"
173 |       params:
174 |         name: "get_code_versions"
175 |         arguments: {}
176 |     expect:
177 |       response:
178 |         jsonrpc: "2.0"
179 |         id: "data-array"
180 |         result:
181 |           content:
182 |             match:arrayElements:
183 |               type: "text"
184 |               text: "match:regex:[\\s\\S]*data[\\s\\S]*\\[[\\s\\S]*\\][\\s\\S]*"
185 |           isError: false
186 |       stderr: "toBeEmpty"
187 | 
188 |   - it: "should include code version metadata"
189 |     request:
190 |       jsonrpc: "2.0"
191 |       id: "version-metadata"
192 |       method: "tools/call"
193 |       params:
194 |         name: "get_code_versions"
195 |         arguments: {}
196 |     expect:
197 |       response:
198 |         jsonrpc: "2.0"
199 |         id: "version-metadata"
200 |         result:
201 |           content:
202 |             match:arrayElements:
203 |               type: "text"
204 |               text: "match:regex:[\\s\\S]*(?:id)[\\s\\S]*(?:active)[\\s\\S]*"
205 |           isError: false
206 |       stderr: "toBeEmpty"
207 | 
208 |   # ==================================================================================
209 |   # PERFORMANCE TESTS
210 |   # ==================================================================================
211 |   - it: "should respond within acceptable time limit"
212 |     request:
213 |       jsonrpc: "2.0"
214 |       id: "performance-test"
215 |       method: "tools/call"
216 |       params:
217 |         name: "get_code_versions"
218 |         arguments: {}
219 |     expect:
220 |       response:
221 |         jsonrpc: "2.0"
222 |         id: "performance-test"
223 |         result:
224 |           content:
225 |             match:arrayElements:
226 |               match:partial:
227 |                 type: "text"
228 |           isError: false
229 |       stderr: "toBeEmpty"
230 |     performance:
231 |       maxResponseTime: "2000ms"
232 | 
233 |   - it: "should maintain consistent performance on repeat calls"
234 |     request:
235 |       jsonrpc: "2.0"
236 |       id: "performance-consistency"
237 |       method: "tools/call"
238 |       params:
239 |         name: "get_code_versions"
240 |         arguments: {}
241 |     expect:
242 |       response:
243 |         jsonrpc: "2.0"
244 |         id: "performance-consistency"
245 |         result:
246 |           content:
247 |             match:arrayElements:
248 |               match:partial:
249 |                 type: "text"
250 |           isError: false
251 |       stderr: "toBeEmpty"
252 |     performance:
253 |       maxResponseTime: "2000ms"
254 | 
255 |   # ==================================================================================
256 |   # ERROR HANDLING TESTS (Edge Cases)
257 |   # ==================================================================================
258 |   - it: "should return validation error for null arguments"
259 |     request:
260 |       jsonrpc: "2.0"
261 |       id: "null-args"
262 |       method: "tools/call"
263 |       params:
264 |         name: "get_code_versions"
265 |         arguments: null
266 |     expect:
267 |       response:
268 |         jsonrpc: "2.0"
269 |         id: "null-args"
270 |         error:
271 |           code: "match:type:number"
272 |           message: "match:type:string"
273 |       stderr: "toBeEmpty"
```

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

```javascript
  1 | /**
  2 |  * WebDAV Route Handler
  3 |  * 
  4 |  * Handles WebDAV requests including PROPFIND for directory listings 
  5 |  * and GET requests for file content with range support.
  6 |  * Modular implementation following single responsibility principle.
  7 |  */
  8 | 
  9 | const express = require('express');
 10 | const fs = require('fs');
 11 | const path = require('path');
 12 | const { URL } = require('url');
 13 | const {
 14 |     generateFileXmlResponse,
 15 |     generateDirectoryXmlResponse,
 16 |     generateErrorXmlResponse,
 17 |     parseDepthHeader
 18 | } = require('../utils/webdav-xml');
 19 | 
 20 | class WebDAVRouteHandler {
 21 |     constructor(config) {
 22 |         this.config = config;
 23 |         this.webdavConfig = config.getWebdavConfig();
 24 |         this.router = express.Router();
 25 |         this.setupRoutes();
 26 |     }
 27 | 
 28 |     setupRoutes() {
 29 |         // Handle PROPFIND method (custom method support)
 30 |         this.router.use((req, res, next) => {
 31 |             if (req.method === 'PROPFIND') {
 32 |                 this.handlePropfind(req, res);
 33 |             } else {
 34 |                 next();
 35 |             }
 36 |         });
 37 | 
 38 |         // WebDAV base path routes
 39 |         this.router.get(`${this.webdavConfig.basePath}/Logs/*`, (req, res) => {
 40 |             this.handleGet(req, res);
 41 |         });
 42 | 
 43 |         // Direct logs path routes (for backward compatibility)
 44 |         this.router.get('/Logs/*', (req, res) => {
 45 |             this.handleGet(req, res);
 46 |         });
 47 | 
 48 |         // Root logs directory
 49 |         this.router.get('/Logs', (req, res) => {
 50 |             this.handleGet(req, res);
 51 |         });
 52 |     }
 53 | 
 54 |     /**
 55 |      * Map URL path to file system path
 56 |      */
 57 |     mapUrlToFilePath(urlPath) {
 58 |         // Handle SFCC WebDAV path structure
 59 |         if (urlPath.startsWith(this.webdavConfig.basePath)) {
 60 |             // Remove the WebDAV base path and map to our mock structure
 61 |             let relativePath = urlPath.replace(this.webdavConfig.basePath, '');
 62 |             // Convert uppercase Logs to lowercase logs for file system, but remove the leading /Logs since it's already in logsPath
 63 |             relativePath = relativePath.replace(/^\/Logs/, '');
 64 |             return path.join(this.webdavConfig.logsPath, relativePath);
 65 |         }
 66 |         
 67 |         // Fallback: treat as direct path to logs (for backward compatibility)
 68 |         let relativePath = urlPath.replace(/^\/+/, '');
 69 |         // Convert uppercase Logs to lowercase logs for file system, but remove the leading Logs since it's already in logsPath
 70 |         relativePath = relativePath.replace(/^Logs/, '');
 71 |         return path.join(this.webdavConfig.logsPath, relativePath);
 72 |     }
 73 | 
 74 |     /**
 75 |      * Handle GET requests for file content
 76 |      */
 77 |     handleGet(req, res) {
 78 |         try {
 79 |             const urlPath = decodeURIComponent(req.path);
 80 |             const fsPath = this.mapUrlToFilePath(urlPath);
 81 | 
 82 |             if (this.config.isDevMode) {
 83 |                 console.log(`[WebDAV] GET ${urlPath} -> ${fsPath}`);
 84 |             }
 85 | 
 86 |             if (!fs.existsSync(fsPath)) {
 87 |                 return res.status(404).send('Not Found');
 88 |             }
 89 | 
 90 |             const stats = fs.statSync(fsPath);
 91 |             if (stats.isDirectory()) {
 92 |                 return res.status(404).send('Not Found');
 93 |             }
 94 | 
 95 |             // Handle range requests for partial content
 96 |             const rangeHeader = req.headers.range;
 97 |             if (rangeHeader) {
 98 |                 return this.handleRangeRequest(req, res, fsPath, stats);
 99 |             }
100 | 
101 |             // Normal full file request
102 |             res.set({
103 |                 'Content-Type': 'text/plain',
104 |                 'Content-Length': stats.size,
105 |                 'Accept-Ranges': 'bytes'
106 |             });
107 |             
108 |             const stream = fs.createReadStream(fsPath);
109 |             stream.pipe(res);
110 | 
111 |         } catch (error) {
112 |             console.error('[WebDAV] Error handling GET request:', error);
113 |             res.status(500).send('Internal Server Error');
114 |         }
115 |     }
116 | 
117 |     /**
118 |      * Handle range requests for partial content
119 |      */
120 |     handleRangeRequest(req, res, fsPath, stats) {
121 |         try {
122 |             const rangeHeader = req.headers.range;
123 |             const fileSize = stats.size;
124 |             
125 |             if (this.config.isDevMode) {
126 |                 console.log(`[WebDAV] Range request: ${rangeHeader} for file ${fsPath} (size: ${fileSize})`);
127 |             }
128 | 
129 |             // Parse range header (e.g., "bytes=0-499" or "bytes=500-999")
130 |             const rangeMatch = rangeHeader.match(/bytes=(\d+)-(\d*)/);
131 |             if (!rangeMatch) {
132 |                 res.set('Content-Range', `bytes */${fileSize}`);
133 |                 return res.status(416).send('Range Not Satisfiable');
134 |             }
135 | 
136 |             const start = parseInt(rangeMatch[1], 10);
137 |             const end = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : fileSize - 1;
138 | 
139 |             // Validate range
140 |             if (start >= fileSize || end >= fileSize || start > end) {
141 |                 res.set('Content-Range', `bytes */${fileSize}`);
142 |                 return res.status(416).send('Range Not Satisfiable');
143 |             }
144 | 
145 |             const contentLength = end - start + 1;
146 | 
147 |             if (this.config.isDevMode) {
148 |                 console.log(`[WebDAV] Range: ${start}-${end}, Content-Length: ${contentLength}`);
149 |             }
150 | 
151 |             // Send partial content response
152 |             res.set({
153 |                 'Content-Type': 'text/plain',
154 |                 'Content-Length': contentLength,
155 |                 'Content-Range': `bytes ${start}-${end}/${fileSize}`,
156 |                 'Accept-Ranges': 'bytes'
157 |             });
158 | 
159 |             res.status(206); // Partial Content
160 |             const stream = fs.createReadStream(fsPath, { start, end });
161 |             stream.pipe(res);
162 | 
163 |         } catch (error) {
164 |             console.error('[WebDAV] Error handling range request:', error);
165 |             res.status(500).send('Internal Server Error');
166 |         }
167 |     }
168 | 
169 |     /**
170 |      * Handle PROPFIND requests for directory listings
171 |      */
172 |     handlePropfind(req, res) {
173 |         try {
174 |             const urlPath = decodeURIComponent(req.path);
175 |             const fsPath = this.mapUrlToFilePath(urlPath);
176 | 
177 |             if (this.config.isDevMode) {
178 |                 console.log(`[WebDAV] PROPFIND ${urlPath} -> ${fsPath}`);
179 |             }
180 | 
181 |             if (!fs.existsSync(fsPath)) {
182 |                 if (this.config.isDevMode) {
183 |                     console.log(`[WebDAV] File not found: ${fsPath}`);
184 |                 }
185 |                 return res.status(404).send('Not Found');
186 |             }
187 | 
188 |             const stats = fs.statSync(fsPath);
189 |             
190 |             if (this.config.isDevMode) {
191 |                 console.log(`[WebDAV] File stats: size=${stats.size}, isFile=${stats.isFile()}, isDirectory=${stats.isDirectory()}`);
192 |             }
193 |             
194 |             // Handle PROPFIND for individual files
195 |             if (stats.isFile()) {
196 |                 const xml = generateFileXmlResponse(urlPath, stats);
197 | 
198 |                 if (this.config.isDevMode) {
199 |                     console.log(`[WebDAV] Returning file PROPFIND response for ${urlPath}`);
200 |                 }
201 | 
202 |                 res.set('Content-Type', 'application/xml');
203 |                 return res.status(207).send(xml);
204 |             }
205 | 
206 |             // Handle PROPFIND for directories
207 |             if (!stats.isDirectory()) {
208 |                 return res.status(404).send('Not Found');
209 |             }
210 | 
211 |             // Read directory contents
212 |             const itemNames = fs.readdirSync(fsPath);
213 |             const items = itemNames.map(name => {
214 |                 const itemPath = path.join(fsPath, name);
215 |                 const itemStats = fs.statSync(itemPath);
216 |                 return { name, stats: itemStats };
217 |             });
218 | 
219 |             const xml = generateDirectoryXmlResponse(urlPath, items, stats);
220 | 
221 |             res.set('Content-Type', 'application/xml');
222 |             res.status(207).send(xml);
223 | 
224 |         } catch (error) {
225 |             console.error(`[WebDAV] Error handling PROPFIND request for ${req.path}:`, error);
226 |             res.status(500).send('Internal Server Error');
227 |         }
228 |     }
229 | 
230 |     /**
231 |      * Get the configured router
232 |      */
233 |     getRouter() {
234 |         return this.router;
235 |     }
236 | }
237 | 
238 | module.exports = WebDAVRouteHandler;
```

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

```typescript
  1 | /**
  2 |  * Log file reading operations with range request support
  3 |  */
  4 | 
  5 | import type { WebDAVClient } from 'webdav';
  6 | import { Logger } from '../../utils/logger.js';
  7 | import { LOG_CONSTANTS } from './log-constants.js';
  8 | import type { FileReadOptions } from './log-types.js';
  9 | 
 10 | export class LogFileReader {
 11 |   private logger: Logger;
 12 |   private webdavClient: WebDAVClient;
 13 | 
 14 |   constructor(webdavClient: WebDAVClient, logger: Logger) {
 15 |     this.webdavClient = webdavClient;
 16 |     this.logger = logger;
 17 |   }
 18 | 
 19 |   /**
 20 |    * Get the last portion of a file using range requests to avoid loading huge files
 21 |    *
 22 |    * @param filename - The file path to read
 23 |    * @param options - Read options including maxBytes
 24 |    * @returns Promise<string> - The file content as a string
 25 |    */
 26 |   async getFileContentsTail(
 27 |     filename: string,
 28 |     options: FileReadOptions = {},
 29 |   ): Promise<string> {
 30 |     const { maxBytes = LOG_CONSTANTS.DEFAULT_TAIL_BYTES } = options;
 31 | 
 32 |     this.logger.debug(`Reading file tail: ${filename} (maxBytes: ${maxBytes})`);
 33 | 
 34 |     try {
 35 |       // First, try to get file info to determine the file size
 36 |       this.logger.debug(`Attempting stat for file: ${filename}`);
 37 |       const stat = await this.webdavClient.stat(filename);
 38 |       this.logger.debug(`Stat successful for ${filename}:`, stat);
 39 |       const fileSize = (stat as any).size;
 40 | 
 41 |       if (!fileSize || fileSize <= maxBytes) {
 42 |         // File is small enough or size unknown, just get the whole file
 43 |         this.logger.debug(`File ${filename} is small (${fileSize} bytes), reading full content`);
 44 |         return await this.getFullFileContents(filename);
 45 |       }
 46 | 
 47 |       // File is large, get only the last portion using range request
 48 |       this.logger.debug(`File ${filename} is large (${fileSize} bytes), using range request for last ${maxBytes} bytes`);
 49 |       const startByte = fileSize - maxBytes;
 50 |       return await this.getRangeFileContents(filename, startByte, fileSize - 1);
 51 | 
 52 |     } catch (statError) {
 53 |       const error = statError as Error;
 54 |       this.logger.warn(`Failed to get file stats for ${filename}, falling back to full file. Error:`, {
 55 |         message: error.message,
 56 |         name: error.name,
 57 |         code: (error as any).code,
 58 |         status: (error as any).status,
 59 |       });
 60 |       return await this.getFullFileContents(filename);
 61 |     }
 62 |   }
 63 | 
 64 |   /**
 65 |    * Read the full contents of a file
 66 |    */
 67 |   private async getFullFileContents(filename: string): Promise<string> {
 68 |     const content = await this.webdavClient.getFileContents(filename, { format: 'text' });
 69 |     return content as string;
 70 |   }
 71 | 
 72 |   /**
 73 |    * Read from the beginning of a file with optional size limit
 74 |    *
 75 |    * @param filename - The file path to read
 76 |    * @param maxBytes - Maximum number of bytes to read from the beginning
 77 |    * @returns Promise<string> - The file content as a string (truncated if needed)
 78 |    */
 79 |   async getFileContentsHead(
 80 |     filename: string,
 81 |     maxBytes?: number,
 82 |   ): Promise<string> {
 83 |     this.logger.debug(`Reading file head: ${filename} (maxBytes: ${maxBytes ?? 'unlimited'})`);
 84 | 
 85 |     if (!maxBytes) {
 86 |       // No size limit, read full file
 87 |       return await this.getFullFileContents(filename);
 88 |     }
 89 | 
 90 |     try {
 91 |       // First, try to get file info to determine the file size
 92 |       this.logger.debug(`Attempting stat for file: ${filename}`);
 93 |       const stat = await this.webdavClient.stat(filename);
 94 |       this.logger.debug(`Stat successful for ${filename}:`, stat);
 95 |       const fileSize = (stat as any).size;
 96 | 
 97 |       if (!fileSize || fileSize <= maxBytes) {
 98 |         // File is small enough or size unknown, just get the whole file
 99 |         this.logger.debug(`File ${filename} is small (${fileSize} bytes), reading full content`);
100 |         return await this.getFullFileContents(filename);
101 |       }
102 | 
103 |       // File is large, get only the first portion using range request
104 |       this.logger.debug(`File ${filename} is large (${fileSize} bytes), using range request for first ${maxBytes} bytes`);
105 |       return await this.getRangeFileContents(filename, 0, maxBytes - 1);
106 | 
107 |     } catch (statError) {
108 |       const error = statError as Error;
109 |       this.logger.warn(`Failed to get file stats for ${filename}, falling back to full file with truncation. Error:`, {
110 |         message: error.message,
111 |         name: error.name,
112 |         code: (error as any).code,
113 |         status: (error as any).status,
114 |       });
115 |       // Fallback to reading full file and truncating
116 |       const content = await this.getFullFileContents(filename);
117 |       if (content.length > maxBytes) {
118 |         this.logger.debug(`Truncating full file content from ${content.length} to ${maxBytes} characters`);
119 |         return content.substring(0, maxBytes);
120 |       }
121 |       return content;
122 |     }
123 |   }
124 | 
125 |   /**
126 |    * Read a range of bytes from a file using streaming
127 |    *
128 |    * @param filename - The file path to read
129 |    * @param start - Start byte position (0-based)
130 |    * @param end - End byte position (inclusive)
131 |    * @returns Promise<string> - The file content as a string
132 |    */
133 |   private async getRangeFileContents(
134 |     filename: string,
135 |     start: number,
136 |     end: number,
137 |   ): Promise<string> {
138 |     return new Promise((resolve, reject) => {
139 |       try {
140 |         const stream = this.webdavClient.createReadStream(filename, {
141 |           range: { start, end },
142 |         });
143 | 
144 |         const chunks: Buffer[] = [];
145 | 
146 |         stream.on('data', (chunk: Buffer) => {
147 |           chunks.push(chunk);
148 |         });
149 | 
150 |         stream.on('end', () => {
151 |           const content = Buffer.concat(chunks).toString('utf-8');
152 |           this.logger.debug(`Successfully read ${content.length} characters from range request (${start}-${end})`);
153 |           resolve(content);
154 |         });
155 | 
156 |         stream.on('error', (error: any) => {
157 |           this.logger.warn(`Failed to read range ${start}-${end} for ${filename}, falling back to full file:`, error);
158 |           // Fallback to getting the full file
159 |           this.getFullFileContents(filename)
160 |             .then((content: string) => {
161 |               // Truncate to the requested range if possible
162 |               const requestedLength = end - start + 1;
163 |               if (start === 0 && content.length > requestedLength) {
164 |                 // Reading from start, truncate
165 |                 resolve(content.substring(0, requestedLength));
166 |               } else {
167 |                 // Reading from end or full file is smaller, return what we have
168 |                 resolve(content);
169 |               }
170 |             })
171 |             .catch((fallbackError: any) => reject(fallbackError));
172 |         });
173 |       } catch (error) {
174 |         // If createReadStream fails, fall back to full file
175 |         this.logger.warn(`Failed to create read stream for ${filename}, falling back to full file:`, error);
176 |         this.getFullFileContents(filename)
177 |           .then((content: string) => {
178 |             const requestedLength = end - start + 1;
179 |             if (start === 0 && content.length > requestedLength) {
180 |               resolve(content.substring(0, requestedLength));
181 |             } else {
182 |               resolve(content);
183 |             }
184 |           })
185 |           .catch((fallbackError: any) => reject(fallbackError));
186 |       }
187 |     });
188 |   }
189 | 
190 |   /**
191 |    * Read multiple files with tail optimization
192 |    */
193 |   async readMultipleFiles(
194 |     filenames: string[],
195 |     options: FileReadOptions = {},
196 |   ): Promise<Map<string, string>> {
197 |     const results = new Map<string, string>();
198 | 
199 |     for (const filename of filenames) {
200 |       try {
201 |         const content = await this.getFileContentsTail(filename, options);
202 |         results.set(filename, content);
203 |       } catch (error) {
204 |         this.logger.error(`Error reading file ${filename}:`, error);
205 |         // Continue processing other files even if one fails
206 |       }
207 |     }
208 | 
209 |     return results;
210 |   }
211 | 
212 |   /**
213 |    * Check if a file exists and get its basic metadata
214 |    */
215 |   async getFileInfo(filename: string): Promise<{ exists: boolean; size?: number; lastmod?: string }> {
216 |     try {
217 |       const stat = await this.webdavClient.stat(filename);
218 |       return {
219 |         exists: true,
220 |         size: (stat as any).size,
221 |         lastmod: (stat as any).lastmod,
222 |       };
223 |     } catch {
224 |       return { exists: false };
225 |     }
226 |   }
227 | }
228 | 
```

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

```markdown
  1 | # Instructions for Creating a Salesforce B2C Commerce (SFRA) Cartridge
  2 | 
  3 | This document provides instructions to create, configure, and deploy a new custom cartridge for Salesforce B2C Commerce using the Storefront Reference Architecture (SFRA).
  4 | 
  5 | **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.
  6 | 
  7 | ## 1. Core Principles
  8 | 
  9 | **Cartridge:** A cartridge is a self-contained module for code and data. It is the fundamental unit for extending functionality.
 10 | 
 11 | **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.
 12 | 
 13 | **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.
 14 | 
 15 | **Example Path:** `app_custom_mybrand:app_storefront_base`
 16 | 
 17 | ## 2. Prerequisites
 18 | 
 19 | - **Git Client:** Installed and configured.
 20 | - **Node.js:** Version 18 is recommended for compatibility.
 21 | - **SFCC Sandbox:** Access to a sandbox instance, including Business Manager credentials.
 22 | - **GitHub Access:** Ability to clone SalesforceCommerceCloud repositories.
 23 | 
 24 | ## 3. Environment Setup
 25 | 
 26 | Create a parent project directory.
 27 | 
 28 | Clone the following repositories as siblings inside the parent directory:
 29 | 
 30 | Note: This step is not necessary unless specifically asked for. You should only clone these if the user requests it, for most
 31 | projects, there is only a need for the new cartridge that is being built - but not the entire storefront reference architecture.
 32 | 
 33 | ```bash
 34 | # Contains the base cartridge (app_storefront_base)
 35 | git clone [email protected]:SalesforceCommerceCloud/storefront-reference-architecture.git
 36 | 
 37 | # Contains build and deployment scripts
 38 | git clone [email protected]:SalesforceCommerceCloud/sgmf-scripts.git
 39 | ```
 40 | 
 41 | Install dependencies:
 42 | 
 43 | ```bash
 44 | cd storefront-reference-architecture
 45 | npm install
 46 | ```
 47 | 
 48 | ## 4. Cartridge File Structure
 49 | 
 50 | A new cartridge should be created using the provided scaffolding tool. The core structure is as follows:
 51 | 
 52 | ```
 53 | package.json
 54 | .eslintrc.json
 55 | .eslintignore
 56 | .stylelintrc.json
 57 | .gitignore
 58 | README.md
 59 | dw.json    
 60 | webpack.config.js           
 61 | cartridges/
 62 | └── plugin_my_custom_cartridge/
 63 |     └── cartridge/
 64 |         ├── client/             
 65 |         │   └── default/
 66 |         │       ├── js/
 67 |         │       └── scss/
 68 |         ├── controllers/        
 69 |         ├── models/             
 70 |         ├── scripts/            
 71 |         └── templates/       
 72 |             └── default/
 73 | ```
 74 | 
 75 | Optional but common directories:
 76 | 
 77 | - `cartridge/forms/default/`: XML form definitions.
 78 | - `cartridge/services/`: Definitions for external web service integrations.
 79 | - `cartridge/scripts/jobs/`: Scripts for automated tasks scheduled in Business Manager.
 80 | - `cartridge/properties/`: Localization string files (.properties).
 81 | 
 82 | ## 5. Creating a New Cartridge
 83 | 
 84 | ### Step 1: Generate the Cartridge Structure
 85 | 
 86 | !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.
 87 | 
 88 | **Using MCP Server (Recommended)**
 89 | Use the `generate_cartridge_structure` tool from this MCP server to automatically create the cartridge structure:
 90 | 
 91 | ```json
 92 | {
 93 |   "cartridgeName": "plugin_my_custom_cartridge",
 94 |   "targetPath": "/path/to/your/project",
 95 |   "fullProjectSetup": true
 96 | }
 97 | ```
 98 | 
 99 | This tool will:
100 | - Create all necessary configuration files (package.json, webpack.config.js, etc.)
101 | - Set up the complete cartridge structure with proper organization
102 | - Ensure the cartridge is created exactly where needed in your project structure
103 | - Generate all required directories and files for a fully functional cartridge
104 | 
105 | ## 6. "Hello, World" Example
106 | 
107 | ### Step 1: Navigate to Your Project
108 | 
109 | After creating the cartridge structure, navigate to your project directory:
110 | 
111 | ```bash
112 | # If you kept the subdirectory structure:
113 | cd plugin_my_custom_cartridge
114 | 
115 | # If you moved files to root level, you're already in the right place
116 | ```
117 | 
118 | ### Step 2: Create the Controller
119 | 
120 | **File:** `cartridges/plugin_my_custom_cartridge/cartridge/controllers/Hello.js`
121 | 
122 | ```javascript
123 | 'use strict';
124 | 
125 | var server = require('server');
126 | 
127 | // URL: /Hello-Show
128 | server.get('Show', function (req, res, next) {
129 |     res.render('hello/helloTemplate', {
130 |         message: 'Hello from a custom cartridge!'
131 |     });
132 |     next();
133 | });
134 | 
135 | module.exports = server.exports();
136 | ```
137 | 
138 | ### Step 3: Create the ISML Template
139 | 
140 | **File:** `cartridges/plugin_my_custom_cartridge/cartridge/templates/default/hello/helloTemplate.isml`
141 | 
142 | ```html
143 | <iscontent type="text/html" charset="UTF-8" compact="true"/>
144 | <isdecorate template="common/layout/page">
145 |     <isreplace name="main">
146 |         <div class="container">
147 |             <h1>Custom Page</h1>
148 |             <p>${pdict.message}</p>
149 |         </div>
150 |     </isreplace>
151 | </isdecorate>
152 | ```
153 | 
154 | ### Step 4: Update Deployment Configuration
155 | 
156 | Ensure your `dw.json` file has the correct sandbox credentials (this file is automatically generated but needs your specific sandbox details).
157 | 
158 | ## 7. Deployment and Registration
159 | 
160 | ### Step 1: Upload the Cartridge
161 | 
162 | From the project root, run:
163 | 
164 | ```bash
165 | npm run uploadCartridge plugin_my_custom_cartridge
166 | ```
167 | 
168 | For continuous development, use `npm run watch`.
169 | 
170 | ## 8. Naming Conventions & Best Practices
171 | 
172 | **Cartridge Naming:** Use standard prefixes.
173 | 
174 | - `app_custom_*`: Site-specific customizations.
175 | - `int_*`: Third-party integrations.
176 | - `bm_*`: Business Manager extensions.
177 | - `plugin_*`: Reusable SFRA feature extensions.
178 | 
179 | **File Naming:** Controllers use PascalCase.js. Other JS files use camelCase.js.
180 | 
181 | **NEVER modify app_storefront_base or other base/plugin cartridges directly.**
182 | 
183 | **Extend, Don't Replace:** Use server.append() or server.prepend() to extend controllers. Avoid server.replace().
184 | 
185 | **Logic-less Templates:** Keep business logic out of ISML files. Use models to prepare data.
186 | 
187 | **Security:** Protect all state-changing POST requests with CSRF tokens. Properly encode all user-provided output to prevent XSS.
188 | 
189 | **Localization:** Use Resource.msg() in templates to fetch text from .properties files.
190 | 
191 | ## 9. MCP Integration Workflow
192 | 
193 | When using this MCP server for cartridge development, follow this enhanced workflow:
194 | 
195 | 1. **Generate Cartridge Structure**: Use the `generate_cartridge_structure` tool to create the initial cartridge with all necessary files and configurations
196 | 2. **Get Best Practices**: Use `get_best_practice_guide` with "sfra_controllers" for controller development patterns
197 | 3. **Search Documentation**: Use `search_sfcc_classes` and `get_sfcc_class_info` for API reference
198 | 4. **Validate Implementation**: Use `search_best_practices` to ensure your code follows security and performance guidelines
199 | 5. **Debug Issues**: Use log analysis tools to troubleshoot deployment or runtime issues
200 | 6. **Handle Deployment Issues**: If new cartridge features (jobs, SCAPI endpoints, hooks) don't appear after upload:
201 |    - **Check Code Versions**: Use `get_code_versions` tool to see available versions
202 |    - **Activate Version**: Use `activate_code_version` tool to ensure proper registration
203 |    - **Alternative**: Manually switch code versions in Business Manager (Administration > Site Development > Code Deployment)
204 | 
205 | ### Common Deployment Troubleshooting
206 | 
207 | **Issue**: Custom jobs or SCAPI endpoints not visible after cartridge deployment
208 | **Solution**: 
209 | 1. Use MCP `get_code_versions` tool to check available code versions
210 | 2. Use MCP `activate_code_version` tool to switch to the uploaded version
211 | 3. Verify registration in logs using log analysis tools
212 | 
213 | **Issue**: Hooks not taking effect after deployment
214 | **Solution**: Follow the same code version activation process above
215 | 
216 | 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.
217 | 
```

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

```markdown
  1 | ## Package: TopLevel
  2 | 
  3 | # Class Number
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - Number
  9 | 
 10 | ## Description
 11 | 
 12 | 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.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### EPSILON
 17 | 
 18 | **Type:** Number
 19 | 
 20 | 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.
 21 | 
 22 | ### MAX_SAFE_INTEGER
 23 | 
 24 | **Type:** Number
 25 | 
 26 | The maximum safe integer in JavaScript.
 27 | 
 28 | ### MAX_VALUE
 29 | 
 30 | **Type:** Number
 31 | 
 32 | The largest representable Number.
 33 | 
 34 | ### MIN_SAFE_INTEGER
 35 | 
 36 | **Type:** Number
 37 | 
 38 | The minimum safe integer in JavaScript.
 39 | 
 40 | ### MIN_VALUE
 41 | 
 42 | **Type:** Number
 43 | 
 44 | The smallest representable Number.
 45 | 
 46 | ### NEGATIVE_INFINITY
 47 | 
 48 | **Type:** Number
 49 | 
 50 | Negative infinite value; returned on overflow;
 51 | 
 52 | ### POSITIVE_INFINITY
 53 | 
 54 | **Type:** Number
 55 | 
 56 | Negative infinite value; returned on overflow;
 57 | 
 58 | ## Properties
 59 | 
 60 | ## Constructor Summary
 61 | 
 62 | Number() Constructs a Number with value 0
 63 | 
 64 | Number(num : Number) Constructs a new Number using the specified Number.
 65 | 
 66 | Number(value : String) Constructs a Number using the specified value.
 67 | 
 68 | ## Method Summary
 69 | 
 70 | ### isFinite
 71 | 
 72 | **Signature:** `static isFinite(value : Object) : boolean`
 73 | 
 74 | Determines whether the passed value is a finite number.
 75 | 
 76 | ### isInteger
 77 | 
 78 | **Signature:** `static isInteger(value : Object) : boolean`
 79 | 
 80 | Determines whether the passed value is an integer number.
 81 | 
 82 | ### isNaN
 83 | 
 84 | **Signature:** `static isNaN(value : Object) : boolean`
 85 | 
 86 | Determines whether the passed value is NaN.
 87 | 
 88 | ### isSafeInteger
 89 | 
 90 | **Signature:** `static isSafeInteger(value : Object) : boolean`
 91 | 
 92 | Determines whether the passed value is a safe integer number.
 93 | 
 94 | ### parseFloat
 95 | 
 96 | **Signature:** `static parseFloat(s : String) : Number`
 97 | 
 98 | Parses a String into an float Number.
 99 | 
100 | ### parseInt
101 | 
102 | **Signature:** `static parseInt(s : String) : Number`
103 | 
104 | Parses a String into an integer Number.
105 | 
106 | ### parseInt
107 | 
108 | **Signature:** `static parseInt(s : String, radix : Number) : Number`
109 | 
110 | Parses a String into an integer Number using the specified radix.
111 | 
112 | ### toExponential
113 | 
114 | **Signature:** `toExponential() : String`
115 | 
116 | Converts this Number to a String using exponential notation.
117 | 
118 | ### toExponential
119 | 
120 | **Signature:** `toExponential(digits : Number) : String`
121 | 
122 | Converts this Number to a String using exponential notation with the specified number of digits after the decimal place.
123 | 
124 | ### toFixed
125 | 
126 | **Signature:** `toFixed() : String`
127 | 
128 | Converts a Number to a String that contains a no fractional part.
129 | 
130 | ### toFixed
131 | 
132 | **Signature:** `toFixed(digits : Number) : String`
133 | 
134 | Converts a Number to a String that contains a specified number of digits after the decimal place.
135 | 
136 | ### toLocaleString
137 | 
138 | **Signature:** `toLocaleString() : String`
139 | 
140 | Converts this Number to a String using local number formatting conventions.
141 | 
142 | ### toPrecision
143 | 
144 | **Signature:** `toPrecision(precision : Number) : String`
145 | 
146 | Converts a Number to a String using the specified number of significant digits.
147 | 
148 | ### toString
149 | 
150 | **Signature:** `toString() : String`
151 | 
152 | A String representation of this Number.
153 | 
154 | ### toString
155 | 
156 | **Signature:** `toString(radix : Number) : String`
157 | 
158 | Converts the number into a string using the specified radix (base).
159 | 
160 | ## Constructor Detail
161 | 
162 | ## Method Detail
163 | 
164 | ## Method Details
165 | 
166 | ### isFinite
167 | 
168 | **Signature:** `static isFinite(value : Object) : boolean`
169 | 
170 | **Description:** Determines whether the passed value is a finite number.
171 | 
172 | **API Versioned:**
173 | 
174 | From version 21.2.
175 | 
176 | **Parameters:**
177 | 
178 | - `value`: The value to check.
179 | 
180 | **Returns:**
181 | 
182 | true if the passed value is a finite number, else false.
183 | 
184 | ---
185 | 
186 | ### isInteger
187 | 
188 | **Signature:** `static isInteger(value : Object) : boolean`
189 | 
190 | **Description:** Determines whether the passed value is an integer number.
191 | 
192 | **API Versioned:**
193 | 
194 | From version 21.2.
195 | 
196 | **Parameters:**
197 | 
198 | - `value`: The value to check.
199 | 
200 | **Returns:**
201 | 
202 | true if the passed value is a finite integer number, else false.
203 | 
204 | ---
205 | 
206 | ### isNaN
207 | 
208 | **Signature:** `static isNaN(value : Object) : boolean`
209 | 
210 | **Description:** Determines whether the passed value is NaN. Unlike the global function, the passed parameter is not converted to number before doing the check.
211 | 
212 | **API Versioned:**
213 | 
214 | From version 21.2.
215 | 
216 | **Parameters:**
217 | 
218 | - `value`: The value to check.
219 | 
220 | **Returns:**
221 | 
222 | true if the passed value is the NaN number value, else false.
223 | 
224 | ---
225 | 
226 | ### isSafeInteger
227 | 
228 | **Signature:** `static isSafeInteger(value : Object) : boolean`
229 | 
230 | **Description:** Determines whether the passed value is a safe integer number.
231 | 
232 | **API Versioned:**
233 | 
234 | From version 21.2.
235 | 
236 | **Parameters:**
237 | 
238 | - `value`: The value to check.
239 | 
240 | **Returns:**
241 | 
242 | true if the passed value is a safe integer number, else false.
243 | 
244 | ---
245 | 
246 | ### parseFloat
247 | 
248 | **Signature:** `static parseFloat(s : String) : Number`
249 | 
250 | **Description:** Parses a String into an float Number.
251 | 
252 | **API Versioned:**
253 | 
254 | From version 21.2.
255 | 
256 | **Parameters:**
257 | 
258 | - `s`: the String to parse.
259 | 
260 | **Returns:**
261 | 
262 | Returns the float as a Number.
263 | 
264 | ---
265 | 
266 | ### parseInt
267 | 
268 | **Signature:** `static parseInt(s : String) : Number`
269 | 
270 | **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.
271 | 
272 | **API Versioned:**
273 | 
274 | From version 21.2.
275 | 
276 | **Parameters:**
277 | 
278 | - `s`: the String to parse.
279 | 
280 | **Returns:**
281 | 
282 | Returns the integer as a Number.
283 | 
284 | ---
285 | 
286 | ### parseInt
287 | 
288 | **Signature:** `static parseInt(s : String, radix : Number) : Number`
289 | 
290 | **Description:** Parses a String into an integer Number using the specified radix.
291 | 
292 | **API Versioned:**
293 | 
294 | From version 21.2.
295 | 
296 | **Parameters:**
297 | 
298 | - `s`: the String to parse.
299 | - `radix`: the radix to use.
300 | 
301 | **Returns:**
302 | 
303 | Returns the integer as a Number.
304 | 
305 | ---
306 | 
307 | ### toExponential
308 | 
309 | **Signature:** `toExponential() : String`
310 | 
311 | **Description:** Converts this Number to a String using exponential notation.
312 | 
313 | **Returns:**
314 | 
315 | a String using exponential notation.
316 | 
317 | ---
318 | 
319 | ### toExponential
320 | 
321 | **Signature:** `toExponential(digits : Number) : String`
322 | 
323 | **Description:** Converts this Number to a String using exponential notation with the specified number of digits after the decimal place.
324 | 
325 | **Parameters:**
326 | 
327 | - `digits`: the number of digits after the decimal place.
328 | 
329 | **Returns:**
330 | 
331 | a String using exponential notation with the specified number of digits after the decimal place.
332 | 
333 | ---
334 | 
335 | ### toFixed
336 | 
337 | **Signature:** `toFixed() : String`
338 | 
339 | **Description:** Converts a Number to a String that contains a no fractional part.
340 | 
341 | **Returns:**
342 | 
343 | a String representation of the number
344 | 
345 | ---
346 | 
347 | ### toFixed
348 | 
349 | **Signature:** `toFixed(digits : Number) : String`
350 | 
351 | **Description:** Converts a Number to a String that contains a specified number of digits after the decimal place.
352 | 
353 | **Parameters:**
354 | 
355 | - `digits`: the number of digits after the decimal place.
356 | 
357 | **Returns:**
358 | 
359 | a String that contains a specified number of digits after the decimal place.
360 | 
361 | ---
362 | 
363 | ### toLocaleString
364 | 
365 | **Signature:** `toLocaleString() : String`
366 | 
367 | **Description:** Converts this Number to a String using local number formatting conventions. The current implementation actually only returns the same as toString().
368 | 
369 | **Returns:**
370 | 
371 | a String using local number formatting conventions.
372 | 
373 | ---
374 | 
375 | ### toPrecision
376 | 
377 | **Signature:** `toPrecision(precision : Number) : String`
378 | 
379 | **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.
380 | 
381 | **Parameters:**
382 | 
383 | - `precision`: the precision to use when converting the Number to a String.
384 | 
385 | **Returns:**
386 | 
387 | a String using the specified number of significant digits.
388 | 
389 | ---
390 | 
391 | ### toString
392 | 
393 | **Signature:** `toString() : String`
394 | 
395 | **Description:** A String representation of this Number.
396 | 
397 | **Returns:**
398 | 
399 | a String representation of this Number.
400 | 
401 | ---
402 | 
403 | ### toString
404 | 
405 | **Signature:** `toString(radix : Number) : String`
406 | 
407 | **Description:** Converts the number into a string using the specified radix (base).
408 | 
409 | **Parameters:**
410 | 
411 | - `radix`: the radix to use.
412 | 
413 | **Returns:**
414 | 
415 | a String representation of this Number.
416 | 
417 | ---
```

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

```markdown
  1 | ## Package: dw.svc
  2 | 
  3 | # Class HTTPService
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.svc.Service
  9 |   - dw.svc.HTTPService
 10 | 
 11 | ## Description
 12 | 
 13 | 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.
 14 | 
 15 | ## Properties
 16 | 
 17 | ### authentication
 18 | 
 19 | **Type:** String
 20 | 
 21 | The authentication type.
 22 | 
 23 | ### cachingTTL
 24 | 
 25 | **Type:** Number
 26 | 
 27 | The caching time to live value.
 28 | 
 29 | ### client
 30 | 
 31 | **Type:** HTTPClient (Read Only)
 32 | 
 33 | The underlying HTTP client object.
 34 | 
 35 | ### encoding
 36 | 
 37 | **Type:** String
 38 | 
 39 | The request body encoding to declare.
 40 | 
 41 | ### hostNameVerification
 42 | 
 43 | **Type:** boolean
 44 | 
 45 | Determines whether host name verification is enabled.
 46 | 
 47 | ### identity
 48 | 
 49 | **Type:** KeyRef
 50 | 
 51 | Gets the identity used for mutual TLS (mTLS).
 52 | 
 53 | ### outFile
 54 | 
 55 | **Type:** File
 56 | 
 57 | The output file, or null if there is none.
 58 | 
 59 | ### requestMethod
 60 | 
 61 | **Type:** String
 62 | 
 63 | The request method.
 64 | 
 65 | ## Constructor Summary
 66 | 
 67 | ## Method Summary
 68 | 
 69 | ### addHeader
 70 | 
 71 | **Signature:** `addHeader(name : String, val : String) : HTTPService`
 72 | 
 73 | Adds an HTTP Header.
 74 | 
 75 | ### addParam
 76 | 
 77 | **Signature:** `addParam(name : String, val : String) : HTTPService`
 78 | 
 79 | Adds a query parameter that will be appended to the URL.
 80 | 
 81 | ### getAuthentication
 82 | 
 83 | **Signature:** `getAuthentication() : String`
 84 | 
 85 | Returns the authentication type.
 86 | 
 87 | ### getCachingTTL
 88 | 
 89 | **Signature:** `getCachingTTL() : Number`
 90 | 
 91 | Returns the caching time to live value.
 92 | 
 93 | ### getClient
 94 | 
 95 | **Signature:** `getClient() : HTTPClient`
 96 | 
 97 | Returns the underlying HTTP client object.
 98 | 
 99 | ### getEncoding
100 | 
101 | **Signature:** `getEncoding() : String`
102 | 
103 | Returns the request body encoding to declare.
104 | 
105 | ### getHostNameVerification
106 | 
107 | **Signature:** `getHostNameVerification() : boolean`
108 | 
109 | Determines whether host name verification is enabled.
110 | 
111 | ### getIdentity
112 | 
113 | **Signature:** `getIdentity() : KeyRef`
114 | 
115 | Gets the identity used for mutual TLS (mTLS).
116 | 
117 | ### getOutFile
118 | 
119 | **Signature:** `getOutFile() : File`
120 | 
121 | Returns the output file, or null if there is none.
122 | 
123 | ### getRequestMethod
124 | 
125 | **Signature:** `getRequestMethod() : String`
126 | 
127 | Returns the request method.
128 | 
129 | ### setAuthentication
130 | 
131 | **Signature:** `setAuthentication(authentication : String) : HTTPService`
132 | 
133 | Sets the type of authentication.
134 | 
135 | ### setCachingTTL
136 | 
137 | **Signature:** `setCachingTTL(ttl : Number) : HTTPService`
138 | 
139 | Enables caching for GET requests.
140 | 
141 | ### setEncoding
142 | 
143 | **Signature:** `setEncoding(encoding : String) : HTTPService`
144 | 
145 | Sets the encoding of the request body (if any).
146 | 
147 | ### setHostNameVerification
148 | 
149 | **Signature:** `setHostNameVerification(enable : boolean) : HTTPService`
150 | 
151 | Sets whether certificate host name verification is enabled.
152 | 
153 | ### setIdentity
154 | 
155 | **Signature:** `setIdentity(keyRef : KeyRef) : HTTPService`
156 | 
157 | Sets the identity (private key) to use when mutual TLS (mTLS) is configured.
158 | 
159 | ### setOutFile
160 | 
161 | **Signature:** `setOutFile(outFile : File) : HTTPService`
162 | 
163 | Sets the output file in which to write the HTTP response body.
164 | 
165 | ### setRequestMethod
166 | 
167 | **Signature:** `setRequestMethod(requestMethod : String) : HTTPService`
168 | 
169 | Sets the HTTP request method.
170 | 
171 | ## Method Detail
172 | 
173 | ## Method Details
174 | 
175 | ### addHeader
176 | 
177 | **Signature:** `addHeader(name : String, val : String) : HTTPService`
178 | 
179 | **Description:** Adds an HTTP Header.
180 | 
181 | **Parameters:**
182 | 
183 | - `name`: Header name.
184 | - `val`: Header value.
185 | 
186 | **Returns:**
187 | 
188 | this HTTP Service.
189 | 
190 | ---
191 | 
192 | ### addParam
193 | 
194 | **Signature:** `addParam(name : String, val : String) : HTTPService`
195 | 
196 | **Description:** Adds a query parameter that will be appended to the URL.
197 | 
198 | **Parameters:**
199 | 
200 | - `name`: Parameter name.
201 | - `val`: Parameter value.
202 | 
203 | **Returns:**
204 | 
205 | this HTTP Service.
206 | 
207 | ---
208 | 
209 | ### getAuthentication
210 | 
211 | **Signature:** `getAuthentication() : String`
212 | 
213 | **Description:** Returns the authentication type.
214 | 
215 | **Returns:**
216 | 
217 | Authentication type.
218 | 
219 | ---
220 | 
221 | ### getCachingTTL
222 | 
223 | **Signature:** `getCachingTTL() : Number`
224 | 
225 | **Description:** Returns the caching time to live value.
226 | 
227 | **Returns:**
228 | 
229 | The caching time to live value in seconds.
230 | 
231 | **See Also:**
232 | 
233 | setCachingTTL(Number)
234 | 
235 | ---
236 | 
237 | ### getClient
238 | 
239 | **Signature:** `getClient() : HTTPClient`
240 | 
241 | **Description:** Returns the underlying HTTP client object.
242 | 
243 | **Returns:**
244 | 
245 | HTTP client object.
246 | 
247 | ---
248 | 
249 | ### getEncoding
250 | 
251 | **Signature:** `getEncoding() : String`
252 | 
253 | **Description:** Returns the request body encoding to declare.
254 | 
255 | **Returns:**
256 | 
257 | Request encoding.
258 | 
259 | ---
260 | 
261 | ### getHostNameVerification
262 | 
263 | **Signature:** `getHostNameVerification() : boolean`
264 | 
265 | **Description:** Determines whether host name verification is enabled.
266 | 
267 | **Returns:**
268 | 
269 | true if verification is enabled, false otherwise
270 | 
271 | ---
272 | 
273 | ### getIdentity
274 | 
275 | **Signature:** `getIdentity() : KeyRef`
276 | 
277 | **Description:** Gets the identity used for mutual TLS (mTLS).
278 | 
279 | **Returns:**
280 | 
281 | Reference to the private key, or null if not configured
282 | 
283 | ---
284 | 
285 | ### getOutFile
286 | 
287 | **Signature:** `getOutFile() : File`
288 | 
289 | **Description:** Returns the output file, or null if there is none.
290 | 
291 | **Returns:**
292 | 
293 | Output file or null.
294 | 
295 | ---
296 | 
297 | ### getRequestMethod
298 | 
299 | **Signature:** `getRequestMethod() : String`
300 | 
301 | **Description:** Returns the request method.
302 | 
303 | **Returns:**
304 | 
305 | HTTP Request method.
306 | 
307 | ---
308 | 
309 | ### setAuthentication
310 | 
311 | **Signature:** `setAuthentication(authentication : String) : HTTPService`
312 | 
313 | **Description:** Sets the type of authentication. Valid values include "BASIC" and "NONE". The default value is BASIC.
314 | 
315 | **Parameters:**
316 | 
317 | - `authentication`: Type of authentication.
318 | 
319 | **Returns:**
320 | 
321 | this HTTP Service.
322 | 
323 | ---
324 | 
325 | ### setCachingTTL
326 | 
327 | **Signature:** `setCachingTTL(ttl : Number) : HTTPService`
328 | 
329 | **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.
330 | 
331 | **Parameters:**
332 | 
333 | - `ttl`: The time to live for the cached content in seconds. A value of 0 disables caching.
334 | 
335 | **See Also:**
336 | 
337 | HTTPClient.enableCaching(Number)
338 | 
339 | ---
340 | 
341 | ### setEncoding
342 | 
343 | **Signature:** `setEncoding(encoding : String) : HTTPService`
344 | 
345 | **Description:** Sets the encoding of the request body (if any). The default value is UTF-8.
346 | 
347 | **Parameters:**
348 | 
349 | - `encoding`: Encoding of the request body.
350 | 
351 | **Returns:**
352 | 
353 | this HTTP Service.
354 | 
355 | ---
356 | 
357 | ### setHostNameVerification
358 | 
359 | **Signature:** `setHostNameVerification(enable : boolean) : HTTPService`
360 | 
361 | **Description:** Sets whether certificate host name verification is enabled. The default value is true. Set it to false to disable host name verification.
362 | 
363 | **Parameters:**
364 | 
365 | - `enable`: true to enable host name verification or false to disable it.
366 | 
367 | **Returns:**
368 | 
369 | this HTTP Service.
370 | 
371 | ---
372 | 
373 | ### setIdentity
374 | 
375 | **Signature:** `setIdentity(keyRef : KeyRef) : HTTPService`
376 | 
377 | **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.
378 | 
379 | **Parameters:**
380 | 
381 | - `keyRef`: Reference to the private key
382 | 
383 | ---
384 | 
385 | ### setOutFile
386 | 
387 | **Signature:** `setOutFile(outFile : File) : HTTPService`
388 | 
389 | **Description:** Sets the output file in which to write the HTTP response body. The default behavior is to not write a file.
390 | 
391 | **Parameters:**
392 | 
393 | - `outFile`: Output file, or null to disable.
394 | 
395 | **Returns:**
396 | 
397 | this HTTP Service.
398 | 
399 | ---
400 | 
401 | ### setRequestMethod
402 | 
403 | **Signature:** `setRequestMethod(requestMethod : String) : HTTPService`
404 | 
405 | **Description:** Sets the HTTP request method. Valid values include GET, PUT, POST, and DELETE. The default value is POST.
406 | 
407 | **Parameters:**
408 | 
409 | - `requestMethod`: HTTP request method.
410 | 
411 | **Returns:**
412 | 
413 | this HTTP Service.
414 | 
415 | ---
```

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

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class Return
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.Extensible
  9 |   - dw.order.AbstractItemCtnr
 10 |     - dw.order.Return
 11 | 
 12 | ## Description
 13 | 
 14 | 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.
 15 | 
 16 | ## Constants
 17 | 
 18 | ### ORDERBY_ITEMID
 19 | 
 20 | **Type:** Object
 21 | 
 22 | Sorting by item id. Use with method getItems() as an argument to method FilteringCollection.sort(Object).
 23 | 
 24 | ### ORDERBY_ITEMPOSITION
 25 | 
 26 | **Type:** Object
 27 | 
 28 | Sorting by the position of the related oder item. Use with method getItems() as an argument to method FilteringCollection.sort(Object).
 29 | 
 30 | ### ORDERBY_UNSORTED
 31 | 
 32 | **Type:** Object
 33 | 
 34 | Unsorted , as it is. Use with method getItems() as an argument to method FilteringCollection.sort(Object).
 35 | 
 36 | ### QUALIFIER_PRODUCTITEMS
 37 | 
 38 | **Type:** Object
 39 | 
 40 | Selects the product items. Use with method getItems() as an argument to method FilteringCollection.select(Object).
 41 | 
 42 | ### QUALIFIER_SERVICEITEMS
 43 | 
 44 | **Type:** Object
 45 | 
 46 | Selects for the service items. Use with method getItems() as an argument to method FilteringCollection.select(Object).
 47 | 
 48 | ### STATUS_COMPLETED
 49 | 
 50 | **Type:** String = "COMPLETED"
 51 | 
 52 | Constant for Return Status COMPLETED
 53 | 
 54 | ### STATUS_NEW
 55 | 
 56 | **Type:** String = "NEW"
 57 | 
 58 | Constant for Return Status NEW
 59 | 
 60 | ## Properties
 61 | 
 62 | ### invoice
 63 | 
 64 | **Type:** Invoice (Read Only)
 65 | 
 66 | Returns null or the previously created Invoice.
 67 | 
 68 | ### invoiceNumber
 69 | 
 70 | **Type:** String (Read Only)
 71 | 
 72 | Returns null or the invoice-number.
 73 | 
 74 | ### items
 75 | 
 76 | **Type:** FilteringCollection (Read Only)
 77 | 
 78 | The ReturnItems contained in the Return, created with method
 79 |  createItem(String).
 80 | 
 81 |  
 82 |  This FilteringCollection can be sorted / filtered using:
 83 |  
 84 |  FilteringCollection.sort(Object) with ORDERBY_ITEMID
 85 |  FilteringCollection.sort(Object) with
 86 |  ORDERBY_ITEMPOSITION
 87 |  FilteringCollection.sort(Object) with ORDERBY_UNSORTED
 88 |  FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS
 89 |  FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS
 90 | 
 91 | ### note
 92 | 
 93 | **Type:** String
 94 | 
 95 | A note for the return.
 96 | 
 97 | ### returnCase
 98 | 
 99 | **Type:** ReturnCase (Read Only)
100 | 
101 | The ReturnCase with which this Return is associated. The ReturnCase
102 |  may represent an RMA (return merchandise authorization).
103 | 
104 | ### returnNumber
105 | 
106 | **Type:** String (Read Only)
107 | 
108 | The return number identifying this return.
109 | 
110 | ### status
111 | 
112 | **Type:** EnumValue
113 | 
114 | Gets the return status.
115 |  
116 |  Possible values are STATUS_NEW, STATUS_COMPLETED.
117 | 
118 | ## Constructor Summary
119 | 
120 | ## Method Summary
121 | 
122 | ### createInvoice
123 | 
124 | **Signature:** `createInvoice() : Invoice`
125 | 
126 | Creates a new Invoice based on this Return.
127 | 
128 | ### createInvoice
129 | 
130 | **Signature:** `createInvoice(invoiceNumber : String) : Invoice`
131 | 
132 | Creates a new Invoice based on this Return.
133 | 
134 | ### createItem
135 | 
136 | **Signature:** `createItem(returnCaseItemID : String) : ReturnItem`
137 | 
138 | Create a ReturnItem based on a ReturnCaseItem.
139 | 
140 | ### getInvoice
141 | 
142 | **Signature:** `getInvoice() : Invoice`
143 | 
144 | Returns null or the previously created Invoice.
145 | 
146 | ### getInvoiceNumber
147 | 
148 | **Signature:** `getInvoiceNumber() : String`
149 | 
150 | Returns null or the invoice-number.
151 | 
152 | ### getItems
153 | 
154 | **Signature:** `getItems() : FilteringCollection`
155 | 
156 | Returns the ReturnItems contained in the Return, created with method createItem(String).
157 | 
158 | ### getNote
159 | 
160 | **Signature:** `getNote() : String`
161 | 
162 | A note for the return.
163 | 
164 | ### getReturnCase
165 | 
166 | **Signature:** `getReturnCase() : ReturnCase`
167 | 
168 | Returns the ReturnCase with which this Return is associated.
169 | 
170 | ### getReturnNumber
171 | 
172 | **Signature:** `getReturnNumber() : String`
173 | 
174 | The return number identifying this return.
175 | 
176 | ### getStatus
177 | 
178 | **Signature:** `getStatus() : EnumValue`
179 | 
180 | Gets the return status.
181 | 
182 | ### setNote
183 | 
184 | **Signature:** `setNote(note : String) : void`
185 | 
186 | Sets a note for the return.
187 | 
188 | ### setStatus
189 | 
190 | **Signature:** `setStatus(statusName : String) : void`
191 | 
192 | Sets the return status.
193 | 
194 | ## Method Detail
195 | 
196 | ## Method Details
197 | 
198 | ### createInvoice
199 | 
200 | **Signature:** `createInvoice() : Invoice`
201 | 
202 | **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.
203 | 
204 | **Returns:**
205 | 
206 | new invoice
207 | 
208 | ---
209 | 
210 | ### createInvoice
211 | 
212 | **Signature:** `createInvoice(invoiceNumber : String) : Invoice`
213 | 
214 | **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.
215 | 
216 | **Parameters:**
217 | 
218 | - `invoiceNumber`: the invoice-number to use
219 | 
220 | **Returns:**
221 | 
222 | the new invoice
223 | 
224 | ---
225 | 
226 | ### createItem
227 | 
228 | **Signature:** `createItem(returnCaseItemID : String) : ReturnItem`
229 | 
230 | **Description:** Create a ReturnItem based on a ReturnCaseItem.
231 | 
232 | **Parameters:**
233 | 
234 | - `returnCaseItemID`: the id of the return case item
235 | 
236 | **Returns:**
237 | 
238 | the created return item
239 | 
240 | ---
241 | 
242 | ### getInvoice
243 | 
244 | **Signature:** `getInvoice() : Invoice`
245 | 
246 | **Description:** Returns null or the previously created Invoice.
247 | 
248 | **Returns:**
249 | 
250 | null or the previously created invoice.
251 | 
252 | **See Also:**
253 | 
254 | createInvoice(String)
255 | 
256 | ---
257 | 
258 | ### getInvoiceNumber
259 | 
260 | **Signature:** `getInvoiceNumber() : String`
261 | 
262 | **Description:** Returns null or the invoice-number.
263 | 
264 | **Returns:**
265 | 
266 | null or the previously created invoice.
267 | 
268 | **See Also:**
269 | 
270 | createInvoice(String)
271 | 
272 | ---
273 | 
274 | ### getItems
275 | 
276 | **Signature:** `getItems() : FilteringCollection`
277 | 
278 | **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
279 | 
280 | **Returns:**
281 | 
282 | the return items
283 | 
284 | **See Also:**
285 | 
286 | ReturnItem
287 | 
288 | ---
289 | 
290 | ### getNote
291 | 
292 | **Signature:** `getNote() : String`
293 | 
294 | **Description:** A note for the return.
295 | 
296 | **Returns:**
297 | 
298 | the note or null
299 | 
300 | ---
301 | 
302 | ### getReturnCase
303 | 
304 | **Signature:** `getReturnCase() : ReturnCase`
305 | 
306 | **Description:** Returns the ReturnCase with which this Return is associated. The ReturnCase may represent an RMA (return merchandise authorization).
307 | 
308 | **Returns:**
309 | 
310 | the return case
311 | 
312 | ---
313 | 
314 | ### getReturnNumber
315 | 
316 | **Signature:** `getReturnNumber() : String`
317 | 
318 | **Description:** The return number identifying this return.
319 | 
320 | **Returns:**
321 | 
322 | the return number
323 | 
324 | ---
325 | 
326 | ### getStatus
327 | 
328 | **Signature:** `getStatus() : EnumValue`
329 | 
330 | **Description:** Gets the return status. Possible values are STATUS_NEW, STATUS_COMPLETED.
331 | 
332 | **Returns:**
333 | 
334 | the status
335 | 
336 | ---
337 | 
338 | ### setNote
339 | 
340 | **Signature:** `setNote(note : String) : void`
341 | 
342 | **Description:** Sets a note for the return.
343 | 
344 | **Parameters:**
345 | 
346 | - `note`: the note
347 | 
348 | ---
349 | 
350 | ### setStatus
351 | 
352 | **Signature:** `setStatus(statusName : String) : void`
353 | 
354 | **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.
355 | 
356 | **Parameters:**
357 | 
358 | - `statusName`: the status
359 | 
360 | ---
```

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

```json
  1 | {
  2 |   "_v": "23.2",
  3 |   "_type": "object_attribute_definition_search_result",
  4 |   "count": 8,
  5 |   "hits": [
  6 |     {
  7 |       "_type": "object_attribute_definition",
  8 |       "_resource_state": "896b31874723fb22d8e7dca243b9b1c5c2de3a2a73645562d9af221a91318612",
  9 |       "creation_date": "2024-02-26T19:35:57.000Z",
 10 |       "effective_id": "c_ID",
 11 |       "externally_defined": false,
 12 |       "externally_managed": false,
 13 |       "id": "ID",
 14 |       "key": true,
 15 |       "last_modified": "2024-02-26T19:35:57.000Z",
 16 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/ID",
 17 |       "localizable": false,
 18 |       "mandatory": true,
 19 |       "min_length": 0,
 20 |       "multi_value_type": false,
 21 |       "order_required": false,
 22 |       "queryable": true,
 23 |       "read_only": false,
 24 |       "requires_encoding": false,
 25 |       "searchable": false,
 26 |       "set_value_type": false,
 27 |       "site_specific": false,
 28 |       "system": false,
 29 |       "value_type": "string",
 30 |       "visible": false
 31 |     },
 32 |     {
 33 |       "_type": "object_attribute_definition",
 34 |       "_resource_state": "69bff66e9e1774188891baa67e28861a73c716636b7a84ae2582c7b4b649c0bd",
 35 |       "creation_date": "2024-02-26T19:35:57.000Z",
 36 |       "display_name": {
 37 |         "default": "UUID"
 38 |       },
 39 |       "effective_id": "UUID",
 40 |       "externally_defined": false,
 41 |       "externally_managed": false,
 42 |       "field_length": 28,
 43 |       "id": "UUID",
 44 |       "key": false,
 45 |       "last_modified": "2024-02-26T19:35:57.000Z",
 46 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/UUID",
 47 |       "localizable": false,
 48 |       "mandatory": true,
 49 |       "min_length": 0,
 50 |       "multi_value_type": false,
 51 |       "order_required": false,
 52 |       "queryable": true,
 53 |       "read_only": true,
 54 |       "requires_encoding": false,
 55 |       "searchable": false,
 56 |       "set_value_type": false,
 57 |       "site_specific": false,
 58 |       "system": true,
 59 |       "value_type": "string",
 60 |       "visible": false
 61 |     },
 62 |     {
 63 |       "_type": "object_attribute_definition",
 64 |       "_resource_state": "7606845698773be81fe494069711f52bfb6092e0909e95948c944611fb259572",
 65 |       "creation_date": "2024-02-26T19:35:57.000Z",
 66 |       "description": {
 67 |         "default": "The ID of the component the history is of."
 68 |       },
 69 |       "display_name": {
 70 |         "default": "Component ID"
 71 |       },
 72 |       "effective_id": "c_componentId",
 73 |       "externally_defined": false,
 74 |       "externally_managed": false,
 75 |       "id": "componentId",
 76 |       "key": false,
 77 |       "last_modified": "2024-02-26T19:35:57.000Z",
 78 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/componentId",
 79 |       "localizable": false,
 80 |       "mandatory": false,
 81 |       "min_length": 0,
 82 |       "multi_value_type": false,
 83 |       "order_required": false,
 84 |       "queryable": true,
 85 |       "read_only": false,
 86 |       "requires_encoding": false,
 87 |       "searchable": false,
 88 |       "set_value_type": false,
 89 |       "site_specific": false,
 90 |       "system": false,
 91 |       "value_type": "string",
 92 |       "visible": false
 93 |     },
 94 |     {
 95 |       "_type": "object_attribute_definition",
 96 |       "_resource_state": "66393d94b5177d0c307d20e948fd3e18687767d72bdcb9913ecde08a312c6275",
 97 |       "creation_date": "2024-02-26T19:35:57.000Z",
 98 |       "display_name": {
 99 |         "default": "Creation Date"
100 |       },
101 |       "effective_id": "creationDate",
102 |       "externally_defined": false,
103 |       "externally_managed": false,
104 |       "id": "creationDate",
105 |       "key": false,
106 |       "last_modified": "2024-02-26T19:35:57.000Z",
107 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/creationDate",
108 |       "localizable": false,
109 |       "mandatory": true,
110 |       "min_length": 0,
111 |       "multi_value_type": false,
112 |       "order_required": false,
113 |       "queryable": true,
114 |       "read_only": true,
115 |       "requires_encoding": false,
116 |       "searchable": false,
117 |       "set_value_type": false,
118 |       "site_specific": false,
119 |       "system": true,
120 |       "value_type": "datetime",
121 |       "visible": false
122 |     },
123 |     {
124 |       "_type": "object_attribute_definition",
125 |       "_resource_state": "8476a3b0562d6aa0900126bab96ccc90e27d0508d604ed432d6757515594a098",
126 |       "creation_date": "2024-02-26T19:35:57.000Z",
127 |       "display_name": {
128 |         "default": "Last Modified"
129 |       },
130 |       "effective_id": "lastModified",
131 |       "externally_defined": false,
132 |       "externally_managed": false,
133 |       "id": "lastModified",
134 |       "key": false,
135 |       "last_modified": "2024-02-26T19:35:57.000Z",
136 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/lastModified",
137 |       "localizable": false,
138 |       "mandatory": true,
139 |       "min_length": 0,
140 |       "multi_value_type": false,
141 |       "order_required": false,
142 |       "queryable": true,
143 |       "read_only": true,
144 |       "requires_encoding": false,
145 |       "searchable": false,
146 |       "set_value_type": false,
147 |       "site_specific": false,
148 |       "system": true,
149 |       "value_type": "datetime",
150 |       "visible": false
151 |     },
152 |     {
153 |       "_type": "object_attribute_definition",
154 |       "_resource_state": "3081b438888efbc03c8bbcddfc033b9097ff6644a59023c047ab88ad6d46a1c3",
155 |       "creation_date": "2024-02-26T19:35:57.000Z",
156 |       "description": {
157 |         "default": "The locale of the value."
158 |       },
159 |       "display_name": {
160 |         "default": "Locale"
161 |       },
162 |       "effective_id": "c_locale",
163 |       "externally_defined": false,
164 |       "externally_managed": false,
165 |       "id": "locale",
166 |       "key": false,
167 |       "last_modified": "2024-02-26T19:35:57.000Z",
168 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/locale",
169 |       "localizable": false,
170 |       "mandatory": false,
171 |       "min_length": 0,
172 |       "multi_value_type": false,
173 |       "order_required": false,
174 |       "queryable": true,
175 |       "read_only": false,
176 |       "requires_encoding": false,
177 |       "searchable": false,
178 |       "set_value_type": false,
179 |       "site_specific": false,
180 |       "system": false,
181 |       "value_type": "string",
182 |       "visible": false
183 |     },
184 |     {
185 |       "_type": "object_attribute_definition",
186 |       "_resource_state": "383d2b5957098f09e256dfc26926790784b382c1d87a14426e1ef7a7e81271f4",
187 |       "creation_date": "2024-02-26T19:35:57.000Z",
188 |       "description": {
189 |         "default": "The user who has made the modification"
190 |       },
191 |       "display_name": {
192 |         "default": "User"
193 |       },
194 |       "effective_id": "c_user",
195 |       "externally_defined": false,
196 |       "externally_managed": false,
197 |       "id": "user",
198 |       "key": false,
199 |       "last_modified": "2024-02-26T19:35:57.000Z",
200 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/user",
201 |       "localizable": false,
202 |       "mandatory": false,
203 |       "min_length": 0,
204 |       "multi_value_type": false,
205 |       "order_required": false,
206 |       "queryable": true,
207 |       "read_only": false,
208 |       "requires_encoding": false,
209 |       "searchable": false,
210 |       "set_value_type": false,
211 |       "site_specific": false,
212 |       "system": false,
213 |       "value_type": "string",
214 |       "visible": false
215 |     },
216 |     {
217 |       "_type": "object_attribute_definition",
218 |       "_resource_state": "ee9469bc639a2cb37bb6d2419f8123397853f0ff40e69d211b665088ae322957",
219 |       "creation_date": "2024-02-26T19:35:57.000Z",
220 |       "description": {
221 |         "default": "The value"
222 |       },
223 |       "display_name": {
224 |         "default": "Value"
225 |       },
226 |       "effective_id": "c_value",
227 |       "externally_defined": false,
228 |       "externally_managed": false,
229 |       "id": "value",
230 |       "key": false,
231 |       "last_modified": "2024-02-26T19:35:57.000Z",
232 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/VersionHistory/attribute_definitions/value",
233 |       "localizable": false,
234 |       "mandatory": false,
235 |       "min_length": 0,
236 |       "multi_value_type": false,
237 |       "order_required": false,
238 |       "queryable": true,
239 |       "read_only": false,
240 |       "requires_encoding": false,
241 |       "searchable": false,
242 |       "set_value_type": false,
243 |       "site_specific": false,
244 |       "system": false,
245 |       "value_type": "string",
246 |       "visible": false
247 |     }
248 |   ],
249 |   "query": {
250 |     "match_all_query": {
251 |       "_type": "match_all_query"
252 |     }
253 |   },
254 |   "start": 0,
255 |   "total": 8
256 | }
```

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

```typescript
  1 | import { ConfigurationFactory } from '../src/config/configuration-factory';
  2 | import { SFCCConfig } from '../src/types/types';
  3 | import { writeFileSync, unlinkSync, existsSync, mkdirSync } from 'fs';
  4 | import { join } from 'path';
  5 | import { tmpdir } from 'os';
  6 | 
  7 | describe('ConfigurationFactory', () => {
  8 |   const testDir = join(tmpdir(), 'sfcc-config-factory-tests');
  9 | 
 10 |   beforeAll(() => {
 11 |     if (!existsSync(testDir)) {
 12 |       mkdirSync(testDir, { recursive: true });
 13 |     }
 14 |   });
 15 | 
 16 |   afterEach(() => {
 17 |     // Clean up test files
 18 |     try {
 19 |       const testFiles = ['valid-dw.json', 'invalid-dw.json', 'missing-dw.json'];
 20 |       testFiles.forEach(file => {
 21 |         const filePath = join(testDir, file);
 22 |         if (existsSync(filePath)) {
 23 |           unlinkSync(filePath);
 24 |         }
 25 |       });
 26 |       // eslint-disable-next-line @typescript-eslint/no-unused-vars
 27 |     } catch (error) {
 28 |       // Ignore cleanup errors
 29 |     }
 30 |   });
 31 | 
 32 |   describe('create', () => {
 33 |     it('should create configuration from dw.json file', () => {
 34 |       const dwJsonContent = {
 35 |         hostname: 'test-instance.demandware.net',
 36 |         username: 'testuser',
 37 |         password: 'testpass',
 38 |         'client-id': 'test-client-id',
 39 |         'client-secret': 'test-client-secret',
 40 |       };
 41 | 
 42 |       const testFile = join(testDir, 'valid-dw.json');
 43 |       writeFileSync(testFile, JSON.stringify(dwJsonContent, null, 2));
 44 | 
 45 |       const config = ConfigurationFactory.create({ dwJsonPath: testFile });
 46 | 
 47 |       expect(config.hostname).toBe('test-instance.demandware.net');
 48 |       expect(config.username).toBe('testuser');
 49 |       expect(config.password).toBe('testpass');
 50 |       expect(config.clientId).toBe('test-client-id');
 51 |       expect(config.clientSecret).toBe('test-client-secret');
 52 |     });
 53 | 
 54 |     it('should throw an error if dw.json file is invalid', () => {
 55 |       const testFile = join(testDir, 'invalid-dw.json');
 56 |       writeFileSync(testFile, '{ invalid json }');
 57 | 
 58 |       expect(() => {
 59 |         ConfigurationFactory.create({ dwJsonPath: testFile });
 60 |       }).toThrow(/Invalid JSON in configuration file:/);
 61 |     });
 62 | 
 63 |     it('should throw an error if dw.json file is missing', () => {
 64 |       const testFile = join(testDir, 'missing-dw.json');
 65 | 
 66 |       expect(() => {
 67 |         ConfigurationFactory.create({ dwJsonPath: testFile });
 68 |       }).toThrow(/dw\.json file not found at:/);
 69 |     });
 70 | 
 71 |     it('should create configuration from provided options', () => {
 72 |       const config = ConfigurationFactory.create({
 73 |         hostname: 'test-instance.demandware.net',
 74 |         username: 'testuser',
 75 |         password: 'testpass',
 76 |         clientId: 'test-client-id',
 77 |         clientSecret: 'test-client-secret',
 78 |         siteId: 'test-site',
 79 |       });
 80 | 
 81 |       expect(config.hostname).toBe('test-instance.demandware.net');
 82 |       expect(config.username).toBe('testuser');
 83 |       expect(config.password).toBe('testpass');
 84 |       expect(config.clientId).toBe('test-client-id');
 85 |       expect(config.clientSecret).toBe('test-client-secret');
 86 |       expect(config.siteId).toBe('test-site');
 87 |     });
 88 | 
 89 |     it('should allow configuration without credentials for local mode', () => {
 90 |       const config = ConfigurationFactory.create({});
 91 |       expect(config.hostname).toBe('');
 92 |       expect(config.username).toBeUndefined();
 93 |       expect(config.password).toBeUndefined();
 94 |       expect(config.clientId).toBeUndefined();
 95 |       expect(config.clientSecret).toBeUndefined();
 96 |     });
 97 | 
 98 |     it('should validate configuration and throw error for hostname without credentials', () => {
 99 |       expect(() => {
100 |         ConfigurationFactory.create({ hostname: 'test-instance.demandware.net' });
101 |       }).toThrow('When hostname is provided, either username/password or OAuth credentials (clientId/clientSecret) must be provided');
102 |     });
103 | 
104 |     it('should validate configuration and throw error for invalid hostname format', () => {
105 |       expect(() => {
106 |         ConfigurationFactory.create({
107 |           hostname: 'invalid@hostname!',
108 |           username: 'testuser',
109 |           password: 'testpass',
110 |         });
111 |       }).toThrow('Invalid hostname format in configuration');
112 |     });
113 | 
114 |     it('should validate configuration and allow hostname with basic auth', () => {
115 |       const config = ConfigurationFactory.create({
116 |         hostname: 'test-instance.demandware.net',
117 |         username: 'testuser',
118 |         password: 'testpass',
119 |       });
120 |       expect(config.hostname).toBe('test-instance.demandware.net');
121 |       expect(config.username).toBe('testuser');
122 |       expect(config.password).toBe('testpass');
123 |     });
124 | 
125 |     it('should validate configuration and allow hostname with OAuth', () => {
126 |       const config = ConfigurationFactory.create({
127 |         hostname: 'test-instance.demandware.net',
128 |         clientId: 'test-client-id',
129 |         clientSecret: 'test-client-secret',
130 |       });
131 |       expect(config.hostname).toBe('test-instance.demandware.net');
132 |       expect(config.clientId).toBe('test-client-id');
133 |       expect(config.clientSecret).toBe('test-client-secret');
134 |     });
135 | 
136 |     it('should validate configuration and allow localhost with port', () => {
137 |       const config = ConfigurationFactory.create({
138 |         hostname: 'localhost:3000',
139 |         username: 'testuser',
140 |         password: 'testpass',
141 |       });
142 |       expect(config.hostname).toBe('localhost:3000');
143 |       expect(config.username).toBe('testuser');
144 |       expect(config.password).toBe('testpass');
145 |     });
146 | 
147 |     it('should validate configuration and allow hostname with custom port', () => {
148 |       const config = ConfigurationFactory.create({
149 |         hostname: 'test-instance.demandware.net:8080',
150 |         clientId: 'test-client-id',
151 |         clientSecret: 'test-client-secret',
152 |       });
153 |       expect(config.hostname).toBe('test-instance.demandware.net:8080');
154 |       expect(config.clientId).toBe('test-client-id');
155 |       expect(config.clientSecret).toBe('test-client-secret');
156 |     });
157 |   });
158 | 
159 |   describe('getCapabilities', () => {
160 |     it('should return correct capabilities for basic auth', () => {
161 |       const config: SFCCConfig = {
162 |         hostname: 'test-instance.demandware.net',
163 |         username: 'testuser',
164 |         password: 'testpass',
165 |       };
166 | 
167 |       const capabilities = ConfigurationFactory.getCapabilities(config);
168 | 
169 |       expect(capabilities.canAccessLogs).toBe(true);
170 |       expect(capabilities.canAccessOCAPI).toBe(false);
171 |       expect(capabilities.canAccessWebDAV).toBe(true);
172 |       expect(capabilities.isLocalMode).toBe(false);
173 |     });
174 | 
175 |     it('should return correct capabilities for OAuth', () => {
176 |       const config: SFCCConfig = {
177 |         hostname: 'test-instance.demandware.net',
178 |         clientId: 'test-client-id',
179 |         clientSecret: 'test-client-secret',
180 |       };
181 | 
182 |       const capabilities = ConfigurationFactory.getCapabilities(config);
183 | 
184 |       expect(capabilities.canAccessLogs).toBe(true);
185 |       expect(capabilities.canAccessOCAPI).toBe(true);
186 |       expect(capabilities.canAccessWebDAV).toBe(true);
187 |       expect(capabilities.isLocalMode).toBe(false);
188 |     });
189 | 
190 |     it('should return correct capabilities for both basic auth and OAuth', () => {
191 |       const config: SFCCConfig = {
192 |         hostname: 'test-instance.demandware.net',
193 |         username: 'testuser',
194 |         password: 'testpass',
195 |         clientId: 'test-client-id',
196 |         clientSecret: 'test-client-secret',
197 |       };
198 | 
199 |       const capabilities = ConfigurationFactory.getCapabilities(config);
200 | 
201 |       expect(capabilities.canAccessLogs).toBe(true);
202 |       expect(capabilities.canAccessOCAPI).toBe(true);
203 |       expect(capabilities.canAccessWebDAV).toBe(true);
204 |       expect(capabilities.isLocalMode).toBe(false);
205 |     });
206 | 
207 |     it('should return local mode capabilities for empty config', () => {
208 |       const config: SFCCConfig = {};
209 | 
210 |       const capabilities = ConfigurationFactory.getCapabilities(config);
211 | 
212 |       expect(capabilities.canAccessLogs).toBe(false);
213 |       expect(capabilities.canAccessOCAPI).toBe(false);
214 |       expect(capabilities.canAccessWebDAV).toBe(false);
215 |       expect(capabilities.isLocalMode).toBe(true);
216 |     });
217 | 
218 |     it('should return local mode capabilities for config without hostname', () => {
219 |       const config: SFCCConfig = {
220 |         hostname: '',
221 |         username: undefined,
222 |         password: undefined,
223 |       };
224 | 
225 |       const capabilities = ConfigurationFactory.getCapabilities(config);
226 | 
227 |       expect(capabilities.canAccessLogs).toBe(false);
228 |       expect(capabilities.canAccessOCAPI).toBe(false);
229 |       expect(capabilities.canAccessWebDAV).toBe(false);
230 |       expect(capabilities.isLocalMode).toBe(true);
231 |     });
232 |   });
233 | });
234 | 
```

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

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class ReturnItem
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.Extensible
  9 |   - dw.order.AbstractItem
 10 |     - dw.order.ReturnItem
 11 | 
 12 | ## Description
 13 | 
 14 | 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.
 15 | 
 16 | ## Properties
 17 | 
 18 | ### basePrice
 19 | 
 20 | **Type:** Money (Read Only)
 21 | 
 22 | Price of a single unit before discount application.
 23 | 
 24 | ### note
 25 | 
 26 | **Type:** String
 27 | 
 28 | Return the note for this return item.
 29 | 
 30 | ### parentItem
 31 | 
 32 | **Type:** ReturnItem
 33 | 
 34 | Returns null or the parent item.
 35 | 
 36 | ### reasonCode
 37 | 
 38 | **Type:** EnumValue
 39 | 
 40 | The reason code for return item. The list of reason codes can be updated
 41 |  by updating meta-data for ReturnItem.
 42 | 
 43 | ### returnCaseItem
 44 | 
 45 | **Type:** ReturnCaseItem (Read Only)
 46 | 
 47 | The return case item related to this item. Should never return null.
 48 | 
 49 | ### returnedQuantity
 50 | 
 51 | **Type:** Quantity
 52 | 
 53 | The Quantity returned. This may return an N/A quantity.
 54 | 
 55 | ### returnNumber
 56 | 
 57 | **Type:** String (Read Only)
 58 | 
 59 | The mandatory returnNumber of the Return to which this item belongs.
 60 | 
 61 | ## Constructor Summary
 62 | 
 63 | ## Method Summary
 64 | 
 65 | ### addTaxItem
 66 | 
 67 | **Signature:** `addTaxItem(amount : Decimal, taxGroup : TaxGroup) : TaxItem`
 68 | 
 69 | Create a new tax-item and add to this item.
 70 | 
 71 | ### applyPriceRate
 72 | 
 73 | **Signature:** `applyPriceRate(factor : Decimal, divisor : Decimal, roundUp : boolean) : void`
 74 | 
 75 | 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.
 76 | 
 77 | ### getBasePrice
 78 | 
 79 | **Signature:** `getBasePrice() : Money`
 80 | 
 81 | Price of a single unit before discount application.
 82 | 
 83 | ### getNote
 84 | 
 85 | **Signature:** `getNote() : String`
 86 | 
 87 | Return the note for this return item.
 88 | 
 89 | ### getParentItem
 90 | 
 91 | **Signature:** `getParentItem() : ReturnItem`
 92 | 
 93 | Returns null or the parent item.
 94 | 
 95 | ### getReasonCode
 96 | 
 97 | **Signature:** `getReasonCode() : EnumValue`
 98 | 
 99 | Returns the reason code for return item.
100 | 
101 | ### getReturnCaseItem
102 | 
103 | **Signature:** `getReturnCaseItem() : ReturnCaseItem`
104 | 
105 | Returns the return case item related to this item.
106 | 
107 | ### getReturnedQuantity
108 | 
109 | **Signature:** `getReturnedQuantity() : Quantity`
110 | 
111 | The Quantity returned.
112 | 
113 | ### getReturnNumber
114 | 
115 | **Signature:** `getReturnNumber() : String`
116 | 
117 | The mandatory returnNumber of the Return to which this item belongs.
118 | 
119 | ### setNote
120 | 
121 | **Signature:** `setNote(note : String) : void`
122 | 
123 | Sets a note for this return item.
124 | 
125 | ### setParentItem
126 | 
127 | **Signature:** `setParentItem(parentItem : ReturnItem) : void`
128 | 
129 | Set a parent item.
130 | 
131 | ### setReasonCode
132 | 
133 | **Signature:** `setReasonCode(reasonCode : String) : void`
134 | 
135 | Set the reason code.
136 | 
137 | ### setReturnedQuantity
138 | 
139 | **Signature:** `setReturnedQuantity(quantity : Quantity) : void`
140 | 
141 | Set the Quantity returned.
142 | 
143 | ### setTaxBasis
144 | 
145 | **Signature:** `setTaxBasis(taxBasis : Money) : void`
146 | 
147 | Set the tax-basis price for this item.
148 | 
149 | ### setTaxItems
150 | 
151 | **Signature:** `setTaxItems(taxItems : Collection) : void`
152 | 
153 | Set the tax-items for this item.
154 | 
155 | ## Method Detail
156 | 
157 | ## Method Details
158 | 
159 | ### addTaxItem
160 | 
161 | **Signature:** `addTaxItem(amount : Decimal, taxGroup : TaxGroup) : TaxItem`
162 | 
163 | **Description:** Create a new tax-item and add to this item.
164 | 
165 | **Parameters:**
166 | 
167 | - `amount`: amount to assign to the tax-item
168 | - `taxGroup`: the TaxGroup to which the item belongs
169 | 
170 | **Returns:**
171 | 
172 | the new tax-item
173 | 
174 | ---
175 | 
176 | ### applyPriceRate
177 | 
178 | **Signature:** `applyPriceRate(factor : Decimal, divisor : Decimal, roundUp : boolean) : void`
179 | 
180 | **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
181 | 
182 | **Parameters:**
183 | 
184 | - `factor`: factor used to calculate rate
185 | - `divisor`: divisor used to calculate rate
186 | - `roundUp`: whether to round up or down on 0.5
187 | 
188 | **See Also:**
189 | 
190 | AbstractItem.getTaxBasis()
191 | AbstractItem.getTax()
192 | AbstractItem.getNetPrice()
193 | AbstractItem.getGrossPrice()
194 | TaxMgr.getTaxationPolicy()
195 | 
196 | ---
197 | 
198 | ### getBasePrice
199 | 
200 | **Signature:** `getBasePrice() : Money`
201 | 
202 | **Description:** Price of a single unit before discount application.
203 | 
204 | **Returns:**
205 | 
206 | Price of a single unit before discount application.
207 | 
208 | ---
209 | 
210 | ### getNote
211 | 
212 | **Signature:** `getNote() : String`
213 | 
214 | **Description:** Return the note for this return item.
215 | 
216 | **Returns:**
217 | 
218 | the note or null
219 | 
220 | ---
221 | 
222 | ### getParentItem
223 | 
224 | **Signature:** `getParentItem() : ReturnItem`
225 | 
226 | **Description:** Returns null or the parent item.
227 | 
228 | **Returns:**
229 | 
230 | null or the parent item.
231 | 
232 | ---
233 | 
234 | ### getReasonCode
235 | 
236 | **Signature:** `getReasonCode() : EnumValue`
237 | 
238 | **Description:** Returns the reason code for return item. The list of reason codes can be updated by updating meta-data for ReturnItem.
239 | 
240 | **Returns:**
241 | 
242 | the return reason code
243 | 
244 | ---
245 | 
246 | ### getReturnCaseItem
247 | 
248 | **Signature:** `getReturnCaseItem() : ReturnCaseItem`
249 | 
250 | **Description:** Returns the return case item related to this item. Should never return null.
251 | 
252 | **Returns:**
253 | 
254 | the return case item related to this item
255 | 
256 | ---
257 | 
258 | ### getReturnedQuantity
259 | 
260 | **Signature:** `getReturnedQuantity() : Quantity`
261 | 
262 | **Description:** The Quantity returned. This may return an N/A quantity.
263 | 
264 | **Returns:**
265 | 
266 | the quantity returned, may be N/A
267 | 
268 | ---
269 | 
270 | ### getReturnNumber
271 | 
272 | **Signature:** `getReturnNumber() : String`
273 | 
274 | **Description:** The mandatory returnNumber of the Return to which this item belongs.
275 | 
276 | **Returns:**
277 | 
278 | the returnNumber of the Return to which this item belongs
279 | 
280 | ---
281 | 
282 | ### setNote
283 | 
284 | **Signature:** `setNote(note : String) : void`
285 | 
286 | **Description:** Sets a note for this return item.
287 | 
288 | **Parameters:**
289 | 
290 | - `note`: the note for this return item to set
291 | 
292 | ---
293 | 
294 | ### setParentItem
295 | 
296 | **Signature:** `setParentItem(parentItem : ReturnItem) : void`
297 | 
298 | **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().
299 | 
300 | **Parameters:**
301 | 
302 | - `parentItem`: The parent item, null is allowed
303 | 
304 | ---
305 | 
306 | ### setReasonCode
307 | 
308 | **Signature:** `setReasonCode(reasonCode : String) : void`
309 | 
310 | **Description:** Set the reason code. The list of reason codes can be updated by updating meta-data for ReturnItem.
311 | 
312 | **Parameters:**
313 | 
314 | - `reasonCode`: the reason code to set
315 | 
316 | ---
317 | 
318 | ### setReturnedQuantity
319 | 
320 | **Signature:** `setReturnedQuantity(quantity : Quantity) : void`
321 | 
322 | **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.
323 | 
324 | **Parameters:**
325 | 
326 | - `quantity`: the quantity returned, null not allowed
327 | 
328 | **See Also:**
329 | 
330 | OrderItem.getReturnedQuantity()
331 | ProductLineItem.getQuantity()
332 | 
333 | ---
334 | 
335 | ### setTaxBasis
336 | 
337 | **Signature:** `setTaxBasis(taxBasis : Money) : void`
338 | 
339 | **Description:** Set the tax-basis price for this item.
340 | 
341 | **Parameters:**
342 | 
343 | - `taxBasis`: the tax basis value.
344 | 
345 | ---
346 | 
347 | ### setTaxItems
348 | 
349 | **Signature:** `setTaxItems(taxItems : Collection) : void`
350 | 
351 | **Description:** Set the tax-items for this item.
352 | 
353 | **Parameters:**
354 | 
355 | - `taxItems`: items
356 | 
357 | **See Also:**
358 | 
359 | addTaxItem(Decimal, TaxGroup)
360 | TaxGroup.create(String, String, String, Decimal)
361 | 
362 | ---
```

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

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class ShippingLineItem
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.PersistentObject
  9 |   - dw.object.ExtensibleObject
 10 |     - dw.order.LineItem
 11 |       - dw.order.ShippingLineItem
 12 | 
 13 | ## Description
 14 | 
 15 | Represents a specific line item in a shipment. The ShippingLineItem defines the general shipping costs of a shipment.
 16 | 
 17 | ## Constants
 18 | 
 19 | ### STANDARD_SHIPPING_ID
 20 | 
 21 | **Type:** String = "STANDARD_SHIPPING"
 22 | 
 23 | Constant used to get the standard shipping line item.
 24 | 
 25 | ## Properties
 26 | 
 27 | ### adjustedGrossPrice
 28 | 
 29 | **Type:** Money (Read Only)
 30 | 
 31 | The price of this shipping line item including tax after
 32 |  shipping adjustments have been applied.
 33 | 
 34 | ### adjustedNetPrice
 35 | 
 36 | **Type:** Money (Read Only)
 37 | 
 38 | The price of this shipping line item, excluding tax after
 39 |  shipping adjustments have been applied.
 40 | 
 41 | ### adjustedPrice
 42 | 
 43 | **Type:** Money (Read Only)
 44 | 
 45 | The adjusted price of this shipping line item. If the line item
 46 |  container is based on net pricing, the adjusted net price is returned. If
 47 |  the line item container is based on gross pricing, the adjusted gross
 48 |  price is returned.
 49 | 
 50 | ### adjustedTax
 51 | 
 52 | **Type:** Money (Read Only)
 53 | 
 54 | The tax of this shipping line item after shipping adjustments
 55 |  have been applied.
 56 | 
 57 | ### ID
 58 | 
 59 | **Type:** String (Read Only)
 60 | 
 61 | The ID of this ShippingLineItem.
 62 | 
 63 | ### orderItem
 64 | 
 65 | **Type:** OrderItem (Read Only)
 66 | 
 67 | The order-item extension for this item, or null.
 68 |  An order-item extension will only exist for a ShippingLineItem which
 69 |  belongs to an Order.
 70 |  
 71 |  Order post-processing APIs (gillian) are now inactive by default and will throw
 72 |  an exception if accessed. Activation needs preliminary approval by Product Management.
 73 |  Please contact support in this case. Existing customers using these APIs are not
 74 |  affected by this change and can use the APIs until further notice.
 75 | 
 76 | ### shippingPriceAdjustments
 77 | 
 78 | **Type:** Collection (Read Only)
 79 | 
 80 | The collection of shipping price adjustments that have been
 81 |  applied to this shipping line item.
 82 | 
 83 | ## Constructor Summary
 84 | 
 85 | ## Method Summary
 86 | 
 87 | ### createShippingPriceAdjustment
 88 | 
 89 | **Signature:** `createShippingPriceAdjustment(promotionID : String) : PriceAdjustment`
 90 | 
 91 | 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.
 92 | 
 93 | ### createShippingPriceAdjustment
 94 | 
 95 | **Signature:** `createShippingPriceAdjustment(promotionID : String, discount : Discount) : PriceAdjustment`
 96 | 
 97 | 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.
 98 | 
 99 | ### getAdjustedGrossPrice
100 | 
101 | **Signature:** `getAdjustedGrossPrice() : Money`
102 | 
103 | Returns the price of this shipping line item including tax after shipping adjustments have been applied.
104 | 
105 | ### getAdjustedNetPrice
106 | 
107 | **Signature:** `getAdjustedNetPrice() : Money`
108 | 
109 | Returns the price of this shipping line item, excluding tax after shipping adjustments have been applied.
110 | 
111 | ### getAdjustedPrice
112 | 
113 | **Signature:** `getAdjustedPrice() : Money`
114 | 
115 | Returns the adjusted price of this shipping line item.
116 | 
117 | ### getAdjustedTax
118 | 
119 | **Signature:** `getAdjustedTax() : Money`
120 | 
121 | Returns the tax of this shipping line item after shipping adjustments have been applied.
122 | 
123 | ### getID
124 | 
125 | **Signature:** `getID() : String`
126 | 
127 | Returns the ID of this ShippingLineItem.
128 | 
129 | ### getOrderItem
130 | 
131 | **Signature:** `getOrderItem() : OrderItem`
132 | 
133 | Returns the order-item extension for this item, or null.
134 | 
135 | ### getShippingPriceAdjustments
136 | 
137 | **Signature:** `getShippingPriceAdjustments() : Collection`
138 | 
139 | Returns the collection of shipping price adjustments that have been applied to this shipping line item.
140 | 
141 | ### removeShippingPriceAdjustment
142 | 
143 | **Signature:** `removeShippingPriceAdjustment(priceAdjustment : PriceAdjustment) : void`
144 | 
145 | Removes the specified shipping price adjustment from this shipping line item.
146 | 
147 | ## Method Detail
148 | 
149 | ## Method Details
150 | 
151 | ### createShippingPriceAdjustment
152 | 
153 | **Signature:** `createShippingPriceAdjustment(promotionID : String) : PriceAdjustment`
154 | 
155 | **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.
156 | 
157 | **Parameters:**
158 | 
159 | - `promotionID`: Promotion ID
160 | 
161 | **Returns:**
162 | 
163 | The new price adjustment line item.
164 | 
165 | ---
166 | 
167 | ### createShippingPriceAdjustment
168 | 
169 | **Signature:** `createShippingPriceAdjustment(promotionID : String, discount : Discount) : PriceAdjustment`
170 | 
171 | **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));
172 | 
173 | **Parameters:**
174 | 
175 | - `promotionID`: Promotion ID
176 | - `discount`: The desired discount, not null
177 | 
178 | **Returns:**
179 | 
180 | The new price adjustment line item.
181 | 
182 | ---
183 | 
184 | ### getAdjustedGrossPrice
185 | 
186 | **Signature:** `getAdjustedGrossPrice() : Money`
187 | 
188 | **Description:** Returns the price of this shipping line item including tax after shipping adjustments have been applied.
189 | 
190 | **Returns:**
191 | 
192 | the price of this shipping line item, including tax after shipping adjustments have been applied.
193 | 
194 | ---
195 | 
196 | ### getAdjustedNetPrice
197 | 
198 | **Signature:** `getAdjustedNetPrice() : Money`
199 | 
200 | **Description:** Returns the price of this shipping line item, excluding tax after shipping adjustments have been applied.
201 | 
202 | **Returns:**
203 | 
204 | the price of this shipping line item, excluding tax after shipping adjustments have been applied.
205 | 
206 | ---
207 | 
208 | ### getAdjustedPrice
209 | 
210 | **Signature:** `getAdjustedPrice() : Money`
211 | 
212 | **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.
213 | 
214 | **Returns:**
215 | 
216 | either the adjusted net or gross price of this shipping line item.
217 | 
218 | ---
219 | 
220 | ### getAdjustedTax
221 | 
222 | **Signature:** `getAdjustedTax() : Money`
223 | 
224 | **Description:** Returns the tax of this shipping line item after shipping adjustments have been applied.
225 | 
226 | **Returns:**
227 | 
228 | the tax of this shipping line item after shipping adjustments have been applied.
229 | 
230 | ---
231 | 
232 | ### getID
233 | 
234 | **Signature:** `getID() : String`
235 | 
236 | **Description:** Returns the ID of this ShippingLineItem.
237 | 
238 | **Returns:**
239 | 
240 | ID of this ShippingLineItem
241 | 
242 | ---
243 | 
244 | ### getOrderItem
245 | 
246 | **Signature:** `getOrderItem() : OrderItem`
247 | 
248 | **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.
249 | 
250 | **Returns:**
251 | 
252 | null or the order-item
253 | 
254 | ---
255 | 
256 | ### getShippingPriceAdjustments
257 | 
258 | **Signature:** `getShippingPriceAdjustments() : Collection`
259 | 
260 | **Description:** Returns the collection of shipping price adjustments that have been applied to this shipping line item.
261 | 
262 | **Returns:**
263 | 
264 | the collection of shipping price adjustments that have been applied to this shipping line item.
265 | 
266 | ---
267 | 
268 | ### removeShippingPriceAdjustment
269 | 
270 | **Signature:** `removeShippingPriceAdjustment(priceAdjustment : PriceAdjustment) : void`
271 | 
272 | **Description:** Removes the specified shipping price adjustment from this shipping line item.
273 | 
274 | **Parameters:**
275 | 
276 | - `priceAdjustment`: The price adjustment line item to remove
277 | 
278 | ---
```

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

```yaml
  1 | # ==================================================================================
  2 | # SFCC MCP Server - get_system_object_definitions Tool YAML Tests (Docs-Only Mode)
  3 | # Tests that system object tools are NOT available in docs-only mode
  4 | # This tool requires SFCC credentials and should not be available without them
  5 | # 
  6 | # Quick Test Commands:
  7 | # aegis "tests/mcp/yaml/get-system-object-definitions.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose
  8 | # aegis "tests/mcp/yaml/get-system-object-definitions.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --debug --timing
  9 | # aegis query --config "aegis.config.docs-only.json"
 10 | # ==================================================================================
 11 | 
 12 | description: "get_system_object_definitions tool tests - Docs-only mode tool availability"
 13 | 
 14 | # ==================================================================================
 15 | # TOOL UNAVAILABILITY IN DOCS-ONLY MODE
 16 | # ==================================================================================
 17 | tests:
 18 |   - it: "should NOT list get_system_object_definitions tool in docs-only mode"
 19 |     request:
 20 |       jsonrpc: "2.0"
 21 |       id: "tool-not-available-docs"
 22 |       method: "tools/list"
 23 |       params: {}
 24 |     expect:
 25 |       response:
 26 |         jsonrpc: "2.0"
 27 |         id: "tool-not-available-docs"
 28 |         result:
 29 |           match:extractField: "tools.*.name"
 30 |           value: "match:not:arrayContains:get_system_object_definitions"
 31 |       stderr: "toBeEmpty"
 32 | 
 33 |   - it: "should NOT list any system object tools in docs-only mode"
 34 |     request:
 35 |       jsonrpc: "2.0"
 36 |       id: "no-system-object-tools"
 37 |       method: "tools/list"
 38 |       params: {}
 39 |     expect:
 40 |       response:
 41 |         jsonrpc: "2.0"
 42 |         id: "no-system-object-tools"
 43 |         result:
 44 |           match:extractField: "tools.*.name"
 45 |           value: "match:not:arrayContains:get_system_object_definition"
 46 |       stderr: "toBeEmpty"
 47 | 
 48 |   - it: "should NOT list site preferences tools in docs-only mode"
 49 |     request:
 50 |       jsonrpc: "2.0"
 51 |       id: "no-site-prefs-tools"
 52 |       method: "tools/list"
 53 |       params: {}
 54 |     expect:
 55 |       response:
 56 |         jsonrpc: "2.0"
 57 |         id: "no-site-prefs-tools"
 58 |         result:
 59 |           match:extractField: "tools.*.name"
 60 |           value: "match:not:arrayContains:search_site_preferences"
 61 |       stderr: "toBeEmpty"
 62 | 
 63 |   - it: "should NOT list custom object attribute tools in docs-only mode"
 64 |     request:
 65 |       jsonrpc: "2.0"
 66 |       id: "no-custom-object-tools"
 67 |       method: "tools/list"
 68 |       params: {}
 69 |     expect:
 70 |       response:
 71 |         jsonrpc: "2.0"
 72 |         id: "no-custom-object-tools"
 73 |         result:
 74 |           match:extractField: "tools.*.name"
 75 |           value: "match:not:arrayContains:search_custom_object_attribute_definitions"
 76 |       stderr: "toBeEmpty"
 77 | 
 78 | # ==================================================================================
 79 | # TOOL CALL BEHAVIOR - AVAILABLE BUT RETURNS ERROR  
 80 | # ==================================================================================
 81 | 
 82 |   - it: "should return configuration error when calling unlisted tool"
 83 |     request:
 84 |       jsonrpc: "2.0"
 85 |       id: "config-error"
 86 |       method: "tools/call"
 87 |       params:
 88 |         name: "get_system_object_definitions"
 89 |         arguments: {}
 90 |     expect:
 91 |       response:
 92 |         jsonrpc: "2.0"
 93 |         id: "config-error"
 94 |         result:
 95 |           content:
 96 |             match:arrayElements:
 97 |               match:partial:
 98 |                 type: "text"
 99 |                 text: "match:contains:OCAPI client not configured"
100 |           isError: true
101 |       performance:
102 |         maxResponseTime: "500ms"  # Error should be fast
103 | 
104 |   - it: "should return proper error result structure for unlisted tool"
105 |     request:
106 |       jsonrpc: "2.0"
107 |       id: "error-result-structure"
108 |       method: "tools/call"
109 |       params:
110 |         name: "get_system_object_definitions"
111 |         arguments: {}
112 |     expect:
113 |       response:
114 |         jsonrpc: "2.0"
115 |         id: "error-result-structure"
116 |         result:
117 |           content: "match:type:array"
118 |           isError: true
119 | 
120 |   - it: "should fail fast when calling unlisted tool with parameters"
121 |     request:
122 |       jsonrpc: "2.0"
123 |       id: "fail-fast-with-params"
124 |       method: "tools/call"
125 |       params:
126 |         name: "get_system_object_definitions"
127 |         arguments:
128 |           start: 0
129 |           count: 10
130 |     expect:
131 |       response:
132 |         jsonrpc: "2.0"
133 |         id: "fail-fast-with-params"
134 |         result:
135 |           content:
136 |             match:arrayElements:
137 |               match:partial:
138 |                 type: "text"
139 |                 text: "match:contains:OCAPI client not configured"
140 |           isError: true
141 |       performance:
142 |         maxResponseTime: "500ms"  # Should fail quickly
143 | 
144 | # ==================================================================================
145 | # DOCS-ONLY MODE TOOL AVAILABILITY VERIFICATION
146 | # ==================================================================================
147 | 
148 |   - it: "should have SFCC documentation tools available"
149 |     request:
150 |       jsonrpc: "2.0"
151 |       id: "docs-tools-available"
152 |       method: "tools/list"
153 |       params: {}
154 |     expect:
155 |       response:
156 |         jsonrpc: "2.0"
157 |         id: "docs-tools-available"
158 |         result:
159 |           match:extractField: "tools.*.name"
160 |           value: "match:arrayContains:get_sfcc_class_info"
161 |       stderr: "toBeEmpty"
162 | 
163 |   - it: "should have SFRA documentation tools available"
164 |     request:
165 |       jsonrpc: "2.0"
166 |       id: "sfra-tools-available"
167 |       method: "tools/list"
168 |       params: {}
169 |     expect:
170 |       response:
171 |         jsonrpc: "2.0"
172 |         id: "sfra-tools-available"
173 |         result:
174 |           match:extractField: "tools.*.name"
175 |           value: "match:arrayContains:get_sfra_document"
176 |       stderr: "toBeEmpty"
177 | 
178 |   - it: "should have best practices tools available"
179 |     request:
180 |       jsonrpc: "2.0"
181 |       id: "best-practices-available"
182 |       method: "tools/list"
183 |       params: {}
184 |     expect:
185 |       response:
186 |         jsonrpc: "2.0"
187 |         id: "best-practices-available"
188 |         result:
189 |           match:extractField: "tools.*.name"
190 |           value: "match:arrayContains:get_best_practice_guide"
191 |       stderr: "toBeEmpty"
192 | 
193 |   - it: "should have cartridge generation tools available"
194 |     request:
195 |       jsonrpc: "2.0"
196 |       id: "cartridge-tools-available"
197 |       method: "tools/list"
198 |       params: {}
199 |     expect:
200 |       response:
201 |         jsonrpc: "2.0"
202 |         id: "cartridge-tools-available"
203 |         result:
204 |           match:extractField: "tools.*.name"
205 |           value: "match:arrayContains:generate_cartridge_structure"
206 |       stderr: "toBeEmpty"
207 | 
208 |   - it: "should have expected number of tools in docs-only mode"
209 |     request:
210 |       jsonrpc: "2.0"
211 |       id: "expected-tool-count"
212 |       method: "tools/list"
213 |       params: {}
214 |     expect:
215 |       response:
216 |         jsonrpc: "2.0"
217 |         id: "expected-tool-count"
218 |         result:
219 |           tools: "match:arrayLength:15"  # Based on actual count from aegis query
220 |       stderr: "toBeEmpty"
221 | 
222 | # ==================================================================================
223 | # CONSISTENCY TESTING
224 | # ==================================================================================
225 | 
226 |   - it: "should consistently exclude system object tools across multiple calls"
227 |     request:
228 |       jsonrpc: "2.0"
229 |       id: "consistent-exclusion-1"
230 |       method: "tools/list"
231 |       params: {}
232 |     expect:
233 |       response:
234 |         jsonrpc: "2.0"
235 |         id: "consistent-exclusion-1"
236 |         result:
237 |           match:extractField: "tools.*.name"
238 |           value: "match:not:arrayContains:get_system_object_definitions"
239 | 
240 |   - it: "should maintain consistent tool exclusion on second call"
241 |     request:
242 |       jsonrpc: "2.0"
243 |       id: "consistent-exclusion-2"
244 |       method: "tools/list"
245 |       params: {}
246 |     expect:
247 |       response:
248 |         jsonrpc: "2.0"
249 |         id: "consistent-exclusion-2"
250 |         result:
251 |           match:extractField: "tools.*.name"
252 |           value: "match:not:arrayContains:get_system_object_definitions"
253 | 
254 |   - it: "should consistently return configuration error across multiple attempts"
255 |     request:
256 |       jsonrpc: "2.0"
257 |       id: "consistent-config-error"
258 |       method: "tools/call"
259 |       params:
260 |         name: "get_system_object_definitions"
261 |         arguments: {}
262 |     expect:
263 |       response:
264 |         jsonrpc: "2.0"
265 |         id: "consistent-config-error"
266 |         result:
267 |           content:
268 |             match:arrayElements:
269 |               match:partial:
270 |                 type: "text"
271 |                 text: "match:contains:OCAPI client not configured"
272 |           isError: true
```

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

```markdown
  1 | ## Package: dw.catalog
  2 | 
  3 | # Class Store
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.PersistentObject
  9 |   - dw.object.ExtensibleObject
 10 |     - dw.catalog.Store
 11 | 
 12 | ## Description
 13 | 
 14 | Represents a store in Commerce Cloud Digital.
 15 | 
 16 | ## Properties
 17 | 
 18 | ### address1
 19 | 
 20 | **Type:** String (Read Only)
 21 | 
 22 | The address1 of the store.
 23 | 
 24 | ### address2
 25 | 
 26 | **Type:** String (Read Only)
 27 | 
 28 | The address2 of the store.
 29 | 
 30 | ### city
 31 | 
 32 | **Type:** String (Read Only)
 33 | 
 34 | The city of the store.
 35 | 
 36 | ### countryCode
 37 | 
 38 | **Type:** EnumValue (Read Only)
 39 | 
 40 | The countryCode of the store.
 41 | 
 42 | ### demandwarePosEnabled
 43 | 
 44 | **Type:** boolean (Read Only)
 45 | 
 46 | The demandwarePosEnabled flag for the store.
 47 |  Indicates that this store uses Commerce Cloud Store for point-of-sale.
 48 | 
 49 | ### email
 50 | 
 51 | **Type:** String (Read Only)
 52 | 
 53 | The email of the store.
 54 | 
 55 | ### fax
 56 | 
 57 | **Type:** String (Read Only)
 58 | 
 59 | The fax of the store.
 60 | 
 61 | ### ID
 62 | 
 63 | **Type:** String (Read Only)
 64 | 
 65 | The ID of the store.
 66 | 
 67 | ### image
 68 | 
 69 | **Type:** MediaFile (Read Only)
 70 | 
 71 | The store image.
 72 | 
 73 | ### inventoryList
 74 | 
 75 | **Type:** ProductInventoryList (Read Only)
 76 | 
 77 | The inventory list the store is associated with. If the
 78 |  store is not associated with a inventory list, or the inventory list does not
 79 |  exist, the method returns null.
 80 | 
 81 | ### inventoryListID
 82 | 
 83 | **Type:** String (Read Only)
 84 | 
 85 | The inventory list id the store is associated with. If the
 86 |  store is not associated with a inventory list, or the inventory list does not
 87 |  exist, the method returns null.
 88 | 
 89 | ### latitude
 90 | 
 91 | **Type:** Number (Read Only)
 92 | 
 93 | The latitude of the store.
 94 | 
 95 | ### longitude
 96 | 
 97 | **Type:** Number (Read Only)
 98 | 
 99 | The longitude of the store.
100 | 
101 | ### name
102 | 
103 | **Type:** String (Read Only)
104 | 
105 | The name of the store.
106 | 
107 | ### phone
108 | 
109 | **Type:** String (Read Only)
110 | 
111 | The phone of the store.
112 | 
113 | ### posEnabled
114 | 
115 | **Type:** boolean (Read Only)
116 | 
117 | The posEnabled flag for the Store.
118 |  Indicates that this store uses Commerce Cloud Store for point-of-sale.
119 | 
120 | ### postalCode
121 | 
122 | **Type:** String (Read Only)
123 | 
124 | The postalCode of the store.
125 | 
126 | ### stateCode
127 | 
128 | **Type:** String (Read Only)
129 | 
130 | The stateCode of the store.
131 | 
132 | ### storeEvents
133 | 
134 | **Type:** MarkupText (Read Only)
135 | 
136 | The storeEvents of the store.
137 | 
138 | ### storeGroups
139 | 
140 | **Type:** Collection (Read Only)
141 | 
142 | All the store groups this store belongs to.
143 | 
144 | ### storeHours
145 | 
146 | **Type:** MarkupText (Read Only)
147 | 
148 | The storeHours of the store.
149 | 
150 | ### storeLocatorEnabled
151 | 
152 | **Type:** boolean (Read Only)
153 | 
154 | The storeLocatorEnabled flag for the store.
155 | 
156 | ## Constructor Summary
157 | 
158 | ## Method Summary
159 | 
160 | ### getAddress1
161 | 
162 | **Signature:** `getAddress1() : String`
163 | 
164 | Returns the address1 of the store.
165 | 
166 | ### getAddress2
167 | 
168 | **Signature:** `getAddress2() : String`
169 | 
170 | Returns the address2 of the store.
171 | 
172 | ### getCity
173 | 
174 | **Signature:** `getCity() : String`
175 | 
176 | Returns the city of the store.
177 | 
178 | ### getCountryCode
179 | 
180 | **Signature:** `getCountryCode() : EnumValue`
181 | 
182 | Returns the countryCode of the store.
183 | 
184 | ### getEmail
185 | 
186 | **Signature:** `getEmail() : String`
187 | 
188 | Returns the email of the store.
189 | 
190 | ### getFax
191 | 
192 | **Signature:** `getFax() : String`
193 | 
194 | Returns the fax of the store.
195 | 
196 | ### getID
197 | 
198 | **Signature:** `getID() : String`
199 | 
200 | Returns the ID of the store.
201 | 
202 | ### getImage
203 | 
204 | **Signature:** `getImage() : MediaFile`
205 | 
206 | Returns the store image.
207 | 
208 | ### getInventoryList
209 | 
210 | **Signature:** `getInventoryList() : ProductInventoryList`
211 | 
212 | Returns the inventory list the store is associated with.
213 | 
214 | ### getInventoryListID
215 | 
216 | **Signature:** `getInventoryListID() : String`
217 | 
218 | Returns the inventory list id the store is associated with.
219 | 
220 | ### getLatitude
221 | 
222 | **Signature:** `getLatitude() : Number`
223 | 
224 | Returns the latitude of the store.
225 | 
226 | ### getLongitude
227 | 
228 | **Signature:** `getLongitude() : Number`
229 | 
230 | Returns the longitude of the store.
231 | 
232 | ### getName
233 | 
234 | **Signature:** `getName() : String`
235 | 
236 | Returns the name of the store.
237 | 
238 | ### getPhone
239 | 
240 | **Signature:** `getPhone() : String`
241 | 
242 | Returns the phone of the store.
243 | 
244 | ### getPostalCode
245 | 
246 | **Signature:** `getPostalCode() : String`
247 | 
248 | Returns the postalCode of the store.
249 | 
250 | ### getStateCode
251 | 
252 | **Signature:** `getStateCode() : String`
253 | 
254 | Returns the stateCode of the store.
255 | 
256 | ### getStoreEvents
257 | 
258 | **Signature:** `getStoreEvents() : MarkupText`
259 | 
260 | Returns the storeEvents of the store.
261 | 
262 | ### getStoreGroups
263 | 
264 | **Signature:** `getStoreGroups() : Collection`
265 | 
266 | Returns all the store groups this store belongs to.
267 | 
268 | ### getStoreHours
269 | 
270 | **Signature:** `getStoreHours() : MarkupText`
271 | 
272 | Returns the storeHours of the store.
273 | 
274 | ### isDemandwarePosEnabled
275 | 
276 | **Signature:** `isDemandwarePosEnabled() : boolean`
277 | 
278 | Returns the demandwarePosEnabled flag for the store.
279 | 
280 | ### isPosEnabled
281 | 
282 | **Signature:** `isPosEnabled() : boolean`
283 | 
284 | Returns the posEnabled flag for the Store.
285 | 
286 | ### isStoreLocatorEnabled
287 | 
288 | **Signature:** `isStoreLocatorEnabled() : boolean`
289 | 
290 | Returns the storeLocatorEnabled flag for the store.
291 | 
292 | ## Method Detail
293 | 
294 | ## Method Details
295 | 
296 | ### getAddress1
297 | 
298 | **Signature:** `getAddress1() : String`
299 | 
300 | **Description:** Returns the address1 of the store.
301 | 
302 | **Returns:**
303 | 
304 | address1 of the store
305 | 
306 | ---
307 | 
308 | ### getAddress2
309 | 
310 | **Signature:** `getAddress2() : String`
311 | 
312 | **Description:** Returns the address2 of the store.
313 | 
314 | **Returns:**
315 | 
316 | address2 of the store
317 | 
318 | ---
319 | 
320 | ### getCity
321 | 
322 | **Signature:** `getCity() : String`
323 | 
324 | **Description:** Returns the city of the store.
325 | 
326 | **Returns:**
327 | 
328 | city of the store
329 | 
330 | ---
331 | 
332 | ### getCountryCode
333 | 
334 | **Signature:** `getCountryCode() : EnumValue`
335 | 
336 | **Description:** Returns the countryCode of the store.
337 | 
338 | **Returns:**
339 | 
340 | countryCode of the store
341 | 
342 | ---
343 | 
344 | ### getEmail
345 | 
346 | **Signature:** `getEmail() : String`
347 | 
348 | **Description:** Returns the email of the store.
349 | 
350 | **Returns:**
351 | 
352 | email of the store
353 | 
354 | ---
355 | 
356 | ### getFax
357 | 
358 | **Signature:** `getFax() : String`
359 | 
360 | **Description:** Returns the fax of the store.
361 | 
362 | **Returns:**
363 | 
364 | fax of the store
365 | 
366 | ---
367 | 
368 | ### getID
369 | 
370 | **Signature:** `getID() : String`
371 | 
372 | **Description:** Returns the ID of the store.
373 | 
374 | **Returns:**
375 | 
376 | ID of the store
377 | 
378 | ---
379 | 
380 | ### getImage
381 | 
382 | **Signature:** `getImage() : MediaFile`
383 | 
384 | **Description:** Returns the store image.
385 | 
386 | **Returns:**
387 | 
388 | the store image.
389 | 
390 | ---
391 | 
392 | ### getInventoryList
393 | 
394 | **Signature:** `getInventoryList() : ProductInventoryList`
395 | 
396 | **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.
397 | 
398 | **Returns:**
399 | 
400 | ProductInventoryList or null
401 | 
402 | ---
403 | 
404 | ### getInventoryListID
405 | 
406 | **Signature:** `getInventoryListID() : String`
407 | 
408 | **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.
409 | 
410 | **Returns:**
411 | 
412 | the inventory list id
413 | 
414 | ---
415 | 
416 | ### getLatitude
417 | 
418 | **Signature:** `getLatitude() : Number`
419 | 
420 | **Description:** Returns the latitude of the store.
421 | 
422 | **Returns:**
423 | 
424 | latitude of the store
425 | 
426 | ---
427 | 
428 | ### getLongitude
429 | 
430 | **Signature:** `getLongitude() : Number`
431 | 
432 | **Description:** Returns the longitude of the store.
433 | 
434 | **Returns:**
435 | 
436 | longitude of the store
437 | 
438 | ---
439 | 
440 | ### getName
441 | 
442 | **Signature:** `getName() : String`
443 | 
444 | **Description:** Returns the name of the store.
445 | 
446 | **Returns:**
447 | 
448 | name of the store
449 | 
450 | ---
451 | 
452 | ### getPhone
453 | 
454 | **Signature:** `getPhone() : String`
455 | 
456 | **Description:** Returns the phone of the store.
457 | 
458 | **Returns:**
459 | 
460 | phone of the store
461 | 
462 | ---
463 | 
464 | ### getPostalCode
465 | 
466 | **Signature:** `getPostalCode() : String`
467 | 
468 | **Description:** Returns the postalCode of the store.
469 | 
470 | **Returns:**
471 | 
472 | postalCode of the store
473 | 
474 | ---
475 | 
476 | ### getStateCode
477 | 
478 | **Signature:** `getStateCode() : String`
479 | 
480 | **Description:** Returns the stateCode of the store.
481 | 
482 | **Returns:**
483 | 
484 | stateCode of the store
485 | 
486 | ---
487 | 
488 | ### getStoreEvents
489 | 
490 | **Signature:** `getStoreEvents() : MarkupText`
491 | 
492 | **Description:** Returns the storeEvents of the store.
493 | 
494 | **Returns:**
495 | 
496 | storeEvents of the store
497 | 
498 | ---
499 | 
500 | ### getStoreGroups
501 | 
502 | **Signature:** `getStoreGroups() : Collection`
503 | 
504 | **Description:** Returns all the store groups this store belongs to.
505 | 
506 | **Returns:**
507 | 
508 | collection of store groups
509 | 
510 | ---
511 | 
512 | ### getStoreHours
513 | 
514 | **Signature:** `getStoreHours() : MarkupText`
515 | 
516 | **Description:** Returns the storeHours of the store.
517 | 
518 | **Returns:**
519 | 
520 | storeHours of the store
521 | 
522 | ---
523 | 
524 | ### isDemandwarePosEnabled
525 | 
526 | **Signature:** `isDemandwarePosEnabled() : boolean`
527 | 
528 | **Description:** Returns the demandwarePosEnabled flag for the store. Indicates that this store uses Commerce Cloud Store for point-of-sale.
529 | 
530 | **Deprecated:**
531 | 
532 | Use isPosEnabled() instead
533 | 
534 | **Returns:**
535 | 
536 | the demandwarePosEnabled flag
537 | 
538 | ---
539 | 
540 | ### isPosEnabled
541 | 
542 | **Signature:** `isPosEnabled() : boolean`
543 | 
544 | **Description:** Returns the posEnabled flag for the Store. Indicates that this store uses Commerce Cloud Store for point-of-sale.
545 | 
546 | **Returns:**
547 | 
548 | the posEnabled flag
549 | 
550 | ---
551 | 
552 | ### isStoreLocatorEnabled
553 | 
554 | **Signature:** `isStoreLocatorEnabled() : boolean`
555 | 
556 | **Description:** Returns the storeLocatorEnabled flag for the store.
557 | 
558 | **Returns:**
559 | 
560 | the storeLocatorEnabled flag
561 | 
562 | ---
```

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

```typescript
  1 | /**
  2 |  * Tests for QueryBuilder utility
  3 |  * Tests URL parameter construction and array handling
  4 |  */
  5 | 
  6 | import { QueryBuilder } from '../src/utils/query-builder.js';
  7 | 
  8 | describe('QueryBuilder', () => {
  9 |   let builder: QueryBuilder;
 10 | 
 11 |   beforeEach(() => {
 12 |     builder = new QueryBuilder();
 13 |   });
 14 | 
 15 |   describe('constructor', () => {
 16 |     it('should initialize with empty parameters', () => {
 17 |       expect(builder).toBeInstanceOf(QueryBuilder);
 18 |     });
 19 |   });
 20 | 
 21 |   describe('add method', () => {
 22 |     it('should add string parameter', () => {
 23 |       const result = builder.add('key', 'value').build();
 24 |       expect(result).toBe('key=value');
 25 |     });
 26 | 
 27 |     it('should add number parameter', () => {
 28 |       const result = builder.add('count', 10).build();
 29 |       expect(result).toBe('count=10');
 30 |     });
 31 | 
 32 |     it('should add boolean parameter', () => {
 33 |       const result = builder.add('active', true).build();
 34 |       expect(result).toBe('active=true');
 35 |     });
 36 | 
 37 |     it('should skip undefined values', () => {
 38 |       const result = builder.add('key', undefined as any).build();
 39 |       expect(result).toBe('');
 40 |     });
 41 | 
 42 |     it('should skip null values', () => {
 43 |       const result = builder.add('key', null as any).build();
 44 |       expect(result).toBe('');
 45 |     });
 46 | 
 47 |     it('should chain multiple adds', () => {
 48 |       const result = builder
 49 |         .add('first', 'value1')
 50 |         .add('second', 'value2')
 51 |         .build();
 52 |       expect(result).toBe('first=value1&second=value2');
 53 |     });
 54 |   });
 55 | 
 56 |   describe('addArray method', () => {
 57 |     it('should handle regular arrays with comma separation', () => {
 58 |       const result = builder.addArray('ids', ['1', '2', '3']).build();
 59 |       expect(result).toBe('ids=1%2C2%2C3');
 60 |     });
 61 | 
 62 |     it('should handle refine parameters with multiple entries', () => {
 63 |       const result = builder.addArray('refine', ['category=shirts', 'color=blue']).build();
 64 |       expect(result).toBe('refine=category%3Dshirts&refine=color%3Dblue');
 65 |     });
 66 | 
 67 |     it('should handle mixed string and number arrays', () => {
 68 |       const result = builder.addArray('values', ['string', 123]).build();
 69 |       expect(result).toBe('values=string%2C123');
 70 |     });
 71 | 
 72 |     it('should skip empty arrays', () => {
 73 |       const result = builder.addArray('empty', []).build();
 74 |       expect(result).toBe('');
 75 |     });
 76 | 
 77 |     it('should skip non-arrays', () => {
 78 |       const result = builder.addArray('invalid', null as any).build();
 79 |       expect(result).toBe('');
 80 |     });
 81 | 
 82 |     it('should chain with other methods', () => {
 83 |       const result = builder
 84 |         .add('single', 'value')
 85 |         .addArray('multiple', ['a', 'b'])
 86 |         .build();
 87 |       expect(result).toBe('single=value&multiple=a%2Cb');
 88 |     });
 89 |   });
 90 | 
 91 |   describe('addFromObject method', () => {
 92 |     it('should add simple object properties', () => {
 93 |       const params = {
 94 |         name: 'test',
 95 |         count: 5,
 96 |         active: true,
 97 |       };
 98 |       const result = builder.addFromObject(params).build();
 99 |       expect(result).toBe('name=test&count=5&active=true');
100 |     });
101 | 
102 |     it('should handle arrays in object', () => {
103 |       const params = {
104 |         ids: ['1', '2'],
105 |         expand: ['details', 'variations'],
106 |       };
107 |       const result = builder.addFromObject(params).build();
108 |       expect(result).toBe('ids=1%2C2&expand=details%2Cvariations');
109 |     });
110 | 
111 |     it('should handle refine arrays specially', () => {
112 |       const params = {
113 |         q: 'shirt',
114 |         refine: ['category=clothing', 'size=large'],
115 |       };
116 |       const result = builder.addFromObject(params).build();
117 |       expect(result).toBe('q=shirt&refine=category%3Dclothing&refine=size%3Dlarge');
118 |     });
119 | 
120 |     it('should skip undefined and null values', () => {
121 |       const params = {
122 |         defined: 'value',
123 |         undefined,
124 |         null: null,
125 |         empty: '',
126 |         zero: 0,
127 |       };
128 |       const result = builder.addFromObject(params).build();
129 |       expect(result).toBe('defined=value&empty=&zero=0');
130 |     });
131 | 
132 |     it('should handle complex object with mixed types', () => {
133 |       const params = {
134 |         q: 'search term',
135 |         count: 20,
136 |         start: 0,
137 |         expand: ['images', 'prices'],
138 |         refine: ['brand=nike', 'color=red'],
139 |         active: true,
140 |         skip: undefined,
141 |       };
142 |       const result = builder.addFromObject(params).build();
143 |       expect(result).toBe(
144 |         'q=search+term&count=20&start=0&expand=images%2Cprices&refine=brand%3Dnike&refine=color%3Dred&active=true',
145 |       );
146 |     });
147 | 
148 |     it('should chain with other methods', () => {
149 |       const result = builder
150 |         .add('manual', 'value')
151 |         .addFromObject({ auto: 'generated' })
152 |         .add('final', 'last')
153 |         .build();
154 |       expect(result).toBe('manual=value&auto=generated&final=last');
155 |     });
156 |   });
157 | 
158 |   describe('build method', () => {
159 |     it('should return empty string for no parameters', () => {
160 |       const result = builder.build();
161 |       expect(result).toBe('');
162 |     });
163 | 
164 |     it('should properly encode special characters', () => {
165 |       const result = builder.add('special', 'value with spaces & symbols!').build();
166 |       expect(result).toBe('special=value+with+spaces+%26+symbols%21');
167 |     });
168 | 
169 |     it('should handle multiple calls to build', () => {
170 |       builder.add('key', 'value');
171 |       const first = builder.build();
172 |       const second = builder.build();
173 |       expect(first).toBe(second);
174 |       expect(first).toBe('key=value');
175 |     });
176 |   });
177 | 
178 |   describe('reset method', () => {
179 |     it('should clear all parameters', () => {
180 |       const result = builder
181 |         .add('first', 'value')
182 |         .add('second', 'value')
183 |         .reset()
184 |         .build();
185 |       expect(result).toBe('');
186 |     });
187 | 
188 |     it('should return QueryBuilder instance for chaining', () => {
189 |       const result = builder.reset();
190 |       expect(result).toBeInstanceOf(QueryBuilder);
191 |       expect(result).toBe(builder);
192 |     });
193 | 
194 |     it('should allow rebuilding after reset', () => {
195 |       const result = builder
196 |         .add('old', 'value')
197 |         .reset()
198 |         .add('new', 'value')
199 |         .build();
200 |       expect(result).toBe('new=value');
201 |     });
202 |   });
203 | 
204 |   describe('static fromObject method', () => {
205 |     it('should create query string from object', () => {
206 |       const params = {
207 |         search: 'test',
208 |         count: 10,
209 |         active: true,
210 |       };
211 |       const result = QueryBuilder.fromObject(params);
212 |       expect(result).toBe('search=test&count=10&active=true');
213 |     });
214 | 
215 |     it('should handle arrays correctly', () => {
216 |       const params = {
217 |         ids: ['1', '2', '3'],
218 |         refine: ['category=shirts', 'size=large'],
219 |       };
220 |       const result = QueryBuilder.fromObject(params);
221 |       expect(result).toBe('ids=1%2C2%2C3&refine=category%3Dshirts&refine=size%3Dlarge');
222 |     });
223 | 
224 |     it('should handle empty object', () => {
225 |       const result = QueryBuilder.fromObject({});
226 |       expect(result).toBe('');
227 |     });
228 | 
229 |     it('should handle complex OCAPI-style parameters', () => {
230 |       const params = {
231 |         q: 'mens shoes',
232 |         count: 25,
233 |         start: 50,
234 |         expand: ['images', 'variations', 'prices'],
235 |         refine: ['category=footwear', 'brand=nike', 'size=10'],
236 |         sort: 'price-asc',
237 |         currency: 'USD',
238 |         locale: 'en_US',
239 |       };
240 |       const result = QueryBuilder.fromObject(params);
241 | 
242 |       expect(result).toContain('q=mens+shoes');
243 |       expect(result).toContain('count=25');
244 |       expect(result).toContain('start=50');
245 |       expect(result).toContain('expand=images%2Cvariations%2Cprices');
246 |       expect(result).toContain('refine=category%3Dfootwear');
247 |       expect(result).toContain('refine=brand%3Dnike');
248 |       expect(result).toContain('refine=size%3D10');
249 |       expect(result).toContain('sort=price-asc');
250 |       expect(result).toContain('currency=USD');
251 |       expect(result).toContain('locale=en_US');
252 |     });
253 |   });
254 | 
255 |   describe('edge cases', () => {
256 |     it('should handle empty strings', () => {
257 |       const result = builder.add('empty', '').build();
258 |       expect(result).toBe('empty=');
259 |     });
260 | 
261 |     it('should handle zero values', () => {
262 |       const result = builder.add('zero', 0).build();
263 |       expect(result).toBe('zero=0');
264 |     });
265 | 
266 |     it('should handle false values', () => {
267 |       const result = builder.add('false', false).build();
268 |       expect(result).toBe('false=false');
269 |     });
270 | 
271 |     it('should handle arrays with empty strings', () => {
272 |       const result = builder.addArray('mixed', ['value', '', 'another']).build();
273 |       expect(result).toBe('mixed=value%2C%2Canother');
274 |     });
275 | 
276 |     it('should handle Unicode characters', () => {
277 |       const result = builder.add('unicode', 'café ñoño 中文').build();
278 |       expect(result).toBe('unicode=caf%C3%A9+%C3%B1o%C3%B1o+%E4%B8%AD%E6%96%87');
279 |     });
280 |   });
281 | });
282 | 
```
Page 16/61FirstPrevNextLast