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

--------------------------------------------------------------------------------
/tests/mcp/node/get-latest-info.full-mode.programmatic.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { test, describe, before, after, beforeEach } from 'node:test';
  2 | import { strict as assert } from 'node:assert';
  3 | import { connect } from 'mcp-aegis';
  4 | 
  5 | describe('get_latest_info Tool - Advanced Programmatic Tests (Full Mode)', () => {
  6 |   let client;
  7 | 
  8 |   before(async () => {
  9 |     client = await connect('./aegis.config.with-dw.json');
 10 |   });
 11 | 
 12 |   after(async () => {
 13 |     if (client?.connected) {
 14 |       await client.disconnect();
 15 |     }
 16 |   });
 17 | 
 18 |   beforeEach(() => {
 19 |     // CRITICAL: Clear all buffers to prevent leaking into next tests
 20 |     client.clearAllBuffers();
 21 |   });
 22 | 
 23 |   // Helper function to get current date in YYYYMMDD format
 24 |   function getCurrentDateString() {
 25 |     const now = new Date();
 26 |     const year = now.getFullYear();
 27 |     const month = String(now.getMonth() + 1).padStart(2, '0');
 28 |     const day = String(now.getDate()).padStart(2, '0');
 29 |     return `${year}${month}${day}`;
 30 |   }
 31 | 
 32 |   // ========================================
 33 |   // COMPLEX BUSINESS LOGIC VALIDATION
 34 |   // ========================================
 35 | 
 36 |   describe('Complex Business Logic Validation', () => {
 37 |     test('should return info messages in chronological order with detailed parsing', async () => {
 38 |       const result = await client.callTool('get_latest_info', { limit: 2 });
 39 |       
 40 |       assertValidMCPResponse(result);
 41 |       const text = result.content[0].text;
 42 |       
 43 |       // Complex chronological order validation - requires detailed parsing and comparison
 44 |       const jobExecution = 'Executing job [sfcc-export-dw-analytics-site-config][2664334]';
 45 |       const stepExecution = 'Executing step [ExportDWAnalyticsSiteConfigurationStep][5846619] for [Organization]';
 46 |       
 47 |       assert.ok(text.includes(jobExecution), 'Should contain job execution entry');
 48 |       assert.ok(text.includes(stepExecution), 'Should contain step execution entry');
 49 |       
 50 |       // Verify chronological ordering through position analysis
 51 |       const jobIndex = text.indexOf(jobExecution);
 52 |       const stepIndex = text.indexOf(stepExecution);
 53 |       assert.ok(jobIndex < stepIndex && jobIndex !== -1 && stepIndex !== -1,
 54 |         'Newest info (job) should appear before older info (step) in response');
 55 |     });
 56 | 
 57 |     test('should validate business logic for SFCC info-level log filtering', async () => {
 58 |       const result = await client.callTool('get_latest_info', { limit: 10 });
 59 |       
 60 |       assertValidMCPResponse(result);
 61 |       const logText = result.content[0].text;
 62 |       
 63 |       // Complex business logic validation - SFCC-specific log filtering
 64 |       assert.ok(logText.includes('info-blade'), 'Should reference info log files specifically');
 65 |       assert.ok(logText.includes('INFO'), 'Should contain INFO log level entries');
 66 |       
 67 |       // Critical: Verify log level isolation (complex filtering validation)
 68 |       assert.ok(!logText.includes('ERROR'), 'Should not contain ERROR entries');
 69 |       assert.ok(!logText.includes('WARN'), 'Should not contain WARN entries');
 70 |       assert.ok(!logText.includes('DEBUG'), 'Should not contain DEBUG entries');
 71 |     });
 72 | 
 73 |     test('should parse and validate complex SFCC log entry structure', async () => {
 74 |       const result = await client.callTool('get_latest_info', { limit: 1 });
 75 |       
 76 |       assertValidMCPResponse(result);
 77 |       const logText = result.content[0].text;
 78 |       
 79 |       // Complex log structure parsing and validation
 80 |       const timestampPattern = /\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} GMT\]/;
 81 |       const sfccPattern = /(SystemJobThread|PipelineCallServlet|Sites-)/;
 82 |       
 83 |       assert.ok(timestampPattern.test(logText), 'Should contain valid SFCC timestamp format');
 84 |       assert.ok(sfccPattern.test(logText), 'Should contain SFCC-specific system components');
 85 |       
 86 |       // Complex structure validation
 87 |       assert.ok(logText.includes('['), 'Should contain log file reference brackets');
 88 |       assert.ok(logText.includes('] ['), 'Should contain proper timestamp delimiters');
 89 |       assert.ok(logText.includes('GMT]'), 'Should contain GMT timezone specification');
 90 |     });
 91 |   });
 92 | 
 93 |   // ========================================
 94 |   // DYNAMIC PARAMETER VALIDATION
 95 |   // ========================================
 96 | 
 97 |   describe('Dynamic Parameter Validation', () => {
 98 |     test('should validate parameter types dynamically with complex logic', async () => {
 99 |       const testCases = [
100 |         { params: { limit: null }, shouldSucceed: false, description: 'null limit' },
101 |         { params: { limit: undefined }, shouldSucceed: true, description: 'undefined limit (uses default)' },
102 |         { params: { limit: [] }, shouldSucceed: false, description: 'array limit' },
103 |         { params: { limit: {} }, shouldSucceed: false, description: 'object limit' },
104 |         { params: { date: null }, shouldSucceed: true, description: 'null date (uses default)' },
105 |         { params: { date: 123 }, shouldSucceed: true, description: 'numeric date (handled gracefully)' }
106 |       ];
107 | 
108 |       // Complex dynamic validation with detailed result analysis
109 |       for (const testCase of testCases) {
110 |         const result = await client.callTool('get_latest_info', testCase.params);
111 |         assertValidMCPResponse(result);
112 |         
113 |         if (testCase.shouldSucceed) {
114 |           assert.ok(result.content.length > 0, 
115 |             `Should have content for ${testCase.description}: ${JSON.stringify(testCase.params)}`);
116 |         } else {
117 |           // Complex validation: either error response or graceful handling
118 |           assert.ok(result.isError || result.content.length > 0,
119 |             `Should either error or handle gracefully for ${testCase.description}`);
120 |         }
121 |       }
122 |     });
123 | 
124 |     test('should handle complex boundary condition matrix', async () => {
125 |       const boundaryTests = [
126 |         { limit: 1, shouldSucceed: true, category: 'minimum valid' },
127 |         { limit: 1000, shouldSucceed: true, category: 'maximum valid' },
128 |         { limit: 1001, shouldSucceed: false, category: 'over maximum' },
129 |         { limit: 0, shouldSucceed: false, category: 'zero (invalid)' },
130 |         { limit: -1, shouldSucceed: false, category: 'negative' }
131 |       ];
132 |       
133 |       // Complex boundary validation with categorization
134 |       for (const test of boundaryTests) {
135 |         const result = await client.callTool('get_latest_info', { limit: test.limit });
136 |         assertValidMCPResponse(result);
137 |         
138 |         if (test.shouldSucceed) {
139 |           assert.equal(result.isError, false, 
140 |             `Boundary test ${test.category} (limit: ${test.limit}) should succeed`);
141 |         } else {
142 |           assert.equal(result.isError, true, 
143 |             `Boundary test ${test.category} (limit: ${test.limit}) should fail validation`);
144 |         }
145 |       }
146 |     });
147 |   });
148 | 
149 |   // ========================================
150 |   // MULTI-STEP WORKFLOWS AND CONSISTENCY
151 |   // ========================================
152 | 
153 |   describe('Multi-Step Workflows and Consistency', () => {
154 |     test('should maintain consistency across sequential operations', async () => {
155 |       const results = [];
156 |       
157 |       // Complex multi-step workflow simulation
158 |       for (let i = 0; i < 5; i++) {
159 |         const result = await client.callTool('get_latest_info', { limit: 5 });
160 |         results.push(result);
161 |       }
162 |       
163 |       // Complex consistency validation across multiple calls
164 |       results.forEach((result, index) => {
165 |         assertValidMCPResponse(result);
166 |         assert.equal(result.isError, false, `Sequential call ${index + 1} should succeed`);
167 |         assert.ok(result.content[0].text.includes('Latest 5 info messages'), 
168 |           `Sequential call ${index + 1} should show consistent limit`);
169 |       });
170 |       
171 |       // Complex cross-result consistency validation
172 |       const firstText = results[0].content[0].text;
173 |       const lastText = results[results.length - 1].content[0].text;
174 |       assert.equal(firstText, lastText, 'Sequential calls should return identical results for same parameters');
175 |     });
176 | 
177 |     test('should handle complex parameter combination workflows', async () => {
178 |       const paramCombinations = [
179 |         {},
180 |         { limit: 5 },
181 |         { date: getCurrentDateString() },
182 |         { limit: 3, date: getCurrentDateString() },
183 |         { limit: 1 },
184 |         { limit: 50 }
185 |       ];
186 |       
187 |       // Complex workflow validation with parameter combinations
188 |       for (const params of paramCombinations) {
189 |         const result = await client.callTool('get_latest_info', params);
190 |         assertValidMCPResponse(result);
191 |         
192 |         // Complex validation logic for parameter combinations
193 |         const hasValidLimit = !params.limit || (params.limit > 0 && params.limit <= 1000);
194 |         if (hasValidLimit) {
195 |           assert.equal(result.isError, false, 
196 |             `Valid parameter combination should succeed: ${JSON.stringify(params)}`);
197 |           
198 |           // Verify expected limit is reflected in response
199 |           const expectedLimit = params.limit || 10; // default limit
200 |           assert.ok(result.content[0].text.includes(`Latest ${expectedLimit} info messages`),
201 |             `Should show correct limit for combination: ${JSON.stringify(params)}`);
202 |         }
203 |       }
204 |     });
205 | 
206 |     test('should validate complex limit range processing', async () => {
207 |       const limitTests = [1, 5, 10, 25, 50, 100];
208 |       
209 |       // Complex range validation with detailed analysis
210 |       for (const limit of limitTests) {
211 |         const result = await client.callTool('get_latest_info', { limit });
212 |         
213 |         assertValidMCPResponse(result);
214 |         assert.equal(result.isError, false, `Limit ${limit} should process successfully`);
215 |         assert.ok(result.content[0].text.includes(`Latest ${limit} info messages`), 
216 |           `Should correctly process limit ${limit}`);
217 |         
218 |         // Complex content length analysis
219 |         const logText = result.content[0].text;
220 |         const separatorCount = (logText.match(/---/g) || []).length;
221 |         
222 |         // Validate separator count matches expected entries (limit - 1 separators for limit entries)
223 |         if (limit > 1) {
224 |           assert.ok(separatorCount >= 1, 
225 |             `Should have separators for limit ${limit} (found ${separatorCount})`);
226 |         }
227 |       }
228 |     });
229 |   });
230 | 
231 |   // ========================================
232 |   // ERROR RECOVERY AND RESILIENCE
233 |   // ========================================
234 | 
235 |   describe('Error Recovery and Resilience', () => {
236 |     test('should demonstrate complex error recovery workflow', async () => {
237 |       // Complex error recovery scenario with state validation
238 |       
239 |       // Step 1: Trigger error condition
240 |       const errorResult = await client.callTool('get_latest_info', { limit: -1 });
241 |       assert.equal(errorResult.isError, true, 'Should error with negative limit');
242 |       
243 |       // Step 2: Verify normal operation resumes immediately
244 |       const normalResult = await client.callTool('get_latest_info', { limit: 5 });
245 |       assertValidMCPResponse(normalResult);
246 |       assert.equal(normalResult.isError, false, 'Should recover and work normally');
247 |       
248 |       // Step 3: Verify complex recovery with different parameters
249 |       const recoveryResult = await client.callTool('get_latest_info', { 
250 |         limit: 3, 
251 |         date: getCurrentDateString() 
252 |       });
253 |       assertValidMCPResponse(recoveryResult);
254 |       assert.equal(recoveryResult.isError, false, 'Should handle complex parameters after error');
255 |     });
256 | 
257 |     test('should maintain state integrity after complex invalid operations', async () => {
258 |       // Complex state integrity validation
259 |       const invalidOperations = [
260 |         { limit: -10, description: 'negative limit' },
261 |         { limit: 'abc', description: 'string limit' },
262 |         { date: 'invalid', description: 'invalid date' },
263 |         { limit: null, description: 'null limit' },
264 |         { limit: 1001, description: 'over-limit' }
265 |       ];
266 |       
267 |       // Execute complex series of invalid operations
268 |       for (const invalidOp of invalidOperations) {
269 |         await client.callTool('get_latest_info', invalidOp);
270 |         // Note: Don't assert on individual results as some may be handled gracefully
271 |       }
272 |       
273 |       // Complex state validation after error series
274 |       const result = await client.callTool('get_latest_info', { limit: 3 });
275 |       assertValidMCPResponse(result);
276 |       assert.equal(result.isError, false, 'Should work normally after series of invalid operations');
277 |       assert.ok(result.content[0].text.includes('Latest 3 info messages'),
278 |         'Should produce correct output after error recovery');
279 |     });
280 |   });
281 | 
282 |   // ========================================
283 |   // INTEGRATION AND PROTOCOL COMPLIANCE
284 |   // ========================================
285 | 
286 |   describe('Integration and Protocol Compliance', () => {
287 |     test('should integrate properly with MCP protocol and tool discovery', async () => {
288 |       // Complex MCP protocol compliance validation
289 |       const tools = await client.listTools();
290 |       const infoTool = tools.find(tool => tool.name === 'get_latest_info');
291 |       
292 |       assert.ok(infoTool, 'get_latest_info tool should be discoverable via MCP protocol');
293 |       assert.ok(infoTool.description, 'Tool should have comprehensive description');
294 |       assert.ok(infoTool.inputSchema, 'Tool should have valid input schema');
295 |       
296 |       // Complex schema validation
297 |       const schema = infoTool.inputSchema;
298 |       assert.equal(schema.type, 'object', 'Input schema should be object type');
299 |       assert.ok(schema.properties, 'Schema should define properties');
300 |       
301 |       // Verify tool execution works through MCP protocol
302 |       const result = await client.callTool('get_latest_info', {});
303 |       assertValidMCPResponse(result);
304 |       assert.equal(result.isError, false, 'Tool should execute successfully via MCP protocol');
305 |     });
306 | 
307 |     test('should validate complex date parameter integration', async () => {
308 |       // Complex date parameter validation with current date integration
309 |       const testDate = getCurrentDateString();
310 |       
311 |       const result = await client.callTool('get_latest_info', { 
312 |         date: testDate, 
313 |         limit: 2 
314 |       });
315 |       
316 |       assertValidMCPResponse(result);
317 |       assert.equal(result.isError, false, 'Should handle current date parameter');
318 |       assert.ok(result.content[0].text.includes('Latest 2 info messages'), 
319 |         'Should show correct limit with date parameter');
320 |       
321 |       // Complex validation: date parameter should not affect content structure
322 |       const defaultResult = await client.callTool('get_latest_info', { limit: 2 });
323 |       assertValidMCPResponse(defaultResult);
324 |       
325 |       // Compare structure consistency
326 |       const dateTextLines = result.content[0].text.split('\n').length;
327 |       const defaultTextLines = defaultResult.content[0].text.split('\n').length;
328 |       assert.ok(Math.abs(dateTextLines - defaultTextLines) <= 1,
329 |         'Date parameter should not significantly alter response structure');
330 |     });
331 |   });
332 | });
333 | 
334 | // ========================================
335 | // HELPER FUNCTIONS
336 | // ========================================
337 | 
338 | /**
339 |  * Validates that a result follows the MCP response structure
340 |  */
341 | function assertValidMCPResponse(result) {
342 |   assert.ok(result, 'Result should exist');
343 |   assert.ok(result.content, 'Result should have content');
344 |   assert.ok(Array.isArray(result.content), 'Content should be array');
345 |   assert.equal(typeof result.isError, 'boolean', 'isError should be boolean');
346 |   assert.ok(result.content.length > 0, 'Content should not be empty');
347 | }
348 | 
```

--------------------------------------------------------------------------------
/docs/dw_order/ShippingOrder.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.order
  2 | 
  3 | # Class ShippingOrder
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.object.Extensible
  9 |   - dw.order.AbstractItemCtnr
 10 |     - dw.order.ShippingOrder
 11 | 
 12 | ## Description
 13 | 
 14 | A shipping order is used to specify items that should be shipped, and is typically exported to, and updated by a back-office warehouse management system. An Order can have n shipping orders expressing how the order is to be shipped. The creation, export and update of shipping orders is largely handled by custom logic in scripts by implementing ShippingOrderHooks. Use method Order.createShippingOrder() for creation and add items using createShippingOrderItem(OrderItem, Quantity) - each item is related to an order item which in turn represents a product- or shipping- line item in the order. A shipping order has a status calculated from its item status, one of CONFIRMED - shipping order not yet exported, with 0 items, or all items in status CONFIRMED. WAREHOUSE - shipping order exported, with all items in status WAREHOUSE. SHIPPED - exported shipping order has been updated, with 1-n items in status SHIPPED and 0-n CANCELLED. CANCELLED - exported shipping order has been updated, with all items in status CANCELLED. The following status transitions are supported. Every status transition is documented by the addition of an order note such as 'Shipping order 123456 status changed to WAREHOUSE.': From To When Use CONFIRMED WAREHOUSE Shipping order exported Call setStatusWarehouse() - note this is the only way to set the items to status WAREHOUSE WAREHOUSE SHIPPED One or more items have been SHIPPED Call ShippingOrderItem.setStatus(String) using ShippingOrderItem.STATUS_SHIPPED WAREHOUSE CANCELLED All items have been CANCELLED Call ShippingOrderItem.setStatus(String) using ShippingOrderItem.STATUS_CANCELLED Order post-processing APIs (gillian) are now inactive by default and will throw an exception if accessed. Activation needs preliminary approval by Product Management. Please contact support in this case. Existing customers using these APIs are not affected by this change and can use the APIs until further notice.
 15 | 
 16 | ## Constants
 17 | 
 18 | ### ORDERBY_ITEMID
 19 | 
 20 | **Type:** Object
 21 | 
 22 | Sorting by item id. Use with method getItems() as an argument to method FilteringCollection.sort(Object).
 23 | 
 24 | ### ORDERBY_ITEMPOSITION
 25 | 
 26 | **Type:** Object
 27 | 
 28 | Sorting by the position of the related oder item. Use with method getItems() as an argument to method FilteringCollection.sort(Object).
 29 | 
 30 | ### ORDERBY_UNSORTED
 31 | 
 32 | **Type:** Object
 33 | 
 34 | Unsorted , as it is. Use with method getItems() as an argument to method FilteringCollection.sort(Object).
 35 | 
 36 | ### QUALIFIER_PRODUCTITEMS
 37 | 
 38 | **Type:** Object
 39 | 
 40 | Selects the product items. Use with method getItems() as an argument to method FilteringCollection.select(Object).
 41 | 
 42 | ### QUALIFIER_SERVICEITEMS
 43 | 
 44 | **Type:** Object
 45 | 
 46 | Selects for the service items. Use with method getItems() as an argument to method FilteringCollection.select(Object).
 47 | 
 48 | ### STATUS_CANCELLED
 49 | 
 50 | **Type:** String = "CANCELLED"
 51 | 
 52 | Constant for Shipping Order Status CANCELLED
 53 | 
 54 | ### STATUS_CONFIRMED
 55 | 
 56 | **Type:** String = "CONFIRMED"
 57 | 
 58 | Constant for Shipping Order Status CONFIRMED
 59 | 
 60 | ### STATUS_SHIPPED
 61 | 
 62 | **Type:** String = "SHIPPED"
 63 | 
 64 | Constant for Shipping Order Status SHIPPED
 65 | 
 66 | ### STATUS_WAREHOUSE
 67 | 
 68 | **Type:** String = "WAREHOUSE"
 69 | 
 70 | Constant for Shipping Order Status WAREHOUSE
 71 | 
 72 | ## Properties
 73 | 
 74 | ### invoice
 75 | 
 76 | **Type:** Invoice (Read Only)
 77 | 
 78 | Returns null or the previously created Invoice.
 79 | 
 80 | ### invoiceNumber
 81 | 
 82 | **Type:** String (Read Only)
 83 | 
 84 | Returns null or the invoice-number.
 85 | 
 86 | ### items
 87 | 
 88 | **Type:** FilteringCollection (Read Only)
 89 | 
 90 | A filtering collection of the shipping order items belonging to the
 91 |  shipping order.
 92 |  
 93 |  This FilteringCollection could be sorted / filtered
 94 |  using:
 95 |  
 96 |  FilteringCollection.sort(Object) with
 97 |  ORDERBY_ITEMID
 98 |  FilteringCollection.sort(Object) with
 99 |  ORDERBY_ITEMPOSITION
100 |  FilteringCollection.sort(Object) with
101 |  ORDERBY_UNSORTED
102 |  FilteringCollection.select(Object) with
103 |  QUALIFIER_PRODUCTITEMS
104 |  FilteringCollection.select(Object) with
105 |  QUALIFIER_SERVICEITEMS
106 | 
107 | ### shipDate
108 | 
109 | **Type:** Date
110 | 
111 | Gets the shipping date.
112 |  
113 |  Returns null if this shipping order is not yet shipped.
114 | 
115 | ### shippingAddress
116 | 
117 | **Type:** OrderAddress
118 | 
119 | The shipping address (optional, can be null).
120 | 
121 |  
122 |  Note: the shipping address is not copied into the
123 |  ShippingOrder but is a link to a
124 |  OrderAddress held in the Order.
125 | 
126 | ### shippingMethod
127 | 
128 | **Type:** ShippingMethod (Read Only)
129 | 
130 | The shipping method of the shipping order.
131 |  
132 |  Can be null.
133 | 
134 | ### shippingOrderNumber
135 | 
136 | **Type:** String (Read Only)
137 | 
138 | Gets the shipping order number.
139 | 
140 | ### status
141 | 
142 | **Type:** EnumValue (Read Only)
143 | 
144 | Gets the status of this shipping order. The status is read-only and
145 |  calculated from the item status. See class documentation for more
146 |  details.
147 |  The possible values are STATUS_CONFIRMED,
148 |  STATUS_WAREHOUSE, STATUS_SHIPPED,
149 |  STATUS_CANCELLED.
150 | 
151 | ### trackingInfos
152 | 
153 | **Type:** Collection (Read Only)
154 | 
155 | Gets all tracking informations for this shipping order.
156 | 
157 | ## Constructor Summary
158 | 
159 | ## Method Summary
160 | 
161 | ### addTrackingInfo
162 | 
163 | **Signature:** `addTrackingInfo(trackingInfoID : String) : TrackingInfo`
164 | 
165 | Adds a tracking info to this shipping order with the given ID.
166 | 
167 | ### createInvoice
168 | 
169 | **Signature:** `createInvoice() : Invoice`
170 | 
171 | Creates a new Invoice based on this ShippingOrder.
172 | 
173 | ### createInvoice
174 | 
175 | **Signature:** `createInvoice(invoiceNumber : String) : Invoice`
176 | 
177 | Creates a new Invoice based on this ShippingOrder.
178 | 
179 | ### createShippingOrderItem
180 | 
181 | **Signature:** `createShippingOrderItem(orderItem : OrderItem, quantity : Quantity) : ShippingOrderItem`
182 | 
183 | Create a ShippingOrderItem in the shipping order with the number shippingOrderNumber.
184 | 
185 | ### createShippingOrderItem
186 | 
187 | **Signature:** `createShippingOrderItem(orderItem : OrderItem, quantity : Quantity, splitIfPartial : boolean) : ShippingOrderItem`
188 | 
189 | Create a ShippingOrderItem in the shipping order with the number shippingOrderNumber.
190 | 
191 | ### getInvoice
192 | 
193 | **Signature:** `getInvoice() : Invoice`
194 | 
195 | Returns null or the previously created Invoice.
196 | 
197 | ### getInvoiceNumber
198 | 
199 | **Signature:** `getInvoiceNumber() : String`
200 | 
201 | Returns null or the invoice-number.
202 | 
203 | ### getItems
204 | 
205 | **Signature:** `getItems() : FilteringCollection`
206 | 
207 | A filtering collection of the shipping order items belonging to the shipping order.
208 | 
209 | ### getShipDate
210 | 
211 | **Signature:** `getShipDate() : Date`
212 | 
213 | Gets the shipping date.
214 | 
215 | ### getShippingAddress
216 | 
217 | **Signature:** `getShippingAddress() : OrderAddress`
218 | 
219 | Returns the shipping address (optional, can be null).
220 | 
221 | ### getShippingMethod
222 | 
223 | **Signature:** `getShippingMethod() : ShippingMethod`
224 | 
225 | Returns the shipping method of the shipping order.
226 | 
227 | ### getShippingOrderNumber
228 | 
229 | **Signature:** `getShippingOrderNumber() : String`
230 | 
231 | Gets the shipping order number.
232 | 
233 | ### getStatus
234 | 
235 | **Signature:** `getStatus() : EnumValue`
236 | 
237 | Gets the status of this shipping order.
238 | 
239 | ### getTrackingInfo
240 | 
241 | **Signature:** `getTrackingInfo(trackingInfoID : String) : TrackingInfo`
242 | 
243 | Gets a tracking info for this shipping order.
244 | 
245 | ### getTrackingInfos
246 | 
247 | **Signature:** `getTrackingInfos() : Collection`
248 | 
249 | Gets all tracking informations for this shipping order.
250 | 
251 | ### setShipDate
252 | 
253 | **Signature:** `setShipDate(date : Date) : void`
254 | 
255 | Sets the shipping date.
256 | 
257 | ### setShippingAddress
258 | 
259 | **Signature:** `setShippingAddress(address : OrderAddress) : void`
260 | 
261 | Set a shipping address for the shipping order.
262 | 
263 | ### setShippingMethodID
264 | 
265 | **Signature:** `setShippingMethodID(shippingMethodID : String) : void`
266 | 
267 | Set the id of shipping method.
268 | 
269 | ### setStatusWarehouse
270 | 
271 | **Signature:** `setStatusWarehouse() : void`
272 | 
273 | Set a CONFIRMED shipping order (all items in status CONFIRMED) to status WAREHOUSE (all items in status WAREHOUSE). Note - this method is the only way to transition a shipping order from CONFIRMED to WAREHOUSE.
274 | 
275 | ## Method Detail
276 | 
277 | ## Method Details
278 | 
279 | ### addTrackingInfo
280 | 
281 | **Signature:** `addTrackingInfo(trackingInfoID : String) : TrackingInfo`
282 | 
283 | **Description:** Adds a tracking info to this shipping order with the given ID.
284 | 
285 | **Parameters:**
286 | 
287 | - `trackingInfoID`: the tracking info id
288 | 
289 | **Returns:**
290 | 
291 | the new tracking info
292 | 
293 | **See Also:**
294 | 
295 | TrackingInfo
296 | 
297 | ---
298 | 
299 | ### createInvoice
300 | 
301 | **Signature:** `createInvoice() : Invoice`
302 | 
303 | **Description:** Creates a new Invoice based on this ShippingOrder. The shipping-order-number will be used as the invoice-number. The Invoice can then be accessed using getInvoice() or getInvoiceNumber() can be used. The method must not be called more than once for a ShippingOrder, nor may 2 Invoices exist with the same invoice-number. The new Invoice is a debit-invoice with a status Invoice.STATUS_NOT_PAID, and will be passed to the capture payment-hook in a separate database transaction for processing.
304 | 
305 | **Returns:**
306 | 
307 | new invoice
308 | 
309 | ---
310 | 
311 | ### createInvoice
312 | 
313 | **Signature:** `createInvoice(invoiceNumber : String) : Invoice`
314 | 
315 | **Description:** Creates a new Invoice based on this ShippingOrder. The invoice-number must be specified as an argument.The Invoice can then be accessed using getInvoice() or getInvoiceNumber() can be used. The method must not be called more than once for a ShippingOrder, nor may 2 Invoices exist with the same invoice-number. The new Invoice is a debit-invoice with a status Invoice.STATUS_NOT_PAID, and will be passed to the capture payment-hook in a separate database transaction for processing.
316 | 
317 | **Parameters:**
318 | 
319 | - `invoiceNumber`: the invoice-number to use
320 | 
321 | **Returns:**
322 | 
323 | new invoice
324 | 
325 | ---
326 | 
327 | ### createShippingOrderItem
328 | 
329 | **Signature:** `createShippingOrderItem(orderItem : OrderItem, quantity : Quantity) : ShippingOrderItem`
330 | 
331 | **Description:** Create a ShippingOrderItem in the shipping order with the number shippingOrderNumber. The quantity of the new item can be optionally specified. A quantity of null indicates the new item should be based on the entire order item and is recommended for ShippingLineItems. If a quantity is specified for a ProductLineItem which is less than ProductLineItem.getQuantity() the ProductLineItem will be split, creating a new ProductLineItem. The new ShippingOrderItem will be associated with the new ProductLineItem, which will receive the specified quantity. See also createShippingOrderItem(OrderItem, Quantity, Boolean).
332 | 
333 | **Parameters:**
334 | 
335 | - `orderItem`: the order item for which the shipping order item is to be created
336 | - `quantity`: the quantity for which the shipping order item will be created
337 | 
338 | **Returns:**
339 | 
340 | the created shipping order item
341 | 
342 | ---
343 | 
344 | ### createShippingOrderItem
345 | 
346 | **Signature:** `createShippingOrderItem(orderItem : OrderItem, quantity : Quantity, splitIfPartial : boolean) : ShippingOrderItem`
347 | 
348 | **Description:** Create a ShippingOrderItem in the shipping order with the number shippingOrderNumber. The quantity of the new item can be optionally specified. A quantity of null indicates the new item should be based on the entire order item and is recommended for ShippingLineItems. If the specified quantity is less than ProductLineItem.getQuantity() the ProductLineItem will be split or not depending on splitIfPartial parameter. When split is true, the method is equivalent to createShippingOrderItem(OrderItem, Quantity).
349 | 
350 | **Parameters:**
351 | 
352 | - `orderItem`: the order item for which the shipping order item is to be created
353 | - `quantity`: the quantity for which the shipping order item will be created, not null
354 | - `splitIfPartial`: the flag whether ProductLineItem should be split when requested quantity is less than ProductLineItem's quantity
355 | 
356 | **Returns:**
357 | 
358 | the created shipping order item
359 | 
360 | ---
361 | 
362 | ### getInvoice
363 | 
364 | **Signature:** `getInvoice() : Invoice`
365 | 
366 | **Description:** Returns null or the previously created Invoice.
367 | 
368 | **Returns:**
369 | 
370 | null or the previously created invoice.
371 | 
372 | **See Also:**
373 | 
374 | createInvoice(String)
375 | 
376 | ---
377 | 
378 | ### getInvoiceNumber
379 | 
380 | **Signature:** `getInvoiceNumber() : String`
381 | 
382 | **Description:** Returns null or the invoice-number.
383 | 
384 | **Returns:**
385 | 
386 | null or the previously created invoice number.
387 | 
388 | **See Also:**
389 | 
390 | createInvoice(String)
391 | 
392 | ---
393 | 
394 | ### getItems
395 | 
396 | **Signature:** `getItems() : FilteringCollection`
397 | 
398 | **Description:** A filtering collection of the shipping order items belonging to the shipping order. This FilteringCollection could be sorted / filtered using: FilteringCollection.sort(Object) with ORDERBY_ITEMID FilteringCollection.sort(Object) with ORDERBY_ITEMPOSITION FilteringCollection.sort(Object) with ORDERBY_UNSORTED FilteringCollection.select(Object) with QUALIFIER_PRODUCTITEMS FilteringCollection.select(Object) with QUALIFIER_SERVICEITEMS
399 | 
400 | **Returns:**
401 | 
402 | the filtering collection of the shipping items.
403 | 
404 | **See Also:**
405 | 
406 | createShippingOrderItem(OrderItem, Quantity)
407 | ShippingOrderItem
408 | 
409 | ---
410 | 
411 | ### getShipDate
412 | 
413 | **Signature:** `getShipDate() : Date`
414 | 
415 | **Description:** Gets the shipping date. Returns null if this shipping order is not yet shipped.
416 | 
417 | **Returns:**
418 | 
419 | the shipping date or null
420 | 
421 | ---
422 | 
423 | ### getShippingAddress
424 | 
425 | **Signature:** `getShippingAddress() : OrderAddress`
426 | 
427 | **Description:** Returns the shipping address (optional, can be null). Note: the shipping address is not copied into the ShippingOrder but is a link to a OrderAddress held in the Order.
428 | 
429 | **Returns:**
430 | 
431 | the shipping address or null
432 | 
433 | ---
434 | 
435 | ### getShippingMethod
436 | 
437 | **Signature:** `getShippingMethod() : ShippingMethod`
438 | 
439 | **Description:** Returns the shipping method of the shipping order. Can be null.
440 | 
441 | **Returns:**
442 | 
443 | the shipping method or null
444 | 
445 | ---
446 | 
447 | ### getShippingOrderNumber
448 | 
449 | **Signature:** `getShippingOrderNumber() : String`
450 | 
451 | **Description:** Gets the shipping order number.
452 | 
453 | **Returns:**
454 | 
455 | the shipping order number
456 | 
457 | ---
458 | 
459 | ### getStatus
460 | 
461 | **Signature:** `getStatus() : EnumValue`
462 | 
463 | **Description:** Gets the status of this shipping order. The status is read-only and calculated from the item status. See class documentation for more details. The possible values are STATUS_CONFIRMED, STATUS_WAREHOUSE, STATUS_SHIPPED, STATUS_CANCELLED.
464 | 
465 | **Returns:**
466 | 
467 | the status
468 | 
469 | ---
470 | 
471 | ### getTrackingInfo
472 | 
473 | **Signature:** `getTrackingInfo(trackingInfoID : String) : TrackingInfo`
474 | 
475 | **Description:** Gets a tracking info for this shipping order.
476 | 
477 | **Parameters:**
478 | 
479 | - `trackingInfoID`: the tracking info id
480 | 
481 | **Returns:**
482 | 
483 | the tracking info or null
484 | 
485 | **See Also:**
486 | 
487 | TrackingInfo
488 | 
489 | ---
490 | 
491 | ### getTrackingInfos
492 | 
493 | **Signature:** `getTrackingInfos() : Collection`
494 | 
495 | **Description:** Gets all tracking informations for this shipping order.
496 | 
497 | **Returns:**
498 | 
499 | all tracking informations for this shipping order
500 | 
501 | **See Also:**
502 | 
503 | TrackingInfo
504 | 
505 | ---
506 | 
507 | ### setShipDate
508 | 
509 | **Signature:** `setShipDate(date : Date) : void`
510 | 
511 | **Description:** Sets the shipping date.
512 | 
513 | **Parameters:**
514 | 
515 | - `date`: the ship date
516 | 
517 | ---
518 | 
519 | ### setShippingAddress
520 | 
521 | **Signature:** `setShippingAddress(address : OrderAddress) : void`
522 | 
523 | **Description:** Set a shipping address for the shipping order.
524 | 
525 | **Parameters:**
526 | 
527 | - `address`: the shipping address
528 | 
529 | **See Also:**
530 | 
531 | getShippingAddress()
532 | 
533 | ---
534 | 
535 | ### setShippingMethodID
536 | 
537 | **Signature:** `setShippingMethodID(shippingMethodID : String) : void`
538 | 
539 | **Description:** Set the id of shipping method.
540 | 
541 | **Parameters:**
542 | 
543 | - `shippingMethodID`: the id of the shipping method
544 | 
545 | **See Also:**
546 | 
547 | ShippingMethod.getID()
548 | 
549 | ---
550 | 
551 | ### setStatusWarehouse
552 | 
553 | **Signature:** `setStatusWarehouse() : void`
554 | 
555 | **Description:** Set a CONFIRMED shipping order (all items in status CONFIRMED) to status WAREHOUSE (all items in status WAREHOUSE). Note - this method is the only way to transition a shipping order from CONFIRMED to WAREHOUSE.
556 | 
557 | **Throws:**
558 | 
559 | IllegalArgumentException - if the shipping order is in a status other than CONFIRMED.
560 | 
561 | ---
```

