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

--------------------------------------------------------------------------------
/src/utils/cache.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * In-Memory Caching Module
  3 |  *
  4 |  * Provides efficient caching with TTL (Time-To-Live) and LRU (Least Recently Used) eviction
  5 |  * to reduce IO operations and improve response times for SFCC documentation queries.
  6 |  */
  7 | 
  8 | export interface CacheEntry<T> {
  9 |   value: T;
 10 |   timestamp: number;
 11 |   accessCount: number;
 12 |   lastAccessed: number;
 13 | }
 14 | 
 15 | export interface CacheOptions {
 16 |   maxSize?: number;
 17 |   ttlMs?: number;
 18 |   cleanupIntervalMs?: number;
 19 | }
 20 | 
 21 | export class InMemoryCache<T> {
 22 |   private cache = new Map<string, CacheEntry<T>>();
 23 |   private readonly maxSize: number;
 24 |   private readonly ttlMs: number;
 25 |   private cleanupTimer?: NodeJS.Timeout;
 26 | 
 27 |   constructor(options: CacheOptions = {}) {
 28 |     this.maxSize = options.maxSize ?? 1000;
 29 |     this.ttlMs = options.ttlMs ?? 60 * 60 * 1000; // 1 hour default for static data
 30 | 
 31 |     // Setup automatic cleanup - less frequent for static data
 32 |     const cleanupInterval = options.cleanupIntervalMs ?? 10 * 60 * 1000; // 10 minutes
 33 |     this.cleanupTimer = setInterval(() => this.cleanup(), cleanupInterval);
 34 |   }
 35 | 
 36 |   /**
 37 |    * Store a value in the cache
 38 |    */
 39 |   set(key: string, value: T): void {
 40 |     const now = Date.now();
 41 | 
 42 |     // Handle zero max size - don't store anything
 43 |     if (this.maxSize === 0) {
 44 |       return;
 45 |     }
 46 | 
 47 |     // If at max capacity and adding a new key, remove LRU item first
 48 |     if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
 49 |       this.evictLRU();
 50 |     }
 51 | 
 52 |     this.cache.set(key, {
 53 |       value,
 54 |       timestamp: now,
 55 |       accessCount: 0,
 56 |       lastAccessed: now,
 57 |     });
 58 |   }
 59 | 
 60 |   /**
 61 |    * Retrieve a value from the cache
 62 |    */
 63 |   get(key: string): T | undefined {
 64 |     const entry = this.cache.get(key);
 65 | 
 66 |     if (!entry) {
 67 |       return undefined;
 68 |     }
 69 | 
 70 |     const now = Date.now();
 71 | 
 72 |     // Check if entry has expired
 73 |     if (now - entry.timestamp > this.ttlMs) {
 74 |       this.cache.delete(key);
 75 |       return undefined;
 76 |     }
 77 | 
 78 |     // Update access statistics
 79 |     entry.accessCount++;
 80 |     entry.lastAccessed = now;
 81 | 
 82 |     return entry.value;
 83 |   }
 84 | 
 85 |   /**
 86 |    * Check if a key exists in the cache (without updating access stats)
 87 |    */
 88 |   has(key: string): boolean {
 89 |     const entry = this.cache.get(key);
 90 | 
 91 |     if (!entry) {
 92 |       return false;
 93 |     }
 94 | 
 95 |     // Check if entry has expired
 96 |     if (Date.now() - entry.timestamp > this.ttlMs) {
 97 |       this.cache.delete(key);
 98 |       return false;
 99 |     }
100 | 
101 |     return true;
102 |   }
103 | 
104 |   /**
105 |    * Remove a specific key from the cache
106 |    */
107 |   delete(key: string): boolean {
108 |     return this.cache.delete(key);
109 |   }
110 | 
111 |   /**
112 |    * Clear all entries from the cache
113 |    */
114 |   clear(): void {
115 |     this.cache.clear();
116 |   }
117 | 
118 |   /**
119 |    * Get cache statistics
120 |    */
121 |   getStats(): {
122 |     size: number;
123 |     maxSize: number;
124 |     hitRate: number;
125 |     entries: Array<{ key: string; accessCount: number; age: number }>;
126 |     } {
127 |     const now = Date.now();
128 |     const entries = Array.from(this.cache.entries()).map(([key, entry]) => ({
129 |       key,
130 |       accessCount: entry.accessCount,
131 |       age: now - entry.timestamp,
132 |     }));
133 | 
134 |     const totalAccesses = entries.reduce((sum, entry) => sum + entry.accessCount, 0);
135 |     const totalHits = entries.filter(entry => entry.accessCount > 0).length;
136 | 
137 |     return {
138 |       size: this.cache.size,
139 |       maxSize: this.maxSize,
140 |       hitRate: totalAccesses > 0 ? totalHits / totalAccesses : 0,
141 |       entries,
142 |     };
143 |   }
144 | 
145 |   /**
146 |    * Remove expired entries from the cache
147 |    */
148 |   private cleanup(): void {
149 |     const now = Date.now();
150 |     const expiredKeys: string[] = [];
151 | 
152 |     for (const [key, entry] of this.cache.entries()) {
153 |       if (now - entry.timestamp > this.ttlMs) {
154 |         expiredKeys.push(key);
155 |       }
156 |     }
157 | 
158 |     expiredKeys.forEach(key => this.cache.delete(key));
159 |   }
160 | 
161 |   /**
162 |    * Evict the least recently used item
163 |    */
164 |   private evictLRU(): void {
165 |     let oldestKey: string | undefined;
166 |     let oldestTime = Date.now();
167 | 
168 |     for (const [key, entry] of this.cache.entries()) {
169 |       if (entry.lastAccessed < oldestTime) {
170 |         oldestTime = entry.lastAccessed;
171 |         oldestKey = key;
172 |       }
173 |     }
174 | 
175 |     if (oldestKey) {
176 |       this.cache.delete(oldestKey);
177 |     }
178 |   }
179 | 
180 |   /**
181 |    * Cleanup resources
182 |    */
183 |   destroy(): void {
184 |     if (this.cleanupTimer) {
185 |       clearInterval(this.cleanupTimer);
186 |       this.cleanupTimer = undefined;
187 |     }
188 |     this.clear();
189 |   }
190 | }
191 | 
192 | /**
193 |  * Multi-layer cache manager for different types of data
194 |  */
195 | export class CacheManager {
196 |   private fileContentCache: InMemoryCache<string>;
197 |   private classDetailsCache: InMemoryCache<any>;
198 |   private searchResultsCache: InMemoryCache<any>;
199 |   private methodSearchCache: InMemoryCache<any>;
200 | 
201 |   constructor() {
202 |     // Much longer TTL for static documentation data that doesn't change during server runtime
203 |     this.fileContentCache = new InMemoryCache<string>({
204 |       maxSize: 500,
205 |       ttlMs: 4 * 60 * 60 * 1000, // 4 hours - raw file content is completely static
206 |       cleanupIntervalMs: 30 * 60 * 1000, // 30 minutes cleanup interval
207 |     });
208 | 
209 |     this.classDetailsCache = new InMemoryCache({
210 |       maxSize: 300,
211 |       ttlMs: 2 * 60 * 60 * 1000, // 2 hours - parsed data is static
212 |       cleanupIntervalMs: 20 * 60 * 1000, // 20 minutes cleanup interval
213 |     });
214 | 
215 |     this.searchResultsCache = new InMemoryCache({
216 |       maxSize: 200,
217 |       ttlMs: 60 * 60 * 1000, // 1 hour - search results are static
218 |       cleanupIntervalMs: 15 * 60 * 1000, // 15 minutes cleanup interval
219 |     });
220 | 
221 |     this.methodSearchCache = new InMemoryCache({
222 |       maxSize: 100,
223 |       ttlMs: 60 * 60 * 1000, // 1 hour - method search results are static
224 |       cleanupIntervalMs: 15 * 60 * 1000, // 15 minutes cleanup interval
225 |     });
226 |   }
227 | 
228 |   getFileContent(key: string): string | undefined {
229 |     return this.fileContentCache.get(key);
230 |   }
231 | 
232 |   setFileContent(key: string, content: string): void {
233 |     this.fileContentCache.set(key, content);
234 |   }
235 | 
236 |   getClassDetails(key: string): any {
237 |     return this.classDetailsCache.get(key);
238 |   }
239 | 
240 |   setClassDetails(key: string, details: any): void {
241 |     this.classDetailsCache.set(key, details);
242 |   }
243 | 
244 |   getSearchResults(key: string): any {
245 |     return this.searchResultsCache.get(key);
246 |   }
247 | 
248 |   setSearchResults(key: string, results: any): void {
249 |     this.searchResultsCache.set(key, results);
250 |   }
251 | 
252 |   getMethodSearch(key: string): any {
253 |     return this.methodSearchCache.get(key);
254 |   }
255 | 
256 |   setMethodSearch(key: string, results: any): void {
257 |     this.methodSearchCache.set(key, results);
258 |   }
259 | 
260 |   /**
261 |    * Get comprehensive cache statistics
262 |    */
263 |   getAllStats(): {
264 |     fileContent: ReturnType<InMemoryCache<any>['getStats']>;
265 |     classDetails: ReturnType<InMemoryCache<any>['getStats']>;
266 |     searchResults: ReturnType<InMemoryCache<any>['getStats']>;
267 |     methodSearch: ReturnType<InMemoryCache<any>['getStats']>;
268 |     } {
269 |     return {
270 |       fileContent: this.fileContentCache.getStats(),
271 |       classDetails: this.classDetailsCache.getStats(),
272 |       searchResults: this.searchResultsCache.getStats(),
273 |       methodSearch: this.methodSearchCache.getStats(),
274 |     };
275 |   }
276 | 
277 |   /**
278 |    * Clear all caches
279 |    */
280 |   clearAll(): void {
281 |     this.fileContentCache.clear();
282 |     this.classDetailsCache.clear();
283 |     this.searchResultsCache.clear();
284 |     this.methodSearchCache.clear();
285 |   }
286 | 
287 |   /**
288 |    * Cleanup all resources
289 |    */
290 |   destroy(): void {
291 |     this.fileContentCache.destroy();
292 |     this.classDetailsCache.destroy();
293 |     this.searchResultsCache.destroy();
294 |     this.methodSearchCache.destroy();
295 |   }
296 | }
297 | 
```

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

```typescript
  1 | /**
  2 |  * Output formatting and presentation logic for logs
  3 |  */
  4 | 
  5 | import { formatBytes } from '../../utils/utils.js';
  6 | import { LOG_CONSTANTS, LOG_MESSAGES } from './log-constants.js';
  7 | import type { LogSummary, LogFileInfo, LogLevel, JobLogInfo } from './log-types.js';
  8 | 
  9 | export class LogFormatter {
 10 |   /**
 11 |    * Format latest log entries response
 12 |    */
 13 |   static formatLatestLogs(
 14 |     entries: string[],
 15 |     level: LogLevel,
 16 |     limit: number,
 17 |     files: string[],
 18 |   ): string {
 19 |     const fileList = files.join(', ');
 20 |     return `Latest ${limit} ${level} messages from files: ${fileList}\n\n${entries.join('\n\n---\n\n')}`;
 21 |   }
 22 | 
 23 |   /**
 24 |    * Format search results
 25 |    */
 26 |   static formatSearchResults(
 27 |     matches: string[],
 28 |     pattern: string,
 29 |     date: string,
 30 |   ): string {
 31 |     if (matches.length === 0) {
 32 |       return LOG_MESSAGES.NO_SEARCH_MATCHES(pattern, date);
 33 |     }
 34 | 
 35 |     return `${LOG_MESSAGES.SEARCH_RESULTS(matches.length, pattern)}\n\n${matches.join('\n\n')}`;
 36 |   }
 37 | 
 38 |   /**
 39 |    * Format "no files found" message
 40 |    */
 41 |   static formatNoFilesFound(
 42 |     level: LogLevel,
 43 |     date: string,
 44 |     availableFiles: string[],
 45 |   ): string {
 46 |     const available = availableFiles.join(', ');
 47 |     return LOG_MESSAGES.NO_FILES_FOUND(level, date, available);
 48 |   }
 49 | 
 50 |   /**
 51 |    * Format log summary into a readable string
 52 |    */
 53 |   static formatLogSummary(summary: LogSummary): string {
 54 |     const keyIssuesSection = summary.keyIssues.length > 0
 55 |       ? summary.keyIssues.map((issue: string) => `- ${issue}`).join('\n')
 56 |       : 'No major issues detected';
 57 | 
 58 |     return [
 59 |       `Log Summary for ${summary.date}:`,
 60 |       '',
 61 |       '📊 Counts:',
 62 |       `- Errors: ${summary.errorCount}`,
 63 |       `- Warnings: ${summary.warningCount}`,
 64 |       `- Info: ${summary.infoCount}`,
 65 |       `- Debug: ${summary.debugCount}`,
 66 |       '',
 67 |       `📁 Log Files (${summary.files.length}):`,
 68 |       summary.files.map((f: string) => `- ${f}`).join('\n'),
 69 |       '',
 70 |       '🔥 Key Issues:',
 71 |       keyIssuesSection,
 72 |     ].join('\n');
 73 |   }
 74 | 
 75 |   /**
 76 |    * Format log files list with metadata
 77 |    */
 78 |   static formatLogFilesList(logFiles: LogFileInfo[]): string {
 79 |     const totalFiles = (logFiles as any).totalCount ?? logFiles.length;
 80 |     const showingText = totalFiles > LOG_CONSTANTS.MAX_LOG_FILES_DISPLAY
 81 |       ? ` (showing latest ${LOG_CONSTANTS.MAX_LOG_FILES_DISPLAY} of ${totalFiles} total)`
 82 |       : '';
 83 | 
 84 |     return `Available log files${showingText}:\n\n${logFiles.map((file: LogFileInfo) =>
 85 |       `📄 ${file.name}\n   Size: ${formatBytes(file.size)}\n   Modified: ${file.lastModified}`,
 86 |     ).join('\n\n')}`;
 87 |   }
 88 | 
 89 |   /**
 90 |    * Format file processing summary
 91 |    */
 92 |   static formatProcessingSummary(
 93 |     entriesReturned: number,
 94 |     filesProcessed: number,
 95 |     totalEntries: number,
 96 |   ): string {
 97 |     return `Parsed ${totalEntries} total entries from ${filesProcessed} files, returning latest ${entriesReturned}`;
 98 |   }
 99 | 
100 |   /**
101 |    * Format log level statistics
102 |    */
103 |   static formatLogLevelStats(stats: Record<LogLevel, number>): string {
104 |     const entries = Object.entries(stats) as Array<[LogLevel, number]>;
105 |     return entries
106 |       .map(([level, count]) => `${level}: ${count}`)
107 |       .join(', ');
108 |   }
109 | 
110 |   /**
111 |    * Format error message with context
112 |    */
113 |   static formatError(operation: string, error: unknown): string {
114 |     const message = error instanceof Error ? error.message : String(error);
115 |     return `Failed to ${operation}: ${message}`;
116 |   }
117 | 
118 |   /**
119 |    * Format file list for debugging
120 |    */
121 |   static formatFileList(files: string[], prefix = ''): string {
122 |     return files.map(f => `${prefix}${f}`).join(', ');
123 |   }
124 | 
125 |   /**
126 |    * Format timestamp for display
127 |    */
128 |   static formatTimestamp(date: Date): string {
129 |     return date.toISOString().replace('T', ' ').substring(0, 19);
130 |   }
131 | 
132 |   /**
133 |    * Format log entry with timestamp and level highlighting
134 |    */
135 |   static formatLogEntry(entry: string, highlightLevel = false): string {
136 |     if (!highlightLevel) {
137 |       return entry;
138 |     }
139 | 
140 |     return entry
141 |       .replace(/ ERROR /g, ' 🔴 ERROR ')
142 |       .replace(/ WARN /g, ' 🟡 WARN ')
143 |       .replace(/ INFO /g, ' 🔵 INFO ')
144 |       .replace(/ DEBUG /g, ' 🟢 DEBUG ');
145 |   }
146 | 
147 |   /**
148 |    * Format progress indicator
149 |    */
150 |   static formatProgress(current: number, total: number, operation: string): string {
151 |     const percentage = Math.round((current / total) * 100);
152 |     return `${operation}: ${current}/${total} (${percentage}%)`;
153 |   }
154 | 
155 |   /**
156 |    * Format file size summary
157 |    */
158 |   static formatFileSizes(files: Array<{ name: string; size: number }>): string {
159 |     const totalSize = files.reduce((sum, file) => sum + file.size, 0);
160 |     const avgSize = totalSize / files.length;
161 | 
162 |     return `Total: ${formatBytes(totalSize)}, Average: ${formatBytes(avgSize)}, Files: ${files.length}`;
163 |   }
164 | 
165 |   /**
166 |    * Truncate long text with ellipsis
167 |    */
168 |   static truncateText(text: string, maxLength: number): string {
169 |     if (text.length <= maxLength) {
170 |       return text;
171 |     }
172 |     return `${text.substring(0, maxLength - 3)}...`;
173 |   }
174 | 
175 |   /**
176 |    * Format job log list
177 |    */
178 |   static formatJobLogList(jobLogs: JobLogInfo[]): string {
179 |     if (jobLogs.length === 0) {
180 |       return 'No job logs found.';
181 |     }
182 | 
183 |     return `Found ${jobLogs.length} job logs:\n\n${jobLogs.map((jobLog: JobLogInfo) => {
184 |       const baseInfo = `🔧 Job: ${jobLog.jobName}\n   ID: ${jobLog.jobId}\n   File: ${jobLog.logFile.split('/').pop()}\n   Modified: ${jobLog.lastModified}`;
185 |       const sizeInfo = jobLog.size ? `\n   Size: ${formatBytes(jobLog.size)}` : '';
186 |       return baseInfo + sizeInfo;
187 |     }).join('\n\n')}`;
188 |   }
189 | 
190 |   /**
191 |    * Format job log entries with job context
192 |    */
193 |   static formatJobLogEntries(
194 |     entries: string[],
195 |     level: LogLevel | 'all',
196 |     limit: number,
197 |     jobContext?: string,
198 |   ): string {
199 |     const levelDisplay = level === 'all' ? 'all levels' : level;
200 |     const contextText = jobContext ? ` from ${jobContext}` : '';
201 | 
202 |     return `Latest ${limit} ${levelDisplay} messages${contextText}:\n\n${entries.join('\n\n---\n\n')}`;
203 |   }
204 | 
205 |   /**
206 |    * Format job execution summary
207 |    */
208 |   static formatJobExecutionSummary(summary: {
209 |     startTime?: string;
210 |     endTime?: string;
211 |     status?: string;
212 |     duration?: string;
213 |     errorCount: number;
214 |     warningCount: number;
215 |     steps: string[];
216 |   }, jobName: string): string {
217 |     const sections = [
218 |       `Job Execution Summary: ${jobName}`,
219 |       '',
220 |       '⏱️ Timing:',
221 |       `- Start: ${summary.startTime ?? 'Unknown'}`,
222 |       `- End: ${summary.endTime ?? 'Unknown'}`,
223 |       `- Duration: ${summary.duration ?? 'Unknown'}`,
224 |       '',
225 |       '📊 Status:',
226 |       `- Status: ${summary.status ?? 'Unknown'}`,
227 |       `- Errors: ${summary.errorCount}`,
228 |       `- Warnings: ${summary.warningCount}`,
229 |     ];
230 | 
231 |     if (summary.steps.length > 0) {
232 |       sections.push(
233 |         '',
234 |         '🔄 Steps:',
235 |         ...summary.steps.map(step => `- ${step}`),
236 |       );
237 |     }
238 | 
239 |     return sections.join('\n');
240 |   }
241 | 
242 |   /**
243 |    * Format job search results
244 |    */
245 |   static formatJobSearchResults(
246 |     matches: string[],
247 |     pattern: string,
248 |     jobContext?: string,
249 |   ): string {
250 |     if (matches.length === 0) {
251 |       const contextText = jobContext ? ` in ${jobContext} logs` : ' in job logs';
252 |       return `No matches found for "${pattern}"${contextText}`;
253 |     }
254 | 
255 |     const contextText = jobContext ? ` in ${jobContext} logs` : ' in job logs';
256 |     return `Found ${matches.length} matches for "${pattern}"${contextText}:\n\n${matches.join('\n\n')}`;
257 |   }
258 | }
259 | 
```

--------------------------------------------------------------------------------
/docs/dw_extensions.payments/SalesforcePaymentMethod.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.extensions.payments
  2 | 
  3 | # Class SalesforcePaymentMethod
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.extensions.payments.SalesforcePaymentMethod
  9 | 
 10 | ## Description
 11 | 
 12 | Salesforce Payments representation of a payment method object. See Salesforce Payments documentation for how to gain access and configure it for use on your sites. A payment method contains information about a credential used by a shopper to attempt payment, such as a payment card or bank account. The available information differs for each type of payment method. It includes only limited information that can be safely presented to a shopper to remind them what credential they used, and specifically not complete card, account, or other numbers that could be used to make future payments.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### TYPE_AFTERPAY_CLEARPAY
 17 | 
 18 | **Type:** String = "afterpay_clearpay"
 19 | 
 20 | Represents the Afterpay Clearpay payment method.
 21 | 
 22 | ### TYPE_BANCONTACT
 23 | 
 24 | **Type:** String = "bancontact"
 25 | 
 26 | Represents the Bancontact payment method.
 27 | 
 28 | ### TYPE_CARD
 29 | 
 30 | **Type:** String = "card"
 31 | 
 32 | Represents a credit card type of payment method.
 33 | 
 34 | ### TYPE_EPS
 35 | 
 36 | **Type:** String = "eps"
 37 | 
 38 | Represents the EPS (Electronic Payment Standard) payment method.
 39 | 
 40 | ### TYPE_IDEAL
 41 | 
 42 | **Type:** String = "ideal"
 43 | 
 44 | Represents the iDEAL payment method.
 45 | 
 46 | ### TYPE_KLARNA
 47 | 
 48 | **Type:** String = "klarna"
 49 | 
 50 | Represents the Klarna payment method.
 51 | 
 52 | ### TYPE_SEPA_DEBIT
 53 | 
 54 | **Type:** String = "sepa_debit"
 55 | 
 56 | Represents the SEPA Debit payment method.
 57 | 
 58 | ## Properties
 59 | 
 60 | ### bank
 61 | 
 62 | **Type:** String (Read Only)
 63 | 
 64 | The bank of this payment method, or null if none is available. Available on
 65 |  TYPE_IDEAL and TYPE_EPS type methods.
 66 | 
 67 | ### bankCode
 68 | 
 69 | **Type:** String (Read Only)
 70 | 
 71 | The bank code of this payment method, or null if none is available. Available on
 72 |  TYPE_SEPA_DEBIT and TYPE_BANCONTACT type methods.
 73 | 
 74 | ### bankName
 75 | 
 76 | **Type:** String (Read Only)
 77 | 
 78 | The bank name of this payment method, or null if none is available. Available on
 79 |  TYPE_BANCONTACT type methods.
 80 | 
 81 | ### branchCode
 82 | 
 83 | **Type:** String (Read Only)
 84 | 
 85 | The bank branch code of this payment method, or null if none is available. Available on
 86 |  TYPE_SEPA_DEBIT type methods.
 87 | 
 88 | ### brand
 89 | 
 90 | **Type:** String (Read Only)
 91 | 
 92 | The brand of this payment method, or null if none is available. Available on
 93 |  TYPE_CARD type methods.
 94 | 
 95 | ### country
 96 | 
 97 | **Type:** String (Read Only)
 98 | 
 99 | The country of this payment method, or null if none is available. Available on
100 |  TYPE_SEPA_DEBIT type methods.
101 | 
102 | ### ID
103 | 
104 | **Type:** String (Read Only)
105 | 
106 | The identifier of this payment method.
107 | 
108 | ### last4
109 | 
110 | **Type:** String (Read Only)
111 | 
112 | The last 4 digits of the credential for this payment method, or null if none is available.
113 |  Available on TYPE_CARD, TYPE_SEPA_DEBIT, and
114 |  TYPE_BANCONTACT type methods.
115 | 
116 | ### paymentMethodCategory
117 | 
118 | **Type:** String (Read Only)
119 | 
120 | The payment method category of this payment method, or null if none is available. Available
121 |  on TYPE_KLARNA type methods.
122 | 
123 | ### type
124 | 
125 | **Type:** String (Read Only)
126 | 
127 | The type of this payment method.
128 | 
129 | ## Constructor Summary
130 | 
131 | ## Method Summary
132 | 
133 | ### getBank
134 | 
135 | **Signature:** `getBank() : String`
136 | 
137 | Returns the bank of this payment method, or null if none is available.
138 | 
139 | ### getBankCode
140 | 
141 | **Signature:** `getBankCode() : String`
142 | 
143 | Returns the bank code of this payment method, or null if none is available.
144 | 
145 | ### getBankName
146 | 
147 | **Signature:** `getBankName() : String`
148 | 
149 | Returns the bank name of this payment method, or null if none is available.
150 | 
151 | ### getBranchCode
152 | 
153 | **Signature:** `getBranchCode() : String`
154 | 
155 | Returns the bank branch code of this payment method, or null if none is available.
156 | 
157 | ### getBrand
158 | 
159 | **Signature:** `getBrand() : String`
160 | 
161 | Returns the brand of this payment method, or null if none is available.
162 | 
163 | ### getCountry
164 | 
165 | **Signature:** `getCountry() : String`
166 | 
167 | Returns the country of this payment method, or null if none is available.
168 | 
169 | ### getID
170 | 
171 | **Signature:** `getID() : String`
172 | 
173 | Returns the identifier of this payment method.
174 | 
175 | ### getLast4
176 | 
177 | **Signature:** `getLast4() : String`
178 | 
179 | Returns the last 4 digits of the credential for this payment method, or null if none is available.
180 | 
181 | ### getPaymentDetails
182 | 
183 | **Signature:** `getPaymentDetails(paymentInstrument : OrderPaymentInstrument) : SalesforcePaymentDetails`
184 | 
185 | Returns the details to the Salesforce Payments payment for this payment method, using the given payment instrument.
186 | 
187 | ### getPaymentMethodCategory
188 | 
189 | **Signature:** `getPaymentMethodCategory() : String`
190 | 
191 | Returns the payment method category of this payment method, or null if none is available.
192 | 
193 | ### getType
194 | 
195 | **Signature:** `getType() : String`
196 | 
197 | Returns the type of this payment method.
198 | 
199 | ## Method Detail
200 | 
201 | ## Method Details
202 | 
203 | ### getBank
204 | 
205 | **Signature:** `getBank() : String`
206 | 
207 | **Description:** Returns the bank of this payment method, or null if none is available. Available on TYPE_IDEAL and TYPE_EPS type methods.
208 | 
209 | **Returns:**
210 | 
211 | payment method bank
212 | 
213 | ---
214 | 
215 | ### getBankCode
216 | 
217 | **Signature:** `getBankCode() : String`
218 | 
219 | **Description:** Returns the bank code of this payment method, or null if none is available. Available on TYPE_SEPA_DEBIT and TYPE_BANCONTACT type methods.
220 | 
221 | **Returns:**
222 | 
223 | payment method bank code
224 | 
225 | ---
226 | 
227 | ### getBankName
228 | 
229 | **Signature:** `getBankName() : String`
230 | 
231 | **Description:** Returns the bank name of this payment method, or null if none is available. Available on TYPE_BANCONTACT type methods.
232 | 
233 | **Returns:**
234 | 
235 | payment method bank name
236 | 
237 | ---
238 | 
239 | ### getBranchCode
240 | 
241 | **Signature:** `getBranchCode() : String`
242 | 
243 | **Description:** Returns the bank branch code of this payment method, or null if none is available. Available on TYPE_SEPA_DEBIT type methods.
244 | 
245 | **Returns:**
246 | 
247 | payment method bank branch code
248 | 
249 | ---
250 | 
251 | ### getBrand
252 | 
253 | **Signature:** `getBrand() : String`
254 | 
255 | **Description:** Returns the brand of this payment method, or null if none is available. Available on TYPE_CARD type methods.
256 | 
257 | **Returns:**
258 | 
259 | payment method brand
260 | 
261 | ---
262 | 
263 | ### getCountry
264 | 
265 | **Signature:** `getCountry() : String`
266 | 
267 | **Description:** Returns the country of this payment method, or null if none is available. Available on TYPE_SEPA_DEBIT type methods.
268 | 
269 | **Returns:**
270 | 
271 | payment method country
272 | 
273 | ---
274 | 
275 | ### getID
276 | 
277 | **Signature:** `getID() : String`
278 | 
279 | **Description:** Returns the identifier of this payment method.
280 | 
281 | **Returns:**
282 | 
283 | payment method identifier
284 | 
285 | ---
286 | 
287 | ### getLast4
288 | 
289 | **Signature:** `getLast4() : String`
290 | 
291 | **Description:** Returns the last 4 digits of the credential for this payment method, or null if none is available. Available on TYPE_CARD, TYPE_SEPA_DEBIT, and TYPE_BANCONTACT type methods.
292 | 
293 | **Returns:**
294 | 
295 | payment method credential last 4 digits
296 | 
297 | ---
298 | 
299 | ### getPaymentDetails
300 | 
301 | **Signature:** `getPaymentDetails(paymentInstrument : OrderPaymentInstrument) : SalesforcePaymentDetails`
302 | 
303 | **Description:** Returns the details to the Salesforce Payments payment for this payment method, using the given payment instrument.
304 | 
305 | **Parameters:**
306 | 
307 | - `paymentInstrument`: payment instrument
308 | 
309 | **Returns:**
310 | 
311 | The payment details
312 | 
313 | ---
314 | 
315 | ### getPaymentMethodCategory
316 | 
317 | **Signature:** `getPaymentMethodCategory() : String`
318 | 
319 | **Description:** Returns the payment method category of this payment method, or null if none is available. Available on TYPE_KLARNA type methods.
320 | 
321 | **Returns:**
322 | 
323 | payment method category
324 | 
325 | ---
326 | 
327 | ### getType
328 | 
329 | **Signature:** `getType() : String`
330 | 
331 | **Description:** Returns the type of this payment method.
332 | 
333 | **Returns:**
334 | 
335 | payment method type
336 | 
337 | **See Also:**
338 | 
339 | TYPE_BANCONTACT
340 | TYPE_CARD
341 | TYPE_EPS
342 | TYPE_AFTERPAY_CLEARPAY
343 | TYPE_IDEAL
344 | TYPE_SEPA_DEBIT
345 | 
346 | ---
```

