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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/docs/dw_content/ContentSearchModel.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.content
  2 | 
  3 | # Class ContentSearchModel
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.catalog.SearchModel
  9 |   - dw.content.ContentSearchModel
 10 | 
 11 | ## Description
 12 | 
 13 | The class is the central interface to a content search result and a content search refinement. It also provides utility methods to generate a search URL.
 14 | 
 15 | ## Constants
 16 | 
 17 | ### CONTENTID_PARAMETER
 18 | 
 19 | **Type:** String = "cid"
 20 | 
 21 | URL Parameter for the content ID
 22 | 
 23 | ### FOLDERID_PARAMETER
 24 | 
 25 | **Type:** String = "fdid"
 26 | 
 27 | URL Parameter for the folder ID
 28 | 
 29 | ## Properties
 30 | 
 31 | ### content
 32 | 
 33 | **Type:** Iterator (Read Only)
 34 | 
 35 | An Iterator containing all Content Assets that are the result of the
 36 |  search.
 37 | 
 38 | ### contentID
 39 | 
 40 | **Type:** String
 41 | 
 42 | The content ID against which the search results apply.
 43 | 
 44 | ### deepestCommonFolder
 45 | 
 46 | **Type:** Folder (Read Only)
 47 | 
 48 | The deepest common folder of all content assets in the search result.
 49 | 
 50 | ### filteredByFolder
 51 | 
 52 | **Type:** boolean
 53 | 
 54 | The method returns true, if the content search result is filtered by the folder and it is not subsequently
 55 |  possible to search for content assets that belong to no folder (e.g. those for Page Designer).
 56 | 
 57 | ### folder
 58 | 
 59 | **Type:** Folder (Read Only)
 60 | 
 61 | The folder against which the search results apply.
 62 | 
 63 | ### folderID
 64 | 
 65 | **Type:** String
 66 | 
 67 | The folder ID against which the search results apply.
 68 | 
 69 | ### folderSearch
 70 | 
 71 | **Type:** boolean (Read Only)
 72 | 
 73 | The method returns true, if this is a pure search for a folder. The
 74 |  method checks, that a folder ID is specified and no search phrase is
 75 |  specified.
 76 | 
 77 | ### pageMetaTags
 78 | 
 79 | **Type:** Array (Read Only)
 80 | 
 81 | All page meta tags, defined for this instance for which content can be generated.
 82 | 
 83 |  The meta tag content is generated based on the content listing page meta tag context and rules.
 84 |  The rules are obtained from the current folder context or inherited from the parent folder,
 85 |  up to the root folder.
 86 | 
 87 | ### recursiveFolderSearch
 88 | 
 89 | **Type:** boolean
 90 | 
 91 | Get the flag that determines if the folder search will
 92 |  be recursive.
 93 | 
 94 | ### refinedByFolder
 95 | 
 96 | **Type:** boolean (Read Only)
 97 | 
 98 | The method returns true, if the search is refined by a folder.
 99 |  The method checks, that a folder ID is specified.