--------------------------------------------------------------------------------
/tests/mcp/yaml/activate-code-version.full-mode.test.mcp.yml:
--------------------------------------------------------------------------------

```yaml
  1 | # ==================================================================================
  2 | # SFCC MCP Server - activate_code_version Tool YAML Tests (Full Mode)
  3 | # Tests code version activation functionality with SFCC credentials
  4 | # This tool provides code version management for deployment troubleshooting
  5 | #
  6 | # Test Coverage:
  7 | # - Tool availability and definition validation in full mode
  8 | # - Parameter validation (required codeVersionId, empty/null handling)
  9 | # - Error responses for non-existent and invalid code versions
 10 | # - Response structure validation for both success and error cases
 11 | # - Performance requirements (under 2000ms for OCAPI calls)
 12 | # - Edge cases (long IDs, special characters)
 13 | # - Consistency across different invalid inputs
 14 | # - SFCC fault information parsing
 15 | # 
 16 | # Quick Test Commands:
 17 | # aegis "tests/mcp/yaml/activate-code-version.full-mode.test.mcp.yml" --config "aegis.config.with-dw.json" --verbose
 18 | # aegis query activate_code_version 'codeVersionId:test-version-001' --config "aegis.config.with-dw.json"
 19 | # ==================================================================================
 20 | 
 21 | description: "activate_code_version tool full mode tests - Core functionality validation"
 22 | 
 23 | tests:
 24 |   # ==================================================================================
 25 |   # TOOL AVAILABILITY TESTS
 26 |   # ==================================================================================
 27 |   - it: "should list activate_code_version tool in full mode"
 28 |     request:
 29 |       jsonrpc: "2.0"
 30 |       id: "tool-availability-full"
 31 |       method: "tools/list"
 32 |       params: {}
 33 |     expect:
 34 |       response:
 35 |         jsonrpc: "2.0"
 36 |         id: "tool-availability-full"
 37 |         result:
 38 |           tools:
 39 |             match:arrayElements:
 40 |               match:partial:
 41 |                 name: "match:type:string"
 42 |                 description: "match:type:string"
 43 |           match:extractField: "tools.*.name"
 44 |           value: "match:arrayContains:activate_code_version"
 45 |       stderr: "toBeEmpty"
 46 | 
 47 |   - it: "should have correct tool definition in full mode"
 48 |     request:
 49 |       jsonrpc: "2.0"
 50 |       id: "tool-definition-full"
 51 |       method: "tools/list"
 52 |       params: {}
 53 |     expect:
 54 |       response:
 55 |         jsonrpc: "2.0"
 56 |         id: "tool-definition-full"
 57 |         result:
 58 |           tools: "match:arrayContains:name:activate_code_version"
 59 |       stderr: "toBeEmpty"
 60 | 
 61 |   - it: "should have proper tool schema definition"
 62 |     request:
 63 |       jsonrpc: "2.0"
 64 |       id: "tool-schema-validation"
 65 |       method: "tools/list"
 66 |       params: {}
 67 |     expect:
 68 |       response:
 69 |         jsonrpc: "2.0"
 70 |         id: "tool-schema-validation"
 71 |         result:
 72 |           tools: "match:arrayContains:name:activate_code_version"
 73 |       stderr: "toBeEmpty"
 74 | 
 75 |   # ==================================================================================
 76 |   # SETUP - RESET TO KNOWN STATE
 77 |   # ==================================================================================
 78 |   - it: "should reset to known state before testing"
 79 |     request:
 80 |       jsonrpc: "2.0"
 81 |       id: "reset-state"
 82 |       method: "tools/call"
 83 |       params:
 84 |         name: "activate_code_version"
 85 |         arguments:
 86 |           codeVersionId: "reset_version"
 87 |     expect:
 88 |       response:
 89 |         jsonrpc: "2.0"
 90 |         id: "reset-state"
 91 |         result:
 92 |           content:
 93 |             match:arrayElements:
 94 |               type: "text"
 95 |               text: "match:type:string"
 96 |           isError: false
 97 |       stderr: "toBeEmpty"
 98 | 
 99 |   # ==================================================================================
100 |   # SUCCESSFUL ACTIVATION TESTS
101 |   # ==================================================================================
102 |   - it: "should successfully activate first test code version"
103 |     request:
104 |       jsonrpc: "2.0"
105 |       id: "activate-success-1"
106 |       method: "tools/call"
107 |       params:
108 |         name: "activate_code_version"
109 |         arguments:
110 |           codeVersionId: "test_activation"
111 |     expect:
112 |       response:
113 |         jsonrpc: "2.0"
114 |         id: "activate-success-1"
115 |         result:
116 |           content:
117 |             match:arrayElements:
118 |               type: "text"
119 |               text: "match:contains:\"id\": \"test_activation\""
120 |           isError: false
121 |       stderr: "toBeEmpty"
122 |     performance:
123 |       maxResponseTime: "2000ms"
124 | 
125 |   - it: "should successfully activate second test code version"
126 |     request:
127 |       jsonrpc: "2.0"
128 |       id: "activate-success-2"
129 |       method: "tools/call"
130 |       params:
131 |         name: "activate_code_version"
132 |         arguments:
133 |           codeVersionId: "simple_id"
134 |     expect:
135 |       response:
136 |         jsonrpc: "2.0"
137 |         id: "activate-success-2"
138 |         result:
139 |           content:
140 |             match:arrayElements:
141 |               type: "text"
142 |               text: "match:contains:\"id\": \"simple_id\""
143 |           isError: false
144 |       stderr: "toBeEmpty"
145 |     performance:
146 |       maxResponseTime: "2000ms"
147 | 
148 |   - it: "should successfully activate third test code version with dashes"
149 |     request:
150 |       jsonrpc: "2.0"
151 |       id: "activate-success-3"
152 |       method: "tools/call"
153 |       params:
154 |         name: "activate_code_version"
155 |         arguments:
156 |           codeVersionId: "version-with-dashes"
157 |     expect:
158 |       response:
159 |         jsonrpc: "2.0"
160 |         id: "activate-success-3"
161 |         result:
162 |           content:
163 |             match:arrayElements:
164 |               type: "text"
165 |               text: "match:contains:\"id\": \"version-with-dashes\""
166 |           isError: false
167 |       stderr: "toBeEmpty"
168 |     performance:
169 |       maxResponseTime: "2000ms"
170 | 
171 |   # ==================================================================================
172 |   # RE-ACTIVATION FAILURE TEST
173 |   # ==================================================================================
174 |   - it: "should fail when attempting to re-activate currently active version"
175 |     request:
176 |       jsonrpc: "2.0"
177 |       id: "reactivate-failure"
178 |       method: "tools/call"
179 |       params:
180 |         name: "activate_code_version"
181 |         arguments:
182 |           codeVersionId: "version-with-dashes"
183 |     expect:
184 |       response:
185 |         jsonrpc: "2.0"
186 |         id: "reactivate-failure"
187 |         result:
188 |           content:
189 |             match:arrayElements:
190 |               type: "text"
191 |               text: "match:contains:already active"
192 |           isError: true
193 |       stderr: "toBeEmpty"
194 |     performance:
195 |       maxResponseTime: "2000ms"
196 | 
197 |   # ==================================================================================
198 |   # RESET TO KNOWN STATE (Final)
199 |   # ==================================================================================
200 |   - it: "should reset back to reset_version to clean up test state"
201 |     request:
202 |       jsonrpc: "2.0"
203 |       id: "final-reset"
204 |       method: "tools/call"
205 |       params:
206 |         name: "activate_code_version"
207 |         arguments:
208 |           codeVersionId: "reset_version"
209 |     expect:
210 |       response:
211 |         jsonrpc: "2.0"
212 |         id: "final-reset"
213 |         result:
214 |           content:
215 |             match:arrayElements:
216 |               type: "text"
217 |               text: "match:contains:\"id\": \"reset_version\""
218 |           isError: false
219 |       stderr: "toBeEmpty"
220 |     performance:
221 |       maxResponseTime: "2000ms"
222 | 
223 |   # ==================================================================================
224 |   # PARAMETER VALIDATION TESTS
225 |   # ==================================================================================
226 |   - it: "should reject missing required parameter"
227 |     request:
228 |       jsonrpc: "2.0"
229 |       id: "missing-param"
230 |       method: "tools/call"
231 |       params:
232 |         name: "activate_code_version"
233 |         arguments: {}
234 |     expect:
235 |       response:
236 |         jsonrpc: "2.0"
237 |         id: "missing-param"
238 |         result:
239 |           content:
240 |             match:arrayElements:
241 |               type: "text"
242 |               text: "match:contains:codeVersionId must be a non-empty string"
243 |           isError: true
244 |       stderr: "toBeEmpty"
245 |     performance:
246 |       maxResponseTime: "1000ms"
247 | 
248 |   - it: "should reject empty codeVersionId"
249 |     request:
250 |       jsonrpc: "2.0"
251 |       id: "empty-param"
252 |       method: "tools/call"
253 |       params:
254 |         name: "activate_code_version"
255 |         arguments:
256 |           codeVersionId: ""
257 |     expect:
258 |       response:
259 |         jsonrpc: "2.0"
260 |         id: "empty-param"
261 |         result:
262 |           content:
263 |             match:arrayElements:
264 |               type: "text"
265 |               text: "match:contains:codeVersionId must be a non-empty string"
266 |           isError: true
267 |       stderr: "toBeEmpty"
268 |     performance:
269 |       maxResponseTime: "1000ms"
270 | 
271 |   - it: "should reject null codeVersionId"
272 |     request:
273 |       jsonrpc: "2.0"
274 |       id: "null-param"
275 |       method: "tools/call"
276 |       params:
277 |         name: "activate_code_version"
278 |         arguments:
279 |           codeVersionId: null
280 |     expect:
281 |       response:
282 |         jsonrpc: "2.0"
283 |         id: "null-param"
284 |         result:
285 |           content:
286 |             match:arrayElements:
287 |               type: "text"
288 |               text: "match:contains:codeVersionId must be a non-empty string"
289 |           isError: true
290 |       stderr: "toBeEmpty"
291 |     performance:
292 |       maxResponseTime: "1000ms"
293 | 
294 |   # ==================================================================================
295 |   # BASIC FUNCTIONALITY TESTS (Using Non-Existent Code Version)
296 |   # ==================================================================================
297 |   - it: "should handle non-existent code version gracefully"
298 |     request:
299 |       jsonrpc: "2.0"
300 |       id: "non-existent-version"
301 |       method: "tools/call"
302 |       params:
303 |         name: "activate_code_version"
304 |         arguments:
305 |           codeVersionId: "test-version-nonexistent"
306 |     expect:
307 |       response:
308 |         jsonrpc: "2.0"
309 |         id: "non-existent-version"
310 |         result:
311 |           content:
312 |             match:arrayElements:
313 |               type: "text"
314 |               text: "match:regex:Error[\\s\\S]*404[\\s\\S]*not found[\\s\\S]*"
315 |           isError: true
316 |       stderr: "toBeEmpty"
317 |     performance:
318 |       maxResponseTime: "2000ms"
319 | 
320 |   - it: "should provide meaningful error for invalid code version ID"
321 |     request:
322 |       jsonrpc: "2.0"
323 |       id: "invalid-version-id"
324 |       method: "tools/call"
325 |       params:
326 |         name: "activate_code_version"
327 |         arguments:
328 |           codeVersionId: "invalid@#$%^&*()"
329 |     expect:
330 |       response:
331 |         jsonrpc: "2.0"
332 |         id: "invalid-version-id"
333 |         result:
334 |           content:
335 |             match:arrayElements:
336 |               type: "text"
337 |               text: "match:regex:Error[\\s\\S]*(404|not found|invalid)[\\s\\S]*"
338 |           isError: true
339 |       stderr: "toBeEmpty"
340 |     performance:
341 |       maxResponseTime: "2000ms"
342 | 
343 |   # ==================================================================================
344 |   # RESPONSE STRUCTURE VALIDATION
345 |   # ==================================================================================
346 |   - it: "should return proper error response structure"
347 |     request:
348 |       jsonrpc: "2.0"
349 |       id: "error-structure"
350 |       method: "tools/call"
351 |       params:
352 |         name: "activate_code_version"
353 |         arguments:
354 |           codeVersionId: "test-nonexistent"
355 |     expect:
356 |       response:
357 |         jsonrpc: "2.0"
358 |         id: "error-structure"
359 |         result:
360 |           content:
361 |             match:arrayElements:
362 |               match:partial:
363 |                 type: "text"
364 |                 text: "match:type:string"
365 |           isError: true
366 |       stderr: "toBeEmpty"
367 | 
368 |   - it: "should include SFCC fault information in error response"
369 |     request:
370 |       jsonrpc: "2.0"
371 |       id: "sfcc-fault-info"
372 |       method: "tools/call"
373 |       params:
374 |         name: "activate_code_version"
375 |         arguments:
376 |           codeVersionId: "test-fault-info"
377 |     expect:
378 |       response:
379 |         jsonrpc: "2.0"
380 |         id: "sfcc-fault-info"
381 |         result:
382 |           content:
383 |             match:arrayElements:
384 |               type: "text"
385 |               text: "match:regex:Error[\\s\\S]*(fault|InvalidParameterException|404)[\\s\\S]*"
386 |           isError: true
387 |       stderr: "toBeEmpty"
388 | 
389 |   # ==================================================================================
390 |   # PERFORMANCE TESTS
391 |   # ==================================================================================
392 |   - it: "should meet performance requirements for error responses"
393 |     request:
394 |       jsonrpc: "2.0"
395 |       id: "performance-error"
396 |       method: "tools/call"
397 |       params:
398 |         name: "activate_code_version"
399 |         arguments:
400 |           codeVersionId: "test-performance"
401 |     expect:
402 |       response:
403 |         jsonrpc: "2.0"
404 |         id: "performance-error"
405 |         result:
406 |           content:
407 |             match:arrayElements:
408 |               type: "text"
409 |               text: "match:contains:Error"
410 |           isError: true
411 |       stderr: "toBeEmpty"
412 |     performance:
413 |       maxResponseTime: "2000ms"
414 | 
415 |   # ==================================================================================
416 |   # EDGE CASE TESTS
417 |   # ==================================================================================
418 |   - it: "should handle very long code version ID"
419 |     request:
420 |       jsonrpc: "2.0"
421 |       id: "long-version-id"
422 |       method: "tools/call"
423 |       params:
424 |         name: "activate_code_version"
425 |         arguments:
426 |           codeVersionId: "test-very-long-code-version-id-that-exceeds-normal-length-expectations-and-might-cause-issues"
427 |     expect:
428 |       response:
429 |         jsonrpc: "2.0"
430 |         id: "long-version-id"
431 |         result:
432 |           content:
433 |             match:arrayElements:
434 |               type: "text"
435 |               text: "match:regex:Error[\\s\\S]*(404|not found)[\\s\\S]*"
436 |           isError: true
437 |       stderr: "toBeEmpty"
438 |     performance:
439 |       maxResponseTime: "2000ms"
440 | 
441 |   - it: "should handle special characters in code version ID"
442 |     request:
443 |       jsonrpc: "2.0"
444 |       id: "special-chars"
445 |       method: "tools/call"
446 |       params:
447 |         name: "activate_code_version"
448 |         arguments:
449 |           codeVersionId: "test-version-with-spaces and-chars"
450 |     expect:
451 |       response:
452 |         jsonrpc: "2.0"
453 |         id: "special-chars"
454 |         result:
455 |           content:
456 |             match:arrayElements:
457 |               type: "text"
458 |               text: "match:regex:Error[\\s\\S]*(404|not found)[\\s\\S]*"
459 |           isError: true
460 |       stderr: "toBeEmpty"
461 |     performance:
462 |       maxResponseTime: "2000ms"
463 | 
464 |   # ==================================================================================
465 |   # CONSISTENCY TESTS
466 |   # ==================================================================================
467 |   - it: "should return consistent error format across different invalid inputs"
468 |     request:
469 |       jsonrpc: "2.0"
470 |       id: "consistent-errors-1"
471 |       method: "tools/call"
472 |       params:
473 |         name: "activate_code_version"
474 |         arguments:
475 |           codeVersionId: "test-consistency-1"
476 |     expect:
477 |       response:
478 |         jsonrpc: "2.0"
479 |         id: "consistent-errors-1"
480 |         result:
481 |           content:
482 |             match:arrayElements:
483 |               match:partial:
484 |                 type: "text"
485 |           isError: true
486 |       stderr: "toBeEmpty"
487 | 
488 |   - it: "should return consistent error format for different invalid version"
489 |     request:
490 |       jsonrpc: "2.0"
491 |       id: "consistent-errors-2"
492 |       method: "tools/call"
493 |       params:
494 |         name: "activate_code_version"
495 |         arguments:
496 |           codeVersionId: "test-consistency-2"
497 |     expect:
498 |       response:
499 |         jsonrpc: "2.0"
500 |         id: "consistent-errors-2"
501 |         result:
502 |           content:
503 |             match:arrayElements:
504 |               match:partial:
505 |                 type: "text"
506 |           isError: true
507 |       stderr: "toBeEmpty"
```