--------------------------------------------------------------------------------
/docs/dw_web/Cookie.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.web
  2 | 
  3 | # Class Cookie
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.web.Cookie
  9 | 
 10 | ## Description
 11 | 
 12 | Represents an HTTP cookie used for storing information on a client browser. Cookies are passed along in the HTTP request and can be retrieved by calling dw.system.Request.getHttpCookies(). Cookies must comply with RFC6265. We recommend you use only printable ASCII characters without separators, such as a comma or equal sign. If JSON is used as a cookie value, it must be encoded. Note: this class allows access to sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12. See Request.getHttpCookies().
 13 | 
 14 | ## Constants
 15 | 
 16 | ### EMPTYNAME
 17 | 
 18 | **Type:** String = "dw_emptyname__"
 19 | 
 20 | Default name for cookies with empty strings.
 21 | 
 22 | ## Properties
 23 | 
 24 | ### comment
 25 | 
 26 | **Type:** String
 27 | 
 28 | The comment associated with the cookie.
 29 | 
 30 | ### domain
 31 | 
 32 | **Type:** String
 33 | 
 34 | The domain associated with the cookie.
 35 | 
 36 | ### httpOnly
 37 | 
 38 | **Type:** boolean
 39 | 
 40 | Identifies if the cookie is http-only.
 41 | 
 42 | ### maxAge
 43 | 
 44 | **Type:** Number
 45 | 
 46 | The maximum age of the cookie, specified in seconds.
 47 |  By default, -1 indicating the cookie will persist until client shutdown.
 48 | 
 49 | ### name
 50 | 
 51 | **Type:** String (Read Only)
 52 | 
 53 | The cookie's name.
 54 | 
 55 | ### path
 56 | 
 57 | **Type:** String
 58 | 
 59 | The path for the cookie.
 60 | 
 61 | ### secure
 62 | 
 63 | **Type:** boolean
 64 | 
 65 | Identifies if the cookie is secure.
 66 | 
 67 | ### value
 68 | 
 69 | **Type:** String
 70 | 
 71 | The cookie's value.
 72 | 
 73 | ### version
 74 | 
 75 | **Type:** Number
 76 | 
 77 | The version for the cookie. 0 means original Netscape cookie and
 78 |  1 means RFC 2109 compliant cookie.
 79 | 
 80 | ## Constructor Summary
 81 | 
 82 | Cookie(name : String, value : String) Constructs a new cookie using the specified name and value.
 83 | 
 84 | ## Method Summary
 85 | 
 86 | ### getComment
 87 | 
 88 | **Signature:** `getComment() : String`
 89 | 
 90 | Returns the comment associated with the cookie.
 91 | 
 92 | ### getDomain
 93 | 
 94 | **Signature:** `getDomain() : String`
 95 | 
 96 | Returns the domain associated with the cookie.
 97 | 
 98 | ### getMaxAge
 99 | 
