This is page 6 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .DS_Store ├── .github │ ├── dependabot.yml │ ├── instructions │ │ ├── mcp-node-tests.instructions.md │ │ └── mcp-yml-tests.instructions.md │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── PULL_REQUEST_TEMPLATE │ │ ├── bug_fix.md │ │ ├── documentation.md │ │ └── new_tool.md │ ├── pull_request_template.md │ └── workflows │ ├── ci.yml │ ├── deploy-pages.yml │ ├── publish.yml │ └── update-docs.yml ├── .gitignore ├── .husky │ └── pre-commit ├── aegis.config.docs-only.json ├── aegis.config.json ├── aegis.config.with-dw.json ├── AGENTS.md ├── ai-instructions │ ├── claude-desktop │ │ └── claude_custom_instructions.md │ ├── cursor │ │ └── .cursor │ │ └── rules │ │ ├── debugging-workflows.mdc │ │ ├── hooks-development.mdc │ │ ├── isml-templates.mdc │ │ ├── job-framework.mdc │ │ ├── performance-optimization.mdc │ │ ├── scapi-endpoints.mdc │ │ ├── security-patterns.mdc │ │ ├── sfcc-development.mdc │ │ ├── sfra-controllers.mdc │ │ ├── sfra-models.mdc │ │ ├── system-objects.mdc │ │ └── testing-patterns.mdc │ └── github-copilot │ └── copilot-instructions.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── docs │ ├── best-practices │ │ ├── cartridge_creation.md │ │ ├── isml_templates.md │ │ ├── job_framework.md │ │ ├── localserviceregistry.md │ │ ├── ocapi_hooks.md │ │ ├── performance.md │ │ ├── scapi_custom_endpoint.md │ │ ├── scapi_hooks.md │ │ ├── security.md │ │ ├── sfra_client_side_js.md │ │ ├── sfra_controllers.md │ │ ├── sfra_models.md │ │ └── sfra_scss.md │ ├── dw_campaign │ │ ├── ABTest.md │ │ ├── ABTestMgr.md │ │ ├── ABTestSegment.md │ │ ├── AmountDiscount.md │ │ ├── ApproachingDiscount.md │ │ ├── BonusChoiceDiscount.md │ │ ├── BonusDiscount.md │ │ ├── Campaign.md │ │ ├── CampaignMgr.md │ │ ├── CampaignStatusCodes.md │ │ ├── Coupon.md │ │ ├── CouponMgr.md │ │ ├── CouponRedemption.md │ │ ├── CouponStatusCodes.md │ │ ├── Discount.md │ │ ├── DiscountPlan.md │ │ ├── FixedPriceDiscount.md │ │ ├── FixedPriceShippingDiscount.md │ │ ├── FreeDiscount.md │ │ ├── FreeShippingDiscount.md │ │ ├── PercentageDiscount.md │ │ ├── PercentageOptionDiscount.md │ │ ├── PriceBookPriceDiscount.md │ │ ├── Promotion.md │ │ ├── PromotionMgr.md │ │ ├── PromotionPlan.md │ │ ├── SlotContent.md │ │ ├── SourceCodeGroup.md │ │ ├── SourceCodeInfo.md │ │ ├── SourceCodeStatusCodes.md │ │ └── TotalFixedPriceDiscount.md │ ├── dw_catalog │ │ ├── Catalog.md │ │ ├── CatalogMgr.md │ │ ├── Category.md │ │ ├── CategoryAssignment.md │ │ ├── CategoryLink.md │ │ ├── PriceBook.md │ │ ├── PriceBookMgr.md │ │ ├── Product.md │ │ ├── ProductActiveData.md │ │ ├── ProductAttributeModel.md │ │ ├── ProductAvailabilityLevels.md │ │ ├── ProductAvailabilityModel.md │ │ ├── ProductInventoryList.md │ │ ├── ProductInventoryMgr.md │ │ ├── ProductInventoryRecord.md │ │ ├── ProductLink.md │ │ ├── ProductMgr.md │ │ ├── ProductOption.md │ │ ├── ProductOptionModel.md │ │ ├── ProductOptionValue.md │ │ ├── ProductPriceInfo.md │ │ ├── ProductPriceModel.md │ │ ├── ProductPriceTable.md │ │ ├── ProductSearchHit.md │ │ ├── ProductSearchModel.md │ │ ├── ProductSearchRefinementDefinition.md │ │ ├── ProductSearchRefinements.md │ │ ├── ProductSearchRefinementValue.md │ │ ├── ProductVariationAttribute.md │ │ ├── ProductVariationAttributeValue.md │ │ ├── ProductVariationModel.md │ │ ├── Recommendation.md │ │ ├── SearchModel.md │ │ ├── SearchRefinementDefinition.md │ │ ├── SearchRefinements.md │ │ ├── SearchRefinementValue.md │ │ ├── SortingOption.md │ │ ├── SortingRule.md │ │ ├── Store.md │ │ ├── StoreGroup.md │ │ ├── StoreInventoryFilter.md │ │ ├── StoreInventoryFilterValue.md │ │ ├── StoreMgr.md │ │ ├── Variant.md │ │ └── VariationGroup.md │ ├── dw_content │ │ ├── Content.md │ │ ├── ContentMgr.md │ │ ├── ContentSearchModel.md │ │ ├── ContentSearchRefinementDefinition.md │ │ ├── ContentSearchRefinements.md │ │ ├── ContentSearchRefinementValue.md │ │ ├── Folder.md │ │ ├── Library.md │ │ ├── MarkupText.md │ │ └── MediaFile.md │ ├── dw_crypto │ │ ├── CertificateRef.md │ │ ├── CertificateUtils.md │ │ ├── Cipher.md │ │ ├── Encoding.md │ │ ├── JWE.md │ │ ├── JWEHeader.md │ │ ├── JWS.md │ │ ├── JWSHeader.md │ │ ├── KeyRef.md │ │ ├── Mac.md │ │ ├── MessageDigest.md │ │ ├── SecureRandom.md │ │ ├── Signature.md │ │ ├── WeakCipher.md │ │ ├── WeakMac.md │ │ ├── WeakMessageDigest.md │ │ ├── WeakSignature.md │ │ └── X509Certificate.md │ ├── dw_customer │ │ ├── AddressBook.md │ │ ├── AgentUserMgr.md │ │ ├── AgentUserStatusCodes.md │ │ ├── AuthenticationStatus.md │ │ ├── Credentials.md │ │ ├── Customer.md │ │ ├── CustomerActiveData.md │ │ ├── CustomerAddress.md │ │ ├── CustomerCDPData.md │ │ ├── CustomerContextMgr.md │ │ ├── CustomerGroup.md │ │ ├── CustomerList.md │ │ ├── CustomerMgr.md │ │ ├── CustomerPasswordConstraints.md │ │ ├── CustomerPaymentInstrument.md │ │ ├── CustomerStatusCodes.md │ │ ├── EncryptedObject.md │ │ ├── ExternalProfile.md │ │ ├── OrderHistory.md │ │ ├── ProductList.md │ │ ├── ProductListItem.md │ │ ├── ProductListItemPurchase.md │ │ ├── ProductListMgr.md │ │ ├── ProductListRegistrant.md │ │ ├── Profile.md │ │ └── Wallet.md │ ├── dw_extensions.applepay │ │ ├── ApplePayHookResult.md │ │ └── ApplePayHooks.md │ ├── dw_extensions.facebook │ │ ├── FacebookFeedHooks.md │ │ └── FacebookProduct.md │ ├── dw_extensions.paymentrequest │ │ ├── PaymentRequestHookResult.md │ │ └── PaymentRequestHooks.md │ ├── dw_extensions.payments │ │ ├── SalesforceBancontactPaymentDetails.md │ │ ├── SalesforceCardPaymentDetails.md │ │ ├── SalesforceEpsPaymentDetails.md │ │ ├── SalesforceIdealPaymentDetails.md │ │ ├── SalesforceKlarnaPaymentDetails.md │ │ ├── SalesforcePaymentDetails.md │ │ ├── SalesforcePaymentIntent.md │ │ ├── SalesforcePaymentMethod.md │ │ ├── SalesforcePaymentRequest.md │ │ ├── SalesforcePaymentsHooks.md │ │ ├── SalesforcePaymentsMgr.md │ │ ├── SalesforcePaymentsSiteConfiguration.md │ │ ├── SalesforcePayPalOrder.md │ │ ├── SalesforcePayPalOrderAddress.md │ │ ├── SalesforcePayPalOrderPayer.md │ │ ├── SalesforcePayPalPaymentDetails.md │ │ ├── SalesforceSepaDebitPaymentDetails.md │ │ └── SalesforceVenmoPaymentDetails.md │ ├── dw_extensions.pinterest │ │ ├── PinterestAvailability.md │ │ ├── PinterestFeedHooks.md │ │ ├── PinterestOrder.md │ │ ├── PinterestOrderHooks.md │ │ └── PinterestProduct.md │ ├── dw_io │ │ ├── CSVStreamReader.md │ │ ├── CSVStreamWriter.md │ │ ├── File.md │ │ ├── FileReader.md │ │ ├── FileWriter.md │ │ ├── InputStream.md │ │ ├── OutputStream.md │ │ ├── PrintWriter.md │ │ ├── RandomAccessFileReader.md │ │ ├── Reader.md │ │ ├── StringWriter.md │ │ ├── Writer.md │ │ ├── XMLIndentingStreamWriter.md │ │ ├── XMLStreamConstants.md │ │ ├── XMLStreamReader.md │ │ └── XMLStreamWriter.md │ ├── dw_job │ │ ├── JobExecution.md │ │ └── JobStepExecution.md │ ├── dw_net │ │ ├── FTPClient.md │ │ ├── FTPFileInfo.md │ │ ├── HTTPClient.md │ │ ├── HTTPRequestPart.md │ │ ├── Mail.md │ │ ├── SFTPClient.md │ │ ├── SFTPFileInfo.md │ │ ├── WebDAVClient.md │ │ └── WebDAVFileInfo.md │ ├── dw_object │ │ ├── ActiveData.md │ │ ├── CustomAttributes.md │ │ ├── CustomObject.md │ │ ├── CustomObjectMgr.md │ │ ├── Extensible.md │ │ ├── ExtensibleObject.md │ │ ├── Note.md │ │ ├── ObjectAttributeDefinition.md │ │ ├── ObjectAttributeGroup.md │ │ ├── ObjectAttributeValueDefinition.md │ │ ├── ObjectTypeDefinition.md │ │ ├── PersistentObject.md │ │ ├── SimpleExtensible.md │ │ └── SystemObjectMgr.md │ ├── dw_order │ │ ├── AbstractItem.md │ │ ├── AbstractItemCtnr.md │ │ ├── Appeasement.md │ │ ├── AppeasementItem.md │ │ ├── Basket.md │ │ ├── BasketMgr.md │ │ ├── BonusDiscountLineItem.md │ │ ├── CouponLineItem.md │ │ ├── CreateAgentBasketLimitExceededException.md │ │ ├── CreateBasketFromOrderException.md │ │ ├── CreateCouponLineItemException.md │ │ ├── CreateOrderException.md │ │ ├── CreateTemporaryBasketLimitExceededException.md │ │ ├── GiftCertificate.md │ │ ├── GiftCertificateLineItem.md │ │ ├── GiftCertificateMgr.md │ │ ├── GiftCertificateStatusCodes.md │ │ ├── Invoice.md │ │ ├── InvoiceItem.md │ │ ├── LineItem.md │ │ ├── LineItemCtnr.md │ │ ├── Order.md │ │ ├── OrderAddress.md │ │ ├── OrderItem.md │ │ ├── OrderMgr.md │ │ ├── OrderPaymentInstrument.md │ │ ├── OrderProcessStatusCodes.md │ │ ├── PaymentCard.md │ │ ├── PaymentInstrument.md │ │ ├── PaymentMethod.md │ │ ├── PaymentMgr.md │ │ ├── PaymentProcessor.md │ │ ├── PaymentStatusCodes.md │ │ ├── PaymentTransaction.md │ │ ├── PriceAdjustment.md │ │ ├── PriceAdjustmentLimitTypes.md │ │ ├── ProductLineItem.md │ │ ├── ProductShippingCost.md │ │ ├── ProductShippingLineItem.md │ │ ├── ProductShippingModel.md │ │ ├── Return.md │ │ ├── ReturnCase.md │ │ ├── ReturnCaseItem.md │ │ ├── ReturnItem.md │ │ ├── Shipment.md │ │ ├── ShipmentShippingCost.md │ │ ├── ShipmentShippingModel.md │ │ ├── ShippingLineItem.md │ │ ├── ShippingLocation.md │ │ ├── ShippingMethod.md │ │ ├── ShippingMgr.md │ │ ├── ShippingOrder.md │ │ ├── ShippingOrderItem.md │ │ ├── SumItem.md │ │ ├── TaxGroup.md │ │ ├── TaxItem.md │ │ ├── TaxMgr.md │ │ ├── TrackingInfo.md │ │ └── TrackingRef.md │ ├── dw_order.hooks │ │ ├── CalculateHooks.md │ │ ├── OrderHooks.md │ │ ├── PaymentHooks.md │ │ ├── ReturnHooks.md │ │ └── ShippingOrderHooks.md │ ├── dw_rpc │ │ ├── SOAPUtil.md │ │ ├── Stub.md │ │ └── WebReference.md │ ├── dw_suggest │ │ ├── BrandSuggestions.md │ │ ├── CategorySuggestions.md │ │ ├── ContentSuggestions.md │ │ ├── CustomSuggestions.md │ │ ├── ProductSuggestions.md │ │ ├── SearchPhraseSuggestions.md │ │ ├── SuggestedCategory.md │ │ ├── SuggestedContent.md │ │ ├── SuggestedPhrase.md │ │ ├── SuggestedProduct.md │ │ ├── SuggestedTerm.md │ │ ├── SuggestedTerms.md │ │ ├── Suggestions.md │ │ └── SuggestModel.md │ ├── dw_svc │ │ ├── FTPService.md │ │ ├── FTPServiceDefinition.md │ │ ├── HTTPFormService.md │ │ ├── HTTPFormServiceDefinition.md │ │ ├── HTTPService.md │ │ ├── HTTPServiceDefinition.md │ │ ├── LocalServiceRegistry.md │ │ ├── Result.md │ │ ├── Service.md │ │ ├── ServiceCallback.md │ │ ├── ServiceConfig.md │ │ ├── ServiceCredential.md │ │ ├── ServiceDefinition.md │ │ ├── ServiceProfile.md │ │ ├── ServiceRegistry.md │ │ ├── SOAPService.md │ │ └── SOAPServiceDefinition.md │ ├── dw_system │ │ ├── AgentUserStatusCodes.md │ │ ├── Cache.md │ │ ├── CacheMgr.md │ │ ├── HookMgr.md │ │ ├── InternalObject.md │ │ ├── JobProcessMonitor.md │ │ ├── Log.md │ │ ├── Logger.md │ │ ├── LogNDC.md │ │ ├── OrganizationPreferences.md │ │ ├── Pipeline.md │ │ ├── PipelineDictionary.md │ │ ├── RemoteInclude.md │ │ ├── Request.md │ │ ├── RequestHooks.md │ │ ├── Response.md │ │ ├── RESTErrorResponse.md │ │ ├── RESTResponseMgr.md │ │ ├── RESTSuccessResponse.md │ │ ├── SearchStatus.md │ │ ├── Session.md │ │ ├── Site.md │ │ ├── SitePreferences.md │ │ ├── Status.md │ │ ├── StatusItem.md │ │ ├── System.md │ │ └── Transaction.md │ ├── dw_util │ │ ├── ArrayList.md │ │ ├── Assert.md │ │ ├── BigInteger.md │ │ ├── Bytes.md │ │ ├── Calendar.md │ │ ├── Collection.md │ │ ├── Currency.md │ │ ├── DateUtils.md │ │ ├── Decimal.md │ │ ├── FilteringCollection.md │ │ ├── Geolocation.md │ │ ├── HashMap.md │ │ ├── HashSet.md │ │ ├── Iterator.md │ │ ├── LinkedHashMap.md │ │ ├── LinkedHashSet.md │ │ ├── List.md │ │ ├── Locale.md │ │ ├── Map.md │ │ ├── MapEntry.md │ │ ├── MappingKey.md │ │ ├── MappingMgr.md │ │ ├── PropertyComparator.md │ │ ├── SecureEncoder.md │ │ ├── SecureFilter.md │ │ ├── SeekableIterator.md │ │ ├── Set.md │ │ ├── SortedMap.md │ │ ├── SortedSet.md │ │ ├── StringUtils.md │ │ ├── Template.md │ │ └── UUIDUtils.md │ ├── dw_value │ │ ├── EnumValue.md │ │ ├── MimeEncodedText.md │ │ ├── Money.md │ │ └── Quantity.md │ ├── dw_web │ │ ├── ClickStream.md │ │ ├── ClickStreamEntry.md │ │ ├── Cookie.md │ │ ├── Cookies.md │ │ ├── CSRFProtection.md │ │ ├── Form.md │ │ ├── FormAction.md │ │ ├── FormElement.md │ │ ├── FormElementValidationResult.md │ │ ├── FormField.md │ │ ├── FormFieldOption.md │ │ ├── FormFieldOptions.md │ │ ├── FormGroup.md │ │ ├── FormList.md │ │ ├── FormListItem.md │ │ ├── Forms.md │ │ ├── HttpParameter.md │ │ ├── HttpParameterMap.md │ │ ├── LoopIterator.md │ │ ├── PageMetaData.md │ │ ├── PageMetaTag.md │ │ ├── PagingModel.md │ │ ├── Resource.md │ │ ├── URL.md │ │ ├── URLAction.md │ │ ├── URLParameter.md │ │ ├── URLRedirect.md │ │ ├── URLRedirectMgr.md │ │ └── URLUtils.md │ ├── sfra │ │ ├── account.md │ │ ├── address.md │ │ ├── billing.md │ │ ├── cart.md │ │ ├── categories.md │ │ ├── content.md │ │ ├── locale.md │ │ ├── order.md │ │ ├── payment.md │ │ ├── price-default.md │ │ ├── price-range.md │ │ ├── price-tiered.md │ │ ├── product-bundle.md │ │ ├── product-full.md │ │ ├── product-line-items.md │ │ ├── product-search.md │ │ ├── product-tile.md │ │ ├── querystring.md │ │ ├── render.md │ │ ├── request.md │ │ ├── response.md │ │ ├── server.md │ │ ├── shipping.md │ │ ├── store.md │ │ ├── stores.md │ │ └── totals.md │ └── TopLevel │ ├── APIException.md │ ├── arguments.md │ ├── Array.md │ ├── ArrayBuffer.md │ ├── BigInt.md │ ├── Boolean.md │ ├── ConversionError.md │ ├── DataView.md │ ├── Date.md │ ├── Error.md │ ├── ES6Iterator.md │ ├── EvalError.md │ ├── Fault.md │ ├── Float32Array.md │ ├── Float64Array.md │ ├── Function.md │ ├── Generator.md │ ├── global.md │ ├── Int16Array.md │ ├── Int32Array.md │ ├── Int8Array.md │ ├── InternalError.md │ ├── IOError.md │ ├── Iterable.md │ ├── Iterator.md │ ├── JSON.md │ ├── Map.md │ ├── Math.md │ ├── Module.md │ ├── Namespace.md │ ├── Number.md │ ├── Object.md │ ├── QName.md │ ├── RangeError.md │ ├── ReferenceError.md │ ├── RegExp.md │ ├── Set.md │ ├── StopIteration.md │ ├── String.md │ ├── Symbol.md │ ├── SyntaxError.md │ ├── SystemError.md │ ├── TypeError.md │ ├── Uint16Array.md │ ├── Uint32Array.md │ ├── Uint8Array.md │ ├── Uint8ClampedArray.md │ ├── URIError.md │ ├── WeakMap.md │ ├── WeakSet.md │ ├── XML.md │ ├── XMLList.md │ └── XMLStreamError.md ├── docs-site │ ├── .gitignore │ ├── App.tsx │ ├── components │ │ ├── Badge.tsx │ │ ├── BreadcrumbSchema.tsx │ │ ├── CodeBlock.tsx │ │ ├── Collapsible.tsx │ │ ├── ConfigBuilder.tsx │ │ ├── ConfigHero.tsx │ │ ├── ConfigModeTabs.tsx │ │ ├── icons.tsx │ │ ├── Layout.tsx │ │ ├── LightCodeContainer.tsx │ │ ├── NewcomerCTA.tsx │ │ ├── NextStepsStrip.tsx │ │ ├── OnThisPage.tsx │ │ ├── Search.tsx │ │ ├── SEO.tsx │ │ ├── Sidebar.tsx │ │ ├── StructuredData.tsx │ │ ├── ToolCard.tsx │ │ ├── ToolFilters.tsx │ │ ├── Typography.tsx │ │ └── VersionBadge.tsx │ ├── constants.tsx │ ├── index.html │ ├── main.tsx │ ├── metadata.json │ ├── package-lock.json │ ├── package.json │ ├── pages │ │ ├── AIInterfacesPage.tsx │ │ ├── ConfigurationPage.tsx │ │ ├── DevelopmentPage.tsx │ │ ├── ExamplesPage.tsx │ │ ├── FeaturesPage.tsx │ │ ├── HomePage.tsx │ │ ├── SecurityPage.tsx │ │ ├── ToolsPage.tsx │ │ └── TroubleshootingPage.tsx │ ├── postcss.config.js │ ├── public │ │ ├── .well-known │ │ │ └── security.txt │ │ ├── 404.html │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── explain-product-pricing-methods-no-mcp.png │ │ ├── explain-product-pricing-methods.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── llms.txt │ │ ├── robots.txt │ │ ├── site.webmanifest │ │ └── sitemap.xml │ ├── README.md │ ├── scripts │ │ ├── generate-search-index.js │ │ ├── generate-sitemap.js │ │ └── search-dev.js │ ├── src │ │ └── styles │ │ ├── input.css │ │ └── prism-theme.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── types.ts │ ├── utils │ │ ├── search.ts │ │ └── toolsData.ts │ └── vite.config.ts ├── eslint.config.js ├── jest.config.js ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── scripts │ └── convert-docs.js ├── SECURITY.md ├── server.json ├── src │ ├── clients │ │ ├── base │ │ │ ├── http-client.ts │ │ │ ├── oauth-token.ts │ │ │ └── ocapi-auth-client.ts │ │ ├── best-practices-client.ts │ │ ├── cartridge-generation-client.ts │ │ ├── docs │ │ │ ├── class-content-parser.ts │ │ │ ├── class-name-resolver.ts │ │ │ ├── documentation-scanner.ts │ │ │ ├── index.ts │ │ │ └── referenced-types-extractor.ts │ │ ├── docs-client.ts │ │ ├── log-client.ts │ │ ├── logs │ │ │ ├── index.ts │ │ │ ├── log-analyzer.ts │ │ │ ├── log-client.ts │ │ │ ├── log-constants.ts │ │ │ ├── log-file-discovery.ts │ │ │ ├── log-file-reader.ts │ │ │ ├── log-formatter.ts │ │ │ ├── log-processor.ts │ │ │ ├── log-types.ts │ │ │ └── webdav-client-manager.ts │ │ ├── ocapi │ │ │ ├── code-versions-client.ts │ │ │ ├── site-preferences-client.ts │ │ │ └── system-objects-client.ts │ │ ├── ocapi-client.ts │ │ └── sfra-client.ts │ ├── config │ │ ├── configuration-factory.ts │ │ └── dw-json-loader.ts │ ├── core │ │ ├── handlers │ │ │ ├── abstract-log-tool-handler.ts │ │ │ ├── base-handler.ts │ │ │ ├── best-practices-handler.ts │ │ │ ├── cartridge-handler.ts │ │ │ ├── client-factory.ts │ │ │ ├── code-version-handler.ts │ │ │ ├── docs-handler.ts │ │ │ ├── job-log-handler.ts │ │ │ ├── job-log-tool-config.ts │ │ │ ├── log-handler.ts │ │ │ ├── log-tool-config.ts │ │ │ ├── sfra-handler.ts │ │ │ ├── system-object-handler.ts │ │ │ └── validation-helpers.ts │ │ ├── server.ts │ │ └── tool-definitions.ts │ ├── index.ts │ ├── main.ts │ ├── services │ │ ├── file-system-service.ts │ │ ├── index.ts │ │ └── path-service.ts │ ├── tool-configs │ │ ├── best-practices-tool-config.ts │ │ ├── cartridge-tool-config.ts │ │ ├── code-version-tool-config.ts │ │ ├── docs-tool-config.ts │ │ ├── job-log-tool-config.ts │ │ ├── log-tool-config.ts │ │ ├── sfra-tool-config.ts │ │ └── system-object-tool-config.ts │ ├── types │ │ └── types.ts │ └── utils │ ├── cache.ts │ ├── job-log-tool-config.ts │ ├── job-log-utils.ts │ ├── log-cache.ts │ ├── log-tool-config.ts │ ├── log-tool-constants.ts │ ├── log-tool-utils.ts │ ├── logger.ts │ ├── ocapi-url-builder.ts │ ├── path-resolver.ts │ ├── query-builder.ts │ ├── utils.ts │ └── validator.ts ├── tests │ ├── __mocks__ │ │ ├── docs-client.ts │ │ ├── src │ │ │ └── clients │ │ │ └── base │ │ │ └── http-client.js │ │ └── webdav.js │ ├── base-handler.test.ts │ ├── base-http-client.test.ts │ ├── best-practices-handler.test.ts │ ├── cache.test.ts │ ├── cartridge-handler.test.ts │ ├── class-content-parser.test.ts │ ├── class-name-resolver.test.ts │ ├── client-factory.test.ts │ ├── code-version-handler.test.ts │ ├── code-versions-client.test.ts │ ├── config.test.ts │ ├── configuration-factory.test.ts │ ├── docs-handler.test.ts │ ├── documentation-scanner.test.ts │ ├── file-system-service.test.ts │ ├── job-log-handler.test.ts │ ├── job-log-utils.test.ts │ ├── log-client.test.ts │ ├── log-handler.test.ts │ ├── log-processor.test.ts │ ├── logger.test.ts │ ├── mcp │ │ ├── AGENTS.md │ │ ├── node │ │ │ ├── activate-code-version-advanced.full-mode.programmatic.test.js │ │ │ ├── code-versions.full-mode.programmatic.test.js │ │ │ ├── generate-cartridge-structure.docs-only.programmatic.test.js │ │ │ ├── get-available-best-practice-guides.docs-only.programmatic.test.js │ │ │ ├── get-available-sfra-documents.programmatic.test.js │ │ │ ├── get-best-practice-guide.docs-only.programmatic.test.js │ │ │ ├── get-hook-reference.docs-only.programmatic.test.js │ │ │ ├── get-job-execution-summary.full-mode.programmatic.test.js │ │ │ ├── get-job-log-entries.full-mode.programmatic.test.js │ │ │ ├── get-latest-debug.full-mode.programmatic.test.js │ │ │ ├── get-latest-error.full-mode.programmatic.test.js │ │ │ ├── get-latest-info.full-mode.programmatic.test.js │ │ │ ├── get-latest-job-log-files.full-mode.programmatic.test.js │ │ │ ├── get-latest-warn.full-mode.programmatic.test.js │ │ │ ├── get-log-file-contents.full-mode.programmatic.test.js │ │ │ ├── get-sfcc-class-documentation.docs-only.programmatic.test.js │ │ │ ├── get-sfcc-class-info.docs-only.programmatic.test.js │ │ │ ├── get-sfra-categories.docs-only.programmatic.test.js │ │ │ ├── get-sfra-document.programmatic.test.js │ │ │ ├── get-sfra-documents-by-category.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definition.full-mode.programmatic.test.js │ │ │ ├── get-system-object-definitions.docs-only.programmatic.test.js │ │ │ ├── get-system-object-definitions.full-mode.programmatic.test.js │ │ │ ├── list-log-files.full-mode.programmatic.test.js │ │ │ ├── list-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-best-practices.docs-only.programmatic.test.js │ │ │ ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-job-logs-by-name.full-mode.programmatic.test.js │ │ │ ├── search-job-logs.full-mode.programmatic.test.js │ │ │ ├── search-logs.full-mode.programmatic.test.js │ │ │ ├── search-sfcc-classes.docs-only.programmatic.test.js │ │ │ ├── search-sfcc-methods.docs-only.programmatic.test.js │ │ │ ├── search-sfra-documentation.docs-only.programmatic.test.js │ │ │ ├── search-site-preferences.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js │ │ │ ├── search-system-object-attribute-groups.full-mode.programmatic.test.js │ │ │ ├── summarize-logs.full-mode.programmatic.test.js │ │ │ ├── tools.docs-only.programmatic.test.js │ │ │ └── tools.full-mode.programmatic.test.js │ │ ├── README.md │ │ ├── test-fixtures │ │ │ └── dw.json │ │ └── yaml │ │ ├── activate-code-version.docs-only.test.mcp.yml │ │ ├── activate-code-version.full-mode.test.mcp.yml │ │ ├── get_latest_error.test.mcp.yml │ │ ├── get-available-best-practice-guides.docs-only.test.mcp.yml │ │ ├── get-available-best-practice-guides.full-mode.test.mcp.yml │ │ ├── get-available-sfra-documents.docs-only.test.mcp.yml │ │ ├── get-available-sfra-documents.full-mode.test.mcp.yml │ │ ├── get-best-practice-guide.docs-only.test.mcp.yml │ │ ├── get-best-practice-guide.full-mode.test.mcp.yml │ │ ├── get-code-versions.docs-only.test.mcp.yml │ │ ├── get-code-versions.full-mode.test.mcp.yml │ │ ├── get-hook-reference.docs-only.test.mcp.yml │ │ ├── get-hook-reference.full-mode.test.mcp.yml │ │ ├── get-job-execution-summary.full-mode.test.mcp.yml │ │ ├── get-job-log-entries.full-mode.test.mcp.yml │ │ ├── get-latest-debug.full-mode.test.mcp.yml │ │ ├── get-latest-error.full-mode.test.mcp.yml │ │ ├── get-latest-info.full-mode.test.mcp.yml │ │ ├── get-latest-job-log-files.full-mode.test.mcp.yml │ │ ├── get-latest-warn.full-mode.test.mcp.yml │ │ ├── get-log-file-contents.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-documentation.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-documentation.full-mode.test.mcp.yml │ │ ├── get-sfcc-class-info.docs-only.test.mcp.yml │ │ ├── get-sfcc-class-info.full-mode.test.mcp.yml │ │ ├── get-sfra-categories.docs-only.test.mcp.yml │ │ ├── get-sfra-categories.full-mode.test.mcp.yml │ │ ├── get-sfra-document.docs-only.test.mcp.yml │ │ ├── get-sfra-document.full-mode.test.mcp.yml │ │ ├── get-sfra-documents-by-category.docs-only.test.mcp.yml │ │ ├── get-sfra-documents-by-category.full-mode.test.mcp.yml │ │ ├── get-system-object-definition.docs-only.test.mcp.yml │ │ ├── get-system-object-definition.full-mode.test.mcp.yml │ │ ├── get-system-object-definitions.docs-only.test.mcp.yml │ │ ├── get-system-object-definitions.full-mode.test.mcp.yml │ │ ├── list-log-files.full-mode.test.mcp.yml │ │ ├── list-sfcc-classes.docs-only.test.mcp.yml │ │ ├── list-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-best-practices.docs-only.test.mcp.yml │ │ ├── search-best-practices.full-mode.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-custom-object-attribute-definitions.test.mcp.yml │ │ ├── search-job-logs-by-name.full-mode.test.mcp.yml │ │ ├── search-job-logs.full-mode.test.mcp.yml │ │ ├── search-logs.full-mode.test.mcp.yml │ │ ├── search-sfcc-classes.docs-only.test.mcp.yml │ │ ├── search-sfcc-classes.full-mode.test.mcp.yml │ │ ├── search-sfcc-methods.docs-only.test.mcp.yml │ │ ├── search-sfcc-methods.full-mode.test.mcp.yml │ │ ├── search-sfra-documentation.docs-only.test.mcp.yml │ │ ├── search-sfra-documentation.full-mode.test.mcp.yml │ │ ├── search-site-preferences.docs-only.test.mcp.yml │ │ ├── search-site-preferences.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml │ │ ├── search-system-object-attribute-groups.docs-only.test.mcp.yml │ │ ├── search-system-object-attribute-groups.full-mode.test.mcp.yml │ │ ├── summarize-logs.full-mode.test.mcp.yml │ │ ├── tools.docs-only.test.mcp.yml │ │ └── tools.full-mode.test.mcp.yml │ ├── oauth-token.test.ts │ ├── ocapi-auth-client.test.ts │ ├── ocapi-client.test.ts │ ├── path-service.test.ts │ ├── query-builder.test.ts │ ├── referenced-types-extractor.test.ts │ ├── servers │ │ ├── sfcc-mock-server │ │ │ ├── mock-data │ │ │ │ └── ocapi │ │ │ │ ├── code-versions.json │ │ │ │ ├── custom-object-attributes-customapi.json │ │ │ │ ├── custom-object-attributes-globalsettings.json │ │ │ │ ├── custom-object-attributes-versionhistory.json │ │ │ │ ├── site-preferences-ccv.json │ │ │ │ ├── site-preferences-fastforward.json │ │ │ │ ├── site-preferences-sfra.json │ │ │ │ ├── site-preferences-storefront.json │ │ │ │ ├── site-preferences-system.json │ │ │ │ ├── system-object-attribute-groups-campaign.json │ │ │ │ ├── system-object-attribute-groups-category.json │ │ │ │ ├── system-object-attribute-groups-order.json │ │ │ │ ├── system-object-attribute-groups-product.json │ │ │ │ ├── system-object-attribute-groups-sitepreferences.json │ │ │ │ ├── system-object-attributes-customeraddress.json │ │ │ │ ├── system-object-attributes-product-expanded.json │ │ │ │ ├── system-object-attributes-product.json │ │ │ │ ├── system-object-definition-category.json │ │ │ │ ├── system-object-definition-customer.json │ │ │ │ ├── system-object-definition-customeraddress.json │ │ │ │ ├── system-object-definition-order.json │ │ │ │ ├── system-object-definition-product.json │ │ │ │ ├── system-object-definitions-old.json │ │ │ │ └── system-object-definitions.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── README.md │ │ │ ├── scripts │ │ │ │ └── setup-logs.js │ │ │ ├── server.js │ │ │ └── src │ │ │ ├── app.js │ │ │ ├── config │ │ │ │ └── server-config.js │ │ │ ├── middleware │ │ │ │ ├── auth.js │ │ │ │ ├── cors.js │ │ │ │ └── logging.js │ │ │ ├── routes │ │ │ │ ├── ocapi │ │ │ │ │ ├── code-versions-handler.js │ │ │ │ │ ├── oauth-handler.js │ │ │ │ │ ├── ocapi-error-utils.js │ │ │ │ │ ├── ocapi-utils.js │ │ │ │ │ ├── site-preferences-handler.js │ │ │ │ │ └── system-objects-handler.js │ │ │ │ ├── ocapi.js │ │ │ │ └── webdav.js │ │ │ └── utils │ │ │ ├── mock-data-loader.js │ │ │ └── webdav-xml.js │ │ └── sfcc-mock-server-manager.ts │ ├── sfcc-mock-server.test.ts │ ├── site-preferences-client.test.ts │ ├── system-objects-client.test.ts │ ├── utils.test.ts │ ├── validation-helpers.test.ts │ └── validator.test.ts ├── tsconfig.json └── tsconfig.test.json ``` # Files -------------------------------------------------------------------------------- /src/clients/base/oauth-token.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * OAuth Token Manager for SFCC OCAPI 3 | * 4 | * This module provides a singleton Token class that manages OAuth 2.0 access tokens 5 | * for SFCC OCAPI requests. It handles automatic token refresh when tokens expire. 6 | */ 7 | 8 | import { OAuthToken, OAuthTokenResponse } from '../../types/types.js'; 9 | 10 | /** 11 | * Singleton class for managing OAuth tokens 12 | * Handles token storage, expiration checking, and automatic refresh 13 | */ 14 | export class TokenManager { 15 | private static instance: TokenManager; 16 | private tokens: Map<string, OAuthToken> = new Map(); 17 | 18 | private constructor() {} 19 | 20 | /** 21 | * Get the singleton instance of TokenManager 22 | */ 23 | static getInstance(): TokenManager { 24 | if (!TokenManager.instance) { 25 | TokenManager.instance = new TokenManager(); 26 | } 27 | return TokenManager.instance; 28 | } 29 | 30 | /** 31 | * Generate a unique key for the token based on hostname and client ID 32 | */ 33 | private getTokenKey(hostname: string, clientId: string): string { 34 | return `${hostname}:${clientId}`; 35 | } 36 | 37 | /** 38 | * Check if a token is valid (exists and not expired) 39 | * Includes a 60-second buffer to avoid using tokens that are about to expire 40 | */ 41 | isTokenValid(hostname: string, clientId: string): boolean { 42 | const key = this.getTokenKey(hostname, clientId); 43 | const token = this.tokens.get(key); 44 | 45 | if (!token) { 46 | return false; 47 | } 48 | 49 | // Add 60-second buffer to avoid using tokens that are about to expire 50 | const now = Date.now(); 51 | const expirationBuffer = 60 * 1000; // 60 seconds in milliseconds 52 | 53 | return token.expiresAt > (now + expirationBuffer); 54 | } 55 | 56 | /** 57 | * Get a valid token for the given hostname and client ID 58 | * Returns null if no valid token exists 59 | */ 60 | getValidToken(hostname: string, clientId: string): string | null { 61 | if (!this.isTokenValid(hostname, clientId)) { 62 | return null; 63 | } 64 | 65 | const key = this.getTokenKey(hostname, clientId); 66 | const token = this.tokens.get(key); 67 | return token?.accessToken ?? null; 68 | } 69 | 70 | /** 71 | * Store a new token from the OAuth response 72 | */ 73 | storeToken(hostname: string, clientId: string, tokenResponse: OAuthTokenResponse): void { 74 | const key = this.getTokenKey(hostname, clientId); 75 | const now = Date.now(); 76 | 77 | const token: OAuthToken = { 78 | accessToken: tokenResponse.access_token, 79 | tokenType: tokenResponse.token_type, 80 | expiresAt: now + (tokenResponse.expires_in * 1000), // Convert seconds to milliseconds 81 | }; 82 | 83 | this.tokens.set(key, token); 84 | } 85 | 86 | /** 87 | * Clear a token (useful for testing or when a token becomes invalid) 88 | */ 89 | clearToken(hostname: string, clientId: string): void { 90 | const key = this.getTokenKey(hostname, clientId); 91 | this.tokens.delete(key); 92 | } 93 | 94 | /** 95 | * Clear all tokens 96 | */ 97 | clearAllTokens(): void { 98 | this.tokens.clear(); 99 | } 100 | 101 | /** 102 | * Get token expiration time for debugging purposes 103 | */ 104 | getTokenExpiration(hostname: string, clientId: string): Date | null { 105 | const key = this.getTokenKey(hostname, clientId); 106 | const token = this.tokens.get(key); 107 | 108 | if (!token) { 109 | return null; 110 | } 111 | 112 | return new Date(token.expiresAt); 113 | } 114 | } 115 | ``` -------------------------------------------------------------------------------- /docs/TopLevel/Set.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: TopLevel 2 | 3 | # Class Set 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - Set 9 | 10 | ## Description 11 | 12 | A Set can store any kind of element and ensures that no duplicates exist. Objects are stored and iterated in insertion order. 13 | 14 | ## Properties 15 | 16 | ### size 17 | 18 | **Type:** Number 19 | 20 | Number of elements stored in this set. 21 | 22 | ## Constructor Summary 23 | 24 | Set() Creates an empty Set. 25 | 26 | Set(values : Iterable) If the passed value is null or undefined then an empty set is constructed. 27 | 28 | ## Method Summary 29 | 30 | ### add 31 | 32 | **Signature:** `add(object : Object) : Set` 33 | 34 | Adds an element to the set. 35 | 36 | ### clear 37 | 38 | **Signature:** `clear() : void` 39 | 40 | Removes all elements from this set. 41 | 42 | ### delete 43 | 44 | **Signature:** `delete(object : Object) : boolean` 45 | 46 | Removes the element from the set. 47 | 48 | ### entries 49 | 50 | **Signature:** `entries() : ES6Iterator` 51 | 52 | Returns an iterator containing all elements of this set. 53 | 54 | ### forEach 55 | 56 | **Signature:** `forEach(callback : Function) : void` 57 | 58 | Runs the provided callback function once for each element present in this set. 59 | 60 | ### forEach 61 | 62 | **Signature:** `forEach(callback : Function, thisObject : Object) : void` 63 | 64 | Runs the provided callback function once for each element present in this set. 65 | 66 | ### has 67 | 68 | **Signature:** `has(object : Object) : boolean` 69 | 70 | Returns if this set contains the given object. 71 | 72 | ## Constructor Detail 73 | 74 | ## Method Detail 75 | 76 | ## Method Details 77 | 78 | ### add 79 | 80 | **Signature:** `add(object : Object) : Set` 81 | 82 | **Description:** Adds an element to the set. Does nothing if the set already contains the element. 83 | 84 | **Parameters:** 85 | 86 | - `object`: The object to add. 87 | 88 | **Returns:** 89 | 90 | This set object. 91 | 92 | --- 93 | 94 | ### clear 95 | 96 | **Signature:** `clear() : void` 97 | 98 | **Description:** Removes all elements from this set. 99 | 100 | --- 101 | 102 | ### delete 103 | 104 | **Signature:** `delete(object : Object) : boolean` 105 | 106 | **Description:** Removes the element from the set. 107 | 108 | **Parameters:** 109 | 110 | - `object`: The object to be removed. 111 | 112 | **Returns:** 113 | 114 | true if the set contained the object that was removed. Else false is returned. 115 | 116 | --- 117 | 118 | ### entries 119 | 120 | **Signature:** `entries() : ES6Iterator` 121 | 122 | **Description:** Returns an iterator containing all elements of this set. 123 | 124 | --- 125 | 126 | ### forEach 127 | 128 | **Signature:** `forEach(callback : Function) : void` 129 | 130 | **Description:** Runs the provided callback function once for each element present in this set. 131 | 132 | **Parameters:** 133 | 134 | - `callback`: The function to call, which is invoked with three arguments: the element (as value), the element (as index), and the Set object being iterated. 135 | 136 | --- 137 | 138 | ### forEach 139 | 140 | **Signature:** `forEach(callback : Function, thisObject : Object) : void` 141 | 142 | **Description:** Runs the provided callback function once for each element present in this set. 143 | 144 | **Parameters:** 145 | 146 | - `callback`: The function to call, which is invoked with three arguments: the element (as value), the element (as index), and the Set object being iterated. 147 | - `thisObject`: The Object to use as 'this' when executing callback. 148 | 149 | --- 150 | 151 | ### has 152 | 153 | **Signature:** `has(object : Object) : boolean` 154 | 155 | **Description:** Returns if this set contains the given object. 156 | 157 | **Parameters:** 158 | 159 | - `object`: The object to look for. 160 | 161 | **Returns:** 162 | 163 | true if the set contains the object else false is returned. 164 | 165 | --- ``` -------------------------------------------------------------------------------- /docs/dw_object/ObjectAttributeGroup.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.object 2 | 3 | # Class ObjectAttributeGroup 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.ObjectAttributeGroup 9 | 10 | ## Description 11 | 12 | Represents a group of object attributes. 13 | 14 | ## Properties 15 | 16 | ### attributeDefinitions 17 | 18 | **Type:** Collection (Read Only) 19 | 20 | All attribute definitions for this group. The collection 21 | may contain both system attribute definition as well as custom 22 | attribute definitions. 23 | 24 | ### description 25 | 26 | **Type:** String (Read Only) 27 | 28 | The description of this group in the current locale. 29 | 30 | ### displayName 31 | 32 | **Type:** String (Read Only) 33 | 34 | The display name of this group. 35 | 36 | ### ID 37 | 38 | **Type:** String (Read Only) 39 | 40 | The ID of this group. 41 | 42 | ### objectTypeDefinition 43 | 44 | **Type:** ObjectTypeDefinition (Read Only) 45 | 46 | The object type definition to which this attribute group 47 | belongs. 48 | 49 | ### system 50 | 51 | **Type:** boolean (Read Only) 52 | 53 | Identifies if this is an sytem or a custom attribute group. A system 54 | attribute group is pre-defined and can not be deleted. 55 | 56 | ## Constructor Summary 57 | 58 | ## Method Summary 59 | 60 | ### getAttributeDefinitions 61 | 62 | **Signature:** `getAttributeDefinitions() : Collection` 63 | 64 | Returns all attribute definitions for this group. 65 | 66 | ### getDescription 67 | 68 | **Signature:** `getDescription() : String` 69 | 70 | Returns the description of this group in the current locale. 71 | 72 | ### getDisplayName 73 | 74 | **Signature:** `getDisplayName() : String` 75 | 76 | Returns the display name of this group. 77 | 78 | ### getID 79 | 80 | **Signature:** `getID() : String` 81 | 82 | Returns the ID of this group. 83 | 84 | ### getObjectTypeDefinition 85 | 86 | **Signature:** `getObjectTypeDefinition() : ObjectTypeDefinition` 87 | 88 | Returns the object type definition to which this attribute group belongs. 89 | 90 | ### isSystem 91 | 92 | **Signature:** `isSystem() : boolean` 93 | 94 | Identifies if this is an sytem or a custom attribute group. 95 | 96 | ## Method Detail 97 | 98 | ## Method Details 99 | 100 | ### getAttributeDefinitions 101 | 102 | **Signature:** `getAttributeDefinitions() : Collection` 103 | 104 | **Description:** Returns all attribute definitions for this group. The collection may contain both system attribute definition as well as custom attribute definitions. 105 | 106 | **Returns:** 107 | 108 | all attribute definitions for this group. 109 | 110 | --- 111 | 112 | ### getDescription 113 | 114 | **Signature:** `getDescription() : String` 115 | 116 | **Description:** Returns the description of this group in the current locale. 117 | 118 | **Returns:** 119 | 120 | the display name of this group. 121 | 122 | --- 123 | 124 | ### getDisplayName 125 | 126 | **Signature:** `getDisplayName() : String` 127 | 128 | **Description:** Returns the display name of this group. 129 | 130 | **Returns:** 131 | 132 | the display name of this group. 133 | 134 | --- 135 | 136 | ### getID 137 | 138 | **Signature:** `getID() : String` 139 | 140 | **Description:** Returns the ID of this group. 141 | 142 | **Returns:** 143 | 144 | the ID of this group. 145 | 146 | --- 147 | 148 | ### getObjectTypeDefinition 149 | 150 | **Signature:** `getObjectTypeDefinition() : ObjectTypeDefinition` 151 | 152 | **Description:** Returns the object type definition to which this attribute group belongs. 153 | 154 | **Returns:** 155 | 156 | the object type definition to which this attribute group belongs. 157 | 158 | --- 159 | 160 | ### isSystem 161 | 162 | **Signature:** `isSystem() : boolean` 163 | 164 | **Description:** Identifies if this is an sytem or a custom attribute group. A system attribute group is pre-defined and can not be deleted. 165 | 166 | **Returns:** 167 | 168 | true if this is a system attribute group, false otherwise. 169 | 170 | --- ``` -------------------------------------------------------------------------------- /src/utils/log-cache.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { SFCCLogClient } from '../clients/log-client.js'; 2 | 3 | /** 4 | * Simple in-memory cache for log operations 5 | * Provides short-term caching to avoid repeated API calls for common operations 6 | */ 7 | export class LogCache { 8 | private cache = new Map<string, { data: any; timestamp: number; ttl: number }>(); 9 | private readonly DEFAULT_TTL = 5000; // 5 seconds default TTL 10 | private readonly MAX_CACHE_SIZE = 100; // Prevent memory bloat 11 | 12 | /** 13 | * Get cached data if available and not expired 14 | */ 15 | get<T>(key: string): T | null { 16 | const entry = this.cache.get(key); 17 | if (!entry) { 18 | return null; 19 | } 20 | 21 | // Check if expired 22 | if (Date.now() - entry.timestamp > entry.ttl) { 23 | this.cache.delete(key); 24 | return null; 25 | } 26 | 27 | return entry.data as T; 28 | } 29 | 30 | /** 31 | * Set cached data with TTL 32 | */ 33 | set(key: string, data: any, ttl: number = this.DEFAULT_TTL): void { 34 | // Implement basic LRU by removing oldest entries when cache is full 35 | if (this.cache.size >= this.MAX_CACHE_SIZE) { 36 | const oldestKey = this.cache.keys().next().value; 37 | if (oldestKey) { 38 | this.cache.delete(oldestKey); 39 | } 40 | } 41 | 42 | this.cache.set(key, { 43 | data, 44 | timestamp: Date.now(), 45 | ttl, 46 | }); 47 | } 48 | 49 | /** 50 | * Generate cache key for log operations 51 | */ 52 | static generateKey(operation: string, args: Record<string, any>): string { 53 | const sortedArgs = Object.keys(args) 54 | .sort() 55 | .map(key => `${key}=${args[key]}`) 56 | .join('&'); 57 | 58 | return `${operation}:${sortedArgs}`; 59 | } 60 | 61 | /** 62 | * Clear all cached entries 63 | */ 64 | clear(): void { 65 | this.cache.clear(); 66 | } 67 | 68 | /** 69 | * Remove expired entries (can be called periodically) 70 | */ 71 | cleanup(): void { 72 | const now = Date.now(); 73 | for (const [key, entry] of this.cache.entries()) { 74 | if (now - entry.timestamp > entry.ttl) { 75 | this.cache.delete(key); 76 | } 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Enhanced log client wrapper with caching capabilities 83 | * Provides transparent caching for frequently accessed log operations 84 | */ 85 | export class CachedLogClient { 86 | private cache = new LogCache(); 87 | private readonly CACHEABLE_OPERATIONS = new Set([ 88 | 'list_log_files', 89 | 'get_latest_job_log_files', 90 | 'summarize_logs', 91 | ]); 92 | 93 | constructor(private logClient: SFCCLogClient) {} 94 | 95 | /** 96 | * Execute operation with caching if applicable 97 | */ 98 | async executeWithCache<T>( 99 | operation: string, 100 | args: Record<string, any>, 101 | executor: () => Promise<T>, 102 | ttl?: number, 103 | ): Promise<T> { 104 | // Only cache certain operations to avoid stale data issues 105 | if (!this.CACHEABLE_OPERATIONS.has(operation)) { 106 | return executor(); 107 | } 108 | 109 | const cacheKey = LogCache.generateKey(operation, args); 110 | const cached = this.cache.get<T>(cacheKey); 111 | 112 | if (cached !== null) { 113 | return cached; 114 | } 115 | 116 | const result = await executor(); 117 | this.cache.set(cacheKey, result, ttl); 118 | return result; 119 | } 120 | 121 | /** 122 | * Clear cache - useful when log state might have changed 123 | */ 124 | clearCache(): void { 125 | this.cache.clear(); 126 | } 127 | 128 | /** 129 | * Cleanup expired cache entries 130 | */ 131 | cleanupCache(): void { 132 | this.cache.cleanup(); 133 | } 134 | } 135 | ``` -------------------------------------------------------------------------------- /docs/TopLevel/Generator.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: TopLevel 2 | 3 | # Class Generator 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - Generator 9 | 10 | ## Description 11 | 12 | A generator is a special type of function that works as a factory for iterators and it allows you to define an iterative algorithm by writing a single function which can maintain its own state. A function becomes a generator if it contains one or more yield statements. When a generator function is called, the body of the function does not execute straight away; instead, it returns a generator-iterator object. Each call to the generator-iterator's next() method will execute the body of the function up to the next yield statement and return its result. When either the end of the function or a return statement is reached, a StopIteration exception is thrown. For example, the following fib() function is a Fibonacci number generator, that returns the generator when it encounters the yield statement: function fib() { var fibNum = 0, j = 1; while (true) { yield fibNum; var t = fibNum; fibNum = j; j += t; } } To use the generator, simply call the next() method to access the values returned by the function: var gen = fib(); for (var i = 0; i < 10; i++) { document.write(gen.next() " "); } 13 | 14 | ## Constructor Summary 15 | 16 | Generator() 17 | 18 | ## Method Summary 19 | 20 | ### close 21 | 22 | **Signature:** `close() : void` 23 | 24 | Closes the iteration of the generator. 25 | 26 | ### next 27 | 28 | **Signature:** `next() : Object` 29 | 30 | Resumes the iteration of the generator by continuing the function at the statement after the yield statement. 31 | 32 | ### send 33 | 34 | **Signature:** `send(value : Object) : Object` 35 | 36 | Allows you to control the resumption of the iterative algorithm. 37 | 38 | ## Constructor Detail 39 | 40 | ## Method Detail 41 | 42 | ## Method Details 43 | 44 | ### close 45 | 46 | **Signature:** `close() : void` 47 | 48 | **Description:** Closes the iteration of the generator. Any finally clauses active in the generator function are run. If a finally clause throws any exception other than StopIteration, the exception is propagated to the caller of the close() method. 49 | 50 | --- 51 | 52 | ### next 53 | 54 | **Signature:** `next() : Object` 55 | 56 | **Description:** Resumes the iteration of the generator by continuing the function at the statement after the yield statement. This function throws a StopIterator exception when there are no additional iterative steps. 57 | 58 | **Returns:** 59 | 60 | the result of resuming the iterative algorithm or a StopIterator exception if the sequence is exhausted. 61 | 62 | **See Also:** 63 | 64 | StopIteration 65 | 66 | --- 67 | 68 | ### send 69 | 70 | **Signature:** `send(value : Object) : Object` 71 | 72 | **Description:** Allows you to control the resumption of the iterative algorithm. Once a generator has been started by calling its next() method, you can use send() and pass a specific value that will be treated as the result of the last yield. The generator will then return the operand of the subsequent yield. You can't start a generator at an arbitrary point; you must start it with next() before you can send() it a specific value. Note that calling send(undefined) is equivalent to calling next(). However, starting a newborn generator with any value other than 'undefined' when calling send() will result in a TypeError exception. 73 | 74 | **Parameters:** 75 | 76 | - `value`: the value to use. 77 | 78 | --- ``` -------------------------------------------------------------------------------- /docs/dw_crypto/JWEHeader.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.crypto 2 | 3 | # Class JWEHeader 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.crypto.JWEHeader 9 | 10 | ## Description 11 | 12 | This class represents an immutable header of a JWE (JSON Web Encryption) object. 13 | 14 | ## Properties 15 | 16 | ### algorithm 17 | 18 | **Type:** String (Read Only) 19 | 20 | Get the value of the algorithm parameter (alg). 21 | 22 | ### encryptionAlgorithm 23 | 24 | **Type:** String (Read Only) 25 | 26 | Get the value of the encryption algorithm parameter (enc). 27 | 28 | ## Constructor Summary 29 | 30 | ## Method Summary 31 | 32 | ### getAlgorithm 33 | 34 | **Signature:** `getAlgorithm() : String` 35 | 36 | Get the value of the algorithm parameter (alg). 37 | 38 | ### getEncryptionAlgorithm 39 | 40 | **Signature:** `getEncryptionAlgorithm() : String` 41 | 42 | Get the value of the encryption algorithm parameter (enc). 43 | 44 | ### parse 45 | 46 | **Signature:** `static parse(map : Object) : JWEHeader` 47 | 48 | Convert the given Map or JavaScript object into a JWE header. 49 | 50 | ### parseEncoded 51 | 52 | **Signature:** `static parseEncoded(base64encoded : String) : JWEHeader` 53 | 54 | Parse the given string as a Base64URL-encoded JWE header. 55 | 56 | ### parseJSON 57 | 58 | **Signature:** `static parseJSON(json : String) : JWEHeader` 59 | 60 | Parse the given string as a JWE header. 61 | 62 | ### toMap 63 | 64 | **Signature:** `toMap() : Map` 65 | 66 | Get a copy of these headers as a Map. 67 | 68 | ### toString 69 | 70 | **Signature:** `toString() : String` 71 | 72 | Get the content of the headers as a JSON String. 73 | 74 | ## Method Detail 75 | 76 | ## Method Details 77 | 78 | ### getAlgorithm 79 | 80 | **Signature:** `getAlgorithm() : String` 81 | 82 | **Description:** Get the value of the algorithm parameter (alg). 83 | 84 | **Returns:** 85 | 86 | Algorithm parameter from this header. 87 | 88 | --- 89 | 90 | ### getEncryptionAlgorithm 91 | 92 | **Signature:** `getEncryptionAlgorithm() : String` 93 | 94 | **Description:** Get the value of the encryption algorithm parameter (enc). 95 | 96 | **Returns:** 97 | 98 | Encryption algorithm parameter from this header. 99 | 100 | --- 101 | 102 | ### parse 103 | 104 | **Signature:** `static parse(map : Object) : JWEHeader` 105 | 106 | **Description:** Convert the given Map or JavaScript object into a JWE header. All keys correspond to JWE parameters. The algorithm (alg) and encryption method (enc) parameters are required. See JWE.decrypt(KeyRef) for supported values. 107 | 108 | **Parameters:** 109 | 110 | - `map`: Map or object data to convert. 111 | 112 | **Returns:** 113 | 114 | JWE Header. 115 | 116 | --- 117 | 118 | ### parseEncoded 119 | 120 | **Signature:** `static parseEncoded(base64encoded : String) : JWEHeader` 121 | 122 | **Description:** Parse the given string as a Base64URL-encoded JWE header. The algorithm (alg) and encryption method (enc) parameters are required. See JWE.decrypt(KeyRef) for supported values. 123 | 124 | **Parameters:** 125 | 126 | - `base64encoded`: Base64URL string to parse. 127 | 128 | **Returns:** 129 | 130 | JWE Header. 131 | 132 | --- 133 | 134 | ### parseJSON 135 | 136 | **Signature:** `static parseJSON(json : String) : JWEHeader` 137 | 138 | **Description:** Parse the given string as a JWE header. The algorithm (alg) and encryption method (enc) parameters are required. See JWE.decrypt(KeyRef) for supported values. 139 | 140 | **Parameters:** 141 | 142 | - `json`: JSON string to parse. 143 | 144 | **Returns:** 145 | 146 | JWE Header. 147 | 148 | --- 149 | 150 | ### toMap 151 | 152 | **Signature:** `toMap() : Map` 153 | 154 | **Description:** Get a copy of these headers as a Map. 155 | 156 | **Returns:** 157 | 158 | Copy of the JWE headers. 159 | 160 | --- 161 | 162 | ### toString 163 | 164 | **Signature:** `toString() : String` 165 | 166 | **Description:** Get the content of the headers as a JSON String. 167 | 168 | **Returns:** 169 | 170 | JSON String. 171 | 172 | --- ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: ❓ Question/Help 2 | description: Ask a question or get help with using the SFCC Development MCP Server 3 | title: "[Question]: " 4 | labels: ["question", "needs-triage"] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Need help or have a question? We're here to help! Please provide as much detail as possible so we can assist you effectively. 11 | 12 | - type: checkboxes 13 | id: terms 14 | attributes: 15 | label: Pre-submission Checklist 16 | description: Please confirm you have completed these steps before asking your question. 17 | options: 18 | - label: I have searched existing issues and discussions for similar questions 19 | required: true 20 | - label: I have read the README.md and relevant documentation 21 | required: true 22 | - label: I have checked the troubleshooting section (if applicable) 23 | required: true 24 | 25 | - type: dropdown 26 | id: question-category 27 | attributes: 28 | label: Question Category 29 | description: What category does your question fall into? 30 | options: 31 | - Installation and Setup 32 | - Configuration and Authentication 33 | - Using MCP Tools 34 | - SFCC Integration 35 | - Troubleshooting 36 | - Best Practices 37 | - Performance 38 | - Security 39 | - Development/Contributing 40 | - Other 41 | validations: 42 | required: true 43 | 44 | - type: textarea 45 | id: question 46 | attributes: 47 | label: Your Question 48 | description: What would you like to know? 49 | placeholder: Please ask your question clearly and provide context... 50 | validations: 51 | required: true 52 | 53 | - type: textarea 54 | id: context 55 | attributes: 56 | label: Context and Background 57 | description: Provide relevant context about what you're trying to accomplish 58 | placeholder: | 59 | - What are you trying to achieve? 60 | - What's your current setup? 61 | - What have you tried so far? 62 | 63 | - type: textarea 64 | id: current-setup 65 | attributes: 66 | label: Current Setup 67 | description: Describe your current configuration and environment 68 | value: | 69 | - OS: [e.g., macOS 14.1, Windows 11, Ubuntu 22.04] 70 | - Node.js version: [run `node --version`] 71 | - SFCC Dev MCP version: [check package.json version] 72 | - Operating mode: [Documentation-only or Full mode] 73 | - MCP Client: [e.g., Claude Desktop, Custom implementation] 74 | 75 | - type: textarea 76 | id: attempted-solutions 77 | attributes: 78 | label: What Have You Tried? 79 | description: What steps have you already taken to solve this? 80 | placeholder: | 81 | 1. I tried... 82 | 2. I looked at... 83 | 3. I configured... 84 | 85 | - type: textarea 86 | id: expected-outcome 87 | attributes: 88 | label: Expected Outcome 89 | description: What do you expect or hope to happen? 90 | 91 | - type: textarea 92 | id: relevant-logs 93 | attributes: 94 | label: Relevant Logs or Error Messages 95 | description: Include any relevant logs or error messages (remove sensitive information) 96 | render: shell 97 | 98 | - type: textarea 99 | id: additional-info 100 | attributes: 101 | label: Additional Information 102 | description: Any other information that might be helpful 103 | ``` -------------------------------------------------------------------------------- /docs/dw_customer/Wallet.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.customer 2 | 3 | # Class Wallet 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.customer.Wallet 9 | 10 | ## Description 11 | 12 | Represents a set of payment instruments associated with a registered customer. Note: this class allows access to sensitive personal and private information. Pay attention to appropriate legal and regulatory requirements when developing. 13 | 14 | ## Properties 15 | 16 | ### paymentInstruments 17 | 18 | **Type:** Collection (Read Only) 19 | 20 | A collection of all payment instruments associated with the 21 | related customer. 22 | 23 | ## Constructor Summary 24 | 25 | ## Method Summary 26 | 27 | ### createPaymentInstrument 28 | 29 | **Signature:** `createPaymentInstrument(paymentMethodId : String) : CustomerPaymentInstrument` 30 | 31 | Creates a new, empty payment instrument object associated with the related customer for the given payment method. 32 | 33 | ### getPaymentInstruments 34 | 35 | **Signature:** `getPaymentInstruments() : Collection` 36 | 37 | Returns a collection of all payment instruments associated with the related customer. 38 | 39 | ### getPaymentInstruments 40 | 41 | **Signature:** `getPaymentInstruments(paymentMethodID : String) : Collection` 42 | 43 | Returns a collection of all payment instruments associated with the related customer filtered by the given payment method id. 44 | 45 | ### removePaymentInstrument 46 | 47 | **Signature:** `removePaymentInstrument(instrument : CustomerPaymentInstrument) : void` 48 | 49 | Removes a payment instrument associated with the customer. 50 | 51 | ## Method Detail 52 | 53 | ## Method Details 54 | 55 | ### createPaymentInstrument 56 | 57 | **Signature:** `createPaymentInstrument(paymentMethodId : String) : CustomerPaymentInstrument` 58 | 59 | **Description:** Creates a new, empty payment instrument object associated with the related customer for the given payment method. 60 | 61 | **Parameters:** 62 | 63 | - `paymentMethodId`: the id of a payment method 64 | 65 | **Returns:** 66 | 67 | the new payment instrument object. 68 | 69 | **Throws:** 70 | 71 | NullArgumentException - If passed 'paymentMethodId' is null. 72 | 73 | --- 74 | 75 | ### getPaymentInstruments 76 | 77 | **Signature:** `getPaymentInstruments() : Collection` 78 | 79 | **Description:** Returns a collection of all payment instruments associated with the related customer. 80 | 81 | **Returns:** 82 | 83 | Collection of all payment instruments. 84 | 85 | --- 86 | 87 | ### getPaymentInstruments 88 | 89 | **Signature:** `getPaymentInstruments(paymentMethodID : String) : Collection` 90 | 91 | **Description:** Returns a collection of all payment instruments associated with the related customer filtered by the given payment method id. If null is passed as payment method id all payment instruments of the customer will be retrieved. If for the given payment method id no payment instrument is associated with the customer an empty collection will be returned. 92 | 93 | **Parameters:** 94 | 95 | - `paymentMethodID`: the paymentMethodID the payment method id to filter for 96 | 97 | **Returns:** 98 | 99 | Collection of payment instruments for a payment method. 100 | 101 | --- 102 | 103 | ### removePaymentInstrument 104 | 105 | **Signature:** `removePaymentInstrument(instrument : CustomerPaymentInstrument) : void` 106 | 107 | **Description:** Removes a payment instrument associated with the customer. 108 | 109 | **Parameters:** 110 | 111 | - `instrument`: the instrument associated with this customer 112 | 113 | **Throws:** 114 | 115 | NullArgumentException - If passed 'instrument' is null. 116 | IllegalArgumentException - If passed 'instrument' belongs to an other customer 117 | 118 | --- ``` -------------------------------------------------------------------------------- /docs/dw_extensions.payments/SalesforcePayPalOrderAddress.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.extensions.payments 2 | 3 | # Class SalesforcePayPalOrderAddress 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.extensions.payments.SalesforcePayPalOrderAddress 9 | 10 | ## Description 11 | 12 | Salesforce Payments representation of a PayPal order address object. See Salesforce Payments documentation for how to gain access and configure it for use on your sites. 13 | 14 | ## Properties 15 | 16 | ### addressLine1 17 | 18 | **Type:** String (Read Only) 19 | 20 | The address line 1. 21 | 22 | ### addressLine2 23 | 24 | **Type:** String (Read Only) 25 | 26 | The address line 2. 27 | 28 | ### adminArea1 29 | 30 | **Type:** String (Read Only) 31 | 32 | The address highest level sub-division in a country, which is usually a province, state, or ISO-3166-2 33 | subdivision. 34 | 35 | ### adminArea2 36 | 37 | **Type:** String (Read Only) 38 | 39 | The address city, town, or village. 40 | 41 | ### countryCode 42 | 43 | **Type:** String (Read Only) 44 | 45 | The address two-character ISO 3166-1 code that identifies the country or region. 46 | 47 | ### fullName 48 | 49 | **Type:** String (Read Only) 50 | 51 | The address full name. 52 | 53 | ### postalCode 54 | 55 | **Type:** String (Read Only) 56 | 57 | The address postal code. 58 | 59 | ## Constructor Summary 60 | 61 | ## Method Summary 62 | 63 | ### getAddressLine1 64 | 65 | **Signature:** `getAddressLine1() : String` 66 | 67 | Returns the address line 1. 68 | 69 | ### getAddressLine2 70 | 71 | **Signature:** `getAddressLine2() : String` 72 | 73 | Returns the address line 2. 74 | 75 | ### getAdminArea1 76 | 77 | **Signature:** `getAdminArea1() : String` 78 | 79 | Returns the address highest level sub-division in a country, which is usually a province, state, or ISO-3166-2 subdivision. 80 | 81 | ### getAdminArea2 82 | 83 | **Signature:** `getAdminArea2() : String` 84 | 85 | Returns the address city, town, or village. 86 | 87 | ### getCountryCode 88 | 89 | **Signature:** `getCountryCode() : String` 90 | 91 | Returns the address two-character ISO 3166-1 code that identifies the country or region. 92 | 93 | ### getFullName 94 | 95 | **Signature:** `getFullName() : String` 96 | 97 | Returns the address full name. 98 | 99 | ### getPostalCode 100 | 101 | **Signature:** `getPostalCode() : String` 102 | 103 | Returns the address postal code. 104 | 105 | ## Method Detail 106 | 107 | ## Method Details 108 | 109 | ### getAddressLine1 110 | 111 | **Signature:** `getAddressLine1() : String` 112 | 113 | **Description:** Returns the address line 1. 114 | 115 | **Returns:** 116 | 117 | address line 1 118 | 119 | --- 120 | 121 | ### getAddressLine2 122 | 123 | **Signature:** `getAddressLine2() : String` 124 | 125 | **Description:** Returns the address line 2. 126 | 127 | **Returns:** 128 | 129 | address line 2 130 | 131 | --- 132 | 133 | ### getAdminArea1 134 | 135 | **Signature:** `getAdminArea1() : String` 136 | 137 | **Description:** Returns the address highest level sub-division in a country, which is usually a province, state, or ISO-3166-2 subdivision. 138 | 139 | **Returns:** 140 | 141 | address highest level sub-division in a country, such as a state 142 | 143 | --- 144 | 145 | ### getAdminArea2 146 | 147 | **Signature:** `getAdminArea2() : String` 148 | 149 | **Description:** Returns the address city, town, or village. 150 | 151 | **Returns:** 152 | 153 | address city, town, or village 154 | 155 | --- 156 | 157 | ### getCountryCode 158 | 159 | **Signature:** `getCountryCode() : String` 160 | 161 | **Description:** Returns the address two-character ISO 3166-1 code that identifies the country or region. 162 | 163 | **Returns:** 164 | 165 | address country code 166 | 167 | --- 168 | 169 | ### getFullName 170 | 171 | **Signature:** `getFullName() : String` 172 | 173 | **Description:** Returns the address full name. 174 | 175 | **Returns:** 176 | 177 | address full name 178 | 179 | --- 180 | 181 | ### getPostalCode 182 | 183 | **Signature:** `getPostalCode() : String` 184 | 185 | **Description:** Returns the address postal code. 186 | 187 | **Returns:** 188 | 189 | address postal code 190 | 191 | --- ``` -------------------------------------------------------------------------------- /src/types/types.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Type definitions for SFCC MCP Server 3 | * 4 | * This module contains all TypeScript interfaces and types used throughout 5 | * the SFCC (Salesforce B2C Commerce Cloud) MCP server application. 6 | */ 7 | 8 | /** 9 | * Configuration interface for SFCC connection 10 | * Supports both basic authentication (username/password) and OAuth (clientId/clientSecret) 11 | */ 12 | export interface SFCCConfig { 13 | /** SFCC hostname (e.g., zziu-006.dx.commercecloud.salesforce.com) */ 14 | hostname?: string; 15 | /** Username for basic authentication (optional if using OAuth) */ 16 | username?: string; 17 | /** Password for basic authentication (optional if using OAuth) */ 18 | password?: string; 19 | /** Client ID for OAuth authentication (optional if using basic auth) */ 20 | clientId?: string; 21 | /** Client secret for OAuth authentication (optional if using basic auth) */ 22 | clientSecret?: string; 23 | /** Site ID for SFCC instance */ 24 | siteId?: string; 25 | } 26 | 27 | /** 28 | * Configuration structure from dw.json file 29 | * This matches the standard Salesforce Commerce Cloud dw.json configuration format 30 | */ 31 | export interface DwJsonConfig { 32 | /** SFCC hostname */ 33 | hostname: string; 34 | /** Username for WebDAV access */ 35 | username: string; 36 | /** Password for WebDAV access */ 37 | password: string; 38 | /** Optional code version */ 39 | 'code-version'?: string; 40 | /** Optional client ID for OAuth */ 41 | 'client-id'?: string; 42 | /** Optional client secret for OAuth */ 43 | 'client-secret'?: string; 44 | /** Optional site ID for SFCC instance */ 45 | 'site-id'?: string; 46 | } 47 | 48 | /** 49 | * Log levels supported by the SFCC logging system 50 | */ 51 | export type LogLevel = 'error' | 'warn' | 'info' | 'debug'; 52 | 53 | /** 54 | * Structure for log file metadata 55 | */ 56 | export interface LogFileInfo { 57 | /** File name */ 58 | name: string; 59 | /** File size in bytes */ 60 | size: number; 61 | /** Last modification timestamp */ 62 | lastModified: string; 63 | } 64 | 65 | /** 66 | * Summary statistics for log analysis 67 | */ 68 | export interface LogSummary { 69 | /** Date of the logs being summarized */ 70 | date: string; 71 | /** Number of error entries found */ 72 | errorCount: number; 73 | /** Number of warning entries found */ 74 | warningCount: number; 75 | /** Number of info entries found */ 76 | infoCount: number; 77 | /** Number of debug entries found */ 78 | debugCount: number; 79 | /** List of unique error patterns identified */ 80 | keyIssues: string[]; 81 | /** List of log files analyzed */ 82 | files: string[]; 83 | } 84 | 85 | /** 86 | * OAuth 2.0 token response from SFCC authorization server 87 | */ 88 | export interface OAuthTokenResponse { 89 | /** Token expiration time in seconds */ 90 | expires_in: number; 91 | /** Token type (always "Bearer" for SFCC) */ 92 | token_type: string; 93 | /** The actual access token */ 94 | access_token: string; 95 | } 96 | 97 | /** 98 | * OAuth token with expiration tracking 99 | */ 100 | export interface OAuthToken { 101 | /** The access token */ 102 | accessToken: string; 103 | /** Token type */ 104 | tokenType: string; 105 | /** When the token expires (timestamp) */ 106 | expiresAt: number; 107 | } 108 | 109 | /** 110 | * OCAPI client configuration 111 | */ 112 | export interface OCAPIConfig { 113 | /** SFCC hostname */ 114 | hostname: string; 115 | /** OAuth client ID */ 116 | clientId: string; 117 | /** OAuth client secret */ 118 | clientSecret: string; 119 | /** Site ID (optional, for shop API) */ 120 | siteId?: string; 121 | /** API version (default: v21_3) */ 122 | version?: string; 123 | } 124 | ``` -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- ```markdown 1 | ## 📋 Pull Request Summary 2 | 3 | ### Type of Change 4 | <!-- Please select the type of change this PR introduces --> 5 | - [ ] 🐛 Bug fix (non-breaking change which fixes an issue) 6 | - [ ] ✨ New feature (non-breaking change which adds functionality) 7 | - [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected) 8 | - [ ] 📚 Documentation update (changes to documentation only) 9 | - [ ] 🔧 Configuration/tooling update (changes to build process, CI/CD, etc.) 10 | - [ ] ♻️ Code refactoring (no functional changes, no API changes) 11 | - [ ] 🎨 Style/formatting changes (code formatting, missing semicolons, etc.) 12 | - [ ] ⚡ Performance improvement 13 | - [ ] 🧪 Test improvements 14 | - [ ] 🔒 Security enhancement 15 | 16 | ### Description 17 | <!-- Provide a clear and concise description of what this PR does --> 18 | 19 | 20 | ### Related Issues 21 | <!-- Link to related issues using keywords like "Fixes #123" or "Closes #456" --> 22 | - Fixes # 23 | - Related to # 24 | 25 | ### Changes Made 26 | <!-- List the main changes made in this PR --> 27 | - 28 | - 29 | - 30 | 31 | ### Testing 32 | <!-- Describe how you tested your changes --> 33 | - [ ] Unit tests added/updated 34 | - [ ] Integration tests added/updated 35 | - [ ] Manual testing performed 36 | - [ ] Tested in documentation-only mode 37 | - [ ] Tested in full mode (with SFCC credentials) 38 | - [ ] All existing tests pass 39 | 40 | ### Test Coverage 41 | <!-- Describe what testing was done --> 42 | 43 | 44 | ### Breaking Changes 45 | <!-- If this is a breaking change, describe what breaks and how to migrate --> 46 | 47 | 48 | ### Documentation 49 | <!-- Check all that apply --> 50 | - [ ] README.md updated (if user-facing changes) 51 | - [ ] copilot-instructions.md updated (if architectural changes) 52 | - [ ] API documentation updated 53 | - [ ] Best practices guides updated 54 | - [ ] Code comments added/updated 55 | - [ ] No documentation changes needed 56 | 57 | ### Performance Impact 58 | <!-- Describe any performance implications --> 59 | - [ ] No performance impact 60 | - [ ] Performance improvement 61 | - [ ] Minor performance impact (acceptable) 62 | - [ ] Significant performance impact (needs discussion) 63 | 64 | ### Security Considerations 65 | <!-- Describe any security implications --> 66 | - [ ] No security impact 67 | - [ ] Security improvement 68 | - [ ] Reviewed for security implications 69 | - [ ] No new external dependencies 70 | - [ ] New dependencies reviewed for security 71 | 72 | ### Checklist 73 | <!-- Ensure all items are checked before requesting review --> 74 | - [ ] Code follows the project's style guidelines 75 | - [ ] Self-review of the code has been performed 76 | - [ ] Code is well-commented, particularly in hard-to-understand areas 77 | - [ ] Changes generate no new warnings or errors 78 | - [ ] Any dependent changes have been merged and published 79 | - [ ] Version numbers updated (if applicable) 80 | - [ ] Changelog updated (if applicable) 81 | 82 | ### Reviewer Notes 83 | <!-- Any specific areas you'd like reviewers to focus on --> 84 | 85 | 86 | ### Screenshots/Examples 87 | <!-- If applicable, add screenshots or code examples --> 88 | 89 | 90 | --- 91 | 92 | ### For Maintainers 93 | <!-- This section is for maintainers to fill out during review --> 94 | 95 | #### Review Checklist 96 | - [ ] Code quality and style 97 | - [ ] Test coverage adequate 98 | - [ ] Documentation updated appropriately 99 | - [ ] Security implications reviewed 100 | - [ ] Performance impact acceptable 101 | - [ ] Breaking changes properly documented 102 | - [ ] Version bump needed 103 | - [ ] Release notes needed 104 | ``` -------------------------------------------------------------------------------- /tests/path-service.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { PathService, MockPathService } from '../src/services/path-service.js'; 2 | 3 | describe('PathService', () => { 4 | describe('Production PathService', () => { 5 | let service: PathService; 6 | 7 | beforeEach(() => { 8 | service = new PathService(); 9 | }); 10 | 11 | it('should be instantiable', () => { 12 | expect(service).toBeInstanceOf(PathService); 13 | }); 14 | 15 | it('should join paths correctly', () => { 16 | expect(service.join('a', 'b', 'c')).toMatch(/a.b.c/); 17 | }); 18 | 19 | it('should resolve paths correctly', () => { 20 | const resolved = service.resolve('test', 'path'); 21 | expect(service.isAbsolute(resolved)).toBe(true); 22 | }); 23 | 24 | it('should get dirname correctly', () => { 25 | const result = service.dirname('/path/to/file.txt'); 26 | expect(result).toMatch(/.*path.to/); 27 | }); 28 | 29 | it('should get basename correctly', () => { 30 | expect(service.basename('/path/to/file.txt')).toBe('file.txt'); 31 | expect(service.basename('/path/to/file.txt', '.txt')).toBe('file'); 32 | }); 33 | 34 | it('should get extension correctly', () => { 35 | expect(service.extname('/path/to/file.txt')).toBe('.txt'); 36 | expect(service.extname('/path/to/file')).toBe(''); 37 | }); 38 | 39 | it('should normalize paths correctly', () => { 40 | const normalized = service.normalize('/path//to///file.txt'); 41 | expect(normalized).not.toMatch(/\/\/+/); 42 | }); 43 | 44 | it('should detect absolute paths correctly', () => { 45 | expect(service.isAbsolute('/absolute/path')).toBe(true); 46 | expect(service.isAbsolute('relative/path')).toBe(false); 47 | }); 48 | }); 49 | 50 | describe('MockPathService', () => { 51 | let mockService: MockPathService; 52 | 53 | beforeEach(() => { 54 | mockService = new MockPathService(); 55 | }); 56 | 57 | it('should be instantiable', () => { 58 | expect(mockService).toBeInstanceOf(MockPathService); 59 | }); 60 | 61 | it('should join paths with forward slashes', () => { 62 | expect(mockService.join('a', 'b', 'c')).toBe('a/b/c'); 63 | }); 64 | 65 | it('should resolve paths with mock root', () => { 66 | expect(mockService.resolve('test', 'path')).toBe('/mock/root/test/path'); 67 | expect(mockService.resolve('/absolute', 'path')).toBe('/absolute/path'); 68 | }); 69 | 70 | it('should get dirname correctly', () => { 71 | expect(mockService.dirname('/path/to/file.txt')).toBe('/path/to'); 72 | expect(mockService.dirname('/file.txt')).toBe('/'); 73 | }); 74 | 75 | it('should get basename correctly', () => { 76 | expect(mockService.basename('/path/to/file.txt')).toBe('file.txt'); 77 | expect(mockService.basename('/path/to/file.txt', '.txt')).toBe('file'); 78 | expect(mockService.basename('/path/to/file.txt', '.js')).toBe('file.txt'); 79 | }); 80 | 81 | it('should get extension correctly', () => { 82 | expect(mockService.extname('/path/to/file.txt')).toBe('.txt'); 83 | expect(mockService.extname('/path/to/file.min.js')).toBe('.js'); 84 | expect(mockService.extname('/path/to/file')).toBe(''); 85 | }); 86 | 87 | it('should normalize paths correctly', () => { 88 | expect(mockService.normalize('/path//to///file.txt')).toBe('/path/to/file.txt'); 89 | }); 90 | 91 | it('should detect absolute paths correctly', () => { 92 | expect(mockService.isAbsolute('/absolute/path')).toBe(true); 93 | expect(mockService.isAbsolute('relative/path')).toBe(false); 94 | }); 95 | }); 96 | }); 97 | ``` -------------------------------------------------------------------------------- /docs-site/components/SEO.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React from 'react'; 2 | import { Head } from 'vite-react-ssg'; 3 | 4 | interface SEOProps { 5 | title?: string; 6 | description?: string; 7 | keywords?: string; 8 | canonical?: string; 9 | ogImage?: string; 10 | ogType?: 'website' | 'article'; 11 | twitterCard?: 'summary' | 'summary_large_image'; 12 | noindex?: boolean; 13 | } 14 | 15 | const SEO: React.FC<SEOProps> = ({ 16 | title = 'SFCC Development MCP Server', 17 | description = 'Model Context Protocol server for Salesforce B2C Commerce Cloud development. Access comprehensive documentation, analyze logs, explore system objects, and get best practices with AI assistance.', 18 | keywords = 'SFCC, Salesforce Commerce Cloud, Model Context Protocol, MCP server, AI development tools, SFCC documentation, Commerce Cloud development', 19 | canonical, 20 | ogImage = 'https://sfcc-mcp-dev.rhino-inquisitor.com/explain-product-pricing-methods.png', 21 | ogType = 'website', 22 | twitterCard = 'summary_large_image', 23 | structuredData, 24 | noindex = false 25 | }) => { 26 | const baseUrl = 'https://sfcc-mcp-dev.rhino-inquisitor.com'; 27 | const fullCanonical = canonical ? `${baseUrl}${canonical}` : baseUrl; 28 | const fullTitle = title === 'SFCC Development MCP Server' ? title : `${title} | SFCC Development MCP Server`; 29 | 30 | return ( 31 | <Head> 32 | <title>{fullTitle}</title> 33 | <meta name="description" content={description} /> 34 | <meta name="keywords" content={keywords} /> 35 | <link rel="canonical" href={fullCanonical} /> 36 | 37 | {/* Basic Meta Tags */} 38 | <meta name="author" content="Thomas Theunen" /> 39 | <meta name="publisher" content="Thomas Theunen" /> 40 | {noindex ? ( 41 | <meta name="robots" content="noindex, nofollow" /> 42 | ) : ( 43 | <meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /> 44 | )} 45 | 46 | {/* Open Graph Tags */} 47 | <meta property="og:title" content={fullTitle} /> 48 | <meta property="og:description" content={description} /> 49 | <meta property="og:type" content={ogType} /> 50 | <meta property="og:url" content={fullCanonical} /> 51 | <meta property="og:site_name" content="SFCC Development MCP Server" /> 52 | <meta property="og:image" content={ogImage} /> 53 | <meta property="og:image:alt" content={title} /> 54 | <meta property="og:image:width" content="1200" /> 55 | <meta property="og:image:height" content="630" /> 56 | <meta property="og:locale" content="en_US" /> 57 | 58 | {/* Twitter Card Tags */} 59 | <meta name="twitter:card" content={twitterCard} /> 60 | <meta name="twitter:title" content={fullTitle} /> 61 | <meta name="twitter:description" content={description} /> 62 | <meta name="twitter:image" content={ogImage} /> 63 | <meta name="twitter:image:alt" content={title} /> 64 | <meta name="twitter:creator" content="@taurgis" /> 65 | <meta name="twitter:site" content="@taurgis" /> 66 | 67 | {/* Additional Meta Tags */} 68 | <meta name="application-name" content="SFCC Development MCP Server" /> 69 | <meta name="msapplication-tooltip" content={description} /> 70 | <meta name="apple-mobile-web-app-title" content="SFCC MCP Server" /> 71 | <meta name="apple-mobile-web-app-capable" content="yes" /> 72 | <meta name="apple-mobile-web-app-status-bar-style" content="default" /> 73 | 74 | 75 | </Head> 76 | ); 77 | }; 78 | 79 | export default SEO; ``` -------------------------------------------------------------------------------- /docs/dw_svc/FTPService.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.svc 2 | 3 | # Class FTPService 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.svc.Service 9 | - dw.svc.FTPService 10 | 11 | ## Description 12 | 13 | Represents an FTP or SFTP Service. There are two basic styles of configuration for this service. In the first style, createRequest is implemented to call the setOperation method on the Service. This will cause the single operation to be performed and returned as the data object in the parseResponse method. Any error status is set automatically based on the returned value of the operation. In the second style, execute is implemented to perform one or more operations using the serviceClient available on the Service object. This serviceClient will be either an FTPClient or an SFTPClient. The return value of execute will be passed as the data object in the parseResponse method. Note that the use of the FTP client is deprecated, and SFTP should be used instead. 14 | 15 | ## Properties 16 | 17 | ### autoDisconnect 18 | 19 | **Type:** boolean 20 | 21 | The status of whether the underlying FTP connection will be disconnected after the service call. 22 | 23 | ### client 24 | 25 | **Type:** Object (Read Only) 26 | 27 | The underlying client object. 28 | 29 | This is either an FTPClient or SFTPClient, depending on the protocol. 30 | 31 | ## Constructor Summary 32 | 33 | ## Method Summary 34 | 35 | ### getClient 36 | 37 | **Signature:** `getClient() : Object` 38 | 39 | Returns the underlying client object. 40 | 41 | ### isAutoDisconnect 42 | 43 | **Signature:** `isAutoDisconnect() : boolean` 44 | 45 | Returns the status of whether the underlying FTP connection will be disconnected after the service call. 46 | 47 | ### setAutoDisconnect 48 | 49 | **Signature:** `setAutoDisconnect(b : boolean) : FTPService` 50 | 51 | Sets the auto-disconnect flag. 52 | 53 | ### setOperation 54 | 55 | **Signature:** `setOperation(name : String, args : Object...) : FTPService` 56 | 57 | Sets a single operation to perform during the execute phase of the service. 58 | 59 | ## Method Detail 60 | 61 | ## Method Details 62 | 63 | ### getClient 64 | 65 | **Signature:** `getClient() : Object` 66 | 67 | **Description:** Returns the underlying client object. This is either an FTPClient or SFTPClient, depending on the protocol. 68 | 69 | **Returns:** 70 | 71 | (S)FTP Client object. 72 | 73 | --- 74 | 75 | ### isAutoDisconnect 76 | 77 | **Signature:** `isAutoDisconnect() : boolean` 78 | 79 | **Description:** Returns the status of whether the underlying FTP connection will be disconnected after the service call. 80 | 81 | **Returns:** 82 | 83 | The auto-disconnect flag. 84 | 85 | --- 86 | 87 | ### setAutoDisconnect 88 | 89 | **Signature:** `setAutoDisconnect(b : boolean) : FTPService` 90 | 91 | **Description:** Sets the auto-disconnect flag. If true, the underlying FTP connection will be disconnected after the service call. If false then it will remain open. The default value is true. 92 | 93 | **Parameters:** 94 | 95 | - `b`: true to enable auto-disconnect, false otherwise. 96 | 97 | **Returns:** 98 | 99 | this FTP or SFTP Service. 100 | 101 | --- 102 | 103 | ### setOperation 104 | 105 | **Signature:** `setOperation(name : String, args : Object...) : FTPService` 106 | 107 | **Description:** Sets a single operation to perform during the execute phase of the service. The given arguments make up a method name and arguments on the underlying getClient() object. This method will be invoked during execution, with the result passed into the callback's parseResponse method. This is required unless the callback defines an execute method. 108 | 109 | **Parameters:** 110 | 111 | - `name`: Method name. 112 | - `args`: Method arguments. 113 | 114 | **Returns:** 115 | 116 | this FTP or SFTP Service. 117 | 118 | --- ``` -------------------------------------------------------------------------------- /docs/dw_order.hooks/CalculateHooks.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.order.hooks 2 | 3 | # Class CalculateHooks 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - dw.order.hooks.CalculateHooks 8 | 9 | ## Description 10 | 11 | This interface represents all script hooks that can be registered to customize the order and basket calculation functionality. It contains the extension points (hook names), and the functions that are called by each extension point. A function must be defined inside a JavaScript source and must be exported. The script with the exported hook function must be located inside a site cartridge. Inside the site cartridge a 'package.json' file with a 'hooks' entry must exist. "hooks": "./hooks.json" The hooks entry links to a json file, relative to the 'package.json' file. This file lists all registered hooks inside the hooks property: "hooks": [ {"name": "dw.order.calculate", "script": "./calculate.js"} ] A hook entry has a 'name' and a 'script' property. The 'name' contains the extension point, the hook name. The 'script' contains the script relative to the hooks file, with the exported hook function. 12 | 13 | ## Constants 14 | 15 | ## Properties 16 | 17 | ## Constructor Summary 18 | 19 | ## Method Summary 20 | 21 | ### calculate 22 | 23 | **Signature:** `calculate(lineItemCtnr : LineItemCtnr) : Status` 24 | 25 | The function is called by extension point extensionPointCalculate. 26 | 27 | ### calculateShipping 28 | 29 | **Signature:** `calculateShipping(lineItemCtnr : LineItemCtnr) : Status` 30 | 31 | The function is called by extension point extensionPointCalculateShipping. 32 | 33 | ### calculateTax 34 | 35 | **Signature:** `calculateTax(lineItemCtnr : LineItemCtnr) : Status` 36 | 37 | The function is called by extension point extensionPointCalculateTax. 38 | 39 | ## Method Detail 40 | 41 | ## Method Details 42 | 43 | ### calculate 44 | 45 | **Signature:** `calculate(lineItemCtnr : LineItemCtnr) : Status` 46 | 47 | **Description:** The function is called by extension point extensionPointCalculate. It provides a single place for the line item container calculation. To provide a fallback for existing implementations, the default implementation calls the hook dw.ocapi.shop.basket.calculate. However, this hook is deprecated, and calling it will create entries in the deprecated API usage logs. You should override this function to use dw.order.calculate instead. If you provide your own implementation, you should provide and use the following hooks. Best practice is to use the hook manager to retrieve them in the calculate hook, and avoid calling them directly. extensionPointCalculateTax for tax calculation extensionPointCalculateShipping for shipping calculation 48 | 49 | **Parameters:** 50 | 51 | - `lineItemCtnr`: the line item container to be (re)calculated. 52 | 53 | --- 54 | 55 | ### calculateShipping 56 | 57 | **Signature:** `calculateShipping(lineItemCtnr : LineItemCtnr) : Status` 58 | 59 | **Description:** The function is called by extension point extensionPointCalculateShipping. It provides a single place for shipping calculation during the line item container calculation. 60 | 61 | **Parameters:** 62 | 63 | - `lineItemCtnr`: the line item container to be (re)calculated. 64 | 65 | --- 66 | 67 | ### calculateTax 68 | 69 | **Signature:** `calculateTax(lineItemCtnr : LineItemCtnr) : Status` 70 | 71 | **Description:** The function is called by extension point extensionPointCalculateTax. It provides a single place for tax calculation during the line item container calculation. 72 | 73 | **Parameters:** 74 | 75 | - `lineItemCtnr`: the line item container to be (re)calculated. 76 | 77 | --- ``` -------------------------------------------------------------------------------- /docs/dw_catalog/ProductPriceTable.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.catalog 2 | 3 | # Class ProductPriceTable 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.catalog.ProductPriceTable 9 | 10 | ## Description 11 | 12 | A ProductPriceTable is a map of quantities to prices representing the potentially tiered prices of a product in Commerce Cloud Digital. The price of a product is the price associated with the largest quantity in the ProductPriceTable which does not exceed the purchase quantity. 13 | 14 | ## Properties 15 | 16 | ### quantities 17 | 18 | **Type:** Collection (Read Only) 19 | 20 | All quantities stored in the price table. 21 | 22 | ## Constructor Summary 23 | 24 | ## Method Summary 25 | 26 | ### getNextQuantity 27 | 28 | **Signature:** `getNextQuantity(quantity : Quantity) : Quantity` 29 | 30 | Returns the quantity following the passed quantity in the price table. 31 | 32 | ### getPercentage 33 | 34 | **Signature:** `getPercentage(quantity : Quantity) : Number` 35 | 36 | Returns the percentage off value of the price related to the passed quantity, calculated based on the price of the products minimum order quantity. 37 | 38 | ### getPrice 39 | 40 | **Signature:** `getPrice(quantity : Quantity) : Money` 41 | 42 | Returns the monetary price for the passed order quantity. 43 | 44 | ### getPriceBook 45 | 46 | **Signature:** `getPriceBook(quantity : Quantity) : PriceBook` 47 | 48 | Returns the price book which defined the monetary price for the passed order quantity. 49 | 50 | ### getQuantities 51 | 52 | **Signature:** `getQuantities() : Collection` 53 | 54 | Returns all quantities stored in the price table. 55 | 56 | ## Method Detail 57 | 58 | ## Method Details 59 | 60 | ### getNextQuantity 61 | 62 | **Signature:** `getNextQuantity(quantity : Quantity) : Quantity` 63 | 64 | **Description:** Returns the quantity following the passed quantity in the price table. If the passed quantity is the last entry in the price table, null is returned. 65 | 66 | **Parameters:** 67 | 68 | - `quantity`: the quantity to use to locate the next quantity in the price table. 69 | 70 | **Returns:** 71 | 72 | the next quantity or null. 73 | 74 | --- 75 | 76 | ### getPercentage 77 | 78 | **Signature:** `getPercentage(quantity : Quantity) : Number` 79 | 80 | **Description:** Returns the percentage off value of the price related to the passed quantity, calculated based on the price of the products minimum order quantity. 81 | 82 | **Parameters:** 83 | 84 | - `quantity`: the price quantity to compute the percentage off. 85 | 86 | **Returns:** 87 | 88 | the percentage off value of the price related to the passed quantity. 89 | 90 | --- 91 | 92 | ### getPrice 93 | 94 | **Signature:** `getPrice(quantity : Quantity) : Money` 95 | 96 | **Description:** Returns the monetary price for the passed order quantity. If no price is defined for the passed quantity, null is returned. This can happen if for example no price is defined for a single item. 97 | 98 | **Parameters:** 99 | 100 | - `quantity`: the quantity to use to determine price. 101 | 102 | **Returns:** 103 | 104 | price amount for the passed quantity 105 | 106 | --- 107 | 108 | ### getPriceBook 109 | 110 | **Signature:** `getPriceBook(quantity : Quantity) : PriceBook` 111 | 112 | **Description:** Returns the price book which defined the monetary price for the passed order quantity. If no price is defined for the passed quantity, null is returned. This can happen if for example no price is defined for a single item. 113 | 114 | **Parameters:** 115 | 116 | - `quantity`: the quantity to use to determine price. 117 | 118 | **Returns:** 119 | 120 | the price book defining this price, or null 121 | 122 | --- 123 | 124 | ### getQuantities 125 | 126 | **Signature:** `getQuantities() : Collection` 127 | 128 | **Description:** Returns all quantities stored in the price table. 129 | 130 | **Returns:** 131 | 132 | all price table quantities. 133 | 134 | --- ``` -------------------------------------------------------------------------------- /docs-site/components/ConfigModeTabs.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React from 'react'; 2 | import CodeBlock, { InlineCode } from './CodeBlock'; 3 | 4 | export const ConfigModeTabs: React.FC = () => { 5 | const [active, setActive] = React.useState<'docs' | 'full' | 'env'>('docs'); 6 | const tabBase = 'px-5 py-2 rounded-full text-xs md:text-sm font-medium transition border'; 7 | return ( 8 | <div> 9 | <div role="tablist" aria-label="Configuration modes" className="flex flex-wrap gap-3 mb-8"> 10 | <button aria-selected={active==='docs'} onClick={()=>setActive('docs')} className={`${tabBase} ${active==='docs' ? 'bg-green-600 text-white border-green-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-green-400 hover:text-green-600'}`}>Docs Only</button> 11 | <button aria-selected={active==='full'} onClick={()=>setActive('full')} className={`${tabBase} ${active==='full' ? 'bg-blue-600 text-white border-blue-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-blue-400 hover:text-blue-600'}`}>Full Mode</button> 12 | <button aria-selected={active==='env'} onClick={()=>setActive('env')} className={`${tabBase} ${active==='env' ? 'bg-purple-600 text-white border-purple-600 shadow' : 'bg-white text-gray-700 border-gray-200 hover:border-purple-400 hover:text-purple-600'}`}>Env Vars</button> 13 | </div> 14 | {active==='docs' && ( 15 | <div role="tabpanel" className="space-y-4 animate-fade-in"> 16 | <p className="text-sm text-gray-600">No credentials required. Add the server to your AI client:</p> 17 | <CodeBlock language="json" code={`{\n \"mcpServers\": {\n \"sfcc-dev\": {\n \"command\": \"npx\",\n \"args\": [\"sfcc-dev-mcp\"]\n }\n }\n}`} /> 18 | <ul className="text-xs text-gray-600 list-disc pl-5 space-y-1"> 19 | <li>Enables documentation, best practices & cartridge generation</li> 20 | <li>Upgrade anytime by adding <InlineCode>--dw-json</InlineCode></li> 21 | </ul> 22 | </div> 23 | )} 24 | {active==='full' && ( 25 | <div role="tabpanel" className="space-y-4 animate-fade-in"> 26 | <p className="text-sm text-gray-600">Provide a <InlineCode>dw.json</InlineCode> file to unlock logs, system objects & code versions.</p> 27 | <CodeBlock language="json" code={`{\n \"mcpServers\": {\n \"sfcc-dev\": {\n \"command\": \"npx\",\n \"args\": [\"sfcc-dev-mcp\", \"--dw-json\", \"/path/to/dw.json\"]\n }\n }\n}`} /> 28 | <p className="text-xs text-gray-500">Add <InlineCode>--debug true</InlineCode> temporarily when diagnosing configuration issues.</p> 29 | </div> 30 | )} 31 | {active==='env' && ( 32 | <div role="tabpanel" className="space-y-4 animate-fade-in"> 33 | <p className="text-sm text-gray-600">Use environment variables in CI or container setups. No file needed.</p> 34 | <CodeBlock language="bash" code={`export SFCC_HOSTNAME=\"your-instance.sandbox.us01.dx.commercecloud.salesforce.com\"\nexport SFCC_USERNAME=\"your-username\"\nexport SFCC_PASSWORD=\"your-password\"\nexport SFCC_CLIENT_ID=\"your-client-id\"\nexport SFCC_CLIENT_SECRET=\"your-client-secret\"\n\nnpx sfcc-dev-mcp`} /> 35 | <p className="text-xs text-gray-500">Command-line <InlineCode>--dw-json</InlineCode> always overrides env vars.</p> 36 | </div> 37 | )} 38 | </div> 39 | ); 40 | }; 41 | 42 | export default ConfigModeTabs; 43 | ``` -------------------------------------------------------------------------------- /docs/dw_customer/ProductListRegistrant.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.customer 2 | 3 | # Class ProductListRegistrant 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.customer.ProductListRegistrant 11 | 12 | ## Description 13 | 14 | A ProductListRegistrant is typically associated with an event related product list such as a gift registry. It holds information about a person associated with the event such as a bride or groom. 15 | 16 | ## Properties 17 | 18 | ### email 19 | 20 | **Type:** String 21 | 22 | The email address of the registrant or null. 23 | 24 | ### firstName 25 | 26 | **Type:** String 27 | 28 | The first name of the registrant or null. 29 | 30 | ### lastName 31 | 32 | **Type:** String 33 | 34 | The last name of the registrant or null. 35 | 36 | ### role 37 | 38 | **Type:** String 39 | 40 | The role of the registrant or null. The role of a registrant 41 | can be for example the bride of a bridal couple. 42 | 43 | ## Constructor Summary 44 | 45 | ## Method Summary 46 | 47 | ### getEmail 48 | 49 | **Signature:** `getEmail() : String` 50 | 51 | Returns the email address of the registrant or null. 52 | 53 | ### getFirstName 54 | 55 | **Signature:** `getFirstName() : String` 56 | 57 | Returns the first name of the registrant or null. 58 | 59 | ### getLastName 60 | 61 | **Signature:** `getLastName() : String` 62 | 63 | Returns the last name of the registrant or null. 64 | 65 | ### getRole 66 | 67 | **Signature:** `getRole() : String` 68 | 69 | Returns the role of the registrant or null. 70 | 71 | ### setEmail 72 | 73 | **Signature:** `setEmail(email : String) : void` 74 | 75 | Sets the email address of the registrant. 76 | 77 | ### setFirstName 78 | 79 | **Signature:** `setFirstName(firstName : String) : void` 80 | 81 | Sets the first name of the registrant. 82 | 83 | ### setLastName 84 | 85 | **Signature:** `setLastName(lastName : String) : void` 86 | 87 | Sets the last name of the registrant. 88 | 89 | ### setRole 90 | 91 | **Signature:** `setRole(role : String) : void` 92 | 93 | Sets the role of the registrant. 94 | 95 | ## Method Detail 96 | 97 | ## Method Details 98 | 99 | ### getEmail 100 | 101 | **Signature:** `getEmail() : String` 102 | 103 | **Description:** Returns the email address of the registrant or null. 104 | 105 | **Returns:** 106 | 107 | the email address of the registrant or null. 108 | 109 | --- 110 | 111 | ### getFirstName 112 | 113 | **Signature:** `getFirstName() : String` 114 | 115 | **Description:** Returns the first name of the registrant or null. 116 | 117 | **Returns:** 118 | 119 | the first name of the registrant or null. 120 | 121 | --- 122 | 123 | ### getLastName 124 | 125 | **Signature:** `getLastName() : String` 126 | 127 | **Description:** Returns the last name of the registrant or null. 128 | 129 | **Returns:** 130 | 131 | the last name of the registrant or null. 132 | 133 | --- 134 | 135 | ### getRole 136 | 137 | **Signature:** `getRole() : String` 138 | 139 | **Description:** Returns the role of the registrant or null. The role of a registrant can be for example the bride of a bridal couple. 140 | 141 | **Returns:** 142 | 143 | the role name of the registrant or null. 144 | 145 | --- 146 | 147 | ### setEmail 148 | 149 | **Signature:** `setEmail(email : String) : void` 150 | 151 | **Description:** Sets the email address of the registrant. 152 | 153 | **Parameters:** 154 | 155 | - `email`: the email address of the registrant. 156 | 157 | --- 158 | 159 | ### setFirstName 160 | 161 | **Signature:** `setFirstName(firstName : String) : void` 162 | 163 | **Description:** Sets the first name of the registrant. 164 | 165 | **Parameters:** 166 | 167 | - `firstName`: the first name of the registrant. 168 | 169 | --- 170 | 171 | ### setLastName 172 | 173 | **Signature:** `setLastName(lastName : String) : void` 174 | 175 | **Description:** Sets the last name of the registrant. 176 | 177 | **Parameters:** 178 | 179 | - `lastName`: the last name of the registrant. 180 | 181 | --- 182 | 183 | ### setRole 184 | 185 | **Signature:** `setRole(role : String) : void` 186 | 187 | **Description:** Sets the role of the registrant. 188 | 189 | **Parameters:** 190 | 191 | - `role`: the role of the registrant. 192 | 193 | --- ``` -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Publish to NPM 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | id-token: write 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v5 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | fetch-depth: 0 20 | 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v5 23 | with: 24 | node-version: '20' 25 | registry-url: 'https://registry.npmjs.org' 26 | cache: 'npm' 27 | 28 | - name: Configure Git 29 | run: | 30 | git config --global user.name "github-actions[bot]" 31 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 32 | 33 | - name: Install dependencies 34 | run: npm ci 35 | 36 | - name: Build project 37 | run: npm run build 38 | 39 | - name: Setup SFCC mock server 40 | run: | 41 | cd tests/servers/sfcc-mock-server 42 | npm install 43 | npm run setup:logs 44 | node server.js --port 3000 & 45 | sleep 5 46 | 47 | - name: Run tests 48 | run: npm test 49 | 50 | - name: Run linting 51 | run: npm run lint:check 52 | 53 | - name: Final build for publishing 54 | run: npm run build 55 | 56 | - name: Extract version and create release branch 57 | run: | 58 | # Extract version from the release tag (remove 'v' prefix if present) 59 | VERSION=${GITHUB_REF#refs/tags/} 60 | VERSION=${VERSION#v} 61 | echo "Setting package version to: $VERSION" 62 | 63 | # Create and switch to release branch 64 | BRANCH_NAME="release/v$VERSION" 65 | echo "Creating release branch: $BRANCH_NAME" 66 | git checkout -b $BRANCH_NAME 67 | 68 | # Update package version 69 | npm version $VERSION --no-git-tag-version 70 | 71 | # Commit the version update 72 | git add package.json package-lock.json 73 | git commit -m "chore: bump version to $VERSION for release" 74 | 75 | # Push the release branch 76 | git push origin $BRANCH_NAME 77 | 78 | - name: Stop SFCC mock server 79 | if: always() 80 | run: | 81 | # Kill SFCC mock server process 82 | pkill -f "node.*server.js" || true 83 | # Fallback: kill any remaining server processes on port 3000 84 | lsof -ti :3000 | xargs kill -9 2>/dev/null || true 85 | 86 | - name: Publish to NPM 87 | run: npm publish 88 | env: 89 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 90 | 91 | - name: Install MCP Publisher 92 | run: | 93 | curl -L "https://github.com/modelcontextprotocol/registry/releases/download/v1.0.0/mcp-publisher_1.0.0_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher 94 | 95 | - name: Update server.json version 96 | run: | 97 | # Extract version from the release tag (remove 'v' prefix if present) 98 | VERSION=${GITHUB_REF#refs/tags/} 99 | VERSION=${VERSION#v} 100 | echo "Updating server.json version to: $VERSION" 101 | 102 | # Update server.json version to match the release 103 | jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v' server.json > tmp && mv tmp server.json 104 | 105 | - name: Login to MCP Registry 106 | run: ./mcp-publisher login github-oidc 107 | 108 | - name: Publish to MCP Registry 109 | run: ./mcp-publisher publish 110 | ``` -------------------------------------------------------------------------------- /tests/servers/sfcc-mock-server/src/middleware/auth.js: -------------------------------------------------------------------------------- ```javascript 1 | /** 2 | * Authentication Middleware 3 | * 4 | * Handles OAuth token generation and validation for OCAPI endpoints. 5 | * Maintains simple in-memory token storage suitable for testing. 6 | */ 7 | 8 | class AuthenticationManager { 9 | constructor(config) { 10 | this.config = config; 11 | this.activeTokens = new Set(); 12 | this.validCredentials = config.validCredentials; 13 | } 14 | 15 | /** 16 | * Handle OAuth2 token requests 17 | */ 18 | handleOAuthToken(req, res) { 19 | const { grant_type } = req.body; 20 | 21 | // Validate grant type 22 | if (grant_type !== 'client_credentials' && 23 | grant_type !== 'urn:demandware:params:oauth:grant-type:client-id:dwsid:dwsecuretoken') { 24 | return res.status(400).json({ 25 | error: 'unsupported_grant_type', 26 | error_description: 'The authorization grant type is not supported' 27 | }); 28 | } 29 | 30 | // Extract credentials from Basic Auth header 31 | const authHeader = req.headers.authorization; 32 | if (!authHeader || !authHeader.startsWith('Basic ')) { 33 | return res.status(401).json({ 34 | error: 'invalid_client', 35 | error_description: 'Client authentication failed - missing Basic auth header' 36 | }); 37 | } 38 | 39 | const base64Credentials = authHeader.split(' ')[1]; 40 | const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); 41 | const [client_id, client_secret] = credentials.split(':'); 42 | 43 | // Validate credentials 44 | if (client_id !== this.validCredentials.clientId || 45 | client_secret !== this.validCredentials.clientSecret) { 46 | return res.status(401).json({ 47 | error: 'invalid_client', 48 | error_description: 'Client authentication failed' 49 | }); 50 | } 51 | 52 | // Generate mock access token 53 | const accessToken = `mock_token_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; 54 | this.activeTokens.add(accessToken); 55 | 56 | res.json({ 57 | access_token: accessToken, 58 | token_type: 'Bearer', 59 | expires_in: 3600, 60 | scope: 'SFCC_DATA_API' 61 | }); 62 | } 63 | 64 | /** 65 | * Middleware to require Bearer token authentication 66 | */ 67 | requireAuth() { 68 | return (req, res, next) => { 69 | const authHeader = req.headers.authorization; 70 | 71 | if (!authHeader || !authHeader.startsWith('Bearer ')) { 72 | return res.status(401).json({ 73 | error: 'unauthorized', 74 | message: 'Missing or invalid authorization header' 75 | }); 76 | } 77 | 78 | const token = authHeader.substring(7); 79 | 80 | if (!this.activeTokens.has(token)) { 81 | return res.status(401).json({ 82 | error: 'invalid_token', 83 | message: 'The access token is invalid or expired' 84 | }); 85 | } 86 | 87 | req.accessToken = token; 88 | next(); 89 | }; 90 | } 91 | 92 | /** 93 | * Clear expired tokens (for cleanup) 94 | */ 95 | clearExpiredTokens() { 96 | // For testing purposes, we'll keep tokens active 97 | // In a real implementation, you'd track token expiration 98 | } 99 | } 100 | 101 | module.exports = AuthenticationManager; ``` -------------------------------------------------------------------------------- /src/clients/docs/class-name-resolver.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Class Name Resolver 3 | * 4 | * Responsible for normalizing and resolving SFCC class names between 5 | * different formats (dot notation vs underscore notation) and extracting 6 | * simple class names from fully qualified names. 7 | * 8 | * Single Responsibility: Class name format conversion and resolution 9 | */ 10 | 11 | export class ClassNameResolver { 12 | /** 13 | * Normalize class name to handle both dot and underscore formats 14 | * Examples: 15 | * - dw.content.ContentMgr -> dw_content.ContentMgr 16 | * - dw_content.ContentMgr -> dw_content.ContentMgr (unchanged) 17 | * - ContentMgr -> ContentMgr (unchanged) 18 | */ 19 | static normalizeClassName(className: string): string { 20 | // If it contains dots but not underscores in the package part, convert dots to underscores 21 | if (className.includes('.') && !className.includes('_')) { 22 | // Split by dots and convert package parts (all but last) to use underscores 23 | const parts = className.split('.'); 24 | if (parts.length > 1) { 25 | const packageParts = parts.slice(0, -1); 26 | const simpleClassName = parts[parts.length - 1]; 27 | return `${packageParts.join('_')}.${simpleClassName}`; 28 | } 29 | } 30 | return className; 31 | } 32 | 33 | /** 34 | * Extract simple class name from full class name 35 | * Examples: 36 | * - dw_content.ContentMgr -> ContentMgr 37 | * - ContentMgr -> ContentMgr 38 | */ 39 | static extractSimpleClassName(className: string): string { 40 | const parts = className.split('.'); 41 | return parts[parts.length - 1]; 42 | } 43 | 44 | /** 45 | * Convert class names from internal format to official format 46 | * Examples: 47 | * - dw_content.ContentMgr -> dw.content.ContentMgr 48 | * - TopLevel.String -> String 49 | */ 50 | static toOfficialFormat(className: string): string { 51 | return className.replace(/_/g, '.'); 52 | } 53 | 54 | /** 55 | * Find class matches by simple class name 56 | * Useful when multiple packages contain classes with the same name 57 | */ 58 | static findClassMatches( 59 | targetClassName: string, 60 | classCache: Map<string, any>, 61 | ): Array<{ key: string; info: any }> { 62 | const normalizedTarget = this.normalizeClassName(targetClassName); 63 | const simpleTarget = this.extractSimpleClassName(normalizedTarget); 64 | 65 | return Array.from(classCache.entries()) 66 | .filter(([, info]) => info.className === simpleTarget) 67 | .map(([key, info]) => ({ key, info })); 68 | } 69 | 70 | /** 71 | * Resolve class name with fallback logic 72 | * First tries exact match, then falls back to simple name matching 73 | */ 74 | static resolveClassName( 75 | className: string, 76 | classCache: Map<string, any>, 77 | ): { key: string; info: any } | null { 78 | // Normalize class name to support both formats 79 | const normalizedClassName = this.normalizeClassName(className); 80 | 81 | // Try exact match first with normalized name 82 | const exactMatch = classCache.get(normalizedClassName); 83 | if (exactMatch) { 84 | return { key: normalizedClassName, info: exactMatch }; 85 | } 86 | 87 | // If not found, try to find by class name only (without package) 88 | const matches = this.findClassMatches(normalizedClassName, classCache); 89 | 90 | if (matches.length === 1) { 91 | return matches[0]; 92 | } else if (matches.length > 1) { 93 | const matchKeys = matches.map(({ key }) => key).join(', '); 94 | throw new Error(`Multiple classes found with name "${this.extractSimpleClassName(normalizedClassName)}": ${matchKeys}`); 95 | } 96 | 97 | return null; 98 | } 99 | } 100 | ``` -------------------------------------------------------------------------------- /docs/sfra/product-tile.md: -------------------------------------------------------------------------------- ```markdown 1 | # SFRA Product Tile Model 2 | 3 | ## Overview 4 | 5 | The Product Tile model represents a product in tile/grid view format, typically used in product listing pages, search results, and category pages. It provides essential product information optimized for display in compact tile formats. 6 | 7 | ## Module Function 8 | 9 | ```javascript 10 | module.exports = function productTile(product, apiProduct, productType) 11 | ``` 12 | 13 | Decorates a product object with product tile information using various decorators. 14 | 15 | ### Parameters 16 | 17 | - `product` (Object) - Product Model to be decorated 18 | - `apiProduct` (dw.catalog.Product) - Product information returned by the script API 19 | - `productType` (string) - Product type information ('product', 'variant', 'master', 'set', etc.) 20 | 21 | ### Returns 22 | 23 | Object - Decorated product model with tile-specific information 24 | 25 | ## Applied Decorators 26 | 27 | The product tile model applies the following decorators: 28 | 29 | ### base 30 | Adds fundamental product information including uuid, id, productName, productType, and brand. 31 | 32 | ### searchPrice 33 | Adds pricing information optimized for search results and product tiles: `price` property with promotional pricing. 34 | 35 | ### images 36 | Adds product images with the following configuration: 37 | - **Types:** ['medium'] - Medium-sized images suitable for tiles 38 | - **Quantity:** 'single' - Single image per type 39 | 40 | ### ratings 41 | Adds product rating information: `rating` value calculated from product ID. 42 | 43 | ### setProductsCollection (conditional) 44 | Applied only when `productType === 'set'`. Adds `numberOfProductsInSet` property with count of products in the set. 45 | 46 | ### searchVariationAttributes 47 | Adds variation attribute information: `variationAttributes` array optimized for search results and product listings (specifically color swatches). 48 | 49 | ## Usage Example 50 | 51 | ```javascript 52 | var productTileDecorator = require('*/cartridge/models/product/productTile'); 53 | var productFactory = require('*/cartridge/scripts/factories/product'); 54 | 55 | // Create base product model 56 | var product = productFactory.get({ pid: 'product-id' }); 57 | 58 | // Apply product tile decorations 59 | var tileProduct = productTileDecorator(product, apiProduct, 'product'); 60 | 61 | // Access tile-specific properties 62 | console.log(tileProduct.images.medium); 63 | console.log(tileProduct.price); 64 | console.log(tileProduct.ratings); 65 | ``` 66 | 67 | ## Typical Properties After Decoration 68 | 69 | After applying the product tile decorators, the product object typically contains: 70 | 71 | - **uuid** - Product UUID 72 | - **id** - Product ID 73 | - **productName** - Product name 74 | - **productType** - Product type 75 | - **brand** - Product brand 76 | - **price** - Pricing information with promotions (DefaultPrice or RangePrice) 77 | - **images** - Medium-sized product images 78 | - **rating** - Product rating value 79 | - **variationAttributes** - Color variation swatches and options 80 | - **numberOfProductsInSet** - Number of products in set (for product sets only) 81 | 82 | ## Notes 83 | 84 | - Optimized for performance in product listing scenarios 85 | - Uses medium-sized images to balance quality and load time 86 | - Includes promotional pricing for accurate display 87 | - Handles different product types (simple, master, variant, set) 88 | - Variation attributes are optimized for search/listing context 89 | 90 | ## Related Models 91 | 92 | - **Full Product Model** - More detailed product information 93 | - **Product Bundle Model** - For bundled products 94 | - **Product Set Model** - For product sets 95 | - **Product Decorators** - Individual decoration functions 96 | ``` -------------------------------------------------------------------------------- /docs/dw_catalog/ProductInventoryList.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.catalog 2 | 3 | # Class ProductInventoryList 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.PersistentObject 9 | - dw.object.ExtensibleObject 10 | - dw.catalog.ProductInventoryList 11 | 12 | ## Description 13 | 14 | The ProductInventoryList provides access to ID, description and defaultInStockFlag of the list. Furthermore inventory records can be accessed by product or product ID. When using Omnichannel Inventory (OCI): B2C Commerce uses ProductInventoryLists to reference and expose OCI Locations and Location Groups. They're required for synchronizing availability data and creating reservations. Create a ProductInventoryList in B2C Commerce for each OCI Location and Location Group that B2C Commerce will access. The ProductInventoryList ID must match the External Reference field on the corresponding Location or Location Group. A ProductInventoryList ID/External Reference must have between 2 and 128 characters (inclusive). It can include only lowercase letters, uppercase letters, digits, hyphens, and underscores. 15 | 16 | ## Properties 17 | 18 | ### defaultInStockFlag 19 | 20 | **Type:** boolean (Read Only) 21 | 22 | The default in-stock flag of the inventory list. 23 | 24 | ### description 25 | 26 | **Type:** String (Read Only) 27 | 28 | The description of the inventory list. 29 | 30 | ### ID 31 | 32 | **Type:** String (Read Only) 33 | 34 | The ID of the inventory list. 35 | 36 | ## Constructor Summary 37 | 38 | ## Method Summary 39 | 40 | ### getDefaultInStockFlag 41 | 42 | **Signature:** `getDefaultInStockFlag() : boolean` 43 | 44 | Returns the default in-stock flag of the inventory list. 45 | 46 | ### getDescription 47 | 48 | **Signature:** `getDescription() : String` 49 | 50 | Returns the description of the inventory list. 51 | 52 | ### getID 53 | 54 | **Signature:** `getID() : String` 55 | 56 | Returns the ID of the inventory list. 57 | 58 | ### getRecord 59 | 60 | **Signature:** `getRecord(product : Product) : ProductInventoryRecord` 61 | 62 | Returns the inventory record for the specified product or null if there is no record for the product in this list. 63 | 64 | ### getRecord 65 | 66 | **Signature:** `getRecord(productID : String) : ProductInventoryRecord` 67 | 68 | Returns the inventory record for the specified product ID or null if there is no record for the product id in this list. 69 | 70 | ## Method Detail 71 | 72 | ## Method Details 73 | 74 | ### getDefaultInStockFlag 75 | 76 | **Signature:** `getDefaultInStockFlag() : boolean` 77 | 78 | **Description:** Returns the default in-stock flag of the inventory list. 79 | 80 | **Returns:** 81 | 82 | Default in-stock flag of inventory list. 83 | 84 | --- 85 | 86 | ### getDescription 87 | 88 | **Signature:** `getDescription() : String` 89 | 90 | **Description:** Returns the description of the inventory list. 91 | 92 | **Returns:** 93 | 94 | Description of inventory list. 95 | 96 | --- 97 | 98 | ### getID 99 | 100 | **Signature:** `getID() : String` 101 | 102 | **Description:** Returns the ID of the inventory list. 103 | 104 | **Returns:** 105 | 106 | ID of inventory list. 107 | 108 | --- 109 | 110 | ### getRecord 111 | 112 | **Signature:** `getRecord(product : Product) : ProductInventoryRecord` 113 | 114 | **Description:** Returns the inventory record for the specified product or null if there is no record for the product in this list. 115 | 116 | **Parameters:** 117 | 118 | - `product`: The product to lookup inventory record. 119 | 120 | **Returns:** 121 | 122 | Inventory record or null if not found. 123 | 124 | --- 125 | 126 | ### getRecord 127 | 128 | **Signature:** `getRecord(productID : String) : ProductInventoryRecord` 129 | 130 | **Description:** Returns the inventory record for the specified product ID or null if there is no record for the product id in this list. 131 | 132 | **Parameters:** 133 | 134 | - `productID`: The product ID to lookup inventory record. 135 | 136 | **Returns:** 137 | 138 | Inventory record or null if not found. 139 | 140 | --- ``` -------------------------------------------------------------------------------- /docs/dw_web/FormElementValidationResult.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.web 2 | 3 | # Class FormElementValidationResult 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.web.FormElementValidationResult 9 | 10 | ## Description 11 | 12 | Represents a form element validation result. The validation script specified for form groups and fields can create such FormElementValidationResult with the desired validity, message and data and can then return it. The server side form element validation will evaluate these settings, i.e. calculate the corresponding element validity and message. The optional data provided with this instance will be kept and can be accessed again from the form element after server side validation. 13 | 14 | ## Properties 15 | 16 | ### data 17 | 18 | **Type:** Map (Read Only) 19 | 20 | Provides optional data acquired during validation. 21 | 22 | ### message 23 | 24 | **Type:** String 25 | 26 | Provides an optional message in case of validation failure. 27 | 28 | ### valid 29 | 30 | **Type:** boolean 31 | 32 | States if the validation succeeded or failed. 33 | 34 | ## Constructor Summary 35 | 36 | FormElementValidationResult(valid : boolean) Creates a FormElementValidationResult with given setting for the validity but without any message. 37 | 38 | FormElementValidationResult(valid : boolean, message : String) Creates a FormElementValidationResult with given setting for the validity and corresponding message. 39 | 40 | FormElementValidationResult(valid : boolean, message : String, data : Map) Creates a FormElementValidationResult with given setting for the validity and corresponding message. 41 | 42 | ## Method Summary 43 | 44 | ### addData 45 | 46 | **Signature:** `addData(key : Object, value : Object) : void` 47 | 48 | Adds optional data acquired during validation. 49 | 50 | ### getData 51 | 52 | **Signature:** `getData() : Map` 53 | 54 | Provides optional data acquired during validation. 55 | 56 | ### getMessage 57 | 58 | **Signature:** `getMessage() : String` 59 | 60 | Provides an optional message in case of validation failure. 61 | 62 | ### isValid 63 | 64 | **Signature:** `isValid() : boolean` 65 | 66 | States if the validation succeeded or failed. 67 | 68 | ### setMessage 69 | 70 | **Signature:** `setMessage(message : String) : void` 71 | 72 | Sets an optional message in case of validation failure. 73 | 74 | ### setValid 75 | 76 | **Signature:** `setValid(valid : boolean) : void` 77 | 78 | Sets if the validation succeeded or failed. 79 | 80 | ## Constructor Detail 81 | 82 | ## Method Detail 83 | 84 | ## Method Details 85 | 86 | ### addData 87 | 88 | **Signature:** `addData(key : Object, value : Object) : void` 89 | 90 | **Description:** Adds optional data acquired during validation. 91 | 92 | **Parameters:** 93 | 94 | - `key`: the key for which the data value will be stored 95 | - `value`: the data value that is stored for the given key 96 | 97 | --- 98 | 99 | ### getData 100 | 101 | **Signature:** `getData() : Map` 102 | 103 | **Description:** Provides optional data acquired during validation. 104 | 105 | **Returns:** 106 | 107 | the data acquired during validation 108 | 109 | --- 110 | 111 | ### getMessage 112 | 113 | **Signature:** `getMessage() : String` 114 | 115 | **Description:** Provides an optional message in case of validation failure. 116 | 117 | **Returns:** 118 | 119 | the message for validation failure 120 | 121 | --- 122 | 123 | ### isValid 124 | 125 | **Signature:** `isValid() : boolean` 126 | 127 | **Description:** States if the validation succeeded or failed. 128 | 129 | **Returns:** 130 | 131 | true if the validation succeeded 132 | 133 | --- 134 | 135 | ### setMessage 136 | 137 | **Signature:** `setMessage(message : String) : void` 138 | 139 | **Description:** Sets an optional message in case of validation failure. 140 | 141 | **Parameters:** 142 | 143 | - `message`: the message for validation failure 144 | 145 | --- 146 | 147 | ### setValid 148 | 149 | **Signature:** `setValid(valid : boolean) : void` 150 | 151 | **Description:** Sets if the validation succeeded or failed. 152 | 153 | **Parameters:** 154 | 155 | - `valid`: if the validation succeeded 156 | 157 | --- ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "sfcc-dev-mcp", 3 | "version": "1.0.14", 4 | "description": "MCP server for Salesforce B2C Commerce Cloud development assistance including logs, debugging, and development tools", 5 | "mcpName": "io.github.taurgis/sfcc-dev-mcp", 6 | "main": "dist/index.js", 7 | "bin": { 8 | "sfcc-dev-mcp": "./dist/main.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/taurgis/sfcc-dev-mcp.git" 13 | }, 14 | "homepage": "https://github.com/taurgis/sfcc-dev-mcp#readme", 15 | "bugs": { 16 | "url": "https://github.com/taurgis/sfcc-dev-mcp/issues" 17 | }, 18 | "scripts": { 19 | "build": "tsc && rm -rf ./dist/docs && cp -r ./docs ./dist/docs", 20 | "start": "node dist/main.js", 21 | "dev": "tsx src/main.ts", 22 | "inspector": "npx @modelcontextprotocol/inspector node dist/main.js", 23 | "convert-docs": "node scripts/convert-docs.js", 24 | "convert-docs:test": "node scripts/convert-docs.js --test", 25 | "convert-docs:limit": "node scripts/convert-docs.js --limit 5", 26 | "github-pages": "cd docs-site && rm -f Gemfile.lock && bundle install --path vendor/bundle && bundle exec jekyll serve --livereload --host=0.0.0.0", 27 | "test": "npm run test:all", 28 | "test:watch": "jest --watch", 29 | "test:coverage": "jest --coverage", 30 | "test:mcp:yaml": "aegis 'tests/mcp/yaml/*.docs-only.test.mcp.yml' --config './aegis.config.docs-only.json'", 31 | "test:mcp:yaml:full": "aegis 'tests/mcp/yaml/*.full-mode.test.mcp.yml' --config './aegis.config.with-dw.json'", 32 | "test:mcp:node": "node --test tests/mcp/node/*.programmatic.test.js", 33 | "test:mcp:all": "npm run test:mcp:yaml && npm run test:mcp:yaml:full && npm run test:mcp:node", 34 | "test:mcp:ci": "npm run test:mcp:yaml -- --json && npm run test:mcp:yaml:full -- --json && npm run test:mcp:node", 35 | "test:all": "jest && npm run test:mock-server:setup && npm run test:mcp:all", 36 | "test:mock-server:setup": "cd tests/servers/sfcc-mock-server && npm install && npm run setup", 37 | "test:mock-server:start": "cd tests/servers/sfcc-mock-server && npm start", 38 | "test:mock-server": "jest --testPathPatterns=sfcc-mock-server", 39 | "validate:server-json": "node -e \"const {readFileSync} = require('fs'); const serverJson = JSON.parse(readFileSync('./server.json', 'utf8')); console.log('server.json is valid JSON'); console.log('Server name:', serverJson.name); console.log('Version:', serverJson.version); console.log('Package identifier:', serverJson.packages[0].identifier);\"", 40 | "lint": "eslint .", 41 | "lint:fix": "eslint . --fix", 42 | "lint:check": "eslint . --max-warnings 0", 43 | "prepublishOnly": "npm run build", 44 | "prepare": "husky" 45 | }, 46 | "keywords": [ 47 | "mcp", 48 | "salesforce", 49 | "commerce-cloud", 50 | "sfcc", 51 | "development", 52 | "logs", 53 | "debugging", 54 | "webdav" 55 | ], 56 | "author": "Thomas Theunen <[email protected]>", 57 | "license": "MIT", 58 | "engines": { 59 | "node": ">=18", 60 | "npm": ">=8" 61 | }, 62 | "type": "module", 63 | "dependencies": { 64 | "@modelcontextprotocol/sdk": "1.18.0", 65 | "webdav": "5.8.0" 66 | }, 67 | "devDependencies": { 68 | "@eslint/js": "9.36.0", 69 | "@types/eslint": "9.6.1", 70 | "@types/jest": "30.0.0", 71 | "@types/node": "24.4.0", 72 | "ajv": "^8.17.1", 73 | "ajv-formats": "^3.0.1", 74 | "eslint": "9.36.0", 75 | "husky": "9.1.7", 76 | "jest": "30.1.3", 77 | "mcp-aegis": "1.0.18", 78 | "ts-jest": "29.4.4", 79 | "tsx": "4.20.5", 80 | "typescript": "5.9.2", 81 | "typescript-eslint": "8.44.0" 82 | } 83 | } 84 | ``` -------------------------------------------------------------------------------- /docs/sfra/content.md: -------------------------------------------------------------------------------- ```markdown 1 | # SFRA Content Model 2 | 3 | ## Overview 4 | 5 | The Content model represents a content asset in SFRA applications. It provides structured access to content information including body text, metadata, and rendering template information for content management and display. 6 | 7 | ## Constructor 8 | 9 | ```javascript 10 | function content(contentValue, renderingTemplate) 11 | ``` 12 | 13 | Creates a Content model instance from a content asset. 14 | 15 | ### Parameters 16 | 17 | - `contentValue` (dw.content.Content) - Result of ContentMgr.getContent call 18 | - `renderingTemplate` (string) - Optional rendering template for the content 19 | 20 | ### Returns 21 | 22 | Object - Content model instance, or null if content is not online 23 | 24 | ## Properties 25 | 26 | ### body 27 | **Type:** string | null 28 | 29 | The main content body text from the content asset's custom.body attribute. 30 | 31 | ### UUID 32 | **Type:** string 33 | 34 | Unique identifier for the content asset. 35 | 36 | ### ID 37 | **Type:** string 38 | 39 | Content asset ID for referencing and linking. 40 | 41 | ### name 42 | **Type:** string 43 | 44 | Display name of the content asset. 45 | 46 | ### template 47 | **Type:** string 48 | 49 | Rendering template path for the content. Uses either: 50 | - Content asset's specified template 51 | - Provided renderingTemplate parameter 52 | - Default: 'components/content/contentAssetInc' 53 | 54 | ### pageTitle 55 | **Type:** string 56 | 57 | SEO page title for the content asset. 58 | 59 | ### pageDescription 60 | **Type:** string 61 | 62 | SEO meta description for the content asset. 63 | 64 | ### pageKeywords 65 | **Type:** string 66 | 67 | SEO meta keywords for the content asset. 68 | 69 | ### pageMetaTags 70 | **Type:** Array 71 | 72 | Additional SEO meta tags for the content asset. 73 | 74 | ## Content Availability 75 | 76 | The model only processes online content assets. If `contentValue.online` is false, the constructor returns null instead of a content object. 77 | 78 | ## Template Resolution 79 | 80 | Template selection follows this priority: 81 | 1. Content asset's own template property 82 | 2. Provided renderingTemplate parameter 83 | 3. Default template: 'components/content/contentAssetInc' 84 | 85 | ## Usage Example 86 | 87 | ```javascript 88 | var ContentModel = require('*/cartridge/models/content'); 89 | var ContentMgr = require('dw/content/ContentMgr'); 90 | 91 | // Get content asset 92 | var contentAsset = ContentMgr.getContent('privacy-policy'); 93 | var customTemplate = 'pages/content/contentPage'; 94 | 95 | var content = new ContentModel(contentAsset, customTemplate); 96 | 97 | if (content) { 98 | // Access content properties 99 | console.log(content.name); // "Privacy Policy" 100 | console.log(content.ID); // "privacy-policy" 101 | console.log(content.body); // Content body HTML 102 | console.log(content.template); // Template path 103 | 104 | // Access SEO properties 105 | console.log(content.pageTitle); // SEO title 106 | console.log(content.pageDescription); // SEO description 107 | } else { 108 | console.log('Content is offline or not found'); 109 | } 110 | ``` 111 | 112 | ## SEO Support 113 | 114 | The model provides comprehensive SEO metadata: 115 | - **pageTitle** - For HTML title tag 116 | - **pageDescription** - For meta description 117 | - **pageKeywords** - For meta keywords 118 | - **pageMetaTags** - For additional custom meta tags 119 | 120 | ## Notes 121 | 122 | - Only processes online content assets 123 | - Provides flexible template resolution 124 | - Includes comprehensive SEO metadata 125 | - Handles missing content gracefully (returns null) 126 | - Body content comes from custom.body attribute 127 | - Template fallback ensures content can always be rendered 128 | 129 | ## Related Models 130 | 131 | - **Page Models** - May include content assets 132 | - **Search Models** - May return content in search results 133 | - **Category Models** - May reference related content 134 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: 📚 Documentation Issue 2 | description: Report issues with documentation or suggest documentation improvements 3 | title: "[Docs]: " 4 | labels: ["documentation", "needs-triage"] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for helping improve our documentation! Please provide details about the documentation issue or improvement. 11 | 12 | - type: dropdown 13 | id: doc-type 14 | attributes: 15 | label: Documentation Type 16 | description: What type of documentation is this about? 17 | options: 18 | - README.md 19 | - API Documentation (docs/ folder) 20 | - Best Practices Guides 21 | - Code Comments/JSDoc 22 | - Setup/Installation Instructions 23 | - Configuration Guide 24 | - Troubleshooting Guide 25 | - Contributing Guidelines 26 | - GitHub Templates 27 | - Other 28 | validations: 29 | required: true 30 | 31 | - type: dropdown 32 | id: issue-type 33 | attributes: 34 | label: Issue Type 35 | description: What kind of documentation issue is this? 36 | options: 37 | - Missing documentation 38 | - Incorrect/outdated information 39 | - Unclear or confusing content 40 | - Broken links or references 41 | - Formatting issues 42 | - Grammar/spelling errors 43 | - Missing examples 44 | - Inconsistent information 45 | - Accessibility issues 46 | - Other 47 | validations: 48 | required: true 49 | 50 | - type: textarea 51 | id: location 52 | attributes: 53 | label: Location 54 | description: Where is the documentation issue located? 55 | placeholder: | 56 | - File path: docs/best-practices/cartridge_creation.md 57 | - Section: "Setting up Authentication" 58 | - Line number: 45 59 | - URL: (if applicable) 60 | validations: 61 | required: true 62 | 63 | - type: textarea 64 | id: current-content 65 | attributes: 66 | label: Current Content (if applicable) 67 | description: Quote or describe the current problematic content 68 | render: markdown 69 | 70 | - type: textarea 71 | id: issue-description 72 | attributes: 73 | label: Issue Description 74 | description: Clearly describe what's wrong or what's missing 75 | placeholder: Explain what makes the current documentation problematic or what information is missing... 76 | validations: 77 | required: true 78 | 79 | - type: textarea 80 | id: suggested-improvement 81 | attributes: 82 | label: Suggested Improvement 83 | description: How should this be fixed or what should be added? 84 | placeholder: Provide your suggestion for improving the documentation... 85 | validations: 86 | required: true 87 | 88 | - type: textarea 89 | id: user-impact 90 | attributes: 91 | label: User Impact 92 | description: How does this documentation issue affect users? 93 | placeholder: | 94 | - Makes it difficult to get started 95 | - Causes confusion during setup 96 | - Leads to incorrect implementation 97 | - Wastes developer time... 98 | 99 | - type: dropdown 100 | id: audience 101 | attributes: 102 | label: Target Audience 103 | description: Who is primarily affected by this documentation issue? 104 | multiple: true 105 | options: 106 | - New users/first-time setup 107 | - SFCC developers 108 | - MCP server developers 109 | - Contributors 110 | - All users 111 | - Advanced users only 112 | 113 | - type: textarea 114 | id: additional-context 115 | attributes: 116 | label: Additional Context 117 | description: Any additional context, examples, or references that would help improve the documentation 118 | ``` -------------------------------------------------------------------------------- /docs/dw_util/Currency.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.util 2 | 3 | # Class Currency 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.util.Currency 9 | 10 | ## Description 11 | 12 | Represents a currency supported by the system. 13 | 14 | ## Properties 15 | 16 | ### currencyCode 17 | 18 | **Type:** String (Read Only) 19 | 20 | Gets the ISO 4217 mnemonic currency code of this currency. 21 | 22 | ### defaultFractionDigits 23 | 24 | **Type:** Number (Read Only) 25 | 26 | Gets the default number of fraction digits used with this currency. 27 | For example, the default number of fraction digits for the Euro is 2, 28 | while for the Japanese Yen it's 0. 29 | 30 | ### name 31 | 32 | **Type:** String (Read Only) 33 | 34 | Gets a long name for this currency. e.g. "United States Dollar". 35 | The returned name is the one stored in the system for this currency. 36 | Currently only English names are available, but in the future 37 | this method may return a locale-specific name. 38 | 39 | ### symbol 40 | 41 | **Type:** String (Read Only) 42 | 43 | Gets the symbol of this currency. e.g. "$" for the US Dollar. 44 | 45 | ## Constructor Summary 46 | 47 | ## Method Summary 48 | 49 | ### getCurrency 50 | 51 | **Signature:** `static getCurrency(currencyCode : String) : Currency` 52 | 53 | Returns a Currency instance for the given currency code, or null if there is no such currency. 54 | 55 | ### getCurrencyCode 56 | 57 | **Signature:** `getCurrencyCode() : String` 58 | 59 | Gets the ISO 4217 mnemonic currency code of this currency. 60 | 61 | ### getDefaultFractionDigits 62 | 63 | **Signature:** `getDefaultFractionDigits() : Number` 64 | 65 | Gets the default number of fraction digits used with this currency. 66 | 67 | ### getName 68 | 69 | **Signature:** `getName() : String` 70 | 71 | Gets a long name for this currency. 72 | 73 | ### getSymbol 74 | 75 | **Signature:** `getSymbol() : String` 76 | 77 | Gets the symbol of this currency. 78 | 79 | ### toString 80 | 81 | **Signature:** `toString() : String` 82 | 83 | Returns the ISO 4217 mnemonic currency code of this currency. 84 | 85 | ## Method Detail 86 | 87 | ## Method Details 88 | 89 | ### getCurrency 90 | 91 | **Signature:** `static getCurrency(currencyCode : String) : Currency` 92 | 93 | **Description:** Returns a Currency instance for the given currency code, or null if there is no such currency. 94 | 95 | **Parameters:** 96 | 97 | - `currencyCode`: the ISO 4217 mnemonic code of the currency. 98 | 99 | **Returns:** 100 | 101 | the Currency instance for the given currency code. 102 | 103 | --- 104 | 105 | ### getCurrencyCode 106 | 107 | **Signature:** `getCurrencyCode() : String` 108 | 109 | **Description:** Gets the ISO 4217 mnemonic currency code of this currency. 110 | 111 | **Returns:** 112 | 113 | the ISO 4217 mnemonic currency code of this currency. 114 | 115 | --- 116 | 117 | ### getDefaultFractionDigits 118 | 119 | **Signature:** `getDefaultFractionDigits() : Number` 120 | 121 | **Description:** Gets the default number of fraction digits used with this currency. For example, the default number of fraction digits for the Euro is 2, while for the Japanese Yen it's 0. 122 | 123 | **Returns:** 124 | 125 | the default number of fraction digits used with this currency. 126 | 127 | --- 128 | 129 | ### getName 130 | 131 | **Signature:** `getName() : String` 132 | 133 | **Description:** Gets a long name for this currency. e.g. "United States Dollar". The returned name is the one stored in the system for this currency. Currently only English names are available, but in the future this method may return a locale-specific name. 134 | 135 | **Returns:** 136 | 137 | a long name for this currency. e.g. "United States Dollar". 138 | 139 | --- 140 | 141 | ### getSymbol 142 | 143 | **Signature:** `getSymbol() : String` 144 | 145 | **Description:** Gets the symbol of this currency. e.g. "$" for the US Dollar. 146 | 147 | **Returns:** 148 | 149 | the symbol of this currency. 150 | 151 | --- 152 | 153 | ### toString 154 | 155 | **Signature:** `toString() : String` 156 | 157 | **Description:** Returns the ISO 4217 mnemonic currency code of this currency. 158 | 159 | **Returns:** 160 | 161 | the ISO 4217 mnemonic currency code of this currency. 162 | 163 | --- ``` -------------------------------------------------------------------------------- /src/tool-configs/sfra-tool-config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { GenericToolSpec, ToolExecutionContext } from '../core/handlers/base-handler.js'; 2 | import { ToolArguments } from '../core/handlers/base-handler.js'; 3 | import { ValidationHelpers, CommonValidations } from '../core/handlers/validation-helpers.js'; 4 | import { SFRAClient } from '../clients/sfra-client.js'; 5 | 6 | export const SFRA_TOOL_NAMES = [ 7 | 'get_available_sfra_documents', 8 | 'get_sfra_document', 9 | 'search_sfra_documentation', 10 | 'get_sfra_documents_by_category', 11 | 'get_sfra_categories', 12 | ] as const; 13 | 14 | export type SFRAToolName = typeof SFRA_TOOL_NAMES[number]; 15 | export const SFRA_TOOL_NAMES_SET = new Set<SFRAToolName>(SFRA_TOOL_NAMES); 16 | 17 | /** 18 | * Configuration for SFRA documentation tools 19 | * Maps each tool to its validation, execution, and messaging logic 20 | */ 21 | export const SFRA_TOOL_CONFIG: Record<SFRAToolName, GenericToolSpec<ToolArguments, any>> = { 22 | get_available_sfra_documents: { 23 | defaults: (args: ToolArguments) => args, 24 | validate: (_args: ToolArguments, _toolName: string) => { 25 | // No validation needed for list operation 26 | }, 27 | exec: async (_args: ToolArguments, context: ToolExecutionContext) => { 28 | const client = context.sfraClient as SFRAClient; 29 | return client.getAvailableDocuments(); 30 | }, 31 | logMessage: (_args: ToolArguments) => 'List SFRA docs', 32 | }, 33 | 34 | get_sfra_document: { 35 | defaults: (args: ToolArguments) => args, 36 | validate: (args: ToolArguments, toolName: string) => { 37 | ValidationHelpers.validateArguments(args, CommonValidations.requiredString('documentName'), toolName); 38 | }, 39 | exec: async (args: ToolArguments, context: ToolExecutionContext) => { 40 | const client = context.sfraClient as SFRAClient; 41 | const result = await client.getSFRADocument(args.documentName as string); 42 | if (!result) { 43 | throw new Error(`SFRA document "${args.documentName}" not found`); 44 | } 45 | return result; 46 | }, 47 | logMessage: (args: ToolArguments) => `SFRA doc ${args.documentName}`, 48 | }, 49 | 50 | search_sfra_documentation: { 51 | defaults: (args: ToolArguments) => args, 52 | validate: (args: ToolArguments, toolName: string) => { 53 | ValidationHelpers.validateArguments(args, CommonValidations.requiredString('query'), toolName); 54 | }, 55 | exec: async (args: ToolArguments, context: ToolExecutionContext) => { 56 | const client = context.sfraClient as SFRAClient; 57 | return client.searchSFRADocumentation(args.query as string); 58 | }, 59 | logMessage: (args: ToolArguments) => `Search SFRA ${args.query}`, 60 | }, 61 | 62 | get_sfra_documents_by_category: { 63 | defaults: (args: ToolArguments) => args, 64 | validate: (args: ToolArguments, toolName: string) => { 65 | ValidationHelpers.validateArguments(args, CommonValidations.requiredString('category'), toolName); 66 | }, 67 | exec: async (args: ToolArguments, context: ToolExecutionContext) => { 68 | const client = context.sfraClient as SFRAClient; 69 | return client.getDocumentsByCategory(args.category as string); 70 | }, 71 | logMessage: (args: ToolArguments) => `SFRA docs by category ${args.category}`, 72 | }, 73 | 74 | get_sfra_categories: { 75 | defaults: (args: ToolArguments) => args, 76 | validate: (_args: ToolArguments, _toolName: string) => { 77 | // No validation needed for list operation 78 | }, 79 | exec: async (_args: ToolArguments, context: ToolExecutionContext) => { 80 | const client = context.sfraClient as SFRAClient; 81 | return client.getAvailableCategories(); 82 | }, 83 | logMessage: (_args: ToolArguments) => 'SFRA categories', 84 | }, 85 | }; 86 | ``` -------------------------------------------------------------------------------- /docs/dw_util/Template.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.util 2 | 3 | # Class Template 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.util.Template 9 | 10 | ## Description 11 | 12 | Reads an ISML template from the file system and renders it into a MimeEncodedText object. Optional substitution values can be passed to the isml template via the render(Map) method. Substitution parameters can be accessed within the template through <isprint value="${param.parameter}"> or for backward compatibility through <isprint value="${pdict.parameter}"> The access through pdict only gives access to the parameter map provided at rendering time and doesn't offer access to the system PipelineDictionary. The pdict access to the property map is only considered to ease the transition from SendMail pipelet API based templates. If the PipelineDictionary or properties of the PipelineDictionary are needed, they need to be included in the Property map passed to the render method. 13 | 14 | ## Constructor Summary 15 | 16 | Template(templateName : String) Creates a new template. 17 | 18 | Template(templateName : String, localeID : String) Creates a new template with the locale being set to the given localeID. 19 | 20 | ## Method Summary 21 | 22 | ### render 23 | 24 | **Signature:** `render() : MimeEncodedText` 25 | 26 | Renders the template specified at instantiation time, without any substitution parameters. 27 | 28 | ### render 29 | 30 | **Signature:** `render(params : Map) : MimeEncodedText` 31 | 32 | Renders the template specified at instantiation time with the given substitution parameters. 33 | 34 | ### setLocale 35 | 36 | **Signature:** `setLocale(localeID : String) : Template` 37 | 38 | Sets an optional localeID which is used instead of the current requests localeID. 39 | 40 | ## Constructor Detail 41 | 42 | ## Method Detail 43 | 44 | ## Method Details 45 | 46 | ### render 47 | 48 | **Signature:** `render() : MimeEncodedText` 49 | 50 | **Description:** Renders the template specified at instantiation time, without any substitution parameters. Any isprint tags referring to param/pdict will be unresolved and will be replaced with empty strings. If there's an explicit localeID set through setLocale(String), it takes precedence over the localeID associated with the current request. 51 | 52 | **Returns:** 53 | 54 | MimeEncodedText with isprint tags referring to param/pdict replaced with an empty String 55 | 56 | --- 57 | 58 | ### render 59 | 60 | **Signature:** `render(params : Map) : MimeEncodedText` 61 | 62 | **Description:** Renders the template specified at instantiation time with the given substitution parameters. These parameters are available to ISML templates through variables named 'param' and 'pdict'. Note that in this context, pdict is not referring to the system PipelineDictionary, as the System Pipeline Dictionary is not accessible from this script API. If there's an explicit localeID set through setLocale(String), it takes precedence over the localeID associated with the current request. 63 | 64 | **Parameters:** 65 | 66 | - `params`: Map of substitution parameters which are specified within the ISML template. Access is available from within the ISML template through named variables param or pdict. 67 | 68 | **Returns:** 69 | 70 | MimeEncodedText containing the rendered template. Variables in the template referring to param/pdict are replaced with the value from the params map or empty if the value isn't found in the map 71 | 72 | --- 73 | 74 | ### setLocale 75 | 76 | **Signature:** `setLocale(localeID : String) : Template` 77 | 78 | **Description:** Sets an optional localeID which is used instead of the current requests localeID. 79 | 80 | **Parameters:** 81 | 82 | - `localeID`: to be used for processing this template. Throws an exception if localeID is blank 83 | 84 | **Returns:** 85 | 86 | this Template object 87 | 88 | --- ``` -------------------------------------------------------------------------------- /docs/dw_system/SearchStatus.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.system 2 | 3 | # Class SearchStatus 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.system.SearchStatus 9 | 10 | ## Description 11 | 12 | A SearchStatus is used for communicating a Search API status back to a client. A status consists of status code and description. More information about search API call can be fetched by using SearchStatus class method getStatusCode and getDescription, which can be used by clients to perform different operations. 13 | 14 | ## Constants 15 | 16 | ### EMPTY_QUERY 17 | 18 | **Type:** Number = 6 19 | 20 | EMPTY_QUERY search result status code 6, this indicates that search has been made with empty query. 21 | 22 | ### ERROR 23 | 24 | **Type:** Number = 9 25 | 26 | ERROR search result status code 9, this indicates that internal server error has been occurred. 27 | 28 | ### LIMITED 29 | 30 | **Type:** Number = 2 31 | 32 | LIMITED search result status code 2, this indicates that limitations on search result have been applied and full search result is not returned. 33 | 34 | ### NO_CATALOG 35 | 36 | **Type:** Number = 4 37 | 38 | NO_CATALOG search result status code 4, this indicates that there is no catalog associated for search query. 39 | 40 | ### NO_CATEGORY 41 | 42 | **Type:** Number = 5 43 | 44 | NO_CATEGORY search result status code 5, this indicates that there is no category associated for search query. 45 | 46 | ### NO_INDEX 47 | 48 | **Type:** Number = 8 49 | 50 | NO_INDEX search result status code 8, this indicates that there is no active search index available. 51 | 52 | ### NOT_EXECUTED 53 | 54 | **Type:** Number = 0 55 | 56 | NOT_EXECUTED search result status code 0, this indicates that search API call has not been made on SearchModel. 57 | 58 | ### OFFLINE_CATEGORY 59 | 60 | **Type:** Number = 7 61 | 62 | OFFLINE_CATEGORY search result status code 7, this indicates that the category associated with search query is offline. 63 | 64 | ### ROOT_SEARCH 65 | 66 | **Type:** Number = 3 67 | 68 | ROOT_SEARCH search result status code 3, this indicates that search result is returned for ROOT search. 69 | 70 | ### SUCCESSFUL 71 | 72 | **Type:** Number = 1 73 | 74 | SUCCESSFUL search result status code 1, this indicates that search API call is executed without any issue. 75 | 76 | ## Properties 77 | 78 | ### description 79 | 80 | **Type:** String (Read Only) 81 | 82 | Returns status code description of search result, it provides more details about search API call status. 83 | 84 | ### statusCode 85 | 86 | **Type:** Number (Read Only) 87 | 88 | Returns status code of search result, by default it will return 0 which means that search has not been executed 89 | on SearchModel. 90 | 91 | ## Constructor Summary 92 | 93 | ## Method Summary 94 | 95 | ### getDescription 96 | 97 | **Signature:** `getDescription() : String` 98 | 99 | Returns status code description of search result, it provides more details about search API call status. 100 | 101 | ### getStatusCode 102 | 103 | **Signature:** `getStatusCode() : Number` 104 | 105 | Returns status code of search result, by default it will return 0 which means that search has not been executed on SearchModel. 106 | 107 | ### toString 108 | 109 | **Signature:** `toString() : String` 110 | 111 | Returns string values of status code and description. 112 | 113 | ## Method Detail 114 | 115 | ## Method Details 116 | 117 | ### getDescription 118 | 119 | **Signature:** `getDescription() : String` 120 | 121 | **Description:** Returns status code description of search result, it provides more details about search API call status. 122 | 123 | **Returns:** 124 | 125 | search status description 126 | 127 | --- 128 | 129 | ### getStatusCode 130 | 131 | **Signature:** `getStatusCode() : Number` 132 | 133 | **Description:** Returns status code of search result, by default it will return 0 which means that search has not been executed on SearchModel. 134 | 135 | **Returns:** 136 | 137 | search status code 138 | 139 | --- 140 | 141 | ### toString 142 | 143 | **Signature:** `toString() : String` 144 | 145 | **Description:** Returns string values of status code and description. 146 | 147 | **Returns:** 148 | 149 | search status string 150 | 151 | --- ``` -------------------------------------------------------------------------------- /docs/dw_object/CustomAttributes.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.object 2 | 3 | # Class CustomAttributes 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.object.CustomAttributes 9 | 10 | ## Description 11 | 12 | This class is used together with other classes that contain custom attributes and is used to read and write these attributes. The actual attributes are accessible as ECMA properties. The syntax for setting and retrieving the value of a custom attribute depends upon the type of the attribute. If the wrong syntax is used to set an individual attribute than an exception will be thrown. The following script examples demonstrate how to work with custom attributes. Suppose we have an ExtensibleObject named "eo" possessing attributes of all the different types supported by the Commerce Cloud Digital metadata system. The following script snippet shows that setting single-valued attributes is simply a matter of using the assignment operator and standard ECMA primitives and built-in types: // attribute of value type 'Boolean' eo.custom.bvalue = true; var b : Boolean = eo.custom.bvalue; // attribute of value type 'Integer' eo.custom.ivalue = 10; var i : Number = eo.custom.ivalue; // attribute of value type 'Number' eo.custom.dvalue = 99.99; var d : Number = eo.custom.dvalue; // attribute of value type 'String' eo.custom.svalue = "String1"; var s : String = eo.custom.svalue; // attribute of value type 'Email' eo.custom.emailvalue = "[email protected]"; var e : String = eo.custom.emailvalue; // attribute of value type 'Text' eo.custom.tvalue = "laaaaaaaaaaaarge text"; var t : String = eo.custom.tvalue; // attribute of value type 'Date' eo.custom.dtvalue = new Date; var date : Date = eo.custom.dtvalue; Setting and retrieving the values for multi-value attributes is also straightforward and uses ECMA arrays to represent the multiple values. Set-of attributes and enum-of attributes are handled in a very similar manner. The chief difference is that enum-of attributes are limited to a prescribed set of value definitions whereas set-of attributes are open-ended. Furthermore, each value in an enum-of attribute has a value and a display name which affects the retrieval logic. Multi-value attributes are returned as an array. This array is read-only and can't be used to update the multi-value attribute. To update the multi-value attribute an array with new values must be assigned to the attribute. // attribute of value type 'Set of String' // set the attribute value only if it hasn't been already set if( !('setofstringvalue' in eo.custom) ) { eo.custom.setofstringvalue = new Array("abc","def","ghi"); } // returns an Array of String instances var setofstring : Array = eo.custom.setofstringvalue; var s1 : String = setofstring[0]; var s2 : String = setofstring[1]; var s3 : String = setofstring[2]; // attribute of value type 'Enum of Integer' with multi-value handling eo.custom.enumofintmultivalue = new Array(1, 2, 3); // returns an Array of EnumValue instances var enumofintmulti : Array = eo.custom.enumofintmultivalue; var value1 : Number = enumofintmulti[0].getValue(); var displayvalue1 : String = enumofintmulti[0].getDisplayValue(); var value2 : Number = enumofintmulti[1].getValue(); var displayvalue2 : String = enumofintmulti[1].getDisplayValue(); var value3 : Number = enumofintmulti[2].getValue(); var displayvalue3 : String = enumofintmulti[2].getDisplayValue(); For further details on the Commerce Cloud Digital attribute system, see the core Commerce Cloud Digital documentation. 13 | 14 | ## Constructor Summary 15 | 16 | ## Method Summary ``` -------------------------------------------------------------------------------- /docs/dw_util/SortedMap.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.util 2 | 3 | # Class SortedMap 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.util.Map 9 | - dw.util.SortedMap 10 | 11 | ## Description 12 | 13 | A map that further guarantees that it will be in ascending key order, sorted according to the natural ordering of its keys, or by a comparator provided at sorted map creation time. This order is reflected when iterating over the sorted map's collection views (returned by the entrySet, keySet and values methods). Note that sorting by natural order is only supported for Number, String, Date, Money and Quantity as key. 14 | 15 | ## Constructor Summary 16 | 17 | SortedMap() Constructor to create a new SortedMap. 18 | 19 | SortedMap(comparator : Object) Constructor to create a new SortedMap. 20 | 21 | ## Method Summary 22 | 23 | ### clone 24 | 25 | **Signature:** `clone() : SortedMap` 26 | 27 | Returns a shallow copy of this map. 28 | 29 | ### firstKey 30 | 31 | **Signature:** `firstKey() : Object` 32 | 33 | Returns the first (lowest) key currently in this sorted map. 34 | 35 | ### headMap 36 | 37 | **Signature:** `headMap(key : Object) : SortedMap` 38 | 39 | Returns a view of the portion of this map whose keys are strictly less than toKey. 40 | 41 | ### lastKey 42 | 43 | **Signature:** `lastKey() : Object` 44 | 45 | Returns the last (highest) key currently in this sorted map. 46 | 47 | ### subMap 48 | 49 | **Signature:** `subMap(from : Object, to : Object) : SortedMap` 50 | 51 | Returns a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive. 52 | 53 | ### tailMap 54 | 55 | **Signature:** `tailMap(key : Object) : SortedMap` 56 | 57 | Returns a view of the portion of this map whose keys are greater than or equal to fromKey. 58 | 59 | ## Constructor Detail 60 | 61 | ## Method Detail 62 | 63 | ## Method Details 64 | 65 | ### clone 66 | 67 | **Signature:** `clone() : SortedMap` 68 | 69 | **Description:** Returns a shallow copy of this map. 70 | 71 | **Returns:** 72 | 73 | a shallow copy of this map. 74 | 75 | --- 76 | 77 | ### firstKey 78 | 79 | **Signature:** `firstKey() : Object` 80 | 81 | **Description:** Returns the first (lowest) key currently in this sorted map. 82 | 83 | **Returns:** 84 | 85 | the first (lowest) key currently in this sorted map. 86 | 87 | --- 88 | 89 | ### headMap 90 | 91 | **Signature:** `headMap(key : Object) : SortedMap` 92 | 93 | **Description:** Returns a view of the portion of this map whose keys are strictly less than toKey. 94 | 95 | **Parameters:** 96 | 97 | - `key`: high endpoint (exclusive) of the headMap. 98 | 99 | **Returns:** 100 | 101 | a view of the portion of this map whose keys are strictly less than toKey. 102 | 103 | --- 104 | 105 | ### lastKey 106 | 107 | **Signature:** `lastKey() : Object` 108 | 109 | **Description:** Returns the last (highest) key currently in this sorted map. 110 | 111 | **Returns:** 112 | 113 | the last (highest) key currently in this sorted map. 114 | 115 | --- 116 | 117 | ### subMap 118 | 119 | **Signature:** `subMap(from : Object, to : Object) : SortedMap` 120 | 121 | **Description:** Returns a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive. (If fromKey and toKey are equal, the returned sorted map is empty.) 122 | 123 | **Parameters:** 124 | 125 | - `from`: low endpoint (inclusive) of the subMap. 126 | - `to`: high endpoint (exclusive) of the subMap. 127 | 128 | **Returns:** 129 | 130 | a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive. 131 | 132 | --- 133 | 134 | ### tailMap 135 | 136 | **Signature:** `tailMap(key : Object) : SortedMap` 137 | 138 | **Description:** Returns a view of the portion of this map whose keys are greater than or equal to fromKey. The returned sorted map is backed by this map, so changes in the returned sorted map are reflected in this map, and vice-versa. The returned sorted map supports all optional map operations. 139 | 140 | **Parameters:** 141 | 142 | - `key`: low endpoint (inclusive) of the tailMap. 143 | 144 | **Returns:** 145 | 146 | a view of the portion of this map whose keys are greater than or equal to fromKey. 147 | 148 | --- ``` -------------------------------------------------------------------------------- /docs/dw_system/Transaction.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.system 2 | 3 | # Class Transaction 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.system.Transaction 9 | 10 | ## Description 11 | 12 | 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 }); 13 | 14 | ## Constructor Summary 15 | 16 | ## Method Summary 17 | 18 | ### begin 19 | 20 | **Signature:** `static begin() : void` 21 | 22 | Begins a transaction. 23 | 24 | ### commit 25 | 26 | **Signature:** `static commit() : void` 27 | 28 | Commits the current transaction. 29 | 30 | ### rollback 31 | 32 | **Signature:** `static rollback() : void` 33 | 34 | Rolls back the current transaction. 35 | 36 | ### wrap 37 | 38 | **Signature:** `static wrap(callback : Function) : Object` 39 | 40 | Encloses the provided callback function in a begin-commit transactional context. 41 | 42 | ## Method Detail 43 | 44 | ## Method Details 45 | 46 | ### begin 47 | 48 | **Signature:** `static begin() : void` 49 | 50 | **Description:** Begins a transaction. 51 | 52 | --- 53 | 54 | ### commit 55 | 56 | **Signature:** `static commit() : void` 57 | 58 | **Description:** Commits the current transaction. The transaction must have been started with begin() before. 59 | 60 | --- 61 | 62 | ### rollback 63 | 64 | **Signature:** `static rollback() : void` 65 | 66 | **Description:** Rolls back the current transaction. The transaction must have been started with begin() before. 67 | 68 | --- 69 | 70 | ### wrap 71 | 72 | **Signature:** `static wrap(callback : Function) : Object` 73 | 74 | **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. 75 | 76 | **Parameters:** 77 | 78 | - `callback`: a function that should be executed within a transactional context 79 | 80 | **Returns:** 81 | 82 | the result of the callback function, if it returns something 83 | 84 | --- ``` -------------------------------------------------------------------------------- /docs/dw_customer/AgentUserMgr.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Package: dw.customer 2 | 3 | # Class AgentUserMgr 4 | 5 | ## Inheritance Hierarchy 6 | 7 | - Object 8 | - dw.customer.AgentUserMgr 9 | 10 | ## Description 11 | 12 | Provides helper methods for handling agent user functionality (login and logout) Pay attention to appropriate legal and regulatory requirements related to this functionality. 13 | 14 | ## Constructor Summary 15 | 16 | ## Method Summary 17 | 18 | ### loginAgentUser 19 | 20 | **Signature:** `static loginAgentUser(login : String, password : String) : Status` 21 | 22 | Logs in an agent user (which for example is authorized to login on-behalf of a customer for instance to place an order). 23 | 24 | ### loginOnBehalfOfCustomer 25 | 26 | **Signature:** `static loginOnBehalfOfCustomer(customer : Customer) : Status` 27 | 28 | 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. 29 | 30 | ### logoutAgentUser 31 | 32 | **Signature:** `static logoutAgentUser() : Status` 33 | 34 | Performs a logout of the agent user and the current customer which are attached to the current session. 35 | 36 | ## Method Detail 37 | 38 | ## Method Details 39 | 40 | ### loginAgentUser 41 | 42 | **Signature:** `static loginAgentUser(login : String, password : String) : Status` 43 | 44 | **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. 45 | 46 | **Parameters:** 47 | 48 | - `login`: the login name for the agent user. 49 | - `password`: the password for the agent user. 50 | 51 | **Returns:** 52 | 53 | the login status (OK if successful, error code otherwise). 54 | 55 | --- 56 | 57 | ### loginOnBehalfOfCustomer 58 | 59 | **Signature:** `static loginOnBehalfOfCustomer(customer : Customer) : Status` 60 | 61 | **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' 62 | 63 | **Parameters:** 64 | 65 | - `customer`: The customer, which should be logged instead of the agent user 66 | 67 | **Returns:** 68 | 69 | the login status (OK if successful, error code otherwise). 70 | 71 | --- 72 | 73 | ### logoutAgentUser 74 | 75 | **Signature:** `static logoutAgentUser() : Status` 76 | 77 | **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. 78 | 79 | **Returns:** 80 | 81 | the logout status (OK if successful, error code otherwise). 82 | 83 | --- ``` -------------------------------------------------------------------------------- /docs/sfra/price-default.md: -------------------------------------------------------------------------------- ```markdown 1 | # SFRA Default Price Model 2 | 3 | ## Overview 4 | 5 | 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. 6 | 7 | ## Constructor 8 | 9 | ```javascript 10 | function DefaultPrice(salesPrice, listPrice) 11 | ``` 12 | 13 | Creates a Default Price model instance with sales and list pricing information. 14 | 15 | ### Parameters 16 | 17 | - `salesPrice` (dw.value.Money) - Sales price from the API 18 | - `listPrice` (dw.value.Money) - List price from the API (optional) 19 | 20 | ## Properties 21 | 22 | ### sales 23 | **Type:** Object 24 | 25 | Sales price information containing: 26 | - `value` (number | null) - Decimal price value 27 | - `currency` (string | null) - Currency code 28 | - `formatted` (string | null) - Formatted price string for display 29 | - `decimalPrice` (string | null) - Decimal price as string 30 | 31 | ### list 32 | **Type:** Object | null 33 | 34 | List price information (same structure as sales) or null if no list price provided. 35 | 36 | ## Helper Functions 37 | 38 | ### toPriceModel(price) 39 | Converts an API Money object to a structured price model. 40 | 41 | **Parameters:** 42 | - `price` (dw.value.Money) - Price object from the API 43 | 44 | **Returns:** Object - Formatted price object with value, currency, formatted string, and decimal price 45 | 46 | ## Price Object Structure 47 | 48 | Each price object (sales/list) contains: 49 | 50 | ### value 51 | Raw numeric price value for calculations and comparisons. 52 | 53 | ### currency 54 | Three-letter currency code (e.g., "USD", "EUR"). 55 | 56 | ### formatted 57 | User-friendly formatted price string with currency symbol (e.g., "$29.99"). 58 | 59 | ### decimalPrice 60 | String representation of the decimal price for precise display. 61 | 62 | ## Usage Example 63 | 64 | ```javascript 65 | var DefaultPrice = require('*/cartridge/models/price/default'); 66 | 67 | // Get prices from product 68 | var product = ProductMgr.getProduct('product-id'); 69 | var salesPrice = product.getPriceModel().getPrice(); 70 | var listPrice = product.getPriceModel().getPriceBookPrice('list-price-book'); 71 | 72 | var price = new DefaultPrice(salesPrice, listPrice); 73 | 74 | // Access sales price 75 | console.log(price.sales.formatted); // "$29.99" 76 | console.log(price.sales.value); // 29.99 77 | console.log(price.sales.currency); // "USD" 78 | 79 | // Check for list price (original/MSRP) 80 | if (price.list) { 81 | console.log('Was: ' + price.list.formatted); 82 | console.log('Now: ' + price.sales.formatted); 83 | 84 | // Calculate discount percentage 85 | var discount = ((price.list.value - price.sales.value) / price.list.value) * 100; 86 | console.log('Save ' + discount.toFixed(0) + '%'); 87 | } 88 | ``` 89 | 90 | ## Price Availability 91 | 92 | The model handles unavailable prices gracefully: 93 | - When price is not available, all properties return null 94 | - Prevents errors when price calculation fails 95 | - Provides consistent structure regardless of price availability 96 | 97 | ## Currency Support 98 | 99 | The model supports international pricing: 100 | - Currency codes from the Money object 101 | - Formatted strings respect locale settings 102 | - Decimal representation for precise calculations 103 | 104 | ## Notes 105 | 106 | - Handles both sales and list prices 107 | - Gracefully manages unavailable prices with null values 108 | - Provides multiple price formats for different use cases 109 | - Supports international currencies and formatting 110 | - List price is optional (may be null) 111 | - Decimal price provides precise string representation 112 | 113 | ## Related Models 114 | 115 | - **Tiered Price Model** - For quantity-based pricing 116 | - **Range Price Model** - For price range displays 117 | - **Product Models** - Use price models for product pricing 118 | - **Cart Models** - Use price models for line item pricing 119 | ```