--------------------------------------------------------------------------------
/src/clients/cartridge-generation-client.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * SFCC Cartridge Generation Client
  3 |  *
  4 |  * This client handles the generation of SFCC cartridge directory structures
  5 |  * with all necessary files and configurations, replacing the outdated sgmf-scripts
  6 |  * with a modern, integrated approach.
  7 |  */
  8 | 
  9 | import { Logger } from '../utils/logger.js';
 10 | import { IFileSystemService, IPathService } from '../services/index.js';
 11 | 
 12 | interface CartridgeGenerationOptions {
 13 |   cartridgeName: string;
 14 |   targetPath?: string;
 15 |   fullProjectSetup?: boolean;
 16 | }
 17 | 
 18 | interface CartridgeTemplates {
 19 |   packageJson: (cartridgeName: string) => object;
 20 |   dwJson: () => object;
 21 |   webpackConfig: (cartridgeName: string) => string;
 22 |   dotProject: (cartridgeName: string) => string;
 23 |   projectProperties: (cartridgeName: string) => string;
 24 |   eslintrc: () => object;
 25 |   stylelintrc: () => object;
 26 |   eslintignore: () => string;
 27 |   gitignore: () => string;
 28 | }
 29 | 
 30 | export class CartridgeGenerationClient {
 31 |   private logger: Logger;
 32 |   private templates: CartridgeTemplates;
 33 |   private fileSystem: IFileSystemService;
 34 |   private pathService: IPathService;
 35 | 
 36 |   constructor(fileSystem: IFileSystemService, pathService: IPathService) {
 37 |     this.logger = Logger.getChildLogger('CartridgeGenerationClient');
 38 |     this.fileSystem = fileSystem;
 39 |     this.pathService = pathService;
 40 |     this.templates = this.initializeTemplates();
 41 |   }
 42 | 
 43 |   /**
 44 |    * Normalize the target path by removing /cartridges or /cartridges/ from the end
 45 |    * The cartridge creation always happens from the root folder
 46 |    */
 47 |   private normalizeTargetPath(targetPath: string): string {
 48 |     // Remove trailing slashes first
 49 |     let normalized = targetPath.replace(/\/+$/, '');
 50 | 
 51 |     // Remove /cartridges from the end if present
 52 |     if (normalized.endsWith('/cartridges')) {
 53 |       normalized = normalized.slice(0, -11); // Remove '/cartridges' (11 characters)
 54 |     }
 55 | 
 56 |     this.logger.debug(`Normalized target path from '${targetPath}' to '${normalized}'`);
 57 |     return normalized;
 58 |   }
 59 | 
 60 |   /**
 61 |    * Generate a complete cartridge structure
 62 |    */
 63 |   async generateCartridgeStructure(options: CartridgeGenerationOptions): Promise<{
 64 |     success: boolean;
 65 |     message: string;
 66 |     createdFiles: string[];
 67 |     createdDirectories: string[];
 68 |     skippedFiles: string[];
 69 |   }> {
 70 |     const { cartridgeName, targetPath, fullProjectSetup = true } = options;
 71 |     const createdFiles: string[] = [];
 72 |     const createdDirectories: string[] = [];
 73 |     const skippedFiles: string[] = [];
 74 | 
 75 |     try {
 76 |       this.logger.info(`Starting cartridge generation for: ${cartridgeName}`);
 77 | 
 78 |       // Determine the working directory and normalize path
 79 |       let workingDir = targetPath ?? process.cwd();
 80 |       workingDir = this.normalizeTargetPath(workingDir);
 81 | 
 82 |       if (fullProjectSetup) {
 83 |         // Full project setup - create everything directly in the working directory
 84 |         this.logger.info(`Creating full project setup directly in: ${workingDir}`);
 85 | 
 86 |         // Ensure the working directory exists
 87 |         await this.ensureDirectory(workingDir);
 88 |         if (!createdDirectories.includes(workingDir)) {
 89 |           createdDirectories.push(workingDir);
 90 |         }
 91 | 
 92 |         // Create root files directly in working directory
 93 |         await this.createRootFiles(workingDir, cartridgeName, createdFiles, skippedFiles);
 94 | 
 95 |         // Create cartridge structure directly in working directory
 96 |         await this.createCartridgeStructure(workingDir, cartridgeName, createdFiles, createdDirectories, skippedFiles);
 97 | 
 98 |         return {
 99 |           success: true,
100 |           message: `Successfully created full project setup for cartridge '${cartridgeName}' in '${workingDir}'`,
101 |           createdFiles,
102 |           createdDirectories,
103 |           skippedFiles,
104 |         };
105 |       } else {
106 |         // Cartridge-only setup - add to existing project
107 |         const cartridgesDir = this.pathService.join(workingDir, 'cartridges');
108 | 
109 |         // Ensure cartridges directory exists
110 |         await this.ensureDirectory(cartridgesDir);
111 |         if (!createdDirectories.includes(cartridgesDir)) {
112 |           createdDirectories.push(cartridgesDir);
113 |         }
114 | 
115 |         // Create cartridge structure
116 |         await this.createCartridgeStructure(workingDir, cartridgeName, createdFiles, createdDirectories, skippedFiles);
117 | 
118 |         return {
119 |           success: true,
120 |           message: `Successfully created cartridge '${cartridgeName}' in existing project at '${workingDir}'`,
121 |           createdFiles,
122 |           createdDirectories,
123 |           skippedFiles,
124 |         };
125 |       }
126 |     } catch (error) {
127 |       this.logger.error('Error generating cartridge structure:', error);
128 |       return {
129 |         success: false,
130 |         message: `Failed to generate cartridge structure: ${error instanceof Error ? error.message : 'Unknown error'}`,
131 |         createdFiles,
132 |         createdDirectories,
133 |         skippedFiles,
134 |       };
135 |     }
136 |   }
137 | 
138 |   /**
139 |    * Create root project files (package.json, webpack, etc.)
140 |    */
141 |   private async createRootFiles(
142 |     projectDir: string,
143 |     cartridgeName: string,
144 |     createdFiles: string[],
145 |     skippedFiles: string[],
146 |   ): Promise<void> {
147 |     // Create package.json
148 |     const packageJsonPath = this.pathService.join(projectDir, 'package.json');
149 |     await this.safeWriteFile(
150 |       packageJsonPath,
151 |       JSON.stringify(this.templates.packageJson(cartridgeName), null, 2),
152 |       createdFiles,
153 |       skippedFiles,
154 |     );
155 | 
156 |     // Create dw.json
157 |     const dwJsonPath = this.pathService.join(projectDir, 'dw.json');
158 |     await this.safeWriteFile(
159 |       dwJsonPath,
160 |       JSON.stringify(this.templates.dwJson(), null, 2),
161 |       createdFiles,
162 |       skippedFiles,
163 |     );
164 | 
165 |     // Create webpack.config.js
166 |     const webpackPath = this.pathService.join(projectDir, 'webpack.config.js');
167 |     await this.safeWriteFile(
168 |       webpackPath,
169 |       this.templates.webpackConfig(cartridgeName),
170 |       createdFiles,
171 |       skippedFiles,
172 |     );
173 | 
174 |     // Create .eslintrc.json
175 |     const eslintrcPath = this.pathService.join(projectDir, '.eslintrc.json');
176 |     await this.safeWriteFile(
177 |       eslintrcPath,
178 |       JSON.stringify(this.templates.eslintrc(), null, 2),
179 |       createdFiles,
180 |       skippedFiles,
181 |     );
182 | 
183 |     // Create .stylelintrc.json
184 |     const stylelintrcPath = this.pathService.join(projectDir, '.stylelintrc.json');
185 |     await this.safeWriteFile(
186 |       stylelintrcPath,
187 |       JSON.stringify(this.templates.stylelintrc(), null, 2),
188 |       createdFiles,
189 |       skippedFiles,
190 |     );
191 | 
192 |     // Create .eslintignore
193 |     const eslintignorePath = this.pathService.join(projectDir, '.eslintignore');
194 |     await this.safeWriteFile(
195 |       eslintignorePath,
196 |       this.templates.eslintignore(),
197 |       createdFiles,
198 |       skippedFiles,
199 |     );
200 | 
201 |     // Create .gitignore
202 |     const gitignorePath = this.pathService.join(projectDir, '.gitignore');
203 |     await this.safeWriteFile(
204 |       gitignorePath,
205 |       this.templates.gitignore(),
206 |       createdFiles,
207 |       skippedFiles,
208 |     );
209 |   }
210 | 
211 |   /**
212 |    * Create the cartridge directory structure
213 |    */
214 |   private async createCartridgeStructure(
215 |     baseDir: string,
216 |     cartridgeName: string,
217 |     createdFiles: string[],
218 |     createdDirectories: string[],
219 |     skippedFiles: string[],
220 |   ): Promise<void> {
221 |     // Create cartridges directory
222 |     const cartridgesDir = this.pathService.join(baseDir, 'cartridges');
223 |     await this.ensureDirectory(cartridgesDir);
224 |     createdDirectories.push(cartridgesDir);
225 | 
226 |     // Create specific cartridge directory
227 |     const cartridgeDir = this.pathService.join(cartridgesDir, cartridgeName);
228 |     await this.ensureDirectory(cartridgeDir);
229 |     createdDirectories.push(cartridgeDir);
230 | 
231 |     // Create .project file
232 |     const projectPath = this.pathService.join(cartridgeDir, '.project');
233 |     await this.safeWriteFile(
234 |       projectPath,
235 |       this.templates.dotProject(cartridgeName),
236 |       createdFiles,
237 |       skippedFiles,
238 |     );
239 | 
240 |     // Create cartridge subdirectory
241 |     const cartridgeSubDir = this.pathService.join(cartridgeDir, 'cartridge');
242 |     await this.ensureDirectory(cartridgeSubDir);
243 |     createdDirectories.push(cartridgeSubDir);
244 | 
245 |     // Create cartridge properties file
246 |     const propertiesPath = this.pathService.join(cartridgeSubDir, `${cartridgeName}.properties`);
247 |     await this.safeWriteFile(
248 |       propertiesPath,
249 |       this.templates.projectProperties(cartridgeName),
250 |       createdFiles,
251 |       skippedFiles,
252 |     );
253 | 
254 |     // Create directory structure
255 |     const directories = [
256 |       'controllers',
257 |       'models',
258 |       'templates',
259 |       'templates/default',
260 |       'templates/resources',
261 |       'client',
262 |       'client/default',
263 |       'client/default/js',
264 |       'client/default/scss',
265 |     ];
266 | 
267 |     for (const dir of directories) {
268 |       const fullPath = this.pathService.join(cartridgeSubDir, dir);
269 |       await this.ensureDirectory(fullPath);
270 |       createdDirectories.push(fullPath);
271 |     }
272 |   }
273 | 
274 |   /**
275 |    * Ensure a directory exists, create if it doesn't
276 |    */
277 |   private async ensureDirectory(dirPath: string): Promise<void> {
278 |     try {
279 |       await this.fileSystem.access(dirPath);
280 |     } catch {
281 |       await this.fileSystem.mkdir(dirPath, { recursive: true });
282 |       this.logger.info(`Created directory: ${dirPath}`);
283 |     }
284 |   }
285 | 
286 |   /**
287 |    * Safely write a file, skipping if it already exists
288 |    */
289 |   private async safeWriteFile(
290 |     filePath: string,
291 |     content: string,
292 |     createdFiles: string[],
293 |     skippedFiles: string[],
294 |   ): Promise<void> {
295 |     try {
296 |       await this.fileSystem.access(filePath);
297 |       // File exists, skip it
298 |       skippedFiles.push(filePath);
299 |       this.logger.info(`Skipped existing file: ${filePath}`);
300 |     } catch {
301 |       // File doesn't exist, create it
302 |       await this.fileSystem.writeFile(filePath, content);
303 |       createdFiles.push(filePath);
304 |       this.logger.info(`Created file: ${filePath}`);
305 |     }
306 |   }
307 | 
308 |   /**
309 |    * Initialize all file templates
310 |    */
311 |   private initializeTemplates(): CartridgeTemplates {
312 |     return {
313 |       packageJson: (cartridgeName: string) => ({
314 |         name: cartridgeName,
315 |         version: '0.0.1',
316 |         description: 'New overlay cartridge',
317 |         main: 'index.js',
318 |         scripts: {
319 |           'lint': 'npm run lint:css && npm run lint:js',
320 |           'lint:css': 'sgmf-scripts --lint css',
321 |           'lint:js': 'sgmf-scripts --lint js',
322 |           'lint:fix': 'eslint cartridges --fix',
323 |           upload: 'sgmf-scripts --upload -- ',
324 |           uploadCartridge: `sgmf-scripts --uploadCartridge ${cartridgeName}`,
325 |           'compile:js': 'sgmf-scripts --compile js',
326 |           'compile:scss': 'sgmf-scripts --compile css',
327 |         },
328 |         devDependencies: {
329 |           autoprefixer: '^10.4.14',
330 |           bestzip: '^2.2.1',
331 |           'css-loader': '^6.0.0',
332 |           'css-minimizer-webpack-plugin': '^5.0.1',
333 |           eslint: '^8.56.0',
334 |           'eslint-config-airbnb-base': '^15.0.0',
335 |           'eslint-config-prettier': '^9.1.0',
336 |           'eslint-plugin-import': '^2.29.0',
337 |           'mini-css-extract-plugin': '^2.7.6',
338 |           'postcss-loader': '^7.0.0',
339 |           sass: '^1.69.7',
340 |           'sass-loader': '^13.3.2',
341 |           'sgmf-scripts': '^3.0.0',
342 |           shx: '^0.3.4',
343 |           stylelint: '^15.4.0',
344 |           'stylelint-config-standard-scss': '^11.0.0',
345 |           'webpack-remove-empty-scripts': '^1.0.4',
346 |         },
347 |         browserslist: [
348 |           'last 2 versions',
349 |           'ie >= 10',
350 |         ],
351 |       }),
352 | 
353 |       dwJson: () => ({
354 |         hostname: '',
355 |         username: '',
356 |         password: '',
357 |         'code-version': '',
358 |       }),
359 | 
360 |       webpackConfig: (cartridgeName: string) => `'use strict';
361 | 
362 | var path = require('path');
363 | var MiniCssExtractPlugin = require('mini-css-extract-plugin');
364 | var CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
365 | var sgmfScripts = require('sgmf-scripts');
366 | var RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
367 | 
368 | module.exports = [{
369 |     mode: 'development',
370 |     name: 'js',
371 |     entry: sgmfScripts.createJsPath(),
372 |     output: {
373 |         path: path.resolve('./cartridges/${cartridgeName}/cartridge/static'),
374 |         filename: '[name].js'
375 |     }
376 | }, {
377 |     mode: 'none',
378 |     name: 'scss',
379 |     entry: sgmfScripts.createScssPath(),
380 |     output: {
381 |         path: path.resolve('./cartridges/${cartridgeName}/cartridge/static')
382 |     },
383 |     module: {
384 |         rules: [{
385 |             test: /\\.scss$/,
386 |             use: [{
387 |                 loader: MiniCssExtractPlugin.loader,
388 |                 options: {
389 |                     esModule: false
390 |                 }
391 |             },
392 |             {
393 |                 loader: 'css-loader',
394 |                 options: {
395 |                     url: false
396 |                 }
397 |             }, {
398 |                 loader: 'postcss-loader',
399 |                 options: {
400 |                     postcssOptions: {
401 |                         plugins: [require('autoprefixer')]
402 |                     }
403 |                 }
404 |             }, {
405 |                 loader: 'sass-loader',
406 |                 options: {
407 |                     implementation: require('sass'),
408 |                     sassOptions: {
409 |                         includePaths: [
410 |                             path.resolve(path.resolve(process.cwd(), '../storefront-reference-architecture/node_modules/')),
411 |                             path.resolve(process.cwd(), '../storefront-reference-architecture/node_modules/flag-icons/sass')
412 |                         ]
413 |                     }
414 |                 }
415 |             }]
416 |         }]
417 |     },
418 |     plugins: [
419 |         new RemoveEmptyScriptsPlugin(),
420 |         new MiniCssExtractPlugin({
421 |             filename: '[name].css',
422 |             chunkFilename: '[name].css'
423 |         })
424 |     ],
425 |     optimization: {
426 |         minimizer: ['...', new CssMinimizerPlugin()]
427 |     }
428 | }];`,
429 | 
430 |       dotProject: (cartridgeName: string) => `<?xml version="1.0" encoding="UTF-8"?>
431 | <projectDescription>
432 |     <name>${cartridgeName}</name>
433 |     <comment></comment>
434 |     <projects>
435 |     </projects>
436 |     <buildSpec>
437 |         <buildCommand>
438 |             <name>com.demandware.studio.core.beehiveElementBuilder</name>
439 |             <arguments>
440 |             </arguments>
441 |         </buildCommand>
442 |     </buildSpec>
443 |     <natures>
444 |         <nature>com.demandware.studio.core.beehiveNature</nature>
445 |     </natures>
446 | </projectDescription>`,
447 | 
448 |       projectProperties: (cartridgeName: string) => `## cartridge.properties for cartridge ${cartridgeName}
449 | #demandware.cartridges.${cartridgeName}.multipleLanguageStorefront=true`,
450 | 
451 |       eslintrc: () => ({
452 |         root: true,
453 |         extends: 'airbnb-base/legacy',
454 |         globals: {
455 |           session: 'readonly',
456 |           request: 'readonly',
457 |         },
458 |         rules: {
459 |           'import/no-unresolved': 'off',
460 |           indent: ['error', 4, { SwitchCase: 1, VariableDeclarator: 1 }],
461 |           'func-names': 'off',
462 |           'require-jsdoc': 'error',
463 |           'valid-jsdoc': ['error', {
464 |             preferType: {
465 |               Boolean: 'boolean',
466 |               Number: 'number',
467 |               object: 'Object',
468 |               String: 'string',
469 |             },
470 |             requireReturn: false,
471 |           }],
472 |           'vars-on-top': 'off',
473 |           'global-require': 'off',
474 |           'no-shadow': ['error', { allow: ['err', 'callback'] }],
475 |           'max-len': 'off',
476 |           'no-plusplus': 'off',
477 |         },
478 |       }),
479 | 
480 |       stylelintrc: () => ({
481 |         extends: 'stylelint-config-standard-scss',
482 |         plugins: [
483 |           'stylelint-scss',
484 |         ],
485 |       }),
486 | 
487 |       eslintignore: () => `node_modules/
488 | cartridges/**/cartridge/static/
489 | coverage/
490 | doc/
491 | bin/
492 | codecept.conf.js`,
493 | 
494 |       gitignore: () => `node_modules/
495 | cartridges/*/cartridge/static/
496 | .DS_Store
497 | *.log
498 | npm-debug.log*
499 | yarn-debug.log*
500 | yarn-error.log*
501 | coverage/
502 | .nyc_output/
503 | .env
504 | dw.json`,
505 |     };
506 |   }
507 | }
508 | 
```

--------------------------------------------------------------------------------
/docs/dw_io/File.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.io
  2 | 
  3 | # Class File
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.io.File
  9 | 
 10 | ## Description
 11 | 
 12 | Represents a file resource accessible from scripting. As with java.io.File, a File is essentially an "abstract pathname" which may or may not denote an actual file on the file system. Methods createNewFile, mkdir, mkdirs, and remove are provided to actually manipulate physical files. File access is limited to certain virtual directories. These directories are a subset of those accessible through WebDAV. As a result of this restriction, pathnames must be one of the following forms: /TEMP(/...) /IMPEX(/...) /REALMDATA(/...) /CATALOGS/[Catalog Name](/...) /LIBRARIES/[Library Name](/...) Note, that these paths are analogous to the WebDAV URIs used to access the same directories. The files are stored in a shared file system where multiple processes could access the same file. The programmer has to make sure no more than one process writes to a file at a given time. This class provides other useful methods for listing the children of a directory and for working with zip files. Note: when this class is used with sensitive data, be careful in persisting sensitive information. For performance reasons no more than 100,000 files (regular files and directories) should be stored in a directory.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### CATALOGS
 17 | 
 18 | **Type:** String = "CATALOGS"
 19 | 
 20 | Catalogs root directory.
 21 | 
 22 | ### CUSTOMER_SNAPSHOTS
 23 | 
 24 | **Type:** String = "CUSTOMERSNAPSHOTS"
 25 | 
 26 | Customer snapshots root directory.
 27 | 
 28 | ### CUSTOMERPI
 29 | 
 30 | **Type:** String = "CUSTOMERPI"
 31 | 
 32 | Customer Payment Instrument root directory.
 33 | 
 34 | ### DYNAMIC
 35 | 
 36 | **Type:** String = "DYNAMIC"
 37 | 
 38 | Reserved for future use.
 39 | 
 40 | ### IMPEX
 41 | 
 42 | **Type:** String = "IMPEX"
 43 | 
 44 | Import/export root directory.
 45 | 
 46 | ### LIBRARIES
 47 | 
 48 | **Type:** String = "LIBRARIES"
 49 | 
 50 | Libraries root directory.
 51 | 
 52 | ### REALMDATA
 53 | 
 54 | **Type:** String = "REALMDATA"
 55 | 
 56 | RealmData root directory.
 57 | 
 58 | ### SEPARATOR
 59 | 
 60 | **Type:** String = "/"
 61 | 
 62 | The UNIX style '/' path separator, which must be used for files paths.
 63 | 
 64 | ### STATIC
 65 | 
 66 | **Type:** String = "STATIC"
 67 | 
 68 | Static content root directory.
 69 | 
 70 | ### TEMP
 71 | 
 72 | **Type:** String = "TEMP"
 73 | 
 74 | Temp root directory.
 75 | 
 76 | ## Properties
 77 | 
 78 | ### directory
 79 | 
 80 | **Type:** boolean (Read Only)
 81 | 
 82 | Indicates that this file is a directory.
 83 | 
 84 | ### file
 85 | 
 86 | **Type:** boolean (Read Only)
 87 | 
 88 | Indicates if this file is a file.
 89 | 
 90 | ### fullPath
 91 | 
 92 | **Type:** String (Read Only)
 93 | 
 94 | Return the full file path denoted by this File.
 95 |  This value will be the same regardless of which constructor was
 96 |  used to create this File.
 97 | 
 98 | ### name
 99 | 
100 | **Type:** String (Read Only)
101 | 
102 | The name of the file or directory denoted by this object. This is
103 |  just the last name in the pathname's name sequence. If the pathname's
104 |  name sequence is empty, then the empty string is returned.
105 | 
106 | ### path
107 | 
108 | **Type:** String (Read Only)
109 | 
110 | The portion of the path relative to the root directory.
111 | 
112 | ### rootDirectoryType
113 | 
114 | **Type:** String (Read Only)
115 | 
116 | The root directory type, e.g. "IMPEX" represented by this
117 |  File.
118 | 
119 | ## Constructor Summary
120 | 
121 | File(absPath : String) Creates a File from the given absolute file path in the file namespace.
122 | 
123 | File(rootDir : File, relPath : String) Creates a File given a root directory and a relative path.
124 | 
125 | ## Method Summary
126 | 
127 | ### copyTo
128 | 
129 | **Signature:** `copyTo(file : File) : File`
130 | 
131 | Copy a file.
132 | 
133 | ### createNewFile
134 | 
135 | **Signature:** `createNewFile() : boolean`
136 | 
137 | Create file.
138 | 
139 | ### exists
140 | 
141 | **Signature:** `exists() : boolean`
142 | 
143 | Indicates if the file exists.
144 | 
145 | ### getFullPath
146 | 
147 | **Signature:** `getFullPath() : String`
148 | 
149 | Return the full file path denoted by this File.
150 | 
151 | ### getName
152 | 
153 | **Signature:** `getName() : String`
154 | 
155 | Returns the name of the file or directory denoted by this object.
156 | 
157 | ### getPath
158 | 
159 | **Signature:** `getPath() : String`
160 | 
161 | Returns the portion of the path relative to the root directory.
162 | 
163 | ### getRootDirectory
164 | 
165 | **Signature:** `static getRootDirectory(rootDir : String, args : String...) : File`
166 | 
167 | Returns a File representing a directory for the specified root directory type.
168 | 
169 | ### getRootDirectoryType
170 | 
171 | **Signature:** `getRootDirectoryType() : String`
172 | 
173 | Returns the root directory type, e.g.
174 | 
175 | ### gunzip
176 | 
177 | **Signature:** `gunzip(root : File) : void`
178 | 
179 | Assumes this instance is a gzip file.
180 | 
181 | ### gzip
182 | 
183 | **Signature:** `gzip(outputZipFile : File) : void`
184 | 
185 | GZip this instance into a new gzip file.
186 | 
187 | ### isDirectory
188 | 
189 | **Signature:** `isDirectory() : boolean`
190 | 
191 | Indicates that this file is a directory.
192 | 
193 | ### isFile
194 | 
195 | **Signature:** `isFile() : boolean`
196 | 
197 | Indicates if this file is a file.
198 | 
199 | ### lastModified
200 | 
201 | **Signature:** `lastModified() : Number`
202 | 
203 | Return the time, in milliseconds, that this file was last modified.
204 | 
205 | ### length
206 | 
207 | **Signature:** `length() : Number`
208 | 
209 | Return the length of the file in bytes.
210 | 
211 | ### list
212 | 
213 | **Signature:** `list() : String[]`
214 | 
215 | Returns an array of strings naming the files and directories in the directory denoted by this object.
216 | 
217 | ### listFiles
218 | 
219 | **Signature:** `listFiles() : List`
220 | 
221 | Returns an array of File objects in the directory denoted by this File.
222 | 
223 | ### listFiles
224 | 
225 | **Signature:** `listFiles(filter : Function) : List`
226 | 
227 | Returns an array of File objects denoting the files and directories in the directory denoted by this object that satisfy the specified filter.
228 | 
229 | ### md5
230 | 
231 | **Signature:** `md5() : String`
232 | 
233 | Returns an MD5 hash of the content of the file of this instance.
234 | 
235 | ### mkdir
236 | 
237 | **Signature:** `mkdir() : boolean`
238 | 
239 | Creates a directory.
240 | 
241 | ### mkdirs
242 | 
243 | **Signature:** `mkdirs() : boolean`
244 | 
245 | Creates a directory, including, its parent directories, as needed.
246 | 
247 | ### remove
248 | 
249 | **Signature:** `remove() : boolean`
250 | 
251 | Deletes the file or directory denoted by this object.
252 | 
253 | ### renameTo
254 | 
255 | **Signature:** `renameTo(file : File) : boolean`
256 | 
257 | Rename file.
258 | 
259 | ### unzip
260 | 
261 | **Signature:** `unzip(root : File) : void`
262 | 
263 | Assumes this instance is a zip file.
264 | 
265 | ### zip
266 | 
267 | **Signature:** `zip(outputZipFile : File) : void`
268 | 
269 | Zip this instance into a new zip file.
270 | 
271 | ## Constructor Detail
272 | 
273 | ## Method Detail
274 | 
275 | ## Method Details
276 | 
277 | ### copyTo
278 | 
279 | **Signature:** `copyTo(file : File) : File`
280 | 
281 | **Description:** Copy a file. Directories cannot be copied. This method cannot be used from storefront requests.
282 | 
283 | **Parameters:**
284 | 
285 | - `file`: the File object to copy to
286 | 
287 | **Returns:**
288 | 
289 | a reference to the copied file.
290 | 
291 | **Throws:**
292 | 
293 | IOException - if there is an interruption during file copy.
294 | FileAlreadyExistsException - if the file to copy to already exists
295 | UnsupportedOperationException - if invoked from a storefront request
296 | 
297 | ---
298 | 
299 | ### createNewFile
300 | 
301 | **Signature:** `createNewFile() : boolean`
302 | 
303 | **Description:** Create file.
304 | 
305 | **Returns:**
306 | 
307 | boolean, true - if file has been created, false - file already exists
308 | 
309 | **Throws:**
310 | 
311 | - Exception
312 | 
313 | ---
314 | 
315 | ### exists
316 | 
317 | **Signature:** `exists() : boolean`
318 | 
319 | **Description:** Indicates if the file exists.
320 | 
321 | **Returns:**
322 | 
323 | true if file exists, false otherwise.
324 | 
325 | ---
326 | 
327 | ### getFullPath
328 | 
329 | **Signature:** `getFullPath() : String`
330 | 
331 | **Description:** Return the full file path denoted by this File. This value will be the same regardless of which constructor was used to create this File.
332 | 
333 | **Returns:**
334 | 
335 | the full file path.
336 | 
337 | ---
338 | 
339 | ### getName
340 | 
341 | **Signature:** `getName() : String`
342 | 
343 | **Description:** Returns the name of the file or directory denoted by this object. This is just the last name in the pathname's name sequence. If the pathname's name sequence is empty, then the empty string is returned.
344 | 
345 | **Returns:**
346 | 
347 | The name of the file or directory denoted by this object.
348 | 
349 | ---
350 | 
351 | ### getPath
352 | 
353 | **Signature:** `getPath() : String`
354 | 
355 | **Description:** Returns the portion of the path relative to the root directory.
356 | 
357 | **Deprecated:**
358 | 
359 | Use getFullPath() to access the full path. This method does not return the correct path for files in the CATALOGS or LIBRARIES virtual directories.
360 | 
361 | **Returns:**
362 | 
363 | the relative file path, possibly blank but not null.
364 | 
365 | ---
366 | 
367 | ### getRootDirectory
368 | 
369 | **Signature:** `static getRootDirectory(rootDir : String, args : String...) : File`
370 | 
371 | **Description:** Returns a File representing a directory for the specified root directory type. If the root directory type is CATALOGS or LIBRARIES, then an additional argument representing the specific catalog or library must be provided. Otherwise, no additional arguments are needed.
372 | 
373 | **Parameters:**
374 | 
375 | - `rootDir`: root directory type (see the constants defined in this class)
376 | - `args`: root directory specific arguments
377 | 
378 | **Returns:**
379 | 
380 | File object representing the directory
381 | 
382 | ---
383 | 
384 | ### getRootDirectoryType
385 | 
386 | **Signature:** `getRootDirectoryType() : String`
387 | 
388 | **Description:** Returns the root directory type, e.g. "IMPEX" represented by this File.
389 | 
390 | **Returns:**
391 | 
392 | root directory type
393 | 
394 | ---
395 | 
396 | ### gunzip
397 | 
398 | **Signature:** `gunzip(root : File) : void`
399 | 
400 | **Description:** Assumes this instance is a gzip file. Unzipping it will explode the contents in the directory passed in (root).
401 | 
402 | **Parameters:**
403 | 
404 | - `root`: a File indicating root. root must be a directory.
405 | 
406 | **Throws:**
407 | 
408 | Exception - if the zip files contents can't be exploded.
409 | 
410 | ---
411 | 
412 | ### gzip
413 | 
414 | **Signature:** `gzip(outputZipFile : File) : void`
415 | 
416 | **Description:** GZip this instance into a new gzip file. If you're zipping a file, then a single entry, the instance, is included in the output gzip file. Note that a new File is created. GZipping directories is not supported. This file is never modified.
417 | 
418 | **Parameters:**
419 | 
420 | - `outputZipFile`: the zip file created.
421 | 
422 | **Throws:**
423 | 
424 | IOException - if the zip file can't be created.
425 | 
426 | ---
427 | 
428 | ### isDirectory
429 | 
430 | **Signature:** `isDirectory() : boolean`
431 | 
432 | **Description:** Indicates that this file is a directory.
433 | 
434 | **Returns:**
435 | 
436 | true if the file is a directory, false otherwise.
437 | 
438 | ---
439 | 
440 | ### isFile
441 | 
442 | **Signature:** `isFile() : boolean`
443 | 
444 | **Description:** Indicates if this file is a file.
445 | 
446 | **Returns:**
447 | 
448 | true if the file is a file, false otherwise.
449 | 
450 | ---
451 | 
452 | ### lastModified
453 | 
454 | **Signature:** `lastModified() : Number`
455 | 
456 | **Description:** Return the time, in milliseconds, that this file was last modified.
457 | 
458 | **Returns:**
459 | 
460 | the time, in milliseconds, that this file was last modified.
461 | 
462 | ---
463 | 
464 | ### length
465 | 
466 | **Signature:** `length() : Number`
467 | 
468 | **Description:** Return the length of the file in bytes.
469 | 
470 | **Returns:**
471 | 
472 | the file length in bytes.
473 | 
474 | ---
475 | 
476 | ### list
477 | 
478 | **Signature:** `list() : String[]`
479 | 
480 | **Description:** Returns an array of strings naming the files and directories in the directory denoted by this object. If this object does not denote a directory, then this method returns null. Otherwise an array of strings is returned, one for each file or directory in the directory. Names denoting the directory itself and the directory's parent directory are not included in the result. Each string is a file name rather than a complete path. There is no guarantee that the name strings in the resulting array will appear in any specific order; they are not, in particular, guaranteed to appear in alphabetical order.
481 | 
482 | **Returns:**
483 | 
484 | An array of strings naming the files and directories in the directory denoted by this File. The array will be empty if the directory is empty. Returns null if this File does not denote a directory.
485 | 
486 | ---
487 | 
488 | ### listFiles
489 | 
490 | **Signature:** `listFiles() : List`
491 | 
492 | **Description:** Returns an array of File objects in the directory denoted by this File. If this File does not denote a directory, then this method returns null. Otherwise an array of File objects is returned, one for each file or directory in the directory. Files denoting the directory itself and the directory's parent directory are not included in the result. There is no guarantee that the files in the resulting array will appear in any specific order; they are not, in particular, guaranteed to appear in alphabetical order. Example usage: // Assume "foo" is an accessible directory. var this_directory : dw.io.File = new File("foo"); // Find all files in directory foo, one level "down". // listFiles() will not traverse subdirectories. var folder : dw.util.List = this_directory.listFiles(); var first_element : dw.io.File = folder[0]; function modification_comparison(lhs : File, rhs : File) { return lhs.lastModified() < rhs.lastModified(); } function lexigraphic_comparison(lhs: File, rhs : File) { return lhs.getName() < rhs.getName(); } var time_ordered_folder : dw.util.ArrayList = folder.sort(modification_comparison); var alphabetic_folder : dw.util.ArrayList = folder.sort(lexigraphic_comparison);
493 | 
494 | **Returns:**
495 | 
496 | a list of File objects or null if this is not a directory.
497 | 
498 | ---
499 | 
500 | ### listFiles
501 | 
502 | **Signature:** `listFiles(filter : Function) : List`
503 | 
504 | **Description:** Returns an array of File objects denoting the files and directories in the directory denoted by this object that satisfy the specified filter. The behavior of this method is the same as that of the listFiles() method, except that the files in the returned array must satisfy the filter. The filter is a Javascript function which accepts one argument, a File, and returns true or false depending on whether the file meets the filter conditions. If the given filter is null then all files are accepted. Otherwise, a file satisfies the filter if and only if the filter returns true. Example usage: // Assume "foo" is an accessible directory. var this_directory : dw.io.File = new File("foo"); function longer_than_3(candidate : dw.io.File) { return candidate.getName().length > 3; } // Find all files in directory foo, one level "down", // such that the filename is longer than 3 characters. var folder_long_names : dw.util.List = this_directory.listFiles(longer_than_3);
505 | 
506 | **Parameters:**
507 | 
508 | - `filter`: a Javascript function which accepts a File argument and returns true or false.
509 | 
510 | **Returns:**
511 | 
512 | list of File objects or null if this is not a directory
513 | 
514 | ---
515 | 
516 | ### md5
517 | 
518 | **Signature:** `md5() : String`
519 | 
520 | **Description:** Returns an MD5 hash of the content of the file of this instance.
521 | 
522 | **Returns:**
523 | 
524 | The MD5 hash of the file's content.
525 | 
526 | **Throws:**
527 | 
528 | Exception - if the file could not be read or is a directory.
529 | 
530 | ---
531 | 
532 | ### mkdir
533 | 
534 | **Signature:** `mkdir() : boolean`
535 | 
536 | **Description:** Creates a directory.
537 | 
538 | **Returns:**
539 | 
540 | true if file creation succeeded, false otherwise.
541 | 
542 | ---
543 | 
544 | ### mkdirs
545 | 
546 | **Signature:** `mkdirs() : boolean`
547 | 
548 | **Description:** Creates a directory, including, its parent directories, as needed.
549 | 
550 | **Returns:**
551 | 
552 | true if file creation succeeded, false otherwise.
553 | 
554 | ---
555 | 
556 | ### remove
557 | 
558 | **Signature:** `remove() : boolean`
559 | 
560 | **Description:** Deletes the file or directory denoted by this object. If this File represents a directory, then the directory must be empty in order to be deleted.
561 | 
562 | **Returns:**
563 | 
564 | true if file deletion succeeded, false otherwise
565 | 
566 | ---
567 | 
568 | ### renameTo
569 | 
570 | **Signature:** `renameTo(file : File) : boolean`
571 | 
572 | **Description:** Rename file.
573 | 
574 | **Parameters:**
575 | 
576 | - `file`: the File object to rename to
577 | 
578 | **Returns:**
579 | 
580 | boolean, true - if file rename succeeded, false - failed
581 | 
582 | ---
583 | 
584 | ### unzip
585 | 
586 | **Signature:** `unzip(root : File) : void`
587 | 
588 | **Description:** Assumes this instance is a zip file. Unzipping it will explode the contents in the directory passed in (root).
589 | 
590 | **Parameters:**
591 | 
592 | - `root`: a File indicating root. root must be a directory.
593 | 
594 | **Throws:**
595 | 
596 | Exception - if the zip files contents can't be exploded.
597 | 
598 | ---
599 | 
600 | ### zip
601 | 
602 | **Signature:** `zip(outputZipFile : File) : void`
603 | 
604 | **Description:** Zip this instance into a new zip file. If you're zipping a directory, the directory itself and all its children files to any level (any number of subdirectories) are included in the zip file. The directory will be the only entry in the archive (single root). If you're zipping a file, then a single entry, the instance, is included in the output zip file. Note that a new File is created. This file is never modified.
605 | 
606 | **Parameters:**
607 | 
608 | - `outputZipFile`: the zip file created.
609 | 
610 | **Throws:**
611 | 
612 | IOException - if the zip file can't be created.
613 | 
614 | ---
```