100 | **Signature:** `getMaxAge() : Number`
101 | 
102 | Returns the maximum age of the cookie, specified in seconds.
103 | 
104 | ### getName
105 | 
106 | **Signature:** `getName() : String`
107 | 
108 | Returns the cookie's name.
109 | 
110 | ### getPath
111 | 
112 | **Signature:** `getPath() : String`
113 | 
114 | Returns the path for the cookie.
115 | 
116 | ### getSecure
117 | 
118 | **Signature:** `getSecure() : boolean`
119 | 
120 | Identifies if the cookie is secure.
121 | 
122 | ### getValue
123 | 
124 | **Signature:** `getValue() : String`
125 | 
126 | Returns the cookie's value.
127 | 
128 | ### getVersion
129 | 
130 | **Signature:** `getVersion() : Number`
131 | 
132 | Returns the version for the cookie.
133 | 
134 | ### isHttpOnly
135 | 
136 | **Signature:** `isHttpOnly() : boolean`
137 | 
138 | Identifies if the cookie is http-only.
139 | 
140 | ### setComment
141 | 
142 | **Signature:** `setComment(comment : String) : void`
143 | 
144 | Sets the comment associated with the cookie.
145 | 
146 | ### setDomain
147 | 
148 | **Signature:** `setDomain(domain : String) : void`
149 | 
150 | Sets the domain associated with the cookie.
151 | 
152 | ### setHttpOnly
153 | 
154 | **Signature:** `setHttpOnly(httpOnly : boolean) : void`
155 | 
156 | Sets the http-only state for the cookie.
157 | 
158 | ### setMaxAge
159 | 
160 | **Signature:** `setMaxAge(age : Number) : void`
161 | 
162 | Sets the maximum age of the cookie in seconds.
163 | 
164 | ### setPath
165 | 
166 | **Signature:** `setPath(path : String) : void`
167 | 
168 | Sets the path for the cookie.
169 | 
170 | ### setSecure
171 | 
172 | **Signature:** `setSecure(secure : boolean) : void`
173 | 
174 | Sets the secure state for the cookie.
175 | 
176 | ### setValue
177 | 
178 | **Signature:** `setValue(value : String) : void`
179 | 
180 | Sets the cookie's value.
181 | 
182 | ### setVersion
183 | 
184 | **Signature:** `setVersion(version : Number) : void`
185 | 
186 | Sets the version for the cookie.
187 | 
188 | ## Constructor Detail
189 | 
190 | ## Method Detail
191 | 
192 | ## Method Details
193 | 
194 | ### getComment
195 | 
196 | **Signature:** `getComment() : String`
197 | 
198 | **Description:** Returns the comment associated with the cookie.
199 | 
200 | **Returns:**
201 | 
202 | the comment associated with the cookie.
203 | 
204 | ---
205 | 
206 | ### getDomain
207 | 
208 | **Signature:** `getDomain() : String`
209 | 
210 | **Description:** Returns the domain associated with the cookie.
211 | 
212 | **Returns:**
213 | 
214 | the domain associated with the cookie.
215 | 
216 | ---
217 | 
218 | ### getMaxAge
219 | 
220 | **Signature:** `getMaxAge() : Number`
221 | 
222 | **Description:** Returns the maximum age of the cookie, specified in seconds. By default, -1 indicating the cookie will persist until client shutdown.
223 | 
224 | **Returns:**
225 | 
226 | an integer specifying the maximum age of the cookie in seconds; if negative, means the cookie persists until client shutdown
227 | 
228 | ---
229 | 
230 | ### getName
231 | 
232 | **Signature:** `getName() : String`
233 | 
234 | **Description:** Returns the cookie's name.
235 | 
236 | **Returns:**
237 | 
238 | the cookie's name.
239 | 
240 | ---
241 | 
242 | ### getPath
243 | 
244 | **Signature:** `getPath() : String`
245 | 
246 | **Description:** Returns the path for the cookie.
247 | 
248 | **Returns:**
249 | 
250 | the path for the cookie.
251 | 
252 | ---
253 | 
254 | ### getSecure
255 | 
256 | **Signature:** `getSecure() : boolean`
257 | 
258 | **Description:** Identifies if the cookie is secure.
259 | 
260 | **Returns:**
261 | 
262 | true if the cookie is secure, false otherwise.
263 | 
264 | ---
265 | 
266 | ### getValue
267 | 
268 | **Signature:** `getValue() : String`
269 | 
270 | **Description:** Returns the cookie's value.
271 | 
272 | **Returns:**
273 | 
274 | the cookie's value.
275 | 
276 | ---
277 | 
278 | ### getVersion
279 | 
280 | **Signature:** `getVersion() : Number`
281 | 
282 | **Description:** Returns the version for the cookie. 0 means original Netscape cookie and 1 means RFC 2109 compliant cookie.
283 | 
284 | **Returns:**
285 | 
286 | the version for the cookie.
287 | 
288 | ---
289 | 
290 | ### isHttpOnly
291 | 
292 | **Signature:** `isHttpOnly() : boolean`
293 | 
294 | **Description:** Identifies if the cookie is http-only.
295 | 
296 | **Returns:**
297 | 
298 | true if the cookie is http-only, false otherwise.
299 | 
300 | ---
301 | 
302 | ### setComment
303 | 
304 | **Signature:** `setComment(comment : String) : void`
305 | 
306 | **Description:** Sets the comment associated with the cookie. Setting a comment automatically changes the cookie to be a RFC 2109 (set-cookie2) compliant cookie, because comments are only supported with RFC cookies and not with Netscapes original cookie.
307 | 
308 | **Parameters:**
309 | 
310 | - `comment`: the comment associated with the cookie.
311 | 
312 | ---
313 | 
314 | ### setDomain
315 | 
316 | **Signature:** `setDomain(domain : String) : void`
317 | 
318 | **Description:** Sets the domain associated with the cookie.
319 | 
320 | **Parameters:**
321 | 
322 | - `domain`: the comment associated with the cookie.
323 | 
324 | ---
325 | 
326 | ### setHttpOnly
327 | 
328 | **Signature:** `setHttpOnly(httpOnly : boolean) : void`
329 | 
330 | **Description:** Sets the http-only state for the cookie.
331 | 
332 | **Parameters:**
333 | 
334 | - `httpOnly`: sets http-only state for the cookie.
335 | 
336 | ---
337 | 
338 | ### setMaxAge
339 | 
340 | **Signature:** `setMaxAge(age : Number) : void`
341 | 
342 | **Description:** Sets the maximum age of the cookie in seconds. A positive value indicates that the cookie will expire after that many seconds have passed. Note that the value is the maximum age when the cookie will expire, not the cookie's current age. A negative value means that the cookie is not stored persistently and will be deleted when the client exits. A zero value causes the cookie to be deleted.
343 | 
344 | **Parameters:**
345 | 
346 | - `age`: an integer specifying the maximum age of the cookie in seconds; if negative, means the cookie is not stored; if zero, deletes the cookie
347 | 
348 | ---
349 | 
350 | ### setPath
351 | 
352 | **Signature:** `setPath(path : String) : void`
353 | 
354 | **Description:** Sets the path for the cookie.
355 | 
356 | **Parameters:**
357 | 
358 | - `path`: the path for the cookie.
359 | 
360 | ---
361 | 
362 | ### setSecure
363 | 
364 | **Signature:** `setSecure(secure : boolean) : void`
365 | 
366 | **Description:** Sets the secure state for the cookie.
367 | 
368 | **Parameters:**
369 | 
370 | - `secure`: sets secure state for the cookie.
371 | 
372 | ---
373 | 
374 | ### setValue
375 | 
376 | **Signature:** `setValue(value : String) : void`
377 | 
378 | **Description:** Sets the cookie's value.
379 | 
380 | **Parameters:**
381 | 
382 | - `value`: the value to set in the cookie.
383 | 
384 | ---
385 | 
386 | ### setVersion
387 | 
388 | **Signature:** `setVersion(version : Number) : void`
389 | 
390 | **Description:** Sets the version for the cookie. 0 means original Netscape cookie and 1 means RFC 2109 compliant cookie. The default is 0.
391 | 
392 | **Parameters:**
393 | 
394 | - `version`: the version for the cookie.
395 | 
396 | ---
```

--------------------------------------------------------------------------------
/docs/dw_campaign/Coupon.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.campaign
  2 | 
  3 | # Class Coupon
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.PersistentObject
  9 |   - dw.campaign.Coupon
 10 | 
 11 | ## Description
 12 | 
 13 | Represents a coupon in Commerce Cloud Digital.
 14 | 
 15 | ## Constants
 16 | 
 17 | ### TYPE_MULTIPLE_CODES
 18 | 
 19 | **Type:** String = "MULTIPLE_CODES"
 20 | 
 21 | Constant representing coupon type multiple-codes.
 22 | 
 23 | ### TYPE_SINGLE_CODE
 24 | 
 25 | **Type:** String = "SINGLE_CODE"
 26 | 
 27 | Constant representing coupon type single-code.
 28 | 
 29 | ### TYPE_SYSTEM_CODES
 30 | 
 31 | **Type:** String = "SYSTEM_CODES"
 32 | 
 33 | Constant representing coupon type system-codes.
 34 | 
 35 | ## Properties
 36 | 
 37 | ### codePrefix
 38 | 
 39 | **Type:** String (Read Only)
 40 | 
 41 | The prefix defined for coupons of type TYPE_SYSTEM_CODES
 42 |  If no prefix is defined, or coupon is of type TYPE_SINGLE_CODE
 43 |  or TYPE_MULTIPLE_CODES, null is returned.
 44 | 
 45 | ### enabled
 46 | 
 47 | **Type:** boolean (Read Only)
 48 | 
 49 | Returns true if coupon is enabled, else false.
 50 | 
 51 | ### ID
 52 | 
 53 | **Type:** String (Read Only)
 54 | 
 55 | The ID of the coupon.
 56 | 
 57 | ### nextCouponCode
 58 | 
 59 | **Type:** String (Read Only)
 60 | 
 61 | The next unissued code of this coupon.
 62 |  For single-code coupons, the single fixed coupon code is returned.
 63 |  For all multi-code coupons, the next available, unissued coupon code is returned.
 64 |  If all codes of the coupon have been issued, then there is no next code, and null is returned.
 65 | 
 66 |  A transaction is required when calling this method. This needs to be ensured by the calling script.
 67 | 
 68 | ### promotions
 69 | 
 70 | **Type:** Collection (Read Only)
 71 | 
 72 | The coupon-based promotions directly or indirectly (through
 73 |  campaigns) assigned to this coupon.
 74 | 
 75 | ### redemptionLimitPerCode
 76 | 
 77 | **Type:** Number (Read Only)
 78 | 
 79 | The defined limit on redemption per coupon code. Null is
 80 |  returned if no limit is defined, which means that each code can be
 81 |  redeemed an unlimited number of times.
 82 | 
 83 | ### redemptionLimitPerCustomer
 84 | 
 85 | **Type:** Number (Read Only)
 86 | 
 87 | The defined limit on redemption of this coupon per customer.
 88 |  Null is returned if no limit is defined, which means that customers can
 89 |  redeem this coupon an unlimited number of times.
 90 | 
 91 | ### redemptionLimitPerTimeFrame
 92 | 
 93 | **Type:** Number (Read Only)
 94 | 
 95 | The defined limit on redemption per customer per time-frame (see
 96 |  getRedemptionLimitTimeFrame(). Null is returned if no limit is
 97 |  defined, which means that there is no time-specific redemption limit for
 98 |  customers.
 99 | 
100 | ### redemptionLimitTimeFrame
101 | 
102 | **Type:** Number (Read Only)
103 | 
104 | The time-frame (in days) of the defined limit on redemption per
105 |  customer per time-frame. Null is returned if no limit is defined, which
106 |  means that there is no time-specific redemption limit for customers.
107 | 
108 | ### type
109 | 
110 | **Type:** String (Read Only)
111 | 
112 | The coupon type.
113 |  Possible values are TYPE_SINGLE_CODE, TYPE_MULTIPLE_CODES
114 |  and TYPE_SYSTEM_CODES.
115 | 
116 | ## Constructor Summary
117 | 
118 | ## Method Summary
119 | 
120 | ### getCodePrefix
121 | 
122 | **Signature:** `getCodePrefix() : String`
123 | 
124 | Returns the prefix defined for coupons of type TYPE_SYSTEM_CODES If no prefix is defined, or coupon is of type TYPE_SINGLE_CODE or TYPE_MULTIPLE_CODES, null is returned.
125 | 
126 | ### getID
127 | 
128 | **Signature:** `getID() : String`
129 | 
130 | Returns the ID of the coupon.
131 | 
132 | ### getNextCouponCode
133 | 
134 | **Signature:** `getNextCouponCode() : String`
135 | 
136 | Returns the next unissued code of this coupon.
137 | 
138 | ### getPromotions
139 | 
140 | **Signature:** `getPromotions() : Collection`
141 | 
142 | Returns the coupon-based promotions directly or indirectly (through campaigns) assigned to this coupon.
143 | 
144 | ### getRedemptionLimitPerCode
145 | 
146 | **Signature:** `getRedemptionLimitPerCode() : Number`
147 | 
148 | Returns the defined limit on redemption per coupon code.
149 | 
150 | ### getRedemptionLimitPerCustomer
151 | 
152 | **Signature:** `getRedemptionLimitPerCustomer() : Number`
153 | 
154 | Returns the defined limit on redemption of this coupon per customer.
155 | 
156 | ### getRedemptionLimitPerTimeFrame
157 | 
158 | **Signature:** `getRedemptionLimitPerTimeFrame() : Number`
159 | 
160 | Returns the defined limit on redemption per customer per time-frame (see getRedemptionLimitTimeFrame().
161 | 
162 | ### getRedemptionLimitTimeFrame
163 | 
164 | **Signature:** `getRedemptionLimitTimeFrame() : Number`
165 | 
166 | Returns the time-frame (in days) of the defined limit on redemption per customer per time-frame.
167 | 
168 | ### getType
169 | 
170 | **Signature:** `getType() : String`
171 | 
172 | Returns the coupon type.
173 | 
174 | ### isEnabled
175 | 
176 | **Signature:** `isEnabled() : boolean`
177 | 
178 | Returns true if coupon is enabled, else false.
179 | 
180 | ## Method Detail
181 | 
182 | ## Method Details
183 | 
184 | ### getCodePrefix
185 | 
186 | **Signature:** `getCodePrefix() : String`
187 | 
188 | **Description:** Returns the prefix defined for coupons of type TYPE_SYSTEM_CODES If no prefix is defined, or coupon is of type TYPE_SINGLE_CODE or TYPE_MULTIPLE_CODES, null is returned.
189 | 
190 | **Returns:**
191 | 
192 | Coupon code prefix or null
193 | 
194 | ---
195 | 
196 | ### getID
197 | 
198 | **Signature:** `getID() : String`
199 | 
200 | **Description:** Returns the ID of the coupon.
201 | 
202 | **Returns:**
203 | 
204 | Coupon ID
205 | 
206 | ---
207 | 
208 | ### getNextCouponCode
209 | 
210 | **Signature:** `getNextCouponCode() : String`
211 | 
212 | **Description:** Returns the next unissued code of this coupon. For single-code coupons, the single fixed coupon code is returned. For all multi-code coupons, the next available, unissued coupon code is returned. If all codes of the coupon have been issued, then there is no next code, and null is returned. A transaction is required when calling this method. This needs to be ensured by the calling script.
213 | 
214 | **Returns:**
215 | 
216 | Next available code of this coupon, or null if there are no available codes.
217 | 
218 | ---
219 | 
220 | ### getPromotions
221 | 
222 | **Signature:** `getPromotions() : Collection`
223 | 
224 | **Description:** Returns the coupon-based promotions directly or indirectly (through campaigns) assigned to this coupon.
225 | 
226 | **Returns:**
227 | 
228 | Promotions assigned to the coupon in no particular order.
229 | 
230 | ---
231 | 
232 | ### getRedemptionLimitPerCode
233 | 
234 | **Signature:** `getRedemptionLimitPerCode() : Number`
235 | 
236 | **Description:** Returns the defined limit on redemption per coupon code. Null is returned if no limit is defined, which means that each code can be redeemed an unlimited number of times.
237 | 
238 | **Returns:**
239 | 
240 | The maximum number of redemption per coupon code
241 | 
242 | ---
243 | 
244 | ### getRedemptionLimitPerCustomer
245 | 
246 | **Signature:** `getRedemptionLimitPerCustomer() : Number`
247 | 
248 | **Description:** Returns the defined limit on redemption of this coupon per customer. Null is returned if no limit is defined, which means that customers can redeem this coupon an unlimited number of times.
249 | 
250 | **Returns:**
251 | 
252 | The maximum number of redemption per customer
253 | 
254 | ---
255 | 
256 | ### getRedemptionLimitPerTimeFrame
257 | 
258 | **Signature:** `getRedemptionLimitPerTimeFrame() : Number`
259 | 
260 | **Description:** Returns the defined limit on redemption per customer per time-frame (see getRedemptionLimitTimeFrame(). Null is returned if no limit is defined, which means that there is no time-specific redemption limit for customers.
261 | 
262 | **Returns:**
263 | 
264 | The maximum number of redemption per customer within time-frame
265 | 
266 | **See Also:**
267 | 
268 | getRedemptionLimitTimeFrame()
269 | 
270 | ---
271 | 
272 | ### getRedemptionLimitTimeFrame
273 | 
274 | **Signature:** `getRedemptionLimitTimeFrame() : Number`
275 | 
276 | **Description:** Returns the time-frame (in days) of the defined limit on redemption per customer per time-frame. Null is returned if no limit is defined, which means that there is no time-specific redemption limit for customers.
277 | 
278 | **Returns:**
279 | 
280 | Timeframe (days) of redemption per time
281 | 
282 | **See Also:**
283 | 
284 | getRedemptionLimitPerTimeFrame()
285 | 
286 | ---
287 | 
288 | ### getType
289 | 
290 | **Signature:** `getType() : String`
291 | 
292 | **Description:** Returns the coupon type. Possible values are TYPE_SINGLE_CODE, TYPE_MULTIPLE_CODES and TYPE_SYSTEM_CODES.
293 | 
294 | **Returns:**
295 | 
296 | Coupon type
297 | 
298 | ---
299 | 
300 | ### isEnabled
301 | 
302 | **Signature:** `isEnabled() : boolean`
303 | 
304 | **Description:** Returns true if coupon is enabled, else false.
305 | 
306 | **Returns:**
307 | 
308 | true if coupon is enabled.
309 | 
310 | ---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/get-job-log-entries.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
  1 | ---
  2 | description: "Test get_job_log_entries tool in full mode - Aegis framework validation and core MCP functionality"
  3 | tests:
  4 |   # Core functionality tests - essential MCP protocol validation
  5 |   - it: "should retrieve job log entries with default parameters"
  6 |     request:
  7 |       jsonrpc: "2.0"
  8 |       id: "job-entries-default"
  9 |       method: "tools/call"
 10 |       params:
 11 |         name: "get_job_log_entries"
 12 |         arguments: {}
 13 |     expect:
 14 |       response:
 15 |         jsonrpc: "2.0"
 16 |         id: "job-entries-default"
 17 |         result:
 18 |           content:
 19 |             match:arrayElements:
 20 |               match:partial:
 21 |                 type: "text"
 22 |                 text: "match:contains:Latest"
 23 |           isError: false
 24 |       stderr: "toBeEmpty"
 25 |     performance:
 26 |       maxResponseTime: "2000ms"
 27 | 
 28 |   - it: "should respect limit parameter"
 29 |     request:
 30 |       jsonrpc: "2.0"
 31 |       id: "job-entries-limit"
 32 |       method: "tools/call"
 33 |       params:
 34 |         name: "get_job_log_entries"
 35 |         arguments:
 36 |           limit: 5
 37 |     expect:
 38 |       response:
 39 |         jsonrpc: "2.0"
 40 |         id: "job-entries-limit"
 41 |         result:
 42 |           content:
 43 |             match:arrayElements:
 44 |               match:partial:
 45 |                 type: "text"
 46 |                 text: "match:contains:Latest 5"
 47 |           isError: false
 48 |       stderr: "toBeEmpty"
 49 |     performance:
 50 |       maxResponseTime: "2000ms"
 51 | 
 52 |   # Parameter validation tests - level filtering (sample representative cases)
 53 |   - it: "should filter by error level"
 54 |     request:
 55 |       jsonrpc: "2.0"
 56 |       id: "job-entries-error"
 57 |       method: "tools/call"
 58 |       params:
 59 |         name: "get_job_log_entries"
 60 |         arguments:
 61 |           level: "error"
 62 |           limit: 3
 63 |     expect:
 64 |       response:
 65 |         jsonrpc: "2.0"
 66 |         id: "job-entries-error"
 67 |         result:
 68 |           content:
 69 |             match:arrayElements:
 70 |               match:partial:
 71 |                 type: "text"
 72 |                 text: "match:contains:error messages"
 73 |           isError: false
 74 |       stderr: "toBeEmpty"
 75 | 
 76 |   - it: "should filter by info level"
 77 |     request:
 78 |       jsonrpc: "2.0"
 79 |       id: "job-entries-info"
 80 |       method: "tools/call"
 81 |       params:
 82 |         name: "get_job_log_entries"
 83 |         arguments:
 84 |           level: "info"
 85 |           limit: 3
 86 |     expect:
 87 |       response:
 88 |         jsonrpc: "2.0"
 89 |         id: "job-entries-info"
 90 |         result:
 91 |           content:
 92 |             match:arrayElements:
 93 |               match:partial:
 94 |                 type: "text"
 95 |                 text: "match:contains:info messages"
 96 |           isError: false
 97 |       stderr: "toBeEmpty"
 98 | 
 99 |   # Job name filtering tests
100 |   - it: "should filter by job name"
101 |     request:
102 |       jsonrpc: "2.0"
103 |       id: "job-entries-by-name"
104 |       method: "tools/call"
105 |       params:
106 |         name: "get_job_log_entries"
107 |         arguments:
108 |           jobName: "ProcessOrders"
109 |           limit: 3
110 |     expect:
111 |       response:
112 |         jsonrpc: "2.0"
113 |         id: "job-entries-by-name"
114 |         result:
115 |           content:
116 |             match:arrayElements:
117 |               match:partial:
118 |                 type: "text"
119 |                 text: "match:contains:from job: ProcessOrders"
120 |           isError: false
121 |       stderr: "toBeEmpty"
122 | 
123 |   - it: "should combine job name and level parameters"
124 |     request:
125 |       jsonrpc: "2.0"
126 |       id: "job-entries-name-level"
127 |       method: "tools/call"
128 |       params:
129 |         name: "get_job_log_entries"
130 |         arguments:
131 |           jobName: "ImportCatalog"
132 |           level: "info"
133 |           limit: 3
134 |     expect:
135 |       response:
136 |         jsonrpc: "2.0"
137 |         id: "job-entries-name-level"
138 |         result:
139 |           content:
140 |             match:arrayElements:
141 |               match:partial:
142 |                 type: "text"
143 |                 text: "match:regex:.*info messages from job: ImportCatalog"
144 |           isError: false
145 |       stderr: "toBeEmpty"
146 | 
147 |   # Content structure validation - Aegis pattern matching capabilities
148 |   - it: "should contain SystemJobThread pattern in job logs"
149 |     request:
150 |       jsonrpc: "2.0"
151 |       id: "job-entries-content"
152 |       method: "tools/call"
153 |       params:
154 |         name: "get_job_log_entries"
155 |         arguments:
156 |           limit: 5
157 |     expect:
158 |       response:
159 |         jsonrpc: "2.0"
160 |         id: "job-entries-content"
161 |         result:
162 |           content:
163 |             match:arrayElements:
164 |               match:partial:
165 |                 type: "text"
166 |                 text: "match:contains:SystemJobThread"
167 |           isError: false
168 |       stderr: "toBeEmpty"
169 | 
170 |   - it: "should include proper GMT timestamps"
171 |     request:
172 |       jsonrpc: "2.0"
173 |       id: "job-entries-timestamps"
174 |       method: "tools/call"
175 |       params:
176 |         name: "get_job_log_entries"
177 |         arguments:
178 |           limit: 3
179 |     expect:
180 |       response:
181 |         jsonrpc: "2.0"
182 |         id: "job-entries-timestamps"
183 |         result:
184 |           content:
185 |             match:arrayElements:
186 |               match:partial:
187 |                 type: "text"
188 |                 text: "match:regex:\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3} GMT"
189 |           isError: false
190 |       stderr: "toBeEmpty"
191 | 
192 |   # Error handling tests - essential MCP error response validation
193 |   - it: "should handle zero limit with proper error response"
194 |     request:
195 |       jsonrpc: "2.0"
196 |       id: "job-entries-zero-limit"
197 |       method: "tools/call"
198 |       params:
199 |         name: "get_job_log_entries"
200 |         arguments:
201 |           limit: 0
202 |     expect:
203 |       response:
204 |         jsonrpc: "2.0"
205 |         id: "job-entries-zero-limit"
206 |         result:
207 |           content:
208 |             match:arrayElements:
209 |               match:partial:
210 |                 type: "text"
211 |                 text: "match:contains:Invalid limit"
212 |           isError: true
213 |       stderr: "toBeEmpty"
214 | 
215 |   - it: "should handle invalid log level gracefully"
216 |     request:
217 |       jsonrpc: "2.0"
218 |       id: "job-entries-invalid-level"
219 |       method: "tools/call"
220 |       params:
221 |         name: "get_job_log_entries"
222 |         arguments:
223 |           level: "invalid"
224 |           limit: 5
225 |     expect:
226 |       response:
227 |         jsonrpc: "2.0"
228 |         id: "job-entries-invalid-level"
229 |         result:
230 |           content:
231 |             match:arrayElements:
232 |               match:partial:
233 |                 type: "text"
234 |                 text: "match:contains:Error"
235 |           isError: true
236 |       stderr: "toBeEmpty"
237 | 
238 |   - it: "should handle nonexistent job name gracefully"
239 |     request:
240 |       jsonrpc: "2.0"
241 |       id: "job-entries-nonexistent-job"
242 |       method: "tools/call"
243 |       params:
244 |         name: "get_job_log_entries"
245 |         arguments:
246 |           jobName: "NonExistentJob123"
247 |           limit: 3
248 |     expect:
249 |       response:
250 |         jsonrpc: "2.0"
251 |         id: "job-entries-nonexistent-job"
252 |         result:
253 |           content:
254 |             match:arrayElements:
255 |               match:partial:
256 |                 type: "text"
257 |                 text: "match:contains:No job logs found"
258 |           isError: false
259 |       stderr: "toBeEmpty"
260 | 
261 |   # Performance test - single representative case for aegis framework validation
262 |   - it: "should respond within reasonable time for standard request"
263 |     request:
264 |       jsonrpc: "2.0"
265 |       id: "job-entries-performance"
266 |       method: "tools/call"
267 |       params:
268 |         name: "get_job_log_entries"
269 |         arguments:
270 |           limit: 10
271 |     expect:
272 |       response:
273 |         jsonrpc: "2.0"
274 |         id: "job-entries-performance"
275 |         result:
276 |           content:
277 |             match:arrayElements:
278 |               match:partial:
279 |                 type: "text"
280 |                 text: "match:type:string"
281 |           isError: false
282 |       stderr: "toBeEmpty"
283 |     performance:
284 |       maxResponseTime: "2500ms"
285 | 
```