100 | 
101 | ### refinedFolderSearch
102 | 
103 | **Type:** boolean (Read Only)
104 | 
105 | Identifies if this is a folder search and is refined with further
106 |  criteria, like a name refinement or an attribute refinement.
107 | 
108 | ### refinements
109 | 
110 | **Type:** ContentSearchRefinements (Read Only)
111 | 
112 | The set of search refinements used in this search.
113 | 
114 | ## Constructor Summary
115 | 
116 | ContentSearchModel() Constructs a new ContentSearchModel.
117 | 
118 | ## Method Summary
119 | 
120 | ### getContent
121 | 
122 | **Signature:** `getContent() : Iterator`
123 | 
124 | Returns an Iterator containing all Content Assets that are the result of the search.
125 | 
126 | ### getContentID
127 | 
128 | **Signature:** `getContentID() : String`
129 | 
130 | Returns the content ID against which the search results apply.
131 | 
132 | ### getDeepestCommonFolder
133 | 
134 | **Signature:** `getDeepestCommonFolder() : Folder`
135 | 
136 | Returns the deepest common folder of all content assets in the search result.
137 | 
138 | ### getFolder
139 | 
140 | **Signature:** `getFolder() : Folder`
141 | 
142 | Returns the folder against which the search results apply.
143 | 
144 | ### getFolderID
145 | 
146 | **Signature:** `getFolderID() : String`
147 | 
148 | Returns the folder ID against which the search results apply.
149 | 
150 | ### getPageMetaTag
151 | 
152 | **Signature:** `getPageMetaTag(id : String) : PageMetaTag`
153 | 
154 | Returns the page meta tag for the specified id.
155 | 
156 | ### getPageMetaTags
157 | 
158 | **Signature:** `getPageMetaTags() : Array`
159 | 
160 | Returns all page meta tags, defined for this instance for which content can be generated.
161 | 
162 | ### getRefinements
163 | 
164 | **Signature:** `getRefinements() : ContentSearchRefinements`
165 | 
166 | Returns the set of search refinements used in this search.
167 | 
168 | ### isFilteredByFolder
169 | 
170 | **Signature:** `isFilteredByFolder() : boolean`
171 | 
172 | The method returns true, if the content search result is filtered by the folder and it is not subsequently possible to search for content assets that belong to no folder (e.g.
173 | 
174 | ### isFolderSearch
175 | 
176 | **Signature:** `isFolderSearch() : boolean`
177 | 
178 | The method returns true, if this is a pure search for a folder.
179 | 
180 | ### isRecursiveFolderSearch
181 | 
182 | **Signature:** `isRecursiveFolderSearch() : boolean`
183 | 
184 | Get the flag that determines if the folder search will be recursive.
185 | 
186 | ### isRefinedByFolder
187 | 
188 | **Signature:** `isRefinedByFolder() : boolean`
189 | 
190 | The method returns true, if the search is refined by a folder.
191 | 
192 | ### isRefinedFolderSearch
193 | 
194 | **Signature:** `isRefinedFolderSearch() : boolean`
195 | 
196 | Identifies if this is a folder search and is refined with further criteria, like a name refinement or an attribute refinement.
197 | 
198 | ### search
199 | 
200 | **Signature:** `search() : SearchStatus`
201 | 
202 | Execute the search.
203 | 
204 | ### setContentID
205 | 
206 | **Signature:** `setContentID(contentID : String) : void`
207 | 
208 | Sets the contentID used in this search.
209 | 
210 | ### setFilteredByFolder
211 | 
212 | **Signature:** `setFilteredByFolder(filteredByFolder : boolean) : void`
213 | 
214 | Set a flag to indicate if the search is filtered by the folder.
215 | 
216 | ### setFolderID
217 | 
218 | **Signature:** `setFolderID(folderID : String) : void`
219 | 
220 | Sets the folderID used in this search.
221 | 
222 | ### setRecursiveFolderSearch
223 | 
224 | **Signature:** `setRecursiveFolderSearch(recurse : boolean) : void`
225 | 
226 | Set a flag to indicate if the search in folder should be recursive.
227 | 
228 | ### urlForContent
229 | 
230 | **Signature:** `static urlForContent(action : String, cid : String) : URL`
231 | 
232 | Returns an URL that you can use to execute a query for a specific Content.
233 | 
234 | ### urlForContent
235 | 
236 | **Signature:** `static urlForContent(url : URL, cid : String) : URL`
237 | 
238 | Returns an URL that you can use to execute a query for a specific Content.
239 | 
240 | ### urlForFolder
241 | 
242 | **Signature:** `static urlForFolder(action : String, fid : String) : URL`
243 | 
244 | Returns an URL that you can use to execute a query for a specific Folder.
245 | 
246 | ### urlForFolder
247 | 
248 | **Signature:** `static urlForFolder(url : URL, fid : String) : URL`
249 | 
250 | Returns an URL that you can use to execute a query for a specific Folder.
251 | 
252 | ### urlForRefine
253 | 
254 | **Signature:** `static urlForRefine(action : String, name : String, value : String) : URL`
255 | 
256 | Returns an URL that you can use to execute a query for a specific attribute name-value pair.
257 | 
258 | ### urlForRefine
259 | 
260 | **Signature:** `static urlForRefine(url : URL, name : String, value : String) : URL`
261 | 
262 | Returns an URL that you can use to execute a query for a specific attribute name-value pair.
263 | 
264 | ### urlRefineFolder
265 | 
266 | **Signature:** `urlRefineFolder(action : String, refineFolderID : String) : URL`
267 | 
268 | Returns an URL that you can use to re-execute the query using the specified pipeline action and folder refinement.
269 | 
270 | ### urlRefineFolder
271 | 
272 | **Signature:** `urlRefineFolder(url : URL, refineFolderID : String) : URL`
273 | 
274 | Returns an URL that you can use to re-execute the query using the specified URL and folder refinement.
275 | 
276 | ### urlRelaxFolder
277 | 
278 | **Signature:** `urlRelaxFolder(action : String) : URL`
279 | 
280 | Returns an URL that you can use to re-execute the query with no folder refinement.
281 | 
282 | ### urlRelaxFolder
283 | 
284 | **Signature:** `urlRelaxFolder(url : URL) : URL`
285 | 
286 | Returns an URL that you can use to re-execute the query with no folder refinement.
287 | 
288 | ## Constructor Detail
289 | 
290 | ## Method Detail
291 | 
292 | ## Method Details
293 | 
294 | ### getContent
295 | 
296 | **Signature:** `getContent() : Iterator`
297 | 
298 | **Description:** Returns an Iterator containing all Content Assets that are the result of the search.
299 | 
300 | **Returns:**
301 | 
302 | an Iterator containing all Content Assets that are the result of the search.
303 | 
304 | ---
305 | 
306 | ### getContentID
307 | 
308 | **Signature:** `getContentID() : String`
309 | 
310 | **Description:** Returns the content ID against which the search results apply.
311 | 
312 | **Returns:**
313 | 
314 | the content ID against which the search results apply.
315 | 
316 | ---
317 | 
318 | ### getDeepestCommonFolder
319 | 
320 | **Signature:** `getDeepestCommonFolder() : Folder`
321 | 
322 | **Description:** Returns the deepest common folder of all content assets in the search result.
323 | 
324 | **Returns:**
325 | 
326 | the deepest common folder of all content assets in the search result of this search model.
327 | 
328 | ---
329 | 
330 | ### getFolder
331 | 
332 | **Signature:** `getFolder() : Folder`
333 | 
334 | **Description:** Returns the folder against which the search results apply.
335 | 
336 | **Returns:**
337 | 
338 | the folder against which the search results apply.
339 | 
340 | ---
341 | 
342 | ### getFolderID
343 | 
344 | **Signature:** `getFolderID() : String`
345 | 
346 | **Description:** Returns the folder ID against which the search results apply.
347 | 
348 | **Returns:**
349 | 
350 | the folder ID against which the search results apply.
351 | 
352 | ---
353 | 
354 | ### getPageMetaTag
355 | 
356 | **Signature:** `getPageMetaTag(id : String) : PageMetaTag`
357 | 
358 | **Description:** Returns the page meta tag for the specified id. The meta tag content is generated based on the content listing page meta tag context and rule. The rule is obtained from the current folder context or inherited from the parent folder, up to the root folder. Null will be returned if the meta tag is undefined on the current instance, or if no rule can be found for the current context, or if the rule resolves to an empty string.
359 | 
360 | **Parameters:**
361 | 
362 | - `id`: the ID to get the page meta tag for
363 | 
364 | **Returns:**
365 | 
366 | page meta tag containing content generated based on rules
367 | 
368 | ---
369 | 
370 | ### getPageMetaTags
371 | 
372 | **Signature:** `getPageMetaTags() : Array`
373 | 
374 | **Description:** Returns all page meta tags, defined for this instance for which content can be generated. The meta tag content is generated based on the content listing page meta tag context and rules. The rules are obtained from the current folder context or inherited from the parent folder, up to the root folder.
375 | 
376 | **Returns:**
377 | 
378 | page meta tags defined for this instance, containing content generated based on rules
379 | 
380 | ---
381 | 
382 | ### getRefinements
383 | 
384 | **Signature:** `getRefinements() : ContentSearchRefinements`
385 | 
386 | **Description:** Returns the set of search refinements used in this search.
387 | 
388 | **Returns:**
389 | 
390 | the set of search refinements used in this search.
391 | 
392 | ---
393 | 
394 | ### isFilteredByFolder
395 | 
396 | **Signature:** `isFilteredByFolder() : boolean`
397 | 
398 | **Description:** The method returns true, if the content search result is filtered by the folder and it is not subsequently possible to search for content assets that belong to no folder (e.g. those for Page Designer).
399 | 
400 | **Returns:**
401 | 
402 | True if this is filtered by the folder of the content asset.
403 | 
404 | ---
405 | 
406 | ### isFolderSearch
407 | 
408 | **Signature:** `isFolderSearch() : boolean`
409 | 
410 | **Description:** The method returns true, if this is a pure search for a folder. The method checks, that a folder ID is specified and no search phrase is specified.
411 | 
412 | **Returns:**
413 | 
414 | True if this is a folder search.
415 | 
416 | ---
417 | 
418 | ### isRecursiveFolderSearch
419 | 
420 | **Signature:** `isRecursiveFolderSearch() : boolean`
421 | 
422 | **Description:** Get the flag that determines if the folder search will be recursive.
423 | 
424 | **Returns:**
425 | 
426 | true if the folder search will be recursive, false otherwise
427 | 
428 | ---
429 | 
430 | ### isRefinedByFolder
431 | 
432 | **Signature:** `isRefinedByFolder() : boolean`
433 | 
434 | **Description:** The method returns true, if the search is refined by a folder. The method checks, that a folder ID is specified.
435 | 
436 | **Returns:**
437 | 
438 | true, if the search is refined by a folder, false otherwise.
439 | 
440 | ---
441 | 
442 | ### isRefinedFolderSearch
443 | 
444 | **Signature:** `isRefinedFolderSearch() : boolean`
445 | 
446 | **Description:** Identifies if this is a folder search and is refined with further criteria, like a name refinement or an attribute refinement.
447 | 
448 | **Returns:**
449 | 
450 | true if this is a folder search and is refined with further criteria, false otherwise.
451 | 
452 | ---
453 | 
454 | ### search
455 | 
456 | **Signature:** `search() : SearchStatus`
457 | 
458 | **Description:** Execute the search.
459 | 
460 | **Returns:**
461 | 
462 | the searchStatus object with search status code and description of search result.
463 | 
464 | ---
465 | 
466 | ### setContentID
467 | 
468 | **Signature:** `setContentID(contentID : String) : void`
469 | 
470 | **Description:** Sets the contentID used in this search.
471 | 
472 | **Parameters:**
473 | 
474 | - `contentID`: the contentID used in this search.
475 | 
476 | ---
477 | 
478 | ### setFilteredByFolder
479 | 
480 | **Signature:** `setFilteredByFolder(filteredByFolder : boolean) : void`
481 | 
482 | **Description:** Set a flag to indicate if the search is filtered by the folder. Must be set to false to return content assets that do not belong to any folder.
483 | 
484 | **Parameters:**
485 | 
486 | - `filteredByFolder`: filter the search result by folder
487 | 
488 | ---
489 | 
490 | ### setFolderID
491 | 
492 | **Signature:** `setFolderID(folderID : String) : void`
493 | 
494 | **Description:** Sets the folderID used in this search.
495 | 
496 | **Parameters:**
497 | 
498 | - `folderID`: the folderID used in this search.
499 | 
500 | ---
501 | 
502 | ### setRecursiveFolderSearch
503 | 
504 | **Signature:** `setRecursiveFolderSearch(recurse : boolean) : void`
505 | 
506 | **Description:** Set a flag to indicate if the search in folder should be recursive.
507 | 
508 | **Parameters:**
509 | 
510 | - `recurse`: recurse the folder in the search
511 | 
512 | ---
513 | 
514 | ### urlForContent
515 | 
516 | **Signature:** `static urlForContent(action : String, cid : String) : URL`
517 | 
518 | **Description:** Returns an URL that you can use to execute a query for a specific Content. The passed action is used to build an initial url. All search specific attributes are appended.
519 | 
520 | **Parameters:**
521 | 
522 | - `action`: the pipeline action to use.
523 | - `cid`: the content id.
524 | 
525 | **Returns:**
526 | 
527 | an URL that you can use to execute a query for a specific Content. The passed action is used to build an initial url. All search specific attributes are appended.
528 | 
529 | ---
530 | 
531 | ### urlForContent
532 | 
533 | **Signature:** `static urlForContent(url : URL, cid : String) : URL`
534 | 
535 | **Description:** Returns an URL that you can use to execute a query for a specific Content. The passed url can be either a full url or just the name for a pipeline. In the later case a relative URL is created.
536 | 
537 | **Parameters:**
538 | 
539 | - `url`: the URL to use when constructing the new URL.
540 | - `cid`: the content id.
541 | 
542 | **Returns:**
543 | 
544 | an URL that you can use to execute a query for a specific Content. The passed url can be either a full url or just the name for a pipeline. In the later case a relative URL is created.
545 | 
546 | ---
547 | 
548 | ### urlForFolder
549 | 
550 | **Signature:** `static urlForFolder(action : String, fid : String) : URL`
551 | 
552 | **Description:** Returns an URL that you can use to execute a query for a specific Folder.
553 | 
554 | **Parameters:**
555 | 
556 | - `action`: the pipeline action to use.
557 | - `fid`: the id of the Folder to use.
558 | 
559 | **Returns:**
560 | 
561 | an URL that you can use to execute a query for a specific Folder.
562 | 
563 | ---
564 | 
565 | ### urlForFolder
566 | 
567 | **Signature:** `static urlForFolder(url : URL, fid : String) : URL`
568 | 
569 | **Description:** Returns an URL that you can use to execute a query for a specific Folder.
570 | 
571 | **Parameters:**
572 | 
573 | - `url`: the URL to use in constructing the new URL.
574 | - `fid`: the id of the Folder to use.
575 | 
576 | **Returns:**
577 | 
578 | an URL that you can use to execute a query for a specific Folder.
579 | 
580 | ---
581 | 
582 | ### urlForRefine
583 | 
584 | **Signature:** `static urlForRefine(action : String, name : String, value : String) : URL`
585 | 
586 | **Description:** Returns an URL that you can use to execute a query for a specific attribute name-value pair.
587 | 
588 | **Parameters:**
589 | 
590 | - `action`: the pipeline action to use.
591 | - `name`: the name of the attribute.
592 | - `value`: the value for the attribute.
593 | 
594 | **Returns:**
595 | 
596 | an URL that you can use to execute a query for a specific attribute name-value pair.
597 | 
598 | ---
599 | 
600 | ### urlForRefine
601 | 
602 | **Signature:** `static urlForRefine(url : URL, name : String, value : String) : URL`
603 | 
604 | **Description:** Returns an URL that you can use to execute a query for a specific attribute name-value pair.
605 | 
606 | **Parameters:**
607 | 
608 | - `url`: the URL to use when constructing the new URL.
609 | - `name`: the name of the attribute.
610 | - `value`: the value for the attribute.
611 | 
612 | **Returns:**
613 | 
614 | an URL that you can use to execute a query for a specific attribute name-value pair.
615 | 
616 | ---
617 | 
618 | ### urlRefineFolder
619 | 
620 | **Signature:** `urlRefineFolder(action : String, refineFolderID : String) : URL`
621 | 
622 | **Description:** Returns an URL that you can use to re-execute the query using the specified pipeline action and folder refinement.
623 | 
624 | **Parameters:**
625 | 
626 | - `action`: the action to use.
627 | - `refineFolderID`: the folder ID to use as a refinement.
628 | 
629 | **Returns:**
630 | 
631 | an URL that you can use to re-execute the exact same query using the specified pipeline action and folder refinement.
632 | 
633 | ---
634 | 
635 | ### urlRefineFolder
636 | 
637 | **Signature:** `urlRefineFolder(url : URL, refineFolderID : String) : URL`
638 | 
639 | **Description:** Returns an URL that you can use to re-execute the query using the specified URL and folder refinement.
640 | 
641 | **Parameters:**
642 | 
643 | - `url`: the existing URL to use when constructing the new URL.
644 | - `refineFolderID`: the ID of the folder refinement to use.
645 | 
646 | **Returns:**
647 | 
648 | an URL that you can use to re-execute the query using the specified URL and folder refinement.
649 | 
650 | ---
651 | 
652 | ### urlRelaxFolder
653 | 
654 | **Signature:** `urlRelaxFolder(action : String) : URL`
655 | 
656 | **Description:** Returns an URL that you can use to re-execute the query with no folder refinement.
657 | 
658 | **Parameters:**
659 | 
660 | - `action`: the pipeline action to use in the URL.
661 | 
662 | **Returns:**
663 | 
664 | an URL that you can use to re-execute the query with no folder refinement.
665 | 
666 | ---
667 | 
668 | ### urlRelaxFolder
669 | 
670 | **Signature:** `urlRelaxFolder(url : URL) : URL`
671 | 
672 | **Description:** Returns an URL that you can use to re-execute the query with no folder refinement.
673 | 
674 | **Parameters:**
675 | 
676 | - `url`: the existing URL to use when constructing the new URL.
677 | 
678 | **Returns:**
679 | 
680 | an URL that you can use to re-execute the query with no folder refinement.
681 | 
682 | ---
```

--------------------------------------------------------------------------------
/docs/dw_customer/Credentials.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.customer
  2 | 
  3 | # Class Credentials
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.customer.Credentials
  9 | 
 10 | ## Description
 11 | 
 12 | Represents the credentials of a customer. Since 13.6 it is possible to have customers who are not authenticated through a login and password but through an external authentication provider via the OAuth2 protocol. In such cases, the AuthenticationProviderID will point to an OAuth provider configured in the system and the ExternalID will be the unique identifier of the customer on the Authentication Provider's system. For example, if an authentication provider with ID "Google123" is configured pointing to Google and the customer has a logged in into Google in the past and has created a profile there, Google assigns a unique number identifier to that customer. If the storefront is configured to allow authentication through Google and a new customer logs into the storefront using Google, the AuthenticationProviderID property of his Credentials will contain "Google123" and the ExternalID property will contain whatever unique identifier Google has assigned to him. In such cases the password-related properties of the Credentials will be empty. Note: this class handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12.
 13 | 
 14 | ## Properties
 15 | 
 16 | ### authenticationProviderID
 17 | 
 18 | **Type:** String
 19 | 
 20 | The authentication provider ID.
 21 | 
 22 | ### enabled
 23 | 
 24 | **Type:** boolean (Read Only)
 25 | 
 26 | Identifies if this customer is enabled and can log in.
 27 | 
 28 | ### enabledFlag
 29 | 
 30 | **Type:** boolean
 31 | 
 32 | Identifies if this customer is enabled and can log in - same as isEnabled().
 33 | 
 34 | ### externalID
 35 | 
 36 | **Type:** String
 37 | 
 38 | The external ID of the customer.
 39 | 
 40 | ### locked
 41 | 
 42 | **Type:** boolean (Read Only)
 43 | 
 44 | Identifies if this customer is temporarily locked out because of invalid
 45 |  login attempts.  If customer locking is not enabled, this method always
 46 |  returns false.
 47 | 
 48 | ### login
 49 | 
 50 | **Type:** String
 51 | 
 52 | The login of the user. It must be unique.
 53 | 
 54 | ### passwordAnswer
 55 | 
 56 | **Type:** String
 57 | 
 58 | The answer to the password question for the customer. The answer is used
 59 |  with the password question to confirm the identity of a customer when
 60 |  they are trying to fetch their password.
 61 | 
 62 | ### passwordQuestion
 63 | 
 64 | **Type:** String
 65 | 
 66 | The password question for the customer. The password question is
 67 |  used with the password answer to confirm the identity of a customer when
 68 |  they are trying to fetch their password.
 69 | 
 70 | ### passwordSet
 71 | 
 72 | **Type:** boolean (Read Only)
 73 | 
 74 | Returns whether the password is set. Creating externally authenticated customers
 75 |  results in customers with credentials for which the password is not set.
 76 | 
 77 | ### remainingLoginAttempts
 78 | 
 79 | **Type:** Number (Read Only)
 80 | 
 81 | The number of consecutive failed logins after which this customer
 82 |  will be temporarily locked out and prevented from logging in to the
 83 |  current site. This value is based on the number of previous invalid
 84 |  logins for this customer and customer site preferences defining the
 85 |  limits.
 86 | 
 87 |  If this customer is already locked out, this method will always return 0.
 88 |  If customer locking is disabled altogether, or if the system cannot
 89 |  determine the number of failed login attempts for this customer, then
 90 |  this method will return a negative number.
 91 | 
 92 | ## Constructor Summary
 93 | 
 94 | ## Method Summary
 95 | 
 96 | ### createResetPasswordToken
 97 | 
 98 | **Signature:** `createResetPasswordToken() : String`
 99 | 
100 | Generate a random token which can be used for resetting the password of the underlying Customer.
101 | 
102 | ### getAuthenticationProviderID
103 | 
104 | **Signature:** `getAuthenticationProviderID() : String`
105 | 
106 | Returns the authentication provider ID.
107 | 
108 | ### getEnabledFlag
109 | 
110 | **Signature:** `getEnabledFlag() : boolean`
111 | 
112 | Identifies if this customer is enabled and can log in - same as isEnabled().
113 | 
114 | ### getExternalID
115 | 
116 | **Signature:** `getExternalID() : String`
117 | 
118 | Returns the external ID of the customer.
119 | 
120 | ### getLogin
121 | 
122 | **Signature:** `getLogin() : String`
123 | 
124 | Returns the login of the user.
125 | 
126 | ### getPasswordAnswer
127 | 
128 | **Signature:** `getPasswordAnswer() : String`
129 | 
130 | Returns the answer to the password question for the customer.
131 | 
132 | ### getPasswordQuestion
133 | 
134 | **Signature:** `getPasswordQuestion() : String`
135 | 
136 | Returns the password question for the customer.
137 | 
138 | ### getRemainingLoginAttempts
139 | 
140 | **Signature:** `getRemainingLoginAttempts() : Number`
141 | 
142 | Returns the number of consecutive failed logins after which this customer will be temporarily locked out and prevented from logging in to the current site.
143 | 
144 | ### isEnabled
145 | 
146 | **Signature:** `isEnabled() : boolean`
147 | 
148 | Identifies if this customer is enabled and can log in.
149 | 
150 | ### isLocked
151 | 
152 | **Signature:** `isLocked() : boolean`
153 | 
154 | Identifies if this customer is temporarily locked out because of invalid login attempts.
155 | 
156 | ### isPasswordSet
157 | 
158 | **Signature:** `isPasswordSet() : boolean`
159 | 
160 | Returns whether the password is set.
161 | 
162 | ### setAuthenticationProviderID
163 | 
164 | **Signature:** `setAuthenticationProviderID(authenticationProviderID : String) : void`
165 | 
166 | Sets the authentication provider ID corresponding to an OAuth provider configured in the system.
167 | 
168 | ### setEnabledFlag
169 | 
170 | **Signature:** `setEnabledFlag(enabledFlag : boolean) : void`
171 | 
172 | Sets the enabled status of the customer.
173 | 
174 | ### setExternalID
175 | 
176 | **Signature:** `setExternalID(externalID : String) : void`
177 | 
178 | Sets the external ID of the customer at the authentication provider.
179 | 
180 | ### setLogin
181 | 
182 | **Signature:** `setLogin(login : String) : void`
183 | 
184 | Sets the login value for the customer.
185 | 
186 | ### setLogin
187 | 
188 | **Signature:** `setLogin(newLogin : String, currentPassword : String) : boolean`
189 | 
190 | Sets the login value for the customer, and also re-encrypt the customer password based on the new login.
191 | 
192 | ### setPassword
193 | 
194 | **Signature:** `setPassword(newPassword : String, oldPassword : String, verifyOldPassword : boolean) : Status`
195 | 
196 | Sets the password of an authenticated customer. The method can be called for externally authenticated customers as well but these customers will still be externally authenticated so calling the method for such customers does not have an immediate practical benefit.
197 | 
198 | ### setPasswordAnswer
199 | 
200 | **Signature:** `setPasswordAnswer(answer : String) : void`
201 | 
202 | Sets the answer to the password question for the customer.
203 | 
204 | ### setPasswordQuestion
205 | 
206 | **Signature:** `setPasswordQuestion(question : String) : void`
207 | 
208 | Sets the password question for the customer.
209 | 
210 | ### setPasswordWithToken
211 | 
212 | **Signature:** `setPasswordWithToken(token : String, newPassword : String) : Status`
213 | 
214 | Set the password of the specified customer to the specified value.
215 | 
216 | ## Method Detail
217 | 
218 | ## Method Details
219 | 
220 | ### createResetPasswordToken
221 | 
222 | **Signature:** `createResetPasswordToken() : String`
223 | 
224 | **Description:** Generate a random token which can be used for resetting the password of the underlying Customer. The token is guaranteed to be unique and will be valid for 30 minutes. Any token previously generated for this customer will be invalidated.
225 | 
226 | **Returns:**
227 | 
228 | The generated token.
229 | 
230 | ---
231 | 
232 | ### getAuthenticationProviderID
233 | 
234 | **Signature:** `getAuthenticationProviderID() : String`
235 | 
236 | **Description:** Returns the authentication provider ID.
237 | 
238 | **Deprecated:**
239 | 
240 | As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will get the Authentication Provider from the first element of the Customer.getExternalProfiles() collection
241 | 
242 | **Returns:**
243 | 
244 | the authentication provider ID.
245 | 
246 | ---
247 | 
248 | ### getEnabledFlag
249 | 
250 | **Signature:** `getEnabledFlag() : boolean`
251 | 
252 | **Description:** Identifies if this customer is enabled and can log in - same as isEnabled().
253 | 
254 | **Returns:**
255 | 
256 | true if the customer is enabled and can log in, false otherwise.
257 | 
258 | ---
259 | 
260 | ### getExternalID
261 | 
262 | **Signature:** `getExternalID() : String`
263 | 
264 | **Description:** Returns the external ID of the customer.
265 | 
266 | **Deprecated:**
267 | 
268 | As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will get the External ID from the first element of the Customer.getExternalProfiles() collection
269 | 
270 | **Returns:**
271 | 
272 | the external ID of the customer.
273 | 
274 | ---
275 | 
276 | ### getLogin
277 | 
278 | **Signature:** `getLogin() : String`
279 | 
280 | **Description:** Returns the login of the user. It must be unique.
281 | 
282 | **Returns:**
283 | 
284 | the login of the user.
285 | 
286 | ---
287 | 
288 | ### getPasswordAnswer
289 | 
290 | **Signature:** `getPasswordAnswer() : String`
291 | 
292 | **Description:** Returns the answer to the password question for the customer. The answer is used with the password question to confirm the identity of a customer when they are trying to fetch their password.
293 | 
294 | **Returns:**
295 | 
296 | the answer to the password question for the customer.
297 | 
298 | ---
299 | 
300 | ### getPasswordQuestion
301 | 
302 | **Signature:** `getPasswordQuestion() : String`
303 | 
304 | **Description:** Returns the password question for the customer. The password question is used with the password answer to confirm the identity of a customer when they are trying to fetch their password.
305 | 
306 | **Returns:**
307 | 
308 | the password question for the customer.
309 | 
310 | ---
311 | 
312 | ### getRemainingLoginAttempts
313 | 
314 | **Signature:** `getRemainingLoginAttempts() : Number`
315 | 
316 | **Description:** Returns the number of consecutive failed logins after which this customer will be temporarily locked out and prevented from logging in to the current site. This value is based on the number of previous invalid logins for this customer and customer site preferences defining the limits. If this customer is already locked out, this method will always return 0. If customer locking is disabled altogether, or if the system cannot determine the number of failed login attempts for this customer, then this method will return a negative number.
317 | 
318 | **Returns:**
319 | 
320 | The number of consecutive failed logins after which this customer will be locked out.
321 | 
322 | ---
323 | 
324 | ### isEnabled
325 | 
326 | **Signature:** `isEnabled() : boolean`
327 | 
328 | **Description:** Identifies if this customer is enabled and can log in.
329 | 
330 | **Returns:**
331 | 
332 | true if the customer is enabled and can log in, false otherwise.
333 | 
334 | ---
335 | 
336 | ### isLocked
337 | 
338 | **Signature:** `isLocked() : boolean`
339 | 
340 | **Description:** Identifies if this customer is temporarily locked out because of invalid login attempts. If customer locking is not enabled, this method always returns false.
341 | 
342 | **Returns:**
343 | 
344 | true if the customer is locked, false otherwise.
345 | 
346 | ---
347 | 
348 | ### isPasswordSet
349 | 
350 | **Signature:** `isPasswordSet() : boolean`
351 | 
352 | **Description:** Returns whether the password is set. Creating externally authenticated customers results in customers with credentials for which the password is not set.
353 | 
354 | **Returns:**
355 | 
356 | true if the password is set.
357 | 
358 | ---
359 | 
360 | ### setAuthenticationProviderID
361 | 
362 | **Signature:** `setAuthenticationProviderID(authenticationProviderID : String) : void`
363 | 
364 | **Description:** Sets the authentication provider ID corresponding to an OAuth provider configured in the system.
365 | 
366 | **Deprecated:**
367 | 
368 | As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will set the Authentication Provider on the first element of the Customer.getExternalProfiles() collection if there is only one. It will create the collection and add an element if no elements are present. It will not change anything and will log an error if there are more than one elements in the collection.
369 | 
370 | **Parameters:**
371 | 
372 | - `authenticationProviderID`: the authentication Provider ID to set.
373 | 
374 | ---
375 | 
376 | ### setEnabledFlag
377 | 
378 | **Signature:** `setEnabledFlag(enabledFlag : boolean) : void`
379 | 
380 | **Description:** Sets the enabled status of the customer.
381 | 
382 | **Parameters:**
383 | 
384 | - `enabledFlag`: controls if a customer is enabled or not.
385 | 
386 | ---
387 | 
388 | ### setExternalID
389 | 
390 | **Signature:** `setExternalID(externalID : String) : void`
391 | 
392 | **Description:** Sets the external ID of the customer at the authentication provider. The value is provided by the authentication provider during the OAuth authentication and is unique within that provider.
393 | 
394 | **Deprecated:**
395 | 
396 | As of release 17.2, replaced by methods on the new class ExternalProfile which can be obtained from Customer.getExternalProfiles() Until the method is fully removed from the API it will set the ExternalID on the first element of the Customer.getExternalProfiles() collection if there is only one. It will create the collection and add an element if no elements are present. It will not change anything and will log an error if there are more than one elements in the collection.
397 | 
398 | **Parameters:**
399 | 
400 | - `externalID`: the external ID to set.
401 | 
402 | ---
403 | 
404 | ### setLogin
405 | 
406 | **Signature:** `setLogin(login : String) : void`
407 | 
408 | **Description:** Sets the login value for the customer. IMPORTANT: This method should no longer be used for the following reasons: It changes the login without re-encrypting the password. (The customer password is stored internally using a one-way encryption scheme which uses the login as one of its inputs. Therefore changing the login requires re-encrypting the password.) It does not validate the structure of the login to ensure that it only uses acceptable characters. It does not correctly prevent duplicate logins. If the passed login matches a different customer's login exactly, then this method will throw an exception. However, it does not prevent the creation of inexact matches, where two customers have a login differing only by alphabetic case (e.g. "JaneDoe" and "janedoe")
409 | 
410 | **Deprecated:**
411 | 
412 | Use setLogin(String, String)
413 | 
414 | **Parameters:**
415 | 
416 | - `login`: The login value for the customer.
417 | 
418 | ---
419 | 
420 | ### setLogin
421 | 
422 | **Signature:** `setLogin(newLogin : String, currentPassword : String) : boolean`
423 | 
424 | **Description:** Sets the login value for the customer, and also re-encrypt the customer password based on the new login. Customer login must be a sequence of letters, numbers, and the following characters: space, period, ampersand, underscore and dash. This method fails to set the login and returns false in the following cases: newLogin is of an invalid form (e.g. contains invalid characters). currentPassword is not the customer's correct password. newLogin is already in use by another customer (i.e. there is another customer in the system with the exact same login name or a name differing only by alphabetic case.) If newLogin is the same as the existing login, the method does nothing and returns true, regardless of whether currentPassword is the correct password.
425 | 
426 | **Parameters:**
427 | 
428 | - `newLogin`: The login value for the customer.
429 | - `currentPassword`: The customer's current password in plain-text.
430 | 
431 | **Returns:**
432 | 
433 | true if setting the login succeeded, false otherwise.
434 | 
435 | ---
436 | 
437 | ### setPassword
438 | 
439 | **Signature:** `setPassword(newPassword : String, oldPassword : String, verifyOldPassword : boolean) : Status`
440 | 
441 | **Description:** Sets the password of an authenticated customer. The method can be called for externally authenticated customers as well but these customers will still be externally authenticated so calling the method for such customers does not have an immediate practical benefit. If such customers are converted back to regularly authenticated (via login and password) the new password will be used. Method call will fail under any of these conditions: customer is not registered customer is not authenticated verifyOldPassword=true && oldPassword is empty verifyOldPassword=true and oldPassword does not match the existing password newPassword is empty newPassword does not meet acceptance criteria
442 | 
443 | **Parameters:**
444 | 
445 | - `newPassword`: the new password
446 | - `oldPassword`: the old password (optional, only needed if 'verifyOldPassword' is set to 'true'
447 | - `verifyOldPassword`: whether the oldPassword should be verified
448 | 
449 | **Returns:**
450 | 
451 | Status the status of the operation (OK or ERROR). If status is Error, there will be additional information in the Status message
452 | 
453 | ---
454 | 
455 | ### setPasswordAnswer
456 | 
457 | **Signature:** `setPasswordAnswer(answer : String) : void`
458 | 
459 | **Description:** Sets the answer to the password question for the customer.
460 | 
461 | **Parameters:**
462 | 
463 | - `answer`: the answer to the password question.
464 | 
465 | ---
466 | 
467 | ### setPasswordQuestion
468 | 
469 | **Signature:** `setPasswordQuestion(question : String) : void`
470 | 
471 | **Description:** Sets the password question for the customer.
472 | 
473 | **Parameters:**
474 | 
475 | - `question`: the password question.
476 | 
477 | ---
478 | 
479 | ### setPasswordWithToken
480 | 
481 | **Signature:** `setPasswordWithToken(token : String, newPassword : String) : Status`
482 | 
483 | **Description:** Set the password of the specified customer to the specified value. This operation will fail if the specified token is invalid (i.e. not associated with the specified Customer), the token is expired, or the password does not satisfy system password requirements.
484 | 
485 | **Parameters:**
486 | 
487 | - `token`: The token required for performing the password reset.
488 | - `newPassword`: The new password. Must meet all requirements for passwords
489 | 
490 | **Returns:**
491 | 
492 | Status the status of the operation (OK or ERROR). If status is Error, there will be additional information in the Status message
493 | 
494 | ---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/tools.docs-only.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
  1 | # ==================================================================================
  2 | # SFCC MCP Server - Documentation-Only Mode YAML Tests
  3 | # Focused on comprehensive tool metadata validation (NOT tool execution)
  4 | # Uses conductor's enhanced pattern matching for thorough validation
  5 | # 
  6 | # Quick Test Commands:
  7 | # aegis "tests/mcp/yaml/docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --verbose
  8 | # aegis "tests/mcp/yaml/docs-only.test.mcp.yml" --config "aegis.config.docs-only.json" --debug --timing
  9 | # aegis query --config "aegis.config.docs-only.json"  # List all tools
 10 | # aegis query get_sfcc_class_info '{"className": "Catalog"}' --config "aegis.config.docs-only.json"
 11 | # ==================================================================================
 12 | description: "SFCC MCP Server docs-only mode - comprehensive tool metadata validation"
 13 | 
 14 | # ==================================================================================
 15 | # TOOL DISCOVERY & STRUCTURE VALIDATION
 16 | # ==================================================================================
 17 | tests:
 18 |   - it: "should provide tools list with proper JSON-RPC structure"
 19 |     request:
 20 |       jsonrpc: "2.0"
 21 |       id: "tool-list-basic"
 22 |       method: "tools/list"
 23 |       params: {}
 24 |     expect:
 25 |       response:
 26 |         jsonrpc: "2.0"
 27 |         id: "tool-list-basic"
 28 |         result:
 29 |           tools: "match:type:array"
 30 |       stderr: "toBeEmpty"
 31 | 
 32 |   - it: "should provide exactly 15 tools in docs-only mode"
 33 |     request:
 34 |       jsonrpc: "2.0"
 35 |       id: "tool-count-exact"
 36 |       method: "tools/list"
 37 |       params: {}
 38 |     expect:
 39 |       response:
 40 |         jsonrpc: "2.0"
 41 |         id: "tool-count-exact"
 42 |         result:
 43 |           tools: "match:arrayLength:15"
 44 |       stderr: "toBeEmpty"
 45 | 
 46 |   - it: "should have non-empty tools array"
 47 |     request:
 48 |       jsonrpc: "2.0"
 49 |       id: "tool-count-min"
 50 |       method: "tools/list"
 51 |       params: {}
 52 |     expect:
 53 |       response:
 54 |         jsonrpc: "2.0"
 55 |         id: "tool-count-min"
 56 |         result:
 57 |           tools: "match:not:arrayLength:0"
 58 |       stderr: "toBeEmpty"
 59 | 
 60 |   - it: "should have tools array with reasonable size"
 61 |     request:
 62 |       jsonrpc: "2.0"
 63 |       id: "tool-count-range"
 64 |       method: "tools/list"
 65 |       params: {}
 66 |     expect:
 67 |       response:
 68 |         jsonrpc: "2.0"
 69 |         id: "tool-count-range"
 70 |         result:
 71 |           tools: "match:arrayLength:15"
 72 |       stderr: "toBeEmpty"
 73 | 
 74 |   # ==================================================================================
 75 |   # TOOL METADATA STRUCTURE VALIDATION
 76 |   # ==================================================================================
 77 | 
 78 |   - it: "should have valid tool structure with required fields"
 79 |     request:
 80 |       jsonrpc: "2.0"
 81 |       id: "schema-validation-1"
 82 |       method: "tools/list"
 83 |       params: {}
 84 |     expect:
 85 |       response:
 86 |         jsonrpc: "2.0"
 87 |         id: "schema-validation-1"
 88 |         result:
 89 |           tools:
 90 |             match:arrayElements:
 91 |               match:partial:
 92 |                 name: "match:type:string"
 93 |                 description: "match:type:string"
 94 |                 inputSchema: "match:type:object"
 95 |       stderr: "toBeEmpty"
 96 | 
 97 |   - it: "should have tool names following snake_case convention"
 98 |     request:
 99 |       jsonrpc: "2.0"
100 |       id: "tool-names-format"
101 |       method: "tools/list"
102 |       params: {}
103 |     expect:
104 |       response:
105 |         jsonrpc: "2.0"
106 |         id: "tool-names-format"
107 |         result:
108 |           tools:
109 |             match:arrayElements:
110 |               match:partial:
111 |                 name: "match:regex:^[a-z][a-z0-9_]*$"
112 |       stderr: "toBeEmpty"
113 | 
114 |   - it: "should have meaningful tool descriptions"
115 |     request:
116 |       jsonrpc: "2.0"
117 |       id: "tool-descriptions-quality"
118 |       method: "tools/list"
119 |       params: {}
120 |     expect:
121 |       response:
122 |         jsonrpc: "2.0"
123 |         id: "tool-descriptions-quality"
124 |         result:
125 |           tools:
126 |             match:arrayElements:
127 |               match:partial:
128 |                 description: "match:regex:.{20,}"  # At least 20 characters
129 |       stderr: "toBeEmpty"
130 | 
131 |   - it: "should have non-empty tool descriptions"
132 |     request:
133 |       jsonrpc: "2.0"
134 |       id: "tool-descriptions-nonempty"
135 |       method: "tools/list"
136 |       params: {}
137 |     expect:
138 |       response:
139 |         jsonrpc: "2.0"
140 |         id: "tool-descriptions-nonempty"
141 |         result:
142 |           tools:
143 |             match:arrayElements:
144 |               match:partial:
145 |                 description: "match:not:regex:^\\s*$"  # Not empty or whitespace-only
146 |       stderr: "toBeEmpty"
147 | 
148 |   - it: "should have proper inputSchema structure"
149 |     request:
150 |       jsonrpc: "2.0"
151 |       id: "tool-schema-structure"
152 |       method: "tools/list"
153 |       params: {}
154 |     expect:
155 |       response:
156 |         jsonrpc: "2.0"
157 |         id: "tool-schema-structure"
158 |         result:
159 |           tools:
160 |             match:arrayElements:
161 |               match:partial:
162 |                 inputSchema:
163 |                   type: "object"
164 |                   properties: "match:type:object"
165 |       stderr: "toBeEmpty"
166 | 
167 |   # ==================================================================================
168 |   # TOOL NAME EXTRACTION & VALIDATION
169 |   # ==================================================================================
170 | 
171 |   - it: "should extract all expected SFCC tool names"
172 |     request:
173 |       jsonrpc: "2.0"
174 |       id: "tool-names-extract"
175 |       method: "tools/list"
176 |       params: {}
177 |     expect:
178 |       response:
179 |         jsonrpc: "2.0"
180 |         id: "tool-names-extract"
181 |         result:
182 |           match:extractField: "tools.*.name"
183 |           value: "match:arrayContains:get_sfcc_class_info"
184 |       stderr: "toBeEmpty"
185 | 
186 |   - it: "should contain SFCC class documentation tools"
187 |     request:
188 |       jsonrpc: "2.0"
189 |       id: "tool-names-sfcc-class"
190 |       method: "tools/list"
191 |       params: {}
192 |     expect:
193 |       response:
194 |         jsonrpc: "2.0"
195 |         id: "tool-names-sfcc-class"
196 |         result:
197 |           match:extractField: "tools.*.name"
198 |           value: "match:arrayContains:search_sfcc_classes"
199 |       stderr: "toBeEmpty"
200 | 
201 |   - it: "should contain best practice guide tools"
202 |     request:
203 |       jsonrpc: "2.0"
204 |       id: "tool-names-best-practices"
205 |       method: "tools/list"
206 |       params: {}
207 |     expect:
208 |       response:
209 |         jsonrpc: "2.0"
210 |         id: "tool-names-best-practices"
211 |         result:
212 |           match:extractField: "tools.*.name"
213 |           value: "match:arrayContains:get_best_practice_guide"
214 |       stderr: "toBeEmpty"
215 | 
216 |   - it: "should contain SFRA documentation tools"
217 |     request:
218 |       jsonrpc: "2.0"
219 |       id: "tool-names-sfra"
220 |       method: "tools/list"
221 |       params: {}
222 |     expect:
223 |       response:
224 |         jsonrpc: "2.0"
225 |         id: "tool-names-sfra"
226 |         result:
227 |           match:extractField: "tools.*.name"
228 |           value: "match:arrayContains:get_sfra_document"
229 |       stderr: "toBeEmpty"
230 | 
231 |   - it: "should contain cartridge generation tools"
232 |     request:
233 |       jsonrpc: "2.0"
234 |       id: "tool-names-cartridge"
235 |       method: "tools/list"
236 |       params: {}
237 |     expect:
238 |       response:
239 |         jsonrpc: "2.0"
240 |         id: "tool-names-cartridge"
241 |         result:
242 |           match:extractField: "tools.*.name"
243 |           value: "match:arrayContains:generate_cartridge_structure"
244 |       stderr: "toBeEmpty"
245 | 
246 |   # ==================================================================================
247 |   # TOOL SCHEMA VALIDATION BY CATEGORY
248 |   # ==================================================================================
249 | 
250 |   - it: "should have required parameters in SFCC class tools"
251 |     request:
252 |       jsonrpc: "2.0"
253 |       id: "schema-sfcc-class-required"
254 |       method: "tools/list"
255 |       params: {}
256 |     expect:
257 |       response:
258 |         jsonrpc: "2.0"
259 |         id: "schema-sfcc-class-required"
260 |         result:
261 |           tools: "match:arrayContains:name:get_sfcc_class_info"
262 |       stderr: "toBeEmpty"
263 | 
264 |   - it: "should have string type parameters where expected"
265 |     request:
266 |       jsonrpc: "2.0"
267 |       id: "schema-parameter-types"
268 |       method: "tools/list"
269 |       params: {}
270 |     expect:
271 |       response:
272 |         jsonrpc: "2.0"
273 |         id: "schema-parameter-types"
274 |         result:
275 |           match:extractField: "tools.*.inputSchema.properties"
276 |           value: "match:type:array"
277 |       stderr: "toBeEmpty"
278 | 
279 |   # ==================================================================================
280 |   # CROSS-FIELD VALIDATION
281 |   # ==================================================================================
282 | 
283 |   - it: "should have consistent schema structure across tools"
284 |     request:
285 |       jsonrpc: "2.0"
286 |       id: "schema-consistency"
287 |       method: "tools/list"
288 |       params: {}
289 |     expect:
290 |       response:
291 |         jsonrpc: "2.0"
292 |         id: "schema-consistency"
293 |         result:
294 |           match:extractField: "tools.*.inputSchema"
295 |           value: "match:type:array"
296 |       stderr: "toBeEmpty"
297 | 
298 |   - it: "should have required array for tools with required parameters"
299 |     request:
300 |       jsonrpc: "2.0"
301 |       id: "schema-required-validation"
302 |       method: "tools/list"
303 |       params: {}
304 |     expect:
305 |       response:
306 |         jsonrpc: "2.0"
307 |         id: "schema-required-validation"
308 |         result:
309 |           tools: "match:arrayContains:name:get_sfcc_class_info"
310 |       stderr: "toBeEmpty"
311 | 
312 |   - it: "should validate tools have consistent naming and schema patterns"
313 |     request:
314 |       jsonrpc: "2.0"
315 |       id: "comprehensive-tool-validation"
316 |       method: "tools/list"
317 |       params: {}
318 |     expect:
319 |       response:
320 |         jsonrpc: "2.0"
321 |         id: "comprehensive-tool-validation"
322 |         result:
323 |           tools:
324 |             match:arrayElements:
325 |               match:partial:
326 |                 name: "match:regex:^[a-z][a-z0-9_]*$"  # snake_case names
327 |                 description: "match:regex:.{10,}"       # min 10 chars
328 |                 inputSchema:
329 |                   type: "object"
330 |                   properties: "match:type:object"
331 |       stderr: "toBeEmpty"
332 | 
333 |   # ==================================================================================
334 |   # SPECIFIC TOOL VALIDATION
335 |   # ==================================================================================
336 | 
337 |   - it: "should have get_sfcc_class_info tool with proper description"
338 |     request:
339 |       jsonrpc: "2.0"
340 |       id: "tool-specific-class-info"
341 |       method: "tools/list"
342 |       params: {}
343 |     expect:
344 |       response:
345 |         jsonrpc: "2.0"
346 |         id: "tool-specific-class-info"
347 |         result:
348 |           tools: "match:arrayContains:name:get_sfcc_class_info"
349 |       stderr: "toBeEmpty"
350 | 
351 |   - it: "should have generate_cartridge_structure tool with proper description"
352 |     request:
353 |       jsonrpc: "2.0"
354 |       id: "tool-specific-cartridge"
355 |       method: "tools/list"
356 |       params: {}
357 |     expect:
358 |       response:
359 |         jsonrpc: "2.0"
360 |         id: "tool-specific-cartridge"
361 |         result:
362 |           tools: "match:arrayContains:name:generate_cartridge_structure"
363 |       stderr: "toBeEmpty"
364 | 
365 | 
366 |   # ==================================================================================
367 |   # BASIC FUNCTIONALITY VALIDATION (Structure Focus)
368 |   # ==================================================================================
369 | 
370 |   - it: "should execute get_sfcc_class_info with structured MCP response"
371 |     request:
372 |       jsonrpc: "2.0"
373 |       id: "class-info-test-1"
374 |       method: "tools/call"
375 |       params:
376 |         name: "get_sfcc_class_info"
377 |         arguments:
378 |           className: "Catalog"
379 |     expect:
380 |       response:
381 |         jsonrpc: "2.0"
382 |         id: "class-info-test-1"
383 |         result:
384 |           content:
385 |             - type: "text"
386 |               text: "match:type:string"
387 |           isError: false
388 |       stderr: "toBeEmpty"
389 | 
390 |   - it: "should execute search_sfcc_classes with search results structure"
391 |     request:
392 |       jsonrpc: "2.0"
393 |       id: "search-classes-test-1"
394 |       method: "tools/call"
395 |       params:
396 |         name: "search_sfcc_classes"
397 |         arguments:
398 |           query: "catalog"
399 |     expect:
400 |       response:
401 |         jsonrpc: "2.0"
402 |         id: "search-classes-test-1"
403 |         result:
404 |           content:
405 |             - type: "text"
406 |               text: "match:type:string"
407 |           isError: false
408 |       stderr: "toBeEmpty"
409 | 
410 |   - it: "should execute get_available_best_practice_guides with list structure"
411 |     request:
412 |       jsonrpc: "2.0"
413 |       id: "bp-guides-test-1"
414 |       method: "tools/call"
415 |       params:
416 |         name: "get_available_best_practice_guides"
417 |         arguments: {}
418 |     expect:
419 |       response:
420 |         jsonrpc: "2.0"
421 |         id: "bp-guides-test-1"
422 |         result:
423 |           content:
424 |             - type: "text"
425 |               text: "match:contains:cartridge_creation"
426 |           isError: false
427 |       stderr: "toBeEmpty"
428 | 
429 |   - it: "should execute get_best_practice_guide with guide content structure"
430 |     request:
431 |       jsonrpc: "2.0"
432 |       id: "bp-guide-test-1"
433 |       method: "tools/call"
434 |       params:
435 |         name: "get_best_practice_guide"
436 |         arguments:
437 |           guideName: "cartridge_creation"
438 |     expect:
439 |       response:
440 |         jsonrpc: "2.0"
441 |         id: "bp-guide-test-1"
442 |         result:
443 |           content:
444 |             - type: "text"
445 |               text: "match:type:string"
446 |           isError: false
447 |       stderr: "toBeEmpty"
448 | 
449 |   - it: "should execute get_available_sfra_documents with document list structure"
450 |     request:
451 |       jsonrpc: "2.0"
452 |       id: "sfra-docs-test-1"
453 |       method: "tools/call"
454 |       params:
455 |         name: "get_available_sfra_documents"
456 |         arguments: {}
457 |     expect:
458 |       response:
459 |         jsonrpc: "2.0"
460 |         id: "sfra-docs-test-1"
461 |         result:
462 |           content:
463 |             - type: "text"
464 |               text: "match:contains:server"
465 |           isError: false
466 |       stderr: "toBeEmpty"
467 | 
468 |   - it: "should execute get_sfra_document with documentation structure"
469 |     request:
470 |       jsonrpc: "2.0"
471 |       id: "sfra-doc-test-1"
472 |       method: "tools/call"
473 |       params:
474 |         name: "get_sfra_document"
475 |         arguments:
476 |           documentName: "server"
477 |     expect:
478 |       response:
479 |         jsonrpc: "2.0"
480 |         id: "sfra-doc-test-1"
481 |         result:
482 |           content:
483 |             - type: "text"
484 |               text: "match:type:string"
485 |           isError: false
486 |       stderr: "toBeEmpty"
487 | 
488 |   - it: "should execute search_sfra_documentation with search functionality"
489 |     request:
490 |       jsonrpc: "2.0"
491 |       id: "sfra-search-test-1"
492 |       method: "tools/call"
493 |       params:
494 |         name: "search_sfra_documentation"
495 |         arguments:
496 |           query: "render"
497 |     expect:
498 |       response:
499 |         jsonrpc: "2.0"
500 |         id: "sfra-search-test-1"
501 |         result:
502 |           content:
503 |             - type: "text"
504 |               text: "match:type:string"
505 |           isError: false
506 |       stderr: "toBeEmpty"
507 | 
508 |   - it: "should execute get_sfra_categories with category information"
509 |     request:
510 |       jsonrpc: "2.0"
511 |       id: "sfra-categories-test-1"
512 |       method: "tools/call"
513 |       params:
514 |         name: "get_sfra_categories"
515 |         arguments: {}
516 |     expect:
517 |       response:
518 |         jsonrpc: "2.0"
519 |         id: "sfra-categories-test-1"
520 |         result:
521 |           content:
522 |             - type: "text"
523 |               text: "match:type:string"
524 |           isError: false
525 |       stderr: "toBeEmpty"
526 | 
527 |   - it: "should execute generate_cartridge_structure successfully"
528 |     request:
529 |       jsonrpc: "2.0"
530 |       id: "cartridge-gen-test-1"
531 |       method: "tools/call"
532 |       params:
533 |         name: "generate_cartridge_structure"
534 |         arguments:
535 |           cartridgeName: "yaml_test_cartridge"
536 |           targetPath: "/tmp/yaml-test-output"
537 |           fullProjectSetup: false
538 |     expect:
539 |       response:
540 |         jsonrpc: "2.0"
541 |         id: "cartridge-gen-test-1"
542 |         result:
543 |           content:
544 |             - type: "text"
545 |               text: "match:type:string"
546 |           isError: false
547 |       stderr: "toBeEmpty"
548 | 
549 |   # ==================================================================================
550 |   # ERROR HANDLING VALIDATION
551 |   # ==================================================================================
552 | 
553 |   - it: "should handle invalid tool names with error response"
554 |     request:
555 |       jsonrpc: "2.0"
556 |       id: "invalid-tool-test-1"
557 |       method: "tools/call"
558 |       params:
559 |         name: "nonexistent_tool_yaml"
560 |         arguments: {}
561 |     expect:
562 |       response:
563 |         jsonrpc: "2.0"
564 |         id: "invalid-tool-test-1"
565 |         result:
566 |           content:
567 |             - type: "text"
568 |               text: "match:type:string"
569 |           isError: true
570 |       stderr: "toBeEmpty"
571 | 
572 |   - it: "should handle missing required parameters with error response"
573 |     request:
574 |       jsonrpc: "2.0"
575 |       id: "missing-param-test-1"
576 |       method: "tools/call"
577 |       params:
578 |         name: "get_sfcc_class_info"
579 |         arguments: {}
580 |     expect:
581 |       response:
582 |         jsonrpc: "2.0"
583 |         id: "missing-param-test-1"
584 |         result:
585 |           content:
586 |             - type: "text"
587 |               text: "match:type:string"
588 |           isError: true
589 |       stderr: "toBeEmpty"
590 | 
```

--------------------------------------------------------------------------------
/ai-instructions/claude-desktop/claude_custom_instructions.md:
--------------------------------------------------------------------------------

```markdown
  1 | # SFCC Development with Claude Desktop - MCP Integration
  2 | 
  3 | ## 👨‍💻 Claude-Specific Agent Persona
  4 | 
  5 | You are a **Senior Salesforce B2C Commerce Cloud (Demandware) Developer** with 8+ years of hands-on experience building enterprise-grade ecommerce solutions. Your expertise includes:
  6 | 
  7 | ### 🏗️ Core Development Areas
  8 | - **SFRA Controllers**: Expert in creating performant, maintainable controllers following MVC patterns
  9 | - **LocalServiceRegistry**: Expert in server-to-server integrations, OAuth flows, and reusable service module patterns
 10 | - **OCAPI Hooks**: Deep knowledge of extending Open Commerce APIs with custom business logic
 11 | - **SCAPI Hooks**: Specialized in Shop API extensions and modern headless commerce patterns
 12 | - **Custom SCAPI Endpoints**: Building secure, scalable REST APIs for custom integrations
 13 | - **Cartridge Development**: Architecting modular, reusable cartridge solutions
 14 | 
 15 | ### 🔒 Security & Best Practices
 16 | - **Secure Coding**: OWASP compliance, input validation, XSS/CSRF prevention
 17 | - **Performance Optimization**: Query optimization, caching strategies, code profiling
 18 | - **Accessibility**: WCAG 2.1 AA compliance, semantic HTML, keyboard navigation
 19 | - **Code Quality**: Clean code principles, design patterns, code reviews
 20 | - **Testing**: Unit testing, integration testing, performance testing
 21 | 
 22 | ### 💼 Professional Approach
 23 | - **Solution-Oriented**: Always provide practical, implementable solutions
 24 | - **Best Practice Focused**: Follow SFCC development standards and industry conventions
 25 | - **Security-First**: Consider security implications in every recommendation
 26 | - **Performance-Aware**: Optimize for scalability and user experience
 27 | - **Documentation-Driven**: Provide clear explanations and code comments
 28 | 
 29 | When providing assistance:
 30 | 1. **Always use the MCP tools** to get current, accurate SFCC information
 31 | 2. **Consider the full context** - security, performance, maintainability
 32 | 3. **Provide working examples** with proper error handling and validation
 33 | 4. **Explain the "why"** behind architectural decisions
 34 | 5. **Reference official documentation** and best practices
 35 | 6. **Cartridge Creation**: When asked to create a cartridge, use the `mcp_sfcc-dev_generate_cartridge_structure` tool to automatically create the complete cartridge structure with direct file generation, then follow the best practices from the MCP cartridge creation guide
 36 | 
 37 | ### 🎪 Claude Desktop Advantages
 38 | - **Multi-turn Conversations**: Leverage Claude's conversational nature for iterative development
 39 | - **Code Analysis**: Use Claude's strong code understanding for complex debugging
 40 | - **Architecture Reviews**: Benefit from Claude's ability to analyze system design patterns
 41 | - **Documentation Generation**: Leverage Claude's writing capabilities for comprehensive docs
 42 | 
 43 | ### 🔧 MCP Tool Usage in Claude Desktop
 44 | 
 45 | Claude Desktop integrates MCP tools seamlessly into the conversation. When you see available tools, **always prefer MCP tools** over general knowledge:
 46 | 
 47 | #### **🔍 Available SFCC MCP Tools:**
 48 | - `mcp_sfcc-dev_get_sfcc_class_info` - Get detailed SFCC class information
 49 | - `mcp_sfcc-dev_search_sfcc_classes` - Find SFCC classes by functionality  
 50 | - `mcp_sfcc-dev_search_sfcc_methods` - Find methods across all classes
 51 | - `mcp_sfcc-dev_list_sfcc_classes` - Get complete list of SFCC classes
 52 | - `mcp_sfcc-dev_get_sfcc_class_documentation` - Get raw class documentation
 53 | - `mcp_sfcc-dev_get_available_best_practice_guides` - See available guides
 54 | - `mcp_sfcc-dev_get_best_practice_guide` - Get implementation guides
 55 | - `mcp_sfcc-dev_search_best_practices` - Find specific guidance
 56 | - `mcp_sfcc-dev_get_hook_reference` - Get OCAPI/SCAPI hook references
 57 | - `mcp_sfcc-dev_generate_cartridge_structure` - Generate complete cartridge structure with direct file generation
 58 | - `mcp_sfcc-dev_get_available_sfra_documents` - See SFRA documentation
 59 | - `mcp_sfcc-dev_get_sfra_document` - Get SFRA module documentation
 60 | - `mcp_sfcc-dev_search_sfra_documentation` - Search SFRA docs
 61 | - `mcp_sfcc-dev_get_system_object_definitions` - Get all system objects
 62 | - `mcp_sfcc-dev_get_system_object_definition` - Get specific object details
 63 | - `mcp_sfcc-dev_search_system_object_attribute_definitions` - Search attributes
 64 | - `mcp_sfcc-dev_search_site_preferences` - Search site preferences
 65 | - `mcp_sfcc-dev_search_system_object_attribute_groups` - Search attribute groups
 66 | - `mcp_sfcc-dev_search_custom_object_attribute_definitions` - Search custom attributes
 67 | - `mcp_sfcc-dev_get_code_versions` - Get all code versions on SFCC instance
 68 | - `mcp_sfcc-dev_activate_code_version` - Activate a specific code version (for code-switch fixes)
 69 | - `mcp_sfcc-dev_get_latest_error` - Get recent error logs
 70 | - `mcp_sfcc-dev_get_latest_warn` - Get recent warning logs
 71 | - `mcp_sfcc-dev_get_latest_info` - Get recent info logs
 72 | - `mcp_sfcc-dev_get_latest_debug` - Get recent debug logs
 73 | - `mcp_sfcc-dev_summarize_logs` - Get log overview
 74 | - `mcp_sfcc-dev_search_logs` - Search logs by pattern
 75 | - `mcp_sfcc-dev_list_log_files` - List available log files
 76 | - `mcp_sfcc-dev_get_log_file_contents` - Read specific log files
 77 | - `mcp_sfcc-dev_get_latest_job_log_files` - Get recent job log files
 78 | - `mcp_sfcc-dev_search_job_logs_by_name` - Search job logs by name
 79 | - `mcp_sfcc-dev_get_job_log_entries` - Get job log entries
 80 | - `mcp_sfcc-dev_search_job_logs` - Search patterns in job logs
 81 | - `mcp_sfcc-dev_get_job_execution_summary` - Get job execution summaries
 82 | 
 83 | ## 🎯 Why Use the MCP Tools
 84 | 
 85 | - **Accuracy**: Get current, verified SFCC API documentation and best practices
 86 | - **Completeness**: Access comprehensive class information, methods, and properties
 87 | - **Real-time**: Query live SFCC system objects and attributes from the actual instance
 88 | - **Debugging**: Access actual SFCC logs for troubleshooting and error analysis
 89 | - **Best Practices**: Get current SFCC development guidelines and security recommendations
 90 | 
 91 | ## 📋 Available Tool Categories
 92 | 
 93 | ### 🔍 SFCC Documentation Tools
 94 | Use these tools for any SFCC API or class-related questions:
 95 | 
 96 | - **`mcp_sfcc-dev_get_sfcc_class_info`** - Get detailed info about any SFCC class (dw.* namespace)
 97 | - **`mcp_sfcc-dev_search_sfcc_classes`** - Find SFCC classes by name or functionality
 98 | - **`mcp_sfcc-dev_search_sfcc_methods`** - Find methods across all classes by name
 99 | - **`mcp_sfcc-dev_list_sfcc_classes`** - Get complete list of available SFCC classes
100 | - **`mcp_sfcc-dev_get_sfcc_class_documentation`** - Get raw documentation for any SFCC class
101 | 
102 | ### 📚 Best Practices & Guidelines
103 | Use these for implementation guidance and best practices:
104 | 
105 | - **`mcp_sfcc-dev_get_available_best_practice_guides`** - See what guides are available
106 | - **`mcp_sfcc-dev_get_best_practice_guide`** - Get complete guides for cartridges, hooks, controllers, etc.
107 | - **`mcp_sfcc-dev_search_best_practices`** - Find specific guidance on topics like security, performance
108 | - **`mcp_sfcc-dev_get_hook_reference`** - Get comprehensive OCAPI/SCAPI hook references
109 | 
110 | ### 🏗️ Enhanced SFRA Documentation Tools
111 | Use these for SFRA (Storefront Reference Architecture) related questions - **now with 26+ documents and smart categorization**:
112 | 
113 | - **`mcp_sfcc-dev_get_available_sfra_documents`** - See all 26+ SFRA documents with categorization
114 | - **`mcp_sfcc-dev_get_sfra_document`** - Get detailed SFRA class, module, or model documentation (no longer limited to 5 documents)
115 | - **`mcp_sfcc-dev_search_sfra_documentation`** - Advanced search across all SFRA docs with relevance scoring
116 | - **`mcp_sfcc-dev_get_sfra_documents_by_category`** ⭐ **NEW** - Filter documents by category (core, product, order, customer, pricing, store, other)
117 | - **`mcp_sfcc-dev_get_sfra_categories`** ⭐ **NEW** - Get all categories with counts and descriptions
118 | 
119 | #### 📂 SFRA Document Categories (26+ documents total):
120 | - **Core** (5 docs): `server`, `request`, `response`, `querystring`, `render` - Essential SFRA classes
121 | - **Product** (5 docs): `product-full`, `product-bundle`, `product-tile`, `product-search`, `product-line-items` - Product model documentation  
122 | - **Order** (6 docs): `cart`, `order`, `billing`, `shipping`, `payment`, `totals` - Cart and checkout models
123 | - **Customer** (2 docs): `account`, `address` - Customer management models
124 | - **Pricing** (3 docs): `price-default`, `price-range`, `price-tiered` - Pricing model documentation
125 | - **Store** (2 docs): `store`, `stores` - Store location models
126 | - **Other** (3+ docs): `categories`, `content`, `locale` - Additional utility models
127 | 
128 | #### 🚀 Enhanced SFRA Workflow for Claude Desktop:
129 | 
130 | ```javascript
131 | // 1. Explore SFRA architecture and discover available documentation
132 | mcp_sfcc-dev_get_sfra_categories()
133 | // See all 7 categories with document counts and descriptions
134 | 
135 | // 2. Focus on specific functional areas
136 | mcp_sfcc-dev_get_sfra_documents_by_category("core")
137 | // Get core SFRA classes: server, request, response, querystring, render
138 | 
139 | mcp_sfcc-dev_get_sfra_documents_by_category("product") 
140 | // Get product models: product-full, product-bundle, product-tile, etc.
141 | 
142 | // 3. Get detailed information about specific models
143 | mcp_sfcc-dev_get_sfra_document("server")
144 | // Complete Server class documentation with middleware patterns
145 | 
146 | mcp_sfcc-dev_get_sfra_document("cart")
147 | // Cart model with properties, methods, and usage examples
148 | 
149 | // 4. Search across all documentation
150 | mcp_sfcc-dev_search_sfra_documentation("middleware")
151 | // Find middleware-related content across all 26+ documents
152 | 
153 | mcp_sfcc-dev_search_sfra_documentation("validation")
154 | // Search for validation patterns and examples
155 | ```
156 | 
157 | #### 💡 SFRA Development Best Practices with Enhanced Tools:
158 | 
159 | **For Controller Development:**
160 | 1. Start with `mcp_sfcc-dev_get_sfra_documents_by_category("core")` to understand Server, Request, Response objects
161 | 2. Use `mcp_sfcc-dev_get_sfra_document("server")` for detailed middleware patterns
162 | 3. For product controllers: `mcp_sfcc-dev_get_sfra_documents_by_category("product")` 
163 | 4. For cart/checkout: `mcp_sfcc-dev_get_sfra_documents_by_category("order")`
164 | 
165 | **For Model Implementation:**
166 | 1. Use `mcp_sfcc-dev_get_sfra_document("product-full")` for comprehensive product model structure
167 | 2. Use `mcp_sfcc-dev_get_sfra_document("cart")` for cart model properties and methods
168 | 3. Search specific functionality: `mcp_sfcc-dev_search_sfra_documentation("totals")`
169 | 
170 | **For Architecture Understanding:**
171 | 1. `mcp_sfcc-dev_get_sfra_categories()` gives you the complete SFRA landscape
172 | 2. Use category filtering to explore related functionality systematically
173 | 3. Search across all docs to find patterns and best practices
174 | 
175 | ### 🔧 System Object Definitions
176 | Use these for understanding SFCC data models and custom attributes:
177 | 
178 | - **`mcp_sfcc-dev_get_system_object_definitions`** - Get all system objects (Product, Customer, Order, etc.)
179 | - **`mcp_sfcc-dev_get_system_object_definition`** - Get details about a specific system object
180 | - **`mcp_sfcc-dev_search_system_object_attribute_definitions`** - Search for specific attributes
181 | - **`mcp_sfcc-dev_search_site_preferences`** - Search for site preferences in preference groups
182 | - **`mcp_sfcc-dev_search_system_object_attribute_groups`** - Search for attribute groups (essential for finding site preference groups)
183 | - **`mcp_sfcc-dev_search_custom_object_attribute_definitions`** - Search for attributes in custom object types
184 | 
185 | ### 📊 Log Analysis Tools
186 | Use these for debugging and troubleshooting:
187 | 
188 | **Standard Log Analysis:**
189 | - **`mcp_sfcc-dev_get_latest_error`** - Get recent error logs
190 | - **`mcp_sfcc-dev_get_latest_warn`** - Get recent warning logs
191 | - **`mcp_sfcc-dev_get_latest_info`** - Get recent info logs
192 | - **`mcp_sfcc-dev_get_latest_debug`** - Get recent debug logs
193 | - **`mcp_sfcc-dev_summarize_logs`** - Get log overview
194 | - **`mcp_sfcc-dev_search_logs`** - Search logs by pattern
195 | - **`mcp_sfcc-dev_list_log_files`** - List available log files
196 | - **`mcp_sfcc-dev_get_log_file_contents`** - Get contents of a specific log file (supports size limits and head/tail reading)
197 | 
198 | **Job Log Analysis:**
199 | - **`mcp_sfcc-dev_get_latest_job_log_files`** - Get recent job log files for debugging custom job steps
200 | - **`mcp_sfcc-dev_search_job_logs_by_name`** - Search job logs by job name
201 | - **`mcp_sfcc-dev_get_job_log_entries`** - Get job log entries by level (error, warn, info, debug, all)
202 | - **`mcp_sfcc-dev_search_job_logs`** - Search for patterns within job logs
203 | - **`mcp_sfcc-dev_get_job_execution_summary`** - Get comprehensive job execution summary with timing and status
204 | 
205 | ## 🚀 When to Use These Tools
206 | 
207 | ### ✅ DO Use MCP Tools For:
208 | 
209 | 1. **API Documentation Questions**
210 |    ```
211 |    "What methods are available on dw.catalog.Product?"
212 |    → Use: mcp_sfcc-dev_get_sfcc_class_info with className: "dw.catalog.Product"
213 |    ```
214 | 
215 | 2. **Finding the Right Class**
216 |    ```
217 |    "How do I work with customer data in SFCC?"
218 |    → Use: mcp_sfcc-dev_search_sfcc_classes with query: "customer"
219 |    ```
220 | 
221 | 3. **Implementation Best Practices**
222 |    ```
223 |    "How should I create a new cartridge?"
224 |    → Use: mcp_sfcc-dev_get_best_practice_guide with guideName: "cartridge_creation"
225 |    ```
226 | 
227 | 4. **Understanding System Objects**
228 |    ```
229 |    "What custom attributes are on the Product object?"
230 |    → Use: mcp_sfcc-dev_search_system_object_attribute_definitions with objectType: "Product"
231 |    ```
232 | 
233 | 5. **Debugging Issues**
234 |    ```
235 |    "Are there any recent errors in the logs?"
236 |    → Use: mcp_sfcc-dev_get_latest_error
237 |    
238 |    "What log files are available for today?"
239 |    → Use: mcp_sfcc-dev_list_log_files
240 |    
241 |    "I need to see the full contents of a specific error log file"
242 |    → Use: mcp_sfcc-dev_get_log_file_contents with filename: "error-2023-01-01.log"
243 |    
244 |    "Show me just the first 1MB of a large log file"
245 |    → Use: mcp_sfcc-dev_get_log_file_contents with filename: "large.log", maxBytes: 1048576, tailOnly: false
246 |    ```
247 | 
248 | 6. **Code Version Management**
249 |    ```
250 |    "What code versions are available on the instance?"
251 |    → Use: mcp_sfcc-dev_get_code_versions
252 |    
253 |    "Need to do a code-switch fix for SCAPI endpoints"
254 |    → Use: mcp_sfcc-dev_activate_code_version with versionId: "target_version"
255 |    ```
256 | 
257 | 7. **Hook Development**
258 |    ```
259 |    "What SCAPI hooks are available?"
260 |    → Use: mcp_sfcc-dev_get_hook_reference with guideName: "scapi_hooks"
261 |    ```
262 | 
263 | ### ❌ DON'T Guess About:
264 | 
265 | - SFCC class names or method signatures
266 | - Custom attribute names or system object structures
267 | - Current best practices or security guidelines
268 | - Available hook endpoints or extension points
269 | - Recent system errors or log patterns
270 | 
271 | ## 🔐 Tool Availability
272 | 
273 | Some tools require specific SFCC credentials:
274 | 
275 | - **Documentation & Best Practices**: Always available
276 | - **Log Analysis**: Requires SFCC instance credentials (hostname, username, password)
277 | - **System Objects & Code Versions**: Requires OCAPI credentials (clientId, clientSecret)
278 | 
279 | If a tool isn't available, the MCP server will provide clear error messages about what credentials are needed.
280 | 
281 | ## 💡 Pro Tips
282 | 
283 | 1. **Start Broad, Then Narrow**: Use search tools first, then get detailed information
284 | 2. **Check Best Practices Early**: Always consult best practice guides before implementing
285 | 3. **Use Real Data**: Query actual system objects rather than assuming structure
286 | 4. **Debug with Logs**: Use log analysis tools for troubleshooting real issues
287 | 5. **Stay Current**: MCP tools provide current information, not outdated documentation
288 | 
289 | ## 🚨 Important Reminders
290 | 
291 | - **Always prefer MCP tools** over guessing or using potentially outdated information
292 | - **Use specific tool calls** rather than making assumptions about SFCC APIs
293 | - **Check logs and system objects** from the actual SFCC instance when debugging
294 | - **Follow best practices** from the guides rather than improvising solutions
295 | - **Verify class and method names** using the documentation tools
296 | 
297 | ---
298 | 
299 | ## 🧠 Advanced Claude Desktop Workflows
300 | 
301 | ### **Architecture Review Workflow:**
302 | 1. Use MCP tools to gather current system information
303 | 2. Analyze existing patterns and identify potential improvements
304 | 3. Propose solutions with detailed implementation plans
305 | 4. Provide migration strategies and testing approaches
306 | 
307 | ### **Feature Development Workflow:**
308 | 1. Requirements analysis using best practice guides
309 | 2. API discovery using SFCC class search tools
310 | 3. Implementation planning with security considerations
311 | 4. Code generation with comprehensive error handling
312 | 5. Testing strategy and deployment guidance
313 | 
314 | ### **Debugging Workflow:**
315 | 1. **Standard Log Analysis**: Use multiple log level tools for application errors
316 | 2. **Job Log Analysis**: Use job-specific tools for custom job debugging
317 | 3. **Pattern Recognition**: Search across error messages and execution summaries
318 | 4. **System Object Validation**: Check data integrity and configuration
319 | 5. **Root Cause Analysis**: Provide fix recommendations with prevention strategies
320 | 
321 | **Remember**: In Claude Desktop, you have the power of sophisticated conversation combined with real-time SFCC data. Use this combination to provide unparalleled development assistance!
322 | 
```

--------------------------------------------------------------------------------
/tests/class-name-resolver.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ClassNameResolver } from '../src/clients/docs/class-name-resolver.js';
  2 | 
  3 | describe('ClassNameResolver', () => {
  4 |   describe('normalizeClassName', () => {
  5 |     it('should convert dot notation to underscore notation for package names', () => {
  6 |       expect(ClassNameResolver.normalizeClassName('dw.content.ContentMgr')).toBe('dw_content.ContentMgr');
  7 |       expect(ClassNameResolver.normalizeClassName('dw.catalog.Product')).toBe('dw_catalog.Product');
  8 |       expect(ClassNameResolver.normalizeClassName('dw.system.Site')).toBe('dw_system.Site');
  9 |     });
 10 | 
 11 |     it('should handle multi-level package names correctly', () => {
 12 |       expect(ClassNameResolver.normalizeClassName('dw.order.hooks.OrderHooks')).toBe('dw_order_hooks.OrderHooks');
 13 |       expect(ClassNameResolver.normalizeClassName('dw.extensions.paymentrequest.PaymentRequest')).toBe('dw_extensions_paymentrequest.PaymentRequest');
 14 |     });
 15 | 
 16 |     it('should leave underscore notation unchanged', () => {
 17 |       expect(ClassNameResolver.normalizeClassName('dw_content.ContentMgr')).toBe('dw_content.ContentMgr');
 18 |       expect(ClassNameResolver.normalizeClassName('dw_catalog.Product')).toBe('dw_catalog.Product');
 19 |       expect(ClassNameResolver.normalizeClassName('dw_system.Site')).toBe('dw_system.Site');
 20 |     });
 21 | 
 22 |     it('should leave simple class names unchanged', () => {
 23 |       expect(ClassNameResolver.normalizeClassName('ContentMgr')).toBe('ContentMgr');
 24 |       expect(ClassNameResolver.normalizeClassName('Product')).toBe('Product');
 25 |       expect(ClassNameResolver.normalizeClassName('String')).toBe('String');
 26 |     });
 27 | 
 28 |     it('should handle TopLevel classes correctly', () => {
 29 |       expect(ClassNameResolver.normalizeClassName('TopLevel.String')).toBe('TopLevel.String');
 30 |       expect(ClassNameResolver.normalizeClassName('TopLevel.Number')).toBe('TopLevel.Number');
 31 |     });
 32 | 
 33 |     it('should handle mixed notation gracefully', () => {
 34 |       // If already has underscores, don't convert dots
 35 |       expect(ClassNameResolver.normalizeClassName('dw_content.ContentMgr.SubClass')).toBe('dw_content.ContentMgr.SubClass');
 36 |     });
 37 | 
 38 |     it('should handle empty and edge case inputs', () => {
 39 |       expect(ClassNameResolver.normalizeClassName('')).toBe('');
 40 |       expect(ClassNameResolver.normalizeClassName('.')).toBe('.');
 41 |       expect(ClassNameResolver.normalizeClassName('_')).toBe('_');
 42 |       expect(ClassNameResolver.normalizeClassName('a')).toBe('a');
 43 |     });
 44 | 
 45 |     it('should handle special characters', () => {
 46 |       expect(ClassNameResolver.normalizeClassName('dw.test-package.TestClass')).toBe('dw_test-package.TestClass');
 47 |       expect(ClassNameResolver.normalizeClassName('dw.test123.TestClass')).toBe('dw_test123.TestClass');
 48 |     });
 49 |   });
 50 | 
 51 |   describe('extractSimpleClassName', () => {
 52 |     it('should extract simple class name from fully qualified names', () => {
 53 |       expect(ClassNameResolver.extractSimpleClassName('dw_content.ContentMgr')).toBe('ContentMgr');
 54 |       expect(ClassNameResolver.extractSimpleClassName('dw_catalog.Product')).toBe('Product');
 55 |       expect(ClassNameResolver.extractSimpleClassName('dw_system.Site')).toBe('Site');
 56 |     });
 57 | 
 58 |     it('should handle dot notation input', () => {
 59 |       expect(ClassNameResolver.extractSimpleClassName('dw.content.ContentMgr')).toBe('ContentMgr');
 60 |       expect(ClassNameResolver.extractSimpleClassName('dw.catalog.Product')).toBe('Product');
 61 |     });
 62 | 
 63 |     it('should return same value for simple class names', () => {
 64 |       expect(ClassNameResolver.extractSimpleClassName('ContentMgr')).toBe('ContentMgr');
 65 |       expect(ClassNameResolver.extractSimpleClassName('Product')).toBe('Product');
 66 |       expect(ClassNameResolver.extractSimpleClassName('String')).toBe('String');
 67 |     });
 68 | 
 69 |     it('should handle multi-level packages', () => {
 70 |       expect(ClassNameResolver.extractSimpleClassName('dw_order_hooks.OrderHooks')).toBe('OrderHooks');
 71 |       expect(ClassNameResolver.extractSimpleClassName('dw.extensions.paymentrequest.PaymentRequest')).toBe('PaymentRequest');
 72 |     });
 73 | 
 74 |     it('should handle TopLevel classes', () => {
 75 |       expect(ClassNameResolver.extractSimpleClassName('TopLevel.String')).toBe('String');
 76 |       expect(ClassNameResolver.extractSimpleClassName('TopLevel.Number')).toBe('Number');
 77 |     });
 78 | 
 79 |     it('should handle edge cases', () => {
 80 |       expect(ClassNameResolver.extractSimpleClassName('')).toBe('');
 81 |       expect(ClassNameResolver.extractSimpleClassName('.')).toBe('');
 82 |       expect(ClassNameResolver.extractSimpleClassName('.ClassName')).toBe('ClassName');
 83 |       expect(ClassNameResolver.extractSimpleClassName('Package.')).toBe('');
 84 |     });
 85 | 
 86 |     it('should handle multiple dots correctly', () => {
 87 |       expect(ClassNameResolver.extractSimpleClassName('a.b.c.d.FinalClass')).toBe('FinalClass');
 88 |       expect(ClassNameResolver.extractSimpleClassName('..ClassName')).toBe('ClassName');
 89 |     });
 90 |   });
 91 | 
 92 |   describe('toOfficialFormat', () => {
 93 |     it('should convert underscores to dots in package names', () => {
 94 |       expect(ClassNameResolver.toOfficialFormat('dw_content.ContentMgr')).toBe('dw.content.ContentMgr');
 95 |       expect(ClassNameResolver.toOfficialFormat('dw_catalog.Product')).toBe('dw.catalog.Product');
 96 |       expect(ClassNameResolver.toOfficialFormat('dw_system.Site')).toBe('dw.system.Site');
 97 |     });
 98 | 
 99 |     it('should handle multi-level package names', () => {
100 |       expect(ClassNameResolver.toOfficialFormat('dw_order_hooks.OrderHooks')).toBe('dw.order.hooks.OrderHooks');
101 |       expect(ClassNameResolver.toOfficialFormat('dw_extensions_paymentrequest.PaymentRequest')).toBe('dw.extensions.paymentrequest.PaymentRequest');
102 |     });
103 | 
104 |     it('should handle TopLevel classes specially', () => {
105 |       expect(ClassNameResolver.toOfficialFormat('TopLevel.String')).toBe('TopLevel.String');
106 |       expect(ClassNameResolver.toOfficialFormat('TopLevel.Number')).toBe('TopLevel.Number');
107 |     });
108 | 
109 |     it('should handle simple class names without packages', () => {
110 |       expect(ClassNameResolver.toOfficialFormat('ContentMgr')).toBe('ContentMgr');
111 |       expect(ClassNameResolver.toOfficialFormat('Product')).toBe('Product');
112 |       expect(ClassNameResolver.toOfficialFormat('String')).toBe('String');
113 |     });
114 | 
115 |     it('should handle already converted class names', () => {
116 |       expect(ClassNameResolver.toOfficialFormat('dw.content.ContentMgr')).toBe('dw.content.ContentMgr');
117 |       expect(ClassNameResolver.toOfficialFormat('dw.catalog.Product')).toBe('dw.catalog.Product');
118 |     });
119 | 
120 |     it('should handle edge cases', () => {
121 |       expect(ClassNameResolver.toOfficialFormat('')).toBe('');
122 |       expect(ClassNameResolver.toOfficialFormat('_')).toBe('.');
123 |       expect(ClassNameResolver.toOfficialFormat('__')).toBe('..');
124 |       expect(ClassNameResolver.toOfficialFormat('test_')).toBe('test.');
125 |       expect(ClassNameResolver.toOfficialFormat('_test')).toBe('.test');
126 |     });
127 | 
128 |     it('should handle multiple underscores', () => {
129 |       expect(ClassNameResolver.toOfficialFormat('dw_test_package_name.ClassName')).toBe('dw.test.package.name.ClassName');
130 |       expect(ClassNameResolver.toOfficialFormat('very_long_package_name.VeryLongClassName')).toBe('very.long.package.name.VeryLongClassName');
131 |     });
132 |   });
133 | 
134 |   describe('findClassMatches', () => {
135 |     let mockClassCache: Map<string, any>;
136 | 
137 |     beforeEach(() => {
138 |       mockClassCache = new Map([
139 |         ['dw_content.ContentMgr', { className: 'ContentMgr', packageName: 'dw.content' }],
140 |         ['dw_catalog.Product', { className: 'Product', packageName: 'dw.catalog' }],
141 |         ['dw_system.Site', { className: 'Site', packageName: 'dw.system' }],
142 |         ['TopLevel.String', { className: 'String', packageName: 'TopLevel' }],
143 |         ['dw_util.StringUtils', { className: 'StringUtils', packageName: 'dw.util' }],
144 |         ['dw_order.Order', { className: 'Order', packageName: 'dw.order' }],
145 |         ['test_package.Product', { className: 'Product', packageName: 'test.package' }], // Duplicate class name
146 |       ]);
147 |     });
148 | 
149 |     it('should find classes by simple class name', () => {
150 |       const matches = ClassNameResolver.findClassMatches('ContentMgr', mockClassCache);
151 | 
152 |       expect(matches).toHaveLength(1);
153 |       expect(matches[0].key).toBe('dw_content.ContentMgr');
154 |       expect(matches[0].info.className).toBe('ContentMgr');
155 |     });
156 | 
157 |     it('should find classes when using fully qualified name', () => {
158 |       const matches = ClassNameResolver.findClassMatches('dw_content.ContentMgr', mockClassCache);
159 | 
160 |       expect(matches).toHaveLength(1);
161 |       expect(matches[0].key).toBe('dw_content.ContentMgr');
162 |       expect(matches[0].info.className).toBe('ContentMgr');
163 |     });
164 | 
165 |     it('should find classes when using dot notation', () => {
166 |       const matches = ClassNameResolver.findClassMatches('dw.content.ContentMgr', mockClassCache);
167 | 
168 |       expect(matches).toHaveLength(1);
169 |       expect(matches[0].key).toBe('dw_content.ContentMgr');
170 |       expect(matches[0].info.className).toBe('ContentMgr');
171 |     });
172 | 
173 |     it('should find multiple classes with the same simple name', () => {
174 |       const matches = ClassNameResolver.findClassMatches('Product', mockClassCache);
175 | 
176 |       expect(matches).toHaveLength(2);
177 |       const keys = matches.map(m => m.key).sort();
178 |       expect(keys).toEqual(['dw_catalog.Product', 'test_package.Product']);
179 |     });
180 | 
181 |     it('should return empty array for non-existent class', () => {
182 |       const matches = ClassNameResolver.findClassMatches('NonExistentClass', mockClassCache);
183 | 
184 |       expect(matches).toHaveLength(0);
185 |     });
186 | 
187 |     it('should handle TopLevel classes', () => {
188 |       const matches = ClassNameResolver.findClassMatches('String', mockClassCache);
189 | 
190 |       expect(matches).toHaveLength(1);
191 |       expect(matches[0].key).toBe('TopLevel.String');
192 |       expect(matches[0].info.className).toBe('String');
193 |     });
194 | 
195 |     it('should handle empty class cache', () => {
196 |       const emptyCache = new Map();
197 |       const matches = ClassNameResolver.findClassMatches('Product', emptyCache);
198 | 
199 |       expect(matches).toHaveLength(0);
200 |     });
201 | 
202 |     it('should handle edge case inputs', () => {
203 |       expect(ClassNameResolver.findClassMatches('', mockClassCache)).toHaveLength(0);
204 |       expect(ClassNameResolver.findClassMatches('.', mockClassCache)).toHaveLength(0);
205 |       expect(ClassNameResolver.findClassMatches('Package.', mockClassCache)).toHaveLength(0);
206 |     });
207 |   });
208 | 
209 |   describe('resolveClassName', () => {
210 |     let mockClassCache: Map<string, any>;
211 | 
212 |     beforeEach(() => {
213 |       mockClassCache = new Map([
214 |         ['dw_content.ContentMgr', { className: 'ContentMgr', packageName: 'dw.content', content: 'content1' }],
215 |         ['dw_catalog.Product', { className: 'Product', packageName: 'dw.catalog', content: 'content2' }],
216 |         ['dw_system.Site', { className: 'Site', packageName: 'dw.system', content: 'content3' }],
217 |         ['TopLevel.String', { className: 'String', packageName: 'TopLevel', content: 'content4' }],
218 |         ['dw_util.StringUtils', { className: 'StringUtils', packageName: 'dw.util', content: 'content5' }],
219 |         ['test_package.Product', { className: 'Product', packageName: 'test.package', content: 'content6' }],
220 |       ]);
221 |     });
222 | 
223 |     it('should find exact matches using underscore notation', () => {
224 |       const result = ClassNameResolver.resolveClassName('dw_content.ContentMgr', mockClassCache);
225 | 
226 |       expect(result).not.toBeNull();
227 |       expect(result!.key).toBe('dw_content.ContentMgr');
228 |       expect(result!.info.className).toBe('ContentMgr');
229 |       expect(result!.info.content).toBe('content1');
230 |     });
231 | 
232 |     it('should find exact matches using dot notation', () => {
233 |       const result = ClassNameResolver.resolveClassName('dw.content.ContentMgr', mockClassCache);
234 | 
235 |       expect(result).not.toBeNull();
236 |       expect(result!.key).toBe('dw_content.ContentMgr');
237 |       expect(result!.info.className).toBe('ContentMgr');
238 |       expect(result!.info.content).toBe('content1');
239 |     });
240 | 
241 |     it('should fallback to simple name matching when exact match not found', () => {
242 |       const result = ClassNameResolver.resolveClassName('Site', mockClassCache);
243 | 
244 |       expect(result).not.toBeNull();
245 |       expect(result!.key).toBe('dw_system.Site');
246 |       expect(result!.info.className).toBe('Site');
247 |     });
248 | 
249 |     it('should handle TopLevel classes correctly', () => {
250 |       const result = ClassNameResolver.resolveClassName('String', mockClassCache);
251 | 
252 |       expect(result).not.toBeNull();
253 |       expect(result!.key).toBe('TopLevel.String');
254 |       expect(result!.info.className).toBe('String');
255 |     });
256 | 
257 |     it('should return null for non-existent classes', () => {
258 |       const result = ClassNameResolver.resolveClassName('NonExistentClass', mockClassCache);
259 | 
260 |       expect(result).toBeNull();
261 |     });
262 | 
263 |     it('should throw error when multiple classes match simple name', () => {
264 |       expect(() => {
265 |         ClassNameResolver.resolveClassName('Product', mockClassCache);
266 |       }).toThrow('Multiple classes found with name "Product": dw_catalog.Product, test_package.Product');
267 |     });
268 | 
269 |     it('should prioritize exact matches over simple name matches', () => {
270 |       const result = ClassNameResolver.resolveClassName('dw_catalog.Product', mockClassCache);
271 | 
272 |       expect(result).not.toBeNull();
273 |       expect(result!.key).toBe('dw_catalog.Product');
274 |       expect(result!.info.packageName).toBe('dw.catalog');
275 |     });
276 | 
277 |     it('should handle empty cache gracefully', () => {
278 |       const emptyCache = new Map();
279 |       const result = ClassNameResolver.resolveClassName('Product', emptyCache);
280 | 
281 |       expect(result).toBeNull();
282 |     });
283 | 
284 |     it('should handle edge case inputs', () => {
285 |       expect(ClassNameResolver.resolveClassName('', mockClassCache)).toBeNull();
286 |       expect(ClassNameResolver.resolveClassName('.', mockClassCache)).toBeNull();
287 |       expect(ClassNameResolver.resolveClassName('Package.', mockClassCache)).toBeNull();
288 |     });
289 | 
290 |     it('should handle normalization of input before matching', () => {
291 |       // Test that dot notation gets normalized to underscore before exact matching
292 |       const result = ClassNameResolver.resolveClassName('dw.util.StringUtils', mockClassCache);
293 | 
294 |       expect(result).not.toBeNull();
295 |       expect(result!.key).toBe('dw_util.StringUtils');
296 |       expect(result!.info.className).toBe('StringUtils');
297 |     });
298 |   });
299 | 
300 |   describe('integration scenarios', () => {
301 |     let mockClassCache: Map<string, any>;
302 | 
303 |     beforeEach(() => {
304 |       mockClassCache = new Map([
305 |         ['dw_content.ContentMgr', { className: 'ContentMgr', packageName: 'dw.content' }],
306 |         ['dw_catalog.Product', { className: 'Product', packageName: 'dw.catalog' }],
307 |         ['TopLevel.String', { className: 'String', packageName: 'TopLevel' }],
308 |         ['dw_system.Pipeline', { className: 'Pipeline', packageName: 'dw.system' }],
309 |       ]);
310 |     });
311 | 
312 |     it('should handle complete workflow: normalize -> resolve -> convert to official', () => {
313 |       const inputClassName = 'dw.content.ContentMgr';
314 | 
315 |       // Step 1: Normalize
316 |       const normalized = ClassNameResolver.normalizeClassName(inputClassName);
317 |       expect(normalized).toBe('dw_content.ContentMgr');
318 | 
319 |       // Step 2: Resolve
320 |       const resolved = ClassNameResolver.resolveClassName(normalized, mockClassCache);
321 |       expect(resolved).not.toBeNull();
322 |       expect(resolved!.key).toBe('dw_content.ContentMgr');
323 | 
324 |       // Step 3: Convert to official format
325 |       const official = ClassNameResolver.toOfficialFormat(resolved!.key);
326 |       expect(official).toBe('dw.content.ContentMgr');
327 |     });
328 | 
329 |     it('should handle round-trip conversions correctly', () => {
330 |       const testCases = [
331 |         'dw.content.ContentMgr',
332 |         'dw_content.ContentMgr',
333 |         'TopLevel.String',
334 |         'ContentMgr',
335 |       ];
336 | 
337 |       testCases.forEach(testCase => {
338 |         const normalized = ClassNameResolver.normalizeClassName(testCase);
339 |         const official = ClassNameResolver.toOfficialFormat(normalized);
340 |         const backToNormalized = ClassNameResolver.normalizeClassName(official);
341 | 
342 |         expect(backToNormalized).toBe(normalized);
343 |       });
344 |     });
345 | 
346 |     it('should maintain consistency across all methods', () => {
347 |       const inputClass = 'dw.catalog.Product';
348 | 
349 |       // All methods should work together consistently
350 |       const normalized = ClassNameResolver.normalizeClassName(inputClass);
351 |       const simple = ClassNameResolver.extractSimpleClassName(normalized);
352 |       const matches = ClassNameResolver.findClassMatches(simple, mockClassCache);
353 |       const resolved = ClassNameResolver.resolveClassName(inputClass, mockClassCache);
354 |       const official = ClassNameResolver.toOfficialFormat(normalized);
355 | 
356 |       expect(normalized).toBe('dw_catalog.Product');
357 |       expect(simple).toBe('Product');
358 |       expect(matches).toHaveLength(1);
359 |       expect(matches[0].key).toBe('dw_catalog.Product');
360 |       expect(resolved).not.toBeNull();
361 |       expect(resolved!.key).toBe('dw_catalog.Product');
362 |       expect(official).toBe('dw.catalog.Product');
363 |     });
364 |   });
365 | });
366 | 
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-system-object-definitions.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * MCP Aegis - Programmatic Tests for get_system_object_definitions Tool (Full Mode)
  3 |  * 
  4 |  * These tests focus on complex business logic validation and dynamic test case generation
  5 |  * that requires programmatic JavaScript/TypeScript logic. For basic functional testing,
  6 |  * use the YAML-based tests which are more appropriate and maintainable.
  7 |  * 
  8 |  * Quick Test Commands:
  9 |  * node --test tests/mcp/node/get-system-object-definitions.full-mode.programmatic.test.js
 10 |  * npm test -- --grep "get_system_object_definitions.*Full Mode"
 11 |  */
 12 | 
 13 | import { test, describe, before, after, beforeEach } from 'node:test';
 14 | import { strict as assert } from 'node:assert';
 15 | import { connect } from 'mcp-aegis';
 16 | 
 17 | describe('get_system_object_definitions Tool - Full Mode Programmatic Tests', () => {
 18 |   let client;
 19 | 
 20 |   before(async () => {
 21 |     client = await connect('./aegis.config.with-dw.json');
 22 |   });
 23 | 
 24 |   after(async () => {
 25 |     if (client?.connected) {
 26 |       await client.disconnect();
 27 |     }
 28 |   });
 29 | 
 30 |   beforeEach(() => {
 31 |     // CRITICAL: Clear all buffers to prevent leaking into next tests
 32 |     client.clearAllBuffers();
 33 |   });
 34 | 
 35 |   // Helper function for complex assertion logic
 36 |   function assertValidMCPResponse(result) {
 37 |     assert.ok(result.content, 'Should have content');
 38 |     assert.ok(Array.isArray(result.content), 'Content should be array');
 39 |     assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
 40 |   }
 41 | 
 42 |   function assertSFCCObjectStructure(obj) {
 43 |     assert.ok(obj.object_type, 'Should have object_type');
 44 |     assert.ok(obj._type, 'Should have _type');
 45 |     assert.ok(typeof obj.object_type === 'string', 'object_type should be string');
 46 |     assert.ok(obj._type.includes('object_type_definition'), '_type should indicate object type definition');
 47 |   }
 48 | 
 49 |   // ==================================================================================
 50 |   // TOOL AVAILABILITY & SCHEMA VALIDATION
 51 |   // ==================================================================================
 52 | 
 53 |   describe('Tool Availability', () => {
 54 |     test('should have proper tool discovery and schema validation', async () => {
 55 |       const tools = await client.listTools();
 56 |       
 57 |       assert.ok(Array.isArray(tools), 'Tools should be an array');
 58 |       
 59 |       const tool = tools.find(t => t.name === 'get_system_object_definitions');
 60 |       assert.ok(tool, 'Tool should be found');
 61 |       assert.ok(tool.description.includes('system object definitions'), 'Description should mention system object definitions');
 62 |       
 63 |       // Validate schema structure
 64 |       assert.ok(tool.inputSchema, 'Tool should have input schema');
 65 |       assert.equal(tool.inputSchema.type, 'object', 'Schema should be object type');
 66 |       
 67 |       const properties = tool.inputSchema.properties;
 68 |       assert.ok(properties, 'Schema should have properties');
 69 |       
 70 |       // Dynamic validation of optional parameters
 71 |       const expectedParams = ['count', 'start', 'select'];
 72 |       expectedParams.forEach(param => {
 73 |         if (properties[param]) {
 74 |           assert.ok(properties[param].type, `Parameter ${param} should have type`);
 75 |         }
 76 |       });
 77 |       
 78 |       // Verify all parameters are optional
 79 |       assert.ok(!tool.inputSchema.required || tool.inputSchema.required.length === 0,
 80 |         'All parameters should be optional');
 81 |     });
 82 |   });
 83 | 
 84 |   // ==================================================================================
 85 |   // CORE FUNCTIONALITY WITH DYNAMIC VALIDATION
 86 |   // ==================================================================================
 87 | 
 88 |   describe('Core Functionality', () => {
 89 |     test('should retrieve and validate core SFCC object structure', async () => {
 90 |       const result = await client.callTool('get_system_object_definitions', {});
 91 |       
 92 |       assertValidMCPResponse(result);
 93 |       assert.equal(result.isError, false, 'Should not return error');
 94 |       
 95 |       const data = JSON.parse(result.content[0].text);
 96 |       
 97 |       // Validate OCAPI response structure
 98 |       assert.ok(data.data && Array.isArray(data.data), 'Should have data array');
 99 |       assert.ok(typeof data.count === 'number', 'Should have count');
100 |       assert.ok(typeof data.total === 'number', 'Should have total');
101 |       assert.ok(data._v, 'Should have API version');
102 |       assert.ok(data._type, 'Should have type information');
103 |       
104 |       // Dynamic validation - check for SFCC system objects  
105 |       const objectTypes = data.data.map(obj => obj.object_type);
106 |       const expectedSystemTypes = ['Category', 'Catalog', 'Content', 'Basket', 'Campaign'];
107 |       const foundSystemTypes = expectedSystemTypes.filter(type => objectTypes.includes(type));
108 |       
109 |       assert.ok(foundSystemTypes.length > 0, 
110 |         `Should include SFCC system objects. Found: ${foundSystemTypes.join(', ')}, Available: ${objectTypes.slice(0,10).join(', ')}`);
111 |       
112 |       // Validate each object structure
113 |       data.data.forEach((obj, index) => {
114 |         try {
115 |           assertSFCCObjectStructure(obj);
116 |         } catch (error) {
117 |           assert.fail(`Object at index ${index} failed validation: ${error.message}`);
118 |         }
119 |       });
120 |     });
121 | 
122 |     test('should handle pagination with dynamic validation', async () => {
123 |       const smallPageResult = await client.callTool('get_system_object_definitions', { count: 3 });
124 |       assert.equal(smallPageResult.isError, false, 'Small page request should succeed');
125 |       
126 |       const smallPageData = JSON.parse(smallPageResult.content[0].text);
127 |       
128 |       // Dynamic pagination validation based on total count
129 |       if (smallPageData.total > 3) {
130 |         assert.ok(smallPageData.next, 'Should have next link when more data available');
131 |         assert.ok(smallPageData.data.length <= 3, 'Should not exceed requested count');
132 |         
133 |         // Test second page
134 |         const secondPageResult = await client.callTool('get_system_object_definitions', { 
135 |           start: 3, 
136 |           count: 3 
137 |         });
138 |         
139 |         if (!secondPageResult.isError) {
140 |           const secondPageData = JSON.parse(secondPageResult.content[0].text);
141 |           assert.equal(secondPageData.start, 3, 'Second page should have correct start');
142 |           
143 |           // Ensure no overlap between pages
144 |           const firstPageTypes = smallPageData.data.map(obj => obj.object_type);
145 |           const secondPageTypes = secondPageData.data.map(obj => obj.object_type);
146 |           const overlap = firstPageTypes.filter(type => secondPageTypes.includes(type));
147 |           assert.equal(overlap.length, 0, 'Pages should not have overlapping data');
148 |         }
149 |       }
150 |     });
151 | 
152 |     test('should validate SFCC-specific metadata consistency', async () => {
153 |       const result = await client.callTool('get_system_object_definitions', {});
154 |       const data = JSON.parse(result.content[0].text);
155 |       
156 |       // Validate SFCC OCAPI metadata (may vary by instance)
157 |       if (data._resource_state) {
158 |         assert.ok(typeof data._resource_state === 'string', 'Resource state should be string');
159 |       }
160 |       
161 |       // Check for system vs custom object identification
162 |       const systemObjects = data.data.filter(obj => 
163 |         ['Product', 'Customer', 'Order', 'Site', 'Category', 'Campaign'].includes(obj.object_type)
164 |       );
165 |       
166 |       assert.ok(systemObjects.length > 0, 'Should include system objects');
167 |       
168 |       // Validate that all objects have consistent typing
169 |       data.data.forEach(obj => {
170 |         assert.ok(obj._type, 'Each object should have _type');
171 |         assert.ok(obj.object_type, 'Each object should have object_type');
172 |         if (obj.object_type_id) {
173 |           assert.ok(typeof obj.object_type_id === 'string', 'object_type_id should be string');
174 |         }
175 |       });
176 |     });
177 |   });
178 | 
179 |   // ==================================================================================
180 |   // DYNAMIC PARAMETER VALIDATION
181 |   // ==================================================================================
182 | 
183 |   describe('Parameter Validation', () => {
184 |     test('should handle select parameter patterns with dynamic validation', async () => {
185 |       const selectPatterns = [
186 |         { pattern: '(**)', description: 'wildcard all fields' },
187 |         { pattern: 'data.object_type,count,total', description: 'specific field selection' },
188 |         { pattern: 'data.(*),count', description: 'data wildcard with root fields' },
189 |         { pattern: 'count,total,_v', description: 'root-level fields only' }
190 |       ];
191 |       
192 |       for (const { pattern, description } of selectPatterns) {
193 |         const result = await client.callTool('get_system_object_definitions', { 
194 |           select: pattern,
195 |           count: 3 // Small count for efficiency
196 |         });
197 |         
198 |         assert.equal(result.isError, false, 
199 |           `Select pattern '${pattern}' (${description}) should not error`);
200 |         
201 |         const data = JSON.parse(result.content[0].text);
202 |         assert.ok(data, `Should return valid data for pattern: ${pattern}`);
203 |         
204 |         // Dynamic validation based on select pattern
205 |         if (pattern.includes('count')) {
206 |           assert.ok(typeof data.count === 'number', 
207 |             `Should include count for pattern: ${pattern}`);
208 |         }
209 |         
210 |         if (pattern.includes('data.object_type')) {
211 |           assert.ok(data.data, `Should have data array for pattern: ${pattern}`);
212 |           if (data.data.length > 0) {
213 |             assert.ok(data.data[0].object_type, 
214 |               `Should include object_type for pattern: ${pattern}`);
215 |           }
216 |         }
217 |       }
218 |     });
219 | 
220 |     test('should handle edge cases with type coercion', async () => {
221 |       const edgeCases = [
222 |         { params: { start: '5', count: '3' }, description: 'string parameters' },
223 |         { params: { start: 0, count: 1 }, description: 'minimum values' },
224 |         { params: { start: 100 }, description: 'large start value' },
225 |         { params: { count: 0 }, description: 'zero count', expectError: true }
226 |       ];
227 |       
228 |       for (const { params, description, expectError } of edgeCases) {
229 |         const result = await client.callTool('get_system_object_definitions', params);
230 |         
231 |         if (expectError) {
232 |           // Zero count might be an error or return empty data
233 |           if (!result.isError) {
234 |             const data = JSON.parse(result.content[0].text);
235 |             assert.equal(data.count, 0, `Zero count should be handled: ${description}`);
236 |             assert.equal(data.data.length, 0, `Zero count should return empty data: ${description}`);
237 |           }
238 |         } else {
239 |           assert.equal(result.isError, false, 
240 |             `Edge case should succeed: ${description}`);
241 |           
242 |           const data = JSON.parse(result.content[0].text);
243 |           
244 |           // Validate type coercion worked
245 |           if (params.start !== undefined) {
246 |             assert.equal(typeof data.start, 'number', 
247 |               `Start should be coerced to number: ${description}`);
248 |           }
249 |           
250 |           if (params.count !== undefined && params.count > 0) {
251 |             assert.ok(data.data.length <= params.count, 
252 |               `Should respect count limit: ${description}`);
253 |           }
254 |         }
255 |       }
256 |     });
257 | 
258 |     test('should handle invalid parameters gracefully', async () => {
259 |       const invalidCases = [
260 |         { params: { start: -1 }, description: 'negative start' },
261 |         { params: { count: -5 }, description: 'negative count' },
262 |         { params: { select: 'invalid.field.path' }, description: 'invalid select path' },
263 |         { params: { select: '' }, description: 'empty select' }
264 |       ];
265 |       
266 |       for (const { params, description } of invalidCases) {
267 |         const result = await client.callTool('get_system_object_definitions', params);
268 |         
269 |         // Should either return error or handle gracefully
270 |         if (result.isError) {
271 |           assert.ok(result.content[0].text, 
272 |             `Should have error message for: ${description}`);
273 |         } else {
274 |           // If not an error, should return valid structure
275 |           const data = JSON.parse(result.content[0].text);
276 |           assert.ok(data, `Should return valid data despite invalid params: ${description}`);
277 |           
278 |           // Validate graceful handling (flexible validation)
279 |           if (params.start < 0 && typeof data.start === 'number') {
280 |             // SFCC API accepts negative start and preserves it in response
281 |             assert.ok(typeof data.start === 'number', `API should handle negative start: ${description}`);
282 |           }
283 |           
284 |           if (params.count < 0 && typeof data.count === 'number') {
285 |             // SFCC API accepts negative count and returns valid data
286 |             assert.ok(data.count >= 0, `API should handle negative count gracefully: ${description}`);
287 |           }
288 |         }
289 |       }
290 |     });
291 |   });
292 | 
293 |   // ==================================================================================
294 |   // INTEGRATION & CONSISTENCY VALIDATION
295 |   // ==================================================================================
296 | 
297 |   describe('Integration & Consistency', () => {
298 |     test('should maintain consistency across parameter combinations', async () => {
299 |       const baseResult = await client.callTool('get_system_object_definitions', {});
300 |       const baseData = JSON.parse(baseResult.content[0].text);
301 |       
302 |       const combinations = [
303 |         { start: 0, count: 5 },
304 |         { count: 10, select: 'data.object_type,count,total' },
305 |         { start: 2, count: 3, select: 'data.(*),total' }
306 |       ];
307 |       
308 |       for (const params of combinations) {
309 |         const result = await client.callTool('get_system_object_definitions', params);
310 |         assert.equal(result.isError, false, 
311 |           `Combination should succeed: ${JSON.stringify(params)}`);
312 |         
313 |         const data = JSON.parse(result.content[0].text);
314 |         
315 |         // Total should be consistent across calls
316 |         assert.equal(data.total, baseData.total, 
317 |           `Total should be consistent for params: ${JSON.stringify(params)}`);
318 |         
319 |         // API version should be consistent
320 |         if (data._v && baseData._v) {
321 |           assert.equal(data._v, baseData._v, 
322 |             `API version should be consistent for params: ${JSON.stringify(params)}`);
323 |         }
324 |       }
325 |     });
326 | 
327 |     test('should provide stable pagination behavior', async () => {
328 |       // Test pagination stability by requesting overlapping windows
329 |       const firstPage = await client.callTool('get_system_object_definitions', { 
330 |         start: 0, 
331 |         count: 5 
332 |       });
333 |       
334 |       const secondPage = await client.callTool('get_system_object_definitions', { 
335 |         start: 3, 
336 |         count: 5 
337 |       });
338 |       
339 |       if (!firstPage.isError && !secondPage.isError) {
340 |         const firstData = JSON.parse(firstPage.content[0].text);
341 |         const secondData = JSON.parse(secondPage.content[0].text);
342 |         
343 |         // Check for expected overlap in stable sort
344 |         if (firstData.data.length >= 5 && secondData.data.length > 2) {
345 |           const firstPageLast2 = firstData.data.slice(3, 5).map(obj => obj.object_type);
346 |           const secondPageFirst2 = secondData.data.slice(0, 2).map(obj => obj.object_type);
347 |           
348 |           assert.deepEqual(firstPageLast2, secondPageFirst2, 
349 |             'Overlapping pagination should return consistent results');
350 |         }
351 |       }
352 |     });
353 | 
354 |     test('should handle comprehensive SFCC business validation', async () => {
355 |       const result = await client.callTool('get_system_object_definitions', {});
356 |       const data = JSON.parse(result.content[0].text);
357 |       
358 |       // Business rule validation for SFCC (flexible for different instances)
359 |       const systemObjectTypes = data.data.map(obj => obj.object_type);
360 |       const criticalSFCCTypes = ['Category', 'Catalog', 'Content', 'Basket', 'Campaign'];
361 |       
362 |       // Check if SFCC instance has at least some critical system objects
363 |       const foundCriticalTypes = criticalSFCCTypes.filter(type => systemObjectTypes.includes(type));
364 |       assert.ok(foundCriticalTypes.length > 0, 
365 |         `SFCC instance should include at least one critical system object. Found: ${foundCriticalTypes.join(', ')}, Available: ${systemObjectTypes.slice(0,5).join(', ')}`);
366 |       
367 |       // Validate object type ID consistency for found objects
368 |       data.data.forEach(obj => {
369 |         if (obj.object_type_id) {
370 |           // Object type ID should be related to object type
371 |           assert.ok(typeof obj.object_type_id === 'string', 
372 |             `object_type_id should be string for ${obj.object_type}`);
373 |           
374 |           // Critical objects should have non-empty IDs if they exist
375 |           if (foundCriticalTypes.includes(obj.object_type)) {
376 |             assert.ok(obj.object_type_id.length > 0, 
377 |               `System object ${obj.object_type} should have non-empty object_type_id`);
378 |           }
379 |         }
380 |       });
381 |       
382 |       // Validate that we have a reasonable number of object types for an SFCC instance
383 |       assert.ok(data.total >= 1, 'SFCC instance should have at least 1 system object type');
384 |       assert.ok(data.total <= 200, 'SFCC instance should not have excessive object types');
385 |     });
386 |   });
387 | });
```

--------------------------------------------------------------------------------
/docs/dw_order/OrderItem.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class OrderItem
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.order.OrderItem
  9 | 
 10 | ## Description
 11 | 
 12 | Defines extensions to ProductLineItems and ShippingLineItems belonging to an order. The order-item can be accessed using ProductLineItem.getOrderItem() or ShippingLineItem.getOrderItem() - these methods return null if the item is associated with a basket rather than an order. Alternative access is available using Order.getOrderItem(String) by passing the itemID used to identify the order-item in for example export files. The associated order-item can also be accessed from invoice-items, shipping-order-items, return-items and return-case-items using AbstractItem.getOrderItem(). The order-item provides an item-level status and type, methods for accessing and creating associated items, and methods used to allocate inventory for shipping-order creation. 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.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### STATUS_BACKORDER
 17 | 
 18 | **Type:** String = "BACKORDER"
 19 | 
 20 | Constant for Order Item Status BACKORDER
 21 | 
 22 | ### STATUS_CANCELLED
 23 | 
 24 | **Type:** String = "CANCELLED"
 25 | 
 26 | Constant for Order Item Status CANCELLED
 27 | 
 28 | ### STATUS_CONFIRMED
 29 | 
 30 | **Type:** String = "CONFIRMED"
 31 | 
 32 | Constant for Order Item Status CONFIRMED
 33 | 
 34 | ### STATUS_CREATED
 35 | 
 36 | **Type:** String = "CREATED"
 37 | 
 38 | Constant for Order Item Status CREATED
 39 | 
 40 | ### STATUS_NEW
 41 | 
 42 | **Type:** String = "NEW"
 43 | 
 44 | Constant for Order Item Status NEW
 45 | 
 46 | ### STATUS_OPEN
 47 | 
 48 | **Type:** String = "OPEN"
 49 | 
 50 | Constant for Order Item Status OPEN
 51 | 
 52 | ### STATUS_SHIPPED
 53 | 
 54 | **Type:** String = "SHIPPED"
 55 | 
 56 | Constant for Order Item Status SHIPPED
 57 | 
 58 | ### STATUS_WAREHOUSE
 59 | 
 60 | **Type:** String = "WAREHOUSE"
 61 | 
 62 | Constant for Order Item Status WAREHOUSE
 63 | 
 64 | ### TYPE_PRODUCT
 65 | 
 66 | **Type:** String = "PRODUCT"
 67 | 
 68 | Constant for Order Item Type PRODUCT
 69 | 
 70 | ### TYPE_SERVICE
 71 | 
 72 | **Type:** String = "SERVICE"
 73 | 
 74 | Constant for Order Item Type SERVICE
 75 | 
 76 | ## Properties
 77 | 
 78 | ### appeasedAmount
 79 | 
 80 | **Type:** Money (Read Only)
 81 | 
 82 | Sum of amounts appeased for this item, calculated by iterating over
 83 |  invoice items associated with the item.
 84 | 
 85 | ### capturedAmount
 86 | 
 87 | **Type:** Money (Read Only)
 88 | 
 89 | Sum of amounts captured for this item, calculated by iterating over
 90 |  invoice items associated with the item.
 91 | 
 92 | ### invoiceItems
 93 | 
 94 | **Type:** Collection (Read Only)
 95 | 
 96 | All invoice items associated with this item, each
 97 |  InvoiceItem will belong to a different
 98 |  Invoice, which can also be accessed using
 99 |  Order.getInvoices() or Order.getInvoice(String).
100 | 
101 | ### itemID
102 | 
103 | **Type:** String (Read Only)
104 | 
105 | The itemID used to identify the OrderItem. Note this is
106 |  not a UUID, it is created internally when the OrderItem
107 |  instance is created, and is typically used within export files to
108 |  identify the item.
109 | 
110 | ### lineItem
111 | 
112 | **Type:** LineItem (Read Only)
113 | 
114 | The line item which is being extended by this instance.
115 | 
116 | ### refundedAmount
117 | 
118 | **Type:** Money (Read Only)
119 | 
120 | Sum of amounts refunded for this item, calculated by iterating over
121 |  invoice items associated with the item.
122 | 
123 | ### returnCaseItems
124 | 
125 | **Type:** Collection (Read Only)
126 | 
127 | All return case items associated with this item,
128 |  each ReturnCaseItem will belong to a different
129 |  ReturnCase, which can also be accessed using
130 |  Order.getReturnCases() or Order.getReturnCase(String).
131 | 
132 | ### returnedQuantity
133 | 
134 | **Type:** Quantity (Read Only)
135 | 
136 | The quantity returned, dynamically sum of quantities held by associated
137 |  ReturnItems.
138 | 
139 | ### shippingOrderItem
140 | 
141 | **Type:** ShippingOrderItem (Read Only)
142 | 
143 | The last added non-cancelled shipping order item if one exists, otherwise null.
144 |  
145 |  Multiple shipping order items that are not in status ShippingOrderItem.STATUS_CANCELLED
146 |  can exist for one OrderItem, for example if the OrderItem has been split for shipping purposes.
147 |  The method returns null if no non-cancelled shipping order item exists.
148 | 
149 | ### shippingOrderItems
150 | 
151 | **Type:** Collection (Read Only)
152 | 
153 | A collection of the ShippingOrderItems created for this item.
154 |  ShippingOrder items represents the whole or part of this item which could
155 |  be delivered, and belong to a shipping order.
156 |  Note that the cancelled shipping order items are returned too.
157 |  This method is equivalent to getShippingOrderItems(Boolean)
158 |  called with parameter true.
159 | 
160 | ### splitItems
161 | 
162 | **Type:** Collection (Read Only)
163 | 
164 | A collection of all split OrderItems associated with this item. Inverse relation to getSplitSourceItem().
165 |  
166 |  Split order items are created when
167 |  
168 |  creating a ShippingOrderItem for a ShippingOrder, see ShippingOrder.createShippingOrderItem(OrderItem, Quantity)
169 |  splitting an existing ShippingOrderItem, see ShippingOrderItem.split(Quantity)
170 |  
171 |  with a specified quantity less than the existing quantity of the associated ProductLineItem. In this case the associated ProductLineItem
172 |  is split by creating a new ProductLineItem and associating a new ShippingOrderItem with this item. The new ShippingOrderItem
173 |  receives the specified quantity and the quantity of the item is set to the remaining quantity. All split items are associated to their originating item via
174 |  the "split source item" association.
175 | 
176 | ### splitSourceItem
177 | 
178 | **Type:** OrderItem (Read Only)
179 | 
180 | The split source item associated with this item, if existing. Inverse relation to getSplitItems().
181 |  
182 |  A split source item is associated after the successful creation of a split item with a quantity less than the existing quantity of the item to split.
183 |  For details see getSplitItems().
184 | 
185 | ### status
186 | 
187 | **Type:** EnumValue
188 | 
189 | Gets the order item status.
190 |  The possible values are:
191 |  
192 |  STATUS_NEW
193 |  STATUS_OPEN
194 |  STATUS_BACKORDER
195 |  STATUS_CONFIRMED
196 |  STATUS_WAREHOUSE
197 |  STATUS_SHIPPED
198 |  STATUS_CANCELLED
199 | 
200 | ### type
201 | 
202 | **Type:** EnumValue (Read Only)
203 | 
204 | The type of line item with which this instance is associated, one
205 |  of
206 |  
207 |  SERVICE (method getLineItem() returns a
208 |  ShippingLineItem
209 |  PRODUCT (method getLineItem() returns a
210 |  ProductLineItem
211 | 
212 | ## Constructor Summary
213 | 
214 | ## Method Summary
215 | 
216 | ### allocateInventory
217 | 
218 | **Signature:** `allocateInventory(partialAllocation : boolean) : Quantity`
219 | 
220 | Please note that this method is disabled by default.
221 | 
222 | ### getAppeasedAmount
223 | 
224 | **Signature:** `getAppeasedAmount() : Money`
225 | 
226 | Sum of amounts appeased for this item, calculated by iterating over invoice items associated with the item.
227 | 
228 | ### getCapturedAmount
229 | 
230 | **Signature:** `getCapturedAmount() : Money`
231 | 
232 | Sum of amounts captured for this item, calculated by iterating over invoice items associated with the item.
233 | 
234 | ### getInvoiceItems
235 | 
236 | **Signature:** `getInvoiceItems() : Collection`
237 | 
238 | Returns all invoice items associated with this item, each InvoiceItem will belong to a different Invoice, which can also be accessed using Order.getInvoices() or Order.getInvoice(String).
239 | 
240 | ### getItemID
241 | 
242 | **Signature:** `getItemID() : String`
243 | 
244 | The itemID used to identify the OrderItem.
245 | 
246 | ### getLineItem
247 | 
248 | **Signature:** `getLineItem() : LineItem`
249 | 
250 | Returns the line item which is being extended by this instance.
251 | 
252 | ### getRefundedAmount
253 | 
254 | **Signature:** `getRefundedAmount() : Money`
255 | 
256 | Sum of amounts refunded for this item, calculated by iterating over invoice items associated with the item.
257 | 
258 | ### getReturnCaseItems
259 | 
260 | **Signature:** `getReturnCaseItems() : Collection`
261 | 
262 | Returns all return case items associated with this item, each ReturnCaseItem will belong to a different ReturnCase, which can also be accessed using Order.getReturnCases() or Order.getReturnCase(String).
263 | 
264 | ### getReturnedQuantity
265 | 
266 | **Signature:** `getReturnedQuantity() : Quantity`
267 | 
268 | The quantity returned, dynamically sum of quantities held by associated ReturnItems.
269 | 
270 | ### getShippingOrderItem
271 | 
272 | **Signature:** `getShippingOrderItem() : ShippingOrderItem`
273 | 
274 | The last added non-cancelled shipping order item if one exists, otherwise null.
275 | 
276 | ### getShippingOrderItems
277 | 
278 | **Signature:** `getShippingOrderItems() : Collection`
279 | 
280 | Returns a collection of the ShippingOrderItems created for this item.
281 | 
282 | ### getShippingOrderItems
283 | 
284 | **Signature:** `getShippingOrderItems(includeCancelled : boolean) : Collection`
285 | 
286 | Returns a collection of the ShippingOrderItems created for this item.
287 | 
288 | ### getSplitItems
289 | 
290 | **Signature:** `getSplitItems() : Collection`
291 | 
292 | Returns a collection of all split OrderItems associated with this item.
293 | 
294 | ### getSplitSourceItem
295 | 
296 | **Signature:** `getSplitSourceItem() : OrderItem`
297 | 
298 | Returns the split source item associated with this item, if existing.
299 | 
300 | ### getStatus
301 | 
302 | **Signature:** `getStatus() : EnumValue`
303 | 
304 | Gets the order item status. The possible values are: STATUS_NEW STATUS_OPEN STATUS_BACKORDER STATUS_CONFIRMED STATUS_WAREHOUSE STATUS_SHIPPED STATUS_CANCELLED
305 | 
306 | ### getType
307 | 
308 | **Signature:** `getType() : EnumValue`
309 | 
310 | Returns the type of line item with which this instance is associated, one of SERVICE (method getLineItem() returns a ShippingLineItem PRODUCT (method getLineItem() returns a ProductLineItem
311 | 
312 | ### setStatus
313 | 
314 | **Signature:** `setStatus(status : String) : void`
315 | 
316 | Set the status of the order item, use one of the values documented in getStatus().
317 | 
318 | ## Method Detail
319 | 
320 | ## Method Details
321 | 
322 | ### allocateInventory
323 | 
324 | **Signature:** `allocateInventory(partialAllocation : boolean) : Quantity`
325 | 
326 | **Description:** Please note that this method is disabled by default. Please contact support for enabling it. Attempts to allocate inventory for the item and returns the quantity that could be allocated or null if no allocation was possible. All option product line items are allocated with their parent. Note that for items with option product line items no partial allocation is possible. That means the partialAllocation parameter will in this case always be considered as false
327 | 
328 | **Parameters:**
329 | 
330 | - `partialAllocation`: true accept a partial allocation as a result. Partial allocation is only possible when no option product line items are included, false only full allocation will be used, partial allocation will be released automatically
331 | 
332 | **Returns:**
333 | 
334 | successful: the newly allocated quantity failed: null
335 | 
336 | ---
337 | 
338 | ### getAppeasedAmount
339 | 
340 | **Signature:** `getAppeasedAmount() : Money`
341 | 
342 | **Description:** Sum of amounts appeased for this item, calculated by iterating over invoice items associated with the item.
343 | 
344 | **Returns:**
345 | 
346 | Sum of amounts refunded for this item
347 | 
348 | ---
349 | 
350 | ### getCapturedAmount
351 | 
352 | **Signature:** `getCapturedAmount() : Money`
353 | 
354 | **Description:** Sum of amounts captured for this item, calculated by iterating over invoice items associated with the item.
355 | 
356 | **Returns:**
357 | 
358 | Sum of amounts captured for this item
359 | 
360 | ---
361 | 
362 | ### getInvoiceItems
363 | 
364 | **Signature:** `getInvoiceItems() : Collection`
365 | 
366 | **Description:** Returns all invoice items associated with this item, each InvoiceItem will belong to a different Invoice, which can also be accessed using Order.getInvoices() or Order.getInvoice(String).
367 | 
368 | **Returns:**
369 | 
370 | invoice items associated with this item
371 | 
372 | ---
373 | 
374 | ### getItemID
375 | 
376 | **Signature:** `getItemID() : String`
377 | 
378 | **Description:** The itemID used to identify the OrderItem. Note this is not a UUID, it is created internally when the OrderItem instance is created, and is typically used within export files to identify the item.
379 | 
380 | **Returns:**
381 | 
382 | the itemID of the OrderItem
383 | 
384 | ---
385 | 
386 | ### getLineItem
387 | 
388 | **Signature:** `getLineItem() : LineItem`
389 | 
390 | **Description:** Returns the line item which is being extended by this instance.
391 | 
392 | **Returns:**
393 | 
394 | the line item associated with this instance
395 | 
396 | ---
397 | 
398 | ### getRefundedAmount
399 | 
400 | **Signature:** `getRefundedAmount() : Money`
401 | 
402 | **Description:** Sum of amounts refunded for this item, calculated by iterating over invoice items associated with the item.
403 | 
404 | **Returns:**
405 | 
406 | Sum of amounts refunded for this item
407 | 
408 | ---
409 | 
410 | ### getReturnCaseItems
411 | 
412 | **Signature:** `getReturnCaseItems() : Collection`
413 | 
414 | **Description:** Returns all return case items associated with this item, each ReturnCaseItem will belong to a different ReturnCase, which can also be accessed using Order.getReturnCases() or Order.getReturnCase(String).
415 | 
416 | **Returns:**
417 | 
418 | return case items associated with this item
419 | 
420 | ---
421 | 
422 | ### getReturnedQuantity
423 | 
424 | **Signature:** `getReturnedQuantity() : Quantity`
425 | 
426 | **Description:** The quantity returned, dynamically sum of quantities held by associated ReturnItems.
427 | 
428 | **Returns:**
429 | 
430 | quantity returned, the sum of quantities held by associated ReturnItems
431 | 
432 | ---
433 | 
434 | ### getShippingOrderItem
435 | 
436 | **Signature:** `getShippingOrderItem() : ShippingOrderItem`
437 | 
438 | **Description:** The last added non-cancelled shipping order item if one exists, otherwise null. Multiple shipping order items that are not in status ShippingOrderItem.STATUS_CANCELLED can exist for one OrderItem, for example if the OrderItem has been split for shipping purposes. The method returns null if no non-cancelled shipping order item exists.
439 | 
440 | **Deprecated:**
441 | 
442 | This item is deprecated.
443 | 
444 | **Returns:**
445 | 
446 | the last not cancelled shipping order item or null
447 | 
448 | ---
449 | 
450 | ### getShippingOrderItems
451 | 
452 | **Signature:** `getShippingOrderItems() : Collection`
453 | 
454 | **Description:** Returns a collection of the ShippingOrderItems created for this item. ShippingOrder items represents the whole or part of this item which could be delivered, and belong to a shipping order. Note that the cancelled shipping order items are returned too. This method is equivalent to getShippingOrderItems(Boolean) called with parameter true.
455 | 
456 | **Returns:**
457 | 
458 | collection of the shipping order items created for this item
459 | 
460 | ---
461 | 
462 | ### getShippingOrderItems
463 | 
464 | **Signature:** `getShippingOrderItems(includeCancelled : boolean) : Collection`
465 | 
466 | **Description:** Returns a collection of the ShippingOrderItems created for this item. ShippingOrder items represent the whole or part of this item which could be delivered, and belong to a shipping order. Depending on the includeCancelled parameter the cancelled shipping order items will be returned or not.
467 | 
468 | **Parameters:**
469 | 
470 | - `includeCancelled`: true all shipping order items, including the cancelled, created for this item will be returned false only non-cancelled shipping order items created for this item will be returned
471 | 
472 | **Returns:**
473 | 
474 | collection of the shipping order items created for this item
475 | 
476 | ---
477 | 
478 | ### getSplitItems
479 | 
480 | **Signature:** `getSplitItems() : Collection`
481 | 
482 | **Description:** Returns a collection of all split OrderItems associated with this item. Inverse relation to getSplitSourceItem(). Split order items are created when creating a ShippingOrderItem for a ShippingOrder, see ShippingOrder.createShippingOrderItem(OrderItem, Quantity) splitting an existing ShippingOrderItem, see ShippingOrderItem.split(Quantity) with a specified quantity less than the existing quantity of the associated ProductLineItem. In this case the associated ProductLineItem is split by creating a new ProductLineItem and associating a new ShippingOrderItem with this item. The new ShippingOrderItem receives the specified quantity and the quantity of the item is set to the remaining quantity. All split items are associated to their originating item via the "split source item" association.
483 | 
484 | **Returns:**
485 | 
486 | the split order items associated with this item
487 | 
488 | ---
489 | 
490 | ### getSplitSourceItem
491 | 
492 | **Signature:** `getSplitSourceItem() : OrderItem`
493 | 
494 | **Description:** Returns the split source item associated with this item, if existing. Inverse relation to getSplitItems(). A split source item is associated after the successful creation of a split item with a quantity less than the existing quantity of the item to split. For details see getSplitItems().
495 | 
496 | **Returns:**
497 | 
498 | the split source item or null
499 | 
500 | ---
501 | 
502 | ### getStatus
503 | 
504 | **Signature:** `getStatus() : EnumValue`
505 | 
506 | **Description:** Gets the order item status. The possible values are: STATUS_NEW STATUS_OPEN STATUS_BACKORDER STATUS_CONFIRMED STATUS_WAREHOUSE STATUS_SHIPPED STATUS_CANCELLED
507 | 
508 | **Returns:**
509 | 
510 | the status
511 | 
512 | ---
513 | 
514 | ### getType
515 | 
516 | **Signature:** `getType() : EnumValue`
517 | 
518 | **Description:** Returns the type of line item with which this instance is associated, one of SERVICE (method getLineItem() returns a ShippingLineItem PRODUCT (method getLineItem() returns a ProductLineItem
519 | 
520 | **Returns:**
521 | 
522 | the type of order item, one of TYPE_PRODUCT or TYPE_SERVICE.
523 | 
524 | ---
525 | 
526 | ### setStatus
527 | 
528 | **Signature:** `setStatus(status : String) : void`
529 | 
530 | **Description:** Set the status of the order item, use one of the values documented in getStatus(). If the order item has a related shipping order item (see getShippingOrderItem()) the status of the shipping order item will be adjusted to the same status. Setting the status of an order item might also change the status of the related order. The following rules apply in top-down order: all items STATUS_CANCELLED - order status is Order.ORDER_STATUS_CANCELLED at least one item in status STATUS_SHIPPED and all other items are STATUS_CANCELLED order status is Order.ORDER_STATUS_COMPLETED at least one item in status STATUS_CREATED, STATUS_OPEN, STATUS_NEW , STATUS_BACKORDER - order status is Order.ORDER_STATUS_OPEN, order confirmation status is Order.CONFIRMATION_STATUS_NOTCONFIRMED other combinations will have only items in STATUS_CONFIRMED, STATUS_CANCELLED and STATUS_SHIPPED - order status is Order.ORDER_STATUS_OPEN, order confirmation status is Order.CONFIRMATION_STATUS_CONFIRMED
531 | 
532 | **Parameters:**
533 | 
534 | - `status`: status string matching one of the values for status
535 | 
536 | ---
```