--------------------------------------------------------------------------------
/docs/dw_system/Response.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.system
  2 | 
  3 | # Class Response
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.system.Response
  9 | 
 10 | ## Description
 11 | 
 12 | Represents an HTTP response in Commerce Cloud Digital. An instance of this class is implicitly available within Digital script under the variable "response". The Response object can be used to set cookies and specific HTTP headers, for directly accessing the output stream or for sending redirects.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### ACCESS_CONTROL_ALLOW_CREDENTIALS
 17 | 
 18 | **Type:** String = "Access-Control-Allow-Credentials"
 19 | 
 20 | An allowed header name constant for Access-Control-Allow-Credentials
 21 | 
 22 | ### ACCESS_CONTROL_ALLOW_HEADERS
 23 | 
 24 | **Type:** String = "Access-Control-Allow-Headers"
 25 | 
 26 | An allowed header name constant for Access-Control-Allow-Headers
 27 | 
 28 | ### ACCESS_CONTROL_ALLOW_METHODS
 29 | 
 30 | **Type:** String = "Access-Control-Allow-Methods"
 31 | 
 32 | An allowed header name constant for Access-Control-Allow-Methods
 33 | 
 34 | ### ACCESS_CONTROL_ALLOW_ORIGIN
 35 | 
 36 | **Type:** String = "Access-Control-Allow-Origin"
 37 | 
 38 | An allowed header name constant for Access-Control-Allow-Origin
 39 | 
 40 | ### ACCESS_CONTROL_EXPOSE_HEADERS
 41 | 
 42 | **Type:** String = "Access-Control-Expose-Headers"
 43 | 
 44 | An allowed header name constant for Access-Control-Expose-Headers
 45 | 
 46 | ### ALLOW
 47 | 
 48 | **Type:** String = "Allow"
 49 | 
 50 | An allowed header name constant for Allow
 51 | 
 52 | ### CONTENT_DISPOSITION
 53 | 
 54 | **Type:** String = "Content-Disposition"
 55 | 
 56 | An allowed header name constant for Content-Disposition
 57 | 
 58 | ### CONTENT_LANGUAGE
 59 | 
 60 | **Type:** String = "Content-Language"
 61 | 
 62 | An allowed header name constant for Content-Language
 63 | 
 64 | ### CONTENT_LOCATION
 65 | 
 66 | **Type:** String = "Content-Location"
 67 | 
 68 | An allowed header name constant for Content-Location
 69 | 
 70 | ### CONTENT_MD5
 71 | 
 72 | **Type:** String = "Content-MD5"
 73 | 
 74 | An allowed header name constant for Content-MD5
 75 | 
 76 | ### CONTENT_SECURITY_POLICY
 77 | 
 78 | **Type:** String = "Content-Security-Policy"
 79 | 
 80 | An allowed header name constant for Content-Security-Policy. Note: The Commerce Cloud platform can override this header for tools like the Storefront Toolkit.
 81 | 
 82 | ### CONTENT_SECURITY_POLICY_REPORT_ONLY
 83 | 
 84 | **Type:** String = "Content-Security-Policy-Report-Only"
 85 | 
 86 | An allowed header name constant for Content-Security-Policy-Report-Only. You can set this response header only for storefront requests. Report recipient can't be a B2C Commerce system.
 87 | 
 88 | ### CONTENT_TYPE
 89 | 
 90 | **Type:** String = "Content-Type"
 91 | 
 92 | An allowed header name constant for Content-Type
 93 | 
 94 | ### CROSS_ORIGIN_EMBEDDER_POLICY
 95 | 
 96 | **Type:** String = "Cross-Origin-Embedder-Policy"
 97 | 
 98 | An allowed header name constant for Cross-Origin-Embedder-Policy
 99 | 