--------------------------------------------------------------------------------
/docs/dw_order/PaymentMethod.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class PaymentMethod
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.PersistentObject
  9 |   - dw.object.ExtensibleObject
 10 |     - dw.order.PaymentMethod
 11 | 
 12 | ## Description
 13 | 
 14 | The PaymentMethod class represents a logical type of payment a customer can make in the storefront. This class provides methods to access the payment method attributes, status, and (for card-based payment methods) the related payment cards. A typical storefront presents the customer a list of payment methods that a customer can choose from after he has entered his billing address during the checkout. PaymentMgr.getApplicablePaymentMethods(Customer, String, Number) is used to determine the PaymentMethods that are relevant for the customer based on the amount of his order, his customer groups, and his shipping address.
 15 | 
 16 | ## Properties
 17 | 
 18 | ### active
 19 | 
 20 | **Type:** boolean (Read Only)
 21 | 
 22 | Returns 'true' if payment method is active (enabled), otherwise 'false' is returned.
 23 | 
 24 | ### activePaymentCards
 25 | 
 26 | **Type:** List (Read Only)
 27 | 
 28 | Returns enabled payment cards that are assigned to this payment method, regardless
 29 |  of current customer, country or payment amount restrictions.
 30 |  The payment cards are sorted as defined in the Business Manager.
 31 | 
 32 | ### description
 33 | 
 34 | **Type:** MarkupText (Read Only)
 35 | 
 36 | The description of the payment method.
 37 | 
 38 | ### ID
 39 | 
 40 | **Type:** String (Read Only)
 41 | 
 42 | The unique ID of the payment method.
 43 | 
 44 | ### image
 45 | 
 46 | **Type:** MediaFile (Read Only)
 47 | 
 48 | The reference to the payment method image.
 49 | 
 50 | ### name
 51 | 
 52 | **Type:** String (Read Only)
 53 | 
 54 | The name of the payment method.
 55 | 
 56 | ### paymentProcessor
 57 | 
 58 | **Type:** PaymentProcessor (Read Only)
 59 | 
 60 | The payment processor associated to this payment method.
 61 | 
 62 | ## Constructor Summary
 63 | 
 64 | ## Method Summary
 65 | 
 66 | ### getActivePaymentCards
 67 | 
 68 | **Signature:** `getActivePaymentCards() : List`
 69 | 
 70 | Returns enabled payment cards that are assigned to this payment method, regardless of current customer, country or payment amount restrictions.
 71 | 
 72 | ### getApplicablePaymentCards
 73 | 
 74 | **Signature:** `getApplicablePaymentCards(customer : Customer, countryCode : String, paymentAmount : Number) : List`
 75 | 
 76 | Returns the sorted list of all enabled payment cards of this payment method applicable for the specified customer, country, payment amount and the session currency The payment cards are sorted as defined in the Business Manager.
 77 | 
 78 | ### getDescription
 79 | 
 80 | **Signature:** `getDescription() : MarkupText`
 81 | 
 82 | Returns the description of the payment method.
 83 | 
 84 | ### getID
 85 | 
 86 | **Signature:** `getID() : String`
 87 | 
 88 | Returns the unique ID of the payment method.
 89 | 
 90 | ### getImage
 91 | 
 92 | **Signature:** `getImage() : MediaFile`
 93 | 
 94 | Returns the reference to the payment method image.
 95 | 
 96 | ### getName
 97 | 
 98 | **Signature:** `getName() : String`
 99 | 
100 | Returns the name of the payment method.
101 | 
102 | ### getPaymentProcessor
103 | 
104 | **Signature:** `getPaymentProcessor() : PaymentProcessor`
105 | 
106 | Returns the payment processor associated to this payment method.
107 | 
108 | ### isActive
109 | 
110 | **Signature:** `isActive() : boolean`
111 | 
112 | Returns 'true' if payment method is active (enabled), otherwise 'false' is returned.
113 | 
114 | ### isApplicable
115 | 
116 | **Signature:** `isApplicable(customer : Customer, countryCode : String, paymentAmount : Number) : boolean`
117 | 
118 | Returns 'true' if this payment method is applicable for the specified customer, country and payment amount and the session currency.
119 | 
120 | ## Method Detail
121 | 
122 | ## Method Details
123 | 
124 | ### getActivePaymentCards
125 | 
126 | **Signature:** `getActivePaymentCards() : List`
127 | 
128 | **Description:** Returns enabled payment cards that are assigned to this payment method, regardless of current customer, country or payment amount restrictions. The payment cards are sorted as defined in the Business Manager.
129 | 
130 | **Returns:**
131 | 
132 | List of enabled payment cards of current site
133 | 
134 | ---
135 | 
136 | ### getApplicablePaymentCards
137 | 
138 | **Signature:** `getApplicablePaymentCards(customer : Customer, countryCode : String, paymentAmount : Number) : List`
139 | 
140 | **Description:** Returns the sorted list of all enabled payment cards of this payment method applicable for the specified customer, country, payment amount and the session currency The payment cards are sorted as defined in the Business Manager. A payment card is applicable if the card is restricted by customer group, and at least one of the groups of the specified customer is assigned to the card the card is restricted by billing country, and the specified country code is assigned to the card the card is restricted by payment amount for the session currency, and the specified payment amount is within the limits of the min/max payment amount defined for the method and the session currency the card is restricted by currency code, and the specified currency code matches session currency. All parameters are optional, and if not specified, the respective restriction won't be validated. For example, if a card is restricted by billing country, but no country code is specified, this card will be returned, unless it is filtered out by customer group or payment amount.
141 | 
142 | **Parameters:**
143 | 
144 | - `customer`: Customer or null
145 | - `countryCode`: Billing country code or null
146 | - `paymentAmount`: Payment amount or null
147 | 
148 | **Returns:**
149 | 
150 | List of applicable payment cards of this payment method
151 | 
152 | ---
153 | 
154 | ### getDescription
155 | 
156 | **Signature:** `getDescription() : MarkupText`
157 | 
158 | **Description:** Returns the description of the payment method.
159 | 
160 | **Returns:**
161 | 
162 | Description of the payment method.
163 | 
164 | ---
165 | 
166 | ### getID
167 | 
168 | **Signature:** `getID() : String`
169 | 
170 | **Description:** Returns the unique ID of the payment method.
171 | 
172 | **Returns:**
173 | 
174 | ID of the payment method.
175 | 
176 | ---
177 | 
178 | ### getImage
179 | 
180 | **Signature:** `getImage() : MediaFile`
181 | 
182 | **Description:** Returns the reference to the payment method image.
183 | 
184 | **Returns:**
185 | 
186 | Image of the payment method.
187 | 
188 | ---
189 | 
190 | ### getName
191 | 
192 | **Signature:** `getName() : String`
193 | 
194 | **Description:** Returns the name of the payment method.
195 | 
196 | **Returns:**
197 | 
198 | Name of the payment method.
199 | 
200 | ---
201 | 
202 | ### getPaymentProcessor
203 | 
204 | **Signature:** `getPaymentProcessor() : PaymentProcessor`
205 | 
206 | **Description:** Returns the payment processor associated to this payment method.
207 | 
208 | **Returns:**
209 | 
210 | the payment processor associated to this payment method.
211 | 
212 | ---
213 | 
214 | ### isActive
215 | 
216 | **Signature:** `isActive() : boolean`
217 | 
218 | **Description:** Returns 'true' if payment method is active (enabled), otherwise 'false' is returned.
219 | 
220 | **Returns:**
221 | 
222 | true if payment method is active, otherwise false.
223 | 
224 | ---
225 | 
226 | ### isApplicable
227 | 
228 | **Signature:** `isApplicable(customer : Customer, countryCode : String, paymentAmount : Number) : boolean`
229 | 
230 | **Description:** Returns 'true' if this payment method is applicable for the specified customer, country and payment amount and the session currency. The payment method is applicable if the method is restricted by customer group, and at least one of the groups of the specified customer is assigned to the method the method is restricted by billing country, and the specified country code is assigned to the method the method is restricted by payment amount for the session currency, and the specified payment amount is within the limits of the min/max payment amount defined for the method and the session currency the method is restricted by currency code, and the specified currency code matches session currency. All parameters are optional, and if not specified, the respective restriction won't be validated. For example, if a method is restricted by billing country, but no country code is specified, this method will be returned, unless it is filtered out by customer group or payment amount.
231 | 
232 | **Parameters:**
233 | 
234 | - `customer`: Customer or null
235 | - `countryCode`: Billing country code or null
236 | - `paymentAmount`: Payment amount or null
237 | 
238 | **Returns:**
239 | 
240 | true if payment method is applicable, false otherwise
241 | 
242 | ---
```

--------------------------------------------------------------------------------
/src/core/handlers/base-handler.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Logger } from '../../utils/logger.js';
  2 | import { SFCCConfig } from '../../types/types.js';
  3 | 
  4 | export interface HandlerContext {
  5 |   logger: Logger;
  6 |   config: SFCCConfig;
  7 |   capabilities: {
  8 |     canAccessLogs: boolean;
  9 |     canAccessOCAPI: boolean;
 10 |   };
 11 | }
 12 | 
 13 | export interface ToolExecutionResult {
 14 |   content: Array<{ type: 'text'; text: string }>;
 15 |   isError?: boolean;
 16 | }
 17 | 
 18 | export interface ToolArguments {
 19 |   [key: string]: any;
 20 | }
 21 | 
 22 | /**
 23 |  * Generic tool specification interface
 24 |  * Defines the contract for declarative tool configuration
 25 |  */
 26 | export interface GenericToolSpec<TArgs = ToolArguments, TResult = any> {
 27 |   /** Optional validation function for tool arguments */
 28 |   validate?: (args: TArgs, toolName: string) => void;
 29 |   /** Optional function to apply default values to arguments */
 30 |   defaults?: (args: TArgs) => Partial<TArgs>;
 31 |   /** Main execution function for the tool */
 32 |   exec: (args: TArgs, context: ToolExecutionContext) => Promise<TResult>;
 33 |   /** Function to generate log message for the tool execution */
 34 |   logMessage: (args: TArgs) => string;
 35 | }
 36 | 
 37 | /**
 38 |  * Context provided to tool execution functions
 39 |  * Allows tools to access clients and other resources
 40 |  */
 41 | export interface ToolExecutionContext {
 42 |   /** Handler context with configuration and capabilities */
 43 |   handlerContext: HandlerContext;
 44 |   /** Logger instance for the handler */
 45 |   logger: any;
 46 |   /** Additional context data that can be provided by concrete handlers */
 47 |   [key: string]: any;
 48 | }
 49 | 
 50 | export class HandlerError extends Error {
 51 |   constructor(
 52 |     message: string,
 53 |     public readonly toolName: string,
 54 |     public readonly code: string = 'HANDLER_ERROR',
 55 |     public readonly details?: any,
 56 |   ) {
 57 |     super(message);
 58 |     this.name = 'HandlerError';
 59 |   }
 60 | }
 61 | 
 62 | export abstract class BaseToolHandler<TToolName extends string = string> {
 63 |   protected context: HandlerContext;
 64 |   protected logger: Logger;
 65 |   private _isInitialized = false;
 66 | 
 67 |   constructor(context: HandlerContext, subLoggerName: string) {
 68 |     this.context = context;
 69 |     this.logger = Logger.getChildLogger(`Handler:${subLoggerName}`);
 70 |   }
 71 | 
 72 |   /**
 73 |    * Abstract method to get tool configuration
 74 |    * Each concrete handler implements this with their specific config
 75 |    */
 76 |   protected abstract getToolConfig(): Record<TToolName, GenericToolSpec>;
 77 | 
 78 |   /**
 79 |    * Abstract method to get tool name set for O(1) lookup
 80 |    * Each concrete handler implements this with their specific tool set
 81 |    */
 82 |   protected abstract getToolNameSet(): Set<string>;
 83 | 
 84 |   /**
 85 |    * Abstract method to create execution context
 86 |    * Each concrete handler can provide specialized context
 87 |    */
 88 |   protected abstract createExecutionContext(): Promise<ToolExecutionContext>;
 89 | 
 90 |   /**
 91 |    * Check if this handler can handle the given tool
 92 |    */
 93 |   canHandle(toolName: string): boolean {
 94 |     return this.getToolNameSet().has(toolName);
 95 |   }
 96 | 
 97 |   /**
 98 |    * Config-driven tool execution
 99 |    * Handles validation, defaults, execution, and logging uniformly
100 |    */
101 |   async handle(toolName: string, args: ToolArguments, startTime: number): Promise<ToolExecutionResult> {
102 |     if (!this.canHandle(toolName)) {
103 |       throw new Error(`Unsupported tool: ${toolName}`);
104 |     }
105 | 
106 |     const toolConfig = this.getToolConfig();
107 |     const spec = toolConfig[toolName as TToolName];
108 | 
109 |     if (!spec) {
110 |       throw new Error(`No configuration found for tool: ${toolName}`);
111 |     }
112 | 
113 |     return this.executeWithLogging(
114 |       toolName,
115 |       startTime,
116 |       () => this.dispatchTool(spec, args),
117 |       spec.logMessage(this.applyDefaults(spec, args)),
118 |     );
119 |   }
120 | 
121 |   /**
122 |    * Generic tool dispatch using configuration
123 |    * Handles validation, defaults, and execution
124 |    */
125 |   private async dispatchTool(spec: GenericToolSpec, args: ToolArguments): Promise<any> {
126 |     const context = await this.createExecutionContext();
127 |     const processedArgs = this.createValidatedArgs(spec, args, 'tool');
128 | 
129 |     return spec.exec(processedArgs, context);
130 |   }
131 | 
132 |   /**
133 |    * Apply default values to arguments
134 |    */
135 |   private applyDefaults(spec: GenericToolSpec, args: ToolArguments): ToolArguments {
136 |     if (!spec.defaults) {
137 |       return args;
138 |     }
139 | 
140 |     const defaults = spec.defaults(args);
141 |     return { ...args, ...defaults };
142 |   }
143 | 
144 |   /**
145 |    * Create validated arguments with defaults applied
146 |    */
147 |   private createValidatedArgs(spec: GenericToolSpec, args: ToolArguments, toolName: string): ToolArguments {
148 |     // Apply defaults first
149 |     const processedArgs = this.applyDefaults(spec, args);
150 | 
151 |     // Validate if validator exists
152 |     if (spec.validate) {
153 |       spec.validate(processedArgs, toolName);
154 |     }
155 | 
156 |     return processedArgs;
157 |   }
158 | 
159 |   /**
160 |    * Initialize the handler (lazy initialization)
161 |    */
162 |   protected async initialize(): Promise<void> {
163 |     if (this._isInitialized) {
164 |       return;
165 |     }
166 |     await this.onInitialize();
167 |     this._isInitialized = true;
168 |   }
169 | 
170 |   /**
171 |    * Override this method for custom initialization logic
172 |    */
173 |   protected async onInitialize(): Promise<void> {
174 |     // Default: no-op
175 |   }
176 | 
177 |   /**
178 |    * Clean up resources when handler is destroyed
179 |    */
180 |   async dispose(): Promise<void> {
181 |     await this.onDispose();
182 |     this._isInitialized = false;
183 |   }
184 | 
185 |   /**
186 |    * Override this method for custom cleanup logic
187 |    */
188 |   protected async onDispose(): Promise<void> {
189 |     // Default: no-op
190 |   }
191 | 
192 |   /**
193 |    * Validate required arguments
194 |    */
195 |   protected validateArgs(args: ToolArguments, required: string[], toolName: string): void {
196 |     for (const field of required) {
197 |       if (!args?.[field]) {
198 |         throw new HandlerError(
199 |           `${field} is required`,
200 |           toolName,
201 |           'MISSING_ARGUMENT',
202 |           { required, provided: Object.keys(args || {}) },
203 |         );
204 |       }
205 |     }
206 |   }
207 | 
208 |   /**
209 |    * Create a standardized response
210 |    */
211 |   protected createResponse(data: any, stringify: boolean = true): ToolExecutionResult {
212 |     return {
213 |       content: [
214 |         { type: 'text', text: stringify ? JSON.stringify(data, null, 2) : data },
215 |       ],
216 |       isError: false,
217 |     };
218 |   }
219 | 
220 |   /**
221 |    * Create an error response
222 |    */
223 |   protected createErrorResponse(error: Error, toolName: string): ToolExecutionResult {
224 |     this.logger.error(`Error in ${toolName}:`, error);
225 |     return {
226 |       content: [
227 |         {
228 |           type: 'text',
229 |           text: error instanceof HandlerError
230 |             ? `Error: ${error.message}`
231 |             : `Error: ${error.message}`,
232 |         },
233 |       ],
234 |       isError: true,
235 |     };
236 |   }
237 | 
238 |   /**
239 |    * Execute a tool operation with standardized logging and error handling
240 |    */
241 |   protected async executeWithLogging(
242 |     toolName: string,
243 |     startTime: number,
244 |     operation: () => Promise<any>,
245 |     logMessage?: string,
246 |   ): Promise<ToolExecutionResult> {
247 |     try {
248 |       await this.initialize();
249 | 
250 |       if (logMessage) {
251 |         this.logger.debug(logMessage);
252 |       }
253 | 
254 |       const result = await operation();
255 |       this.logger.timing(toolName, startTime);
256 | 
257 |       // Log result metadata for debugging
258 |       this.logger.debug(`${toolName} completed successfully`, {
259 |         resultType: typeof result,
260 |         resultLength: Array.isArray(result) ? result.length : undefined,
261 |         hasData: result != null,
262 |       });
263 | 
264 |       return this.createResponse(result);
265 |     } catch (error) {
266 |       this.logger.timing(`${toolName}_error`, startTime);
267 |       return this.createErrorResponse(error as Error, toolName);
268 |     }
269 |   }
270 | 
271 |   /**
272 |    * @deprecated Use executeWithLogging instead
273 |    */
274 |   protected async wrap(
275 |     toolName: string,
276 |     startTime: number,
277 |     fn: () => Promise<any>,
278 |     logMessage?: string,
279 |   ): Promise<ToolExecutionResult> {
280 |     return this.executeWithLogging(toolName, startTime, fn, logMessage);
281 |   }
282 | }
283 | 
```