--------------------------------------------------------------------------------
/tests/mcp/node/get-job-execution-summary.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('get_job_execution_summary - Advanced Programmatic Tests', () => {
  6 |   let client;
  7 |   let discoveredJobNames = [];
  8 | 
  9 |   before(async () => {
 10 |     client = await connect('./aegis.config.with-dw.json');
 11 |     await discoverJobNames();
 12 |   });
 13 | 
 14 |   after(async () => {
 15 |     if (client?.connected) {
 16 |       await client.disconnect();
 17 |     }
 18 |   });
 19 | 
 20 |   beforeEach(() => {
 21 |     // CRITICAL: Clear all buffers to prevent leaking into next tests
 22 |     client.clearAllBuffers(); // Recommended - comprehensive protection
 23 |   });
 24 | 
 25 |   // Optimized helper functions focused on complex validation
 26 |   function assertValidMCPResponse(result) {
 27 |     assert.ok(result.content, 'Should have content');
 28 |     assert.ok(Array.isArray(result.content), 'Content should be array');
 29 |     assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
 30 |   }
 31 | 
 32 |   function parseResponseText(text) {
 33 |     return text.startsWith('"') && text.endsWith('"') 
 34 |       ? JSON.parse(text) 
 35 |       : text;
 36 |   }
 37 | 
 38 |   function assertJobExecutionSummaryFormat(result, jobName) {
 39 |     assertValidMCPResponse(result);
 40 |     assert.equal(result.isError, false, 'Should not be an error response');
 41 |     
 42 |     const text = parseResponseText(result.content[0].text);
 43 |     
 44 |     if (text.includes('No job logs found')) {
 45 |       assert.ok(text.includes(`No job logs found for job name: ${jobName}`),
 46 |         'No results message should include job name');
 47 |       return null;
 48 |     }
 49 |     
 50 |     // Validate comprehensive execution summary format
 51 |     assert.ok(text.includes(`Job Execution Summary: ${jobName}`),
 52 |       'Should contain job execution summary header with job name');
 53 |     assert.ok(text.includes('⏱️ Timing:') && text.includes('📊 Status:'),
 54 |       'Should contain emoji-formatted sections');
 55 |     assert.ok(text.includes('Start:') && text.includes('End:') && text.includes('Duration:'),
 56 |       'Should contain complete timing information');
 57 |     assert.ok(text.includes('Status:') && text.includes('Errors:') && text.includes('Warnings:'),
 58 |       'Should contain complete status information');
 59 |     
 60 |     return parseJobExecutionSummary(text);
 61 |   }
 62 | 
 63 |   function parseJobExecutionSummary(text) {
 64 |     const summary = {
 65 |       jobName: null,
 66 |       timing: { start: null, end: null, duration: null },
 67 |       status: { status: null, errors: null, warnings: null }
 68 |     };
 69 |     
 70 |     // Extract structured data from response
 71 |     const jobNameMatch = text.match(/Job Execution Summary: ([^\n\r]+)/);
 72 |     if (jobNameMatch) summary.jobName = jobNameMatch[1].trim();
 73 |     
 74 |     const startMatch = text.match(/- Start: ([^\n\r]+)/);
 75 |     if (startMatch) summary.timing.start = startMatch[1].trim();
 76 |     
 77 |     const endMatch = text.match(/- End: ([^\n\r]+)/);
 78 |     if (endMatch) summary.timing.end = endMatch[1].trim();
 79 |     
 80 |     const durationMatch = text.match(/- Duration: ([^\n\r]+)/);
 81 |     if (durationMatch) summary.timing.duration = durationMatch[1].trim();
 82 |     
 83 |     const statusMatch = text.match(/- Status: ([^\n\r]+)/);
 84 |     if (statusMatch) summary.status.status = statusMatch[1].trim();
 85 |     
 86 |     const errorsMatch = text.match(/- Errors: ([^\n\r]+)/);
 87 |     if (errorsMatch) summary.status.errors = errorsMatch[1].trim();
 88 |     
 89 |     const warningsMatch = text.match(/- Warnings: ([^\n\r]+)/);
 90 |     if (warningsMatch) summary.status.warnings = warningsMatch[1].trim();
 91 |     
 92 |     return summary;
 93 |   }
 94 | 
 95 |   function assertSuccessResponse(result) {
 96 |     assertValidMCPResponse(result);
 97 |     assert.equal(result.isError, false, 'Response should not be error');
 98 |   }
 99 | 
100 |   function assertErrorResponse(result, expectedErrorText = null) {
101 |     assertValidMCPResponse(result);
102 |     assert.equal(result.isError, true, 'Response should be error');
103 |     
104 |     if (expectedErrorText) {
105 |       const responseText = parseResponseText(result.content[0].text);
106 |       assert.ok(responseText.includes(expectedErrorText), 
107 |         `Error message should contain: ${expectedErrorText}`);
108 |     }
109 |   }
110 | 
111 |   function assertTextContent(result, expectedText) {
112 |     assertSuccessResponse(result);
113 |     const responseText = parseResponseText(result.content[0].text);
114 |     assert.ok(responseText.includes(expectedText), 
115 |       `Response should contain: ${expectedText}`);
116 |   }
117 | 
118 |   async function discoverJobNames() {
119 |     console.log('🔍 Discovering available job names for advanced testing...');
120 |     
121 |     try {
122 |       const result = await client.callTool('get_latest_job_log_files', { limit: 10 });
123 |       if (!result.isError) {
124 |         const text = parseResponseText(result.content[0].text);
125 |         const jobs = extractJobInfo(text);
126 |         discoveredJobNames = jobs.map(job => job.jobName);
127 |         console.log(`🔧 Found ${discoveredJobNames.length} jobs:`, discoveredJobNames);
128 |       }
129 |     } catch (error) {
130 |       console.warn('⚠️ Could not discover job names:', error.message);
131 |       discoveredJobNames = ['ImportCatalog', 'ProcessOrders'];
132 |     }
133 |   }
134 | 
135 |   function extractJobInfo(responseText) {
136 |     const jobs = [];
137 |     const text = parseResponseText(responseText);
138 |     const sections = text.split('🔧 Job: ').slice(1);
139 |     
140 |     for (const section of sections) {
141 |       const lines = section.split('\n');
142 |       const jobName = lines[0].trim();
143 |       
144 |       let jobId = null;
145 |       for (const line of lines) {
146 |         const idMatch = line.match(/ID: (\d+)/);
147 |         if (idMatch) jobId = idMatch[1];
148 |       }
149 |       
150 |       if (jobName && jobId) {
151 |         jobs.push({ jobName, jobId });
152 |       }
153 |     }
154 |     
155 |     return jobs;
156 |   }
157 | 
158 |   // Core functionality tests - focus on complex parsing and validation
159 |   describe('Core Functionality & Format Validation', () => {
160 |     test('should retrieve and parse job execution summary structure', async () => {
161 |       if (discoveredJobNames.length === 0) {
162 |         console.log('⏭️ Skipping test - no job names discovered');
163 |         return;
164 |       }
165 |       
166 |       const jobName = discoveredJobNames[0];
167 |       const result = await client.callTool('get_job_execution_summary', { jobName });
168 |       
169 |       const summary = assertJobExecutionSummaryFormat(result, jobName);
170 |       
171 |       if (summary) {
172 |         // Validate parsed structure for existing jobs
173 |         assert.equal(summary.jobName, jobName, 'Parsed job name should match requested');
174 |         assert.ok(summary.timing.start, 'Should have start time');
175 |         assert.ok(summary.timing.end, 'Should have end time');
176 |         assert.ok(summary.timing.duration !== null, 'Should have duration');
177 |         assert.ok(summary.status.status, 'Should have status');
178 |         assert.ok(summary.status.errors !== null, 'Should have error count');
179 |         assert.ok(summary.status.warnings !== null, 'Should have warning count');
180 |       }
181 |     });
182 | 
183 |     test('should validate timing format in execution summary', async () => {
184 |       if (discoveredJobNames.length === 0) {
185 |         console.log('⏭️ Skipping test - no job names discovered');
186 |         return;
187 |       }
188 |       
189 |       const jobName = discoveredJobNames[0];
190 |       const result = await client.callTool('get_job_execution_summary', { jobName });
191 |       
192 |       const summary = assertJobExecutionSummaryFormat(result, jobName);
193 |       
194 |       if (summary) {
195 |         // Validate datetime format (YYYY-MM-DD HH:MM:SS)
196 |         assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(summary.timing.start),
197 |           `Start time should match datetime format: ${summary.timing.start}`);
198 |         assert.ok(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(summary.timing.end),
199 |           `End time should match datetime format: ${summary.timing.end}`);
200 |         assert.ok(/\d+s/.test(summary.timing.duration),
201 |           `Duration should be in seconds format: ${summary.timing.duration}`);
202 |       }
203 |     });
204 | 
205 |     test('should validate status information format', async () => {
206 |       if (discoveredJobNames.length === 0) {
207 |         console.log('⏭️ Skipping test - no job names discovered');
208 |         return;
209 |       }
210 |       
211 |       const jobName = discoveredJobNames[0];
212 |       const result = await client.callTool('get_job_execution_summary', { jobName });
213 |       
214 |       const summary = assertJobExecutionSummaryFormat(result, jobName);
215 |       
216 |       if (summary) {
217 |         // Status should be a string
218 |         assert.ok(typeof summary.status.status === 'string', 'Status should be string');
219 |         
220 |         // Errors and warnings should be numeric strings
221 |         assert.ok(/\d+/.test(summary.status.errors),
222 |           `Errors should be numeric: ${summary.status.errors}`);
223 |         assert.ok(/\d+/.test(summary.status.warnings),
224 |           `Warnings should be numeric: ${summary.status.warnings}`);
225 |       }
226 |     });
227 | 
228 |     test('should handle non-existent job gracefully with proper messaging', async () => {
229 |       const result = await client.callTool('get_job_execution_summary', { 
230 |         jobName: 'NonExistentJob12345' 
231 |       });
232 |       
233 |       assertValidMCPResponse(result);
234 |       assert.equal(result.isError, false, 'Should not be an error response for non-existent job');
235 |       assertTextContent(result, 'No job logs found for job name: NonExistentJob12345');
236 |     });
237 |   });
238 | 
239 |   // Streamlined parameter validation - focus on key error cases
240 |   describe('Parameter Validation', () => {
241 |     test('should return error for missing jobName parameter', async () => {
242 |       const result = await client.callTool('get_job_execution_summary', {});
243 |       assertErrorResponse(result, 'jobName must be a non-empty string');
244 |     });
245 | 
246 |     test('should return error for empty jobName parameter', async () => {
247 |       const result = await client.callTool('get_job_execution_summary', { jobName: '' });
248 |       assertErrorResponse(result, 'jobName must be a non-empty string');
249 |     });
250 | 
251 |     test('should return error for null jobName parameter', async () => {
252 |       const result = await client.callTool('get_job_execution_summary', { jobName: null });
253 |       assertErrorResponse(result, 'jobName must be a non-empty string');
254 |     });
255 | 
256 |     test('should return error for non-string jobName parameter', async () => {
257 |       const result = await client.callTool('get_job_execution_summary', { jobName: 123 });
258 |       assertErrorResponse(result, 'jobName must be a non-empty string');
259 |     });
260 | 
261 |     test('should return error for whitespace-only jobName', async () => {
262 |       const result = await client.callTool('get_job_execution_summary', { jobName: '   ' });
263 |       assertErrorResponse(result, 'jobName must be a non-empty string');
264 |     });
265 |   });
266 | 
267 |   // Focus on meaningful edge cases that test real-world scenarios
268 |   describe('Edge Cases & Real-World Scenarios', () => {
269 |     test('should handle job names with special characters', async () => {
270 |       const specialJobName = 'Job-With-Dashes_And_Underscores.123';
271 |       const result = await client.callTool('get_job_execution_summary', { 
272 |         jobName: specialJobName 
273 |       });
274 |       
275 |       assertValidMCPResponse(result);
276 |       assert.equal(result.isError, false);
277 |       assertTextContent(result, `No job logs found for job name: ${specialJobName}`);
278 |     });
279 | 
280 |     test('should handle job names with Unicode characters', async () => {
281 |       const unicodeJobName = 'Job_测试_🔧_Тест';
282 |       const result = await client.callTool('get_job_execution_summary', { 
283 |         jobName: unicodeJobName 
284 |       });
285 |       
286 |       assertValidMCPResponse(result);
287 |       assert.equal(result.isError, false);
288 |       assertTextContent(result, `No job logs found for job name: ${unicodeJobName}`);
289 |     });
290 | 
291 |     test('should handle very long job names', async () => {
292 |       const longJobName = 'VeryLongJobNameThatMightCauseIssuesWithSomeSystemsBecauseItExceedsTypicalLimits'.repeat(2);
293 |       const result = await client.callTool('get_job_execution_summary', { 
294 |         jobName: longJobName 
295 |       });
296 |       
297 |       assertValidMCPResponse(result);
298 |       assert.equal(result.isError, false);
299 |       assertTextContent(result, 'No job logs found for job name:');
300 |     });
301 |   });
302 | 
303 |   // Advanced multi-job testing with discovered jobs
304 |   describe('Multi-Job Integration Testing', () => {
305 |     test('should handle discovered job names with execution summaries', async () => {
306 |       if (discoveredJobNames.length === 0) {
307 |         console.log('⏭️ Skipping test - no job names discovered');
308 |         return;
309 |       }
310 | 
311 |       const results = [];
312 |       
313 |       // Test up to 3 discovered jobs sequentially (avoid concurrent requests)
314 |       const jobsToTest = discoveredJobNames.slice(0, 3);
315 |       
316 |       for (const jobName of jobsToTest) {
317 |         const result = await client.callTool('get_job_execution_summary', { jobName });
318 |         results.push({ jobName, result });
319 |         
320 |         // Each result should be valid
321 |         assertValidMCPResponse(result);
322 |         assert.equal(result.isError, false, `Job ${jobName} should not error`);
323 |         
324 |         const summary = assertJobExecutionSummaryFormat(result, jobName);
325 |         if (summary) {
326 |           assert.equal(summary.jobName, jobName, 'Parsed job name should match');
327 |         }
328 |       }
329 |       
330 |       assert.ok(results.length > 0, 'Should have tested at least one job');
331 |       console.log(`✅ Successfully tested ${results.length} discovered jobs`);
332 |     });
333 | 
334 |     test('should maintain consistent response format across different jobs', async () => {
335 |       if (discoveredJobNames.length < 2) {
336 |         console.log('⏭️ Skipping test - need at least 2 discovered jobs');
337 |         return;
338 |       }
339 |       
340 |       const results = [];
341 |       
342 |       // Test first 2 jobs for consistency
343 |       for (const jobName of discoveredJobNames.slice(0, 2)) {
344 |         const result = await client.callTool('get_job_execution_summary', { jobName });
345 |         results.push(result);
346 |         
347 |         assertValidMCPResponse(result);
348 |         assert.equal(result.content.length, 1, 'Should have exactly one content item');
349 |         assert.equal(result.content[0].type, 'text', 'Content should be text type');
350 |       }
351 |       
352 |       // All results should have consistent structure
353 |       results.forEach((result, index) => {
354 |         assert.equal(typeof result.isError, 'boolean', `Result ${index} isError should be boolean`);
355 |         assert.ok(Array.isArray(result.content), `Result ${index} content should be array`);
356 |       });
357 |     });
358 |   });
359 | 
360 |   // Integration with job log ecosystem
361 |   describe('Integration with Job Log Ecosystem', () => {
362 |     test('should integrate with discovered job ecosystem effectively', async () => {
363 |       if (discoveredJobNames.length === 0) {
364 |         console.log('⏭️ Skipping test - no job names discovered');
365 |         return;
366 |       }
367 |       
368 |       // Get job log files to validate integration
369 |       const logFilesResult = await client.callTool('get_latest_job_log_files', { limit: 5 });
370 |       assertValidMCPResponse(logFilesResult);
371 |       assert.equal(logFilesResult.isError, false, 'Should get job log files successfully');
372 |       
373 |       // Parse available jobs
374 |       const logText = parseResponseText(logFilesResult.content[0].text);
375 |       const availableJobs = extractJobInfo(logText);
376 |       
377 |       assert.ok(availableJobs.length > 0, 'Should find available jobs from log files');
378 |       
379 |       // Test execution summary for first available job
380 |       const firstJob = availableJobs[0];
381 |       const summaryResult = await client.callTool('get_job_execution_summary', { 
382 |         jobName: firstJob.jobName 
383 |       });
384 |       
385 |       assertValidMCPResponse(summaryResult);
386 |       
387 |       // Should either get a summary or a "no logs found" message
388 |       const summaryText = parseResponseText(summaryResult.content[0].text);
389 |       const hasValidResponse = summaryText.includes('Job Execution Summary:') || 
390 |                               summaryText.includes('No job logs found');
391 |       
392 |       assert.ok(hasValidResponse, 'Should get either execution summary or no logs message');
393 |       
394 |       console.log(`✅ Successfully integrated with job ecosystem (${availableJobs.length} jobs available)`);
395 |     });
396 | 
397 |     test('should validate execution summary data integrity', async () => {
398 |       if (discoveredJobNames.length === 0) {
399 |         console.log('⏭️ Skipping test - no job names discovered');
400 |         return;
401 |       }
402 |       
403 |       const jobName = discoveredJobNames[0];
404 |       const result = await client.callTool('get_job_execution_summary', { jobName });
405 |       
406 |       const summary = assertJobExecutionSummaryFormat(result, jobName);
407 |       
408 |       if (summary) {
409 |         // Validate data integrity
410 |         assert.ok(summary.jobName.length > 0, 'Job name should not be empty');
411 |         
412 |         // Parse numeric values for validation
413 |         const errorCount = parseInt(summary.status.errors, 10);
414 |         const warningCount = parseInt(summary.status.warnings, 10);
415 |         
416 |         assert.ok(!isNaN(errorCount), 'Error count should be numeric');
417 |         assert.ok(!isNaN(warningCount), 'Warning count should be numeric');
418 |         assert.ok(errorCount >= 0, 'Error count should be non-negative');
419 |         assert.ok(warningCount >= 0, 'Warning count should be non-negative');
420 |         
421 |         // Validate duration format and logic
422 |         const durationMatch = summary.timing.duration.match(/(\d+)s/);
423 |         if (durationMatch) {
424 |           const durationSeconds = parseInt(durationMatch[1], 10);
425 |           assert.ok(durationSeconds >= 0, 'Duration should be non-negative');
426 |         }
427 |         
428 |         console.log(`✅ Data integrity validated for job ${jobName}`);
429 |       }
430 |     });
431 |   });
432 | });
433 | 
```
Page 30/61FirstPrevNextLast