100 | ### CROSS_ORIGIN_EMBEDDER_POLICY_REPORT_ONLY
101 | 
102 | **Type:** String = "Cross-Origin-Embedder-Policy-Report-Only"
103 | 
104 | An allowed header name constant for Cross-Origin-Embedder-Policy-Report-Only. You can set this response header only for storefront requests. Report recipient can't be a B2C Commerce system.
105 | 
106 | ### CROSS_ORIGIN_OPENER_POLICY
107 | 
108 | **Type:** String = "Cross-Origin-Opener-Policy"
109 | 
110 | An allowed header name constant for Cross-Origin-Opener-Policy
111 | 
112 | ### CROSS_ORIGIN_OPENER_POLICY_REPORT_ONLY
113 | 
114 | **Type:** String = "Cross-Origin-Opener-Policy-Report-Only"
115 | 
116 | An allowed header name constant for Cross-Origin-Opener-Policy-Report-Only. You can set this response header only for storefront requests. Report recipient can't be a B2C Commerce system.
117 | 
118 | ### CROSS_ORIGIN_RESOURCE_POLICY
119 | 
120 | **Type:** String = "Cross-Origin-Resource-Policy"
121 | 
122 | An allowed header name constant for Cross-Origin-Resource-Policy
123 | 
124 | ### LINK
125 | 
126 | **Type:** String = "Link"
127 | 
128 | An allowed header name constant for Link
129 | 
130 | ### LOCATION
131 | 
132 | **Type:** String = "Location"
133 | 
134 | An allowed header name constant for Location
135 | 
136 | ### PERMISSIONS_POLICY
137 | 
138 | **Type:** String = "Permissions-Policy"
139 | 
140 | An allowed header name constant for Permissions-Policy
141 | 
142 | ### PLATFORM_FOR_PRIVACY_PREFERENCES_PROJECT
143 | 
144 | **Type:** String = "P3P"
145 | 
146 | An allowed header name constant for Platform for Privacy Preferences Project
147 | 
148 | ### REFERRER_POLICY
149 | 
150 | **Type:** String = "Referrer-Policy"
151 | 
152 | An allowed header name constant for Referrer-Policy
153 | 
154 | ### REFRESH
155 | 
156 | **Type:** String = "Refresh"
157 | 
158 | An allowed header name constant for Refresh
159 | 
160 | ### RETRY_AFTER
161 | 
162 | **Type:** String = "Retry-After"
163 | 
164 | An allowed header name constant for Retry-After
165 | 
166 | ### SERVICE_WORKER_ALLOWED
167 | 
168 | **Type:** String = "service-worker-allowed"
169 | 
170 | An allowed header name constant for service-worker-allowed
171 | 
172 | ### VARY
173 | 
174 | **Type:** String = "Vary"
175 | 
176 | An allowed header name constant for Vary
177 | 
178 | ### X_CONTENT_TYPE_OPTIONS
179 | 
180 | **Type:** String = "X-Content-Type-Options"
181 | 
182 | An allowed header name constant for X-Content-Type-Options
183 | 
184 | ### X_FRAME_OPTIONS
185 | 
186 | **Type:** String = "X-FRAME-OPTIONS"
187 | 
188 | An allowed header name constant for X-FRAME-OPTIONS. Note: The Commerce Cloud platform can override this header for tools like the Storefront Toolkit.
189 | 
190 | ### X_FRAME_OPTIONS_ALLOW_FROM
191 | 
192 | **Type:** String = "ALLOW-FROM"
193 | 
194 | An allowed value ALLOW-FROM for X-FRAME-OPTIONS
195 | 
196 | ### X_FRAME_OPTIONS_DENY_VALUE
197 | 
198 | **Type:** String = "DENY"
199 | 
200 | An allowed value DENY for X-FRAME-OPTIONS
201 | 
202 | ### X_FRAME_OPTIONS_SAMEORIGIN_VALUE
203 | 
204 | **Type:** String = "SAMEORIGIN"
205 | 
206 | An allowed value SAME-ORIGIN value for X-FRAME-OPTIONS
207 | 
208 | ### X_ROBOTS_TAG
209 | 
210 | **Type:** String = "X-Robots-Tag"
211 | 
212 | An allowed header name constant for X-Robots-Tag
213 | 
214 | ### X_XSS_PROTECTION
215 | 
216 | **Type:** String = "X-XSS-Protection"
217 | 
218 | An allowed header name constant for X-XSS-Protection
219 | 
220 | ## Properties
221 | 
222 | ### writer
223 | 
224 | **Type:** PrintWriter (Read Only)
225 | 
226 | A print writer which can be used to print content directly to the response.
227 | 
228 | ## Constructor Summary
229 | 
230 | ## Method Summary
231 | 
232 | ### addHttpCookie
233 | 
234 | **Signature:** `addHttpCookie(cookie : Cookie) : void`
235 | 
236 | Adds the specified cookie to the outgoing response.
237 | 
238 | ### addHttpHeader
239 | 
240 | **Signature:** `addHttpHeader(name : String, value : String) : void`
241 | 
242 | Adds a response header with the given name and value.
243 | 
244 | ### containsHttpHeader
245 | 
246 | **Signature:** `containsHttpHeader(name : String) : boolean`
247 | 
248 | Checks whether the response message header has a field with the specified name.
249 | 
250 | ### getWriter
251 | 
252 | **Signature:** `getWriter() : PrintWriter`
253 | 
254 | Returns a print writer which can be used to print content directly to the response.
255 | 
256 | ### redirect
257 | 
258 | **Signature:** `redirect(url : URL) : void`
259 | 
260 | Sends a temporary redirect response (HTTP status 302) to the client for the specified redirect location URL.
261 | 
262 | ### redirect
263 | 
264 | **Signature:** `redirect(url : URL, status : Number) : void`
265 | 
266 | Sends a redirect response with the given status to the client for the specified redirect location URL.
267 | 
268 | ### redirect
269 | 
270 | **Signature:** `redirect(location : String) : void`
271 | 
272 | Sends a temporary redirect response (HTTP status 302) to the client for the specified redirect location URL.
273 | 
274 | ### redirect
275 | 
276 | **Signature:** `redirect(location : String, status : Number) : void`
277 | 
278 | Sends a redirect response with the given status to the client for the specified redirect location URL.
279 | 
280 | ### redirect
281 | 
282 | **Signature:** `redirect(redirect : URLRedirect) : void`
283 | 
284 | Sends a redirect response with the given status to the client for the specified redirect location URL.
285 | 
286 | ### setBuffered
287 | 
288 | **Signature:** `setBuffered(buffered : boolean) : void`
289 | 
290 | Sets whether the output should be buffered or streamed directly to the client.
291 | 
292 | ### setContentType
293 | 
294 | **Signature:** `setContentType(contentType : String) : void`
295 | 
296 | Sets the content type for this response.
297 | 
298 | ### setExpires
299 | 
300 | **Signature:** `setExpires(expires : Number) : void`
301 | 
302 | Sets the cache expiration time for the response.
303 | 
304 | ### setExpires
305 | 
306 | **Signature:** `setExpires(expires : Date) : void`
307 | 
308 | Convenience method for setExpires(Number) which takes a Date object.
309 | 
310 | ### setHttpHeader
311 | 
312 | **Signature:** `setHttpHeader(name : String, value : String) : void`
313 | 
314 | Adds a response header with the given name and value.
315 | 
316 | ### setStatus
317 | 
318 | **Signature:** `setStatus(status : Number) : void`
319 | 
320 | Sets the HTTP response code.
321 | 
322 | ### setVaryBy
323 | 
324 | **Signature:** `setVaryBy(varyBy : String) : void`
325 | 
326 | Marks the response as personalized with the given variant identifier.
327 | 
328 | ## Method Detail
329 | 
330 | ## Method Details
331 | 
332 | ### addHttpCookie
333 | 
334 | **Signature:** `addHttpCookie(cookie : Cookie) : void`
335 | 
336 | **Description:** Adds the specified cookie to the outgoing response. This method can be called multiple times to set more than one cookie. If a cookie with the same cookie name, domain and path is set multiple times for the same response, only the last set cookie with this name is sent to the client. This method can be used to set, update or delete cookies at the client. If the cookie doesn't exist at the client, it is set initially. If a cookie with the same name, domain and path already exists at the client, it is updated. A cookie can be deleted at the client by submitting a cookie with the maxAge attribute set to 0 (see Cookie.setMaxAge() for more information). Example, how a cookie can be deleted at the client: var cookie : Cookie = new Cookie("SomeName", "Simple Value"); cookie.setMaxAge(0); response.addHttpCookie(cookie); You can't set a cookie's SameSite attribute using the API. The server sets SameSite to None if either the developer sets the cookie's Secure flag or the global security preference Enforce HTTPS is enabled, in which case the Secure flag is also set. Otherwise, the server doesn't set the SameSite attribute and the browser uses its own default SameSite setting. The SameSite attribute is not sent with a cookie if the server detects that the client doesn't correctly interpret the attribute.
337 | 
338 | **Parameters:**
339 | 
340 | - `cookie`: a Cookie object
341 | 
342 | ---
343 | 
344 | ### addHttpHeader
345 | 
346 | **Signature:** `addHttpHeader(name : String, value : String) : void`
347 | 
348 | **Description:** Adds a response header with the given name and value. This method allows response headers to have multiple values. For public headers, only the names listed in the "Constants" section are allowed. Custom header names must begin with the prefix "X-SF-CC-" and can contain only alphanumeric characters, dash, and underscore.
349 | 
350 | **Parameters:**
351 | 
352 | - `name`: the name to use for the response header.
353 | - `value`: the value to use.
354 | 
355 | ---
356 | 
357 | ### containsHttpHeader
358 | 
359 | **Signature:** `containsHttpHeader(name : String) : boolean`
360 | 
361 | **Description:** Checks whether the response message header has a field with the specified name.
362 | 
363 | **Parameters:**
364 | 
365 | - `name`: the name to use.
366 | 
367 | ---
368 | 
369 | ### getWriter
370 | 
371 | **Signature:** `getWriter() : PrintWriter`
372 | 
373 | **Description:** Returns a print writer which can be used to print content directly to the response.
374 | 
375 | ---
376 | 
377 | ### redirect
378 | 
379 | **Signature:** `redirect(url : URL) : void`
380 | 
381 | **Description:** Sends a temporary redirect response (HTTP status 302) to the client for the specified redirect location URL.
382 | 
383 | **Parameters:**
384 | 
385 | - `url`: the URL object for the target location, must be not null
386 | 
387 | ---
388 | 
389 | ### redirect
390 | 
391 | **Signature:** `redirect(url : URL, status : Number) : void`
392 | 
393 | **Description:** Sends a redirect response with the given status to the client for the specified redirect location URL.
394 | 
395 | **Parameters:**
396 | 
397 | - `url`: the URL object with the redirect location, must be not null
398 | - `status`: the status code for this redirect, must be 301, 302 or 307
399 | 
400 | ---
401 | 
402 | ### redirect
403 | 
404 | **Signature:** `redirect(location : String) : void`
405 | 
406 | **Description:** Sends a temporary redirect response (HTTP status 302) to the client for the specified redirect location URL. The target location must be a relative or an absolute URL.
407 | 
408 | **Parameters:**
409 | 
410 | - `location`: the target location as a string, must be not empty
411 | 
412 | ---
413 | 
414 | ### redirect
415 | 
416 | **Signature:** `redirect(location : String, status : Number) : void`
417 | 
418 | **Description:** Sends a redirect response with the given status to the client for the specified redirect location URL.
419 | 
420 | **Parameters:**
421 | 
422 | - `location`: the redirect location, must be not empty
423 | - `status`: the status code for this redirect, must be 301, 302 or 307
424 | 
425 | ---
426 | 
427 | ### redirect
428 | 
429 | **Signature:** `redirect(redirect : URLRedirect) : void`
430 | 
431 | **Description:** Sends a redirect response with the given status to the client for the specified redirect location URL.
432 | 
433 | **Parameters:**
434 | 
435 | - `redirect`: the URLRedirect object with the location and status, must be not null
436 | 
437 | ---
438 | 
439 | ### setBuffered
440 | 
441 | **Signature:** `setBuffered(buffered : boolean) : void`
442 | 
443 | **Description:** Sets whether the output should be buffered or streamed directly to the client. By default, buffering is enabled. The mode can only be changed before anything has been written to the response. Switching buffering off and using streaming mode is recommended for sending large responses.
444 | 
445 | **Parameters:**
446 | 
447 | - `buffered`: if true, buffering is used, if false the response will be streamed
448 | 
449 | ---
450 | 
451 | ### setContentType
452 | 
453 | **Signature:** `setContentType(contentType : String) : void`
454 | 
455 | **Description:** Sets the content type for this response. This method may only be called before any output is written to the response.
456 | 
457 | **Parameters:**
458 | 
459 | - `contentType`: the MIME type of the content, like "text/html", "application/json" etc.
460 | 
461 | ---
462 | 
463 | ### setExpires
464 | 
465 | **Signature:** `setExpires(expires : Number) : void`
466 | 
467 | **Description:** Sets the cache expiration time for the response. The response will only be cached if caching was not disabled previously. By default, responses are not cached. This method can be called multiple times during request processing. If caching is enabled, the lowest expiration time, resulting from the invocations of the method becomes the cache expiration time. This is only used for HTTP requests. Streamed responses cannot be cached. This method is an alternative for setting the cache time using the <iscache> tag in ISML templates.
468 | 
469 | **Parameters:**
470 | 
471 | - `expires`: the expiration time in milliseconds since January 1, 1970, 00:00:00 GMT
472 | 
473 | ---
474 | 
475 | ### setExpires
476 | 
477 | **Signature:** `setExpires(expires : Date) : void`
478 | 
479 | **Description:** Convenience method for setExpires(Number) which takes a Date object.
480 | 
481 | **Parameters:**
482 | 
483 | - `expires`: a Date object.
484 | 
485 | ---
486 | 
487 | ### setHttpHeader
488 | 
489 | **Signature:** `setHttpHeader(name : String, value : String) : void`
490 | 
491 | **Description:** Adds a response header with the given name and value. If one or more value(s) have already been set, the new value overwrites the previous one. The containsHttpHeader(String) method can be used to test for the presence of a header before setting its value. For public headers, only the names listed in the "Constants" section are allowed. Custom header names must begin with the prefix "X-SF-CC-" and can contain only alphanumeric characters, dash, and underscore.
492 | 
493 | **Parameters:**
494 | 
495 | - `name`: the name to use for the response header.
496 | - `value`: the value to use.
497 | 
498 | ---
499 | 
500 | ### setStatus
501 | 
502 | **Signature:** `setStatus(status : Number) : void`
503 | 
504 | **Description:** Sets the HTTP response code.
505 | 
506 | **Parameters:**
507 | 
508 | - `status`: a standard-conform HTTP status code, for example 200 for "OK"
509 | 
510 | ---
511 | 
512 | ### setVaryBy
513 | 
514 | **Signature:** `setVaryBy(varyBy : String) : void`
515 | 
516 | **Description:** Marks the response as personalized with the given variant identifier. Commerce Cloud Digital identifies unique pages based on a combination of pricebook, promotion, sorting rule and A/B test segments, caches the different variants of the page, and then delivers the correct version to the user. If a page is personalized by means other than pricebook, promotion, sorting rule and A/B test, the page must not be cached, because the wrong variants of the page would be delivered to the user. For performance reasons, a page should only be marked as personalized if it really is. Otherwise, the performance can unnecessarily degrade. This method has the same effect as using <iscache varyby="price_promotion" /> tag in an ISML template. Once the vary-by value was set, either using this method or by the <iscache> tag in a template, the entire response is treated as personalized.
517 | 
518 | **Parameters:**
519 | 
520 | - `varyBy`: the variation criteria, currently only "price_promotion" is supported, any other value has no effect
521 | 
522 | ---
```

--------------------------------------------------------------------------------
/docs/best-practices/ocapi_hooks.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Quick Guide: Salesforce B2C Commerce OCAPI Hooks
  2 | 
  3 | This guide provides best practices and examples for implementing OCAPI hooks in Salesforce B2C Commerce Cloud.
  4 | 
  5 | **IMPORTANT**: Before implementing OCAPI hooks, consult the **Performance and Stability Best Practices** guide from this MCP server. Pay special attention to the OCAPI-specific performance requirements and hook development guidelines to ensure optimal performance and avoid database-intensive operations.
  6 | 
  7 | ## 1. Core Concepts
  8 | 
  9 | OCAPI hooks are server-side extension points that allow you to inject custom B2C Commerce Script logic into the lifecycle of an OCAPI request. They are used to augment, validate, or modify the behavior of existing API endpoints.
 10 | 
 11 | ### Hook Types & Execution Order
 12 | 
 13 | There are three main hook types, executed in a specific order for state-changing requests (POST, PATCH, etc.):
 14 | 
 15 | #### `before<HTTP_Method>`
 16 | Executes before core platform logic.
 17 | 
 18 | - **Use Case**: Input validation, data preprocessing, custom authorization checks.
 19 | - **Context**: Runs within the database transaction. Can modify the incoming request document.
 20 | 
 21 | #### `after<HTTP_Method>`
 22 | Executes after core platform logic succeeds but before the response is generated.
 23 | 
 24 | - **Use Case**: Business logic side effects, such as calling an external ERP/OMS, triggering basket recalculation, or saving data to custom objects.
 25 | - **Context**: Runs within the same database transaction. Operates on persistent Script API objects (e.g., `dw.order.Basket`).
 26 | 
 27 | #### `modify<HTTP_Method>Response`
 28 | Executes last, after the platform generates the JSON response.
 29 | 
 30 | - **Use Case**: Final formatting of the JSON response. Add, remove, or reformat attributes (especially `c_` custom attributes) before sending to the client.
 31 | - **Context**: NOT transactional. Attempting to modify persistent data (e.g., `basket.setCustomerEmail()`) will throw an `ORMTransactionException`.
 32 | 
 33 | | Hook Type | Transactional? | Can Modify Persistent Data? | Primary Purpose |
 34 | |-----------|---------------|----------------------------|-----------------|
 35 | | before | Yes | Yes | Validation & Preprocessing |
 36 | | after | Yes | Yes | Business Logic & Side Effects |
 37 | | modifyResponse | No | No | Formatting the JSON Response |
 38 | 
 39 | ## 2. Registration
 40 | 
 41 | Hooks are registered in a custom cartridge via two files.
 42 | 
 43 | ### `package.json` (Cartridge Root)
 44 | 
 45 | This file points to your hooks configuration file.
 46 | 
 47 | ```json
 48 | {
 49 |   "name": "my-hooks-cartridge",
 50 |   "hooks": "./cartridge/hooks.json"
 51 | }
 52 | ```
 53 | 
 54 | ### `hooks.json` (e.g., `/cartridge/hooks.json`)
 55 | 
 56 | This file maps the official hook extension point name to your script file.
 57 | 
 58 | ```json
 59 | {
 60 |   "hooks": [
 61 |     {
 62 |       "name": "dw.ocapi.shop.customer.address.beforePATCH",
 63 |       "script": "./customer/addressValidation.js"
 64 |     },
 65 |     {
 66 |       "name": "dw.ocapi.shop.customer.modifyGETResponse",
 67 |       "script": "./customer/enrichCustomerResponse.js"
 68 |     },
 69 |     {
 70 |       "name": "dw.ocapi.shop.order.afterPOST",
 71 |       "script": "./order/notifyOms.js"
 72 |     }
 73 |   ]
 74 | }
 75 | ```
 76 | 
 77 | ### Recommended Cartridge Structure
 78 | 
 79 | Organize hook scripts by the resource they modify for better maintainability.
 80 | 
 81 | ```
 82 | /cartridge
 83 | └──hooks.json
 84 | └──/scripts
 85 |    └──/hooks
 86 |       ├──/customer
 87 |       │  ├── addressValidation.js
 88 |       │  └── enrichCustomerResponse.js
 89 |       └──/order
 90 |          └── notifyOms.js
 91 | ```
 92 | 
 93 | ## 3. Core Implementation Patterns
 94 | 
 95 | ### Script Structure (CommonJS)
 96 | 
 97 | Hook scripts are CommonJS modules. The function name must be exported and match the hook's method name (e.g., `afterPOST`).
 98 | 
 99 | ```javascript