--------------------------------------------------------------------------------
/docs/dw_order/OrderPaymentInstrument.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class OrderPaymentInstrument
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.PersistentObject
  9 |   - dw.object.ExtensibleObject
 10 |     - dw.customer.EncryptedObject
 11 |       - dw.order.PaymentInstrument
 12 |       - dw.order.OrderPaymentInstrument
 13 | 
 14 | ## Description
 15 | 
 16 | Represents any payment instrument used to pay orders, such as credit card or bank transfer. The object defines standard methods for credit card payment, and can be extended by attributes appropriate for other payment methods.
 17 | 
 18 | ## Properties
 19 | 
 20 | ### bankAccountDriversLicense
 21 | 
 22 | **Type:** String (Read Only)
 23 | 
 24 | The driver's license associated with a bank account if the calling
 25 |  context meets the following criteria: 
 26 |  
 27 |  
 28 |  If the method call happens in the context of a storefront request and
 29 |  the current customer is identical to the customer related to the basket
 30 |  or order, and the current protocol is HTTPS.
 31 |  
 32 |  
 33 |  If the method call happens in the context of the business manager and the
 34 |  current user has permission to the Orders module.
 35 |  
 36 |  
 37 |  Otherwise, the method throws an exception.
 38 | 
 39 | ### bankAccountNumber
 40 | 
 41 | **Type:** String (Read Only)
 42 | 
 43 | The account number if the calling context meets
 44 |  the following criteria: 
 45 |  
 46 |  
 47 |  If the method call happens in the context of a storefront request and
 48 |  the current customer is identical to the customer related to the basket
 49 |  or order, and the current protocol is HTTPS.
 50 |  
 51 |  
 52 |  If the method call happens in the context of the business manager and the
 53 |  current user has permissions to the Orders module.
 54 |  
 55 |  
 56 |  Otherwise, the method throws an exception.
 57 | 
 58 | ### capturedAmount
 59 | 
 60 | **Type:** Money (Read Only)
 61 | 
 62 | The sum of the captured amounts. The captured amounts
 63 |  are calculated on the fly. Associate a payment capture for an Payment Instrument with an Invoice
 64 |  using Invoice method addCaptureTransaction.
 65 | 
 66 | ### creditCardNumber
 67 | 
 68 | **Type:** String (Read Only)
 69 | 
 70 | The de-crypted creditcard number if the calling context meets
 71 |  the following criteria: 
 72 |  
 73 |  
 74 |  If the method call happens in the context of a storefront request and
 75 |  the current authenticated customer is referenced by the basket or order, and the current protocol is HTTPS.
 76 |  
 77 |  
 78 |  If the customer is anonymous, and the order references this customer, and the protocol is secure and
 79 |  the order status is CREATED.
 80 |  
 81 |  
 82 |  If the method call happens in the context of the business manager and the
 83 |  current user has the permission to manage orders.
 84 |  
 85 |  
 86 |  If the payment information has not been masked as a result of the data retention security policy
 87 |  for the site.
 88 |  
 89 |  
 90 |  Otherwise, the method returns the masked credit card number.
 91 | 
 92 | ### paymentTransaction
 93 | 
 94 | **Type:** PaymentTransaction (Read Only)
 95 | 
 96 | The Payment Transaction for this Payment Instrument or null.
 97 | 
 98 | ### refundedAmount
 99 | 
100 | **Type:** Money (Read Only)
101 | 
102 | The sum of the refunded amounts. The refunded amounts
103 |  are calculated on the fly. Associate a payment refund for an Payment Instrument with an Invoice
104 |  using Invoice method addRefundTransaction.
105 | 
106 | ## Constructor Summary
107 | 
108 | ## Method Summary
109 | 
110 | ### getBankAccountDriversLicense
111 | 
112 | **Signature:** `getBankAccountDriversLicense() : String`
113 | 
114 | Returns the driver's license associated with a bank account if the calling context meets the following criteria: If the method call happens in the context of a storefront request and the current customer is identical to the customer related to the basket or order, and the current protocol is HTTPS.
115 | 
116 | ### getBankAccountNumber
117 | 
118 | **Signature:** `getBankAccountNumber() : String`
119 | 
120 | Returns the account number if the calling context meets the following criteria: If the method call happens in the context of a storefront request and the current customer is identical to the customer related to the basket or order, and the current protocol is HTTPS.
121 | 
122 | ### getCapturedAmount
123 | 
124 | **Signature:** `getCapturedAmount() : Money`
125 | 
126 | Returns the sum of the captured amounts.
127 | 
128 | ### getCreditCardNumber
129 | 
130 | **Signature:** `getCreditCardNumber() : String`
131 | 
132 | Returns the de-crypted creditcard number if the calling context meets the following criteria: If the method call happens in the context of a storefront request and the current authenticated customer is referenced by the basket or order, and the current protocol is HTTPS.
133 | 
134 | ### getPaymentTransaction
135 | 
136 | **Signature:** `getPaymentTransaction() : PaymentTransaction`
137 | 
138 | Returns the Payment Transaction for this Payment Instrument or null.
139 | 
140 | ### getRefundedAmount
141 | 
142 | **Signature:** `getRefundedAmount() : Money`
143 | 
144 | Returns the sum of the refunded amounts.
145 | 
146 | ## Method Detail
147 | 
148 | ## Method Details
149 | 
150 | ### getBankAccountDriversLicense
151 | 
152 | **Signature:** `getBankAccountDriversLicense() : String`
153 | 
154 | **Description:** Returns the driver's license associated with a bank account if the calling context meets the following criteria: If the method call happens in the context of a storefront request and the current customer is identical to the customer related to the basket or order, and the current protocol is HTTPS. If the method call happens in the context of the business manager and the current user has permission to the Orders module. Otherwise, the method throws an exception.
155 | 
156 | **Returns:**
157 | 
158 | the driver's license number if the calling context meets the necessary criteria.
159 | 
160 | ---
161 | 
162 | ### getBankAccountNumber
163 | 
164 | **Signature:** `getBankAccountNumber() : String`
165 | 
166 | **Description:** Returns the account number if the calling context meets the following criteria: If the method call happens in the context of a storefront request and the current customer is identical to the customer related to the basket or order, and the current protocol is HTTPS. If the method call happens in the context of the business manager and the current user has permissions to the Orders module. Otherwise, the method throws an exception.
167 | 
168 | **Returns:**
169 | 
170 | the account number if the calling context meets the necessary criteria.
171 | 
172 | ---
173 | 
174 | ### getCapturedAmount
175 | 
176 | **Signature:** `getCapturedAmount() : Money`
177 | 
178 | **Description:** Returns the sum of the captured amounts. The captured amounts are calculated on the fly. Associate a payment capture for an Payment Instrument with an Invoice using Invoice method addCaptureTransaction.
179 | 
180 | **Returns:**
181 | 
182 | sum of captured amounts
183 | 
184 | ---
185 | 
186 | ### getCreditCardNumber
187 | 
188 | **Signature:** `getCreditCardNumber() : String`
189 | 
190 | **Description:** Returns the de-crypted creditcard number if the calling context meets the following criteria: If the method call happens in the context of a storefront request and the current authenticated customer is referenced by the basket or order, and the current protocol is HTTPS. If the customer is anonymous, and the order references this customer, and the protocol is secure and the order status is CREATED. If the method call happens in the context of the business manager and the current user has the permission to manage orders. If the payment information has not been masked as a result of the data retention security policy for the site. Otherwise, the method returns the masked credit card number.
191 | 
192 | **Returns:**
193 | 
194 | the de-crypted creditcard number if the calling context meets the necessary criteria.
195 | 
196 | ---
197 | 
198 | ### getPaymentTransaction
199 | 
200 | **Signature:** `getPaymentTransaction() : PaymentTransaction`
201 | 
202 | **Description:** Returns the Payment Transaction for this Payment Instrument or null.
203 | 
204 | **Returns:**
205 | 
206 | the Payment Transaction for this Payment Instrument or null.
207 | 
208 | ---
209 | 
210 | ### getRefundedAmount
211 | 
212 | **Signature:** `getRefundedAmount() : Money`
213 | 
214 | **Description:** Returns the sum of the refunded amounts. The refunded amounts are calculated on the fly. Associate a payment refund for an Payment Instrument with an Invoice using Invoice method addRefundTransaction.
215 | 
216 | **Returns:**
217 | 
218 | sum of refunded amounts
219 | 
220 | ---
```

--------------------------------------------------------------------------------
/tests/servers/sfcc-mock-server/mock-data/ocapi/system-object-attributes-product-expanded.json:
--------------------------------------------------------------------------------

```json
  1 | {
  2 |   "_v": "23.2",
  3 |   "_type": "object_attribute_definition_search_result",
  4 |   "count": 20,
  5 |   "hits": [
  6 |     {
  7 |       "_type": "object_attribute_definition",
  8 |       "_resource_state": "47de03c012d48eee975077f45131088fe24f34660ecf4dc69de5737257836fda",
  9 |       "id": "EAN",
 10 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/EAN"
 11 |     },
 12 |     {
 13 |       "_type": "object_attribute_definition",
 14 |       "_resource_state": "d9215260b911efb2f40c78cb1b9a7134feca85bf429a9462371db6a16d22e456",
 15 |       "id": "ID",
 16 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/ID"
 17 |     },
 18 |     {
 19 |       "_type": "object_attribute_definition",
 20 |       "_resource_state": "5d41eb23dc9228d397af90560aaa1b8b0ff825e6d8699a56977366d3534c5cc6",
 21 |       "id": "UPC",
 22 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/UPC"
 23 |     },
 24 |     {
 25 |       "_type": "object_attribute_definition",
 26 |       "_resource_state": "461201495377a8c0e50340bbce3ce929cecfbb74022d1ba87ff3bfeb2501b295",
 27 |       "id": "UUID",
 28 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/UUID"
 29 |     },
 30 |     {
 31 |       "_type": "object_attribute_definition",
 32 |       "_resource_state": "94d3911611a738b687448c2742b6b61216925f45e60627906e871da0d752f35b",
 33 |       "id": "Wool",
 34 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/Wool"
 35 |     },
 36 |     {
 37 |       "_type": "object_attribute_definition",
 38 |       "_resource_state": "f3714641f6cd57c36db45c8976a743e21dafdb52d753ea67013ef9a9209488f3",
 39 |       "id": "available",
 40 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/available"
 41 |     },
 42 |     {
 43 |       "_type": "object_attribute_definition",
 44 |       "_resource_state": "5945247ee819586780982084e8099326c6cc04499492ff82f8603ecb224172d0",
 45 |       "id": "availableForInStorePickup",
 46 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/availableForInStorePickup"
 47 |     },
 48 |     {
 49 |       "_type": "object_attribute_definition",
 50 |       "_resource_state": "835703bec9a082f3525872afaa1d371c985c70359812e1e4a751fb2d9b200df6",
 51 |       "id": "baseImageGroup",
 52 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/baseImageGroup"
 53 |     },
 54 |     {
 55 |       "_type": "object_attribute_definition",
 56 |       "_resource_state": "67a18a0bbc59a1e4dd7e22d3fb6e1b624b9aacb68c4bb1cd18fe5dbe99d3e21b",
 57 |       "id": "bottomType",
 58 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/bottomType"
 59 |     },
 60 |     {
 61 |       "_type": "object_attribute_definition",
 62 |       "_resource_state": "aa8ad7704c3818641e179362b17cdca0b56a411e0b0c2bbcd99689bc54193554",
 63 |       "id": "brand",
 64 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/brand"
 65 |     },
 66 |     {
 67 |       "_type": "object_attribute_definition",
 68 |       "_resource_state": "503a227e5ed4449c29e36df3d093f000f5da55a8c3f15d50136e3a6371a0aa0a",
 69 |       "id": "bundledProducts",
 70 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/bundledProducts"
 71 |     },
 72 |     {
 73 |       "_type": "object_attribute_definition",
 74 |       "_resource_state": "89b49b5c86e3e1b9b7bd6b84e8ea93f5a56e4e2e8f5b0d1b1a3b2c3d4e5f6789",
 75 |       "id": "catalog",
 76 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/catalog"
 77 |     },
 78 |     {
 79 |       "_type": "object_attribute_definition",
 80 |       "_resource_state": "12345678901234567890123456789012345678901234567890123456789012345",
 81 |       "id": "categories",
 82 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/categories"
 83 |     },
 84 |     {
 85 |       "_type": "object_attribute_definition",
 86 |       "_resource_state": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef",
 87 |       "id": "classificationCategory",
 88 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/classificationCategory"
 89 |     },
 90 |     {
 91 |       "_type": "object_attribute_definition",
 92 |       "_resource_state": "fedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcba",
 93 |       "id": "color",
 94 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/color"
 95 |     },
 96 |     {
 97 |       "_type": "object_attribute_definition",
 98 |       "_resource_state": "1111111111111111111111111111111111111111111111111111111111111111",
 99 |       "id": "creationDate",
100 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/creationDate"
101 |     },
102 |     {
103 |       "_type": "object_attribute_definition",
104 |       "_resource_state": "2222222222222222222222222222222222222222222222222222222222222222",
105 |       "id": "custom",
106 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/custom"
107 |     },
108 |     {
109 |       "_type": "object_attribute_definition",
110 |       "_resource_state": "3333333333333333333333333333333333333333333333333333333333333333",
111 |       "id": "description",
112 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/description"
113 |     },
114 |     {
115 |       "_type": "object_attribute_definition",
116 |       "_resource_state": "4444444444444444444444444444444444444444444444444444444444444444",
117 |       "id": "imageGroups",
118 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/imageGroups"
119 |     },
120 |     {
121 |       "_type": "object_attribute_definition",
122 |       "_resource_state": "5555555555555555555555555555555555555555555555555555555555555555",
123 |       "id": "lastModified",
124 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/lastModified"
125 |     }
126 |   ],
127 |   "next": {
128 |     "_type": "result_page",
129 |     "count": 5,
130 |     "start": 5
131 |   },
132 |   "query": {
133 |     "match_all_query": {
134 |       "_type": "match_all_query"
135 |     }
136 |   },
137 |   "start": 0,
138 |   "total": 113,
139 |   "expandedData": {
140 |     "brand": {
141 |       "_type": "object_attribute_definition",
142 |       "_resource_state": "aa8ad7704c3818641e179362b17cdca0b56a411e0b0c2bbcd99689bc54193554",
143 |       "creation_date": "2024-02-19T10:18:31.000Z",
144 |       "display_name": {
145 |         "de": "Marke",
146 |         "de-DE": "Marke",
147 |         "it": "Marca",
148 |         "fr": "Marque",
149 |         "zh-CN": "品牌",
150 |         "es": "Marca",
151 |         "fr-CA": "Marque",
152 |         "it-IT": "Marca",
153 |         "default": "Brand",
154 |         "ja": "ブランド",
155 |         "fr-FR": "Marque",
156 |         "ja-JP": "ブランド",
157 |         "nl": "Merk"
158 |       },
159 |       "effective_id": "brand",
160 |       "externally_defined": false,
161 |       "externally_managed": false,
162 |       "field_length": 0,
163 |       "id": "brand",
164 |       "key": false,
165 |       "last_modified": "2024-02-19T10:22:33.000Z",
166 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product/attribute_definitions/brand",
167 |       "localizable": false,
168 |       "mandatory": false,
169 |       "min_length": 0,
170 |       "multi_value_type": false,
171 |       "order_required": false,
172 |       "queryable": true,
173 |       "read_only": false,
174 |       "requires_encoding": false,
175 |       "searchable": false,
176 |       "set_value_type": false,
177 |       "site_specific": false,
178 |       "system": true,
179 |       "value_type": "string",
180 |       "visible": true
181 |     }
182 |   }
183 | }
```

--------------------------------------------------------------------------------
/docs/dw_content/ContentSearchRefinements.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.content
  2 | 
  3 | # Class ContentSearchRefinements
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.catalog.SearchRefinements
  9 |   - dw.content.ContentSearchRefinements
 10 | 
 11 | ## Description
 12 | 
 13 | This class provides an interface to refinement options for the content asset search. In a typical usage, the client application UI displays the search refinements along with the search results and allows customers to "refine" the results (i.e. limit the results that are shown) by specifying additional criteria, or "relax" (i.e. broaden) the results after previously refining. The two types of content search refinements are: Refine By Folder: Limit the content assets to those assigned to specific child/ancestor folder of the search folder. Refine By Attribute: Limit the content assets to those with specific values for a given attribute. Values may be grouped into "buckets" so that a given set of values are represented as a single refinement option. Rendering a content search refinement UI typically begins with iterating the refinement definitions for the search result. Call SearchRefinements.getRefinementDefinitions() or SearchRefinements.getAllRefinementDefinitions() to retrieve the appropriate collection of refinement definitions. For each definition, display the available refinement values by calling getAllRefinementValues(ContentSearchRefinementDefinition). Depending on the type of the refinement definition, the application must use slightly different logic to display the refinement widgets. For all 2 types, methods in ContentSearchModel are used to generate URLs to render hyperlinks in the UI. When clicked, these links trigger a call to the Search pipelet which in turn applies the appropriate filters to the native search result.
 14 | 
 15 | ## Properties
 16 | 
 17 | ### folderRefinementDefinition
 18 | 
 19 | **Type:** ContentSearchRefinementDefinition (Read Only)
 20 | 
 21 | The appropriate folder refinement definition based on the search
 22 |  result. The folder refinement definition returned will be the first that
 23 |  can be found traversing the folder tree upward starting at the deepest
 24 |  common folder of the search result.
 25 | 
 26 | ### matchingFolders
 27 | 
 28 | **Type:** Collection (Read Only)
 29 | 
 30 | A collection of matching folders.
 31 | 
 32 | ## Constructor Summary
 33 | 
 34 | ## Method Summary
 35 | 
 36 | ### getAllRefinementValues
 37 | 
 38 | **Signature:** `getAllRefinementValues(definition : ContentSearchRefinementDefinition) : Collection`
 39 | 
 40 | Returns a sorted collection of refinement values for the given refinement definition.
 41 | 
 42 | ### getFolderHits
 43 | 
 44 | **Signature:** `getFolderHits(folder : Folder) : Number`
 45 | 
 46 | Returns the number of search hits for the passed folder object.
 47 | 
 48 | ### getFolderRefinementDefinition
 49 | 
 50 | **Signature:** `getFolderRefinementDefinition() : ContentSearchRefinementDefinition`
 51 | 
 52 | Returns the appropriate folder refinement definition based on the search result.
 53 | 
 54 | ### getMatchingFolders
 55 | 
 56 | **Signature:** `getMatchingFolders() : Collection`
 57 | 
 58 | Returns a collection of matching folders.
 59 | 
 60 | ### getNextLevelFolderRefinementValues
 61 | 
 62 | **Signature:** `getNextLevelFolderRefinementValues(folder : Folder) : Collection`
 63 | 
 64 | Returns folder refinement values based on the current search result filtered such that only folder refinements representing children of the given folder are present.
 65 | 
 66 | ### getRefinementValue
 67 | 
 68 | **Signature:** `getRefinementValue(definition : ContentSearchRefinementDefinition, value : String) : ContentSearchRefinementValue`
 69 | 
 70 | Returns the refinement value (incl.
 71 | 
 72 | ### getRefinementValue
 73 | 
 74 | **Signature:** `getRefinementValue(name : String, value : String) : ContentSearchRefinementValue`
 75 | 
 76 | Returns the refinement value (incl.
 77 | 
 78 | ### getRefinementValues
 79 | 
 80 | **Signature:** `getRefinementValues(definition : ContentSearchRefinementDefinition) : Collection`
 81 | 
 82 | Returns a collection of refinement values for the given refinement definition.
 83 | 
 84 | ## Method Detail
 85 | 
 86 | ## Method Details
 87 | 
 88 | ### getAllRefinementValues
 89 | 
 90 | **Signature:** `getAllRefinementValues(definition : ContentSearchRefinementDefinition) : Collection`
 91 | 
 92 | **Description:** Returns a sorted collection of refinement values for the given refinement definition. The returned collection includes all refinement values for which the hit count is greater than 0 within the search result when the passed refinement definitions is excluded from filtering the search hits but all other refinement filters are still applied. This is useful for rendering broadening options for the refinement definitions that the search is already refined by. It is important to note that this method does NOT return refinement values independent of the search result.
 93 | 
 94 | **Parameters:**
 95 | 
 96 | - `definition`: The refinement definition to return refinement values for.
 97 | 
 98 | **Returns:**
 99 | 
100 | The collection of ContentSearchRefinementValue instances sorted according to the settings of the definition.
101 | 
102 | ---
103 | 
104 | ### getFolderHits
105 | 
106 | **Signature:** `getFolderHits(folder : Folder) : Number`
107 | 
108 | **Description:** Returns the number of search hits for the passed folder object.
109 | 
110 | **Parameters:**
111 | 
112 | - `folder`: Folder object.
113 | 
114 | **Returns:**
115 | 
116 | Number of search hits.
117 | 
118 | ---
119 | 
120 | ### getFolderRefinementDefinition
121 | 
122 | **Signature:** `getFolderRefinementDefinition() : ContentSearchRefinementDefinition`
123 | 
124 | **Description:** Returns the appropriate folder refinement definition based on the search result. The folder refinement definition returned will be the first that can be found traversing the folder tree upward starting at the deepest common folder of the search result.
125 | 
126 | **Returns:**
127 | 
128 | The folder refinement definition or null if none can be found.
129 | 
130 | ---
131 | 
132 | ### getMatchingFolders
133 | 
134 | **Signature:** `getMatchingFolders() : Collection`
135 | 
136 | **Description:** Returns a collection of matching folders.
137 | 
138 | **Returns:**
139 | 
140 | Collection of matching folders.
141 | 
142 | ---
143 | 
144 | ### getNextLevelFolderRefinementValues
145 | 
146 | **Signature:** `getNextLevelFolderRefinementValues(folder : Folder) : Collection`
147 | 
148 | **Description:** Returns folder refinement values based on the current search result filtered such that only folder refinements representing children of the given folder are present. If no folder is given, the method uses the library's root folder. The refinement value content counts represent all hits contained in the library tree starting at the corresponding child folder.
149 | 
150 | **Parameters:**
151 | 
152 | - `folder`: The folder to return child folder refinement values for.
153 | 
154 | **Returns:**
155 | 
156 | The refinement values for all child folders of the given folder.
157 | 
158 | ---
159 | 
160 | ### getRefinementValue
161 | 
162 | **Signature:** `getRefinementValue(definition : ContentSearchRefinementDefinition, value : String) : ContentSearchRefinementValue`
163 | 
164 | **Description:** Returns the refinement value (incl. content hit count) for the given refinement definition and the given (selected) value.
165 | 
166 | **Parameters:**
167 | 
168 | - `definition`: The definition to return the refinement for.
169 | - `value`: The value to return the refinement value for.
170 | 
171 | **Returns:**
172 | 
173 | The refinement value.
174 | 
175 | ---
176 | 
177 | ### getRefinementValue
178 | 
179 | **Signature:** `getRefinementValue(name : String, value : String) : ContentSearchRefinementValue`
180 | 
181 | **Description:** Returns the refinement value (incl. content hit count) for the given attribute refinement and the given (selected) value.
182 | 
183 | **Parameters:**
184 | 
185 | - `name`: The name of the refinement attribute.
186 | - `value`: The value to return the refinement value for.
187 | 
188 | **Returns:**
189 | 
190 | The refinement value.
191 | 
192 | ---
193 | 
194 | ### getRefinementValues
195 | 
196 | **Signature:** `getRefinementValues(definition : ContentSearchRefinementDefinition) : Collection`
197 | 
198 | **Description:** Returns a collection of refinement values for the given refinement definition. The returned refinement values only include those that are part of the actual search result (i.e. hit count will always be > 0).
199 | 
200 | **Parameters:**
201 | 
202 | - `definition`: The refinement definition to return refinement values for.
203 | 
204 | **Returns:**
205 | 
206 | The collection of refinement values sorted according to the settings of the definition.
207 | 
208 | ---
```

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

