#
tokens: 48905/50000 13/825 files (page 18/61)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 18 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

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

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class Appeasement
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.Extensible
  9 |   - dw.order.AbstractItemCtnr
 10 |     - dw.order.Appeasement
 11 | 
 12 | ## Description
 13 | 
 14 | The Appeasement represents a shopper request for an order credit. Example: The buyer finds any problem with the products but he agrees to preserve them, if he would be compensated, rather than return them. The Appeasement contains 1..n appeasement items. Each appeasement item is associated with one OrderItem usually representing an Order ProductLineItem. An Appeasement can have one of these status values: OPEN - the appeasement is open and appeasement items could be added to it COMPLETED - the appeasement is complete and it is not allowed to add new items to it, this is a precondition for refunding the customer for an appeasement. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.
 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 order 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 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 Appeasement Status COMPLETED
 53 | 
 54 | ### STATUS_OPEN
 55 | 
 56 | **Type:** String = "OPEN"
 57 | 
 58 | Constant for Appeasement Status OPEN
 59 | 
 60 | ## Properties
 61 | 
 62 | ### appeasementNumber
 63 | 
 64 | **Type:** String (Read Only)
 65 | 
 66 | The appeasement number.
 67 | 
 68 | ### invoice
 69 | 
 70 | **Type:** Invoice (Read Only)
 71 | 
 72 | Returns null or the previously created Invoice.
 73 | 
 74 | ### invoiceNumber
 75 | 
 76 | **Type:** String (Read Only)
 77 | 
 78 | Returns null or the invoice-number.
 79 | 
 80 | ### items
 81 | 
 82 | **Type:** FilteringCollection (Read Only)
 83 | 
 84 | A filtering collection of the appeasement items belonging to the appeasement.
 85 |  
 86 |  This FilteringCollection could be sorted / filtered using:
 87 |  
 88 |  FilteringCollection.sort(Object) with ORDERBY_ITEMID
 89 |  FilteringCollection.sort(Object) with ORDERBY_ITEMPOSITION
 90 |  FilteringCollection.sort(Object) with ORDERBY_UNSORTED
 91 |  FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS
 92 |  FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS
 93 | 
 94 | ### reasonCode
 95 | 
 96 | **Type:** EnumValue
 97 | 
 98 | The reason code for the appeasement. The list of reason codes can be updated
 99 |  by updating meta-data for Appeasement.
100 | 
101 | ### reasonNote
102 | 
103 | **Type:** String
104 | 
105 | The reason note for the appeasement.
106 | 
107 | ### status
108 | 
109 | **Type:** EnumValue
110 | 
111 | Gets the status of this appeasement.
112 |  The possible values are STATUS_OPEN, STATUS_COMPLETED.
113 | 
114 | ## Constructor Summary
115 | 
116 | ## Method Summary
117 | 
118 | ### addItems
119 | 
120 | **Signature:** `addItems(totalAmount : Money, orderItems : List) : void`
121 | 
122 | Creates appeasement items corresponding to certain order items and adds them to the appeasement.
123 | 
124 | ### createInvoice
125 | 
126 | **Signature:** `createInvoice() : Invoice`
127 | 
128 | Creates a new Invoice based on this Appeasement.
129 | 
130 | ### createInvoice
131 | 
132 | **Signature:** `createInvoice(invoiceNumber : String) : Invoice`
133 | 
134 | Creates a new Invoice based on this Appeasement.
135 | 
136 | ### getAppeasementNumber
137 | 
138 | **Signature:** `getAppeasementNumber() : String`
139 | 
140 | Returns the appeasement number.
141 | 
142 | ### getInvoice
143 | 
144 | **Signature:** `getInvoice() : Invoice`
145 | 
146 | Returns null or the previously created Invoice.
147 | 
148 | ### getInvoiceNumber
149 | 
150 | **Signature:** `getInvoiceNumber() : String`
151 | 
152 | Returns null or the invoice-number.
153 | 
154 | ### getItems
155 | 
156 | **Signature:** `getItems() : FilteringCollection`
157 | 
158 | Returns a filtering collection of the appeasement items belonging to the appeasement.
159 | 
160 | ### getReasonCode
161 | 
162 | **Signature:** `getReasonCode() : EnumValue`
163 | 
164 | Returns the reason code for the appeasement.
165 | 
166 | ### getReasonNote
167 | 
168 | **Signature:** `getReasonNote() : String`
169 | 
170 | Returns the reason note for the appeasement.
171 | 
172 | ### getStatus
173 | 
174 | **Signature:** `getStatus() : EnumValue`
175 | 
176 | Gets the status of this appeasement. The possible values are STATUS_OPEN, STATUS_COMPLETED.
177 | 
178 | ### setReasonCode
179 | 
180 | **Signature:** `setReasonCode(reasonCode : String) : void`
181 | 
182 | Set the reason code for the appeasement.
183 | 
184 | ### setReasonNote
185 | 
186 | **Signature:** `setReasonNote(reasonNote : String) : void`
187 | 
188 | Sets the reason note for the appeasement.
189 | 
190 | ### setStatus
191 | 
192 | **Signature:** `setStatus(appeasementStatus : String) : void`
193 | 
194 | Sets the appeasement status.
195 | 
196 | ## Method Detail
197 | 
198 | ## Method Details
199 | 
200 | ### addItems
201 | 
202 | **Signature:** `addItems(totalAmount : Money, orderItems : List) : void`
203 | 
204 | **Description:** Creates appeasement items corresponding to certain order items and adds them to the appeasement.
205 | 
206 | **Parameters:**
207 | 
208 | - `totalAmount`: the appeasement amount corresponding to the provided order items; this amount is the net price when the order is net based and respectively - gross price when the order is gross based
209 | - `orderItems`: the order items for which appeasement items should be created
210 | 
211 | ---
212 | 
213 | ### createInvoice
214 | 
215 | **Signature:** `createInvoice() : Invoice`
216 | 
217 | **Description:** Creates a new Invoice based on this Appeasement. The appeasement-number will be used as the invoice-number. The method must not be called more than once for an Appeasement, nor may 2 invoices exist with the same invoice-number. The new Invoice is a credit-invoice with a Invoice.STATUS_NOT_PAID status, and should be passed to the refund payment-hook in a separate database transaction for processing.
218 | 
219 | **Returns:**
220 | 
221 | the created invoice
222 | 
223 | ---
224 | 
225 | ### createInvoice
226 | 
227 | **Signature:** `createInvoice(invoiceNumber : String) : Invoice`
228 | 
229 | **Description:** Creates a new Invoice based on this Appeasement. The invoice-number must be specified as an argument. The method must not be called more than once for an Appeasement, nor may 2 invoices exist with the same invoice-number. The new Invoice is a credit-invoice with a Invoice.STATUS_NOT_PAID status, and should be passed to the refund payment-hook in a separate database transaction for processing.
230 | 
231 | **Parameters:**
232 | 
233 | - `invoiceNumber`: the invoice-number to be used in the appeasement creation process
234 | 
235 | **Returns:**
236 | 
237 | the created invoice
238 | 
239 | ---
240 | 
241 | ### getAppeasementNumber
242 | 
243 | **Signature:** `getAppeasementNumber() : String`
244 | 
245 | **Description:** Returns the appeasement number.
246 | 
247 | **Returns:**
248 | 
249 | the appeasement number
250 | 
251 | ---
252 | 
253 | ### getInvoice
254 | 
255 | **Signature:** `getInvoice() : Invoice`
256 | 
257 | **Description:** Returns null or the previously created Invoice.
258 | 
259 | **Returns:**
260 | 
261 | null or the previously created invoice
262 | 
263 | **See Also:**
264 | 
265 | createInvoice(String)
266 | 
267 | ---
268 | 
269 | ### getInvoiceNumber
270 | 
271 | **Signature:** `getInvoiceNumber() : String`
272 | 
273 | **Description:** Returns null or the invoice-number.
274 | 
275 | **Returns:**
276 | 
277 | null or the number of the previously created invoice
278 | 
279 | **See Also:**
280 | 
281 | createInvoice(String)
282 | 
283 | ---
284 | 
285 | ### getItems
286 | 
287 | **Signature:** `getItems() : FilteringCollection`
288 | 
289 | **Description:** Returns a filtering collection of the appeasement items belonging to the appeasement. This FilteringCollection could be sorted / filtered using: FilteringCollection.sort(Object) with ORDERBY_ITEMID FilteringCollection.sort(Object) with ORDERBY_ITEMPOSITION FilteringCollection.sort(Object) with ORDERBY_UNSORTED FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS
290 | 
291 | **Returns:**
292 | 
293 | the filtering collection of the appeasement items
294 | 
295 | ---
296 | 
297 | ### getReasonCode
298 | 
299 | **Signature:** `getReasonCode() : EnumValue`
300 | 
301 | **Description:** Returns the reason code for the appeasement. The list of reason codes can be updated by updating meta-data for Appeasement.
302 | 
303 | **Returns:**
304 | 
305 | the appeasement reason code
306 | 
307 | ---
308 | 
309 | ### getReasonNote
310 | 
311 | **Signature:** `getReasonNote() : String`
312 | 
313 | **Description:** Returns the reason note for the appeasement.
314 | 
315 | **Returns:**
316 | 
317 | the reason note or null
318 | 
319 | ---
320 | 
321 | ### getStatus
322 | 
323 | **Signature:** `getStatus() : EnumValue`
324 | 
325 | **Description:** Gets the status of this appeasement. The possible values are STATUS_OPEN, STATUS_COMPLETED.
326 | 
327 | **Returns:**
328 | 
329 | the status
330 | 
331 | ---
332 | 
333 | ### setReasonCode
334 | 
335 | **Signature:** `setReasonCode(reasonCode : String) : void`
336 | 
337 | **Description:** Set the reason code for the appeasement. The list of reason codes can be updated by updating meta-data for Appeasement.
338 | 
339 | **Parameters:**
340 | 
341 | - `reasonCode`: the reason code to set
342 | 
343 | ---
344 | 
345 | ### setReasonNote
346 | 
347 | **Signature:** `setReasonNote(reasonNote : String) : void`
348 | 
349 | **Description:** Sets the reason note for the appeasement.
350 | 
351 | **Parameters:**
352 | 
353 | - `reasonNote`: the reason note for the appeasement to set
354 | 
355 | ---
356 | 
357 | ### setStatus
358 | 
359 | **Signature:** `setStatus(appeasementStatus : String) : void`
360 | 
361 | **Description:** Sets the appeasement status. The possible values are STATUS_OPEN, STATUS_COMPLETED. When set to status COMPLETED, only the the custom attributes of its appeasement items can be changed.
362 | 
363 | **Parameters:**
364 | 
365 | - `appeasementStatus`: the appeasement status to set.
366 | 
367 | ---
```

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

```json
  1 | {
  2 |   "_v": "23.2",
  3 |   "_type": "code_version_result",
  4 |   "count": 19,
  5 |   "data": [
  6 |     {
  7 |       "_type": "code_version",
  8 |       "activation_time": "2025-09-22T10:00:00.000Z",
  9 |       "active": true,
 10 |       "cartridges": ["reset_cartridge"],
 11 |       "compatibility_mode": "22.7",
 12 |       "id": "reset_version",
 13 |       "last_modification_time": "2025-09-22T10:00:00Z",
 14 |       "rollback": false,
 15 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reset_version"
 16 |     },
 17 |     {
 18 |       "_type": "code_version",
 19 |       "active": false,
 20 |       "cartridges": [
 21 |         "app_storefront_base",
 22 |         "bm_app_storefront_base",
 23 |         "bm_fastforward_backinstock",
 24 |         "bm_fastforward_base",
 25 |         "bm_fastforward_configurator",
 26 |         "bm_fastforward_configurator_pd",
 27 |         "bm_fastforward_customer_insights",
 28 |         "bm_fastforward_google",
 29 |         "bm_fastforward_gpt",
 30 |         "bm_fastforward_insights",
 31 |         "bm_fastforward_managed_runtime",
 32 |         "bm_fastforward_reviews",
 33 |         "bm_fastforward_starter_kit",
 34 |         "bm_fastforward_starter_kit_pd",
 35 |         "bm_fastforward_store_manager",
 36 |         "bm_fastforward_tools",
 37 |         "chance",
 38 |         "date-fns",
 39 |         "fast-xml-parser",
 40 |         "google",
 41 |         "jsPDF",
 42 |         "lodash",
 43 |         "modules",
 44 |         "moment",
 45 |         "openai",
 46 |         "plugin_backinstock",
 47 |         "plugin_demo_mcp",
 48 |         "plugin_demo_mcp_2",
 49 |         "plugin_fastforward_configurator",
 50 |         "plugin_fastforward_starter_kit",
 51 |         "plugin_mcp_example",
 52 |         "plugin_openai",
 53 |         "plugin_testlibraries",
 54 |         "ramda",
 55 |         "rhinodb",
 56 |         "salesforce-managed-runtime"
 57 |       ],
 58 |       "compatibility_mode": "22.7",
 59 |       "id": "SFRA_AP_01_24_2024",
 60 |       "last_modification_time": "2025-09-18T13:18:21Z",
 61 |       "rollback": false,
 62 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/SFRA_AP_01_24_2024"
 63 |     },
 64 |     {
 65 |       "_type": "code_version",
 66 |       "active": false,
 67 |       "cartridges": [
 68 |         "bm_fastforward_base",
 69 |         "bm_fastforward_configurator",
 70 |         "bm_fastforward_configurator_pd",
 71 |         "plugin_fastforward_configurator"
 72 |       ],
 73 |       "compatibility_mode": "22.7",
 74 |       "id": "SFRA_AP_01_24_2024 ",
 75 |       "last_modification_time": "2025-03-05T14:55:22Z",
 76 |       "rollback": false,
 77 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/SFRA_AP_01_24_2024 "
 78 |     },
 79 |     {
 80 |       "_type": "code_version",
 81 |       "activation_time": "2025-09-18T13:41:07Z",
 82 |       "active": false,
 83 |       "cartridges": [
 84 |         "bm_fastforward_base",
 85 |         "bm_fastforward_configurator", 
 86 |         "bm_fastforward_configurator_pd",
 87 |         "plugin_fastforward_configurator"
 88 |       ],
 89 |       "compatibility_mode": "22.7",
 90 |       "id": "version1",
 91 |       "last_modification_time": "2025-03-05T14:12:00Z",
 92 |       "rollback": true,
 93 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/version1"
 94 |     },
 95 |     {
 96 |       "_type": "code_version",
 97 |       "active": false,
 98 |       "cartridges": ["test_cartridge"],
 99 |       "compatibility_mode": "22.7",
100 |       "id": "test_activation",
101 |       "last_modification_time": "2025-09-22T12:00:00Z",
102 |       "rollback": false,
103 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/test_activation"
104 |     },
105 |     {
106 |       "_type": "code_version",
107 |       "active": false,
108 |       "cartridges": ["simple_cartridge"],
109 |       "compatibility_mode": "22.7",
110 |       "id": "simple_id",
111 |       "last_modification_time": "2025-09-22T12:00:00Z",
112 |       "rollback": false,
113 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/simple_id"
114 |     },
115 |     {
116 |       "_type": "code_version",
117 |       "active": false,
118 |       "cartridges": ["version_cartridge"],
119 |       "compatibility_mode": "22.7",
120 |       "id": "version-with-dashes",
121 |       "last_modification_time": "2025-09-22T12:00:00Z",
122 |       "rollback": false,
123 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/version-with-dashes"
124 |     },
125 |     {
126 |       "_type": "code_version",
127 |       "active": false,
128 |       "cartridges": ["version_cartridge"],
129 |       "compatibility_mode": "22.7",
130 |       "id": "version_with_underscores",
131 |       "last_modification_time": "2025-09-22T12:00:00Z",
132 |       "rollback": false,
133 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/version_with_underscores"
134 |     },
135 |     {
136 |       "_type": "code_version",
137 |       "active": false,
138 |       "cartridges": ["version_cartridge"],
139 |       "compatibility_mode": "22.7",
140 |       "id": "Version123",
141 |       "last_modification_time": "2025-09-22T12:00:00Z",
142 |       "rollback": false,
143 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/Version123"
144 |     },
145 |     {
146 |       "_type": "code_version",
147 |       "active": false,
148 |       "cartridges": ["test_cartridge"],
149 |       "compatibility_mode": "22.7",
150 |       "id": "seq_test_1",
151 |       "last_modification_time": "2025-09-22T12:00:00Z",
152 |       "rollback": false,
153 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/seq_test_1"
154 |     },
155 |     {
156 |       "_type": "code_version",
157 |       "active": false,
158 |       "cartridges": ["test_cartridge"],
159 |       "compatibility_mode": "22.7",
160 |       "id": "seq_test_2",
161 |       "last_modification_time": "2025-09-22T12:00:00Z",
162 |       "rollback": false,
163 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/seq_test_2"
164 |     },
165 |     {
166 |       "_type": "code_version",
167 |       "active": false,
168 |       "cartridges": ["test_cartridge"],
169 |       "compatibility_mode": "22.7",
170 |       "id": "seq_test_3",
171 |       "last_modification_time": "2025-09-22T12:00:00Z",
172 |       "rollback": false,
173 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/seq_test_3"
174 |     },
175 |     {
176 |       "_type": "code_version",
177 |       "active": false,
178 |       "cartridges": ["test_cartridge"],
179 |       "compatibility_mode": "22.7",
180 |       "id": "workflow_test_version",
181 |       "last_modification_time": "2025-09-22T12:00:00Z",
182 |       "rollback": false,
183 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/workflow_test_version"
184 |     },
185 |     {
186 |       "_type": "code_version",
187 |       "active": false,
188 |       "cartridges": ["recovery_cartridge"],
189 |       "compatibility_mode": "22.7",
190 |       "id": "recovery_test",
191 |       "last_modification_time": "2025-09-22T12:00:00Z",
192 |       "rollback": false,
193 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/recovery_test"
194 |     },
195 |     {
196 |       "_type": "code_version",
197 |       "active": false,
198 |       "cartridges": ["reliability_cartridge"],
199 |       "compatibility_mode": "22.7",
200 |       "id": "reliability_test_0",
201 |       "last_modification_time": "2025-09-22T12:00:00Z",
202 |       "rollback": false,
203 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reliability_test_0"
204 |     },
205 |     {
206 |       "_type": "code_version",
207 |       "active": false,
208 |       "cartridges": ["reliability_cartridge"],
209 |       "compatibility_mode": "22.7",
210 |       "id": "reliability_test_1",
211 |       "last_modification_time": "2025-09-22T12:00:00Z",
212 |       "rollback": false,
213 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reliability_test_1"
214 |     },
215 |     {
216 |       "_type": "code_version",
217 |       "active": false,
218 |       "cartridges": ["reliability_cartridge"],
219 |       "compatibility_mode": "22.7",
220 |       "id": "reliability_test_2",
221 |       "last_modification_time": "2025-09-22T12:00:00Z",
222 |       "rollback": false,
223 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/reliability_test_2"
224 |     },
225 |     {
226 |       "_type": "code_version",
227 |       "active": false,
228 |       "cartridges": ["deploy_cartridge"],
229 |       "compatibility_mode": "22.7",
230 |       "id": "deployment_v1.2.3",
231 |       "last_modification_time": "2025-09-22T12:00:00Z",
232 |       "rollback": false,
233 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/deployment_v1.2.3"
234 |     },
235 |     {
236 |       "_type": "code_version",
237 |       "active": false,
238 |       "cartridges": ["deploy_cartridge"],
239 |       "compatibility_mode": "22.7",
240 |       "id": "deployment_simulation_v1",
241 |       "last_modification_time": "2025-09-22T12:00:00Z",
242 |       "rollback": false,
243 |       "web_dav_url": "https://development-na01-sandbox.dx.commercecloud.salesforce.com/on/demandware.servlet/webdav/Sites/Cartridges/deployment_simulation_v1"
244 |     }
245 |   ],
246 |   "total": 19
247 | }
248 | 
```

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

```markdown
  1 | ## Package: dw.util
  2 | 
  3 | # Class Decimal
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.util.Decimal
  9 | 
 10 | ## Description
 11 | 
 12 | The Decimal class is a helper class to perform decimal arithmetic in scripts and to represent a decimal number with arbitrary length. The decimal class avoids arithmetic errors, which are typical for calculating with floating numbers, that are based on a binary mantissa. The class is designed in a way that it can be used very similar to a desktop calculator. var d = new Decimal( 10.0 ); var result = d.add( 2.0 ).sub( 3.0 ).get(); The above code will return 9 as result.
 13 | 
 14 | ## Constructor Summary
 15 | 
 16 | Decimal() Constructs a new Decimal with the value 0.
 17 | 
 18 | Decimal(value : Number) Constructs a new decimal using the specified Number value.
 19 | 
 20 | Decimal(value : BigInt) Constructs a new decimal using the specified BigInt value.
 21 | 
 22 | Decimal(value : String) Constructs a new Decimal using the specified string representation of a number.
 23 | 
 24 | ## Method Summary
 25 | 
 26 | ### abs
 27 | 
 28 | **Signature:** `abs() : Decimal`
 29 | 
 30 | Returns a new Decimal with the absolute value of this Decimal.
 31 | 
 32 | ### add
 33 | 
 34 | **Signature:** `add(value : Number) : Decimal`
 35 | 
 36 | Adds a Number value to this Decimal and returns the new Decimal.
 37 | 
 38 | ### add
 39 | 
 40 | **Signature:** `add(value : Decimal) : Decimal`
 41 | 
 42 | Adds a Decimal value to this Decimal and returns the new Decimal.
 43 | 
 44 | ### addPercent
 45 | 
 46 | **Signature:** `addPercent(value : Number) : Decimal`
 47 | 
 48 | Adds a percentage value to the current value of the decimal.
 49 | 
 50 | ### addPercent
 51 | 
 52 | **Signature:** `addPercent(value : Decimal) : Decimal`
 53 | 
 54 | Adds a percentage value to the current value of the decimal.
 55 | 
 56 | ### divide
 57 | 
 58 | **Signature:** `divide(value : Number) : Decimal`
 59 | 
 60 | Divides the specified Number value with this decimal and returns the new decimal.
 61 | 
 62 | ### divide
 63 | 
 64 | **Signature:** `divide(value : Decimal) : Decimal`
 65 | 
 66 | Divides the specified Decimal value with this decimal and returns the new decimal.
 67 | 
 68 | ### equals
 69 | 
 70 | **Signature:** `equals(other : Object) : boolean`
 71 | 
 72 | Compares two decimal values whether they are equivalent.
 73 | 
 74 | ### get
 75 | 
 76 | **Signature:** `get() : Number`
 77 | 
 78 | Returns the value of the Decimal as a Number.
 79 | 
 80 | ### hashCode
 81 | 
 82 | **Signature:** `hashCode() : Number`
 83 | 
 84 | Calculates the hash code for this decimal;
 85 | 
 86 | ### multiply
 87 | 
 88 | **Signature:** `multiply(value : Number) : Decimal`
 89 | 
 90 | Multiples the specified Number value with this Decimal and returns the new Decimal.
 91 | 
 92 | ### multiply
 93 | 
 94 | **Signature:** `multiply(value : Decimal) : Decimal`
 95 | 
 96 | Multiples the specified Decimal value with this Decimal and returns the new Decimal.
 97 | 
 98 | ### negate
 99 | 