100 | 'use strict';
101 | var Status = require('dw/system/Status');
102 | 
103 | /**
104 |  * @param {dw.customer.Customer} customer - The customer object.
105 |  * @param {Object} customerResponse - The response document to be modified.
106 |  * @returns {dw.system.Status} - A status object.
107 |  */
108 | exports.modifyGETResponse = function (customer, customerResponse) {
109 |     // Your logic here
110 |     return new Status(Status.OK);
111 | };
112 | ```
113 | 
114 | ### Signaling Success vs. Failure (`dw.system.Status`)
115 | 
116 | Use the Status object to control the execution flow.
117 | 
118 | - **Success**: `return new Status(Status.OK);` or `return void` (for Shop API hooks, void is often preferred to allow other hooks in the cartridge path to run).
119 | - **Controlled Failure**: `return new Status(Status.ERROR, 'ERROR_CODE', 'Descriptive message.');` This halts execution, rolls back the transaction, and returns an HTTP 400 error with a fault document containing your code and message.
120 | 
121 | ### Data Integrity (`dw.system.Transaction`)
122 | 
123 | All modifications to persistent data in `before` or `after` hooks must be wrapped in a transaction.
124 | 
125 | ```javascript
126 | var Transaction = require('dw/system/Transaction');
127 | 
128 | Transaction.wrap(function () {
129 |     customer.getProfile().custom.lastAddressChange = new Date();
130 | });
131 | ```
132 | 
133 | ## 4. Code Examples
134 | 
135 | ### Example 1: Custom Validation (before hook)
136 | 
137 | Reject an address update if the US postal code format is invalid.
138 | 
139 | **Hook**: `dw.ocapi.shop.customer.address.beforePATCH`  
140 | **Script**: `cartridge/scripts/hooks/customer/addressValidation.js`
141 | 
142 | ```javascript
143 | 'use strict';
144 | 
145 | var Status = require('dw/system/Status');
146 | 
147 | exports.beforePATCH = function (customer, addressName, customerAddress) {
148 |     var countryCode = customerAddress.country_code;
149 |     var postalCode = customerAddress.postal_code;
150 | 
151 |     if (countryCode === 'US' && postalCode) {
152 |         var postalCodeRegex = /^\d{5}(-\d{4})?$/;
153 |         if (!postalCodeRegex.test(postalCode)) {
154 |             // Reject the request with a specific error
155 |             return new Status(Status.ERROR, 'INVALID_POSTAL_CODE', 'The postal code format is invalid for the United States.');
156 |         }
157 |     }
158 |     return new Status(Status.OK);
159 | };
160 | ```
161 | 
162 | ### Example 2: Enriching a Response (modifyResponse hook)
163 | 
164 | Add a custom flag `c_isPreferredCustomer` to the customer GET response.
165 | 
166 | **Hook**: `dw.ocapi.shop.customer.modifyGETResponse`  
167 | **Script**: `cartridge/scripts/hooks/customer/enrichCustomerResponse.js`
168 | 
169 | ```javascript
170 | 'use strict';
171 | 
172 | var Status = require('dw/system/Status');
173 | 
174 | exports.modifyGETResponse = function (customer, customerResponse) {
175 |     // Logic to determine if the customer is preferred
176 |     var isPreferred = customer.isMemberOfCustomerGroup('Preferred');
177 | 
178 |     // Add a custom attribute directly to the response document.
179 |     // This does NOT save anything to the database.
180 |     customerResponse.c_isPreferredCustomer = isPreferred;
181 | 
182 |     return new Status(Status.OK);
183 | };
184 | ```
185 | 
186 | ### Example 3: External Service Integration (after hook)
187 | 
188 | Notify an external Order Management System (OMS) after an order is created.
189 | 
190 | **Hook**: `dw.ocapi.shop.order.afterPOST`  
191 | **Script**: `cartridge/scripts/hooks/order/notifyOms.js`
192 | 
193 | ```javascript
194 | 'use strict';
195 | 
196 | var Status = require('dw/system/Status');
197 | var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry');
198 | var Logger = require('dw/system/Logger').getLogger('OmsIntegrationHook');
199 | 
200 | exports.afterPOST = function (order) {
201 |     try {
202 |         var omsService = LocalServiceRegistry.createService('oms.http.service', { /* service config */ });
203 |         var payload = { orderNo: order.getOrderNo(), total: order.getTotalGrossPrice().getValue() };
204 |         var result = omsService.call({ payload: payload });
205 | 
206 |         if (!result.isOk()) {
207 |             // Log the error for monitoring, but don't return Status.ERROR.
208 |             // The order is already created; returning an error here would be misleading to the client.
209 |             Logger.error('Failed to notify OMS for order {0}. Error: {1}', order.getOrderNo(), result.getErrorMessage());
210 |         }
211 |     } catch (e) {
212 |         Logger.error('Exception notifying OMS for order {0}. Exception: {1}', order.getOrderNo(), e.toString());
213 |     }
214 | 
215 |     // Always return OK because the primary operation (order creation) was successful.
216 |     return new Status(Status.OK);
217 | };
218 | ```
219 | 
220 | ## 5. Key Best Practices
221 | 
222 | ### Performance
223 | 
224 | - **DON'T** perform expensive database lookups inside a hook (e.g., `ProductMgr.getProduct()`).
225 | - **DO** be aware of caching. Hooks on cacheable GET endpoints only run on a cache miss.
226 | - **DO** keep hook logic simple and efficient.
227 | 
228 | ### Security
229 | 
230 | - **DO** treat all client input as untrusted. Sanitize and validate data in before hooks.
231 | - **DO** re-authorize sensitive actions within the hook. For example, use `OrderMgr.getOrder(orderNumber, orderToken)` instead of just `OrderMgr.getOrder(orderNumber)`.
232 | 
233 | ### Error Handling & Resilience
234 | 
235 | - **DO** wrap all hook logic in `try/catch` blocks.
236 | - **DO** use `dw.system.Logger` with custom categories and include the `request.requestID` for easy tracing.
237 | - **BE AWARE** of the Hook Circuit Breaker. If a hook fails more than 50% of the time in its last 100 executions, it will be disabled for 60 seconds, returning HTTP 503.
238 | 
239 | ### Testing
240 | 
241 | - **DO** use the `dw-api-mock` library to unit test hook logic locally in a Node.js environment.
242 | - **DO** use API clients like Postman for integration testing on a sandbox.
243 | 
244 | ## 6. Comprehensive Hook Reference
245 | 
246 | ### Shop API Hooks
247 | 
248 | | API Endpoint (Method & Path) | Available Hook Extension Points |
249 | |------------------------------|----------------------------------|
250 | | **Authentication** |  |
251 | | `POST /customers/auth` | `dw.ocapi.shop.auth.beforePOST`, `dw.ocapi.shop.auth.afterPOST`, `dw.ocapi.shop.auth.modifyPOSTResponse` |
252 | | **Basket** |  |
253 | | `POST /baskets` | `dw.ocapi.shop.basket.beforePOST_v2`, `dw.ocapi.shop.basket.afterPOST`, `dw.ocapi.shop.basket.modifyPOSTResponse`, `dw.ocapi.shop.basket.validateBasket` |
254 | | `GET /baskets/{basket_id}` | `dw.ocapi.shop.basket.beforeGET`, `dw.ocapi.shop.basket.modifyGETResponse`, `dw.ocapi.shop.basket.validateBasket` |
255 | | `PATCH /baskets/{basket_id}` | `dw.ocapi.shop.basket.beforePATCH`, `dw.ocapi.shop.basket.afterPATCH`, `dw.ocapi.shop.basket.modifyPATCHResponse`, `dw.ocapi.shop.basket.validateBasket` |
256 | | `DELETE /baskets/{basket_id}` | `dw.ocapi.shop.basket.beforeDELETE`, `dw.ocapi.shop.basket.afterDELETE` |
257 | | `PUT /baskets/{basket_id}/billing_address` | `dw.ocapi.shop.basket.billing_address.beforePUT`, `dw.ocapi.shop.basket.billing_address.afterPUT`, `dw.ocapi.shop.basket.billing_address.modifyPUTResponse` |
258 | | `POST /baskets/{basket_id}/coupons` | `dw.ocapi.shop.basket.coupon.beforePOST`, `dw.ocapi.shop.basket.coupon.afterPOST`, `dw.ocapi.shop.basket.coupon.modifyPOSTResponse` |
259 | | `DELETE /baskets/{basket_id}/coupons/{coupon_item_id}` | `dw.ocapi.shop.basket.coupon.beforeDELETE`, `dw.ocapi.shop.basket.coupon.afterDELETE`, `dw.ocapi.shop.basket.coupon.modifyDELETEResponse` |
260 | | `POST /baskets/{basket_id}/items` | `dw.ocapi.shop.basket.items.beforePOST`, `dw.ocapi.shop.basket.items.afterPOST`, `dw.ocapi.shop.basket.items.modifyPOSTResponse` |
261 | | `POST /baskets/{basket_id}/payment_instruments` | `dw.ocapi.shop.basket.payment_instrument.beforePOST`, `dw.ocapi.shop.basket.payment_instrument.afterPOST`, `dw.ocapi.shop.basket.payment_instrument.modifyPOSTResponse` |
262 | | **Customer** |  |
263 | | `POST /customers` | `dw.ocapi.shop.customer.beforePOST`, `dw.ocapi.shop.customer.afterPOST`, `dw.ocapi.shop.customer.modifyPOSTResponse` |
264 | | `GET /customers/{customer_id}` | `dw.ocapi.shop.customer.beforeGET`, `dw.ocapi.shop.customer.modifyGETResponse` |
265 | | `PATCH /customers/{customer_id}` | `dw.ocapi.shop.customer.beforePATCH`, `dw.ocapi.shop.customer.afterPATCH`, `dw.ocapi.shop.customer.modifyPATCHResponse` |
266 | | `POST /customers/{customer_id}/addresses` | `dw.ocapi.shop.customer.addresses.beforePOST`, `dw.ocapi.shop.customer.addresses.afterPOST`, `dw.ocapi.shop.customer.address.modifyPOSTResponse` |
267 | | `PATCH /customers/{customer_id}/addresses/{address_name}` | `dw.ocapi.shop.customer.address.beforePATCH`, `dw.ocapi.shop.customer.address.afterPATCH`, `dw.ocapi.shop.customer.address.modifyPATCHResponse` |
268 | | `DELETE /customers/{customer_id}/addresses/{address_name}` | `dw.ocapi.shop.customer.address.beforeDELETE`, `dw.ocapi.shop.customer.address.afterDELETE` |
269 | | **Order** |  |
270 | | `POST /orders` | `dw.ocapi.shop.order.beforePOST`, `dw.ocapi.shop.order.afterPOST`, `dw.ocapi.shop.order.modifyPOSTResponse` |
271 | | `GET /orders/{order_no}` | `dw.ocapi.shop.order.beforeGET`, `dw.ocapi.shop.order.modifyGETResponse` |
272 | | `PATCH /orders/{order_no}` | `dw.ocapi.shop.order.beforePATCH`, `dw.ocapi.shop.order.afterPATCH`, `dw.ocapi.shop.order.modifyPATCHResponse` |
273 | | `POST /orders/{order_no}/payment_instruments` | `dw.ocapi.shop.order.payment_instrument.beforePOST`, `dw.ocapi.shop.order.payment_instrument.afterPOST`, `dw.ocapi.shop.order.payment_instrument.modifyPOSTResponse` |
274 | | **Product & Catalog** |  |
275 | | `GET /products/{id}` | `dw.ocapi.shop.product.beforeGET`, `dw.ocapi.shop.product.modifyGETResponse` |
276 | | `GET /product_search` | `dw.ocapi.shop.product_search.beforeGET`, `dw.ocapi.shop.product_search.modifyGETResponse` |
277 | | `GET /categories/{id}` | `dw.ocapi.shop.category.beforeGET`, `dw.ocapi.shop.category.modifyGETResponse` |
278 | | `GET /content/{id}` | `dw.ocapi.shop.content.beforeGET`, `dw.ocapi.shop.content.modifyGETResponse` |
279 | 
280 | ### Data API Hooks
281 | 
282 | | API Endpoint (Method & Path) | Available Hook Extension Points |
283 | |------------------------------|----------------------------------|
284 | | **Custom Object** |  |
285 | | `PUT /custom_objects/{object_type}/{key}` | `dw.ocapi.data.object.beforePut`, `dw.ocapi.data.object.afterPut` |
286 | | `PATCH /custom_objects/{object_type}/{key}` | `dw.ocapi.data.object.beforePatch`, `dw.ocapi.data.object.afterPatch` |
287 | | `DELETE /custom_objects/{object_type}/{key}` | `dw.ocapi.data.object.beforeDelete`, `dw.ocapi.data.object.afterDelete` |
288 | | **Customer** |  |
289 | | `POST /customer_lists/{list_id}/customers` | `dw.ocapi.data.customer_list.customers.beforePOST`, `dw.ocapi.data.customer_list.customers.afterPOST` |
290 | | `PATCH /customer_lists/{list_id}/customers/{customer_no}` | `dw.ocapi.data.customer_list.customer.beforePATCH`, `dw.ocapi.data.customer_list.customer.afterPATCH` |
291 | | `POST /customer_lists/{list_id}/customers/{customer_no}/addresses` | `dw.ocapi.data.customer_list.customer.addresses.beforePOST`, `dw.ocapi.data.customer_list.customer.addresses.afterPOST` |
292 | | **Content** |  |
293 | | `PUT /libraries/{library_id}/content/{content_id}` | `dw.ocapi.data.content.content.beforeCreate`, `dw.ocapi.data.content.content.afterCreate` |
294 | | `PATCH /libraries/{library_id}/content/{content_id}` | `dw.ocapi.data.content.content.beforeUpdate`, `dw.ocapi.data.content.content.afterUpdate` |
295 | | `DELETE /libraries/{library_id}/content/{content_id}` | `dw.ocapi.data.content.content.beforeDelete`, `dw.ocapi.data.content.content.afterDelete` |
296 | | **User** |  |
297 | | `PATCH /users/this/password` | `dw.ocapi.data.users.afterPATCH` |
298 | 
299 | ## Troubleshooting Hook Registration
300 | 
301 | **If OCAPI hooks are not executing after deployment:**
302 | 
303 | 1. **Check Code Version**: If hooks don't execute after upload:
304 |    - **Check Available Versions**: Use MCP `get_code_versions` tool to see all code versions on the instance
305 |    - **Activate Different Version**: Use MCP `activate_code_version` tool to switch code versions
306 |    - **Alternative Manual Method**: Switch code versions in Business Manager (Administration > Site Development > Code Deployment > Activate)
307 | 2. **Verify Hook Registration**: Check logs for hook registration confirmations after version activation
308 | 3. **Test Hook Execution**: Make OCAPI calls to endpoints that should trigger your hooks and verify they execute
309 | 4. **Verify API Settings**: Ensure OCAPI settings in Business Manager allow your endpoints and include proper hook configurations
310 | 
311 | **Common Hook Issues:**
312 | - Hooks not triggering → Check code version activation and OCAPI settings
313 | - Hook scripts not found → Verify file paths match registration in hooks.json
314 | - Runtime errors in hooks → Check logs for specific error messages during hook execution
315 | - Data API hooks → Ensure proper authentication and permissions are configured
316 | 
```