```json
  1 | {
  2 |   "_v": "23.2",
  3 |   "_type": "object_type_definitions",
  4 |   "count": 27,
  5 |   "data": [
  6 |     {
  7 |       "_type": "object_type_definition",
  8 |       "_resource_state": "50ddc22db56ece8a16a546bb710d894b8a3d7a90d453c7d3e68c18f1957528b4",
  9 |       "object_type": "Appeasement",
 10 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Appeasement"
 11 |     },
 12 |     {
 13 |       "_type": "object_type_definition",
 14 |       "_resource_state": "690b6ebf6a292ebabff1bceee5700e1d23756cf795295590a6d676d80e76752c",
 15 |       "object_type": "AppeasementItem",
 16 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/AppeasementItem"
 17 |     },
 18 |     {
 19 |       "_type": "object_type_definition",
 20 |       "_resource_state": "8428af5200166cf3149f948ea843f38832a2f77214699a5b0e26605c6098c93b",
 21 |       "object_type": "Basket",
 22 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Basket"
 23 |     },
 24 |     {
 25 |       "_type": "object_type_definition",
 26 |       "_resource_state": "5c583fb95335bb7c08a021f15cec35d972040fd1259d1497fe70e9b0861f0b39",
 27 |       "object_type": "BonusDiscountLineItem",
 28 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/BonusDiscountLineItem"
 29 |     },
 30 |     {
 31 |       "_type": "object_type_definition",
 32 |       "_resource_state": "509b6af7dc05ecc7c74beef901e37d6c48803b02922b782a5f13fd67f2855f61",
 33 |       "object_type": "Campaign",
 34 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Campaign"
 35 |     },
 36 |     {
 37 |       "_type": "object_type_definition",
 38 |       "_resource_state": "3d5d0265c55926e15bcc829ded6977db8bcf0cd276a02a10823adc0e57a1e795",
 39 |       "object_type": "Catalog",
 40 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Catalog"
 41 |     },
 42 |     {
 43 |       "_type": "object_type_definition",
 44 |       "_resource_state": "c02990e0c2dd64cecf2dd7f89b857718ddbf19c1607a30b2a964945f7d653343",
 45 |       "object_type": "Category",
 46 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Category"
 47 |     },
 48 |     {
 49 |       "_type": "object_type_definition",
 50 |       "_resource_state": "fddeadc4d54ecd507c9eeb74388b6e51147d42f45108bb56e569316e06c0c900",
 51 |       "object_type": "CategoryAssignment",
 52 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CategoryAssignment"
 53 |     },
 54 |     {
 55 |       "_type": "object_type_definition",
 56 |       "_resource_state": "bbd46a905ab7393d2ccbc0ffd111eee82c3c331302161308950f40c6eb23396f",
 57 |       "object_type": "Content",
 58 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Content"
 59 |     },
 60 |     {
 61 |       "_type": "object_type_definition",
 62 |       "_resource_state": "c41b2f924e0cf0cb793bf0bd2d50e53e66308a4137d1fed613df584d7ef08a46",
 63 |       "object_type": "Coupon",
 64 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Coupon"
 65 |     },
 66 |     {
 67 |       "_type": "object_type_definition",
 68 |       "_resource_state": "861081f499e48eb616129cd71eb4edad71914636b4f722ec5fd035139c4025ab",
 69 |       "object_type": "CouponLineItem",
 70 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CouponLineItem"
 71 |     },
 72 |     {
 73 |       "_type": "object_type_definition",
 74 |       "_resource_state": "969b2870f867b14ec145ae39ddc0370ea64951112cde3275ff2bf063f045bf96",
 75 |       "object_type": "CustomObject",
 76 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
 77 |     },
 78 |     {
 79 |       "_type": "object_type_definition",
 80 |       "_resource_state": "f6b5e0f6be3e55d750b49d7ebda7ce7e60548ab426deb9c38f847437b83ebc05",
 81 |       "object_type": "CustomObject",
 82 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
 83 |     },
 84 |     {
 85 |       "_type": "object_type_definition",
 86 |       "_resource_state": "0f001293cee7e2041cb11c9ac75df963e00e87992c3fae1294f68a520de26a66",
 87 |       "object_type": "CustomObject",
 88 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
 89 |     },
 90 |     {
 91 |       "_type": "object_type_definition",
 92 |       "_resource_state": "c551d79ad5d03e43f3818b62deb9bc022bf63ab65a25d20a5baace4814479b93",
 93 |       "object_type": "CustomObject",
 94 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
 95 |     },
 96 |     {
 97 |       "_type": "object_type_definition",
 98 |       "_resource_state": "c791af0e68594272ed567adb2a0cd6f317649d268d4a130eaea156453ef93191",
 99 |       "object_type": "CustomObject",
100 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
101 |     },
102 |     {
103 |       "_type": "object_type_definition",
104 |       "_resource_state": "e0ee4be30313bff780fafbe624a7b5fa12dcd1fe612e07c8253cabeb89673eef",
105 |       "object_type": "CustomObject",
106 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
107 |     },
108 |     {
109 |       "_type": "object_type_definition",
110 |       "_resource_state": "37dae93b96f46692eefa0198ebb0343f39b9087a01dc0866ac9f68d607fee347",
111 |       "object_type": "CustomObject",
112 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
113 |     },
114 |     {
115 |       "_type": "object_type_definition",
116 |       "_resource_state": "3f00a07beb99dff8bd23a260e39706aef06a972abee152acbf99cb23f393a874",
117 |       "object_type": "CustomObject",
118 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
119 |     },
120 |     {
121 |       "_type": "object_type_definition",
122 |       "_resource_state": "ca9c5999b32a237368365b3a6d9282112b318e83a87fa44196887ee5e27ad722",
123 |       "object_type": "CustomObject",
124 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomObject"
125 |     },
126 |     {
127 |       "_type": "object_type_definition",
128 |       "_resource_state": "eefce3a725c68b0396b5fe04bfcfdd3308b7e6c2b959cdf1dc4cf6585aefb40e",
129 |       "object_type": "CustomerActiveData",
130 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerActiveData"
131 |     },
132 |     {
133 |       "_type": "object_type_definition",
134 |       "_resource_state": "41677a8c9dc8a3d7bff48395880f3c80eac4d5627d82d03581f7e6ae8e36c0dd",
135 |       "object_type": "CustomerAddress",
136 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerAddress"
137 |     },
138 |     {
139 |       "_type": "object_type_definition",
140 |       "_resource_state": "725f55adb3c725776cc9041735b49bad2c4986f6992fdd36c4c7e730df024acf",
141 |       "object_type": "CustomerCDPData",
142 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerCDPData"
143 |     },
144 |     {
145 |       "_type": "object_type_definition",
146 |       "_resource_state": "fdcad9f0ccbe08c5620d33558d88872465bc4899fa2e1b1d5d662cf2a20e2333",
147 |       "object_type": "CustomerGroup",
148 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerGroup"
149 |     },
150 |     {
151 |       "_type": "object_type_definition",
152 |       "_resource_state": "e81f19ba694f61c8c9773a14d2e1ccde997f33033948e5840ff4fa4ef44381a8",
153 |       "object_type": "CustomerPaymentInstrument",
154 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/CustomerPaymentInstrument"
155 |     },
156 |     {
157 |       "_type": "object_type_definition",
158 |       "_resource_state": "f92c6870f867b14ec145ae39ddc0370ea64951112cde3275ff2bf063f045bf97",
159 |       "object_type": "Product",
160 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Product"
161 |     },
162 |     {
163 |       "_type": "object_type_definition", 
164 |       "_resource_state": "a93d7870f867b14ec145ae39ddc0370ea64951112cde3275ff2bf063f045bf98",
165 |       "object_type": "Order",
166 |       "link": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions/Order"
167 |     }
168 |   ],
169 |   "next": "https://localhost:3000/s/-/dw/data/v23_2/system_object_definitions?start=27&count=25",
170 |   "start": 0,
171 |   "total": 27
172 | }
```

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

