This is page 5 of 43. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=false&page={x} to view the full context. # Directory Structure ``` ├── .DS_Store ├── .github │ ├── dependabot.yml │ ├── instructions │ │ ├── mcp-node-tests.instructions.md │ │ └── mcp-yml-tests.instructions.md │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── PULL_REQUEST_TEMPLATE │ │ ├── bug_fix.md │ │ ├── documentation.md │ │ └── new_tool.md │ ├── pull_request_template.md │ └── workflows │ ├── ci.yml │ ├── deploy-pages.yml │ ├── publish.yml │ └── update-docs.yml ├── .gitignore ├── .husky │ └── pre-commit ├── aegis.config.docs-only.json ├── aegis.config.json ├── aegis.config.with-dw.json ├── AGENTS.md ├── ai-instructions │ ├── claude-desktop │ │ └── claude_custom_instructions.md │ ├── cursor │ │ └── .cursor │ │ └── rules │ │ ├── debugging-workflows.mdc │ │ ├── hooks-development.mdc │ │ ├── isml-templates.mdc │ │ ├── job-framework.mdc │ │ ├── performance-optimization.mdc │ │ ├── scapi-endpoints.mdc │ │ ├── security-patterns.mdc │ │ ├── sfcc-development.mdc │ │ ├── sfra-controllers.mdc │ │ ├── sfra-models.mdc │ │ ├── system-objects.mdc │ │ └── testing-patterns.mdc │ └── github-copilot │ └── copilot-instructions.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── docs │ ├── best-practices │ │ ├── cartridge_creation.md │ │ ├── isml_templates.md │ │ ├── job_framework.md │ │ ├── localserviceregistry.md │ │ ├── ocapi_hooks.md │ │ ├── performance.md │ │ ├── scapi_custom_endpoint.md │ │ ├── scapi_hooks.md │ │ ├── security.md │ │ ├── sfra_client_side_js.md │ │ ├── sfra_controllers.md │ │ ├── sfra_models.md │ │ └── sfra_scss.md │ ├── dw_campaign │ │ ├── ABTest.md │ │ ├── ABTestMgr.md │ │ ├── ABTestSegment.md │ │ ├── AmountDiscount.md │ │ ├── ApproachingDiscount.md │ │ ├── BonusChoiceDiscount.md │ │ ├── BonusDiscount.md │ │ ├── Campaign.md │ │ ├── CampaignMgr.md │ │ ├── CampaignStatusCodes.md │ │ ├── Coupon.md │ │ ├── CouponMgr.md │ │ ├── CouponRedemption.md │ │ ├── CouponStatusCodes.md │ │ ├── Discount.md │ │ ├── DiscountPlan.md │ │ ├── FixedPriceDiscount.md │ │ ├── FixedPriceShippingDiscount.md │ │ ├── FreeDiscount.md │ │ ├── FreeShippingDiscount.md │ │ ├── PercentageDiscount.md │ │ ├── PercentageOptionDiscount.md │ │ ├── PriceBookPriceDiscount.md │ │ ├── Promotion.md │ │ ├── PromotionMgr.md │ │ ├── PromotionPlan.md │ │ ├── SlotContent.md │ │ ├── SourceCodeGroup.md │ │ ├── SourceCodeInfo.md │ │ ├── SourceCodeStatusCodes.md │ │ └── TotalFixedPriceDiscount.md │ ├── dw_catalog │ │ ├── Catalog.md │ │ ├── CatalogMgr.md │ │ ├── Category.md │ │ ├── CategoryAssignment.md │ │ ├── CategoryLink.md │ │ ├── PriceBook.md │ │ ├── PriceBookMgr.md │ │ ├── Product.md │ │ ├── ProductActiveData.md │ │ ├── ProductAttributeModel.md │ │ ├── ProductAvailabilityLevels.md │ │ ├── ProductAvailabilityModel.md │ │ ├── ProductInventoryList.md │ │ ├── ProductInventoryMgr.md │ │ ├── ProductInventoryRecord.md │ │ ├── ProductLink.md │ │ ├── ProductMgr.md │ │ ├── ProductOption.md │ │ ├── ProductOptionModel.md │ │ ├── ProductOptionValue.md │ │ ├── ProductPriceInfo.md │ │ ├── ProductPriceModel.md │ │ ├── ProductPriceTable.md │ │ ├── ProductSearchHit.md │ │ ├── ProductSearchModel.md │ │ ├── ProductSearchRefinementDefinition.md │ │ ├── ProductSearchRefinements.md │ │ ├── ProductSearchRefinementValue.md │ │ ├── ProductVariationAttribute.md │ │ ├── ProductVariationAttributeValue.md │ │ ├── ProductVariationModel.md │ │ ├── Recommendation.md │ │ ├── SearchModel.md │ │ ├── SearchRefinementDefinition.md │ │ ├── SearchRefinements.md │ │ ├── SearchRefinementValue.md │ │ ├── SortingOption.md │ │ ├── SortingRule.md │ │ ├── Store.md │ │ ├── StoreGroup.md │ │ ├── StoreInventoryFilter.md │ │ ├── StoreInventoryFilterValue.md │ │ ├── StoreMgr.md │ │ ├── Variant.md │ │ └── VariationGroup.md │ ├── dw_content │ │ ├── Content.md │ │ ├── ContentMgr.md │ │ ├── ContentSearchModel.md │ │ ├── ContentSearchRefinementDefinition.md │ │ ├── ContentSearchRefinements.md │ │ ├── ContentSearchRefinementValue.md │ │ ├── Folder.md │ │ ├── Library.md │ │ ├── MarkupText.md │ │ └── MediaFile.md │ ├── dw_crypto │ │ ├── CertificateRef.md │ │ ├── CertificateUtils.md │ │ ├── Cipher.md │ │ ├── Encoding.md │ │ ├── JWE.md │ │ ├── JWEHeader.md │ │ ├── JWS.md │ │ ├── JWSHeader.md │ │ ├── KeyRef.md │ │ ├── Mac.md │ │ ├── MessageDigest.md │ │ ├── SecureRandom.md │ │ ├── Signature.md │ │ ├── WeakCipher.md │ │ ├── WeakMac.md │ │ ├── WeakMessageDigest.md │ │ ├── WeakSignature.md │ │ └── X509Certificate.md │ ├── dw_customer │ │ ├── AddressBook.md │ │ ├── AgentUserMgr.md │ │ ├── AgentUserStatusCodes.md │ │ ├── AuthenticationStatus.md │ │ ├── Credentials.md │ │ ├── Customer.md │ │ ├── CustomerActiveData.md │ │ ├── CustomerAddress.md │ │ ├── CustomerCDPData.md │ │ ├── CustomerContextMgr.md │ │ ├── CustomerGroup.md │ │ ├── CustomerList.md │ │ ├── CustomerMgr.md │ │ ├── CustomerPasswordConstraints.md │ │ ├── CustomerPaymentInstrument.md │ │ ├── CustomerStatusCodes.md │ │ ├── EncryptedObject.md │ │ ├── ExternalProfile.md │ │ ├── OrderHistory.md │ │ ├── ProductList.md │ │ ├── ProductListItem.md │ │ ├── ProductListItemPurchase.md │ │ ├── ProductListMgr.md │ │ ├── ProductListRegistrant.md │ │ ├── Profile.md │ │ └── Wallet.md │ ├── dw_extensions.applepay │ │ ├── ApplePayHookResult.md │ │ └── ApplePayHooks.md │ ├── dw_extensions.facebook │ │ ├── FacebookFeedHooks.md │ │ └── FacebookProduct.md │ ├── dw_extensions.paymentrequest │ │ ├── PaymentRequestHookResult.md │ │ └── PaymentRequestHooks.md │ ├── dw_extensions.payments │ │ ├── SalesforceBancontactPaymentDetails.md │ │ ├── SalesforceCardPaymentDetails.md │ │ ├── SalesforceEpsPaymentDetails.md │ │ ├── SalesforceIdealPaymentDetails.md │ │ ├── SalesforceKlarnaPaymentDetails.md │ │ ├── SalesforcePaymentDetails.md │ │ ├── SalesforcePaymentIntent.md │ │ ├── SalesforcePaymentMethod.md │ │ ├── SalesforcePaymentRequest.md │ │ ├── SalesforcePaymentsHooks.md │ │ ├── SalesforcePaymentsMgr.md │ │ ├── SalesforcePaymentsSiteConfiguration.md │ │ ├── SalesforcePayPalOrder.md │ │ ├── SalesforcePayPalOrderAddress.md │ │ ├── SalesforcePayPalOrderPayer.md │ │ ├── SalesforcePayPalPaymentDetails.md │ │ ├── SalesforceSepaDebitPaymentDetails.md │ │ └── SalesforceVenmoPaymentDetails.md │ ├── dw_extensions.pinterest │ │ ├── PinterestAvailability.md │ │ ├── PinterestFeedHooks.md │ │ ├── PinterestOrder.md │ │ ├── PinterestOrderHooks.md │ │ └── PinterestProduct.md │ ├── dw_io │ │ ├── CSVStreamReader.md │ │ ├── CSVStreamWriter.md │ │ ├── File.md │ │ ├── FileReader.md │ │ ├── FileWriter.md │ │ ├── InputStream.md │ │ ├── OutputStream.md │ │ ├── PrintWriter.md │ │ ├── RandomAccessFileReader.md │ │ ├── Reader.md │ │ ├── StringWriter.md │ │ ├── Writer.md │ │ ├── XMLIndentingStreamWriter.md │ │ ├── XMLStreamConstants.md │ │ ├── XMLStreamReader.md │ │ └── XMLStreamWriter.md │ ├── dw_job │ │ ├── JobExecution.md │ │ └── JobStepExecution.md │ ├── dw_net │ │ ├── FTPClient.md │ │ ├── FTPFileInfo.md │ │ ├── HTTPClient.md │ │ ├── HTTPRequestPart.md │ │ ├── Mail.md │ │ ├── SFTPClient.md │ │ ├── SFTPFileInfo.md │ │ ├── WebDAVClient.md │ │ └── WebDAVFileInfo.md │ ├── dw_object │ │ ├── ActiveData.md │ │ ├── CustomAttributes.md │ │ ├── CustomObject.md │ │ ├── CustomObjectMgr.md │ │ ├── Extensible.md │ │ ├── ExtensibleObject.md │ │ ├── Note.md │ │ ├── ObjectAttributeDefinition.md │ │ ├── ObjectAttributeGroup.md │ │ ├── ObjectAttributeValueDefinition.md │ │ ├── ObjectTypeDefinition.md │ │ ├── PersistentObject.md │ │ ├── SimpleExtensible.md │ │ └── SystemObjectMgr.md │ ├── dw_order │ │ ├── AbstractItem.md │ │ ├── AbstractItemCtnr.md │ │ ├── Appeasement.md │ │ ├── AppeasementItem.md │ │ ├── Basket.md │ │ ├── BasketMgr.md │ │ ├── BonusDiscountLineItem.md │ │ ├── CouponLineItem.md │ │ ├── CreateAgentBasketLimitExceededException.md │ │ ├── CreateBasketFromOrderException.md │ │ ├── CreateCouponLineItemException.md │ │ ├── CreateOrderException.md │ │ ├── CreateTemporaryBasketLimitExceededException.md │ │ ├── GiftCertificate.md │ │ ├── GiftCertificateLineItem.md │ │ ├── GiftCertificateMgr.md │ │ ├── GiftCertificateStatusCodes.md │ │ ├── Invoice.md │ │ ├── InvoiceItem.md │ │ ├── LineItem.md │ │ ├── LineItemCtnr.md │ │ ├── Order.md │ │ ├── OrderAddress.md │ │ ├── OrderItem.md │ │ ├── OrderMgr.md │ │ ├── OrderPaymentInstrument.md │ │ ├── OrderProcessStatusCodes.md │ │ ├── PaymentCard.md │ │ ├── PaymentInstrument.md │ │ ├── PaymentMethod.md │ │ ├── PaymentMgr.md │ │ ├── PaymentProcessor.md │ │ ├── PaymentStatusCodes.md │ │ ├── PaymentTransaction.md │ │ ├── PriceAdjustment.md │ │ ├── PriceAdjustmentLimitTypes.md │ │ ├── ProductLineItem.md │ │ ├── ProductShippingCost.md │ │ ├── ProductShippingLineItem.md │ │ ├── ProductShippingModel.md │ │ ├── Return.md │ │ ├── ReturnCase.md │ │ ├── ReturnCaseItem.md │ │ ├── ReturnItem.md │ │ ├── Shipment.md │ │ ├── ShipmentShippingCost.md │ │ ├── ShipmentShippingModel.md │ │ ├── ShippingLineItem.md │ │ ├── ShippingLocation.md │ │ ├── ShippingMethod.md │ │ ├── ShippingMgr.md │ │ ├── ShippingOrder.md │ │ ├── ShippingOrderItem.md │ │ ├── SumItem.md │ │ ├── TaxGroup.md │ │ ├── TaxItem.md │ │ ├── TaxMgr.md │ │ ├── TrackingInfo.md │ │ └── TrackingRef.md │ ├── dw_order.hooks │ │ ├── CalculateHooks.md │ │ ├── OrderHooks.md │ │ ├── PaymentHooks.md │ │ ├── ReturnHooks.md │ │ └── ShippingOrderHooks.md │ ├── dw_rpc │ │ ├── SOAPUtil.md │ │ ├── Stub.md │ │ └── WebReference.md │ ├── dw_suggest │ │ ├── BrandSuggestions.md │ │ ├── CategorySuggestions.md │ │ ├── ContentSuggestions.md │ │ ├── CustomSuggestions.md │ │ ├── ProductSuggestions.md │ │ ├── SearchPhraseSuggestions.md │ │ ├── SuggestedCategory.md │ │ ├── SuggestedContent.md │ │ ├── SuggestedPhrase.md │ │ ├── SuggestedProduct.md │ │ ├── SuggestedTerm.md │ │ ├── SuggestedTerms.md │ │ ├── Suggestions.md │ │ └── SuggestModel.md │ ├── dw_svc │ │ ├── FTPService.md │ │ ├── FTPServiceDefinition.md │ │ ├── HTTPFormService.md │ │ ├── HTTPFormServiceDefinition.md │ │ ├── HTTPService.md │ │ ├── HTTPServiceDefinition.md │ │ ├── LocalServiceRegistry.md │ │ ├── Result.md │ │ ├── Service.md │ │ ├── ServiceCallback.md │ │ ├── ServiceConfig.md │ │ ├── ServiceCredential.md │ │ ├── ServiceDefinition.md │ │ ├── ServiceProfile.md │ │ ├── ServiceRegistry.md │ │ ├── SOAPService.md │ │ └── SOAPServiceDefinition.md │ ├── dw_system │ │ ├── AgentUserStatusCodes.md │ │ ├── Cache.md │ │ ├── CacheMgr.md │ │ ├── HookMgr.md │ │ ├── InternalObject.md │ │ ├── JobProcessMonitor.md │ │ ├── Log.md │ │ ├── Logger.md │ │ ├── LogNDC.md │ │ ├── OrganizationPreferences.md │ │ ├── Pipeline.md │ │ ├── PipelineDictionary.md │ │ ├── RemoteInclude.md │ │ ├── Request.md │ │ ├── RequestHooks.md │ │ ├── Response.md │ │ ├── RESTErrorResponse.md │ │ ├── RESTResponseMgr.md │ │ ├── RESTSuccessResponse.md │ │ ├── SearchStatus.md │ │ ├── Session.md │ │ ├── Site.md │ │ ├── SitePreferences.md │ │ ├── Status.md │ │ ├── StatusItem.md │ │ ├── System.md │ │ └── Transaction.md │ ├── dw_util │ │ ├── ArrayList.md │ │ ├── Assert.md │ │ ├── BigInteger.md │ │ ├── Bytes.md │ │ ├── Calendar.md │ │ ├── Collection.md │ │ ├── Currency.md │ │ ├── DateUtils.md │ │ ├── Decimal.md │ │ ├── FilteringCollection.md │ │ ├── Geolocation.md │ │ ├── HashMap.md │ │ ├── HashSet.md │ │ ├── Iterator.md │ │ ├── LinkedHashMap.md │ │ ├── LinkedHashSet.md │ │ ├── List.md │ │ ├── Locale.md │ │ ├── Map.md │ │ ├── MapEntry.md │ │ ├── MappingKey.md │ │ ├── MappingMgr.md │ │ ├── PropertyComparator.md │ │ ├── SecureEncoder.md │ │ ├── SecureFilter.md │ │ ├── SeekableIterator.md │ │ ├── Set.md │ │ ├── SortedMap.md │ │ ├── SortedSet.md │ │ ├── StringUtils.md │ │ ├── Template.md │ │ └── UUIDUtils.md │ ├── dw_value │ │ ├── EnumValue.md │ │ ├── MimeEncodedText.md │ │ ├── Money.md │ │ └── Quantity.md │ ├── dw_web │ │ ├── ClickStream.md │ │ ├── ClickStreamEntry.md │ │ ├── Cookie.md │ │ ├── Cookies.md │ │ ├── CSRFProtection.md │ │ ├── Form.md │ │ ├── FormAction.md │ │ ├── FormElement.md │ │ ├── FormElementValidationResult.md │ │ ├── FormField.md │ │ ├── FormFieldOption.md │ │ ├── FormFieldOptions.md │ │ ├── FormGroup.md │ │ ├── FormList.md │ │ ├── FormListItem.md │ │ ├── Forms.md │ │ ├── HttpParameter.md │ │ ├── HttpParameterMap.md │ │ ├── LoopIterator.md │ │ ├── PageMetaData.md │ │ ├── PageMetaTag.md │ │ ├── PagingModel.md │ │ ├── Resource.md │ │ ├── URL.md │ │ ├── URLAction.md │ │ ├── URLParameter.md │ │ ├── URLRedirect.md │ │ ├── URLRedirectMgr.md │ │ └── URLUtils.md │ ├── sfra │ │ ├── account.md │ │ ├── address.md │ │ ├── billing.md │ │ ├── cart.md │ │ ├── categories.md │ │ ├── content.md │ │ ├── locale.md │ │ ├── order.md │ │ ├── payment.md │ │ ├── price-default.md │ │ ├── price-range.md │ │ ├── price-tiered.md │ │ ├── product-bundle.md │ │ ├── product-full.md │ │ ├── product-line-items.md │ │ ├── product-search.md │ │ ├── product-tile.md │ │ ├── querystring.md │ │ ├── render.md │ │ ├── request.md │ │ ├── response.md │ │ ├── server.md │ │ ├── shipping.md │ │ ├── store.md │ │ ├── stores.md │ │ └── totals.md │ └── TopLevel │ ├── APIException.md │ ├── arguments.md │ ├── Array.md │ ├── ArrayBuffer.md │ ├── BigInt.md │ ├── Boolean.md │ ├── ConversionError.md │ ├── DataView.md │ ├── Date.md │ ├── Error.md │ ├── ES6Iterator.md │ ├── EvalError.md │ ├── Fault.md │ ├── Float32Array.md │ ├── Float64Array.md │ ├── Function.md │ ├── Generator.md │ ├── global.md │ ├── Int16Array.md │ ├── Int32Array.md │ ├── Int8Array.md │ ├── InternalError.md │ ├── IOError.md │ ├── Iterable.md │ ├── Iterator.md │ ├── JSON.md │ ├── Map.md │ ├── Math.md │ ├── Module.md │ ├── Namespace.md │ ├── Number.md │ ├── Object.md │ ├── QName.md │ ├── RangeError.md │ ├── ReferenceError.md │ ├── RegExp.md │ ├── Set.md │ ├── StopIteration.md │ ├── String.md │ ├── Symbol.md │ ├── SyntaxError.md │ ├── SystemError.md │ ├── TypeError.md │ ├── Uint16Array.md │ ├── Uint32Array.md │ ├── Uint8Array.md │ ├── Uint8ClampedArray.md │ ├── URIError.md │ ├── WeakMap.md │ ├── WeakSet.md │ ├── XML.md │ ├── XMLList.md │ └── XMLStreamError.md ├── docs-site │ ├── .gitignore │ ├── App.tsx │ ├── components │ │ ├── Badge.tsx │ │ ├── BreadcrumbSchema.tsx │ │ ├── CodeBlock.tsx │ │ ├── Collapsible.tsx │ │ ├── ConfigBuilder.tsx │ │ ├── ConfigHero.tsx │ │ ├── ConfigModeTabs.tsx │ │ ├── icons.tsx │ │ ├── Layout.tsx │ │ ├── LightCodeContainer.tsx │ │ ├── NewcomerCTA.tsx │ │ ├── NextStepsStrip.tsx │ │ ├── OnThisPage.tsx │ │ ├── Search.tsx │ │ ├── SEO.tsx │ │ ├── Sidebar.tsx │ │ ├── StructuredData.tsx │ │ ├── ToolCard.tsx │ │ ├── ToolFilters.tsx │ │ ├── Typography.tsx │ │ └── VersionBadge.tsx │ ├── constants.tsx │ ├── index.html │ ├── main.tsx │ ├── metadata.json │ ├── package-lock.json │ ├── package.json │ ├── pages │ │ ├── AIInterfacesPage.tsx │ │ ├── ConfigurationPage.tsx │ │ ├── DevelopmentPage.tsx │ │ ├── ExamplesPage.tsx │ │ ├── FeaturesPage.tsx │ │ ├── HomePage.tsx │ │ ├── SecurityPage.tsx │ │ ├── ToolsPage.tsx │ │ └── TroubleshootingPage.tsx │ ├── postcss.config.js │ ├── public │ │ ├── .well-known │ │ │ └── security.txt │ │ ├── 404.html │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── explain-product-pricing-methods-no-mcp.png │ │ ├── explain-product-pricing-methods.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── llms.txt │ │ ├── robots.txt │ │ ├── site.webmanifest │ │ └── sitemap.xml │ ├── README.md │ ├── scripts │ │ ├── generate-search-index.js │ │ ├── generate-sitemap.js │ │ └── search-dev.js │ ├── src │ │ └── styles │ │ ├── input.css │ │ └── prism-theme.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── types.ts │ ├── utils │ │ ├── search.ts │ │ └── toolsData.ts │ └── vite.config.ts ├── eslint.config.js ├── jest.config.js ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── scripts │ └── convert-docs.js ├── SECURITY.md ├── server.json ├── src │ ├── clients │ │ ├── base │ │ │ ├── http-client.ts │ │ │ ├── oauth-token.ts │ │ │ └── ocapi-auth-client.ts │ │ ├── best-practices-client.ts │ │ ├── cartridge-generation-client.ts │ │ ├── docs │ │ │ ├── class-content-parser.ts │ │ │ ├── class-name-resolver.ts │ │ │ ├── documentation-scanner.ts │ │ │ ├── index.ts │ │ │ └── referenced-types-extractor.ts │ │ ├── docs-client.ts │ │ ├── log-client.ts │ │ ├── logs │ │ │ ├── index.ts │ │ │ ├── log-analyzer.ts │ │ │ ├── log-client.ts │ │ │ ├── log-constants.ts │ │ │ ├── log-file-discovery.ts │ │ │ ├── log-file-reader.ts │ │ │ ├── log-formatter.ts │ │ │ ├── log-processor.ts │ │ │ ├── log-types.ts │ │ │ └── webdav-client-manager.ts │ │ ├── ocapi │ │ │ ├── code-versions-client.ts │ │ │ ├── site-preferences-client.ts │ │ │ └── system-objects-client.ts │ │ ├── ocapi-client.ts │ │ └── sfra-client.ts │ ├── config │ │ ├── configuration-factory.ts │ │ └── dw-json-loader.ts │ ├── core │ │ ├── handlers │ │ │ ├── abstract-log-tool-handler.ts │ │ │ ├── base-handler.ts │ │ │ ├── best-practices-handler.ts │ │ │ ├── cartridge-handler.ts │ │ │ ├── client-factory.ts │ │ │ ├── code-version-handler.ts │ │ │ ├── docs-handler.ts │ │ │ ├── job-log-handler.ts │ │ │ ├── job-log-tool-config.ts │ │ │ ├── log-handler.ts │ │ │ ├── log-tool-config.ts │ │ │ ├── sfra-handler.ts │ │ │ ├── system-object-handler.ts │ │ │ └── validation-helpers.ts │ │ ├── server.ts │ │ └── tool-definitions.ts │ ├── index.ts │ ├── main.ts │ ├── services │ │ ├── file-system-service.ts │ │ ├── index.ts │ │ └── path-service.ts │ ├── tool-configs │ │ ├── best-practices-tool-config.ts │ │ ├── cartridge-tool-config.ts │ │ ├── code-version-tool-config.ts │ │ ├── docs-tool-config.ts │ │ ├── job-log-tool-config.ts │ │ ├── log-tool-config.ts │ │ ├── sfra-tool-config.ts │ │ └── system-object-tool-config.ts │ ├── types │ │ └── types.ts │ └── utils │ ├── cache.ts │ ├── job-log-tool-config.ts │ ├── job-log-utils.ts │ ├── log-cache.ts │ ├── log-tool-config.ts │ ├── log-tool-constants.ts │ ├── log-tool-utils.ts │ ├── logger.ts │ ├── ocapi-url-builder.ts │ ├── path-resolver.ts │ ├── query-builder.ts │ ├── utils.ts │ └── validator.ts ├── tests │ ├── __mocks__ │ │ ├── docs-client.ts │ │ ├── src │ │ │ └── clients │ │ │ └── base │ │ │ └── http-client.js │ │ └── webdav.js │ ├── base-handler.test.ts │ ├── base-http-client.test.ts │ ├── best-practices-handler.test.ts │ ├── cache.test.ts │ ├── cartridge-handler.test.ts │ ├── class-content-parser.test.ts │ ├── class-name-resolver.test.ts │ ├── client-factory.test.ts │ ├── code-version-handler.test.ts │ ├── code-versions-client.test.ts │ ├── config.test.ts │ ├── configuration-factory.test.ts │ ├── docs-handler.test.ts │ ├── documentation-scanner.test.ts │ ├── file-system-service.test.ts │ ├── job-log-handler.test.ts │ ├── job-log-utils.test.ts │ ├── log-client.test.ts │ ├── log-handler.test.ts │ ├── log-processor.test.ts │ ├── logger.test.ts │ ├── mcp │ │ ├── AGENTS.md │ │ ├── node │ │ │ ├── activate-code-version-advanced.full-mode.programmatic.test.js │ │ │ ├── code-versions.full-mode.programmatic.test.js │ │ │ ├── generate-cartridge-structure.docs-only.programmatic.test.js │ │ │ ├── get-available-best-practice-guides.docs-only.programmatic.test.js │ │ │ ├── get-available-sfra-documents.programmatic.test.js │ │ │ ├── get-best-practice-guide.docs-only.programmatic.test.js │ │ │ ├── get-hook-reference.docs-only.programmatic.test.js │ │ │ ├── get-job-execution-summary.full-mode.programmatic.test.js │ │ │ ├── get-job-log-entries.full-mode.programmatic.test.js │ │ │ ├── get-latest-debug.full-mode.programmatic.test.js │ │ │ ├── get-latest-error.full-mode.programmatic.test.js │ │ │ ├── get-latest-info.full-mode.programmatic.test.js │ │ │ ├── get-latest-job-log-files.full-mode.programmatic.test.js │ │ │ ├── get-latest-warn.full-mode.programmatic.test.js │ │ │ ├── get-log-file-contents.full-mode.programmatic.test.js │ │ │ ├── get-sfcc-class-documentation.docs-only.programmatic.test.js │ │ │ ├── get-sfcc-class-info.docs-only.programmatic.test.js │ │ │ ├── get-sfra-categories.docs-only.programmatic.test.js │ │ │ ├── get-sfra-document.programmatic.test.js │ │ │ ├── get-sfra-documents-by-category.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definition.full-mode.programmatic.test.js │ │ │ ├── get-system-object-definitions.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definitions.full-mode.programmatic.test.js │ │ │ ├── list-log-files.full-mode.programmatic.test.js │ │ │ ├── list-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-best-practices.docs-only.programmatic.test.js │ │ │ ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-job-logs-by-name.full-mode.programmatic.test.js │ │ │ ├── search-job-logs.full-mode.programmatic.test.js │ │ │ ├── search-logs.full-mode.programmatic.test.js │ │ │ ├── search-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-sfcc-methods.docs-only.programmatic.test.js │ │ │ ├── search-sfra-documentation.docs-only.programmatic.test.js │ │ │ ├── search-site-preferences.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-groups.full-mode.programmatic.test.js │ │ │ ├── summarize-logs.full-mode.programmatic.test.js │ │ │ ├── tools.docs-only.programmatic.test.js │ │ │ └── tools.full-mode.programmatic.test.js │ │ ├── README.md │ │ ├── test-fixtures │ │ │ └── dw.json │ │ └── yaml │ │ ├── activate-code-version.docs-only.test.mcp.yml │ │ ├── activate-code-version.full-mode.test.mcp.yml │ │ ├── get_latest_error.test.mcp.yml │ │ ├── get-available-best-practice-guides.docs-only.test.mcp.yml │ │ ├── get-available-best-practice-guides.full-mode.test.mcp.yml │ │ ├── get-available-sfra-documents.docs-only.test.mcp.yml │ │ ├── get-available-sfra-documents.full-mode.test.mcp.yml │ │ ├── get-best-practice-guide.docs-only.test.mcp.yml │ │ ├── get-best-practice-guide.full-mode.test.mcp.yml │ │ ├── get-code-versions.docs-only.test.mcp.yml │ │ ├── get-code-versions.full-mode.test.mcp.yml │ │ ├── get-hook-reference.docs-only.test.mcp.yml │ │ ├── get-hook-reference.full-mode.test.mcp.yml │ │ ├── get-job-execution-summary.full-mode.test.mcp.yml │ │ ├── get-job-log-entries.full-mode.test.mcp.yml │ │ ├── get-latest-debug.full-mode.test.mcp.yml │ │ ├── get-latest-error.full-mode.test.mcp.yml │ │ ├── get-latest-info.full-mode.test.mcp.yml │ │ ├── get-latest-job-log-files.full-mode.test.mcp.yml │ │ ├── get-latest-warn.full-mode.test.mcp.yml │ │ ├── get-log-file-contents.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-documentation.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-documentation.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-info.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-info.full-mode.test.mcp.yml │ │ ├── get-sfra-categories.docs-only.test.mcp.yml │ │ ├── get-sfra-categories.full-mode.test.mcp.yml │ │ ├── get-sfra-document.docs-only.test.mcp.yml │ │ ├── get-sfra-document.full-mode.test.mcp.yml │ │ ├── get-sfra-documents-by-category.docs-only.test.mcp.yml │ │ ├── get-sfra-documents-by-category.full-mode.test.mcp.yml │ │ ├── get-system-object-definition.docs-only.test.mcp.yml │ │ ├── get-system-object-definition.full-mode.test.mcp.yml │ │ ├── get-system-object-definitions.docs-only.test.mcp.yml │ │ ├── get-system-object-definitions.full-mode.test.mcp.yml │ │ ├── list-log-files.full-mode.test.mcp.yml │ │ ├── list-sfcc-classes.docs-only.test.mcp.yml │ │ ├── list-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-best-practices.docs-only.test.mcp.yml │ │ ├── search-best-practices.full-mode.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.test.mcp.yml │ │ ├── search-job-logs-by-name.full-mode.test.mcp.yml │ │ ├── search-job-logs.full-mode.test.mcp.yml │ │ ├── search-logs.full-mode.test.mcp.yml │ │ ├── search-sfcc-classes.docs-only.test.mcp.yml │ │ ├── search-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-sfcc-methods.docs-only.test.mcp.yml │ │ ├── search-sfcc-methods.full-mode.test.mcp.yml │ │ ├── search-sfra-documentation.docs-only.test.mcp.yml │ │ ├── search-sfra-documentation.full-mode.test.mcp.yml │ │ ├── search-site-preferences.docs-only.test.mcp.yml │ │ ├── search-site-preferences.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-groups.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-groups.full-mode.test.mcp.yml │ │ ├── summarize-logs.full-mode.test.mcp.yml │ │ ├── tools.docs-only.test.mcp.yml │ │ └── tools.full-mode.test.mcp.yml │ ├── oauth-token.test.ts │ ├── ocapi-auth-client.test.ts │ ├── ocapi-client.test.ts │ ├── path-service.test.ts │ ├── query-builder.test.ts │ ├── referenced-types-extractor.test.ts │ ├── servers │ │ ├── sfcc-mock-server │ │ │ ├── mock-data │ │ │ │ └── ocapi │ │ │ │ ├── code-versions.json │ │ │ │ ├── custom-object-attributes-customapi.json │ │ │ │ ├── custom-object-attributes-globalsettings.json │ │ │ │ ├── custom-object-attributes-versionhistory.json │ │ │ │ ├── site-preferences-ccv.json │ │ │ │ ├── site-preferences-fastforward.json │ │ │ │ ├── site-preferences-sfra.json │ │ │ │ ├── site-preferences-storefront.json │ │ │ │ ├── site-preferences-system.json │ │ │ │ ├── system-object-attribute-groups-campaign.json │ │ │ │ ├── system-object-attribute-groups-category.json │ │ │ │ ├── system-object-attribute-groups-order.json │ │ │ │ ├── system-object-attribute-groups-product.json │ │ │ │ ├── system-object-attribute-groups-sitepreferences.json │ │ │ │ ├── system-object-attributes-customeraddress.json │ │ │ │ ├── system-object-attributes-product-expanded.json │ │ │ │ ├── system-object-attributes-product.json │ │ │ │ ├── system-object-definition-category.json │ │ │ │ ├── system-object-definition-customer.json │ │ │ │ ├── system-object-definition-customeraddress.json │ │ │ │ ├── system-object-definition-order.json │ │ │ │ ├── system-object-definition-product.json │ │ │ │ ├── system-object-definitions-old.json │ │ │ │ └── system-object-definitions.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── scripts │ │ │ │ └── setup-logs.js │ │ │ ├── server.js │ │ │ └── src │ │ │ ├── app.js │ │ │ ├── config │ │ │ │ └── server-config.js │ │ │ ├── middleware │ │ │ │ ├── auth.js │ │ │ │ ├── cors.js │ │ │ │ └── logging.js │ │ │ ├── routes │ │ │ │ ├── ocapi │ │ │ │ │ ├── code-versions-handler.js │ │ │ │ │ ├── oauth-handler.js │ │ │ │ │ ├── ocapi-error-utils.js │ │ │ │ │ ├── ocapi-utils.js │ │ │ │ │ ├── site-preferences-handler.js │ │ │ │ │ └── system-objects-handler.js │ │ │ │ ├── ocapi.js │ │ │ │ └── webdav.js │ │ │ └── utils │ │ │ ├── mock-data-loader.js │ │ │ └── webdav-xml.js │ │ └── sfcc-mock-server-manager.ts │ ├── sfcc-mock-server.test.ts │ ├── site-preferences-client.test.ts │ ├── system-objects-client.test.ts │ ├── utils.test.ts │ ├── validation-helpers.test.ts │ └── validator.test.ts ├── tsconfig.json └── tsconfig.test.json ``` # Files -------------------------------------------------------------------------------- /docs/dw_system/Transaction.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.system # Class Transaction ## Inheritance Hierarchy - Object - dw.system.Transaction ## Description Represents the current transaction. A transaction provides a context for performing atomic changes to persistent business objects. Before a business object can be created, changed, or deleted, a transaction must be started using the begin() method. All changes on the touched business objects will only be made durable when the transaction is committed with commit(). If a transaction is rolled back, all changes so far will be reverted and the business object will have their previous state again. It is possible to begin a transaction multiple times in a nested way (like begin-begin-commit-commit). In this case, in order to commit the changes the commit method must be called symmetrically as often as begin. It is also possible to run multiple transactions within a single request, one after another (like begin-commit-begin-commit). In case of any exception while working with business objects inside of a transaction, the transaction cannot be committed anymore, but only be rolled back. Business code may try to take appropriate actions if it expects business-related problems at commit (for example, constraint violations). When a transaction is still open at the end of a pipeline call, controller call, or job step, the remaining changes are committed unless an exception is thrown. The following best practices exist for using transactions: Avoid long running transactions in jobs. Use one transaction for changes that belong together and need a joint rollback. In most cases, one transaction for all changes in a request is better than multiple transactions for each individual object. Don’t begin and commit a huge number of small transactions in a loop. Avoid changing the same objects in parallel transactions. Example 1 - explicit control: var txn = require('dw/system/Transaction'); txn.begin(); // work with business objects here txn.commit(); Example 2 - implicit control: var txn = require('dw/system/Transaction'); txn.wrap(function(){ // work with business objects here }); ## Constructor Summary ## Method Summary ### begin **Signature:** `static begin() : void` Begins a transaction. ### commit **Signature:** `static commit() : void` Commits the current transaction. ### rollback **Signature:** `static rollback() : void` Rolls back the current transaction. ### wrap **Signature:** `static wrap(callback : Function) : Object` Encloses the provided callback function in a begin-commit transactional context. ## Method Detail ## Method Details ### begin **Signature:** `static begin() : void` **Description:** Begins a transaction. --- ### commit **Signature:** `static commit() : void` **Description:** Commits the current transaction. The transaction must have been started with begin() before. --- ### rollback **Signature:** `static rollback() : void` **Description:** Rolls back the current transaction. The transaction must have been started with begin() before. --- ### wrap **Signature:** `static wrap(callback : Function) : Object` **Description:** Encloses the provided callback function in a begin-commit transactional context. If the transaction cannot be committed successfully, it is rolled back instead and an exception is thrown. **Parameters:** - `callback`: a function that should be executed within a transactional context **Returns:** the result of the callback function, if it returns something --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/AgentUserMgr.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.customer # Class AgentUserMgr ## Inheritance Hierarchy - Object - dw.customer.AgentUserMgr ## Description Provides helper methods for handling agent user functionality (login and logout) Pay attention to appropriate legal and regulatory requirements related to this functionality. ## Constructor Summary ## Method Summary ### loginAgentUser **Signature:** `static loginAgentUser(login : String, password : String) : Status` Logs in an agent user (which for example is authorized to login on-behalf of a customer for instance to place an order). ### loginOnBehalfOfCustomer **Signature:** `static loginOnBehalfOfCustomer(customer : Customer) : Status` This method logs the specified customer into the current session if the current agent user has the functional permission 'Login_On_Behalf' in the current site. ### logoutAgentUser **Signature:** `static logoutAgentUser() : Status` Performs a logout of the agent user and the current customer which are attached to the current session. ## Method Detail ## Method Details ### loginAgentUser **Signature:** `static loginAgentUser(login : String, password : String) : Status` **Description:** Logs in an agent user (which for example is authorized to login on-behalf of a customer for instance to place an order). The login is only allowed during a secure protocol request (https) and only in the storefront context. The user must have the permission 'Login_Agent'. When the login is successful, a new session will be created. Any objects that need to be preserved in the session need to bet set on the session afterwards. A Status object is returned which signals whether the login was successful or not. In case of a login failure the status object contains the reason for this. See AgentUserStatusCodes for more information. **Parameters:** - `login`: the login name for the agent user. - `password`: the password for the agent user. **Returns:** the login status (OK if successful, error code otherwise). --- ### loginOnBehalfOfCustomer **Signature:** `static loginOnBehalfOfCustomer(customer : Customer) : Status` **Description:** This method logs the specified customer into the current session if the current agent user has the functional permission 'Login_On_Behalf' in the current site. The dwcustomer cookie will not be set. The login is only allowed during a secure protocol request (https). A Status object is returned indicating whether the login was successful or not (and indicating the failure reason). See AgentUserStatusCodes for more information. Error conditions include: if the method is not called in the storefront context if the given customer is not a registered customer (anonymous) if the given customer is not registered for the current site if the given customer is disabled if there is no agent user at the current session if the agent user is not logged in if the agent user has not the functional permission 'Login_On_Behalf' **Parameters:** - `customer`: The customer, which should be logged instead of the agent user **Returns:** the login status (OK if successful, error code otherwise). --- ### logoutAgentUser **Signature:** `static logoutAgentUser() : Status` **Description:** Performs a logout of the agent user and the current customer which are attached to the current session. The logout is only allowed during a secure protocol request (https) and only in the storefront context. **Returns:** the logout status (OK if successful, error code otherwise). --- ``` -------------------------------------------------------------------------------- /docs/sfra/price-default.md: -------------------------------------------------------------------------------- ```markdown # SFRA Default Price Model ## Overview The Default Price model represents standard product pricing in SFRA applications. It provides formatted price information including sales prices, list prices, and currency details for product display and calculations. ## Constructor ```javascript function DefaultPrice(salesPrice, listPrice) ``` Creates a Default Price model instance with sales and list pricing information. ### Parameters - `salesPrice` (dw.value.Money) - Sales price from the API - `listPrice` (dw.value.Money) - List price from the API (optional) ## Properties ### sales **Type:** Object Sales price information containing: - `value` (number | null) - Decimal price value - `currency` (string | null) - Currency code - `formatted` (string | null) - Formatted price string for display - `decimalPrice` (string | null) - Decimal price as string ### list **Type:** Object | null List price information (same structure as sales) or null if no list price provided. ## Helper Functions ### toPriceModel(price) Converts an API Money object to a structured price model. **Parameters:** - `price` (dw.value.Money) - Price object from the API **Returns:** Object - Formatted price object with value, currency, formatted string, and decimal price ## Price Object Structure Each price object (sales/list) contains: ### value Raw numeric price value for calculations and comparisons. ### currency Three-letter currency code (e.g., "USD", "EUR"). ### formatted User-friendly formatted price string with currency symbol (e.g., "$29.99"). ### decimalPrice String representation of the decimal price for precise display. ## Usage Example ```javascript var DefaultPrice = require('*/cartridge/models/price/default'); // Get prices from product var product = ProductMgr.getProduct('product-id'); var salesPrice = product.getPriceModel().getPrice(); var listPrice = product.getPriceModel().getPriceBookPrice('list-price-book'); var price = new DefaultPrice(salesPrice, listPrice); // Access sales price console.log(price.sales.formatted); // "$29.99" console.log(price.sales.value); // 29.99 console.log(price.sales.currency); // "USD" // Check for list price (original/MSRP) if (price.list) { console.log('Was: ' + price.list.formatted); console.log('Now: ' + price.sales.formatted); // Calculate discount percentage var discount = ((price.list.value - price.sales.value) / price.list.value) * 100; console.log('Save ' + discount.toFixed(0) + '%'); } ``` ## Price Availability The model handles unavailable prices gracefully: - When price is not available, all properties return null - Prevents errors when price calculation fails - Provides consistent structure regardless of price availability ## Currency Support The model supports international pricing: - Currency codes from the Money object - Formatted strings respect locale settings - Decimal representation for precise calculations ## Notes - Handles both sales and list prices - Gracefully manages unavailable prices with null values - Provides multiple price formats for different use cases - Supports international currencies and formatting - List price is optional (may be null) - Decimal price provides precise string representation ## Related Models - **Tiered Price Model** - For quantity-based pricing - **Range Price Model** - For price range displays - **Product Models** - Use price models for product pricing - **Cart Models** - Use price models for line item pricing ``` -------------------------------------------------------------------------------- /src/utils/job-log-tool-config.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSpec, LogToolValidators } from './log-tool-utils.js'; import { ValidationHelpers, CommonValidations } from '../core/handlers/validation-helpers.js'; import { JobLogToolName, getLimit } from './log-tool-constants.js'; import { JobLogValidators, JobLogFormatters } from './job-log-utils.js'; /** * Configuration for job log tools * Maps each tool to its validation, execution, and messaging logic */ export const JOB_LOG_TOOL_CONFIG: Record<JobLogToolName, ToolSpec> = { get_latest_job_log_files: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'jobFiles'), }), validate: (args, toolName) => LogToolValidators.validateLimit(args.limit as number, toolName), exec: async (args, client) => client.getLatestJobLogFiles(args.limit as number), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Fetching latest job log files', { limit: args.limit as number, }), }, search_job_logs_by_name: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'jobFiles'), }), validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('jobName'), toolName); LogToolValidators.validateLimit(args.limit as number, toolName); }, exec: async (args, client) => client.searchJobLogsByName(args.jobName as string, args.limit as number), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Searching job logs by name', { jobName: args.jobName as string, limit: args.limit as number, }), }, get_job_log_entries: { defaults: (args) => ({ level: args.level ?? 'all', limit: getLimit(args.limit as number, 'jobEntries'), }), validate: (args, toolName) => { JobLogValidators.validateJobLogLevel(args.level as string, toolName); LogToolValidators.validateLimit(args.limit as number, toolName); }, exec: async (args, client) => client.getJobLogEntries( args.level as any, args.limit as number, args.jobName as string, ), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Fetching job log entries', { level: args.level as string, limit: args.limit as number, jobName: args.jobName as string, }), }, search_job_logs: { defaults: (args) => ({ level: args.level ?? 'all', limit: getLimit(args.limit as number, 'jobSearch'), }), validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('pattern'), toolName); JobLogValidators.validateJobLogLevel(args.level as string, toolName); LogToolValidators.validateLimit(args.limit as number, toolName); }, exec: async (args, client) => client.searchJobLogs( args.pattern as string, args.level as any, args.limit as number, args.jobName as string, ), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Searching job logs', { pattern: args.pattern as string, level: args.level as string, limit: args.limit as number, jobName: args.jobName as string, }), }, get_job_execution_summary: { validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('jobName'), toolName); }, exec: async (args, client) => client.getJobExecutionSummary(args.jobName as string), logMessage: (args) => `Getting job execution summary for: ${args.jobName as string}`, }, }; ``` -------------------------------------------------------------------------------- /tests/file-system-service.test.ts: -------------------------------------------------------------------------------- ```typescript import { FileSystemService, MockFileSystemService } from '../src/services/file-system-service.js'; describe('FileSystemService', () => { describe('Production FileSystemService', () => { let service: FileSystemService; beforeEach(() => { service = new FileSystemService(); }); it('should be instantiable', () => { expect(service).toBeInstanceOf(FileSystemService); }); // Note: Testing actual file operations would require integration tests // For unit tests, we mainly test the interface compliance }); describe('MockFileSystemService', () => { let mockService: MockFileSystemService; beforeEach(() => { mockService = new MockFileSystemService(); }); afterEach(() => { mockService.clearMocks(); }); it('should be instantiable', () => { expect(mockService).toBeInstanceOf(MockFileSystemService); }); it('should handle mock files correctly', async () => { const filePath = '/test/file.txt'; const content = 'test content'; // Initially file should not exist expect(await mockService.exists(filePath)).toBe(false); // Set mock file mockService.setMockFile(filePath, content); expect(await mockService.exists(filePath)).toBe(true); // Read mock file const readContent = await mockService.readFile(filePath); expect(readContent).toBe(content); }); it('should handle mock directories correctly', async () => { const dirPath = '/test/dir'; // Initially directory should not exist expect(await mockService.exists(dirPath)).toBe(false); // Set mock directory mockService.setMockDirectory(dirPath); expect(await mockService.exists(dirPath)).toBe(true); }); it('should create directories through mkdir', async () => { const dirPath = '/test/new-dir'; await mockService.mkdir(dirPath, { recursive: true }); expect(await mockService.exists(dirPath)).toBe(true); }); it('should create files through writeFile', async () => { const filePath = '/test/new-file.txt'; const content = 'new content'; await mockService.writeFile(filePath, content); expect(await mockService.exists(filePath)).toBe(true); const readContent = await mockService.readFile(filePath); expect(readContent).toBe(content); }); it('should throw error when reading non-existent file', async () => { const filePath = '/test/non-existent.txt'; await expect(mockService.readFile(filePath)).rejects.toThrow('File not found'); }); it('should handle access correctly', async () => { const existingPath = '/test/existing'; const nonExistentPath = '/test/non-existent'; mockService.setMockFile(existingPath, 'content'); await expect(mockService.access(existingPath)).resolves.not.toThrow(); await expect(mockService.access(nonExistentPath)).rejects.toThrow('Path not accessible'); }); it('should clear mocks correctly', async () => { const filePath = '/test/file.txt'; const dirPath = '/test/dir'; mockService.setMockFile(filePath, 'content'); mockService.setMockDirectory(dirPath); expect(await mockService.exists(filePath)).toBe(true); expect(await mockService.exists(dirPath)).toBe(true); mockService.clearMocks(); expect(await mockService.exists(filePath)).toBe(false); expect(await mockService.exists(dirPath)).toBe(false); }); }); }); ``` -------------------------------------------------------------------------------- /docs/dw_svc/ServiceRegistry.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.svc # Class ServiceRegistry ## Inheritance Hierarchy - Object - dw.svc.ServiceRegistry ## Description The ServiceRegistry is responsible for managing Service definitions and their instances. Typical usage involves several steps: The service is defined in the Business Manager and configured with necessary credentials. The service callback is configured once during cartridge initialization: ServiceRegistry.configure("MyFTPService", { mockExec : function(svc:FTPService, params) { return [ { "name": "file1", "timestamp": new Date(2011, 02, 21)}, { "name": "file2", "timestamp": new Date(2012, 02, 21)}, { "name": "file3", "timestamp": new Date(2013, 02, 21)} ]; }, createRequest: function(svc:FTPService, params) { svc.setOperation("list", "/"); }, parseResponse : function(svc:FTPService, listOutput) { var x : Array = []; var resp : Array = listOutput; for(var i = 0; i < resp.length; i++) { var f = resp[i]; x.push( { "name": f['name'], "timestamp": f['timestamp'] } ); } return x; } }); A new service instance is created and called in order to perform the operation: var result : Result = ServiceRegistry.get("MyFTPService").call(); if(result.status == 'OK') { // The result.object is the object returned by the 'after' callback. } else { // Handle the error. See result.error for more information. } See ServiceCallback for all the callback options, and individual ServiceDefinition classes for customization specific to a service type. ## Constructor Summary ## Method Summary ### configure **Signature:** `static configure(serviceID : String, configObj : Object) : ServiceDefinition` Configure the given serviceId with a callback. ### get **Signature:** `static get(serviceID : String) : Service` Constructs a new instance of the given service. ### getDefinition **Signature:** `static getDefinition(serviceID : String) : ServiceDefinition` Gets a Service Definition. ### isConfigured **Signature:** `static isConfigured(serviceID : String) : boolean` Returns the status of whether the given service has been configured with a callback. ## Method Detail ## Method Details ### configure **Signature:** `static configure(serviceID : String, configObj : Object) : ServiceDefinition` **Description:** Configure the given serviceId with a callback. If the service is already configured, the given callback will replace any existing one. **Parameters:** - `serviceID`: Unique Service ID. - `configObj`: Configuration callback. See ServiceCallback for a description of available callback methods. **Returns:** Associated ServiceDefinition, which can be used for further protocol-specific configuration. --- ### get **Signature:** `static get(serviceID : String) : Service` **Description:** Constructs a new instance of the given service. **Parameters:** - `serviceID`: Unique Service ID. **Returns:** Service instance. --- ### getDefinition **Signature:** `static getDefinition(serviceID : String) : ServiceDefinition` **Description:** Gets a Service Definition. This Service Definition is shared across all Service instances returned by get(String). **Parameters:** - `serviceID`: Unique Service ID. **Returns:** ServiceDefinition --- ### isConfigured **Signature:** `static isConfigured(serviceID : String) : boolean` **Description:** Returns the status of whether the given service has been configured with a callback. **Parameters:** - `serviceID`: Unique Service ID. **Returns:** true if configure has already been called, false otherwise. --- ``` -------------------------------------------------------------------------------- /src/core/handlers/job-log-tool-config.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSpec, LogToolValidators } from '../../utils/log-tool-utils.js'; import { ValidationHelpers, CommonValidations } from './validation-helpers.js'; import { JobLogToolName, getLimit } from '../../utils/log-tool-constants.js'; import { JobLogValidators, JobLogFormatters } from '../../utils/job-log-utils.js'; /** * Configuration for job log tools * Maps each tool to its validation, execution, and messaging logic */ export const JOB_LOG_TOOL_CONFIG: Record<JobLogToolName, ToolSpec> = { get_latest_job_log_files: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'jobFiles'), }), validate: (args, toolName) => LogToolValidators.validateLimit(args.limit as number, toolName), exec: async (args, client) => client.getLatestJobLogFiles(args.limit as number), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Fetching latest job log files', { limit: args.limit as number, }), }, search_job_logs_by_name: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'jobFiles'), }), validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('jobName'), toolName); LogToolValidators.validateLimit(args.limit as number, toolName); }, exec: async (args, client) => client.searchJobLogsByName(args.jobName as string, args.limit as number), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Searching job logs by name', { jobName: args.jobName as string, limit: args.limit as number, }), }, get_job_log_entries: { defaults: (args) => ({ level: args.level ?? 'all', limit: getLimit(args.limit as number, 'jobEntries'), }), validate: (args, toolName) => { JobLogValidators.validateJobLogLevel(args.level as string, toolName); LogToolValidators.validateLimit(args.limit as number, toolName); }, exec: async (args, client) => client.getJobLogEntries( args.level as any, args.limit as number, args.jobName as string, ), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Fetching job log entries', { level: args.level as string, limit: args.limit as number, jobName: args.jobName as string, }), }, search_job_logs: { defaults: (args) => ({ level: args.level ?? 'all', limit: getLimit(args.limit as number, 'jobSearch'), }), validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('pattern'), toolName); JobLogValidators.validateJobLogLevel(args.level as string, toolName); LogToolValidators.validateLimit(args.limit as number, toolName); }, exec: async (args, client) => client.searchJobLogs( args.pattern as string, args.level as any, args.limit as number, args.jobName as string, ), logMessage: (args) => JobLogFormatters.formatJobLogMessage('Searching job logs', { pattern: args.pattern as string, level: args.level as string, limit: args.limit as number, jobName: args.jobName as string, }), }, get_job_execution_summary: { validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('jobName'), toolName); }, exec: async (args, client) => client.getJobExecutionSummary(args.jobName as string), logMessage: (args) => `Getting job execution summary for: ${args.jobName as string}`, }, }; ``` -------------------------------------------------------------------------------- /docs-site/scripts/search-dev.js: -------------------------------------------------------------------------------- ```javascript #!/usr/bin/env node /** * Development utility script for the search index * Provides commands for generation, validation, and testing */ import { generateSearchIndex, writeSearchIndex } from './generate-search-index.js'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const command = process.argv[2]; switch (command) { case 'generate': console.log('🔄 Generating search index...'); try { const index = generateSearchIndex(); writeSearchIndex(index); } catch (error) { console.error('❌ Failed to generate search index:', error.message); process.exit(1); } break; case 'validate': console.log('✅ Validating search index...'); try { const indexPath = path.join(__dirname, '../src/generated-search-index.ts'); if (!fs.existsSync(indexPath)) { console.error('❌ Search index file not found. Run "generate" first.'); process.exit(1); } const stats = fs.statSync(indexPath); const content = fs.readFileSync(indexPath, 'utf-8'); const lines = content.split('\n').length; const size = (stats.size / 1024).toFixed(1); // Extract the number of entries const match = content.match(/GENERATED_SEARCH_INDEX: SearchableItem\[\] = \[([\s\S]*)\];/); if (match) { const entriesCount = (match[1].match(/\{/g) || []).length; console.log('📊 Search index statistics:'); console.log(` • File size: ${size} KB`); console.log(` • Lines: ${lines}`); console.log(` • Search entries: ${entriesCount}`); console.log(` • Last modified: ${stats.mtime.toLocaleString()}`); console.log('✅ Search index is valid'); } else { throw new Error('Invalid search index format'); } } catch (error) { console.error('❌ Search index validation failed:', error.message); process.exit(1); } break; case 'search': { const query = process.argv[3]; if (!query) { console.error('❌ Please provide a search query: npm run search-dev search "your query"'); process.exit(1); } console.log(`🔍 Testing search for: "${query}"`); try { // Dynamically import and test the search function const { searchDocs } = await import('../utils/search.ts'); const results = searchDocs(query); if (results.length === 0) { console.log('📭 No results found'); } else { console.log(`📋 Found ${results.length} results:`); results.forEach((result, index) => { console.log(`\n${index + 1}. ${result.heading} (${result.pageTitle})`); console.log(` Path: ${result.path}`); console.log(` Score: ${result.score}`); console.log(` Snippet: ${result.snippet.substring(0, 100)}...`); }); } } catch (error) { console.error('❌ Search test failed:', error.message); process.exit(1); } break; } default: console.log(` 🔧 Search Index Development Utility Usage: node scripts/search-dev.js <command> Commands: generate Generate the search index from React components validate Check if the search index is valid and show statistics search Test search functionality with a query Examples: node scripts/search-dev.js generate node scripts/search-dev.js validate node scripts/search-dev.js search "pattern matching" `); break; } ``` -------------------------------------------------------------------------------- /docs/dw_customer/ExternalProfile.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.customer # Class ExternalProfile ## Inheritance Hierarchy - Object - dw.customer.ExternalProfile ## Description 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. Note: this class handles sensitive security-related data. Pay special attention to PCI DSS v3. requirements 2, 4, and 12. ## Properties ### authenticationProviderID **Type:** String (Read Only) The authentication provider ID. ### customer **Type:** Customer (Read Only) The customer object related to this profile. ### email **Type:** String The customer's email address. ### externalID **Type:** String (Read Only) The external ID. ### lastLoginTime **Type:** Date (Read Only) The last login time of the customer through the external provider ## Constructor Summary ## Method Summary ### getAuthenticationProviderID **Signature:** `getAuthenticationProviderID() : String` Returns the authentication provider ID. ### getCustomer **Signature:** `getCustomer() : Customer` Returns the customer object related to this profile. ### getEmail **Signature:** `getEmail() : String` Returns the customer's email address. ### getExternalID **Signature:** `getExternalID() : String` Returns the external ID. ### getLastLoginTime **Signature:** `getLastLoginTime() : Date` Returns the last login time of the customer through the external provider ### setEmail **Signature:** `setEmail(email : String) : void` Sets the customer's email address. ## Method Detail ## Method Details ### getAuthenticationProviderID **Signature:** `getAuthenticationProviderID() : String` **Description:** Returns the authentication provider ID. **Returns:** the authentication provider ID. --- ### getCustomer **Signature:** `getCustomer() : Customer` **Description:** Returns the customer object related to this profile. **Returns:** customer object related to profile. --- ### getEmail **Signature:** `getEmail() : String` **Description:** Returns the customer's email address. **Returns:** the customer's email address. --- ### getExternalID **Signature:** `getExternalID() : String` **Description:** Returns the external ID. **Returns:** the external ID. --- ### getLastLoginTime **Signature:** `getLastLoginTime() : Date` **Description:** Returns the last login time of the customer through the external provider **Returns:** the time, when the customer was last logged in through this external provider --- ### setEmail **Signature:** `setEmail(email : String) : void` **Description:** Sets the customer's email address. **Parameters:** - `email`: the customer's email address. --- ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/src/middleware/logging.js: -------------------------------------------------------------------------------- ```javascript /** * Logging Middleware * * Provides request/response logging functionality for debugging and monitoring. * Configurable logging levels and output formatting. */ /** * Create request logging middleware */ function createRequestLogger(isDevMode = false) { if (!isDevMode) { // Return no-op middleware in production mode return (req, res, next) => next(); } return (req, res, next) => { const timestamp = new Date().toISOString(); const method = req.method; const url = req.url; const userAgent = req.headers['user-agent'] || 'Unknown'; console.log(`📥 [${timestamp}] ${method} ${url}`, { headers: { authorization: req.headers.authorization ? '[REDACTED]' : undefined, 'content-type': req.headers['content-type'], 'depth': req.headers.depth, 'range': req.headers.range, 'user-agent': userAgent }, query: Object.keys(req.query).length > 0 ? req.query : undefined, body: req.body && Object.keys(req.body).length > 0 ? req.body : undefined }); next(); }; } /** * Create response logging middleware */ function createResponseLogger(isDevMode = false) { if (!isDevMode) { // Return no-op middleware in production mode return (req, res, next) => next(); } return (req, res, next) => { const originalSend = res.send; const originalJson = res.json; // Override res.send res.send = function(body) { logResponse(req, res, body, 'send'); return originalSend.call(this, body); }; // Override res.json res.json = function(obj) { logResponse(req, res, obj, 'json'); return originalJson.call(this, obj); }; next(); }; } /** * Log response details */ function logResponse(req, res, body, type) { const timestamp = new Date().toISOString(); const method = req.method; const url = req.url; const statusCode = res.statusCode; let bodyPreview = body; // Truncate large responses for readability if (typeof body === 'string' && body.length > 500) { bodyPreview = body.substring(0, 500) + '... [truncated]'; } else if (typeof body === 'object' && body !== null) { const bodyStr = JSON.stringify(body); if (bodyStr.length > 500) { bodyPreview = JSON.stringify(body, null, 2).substring(0, 500) + '... [truncated]'; } } console.log(`📤 [${timestamp}] ${method} ${url} -> ${statusCode}`, { headers: { 'content-type': res.getHeader('content-type'), 'content-length': res.getHeader('content-length'), 'content-range': res.getHeader('content-range') }, type, body: bodyPreview }); } /** * Create error logging middleware */ function createErrorLogger() { return (err, req, res, next) => { const timestamp = new Date().toISOString(); const method = req.method; const url = req.url; console.error(`❌ [${timestamp}] ERROR ${method} ${url}:`, { message: err.message, stack: err.stack, code: err.code }); // Don't handle the error, just log it next(err); }; } module.exports = { createRequestLogger, createResponseLogger, createErrorLogger }; ``` -------------------------------------------------------------------------------- /docs/dw_campaign/CouponMgr.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.campaign # Class CouponMgr ## Inheritance Hierarchy - Object - dw.campaign.CouponMgr ## Description Manager to access coupons. ## Constants ### MR_ERROR_INVALID_SITE_ID **Type:** String = "MASKREDEMPTIONS_SITE_NOT_FOUND" Indicates that an error occurred because a valid data domain cannot be found for given siteID. ## Properties ### coupons **Type:** Collection (Read Only) All coupons in the current site in no specific order. ## Constructor Summary ## Method Summary ### getCoupon **Signature:** `static getCoupon(couponID : String) : Coupon` Returns the coupon with the specified ID. ### getCouponByCode **Signature:** `static getCouponByCode(couponCode : String) : Coupon` Tries to find a coupon for the given coupon code. ### getCoupons **Signature:** `static getCoupons() : Collection` Returns all coupons in the current site in no specific order. ### getRedemptions **Signature:** `static getRedemptions(couponID : String, couponCode : String) : Collection` Returns list of CouponRedemptions for the specified coupon and coupon code, sorted by redemption date descending (i.e. ### maskRedemptions **Signature:** `static maskRedemptions(siteID : String, email : String) : Status` Mask customer email address in coupon redemptions for the given siteID and email address ## Method Detail ## Method Details ### getCoupon **Signature:** `static getCoupon(couponID : String) : Coupon` **Description:** Returns the coupon with the specified ID. **Parameters:** - `couponID`: the coupon identifier. **Returns:** Coupon with specified ID or null --- ### getCouponByCode **Signature:** `static getCouponByCode(couponCode : String) : Coupon` **Description:** Tries to find a coupon for the given coupon code. The method first searches for a coupon with a fixed code matching the passed value. If no such fixed coupon is found, it searches for a coupon with a system-generated code matching the passed value. If found, the coupon is returned. Otherwise, the method returns null. **Parameters:** - `couponCode`: The coupon code to get the coupon for. **Returns:** The coupon with the matching coupon code or null if no coupon was found. --- ### getCoupons **Signature:** `static getCoupons() : Collection` **Description:** Returns all coupons in the current site in no specific order. **Returns:** Coupons in current site --- ### getRedemptions **Signature:** `static getRedemptions(couponID : String, couponCode : String) : Collection` **Description:** Returns list of CouponRedemptions for the specified coupon and coupon code, sorted by redemption date descending (i.e. last redemption first). Usually, there should only either be 0 or 1 redemption. But if a coupon and code is removed and recreated and re-issued later, there might be multiple such redemption records. Returns an empty list if no redemption record exists in system for the specified coupon and code. **Parameters:** - `couponID`: The coupon id to find redemption for. - `couponCode`: The coupon code to find redemption for. **Returns:** A sorted list of CouponRedemptions for the specified coupon and coupon code or an empty list if no redemption record exists. --- ### maskRedemptions **Signature:** `static maskRedemptions(siteID : String, email : String) : Status` **Description:** Mask customer email address in coupon redemptions for the given siteID and email address **Parameters:** - `siteID`: the site ID - `email`: the customer email address **Returns:** The status of the masking result --- ``` -------------------------------------------------------------------------------- /docs/dw_content/ContentMgr.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.content # Class ContentMgr ## Inheritance Hierarchy - Object - dw.content.ContentMgr ## Description Provides helper methods for getting content assets, library folders and the content library of the current site. ## Constants ### PRIVATE_LIBRARY **Type:** String = "PrivateLibrary" The input string to identify that the library is a private site library when invoking getLibrary(String). ## Properties ### siteLibrary **Type:** Library (Read Only) The content library of the current site. ## Constructor Summary ## Method Summary ### getContent **Signature:** `static getContent(id : String) : Content` Returns the content with the corresponding identifier within the current site's site library. ### getContent **Signature:** `static getContent(library : Library, id : String) : Content` Returns the content with the corresponding identifier within the specified library. ### getFolder **Signature:** `static getFolder(id : String) : Folder` Returns the folder identified by the specified id within the current site's site library. ### getFolder **Signature:** `static getFolder(library : Library, id : String) : Folder` Returns the folder identified by the specified id within the specified library. ### getLibrary **Signature:** `static getLibrary(libraryId : String) : Library` Returns the content library specified by the given id. ### getSiteLibrary **Signature:** `static getSiteLibrary() : Library` Returns the content library of the current site. ## Method Detail ## Method Details ### getContent **Signature:** `static getContent(id : String) : Content` **Description:** Returns the content with the corresponding identifier within the current site's site library. **Parameters:** - `id`: the ID of the content asset to find. **Returns:** the content if found, or null if not found. --- ### getContent **Signature:** `static getContent(library : Library, id : String) : Content` **Description:** Returns the content with the corresponding identifier within the specified library. **Parameters:** - `library`: the content library to look for the content in - `id`: the ID of the content asset to find. **Returns:** the content if found, or null if not found. --- ### getFolder **Signature:** `static getFolder(id : String) : Folder` **Description:** Returns the folder identified by the specified id within the current site's site library. **Parameters:** - `id`: the ID of the folder to find. **Returns:** the folder, or null if not found. --- ### getFolder **Signature:** `static getFolder(library : Library, id : String) : Folder` **Description:** Returns the folder identified by the specified id within the specified library. **Parameters:** - `library`: the content library to look for the folder in - `id`: the ID of the folder to find. **Returns:** the folder, or null if not found. --- ### getLibrary **Signature:** `static getLibrary(libraryId : String) : Library` **Description:** Returns the content library specified by the given id. If PRIVATE_LIBRARY is used, then the current site's private library will be returned. **Parameters:** - `libraryId`: the id of the library to return. **Returns:** the library for the passed id. Returns null if there is no content library with that id. --- ### getSiteLibrary **Signature:** `static getSiteLibrary() : Library` **Description:** Returns the content library of the current site. **Returns:** the content library of the current site, or null if there is not content library assigned to the current site. --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/CustomerGroup.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.customer # Class CustomerGroup ## Inheritance Hierarchy - Object - dw.object.PersistentObject - dw.object.ExtensibleObject - dw.customer.CustomerGroup ## Description CustomerGroups provide a means to segment customers by various criteria. A merchant can then provide different site experiences (e.g. promotions, prices, sorting rules) to each customer segment. Customer groups can consist of either an explicit list of customers or a business rule that dynamically determines whether a customer is a member. The former type is called "explicit" and the latter type is called "dynamic". Explicit customer group: Consists of an explicit list of customers. Only registered customers can be member of such a group. isRuleBased==false. Dynamic customer group: Memberships are evaluated by a business rule that is attached to the customer group. Registered as well as anonymous customers can be member of such a group. isRuleBased==true. Note: this class might allow access to sensitive personal and private information, depending on how you segment your customers and the names given to your custoemer groups. Pay attention to appropriate legal and regulatory requirements when developing with this data. ## Properties ### description **Type:** String (Read Only) Gets the value of the description of the customer group. ### ID **Type:** String (Read Only) The unique ID of the customer group. ### ruleBased **Type:** boolean (Read Only) Returns true if the group determines the membership of customers based on rules. Returns false if the group provides explicit assignement of customers. ## Constructor Summary ## Method Summary ### assignCustomer **Signature:** `assignCustomer(customer : Customer) : void` Assigns the specified customer to this group. ### getDescription **Signature:** `getDescription() : String` Gets the value of the description of the customer group. ### getID **Signature:** `getID() : String` Returns the unique ID of the customer group. ### isRuleBased **Signature:** `isRuleBased() : boolean` Returns true if the group determines the membership of customers based on rules. ### unassignCustomer **Signature:** `unassignCustomer(customer : Customer) : void` Unassigns the specified customer from this group. ## Method Detail ## Method Details ### assignCustomer **Signature:** `assignCustomer(customer : Customer) : void` **Description:** Assigns the specified customer to this group. The customer must be registered and the group must not be rule-based. **Parameters:** - `customer`: Registered customer, must not be null. --- ### getDescription **Signature:** `getDescription() : String` **Description:** Gets the value of the description of the customer group. **Returns:** the description of the customer group --- ### getID **Signature:** `getID() : String` **Description:** Returns the unique ID of the customer group. **Returns:** The unique semantic ID of the customer group. --- ### isRuleBased **Signature:** `isRuleBased() : boolean` **Description:** Returns true if the group determines the membership of customers based on rules. Returns false if the group provides explicit assignement of customers. **Returns:** True, if the customer group is rule based. --- ### unassignCustomer **Signature:** `unassignCustomer(customer : Customer) : void` **Description:** Unassigns the specified customer from this group. The customer must be registered and the group must not be rule-based. **Parameters:** - `customer`: Registered customer, must not be null. --- ``` -------------------------------------------------------------------------------- /src/core/handlers/client-factory.ts: -------------------------------------------------------------------------------- ```typescript import { HandlerContext } from './base-handler.js'; import { SFCCLogClient } from '../../clients/log-client.js'; import { OCAPIClient } from '../../clients/ocapi-client.js'; import { OCAPICodeVersionsClient } from '../../clients/ocapi/code-versions-client.js'; import { CartridgeGenerationClient } from '../../clients/cartridge-generation-client.js'; import { Logger } from '../../utils/logger.js'; import { IFileSystemService, IPathService, FileSystemService, PathService } from '../../services/index.js'; /** * Centralized client factory that handles complex initialization logic * and encapsulates the requirements for different client types. */ export class ClientFactory { private context: HandlerContext; private logger: Logger; constructor(context: HandlerContext, logger: Logger) { this.context = context; this.logger = logger; } /** * Create an SFCC Log Client if log access is available */ createLogClient(): SFCCLogClient | null { if (!this.context.capabilities?.canAccessLogs || !this.context.config) { this.logger.debug('Log client not created: missing log access capability or config'); return null; } this.logger.debug('Creating SFCC Log Client'); return new SFCCLogClient(this.context.config); } /** * Create an OCAPI Client if OCAPI access is available */ createOCAPIClient(): OCAPIClient | null { if (!this.hasOCAPICredentials()) { this.logger.debug('OCAPI client not created: missing OCAPI credentials or capability'); return null; } this.logger.debug('Creating OCAPI Client'); return new OCAPIClient({ hostname: this.context.config!.hostname!, clientId: this.context.config!.clientId!, clientSecret: this.context.config!.clientSecret!, version: 'v23_2', }); } /** * Create an OCAPI Code Versions Client if OCAPI access is available */ createCodeVersionsClient(): OCAPICodeVersionsClient | null { if (!this.hasOCAPICredentials()) { this.logger.debug('Code versions client not created: missing OCAPI credentials or capability'); return null; } this.logger.debug('Creating OCAPI Code Versions Client'); return new OCAPICodeVersionsClient({ hostname: this.context.config!.hostname!, clientId: this.context.config!.clientId!, clientSecret: this.context.config!.clientSecret!, version: 'v23_2', }); } /** * Check if OCAPI credentials and capability are available */ private hasOCAPICredentials(): boolean { return !!( this.context.capabilities?.canAccessOCAPI && this.context.config?.hostname && this.context.config?.clientId && this.context.config?.clientSecret ); } /** * Create a Cartridge Generation Client with injected dependencies */ createCartridgeClient( fileSystemService?: IFileSystemService, pathService?: IPathService, ): CartridgeGenerationClient { this.logger.debug('Creating Cartridge Generation Client'); return new CartridgeGenerationClient( fileSystemService ?? new FileSystemService(), pathService ?? new PathService(), ); } /** * Get the required error message for a client type */ static getClientRequiredError(clientType: 'OCAPI' | 'Log'): string { switch (clientType) { case 'OCAPI': return 'OCAPI client not configured - ensure credentials are provided in full mode.'; case 'Log': return 'Log client not configured - ensure log access is enabled.'; default: return 'Required client not configured.'; } } } ``` -------------------------------------------------------------------------------- /docs/dw_catalog/ProductOption.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class ProductOption ## Inheritance Hierarchy - Object - dw.object.PersistentObject - dw.object.ExtensibleObject - dw.catalog.ProductOption ## Description Represents a product option. ## Properties ### defaultValue **Type:** ProductOptionValue (Read Only) The default value for the product option. ### description **Type:** String (Read Only) The product option's short description in the current locale. ### displayName **Type:** String (Read Only) The product option's display name in the current locale. ### htmlName **Type:** String (Read Only) An HTML representation of the option id. ### ID **Type:** String (Read Only) The product option ID. ### image **Type:** MediaFile (Read Only) The product option's image. ### optionValues **Type:** Collection (Read Only) A collection containing the product option values. ## Constructor Summary ## Method Summary ### getDefaultValue **Signature:** `getDefaultValue() : ProductOptionValue` Returns the default value for the product option. ### getDescription **Signature:** `getDescription() : String` Returns the product option's short description in the current locale. ### getDisplayName **Signature:** `getDisplayName() : String` Returns the product option's display name in the current locale. ### getHtmlName **Signature:** `getHtmlName() : String` Returns an HTML representation of the option id. ### getHtmlName **Signature:** `getHtmlName(prefix : String) : String` Returns an HTML representation of the option id with the custom prefix. ### getID **Signature:** `getID() : String` Returns the product option ID. ### getImage **Signature:** `getImage() : MediaFile` Returns the product option's image. ### getOptionValues **Signature:** `getOptionValues() : Collection` Returns a collection containing the product option values. ## Method Detail ## Method Details ### getDefaultValue **Signature:** `getDefaultValue() : ProductOptionValue` **Description:** Returns the default value for the product option. **Returns:** the object for the relation 'defaultValue' --- ### getDescription **Signature:** `getDescription() : String` **Description:** Returns the product option's short description in the current locale. **Returns:** The value of the short description in the current locale, or null if it wasn't found. --- ### getDisplayName **Signature:** `getDisplayName() : String` **Description:** Returns the product option's display name in the current locale. **Returns:** The value of the display name in the current locale, or null if it wasn't found. --- ### getHtmlName **Signature:** `getHtmlName() : String` **Description:** Returns an HTML representation of the option id. **Returns:** an HTML representation of the option id. --- ### getHtmlName **Signature:** `getHtmlName(prefix : String) : String` **Description:** Returns an HTML representation of the option id with the custom prefix. **Parameters:** - `prefix`: a custom prefix for the html name. **Returns:** an HTML representation of the option id. --- ### getID **Signature:** `getID() : String` **Description:** Returns the product option ID. **Returns:** the product option identifier. --- ### getImage **Signature:** `getImage() : MediaFile` **Description:** Returns the product option's image. **Returns:** the product option's image. --- ### getOptionValues **Signature:** `getOptionValues() : Collection` **Description:** Returns a collection containing the product option values. **Returns:** a collection containing the product option values. --- ``` -------------------------------------------------------------------------------- /docs/dw_suggest/SuggestedTerm.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.suggest # Class SuggestedTerm ## Inheritance Hierarchy - Object - dw.suggest.SuggestedTerm ## Description A single suggested term. Each user input term of the search phrase is being processed separately by the suggestion engine. For each original term, a list of terms will be suggested, either completed terms, corrected terms or even the exact term if it is known to the engine. Each suggested term is represented by a instance of this class. The list of suggested terms belonging to a single original term is represented by a instance of SuggestedTerms class. The suggested term value can either be the completed version of the original term, the corrected version of the original term or exactly the original term. ## Properties ### additional **Type:** boolean (Read Only) Returns whether this suggested term is a additional term that has no corresponding term in the original search phrase. ### completed **Type:** boolean (Read Only) Returns whether this suggested term is a auto completed version of the original term. In other words, this method returns true if the original term is a prefix of this suggested term. ### corrected **Type:** boolean (Read Only) Returns whether this suggested term is a corrected version of the original term. ### exactMatch **Type:** boolean (Read Only) Returns whether this suggested term is exactly matching the original term. ### value **Type:** String (Read Only) Returns this suggested term as String value. ## Constructor Summary ## Method Summary ### getValue **Signature:** `getValue() : String` Returns this suggested term as String value. ### isAdditional **Signature:** `isAdditional() : boolean` Returns whether this suggested term is a additional term that has no corresponding term in the original search phrase. ### isCompleted **Signature:** `isCompleted() : boolean` Returns whether this suggested term is a auto completed version of the original term. ### isCorrected **Signature:** `isCorrected() : boolean` Returns whether this suggested term is a corrected version of the original term. ### isExactMatch **Signature:** `isExactMatch() : boolean` Returns whether this suggested term is exactly matching the original term. ## Method Detail ## Method Details ### getValue **Signature:** `getValue() : String` **Description:** Returns this suggested term as String value. **Returns:** the string representation of this suggested term --- ### isAdditional **Signature:** `isAdditional() : boolean` **Description:** Returns whether this suggested term is a additional term that has no corresponding term in the original search phrase. **Returns:** true if this suggested term is a additional term, false otherwise --- ### isCompleted **Signature:** `isCompleted() : boolean` **Description:** Returns whether this suggested term is a auto completed version of the original term. In other words, this method returns true if the original term is a prefix of this suggested term. **Returns:** true if this suggested term is evaluated by auto completion, false otherwise --- ### isCorrected **Signature:** `isCorrected() : boolean` **Description:** Returns whether this suggested term is a corrected version of the original term. **Returns:** true if this suggested term is a corrected version of the original term, false otherwise --- ### isExactMatch **Signature:** `isExactMatch() : boolean` **Description:** Returns whether this suggested term is exactly matching the original term. **Returns:** true if this suggested term exactly matches the original term, false otherwise --- ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/src/utils/webdav-xml.js: -------------------------------------------------------------------------------- ```javascript /** * WebDAV XML Response Utilities * * Handles generation of WebDAV XML responses for PROPFIND and other operations. * Provides clean, reusable functions for XML generation. */ const path = require('path'); /** * Generate WebDAV XML response for a single file */ function generateFileXmlResponse(urlPath, stats) { const filename = path.basename(urlPath); return `<?xml version="1.0" encoding="utf-8"?> <D:multistatus xmlns:D="DAV:"> <D:response> <D:href>${urlPath}</D:href> <D:propstat> <D:prop> <D:displayname>${filename}</D:displayname> <D:getcontentlength>${stats.size}</D:getcontentlength> <D:getlastmodified>${stats.mtime.toUTCString()}</D:getlastmodified> <D:resourcetype></D:resourcetype> <D:getcontenttype>text/plain</D:getcontenttype> </D:prop> <D:status>HTTP/1.1 200 OK</D:status> </D:propstat> </D:response> </D:multistatus>`; } /** * Generate WebDAV XML response for directory listing */ function generateDirectoryXmlResponse(urlPath, items, stats) { const directoryName = path.basename(urlPath) || 'Logs'; // Generate individual item entries const xmlItems = items.map(item => { const isDirectory = item.stats.isDirectory(); const itemUrl = urlPath.endsWith('/') ? `${urlPath}${item.name}` : `${urlPath}/${item.name}`; return ` <D:response> <D:href>${itemUrl}${isDirectory ? '/' : ''}</D:href> <D:propstat> <D:prop> <D:displayname>${item.name}</D:displayname> <D:getcontentlength>${isDirectory ? '' : item.stats.size}</D:getcontentlength> <D:getlastmodified>${item.stats.mtime.toUTCString()}</D:getlastmodified> <D:resourcetype>${isDirectory ? '<D:collection/>' : ''}</D:resourcetype> ${!isDirectory ? '<D:getcontenttype>text/plain</D:getcontenttype>' : ''} </D:prop> <D:status>HTTP/1.1 200 OK</D:status> </D:propstat> </D:response>`; }).join(''); return `<?xml version="1.0" encoding="utf-8"?> <D:multistatus xmlns:D="DAV:"> <D:response> <D:href>${urlPath}</D:href> <D:propstat> <D:prop> <D:displayname>${directoryName}</D:displayname> <D:resourcetype><D:collection/></D:resourcetype> </D:prop> <D:status>HTTP/1.1 200 OK</D:status> </D:propstat> </D:response>${xmlItems} </D:multistatus>`; } /** * Generate WebDAV error response */ function generateErrorXmlResponse(urlPath, statusCode, message) { return `<?xml version="1.0" encoding="utf-8"?> <D:multistatus xmlns:D="DAV:"> <D:response> <D:href>${urlPath}</D:href> <D:propstat> <D:status>HTTP/1.1 ${statusCode} ${message}</D:status> </D:propstat> </D:response> </D:multistatus>`; } /** * Parse WebDAV depth header */ function parseDepthHeader(depthHeader) { if (!depthHeader) return 'infinity'; const depth = depthHeader.toLowerCase(); if (depth === '0') return 0; if (depth === '1') return 1; return 'infinity'; } /** * Escape XML special characters */ function escapeXml(text) { return text .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } module.exports = { generateFileXmlResponse, generateDirectoryXmlResponse, generateErrorXmlResponse, parseDepthHeader, escapeXml }; ``` -------------------------------------------------------------------------------- /docs/sfra/store.md: -------------------------------------------------------------------------------- ```markdown # SFRA Store Model ## Overview The Store model represents a physical store location in SFRA applications. It provides store information including location details, contact information, and store hours for store locator functionality and in-store pickup features. ## Constructor ```javascript function store(storeObject) ``` Creates a Store model instance from a store object. ### Parameters - `storeObject` (dw.catalog.Store) - A Store object from the SFCC API ## Properties ### ID **Type:** string Unique identifier for the store. ### name **Type:** string Display name of the store. ### address1 **Type:** string Primary address line of the store location. ### address2 **Type:** string Secondary address line (suite, building, etc.). ### city **Type:** string City where the store is located. ### postalCode **Type:** string Postal/ZIP code for the store location. ### latitude **Type:** number Latitude coordinate for the store location (used for mapping and distance calculations). ### longitude **Type:** number Longitude coordinate for the store location (used for mapping and distance calculations). ### phone **Type:** string (optional) Store phone number for customer contact. ### stateCode **Type:** string (optional) State or province code for the store location. ### countryCode **Type:** string (optional) Country code for the store location (extracted from countryCode.value). ### storeHours **Type:** string (optional) Store hours information as markup/HTML content for display. ## Usage Example ```javascript var store = require('*/cartridge/models/store'); var StoreMgr = require('dw/catalog/StoreMgr'); // Get a specific store var storeObject = StoreMgr.getStore('store-001'); var storeModel = new store(storeObject); // Access store properties console.log(storeModel.name); // "Downtown Location" console.log(storeModel.address1); // "123 Main St" console.log(storeModel.city); // "New York" console.log(storeModel.phone); // "555-123-4567" // Use coordinates for mapping if (storeModel.latitude && storeModel.longitude) { console.log('Location: ' + storeModel.latitude + ', ' + storeModel.longitude); } // Display store hours if (storeModel.storeHours) { console.log(storeModel.storeHours); // HTML markup for hours display } ``` ## Coordinate System The model provides geographic coordinates for: - **Mapping Integration** - Display stores on maps - **Distance Calculations** - Find nearest stores - **Location Services** - GPS-based store finding ## Conditional Properties Several properties are only included if they exist in the source store object: - **phone** - Only included if store has a phone number - **stateCode** - Only included for locations with state/province - **countryCode** - Only included if country is specified - **storeHours** - Only included if store hours are configured ## Store Hours Format Store hours are provided as markup/HTML content, allowing for: - Rich formatting of hours display - Multiple day ranges and special hours - Holiday hours and exceptions - Custom styling and presentation ## Notes - Handles missing store data gracefully - Provides essential location information for store locators - Includes geographic coordinates for mapping functionality - Supports international stores with flexible address formats - Store hours support rich HTML formatting - Null-safe property access for optional fields ## Related Models - **Stores Model** - Collection of store models - **Product Models** - May include store availability - **Cart Models** - May include store pickup options - **Address Models** - Similar address structure ``` -------------------------------------------------------------------------------- /docs/dw_campaign/SourceCodeInfo.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.campaign # Class SourceCodeInfo ## Inheritance Hierarchy - Object - dw.campaign.SourceCodeInfo ## Description Class representing a code (i.e. a "source code") that has been applied to a customer's session. Source codes can qualify customers for different campaigns, promotions, and other site experiences from those that the typical customer sees. Codes are organized into source code groups. Typically, a code is applied to a customer's session automatically by Commerce Cloud Digital when a customer accesses a Digital URL with a well known request parameter in the querystring. A code may also be explicitly applied to a customer session using the SetSourceCode pipelet. ## Constants ### STATUS_ACTIVE **Type:** Number = 2 The literal source-code is found and currently active. ### STATUS_INACTIVE **Type:** Number = 1 The literal source-code is found but not active. ### STATUS_INVALID **Type:** Number = 0 The literal source-code is not found in the system. ## Properties ### code **Type:** String (Read Only) The literal source-code. ### group **Type:** SourceCodeGroup (Read Only) The associated source-code group. ### redirect **Type:** URLRedirect (Read Only) Retrieves the redirect information from the last processed SourceCodeGroup (active or inactive). If none exists, then the redirect information is retrieved from the source-code preferences, based on the active/inactive status of the SourceCodeGroup. The redirect information is then resolved to the output URL. If the redirect information cannot be resolved to a URL, or there is an error retrieving the preferences, then null is returned. ### status **Type:** Number (Read Only) The status of the source-code. One of the following: STATUS_INVALID - The source code is not found in the system. STATUS_INACTIVE - The source code is found but not active. STATUS_INACTIVE - The source code is found and active. ## Constructor Summary ## Method Summary ### getCode **Signature:** `getCode() : String` The literal source-code. ### getGroup **Signature:** `getGroup() : SourceCodeGroup` The associated source-code group. ### getRedirect **Signature:** `getRedirect() : URLRedirect` Retrieves the redirect information from the last processed SourceCodeGroup (active or inactive). ### getStatus **Signature:** `getStatus() : Number` The status of the source-code. ## Method Detail ## Method Details ### getCode **Signature:** `getCode() : String` **Description:** The literal source-code. **Returns:** the source-code. --- ### getGroup **Signature:** `getGroup() : SourceCodeGroup` **Description:** The associated source-code group. **Returns:** the source-code group. --- ### getRedirect **Signature:** `getRedirect() : URLRedirect` **Description:** Retrieves the redirect information from the last processed SourceCodeGroup (active or inactive). If none exists, then the redirect information is retrieved from the source-code preferences, based on the active/inactive status of the SourceCodeGroup. The redirect information is then resolved to the output URL. If the redirect information cannot be resolved to a URL, or there is an error retrieving the preferences, then null is returned. **Returns:** URLRedirect containing the location and status code, null in case of no redirect was found --- ### getStatus **Signature:** `getStatus() : Number` **Description:** The status of the source-code. One of the following: STATUS_INVALID - The source code is not found in the system. STATUS_INACTIVE - The source code is found but not active. STATUS_INACTIVE - The source code is found and active. **Returns:** the status. --- ``` -------------------------------------------------------------------------------- /docs/dw_util/FilteringCollection.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.util # Class FilteringCollection ## Inheritance Hierarchy - Object - dw.util.Collection - dw.util.FilteringCollection ## Description FilteringCollection is an extension of Collection which provides possibilities to filter the elements to return a new FilteringCollection with a filtered set of elements sort the elements to return a new FilteringCollection with a defined sort order transform the elements to return a new FilteringCollection containing related elements provide a map of the elements against a predefined key Usage - In the current version each FilteringCollection provides a set of predefined qualifier constants which can be passed into the select(Object) method used to filter the elements. Generally qualifiers have the prefix QUALIFIER_. A second method sort(Object) is used to create a new instance with a different element ordering, which takes an orderB< constant. Generally orderBys have the prefix ORDERBY_: examples are ShippingOrder.ORDERBY_ITEMID, ShippingOrder.ORDERBY_ITEMPOSITION, and ORDERBY_REVERSE can be used to provide a FilteringCollection with the reverse ordering. An example with method ShippingOrder.getItems(): var allItems : FilteringCollection = shippingOrder.items; var productItems : FilteringCollection = allItems.select(ShippingOrder.QUALIFIER_PRODUCTITEMS); var serviceItems : FilteringCollection = allItems.select(ShippingOrder.QUALIFIER_SERVICEITEMS); var byPosition : FilteringCollection = productItems.sort(ShippingOrder.ORDERBY_ITEMPOSITION); var revByPosition: FilteringCollection = byPosition.sort(FilteringCollection.ORDERBY_REVERSE); var mapByItemID : Map = allItems.asMap(); ## Constants ### ORDERBY_REVERSE **Type:** Object Pass this orderBy with the sort(Object) method to obtain a new FilteringCollection with the reversed sort order. Only use on a FilteringCollection which has been previously sorted. ## Properties ## Constructor Summary ## Method Summary ### asMap **Signature:** `asMap() : Map` Returns a Map containing the elements of this FilteringCollection against a predefined key. ### select **Signature:** `select(qualifier : Object) : FilteringCollection` Select a new FilteringCollection instance by passing a predefined qualifier as an argument to this method. ### sort **Signature:** `sort(orderBy : Object) : FilteringCollection` Select a new FilteringCollection instance by passing a predefined orderBy as an argument to this method. ## Method Detail ## Method Details ### asMap **Signature:** `asMap() : Map` **Description:** Returns a Map containing the elements of this FilteringCollection against a predefined key. The key used is documented in the method returning the FilteringCollection and is typically the ItemID assigned to an element in the collection. **Returns:** a Map containing the elements of this FilteringCollection against a predefined key. --- ### select **Signature:** `select(qualifier : Object) : FilteringCollection` **Description:** Select a new FilteringCollection instance by passing a predefined qualifier as an argument to this method. See FilteringCollection. **Parameters:** - `qualifier`: possible qualifiers are documented in the method returning the FilteringCollection **Returns:** a new FilteringCollection instance --- ### sort **Signature:** `sort(orderBy : Object) : FilteringCollection` **Description:** Select a new FilteringCollection instance by passing a predefined orderBy as an argument to this method. See FilteringCollection. **Parameters:** - `orderBy`: possible orderBys are documented in the method returning the FilteringCollection **Returns:** a new FilteringCollection instance --- ``` -------------------------------------------------------------------------------- /docs/dw_catalog/CategoryAssignment.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class CategoryAssignment ## Inheritance Hierarchy - Object - dw.object.PersistentObject - dw.object.ExtensibleObject - dw.catalog.CategoryAssignment ## Description Represents a category assignment in Commerce Cloud Digital. ## Properties ### calloutMsg **Type:** MarkupText (Read Only) The category assignment's callout message in the current locale. ### category **Type:** Category (Read Only) The category to which this category assignment is bound. ### image **Type:** MediaFile (Read Only) The category assignment's image. ### longDescription **Type:** MarkupText (Read Only) The category assignment's long description in the current locale. ### name **Type:** String (Read Only) The name of the category assignment in the current locale. ### product **Type:** Product (Read Only) The product to which this category assignment is bound. ### shortDescription **Type:** MarkupText (Read Only) The category assignment's short description in the current locale. ## Constructor Summary ## Method Summary ### getCalloutMsg **Signature:** `getCalloutMsg() : MarkupText` Returns the category assignment's callout message in the current locale. ### getCategory **Signature:** `getCategory() : Category` Returns the category to which this category assignment is bound. ### getImage **Signature:** `getImage() : MediaFile` Returns the category assignment's image. ### getLongDescription **Signature:** `getLongDescription() : MarkupText` Returns the category assignment's long description in the current locale. ### getName **Signature:** `getName() : String` Returns the name of the category assignment in the current locale. ### getProduct **Signature:** `getProduct() : Product` Returns the product to which this category assignment is bound. ### getShortDescription **Signature:** `getShortDescription() : MarkupText` Returns the category assignment's short description in the current locale. ## Method Detail ## Method Details ### getCalloutMsg **Signature:** `getCalloutMsg() : MarkupText` **Description:** Returns the category assignment's callout message in the current locale. **Returns:** the category assignment's callout message in the current locale, or null if it wasn't found. --- ### getCategory **Signature:** `getCategory() : Category` **Description:** Returns the category to which this category assignment is bound. **Returns:** The category to which this category assignment is bound. --- ### getImage **Signature:** `getImage() : MediaFile` **Description:** Returns the category assignment's image. **Returns:** the category assignment's image. --- ### getLongDescription **Signature:** `getLongDescription() : MarkupText` **Description:** Returns the category assignment's long description in the current locale. **Returns:** The category assignment's long description in the current locale, or null if it wasn't found. --- ### getName **Signature:** `getName() : String` **Description:** Returns the name of the category assignment in the current locale. **Returns:** The name of the category assignment for the current locale, or null if it wasn't found. --- ### getProduct **Signature:** `getProduct() : Product` **Description:** Returns the product to which this category assignment is bound. **Returns:** The product to which this category assignment is bound. --- ### getShortDescription **Signature:** `getShortDescription() : MarkupText` **Description:** Returns the category assignment's short description in the current locale. **Returns:** the category assignment's short description in the current locale, or null if it wasn't found. --- ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/src/config/server-config.js: -------------------------------------------------------------------------------- ```javascript /** * Server Configuration Management * * Handles server configuration with sensible defaults and environment overrides. * Follows single responsibility principle for configuration management. */ class ServerConfig { constructor(options = {}) { // Server basics this.port = options.port || process.env.PORT || 3000; this.host = options.host || process.env.HOST || 'localhost'; this.isDevMode = options.dev || process.env.NODE_ENV === 'development' || false; // API versions this.ocapiVersion = options.ocapiVersion || 'v23_2'; // Paths this.mockDataPath = options.mockDataPath || require('path').join(__dirname, '../../mock-data'); this.webdavBasePath = '/on/demandware.servlet/webdav/Sites'; // Authentication - Mock credentials for testing this.validCredentials = { clientId: 'test-client-id', clientSecret: 'test-client-secret', username: 'test-user', password: 'test-password' }; // Features toggle this.features = { webdav: options.enableWebdav !== false, // enabled by default ocapi: options.enableOcapi !== false, // enabled by default cors: options.enableCors !== false, // enabled by default logging: options.enableLogging !== false, // enabled by default randomErrors: options.enableRandomErrors === true // disabled by default for reliable tests }; } /** * Get WebDAV configuration */ getWebdavConfig() { return { basePath: this.webdavBasePath, logsPath: require('path').join(this.mockDataPath, 'logs'), enabled: this.features.webdav }; } /** * Get OCAPI configuration */ getOcapiConfig() { return { version: this.ocapiVersion, basePath: `/s/-/dw/data/${this.ocapiVersion}`, mockDataPath: require('path').join(this.mockDataPath, 'ocapi'), enabled: this.features.ocapi }; } /** * Get full server URL */ getServerUrl() { return `http://${this.host}:${this.port}`; } /** * Get WebDAV logs URL */ getWebdavLogsUrl() { return `${this.getServerUrl()}${this.webdavBasePath}/Logs/`; } /** * Get OCAPI base URL */ getOcapiBaseUrl() { return `${this.getServerUrl()}/s/-/dw/data/${this.ocapiVersion}`; } /** * Validate configuration */ validate() { const errors = []; if (!this.port || this.port < 1 || this.port > 65535) { errors.push('Invalid port number'); } if (!this.host || typeof this.host !== 'string') { errors.push('Invalid host'); } if (!this.features.webdav && !this.features.ocapi) { errors.push('At least one feature (WebDAV or OCAPI) must be enabled'); } return errors; } /** * Get configuration summary for logging */ getSummary() { return { server: `${this.host}:${this.port}`, mode: this.isDevMode ? 'development' : 'production', features: Object.entries(this.features) .filter(([_, enabled]) => enabled) .map(([feature, _]) => feature), endpoints: { ...(this.features.webdav && { webdav: this.getWebdavLogsUrl() }), ...(this.features.ocapi && { ocapi: this.getOcapiBaseUrl() }), health: `${this.getServerUrl()}/health` } }; } } module.exports = ServerConfig; ``` -------------------------------------------------------------------------------- /docs/dw_system/RemoteInclude.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.system # Class RemoteInclude ## Inheritance Hierarchy - Object - dw.system.RemoteInclude ## Description The class represents a remote include value that can be assigned to JSON Object properties. Important notes: Authentication and authorization checks are performed only for the top level request, but NOT for remote include requests. The RestResponseMgr method createScapiRemoteInclude() allows only SCAPI URLs. The RestResponseMgr method createStorefrontControllerRemoteInclude() allows only Controller URLs. Correct rendering of RemoteInclude-containing objects is only performed when processed by dw.system.RESTSuccessResponse.render() method. Please check the provided examples. Example 1. Specify remote include properties. function specifyRemoteIncludeProperties() { var includeValue0 = dw.system.RESTResponseMgr.createScapiRemoteInclude("custom", "sample", "v1", "resource/path/0", dw.web.URLParameter("siteId", "TestWapi")); var includeValue1 = dw.system.RESTResponseMgr.createScapiRemoteInclude("custom", "sample", "v1", "resource/path/1", dw.web.URLParameter("siteId", "TestWapi")); var greeting = { "hello": "world", "includeProperty0": includeValue0, "includeProperty1": includeValue1 }; dw.system.RESTResponseMgr.createSuccess(greeting).render(); } Example 2. Specify array of remote include properties. function specifyArrayOfRemoteIncludes() { var includeValue0 = dw.system.RESTResponseMgr.createScapiRemoteInclude("custom", "sample", "v1", "resource/path/0", dw.web.URLParameter("siteId", "TestWapi")); var includeValue1 = dw.system.RESTResponseMgr.createScapiRemoteInclude("custom", "sample", "v1", "resource/path/1", dw.web.URLParameter("siteId", "TestWapi")); var greeting = { "hello": "world", "includeArray": [includeValue0, includeValue1] }; dw.system.RESTResponseMgr.createSuccess(greeting).render(); } Example 3. Storefront controller remote include. function storefrontRemoteInclude() { let remoteInclude = dw.system.RESTResponseMgr.createStorefrontControllerRemoteInclude(new URLAction("Category-Show", "Sites-MyShop-Site", dw.web.URLParameter("cid", "root"))); let json = { status: "JSONOK", include: remoteInclude }; dw.system.RESTResponseMgr.createSuccess(json).render(); } Error handling: SCAPI: In case of 404 response received on included resource, an empty JSON object '{}' will be supplied in final JSON. In case of 201..299, 3xx, 4xx (excluding 404), 5xx response from included resource, final response status will be 500 'Internal Server Error' Controllers: In case of any non 200 response from the included resource an empty string will be included. Note: In case your response format is JSON be aware that this can result in invalid JSON. ## Properties ### url **Type:** String (Read Only) The URL string value specified for the current instance. ### value **Type:** String (Read Only) ## Constructor Summary ## Method Summary ### getUrl **Signature:** `getUrl() : String` Returns the URL string value specified for the current instance. ### toString **Signature:** `toString() : String` Returns the URL string value specified for the current instance, same as getUrl(). ### valueOf **Signature:** `valueOf() : Object` Returns the URL string value specified for the current instance, same as getUrl(). ## Method Detail ## Method Details ### getUrl **Signature:** `getUrl() : String` **Description:** Returns the URL string value specified for the current instance. --- ### toString **Signature:** `toString() : String` **Description:** Returns the URL string value specified for the current instance, same as getUrl(). --- ### valueOf **Signature:** `valueOf() : Object` **Description:** Returns the URL string value specified for the current instance, same as getUrl(). --- ``` -------------------------------------------------------------------------------- /docs/sfra/price-tiered.md: -------------------------------------------------------------------------------- ```markdown # SFRA Tiered Price Model ## Overview The Tiered Price model represents quantity-based pricing in SFRA applications, where products have different prices based on the quantity purchased. This enables bulk discounts and volume pricing strategies. ## Constructor ```javascript function TieredPrice(priceTable, useSimplePrice) ``` Creates a Tiered Price model instance with quantity-based pricing tiers. ### Parameters - `priceTable` (dw.catalog.ProductPriceTable) - Product price table from the API - `useSimplePrice` (boolean) - Flag indicating if this is for a product tile (optional) ## Properties ### type **Type:** string Always set to 'tiered' to identify this as a tiered price model. ### useSimplePrice **Type:** boolean Flag indicating whether this price is intended for simplified display (e.g., product tiles). Defaults to false. ### tiers **Type:** Array<Object> Array of pricing tier objects, each containing: - `quantity` (number) - Minimum quantity for this price tier - `price` (DefaultPrice) - Price object for this quantity tier ### startingFromPrice **Type:** DefaultPrice The lowest price available across all tiers, representing the "starting from" price for marketing display. Access the formatted price via `.sales.formatted`. ## Tier Structure Each tier object in the tiers array contains: ### quantity Minimum quantity required to qualify for this price tier. ### price ### price Complete DefaultPrice object created with only the sales price for this tier (accessed via `.sales` property). ## Usage Example ```javascript var TieredPrice = require('*/cartridge/models/price/tiered'); // Get tiered pricing from product var product = ProductMgr.getProduct('bulk-product-id'); var priceTable = product.getPriceModel().getPriceTable(); var tieredPrice = new TieredPrice(priceTable, false); console.log(tieredPrice.type); // "tiered" console.log('Starting from: ' + tieredPrice.startingFromPrice.sales.formatted); // Display all price tiers tieredPrice.tiers.forEach(function(tier) { console.log('Buy ' + tier.quantity + '+ for ' + tier.price.sales.formatted + ' each'); }); // Example output: // Buy 1+ for $10.00 each // Buy 5+ for $9.00 each // Buy 10+ for $8.00 each ``` ## Price Calculation Logic The model automatically: 1. **Processes all quantity breaks** from the price table using `collections.map` 2. **Creates DefaultPrice instances** with only sales price for each tier 3. **Identifies the lowest price** by comparing `price.sales.value` across all tiers 4. **Sets startingFromPrice** to the best available price 5. **Maintains tier order** based on quantity requirements 6. **Converts quantities** to numeric values using `quantity.getValue()` ## Display Modes ### Full Pricing (useSimplePrice = false) - Shows all pricing tiers - Includes complete quantity break information - Used on product detail pages ### Simple Pricing (useSimplePrice = true) - Optimized for product tiles and listings - Focuses on starting price and key tiers - Simplified display for grid views ## Notes - Automatically calculates the best "starting from" price by comparing sales values - Each tier DefaultPrice is created with only sales price (access via `.sales` property) - Uses `collections.map` utility to process price table quantities - Supports unlimited number of quantity tiers - Each tier uses DefaultPrice for consistent formatting - Useful for B2B and bulk purchasing scenarios - Type property enables template conditional logic - `useSimplePrice` defaults to `false` if not provided ## Related Models - **DefaultPrice Model** - Used for individual tier pricing - **RangePrice Model** - Alternative pricing model for price ranges - **Product Models** - Use tiered prices for bulk products ``` -------------------------------------------------------------------------------- /docs/sfra/cart.md: -------------------------------------------------------------------------------- ```markdown # SFRA Cart Model ## Overview The Cart model represents the current customer's shopping basket in SFRA applications. It provides comprehensive cart functionality including product line items, shipping methods, totals, discounts, and cart actions. ## Constructor ```javascript function CartModel(basket) ``` Creates a Cart model instance from a basket object. ### Parameters - `basket` (dw.order.Basket) - Current user's basket ## Properties ### items **Type:** Array<Object> Array of product line items in the cart. Each item contains product information, quantity, pricing, and other details. ### numItems **Type:** number Total number of items (quantity) in the cart. ### totals **Type:** TotalsModel Totals model containing cart pricing information including subtotals, taxes, shipping costs, and grand total. ### shipments **Type:** Array<Object> Array of shipment objects, each containing: - `shippingMethods` - Available shipping methods for the shipment - `selectedShippingMethod` - ID of the currently selected shipping method ### numOfShipments **Type:** number Number of shipments in the cart. ### hasBonusProduct **Type:** boolean Indicates whether the cart contains any bonus products from promotions. ### actionUrls **Type:** Object Object containing URLs for cart actions: - `removeProductLineItemUrl` - URL to remove product line items - `updateQuantityUrl` - URL to update item quantities - `selectShippingUrl` - URL to select shipping methods - `submitCouponCodeUrl` - URL to add coupon codes - `removeCouponLineItem` - URL to remove coupon line items ### approachingDiscounts **Type:** Array<Object> Array of approaching discount objects, each containing: - `discountMsg` - Message describing the approaching discount ### valid **Type:** boolean Indicates whether the basket is valid based on validation hooks. ### resources **Type:** Object Object containing localized resource strings: - `numberOfItems` - Formatted message showing number of items in cart - `minicartCountOfItems` - Formatted message for minicart count - `emptyCartMsg` - Message displayed when cart is empty ## Helper Functions ### getApproachingDiscounts(basket, discountPlan) Generates an object of approaching discounts based on current basket and discount plan. **Parameters:** - `basket` (dw.order.Basket) - Current user's basket - `discountPlan` (dw.campaign.DiscountPlan) - Set of applicable discounts **Returns:** Object - Object containing approaching discount information ### getCartActionUrls() Generates an object of URLs used for cart actions. **Returns:** Object - Object containing cart action URLs in string format ## Usage Example ```javascript var CartModel = require('*/cartridge/models/cart'); var BasketMgr = require('dw/order/BasketMgr'); var currentBasket = BasketMgr.getCurrentBasket(); var cart = new CartModel(currentBasket); // Access cart properties console.log(cart.numItems); console.log(cart.totals.grandTotal); console.log(cart.actionUrls.updateQuantityUrl); // Check if cart has items if (cart.items && cart.items.length > 0) { // Process cart items cart.items.forEach(function(item) { console.log(item.productName); }); } ``` ## Notes - If basket is null, the cart will have empty items array and numItems of 0 - The model automatically calculates shipping methods for each shipment - Bonus products from promotions are tracked separately - Cart validation is performed using hooks - All action URLs are generated dynamically using URLUtils ## Related Models - **TotalsModel** - Used for cart pricing calculations - **ProductLineItemsModel** - Used for product line item formatting - **Shipping Models** - Used for shipping method information - **Address Model** - Used for shipping addresses ``` -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- ```javascript import js from '@eslint/js'; import tseslint from 'typescript-eslint'; export default tseslint.config( // Base recommended configs js.configs.recommended, ...tseslint.configs.recommended, // Global settings { languageOptions: { ecmaVersion: 2022, sourceType: 'module', globals: { console: 'readonly', process: 'readonly', Buffer: 'readonly', __dirname: 'readonly', __filename: 'readonly', }, }, }, // TypeScript files { files: ['**/*.ts', '**/*.tsx'], languageOptions: { parser: tseslint.parser, parserOptions: { project: ['./tsconfig.json', './tsconfig.test.json'], tsconfigRootDir: import.meta.dirname, }, }, rules: { // TypeScript specific rules '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/prefer-optional-chain': 'error', '@typescript-eslint/no-empty-object-type': 'off', // General code quality rules 'no-console': 'off', 'prefer-const': 'error', 'no-var': 'error', 'object-shorthand': 'error', 'prefer-arrow-callback': 'error', 'prefer-template': 'error', 'no-trailing-spaces': 'error', 'eol-last': 'error', 'comma-dangle': ['error', 'always-multiline'], 'quotes': ['error', 'single', { avoidEscape: true }], 'semi': ['error', 'always'], // Basic formatting rules 'indent': ['error', 2, { SwitchCase: 1 }], 'linebreak-style': ['error', 'unix'], 'max-len': ['error', { code: 120, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true }], 'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }], 'object-curly-spacing': ['error', 'always'], 'array-bracket-spacing': ['error', 'never'], 'key-spacing': ['error', { beforeColon: false, afterColon: true }], 'comma-spacing': ['error', { before: false, after: true }], 'space-before-blocks': ['error', 'always'], 'space-infix-ops': 'error', 'brace-style': ['error', '1tbs', { allowSingleLine: true }], 'curly': ['error', 'all'], }, }, // Test files { files: ['**/*.test.ts', '**/*.spec.ts', 'tests/**/*.ts'], languageOptions: { globals: { jest: 'readonly', describe: 'readonly', it: 'readonly', expect: 'readonly', beforeEach: 'readonly', afterEach: 'readonly', beforeAll: 'readonly', afterAll: 'readonly', }, }, rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-non-null-assertion': 'off', }, }, // Script files { files: ['scripts/**/*.js'], languageOptions: { sourceType: 'module', globals: { console: 'readonly', process: 'readonly', Buffer: 'readonly', __dirname: 'readonly', __filename: 'readonly', }, }, }, // Mock files (CommonJS format) { files: ['**/__mocks__/**/*.js'], languageOptions: { sourceType: 'script', globals: { module: 'writable', exports: 'writable', jest: 'readonly', }, }, }, // Ignore patterns { ignores: [ 'tmp/**', 'dist/**', 'node_modules/**', 'coverage/**', 'docs-site/**', 'docs-site-old/**', '*.config.js', 'jest.config.js', 'tests/servers/**', ], }, ); ``` -------------------------------------------------------------------------------- /docs/dw_crypto/X509Certificate.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.crypto # Class X509Certificate ## Inheritance Hierarchy - Object - dw.crypto.CertificateRef - dw.crypto.X509Certificate ## Description Represents an X.509 public key certificate as defined in RFC 5280. It provides access to the standard fields of an X.509 certificate including version, serial number, validity period, distinguished names, and signature algorithm. ## Properties ### issuerDN **Type:** String (Read Only) The X.500 distinguished name of the entity that signed this certificate. ### notAfter **Type:** Date (Read Only) The end date of the certificate validity period. ### notBefore **Type:** Date (Read Only) The start date of the certificate validity period. ### serialNumber **Type:** String (Read Only) The certificate serial number in string format. The serial number is a unique positive integer assigned by the CA to each certificate. ### sigAlgName **Type:** String (Read Only) The algorithm used to sign this certificate. The name follows the format defined in RFC 5280 (e.g., "SHA256withRSA", "SHA384withECDSA"). ### subjectDN **Type:** String (Read Only) The X.500 distinguished name of the entity this certificate belongs to. ### version **Type:** Number (Read Only) The X.509 certificate version number. ## Constructor Summary ## Method Summary ### getIssuerDN **Signature:** `getIssuerDN() : String` Returns the X.500 distinguished name of the entity that signed this certificate. ### getNotAfter **Signature:** `getNotAfter() : Date` Returns the end date of the certificate validity period. ### getNotBefore **Signature:** `getNotBefore() : Date` Returns the start date of the certificate validity period. ### getSerialNumber **Signature:** `getSerialNumber() : String` Returns the certificate serial number in string format. ### getSigAlgName **Signature:** `getSigAlgName() : String` Returns the algorithm used to sign this certificate. ### getSubjectDN **Signature:** `getSubjectDN() : String` Returns the X.500 distinguished name of the entity this certificate belongs to. ### getVersion **Signature:** `getVersion() : Number` Returns the X.509 certificate version number. ## Method Detail ## Method Details ### getIssuerDN **Signature:** `getIssuerDN() : String` **Description:** Returns the X.500 distinguished name of the entity that signed this certificate. **Returns:** the issuer's X.500 distinguished name --- ### getNotAfter **Signature:** `getNotAfter() : Date` **Description:** Returns the end date of the certificate validity period. **Returns:** the date after which this certificate is not valid --- ### getNotBefore **Signature:** `getNotBefore() : Date` **Description:** Returns the start date of the certificate validity period. **Returns:** the date before which this certificate is not valid --- ### getSerialNumber **Signature:** `getSerialNumber() : String` **Description:** Returns the certificate serial number in string format. The serial number is a unique positive integer assigned by the CA to each certificate. **Returns:** the certificate serial number as a string --- ### getSigAlgName **Signature:** `getSigAlgName() : String` **Description:** Returns the algorithm used to sign this certificate. The name follows the format defined in RFC 5280 (e.g., "SHA256withRSA", "SHA384withECDSA"). **Returns:** the signature algorithm name --- ### getSubjectDN **Signature:** `getSubjectDN() : String` **Description:** Returns the X.500 distinguished name of the entity this certificate belongs to. **Returns:** the subject's X.500 distinguished name --- ### getVersion **Signature:** `getVersion() : Number` **Description:** Returns the X.509 certificate version number. **Returns:** certificate version (typically 1, 2, or 3) --- ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- ```yaml name: 🐛 Bug Report description: Report a bug or unexpected behavior in the SFCC Development MCP Server title: "[Bug]: " labels: ["bug", "needs-triage"] assignees: [] body: - type: markdown attributes: value: | Thanks for taking the time to report a bug! Please fill out the information below to help us investigate and fix the issue. - type: checkboxes id: terms attributes: label: Pre-flight Checklist description: Please confirm you have completed these steps before submitting the bug report. options: - label: I have searched existing issues to ensure this bug hasn't been reported already required: true - label: I have read the documentation and troubleshooting guide required: true - label: I am using a supported Node.js version (18+) required: true - type: dropdown id: operating-mode attributes: label: Operating Mode description: Which mode were you using when the bug occurred? options: - Documentation-only mode - Full mode (with SFCC credentials) - Not sure validations: required: true - type: textarea id: bug-description attributes: label: Bug Description description: A clear and concise description of what the bug is. placeholder: Describe what happened and what you expected to happen instead. validations: required: true - type: textarea id: reproduction-steps attributes: label: Steps to Reproduce description: Detailed steps to reproduce the behavior. placeholder: | 1. Start the MCP server with... 2. Call the tool... 3. Observe the error... validations: required: true - type: textarea id: expected-behavior attributes: label: Expected Behavior description: What you expected to happen. validations: required: true - type: textarea id: actual-behavior attributes: label: Actual Behavior description: What actually happened, including any error messages. validations: required: true - type: textarea id: environment attributes: label: Environment Information description: Information about your environment value: | - OS: [e.g., macOS 14.1, Windows 11, Ubuntu 22.04] - Node.js version: [run `node --version`] - NPM/Yarn version: [run `npm --version` or `yarn --version`] - SFCC Dev MCP version: [check package.json version] - MCP Client: [e.g., Claude Desktop, Custom implementation] validations: required: true - type: textarea id: logs attributes: label: Relevant Logs description: Any relevant log output or error messages render: shell placeholder: Paste any relevant logs here (please remove any sensitive information like credentials) - type: textarea id: configuration attributes: label: Configuration (Sanitized) description: Your configuration (with sensitive information removed) render: json placeholder: | { "hostname": "your-instance.demandware.net", "clientId": "[REDACTED]", "version": "v21_3" } - type: dropdown id: affected-tools attributes: label: Affected Tools description: Which MCP tools are affected by this bug? multiple: true options: - SFCC Class Documentation Tools - Best Practices Guide Tools - Log Analysis Tools - System Object Tools - Authentication/OAuth - Configuration Loading - All Tools - Not sure - type: textarea id: additional-context attributes: label: Additional Context description: Any other context, screenshots, or information that might be helpful. ``` -------------------------------------------------------------------------------- /docs-site/scripts/generate-sitemap.js: -------------------------------------------------------------------------------- ```javascript #!/usr/bin/env node /** * Sitemap Generator for SFCC Development MCP Server Documentation * * This script generates an XML sitemap for the SFCC Development MCP Server documentation site. * Run this whenever you add new pages or want to update the sitemap. * * Usage: node scripts/generate-sitemap.js */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const baseUrl = 'https://sfcc-mcp-dev.rhino-inquisitor.com'; const currentDate = new Date().toISOString().split('T')[0]; // Define all pages with their priorities and change frequencies const pages = [ { path: '/', priority: '1.0', changefreq: 'weekly', description: 'SFCC Development MCP Server Homepage', }, { path: '/configuration/', priority: '0.9', changefreq: 'monthly', description: 'Configuration Guide', }, { path: '/ai-interfaces/', priority: '0.8', changefreq: 'monthly', description: 'AI Interface Setup Guide', }, { path: '/features/', priority: '0.8', changefreq: 'monthly', description: 'Features Overview', }, { path: '/tools/', priority: '0.8', changefreq: 'monthly', description: 'Available Tools', }, { path: '/examples/', priority: '0.8', changefreq: 'monthly', description: 'Examples and Use Cases', }, { path: '/security/', priority: '0.7', changefreq: 'monthly', description: 'Security Guidelines', }, { path: '/development/', priority: '0.6', changefreq: 'monthly', description: 'Development Guide', }, { path: '/troubleshooting/', priority: '0.7', changefreq: 'monthly', description: 'Troubleshooting Guide', }, ]; function generateSitemap() { const header = `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">`; const footer = ` </urlset>`; const urls = pages.map(page => { // For SSG routing, URLs should point to clean paths without hash fragments const url = page.path === '/' ? baseUrl : `${baseUrl}${page.path}`; return ` <!-- ${page.description} --> <url> <loc>${url}</loc> <lastmod>${currentDate}</lastmod> <changefreq>${page.changefreq}</changefreq> <priority>${page.priority}</priority> </url>`; }).join(''); return header + urls + footer; } function generateRobotsTxt() { return `User-agent: * Allow: / # Sitemap Sitemap: ${baseUrl}/sitemap.xml # Crawl-delay for respectful crawling Crawl-delay: 1 # Allow all common search engines User-agent: Googlebot Allow: / User-agent: Bingbot Allow: / User-agent: Slurp Allow: / User-agent: DuckDuckBot Allow: / # Block certain paths if needed # Disallow: /temp/ # Disallow: /private/`; } // Generate files const sitemap = generateSitemap(); const robots = generateRobotsTxt(); // Write files const publicDir = path.join(__dirname, '..', 'public'); if (!fs.existsSync(publicDir)) { fs.mkdirSync(publicDir, { recursive: true }); } fs.writeFileSync(path.join(publicDir, 'sitemap.xml'), sitemap); fs.writeFileSync(path.join(publicDir, 'robots.txt'), robots); console.log('✅ Sitemap and robots.txt generated successfully!'); console.log(`📄 Generated ${pages.length} URLs in sitemap.xml`); console.log('🤖 Updated robots.txt with sitemap reference'); console.log(`📅 Last modified: ${currentDate}`); // Also log the pages for verification console.log('\n📋 Pages included in sitemap:'); pages.forEach(page => { console.log(` ${page.path} (Priority: ${page.priority}, ${page.changefreq})`); }); ``` -------------------------------------------------------------------------------- /docs/dw_catalog/Catalog.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class Catalog ## Inheritance Hierarchy - Object - dw.object.PersistentObject - dw.object.ExtensibleObject - dw.catalog.Catalog ## Description Represents a Commerce Cloud Digital Catalog. Catalogs are containers of products and other product-related information and can be shared between sites. Every product in the system is contained in (or "owned by") exactly one catalog. Every site has a single "site catalog" which defines the products that are available to purchase on that site. The static method CatalogMgr.getSiteCatalog() returns the site catalog for the current site. Catalogs are organized into a tree of categories with a single top-level root category. Products are assigned to categories within catalogs. They can be assigned to categories in their owning catalog, or other catalogs. They can be assigned to multiple categories within the same catalog. Products that are not assigned to any categories are considered "uncategorized." A product has a single "classification category" in some catalog, and one "primary category" per catalog. The classification category defines the attribute set of the product. The primary category is used as standard presentation context within that catalog in the storefront. While Commerce Cloud Digital does not currently distinguish different catalog types, it is common practice to have two general types of catalog: "Product catalogs" typically contain detailed product information and are frequently generated from some backend PIM system. "Site Catalogs" define the category structure of the storefront and contain primarily the assignments of these categories to the products defined in the product catalogs. The site catalog is assigned to the site. In addition to products and categories, catalogs contain recommendations, shared variation attributes which can be used by multiple master products, and shared product options which can be used by multiple option products. ## Properties ### description **Type:** String (Read Only) The value of the localized extensible object attribute "shortDescription" for the current locale. ### displayName **Type:** String (Read Only) The value of the localized extensible object attribute "displayName" for the current locale. ### ID **Type:** String (Read Only) The value of attribute 'id'. ### root **Type:** Category (Read Only) The object for the relation 'root'. ## Constructor Summary ## Method Summary ### getDescription **Signature:** `getDescription() : String` Returns the value of the localized extensible object attribute "shortDescription" for the current locale. ### getDisplayName **Signature:** `getDisplayName() : String` Returns the value of the localized extensible object attribute "displayName" for the current locale. ### getID **Signature:** `getID() : String` Returns the value of attribute 'id'. ### getRoot **Signature:** `getRoot() : Category` Returns the object for the relation 'root'. ## Method Detail ## Method Details ### getDescription **Signature:** `getDescription() : String` **Description:** Returns the value of the localized extensible object attribute "shortDescription" for the current locale. **Returns:** The value of the attribute for the current locale, or null if it wasn't found. --- ### getDisplayName **Signature:** `getDisplayName() : String` **Description:** Returns the value of the localized extensible object attribute "displayName" for the current locale. **Returns:** The value of the attribute for the current locale, or null if it wasn't found. --- ### getID **Signature:** `getID() : String` **Description:** Returns the value of attribute 'id'. **Returns:** the value of the attribute 'id' --- ### getRoot **Signature:** `getRoot() : Category` **Description:** Returns the object for the relation 'root'. **Returns:** the object for the relation 'root'. --- ``` -------------------------------------------------------------------------------- /docs/dw_catalog/ProductMgr.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class ProductMgr ## Inheritance Hierarchy - Object - dw.catalog.ProductMgr ## Description Provides helper methods for getting products based on Product ID or Catalog. ## Constructor Summary ## Method Summary ### getProduct **Signature:** `static getProduct(productID : String) : Product` Returns the product with the specified id. ### queryAllSiteProducts **Signature:** `static queryAllSiteProducts() : SeekableIterator` Returns all products assigned to the current site. ### queryAllSiteProductsSorted **Signature:** `static queryAllSiteProductsSorted() : SeekableIterator` Returns all products assigned to the current site. ### queryProductsInCatalog **Signature:** `static queryProductsInCatalog(catalog : Catalog) : SeekableIterator` Returns all products assigned to the the specified catalog, where assignment has the same meaning as it does for queryAllSiteProducts(). ### queryProductsInCatalogSorted **Signature:** `static queryProductsInCatalogSorted(catalog : Catalog) : SeekableIterator` Returns all products assigned to the the specified catalog. ## Method Detail ## Method Details ### getProduct **Signature:** `static getProduct(productID : String) : Product` **Description:** Returns the product with the specified id. **Parameters:** - `productID`: the product identifier. **Returns:** Product for specified id or null --- ### queryAllSiteProducts **Signature:** `static queryAllSiteProducts() : SeekableIterator` **Description:** Returns all products assigned to the current site. A product is assigned to a site if it is assigned to at least one category of the site catalog or it is a variant and it's master product is assigned to the current site It is strongly recommended to call close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. **Returns:** Iterator of all site products **See Also:** SeekableIterator.close() --- ### queryAllSiteProductsSorted **Signature:** `static queryAllSiteProductsSorted() : SeekableIterator` **Description:** Returns all products assigned to the current site. Works like queryAllSiteProducts(), but additionally sorts the result set by product ID. It is strongly recommended to call close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. **Returns:** Iterator of all site products sorted by product ID. **See Also:** SeekableIterator.close() --- ### queryProductsInCatalog **Signature:** `static queryProductsInCatalog(catalog : Catalog) : SeekableIterator` **Description:** Returns all products assigned to the the specified catalog, where assignment has the same meaning as it does for queryAllSiteProducts(). It is strongly recommended to call close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. **Parameters:** - `catalog`: The catalog whose assigned products should be returned. **Returns:** Iterator of all products assigned to specified catalog. **See Also:** SeekableIterator.close() --- ### queryProductsInCatalogSorted **Signature:** `static queryProductsInCatalogSorted(catalog : Catalog) : SeekableIterator` **Description:** Returns all products assigned to the the specified catalog. Works like queryProductsInCatalog(), but additionally sorts the result set by product ID. It is strongly recommended to call close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. **Parameters:** - `catalog`: The catalog whose assigned products should be returned. **Returns:** Iterator of all products assigned to specified catalog sorted by product ID. **See Also:** SeekableIterator.close() --- ``` -------------------------------------------------------------------------------- /docs/dw_catalog/CatalogMgr.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class CatalogMgr ## Inheritance Hierarchy - Object - dw.catalog.CatalogMgr ## Description Provides helper methods for getting categories. ## Properties ### siteCatalog **Type:** Catalog (Read Only) The catalog of the current site or null if no catalog is assigned to the site. ### sortingOptions **Type:** List (Read Only) A list containing the sorting options configured for this site. ### sortingRules **Type:** Collection (Read Only) A collection containing all of the sorting rules for this site, including global sorting rules. ## Constructor Summary ## Method Summary ### getCatalog **Signature:** `static getCatalog(id : String) : Catalog` Returns the catalog identified by the specified catalog id. ### getCategory **Signature:** `static getCategory(id : String) : Category` Returns the category of the site catalog identified by the specified category id. ### getSiteCatalog **Signature:** `static getSiteCatalog() : Catalog` Returns the catalog of the current site or null if no catalog is assigned to the site. ### getSortingOption **Signature:** `static getSortingOption(id : String) : SortingOption` Returns the sorting option with the given ID for this site, or null if there is no such option. ### getSortingOptions **Signature:** `static getSortingOptions() : List` Returns a list containing the sorting options configured for this site. ### getSortingRule **Signature:** `static getSortingRule(id : String) : SortingRule` Returns the sorting rule with the given ID for this site, or null if there is no such rule. ### getSortingRules **Signature:** `static getSortingRules() : Collection` Returns a collection containing all of the sorting rules for this site, including global sorting rules. ## Method Detail ## Method Details ### getCatalog **Signature:** `static getCatalog(id : String) : Catalog` **Description:** Returns the catalog identified by the specified catalog id. Returns null if no catalog with the specified id exists in the current organization context. **Parameters:** - `id`: Catalog id **Returns:** the catalog or null. --- ### getCategory **Signature:** `static getCategory(id : String) : Category` **Description:** Returns the category of the site catalog identified by the specified category id. Returns null if no site catalog is defined, or no category with the specified id is found in the site catalog. **Parameters:** - `id`: the category identifier. **Returns:** the category of the site catalog identified by the specified category id or null if no site catalog is found. --- ### getSiteCatalog **Signature:** `static getSiteCatalog() : Catalog` **Description:** Returns the catalog of the current site or null if no catalog is assigned to the site. **Returns:** the catalog of the current site or null. --- ### getSortingOption **Signature:** `static getSortingOption(id : String) : SortingOption` **Description:** Returns the sorting option with the given ID for this site, or null if there is no such option. **Parameters:** - `id`: the ID of the sorting option **Returns:** a SortingOption or null. --- ### getSortingOptions **Signature:** `static getSortingOptions() : List` **Description:** Returns a list containing the sorting options configured for this site. **Returns:** a list of SortingOption objects --- ### getSortingRule **Signature:** `static getSortingRule(id : String) : SortingRule` **Description:** Returns the sorting rule with the given ID for this site, or null if there is no such rule. **Parameters:** - `id`: the ID of the sorting rule **Returns:** a SortingRule or null. --- ### getSortingRules **Signature:** `static getSortingRules() : Collection` **Description:** Returns a collection containing all of the sorting rules for this site, including global sorting rules. **Returns:** a collection of SortingRule objects --- ``` -------------------------------------------------------------------------------- /docs/dw_util/SortedSet.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.util # Class SortedSet ## Inheritance Hierarchy - Object - dw.util.Collection - dw.util.Set - dw.util.SortedSet ## Description A set that further guarantees that its iterator will traverse the set in ascending element order, sorted according to the natural ordering of its elements (only supported for Number, String, Date, Money and Quantity), or by a comparator provided at sorted set creation time. ## Constructor Summary SortedSet() Constructor to create a new SortedSet. SortedSet(comparator : Object) Constructor to create a new SortedSet. SortedSet(collection : Collection) Constructor for a new SortedSet. ## Method Summary ### clone **Signature:** `clone() : SortedSet` Returns a shallow copy of this set. ### first **Signature:** `first() : Object` Returns the first (lowest) element currently in this sorted set. ### headSet **Signature:** `headSet(key : Object) : SortedSet` Returns a view of the portion of this sorted set whose elements are strictly less than toElement. ### last **Signature:** `last() : Object` Returns the last (highest) element currently in this sorted set. ### subSet **Signature:** `subSet(from : Object, to : Object) : SortedSet` Returns a view of the portion of this sorted set whose elements range from fromElement, inclusive, to toElement, exclusive. ### tailSet **Signature:** `tailSet(key : Object) : SortedSet` Returns a view of the portion of this sorted set whose elements are greater than or equal to fromElement. ## Constructor Detail ## Method Detail ## Method Details ### clone **Signature:** `clone() : SortedSet` **Description:** Returns a shallow copy of this set. **Returns:** a shallow copy of this set. --- ### first **Signature:** `first() : Object` **Description:** Returns the first (lowest) element currently in this sorted set. **Returns:** the first (lowest) element currently in this sorted set. --- ### headSet **Signature:** `headSet(key : Object) : SortedSet` **Description:** Returns a view of the portion of this sorted set whose elements are strictly less than toElement. The returned sorted set is backed by this sorted set, so changes in the returned sorted set are reflected in this sorted set, and vice-versa. The returned sorted set supports all optional set operations. **Parameters:** - `key`: high endpoint (exclusive) of the headSet. **Returns:** a view of the specified initial range of this sorted set. --- ### last **Signature:** `last() : Object` **Description:** Returns the last (highest) element currently in this sorted set. **Returns:** the last (highest) element currently in this sorted set. --- ### subSet **Signature:** `subSet(from : Object, to : Object) : SortedSet` **Description:** Returns a view of the portion of this sorted set whose elements range from fromElement, inclusive, to toElement, exclusive. (If fromElement and toElement are equal, the returned sorted set is empty.) The returned sorted set is backed by this sorted set, so changes in the returned sorted set are reflected in this sorted set, and vice-versa. The returned sorted set supports all optional set operations that this sorted set supports. **Parameters:** - `from`: low endpoint (inclusive) of the subSet. - `to`: high endpoint (exclusive) of the subSet. **Returns:** a view of the specified range within this sorted set. --- ### tailSet **Signature:** `tailSet(key : Object) : SortedSet` **Description:** Returns a view of the portion of this sorted set whose elements are greater than or equal to fromElement. The returned sorted set is backed by this sorted set, so changes in the returned sorted set are reflected in this sorted set, and vice-versa. The returned sorted set supports all optional set operations. **Parameters:** - `key`: low endpoint (inclusive) of the tailSet. **Returns:** a view of the specified final range of this sorted set. --- ``` -------------------------------------------------------------------------------- /docs/dw_catalog/PriceBook.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class PriceBook ## Inheritance Hierarchy - Object - dw.object.PersistentObject - dw.object.ExtensibleObject - dw.catalog.PriceBook ## Description Represents a price book. ## Properties ### currencyCode **Type:** String (Read Only) The currency code of the price book. ### description **Type:** String (Read Only) The description of the price book. ### displayName **Type:** String (Read Only) The display name of the price book. ### ID **Type:** String (Read Only) The ID of the price book. ### online **Type:** boolean (Read Only) The online status of the price book. The online status is calculated from the online status flag and the onlineFrom onlineTo dates defined for the price book. ### onlineFlag **Type:** boolean (Read Only) The online status flag of the price book. ### onlineFrom **Type:** Date (Read Only) The date from which the price book is online or valid. ### onlineTo **Type:** Date (Read Only) The date until which the price book is online or valid. ### parentPriceBook **Type:** PriceBook (Read Only) The parent price book. ## Constructor Summary ## Method Summary ### getCurrencyCode **Signature:** `getCurrencyCode() : String` Returns the currency code of the price book. ### getDescription **Signature:** `getDescription() : String` Returns the description of the price book. ### getDisplayName **Signature:** `getDisplayName() : String` Returns the display name of the price book. ### getID **Signature:** `getID() : String` Returns the ID of the price book. ### getOnlineFlag **Signature:** `getOnlineFlag() : boolean` Returns the online status flag of the price book. ### getOnlineFrom **Signature:** `getOnlineFrom() : Date` Returns the date from which the price book is online or valid. ### getOnlineTo **Signature:** `getOnlineTo() : Date` Returns the date until which the price book is online or valid. ### getParentPriceBook **Signature:** `getParentPriceBook() : PriceBook` Returns the parent price book. ### isOnline **Signature:** `isOnline() : boolean` Returns the online status of the price book. ## Method Detail ## Method Details ### getCurrencyCode **Signature:** `getCurrencyCode() : String` **Description:** Returns the currency code of the price book. **Returns:** Currency code of the price book --- ### getDescription **Signature:** `getDescription() : String` **Description:** Returns the description of the price book. **Returns:** Currency code of the price book --- ### getDisplayName **Signature:** `getDisplayName() : String` **Description:** Returns the display name of the price book. **Returns:** Display name of the price book --- ### getID **Signature:** `getID() : String` **Description:** Returns the ID of the price book. **Returns:** ID of the price book --- ### getOnlineFlag **Signature:** `getOnlineFlag() : boolean` **Description:** Returns the online status flag of the price book. **Returns:** the online status flag of the price book. --- ### getOnlineFrom **Signature:** `getOnlineFrom() : Date` **Description:** Returns the date from which the price book is online or valid. **Returns:** the date from which the price book is online or valid. --- ### getOnlineTo **Signature:** `getOnlineTo() : Date` **Description:** Returns the date until which the price book is online or valid. **Returns:** the date until which the price book is online or valid. --- ### getParentPriceBook **Signature:** `getParentPriceBook() : PriceBook` **Description:** Returns the parent price book. **Returns:** Parent price book --- ### isOnline **Signature:** `isOnline() : boolean` **Description:** Returns the online status of the price book. The online status is calculated from the online status flag and the onlineFrom onlineTo dates defined for the price book. **Returns:** The online status of the price book. --- ``` -------------------------------------------------------------------------------- /docs/dw_system/Cache.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.system # Class Cache ## Inheritance Hierarchy - Object - dw.system.Cache ## Description The Cache class represents a custom cache. A cache stores data over multiple requests. Each cartridge can define its own caches for different business requirements. To limit the visibility of cache entries by scope, for example, by site, catalog, or external system, include the scope reference when constructing the key. For example: var cache = CacheMgr.getCache( 'SiteConfigurations' ); cache.get( Site.current.ID + "config", function loadSiteConfiguration() {return loadCfg( Site.current );} ); Do not build the cache key using personal user data, since the key might be visible in log messages. There is never a guarantee that a stored object can be retrieved from the cache. The storage allocated for entries is limited and clearing or invalidation might occur at any time. To maintain the cache size limits, the cache evicts entries that are less likely to be used again. For example, the cache might evict an entry because it hasn't been used recently or very often. Cache entries aren't synchronized between different application servers. The cache returns immutable copies of the original objects put into the cache. Lists are converted to arrays during this process. Only JavaScript primitive values and tree-like object structures can be stored as entries. Object structures can consist of arrays, lists, and basic JavaScript objects. Script API classes are not supported, except List and its subclasses. null can be stored as a value. undefined can't be stored. See CacheMgr for details about how to configure a custom cache. ## Constructor Summary ## Method Summary ### get **Signature:** `get(key : String, loader : Function) : Object` Returns the value associated with key in this cache, or invokes the loader function to generate the entry if there is no entry found. ### get **Signature:** `get(key : String) : Object` Returns the value associated with key in this cache. ### invalidate **Signature:** `invalidate(key : String) : void` Removes the cache entry for key (if one exists) manually before the cache's eviction strategy goes into effect. ### put **Signature:** `put(key : String, value : Object) : void` Stores the specified entry directly into the cache, replacing any previously cached entry for key if one exists. ## Method Detail ## Method Details ### get **Signature:** `get(key : String, loader : Function) : Object` **Description:** Returns the value associated with key in this cache, or invokes the loader function to generate the entry if there is no entry found. The generated entry is stored for future retrieval. If the loader function returns undefined, this value is not stored in the cache. **Parameters:** - `key`: The cache key. - `loader`: The loader function that is called if no value is stored in the cache. **Returns:** The value found in the cache or the value returned from the loader function call. --- ### get **Signature:** `get(key : String) : Object` **Description:** Returns the value associated with key in this cache. If there is no entry in the cache then undefined is returned. **Parameters:** - `key`: The cache key. **Returns:** The stored value or undefined if no value is found in the cache. --- ### invalidate **Signature:** `invalidate(key : String) : void` **Description:** Removes the cache entry for key (if one exists) manually before the cache's eviction strategy goes into effect. **Parameters:** - `key`: The cache key. --- ### put **Signature:** `put(key : String, value : Object) : void` **Description:** Stores the specified entry directly into the cache, replacing any previously cached entry for key if one exists. Storing undefined as value has the same effect as calling invalidate(String) for that key. **Parameters:** - `key`: The cache key. - `value`: The value to be store in the cache. --- ``` -------------------------------------------------------------------------------- /docs/dw_web/FormAction.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.web # Class FormAction ## Inheritance Hierarchy - Object - dw.web.FormElement - dw.web.FormAction ## Description The FormAction class represents the action in form instance hierarchy. ## Properties ### description **Type:** String (Read Only) The optional description for the action. The description could be used as tooltip for the action. ### label **Type:** String (Read Only) The optional label for the action. The label would be typically used as button text. ### object **Type:** Object (Read Only) The object that was bound to the form in which the action is contained. The method is a convenience method for getParent().getObject(). In most cases this is actually the object for which the specific action is triggered. ### submitted **Type:** boolean (Read Only) Identifies if the form action was submitted from the client to the server. ### triggered **Type:** boolean (Read Only) Identifies that this action is triggerd. An action is only triggered if it was submitted and the constraints, regarding a valid form, are met. ### x **Type:** Number (Read Only) In case of an image button, returns the x coordinate of the last click. ### y **Type:** Number (Read Only) In case of an image button, returns the y coordinate of the last click. ## Constructor Summary ## Method Summary ### getDescription **Signature:** `getDescription() : String` Returns the optional description for the action. ### getLabel **Signature:** `getLabel() : String` Returns the optional label for the action. ### getObject **Signature:** `getObject() : Object` Returns the object that was bound to the form in which the action is contained. ### getX **Signature:** `getX() : Number` In case of an image button, returns the x coordinate of the last click. ### getY **Signature:** `getY() : Number` In case of an image button, returns the y coordinate of the last click. ### isSubmitted **Signature:** `isSubmitted() : boolean` Identifies if the form action was submitted from the client to the server. ### isTriggered **Signature:** `isTriggered() : boolean` Identifies that this action is triggerd. ## Method Detail ## Method Details ### getDescription **Signature:** `getDescription() : String` **Description:** Returns the optional description for the action. The description could be used as tooltip for the action. **Returns:** the optional description for the action. --- ### getLabel **Signature:** `getLabel() : String` **Description:** Returns the optional label for the action. The label would be typically used as button text. **Returns:** the optional label for the action. --- ### getObject **Signature:** `getObject() : Object` **Description:** Returns the object that was bound to the form in which the action is contained. The method is a convenience method for getParent().getObject(). In most cases this is actually the object for which the specific action is triggered. **Returns:** the object that was bound to the form in which the action is contained. --- ### getX **Signature:** `getX() : Number` **Description:** In case of an image button, returns the x coordinate of the last click. **Returns:** the x coordinate of the last click. --- ### getY **Signature:** `getY() : Number` **Description:** In case of an image button, returns the y coordinate of the last click. **Returns:** the y coordinate of the last click. --- ### isSubmitted **Signature:** `isSubmitted() : boolean` **Description:** Identifies if the form action was submitted from the client to the server. **Returns:** true if the form action was submitted, false otherwise. --- ### isTriggered **Signature:** `isTriggered() : boolean` **Description:** Identifies that this action is triggerd. An action is only triggered if it was submitted and the constraints, regarding a valid form, are met. **Returns:** true if the action is triggered, false otherwise. --- ``` -------------------------------------------------------------------------------- /docs/dw_web/PageMetaData.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.web # Class PageMetaData ## Inheritance Hierarchy - Object - dw.web.PageMetaData ## Description Contains meta data about the page. For each request an instance of this class will be placed in the pipeline dictionary under the key "CurrentPageMetaData". The information stored in CurrentPageMetaData can be referenced in templates and rendered in an HTML head section: for example: <head> <title>${pdict.CurrentPageMetaData.title}</title> <meta name="description" content="${pdict.CurrentPageMetaData.description}"/> . . . </head> To update the CurrentPageMetaData there is the pipelet UpdatePageMetaData provided. ## Properties ### description **Type:** String The page's description. ### keywords **Type:** String The page's key words. ### pageMetaTags **Type:** Array (Read Only) All page meta tags added to this container. ### title **Type:** String The page's title. ## Constructor Summary ## Method Summary ### addPageMetaTag **Signature:** `addPageMetaTag(pageMetaTag : PageMetaTag) : void` Adds a page meta tag to this container. ### addPageMetaTags **Signature:** `addPageMetaTags(pageMetaTags : Array) : void` Adds a page meta tags list to this container. ### getDescription **Signature:** `getDescription() : String` Returns the page's description. ### getKeywords **Signature:** `getKeywords() : String` Returns the page's key words. ### getPageMetaTags **Signature:** `getPageMetaTags() : Array` Returns all page meta tags added to this container. ### getTitle **Signature:** `getTitle() : String` Returns the page's title. ### isPageMetaTagSet **Signature:** `isPageMetaTagSet(id : String) : boolean` Returns true if a page meta tag with the given ID is set, false otherwise. ### setDescription **Signature:** `setDescription(description : String) : void` Sets the page's description. ### setKeywords **Signature:** `setKeywords(keywords : String) : void` Sets the page's key words. ### setTitle **Signature:** `setTitle(title : String) : void` Sets the page's title. ## Method Detail ## Method Details ### addPageMetaTag **Signature:** `addPageMetaTag(pageMetaTag : PageMetaTag) : void` **Description:** Adds a page meta tag to this container. **Parameters:** - `pageMetaTag`: the page meta tag to be added --- ### addPageMetaTags **Signature:** `addPageMetaTags(pageMetaTags : Array) : void` **Description:** Adds a page meta tags list to this container. **Parameters:** - `pageMetaTags`: the page meta tags list to be added --- ### getDescription **Signature:** `getDescription() : String` **Description:** Returns the page's description. **Returns:** the page's description. --- ### getKeywords **Signature:** `getKeywords() : String` **Description:** Returns the page's key words. **Returns:** the page's key words. --- ### getPageMetaTags **Signature:** `getPageMetaTags() : Array` **Description:** Returns all page meta tags added to this container. **Returns:** page meta tags --- ### getTitle **Signature:** `getTitle() : String` **Description:** Returns the page's title. **Returns:** the page's title. --- ### isPageMetaTagSet **Signature:** `isPageMetaTagSet(id : String) : boolean` **Description:** Returns true if a page meta tag with the given ID is set, false otherwise. **Parameters:** - `id`: the ID to be check if a page meta tag is set **Returns:** true if a page meta tag with the given ID is set, false otherwise --- ### setDescription **Signature:** `setDescription(description : String) : void` **Description:** Sets the page's description. **Parameters:** - `description`: the page's description. --- ### setKeywords **Signature:** `setKeywords(keywords : String) : void` **Description:** Sets the page's key words. **Parameters:** - `keywords`: the page's key words. --- ### setTitle **Signature:** `setTitle(title : String) : void` **Description:** Sets the page's title. **Parameters:** - `title`: the page's title. --- ``` -------------------------------------------------------------------------------- /src/utils/log-tool-config.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSpec, LogToolValidators, LogMessageFormatter } from './log-tool-utils.js'; import { ValidationHelpers, CommonValidations } from '../core/handlers/validation-helpers.js'; import { LogToolName, getLimit } from './log-tool-constants.js'; /** * Configuration for standard log tools * Maps each tool to its validation, execution, and messaging logic */ export const LOG_TOOL_CONFIG: Record<LogToolName, ToolSpec> = { get_latest_error: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_error'), exec: async (args, client) => client.getLatestLogs('error', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('error', args.limit as number, args.date as string), }, get_latest_warn: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_warn'), exec: async (args, client) => client.getLatestLogs('warn', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('warn', args.limit as number, args.date as string), }, get_latest_info: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_info'), exec: async (args, client) => client.getLatestLogs('info', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('info', args.limit as number, args.date as string), }, get_latest_debug: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_debug'), exec: async (args, client) => client.getLatestLogs('debug', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('debug', args.limit as number, args.date as string), }, summarize_logs: { exec: async (args, client) => client.summarizeLogs(args.date as string), logMessage: (args) => LogMessageFormatter.formatSummarizeLogs(args.date as string), }, search_logs: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'search'), }), validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('pattern'), toolName); LogToolValidators.validateLimit(args.limit as number, toolName); if (args.logLevel) { LogToolValidators.validateLogLevel(args.logLevel as string, toolName); } }, exec: async (args, client) => client.searchLogs( args.pattern as string, args.logLevel as any, args.limit as number, args.date as string, ), logMessage: (args) => LogMessageFormatter.formatSearchLogs( args.pattern as string, args.logLevel as string, args.limit as number, args.date as string, ), }, list_log_files: { exec: async (args, client) => client.listLogFiles(), logMessage: () => LogMessageFormatter.formatListLogFiles(), }, get_log_file_contents: { validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('filename'), toolName); LogToolValidators.validateFilename(args.filename as string, toolName); LogToolValidators.validateMaxBytes(args.maxBytes as number, toolName); }, exec: async (args, client) => client.getLogFileContents( args.filename as string, args.maxBytes as number, args.tailOnly as boolean, ), logMessage: (args) => LogMessageFormatter.formatGetLogFileContents( args.filename as string, args.maxBytes as number, args.tailOnly as boolean, ), }, }; ``` -------------------------------------------------------------------------------- /src/core/handlers/log-tool-config.ts: -------------------------------------------------------------------------------- ```typescript import { ToolSpec, LogToolValidators, LogMessageFormatter } from '../../utils/log-tool-utils.js'; import { ValidationHelpers, CommonValidations } from './validation-helpers.js'; import { LogToolName, getLimit } from '../../utils/log-tool-constants.js'; /** * Configuration for standard log tools * Maps each tool to its validation, execution, and messaging logic */ export const LOG_TOOL_CONFIG: Record<LogToolName, ToolSpec> = { get_latest_error: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_error'), exec: async (args, client) => client.getLatestLogs('error', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('error', args.limit as number, args.date as string), }, get_latest_warn: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_warn'), exec: async (args, client) => client.getLatestLogs('warn', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('warn', args.limit as number, args.date as string), }, get_latest_info: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_info'), exec: async (args, client) => client.getLatestLogs('info', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('info', args.limit as number, args.date as string), }, get_latest_debug: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'latest'), }), validate: (args) => LogToolValidators.validateLimit(args.limit as number, 'get_latest_debug'), exec: async (args, client) => client.getLatestLogs('debug', args.limit as number, args.date as string), logMessage: (args) => LogMessageFormatter.formatLatestLogs('debug', args.limit as number, args.date as string), }, summarize_logs: { exec: async (args, client) => client.summarizeLogs(args.date as string), logMessage: (args) => LogMessageFormatter.formatSummarizeLogs(args.date as string), }, search_logs: { defaults: (args) => ({ limit: getLimit(args.limit as number, 'search'), }), validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('pattern'), toolName); LogToolValidators.validateLimit(args.limit as number, toolName); if (args.logLevel) { LogToolValidators.validateLogLevel(args.logLevel as string, toolName); } }, exec: async (args, client) => client.searchLogs( args.pattern as string, args.logLevel as any, args.limit as number, args.date as string, ), logMessage: (args) => LogMessageFormatter.formatSearchLogs( args.pattern as string, args.logLevel as string, args.limit as number, args.date as string, ), }, list_log_files: { exec: async (args, client) => client.listLogFiles(), logMessage: () => LogMessageFormatter.formatListLogFiles(), }, get_log_file_contents: { validate: (args, toolName) => { ValidationHelpers.validateArguments(args, CommonValidations.requiredString('filename'), toolName); LogToolValidators.validateFilename(args.filename as string, toolName); LogToolValidators.validateMaxBytes(args.maxBytes as number, toolName); }, exec: async (args, client) => client.getLogFileContents( args.filename as string, args.maxBytes as number, args.tailOnly as boolean, ), logMessage: (args) => LogMessageFormatter.formatGetLogFileContents( args.filename as string, args.maxBytes as number, args.tailOnly as boolean, ), }, }; ``` -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- ```typescript #!/usr/bin/env node /** * Main entry point for the SFCC Development MCP Server */ import { SFCCDevServer } from './core/server.js'; import { ConfigurationFactory } from './config/configuration-factory.js'; import { Logger } from './utils/logger.js'; import { existsSync } from 'fs'; import { resolve } from 'path'; /** * Parse command line arguments to extract configuration options */ function parseCommandLineArgs(): { dwJsonPath?: string; debug?: boolean } { const args = process.argv.slice(2); const options: { dwJsonPath?: string; debug?: boolean } = {}; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg === '--dw-json' && i + 1 < args.length) { options.dwJsonPath = args[i + 1]; i++; // Skip the next argument since we consumed it } else if (arg === '--debug' && i + 1 < args.length) { const debugValue = args[i + 1].toLowerCase(); options.debug = debugValue === 'true' || debugValue === '1' || debugValue === 'yes'; i++; // Skip the next argument since we consumed it } else if (arg === '--debug') { // Allow --debug without a value to default to true options.debug = true; } } return options; } /** * Find dw.json file in common locations */ function findDwJsonFile(): string | undefined { const commonPaths = [ './dw.json', '../dw.json', '../../dw.json', process.env.HOME ? resolve(process.env.HOME, 'dw.json') : null, ].filter(Boolean) as string[]; for (const path of commonPaths) { if (existsSync(path)) { const logger = Logger.getInstance(); logger.debug(`Found dw.json at: ${path}`); return path; } } return undefined; } /** * Main application entry point */ async function main(): Promise<void> { try { const options = parseCommandLineArgs(); const debug = options.debug ?? false; // Initialize the global logger with debug setting Logger.initialize('SFCC-MCP-Server', true, debug); const logger = Logger.getInstance(); logger.log('Starting SFCC Development MCP Server...'); if (debug) { logger.log('Debug mode enabled'); } // Try to find dw.json if not explicitly provided const dwJsonPath = options.dwJsonPath ?? findDwJsonFile(); // Create configuration using the factory const config = ConfigurationFactory.create({ dwJsonPath, // Add support for environment variables as fallback hostname: process.env.SFCC_HOSTNAME, username: process.env.SFCC_USERNAME, password: process.env.SFCC_PASSWORD, clientId: process.env.SFCC_CLIENT_ID, clientSecret: process.env.SFCC_CLIENT_SECRET, }); // Log configuration summary (without sensitive data) const capabilities = ConfigurationFactory.getCapabilities(config); if (capabilities.isLocalMode) { logger.log('Running in Local Mode - SFCC class documentation only'); logger.log('To access SFCC logs and OCAPI, provide hostname and credentials'); } else { logger.log(`Configuration loaded - Hostname: ${config.hostname}`); logger.log(`Available features: Logs=${capabilities.canAccessLogs}, OCAPI=${capabilities.canAccessOCAPI}, WebDAV=${capabilities.canAccessWebDAV}`); } // Create and start the server const server = new SFCCDevServer(config); await server.run(); } catch (error) { const logger = Logger.getInstance(); logger.error('Failed to start SFCC Development MCP Server:', error); if (error instanceof Error) { if (error.message.includes('not found')) { logger.log('\nConfiguration Help:'); logger.log('1. Create a dw.json file with your SFCC credentials'); logger.log('2. Use --dw-json /path/to/dw.json'); logger.log('3. Set environment variables: SFCC_HOSTNAME, SFCC_USERNAME, SFCC_PASSWORD'); } } process.exit(1); } } // Run the main function main().catch((error) => { const logger = Logger.getInstance(); logger.error('Unhandled error:', error); process.exit(1); }); ``` -------------------------------------------------------------------------------- /docs-site/public/llms.txt: -------------------------------------------------------------------------------- ``` # SFCC Development MCP Server > A comprehensive Model Context Protocol (MCP) server that provides AI assistants with direct access to Salesforce B2C Commerce Cloud development tools, documentation, best practices, and real-time debugging capabilities. This server bridges the gap between AI assistants and SFCC development workflows, offering both documentation-only mode (no credentials required) and full mode (with live SFCC instance access). It provides 36 tools across 8 categories including complete SFCC API documentation, curated best practices guides, enhanced SFRA documentation, real-time log analysis, system object exploration, and automated cartridge generation. Key architectural principles: - **Capability-gated**: Tools requiring SFCC credentials are only exposed when valid configuration is provided - **Modular handlers**: Clean separation between tool routing and domain logic - **Dependency injection**: Testable architecture with service abstractions - **Performance optimized**: Efficient caching and range request optimization for log analysis ## Core Documentation - [Installation Guide](https://sfcc-mcp-dev.rhino-inquisitor.com/#/installation): Step-by-step setup instructions for different AI interfaces (Claude Desktop, Cursor, GitHub Copilot) - [Configuration Guide](https://sfcc-mcp-dev.rhino-inquisitor.com/#/configuration): Detailed configuration options for both documentation-only and full modes - [Available Tools](https://sfcc-mcp-dev.rhino-inquisitor.com/#/tools): Comprehensive list of all 36 tools organized by category with usage examples - [Features Overview](https://sfcc-mcp-dev.rhino-inquisitor.com/#/features): Complete feature breakdown including operating modes and capabilities ## Development Resources - [Development Guide](https://sfcc-mcp-dev.rhino-inquisitor.com/#/development): Architecture overview, adding new tools, testing guidelines, and contribution workflow - [Examples & Use Cases](https://sfcc-mcp-dev.rhino-inquisitor.com/#/examples): Practical examples for documentation access, log analysis, cartridge generation, and debugging workflows - [AI Interface Setup](https://sfcc-mcp-dev.rhino-inquisitor.com/#/ai-interfaces): Platform-specific setup guides for Claude Desktop, Cursor IDE, and GitHub Copilot integration ## Security & Best Practices - [Security Considerations](https://sfcc-mcp-dev.rhino-inquisitor.com/#/security): Credential management, local security practices, and network security guidelines - [Troubleshooting Guide](https://sfcc-mcp-dev.rhino-inquisitor.com/#/troubleshooting): Common issues, debugging steps, and configuration validation ## Tool Categories - **SFCC Documentation Tools (5 tools)**: Complete API class information, method search, and namespace exploration - **Best Practices Guides (4 tools)**: Curated development guidelines for cartridges, hooks, controllers, security, and performance - **SFRA Documentation Tools (5 tools)**: Enhanced Storefront Reference Architecture documentation with dynamic discovery - **Log Analysis Tools (8 tools)**: Real-time error monitoring, pattern matching, and system health analysis - **Job Log Tools (5 tools)**: Specialized job log analysis, execution summaries, and custom job step debugging - **System Object Tools (6 tools)**: Custom attribute discovery, site preference management, and schema exploration - **Cartridge Generation Tools (1 tool)**: Automated cartridge structure creation with complete project setup - **Code Version Tools (2 tools)**: Code version management and deployment activation ## Optional - [GitHub Repository](https://github.com/taurgis/sfcc-dev-mcp): Source code, issues, and contribution guidelines - [NPM Package](https://www.npmjs.com/package/sfcc-dev-mcp): Package installation and version information - [CHANGELOG](https://github.com/taurgis/sfcc-dev-mcp/blob/main/CHANGELOG.md): Release notes and version history - [Contributing Guidelines](https://github.com/taurgis/sfcc-dev-mcp/blob/main/CONTRIBUTING.md): Development setup and contribution workflow ``` -------------------------------------------------------------------------------- /docs/dw_io/RandomAccessFileReader.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.io # Class RandomAccessFileReader ## Inheritance Hierarchy - Object - dw.io.RandomAccessFileReader ## Description Instances of this class support reading from a random access file. A random access file behaves like a large array of bytes stored in the file system. There is a kind of cursor, or index into the implied array, called the file pointer. Read operations read bytes starting at the file pointer and advance the file pointer past the bytes read. The file pointer can be read by the getPosition method and set by the setPosition method. ## Constants ### MAX_READ_BYTES **Type:** Number = 10240L The maximum number of bytes that a single call to readBytes(Number) can return == 10KB ## Properties ### position **Type:** Number The current offset in this file. ## Constructor Summary RandomAccessFileReader(file : File) Construct a reader for random read access to the provided file. ## Method Summary ### close **Signature:** `close() : void` Closes this random access file reader and releases any system resources associated with the stream. ### getPosition **Signature:** `getPosition() : Number` Returns the current offset in this file. ### length **Signature:** `length() : Number` Returns the length of this file. ### readByte **Signature:** `readByte() : Number` Reads a signed eight-bit value from the file starting from the current file pointer. ### readBytes **Signature:** `readBytes(numBytes : Number) : Bytes` Reads up to n bytes from the file starting at the current file pointer. ### setPosition **Signature:** `setPosition(position : Number) : void` Sets the file-pointer offset, measured from the beginning of this file, at which the next read occurs. ## Constructor Detail ## Method Detail ## Method Details ### close **Signature:** `close() : void` **Description:** Closes this random access file reader and releases any system resources associated with the stream. **Throws:** IOException - if an I/O error occurs. --- ### getPosition **Signature:** `getPosition() : Number` **Description:** Returns the current offset in this file. **Returns:** the offset from the beginning of the file, in bytes, at which the next read occurs. **Throws:** IOException - if an I/O error occurs. --- ### length **Signature:** `length() : Number` **Description:** Returns the length of this file. **Returns:** the length of this file, measured in bytes. **Throws:** IOException - if an I/O error occurs. --- ### readByte **Signature:** `readByte() : Number` **Description:** Reads a signed eight-bit value from the file starting from the current file pointer. Since the byte is interpreted as signed, the value returned will always be between -128 and +127. **Returns:** the next byte of this file as a signed eight-bit byte. **Throws:** IOException - if an I/O error occurs or if this file has reached the end. --- ### readBytes **Signature:** `readBytes(numBytes : Number) : Bytes` **Description:** Reads up to n bytes from the file starting at the current file pointer. If there are fewer than n bytes remaining in the file, then as many bytes as possible are read. If no bytes remain in the file, then null is returned. **Parameters:** - `numBytes`: The number of bytes to read. Must be non-negative and smaller than MAX_READ_BYTES or an exception will be thrown. **Returns:** A Bytes object representing the read bytes or null if no bytes were read. **Throws:** IOException - if an I/O error occurs. IllegalArgumentException - if numBytes< 0 or numBytes > MAX_READ_BYTES. --- ### setPosition **Signature:** `setPosition(position : Number) : void` **Description:** Sets the file-pointer offset, measured from the beginning of this file, at which the next read occurs. The offset may be set beyond the end of the file. **Parameters:** - `position`: the offset position, measured in bytes from the beginning of the file, at which to set the file pointer **Throws:** IOException - if position is less than 0 or if an I/O error occurs. --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/OrderHistory.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.customer # Class OrderHistory ## Inheritance Hierarchy - Object - dw.customer.OrderHistory ## Description The class provides access to past orders of the customer. Note: This class allows access to sensitive financial and cardholder data. Pay special attention to PCI DSS v3. requirements 1, 3, 7, and 9. It also allows access to sensitive personal and private information. Pay attention to appropriate legal and regulatory requirements related to this data. Note: The following methods do not work with Salesforce Order Management orders. ## Properties ### orderCount **Type:** Number (Read Only) The number of orders the customer has placed in the store. If the customer is anonymous, this method always returns zero. If an active data record is available for this customer, the orders count is retrieved from that record, otherwise a real-time query is used to get the count. ### orders **Type:** SeekableIterator (Read Only) Retrieves the order history for the customer in the current storefront site. If the result exceeds 1000 orders, only the first 1000 orders are retrieved. Same as orderHistory.getOrders( null, "creationDate DESC" ) It is strongly recommended to call SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. ## Constructor Summary ## Method Summary ### getOrderCount **Signature:** `getOrderCount() : Number` Returns the number of orders the customer has placed in the store. ### getOrders **Signature:** `getOrders() : SeekableIterator` Retrieves the order history for the customer in the current storefront site. ### getOrders **Signature:** `getOrders(query : String, sortString : String, params : Object...) : SeekableIterator` Retrieves the order history for the customer in the current storefront site. ## Method Detail ## Method Details ### getOrderCount **Signature:** `getOrderCount() : Number` **Description:** Returns the number of orders the customer has placed in the store. If the customer is anonymous, this method always returns zero. If an active data record is available for this customer, the orders count is retrieved from that record, otherwise a real-time query is used to get the count. **Returns:** the number of orders the customer has placed in the store. --- ### getOrders **Signature:** `getOrders() : SeekableIterator` **Description:** Retrieves the order history for the customer in the current storefront site. If the result exceeds 1000 orders, only the first 1000 orders are retrieved. Same as orderHistory.getOrders( null, "creationDate DESC" ) It is strongly recommended to call SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. **Returns:** the orders **See Also:** getOrders(String, String, Object...) --- ### getOrders **Signature:** `getOrders(query : String, sortString : String, params : Object...) : SeekableIterator` **Description:** Retrieves the order history for the customer in the current storefront site. If the result exceeds 1000 orders, only the first 1000 orders are retrieved. Optionally, you can retrieve a subset of the orders by specifying a query. At maximum 3 expressions are allowed to be specified and no custom attribute expressions are allowed. It is strongly recommended to call SeekableIterator.close() on the returned SeekableIterator if not all of its elements are being retrieved. This will ensure the proper cleanup of system resources. Example: var orderHistory : dw.customer.OrderHistory = customer.getOrderHistory(); var orders = orderHistory.getOrders("status = {0}", "creationDate DESC", dw.order.Order.ORDER_STATUS_NEW); for each (var order : dw.order.Order in orders) { // ... process orders } orders.close(); **Parameters:** - `query`: optional query - `sortString`: optional sort string - `params`: optional parameters for the query **Returns:** the orders --- ``` -------------------------------------------------------------------------------- /docs/dw_catalog/ProductPriceInfo.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.catalog # Class ProductPriceInfo ## Inheritance Hierarchy - Object - dw.catalog.ProductPriceInfo ## Description Simple class representing a product price point. This class is useful because it provides additional information beyond just the price. Since the system calculates sales prices based on applicable price books, it is sometimes useful to know additional information such as which price book defined a price point, what percentage discount off the base price this value represents, and the date range for which this price point is active. ## Properties ### onlineFrom **Type:** Date (Read Only) The date from which the associated price point is valid. If such a date doesn't exist, e.g. as in the case of a continuous price point, null will be returned. ### onlineTo **Type:** Date (Read Only) The date until which the associated price point is valid. If such a date doesn't exist, e.g. as in the case of a continuous price point, null will be returned. ### percentage **Type:** Number (Read Only) The percentage off value of this price point related to the base price for the product's minimum order quantity. ### price **Type:** Money (Read Only) The monetary price for this price point. ### priceBook **Type:** PriceBook (Read Only) The price book which defined this price point. ### priceInfo **Type:** String (Read Only) The price info associated with this price point. This is an arbitrary string which a merchant can associate with a price entry. This can be used for example, to track which back-end system the price is derived from. ## Constructor Summary ## Method Summary ### getOnlineFrom **Signature:** `getOnlineFrom() : Date` Returns the date from which the associated price point is valid. ### getOnlineTo **Signature:** `getOnlineTo() : Date` Returns the date until which the associated price point is valid. ### getPercentage **Signature:** `getPercentage() : Number` Returns the percentage off value of this price point related to the base price for the product's minimum order quantity. ### getPrice **Signature:** `getPrice() : Money` Returns the monetary price for this price point. ### getPriceBook **Signature:** `getPriceBook() : PriceBook` Returns the price book which defined this price point. ### getPriceInfo **Signature:** `getPriceInfo() : String` Returns the price info associated with this price point. ## Method Detail ## Method Details ### getOnlineFrom **Signature:** `getOnlineFrom() : Date` **Description:** Returns the date from which the associated price point is valid. If such a date doesn't exist, e.g. as in the case of a continuous price point, null will be returned. **Returns:** the date from which the associated price point is valid --- ### getOnlineTo **Signature:** `getOnlineTo() : Date` **Description:** Returns the date until which the associated price point is valid. If such a date doesn't exist, e.g. as in the case of a continuous price point, null will be returned. **Returns:** the date to which the associated price point is valid --- ### getPercentage **Signature:** `getPercentage() : Number` **Description:** Returns the percentage off value of this price point related to the base price for the product's minimum order quantity. **Returns:** the percentage off value of this price point --- ### getPrice **Signature:** `getPrice() : Money` **Description:** Returns the monetary price for this price point. **Returns:** the price amount --- ### getPriceBook **Signature:** `getPriceBook() : PriceBook` **Description:** Returns the price book which defined this price point. **Returns:** the price book defining this price --- ### getPriceInfo **Signature:** `getPriceInfo() : String` **Description:** Returns the price info associated with this price point. This is an arbitrary string which a merchant can associate with a price entry. This can be used for example, to track which back-end system the price is derived from. **Returns:** the price info associated with this price point. --- ``` -------------------------------------------------------------------------------- /docs/sfra/categories.md: -------------------------------------------------------------------------------- ```markdown # SFRA Categories Model ## Overview The Categories model represents a hierarchical structure of categories for navigation menus and category displays in SFRA applications. It provides formatted category information with URLs, subcategories, and menu display logic. ## Constructor ```javascript function categories(items) ``` Creates a Categories model instance from a collection of top-level categories. ### Parameters - `items` (dw.util.ArrayList<dw.catalog.Category>) - Top level categories collection ## Properties ### categories **Type:** Array<Object> Array of formatted category objects that should be shown in menus. Each category contains: - `name` (string) - Category display name - `url` (string) - Category URL (custom alternative URL or search URL) - `id` (string) - Category ID - `subCategories` (Array<Object>) - Array of subcategory objects (if any) - `complexSubCategories` (boolean) - True if any subcategories have their own subcategories ## Helper Functions ### getCategoryUrl(category) Generates the appropriate URL for a category, preferring custom alternative URLs. **Parameters:** - `category` (dw.catalog.Category) - Category object **Returns:** string - Category URL (custom alternative URL or search URL) ### categoryToObject(category) Converts a catalog category to a plain object with menu-appropriate formatting. **Parameters:** - `category` (dw.catalog.Category) - Single category to convert **Returns:** Object | null - Formatted category object or null if not shown in menu ## Category Structure Each category object in the categories array contains: ### Basic Properties - **name** - Category display name for menu labels - **url** - Properly formatted URL for category navigation - **id** - Category identifier for tracking and CSS classes ### Hierarchical Properties - **subCategories** - Nested array of subcategory objects (same structure) - **complexSubCategories** - Boolean flag indicating multi-level nesting ## Menu Display Logic Categories are included in the menu only if: 1. Category has `custom.showInMenu` set to true 2. Category has online products OR online subcategories 3. Category meets the same criteria recursively for subcategories ## URL Handling The model provides flexible URL generation: - **Custom URLs**: Uses `category.custom.alternativeUrl` if available - **Standard URLs**: Falls back to Search-Show with category ID parameter - **URL Cleaning**: Removes HTML entities (& becomes &) ## Usage Example ```javascript var CategoriesModel = require('*/cartridge/models/categories'); var CatalogMgr = require('dw/catalog/CatalogMgr'); // Get site catalog and root categories var siteCatalog = CatalogMgr.getSiteCatalog(); var topLevelCategories = siteCatalog.getRoot().getOnlineSubCategories(); var categoriesModel = new CategoriesModel(topLevelCategories); // Access menu categories categoriesModel.categories.forEach(function(category) { console.log(category.name + ' -> ' + category.url); if (category.subCategories) { category.subCategories.forEach(function(subcat) { console.log(' ' + subcat.name + ' -> ' + subcat.url); }); } if (category.complexSubCategories) { console.log('Category has multi-level navigation'); } }); ``` ## Recursive Structure The model handles deep category hierarchies: - Subcategories are processed recursively with the same logic - Each level maintains the same object structure - The `complexSubCategories` flag helps with menu rendering decisions ## Notes - Only processes categories marked for menu display (`showInMenu`) - Filters out empty categories with no products or subcategories - Maintains hierarchical relationships for multi-level navigation - Provides clean URLs with proper entity handling - Supports both custom and standard URL patterns - Optimized for navigation menu rendering ## Related Models - **Search Models** - Categories link to search results - **Product Models** - Categories contain products - **Content Models** - May reference category information ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/mock-data/ocapi/custom-object-attributes-customapi.json: -------------------------------------------------------------------------------- ```json { "_v": "23.2", "_type": "object_attribute_definition_search_result", "count": 4, "hits": [ { "_type": "object_attribute_definition", "_resource_state": "99fb446a1b38712210cb5572b2a2c5acde8a414f3b70d3fbd887d5239c3902ec", "creation_date": "2025-03-06T07:14:22.000Z", "effective_id": "c_ID", "externally_defined": false, "externally_managed": false, "id": "ID", "key": true, "last_modified": "2025-03-06T07:14:22.000Z", "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/CustomApi/attribute_definitions/ID", "localizable": false, "mandatory": true, "min_length": 0, "multi_value_type": false, "order_required": false, "queryable": true, "read_only": false, "requires_encoding": false, "searchable": false, "set_value_type": false, "site_specific": false, "system": false, "value_type": "string", "visible": false }, { "_type": "object_attribute_definition", "_resource_state": "a2d9fadb3bca35c9dc4d71365ca24ed5e8e98514fa9fd7ece48c5a1214d7b8a4", "creation_date": "2025-03-06T07:14:22.000Z", "display_name": { "default": "UUID" }, "effective_id": "UUID", "externally_defined": false, "externally_managed": false, "field_length": 28, "id": "UUID", "key": false, "last_modified": "2025-03-06T07:14:22.000Z", "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/CustomApi/attribute_definitions/UUID", "localizable": false, "mandatory": true, "min_length": 0, "multi_value_type": false, "order_required": false, "queryable": true, "read_only": true, "requires_encoding": false, "searchable": false, "set_value_type": false, "site_specific": false, "system": true, "value_type": "string", "visible": false }, { "_type": "object_attribute_definition", "_resource_state": "e79f6e1fc72a3b162e732bf0b04798d623e4ca23a2ad86311a1b2fdf76d70f0b", "creation_date": "2025-03-06T07:14:22.000Z", "display_name": { "default": "Creation Date" }, "effective_id": "creationDate", "externally_defined": false, "externally_managed": false, "id": "creationDate", "key": false, "last_modified": "2025-03-06T07:14:22.000Z", "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/CustomApi/attribute_definitions/creationDate", "localizable": false, "mandatory": true, "min_length": 0, "multi_value_type": false, "order_required": false, "queryable": true, "read_only": true, "requires_encoding": false, "searchable": false, "set_value_type": false, "site_specific": false, "system": true, "value_type": "datetime", "visible": false }, { "_type": "object_attribute_definition", "_resource_state": "236466300ffa270a07b8fc8b1c8bc0eea8ddacfc0a7d29eeba5279c438b19655", "creation_date": "2025-03-06T07:14:22.000Z", "display_name": { "default": "Last Modified" }, "effective_id": "lastModified", "externally_defined": false, "externally_managed": false, "id": "lastModified", "key": false, "last_modified": "2025-03-06T07:14:22.000Z", "link": "https://localhost:3000/s/-/dw/data/v23_2/custom_object_definitions/CustomApi/attribute_definitions/lastModified", "localizable": false, "mandatory": true, "min_length": 0, "multi_value_type": false, "order_required": false, "queryable": true, "read_only": true, "requires_encoding": false, "searchable": false, "set_value_type": false, "site_specific": false, "system": true, "value_type": "datetime", "visible": false } ], "query": { "match_all_query": { "_type": "match_all_query" } }, "start": 0, "total": 4 } ``` -------------------------------------------------------------------------------- /docs/dw_net/HTTPRequestPart.md: -------------------------------------------------------------------------------- ```markdown ## Package: dw.net # Class HTTPRequestPart ## Inheritance Hierarchy - Object - dw.net.HTTPRequestPart ## Description This represents a part in a multi-part HTTP POST request. A part always has a name and value. The value may be a String, Bytes, or the contents of a File. A character encoding may be specified for any of these, and the content type and a file name may additionally be specified for the Bytes and File types. Note: when this class is used with sensitive data, be careful in persisting sensitive information. ## Properties ### bytesValue **Type:** Bytes (Read Only) Get the Bytes value of the part. ### contentType **Type:** String (Read Only) The content type of this part. ### encoding **Type:** String (Read Only) Get the charset to be used to encode the string. ### fileName **Type:** String (Read Only) Get the file name to use when sending a file part. ### fileValue **Type:** File (Read Only) Get the file value of the part. ### name **Type:** String (Read Only) Get the name of the part. ### stringValue **Type:** String (Read Only) Get the string value of the part. ## Constructor Summary HTTPRequestPart(name : String, value : String) Construct a part representing a simple string name/value pair. HTTPRequestPart(name : String, value : String, encoding : String) Construct a part representing a simple string name/value pair. HTTPRequestPart(name : String, file : File) Construct a part representing a name/File pair. HTTPRequestPart(name : String, data : Bytes) Construct a part representing a name/bytes pair. HTTPRequestPart(name : String, data : Bytes, contentType : String, encoding : String, fileName : String) Construct a part representing a name/File pair. HTTPRequestPart(name : String, file : File, contentType : String, encoding : String) Construct a part representing a name/File pair. HTTPRequestPart(name : String, file : File, contentType : String, encoding : String, fileName : String) Construct a part representing a name/File pair. ## Method Summary ### getBytesValue **Signature:** `getBytesValue() : Bytes` Get the Bytes value of the part. ### getContentType **Signature:** `getContentType() : String` Returns the content type of this part. ### getEncoding **Signature:** `getEncoding() : String` Get the charset to be used to encode the string. ### getFileName **Signature:** `getFileName() : String` Get the file name to use when sending a file part. ### getFileValue **Signature:** `getFileValue() : File` Get the file value of the part. ### getName **Signature:** `getName() : String` Get the name of the part. ### getStringValue **Signature:** `getStringValue() : String` Get the string value of the part. ## Constructor Detail ## Method Detail ## Method Details ### getBytesValue **Signature:** `getBytesValue() : Bytes` **Description:** Get the Bytes value of the part. **Returns:** The Bytes value, or null if this part is not a Bytes part. --- ### getContentType **Signature:** `getContentType() : String` **Description:** Returns the content type of this part. **Returns:** The content type, or null if content type was not specified. --- ### getEncoding **Signature:** `getEncoding() : String` **Description:** Get the charset to be used to encode the string. **Returns:** The charset, or null if charset was not specified. --- ### getFileName **Signature:** `getFileName() : String` **Description:** Get the file name to use when sending a file part. **Returns:** File name to use in the Mime header, or null for default behavior. --- ### getFileValue **Signature:** `getFileValue() : File` **Description:** Get the file value of the part. **Returns:** The file value, or null if this part is not a file part. --- ### getName **Signature:** `getName() : String` **Description:** Get the name of the part. **Returns:** The part name, never null. --- ### getStringValue **Signature:** `getStringValue() : String` **Description:** Get the string value of the part. **Returns:** The string value, or null if this part is not a string part. --- ```