--------------------------------------------------------------------------------
/docs/dw_extensions.pinterest/PinterestProduct.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Package: dw.extensions.pinterest
  2 | 
  3 | # Class PinterestProduct
  4 | 
  5 | ## Inheritance Hierarchy
  6 | 
  7 | - Object
  8 |   - dw.extensions.pinterest.PinterestProduct
  9 | 
 10 | ## Description
 11 | 
 12 | Represents a row in the Pinterest catalog feed export.
 13 | 
 14 | ## Constants
 15 | 
 16 | ### AVAILABILITY_IN_STOCK
 17 | 
 18 | **Type:** String = "in
 19 | 
 20 | Indicates that the product is in stock.
 21 | 
 22 | ### AVAILABILITY_OUT_OF_STOCK
 23 | 
 24 | **Type:** String = "out
 25 | 
 26 | Indicates that the product is not in stock.
 27 | 
 28 | ### AVAILABILITY_PREORDER
 29 | 
 30 | **Type:** String = "preorder"
 31 | 
 32 | Indicates that the product is availabile in preorder.
 33 | 
 34 | ### CONDITION_NEW
 35 | 
 36 | **Type:** String = "new"
 37 | 
 38 | Indicates that the product has never been used.
 39 | 
 40 | ### CONDITION_REFURBISHED
 41 | 
 42 | **Type:** String = "refurbished"
 43 | 
 44 | Indicates that the product has been used but refurbished.
 45 | 
 46 | ### CONDITION_USED
 47 | 
 48 | **Type:** String = "used"
 49 | 
 50 | Indicates that the product has been used.
 51 | 
 52 | ## Properties
 53 | 
 54 | ### availability
 55 | 
 56 | **Type:** String
 57 | 
 58 | The availability of the Pinterest product. Possible values are
 59 |  AVAILABILITY_IN_STOCK or
 60 |  AVAILABILITY_OUT_OF_STOCK.
 61 | 
 62 | ### brand
 63 | 
 64 | **Type:** String
 65 | 
 66 | The Pinterest brand of the product.
 67 | 
 68 | ### color
 69 | 
 70 | **Type:** String
 71 | 
 72 | The Pinterest color value label of the product.
 73 | 
 74 | ### colorHex
 75 | 
 76 | **Type:** String
 77 | 
 78 | The Pinterest color hex value of the product.
 79 | 
 80 | ### colorImage
 81 | 
 82 | **Type:** URL
 83 | 
 84 | The URL of the image to show in Pinterest for the product color (swatch).
 85 | 
 86 | ### condition
 87 | 
 88 | **Type:** String
 89 | 
 90 | The condition of the Pinterest product. Possible values are
 91 |  CONDITION_NEW,
 92 |  CONDITION_REFURBISHED, or
 93 |  CONDITION_USED.
 94 | 
 95 | ### description
 96 | 
 97 | **Type:** String
 98 | 
 99 | The Pinterest description of the product.