```typescript
  1 | /**
  2 |  * Logger class for standardized logging across the SFCC MCP application.
  3 |  * Provides consistent logging with timestamps and log levels.
  4 |  * Always logs to files for consistent debugging and to avoid interfering with stdio.
  5 |  *
  6 |  * ## Log Directory Location
  7 |  *
  8 |  * The logger uses the operating system's temporary directory via Node.js `os.tmpdir()`:
  9 |  * - **macOS**: `/var/folders/{user-specific-path}/T/sfcc-mcp-logs/`
 10 |  * - **Linux**: `/tmp/sfcc-mcp-logs/` (typically)
 11 |  * - **Windows**: `%TEMP%\sfcc-mcp-logs\` (typically `C:\Users\{user}\AppData\Local\Temp\`)
 12 |  *
 13 |  * This approach provides:
 14 |  * - User-specific isolation (more secure than system-wide `/tmp`)
 15 |  * - Automatic cleanup by the OS
 16 |  * - Platform-appropriate temporary storage
 17 |  * - Proper permissions handling
 18 |  *
 19 |  * To find your log directory, use `Logger.getInstance().getLogDirectory()` or check
 20 |  * the debug logs which show the directory path during initialization.
 21 |  */
 22 | 
 23 | import { appendFileSync, existsSync, mkdirSync } from 'fs';
 24 | import { join } from 'path';
 25 | import { tmpdir } from 'os';
 26 | 
 27 | export class Logger {
 28 |   private context: string;
 29 |   private enableTimestamp: boolean;
 30 |   private debugEnabled: boolean;
 31 |   private logDir: string;
 32 |   private static instance: Logger | null = null;
 33 | 
 34 |   /**
 35 |    * Create a new Logger instance
 36 |    * @param context The context/component name for this logger
 37 |    * @param enableTimestamp Whether to include timestamps in log messages (default: true)
 38 |    * @param debugEnabled Whether to enable debug logging (default: false)
 39 |    * @param customLogDir Custom log directory for testing purposes
 40 |    */
 41 |   constructor(context: string = 'SFCC-MCP', enableTimestamp: boolean = true, debugEnabled: boolean = false, customLogDir?: string) {
 42 |     this.context = context;
 43 |     this.enableTimestamp = enableTimestamp;
 44 |     this.debugEnabled = debugEnabled;
 45 | 
 46 |     // Set up log directory - use custom directory for testing or default for production
 47 |     this.logDir = customLogDir ?? join(tmpdir(), 'sfcc-mcp-logs');
 48 |     if (!existsSync(this.logDir)) {
 49 |       mkdirSync(this.logDir, { recursive: true });
 50 |     }
 51 |   }
 52 | 
 53 |   /**
 54 |    * Initialize the global logger instance with specific settings
 55 |    * This should be called once at application startup
 56 |    */
 57 |   public static initialize(context: string = 'SFCC-MCP', enableTimestamp: boolean = true, debugEnabled: boolean = false, customLogDir?: string): void {
 58 |     Logger.instance = new Logger(context, enableTimestamp, debugEnabled, customLogDir);
 59 |   }
 60 | 
 61 |   /**
 62 |    * Get the global logger instance
 63 |    * If not initialized, creates a default instance
 64 |    */
 65 |   public static getInstance(): Logger {
 66 |     Logger.instance ??= new Logger();
 67 |     return Logger.instance;
 68 |   }
 69 | 
 70 |   /**
 71 |    * Create a child logger with a new context but inheriting other settings from the global instance
 72 |    * @param subContext The sub-context to append to the current context
 73 |    * @returns A new Logger instance with the combined context
 74 |    */
 75 |   public static getChildLogger(subContext: string): Logger {
 76 |     const globalLogger = Logger.getInstance();
 77 |     return new Logger(`${globalLogger.context}:${subContext}`, globalLogger.enableTimestamp, globalLogger.debugEnabled, globalLogger.logDir);
 78 |   }
 79 | 
 80 |   /**
 81 |    * Format a log message with optional timestamp and context
 82 |    * @param message The message to format
 83 |    * @returns Formatted message string
 84 |    */
 85 |   private formatMessage(message: string): string {
 86 |     const timestamp = this.enableTimestamp ? `[${new Date().toISOString()}] ` : '';
 87 |     return `${timestamp}[${this.context}] ${message}`;
 88 |   }
 89 | 
 90 |   /**
 91 |    * Write log message to appropriate log file
 92 |    */
 93 |   private writeLog(level: 'info' | 'warn' | 'error' | 'debug', message: string, ...args: any[]): void {
 94 |     const formattedMessage = this.formatMessage(message);
 95 |     const fullMessage = args.length > 0 ? `${formattedMessage} ${args.map(arg =>
 96 |       typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg),
 97 |     ).join(' ')}` : formattedMessage;
 98 | 
 99 |     // Always write to log files
100 |     const logFile = join(this.logDir, `sfcc-mcp-${level}.log`);
101 |     const logEntry = `${fullMessage}\n`;
102 | 
103 |     try {
104 |       appendFileSync(logFile, logEntry, 'utf8');
105 |     } catch (error) {
106 |       // Fallback: if file logging fails, try stderr for critical errors only
107 |       if (level === 'error') {
108 |         process.stderr.write(`[LOGGER ERROR] Could not write to log file: ${error}\n`);
109 |         process.stderr.write(`${logEntry}`);
110 |       }
111 |     }
112 |   }
113 | 
114 |   /**
115 |    * Log an informational message
116 |    * @param message The message to log
117 |    * @param args Optional arguments to include
118 |    */
119 |   public log(message: string, ...args: any[]): void {
120 |     this.writeLog('info', message, ...args);
121 |   }
122 | 
123 |   /**
124 |    * Log an informational message (alias for log)
125 |    * @param message The message to log
126 |    * @param args Optional arguments to include
127 |    */
128 |   public info(message: string, ...args: any[]): void {
129 |     this.writeLog('info', message, ...args);
130 |   }
131 | 
132 |   /**
133 |    * Log a warning message
134 |    * @param message The warning message to log
135 |    * @param args Optional arguments to include
136 |    */
137 |   public warn(message: string, ...args: any[]): void {
138 |     this.writeLog('warn', message, ...args);
139 |   }
140 | 
141 |   /**
142 |    * Log an error message
143 |    * @param message The error message to log
144 |    * @param args Optional arguments to include
145 |    */
146 |   public error(message: string, ...args: any[]): void {
147 |     this.writeLog('error', message, ...args);
148 |   }
149 | 
150 |   /**
151 |    * Log a debug message (only if debug is enabled)
152 |    * @param message The debug message to log
153 |    * @param args Optional arguments to include
154 |    */
155 |   public debug(message: string, ...args: any[]): void {
156 |     if (this.debugEnabled) {
157 |       this.writeLog('debug', `[DEBUG] ${message}`, ...args);
158 |     }
159 |   }
160 | 
161 |   /**
162 |    * Log method entry with parameters
163 |    * @param methodName The name of the method being entered
164 |    * @param params Optional parameters being passed to the method
165 |    */
166 |   public methodEntry(methodName: string, params?: any): void {
167 |     if (this.debugEnabled) {
168 |       const paramStr = params ? ` with params: ${JSON.stringify(params)}` : '';
169 |       this.debug(`Entering method: ${methodName}${paramStr}`);
170 |     }
171 |   }
172 | 
173 |   /**
174 |    * Log method exit with optional result
175 |    * @param methodName The name of the method being exited
176 |    * @param result Optional result being returned from the method
177 |    */
178 |   public methodExit(methodName: string, result?: any): void {
179 |     if (this.debugEnabled) {
180 |       const resultStr = result !== undefined ? ` with result: ${typeof result === 'object' ? JSON.stringify(result) : result}` : '';
181 |       this.debug(`Exiting method: ${methodName}${resultStr}`);
182 |     }
183 |   }
184 | 
185 |   /**
186 |    * Log performance timing information
187 |    * @param operation The operation being timed
188 |    * @param startTime The start time (from performance.now() or Date.now())
189 |    */
190 |   public timing(operation: string, startTime: number): void {
191 |     if (this.debugEnabled) {
192 |       const duration = Date.now() - startTime;
193 |       this.debug(`Performance: ${operation} took ${duration}ms`);
194 |     }
195 |   }
196 | 
197 |   /**
198 |    * Create a child logger with a new context but inheriting other settings
199 |    * @param subContext The sub-context to append to the current context
200 |    * @returns A new Logger instance with the combined context
201 |    */
202 |   public createChildLogger(subContext: string): Logger {
203 |     return new Logger(`${this.context}:${subContext}`, this.enableTimestamp, this.debugEnabled, this.logDir);
204 |   }
205 | 
206 |   /**
207 |    * Enable or disable debug logging
208 |    * @param enabled Whether debug logging should be enabled
209 |    */
210 |   public setDebugEnabled(enabled: boolean): void {
211 |     this.debugEnabled = enabled;
212 |   }
213 | 
214 |   /**
215 |    * Get the current log directory
216 |    */
217 |   public getLogDirectory(): string {
218 |     return this.logDir;
219 |   }
220 | }
221 | 
222 | // Export the singleton instance getter for convenience
223 | export const getLogger = Logger.getInstance;
224 | 
```

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