100 | **Signature:** `negate() : Decimal`
101 | 
102 | Returns a new Decimal with the negated value of this Decimal.
103 | 
104 | ### round
105 | 
106 | **Signature:** `round(decimals : Number) : Decimal`
107 | 
108 | Rounds the current value of the decimal using the specified number of decimals.
109 | 
110 | ### subtract
111 | 
112 | **Signature:** `subtract(value : Number) : Decimal`
113 | 
114 | Subtracts the specified Number value from this Decimal and returns the new Decimal.
115 | 
116 | ### subtract
117 | 
118 | **Signature:** `subtract(value : Decimal) : Decimal`
119 | 
120 | Subtracts the specified Decimal value from this Decimal and returns the new Decimal.
121 | 
122 | ### subtractPercent
123 | 
124 | **Signature:** `subtractPercent(value : Number) : Decimal`
125 | 
126 | Subtracts a percentage value from the current value of the decimal.
127 | 
128 | ### subtractPercent
129 | 
130 | **Signature:** `subtractPercent(value : Decimal) : Decimal`
131 | 
132 | Subtracts a percentage value from the current value of the decimal.
133 | 
134 | ### toString
135 | 
136 | **Signature:** `toString() : String`
137 | 
138 | Returns a string representation of this object.
139 | 
140 | ### valueOf
141 | 
142 | **Signature:** `valueOf() : Object`
143 | 
144 | The valueOf() method is called by the ECMAScript interpret to return the "natural" value of an object.
145 | 
146 | ## Constructor Detail
147 | 
148 | ## Method Detail
149 | 
150 | ## Method Details
151 | 
152 | ### abs
153 | 
154 | **Signature:** `abs() : Decimal`
155 | 
156 | **Description:** Returns a new Decimal with the absolute value of this Decimal.
157 | 
158 | **Returns:**
159 | 
160 | the new Decimal
161 | 
162 | ---
163 | 
164 | ### add
165 | 
166 | **Signature:** `add(value : Number) : Decimal`
167 | 
168 | **Description:** Adds a Number value to this Decimal and returns the new Decimal.
169 | 
170 | **Parameters:**
171 | 
172 | - `value`: the value to add to this decimal.
173 | 
174 | **Returns:**
175 | 
176 | the new decimal with the value added.
177 | 
178 | ---
179 | 
180 | ### add
181 | 
182 | **Signature:** `add(value : Decimal) : Decimal`
183 | 
184 | **Description:** Adds a Decimal value to this Decimal and returns the new Decimal.
185 | 
186 | **Parameters:**
187 | 
188 | - `value`: the value to add to this decimal.
189 | 
190 | **Returns:**
191 | 
192 | the new decimal with the value added.
193 | 
194 | ---
195 | 
196 | ### addPercent
197 | 
198 | **Signature:** `addPercent(value : Number) : Decimal`
199 | 
200 | **Description:** Adds a percentage value to the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.
201 | 
202 | **Parameters:**
203 | 
204 | - `value`: the value to add.
205 | 
206 | **Returns:**
207 | 
208 | a new decimal with the added percentage value.
209 | 
210 | ---
211 | 
212 | ### addPercent
213 | 
214 | **Signature:** `addPercent(value : Decimal) : Decimal`
215 | 
216 | **Description:** Adds a percentage value to the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.
217 | 
218 | **Parameters:**
219 | 
220 | - `value`: the value to add.
221 | 
222 | **Returns:**
223 | 
224 | a new decimal with the added percentage value.
225 | 
226 | ---
227 | 
228 | ### divide
229 | 
230 | **Signature:** `divide(value : Number) : Decimal`
231 | 
232 | **Description:** Divides the specified Number value with this decimal and returns the new decimal. When performing the division, 34 digits precision and a rounding mode of HALF_EVEN is used to prevent quotients with nonterminating decimal expansions.
233 | 
234 | **Parameters:**
235 | 
236 | - `value`: the value to use to divide this decimal.
237 | 
238 | **Returns:**
239 | 
240 | the new decimal.
241 | 
242 | ---
243 | 
244 | ### divide
245 | 
246 | **Signature:** `divide(value : Decimal) : Decimal`
247 | 
248 | **Description:** Divides the specified Decimal value with this decimal and returns the new decimal. When performing the division, 34 digits precision and a rounding mode of HALF_EVEN is used to prevent quotients with nonterminating decimal expansions.
249 | 
250 | **Parameters:**
251 | 
252 | - `value`: the value to use to divide this decimal.
253 | 
254 | **Returns:**
255 | 
256 | the new decimal.
257 | 
258 | ---
259 | 
260 | ### equals
261 | 
262 | **Signature:** `equals(other : Object) : boolean`
263 | 
264 | **Description:** Compares two decimal values whether they are equivalent.
265 | 
266 | **Parameters:**
267 | 
268 | - `other`: the object to comapre against this decimal.
269 | 
270 | ---
271 | 
272 | ### get
273 | 
274 | **Signature:** `get() : Number`
275 | 
276 | **Description:** Returns the value of the Decimal as a Number.
277 | 
278 | **Returns:**
279 | 
280 | the value of the Decimal.
281 | 
282 | ---
283 | 
284 | ### hashCode
285 | 
286 | **Signature:** `hashCode() : Number`
287 | 
288 | **Description:** Calculates the hash code for this decimal;
289 | 
290 | ---
291 | 
292 | ### multiply
293 | 
294 | **Signature:** `multiply(value : Number) : Decimal`
295 | 
296 | **Description:** Multiples the specified Number value with this Decimal and returns the new Decimal.
297 | 
298 | **Parameters:**
299 | 
300 | - `value`: the value to multiply with this decimal.
301 | 
302 | **Returns:**
303 | 
304 | the new decimal.
305 | 
306 | ---
307 | 
308 | ### multiply
309 | 
310 | **Signature:** `multiply(value : Decimal) : Decimal`
311 | 
312 | **Description:** Multiples the specified Decimal value with this Decimal and returns the new Decimal.
313 | 
314 | **Parameters:**
315 | 
316 | - `value`: the value to multiply with this decimal.
317 | 
318 | **Returns:**
319 | 
320 | the new decimal.
321 | 
322 | ---
323 | 
324 | ### negate
325 | 
326 | **Signature:** `negate() : Decimal`
327 | 
328 | **Description:** Returns a new Decimal with the negated value of this Decimal.
329 | 
330 | **Returns:**
331 | 
332 | the new Decimal
333 | 
334 | ---
335 | 
336 | ### round
337 | 
338 | **Signature:** `round(decimals : Number) : Decimal`
339 | 
340 | **Description:** Rounds the current value of the decimal using the specified number of decimals. The parameter specifies the number of digest after the decimal point.
341 | 
342 | **Parameters:**
343 | 
344 | - `decimals`: the number of decimals to use.
345 | 
346 | **Returns:**
347 | 
348 | the decimal that has been rounded.
349 | 
350 | ---
351 | 
352 | ### subtract
353 | 
354 | **Signature:** `subtract(value : Number) : Decimal`
355 | 
356 | **Description:** Subtracts the specified Number value from this Decimal and returns the new Decimal.
357 | 
358 | **Parameters:**
359 | 
360 | - `value`: the value to add to this decimal.
361 | 
362 | **Returns:**
363 | 
364 | the new decimal with the value subtraced.
365 | 
366 | ---
367 | 
368 | ### subtract
369 | 
370 | **Signature:** `subtract(value : Decimal) : Decimal`
371 | 
372 | **Description:** Subtracts the specified Decimal value from this Decimal and returns the new Decimal.
373 | 
374 | **Parameters:**
375 | 
376 | - `value`: the value to add to this decimal.
377 | 
378 | **Returns:**
379 | 
380 | the new decimal with the value subtraced.
381 | 
382 | ---
383 | 
384 | ### subtractPercent
385 | 
386 | **Signature:** `subtractPercent(value : Number) : Decimal`
387 | 
388 | **Description:** Subtracts a percentage value from the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.
389 | 
390 | **Parameters:**
391 | 
392 | - `value`: the value to subtract.
393 | 
394 | **Returns:**
395 | 
396 | a new decimal with the subtracted percentage value.
397 | 
398 | ---
399 | 
400 | ### subtractPercent
401 | 
402 | **Signature:** `subtractPercent(value : Decimal) : Decimal`
403 | 
404 | **Description:** Subtracts a percentage value from the current value of the decimal. For example a value of 10 represent 10% or a value of 85 represents 85%.
405 | 
406 | **Parameters:**
407 | 
408 | - `value`: the value to subtract.
409 | 
410 | **Returns:**
411 | 
412 | a new decimal with the subtracted percentage value.
413 | 
414 | ---
415 | 
416 | ### toString
417 | 
418 | **Signature:** `toString() : String`
419 | 
420 | **Description:** Returns a string representation of this object.
421 | 
422 | **Returns:**
423 | 
424 | a string representation of this object.
425 | 
426 | ---
427 | 
428 | ### valueOf
429 | 
430 | **Signature:** `valueOf() : Object`
431 | 
432 | **Description:** The valueOf() method is called by the ECMAScript interpret to return the "natural" value of an object. The Decimal object returns its current value as number. With this behavior script snippets can be written like: var d = new Decimal( 10.0 ); var x = 1.0 + d.add( 2.0 ); where x will be at the end 13.0.
433 | 
434 | **Returns:**
435 | 
436 | the value of this object.
437 | 
438 | ---
```

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

```javascript
  1 | #!/usr/bin/env node
  2 | 
  3 | /**
  4 |  * Search Index Generator for SFCC Development MCP Server Documentation
  5 |  *
  6 |  * This script automatically generates a search index by parsing React components
  7 |  * and extracting searchable content. It replaces the manually maintained search
  8 |  * index with an automatically generated one during the build process.
  9 |  */
 10 | 
 11 | import fs from 'fs';
 12 | import path from 'path';
 13 | import { fileURLToPath } from 'url';
 14 | 
 15 | const __filename = fileURLToPath(import.meta.url);
 16 | const __dirname = path.dirname(__filename);
 17 | 
 18 | // Configuration
 19 | const PAGES_DIR = path.join(__dirname, '../pages');
 20 | const OUTPUT_FILE = path.join(__dirname, '../src/generated-search-index.ts');
 21 | 
 22 | /**
 23 |  * Route mappings for pages to their URL paths
 24 |  */
 25 | const ROUTE_MAPPINGS = {
 26 |   'HomePage.tsx': '/',
 27 |   'ConfigurationPage.tsx': '/configuration/',
 28 |   'AIInterfacesPage.tsx': '/ai-interfaces/',
 29 |   'FeaturesPage.tsx': '/features/',
 30 |   'ToolsPage.tsx': '/tools/',
 31 |   'ExamplesPage.tsx': '/examples/',
 32 |   'SecurityPage.tsx': '/security/',
 33 |   'DevelopmentPage.tsx': '/development/',
 34 |   'TroubleshootingPage.tsx': '/troubleshooting/',
 35 | };
 36 | 
 37 | /**
 38 |  * Extract content from JSX strings and React elements
 39 |  */
 40 | function extractTextFromJSX(content) {
 41 |   const textContent = [];
 42 | 
 43 |   // Remove JSX tags but keep the text content
 44 |   const cleanContent = content
 45 |     // Remove comments
 46 |     .replace(/\/\*[\s\S]*?\*\//g, '')
 47 |     .replace(/\/\/.*$/gm, '')
 48 |     // Remove import statements
 49 |     .replace(/^import\s+.*$/gm, '')
 50 |     // Remove className and other JSX attributes
 51 |     .replace(/className=["'`][^"'`]*["'`]/g, '')
 52 |     .replace(/\w+={[^}]*}/g, '');
 53 | 
 54 |   // Extract text between JSX tags
 55 |   const textRegex = />([^<]+)</g;
 56 |   let match;
 57 |   while ((match = textRegex.exec(cleanContent)) !== null) {
 58 |     const text = match[1].trim();
 59 |     // Filter out empty strings, JSX expressions, and very short text
 60 |     if (text &&
 61 |         !text.startsWith('{') &&
 62 |         !text.includes('className') &&
 63 |         !text.includes('src=') &&
 64 |         text.length > 3 &&
 65 |         !/^[{\s}]*$/.test(text)) {
 66 |       textContent.push(text);
 67 |     }
 68 |   }
 69 | 
 70 |   // Also extract string literals that might contain content
 71 |   const stringRegex = /["'`]([^"'`{]+)["'`]/g;
 72 |   while ((match = stringRegex.exec(cleanContent)) !== null) {
 73 |     const text = match[1].trim();
 74 |     if (text &&
 75 |         text.length > 10 &&
 76 |         !text.includes('className') &&
 77 |         !text.includes('src=') &&
 78 |         !text.includes('href=') &&
 79 |         !text.startsWith('http') &&
 80 |         (!text.includes('.') || text.includes(' '))) { // Allow sentences with periods
 81 |       textContent.push(text);
 82 |     }
 83 |   }
 84 | 
 85 |   // Clean up the extracted text
 86 |   return textContent
 87 |     .map(text => text
 88 |       .replace(/\s+/g, ' ') // Normalize whitespace
 89 |       .replace(/[{}]/g, '') // Remove remaining braces
 90 |       .trim())
 91 |     .filter(text => text.length > 5)
 92 |     .join(' ');
 93 | }
 94 | 
 95 | /**
 96 |  * Extract headings and sections from a React component
 97 |  */
 98 | function parseReactComponent(filePath, content) {
 99 |   const results = [];
100 | 
101 |   // Get the page title from the route mapping
102 |   const relativePath = path.relative(PAGES_DIR, filePath);
103 |   const routePath = ROUTE_MAPPINGS[relativePath];
104 | 
105 |   if (!routePath) {
106 |     console.warn(`Warning: No route mapping found for ${relativePath}`);
107 |     return results;
108 |   }
109 | 
110 |   // Extract page title from component name or H1 tags
111 |   let pageTitle = 'Unknown Page';
112 |   const titleMatch = content.match(/pageTitle\s*[:=]\s*["'`]([^"'`]+)["'`]/);
113 |   if (titleMatch) {
114 |     pageTitle = titleMatch[1];
115 |   } else {
116 |     // Fallback: extract from H1 tags
117 |     const h1Match = content.match(/<h1[^>]*>([^<]+)<\/h1>/i);
118 |     if (h1Match) {
119 |       pageTitle = h1Match[1].replace(/\{[^}]*\}/g, '').trim();
120 |     }
121 |   }
122 | 
123 |   // Extract sections based on headings (H1, H2, H3 components and regular h1-h3 tags)
124 |   const jsxComponentPattern = /<H([123])[^>]*id=["']([^"']+)["'][^>]*>([^<]+)<\/H[123]>/gi;
125 |   const regularHeadingPattern = /<(h[1-3])[^>]*(?:id=["']([^"']+)["'])?[^>]*>([^<]+)<\/(h[1-3])>/gi;
126 |   const sections = [];
127 |   let match;
128 | 
129 |   // First, extract JSX component headings (H1, H2, H3)
130 |   while ((match = jsxComponentPattern.exec(content)) !== null) {
131 |     const heading = match[3].trim();
132 |     const headingId = match[2];
133 |     const level = parseInt(match[1]);
134 | 
135 |     if (heading && heading.length > 1) {
136 |       sections.push({
137 |         heading,
138 |         headingId,
139 |         level,
140 |         position: match.index,
141 |       });
142 |     }
143 |   }
144 | 
145 |   // Reset regex for next pass
146 |   regularHeadingPattern.lastIndex = 0;
147 | 
148 |   // Then, extract regular HTML headings
149 |   while ((match = regularHeadingPattern.exec(content)) !== null) {
150 |     const heading = match[3].replace(/\{[^}]*\}/g, '').trim();
151 |     const headingId = match[2];
152 |     const level = parseInt(match[1].charAt(1));
153 | 
154 |     if (heading && heading.length > 1 && !heading.includes('className')) {
155 |       sections.push({
156 |         heading,
157 |         headingId,
158 |         level,
159 |         position: match.index,
160 |       });
161 |     }
162 |   }
163 | 
164 |   // Sort sections by their position in the document
165 |   sections.sort((a, b) => a.position - b.position);
166 | 
167 |   // Filter out duplicate headings at the same position, preferring those with a headingId
168 |   const filteredSections = [];
169 |   for (let i = 0; i < sections.length; i++) {
170 |     const curr = sections[i];
171 |     const next = sections[i + 1];
172 |     if (
173 |       next &&
174 |       curr.position === next.position &&
175 |       curr.heading === next.heading
176 |     ) {
177 |       // Prefer the one with headingId
178 |       if (curr.headingId) {
179 |         filteredSections.push(curr);
180 |       } else if (next.headingId) {
181 |         filteredSections.push(next);
182 |       }
183 |       i++; // Skip the next one
184 |     } else {
185 |       filteredSections.push(curr);
186 |     }
187 |   }
188 | 
189 |   if (filteredSections.length === 0) {
190 |     // If no headings found, create a single entry for the whole page
191 |     const textContent = extractTextFromJSX(content);
192 |     results.push({
193 |       path: routePath,
194 |       pageTitle,
195 |       heading: pageTitle,
196 |       headingId: null, // No heading ID for fallback case
197 |       content: textContent.substring(0, 500), // Limit content length
198 |     });
199 |     return results;
200 |   }
201 | 
202 |   // Create search entries for each section
203 |   filteredSections.forEach((section, index) => {
204 |     const nextSection = filteredSections[index + 1];
205 |     const sectionStart = section.position;
206 |     const sectionEnd = nextSection ? nextSection.position : content.length;
207 | 
208 |     const sectionContent = content.substring(sectionStart, sectionEnd);
209 |     const textContent = extractTextFromJSX(sectionContent);
210 | 
211 |     if (textContent.trim().length > 10) { // Only include sections with meaningful content
212 |       results.push({
213 |         path: routePath,
214 |         pageTitle,
215 |         heading: section.heading,
216 |         headingId: section.headingId || null, // Ensure the property is always present
217 |         content: textContent.substring(0, 300), // Limit content length per section
218 |       });
219 |     }
220 |   });
221 | 
222 |   return results;
223 | }
224 | 
225 | /**
226 |  * Recursively find all React component files
227 |  */
228 | function findReactFiles(dir) {
229 |   const files = [];
230 | 
231 |   function traverse(currentDir) {
232 |     const entries = fs.readdirSync(currentDir, { withFileTypes: true });
233 | 
234 |     for (const entry of entries) {
235 |       const fullPath = path.join(currentDir, entry.name);
236 | 
237 |       if (entry.isDirectory()) {
238 |         traverse(fullPath);
239 |       } else if (entry.isFile() && entry.name.endsWith('.tsx')) {
240 |         files.push(fullPath);
241 |       }
242 |     }
243 |   }
244 | 
245 |   traverse(dir);
246 |   return files;
247 | }
248 | 
249 | /**
250 |  * Generate the search index
251 |  */
252 | function generateSearchIndex() {
253 |   console.log('🔍 Generating search index...');
254 | 
255 |   const reactFiles = findReactFiles(PAGES_DIR);
256 |   const searchIndex = [];
257 | 
258 |   console.log(`Found ${reactFiles.length} React files to process`);
259 | 
260 |   for (const filePath of reactFiles) {
261 |     try {
262 |       const content = fs.readFileSync(filePath, 'utf-8');
263 |       const entries = parseReactComponent(filePath, content);
264 |       searchIndex.push(...entries);
265 | 
266 |       console.log(`  ✓ Processed ${path.relative(PAGES_DIR, filePath)} (${entries.length} entries)`);
267 |     } catch (error) {
268 |       console.error(`  ✗ Failed to process ${filePath}:`, error.message);
269 |     }
270 |   }
271 | 
272 |   console.log(`Generated ${searchIndex.length} search entries`);
273 | 
274 |   return searchIndex;
275 | }
276 | 
277 | /**
278 |  * Write the generated index to a TypeScript file
279 |  */
280 | function writeSearchIndex(searchIndex) {
281 |   const outputDir = path.dirname(OUTPUT_FILE);
282 |   if (!fs.existsSync(outputDir)) {
283 |     fs.mkdirSync(outputDir, { recursive: true });
284 |   }
285 | 
286 |   const tsContent = `// This file is auto-generated by scripts/generate-search-index.js
287 | // Do not edit manually - changes will be overwritten during build
288 | 
289 | export interface SearchableItem {
290 |   path: string;
291 |   pageTitle: string;
292 |   heading: string;
293 |   headingId?: string;
294 |   content: string;
295 | }
296 | 
297 | export const GENERATED_SEARCH_INDEX: SearchableItem[] = ${JSON.stringify(searchIndex, null, 2)};
298 | `;
299 | 
300 |   fs.writeFileSync(OUTPUT_FILE, tsContent, 'utf-8');
301 |   console.log(`✅ Search index written to ${path.relative(process.cwd(), OUTPUT_FILE)}`);
302 | }
303 | 
304 | /**
305 |  * Main execution
306 |  */
307 | function main() {
308 |   try {
309 |     const searchIndex = generateSearchIndex();
310 |     writeSearchIndex(searchIndex);
311 |     console.log('🎉 Search index generation complete!');
312 |   } catch (error) {
313 |     console.error('❌ Failed to generate search index:', error);
314 |     process.exit(1);
315 |   }
316 | }
317 | 
318 | // Run if called directly
319 | if (import.meta.url === `file://${process.argv[1]}`) {
320 |   main();
321 | }
322 | 
323 | export { generateSearchIndex, writeSearchIndex };
324 | 
```

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

```markdown
  1 | ## Package: dw.util
  2 | 
  3 | # Class Assert
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.util.Assert
  9 | 
 10 | ## Description
 11 | 
 12 | The Assert class provides utility methods for assertion events.
 13 | 
 14 | ## Constructor Summary
 15 | 
 16 | ## Method Summary
 17 | 
 18 | ### areEqual
 19 | 
 20 | **Signature:** `static areEqual(arg1 : Object, arg2 : Object) : void`
 21 | 
 22 | Propagates an assertion if the specified objects are not equal.
 23 | 
 24 | ### areEqual
 25 | 
 26 | **Signature:** `static areEqual(arg1 : Object, arg2 : Object, msg : String) : void`
 27 | 
 28 | Propagates an assertion using the specified message if the specified objects are not equal.
 29 | 
 30 | ### areSame
 31 | 
 32 | **Signature:** `static areSame(arg1 : Object, arg2 : Object) : void`
 33 | 
 34 | Propagates an assertion if the specified objects are not the same.
 35 | 
 36 | ### areSame
 37 | 
 38 | **Signature:** `static areSame(arg1 : Object, arg2 : Object, msg : String) : void`
 39 | 
 40 | Propagates an assertion using the specified message if the specified objects are not the same.
 41 | 
 42 | ### fail
 43 | 
 44 | **Signature:** `static fail() : void`
 45 | 
 46 | Propagates a failure assertion.
 47 | 
 48 | ### fail
 49 | 
 50 | **Signature:** `static fail(msg : String) : void`
 51 | 
 52 | Propagates a failure assertion using the specified message.
 53 | 
 54 | ### isEmpty
 55 | 
 56 | **Signature:** `static isEmpty(arg : Object) : void`
 57 | 
 58 | Propagates an assertion if the specified check does not evaluate to an empty object.
 59 | 
 60 | ### isEmpty
 61 | 
 62 | **Signature:** `static isEmpty(arg : Object, msg : String) : void`
 63 | 
 64 | Propagates an assertion using the specified message if the specified check does not evaluate to an empty object.
 65 | 
 66 | ### isFalse
 67 | 
 68 | **Signature:** `static isFalse(check : boolean) : void`
 69 | 
 70 | Propagates an assertion if the specified check does not evaluate to false.
 71 | 
 72 | ### isFalse
 73 | 
 74 | **Signature:** `static isFalse(check : boolean, msg : String) : void`
 75 | 
 76 | Propagates an assertion using the specified message if the specified check does not evaluate to false.
 77 | 
 78 | ### isInstanceOf
 79 | 
 80 | **Signature:** `static isInstanceOf(clazz : Object, arg : Object) : void`
 81 | 
 82 | Propagates an assertion if the specified object 'arg' is not an instance of the specified class 'clazz'.
 83 | 
 84 | ### isInstanceOf
 85 | 
 86 | **Signature:** `static isInstanceOf(clazz : Object, arg : Object, msg : String) : void`
 87 | 
 88 | Propagates an assertion using the specified message if the specified object is not an instance of the specified class.
 89 | 
 90 | ### isNotEmpty
 91 | 
 92 | **Signature:** `static isNotEmpty(arg : Object) : void`
 93 | 
 94 | Propagates an assertion if the specified object is empty.
 95 | 
 96 | ### isNotEmpty
 97 | 
 98 | **Signature:** `static isNotEmpty(arg : Object, msg : String) : void`
 99 | 
100 | Propagates an assertion using the specified message if the specified object is empty.
101 | 
102 | ### isNotNull
103 | 
104 | **Signature:** `static isNotNull(arg : Object) : void`
105 | 
106 | Propagates an assertion if the specified object is null.
107 | 
108 | ### isNotNull
109 | 
110 | **Signature:** `static isNotNull(arg : Object, msg : String) : void`
111 | 
112 | Propagates an assertion using the specified message if the specified object is null.
113 | 
114 | ### isNull
115 | 
116 | **Signature:** `static isNull(arg : Object) : void`
117 | 
118 | Propagates an assertion if the specified object is not null.
119 | 
120 | ### isNull
121 | 
122 | **Signature:** `static isNull(arg : Object, msg : String) : void`
123 | 
124 | Propagates an assertion using the specified message if the specified object is not null.
125 | 
126 | ### isTrue
127 | 
128 | **Signature:** `static isTrue(check : boolean) : void`
129 | 
130 | Propagates an assertion if the specified check does not evaluate to true.
131 | 
132 | ### isTrue
133 | 
134 | **Signature:** `static isTrue(check : boolean, msg : String) : void`
135 | 
136 | Propagates an assertion using the specified message if the specified check does not evaluate to true.
137 | 
138 | ## Method Detail
139 | 
140 | ## Method Details
141 | 
142 | ### areEqual
143 | 
144 | **Signature:** `static areEqual(arg1 : Object, arg2 : Object) : void`
145 | 
146 | **Description:** Propagates an assertion if the specified objects are not equal.
147 | 
148 | **Parameters:**
149 | 
150 | - `arg1`: the first object to check.
151 | - `arg2`: the second object to check.
152 | 
153 | ---
154 | 
155 | ### areEqual
156 | 
157 | **Signature:** `static areEqual(arg1 : Object, arg2 : Object, msg : String) : void`
158 | 
159 | **Description:** Propagates an assertion using the specified message if the specified objects are not equal.
160 | 
161 | **Parameters:**
162 | 
163 | - `arg1`: the first object to check.
164 | - `arg2`: the second object to check.
165 | - `msg`: the assertion message.
166 | 
167 | ---
168 | 
169 | ### areSame
170 | 
171 | **Signature:** `static areSame(arg1 : Object, arg2 : Object) : void`
172 | 
173 | **Description:** Propagates an assertion if the specified objects are not the same.
174 | 
175 | **Parameters:**
176 | 
177 | - `arg1`: the first object to check.
178 | - `arg2`: the second object to check.
179 | 
180 | ---
181 | 
182 | ### areSame
183 | 
184 | **Signature:** `static areSame(arg1 : Object, arg2 : Object, msg : String) : void`
185 | 
186 | **Description:** Propagates an assertion using the specified message if the specified objects are not the same.
187 | 
188 | **Parameters:**
189 | 
190 | - `arg1`: the first object to check.
191 | - `arg2`: the second object to check.
192 | - `msg`: the assertion message.
193 | 
194 | ---
195 | 
196 | ### fail
197 | 
198 | **Signature:** `static fail() : void`
199 | 
200 | **Description:** Propagates a failure assertion.
201 | 
202 | ---
203 | 
204 | ### fail
205 | 
206 | **Signature:** `static fail(msg : String) : void`
207 | 
208 | **Description:** Propagates a failure assertion using the specified message.
209 | 
210 | **Parameters:**
211 | 
212 | - `msg`: the assertion message.
213 | 
214 | ---
215 | 
216 | ### isEmpty
217 | 
218 | **Signature:** `static isEmpty(arg : Object) : void`
219 | 
220 | **Description:** Propagates an assertion if the specified check does not evaluate to an empty object.
221 | 
222 | **Parameters:**
223 | 
224 | - `arg`: the object to check.
225 | 
226 | ---
227 | 
228 | ### isEmpty
229 | 
230 | **Signature:** `static isEmpty(arg : Object, msg : String) : void`
231 | 
232 | **Description:** Propagates an assertion using the specified message if the specified check does not evaluate to an empty object.
233 | 
234 | **Parameters:**
235 | 
236 | - `arg`: the object to check.
237 | - `msg`: the assertion message.
238 | 
239 | ---
240 | 
241 | ### isFalse
242 | 
243 | **Signature:** `static isFalse(check : boolean) : void`
244 | 
245 | **Description:** Propagates an assertion if the specified check does not evaluate to false.
246 | 
247 | **Parameters:**
248 | 
249 | - `check`: the condition to check.
250 | 
251 | ---
252 | 
253 | ### isFalse
254 | 
255 | **Signature:** `static isFalse(check : boolean, msg : String) : void`
256 | 
257 | **Description:** Propagates an assertion using the specified message if the specified check does not evaluate to false.
258 | 
259 | **Parameters:**
260 | 
261 | - `check`: the condition to check.
262 | - `msg`: the assertion message.
263 | 
264 | ---
265 | 
266 | ### isInstanceOf
267 | 
268 | **Signature:** `static isInstanceOf(clazz : Object, arg : Object) : void`
269 | 
270 | **Description:** Propagates an assertion if the specified object 'arg' is not an instance of the specified class 'clazz'. For example, the following call does not propagate an assertion: var test = new dw.util.HashMap(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); But the following call will propagate an assertion: var test = new dw.util.Set(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); Note that 'clazz' can only be a Demandware API Scripting class.
271 | 
272 | **Parameters:**
273 | 
274 | - `clazz`: the scripting class to use to check the object.
275 | - `arg`: the object to check.
276 | 
277 | ---
278 | 
279 | ### isInstanceOf
280 | 
281 | **Signature:** `static isInstanceOf(clazz : Object, arg : Object, msg : String) : void`
282 | 
283 | **Description:** Propagates an assertion using the specified message if the specified object is not an instance of the specified class. For example, the following call does not propagate an assertion: var test = new dw.util.HashMap(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); But the following call will propagate an assertion: var test = new dw.util.Set(); dw.util.Assert.isInstanceOf(dw.util.HashMap, test); Note that 'clazz' can only be a Demandware API Scripting class.
284 | 
285 | **Parameters:**
286 | 
287 | - `clazz`: the scripting class to use to check the object.
288 | - `arg`: the object to check.
289 | - `msg`: the assertion message.
290 | 
291 | ---
292 | 
293 | ### isNotEmpty
294 | 
295 | **Signature:** `static isNotEmpty(arg : Object) : void`
296 | 
297 | **Description:** Propagates an assertion if the specified object is empty.
298 | 
299 | **Parameters:**
300 | 
301 | - `arg`: the object to check.
302 | 
303 | ---
304 | 
305 | ### isNotEmpty
306 | 
307 | **Signature:** `static isNotEmpty(arg : Object, msg : String) : void`
308 | 
309 | **Description:** Propagates an assertion using the specified message if the specified object is empty.
310 | 
311 | **Parameters:**
312 | 
313 | - `arg`: the object to check.
314 | - `msg`: the assertion message.
315 | 
316 | ---
317 | 
318 | ### isNotNull
319 | 
320 | **Signature:** `static isNotNull(arg : Object) : void`
321 | 
322 | **Description:** Propagates an assertion if the specified object is null.
323 | 
324 | **Parameters:**
325 | 
326 | - `arg`: the object to check.
327 | 
328 | ---
329 | 
330 | ### isNotNull
331 | 
332 | **Signature:** `static isNotNull(arg : Object, msg : String) : void`
333 | 
334 | **Description:** Propagates an assertion using the specified message if the specified object is null.
335 | 
336 | **Parameters:**
337 | 
338 | - `arg`: the object to check.
339 | - `msg`: the assertion message.
340 | 
341 | ---
342 | 
343 | ### isNull
344 | 
345 | **Signature:** `static isNull(arg : Object) : void`
346 | 
347 | **Description:** Propagates an assertion if the specified object is not null.
348 | 
349 | **Parameters:**
350 | 
351 | - `arg`: the object to check.
352 | 
353 | ---
354 | 
355 | ### isNull
356 | 
357 | **Signature:** `static isNull(arg : Object, msg : String) : void`
358 | 
359 | **Description:** Propagates an assertion using the specified message if the specified object is not null.
360 | 
361 | **Parameters:**
362 | 
363 | - `arg`: the object to check.
364 | - `msg`: the assertion message.
365 | 
366 | ---
367 | 
368 | ### isTrue
369 | 
370 | **Signature:** `static isTrue(check : boolean) : void`
371 | 
372 | **Description:** Propagates an assertion if the specified check does not evaluate to true.
373 | 
374 | **Parameters:**
375 | 
376 | - `check`: the condition to check.
377 | 
378 | ---
379 | 
380 | ### isTrue
381 | 
382 | **Signature:** `static isTrue(check : boolean, msg : String) : void`
383 | 
384 | **Description:** Propagates an assertion using the specified message if the specified check does not evaluate to true.
385 | 
386 | **Parameters:**
387 | 
388 | - `check`: the condition to check.
389 | - `msg`: the assertion message.
390 | 
391 | ---
```

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

```markdown
  1 | ## Package: dw.object
  2 | 
  3 | # Class ObjectAttributeDefinition
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.ObjectAttributeDefinition
  9 | 
 10 | ## Description
 11 | 
 12 | Represents the definition of an object's attribute.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### VALUE_TYPE_BOOLEAN
 17 | 
 18 | **Type:** Number = 8
 19 | 
 20 | Boolean value type.
 21 | 
 22 | ### VALUE_TYPE_DATE
 23 | 
 24 | **Type:** Number = 6
 25 | 
 26 | Date value type.
 27 | 
 28 | ### VALUE_TYPE_DATETIME
 29 | 
 30 | **Type:** Number = 11
 31 | 
 32 | Date and Time value type.
 33 | 
 34 | ### VALUE_TYPE_EMAIL
 35 | 
 36 | **Type:** Number = 12
 37 | 
 38 | Email value type.
 39 | 
 40 | ### VALUE_TYPE_ENUM_OF_INT
 41 | 
 42 | **Type:** Number = 31
 43 | 
 44 | Enum of int value type.
 45 | 
 46 | ### VALUE_TYPE_ENUM_OF_STRING
 47 | 
 48 | **Type:** Number = 33
 49 | 
 50 | Enum of String value type.
 51 | 
 52 | ### VALUE_TYPE_HTML
 53 | 
 54 | **Type:** Number = 5
 55 | 
 56 | HTML value type.
 57 | 
 58 | ### VALUE_TYPE_IMAGE
 59 | 
 60 | **Type:** Number = 7
 61 | 
 62 | Image value type.
 63 | 
 64 | ### VALUE_TYPE_INT
 65 | 
 66 | **Type:** Number = 1
 67 | 
 68 | int value type.
 69 | 
 70 | ### VALUE_TYPE_MONEY
 71 | 
 72 | **Type:** Number = 9
 73 | 
 74 | Money value type.
 75 | 
 76 | ### VALUE_TYPE_NUMBER
 77 | 
 78 | **Type:** Number = 2
 79 | 
 80 | Number value type.
 81 | 
 82 | ### VALUE_TYPE_PASSWORD
 83 | 
 84 | **Type:** Number = 13
 85 | 
 86 | Password value type.
 87 | 
 88 | ### VALUE_TYPE_QUANTITY
 89 | 
 90 | **Type:** Number = 10
 91 | 
 92 | Quantity value type.
 93 | 
 94 | ### VALUE_TYPE_SET_OF_INT
 95 | 
 96 | **Type:** Number = 21
 97 | 
 98 | Set of int value type.
 99 | 
100 | ### VALUE_TYPE_SET_OF_NUMBER
101 | 
102 | **Type:** Number = 22
103 | 
104 | Set of Number value type.
105 | 
106 | ### VALUE_TYPE_SET_OF_STRING
107 | 
108 | **Type:** Number = 23
109 | 
110 | Set of String value type.
111 | 
112 | ### VALUE_TYPE_STRING
113 | 
114 | **Type:** Number = 3
115 | 
116 | String value type.
117 | 
118 | ### VALUE_TYPE_TEXT
119 | 
120 | **Type:** Number = 4
121 | 
122 | Text value type.
123 | 
124 | ## Properties
125 | 
126 | ### attributeGroups
127 | 
128 | **Type:** Collection (Read Only)
129 | 
130 | All attribute groups the attribute is assigned to.
131 | 
132 | ### defaultValue
133 | 
134 | **Type:** ObjectAttributeValueDefinition (Read Only)
135 | 
136 | Return the default value for the attribute or null if none is defined.
137 | 
138 | ### displayName
139 | 
140 | **Type:** String (Read Only)
141 | 
142 | The display name for the attribute, which can be used in the
143 |  user interface.
144 | 
145 | ### ID
146 | 
147 | **Type:** String (Read Only)
148 | 
149 | The ID of the attribute definition.
150 | 
151 | ### key
152 | 
153 | **Type:** boolean (Read Only)
154 | 
155 | Identifies if the attribute represents the primary key of the object.
156 | 
157 | ### mandatory
158 | 
159 | **Type:** boolean (Read Only)
160 | 
161 | Checks if this attribute is mandatory.
162 | 
163 | ### multiValueType
164 | 
165 | **Type:** VALUE_TYPE_SET_OF_INT (Read Only)
166 | 
167 | Returns true if the attribute can have multiple values.
168 |  Attributes of the following types are multi-value capable:
169 |  
170 |  VALUE_TYPE_SET_OF_INT
171 |  VALUE_TYPE_SET_OF_NUMBER
172 |  VALUE_TYPE_SET_OF_STRING
173 |  
174 |  Additionally, attributes of the following types can be multi-value
175 |  enabled:
176 |  
177 |  VALUE_TYPE_ENUM_OF_INT
178 |  VALUE_TYPE_ENUM_OF_STRING
179 | 
180 | ### objectTypeDefinition
181 | 
182 | **Type:** ObjectTypeDefinition (Read Only)
183 | 
184 | The object type definition in which this attribute is defined.
185 | 
186 | ### setValueType
187 | 
188 | **Type:** boolean (Read Only)
189 | 
190 | Returns true if the attribute is of type 'Set of'.
191 | 
192 | ### system
193 | 
194 | **Type:** boolean (Read Only)
195 | 
196 | Indicates if the attribute is a pre-defined system attribute
197 |  or a custom attribute.
198 | 
199 | ### unit
200 | 
201 | **Type:** String (Read Only)
202 | 
203 | The attribute's unit representation such as
204 |  inches for length or pounds for weight. The value returned by
205 |  this method is based on the attribute itself.
206 | 
207 | ### values
208 | 
209 | **Type:** Collection (Read Only)
210 | 
211 | The list of attribute values. In the user interface only the
212 |  values specified in this list should be offered as valid input values.
213 | 
214 |  The collection contains instances of ObjectAttributeValueDefinition.
215 | 
216 | ### valueTypeCode
217 | 
218 | **Type:** Number (Read Only)
219 | 
220 | A code for the data type stored in the attribute. See constants
221 |  defined in this class.
222 | 
223 | ## Constructor Summary
224 | 
225 | ## Method Summary
226 | 
227 | ### getAttributeGroups
228 | 
229 | **Signature:** `getAttributeGroups() : Collection`
230 | 
231 | Returns all attribute groups the attribute is assigned to.
232 | 
233 | ### getDefaultValue
234 | 
235 | **Signature:** `getDefaultValue() : ObjectAttributeValueDefinition`
236 | 
237 | Return the default value for the attribute or null if none is defined.
238 | 
239 | ### getDisplayName
240 | 
241 | **Signature:** `getDisplayName() : String`
242 | 
243 | Returns the display name for the attribute, which can be used in the user interface.
244 | 
245 | ### getID
246 | 
247 | **Signature:** `getID() : String`
248 | 
249 | Returns the ID of the attribute definition.
250 | 
251 | ### getObjectTypeDefinition
252 | 
253 | **Signature:** `getObjectTypeDefinition() : ObjectTypeDefinition`
254 | 
255 | Returns the object type definition in which this attribute is defined.
256 | 
257 | ### getUnit
258 | 
259 | **Signature:** `getUnit() : String`
260 | 
261 | Returns the attribute's unit representation such as inches for length or pounds for weight.
262 | 
263 | ### getValues
264 | 
265 | **Signature:** `getValues() : Collection`
266 | 
267 | Returns the list of attribute values.
268 | 
269 | ### getValueTypeCode
270 | 
271 | **Signature:** `getValueTypeCode() : Number`
272 | 
273 | Returns a code for the data type stored in the attribute.
274 | 
275 | ### isKey
276 | 
277 | **Signature:** `isKey() : boolean`
278 | 
279 | Identifies if the attribute represents the primary key of the object.
280 | 
281 | ### isMandatory
282 | 
283 | **Signature:** `isMandatory() : boolean`
284 | 
285 | Checks if this attribute is mandatory.
286 | 
287 | ### isMultiValueType
288 | 
289 | **Signature:** `isMultiValueType() : boolean`
290 | 
291 | Returns true if the attribute can have multiple values.
292 | 
293 | ### isSetValueType
294 | 
295 | **Signature:** `isSetValueType() : boolean`
296 | 
297 | Returns true if the attribute is of type 'Set of'.
298 | 
299 | ### isSystem
300 | 
301 | **Signature:** `isSystem() : boolean`
302 | 
303 | Indicates if the attribute is a pre-defined system attribute or a custom attribute.
304 | 
305 | ### requiresEncoding
306 | 
307 | **Signature:** `requiresEncoding() : boolean`
308 | 
309 | Returns a boolean flag indicating whether or not values of this attribute definition should be encoded using the encoding="off" flag in ISML templates.
310 | 
311 | ## Method Detail
312 | 
313 | ## Method Details
314 | 
315 | ### getAttributeGroups
316 | 
317 | **Signature:** `getAttributeGroups() : Collection`
318 | 
319 | **Description:** Returns all attribute groups the attribute is assigned to.
320 | 
321 | **Returns:**
322 | 
323 | all attribute groups the attribute is assigned to.
324 | 
325 | ---
326 | 
327 | ### getDefaultValue
328 | 
329 | **Signature:** `getDefaultValue() : ObjectAttributeValueDefinition`
330 | 
331 | **Description:** Return the default value for the attribute or null if none is defined.
332 | 
333 | **Returns:**
334 | 
335 | the default value for the attribute or null if none is defined.
336 | 
337 | ---
338 | 
339 | ### getDisplayName
340 | 
341 | **Signature:** `getDisplayName() : String`
342 | 
343 | **Description:** Returns the display name for the attribute, which can be used in the user interface.
344 | 
345 | **Returns:**
346 | 
347 | the display name for the attribute, which can be used in the user interface.
348 | 
349 | ---
350 | 
351 | ### getID
352 | 
353 | **Signature:** `getID() : String`
354 | 
355 | **Description:** Returns the ID of the attribute definition.
356 | 
357 | **Returns:**
358 | 
359 | the ID of the attribute definition.
360 | 
361 | ---
362 | 
363 | ### getObjectTypeDefinition
364 | 
365 | **Signature:** `getObjectTypeDefinition() : ObjectTypeDefinition`
366 | 
367 | **Description:** Returns the object type definition in which this attribute is defined.
368 | 
369 | **Returns:**
370 | 
371 | the object type definition in which this attribute is defined.
372 | 
373 | ---
374 | 
375 | ### getUnit
376 | 
377 | **Signature:** `getUnit() : String`
378 | 
379 | **Description:** Returns the attribute's unit representation such as inches for length or pounds for weight. The value returned by this method is based on the attribute itself.
380 | 
381 | **Returns:**
382 | 
383 | the attribute's unit representation such as inches for length or pounds for weight.
384 | 
385 | ---
386 | 
387 | ### getValues
388 | 
389 | **Signature:** `getValues() : Collection`
390 | 
391 | **Description:** Returns the list of attribute values. In the user interface only the values specified in this list should be offered as valid input values. The collection contains instances of ObjectAttributeValueDefinition.
392 | 
393 | **Returns:**
394 | 
395 | a collection of ObjectAttributeValueDefinition instances representing the list of attribute values, or null if no values are specified.
396 | 
397 | ---
398 | 
399 | ### getValueTypeCode
400 | 
401 | **Signature:** `getValueTypeCode() : Number`
402 | 
403 | **Description:** Returns a code for the data type stored in the attribute. See constants defined in this class.
404 | 
405 | **Returns:**
406 | 
407 | a code for the data type stored in the attribute. See constants defined in this class.
408 | 
409 | ---
410 | 
411 | ### isKey
412 | 
413 | **Signature:** `isKey() : boolean`
414 | 
415 | **Description:** Identifies if the attribute represents the primary key of the object.
416 | 
417 | **Returns:**
418 | 
419 | true if the attribute represents the primary key, false otherwise.
420 | 
421 | ---
422 | 
423 | ### isMandatory
424 | 
425 | **Signature:** `isMandatory() : boolean`
426 | 
427 | **Description:** Checks if this attribute is mandatory.
428 | 
429 | **Returns:**
430 | 
431 | true, if this attribute is mandatory
432 | 
433 | ---
434 | 
435 | ### isMultiValueType
436 | 
437 | **Signature:** `isMultiValueType() : boolean`
438 | 
439 | **Description:** Returns true if the attribute can have multiple values. Attributes of the following types are multi-value capable: VALUE_TYPE_SET_OF_INT VALUE_TYPE_SET_OF_NUMBER VALUE_TYPE_SET_OF_STRING Additionally, attributes of the following types can be multi-value enabled: VALUE_TYPE_ENUM_OF_INT VALUE_TYPE_ENUM_OF_STRING
440 | 
441 | **Returns:**
442 | 
443 | true if attributes can have multiple values, otherwise false
444 | 
445 | ---
446 | 
447 | ### isSetValueType
448 | 
449 | **Signature:** `isSetValueType() : boolean`
450 | 
451 | **Description:** Returns true if the attribute is of type 'Set of'.
452 | 
453 | **Deprecated:**
454 | 
455 | Use isMultiValueType() instead.
456 | 
457 | ---
458 | 
459 | ### isSystem
460 | 
461 | **Signature:** `isSystem() : boolean`
462 | 
463 | **Description:** Indicates if the attribute is a pre-defined system attribute or a custom attribute.
464 | 
465 | **Returns:**
466 | 
467 | true if the the attribute is a pre-defined system attribute, false if it is a custom attribute.
468 | 
469 | ---
470 | 
471 | ### requiresEncoding
472 | 
473 | **Signature:** `requiresEncoding() : boolean`
474 | 
475 | **Description:** Returns a boolean flag indicating whether or not values of this attribute definition should be encoded using the encoding="off" flag in ISML templates.
476 | 
477 | **Returns:**
478 | 
479 | a boolean flag indicating whether or not values of this attribute definition should be encoded using the encoding="off" flag in ISML templates.
480 | 
481 | ---
```

--------------------------------------------------------------------------------
/docs/dw_order.hooks/ShippingOrderHooks.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.order.hooks
  2 | 
  3 | # Class ShippingOrderHooks
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - dw.order.hooks.ShippingOrderHooks
  8 | 
  9 | ## Description
 10 | 
 11 | This interface represents all script hooks that can be registered around shipping order lifecycle. It contains the extension points (hook names), and the functions that are called by each extension point. A function must be defined inside a JavaScript source and must be exported. The script with the exported hook function must be located inside a site cartridge. Inside the site cartridge a 'package.json' file with a 'hooks' entry must exist. "hooks": "./hooks.json" The hooks entry links to a json file, relative to the 'package.json' file. This file lists all registered hooks inside the hooks property: "hooks": [ {"name": "dw.order.shippingorder.updateShippingOrderItem", "script": "./shippingOrderUpdate.ds"}, ] A hook entry has a 'name' and a 'script' property. The 'name' contains the extension point, the hook name. The 'script' contains the script relative to the hooks file, with the exported hook function. 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.
 12 | 
 13 | ## Constants
 14 | 
 15 | ## Properties
 16 | 
 17 | ## Constructor Summary
 18 | 
 19 | ## Method Summary
 20 | 
 21 | ### afterStatusChange
 22 | 
 23 | **Signature:** `afterStatusChange(shippingOrder : ShippingOrder) : Status`
 24 | 
 25 | After Status change hook.
 26 | 
 27 | ### changeStatus
 28 | 
 29 | **Signature:** `changeStatus(shippingOrder : ShippingOrder, updateData : ShippingOrder) : Status`
 30 | 
 31 | Change the status of a shipping order.
 32 | 
 33 | ### createShippingOrders
 34 | 
 35 | **Signature:** `createShippingOrders(order : Order) : Status`
 36 | 
 37 | Called during shipping order creation for an order.
 38 | 
 39 | ### notifyStatusChange
 40 | 
 41 | **Signature:** `notifyStatusChange(shippingOrder : ShippingOrder) : Status`
 42 | 
 43 | Notify Status change hook.
 44 | 
 45 | ### prepareCreateShippingOrders
 46 | 
 47 | **Signature:** `prepareCreateShippingOrders(order : Order) : Status`
 48 | 
 49 | Called before shipping order creation for an order takes place.
 50 | 
 51 | ### resolveShippingOrder
 52 | 
 53 | **Signature:** `resolveShippingOrder(updateData : ShippingOrder) : ShippingOrder`
 54 | 
 55 | Resolve the shipping order.
 56 | 
 57 | ### setShippingOrderCancelled
 58 | 
 59 | **Signature:** `setShippingOrderCancelled(updateData : ShippingOrder) : Order`
 60 | 
 61 | Change the status of a shipping order to cancelled.
 62 | 
 63 | ### setShippingOrderShipped
 64 | 
 65 | **Signature:** `setShippingOrderShipped(updateData : ShippingOrder) : Order`
 66 | 
 67 | Change the status of a shipping order to shipped.
 68 | 
 69 | ### setShippingOrderWarehouse
 70 | 
 71 | **Signature:** `setShippingOrderWarehouse(updateData : ShippingOrder) : Order`
 72 | 
 73 | Change the status of a shipping order to warehouse.
 74 | 
 75 | ### updateShippingOrderItem
 76 | 
 77 | **Signature:** `updateShippingOrderItem(shippingOrder : ShippingOrder, updateItem : ShippingOrderItem) : Status`
 78 | 
 79 | Updates the status of a shipping order item.
 80 | 
 81 | ## Method Detail
 82 | 
 83 | ## Method Details
 84 | 
 85 | ### afterStatusChange
 86 | 
 87 | **Signature:** `afterStatusChange(shippingOrder : ShippingOrder) : Status`
 88 | 
 89 | **Description:** After Status change hook. The function is called by extension point extensionPointAfterStatusChange. The implementation of this hook is optional. If defined the hook is called after extensionPointChangeStatus or respectively after extensionPointShippingOrderShipped, extensionPointShippingOrderCancelled or extensionPointShippingOrderWarehouse Runs inside of a transaction.
 90 | 
 91 | **Parameters:**
 92 | 
 93 | - `shippingOrder`: the shipping order to be updated
 94 | 
 95 | **Returns:**
 96 | 
 97 | the resulting status
 98 | 
 99 | ---
100 | 
101 | ### changeStatus
102 | 
103 | **Signature:** `changeStatus(shippingOrder : ShippingOrder, updateData : ShippingOrder) : Status`
104 | 
105 | **Description:** Change the status of a shipping order. The function is called by extension point extensionPointChangeStatus. Runs inside a transaction together with the hooks extensionPointResolveShippingOrder extensionPointUpdateShippingOrderItem. Runs after the iteration over the input's items collection as the last step in this transaction. The implementation of this hook is mandatory.
106 | 
107 | **Parameters:**
108 | 
109 | - `shippingOrder`: the shipping order to be updated
110 | - `updateData`: the input data
111 | 
112 | **Returns:**
113 | 
114 | the resulting status
115 | 
116 | ---
117 | 
118 | ### createShippingOrders
119 | 
120 | **Signature:** `createShippingOrders(order : Order) : Status`
121 | 
122 | **Description:** Called during shipping order creation for an order. The function is called by extension point extensionPointCreateShippingOrders. It is responsible for creating shipping orders and its items for the order. Preparations for shipping order creation can be done before in hook extensionPointPrepareCreateShippingOrders. Runs inside of a transaction. The implementation of this hook is mandatory.
123 | 
124 | **Parameters:**
125 | 
126 | - `order`: the order to create shipping orders for
127 | 
128 | **Returns:**
129 | 
130 | the resulting status
131 | 
132 | ---
133 | 
134 | ### notifyStatusChange
135 | 
136 | **Signature:** `notifyStatusChange(shippingOrder : ShippingOrder) : Status`
137 | 
138 | **Description:** Notify Status change hook. The function is called by extension point extensionPointNotifyStatusChange. The implementation of this hook is optional. If defined the hook is called after extensionPointAfterStatusChange as the last step in the shipping order update process. Runs outside of a transaction.
139 | 
140 | **Parameters:**
141 | 
142 | - `shippingOrder`: the shipping order to be updated
143 | 
144 | **Returns:**
145 | 
146 | the resulting status
147 | 
148 | ---
149 | 
150 | ### prepareCreateShippingOrders
151 | 
152 | **Signature:** `prepareCreateShippingOrders(order : Order) : Status`
153 | 
154 | **Description:** Called before shipping order creation for an order takes place. Typically the hook is used to check the payment authorization status of the order. The function is called by extension point extensionPointPrepareCreateShippingOrders. Runs inside its own transaction. The value of the return status is used to control whether hook createShippingOrders(Order) is called for the order or not. The implementation of this hook is mandatory.
155 | 
156 | **Parameters:**
157 | 
158 | - `order`: the order to create shipping orders for
159 | 
160 | **Returns:**
161 | 
162 | Status.OK successful preparation - continue with shipping order creation for this order. Status.ERROR failed preparation - skip shipping order creation for this order.
163 | 
164 | ---
165 | 
166 | ### resolveShippingOrder
167 | 
168 | **Signature:** `resolveShippingOrder(updateData : ShippingOrder) : ShippingOrder`
169 | 
170 | **Description:** Resolve the shipping order. Will be called as first step of the update. The function is called by extension point extensionPointResolveShippingOrder. Runs inside a transaction together with the hooks extensionPointUpdateShippingOrderItem extensionPointChangeStatus. The implementation of this hook is mandatory.
171 | 
172 | **Parameters:**
173 | 
174 | - `updateData`: the input data
175 | 
176 | **Returns:**
177 | 
178 | the shipping order to update
179 | 
180 | ---
181 | 
182 | ### setShippingOrderCancelled
183 | 
184 | **Signature:** `setShippingOrderCancelled(updateData : ShippingOrder) : Order`
185 | 
186 | **Description:** Change the status of a shipping order to cancelled. The function is called by extension point extensionPointShippingOrderCancelled. This is an optional hook that can be implemented to have full control over status change to destination status Cancelled. The following hooks will be skipped if an implementation for this hook is registered: extensionPointResolveShippingOrder, extensionPointUpdateShippingOrderItem, extensionPointChangeStatus. Runs inside of a transaction.
187 | 
188 | **Parameters:**
189 | 
190 | - `updateData`: the input data
191 | 
192 | **Returns:**
193 | 
194 | the changed order or {code}null{code}
195 | 
196 | ---
197 | 
198 | ### setShippingOrderShipped
199 | 
200 | **Signature:** `setShippingOrderShipped(updateData : ShippingOrder) : Order`
201 | 
202 | **Description:** Change the status of a shipping order to shipped. The function is called by extension point extensionPointShippingOrderShipped. This is an optional hook that can be implemented to have full control over status change to destination status Shipped. The following hooks will be skipped if an implementation for this hook is registered: extensionPointResolveShippingOrder extensionPointUpdateShippingOrderItem, extensionPointChangeStatus. Runs inside of a transaction.
203 | 
204 | **Parameters:**
205 | 
206 | - `updateData`: the input data
207 | 
208 | **Returns:**
209 | 
210 | the changed order or {code}null{code}
211 | 
212 | ---
213 | 
214 | ### setShippingOrderWarehouse
215 | 
216 | **Signature:** `setShippingOrderWarehouse(updateData : ShippingOrder) : Order`
217 | 
218 | **Description:** Change the status of a shipping order to warehouse. The function is called by extension point extensionPointShippingOrderWarehouse. This is an optional hook that can be implemented to have full control over status change to destination status Warehouse. The following hooks will be skipped if an implementation for this hook is registered: extensionPointResolveShippingOrder, extensionPointUpdateShippingOrderItem, extensionPointChangeStatus. Runs inside of a transaction.
219 | 
220 | **Parameters:**
221 | 
222 | - `updateData`: the input data
223 | 
224 | **Returns:**
225 | 
226 | the changed order or {code}null{code}
227 | 
228 | ---
229 | 
230 | ### updateShippingOrderItem
231 | 
232 | **Signature:** `updateShippingOrderItem(shippingOrder : ShippingOrder, updateItem : ShippingOrderItem) : Status`
233 | 
234 | **Description:** Updates the status of a shipping order item. The function is called by extension point extensionPointUpdateShippingOrderItem. Runs inside a transaction together with the hooks extensionPointResolveShippingOrder extensionPointChangeStatus. The implementation of this hook is mandatory.
235 | 
236 | **Parameters:**
237 | 
238 | - `shippingOrder`: the shipping order
239 | - `updateItem`: the input data
240 | 
241 | **Returns:**
242 | 
243 | the resulting status
244 | 
245 | ---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/search-site-preferences.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
  1 | # ==================================================================================
  2 | # SFCC MCP Server - search_site_preferences Tool YAML Tests (Docs-Only Mode)
  3 | # Validates that site preferences search 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/search-site-preferences.docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose
  8 | # aegis query search_site_preferences '{"groupId": "Storefront", "instanceType": "sandbox", "searchRequest": {"query": {"match_all_query": {}}}}' --config "aegis.config.docs-only.json"
  9 | # ==================================================================================
 10 | 
 11 | description: "search_site_preferences tool docs-only mode tests - Tool unavailability validation"
 12 | 
 13 | tests:
 14 |   # ==================================================================================
 15 |   # TOOL UNAVAILABILITY IN DOCS-ONLY MODE
 16 |   # ==================================================================================
 17 |   - it: "should NOT list search_site_preferences tool in docs-only mode"
 18 |     request:
 19 |       jsonrpc: "2.0"
 20 |       id: "tool-not-available-docs"
 21 |       method: "tools/list"
 22 |       params: {}
 23 |     expect:
 24 |       response:
 25 |         jsonrpc: "2.0"
 26 |         id: "tool-not-available-docs"
 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:not:arrayContains:search_site_preferences"
 35 |       stderr: "toBeEmpty"
 36 | 
 37 |   # ==================================================================================
 38 |   # AUTHENTICATION ERROR TESTS (Tool Can Be Called But Returns Error)
 39 |   # ==================================================================================
 40 |   - it: "should return authentication error when calling search_site_preferences in docs-only mode"
 41 |     request:
 42 |       jsonrpc: "2.0"
 43 |       id: "auth-error-storefront"
 44 |       method: "tools/call"
 45 |       params:
 46 |         name: "search_site_preferences"
 47 |         arguments:
 48 |           groupId: "Storefront"
 49 |           instanceType: "sandbox"
 50 |           searchRequest:
 51 |             query:
 52 |               match_all_query: {}
 53 |     expect:
 54 |       response:
 55 |         jsonrpc: "2.0"
 56 |         id: "auth-error-storefront"
 57 |         result:
 58 |           content:
 59 |             - type: "text"
 60 |               text: "match:contains:OCAPI client not configured"
 61 |           isError: true
 62 |       performance:
 63 |         maxResponseTime: "500ms"
 64 |       stderr: "toBeEmpty"
 65 | 
 66 |   - it: "should return authentication error for text query search requests"
 67 |     request:
 68 |       jsonrpc: "2.0"
 69 |       id: "auth-error-text-query"
 70 |       method: "tools/call"
 71 |       params:
 72 |         name: "search_site_preferences"
 73 |         arguments:
 74 |           groupId: "System"
 75 |           instanceType: "development"
 76 |           searchRequest:
 77 |             query:
 78 |               text_query:
 79 |                 fields: ["id", "display_name"]
 80 |                 search_phrase: "test"
 81 |     expect:
 82 |       response:
 83 |         jsonrpc: "2.0"
 84 |         id: "auth-error-text-query"
 85 |         result:
 86 |           content:
 87 |             - type: "text"
 88 |               text: "match:contains:credentials are provided"
 89 |           isError: true
 90 |       performance:
 91 |         maxResponseTime: "500ms"
 92 |       stderr: "toBeEmpty"
 93 | 
 94 |   - it: "should return authentication error for term query requests"
 95 |     request:
 96 |       jsonrpc: "2.0"
 97 |       id: "auth-error-term-query"
 98 |       method: "tools/call"
 99 |       params:
100 |         name: "search_site_preferences"
101 |         arguments:
102 |           groupId: "SFRA"
103 |           instanceType: "staging"
104 |           searchRequest:
105 |             query:
106 |               term_query:
107 |                 fields: ["value_type"]
108 |                 operator: "is"
109 |                 values: ["string"]
110 |     expect:
111 |       response:
112 |         jsonrpc: "2.0"
113 |         id: "auth-error-term-query"
114 |         result:
115 |           content:
116 |             - type: "text"
117 |               text: "match:contains:OCAPI client not configured"
118 |           isError: true
119 |       performance:
120 |         maxResponseTime: "500ms"
121 |       stderr: "toBeEmpty"
122 | 
123 |   - it: "should return authentication error for boolean query requests"
124 |     request:
125 |       jsonrpc: "2.0"
126 |       id: "auth-error-bool-query"
127 |       method: "tools/call"
128 |       params:
129 |         name: "search_site_preferences"
130 |         arguments:
131 |           groupId: "Integration"
132 |           instanceType: "production"
133 |           searchRequest:
134 |             query:
135 |               bool_query:
136 |                 must:
137 |                   - text_query:
138 |                       fields: ["id"]
139 |                       search_phrase: "api"
140 |     expect:
141 |       response:
142 |         jsonrpc: "2.0"
143 |         id: "auth-error-bool-query"
144 |         result:
145 |           content:
146 |             - type: "text"
147 |               text: "match:contains:ensure credentials are provided"
148 |           isError: true
149 |       performance:
150 |         maxResponseTime: "500ms"
151 |       stderr: "toBeEmpty"
152 | 
153 |   - it: "should return authentication error regardless of instance type"
154 |     request:
155 |       jsonrpc: "2.0"
156 |       id: "auth-error-sandbox"
157 |       method: "tools/call"
158 |       params:
159 |         name: "search_site_preferences"
160 |         arguments:
161 |           groupId: "CCV"
162 |           instanceType: "sandbox"
163 |           searchRequest:
164 |             query:
165 |               match_all_query: {}
166 |             count: 5
167 |     expect:
168 |       response:
169 |         jsonrpc: "2.0"
170 |         id: "auth-error-sandbox"
171 |         result:
172 |           content:
173 |             - type: "text"
174 |               text: "match:contains:OCAPI client not configured"
175 |           isError: true
176 |       performance:
177 |         maxResponseTime: "500ms"
178 |       stderr: "toBeEmpty"
179 | 
180 |   - it: "should return authentication error with options parameter"
181 |     request:
182 |       jsonrpc: "2.0"
183 |       id: "auth-error-with-options"
184 |       method: "tools/call"
185 |       params:
186 |         name: "search_site_preferences"
187 |         arguments:
188 |           groupId: "Storefront"
189 |           instanceType: "sandbox"
190 |           searchRequest:
191 |             query:
192 |               match_all_query: {}
193 |           options:
194 |             maskPasswords: false
195 |             expand: "value"
196 |     expect:
197 |       response:
198 |         jsonrpc: "2.0"
199 |         id: "auth-error-with-options"
200 |         result:
201 |           content:
202 |             - type: "text"
203 |               text: "match:contains:OCAPI client not configured"
204 |           isError: true
205 |       performance:
206 |         maxResponseTime: "500ms"
207 |       stderr: "toBeEmpty"
208 | 
209 |   # ==================================================================================
210 |   # PARAMETER VALIDATION IN DOCS-ONLY MODE
211 |   # ==================================================================================
212 |   - it: "should return authentication error even with missing required parameters"
213 |     request:
214 |       jsonrpc: "2.0"
215 |       id: "auth-error-missing-params"
216 |       method: "tools/call"
217 |       params:
218 |         name: "search_site_preferences"
219 |         arguments:
220 |           groupId: "Storefront"
221 |           # Missing instanceType and searchRequest - but should get auth error first
222 |     expect:
223 |       response:
224 |         jsonrpc: "2.0"
225 |         id: "auth-error-missing-params"
226 |         result:
227 |           content:
228 |             - type: "text"
229 |               text: "match:regex:(OCAPI client not configured|required)"
230 |           isError: true
231 |       performance:
232 |         maxResponseTime: "500ms"
233 |       stderr: "toBeEmpty"
234 | 
235 |   # ==================================================================================
236 |   # CONSISTENT ERROR MESSAGING
237 |   # ==================================================================================
238 |   - it: "should provide consistent error message across different preference groups"
239 |     request:
240 |       jsonrpc: "2.0"
241 |       id: "consistent-error-message"
242 |       method: "tools/call"
243 |       params:
244 |         name: "search_site_preferences"
245 |         arguments:
246 |           groupId: "FastForward"
247 |           instanceType: "development"
248 |           searchRequest:
249 |             query:
250 |               text_query:
251 |                 fields: ["description"]
252 |                 search_phrase: "feature"
253 |     expect:
254 |       response:
255 |         jsonrpc: "2.0"
256 |         id: "consistent-error-message"
257 |         result:
258 |           content:
259 |             - type: "text"
260 |               text: "match:regex:(OCAPI client not configured|ensure credentials are provided)"
261 |           isError: true
262 |       performance:
263 |         maxResponseTime: "500ms"
264 |       stderr: "toBeEmpty"
265 | 
266 |   # ==================================================================================
267 |   # FAST ERROR RESPONSE VALIDATION
268 |   # ==================================================================================
269 |   - it: "should return authentication errors quickly without OCAPI calls"
270 |     request:
271 |       jsonrpc: "2.0"
272 |       id: "fast-auth-error"
273 |       method: "tools/call"
274 |       params:
275 |         name: "search_site_preferences"
276 |         arguments:
277 |           groupId: "Storefront"
278 |           instanceType: "sandbox"
279 |           searchRequest:
280 |             query:
281 |               match_all_query: {}
282 |             count: 100
283 |             start: 0
284 |     expect:
285 |       response:
286 |         jsonrpc: "2.0"
287 |         id: "fast-auth-error"
288 |         result:
289 |           content:
290 |             - type: "text"
291 |               text: "match:contains:OCAPI client not configured"
292 |           isError: true
293 |       performance:
294 |         maxResponseTime: "300ms"  # Should be very fast since no actual API call
295 |       stderr: "toBeEmpty"
```

--------------------------------------------------------------------------------
/tests/config.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { loadSecureDwJson } from '../src/config/dw-json-loader.js';
  2 | import { ConfigurationFactory } from '../src/config/configuration-factory.js';
  3 | import { DwJsonConfig } from '../src/types/types.js';
  4 | import { existsSync, writeFileSync, unlinkSync, mkdirSync } from 'fs';
  5 | import { join } from 'path';
  6 | import { tmpdir } from 'os';
  7 | 
  8 | describe('dw-json-loader.ts and configuration-factory.ts', () => {
  9 |   // Create a temporary directory for test files
 10 |   const testDir = join(tmpdir(), 'sfcc-config-tests');
 11 | 
 12 |   beforeAll(() => {
 13 |     if (!existsSync(testDir)) {
 14 |       mkdirSync(testDir, { recursive: true });
 15 |     }
 16 |   });
 17 | 
 18 |   afterAll(() => {
 19 |     // Clean up test files
 20 |     try {
 21 |       const testFiles = [
 22 |         'valid-dw.json',
 23 |         'invalid-json.json',
 24 |         'incomplete-dw.json',
 25 |         'oauth-dw.json',
 26 |         'invalid-hostname-dw.json',
 27 |         'localhost-port-dw.json',
 28 |         'custom-port-dw.json',
 29 |       ];
 30 |       testFiles.forEach(file => {
 31 |         const filePath = join(testDir, file);
 32 |         if (existsSync(filePath)) {
 33 |           unlinkSync(filePath);
 34 |         }
 35 |       });
 36 |     } catch {
 37 |       // Ignore cleanup errors
 38 |     }
 39 |   });
 40 | 
 41 |   afterEach(() => {
 42 |     // Restore all mocks after each test
 43 |     jest.restoreAllMocks();
 44 |   });
 45 | 
 46 |   describe('loadSecureDwJson', () => {
 47 |     it('should load a valid dw.json file with basic auth', () => {
 48 |       const validDwJson: DwJsonConfig = {
 49 |         hostname: 'test-instance.demandware.net',
 50 |         username: 'testuser',
 51 |         password: 'testpass',
 52 |         'client-id': '',
 53 |         'client-secret': '',
 54 |       };
 55 | 
 56 |       const testFile = join(testDir, 'valid-dw.json');
 57 |       writeFileSync(testFile, JSON.stringify(validDwJson, null, 2));
 58 | 
 59 |       const dwConfig = loadSecureDwJson(testFile);
 60 | 
 61 |       expect(dwConfig).toEqual(validDwJson);
 62 |     });
 63 | 
 64 |     it('should load a valid dw.json file with OAuth credentials', () => {
 65 |       const oauthDwJson: DwJsonConfig = {
 66 |         hostname: 'test-instance.demandware.net',
 67 |         username: 'testuser',
 68 |         password: 'testpass',
 69 |         'client-id': 'test-client-id',
 70 |         'client-secret': 'test-client-secret',
 71 |       };
 72 | 
 73 |       const testFile = join(testDir, 'oauth-dw.json');
 74 |       writeFileSync(testFile, JSON.stringify(oauthDwJson, null, 2));
 75 | 
 76 |       const dwConfig = loadSecureDwJson(testFile);
 77 | 
 78 |       expect(dwConfig).toEqual(oauthDwJson);
 79 |     });
 80 | 
 81 |     it('should handle relative paths correctly', () => {
 82 |       const validDwJson: DwJsonConfig = {
 83 |         hostname: 'test-instance.demandware.net',
 84 |         username: 'testuser',
 85 |         password: 'testpass',
 86 |       };
 87 | 
 88 |       const testFile = join(testDir, 'valid-dw.json');
 89 |       writeFileSync(testFile, JSON.stringify(validDwJson, null, 2));
 90 | 
 91 |       // Test with the actual test file path (not relative)
 92 |       const dwConfig = loadSecureDwJson(testFile);
 93 |       expect(dwConfig.hostname).toBe('test-instance.demandware.net');
 94 |     });
 95 | 
 96 |     it('should throw error for non-existent file', () => {
 97 |       const nonExistentFile = join(testDir, 'non-existent.json');
 98 |       expect(() => {
 99 |         loadSecureDwJson(nonExistentFile);
100 |       }).toThrow('Configuration file not found');
101 |     });
102 | 
103 |     it('should throw error for invalid JSON', () => {
104 |       const invalidJsonFile = join(testDir, 'invalid-json.json');
105 |       writeFileSync(invalidJsonFile, '{ invalid json }');
106 | 
107 |       expect(() => {
108 |         loadSecureDwJson(invalidJsonFile);
109 |       }).toThrow('Invalid JSON in configuration file');
110 |     });
111 | 
112 |     it('should throw error for incomplete configuration', () => {
113 |       const incompleteDwJson = {
114 |         hostname: 'test-instance.demandware.net',
115 |         // Missing username and password
116 |       };
117 | 
118 |       const testFile = join(testDir, 'incomplete-dw.json');
119 |       writeFileSync(testFile, JSON.stringify(incompleteDwJson, null, 2));
120 | 
121 |       expect(() => {
122 |         loadSecureDwJson(testFile);
123 |       }).toThrow('Configuration file must contain hostname, username, and password fields');
124 |     });
125 | 
126 |     it('should throw error for invalid hostname format', () => {
127 |       const invalidHostnameDwJson: DwJsonConfig = {
128 |         hostname: 'invalid_hostname_with_underscores',
129 |         username: 'testuser',
130 |         password: 'testpass',
131 |       };
132 | 
133 |       const testFile = join(testDir, 'invalid-hostname-dw.json');
134 |       writeFileSync(testFile, JSON.stringify(invalidHostnameDwJson, null, 2));
135 | 
136 |       expect(() => {
137 |         loadSecureDwJson(testFile);
138 |       }).toThrow('Invalid hostname format in configuration');
139 |     });
140 | 
141 |     it('should load dw.json file with hostname containing port', () => {
142 |       const dwJsonWithPort: DwJsonConfig = {
143 |         hostname: 'localhost:3000',
144 |         username: 'testuser',
145 |         password: 'testpass',
146 |       };
147 | 
148 |       const testFile = join(testDir, 'localhost-port-dw.json');
149 |       writeFileSync(testFile, JSON.stringify(dwJsonWithPort, null, 2));
150 | 
151 |       const dwConfig = loadSecureDwJson(testFile);
152 |       expect(dwConfig.hostname).toBe('localhost:3000');
153 |       expect(dwConfig.username).toBe('testuser');
154 |       expect(dwConfig.password).toBe('testpass');
155 |     });
156 | 
157 |     it('should load dw.json file with custom hostname and port', () => {
158 |       const dwJsonWithCustomPort: DwJsonConfig = {
159 |         hostname: 'test-instance.demandware.net:8080',
160 |         username: 'testuser',
161 |         password: 'testpass',
162 |       };
163 | 
164 |       const testFile = join(testDir, 'custom-port-dw.json');
165 |       writeFileSync(testFile, JSON.stringify(dwJsonWithCustomPort, null, 2));
166 | 
167 |       const dwConfig = loadSecureDwJson(testFile);
168 |       expect(dwConfig.hostname).toBe('test-instance.demandware.net:8080');
169 |       expect(dwConfig.username).toBe('testuser');
170 |       expect(dwConfig.password).toBe('testpass');
171 |     });
172 |   });
173 | 
174 |   describe('ConfigurationFactory', () => {
175 |     it('should create config from dw.json file', () => {
176 |       const validDwJson: DwJsonConfig = {
177 |         hostname: 'test-instance.demandware.net',
178 |         username: 'testuser',
179 |         password: 'testpass',
180 |       };
181 | 
182 |       const testFile = join(testDir, 'valid-dw.json');
183 |       writeFileSync(testFile, JSON.stringify(validDwJson, null, 2));
184 | 
185 |       const config = ConfigurationFactory.create({
186 |         dwJsonPath: testFile,
187 |       });
188 | 
189 |       expect(config.hostname).toBe('test-instance.demandware.net');
190 |       expect(config.username).toBe('testuser');
191 |       expect(config.password).toBe('testpass');
192 |     });
193 | 
194 |     it('should map OAuth credentials from dw.json', () => {
195 |       const oauthDwJson: DwJsonConfig = {
196 |         hostname: 'test-instance.demandware.net',
197 |         username: 'testuser',
198 |         password: 'testpass',
199 |         'client-id': 'test-client-id',
200 |         'client-secret': 'test-client-secret',
201 |       };
202 | 
203 |       const testFile = join(testDir, 'oauth-dw.json');
204 |       writeFileSync(testFile, JSON.stringify(oauthDwJson, null, 2));
205 | 
206 |       const config = ConfigurationFactory.create({
207 |         dwJsonPath: testFile,
208 |       });
209 | 
210 |       expect(config.clientId).toBe('test-client-id');
211 |       expect(config.clientSecret).toBe('test-client-secret');
212 |     });
213 | 
214 |     it('should override dw.json with command-line options', () => {
215 |       const validDwJson: DwJsonConfig = {
216 |         hostname: 'test-instance.demandware.net',
217 |         username: 'testuser',
218 |         password: 'testpass',
219 |       };
220 | 
221 |       const testFile = join(testDir, 'valid-dw.json');
222 |       writeFileSync(testFile, JSON.stringify(validDwJson, null, 2));
223 | 
224 |       const config = ConfigurationFactory.create({
225 |         dwJsonPath: testFile,
226 |         hostname: 'override-hostname.demandware.net',
227 |         username: 'override-user',
228 |       });
229 | 
230 |       expect(config.hostname).toBe('override-hostname.demandware.net');
231 |       expect(config.username).toBe('override-user');
232 |       expect(config.password).toBe('testpass'); // Should keep from dw.json
233 |     });
234 | 
235 |     it('should create config from options only', () => {
236 |       const config = ConfigurationFactory.create({
237 |         hostname: 'test-hostname.demandware.net',
238 |         username: 'testuser',
239 |         password: 'testpass',
240 |       });
241 | 
242 |       expect(config.hostname).toBe('test-hostname.demandware.net');
243 |       expect(config.username).toBe('testuser');
244 |       expect(config.password).toBe('testpass');
245 |     });
246 | 
247 |     it('should create local mode config', () => {
248 |       const config = ConfigurationFactory.createLocalMode();
249 | 
250 |       expect(config.hostname).toBe('');
251 |       expect(config.username).toBeUndefined();
252 |       expect(config.password).toBeUndefined();
253 |       expect(config.clientId).toBeUndefined();
254 |       expect(config.clientSecret).toBeUndefined();
255 |     });
256 | 
257 |     it('should validate configuration capabilities', () => {
258 |       const config = ConfigurationFactory.create({
259 |         hostname: 'test-hostname.demandware.net',
260 |         username: 'testuser',
261 |         password: 'testpass',
262 |         clientId: 'test-client-id',
263 |         clientSecret: 'test-client-secret',
264 |       });
265 | 
266 |       const capabilities = ConfigurationFactory.getCapabilities(config);
267 | 
268 |       expect(capabilities.canAccessLogs).toBe(true);
269 |       expect(capabilities.canAccessOCAPI).toBe(true);
270 |       expect(capabilities.canAccessWebDAV).toBe(true);
271 |       expect(capabilities.isLocalMode).toBe(false);
272 |     });
273 | 
274 |     it('should detect local mode', () => {
275 |       const config = ConfigurationFactory.createLocalMode();
276 |       const capabilities = ConfigurationFactory.getCapabilities(config);
277 | 
278 |       expect(capabilities.canAccessLogs).toBe(false);
279 |       expect(capabilities.canAccessOCAPI).toBe(false);
280 |       expect(capabilities.canAccessWebDAV).toBe(false);
281 |       expect(capabilities.isLocalMode).toBe(true);
282 |     });
283 | 
284 |   });
285 | });
286 | 
```

--------------------------------------------------------------------------------
/docs/sfra/render.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Module render
  2 | 
  3 | ## Description
  4 | 
  5 | The SFRA render module is the core rendering engine that processes and outputs different types of response content in Salesforce Commerce Cloud's Storefront Reference Architecture (SFRA). This module handles the execution of rendering operations that have been queued in the Response object's renderings array during the request lifecycle. It supports multiple output formats including ISML templates, JSON responses, XML output, Page Designer pages, and direct text output. The render module serves as the final step in the SFRA request processing pipeline, converting accumulated view data and rendering instructions into the actual HTTP response content.
  6 | 
  7 | ## Functions
  8 | 
  9 | ### template
 10 | 
 11 | **Signature:** `template(view, viewData) : void`
 12 | 
 13 | Renders an ISML template with the provided data.
 14 | 
 15 | ### json
 16 | 
 17 | **Signature:** `json(data, response) : void`
 18 | 
 19 | Renders an object as JSON output.
 20 | 
 21 | ### xml
 22 | 
 23 | **Signature:** `xml(viewData, response) : void`
 24 | 
 25 | Renders an object as XML output with proper escaping.
 26 | 
 27 | ### page
 28 | 
 29 | **Signature:** `page(pageID, aspectAttributes, data, response) : void`
 30 | 
 31 | Renders a Page Designer page with optional aspect attributes.
 32 | 
 33 | ### applyRenderings
 34 | 
 35 | **Signature:** `applyRenderings(res) : void`
 36 | 
 37 | Processes all rendering instructions in the response object.
 38 | 
 39 | ## Function Detail
 40 | 
 41 | ### template
 42 | 
 43 | **Signature:** `template(view, viewData) : void`
 44 | 
 45 | **Description:** Renders an ISML template using the provided view data. Creates a shallow copy of the view data for isolation and uses SFCC's ISML rendering engine to process the template.
 46 | 
 47 | **Parameters:**
 48 | - `view` (String) - Path to the ISML template file
 49 | - `viewData` (Object) - Data object to be passed as pdict to the template
 50 | 
 51 | **Returns:**
 52 | void
 53 | 
 54 | **Throws:**
 55 | Error with enhanced message including Java stack trace, file name, and line number information.
 56 | 
 57 | **Processing:**
 58 | 1. Creates shallow copy of view data to prevent modification
 59 | 2. Calls `isml.renderTemplate()` with view path and data
 60 | 3. Wraps any rendering errors with enhanced error information
 61 | 
 62 | ### json
 63 | 
 64 | **Signature:** `json(data, response) : void`
 65 | 
 66 | **Description:** Renders the provided data as JSON output with proper content type headers and formatting.
 67 | 
 68 | **Parameters:**
 69 | - `data` (Object) - Object to be serialized as JSON
 70 | - `response` (Response) - SFRA Response object
 71 | 
 72 | **Returns:**
 73 | void
 74 | 
 75 | **Processing:**
 76 | 1. Sets content type to `application/json`
 77 | 2. Serializes data with JSON.stringify using 2-space indentation
 78 | 3. Writes formatted JSON to response writer
 79 | 
 80 | ### xml
 81 | 
 82 | **Signature:** `xml(viewData, response) : void`
 83 | 
 84 | **Description:** Renders the provided view data as XML output with proper character escaping and root element wrapping.
 85 | 
 86 | **Parameters:**
 87 | - `viewData` (Object) - Object containing data to be rendered as XML
 88 | - `response` (Response) - SFRA Response object
 89 | 
 90 | **Returns:**
 91 | void
 92 | 
 93 | **Throws:**
 94 | Error with enhanced message including stack trace, file name, and line number information.
 95 | 
 96 | **XML Character Escaping:**
 97 | - `<` → `&lt;`
 98 | - `>` → `&gt;`
 99 | - `&` → `&amp;`
100 | - `"` → `&quot;`
101 | - `'` → `&apos;`
102 | 
103 | **Processing:**
104 | 1. Creates root `<response>` element
105 | 2. Processes each key in viewData:
106 |    - If key is 'xml', includes raw XML content
107 |    - Otherwise, creates element with escaped content
108 | 3. Closes root element
109 | 4. Sets content type to `application/xml`
110 | 5. Creates XML object and writes to response
111 | 
112 | ### page
113 | 
114 | **Signature:** `page(pageID, aspectAttributes, data, response) : void`
115 | 
116 | **Description:** Renders a Page Designer page using SFCC's PageMgr with optional aspect attributes for advanced page management.
117 | 
118 | **Parameters:**
119 | - `pageID` (String) - ID of the Page Designer page to render
120 | - `aspectAttributes` (dw.util.HashMap) - Optional aspect attributes for PageMgr
121 | - `data` (Object) - Data to be passed to the page
122 | - `response` (Response) - SFRA Response object
123 | 
124 | **Returns:**
125 | void
126 | 
127 | **Processing:**
128 | 1. Checks if aspect attributes exist and are not empty
129 | 2. Calls appropriate PageMgr.renderPage() method:
130 |    - With aspect attributes: `PageMgr.renderPage(pageID, aspectAttributes, JSON.stringify(data))`
131 |    - Without aspect attributes: `PageMgr.renderPage(pageID, JSON.stringify(data))`
132 | 3. Writes rendered page content to response writer
133 | 
134 | ### applyRenderings
135 | 
136 | **Signature:** `applyRenderings(res) : void`
137 | 
138 | **Description:** Processes all queued rendering instructions in the response object's renderings array. This is the main entry point called by the Server during route completion.
139 | 
140 | **Parameters:**
141 | - `res` (Response) - SFRA Response object containing renderings array
142 | 
143 | **Returns:**
144 | void
145 | 
146 | **Throws:**
147 | Error if no renderings are present or if invalid rendering type is encountered.
148 | 
149 | **Rendering Types Processed:**
150 | 
151 | **Render Instructions (`type: 'render'`):**
152 | - `subType: 'isml'` - Calls `template()` with view and viewData
153 | - `subType: 'json'` - Calls `json()` with viewData and response
154 | - `subType: 'xml'` - Calls `xml()` with viewData and response
155 | - `subType: 'page'` - Calls `page()` with page, aspectAttributes, viewData, and response
156 | 
157 | **Print Instructions (`type: 'print'`):**
158 | - Directly writes message to response writer
159 | 
160 | **Validation:**
161 | - Throws error if renderings array is empty
162 | - Throws error for unknown rendering types
163 | - Throws error for render instructions without valid subType
164 | 
165 | ## Rendering Pipeline Integration
166 | 
167 | ### Response Object Integration
168 | 
169 | The render module works closely with the SFRA Response object:
170 | 
171 | **Rendering Queue:**
172 | ```javascript
173 | res.renderings = [
174 |   { type: 'render', subType: 'isml', view: 'template/path' },
175 |   { type: 'print', message: 'Debug output' },
176 |   { type: 'render', subType: 'json' }
177 | ];
178 | ```
179 | 
180 | **View Data Accumulation:**
181 | ```javascript
182 | res.viewData = {
183 |   title: 'Page Title',
184 |   products: [...],
185 |   customer: {...}
186 | };
187 | ```
188 | 
189 | ### Server Integration
190 | 
191 | The Server class calls `applyRenderings()` during route completion:
192 | 
193 | ```javascript
194 | route.on('route:Complete', function onRouteCompleteHandler(req, res) {
195 |     // Cache and personalization handling
196 |     if (res.redirectUrl) {
197 |         // Handle redirects
198 |         return;
199 |     }
200 |     render.applyRenderings(res); // Execute rendering pipeline
201 | });
202 | ```
203 | 
204 | ## Content Type Management
205 | 
206 | ### Automatic Content Types
207 | 
208 | **JSON Responses:**
209 | - Sets `application/json` content type
210 | - Formats output with 2-space indentation
211 | 
212 | **XML Responses:**
213 | - Sets `application/xml` content type
214 | - Automatically escapes special characters
215 | - Wraps content in root `<response>` element
216 | 
217 | **ISML Templates:**
218 | - Content type determined by template output
219 | - Typically `text/html` for web pages
220 | 
221 | **Page Designer:**
222 | - Content type managed by PageMgr
223 | - Usually `text/html` for rendered pages
224 | 
225 | ## Error Handling
226 | 
227 | ### Template Rendering Errors
228 | 
229 | **ISML Template Errors:**
230 | - Catches Java exceptions from template engine
231 | - Enhances error with file name and line number
232 | - Includes original Java stack trace
233 | 
234 | **XML Rendering Errors:**
235 | - Catches XML parsing/creation errors
236 | - Provides enhanced error messaging
237 | - Includes stack trace information
238 | 
239 | ### Validation Errors
240 | 
241 | **Missing Renderings:**
242 | - Throws error if renderings array is empty
243 | - Prevents silent failures in rendering pipeline
244 | 
245 | **Invalid Rendering Types:**
246 | - Validates rendering instruction format
247 | - Throws descriptive errors for unknown types
248 | 
249 | ## Usage Examples
250 | 
251 | ### Basic Template Rendering
252 | ```javascript
253 | // In controller middleware
254 | res.render('product/productDetail', {
255 |     product: productModel,
256 |     breadcrumbs: breadcrumbsModel
257 | });
258 | 
259 | // Results in rendering instruction:
260 | {
261 |     type: 'render',
262 |     subType: 'isml',
263 |     view: 'product/productDetail'
264 | }
265 | ```
266 | 
267 | ### JSON API Response
268 | ```javascript
269 | // In API controller
270 | res.json({
271 |     success: true,
272 |     data: responseData,
273 |     message: 'Operation completed'
274 | });
275 | 
276 | // Results in JSON output with proper headers
277 | ```
278 | 
279 | ### XML Response
280 | ```javascript
281 | // In XML endpoint
282 | res.xml({
283 |     status: 'success',
284 |     xml: '<customData>...</customData>',
285 |     timestamp: new Date().toISOString()
286 | });
287 | 
288 | // Results in properly escaped XML with root element
289 | ```
290 | 
291 | ### Page Designer Integration
292 | ```javascript
293 | // In Page Designer controller
294 | res.page('homepage', aspectAttributes, {
295 |     banners: bannerData,
296 |     featured: featuredProducts
297 | });
298 | 
299 | // Results in Page Designer page rendering
300 | ```
301 | 
302 | ### Mixed Output
303 | ```javascript
304 | // Debug output followed by template
305 | res.print('Debug: Processing complete');
306 | res.render('checkout/confirmation', orderData);
307 | 
308 | // Results in multiple rendering instructions executed in order
309 | ```
310 | 
311 | ## Performance Considerations
312 | 
313 | ### Data Copying
314 | 
315 | **Template Rendering:**
316 | - Creates shallow copy of view data for isolation
317 | - Prevents template modifications from affecting response object
318 | - Minimal performance impact for typical data sizes
319 | 
320 | ### Memory Management
321 | 
322 | **Large Data Sets:**
323 | - JSON serialization with formatting may increase memory usage
324 | - XML escaping creates additional string copies
325 | - Consider streaming for very large responses
326 | 
327 | ### Caching Integration
328 | 
329 | **Template Caching:**
330 | - ISML templates are cached by SFCC platform
331 | - View data is not cached at render level
332 | - Page Designer pages benefit from platform caching
333 | 
334 | ## Security Considerations
335 | 
336 | ### XML Security
337 | 
338 | **Character Escaping:**
339 | - Prevents XML injection attacks
340 | - Escapes all potentially dangerous characters
341 | - Safe handling of user-generated content
342 | 
343 | **Content Validation:**
344 | - XML content must be well-formed
345 | - Error handling prevents partial output
346 | 
347 | ### Template Security
348 | 
349 | **Data Isolation:**
350 | - Shallow copy prevents template-induced data corruption
351 | - Original response data remains unchanged
352 | - Safe passing of sensitive data to templates
353 | 
354 | ---
355 | 
```

--------------------------------------------------------------------------------
/tests/job-log-handler.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { JobLogToolHandler } from '../src/core/handlers/job-log-handler.js';
  2 | import { HandlerContext } from '../src/core/handlers/base-handler.js';
  3 | import { SFCCLogClient } from '../src/clients/log-client.js';
  4 | import { Logger } from '../src/utils/logger.js';
  5 | 
  6 | // Mock dependencies
  7 | jest.mock('../src/clients/log-client.js');
  8 | jest.mock('../src/utils/logger.js');
  9 | 
 10 | describe('JobLogToolHandler', () => {
 11 |   let mockContext: HandlerContext;
 12 |   let handler: JobLogToolHandler;
 13 |   let mockLogger: jest.Mocked<Logger>;
 14 |   let mockLogClient: jest.Mocked<SFCCLogClient>;
 15 | 
 16 |   beforeEach(() => {
 17 |     jest.clearAllMocks();
 18 | 
 19 |     mockLogger = {
 20 |       debug: jest.fn(),
 21 |       info: jest.fn(),
 22 |       warn: jest.fn(),
 23 |       error: jest.fn(),
 24 |       methodEntry: jest.fn(),
 25 |       methodExit: jest.fn(),
 26 |       timing: jest.fn(),
 27 |       log: jest.fn(),
 28 |     } as any;
 29 | 
 30 |     mockLogClient = {
 31 |       getLatestJobLogFiles: jest.fn(),
 32 |       searchJobLogsByName: jest.fn(),
 33 |       getJobLogEntries: jest.fn(),
 34 |       searchJobLogs: jest.fn(),
 35 |       getJobExecutionSummary: jest.fn(),
 36 |     } as any;
 37 | 
 38 |     // Mock the SFCCLogClient constructor
 39 |     (SFCCLogClient as jest.MockedClass<typeof SFCCLogClient>).mockImplementation(() => mockLogClient);
 40 | 
 41 |     // Mock Logger.getChildLogger
 42 |     jest.spyOn(Logger, 'getChildLogger').mockReturnValue(mockLogger);
 43 | 
 44 |     mockContext = {
 45 |       logger: mockLogger,
 46 |       config: {
 47 |         hostname: 'test.demandware.net',
 48 |         username: 'test',
 49 |         password: 'test',
 50 |         clientId: 'test',
 51 |         clientSecret: 'test',
 52 |       },
 53 |       capabilities: {
 54 |         canAccessLogs: true,
 55 |         canAccessOCAPI: true,
 56 |       },
 57 |     };
 58 | 
 59 |     handler = new JobLogToolHandler(mockContext, 'job-log-handler');
 60 |   });
 61 | 
 62 |   afterEach(() => {
 63 |     jest.restoreAllMocks();
 64 |   });
 65 | 
 66 |   // Helper function to initialize handler
 67 |   const initializeHandler = async () => {
 68 |     await (handler as any).initialize();
 69 |   };
 70 | 
 71 |   describe('canHandle', () => {
 72 |     it('should handle job log tool names', () => {
 73 |       const jobLogTools = [
 74 |         'get_latest_job_log_files',
 75 |         'search_job_logs_by_name',
 76 |         'get_job_log_entries',
 77 |         'search_job_logs',
 78 |         'get_job_execution_summary',
 79 |       ];
 80 | 
 81 |       jobLogTools.forEach(toolName => {
 82 |         expect(handler.canHandle(toolName)).toBe(true);
 83 |       });
 84 |     });
 85 | 
 86 |     it('should not handle non-job-log tool names', () => {
 87 |       const nonJobLogTools = [
 88 |         'get_latest_error',
 89 |         'search_logs',
 90 |         'get_log_file_contents',
 91 |         'summarize_logs',
 92 |         'list_log_files',
 93 |         'unknown_tool',
 94 |         '',
 95 |       ];
 96 | 
 97 |       nonJobLogTools.forEach(toolName => {
 98 |         expect(handler.canHandle(toolName)).toBe(false);
 99 |       });
100 |     });
101 | 
102 |     it('should handle case-sensitive tool names only', () => {
103 |       expect(handler.canHandle('GET_LATEST_JOB_LOG_FILES')).toBe(false);
104 |       expect(handler.canHandle('get_Latest_Job_Log_Files')).toBe(false);
105 |       expect(handler.canHandle('get_latest_job_log_files')).toBe(true);
106 |     });
107 |   });
108 | 
109 |   describe('handle method - get_latest_job_log_files', () => {
110 |     beforeEach(async () => {
111 |       await initializeHandler();
112 |     });
113 | 
114 |     it('should handle get_latest_job_log_files with default limit', async () => {
115 |       const mockResult = 'Latest job log files result';
116 |       mockLogClient.getLatestJobLogFiles.mockResolvedValue(mockResult);
117 | 
118 |       const result = await handler.handle('get_latest_job_log_files', {}, Date.now());
119 | 
120 |       expect(mockLogClient.getLatestJobLogFiles).toHaveBeenCalledWith(10); // default limit
121 |       expect(result.content[0].text).toContain(mockResult);
122 |     });
123 | 
124 |     it('should handle get_latest_job_log_files with custom limit', async () => {
125 |       const mockResult = 'Latest job log files result';
126 |       mockLogClient.getLatestJobLogFiles.mockResolvedValue(mockResult);
127 | 
128 |       const result = await handler.handle('get_latest_job_log_files', { limit: 25 }, Date.now());
129 | 
130 |       expect(mockLogClient.getLatestJobLogFiles).toHaveBeenCalledWith(25);
131 |       expect(result.content[0].text).toContain(mockResult);
132 |     });
133 | 
134 |     it('should validate limit parameter', async () => {
135 |       const result = await handler.handle('get_latest_job_log_files', { limit: -1 }, Date.now());
136 | 
137 |       expect(result.isError).toBe(true);
138 |       expect(result.content[0].text).toContain('Invalid limit');
139 |     });
140 |   });
141 | 
142 |   describe('handle method - search_job_logs_by_name', () => {
143 |     beforeEach(async () => {
144 |       await initializeHandler();
145 |     });
146 | 
147 |     it('should handle search_job_logs_by_name with required jobName', async () => {
148 |       const mockResult = 'Search job logs by name result';
149 |       mockLogClient.searchJobLogsByName.mockResolvedValue(mockResult);
150 | 
151 |       const result = await handler.handle('search_job_logs_by_name', {
152 |         jobName: 'TestJob',
153 |         limit: 15,
154 |       }, Date.now());
155 | 
156 |       expect(mockLogClient.searchJobLogsByName).toHaveBeenCalledWith('TestJob', 15);
157 |       expect(result.content[0].text).toContain(mockResult);
158 |     });
159 | 
160 |     it('should validate required jobName parameter', async () => {
161 |       const result = await handler.handle('search_job_logs_by_name', {}, Date.now());
162 | 
163 |       expect(result.isError).toBe(true);
164 |       expect(result.content[0].text).toContain('jobName must be a non-empty string');
165 |     });
166 |   });
167 | 
168 |   describe('handle method - get_job_log_entries', () => {
169 |     beforeEach(async () => {
170 |       await initializeHandler();
171 |     });
172 | 
173 |     it('should handle get_job_log_entries with default parameters', async () => {
174 |       const mockResult = 'Job log entries result';
175 |       mockLogClient.getJobLogEntries.mockResolvedValue(mockResult);
176 | 
177 |       const result = await handler.handle('get_job_log_entries', {}, Date.now());
178 | 
179 |       expect(mockLogClient.getJobLogEntries).toHaveBeenCalledWith('all', 50, undefined); // default limit is 50 for job entries
180 |       expect(result.content[0].text).toContain(mockResult);
181 |     });
182 | 
183 |     it('should validate log level parameter', async () => {
184 |       const result = await handler.handle('get_job_log_entries', { level: 'invalid' }, Date.now());
185 | 
186 |       expect(result.isError).toBe(true);
187 |       expect(result.content[0].text).toContain('Invalid log level: invalid');
188 |     });
189 |   });
190 | 
191 |   describe('handle method - search_job_logs', () => {
192 |     beforeEach(async () => {
193 |       await initializeHandler();
194 |     });
195 | 
196 |     it('should handle search_job_logs with required pattern', async () => {
197 |       const mockResult = 'Search job logs result';
198 |       mockLogClient.searchJobLogs.mockResolvedValue(mockResult);
199 | 
200 |       const result = await handler.handle('search_job_logs', {
201 |         pattern: 'error-pattern',
202 |       }, Date.now());
203 | 
204 |       expect(mockLogClient.searchJobLogs).toHaveBeenCalledWith('error-pattern', 'all', 20, undefined);
205 |       expect(result.content[0].text).toContain(mockResult);
206 |     });
207 | 
208 |     it('should validate required pattern parameter', async () => {
209 |       const result = await handler.handle('search_job_logs', {}, Date.now());
210 | 
211 |       expect(result.isError).toBe(true);
212 |       expect(result.content[0].text).toContain('pattern must be a non-empty string');
213 |     });
214 |   });
215 | 
216 |   describe('handle method - get_job_execution_summary', () => {
217 |     beforeEach(async () => {
218 |       await initializeHandler();
219 |     });
220 | 
221 |     it('should handle get_job_execution_summary with required jobName', async () => {
222 |       const mockResult = 'Job execution summary result';
223 |       mockLogClient.getJobExecutionSummary.mockResolvedValue(mockResult);
224 | 
225 |       const result = await handler.handle('get_job_execution_summary', {
226 |         jobName: 'SummaryJob',
227 |       }, Date.now());
228 | 
229 |       expect(mockLogClient.getJobExecutionSummary).toHaveBeenCalledWith('SummaryJob');
230 |       expect(result.content[0].text).toContain(mockResult);
231 |     });
232 | 
233 |     it('should validate required jobName parameter', async () => {
234 |       const result = await handler.handle('get_job_execution_summary', {}, Date.now());
235 | 
236 |       expect(result.isError).toBe(true);
237 |       expect(result.content[0].text).toContain('jobName must be a non-empty string');
238 |     });
239 |   });
240 | 
241 |   describe('error handling', () => {
242 |     beforeEach(async () => {
243 |       await initializeHandler();
244 |     });
245 | 
246 |     it('should handle client errors gracefully', async () => {
247 |       const clientError = new Error('Client connection failed');
248 |       mockLogClient.getLatestJobLogFiles.mockRejectedValue(clientError);
249 | 
250 |       const result = await handler.handle('get_latest_job_log_files', {}, Date.now());
251 | 
252 |       expect(result.isError).toBe(true);
253 |       expect(result.content[0].text).toContain('Client connection failed');
254 |     });
255 | 
256 |     it('should handle unsupported tool names', async () => {
257 |       await expect(handler.handle('unsupported_tool', {}, Date.now()))
258 |         .rejects.toThrow('Unsupported tool: unsupported_tool');
259 |     });
260 |   });
261 | 
262 |   describe('initialization', () => {
263 |     it('should initialize log client when capabilities allow', async () => {
264 |       await initializeHandler();
265 | 
266 |       expect(SFCCLogClient).toHaveBeenCalledWith(mockContext.config);
267 |       expect(mockLogger.debug).toHaveBeenCalledWith('Log client initialized');
268 |     });
269 | 
270 |     it('should handle missing capabilities gracefully', async () => {
271 |       const contextWithoutLogs = {
272 |         ...mockContext,
273 |         capabilities: { canAccessLogs: false, canAccessOCAPI: false },
274 |       };
275 |       const handlerWithoutLogs = new JobLogToolHandler(contextWithoutLogs, 'job-log');
276 | 
277 |       const result = await handlerWithoutLogs.handle('get_latest_job_log_files', {}, Date.now());
278 | 
279 |       expect(result.isError).toBe(true);
280 |       expect(result.content[0].text).toContain('Log client not configured');
281 |     });
282 |   });
283 | });
284 | 
```

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

```typescript
  1 | /**
  2 |  * Log file discovery, filtering, and metadata operations
  3 |  */
  4 | 
  5 | import type { WebDAVClient } from 'webdav';
  6 | import { Logger } from '../../utils/logger.js';
  7 | import { getCurrentDate, normalizeFilePath } from '../../utils/utils.js';
  8 | import { LOG_CONSTANTS, LOG_FILE_PATTERNS, JOB_LOG_CONSTANTS } from './log-constants.js';
  9 | import type { LogFileMetadata, LogFileInfo, LogLevel, LogFileFilter, JobLogInfo, JobLogFilter } from './log-types.js';
 10 | 
 11 | export class LogFileDiscovery {
 12 |   private logger: Logger;
 13 |   private webdavClient: WebDAVClient;
 14 | 
 15 |   constructor(webdavClient: WebDAVClient, logger: Logger) {
 16 |     this.webdavClient = webdavClient;
 17 |     this.logger = logger;
 18 |   }
 19 | 
 20 |   /**
 21 |    * Get list of log files for a specific date
 22 |    */
 23 |   async getLogFiles(date?: string): Promise<LogFileMetadata[]> {
 24 |     const targetDate = date ?? getCurrentDate();
 25 |     this.logger.methodEntry('getLogFiles', { date: targetDate });
 26 | 
 27 |     const startTime = Date.now();
 28 |     const contents = await this.webdavClient.getDirectoryContents('/');
 29 |     this.logger.timing('webdav_getDirectoryContents', startTime);
 30 | 
 31 |     const logFiles = (contents as any[])
 32 |       .filter((item: any) =>
 33 |         item.type === 'file' &&
 34 |         item.filename.includes(targetDate) &&
 35 |         item.filename.endsWith('.log'),
 36 |       )
 37 |       .map((item: any) => ({
 38 |         filename: item.filename,
 39 |         lastmod: item.lastmod ?? new Date().toISOString(), // Fallback to current time if no lastmod
 40 |       }));
 41 | 
 42 |     this.logger.debug(`Found ${logFiles.length} log files for date ${targetDate}:`, logFiles.map((f: LogFileMetadata) => f.filename));
 43 |     this.logger.methodExit('getLogFiles', { count: logFiles.length });
 44 |     return logFiles;
 45 |   }
 46 | 
 47 |   /**
 48 |    * Filter log files by level and other criteria
 49 |    */
 50 |   filterLogFiles(files: LogFileMetadata[], filter: LogFileFilter): LogFileMetadata[] {
 51 |     if (!filter.level) {
 52 |       return files;
 53 |     }
 54 | 
 55 |     const { level, includeCustom = true } = filter;
 56 | 
 57 |     return files.filter(file => {
 58 |       const filename = normalizeFilePath(file.filename);
 59 |       const standardPattern = LOG_FILE_PATTERNS.STANDARD(level);
 60 |       const customPattern = LOG_FILE_PATTERNS.CUSTOM(level);
 61 | 
 62 |       if (filename.startsWith(standardPattern)) {
 63 |         return true;
 64 |       }
 65 | 
 66 |       if (includeCustom && filename.startsWith(customPattern)) {
 67 |         return true;
 68 |       }
 69 | 
 70 |       return false;
 71 |     });
 72 |   }
 73 | 
 74 |   /**
 75 |    * Get log files filtered by level with detailed logging
 76 |    */
 77 |   async getLogFilesByLevel(level: LogLevel, date?: string): Promise<LogFileMetadata[]> {
 78 |     const targetDate = date ?? getCurrentDate();
 79 |     const allFiles = await this.getLogFiles(targetDate);
 80 | 
 81 |     const filteredFiles = this.filterLogFiles(allFiles, { level, includeCustom: true });
 82 | 
 83 |     this.logger.debug(`Filtered to ${filteredFiles.length} ${level} log files (including custom logs):`,
 84 |       filteredFiles.map(f => f.filename));
 85 | 
 86 |     return filteredFiles;
 87 |   }
 88 | 
 89 |   /**
 90 |    * Sort log files by modification date
 91 |    */
 92 |   sortFilesByDate(files: LogFileMetadata[], descending = true): LogFileMetadata[] {
 93 |     return files.sort((a, b) => {
 94 |       const dateA = new Date(a.lastmod).getTime();
 95 |       const dateB = new Date(b.lastmod).getTime();
 96 |       return descending ? dateB - dateA : dateA - dateB;
 97 |     });
 98 |   }
 99 | 
100 |   /**
101 |    * Get all available log files with detailed metadata
102 |    */
103 |   async getAllLogFiles(): Promise<LogFileInfo[]> {
104 |     try {
105 |       const contents = await this.webdavClient.getDirectoryContents('/');
106 |       const allLogFiles = (contents as any[])
107 |         .filter((item: any) => item.type === 'file' && item.filename.endsWith('.log'));
108 | 
109 |       const logFiles: LogFileInfo[] = allLogFiles
110 |         .map((item: any) => ({
111 |           name: item.filename,
112 |           size: item.size,
113 |           lastModified: item.lastmod,
114 |         }))
115 |         // Sort by lastModified date in descending order (newest first)
116 |         .sort((a: LogFileInfo, b: LogFileInfo) => {
117 |           const dateA = new Date(a.lastModified).getTime();
118 |           const dateB = new Date(b.lastModified).getTime();
119 |           return dateB - dateA;
120 |         })
121 |         // Limit to 50 most recent files
122 |         .slice(0, LOG_CONSTANTS.MAX_LOG_FILES_DISPLAY);
123 | 
124 |       // Store total count for formatting
125 |       (logFiles as any).totalCount = allLogFiles.length;
126 | 
127 |       return logFiles;
128 |     } catch (error) {
129 |       throw new Error(`Failed to list log files: ${error instanceof Error ? error.message : String(error)}`);
130 |     }
131 |   }
132 | 
133 |   /**
134 |    * Get unique log levels available for a specific date
135 |    */
136 |   async getAvailableLogLevels(date?: string): Promise<LogLevel[]> {
137 |     const files = await this.getLogFiles(date);
138 |     const levels = new Set<LogLevel>();
139 | 
140 |     for (const file of files) {
141 |       const filename = normalizeFilePath(file.filename);
142 | 
143 |       for (const level of LOG_CONSTANTS.LOG_LEVELS) {
144 |         if (filename.startsWith(LOG_FILE_PATTERNS.STANDARD(level)) ||
145 |             filename.startsWith(LOG_FILE_PATTERNS.CUSTOM(level))) {
146 |           levels.add(level);
147 |         }
148 |       }
149 |     }
150 | 
151 |     return Array.from(levels);
152 |   }
153 | 
154 |   /**
155 |    * Get log file statistics for a date
156 |    */
157 |   async getLogFileStats(date?: string): Promise<{
158 |     totalFiles: number;
159 |     filesByLevel: Record<LogLevel, number>;
160 |     totalSize: number;
161 |     oldestFile?: string;
162 |     newestFile?: string;
163 |   }> {
164 |     const files = await this.getLogFiles(date);
165 |     const sortedFiles = this.sortFilesByDate(files, true);
166 | 
167 |     const stats = {
168 |       totalFiles: files.length,
169 |       filesByLevel: {} as Record<LogLevel, number>,
170 |       totalSize: 0,
171 |       oldestFile: sortedFiles[sortedFiles.length - 1]?.filename,
172 |       newestFile: sortedFiles[0]?.filename,
173 |     };
174 | 
175 |     // Initialize level counts
176 |     for (const level of LOG_CONSTANTS.LOG_LEVELS) {
177 |       stats.filesByLevel[level] = 0;
178 |     }
179 | 
180 |     // Count files by level
181 |     for (const file of files) {
182 |       for (const level of LOG_CONSTANTS.LOG_LEVELS) {
183 |         const filteredFiles = this.filterLogFiles([file], { level });
184 |         if (filteredFiles.length > 0) {
185 |           stats.filesByLevel[level]++;
186 |         }
187 |       }
188 |     }
189 | 
190 |     return stats;
191 |   }
192 | 
193 |   /**
194 |    * Get job log files from the /jobs/ folder structure
195 |    */
196 |   async getJobLogFiles(filter?: JobLogFilter): Promise<JobLogInfo[]> {
197 |     this.logger.methodEntry('getJobLogFiles', filter);
198 | 
199 |     try {
200 |       // List all directories in the jobs folder
201 |       const jobsContents = await this.webdavClient.getDirectoryContents(JOB_LOG_CONSTANTS.JOBS_FOLDER);
202 | 
203 |       const jobDirs = (jobsContents as any[])
204 |         .filter((item: any) => item.type === 'directory')
205 |         .map((item: any) => ({
206 |           name: item.filename.replace(JOB_LOG_CONSTANTS.JOBS_FOLDER, ''),
207 |           path: item.filename,
208 |           lastModified: item.lastmod ?? new Date().toISOString(),
209 |         }));
210 | 
211 |       this.logger.debug(`Found ${jobDirs.length} job directories`);
212 | 
213 |       const jobLogInfos: JobLogInfo[] = [];
214 | 
215 |       // Process each job directory to find log files
216 |       for (const jobDir of jobDirs) {
217 |         try {
218 |           const jobContents = await this.webdavClient.getDirectoryContents(jobDir.path);
219 |           const logFiles = (jobContents as any[])
220 |             .filter((item: any) =>
221 |               item.type === 'file' &&
222 |               JOB_LOG_CONSTANTS.JOB_LOG_PATTERN.test(item.filename.split('/').pop() ?? ''),
223 |             );
224 | 
225 |           for (const logFile of logFiles) {
226 |             const fileName = logFile.filename.split('/').pop() ?? '';
227 |             jobLogInfos.push({
228 |               jobName: decodeURIComponent(jobDir.name),
229 |               jobId: this.extractJobIdFromFilename(fileName),
230 |               logFile: logFile.filename,
231 |               lastModified: logFile.lastmod ?? jobDir.lastModified,
232 |               size: logFile.size,
233 |             });
234 |           }
235 |         } catch (error) {
236 |           this.logger.warn(`Failed to read job directory ${jobDir.name}: ${error}`);
237 |         }
238 |       }
239 | 
240 |       // Apply filtering
241 |       let filteredLogs = jobLogInfos;
242 | 
243 |       if (filter?.jobName) {
244 |         const searchName = filter.jobName.toLowerCase();
245 |         filteredLogs = filteredLogs.filter(log =>
246 |           log.jobName.toLowerCase().includes(searchName),
247 |         );
248 |       }
249 | 
250 |       // Sort by most recent first if requested (default behavior)
251 |       if (filter?.sortByRecent !== false) {
252 |         filteredLogs.sort((a, b) =>
253 |           new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime(),
254 |         );
255 |       }
256 | 
257 |       // Apply limit
258 |       if (filter?.limit) {
259 |         filteredLogs = filteredLogs.slice(0, filter.limit);
260 |       }
261 | 
262 |       this.logger.methodExit('getJobLogFiles', { count: filteredLogs.length });
263 |       return filteredLogs;
264 | 
265 |     } catch (error) {
266 |       this.logger.error(`Failed to get job log files: ${error}`);
267 |       throw new Error(`Failed to access job logs: ${error instanceof Error ? error.message : String(error)}`);
268 |     }
269 |   }
270 | 
271 |   /**
272 |    * Get the latest job log files, sorted by modification date
273 |    */
274 |   async getLatestJobLogFiles(limit?: number): Promise<JobLogInfo[]> {
275 |     return this.getJobLogFiles({
276 |       limit: limit ?? JOB_LOG_CONSTANTS.DEFAULT_JOB_LOG_LIMIT,
277 |       sortByRecent: true,
278 |     });
279 |   }
280 | 
281 |   /**
282 |    * Search for job logs by job name
283 |    */
284 |   async searchJobLogsByName(jobName: string, limit?: number): Promise<JobLogInfo[]> {
285 |     return this.getJobLogFiles({
286 |       jobName,
287 |       limit: limit ?? JOB_LOG_CONSTANTS.DEFAULT_JOB_LOG_LIMIT,
288 |       sortByRecent: true,
289 |     });
290 |   }
291 | 
292 |   /**
293 |    * Extract job ID from filename (Job-JobName-ID.log format)
294 |    */
295 |   private extractJobIdFromFilename(filename: string): string {
296 |     const match = filename.match(/Job-.+-([^.]+)\.log$/);
297 |     return match ? match[1] : 'unknown';
298 |   }
299 | }
300 | 
```

--------------------------------------------------------------------------------
/docs/dw_catalog/StoreMgr.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.catalog
  2 | 
  3 | # Class StoreMgr
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.catalog.StoreMgr
  9 | 
 10 | ## Description
 11 | 
 12 | Provides helper methods for getting stores based on id and querying for stores based on geolocation.
 13 | 
 14 | ## Properties
 15 | 
 16 | ### allStoreGroups
 17 | 
 18 | **Type:** Collection (Read Only)
 19 | 
 20 | All the store groups of the current site.
 21 | 
 22 | ### storeIDFromSession
 23 | 
 24 | **Type:** String (Read Only)
 25 | 
 26 | Get the store id associated with the current session. By default, the session store id is null.
 27 | 
 28 | ## Constructor Summary
 29 | 
 30 | ## Method Summary
 31 | 
 32 | ### getAllStoreGroups
 33 | 
 34 | **Signature:** `static getAllStoreGroups() : Collection`
 35 | 
 36 | Returns all the store groups of the current site.
 37 | 
 38 | ### getStore
 39 | 
 40 | **Signature:** `static getStore(storeID : String) : Store`
 41 | 
 42 | Returns the store object with the specified id or null if store with this id does not exist in the site.
 43 | 
 44 | ### getStoreGroup
 45 | 
 46 | **Signature:** `static getStoreGroup(storeGroupID : String) : StoreGroup`
 47 | 
 48 | Returns the store group with the specified id or null if the store group with this id does not exist in the current site.
 49 | 
 50 | ### getStoreIDFromSession
 51 | 
 52 | **Signature:** `static getStoreIDFromSession() : String`
 53 | 
 54 | Get the store id associated with the current session.
 55 | 
 56 | ### searchStoresByCoordinates
 57 | 
 58 | **Signature:** `static searchStoresByCoordinates(latitude : Number, longitude : Number, distanceUnit : String, maxDistance : Number, queryString : String, args : Object...) : LinkedHashMap`
 59 | 
 60 | Search for stores based on geo-coordinates.
 61 | 
 62 | ### searchStoresByCoordinates
 63 | 
 64 | **Signature:** `static searchStoresByCoordinates(latitude : Number, longitude : Number, distanceUnit : String, maxDistance : Number) : LinkedHashMap`
 65 | 
 66 | Convenience method.
 67 | 
 68 | ### searchStoresByPostalCode
 69 | 
 70 | **Signature:** `static searchStoresByPostalCode(countryCode : String, postalCode : String, distanceUnit : String, maxDistance : Number, queryString : String, args : Object...) : LinkedHashMap`
 71 | 
 72 | Search for stores by country/postal code and optionally by additional filter criteria.
 73 | 
 74 | ### searchStoresByPostalCode
 75 | 
 76 | **Signature:** `static searchStoresByPostalCode(countryCode : String, postalCode : String, distanceUnit : String, maxDistance : Number) : LinkedHashMap`
 77 | 
 78 | Convenience method.
 79 | 
 80 | ### setStoreIDToSession
 81 | 
 82 | **Signature:** `static setStoreIDToSession(storeID : String) : void`
 83 | 
 84 | Set the store id for the current session.
 85 | 
 86 | ## Method Detail
 87 | 
 88 | ## Method Details
 89 | 
 90 | ### getAllStoreGroups
 91 | 
 92 | **Signature:** `static getAllStoreGroups() : Collection`
 93 | 
 94 | **Description:** Returns all the store groups of the current site.
 95 | 
 96 | **Returns:**
 97 | 
 98 | The store groups of the current site.
 99 | 
100 | ---
101 | 
102 | ### getStore
103 | 
104 | **Signature:** `static getStore(storeID : String) : Store`
105 | 
106 | **Description:** Returns the store object with the specified id or null if store with this id does not exist in the site.
107 | 
108 | **Parameters:**
109 | 
110 | - `storeID`: the store identifier.
111 | 
112 | **Returns:**
113 | 
114 | Store for specified id or null.
115 | 
116 | ---
117 | 
118 | ### getStoreGroup
119 | 
120 | **Signature:** `static getStoreGroup(storeGroupID : String) : StoreGroup`
121 | 
122 | **Description:** Returns the store group with the specified id or null if the store group with this id does not exist in the current site.
123 | 
124 | **Parameters:**
125 | 
126 | - `storeGroupID`: the store group identifier.
127 | 
128 | **Returns:**
129 | 
130 | The store group for specified id or null.
131 | 
132 | ---
133 | 
134 | ### getStoreIDFromSession
135 | 
136 | **Signature:** `static getStoreIDFromSession() : String`
137 | 
138 | **Description:** Get the store id associated with the current session. By default, the session store id is null.
139 | 
140 | **Returns:**
141 | 
142 | store id, null is returned and means no store id is set on session
143 | 
144 | ---
145 | 
146 | ### searchStoresByCoordinates
147 | 
148 | **Signature:** `static searchStoresByCoordinates(latitude : Number, longitude : Number, distanceUnit : String, maxDistance : Number, queryString : String, args : Object...) : LinkedHashMap`
149 | 
150 | **Description:** Search for stores based on geo-coordinates. The method returns a list of stores for the current site that are within a specified distance of a location on the earth and which optionally satisfy additional filter criteria. If no additional criteria are specified, then this method behaves similar to GetNearestStores pipelet. The criteria are specified as a querystring, using the same syntax as SystemObjectMgr.querySystemObjects(String, String, String, Object...) The stores and their distance from the specified location are returned as a LinkedHashMap of Store objects to distances, sorting in ascending order by distance. The distance is interpreted either in miles or kilometers depending on the "distanceUnit" parameter. The latitude and longitude of each store is determined by first looking at Store.getLatitude() and Store.getLongitude(). If these are not set, then the system deduces the location of the store by looking for a configured geolocation matching the store's postal and country codes.
151 | 
152 | **Parameters:**
153 | 
154 | - `latitude`: Latitude coordinate which is the center of the search area. Must not be null or an exception is thrown.
155 | - `longitude`: Longitude coordinate which is the center of the search area. Must not be null or an exception is thrown.
156 | - `distanceUnit`: The distance unit to be used for the calculation. Supported values are 'mi' and 'km' (for miles and kilometers respectively). If an invalid value is passed then 'km' will be used.
157 | - `maxDistance`: Area (radius) in DistanceUnit where Stores will be searched for. If null is passed, a system default is used.
158 | - `queryString`: optional filter criteria specified as querystring.
159 | - `args`: the arguments to fill in the values in the querystring.
160 | 
161 | **Returns:**
162 | 
163 | The stores and their distance from the specified location, sorted in ascending order by distance.
164 | 
165 | ---
166 | 
167 | ### searchStoresByCoordinates
168 | 
169 | **Signature:** `static searchStoresByCoordinates(latitude : Number, longitude : Number, distanceUnit : String, maxDistance : Number) : LinkedHashMap`
170 | 
171 | **Description:** Convenience method. Same as searchStoresByCoordinates(latitude, longitude, distanceUnit, maxDistance, null).
172 | 
173 | **Parameters:**
174 | 
175 | - `latitude`: Latitude coordinate which is the center of the search area. Must not be null or an exception is thrown.
176 | - `longitude`: Longitude coordinate which is the center of the search area. Must not be null or an exception is thrown.
177 | - `distanceUnit`: The distance unit to be used for the calculation. Supported values are 'mi' and 'km' (for miles and kilometers respectively). If an invalid value is passed then 'km' will be used.
178 | - `maxDistance`: Area (radius) in DistanceUnit where Stores will be searched for. If null is passed, a system default is used.
179 | 
180 | **Returns:**
181 | 
182 | The stores and their distance from the specified location, sorted in ascending order by distance.
183 | 
184 | ---
185 | 
186 | ### searchStoresByPostalCode
187 | 
188 | **Signature:** `static searchStoresByPostalCode(countryCode : String, postalCode : String, distanceUnit : String, maxDistance : Number, queryString : String, args : Object...) : LinkedHashMap`
189 | 
190 | **Description:** Search for stores by country/postal code and optionally by additional filter criteria. This method is analagous to searchStoresByCoordinates(Number, Number, String, Number, String, Object...) but identifies a location on the earth indirectly using country and postal code. The method will look first in the saved geolocations of the system to find a geolocation matching the passed country and postal code. If none is found, this method will return an empty map. If one is found, it will use the latitude/longitude of the found geolocation as the center of the search.
191 | 
192 | **Parameters:**
193 | 
194 | - `countryCode`: The country code for the search area, must not be null.
195 | - `postalCode`: The postal code for the center of the search area, must not be null.
196 | - `distanceUnit`: The distance unit to be used for the calculation. Supported values are 'mi' and 'km' (for miles and kilometers respectively). If an invalid value is passed then 'km' will be used.
197 | - `maxDistance`: Area (radius) in DistanceUnit where Stores will be searched for. If null is passed, a system default is used.
198 | - `queryString`: An optional search querystring which provides additional criteria to filter stores by.
199 | - `args`: The arguments providing the dynamic values to the querystring.
200 | 
201 | **Returns:**
202 | 
203 | The stores and their distance from the specified location, sorted in ascending order by distance.
204 | 
205 | ---
206 | 
207 | ### searchStoresByPostalCode
208 | 
209 | **Signature:** `static searchStoresByPostalCode(countryCode : String, postalCode : String, distanceUnit : String, maxDistance : Number) : LinkedHashMap`
210 | 
211 | **Description:** Convenience method. Same as searchStoresByPostalCode(countryCode, postalCode, distanceUnit, maxDistance, null).
212 | 
213 | **Parameters:**
214 | 
215 | - `countryCode`: The country code for the search area, must not be null.
216 | - `postalCode`: The postal code for the center of the search area, must not be null.
217 | - `distanceUnit`: The distance unit to be used for the calculation. Supported values are 'mi' and 'km' (for miles and kilometers respectively). If an invalid value is passed then 'km' will be used.
218 | - `maxDistance`: Area (radius) in DistanceUnit where Stores will be searched for. If null is passed, a system default is used.
219 | 
220 | **Returns:**
221 | 
222 | The stores and their distance from the specified location, sorted in ascending order by distance.
223 | 
224 | ---
225 | 
226 | ### setStoreIDToSession
227 | 
228 | **Signature:** `static setStoreIDToSession(storeID : String) : void`
229 | 
230 | **Description:** Set the store id for the current session. The store id is also saved on the cookie with the cookie name "dw_store" with no expiration time. Null is allowed to remove store id from session, when null is passed in, the cookie will be removed when browser exits.
231 | 
232 | **Parameters:**
233 | 
234 | - `storeID`: the id of the store. The leading and trailing white spaces are removed by system from storeID
235 | 
236 | ---
```
Page 18/61FirstPrevNextLast