100 | 
101 | ### googleProductCategory
102 | 
103 | **Type:** String
104 | 
105 | The category of this product in the Google category taxonomy.
106 | 
107 | ### gtin
108 | 
109 | **Type:** String
110 | 
111 | The Pinterest GTIN of the product.
112 | 
113 | ### ID
114 | 
115 | **Type:** String (Read Only)
116 | 
117 | The ID of the Pinterest product. This is the same as the ID of the Demandware product.
118 | 
119 | ### imageLinks
120 | 
121 | **Type:** List
122 | 
123 | A list containing the URLs of the image to show in Pinterest for the product.
124 | 
125 | ### itemGroupID
126 | 
127 | **Type:** String
128 | 
129 | The ID of the Pinterest item group for the product, that is, its master product.
130 | 
131 | ### itemGroupLink
132 | 
133 | **Type:** URL
134 | 
135 | The URL of the Pinterest item group for the product, that is, the link to its master product in the
136 |  Demandware storefront.
137 | 
138 | ### link
139 | 
140 | **Type:** URL
141 | 
142 | The URL of the Demandware storefront link to the product.
143 | 
144 | ### maxPrice
145 | 
146 | **Type:** Money
147 | 
148 | The maximum price to show in Pinterest for the product.
149 | 
150 | ### minPrice
151 | 
152 | **Type:** Money
153 | 
154 | The minimum price to show in Pinterest for the product.
155 | 
156 | ### price
157 | 
158 | **Type:** Money
159 | 
160 | The price to show in Pinterest for the product.
161 | 
162 | ### productCategory
163 | 
164 | **Type:** String
165 | 
166 | The Pinterest category path of the product.
167 | 
168 | ### returnPolicy
169 | 
170 | **Type:** String
171 | 
172 | The Pinterest return policy of the product.
173 | 
174 | ### size
175 | 
176 | **Type:** String
177 | 
178 | The Pinterest size value label of the product.
179 | 
180 | ### title
181 | 
182 | **Type:** String
183 | 
184 | The Pinterest title of the product.
185 | 
186 | ## Constructor Summary
187 | 
188 | ## Method Summary
189 | 
190 | ### getAvailability
191 | 
192 | **Signature:** `getAvailability() : String`
193 | 
194 | Returns the availability of the Pinterest product.
195 | 
196 | ### getBrand
197 | 
198 | **Signature:** `getBrand() : String`
199 | 
200 | Returns the Pinterest brand of the product.
201 | 
202 | ### getColor
203 | 
204 | **Signature:** `getColor() : String`
205 | 
206 | Returns the Pinterest color value label of the product.
207 | 
208 | ### getColorHex
209 | 
210 | **Signature:** `getColorHex() : String`
211 | 
212 | Returns the Pinterest color hex value of the product.
213 | 
214 | ### getColorImage
215 | 
216 | **Signature:** `getColorImage() : URL`
217 | 
218 | Returns the URL of the image to show in Pinterest for the product color (swatch).
219 | 
220 | ### getCondition
221 | 
222 | **Signature:** `getCondition() : String`
223 | 
224 | Returns the condition of the Pinterest product.
225 | 
226 | ### getDescription
227 | 
228 | **Signature:** `getDescription() : String`
229 | 
230 | Returns the Pinterest description of the product.
231 | 
232 | ### getGoogleProductCategory
233 | 
234 | **Signature:** `getGoogleProductCategory() : String`
235 | 
236 | Returns the category of this product in the Google category taxonomy.
237 | 
238 | ### getGtin
239 | 
240 | **Signature:** `getGtin() : String`
241 | 
242 | Returns the Pinterest GTIN of the product.
243 | 
244 | ### getID
245 | 
246 | **Signature:** `getID() : String`
247 | 
248 | Returns the ID of the Pinterest product.
249 | 
250 | ### getImageLinks
251 | 
252 | **Signature:** `getImageLinks() : List`
253 | 
254 | Returns a list containing the URLs of the image to show in Pinterest for the product.
255 | 
256 | ### getItemGroupID
257 | 
258 | **Signature:** `getItemGroupID() : String`
259 | 
260 | Returns the ID of the Pinterest item group for the product, that is, its master product.
261 | 
262 | ### getItemGroupLink
263 | 
264 | **Signature:** `getItemGroupLink() : URL`
265 | 
266 | Returns the URL of the Pinterest item group for the product, that is, the link to its master product in the Demandware storefront.
267 | 
268 | ### getLink
269 | 
270 | **Signature:** `getLink() : URL`
271 | 
272 | Returns the URL of the Demandware storefront link to the product.
273 | 
274 | ### getMaxPrice
275 | 
276 | **Signature:** `getMaxPrice() : Money`
277 | 
278 | Returns the maximum price to show in Pinterest for the product.
279 | 
280 | ### getMinPrice
281 | 
282 | **Signature:** `getMinPrice() : Money`
283 | 
284 | Returns the minimum price to show in Pinterest for the product.
285 | 
286 | ### getPrice
287 | 
288 | **Signature:** `getPrice() : Money`
289 | 
290 | Returns the price to show in Pinterest for the product.
291 | 
292 | ### getProductCategory
293 | 
294 | **Signature:** `getProductCategory() : String`
295 | 
296 | Returns the Pinterest category path of the product.
297 | 
298 | ### getReturnPolicy
299 | 
300 | **Signature:** `getReturnPolicy() : String`
301 | 
302 | Returns the Pinterest return policy of the product.
303 | 
304 | ### getSize
305 | 
306 | **Signature:** `getSize() : String`
307 | 
308 | Returns the Pinterest size value label of the product.
309 | 
310 | ### getTitle
311 | 
312 | **Signature:** `getTitle() : String`
313 | 
314 | Returns the Pinterest title of the product.
315 | 
316 | ### setAvailability
317 | 
318 | **Signature:** `setAvailability(availability : String) : void`
319 | 
320 | Sets the availability of the Pinterest product.
321 | 
322 | ### setBrand
323 | 
324 | **Signature:** `setBrand(brand : String) : void`
325 | 
326 | Sets the Pinterest brand of the product.
327 | 
328 | ### setColor
329 | 
330 | **Signature:** `setColor(color : String) : void`
331 | 
332 | Sets the Pinterest color value label of the product.
333 | 
334 | ### setColorHex
335 | 
336 | **Signature:** `setColorHex(colorHex : String) : void`
337 | 
338 | Sets the Pinterest color hex value of the product.
339 | 
340 | ### setColorImage
341 | 
342 | **Signature:** `setColorImage(colorImage : URL) : void`
343 | 
344 | Sets the URL of the image to show in Pinterest for the product color (swatch).
345 | 
346 | ### setCondition
347 | 
348 | **Signature:** `setCondition(condition : String) : void`
349 | 
350 | Sets the condition of the Pinterest product.
351 | 
352 | ### setDescription
353 | 
354 | **Signature:** `setDescription(description : String) : void`
355 | 
356 | Sets the Pinterest description of the product.
357 | 
358 | ### setGoogleProductCategory
359 | 
360 | **Signature:** `setGoogleProductCategory(googleProductCategory : String) : void`
361 | 
362 | Sets the category of this product in the Google category taxonomy.
363 | 
364 | ### setGtin
365 | 
366 | **Signature:** `setGtin(gtin : String) : void`
367 | 
368 | Sets the Pinterest GTIN of the product.
369 | 
370 | ### setImageLinks
371 | 
372 | **Signature:** `setImageLinks(imageLinks : List) : void`
373 | 
374 | Sets the list of URLs of images to show in Pinterest for the product.
375 | 
376 | ### setItemGroupID
377 | 
378 | **Signature:** `setItemGroupID(itemGroupID : String) : void`
379 | 
380 | Sets the ID of the Pinterest item group for the product, that is, its master product.
381 | 
382 | ### setItemGroupLink
383 | 
384 | **Signature:** `setItemGroupLink(itemGroupLink : URL) : void`
385 | 
386 | Sets the URL of the Pinterest item group for the product, that is, the link to its master product in the Demandware storefront.
387 | 
388 | ### setLink
389 | 
390 | **Signature:** `setLink(link : URL) : void`
391 | 
392 | Sets the URL of the Demandware storefront link to the product.
393 | 
394 | ### setMaxPrice
395 | 
396 | **Signature:** `setMaxPrice(maxPrice : Money) : void`
397 | 
398 | Sets the maximum price to show in Pinterest for the product.
399 | 
400 | ### setMinPrice
401 | 
402 | **Signature:** `setMinPrice(minPrice : Money) : void`
403 | 
404 | Sets the minimum price to show in Pinterest for the product.
405 | 
406 | ### setPrice
407 | 
408 | **Signature:** `setPrice(price : Money) : void`
409 | 
410 | Sets the price to show in Pinterest for the product.
411 | 
412 | ### setProductCategory
413 | 
414 | **Signature:** `setProductCategory(productCategory : String) : void`
415 | 
416 | Sets the Pinterest category path of the product.
417 | 
418 | ### setReturnPolicy
419 | 
420 | **Signature:** `setReturnPolicy(returnPolicy : String) : void`
421 | 
422 | Sets the Pinterest return policy of the product.
423 | 
424 | ### setSize
425 | 
426 | **Signature:** `setSize(size : String) : void`
427 | 
428 | Sets the Pinterest size value label of the product.
429 | 
430 | ### setTitle
431 | 
432 | **Signature:** `setTitle(title : String) : void`
433 | 
434 | Sets the Pinterest title of the product.
435 | 
436 | ## Method Detail
437 | 
438 | ## Method Details
439 | 
440 | ### getAvailability
441 | 
442 | **Signature:** `getAvailability() : String`
443 | 
444 | **Description:** Returns the availability of the Pinterest product. Possible values are AVAILABILITY_IN_STOCK or AVAILABILITY_OUT_OF_STOCK.
445 | 
446 | ---
447 | 
448 | ### getBrand
449 | 
450 | **Signature:** `getBrand() : String`
451 | 
452 | **Description:** Returns the Pinterest brand of the product.
453 | 
454 | ---
455 | 
456 | ### getColor
457 | 
458 | **Signature:** `getColor() : String`
459 | 
460 | **Description:** Returns the Pinterest color value label of the product.
461 | 
462 | ---
463 | 
464 | ### getColorHex
465 | 
466 | **Signature:** `getColorHex() : String`
467 | 
468 | **Description:** Returns the Pinterest color hex value of the product.
469 | 
470 | ---
471 | 
472 | ### getColorImage
473 | 
474 | **Signature:** `getColorImage() : URL`
475 | 
476 | **Description:** Returns the URL of the image to show in Pinterest for the product color (swatch).
477 | 
478 | ---
479 | 
480 | ### getCondition
481 | 
482 | **Signature:** `getCondition() : String`
483 | 
484 | **Description:** Returns the condition of the Pinterest product. Possible values are CONDITION_NEW, CONDITION_REFURBISHED, or CONDITION_USED.
485 | 
486 | ---
487 | 
488 | ### getDescription
489 | 
490 | **Signature:** `getDescription() : String`
491 | 
492 | **Description:** Returns the Pinterest description of the product.
493 | 
494 | ---
495 | 
496 | ### getGoogleProductCategory
497 | 
498 | **Signature:** `getGoogleProductCategory() : String`
499 | 
500 | **Description:** Returns the category of this product in the Google category taxonomy.
501 | 
502 | ---
503 | 
504 | ### getGtin
505 | 
506 | **Signature:** `getGtin() : String`
507 | 
508 | **Description:** Returns the Pinterest GTIN of the product.
509 | 
510 | ---
511 | 
512 | ### getID
513 | 
514 | **Signature:** `getID() : String`
515 | 
516 | **Description:** Returns the ID of the Pinterest product. This is the same as the ID of the Demandware product.
517 | 
518 | **Returns:**
519 | 
520 | product ID
521 | 
522 | ---
523 | 
524 | ### getImageLinks
525 | 
526 | **Signature:** `getImageLinks() : List`
527 | 
528 | **Description:** Returns a list containing the URLs of the image to show in Pinterest for the product.
529 | 
530 | ---
531 | 
532 | ### getItemGroupID
533 | 
534 | **Signature:** `getItemGroupID() : String`
535 | 
536 | **Description:** Returns the ID of the Pinterest item group for the product, that is, its master product.
537 | 
538 | ---
539 | 
540 | ### getItemGroupLink
541 | 
542 | **Signature:** `getItemGroupLink() : URL`
543 | 
544 | **Description:** Returns the URL of the Pinterest item group for the product, that is, the link to its master product in the Demandware storefront.
545 | 
546 | ---
547 | 
548 | ### getLink
549 | 
550 | **Signature:** `getLink() : URL`
551 | 
552 | **Description:** Returns the URL of the Demandware storefront link to the product.
553 | 
554 | ---
555 | 
556 | ### getMaxPrice
557 | 
558 | **Signature:** `getMaxPrice() : Money`
559 | 
560 | **Description:** Returns the maximum price to show in Pinterest for the product.
561 | 
562 | ---
563 | 
564 | ### getMinPrice
565 | 
566 | **Signature:** `getMinPrice() : Money`
567 | 
568 | **Description:** Returns the minimum price to show in Pinterest for the product.
569 | 
570 | ---
571 | 
572 | ### getPrice
573 | 
574 | **Signature:** `getPrice() : Money`
575 | 
576 | **Description:** Returns the price to show in Pinterest for the product.
577 | 
578 | ---
579 | 
580 | ### getProductCategory
581 | 
582 | **Signature:** `getProductCategory() : String`
583 | 
584 | **Description:** Returns the Pinterest category path of the product.
585 | 
586 | ---
587 | 
588 | ### getReturnPolicy
589 | 
590 | **Signature:** `getReturnPolicy() : String`
591 | 
592 | **Description:** Returns the Pinterest return policy of the product.
593 | 
594 | ---
595 | 
596 | ### getSize
597 | 
598 | **Signature:** `getSize() : String`
599 | 
600 | **Description:** Returns the Pinterest size value label of the product.
601 | 
602 | ---
603 | 
604 | ### getTitle
605 | 
606 | **Signature:** `getTitle() : String`
607 | 
608 | **Description:** Returns the Pinterest title of the product.
609 | 
610 | ---
611 | 
612 | ### setAvailability
613 | 
614 | **Signature:** `setAvailability(availability : String) : void`
615 | 
616 | **Description:** Sets the availability of the Pinterest product. Possible values are AVAILABILITY_IN_STOCK or AVAILABILITY_OUT_OF_STOCK.
617 | 
618 | **Parameters:**
619 | 
620 | - `availability`: the availability status to set for this product
621 | 
622 | ---
623 | 
624 | ### setBrand
625 | 
626 | **Signature:** `setBrand(brand : String) : void`
627 | 
628 | **Description:** Sets the Pinterest brand of the product.
629 | 
630 | **Parameters:**
631 | 
632 | - `brand`: Pinterest brand
633 | 
634 | ---
635 | 
636 | ### setColor
637 | 
638 | **Signature:** `setColor(color : String) : void`
639 | 
640 | **Description:** Sets the Pinterest color value label of the product.
641 | 
642 | **Parameters:**
643 | 
644 | - `color`: Pinterest color value label
645 | 
646 | ---
647 | 
648 | ### setColorHex
649 | 
650 | **Signature:** `setColorHex(colorHex : String) : void`
651 | 
652 | **Description:** Sets the Pinterest color hex value of the product.
653 | 
654 | **Parameters:**
655 | 
656 | - `colorHex`: Pinterest color hex value
657 | 
658 | ---
659 | 
660 | ### setColorImage
661 | 
662 | **Signature:** `setColorImage(colorImage : URL) : void`
663 | 
664 | **Description:** Sets the URL of the image to show in Pinterest for the product color (swatch).
665 | 
666 | **Parameters:**
667 | 
668 | - `colorImage`: link to Pinterest color image
669 | 
670 | ---
671 | 
672 | ### setCondition
673 | 
674 | **Signature:** `setCondition(condition : String) : void`
675 | 
676 | **Description:** Sets the condition of the Pinterest product. Possible values are CONDITION_NEW, CONDITION_REFURBISHED, or CONDITION_USED.
677 | 
678 | **Parameters:**
679 | 
680 | - `condition`: the condition status to set for this product
681 | 
682 | ---
683 | 
684 | ### setDescription
685 | 
686 | **Signature:** `setDescription(description : String) : void`
687 | 
688 | **Description:** Sets the Pinterest description of the product.
689 | 
690 | **Parameters:**
691 | 
692 | - `description`: Pinterest description
693 | 
694 | ---
695 | 
696 | ### setGoogleProductCategory
697 | 
698 | **Signature:** `setGoogleProductCategory(googleProductCategory : String) : void`
699 | 
700 | **Description:** Sets the category of this product in the Google category taxonomy.
701 | 
702 | **Parameters:**
703 | 
704 | - `googleProductCategory`: Google product category
705 | 
706 | ---
707 | 
708 | ### setGtin
709 | 
710 | **Signature:** `setGtin(gtin : String) : void`
711 | 
712 | **Description:** Sets the Pinterest GTIN of the product.
713 | 
714 | **Parameters:**
715 | 
716 | - `gtin`: Pinterest GTIN
717 | 
718 | ---
719 | 
720 | ### setImageLinks
721 | 
722 | **Signature:** `setImageLinks(imageLinks : List) : void`
723 | 
724 | **Description:** Sets the list of URLs of images to show in Pinterest for the product.
725 | 
726 | **Parameters:**
727 | 
728 | - `imageLinks`: links to the product images
729 | 
730 | ---
731 | 
732 | ### setItemGroupID
733 | 
734 | **Signature:** `setItemGroupID(itemGroupID : String) : void`
735 | 
736 | **Description:** Sets the ID of the Pinterest item group for the product, that is, its master product.
737 | 
738 | **Parameters:**
739 | 
740 | - `itemGroupID`: ID of Pinterest item group
741 | 
742 | ---
743 | 
744 | ### setItemGroupLink
745 | 
746 | **Signature:** `setItemGroupLink(itemGroupLink : URL) : void`
747 | 
748 | **Description:** Sets the URL of the Pinterest item group for the product, that is, the link to its master product in the Demandware storefront.
749 | 
750 | **Parameters:**
751 | 
752 | - `itemGroupLink`: link to the Pinterest item group
753 | 
754 | ---
755 | 
756 | ### setLink
757 | 
758 | **Signature:** `setLink(link : URL) : void`
759 | 
760 | **Description:** Sets the URL of the Demandware storefront link to the product.
761 | 
762 | **Parameters:**
763 | 
764 | - `link`: Demandware storefront link to the product
765 | 
766 | ---
767 | 
768 | ### setMaxPrice
769 | 
770 | **Signature:** `setMaxPrice(maxPrice : Money) : void`
771 | 
772 | **Description:** Sets the maximum price to show in Pinterest for the product.
773 | 
774 | **Parameters:**
775 | 
776 | - `maxPrice`: Pinterest maximum price
777 | 
778 | ---
779 | 
780 | ### setMinPrice
781 | 
782 | **Signature:** `setMinPrice(minPrice : Money) : void`
783 | 
784 | **Description:** Sets the minimum price to show in Pinterest for the product.
785 | 
786 | **Parameters:**
787 | 
788 | - `minPrice`: Pinterest minimum price
789 | 
790 | ---
791 | 
792 | ### setPrice
793 | 
794 | **Signature:** `setPrice(price : Money) : void`
795 | 
796 | **Description:** Sets the price to show in Pinterest for the product.
797 | 
798 | **Parameters:**
799 | 
800 | - `price`: Pinterest price
801 | 
802 | ---
803 | 
804 | ### setProductCategory
805 | 
806 | **Signature:** `setProductCategory(productCategory : String) : void`
807 | 
808 | **Description:** Sets the Pinterest category path of the product.
809 | 
810 | **Parameters:**
811 | 
812 | - `productCategory`: Pinterest category path
813 | 
814 | ---
815 | 
816 | ### setReturnPolicy
817 | 
818 | **Signature:** `setReturnPolicy(returnPolicy : String) : void`
819 | 
820 | **Description:** Sets the Pinterest return policy of the product.
821 | 
822 | **Parameters:**
823 | 
824 | - `returnPolicy`: Pinterest return policy
825 | 
826 | ---
827 | 
828 | ### setSize
829 | 
830 | **Signature:** `setSize(size : String) : void`
831 | 
832 | **Description:** Sets the Pinterest size value label of the product.
833 | 
834 | **Parameters:**
835 | 
836 | - `size`: Pinterest size value label
837 | 
838 | ---
839 | 
840 | ### setTitle
841 | 
842 | **Signature:** `setTitle(title : String) : void`
843 | 
844 | **Description:** Sets the Pinterest title of the product.
845 | 
846 | **Parameters:**
847 | 
848 | - `title`: Pinterest title
849 | 
850 | ---
```
Page 28/61FirstPrevNextLast