```javascript
  1 | import { test, describe, before, after, beforeEach } from 'node:test';
  2 | import { strict as assert } from 'node:assert';
  3 | import { connect } from 'mcp-aegis';
  4 | 
  5 | describe('SFCC Development MCP Server - Full Mode with Credentials Tests (Tool Presence Only)', () => {
  6 |   let client;
  7 |   let toolsCache;
  8 | 
  9 |   before(async () => {
 10 |     client = await connect('./aegis.config.with-dw.json');
 11 |   });
 12 | 
 13 |   after(async () => {
 14 |     if (client?.connected) {
 15 |       await client.disconnect();
 16 |     }
 17 |   });
 18 | 
 19 |   beforeEach(() => {
 20 |     // CRITICAL: Clear all buffers to prevent leaking into next tests
 21 |     client.clearAllBuffers(); // Recommended - comprehensive protection
 22 |   });
 23 | 
 24 |   test('should successfully connect to server with credentials', async () => {
 25 |     assert.ok(client.connected, 'Client should be connected');
 26 |   });
 27 | 
 28 |   test('should list all available tools in full mode', async () => {
 29 |     const tools = await client.listTools();
 30 |     toolsCache = tools; // Cache for other tests
 31 |     
 32 |     assert.ok(Array.isArray(tools), 'Tools should be an array');
 33 |     assert.ok(tools.length > 15, 'Should have more tools than documentation-only mode');
 34 |   });
 35 | 
 36 |   test('should have all documentation tools available', async () => {
 37 |     const tools = toolsCache || await client.listTools();
 38 |     const toolNames = tools.map(tool => tool.name);
 39 |     
 40 |     // SFCC Documentation Tools
 41 |     assert.ok(toolNames.includes('get_sfcc_class_info'), 'Should have SFCC class info tool');
 42 |     assert.ok(toolNames.includes('search_sfcc_classes'), 'Should have SFCC class search tool');
 43 |     assert.ok(toolNames.includes('list_sfcc_classes'), 'Should have SFCC class list tool');
 44 |     
 45 |     // Best Practices Tools  
 46 |     assert.ok(toolNames.includes('get_available_best_practice_guides'), 'Should have best practices list tool');
 47 |     assert.ok(toolNames.includes('get_best_practice_guide'), 'Should have best practice guide tool');
 48 |     
 49 |     // SFRA Documentation Tools
 50 |     assert.ok(toolNames.includes('get_available_sfra_documents'), 'Should have SFRA documents list tool');
 51 |     assert.ok(toolNames.includes('get_sfra_document'), 'Should have SFRA document tool');
 52 |     
 53 |     // Cartridge Generation Tools
 54 |     assert.ok(toolNames.includes('generate_cartridge_structure'), 'Should have cartridge generation tool');
 55 |   });
 56 | 
 57 |   test('should have log analysis tools in full mode', async () => {
 58 |     const tools = toolsCache || await client.listTools();
 59 |     const toolNames = tools.map(tool => tool.name);
 60 |     
 61 |     // Log analysis tools should be available with credentials
 62 |     assert.ok(toolNames.includes('get_latest_error'), 'Should have log error tool');
 63 |     assert.ok(toolNames.includes('get_latest_info'), 'Should have log info tool');
 64 |     assert.ok(toolNames.includes('get_latest_warn'), 'Should have log warn tool');
 65 |     assert.ok(toolNames.includes('get_latest_debug'), 'Should have log debug tool');
 66 |     assert.ok(toolNames.includes('summarize_logs'), 'Should have log summary tool');
 67 |     assert.ok(toolNames.includes('search_logs'), 'Should have log search tool');
 68 |     assert.ok(toolNames.includes('list_log_files'), 'Should have log file listing tool');
 69 |   });
 70 | 
 71 |   test('should have OCAPI system object tools in full mode', async () => {
 72 |     const tools = toolsCache || await client.listTools();
 73 |     const toolNames = tools.map(tool => tool.name);
 74 |     
 75 |     // System object tools should be available with credentials
 76 |     assert.ok(toolNames.includes('get_system_object_definitions'), 'Should have system object definitions tool');
 77 |     assert.ok(toolNames.includes('get_system_object_definition'), 'Should have system object definition tool');
 78 |     assert.ok(toolNames.includes('search_system_object_attribute_definitions'), 'Should have system object attribute search tool');
 79 |     assert.ok(toolNames.includes('search_system_object_attribute_groups'), 'Should have system object attribute groups search tool');
 80 |     assert.ok(toolNames.includes('search_site_preferences'), 'Should have site preferences search tool');
 81 |   });
 82 | 
 83 |   test('should have code version tools in full mode', async () => {
 84 |     const tools = toolsCache || await client.listTools();
 85 |     const toolNames = tools.map(tool => tool.name);
 86 |     
 87 |     // Code version tools should be available with credentials
 88 |     assert.ok(toolNames.includes('get_code_versions'), 'Should have code versions tool');
 89 |     assert.ok(toolNames.includes('activate_code_version'), 'Should have code version activation tool');
 90 |   });
 91 | 
 92 |   test('should have job log tools in full mode', async () => {
 93 |     const tools = toolsCache || await client.listTools();
 94 |     const toolNames = tools.map(tool => tool.name);
 95 |     
 96 |     // Job log tools should be available with credentials
 97 |     assert.ok(toolNames.includes('get_latest_job_log_files'), 'Should have job log files tool');
 98 |     assert.ok(toolNames.includes('get_job_log_entries'), 'Should have job log entries tool');
 99 |     assert.ok(toolNames.includes('search_job_logs'), 'Should have job log search tool');
100 |     assert.ok(toolNames.includes('search_job_logs_by_name'), 'Should have job log search by name tool');
101 |     assert.ok(toolNames.includes('get_job_execution_summary'), 'Should have job execution summary tool');
102 |   });
103 | 
104 |   // NOTE: We intentionally do NOT test tool execution in full mode because:
105 |   // 1. The test credentials are not real SFCC instances
106 |   // 2. Tools would fail with authentication/connection errors  
107 |   // 3. This test suite is designed to verify tool PRESENCE, not functionality
108 |   // 4. Tool functionality testing should be done against real SFCC development instances
109 | 
110 |   test('should have exactly 36 tools available in full mode', async () => {
111 |     const tools = toolsCache || await client.listTools();
112 |     
113 |     // Full mode should have exactly 36 tools (same count as YAML test)
114 |     assert.equal(tools.length, 36, `Should have exactly 36 tools, got ${tools.length}`);
115 |   });
116 | 
117 |   test('should have WebDAV-dependent tools (log analysis)', async () => {
118 |     const tools = toolsCache || await client.listTools();
119 |     const toolNames = tools.map(tool => tool.name);
120 |     
121 |     // WebDAV-dependent tools should be available
122 |     assert.ok(toolNames.includes('summarize_logs'), 'Should have summarize_logs tool');
123 |     assert.ok(toolNames.includes('get_log_file_contents'), 'Should have get_log_file_contents tool');
124 |   });
125 | 
126 |   test('should have OCAPI-dependent tools (system objects)', async () => {
127 |     const tools = toolsCache || await client.listTools();
128 |     const toolNames = tools.map(tool => tool.name);
129 |     
130 |     // OCAPI-dependent tools should be available
131 |     assert.ok(toolNames.includes('search_site_preferences'), 'Should have search_site_preferences tool');
132 |     assert.ok(toolNames.includes('search_custom_object_attribute_definitions'), 'Should have search_custom_object_attribute_definitions tool');
133 |   });
134 | 
135 |   test('should include all docs-only tools in full mode', async () => {
136 |     const tools = toolsCache || await client.listTools();
137 |     const toolNames = tools.map(tool => tool.name);
138 |     
139 |     // Verify that docs-only tools are NOT missing in full mode
140 |     assert.ok(toolNames.includes('search_sfra_documentation'), 'Should have search_sfra_documentation tool');
141 |     assert.ok(toolNames.includes('get_best_practice_guide'), 'Should have get_best_practice_guide tool');
142 |     assert.ok(toolNames.includes('get_sfcc_class_info'), 'Should have get_sfcc_class_info tool');
143 |   });
144 | 
145 |   test('should validate all tool schemas in full mode', async () => {
146 |     const tools = toolsCache || await client.listTools();
147 |     
148 |     for (const tool of tools) {
149 |       assert.ok(tool.name, `Tool should have a name: ${JSON.stringify(tool, null, 2)}`);
150 |       assert.ok(tool.description, `Tool ${tool.name} should have a description`);
151 |       assert.ok(tool.inputSchema, `Tool ${tool.name} should have an input schema`);
152 |       assert.equal(tool.inputSchema.type, 'object', `Tool ${tool.name} input schema should be object type`);
153 |     }
154 |   });
155 | });
156 | 
```

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

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class ReturnCaseItem
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.Extensible
  9 |   - dw.order.AbstractItem
 10 |     - dw.order.ReturnCaseItem
 11 | 
 12 | ## Description
 13 | 
 14 | An item of a ReturnCase, created using method ReturnCase.createItem(String). Initially the ReturnCaseItem is NEW. No Return can be created at this point. From NEW the item transitions in CONFIRMED state. Now Return can be created. Next transition is either to PARTIAL_RETURNED or to CANCELLED. At the end the item can be RETURNED (no other Returns can be created. The custom code implementing the ReturnHooks is responsible to provide the logic for the transitions. Please refer to the documentation of ReturnHooks for further information. When the related ReturnCase were confirmed, only the the custom attributes of the return case item can be changed. Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.
 15 | 
 16 | ## Constants
 17 | 
 18 | ### STATUS_CANCELLED
 19 | 
 20 | **Type:** String = "CANCELLED"
 21 | 
 22 | constant for ReturnCase Status CANCELLED
 23 | 
 24 | ### STATUS_CONFIRMED
 25 | 
 26 | **Type:** String = "CONFIRMED"
 27 | 
 28 | constant for ReturnCase Status CONFIRMED
 29 | 
 30 | ### STATUS_NEW
 31 | 
 32 | **Type:** String = "NEW"
 33 | 
 34 | constant for ReturnCase Status NEW
 35 | 
 36 | ### STATUS_PARTIAL_RETURNED
 37 | 
 38 | **Type:** String = "PARTIAL_RETURNED"
 39 | 
 40 | constant for ReturnCase Status PARTIAL RETURNED
 41 | 
 42 | ### STATUS_RETURNED
 43 | 
 44 | **Type:** String = "RETURNED"
 45 | 
 46 | constant for ReturnCase Status RETURNED
 47 | 
 48 | ## Properties
 49 | 
 50 | ### authorizedQuantity
 51 | 
 52 | **Type:** Quantity
 53 | 
 54 | Return the Quantity authorized for this ReturnCaseItem, may be N/A.
 55 | 
 56 | ### basePrice
 57 | 
 58 | **Type:** Money (Read Only)
 59 | 
 60 | Price of a single unit before discount application.
 61 | 
 62 | ### note
 63 | 
 64 | **Type:** String
 65 | 
 66 | Return the note for this return case item.
 67 | 
 68 | ### parentItem
 69 | 
 70 | **Type:** ReturnCaseItem
 71 | 
 72 | Returns null or the parent item.
 73 | 
 74 | ### reasonCode
 75 | 
 76 | **Type:** EnumValue
 77 | 
 78 | The reason code for return case item.
 79 | 
 80 | ### returnCaseNumber
 81 | 
 82 | **Type:** String (Read Only)
 83 | 
 84 | Mandatory number of ReturnCase to which this item belongs
 85 | 
 86 | ### returnItems
 87 | 
 88 | **Type:** Collection (Read Only)
 89 | 
 90 | Unsorted collection of ReturnItems associated with this ReturnCaseItem.
 91 | 
 92 | ### status
 93 | 
 94 | **Type:** EnumValue
 95 | 
 96 | Gets the return case item status.
 97 |  
 98 |  The possible values are STATUS_NEW,STATUS_CONFIRMED,
 99 |  STATUS_PARTIAL_RETURNED, STATUS_RETURNED,
100 |  STATUS_CANCELLED.
101 | 
102 | ## Constructor Summary
103 | 
104 | ## Method Summary
105 | 
106 | ### createReturnItem
107 | 
108 | **Signature:** `createReturnItem(returnNumber : String) : ReturnItem`
109 | 
110 | Create a new ReturnItem for this ReturnCaseItem and assign it to the given Return.
111 | 
112 | ### getAuthorizedQuantity
113 | 
114 | **Signature:** `getAuthorizedQuantity() : Quantity`
115 | 
116 | Return the Quantity authorized for this ReturnCaseItem, may be N/A.
117 | 
118 | ### getBasePrice
119 | 
120 | **Signature:** `getBasePrice() : Money`
121 | 
122 | Price of a single unit before discount application.
123 | 
124 | ### getNote
125 | 
126 | **Signature:** `getNote() : String`
127 | 
128 | Return the note for this return case item.
129 | 
130 | ### getParentItem
131 | 
132 | **Signature:** `getParentItem() : ReturnCaseItem`
133 | 
134 | Returns null or the parent item.
135 | 
136 | ### getReasonCode
137 | 
138 | **Signature:** `getReasonCode() : EnumValue`
139 | 
140 | Returns the reason code for return case item.
141 | 
142 | ### getReturnCaseNumber
143 | 
144 | **Signature:** `getReturnCaseNumber() : String`
145 | 
146 | Mandatory number of ReturnCase to which this item belongs
147 | 
148 | ### getReturnItems
149 | 
150 | **Signature:** `getReturnItems() : Collection`
151 | 
152 | Unsorted collection of ReturnItems associated with this ReturnCaseItem.
153 | 
154 | ### getStatus
155 | 
156 | **Signature:** `getStatus() : EnumValue`
157 | 
158 | Gets the return case item status.
159 | 
160 | ### setAuthorizedQuantity
161 | 
162 | **Signature:** `setAuthorizedQuantity(authorizedQuantity : Quantity) : void`
163 | 
164 | Set the optional authorized Quantity for this item.
165 | 
166 | ### setNote
167 | 
168 | **Signature:** `setNote(note : String) : void`
169 | 
170 | Sets a note for this return case item.
171 | 
172 | ### setParentItem
173 | 
174 | **Signature:** `setParentItem(parentItem : ReturnCaseItem) : void`
175 | 
176 | Set a parent item.
177 | 
178 | ### setReasonCode
179 | 
180 | **Signature:** `setReasonCode(reasonCode : String) : void`
181 | 
182 | Changes the reason code.
183 | 
184 | ### setStatus
185 | 
186 | **Signature:** `setStatus(statusString : String) : void`
187 | 
188 | Sets the status.
189 | 
190 | ## Method Detail
191 | 
192 | ## Method Details
193 | 
194 | ### createReturnItem
195 | 
196 | **Signature:** `createReturnItem(returnNumber : String) : ReturnItem`
197 | 
198 | **Description:** Create a new ReturnItem for this ReturnCaseItem and assign it to the given Return.
199 | 
200 | **Parameters:**
201 | 
202 | - `returnNumber`: number of Return to which new item is assigned.
203 | 
204 | **Returns:**
205 | 
206 | new ReturnItem
207 | 
208 | ---
209 | 
210 | ### getAuthorizedQuantity
211 | 
212 | **Signature:** `getAuthorizedQuantity() : Quantity`
213 | 
214 | **Description:** Return the Quantity authorized for this ReturnCaseItem, may be N/A.
215 | 
216 | **Returns:**
217 | 
218 | the authorized quantity or N/A
219 | 
220 | ---
221 | 
222 | ### getBasePrice
223 | 
224 | **Signature:** `getBasePrice() : Money`
225 | 
226 | **Description:** Price of a single unit before discount application.
227 | 
228 | **Returns:**
229 | 
230 | Price of a single unit before discount application.
231 | 
232 | ---
233 | 
234 | ### getNote
235 | 
236 | **Signature:** `getNote() : String`
237 | 
238 | **Description:** Return the note for this return case item.
239 | 
240 | **Returns:**
241 | 
242 | the note or null
243 | 
244 | ---
245 | 
246 | ### getParentItem
247 | 
248 | **Signature:** `getParentItem() : ReturnCaseItem`
249 | 
250 | **Description:** Returns null or the parent item.
251 | 
252 | **Returns:**
253 | 
254 | null or the parent item.
255 | 
256 | ---
257 | 
258 | ### getReasonCode
259 | 
260 | **Signature:** `getReasonCode() : EnumValue`
261 | 
262 | **Description:** Returns the reason code for return case item.
263 | 
264 | **Returns:**
265 | 
266 | the return reason code
267 | 
268 | ---
269 | 
270 | ### getReturnCaseNumber
271 | 
272 | **Signature:** `getReturnCaseNumber() : String`
273 | 
274 | **Description:** Mandatory number of ReturnCase to which this item belongs
275 | 
276 | **Returns:**
277 | 
278 | number of ReturnCase to which this item belongs
279 | 
280 | ---
281 | 
282 | ### getReturnItems
283 | 
284 | **Signature:** `getReturnItems() : Collection`
285 | 
286 | **Description:** Unsorted collection of ReturnItems associated with this ReturnCaseItem.
287 | 
288 | **Returns:**
289 | 
290 | unsorted collection of ReturnItems associated with this ReturnCaseItem
291 | 
292 | **See Also:**
293 | 
294 | createReturnItem(String)
295 | 
296 | ---
297 | 
298 | ### getStatus
299 | 
300 | **Signature:** `getStatus() : EnumValue`
301 | 
302 | **Description:** Gets the return case item status. The possible values are STATUS_NEW,STATUS_CONFIRMED, STATUS_PARTIAL_RETURNED, STATUS_RETURNED, STATUS_CANCELLED.
303 | 
304 | **Returns:**
305 | 
306 | the status
307 | 
308 | ---
309 | 
310 | ### setAuthorizedQuantity
311 | 
312 | **Signature:** `setAuthorizedQuantity(authorizedQuantity : Quantity) : void`
313 | 
314 | **Description:** Set the optional authorized Quantity for this item. Passing null will result in an N/A Quantity being set.
315 | 
316 | **Parameters:**
317 | 
318 | - `authorizedQuantity`: null or the quantity
319 | 
320 | ---
321 | 
322 | ### setNote
323 | 
324 | **Signature:** `setNote(note : String) : void`
325 | 
326 | **Description:** Sets a note for this return case item.
327 | 
328 | **Parameters:**
329 | 
330 | - `note`: the note for this return case item to set
331 | 
332 | ---
333 | 
334 | ### setParentItem
335 | 
336 | **Signature:** `setParentItem(parentItem : ReturnCaseItem) : void`
337 | 
338 | **Description:** Set a parent item. The parent item must belong to the same ReturnCase. An infinite parent-child loop is disallowed as is a parent-child depth greater than 10. Setting a parent item indicates a dependency of the child item on the parent item, and can be used to form a parallel structure to that accessed using ProductLineItem.getParent().
339 | 
340 | **Parameters:**
341 | 
342 | - `parentItem`: The parent item, null is allowed
343 | 
344 | ---
345 | 
346 | ### setReasonCode
347 | 
348 | **Signature:** `setReasonCode(reasonCode : String) : void`
349 | 
350 | **Description:** Changes the reason code. Initially the reason code is set on return case item creation.
351 | 
352 | **Parameters:**
353 | 
354 | - `reasonCode`: the reason code to set
355 | 
356 | ---
357 | 
358 | ### setStatus
359 | 
360 | **Signature:** `setStatus(statusString : String) : void`
361 | 
362 | **Description:** Sets the status. The possible values are STATUS_NEW,STATUS_CONFIRMED, STATUS_PARTIAL_RETURNED, STATUS_RETURNED, STATUS_CANCELLED.
363 | 
364 | **Parameters:**
365 | 
366 | - `statusString`: the status
367 | 
368 | **Throws:**
369 | 
370 | NullPointerException - if status is null
371 | IllegalArgumentException - if the status transition to the status is not allowed
372 | 
373 | ---
```

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

```typescript
  1 | /**
  2 |  * Tests for OCAPIAuthClient
  3 |  * Tests OAuth authentication functionality for OCAPI
  4 |  */
  5 | 
  6 | import { OCAPIAuthClient } from '../src/clients/base/ocapi-auth-client.js';
  7 | import { TokenManager } from '../src/clients/base/oauth-token.js';
  8 | import { OCAPIConfig, OAuthTokenResponse } from '../src/types/types.js';
  9 | 
 10 | // Mock fetch globally
 11 | global.fetch = jest.fn();
 12 | 
 13 | // Mock TokenManager
 14 | jest.mock('../src/clients/base/oauth-token.js');
 15 | 
 16 | // Mock Logger
 17 | jest.mock('../src/utils/logger.js', () => ({
 18 |   Logger: {
 19 |     initialize: jest.fn(),
 20 |     getInstance: jest.fn(() => ({
 21 |       methodEntry: jest.fn(),
 22 |       methodExit: jest.fn(),
 23 |       debug: jest.fn(),
 24 |       warn: jest.fn(),
 25 |       error: jest.fn(),
 26 |       timing: jest.fn(),
 27 |       log: jest.fn(),
 28 |       info: jest.fn(),
 29 |     })),
 30 |     getChildLogger: jest.fn(() => ({
 31 |       methodEntry: jest.fn(),
 32 |       methodExit: jest.fn(),
 33 |       debug: jest.fn(),
 34 |       warn: jest.fn(),
 35 |       error: jest.fn(),
 36 |       timing: jest.fn(),
 37 |       log: jest.fn(),
 38 |       info: jest.fn(),
 39 |     })),
 40 |   },
 41 | }));
 42 | 
 43 | // Mock BaseHttpClient
 44 | jest.mock('../src/clients/base/http-client.js');
 45 | 
 46 | describe('OCAPIAuthClient', () => {
 47 |   let client: OCAPIAuthClient;
 48 |   let mockTokenManager: jest.Mocked<TokenManager>;
 49 |   let mockFetch: jest.MockedFunction<typeof fetch>;
 50 | 
 51 |   const mockConfig: OCAPIConfig = {
 52 |     hostname: 'test-instance.demandware.net',
 53 |     clientId: 'test-client-id',
 54 |     clientSecret: 'test-client-secret',
 55 |     version: 'v21_3',
 56 |   };
 57 | 
 58 |   beforeEach(() => {
 59 |     jest.clearAllMocks();
 60 | 
 61 |     mockFetch = fetch as jest.MockedFunction<typeof fetch>;
 62 | 
 63 |     // Setup TokenManager mock
 64 |     mockTokenManager = {
 65 |       getValidToken: jest.fn(),
 66 |       storeToken: jest.fn(),
 67 |       clearToken: jest.fn(),
 68 |       getTokenExpiration: jest.fn(),
 69 |       isTokenValid: jest.fn(),
 70 |       clearAllTokens: jest.fn(),
 71 |     } as any;
 72 | 
 73 |     (TokenManager.getInstance as jest.Mock).mockReturnValue(mockTokenManager);
 74 | 
 75 |     client = new OCAPIAuthClient(mockConfig);
 76 | 
 77 |     // Manually set up the logger mock since BaseHttpClient mock isn't working as expected
 78 |     (client as any).logger = {
 79 |       debug: jest.fn(),
 80 |       info: jest.fn(),
 81 |       warn: jest.fn(),
 82 |       error: jest.fn(),
 83 |     };
 84 |   });
 85 | 
 86 |   describe('constructor', () => {
 87 |     it('should initialize with config', () => {
 88 |       expect(client).toBeInstanceOf(OCAPIAuthClient);
 89 |       expect(TokenManager.getInstance).toHaveBeenCalled();
 90 |     });
 91 |   });
 92 | 
 93 |   describe('getAuthHeaders', () => {
 94 |     it('should return Bearer token in auth headers', async () => {
 95 |       const mockToken = 'mock-access-token';
 96 |       mockTokenManager.getValidToken.mockReturnValue(mockToken);
 97 | 
 98 |       const headers = await (client as any).getAuthHeaders();
 99 | 
100 |       expect(headers).toEqual({
101 |         'Authorization': 'Bearer mock-access-token',
102 |       });
103 |       expect(mockTokenManager.getValidToken).toHaveBeenCalledWith(
104 |         mockConfig.hostname,
105 |         mockConfig.clientId,
106 |       );
107 |     });
108 | 
109 |     it('should request new token when no valid token exists', async () => {
110 |       const newToken = 'new-access-token';
111 |       const tokenResponse: OAuthTokenResponse = {
112 |         access_token: newToken,
113 |         token_type: 'bearer',
114 |         expires_in: 3600,
115 |       };
116 | 
117 |       mockTokenManager.getValidToken.mockReturnValue(null);
118 |       mockFetch.mockResolvedValue({
119 |         ok: true,
120 |         json: async () => tokenResponse,
121 |       } as Response);
122 | 
123 |       const headers = await (client as any).getAuthHeaders();
124 | 
125 |       expect(headers).toEqual({
126 |         'Authorization': 'Bearer new-access-token',
127 |       });
128 |       expect(mockFetch).toHaveBeenCalledWith(
129 |         'https://account.demandware.com/dwsso/oauth2/access_token',
130 |         {
131 |           method: 'POST',
132 |           headers: {
133 |             'Authorization': `Basic ${Buffer.from(`${mockConfig.clientId}:${mockConfig.clientSecret}`).toString('base64')}`,
134 |             'Content-Type': 'application/x-www-form-urlencoded',
135 |           },
136 |           body: 'grant_type=client_credentials',
137 |         },
138 |       );
139 |       expect(mockTokenManager.storeToken).toHaveBeenCalledWith(
140 |         mockConfig.hostname,
141 |         mockConfig.clientId,
142 |         tokenResponse,
143 |       );
144 |     });
145 | 
146 |     it('should handle OAuth request failure', async () => {
147 |       mockTokenManager.getValidToken.mockReturnValue(null);
148 |       mockFetch.mockResolvedValue({
149 |         ok: false,
150 |         status: 401,
151 |         statusText: 'Unauthorized',
152 |         text: async () => 'Invalid credentials',
153 |       } as Response);
154 | 
155 |       await expect((client as any).getAuthHeaders()).rejects.toThrow(
156 |         'Failed to get access token: Error: OAuth authentication failed: 401 Unauthorized - Invalid credentials',
157 |       );
158 |     });
159 | 
160 |     it('should handle network errors during token request', async () => {
161 |       mockTokenManager.getValidToken.mockReturnValue(null);
162 |       mockFetch.mockRejectedValue(new Error('Network error'));
163 | 
164 |       await expect((client as any).getAuthHeaders()).rejects.toThrow(
165 |         'Failed to get access token: Error: Network error',
166 |       );
167 |     });
168 |   });
169 | 
170 |   describe('handleAuthError', () => {
171 |     it('should clear token when handling auth error', async () => {
172 |       await (client as any).handleAuthError();
173 | 
174 |       expect(mockTokenManager.clearToken).toHaveBeenCalledWith(
175 |         mockConfig.hostname,
176 |         mockConfig.clientId,
177 |       );
178 |     });
179 |   });
180 | 
181 |   describe('getTokenExpiration', () => {
182 |     it('should return token expiration from TokenManager', () => {
183 |       const mockExpiration = new Date('2025-12-31T23:59:59Z');
184 |       mockTokenManager.getTokenExpiration.mockReturnValue(mockExpiration);
185 | 
186 |       const result = client.getTokenExpiration();
187 | 
188 |       expect(result).toBe(mockExpiration);
189 |       expect(mockTokenManager.getTokenExpiration).toHaveBeenCalledWith(
190 |         mockConfig.hostname,
191 |         mockConfig.clientId,
192 |       );
193 |     });
194 | 
195 |     it('should return null when no token exists', () => {
196 |       mockTokenManager.getTokenExpiration.mockReturnValue(null);
197 | 
198 |       const result = client.getTokenExpiration();
199 | 
200 |       expect(result).toBeNull();
201 |     });
202 |   });
203 | 
204 |   describe('refreshToken', () => {
205 |     it('should clear token and request new one', async () => {
206 |       const newToken = 'refreshed-token';
207 |       const tokenResponse: OAuthTokenResponse = {
208 |         access_token: newToken,
209 |         token_type: 'bearer',
210 |         expires_in: 3600,
211 |       };
212 | 
213 |       mockFetch.mockResolvedValue({
214 |         ok: true,
215 |         json: async () => tokenResponse,
216 |       } as Response);
217 | 
218 |       await client.refreshToken();
219 | 
220 |       expect(mockTokenManager.clearToken).toHaveBeenCalledWith(
221 |         mockConfig.hostname,
222 |         mockConfig.clientId,
223 |       );
224 |       expect(mockFetch).toHaveBeenCalled();
225 |     });
226 | 
227 |     it('should handle refresh token failure', async () => {
228 |       mockFetch.mockResolvedValue({
229 |         ok: false,
230 |         status: 401,
231 |         statusText: 'Unauthorized',
232 |         text: async () => 'Invalid refresh',
233 |       } as Response);
234 | 
235 |       await expect(client.refreshToken()).rejects.toThrow(
236 |         'Failed to get access token',
237 |       );
238 |     });
239 |   });
240 | 
241 |   describe('token management flow', () => {
242 |     it('should use existing token when valid', async () => {
243 |       const existingToken = 'existing-valid-token';
244 |       mockTokenManager.getValidToken.mockReturnValue(existingToken);
245 | 
246 |       const headers = await (client as any).getAuthHeaders();
247 | 
248 |       expect(headers).toEqual({
249 |         'Authorization': 'Bearer existing-valid-token',
250 |       });
251 |       expect(mockFetch).not.toHaveBeenCalled();
252 |     });
253 | 
254 |     it('should store new token after successful request', async () => {
255 |       const tokenResponse: OAuthTokenResponse = {
256 |         access_token: 'new-token',
257 |         token_type: 'bearer',
258 |         expires_in: 7200,
259 |       };
260 | 
261 |       mockTokenManager.getValidToken.mockReturnValue(null);
262 |       mockFetch.mockResolvedValue({
263 |         ok: true,
264 |         json: async () => tokenResponse,
265 |       } as Response);
266 | 
267 |       await (client as any).getAuthHeaders();
268 | 
269 |       expect(mockTokenManager.storeToken).toHaveBeenCalledWith(
270 |         mockConfig.hostname,
271 |         mockConfig.clientId,
272 |         tokenResponse,
273 |       );
274 |     });
275 |   });
276 | });
277 | 
```
Page 15/61FirstPrevNextLast