@dropins/mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE.md +127 -0
  2. package/README.md +314 -0
  3. package/dist/common/project-reader.d.ts +55 -0
  4. package/dist/common/project-reader.js +173 -0
  5. package/dist/common/registry-loader.d.ts +101 -0
  6. package/dist/common/registry-loader.js +386 -0
  7. package/dist/common/response-handling.d.ts +12 -0
  8. package/dist/common/response-handling.js +21 -0
  9. package/dist/common/sanitize.d.ts +8 -0
  10. package/dist/common/sanitize.js +45 -0
  11. package/dist/common/synonyms.d.ts +9 -0
  12. package/dist/common/synonyms.js +127 -0
  13. package/dist/common/telemetry.d.ts +14 -0
  14. package/dist/common/telemetry.js +54 -0
  15. package/dist/common/types.d.ts +308 -0
  16. package/dist/common/types.js +1 -0
  17. package/dist/common/version.d.ts +2 -0
  18. package/dist/common/version.js +14 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +136 -0
  21. package/dist/operations/analyze-project.d.ts +13 -0
  22. package/dist/operations/analyze-project.js +125 -0
  23. package/dist/operations/check-block-health.d.ts +19 -0
  24. package/dist/operations/check-block-health.js +1149 -0
  25. package/dist/operations/check-config.d.ts +13 -0
  26. package/dist/operations/check-config.js +228 -0
  27. package/dist/operations/explain-event-flow.d.ts +16 -0
  28. package/dist/operations/explain-event-flow.js +218 -0
  29. package/dist/operations/get-upgrade-diff.d.ts +13 -0
  30. package/dist/operations/get-upgrade-diff.js +144 -0
  31. package/dist/operations/list-api-functions.d.ts +13 -0
  32. package/dist/operations/list-api-functions.js +53 -0
  33. package/dist/operations/list-containers.d.ts +13 -0
  34. package/dist/operations/list-containers.js +44 -0
  35. package/dist/operations/list-design-tokens.d.ts +13 -0
  36. package/dist/operations/list-design-tokens.js +47 -0
  37. package/dist/operations/list-events.d.ts +16 -0
  38. package/dist/operations/list-events.js +39 -0
  39. package/dist/operations/list-graphql-queries.d.ts +19 -0
  40. package/dist/operations/list-graphql-queries.js +84 -0
  41. package/dist/operations/list-i18n-keys.d.ts +19 -0
  42. package/dist/operations/list-i18n-keys.js +105 -0
  43. package/dist/operations/list-models.d.ts +16 -0
  44. package/dist/operations/list-models.js +80 -0
  45. package/dist/operations/list-slots.d.ts +16 -0
  46. package/dist/operations/list-slots.js +81 -0
  47. package/dist/operations/scaffold-block.d.ts +31 -0
  48. package/dist/operations/scaffold-block.js +331 -0
  49. package/dist/operations/scaffold-extension.d.ts +28 -0
  50. package/dist/operations/scaffold-extension.js +346 -0
  51. package/dist/operations/scaffold-slot.d.ts +22 -0
  52. package/dist/operations/scaffold-slot.js +189 -0
  53. package/dist/operations/search-commerce-docs.d.ts +16 -0
  54. package/dist/operations/search-commerce-docs.js +101 -0
  55. package/dist/operations/search-docs.d.ts +23 -0
  56. package/dist/operations/search-docs.js +298 -0
  57. package/dist/operations/suggest-event-handler.d.ts +16 -0
  58. package/dist/operations/suggest-event-handler.js +175 -0
  59. package/dist/operations/suggest-slot-implementation.d.ts +19 -0
  60. package/dist/operations/suggest-slot-implementation.js +183 -0
  61. package/dist/registry/api-functions.json +3045 -0
  62. package/dist/registry/block-patterns.json +78 -0
  63. package/dist/registry/containers.json +2003 -0
  64. package/dist/registry/design-tokens.json +577 -0
  65. package/dist/registry/docs/boilerplate.json +55 -0
  66. package/dist/registry/docs/dropins-all.json +97 -0
  67. package/dist/registry/docs/dropins-b2b.json +607 -0
  68. package/dist/registry/docs/dropins-cart.json +163 -0
  69. package/dist/registry/docs/dropins-checkout.json +193 -0
  70. package/dist/registry/docs/dropins-order.json +139 -0
  71. package/dist/registry/docs/dropins-payment-services.json +73 -0
  72. package/dist/registry/docs/dropins-personalization.json +67 -0
  73. package/dist/registry/docs/dropins-product-details.json +139 -0
  74. package/dist/registry/docs/dropins-product-discovery.json +85 -0
  75. package/dist/registry/docs/dropins-recommendations.json +67 -0
  76. package/dist/registry/docs/dropins-user-account.json +121 -0
  77. package/dist/registry/docs/dropins-user-auth.json +103 -0
  78. package/dist/registry/docs/dropins-wishlist.json +85 -0
  79. package/dist/registry/docs/get-started.json +85 -0
  80. package/dist/registry/docs/how-tos.json +19 -0
  81. package/dist/registry/docs/index.json +139 -0
  82. package/dist/registry/docs/licensing.json +19 -0
  83. package/dist/registry/docs/merchants.json +523 -0
  84. package/dist/registry/docs/resources.json +13 -0
  85. package/dist/registry/docs/sdk.json +139 -0
  86. package/dist/registry/docs/setup.json +145 -0
  87. package/dist/registry/docs/troubleshooting.json +19 -0
  88. package/dist/registry/events.json +2200 -0
  89. package/dist/registry/examples/index.json +19 -0
  90. package/dist/registry/examples/storefront-checkout.json +377 -0
  91. package/dist/registry/examples/storefront-quote-management.json +49 -0
  92. package/dist/registry/extensions.json +272 -0
  93. package/dist/registry/graphql.json +3469 -0
  94. package/dist/registry/i18n.json +1873 -0
  95. package/dist/registry/models.json +1001 -0
  96. package/dist/registry/sdk.json +2357 -0
  97. package/dist/registry/slots.json +2270 -0
  98. package/dist/registry/tools-components.json +595 -0
  99. package/dist/resources/guides.d.ts +7 -0
  100. package/dist/resources/guides.js +625 -0
  101. package/dist/resources/handlers.d.ts +31 -0
  102. package/dist/resources/handlers.js +322 -0
  103. package/package.json +47 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "section": "resources",
3
+ "label": "Resources",
4
+ "pageCount": 1,
5
+ "pages": [
6
+ {
7
+ "path": "resources/placeholders",
8
+ "title": "Placeholder files",
9
+ "description": "This reference has moved. See Placeholder sheets in the Merchants section.",
10
+ "content": "This reference has moved to [Placeholder sheets](https://experienceleague.adobe.com/developer/commerce/storefront/merchants/blocks/placeholder-sheets/) in the Merchants section. The new page lists every placeholders sheet, what part of the storefront it controls, and where to inspect its current JSON."
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,139 @@
1
+ {
2
+ "section": "sdk",
3
+ "label": "Drop-ins SDK",
4
+ "pageCount": 22,
5
+ "pages": [
6
+ {
7
+ "path": "sdk",
8
+ "title": "Introduction to the Drop-in SDK",
9
+ "description": "The Drop-in SDK is a set of tools and resources that help you build and customize your storefront.",
10
+ "content": "Welcome to the Drop-in SDK Documentation! This SDK provides the tools and resources to help you build and customize your storefront. Whether you're new to drop-in components or looking to optimize your store, this SDK should provide the resources you need. If it doesn't, please reach out to the Commerce team.\n\n## Big picture\n\nFor the frontend, the drop-in SDK includes our design system, which provides the components and design tokens that make merchant rebranding quick and easy.\n\nFor everything else, the drop-in SDK provides a complete framework for mounting, rendering, eventing, testing, and hydrating drop-ins with data from the Commerce backend.\n\n## Get started\n\nGet started with the Drop-in SDK:\n\n- [CLI usage](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/get-started/cli/)\n\nThe Drop-in SDK aims to streamline your development process while maintaining flexibility for customization. By providing a robust set of components, utilities, and design tokens, along with comprehensive documentation and examples, we hope to empower you to create exceptional commerce experiences. Explore the documentation to learn more about the SDK's capabilities and how to leverage them effectively in your projects."
11
+ },
12
+ {
13
+ "path": "sdk/components/overview",
14
+ "title": "Overview",
15
+ "description": "Browse interactive inputs, navigation, layout, and cart primitives shipped with the Storefront SDK to stitch Cart, Checkout, PDP, and account experiences.",
16
+ "content": "A flexible set of interactive building blocks, such as actions, containment,\nnavigation, text, and selection elements, thought to create unique tailored\ncommerce user interfaces.\n\n## Components\n\nThe SDK provides the following collection of reusable UI components:\n\n- [Accordion](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/accordion/) - Expandable content sections\n- [ActionButton](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/actionbutton/) - Button with loading states\n- [ActionButtonGroup](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/actionbuttongroup/) - Group of action buttons\n- [AlertBanner](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/alertbanner/) - Notification banner\n- [Breadcrumbs](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/breadcrumbs/) - Navigation breadcrumbs\n- [Button](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/button/) - Basic button component\n- [Card](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/card/) - Content container\n- [CartItem](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/cartitem/) - Shopping cart item\n- [CartList](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/cartlist/) - Shopping cart list\n- [Checkbox](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/checkbox/) - Checkbox input\n- [ColorSwatch](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/colorswatch/) - Color selection swatch\n- [ContentGrid](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/contentgrid/) - Grid layout for content\n- [Divider](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/divider/) - Horizontal divider\n- [Field](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/field/) - Form field wrapper\n- [Header](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/header/) - Page header\n- [Icon](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/icon/) - SVG icon component\n- [IllustratedMessage](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/illustratedmessage/) - Illustration with text\n- [Image](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/image/) - Image component\n- [ImageSwatch](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/imageswatch/) - Image selection swatch\n- [Incrementer](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/incrementer/) - Incrementer component\n- [InlineAlert](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/inlinealert/) - Inline alert component\n- [Input](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/input/) - Text input field\n- [InputDate](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/inputdate/) - Date input field\n- [InputFile](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/inputfile/) - File upload input\n- [InputPassword](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/inputpassword/) - Password input field\n- [Modal](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/modal/) - Modal component\n- [Pagination](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/pagination/) - Page navigation\n- [Picker](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/picker/) - Selection picker\n- [Price](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/price/) - Price display\n- [PriceRange](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/pricerange/) - Price range display\n- [ProgressSpinner](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/progressspinner/) - Loading indicator\n- [RadioButton](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/radiobutton/) - Radio button input\n- [Skeleton](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/skeleton/) - Loading placeholder\n- [Tag](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/tag/) - Label tag\n- [TextArea](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/textarea/) - Multi-line text input\n- [TextSwatch](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/textswatch/) - Text selection swatch\n- [ToggleButton](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/togglebutton/) - Toggle switch button\n\nBy using these components, you can create a consistent and engaging user experience across your application. Each component is designed to be flexible and customizable, allowing you to tailor the UI to your specific needs and brand."
17
+ },
18
+ {
19
+ "path": "sdk/design",
20
+ "title": "Overview",
21
+ "description": "Learn Base design tokens for color, typography, spacing, shapes, and grids so custom storefront skinning still matches SDK components.",
22
+ "content": "The Base design system provides a comprehensive set of design tokens and guidelines that form the foundation of our component library. It defines core visual elements like colors, typography, spacing, and shapes to ensure consistency across your application. By leveraging these design tokens through our components, you can create cohesive user experiences while maintaining flexibility to customize the look and feel to match your brand.\n\n## Topics\n\n- [Colors](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/design/colors/) - Color tokens and usage\n- [Typography](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/design/typography/) - Font families, sizes and styles\n- [Spacing](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/design/spacing/) - Spacing scale and tokens\n- [Shapes](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/design/shapes/) - Border radius and other shape tokens\n- [Grids](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/design/grid/) - Layout grid system\n\nBy following these design guidelines and utilizing the provided tokens, you can create consistent, accessible, and visually appealing interfaces that align with modern design principles. The Base design system serves as a solid foundation that can be extended and customized to meet your specific needs while maintaining cohesion across your application."
23
+ },
24
+ {
25
+ "path": "sdk/design/base",
26
+ "title": "Base Design System",
27
+ "description": "Base design tokens are the foundation of our design system. They are the core values that make up the visual language of our products.",
28
+ "content": "With the base design system, you can use system and reference tokens to ensure color, typography, spacing, and shape values are consistent across your site.\n\n## What are design tokens?\n\nDesign tokens are design decisions that ensure a unified and cohesive product experience. The tokens represent a consistent use of design data that provides specific values for spacing, color, typography, and shapes—without hard-coding those values.\n\nOur Base design tokens are directly integrated into our shared components. They cover the various options for platforms, component states, and more.\n\n\n## Extending the baseline design tokens\n\nBase includes a set of baseline design tokens and default values for you to\ncustomize, collaborate on, and use in your own designs and product\nimplementation.\n\nIt's important to keep in mind that not every component is going to be\ncaptured in the foundational and baseline design tokens. If they're limited\nfor what you're trying to design or build, just add in the tokens you may\nneed within each category.\n\n## How to use design tokens\n\nTo apply design tokens on your project, make use of the UIProvider, which grants components access to the design tokens as CSS variables. Simply specify the desired design token using the var() CSS function to apply its corresponding value.\n\n```css\n.myComponent {\n background-color: var(--color-brand-500);\n}\n```"
29
+ },
30
+ {
31
+ "path": "sdk/get-started/cli",
32
+ "title": "CLI usage",
33
+ "description": "Run the Elsie CLI from npx to scaffold drop-ins, generate GraphQL types, and automate storefront package maintenance tasks.",
34
+ "content": "To see all the available CLI commands in the terminal, use the `--help` flag:\n\n```bash\nnpx elsie --help\n```\n\n## `gql`\n\nThe `gql` command can generate types and mocks for your GraphQL API. Files will\nbe generated in `<domain package root>/src/__generated__/`.\n\nTo use it, you must first configure your `.elsie.js` with the necessary values:\n\n```js\n// For Adobe Commerce Catalog Service\nschema: {\n endpoint: \"https://catalog-service-sandbox.adobe.io/graphql\",\n headers: {\n \"MAGENTO-ENVIRONMENT-ID\": \"...\"\n \"MAGENTO-STORE-VIEW-CODE\": \"...\"\n \"MAGENTO-WEBSITE-CODE\": \"...\"\n \"MAGENTO-STORE-CODE\": \"...\"\n \"MAGENTO-CUSTOMER-GROUP\": \"...\"\n \"API-KEY\": \"...\"\n }\n}\n```\n\n```js\n// For Adobe Mesh API\nschema: {\n endpoint: \"https://graph.adobe.io/api/.../graphql?api_key=...\",\n headers: {\n \"some-mesh-specific-header\": \"mesh-header-value\"\n }\n}\n```\n\n```js\n// For Adobe Commerce (non-Mesh)\nschema: {\n endpoint: \"https://commerce-backend-url.test.graphql\",\n headers: {}\n}\n```\n\nThen the following commands will generate to\n\n```bash\nnpx elsie gql types\n```\n\n```bash\nnpx elsie gql mocks\n```\n\n## `generate`\n\nSummary list of commands for quick copy/paste.\n\n```bash\nnpx elsie generate config --name <Domain>\n```\n\n```bash\nnpx elsie generate component --pathname <MyUIComponent>\n```\n\n```bash\nnpx elsie generate container --pathname <MyContainer>\n```\n\n```bash\nnpx elsie generate api --pathname <myApiFunction>\n```\n\n## Add Config\n\nGenerate a new `.elsie.js` configuration file for the project.\n\n```bash\nnpx elsie generate config --name <Domain>\n```\n\n```javascript\nmodule.exports = {\n name: '<Domain>',\n api: {\n root: './src/api',\n importAliasRoot: '@/<Domain>/api',\n },\n components: [\n {\n id: 'Components',\n root: './src/components',\n importAliasRoot: '@/<Domain>/components',\n cssPrefix: 'elsie',\n default: true,\n },\n ],\n containers: {\n root: './src/containers',\n importAliasRoot: '@/<Domain>/containers',\n },\n schema: {\n endpoint: process.env.ENDPOINT,\n // Add necessary headers\n headers: {},\n },\n};\n```\n\n## Add Component\n\nGenerate a new UI Component for the project.\n\n```bash\nnpx elsie generate component --pathname <MyUIComponent>\n```\n\n```bash\n🆕 src/components/LoginForm/LoginForm.css created\n🆕 src/components/LoginForm/LoginForm.stories.tsx created\n🆕 src/components/LoginForm/LoginForm.test.tsx created\n🆕 src/components/LoginForm/LoginForm.tsx created\n🆕 src/components/LoginForm/index.ts created\n✍️ src/components/index.ts updated\n```\n\n## Add Container\n\nGenerate a new Frontend Container for the project.\n\n```bash\nnpx elsie generate container --pathname <MyContainer>\n```\n\n```bash\n🆕 src/containers/Login/Login.stories.tsx created\n🆕 src/containers/Login/Login.test.tsx created\n🆕 src/containers/Login/Login.tsx created\n🆕 src/containers/Login/index.ts created\n✍️ src/containers/index.ts updated\n```\n\n## Add Function\n\nGenerate a new API function for the project.\n\n```bash\nnpx elsie generate api --pathname <myApiFunction>\n```\n\n```console\n🆕 src/api/login/login.mdx created\n🆕 src/api/login/login.test.ts created\n🆕 src/api/login/login.ts created\n🆕 src/api/login/index.ts created\n✍️ src/api/index.ts updated\n```"
35
+ },
36
+ {
37
+ "path": "sdk/reference",
38
+ "title": "Overview",
39
+ "description": "Jump into events, renderers, GraphQL helpers, initializer hooks, reCAPTCHA, and slots that power every Commerce drop-in package.",
40
+ "content": "The reference section provides documentation for core SDK functionality like event handling, initialization, rendering, and integration with external services like reCAPTCHA. These APIs form the foundation for building and customizing drop-in components.\n\n## Topics\n\n- [Events](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/events/)\n- [GraphQL](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/graphql/)\n- [Initializer](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/initializer/) \n- [Links](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/links/) \n- [reCAPTCHA](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/recaptcha/)\n- [Render](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/render/)\n- [Slots](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/slots/)\n- [VComponent](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/reference/vcomponent/)\n\nBy understanding and utilizing these core APIs effectively, you can take full advantage of the SDK's capabilities while maintaining control over the implementation details. The reference documentation provides the technical foundation needed to integrate and customize the SDK's functionality within your application."
41
+ },
42
+ {
43
+ "path": "sdk/reference/events",
44
+ "title": "Event Bus",
45
+ "description": "Publish and subscribe on the SDK event bus so cart, checkout, PDP, and auth drop-ins react to shared lifecycle messages.",
46
+ "content": "The Event Bus enables different parts of your application to communicate and stay synchronized through message passing. It supports event-driven architecture for drop-ins, allowing containers to react to changes from other containers and communicate data changes to the storefront.\n\n> **Tip**\n>\n**New to event-driven architecture?** Learn about the [event system architecture and patterns](https://experienceleague.adobe.com/developer/commerce/storefront/dropins/all/events/) including the publish-subscribe pattern, event declarations, and best practices before diving into the API.\n\n\n## Import\n\n**From drop-in project using the SDK:**\n\n```ts\n\n```\n\n**From integration project (storefront):**\n\n```js\n\n```\n\n## Core Methods\n\n### Subscribe to Events\n\nSubscribe to events and receive notifications when they occur.\n\n```ts\nconst eventListener = events.on('<event>', (payload) => {\n // Handle the event payload\n console.log('Event received:', payload);\n});\n\n// Stop listening to the event\neventListener.off();\n```\n\n**Example:**\n```ts\n// Listen for cart updates\nconst cartListener = events.on('cart/data', (cartData) => {\n if (cartData) {\n console.log(`Cart has $ items`);\n updateCartUI(cartData);\n } else {\n console.log('Cart is empty');\n showEmptyCart();\n }\n});\n\n// Later, when you want to stop listening\ncartListener.off();\n```\n\n### Emit Events\n\nBroadcast events to all listeners across your application.\n\n```ts\nevents.emit('<event>', payload);\n```\n\n**Examples:**\n```ts\n// Emit cart data\nconst cartData = {\n id: 'cart-123',\n totalQuantity: 2,\n items: [\n { uid: 'item-1', quantity: 1, sku: 'PROD-001', name: 'Product Name' }\n ]\n};\n\nevents.emit('cart/data', cartData);\n```\n\n### Get Last Event Payload\n\nRetrieve the most recent payload for a specific event.\n\n```ts\nconst lastPayload = events.lastPayload('<event>');\n```\n\n**Example:**\n```ts\n// Get the current cart state without waiting for an event\nconst currentCart = events.lastPayload('cart/data');\n\nif (currentCart) {\n console.log('Current cart total:', currentCart.totalQuantity);\n}\n```\n\n### Enable Debug Logging\n\nEnable console logging to debug event flow.\n\n```ts\n// Enable logging to see all events in console\nevents.enableLogger(true);\n```\n\n## Advanced Features\n\n### Eager Loading\n\nWhen subscribing to events, you can execute the event handler immediately with the last known payload. This is useful for getting the current state without waiting for the next event.\n\n```ts\n// Handler will execute immediately if there's a previous payload\nconst listener = events.on('cart/data', (cartData) => {\n console.log('Cart data received:', cartData);\n}, { eager: true });\n```\n\n**Use cases:**\n- Initialize UI components with current state\n- Avoid waiting for the first event emission\n- Ensure components have the latest data on mount\n\n### Event Scoping\n\nCreate namespaced events to avoid conflicts between different parts of your application.\n\n```ts\n// Subscribe to a scoped event\nconst scopedListener = events.on('data/update', (data) => {\n console.log('Scoped data received:', data);\n}, { scope: 'feature-a' });\n\n// Emit a scoped event\nevents.emit('data/update', payload, { scope: 'feature-a' });\n\n// Get last payload for a scoped event\nconst lastScopedData = events.lastPayload('data/update', { scope: 'feature-a' });\n```\n\n**Scoped event names:**\nWhen using scopes, the actual event name becomes `scope/event`. For example:\n- `'feature-a/data/update'` instead of `'data/update'`\n- `'module-b/user/action'` instead of `'user/action'`\n\n**Use cases:**\n- Separate different features or modules\n- Different contexts within the same application\n- Component-specific event handling\n\n### Combining Options\n\nUse both eager loading and scoping together for powerful event handling.\n\n```ts\n// Subscribe to a scoped event with eager loading\nconst listener = events.on('locale', (locale) => {\n console.log('Current locale:', locale);\n}, { \n eager: true, \n scope: 'user-preferences' \n});\n```\n\n## Event-Driven Drop-ins\n\nThe Event Bus enables drop-ins to be truly event-driven, allowing for loose coupling between components and seamless communication across the application.\n\n### Container-to-Container Communication\n\nContainers can react to changes from other Containers, enabling complex interactions without direct dependencies.\n\n```ts\n// Product Container: Emits when a product is added to cart\nfunction ProductContainer() {\n const handleAddToCart = (product) => {\n // Add to cart logic...\n \n // Notify other containers about the cart change\n events.emit('cart/data', updatedCartData);\n };\n \n return (\n <button onClick={() => handleAddToCart(product)}>\n Add to Cart\n </button>\n );\n}\n\n// Cart Container: Reacts to cart changes from any source\nfunction CartContainer() {\n useEffect(() => {\n const cartListener = events.on('cart/data', (cartData) => {\n updateCartDisplay(cartData);\n updateCartBadge(cartData.totalQuantity);\n }, { eager: true });\n \n return () => cartListener.off();\n }, []);\n \n return ;\n}\n\n// Mini Cart Container: Also reacts to the same cart changes\nfunction MiniCartContainer() {\n useEffect(() => {\n const cartListener = events.on('cart/data', (cartData) => {\n updateMiniCart(cartData);\n }, { eager: true });\n \n return () => cartListener.off();\n }, []);\n \n return ;\n}\n```\n\n### Storefront Communication\n\nDrop-ins can communicate data changes to the storefront, enabling seamless integration with the host application.\n\n```ts\n// Authentication Container: Notifies storefront of login/logout\nfunction AuthContainer() {\n const handleLogin = (userData) => {\n // Login logic...\n \n // Notify storefront of authentication change\n events.emit('authenticated', true);\n };\n \n const handleLogout = () => {\n // Logout logic...\n \n // Notify storefront of authentication change\n events.emit('authenticated', false);\n };\n \n return ;\n}\n\n// Storefront can listen for authentication changes\n// This would be in the host application\nconst authListener = events.on('authenticated', (isAuthenticated) => {\n if (isAuthenticated) {\n showUserMenu();\n enableCheckout();\n } else {\n hideUserMenu();\n disableCheckout();\n }\n}, { eager: true });\n```\n\n## Best Practices\n\n\n1. **Always unsubscribe** from events when components unmount to prevent memory leaks\n2. **Use scopes** to organize events by feature or component\n3. **Enable eager loading** when you need immediate access to current state\n4. **Use descriptive event names** that clearly indicate what data they contain\n5. **Handle null/undefined payloads** gracefully in your event handlers\n6. **Enable logging during development** to debug event flow\n7. **Keep event payloads lightweight** to avoid performance issues\n8. **Document your event contracts** so other developers know what to expect"
47
+ },
48
+ {
49
+ "path": "sdk/reference/graphql",
50
+ "title": "GraphQL Extensibility API",
51
+ "description": "The GraphQL Extensibility API allows developers to extend existing GraphQL operations used by a Drop-in to meet additional data requirements without increasing code complexity or negatively impacting performance. This API provides a flexible and efficient way to customize GraphQL Fragments by integrating build-time modifications into the storefront's development pipeline.",
52
+ "content": "The GraphQL Extensibility API allows developers to extend existing GraphQL operations used by a Drop-in to meet additional data requirements without increasing code complexity or negatively impacting performance. \nThis API provides a flexible and efficient way to customize GraphQL Fragments by integrating build-time modifications into the storefront's development pipeline.\n\n## Extend your Drop-in GraphQL Fragments\n\nTo enable GraphQL Fragments to be extensible in your Drop-in, follow these steps:\n\n\n### Define Your Fragments\n\nCreate the content for the fragments you are exporting.\n\n```ts\n// ./src/api/fragments/MyFragment.ts\n\nexport const MY_FRAGMENT = `\n fragment MY_FRAGMENT on FragmentInterface {\n firstname\n lastname\n \n favorites {\n uid\n name\n }\n }\n`;\n```\n\n\n### Create Fragments Manifest File\n\nNext, create a new file for your project to list all the fragments you want to expose.\n\n```ts\n// ./src/api/fragments.ts\n\nexport { MY_FRAGMENT } from '@/my-dropin/api/graphql/MyFragment';\n```\n\n\n### Update the API Configuration\n\nFinally, add the new file reference to the API configuration in `./.elsie.js`.\n\n```js\n// ./.elsie.js\n\nmodule.exports = {\n name: 'MyDropin',\n api: {\n root: './src/api',\n importAliasRoot: '@/my-dropin/api',\n fragments: './fragments.ts', // 👈 add this line\n },\n components: [\n {\n id: 'Components',\n root: './src/components',\n importAliasRoot: '@/my-dropin/components',\n cssPrefix: 'my-dropin',\n default: true,\n },\n ],\n containers: {\n root: './src/containers',\n importAliasRoot: '@/my-dropin/containers',\n },\n schema: {\n endpoint: process.env.ENDPOINT,\n headers: {}\n }\n};\n```\n\n\n## Extend the Data module\n\nNow that we have made our fragments extensible, we must extend the data model used in our Drop-ins.\n\n\n### Define Typing to the Initializer API\n\nFirst, add the typing to your initializer API in `./src/api/initialize/initialize.ts`.\n\n```ts\n// ./src/api/initialize/initialize.ts\n\n\ntype ConfigProps = {\n langDefinitions?: Lang;\n\n // 👇 add your models configuration\n models?: {\n MyModel?: Model<MyModel>;\n },\n};\n\nexport const initialize = new Initializer<ConfigProps>({\n init: async (config) => {\n const defaultConfig = {};\n initialize.config.setConfig({ ...defaultConfig, ...config });\n },\n\n listeners: () => [],\n});\n\nexport const config = initialize.config;\n```\n\n\n### Extend the Data Transformer\n\nThen, use the configuration to extend the data transformer the model uses by deep merging the existing data transformation with the new configuration.\n\n```ts\n// ./src/data/transforms/transform-my-model.ts\n\n\nexport function transformMyModel(data: any): MyModel {\n const model = {\n name: `$ $`,\n favorites: data.favorites,\n };\n\n // Merge custom transformer, if provided\n return merge(\n model, // default transformer\n config.getConfig().models?.MyModel?.transformer?.(data) // custom transformer\n );\n}\n```\n\n\n## Using the GraphQL Extensibility Feature in Your Storefront\n\nBy extending the GraphQL Fragments and Models of drop-ins, you can leverage the drop-in's existing GraphQL operations and add extra fields needed to meet your specific business requirements. \nThis approach enhances the user experience while maintaining code simplicity and performance efficiency since all modifications are integrated into the storefront's development build-time pipeline.\n\n## Extend Drop-in's Fragments and Models\n\nFollow these steps to extend the GraphQL Fragment and Model in your Drop-in:\n\n\n### Extend the GraphQL Fragment\n\nUse the `overrideGQLOperations` function to extend the existing GraphQL Fragment, allowing you to add fields to the fragment as needed.\n\nThe `overrideGQLOperations` functions accept an array of configuration objects where you must specify:\n\n#### npm: string\n\nThe node module name of the drop-in. i.e. \"@dropins/my-dropin\".\n\n#### operations: string[] \n\nAn array of string or template literal with operations. \n\n- Only one definition can be provided in the operation.\n- These must match the operation name as provided by the drop-in. i.e. `MY_FRAGMENT`.\n- The operations must be valid GraphQL operations, such as Fragment. i.e. `fragment MY_FRAGMENT on FragmentInterface { ... }`.\n- In the case of a Fragment, the fragment name must match the same interface as the drop-in. i.e. `FragmentInterface`.\n- If an existing field that has variables is used:\n - If not variables are provided, the existing variables will be used. i.e. `favorites(page: 1) { ... }`.\n - If new variables are provided, the new variable will be added. i.e. `favorites(page: 1, offset: 5) { ... }`.\n - If the existing variables are changed, the new variables will be used. i.e. `favorites(page: 2) { ... }`.\n\n\n```js\n\noverrideGQLOperations([\n {\n npm: '@dropins/my-dropin',\n operations: [`\n fragment MY_FRAGMENT on FragmentInterface {\n age\n \n favorites {\n quantity\n }\n }\n `],\n },\n]);\n```\n\n\n### Extend the Data Model\n\nNext, update the data models to include the new fields added to the fragment, ensuring that the additional data is correctly processed and available for use in the drop-in. i.e., Slots, Event Bus, etc.\n\n```js\n\ninitializers.register(api.initialize, {\n models: {\n MyModel: {\n transformer: (data) => ({\n age: data?.age,\n favorites: data.favorites.map((favorite) => ({ \n quantity: favorite.quantity,\n })),\n }),\n },\n },\n});\n```"
53
+ },
54
+ {
55
+ "path": "sdk/reference/initializer",
56
+ "title": "Initializer",
57
+ "description": "Initializer is a class that helps you initialize your package.",
58
+ "content": "```typescript\n// my-domain-package/initializer.ts\n\n\n// import { events } from '@adobe/event-bus';\n\ntype ConfigProps = {};\n\nexport const initialize = new Initializer<ConfigProps>({\n init: async (config) => {\n const defaultConfig = {};\n initialize.config.setConfig({ ...defaultConfig, ...config });\n },\n\n listeners: () => [\n // events.on('authenticated', (authenticated) => {\n // console.log('authenticated', authenticated);\n // }),\n ],\n});\n\nexport const config = initialize.config;\n```\n\n```typescript\n// Host Site\n\n\n// Register Packages\ninitializers.register(pkg, { ...config });\n\n// Mount Initializers\nwindow.addEventListener('load', initializers.mount);\n```\n\n## `setImageParamKeys(params)`\n\nThe `setImageParamKeys` method is part of the initializers module in the `@dropins/tools` package. It allows you to set image parameters globally for all drop-in components that use the [Image](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/components/image/) component. CDN-based image optimization services, such as Fastly and Cloudflare, rely on these parameters to optimize images.\n\n### Default behavior\n\nSince Fastly is the default CDN for Adobe Commerce on Cloud, drop-in components add the following Fastly-style parameters to all image URLs by default:\n\n- `width`\n- `height`\n- `auto`\n- `quality`\n- `crop`\n- `fit`\n\nExample:\n\n```text\n/media/catalog/product/adobestoredata/ADB150.jpg?auto=webp&quality=80&crop=false&fit=cover&width=960&height=1191`\n```\n\nTechnical details about CDN-based image optimization differ depending on your backend Adobe Commerce solution:\n\n- Fastly is the default CDN-based image optimization service for Adobe Commerce on Cloud projects. Fastly-style parameters are used by default unless overridden via `setImageParamKeys`.\n \n- Cloudflare is the default CDN-based image optimization service for Adobe Commerce as a Cloud Service and Adobe Commerce Optimizer projects. Fastly-style parameters are used but are proxied through a to transform the parameters to Cloudflare-style parameters. Overriding the default parameters using `setImageParamKeys` may interfere with the proxy transformation and lead to unexpected results.\n\n> **Override defaults**\n>\nIf necessary, you can override these parameters with the `setImageParamKeys` method. For example, if you are using a CDN provider that requires different parameters or URL patterns.\n\n\n### Parameters\n\n- `params` - `{ [key: string]: string | ((data: any) => [string, string]) }`\n - An object of key-value pairs to map image parameters to their respective keys in the URL.\n - The value can be a string or a function that takes the parameter value as an argument and returns a tuple of the new key and transformed value.\n\n### Functionality\n\n- If a parameter key is provided via `setImageParamKeys`, it is used in generating image URLs instead of the default Fastly parameters.\n- If a parameter key is not provided via `setImageParamKeys`, it is omitted from the generated image URLs.\n- If a mapped key is a function and it is not specified as a parameter in the Image component, it is called with `null`. It should return a tuple of the key and value.\n- If a mapping callback is provided, the callback is called with the parameter value (if it exists) and should return a tuple of the new key and transformed value.\n- If a mapping callback returns `null`, the parameter is omitted from the generated image URLs.\n\n### Usage\n\nCall the `setImageParamKeys()` function before the `mountImmediately()` function in the application layer.\n\n```javascript\n// Set global image parameters\ninitializers.setImageParamKeys({\n // Re-map the width parameter to imgWidth\n width: 'imgWidth',\n // Transform the quality parameter\n quality: (value) => ['imgQuality', value * 100],\n // Add an additional parameter to the image URL\n extraParam: () => ['extraParam', 'extraValue'],\n});\n\n// Register and Mount Initializers immediately\ninitializers.mountImmediately(pkg.initialize, {\n // other configurations...\n});\n```\n\nNow, when a dropin uses the Image component to render an image with a width of 300 pixels and quality value of 0.8:\n\n```jsx\n\n```\n\nIt renders the following image element:\n\n```html\n<img\n loading=\"lazy\"\n src=\"https://example.com/image.jpg\"\n srcset=\"https://example.com/image.jpg?imgWidth=300&imgQuality=80&extraParam=extraValue 768w, https://example.com/image.jpg?imgWidth=300&imgQuality=80&extraParam=extraValue 1024w, https://example.com/image.jpg?imgWidth=300&imgQuality=80&extraParam=extraValue 1366w, https://example.com/image.jpg?imgWidth=300&imgQuality=80&extraParam=extraValue 1920w\"\n alt=\"Example Image\"\n width=\"300\"\n height=\"300\"\n/>\n```\n\nIn this example, the width parameter is mapped to imgWidth and the value of the quality parameter is modified and mapped to imgQuality.\n\n## `setGlobalLocale(locale)`\n\nThe `setGlobalLocale()` method is part of the initializers module in the `@dropins/tools` package.\nIt allows you to set a global locale for all drop-ins that use locale-sensitive components like the Price component.\n\n### Default behavior\n\nBy default, locale-sensitive components use the browser's locale or fallback to 'en-US' if no global locale is set. Locale-sensitive components follow this fallback hierarchy:\n\n1. Component-specific `locale` prop (highest priority)\n2. Global locale set via `setGlobalLocale()` \n3. Browser's locale (`navigator.language`)\n4. Default fallback locale (`'en-US'`) (lowest priority)\n\nThis ensures that your storefront always displays properly formatted content, even when specific locales are not configured.\n\n### Parameters\n\n- `locale` - `string` - The locale string (e.g., 'en-US', 'es-MX', 'fr-FR', 'de-DE').\n\n### Functionality\n\n- If a global locale is set via `setGlobalLocale()`, it will be used by components that support locale configuration.\n- Component-specific locale props will take precedence over the global locale.\n- If no global locale is set, components will fall back to the browser's locale or a default locale.\n\n### Usage\n\nCall the `setGlobalLocale()` function before the `mountImmediately()` function in the application layer.\n\n```javascript\n// Set global locale for consistent formatting across all drop-ins\ninitializers.setGlobalLocale('fr-FR');\n\n// Register and Mount Initializers immediately\ninitializers.mountImmediately(pkg.initialize, {\n // other configurations...\n});\n```\n\n#### Basic Example\n\nWhen a drop-in uses the Price component without specifying a locale prop:\n\n```jsx\n\n```\n\nIt will render with the global locale (fr-FR) formatting:\n\n```html\n\n 100,00 €\n\n```\n\n#### Override Example\n\nIf the same component is used with a specific locale prop, that will take precedence over the global setting:\n\n```jsx\n\n```\n\nIt will render with the specified locale (en-US) formatting:\n\n```html\n\n €100.00\n\n```"
59
+ },
60
+ {
61
+ "path": "sdk/reference/links",
62
+ "title": "Links",
63
+ "description": "Use the route pattern to provide composable and accessible navigation.",
64
+ "content": "## Adding Links using the route pattern\n\nWhenever possible, avoid placing `onClick` handlers directly on anchor elements (`<a>`) in drop-in components, such as product or category pages, as this results in accessibility issues and broken browser behavior. Problems include:\n\n- Right-click > Open in New Tab results in blank pages.\n- Middle-click (open in background tab) won't work as expected.\n- Keyboard navigation and screen readers may not trigger the link correctly.\n\nInstead, follow the route pattern to provide composable and accessible navigation.\n\n## How it works\n\nComponents accept a `routeX` function as a prop. The function receives a data model (a product, for example) and returns a URL. Internally, it's used like this:\n\n```tsx\n<a href={routeProduct?.(product) ?? '#'}>...</a>\n```\n\nThis lets developers customize routing logic per storefront while preserving link semantics.\n\n## Example — Component-Side\n\nIn your component (a PLP item, for example):\n\nThe `routeProduct` prop must be optional and default to a # or an non-functional element like a `div` if not provided.\n\n```tsx\ntype Props = {\n routeProduct?: (product: ProductModel) => string;\n};\n\nfunction ProductCard({ product, routeProduct }: Props) {\n return (\n <a href={routeProduct?.(product) ?? '#'}>\n <div>\n </a>\n );\n}\n```\n\n## Example — Storefront-Side\n\nIn the storefront integration (`commerce-cart.js` or `commerce-plp.js`, for example):\n\n```js\n\nprovider.render(ProductList, {\n routeProduct: (product) => rootLink(`/products/$/$`),\n});\n```"
65
+ },
66
+ {
67
+ "path": "sdk/reference/recaptcha",
68
+ "title": "reCAPTCHA Integration",
69
+ "description": "Learn how to integrate the reCAPTCHA module with your storefront.",
70
+ "content": "The reCAPTCHA module is implemented as a standalone SDK package. It enables reCAPTCHA protection, which is natively supported by Adobe Commerce. It also provides an API allowing merchants to integrate reCAPTCHA protection into custom implementations. The current version of the module supports only reCAPTCHA v3 Invisible.\n\n## Installation\n\nTo install this functionality, run the following command:\n\n```bash\nnpm i @adobe/reCAPTCHA\n```\n\n in the _Admin Systems Guide_ describes how to configure Adobe Commerce.\n\n## Integrations\n\nTo integrate ReCaptcha, follow these steps:\n\n1. Load the app.\n\n - Call the `setConfig` method to fetch and set reCAPTCHA configuration.\n - (Optional) Extend the configuration with custom forms if a custom integration not natively supported by Adobe Commerce is planned.\n\n2. Load the page with a protected form. Call the `initReCaptcha` method to add the script and initialize the ReCaptcha badge.\n\n3. Render the form.\n\n - The user interacts with the page and form, providing the required data.\n - The user clicks the submit button.\n\n4. Call the `verifyReCaptcha` API to retrieve the reCAPTCHA token via the Google API.\n\n### Flow for forms covered by Adobe Commerce natively\n\nUse the following workflow for standard Adobe Commerce integrations\n\n1. Set the reCAPTCHA token in the X-ReCaptcha header.\n\n1. Send the request to the backend.\n\n1. Commerce validates the token. If it fails, Commerce returns an error.\n\n\n### Flow for custom integrations\n\nUse one of the following workflows for custom integrations:\n\n- (Recommended) Send the token to Commerce and validate it on the server side. If validation passes, Commerce returns a normal response. Otherwise, it returns an error.\n\n- Validate the token on the client side. If validation passes, send the request.\n\n## reCAPTCHA API\n\nThe reCAPTCHA package exposes the following APIs, allowing developers to integrate it with native Adobe Commerce functionality or set up custom integrations:\n\n* `setConfig`\n* `setEndpoint`\n* `initreCAPTCHA`\n* `verifyreCAPTCHA`\n* `enableLogger`\n\n### setConfig\n\nThe `setConfig` method fetches and stores the reCAPTCHA configuration from the backend.\nIt is essential to call this method before the `initreCAPTCHA` and `verifyreCAPTCHA` methods during app initialization, because other reCAPTCHA functionality will not be fully operational until the configurations are loaded and set up.\n\n#### Usage\n\nThe `setConfig` method is intended to be called without parameters, in which case the backend configuration will be used.\n\n```javascript\n\nreCAPTCHA.setConfig();\n```\n\nHowever, you can pass a configuration object as a parameter to override the backend configurations.\n\n```javascript\n\nreCAPTCHA.setConfig({is_enabled: true});\n```\n\n### setEndpoint\n\nThe `setEndpoint` method sets the endpoint that the recaptcha module uses to fetch configurations. Its use is optional. If reCAPTCHA uses the same mesh endpoint as the rest of the application, you do not need to set it explicitly. It will be inherited from the fetch-graphql package.\n\n#### Usage\n\nThe following example sets the endpoint:\n\n```javascript\n\nreCAPTCHA.setEndpoint('https://www.example.com/graphql');\n```\n\n### initreCAPTCHA\n\nThe `initreCAPTCHA` method initializes reCAPTCHA scripts and badges. Call this method after the `setConfig` method and before rendering forms with reCAPTCHA protection.\n\nThe `initreCAPTCHA` method should be called before each form rendering. Do not call it at the top level of the application, immediately after the `setConfig` API, because this would load reCAPTCHA scripts on all pages, not just those with forms protected by reCAPTCHA.\n\nAdditionally, you must call `initreCAPTCHA` for each form render to ensure the script is in place and the reCAPTCHA badge is properly initialized.\n\n#### Usage\n\nThe following example initializes reCAPTCHA:\n\n```javascript\n\nreCAPTCHA.initreCAPTCHA();\n```\n\n### verifyreCAPTCHA\n\nThe `verifyreCAPTCHA` method retrieves the reCAPTCHA token via the Google API. It should be called after `setConfig` and `initreCAPTCHA`, and before submitting a protected form to retrieve the reCAPTCHA token.\n\nWhen the reCAPTCHA token is retrieved, for forms covered by native Adobe Commerce functionality, it is required to pass this token as an X-reCAPTCHA header.\n\nFor custom implementations, token validation should be implemented on either the server side or client side, and the token should be used according to the custom implementation.\n\n#### Usage\n\nThe following example retrieves the reCAPTCHA token:\n\n```javascript\n\n\nexport const setreCAPTCHAToken = async () => {\n const token = await verifyreCAPTCHA();\n\n if (token) {\n setFetchGraphQlHeader('X-reCAPTCHA', token);\n }\n};\n```\n\n### enableLogger\n\nThe `enableLogger` method enables extensive logging to the console for each step and error during reCAPTCHA operations. It is intended for development and debugging purposes.\n\n#### Usage\n\nThe following example enables the logger:\n\n```javascript \n\nreCAPTCHA.enableLogger(true);\n```"
71
+ },
72
+ {
73
+ "path": "sdk/reference/render",
74
+ "title": "Render",
75
+ "description": "Instantiate the Render class with your Provider to mount Preact drop-ins in the DOM or serialize authenticated HTML fragments.",
76
+ "content": "## Implementing a new render in your dropin\n\nTo implement a new render in your dropin, you must create an instance of the `Render` class from the `@adobe-commerce/elsie/lib` library, passing in a `Provider` component.\n\nThis setup initializes the rendering context with the specified provider, which can manage state, context, or other dependencies required by your components.\n\nBy exporting this `render` instance, you enable different parts of your application to render components within the defined context, ensuring consistent behavior and integration across your application.\n\n```ts\n// Dropin\n\n\nexport const render = new Render();\n```\n\n## Rendering a dropin's container in a storefront\n\nThe render function mounts a drop-in container or component into the DOM and manages its lifecycle. \nIt returns a Promise that resolves to an object containing methods for updating and removing the component instance.\n\n```js\n// Storefront\n\n\nconst wrapper = document.getElementById('my-container-root');\n\nprovider.render(MyContainer, { ...props })(wrapper);\n```\n\n### Using VNode as a property\n\nSome components may require VNodes as properties. If you are using another component from the library, provide the VNode by executing the component as a function.\n\n```js\n// Storefront\n\n\nconst wrapper = document.getElementById('my-container-root');\n\nprovider.render(Button, { \n children: 'My Button', \n icon: Icon({ source: 'Heart' }), \n})(wrapper);\n```\n\nYou may also create your VNode using the `h` function from the Preact library. \n\n```js\n// Storefront\n\n\nconst wrapper = document.getElementById('my-container-root');\n\nprovider.render(Button, { icon: h('div', { id: 'my-vnode' }) })(wrapper);\n```\n\n### Update properties of a rendered component\n\nThe `setProps` method is provided by the instance returned from the `render` function. \nIt allows for dynamic updates to the properties of a rendered component. \nBy accepting an updater function, `setProps` lets you modify the component's props based on its previous state. \nThis method is particularly useful for making incremental changes or responding to user interactions \nwithout re-rendering the entire component. It ensures that the component's state remains consistent\nand up-to-date with the latest data or user inputs.\n\n#### Example\n\n```js\n// Storefront\n\n\nconst wrapper = document.getElementById('my-container-root');\nconst myContainer = await provider.render(MyContainer, { ...props })(wrapper);\n\nconst button = document.getElementById('my-button');\n\nbutton.addEventListener('click', () => {\n // Update the component's props\n myContainer.setProps((prevProps) => ({\n ...prevProps,\n newProp: 'new value',\n }));\n});\n```\n\n### Remove a rendered component from the DOM\n\nThe `remove` method is provided by the instance returned from the `render` function. \nIt allows for the complete removal of a rendered component from the DOM. \nWhen invoked, `remove` ensures that the component and its associated resources are properly cleaned up, \npreventing memory leaks and maintaining the application's overall performance. \nThis method is essential for managing the lifecycle of dynamic components, \nespecially in applications where components need to be frequently added and removed based \non user interactions or other events.\n\n```js\n\n\nconst wrapper = document.getElementById('my-container-root');\nconst myContainer = await provider.render(MyContainer, { ...props })(wrapper);\n\nconst button = document.getElementById('my-button');\n\nbutton.addEventListener('click', () => {\n // Remove the component from the DOM\n myContainer.remove();\n});\n```"
77
+ },
78
+ {
79
+ "path": "sdk/reference/slots",
80
+ "title": "Slots",
81
+ "description": "Define and manage dynamic content insertion within drop-in components.",
82
+ "content": "A Slot is a high-level interface for developers to define and manage dynamic content insertion within drop-in components.\n\n## Context\n\nThe context is defined during implementation of a drop-in and can be used to pass data and functions to the slot.\n\n### Pre-built Methods\n\n- **dictionary**: The dictionary of the selected language.\n- **replaceWith**: A function to replace the slot's content with a new HTML element.\n- **appendChild**: A function to append a new HTML element to the slot's content.\n- **prependChild**: A function to prepend a new HTML element to the slot's content.\n- **appendSibling**: A function to append a new HTML element after the slot's content.\n- **prependSibling**: A function to prepend a new HTML element **before** the slot's content.\n- **getSlotElement**: A function to get a slot element.\n- **onChange**: A function to listen to changes in the slot's context.\n\n---\n\n## Implementing a new slot\n\nThe `` component is used to define a slot in a container. It receives a name and a slot object with the following properties:\n\n### name\n\nThe name of the slot in _PascalCase_. `string` (required).\n\n### slotTag\n\nThe HTML tag to use for the slot's wrapper element. This allows you to change the wrapper element from the default `div` to any valid HTML tag (e.g., 'span', 'p', 'a', etc.). When using specific tags like 'a', you can also provide their respective HTML attributes (e.g., 'href', 'target', etc.).\n\nExample:\n```tsx\n// Render with a span wrapper\n<Slot name=\"MySlot\" slotTag=\"span\">\n Inline content\n</Slot>\n\n// Render with an anchor wrapper\n<Slot name=\"MySlot\" slotTag=\"a\" href=\"https://example.com\" target=\"_blank\">\n Link content\n</Slot>\n```\n\n### contentTag\n\nThe HTML tag to use for wrapping dynamically inserted content within the slot. This is separate from the slot's wrapper tag and allows you to control how dynamic content is structured. Defaults to 'div'.\n\nExample:\n```tsx\n<Slot\n name=\"MySlot\"\n slotTag=\"article\" // The outer wrapper will be an article\n contentTag=\"section\" // Dynamic content will be wrapped in sections\n slot={(ctx) => {\n const elem = document.createElement('div');\n elem.innerHTML = 'Dynamic content';\n ctx.appendChild(elem); // This will be wrapped in a section tag\n }}\n/>\n```\n\n### slot (required)\n\n- `ctx`: An object representing the context of the slot, including methods for manipulating the slot's content.\n\nThe slot property, which is implemented as a promise function, provides developers with the flexibility to dynamically generate and manipulate content within slots. \nHowever, it's important to note that this promise is render-blocking, meaning that the component will not render until the promise is resolved.\n\n### context\n\nThe context property in the Slot component lets developers pass extra information or functionality to customize how the slot\nbehaves or interacts with the application. This information is accessible within the slot's rendering logic, allowing for\ntailored slot behavior based on specific needs or application states.\n\n### render\n\nThe render property in the Slot component lets developers define how the content within the slot should be displayed and should be used when developers\nneed fine-grained control over what content appears within the slot.\nIt's particularly useful when using custom slot methods (see Privates below) in scenarios where the content to be displayed within the slot is dynamic and may depend on\nproperties passed to another component.\n\n### children\n\nThe children property in the Slot component represents the content that is passed directly within the opening and closing tags of the Slot component.\nIt allows developers to include static content directly within the slot, which will be rendered as part of the slot's contents.\nThis property is useful for cases where the content within the slot is static or does not need to be dynamically generated by the slot.\n\n```tsx\n// MyContainer.tsx (Drop-in)\n\n\nexport interface MyContainerProps extends HTMLAttributes<HTMLDivElement> {\n slots?: {\n MyOpenSlot?: SlotProps<{\n // MyOpenSlot Context\n data: MyContainerData;\n }>;\n };\n}\n\nexport const MyContainer: Container<MyContainerProps> = ({\n slots,\n children,\n ...props\n}) => {\n // ...\n\n return (\n \n \n \n );\n};\n```\n\n&nbsp;\n\n```js\n// blocks/my-block.js (storefront)\n\nprovider.render(MyContainer, {\n slots: {\n MyOpenSlot: async (ctx) => {\n // create a new HTML element\n const element = document.createElement('div');\n // set the innerHTML of the new element to the text from the context's data\n element.innerHTML = ctx.data.text;\n\n // append the new element to the slot's content\n ctx.appendChild(element);\n\n // ...or you could also use any of the other slot methods to manipulate the slot's content\n // ctx.replaceWith(element);\n // ctx.prependChild(element);\n // ctx.appendSibling(element);\n // ctx.prependSibling(element);\n\n // to listen and react to changes in the slot's context (lifecycle)\n ctx.onChange((next) => {\n // update the innerHTML of the new element to the new text from the context's data\n element.innerHTML = ctx.data.text;\n });\n },\n },\n});\n```\n\n## Privates\n\nThe `` component has a private interface that serves as a mechanism for managing internal\ncomplexity and promoting clean, modular design within the Slot component or related components.\n\n### \\_registerMethod\n\nThe `_registerMethod` private function is used to register a method in the slot's context which is particularly helpful in scenarios\nwhere dynamic behavior or interactions need to be incorporated into the Slot component.\n\nSlot Methods also include the ability to modify the slot's state or content based on external interactions or changes in application state.\n\n### \\_setProps\n\nThe `_setProps` private function within the Slot component is responsible for dynamically updating the properties of the slot.\nIt allows developers to modify the slot's state or content based on external interactions or changes in application state,\ntriggering re-renders of the slot component with updated properties.\n\n### \\_htmlElementToVNode\n\nThe `_htmlElementToVNode` private function in the Slot component converts HTML elements into virtual DOM nodes (VNodes),\nenabling their integration into Preact components. This conversion facilitates the dynamic insertion of HTML content\ninto slots while benefiting from Preact's virtual DOM reconciliation and rendering.\n\n```tsx\n// MyContainer.tsx (Drop-in)\n\n<Slot\n name=\"MyOpenSlot\"\n slot={slots?.MyOpenSlot}\n context={{\n // custom slot method\n appendButton(callback) {\n // use _registerMethod to register a method in the slot's context\n this._registerMethod((...attrs) => {\n // callback return the values provided by the storefront developer\n const { text, ...buttonProps } = callback(...attrs);\n\n const button = (\n <Button type=\"button\" {...buttonProps}>\n \n </Button>\n );\n\n // use _setProps to update the slot's properties\n this._setProps((prev: any) => ({\n children: [...(prev.children || []), button],\n }));\n });\n },\n }}\n render={(props) => {\n // render the slot's content using props mutated by the slot's methods\n return <Buttons></Buttons>;\n }}\n/>\n```\n\n&nbsp;\n\n```js\n// blocks/my-block.js (storefront)\n\nprovider.render(MyContainer, {\n slots: {\n // Available Slots\n MyOpenSlot: (ctx) => {\n ctx.appendButton: (next, state) => {\n // use state to get the current state of the slot\n const loading = state.get('loading');\n\n return {\n text: loading ? 'Loading' : 'Click me!',\n onClick: async () => {\n // use state to update the state of the slot\n state.set('loading', true);\n\n await doSomething().finally(() => {\n state.set('loading', false);\n });\n },\n };\n },\n },\n },\n});\n```"
83
+ },
84
+ {
85
+ "path": "sdk/reference/VComponent",
86
+ "title": "VComponent",
87
+ "description": "Pass arbitrary VNode props such as headers or footers into containers without forking drop-in source using the VComponent helper.",
88
+ "content": "In modern Preact-based architectures, composability and flexibility are essential for building reusable UI components. `VComponent` is a utility provided by the SDK that enables rendering of virtual nodes (`VNode`) passed as props—empowering consumers to inject arbitrary content while maintaining a clean separation of concerns.\n\n## Why use VComponent?\n\nBy default, Preact allows children to be passed as virtual nodes, enabling dynamic rendering:\n\n```tsx\n<MyComponent>\n <h1>Hello</h1>\n</MyComponent>\n```\nHowever, flexibility increases when we extend this pattern to named props like `header`, `footer`, or `image`. Instead of hardcoding internal markup, we delegate the responsibility of rendering to the consumer.\n\n## Traditional approach (tightly coupled)\n\nThe standard approach to rendering a component is to pass values as props directly to the component.\n\n**Implementation:**\n\n```tsx\nconst Card = ({ imageProps }) => {\n return <img {...imageProps} />;\n};\n```\n\n**Usage:**\n```tsx\n\n```\n\nThis implementation tightly couples the component to a specific HTML element (`<img>`), which limits its flexibility and reuse.\n\n## Composable approach with VComponent\n\nThe composable approach with `VComponent` allows consumers to pass arbitrary DOM nodes through props.\n\n**Implementation:**\n\n```tsx\n\ninterface Props {\n image: VNode;\n}\n\nconst Card = ({ image }: Props) => {\n return ;\n};\n```\n\n**Usage:**\n\n```tsx\n} />\n// or with a custom slot/component\n} />\n```\n\nThis decouples the component from a specific element. Instead, it renders whatever VNode is passed in. Consumers now have full control over what gets displayed.\n\n## How it works\n\n`VComponent` is a thin wrapper around a virtual node (`VNode`). It renders the node it receives as-is, while optionally applying extra props like `className`.\n\nThis makes it ideal for rendering content passed through slots or injected from a higher-order component.\n\n```tsx\n\n```\n\n## When to use it\n\nUse `VComponent` when:\n\n- You want to allow injected custom DOM nodes (icons, slots, rich content)\n- You're designing reusable components meant to be extended or implemented by different consumers (Containers, Slots, etc.)\n\n## Benefits\n\n- Promotes reusability and composability\n- Supports custom rendering logic with no assumptions\n- Reduces internal complexity by offloading rendering decisions\n- Ideal for BYO-UI and dynamic layout strategies"
89
+ },
90
+ {
91
+ "path": "sdk/utilities",
92
+ "title": "Overview",
93
+ "description": "Import debounce helpers, cookie readers, form parsers, deep merges, and other small SDK utilities used across drop-in bundles.",
94
+ "content": "The utilities section provides a collection of helper functions to simplify common programming tasks when working with the SDK. These utilities handle everything from DOM manipulation and event handling to data transformation and form management. Each utility is designed to be lightweight, performant, and easy to integrate into your codebase.\n\n## Topics\n\n- [classList](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/classlist/) - Helper for managing CSS class names\n- [debounce](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/debounce/) - Limit how often a function can be called\n- [deepmerge](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/deepmerge/) - Deeply merge multiple objects\n- [getCookie](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/getcookie/) - Get cookie value from name\n- [getFormErrors](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/getformerrors/) - Get form validation errors\n- [getFormValues](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/getformvalues/) - Get form field values\n- [getPathValue](https://experienceleague.adobe.com/developer/commerce/storefront/sdk/utilities/getpathvalue/) - Get value from obj key\n\nBy leveraging these utility functions, you can streamline your development workflow and reduce boilerplate code while maintaining clean, maintainable implementations. These utilities are designed to work seamlessly with the SDK's components and core functionality, helping you build robust applications more efficiently. Each utility is thoroughly tested and optimized for performance, making them reliable tools for your development needs."
95
+ },
96
+ {
97
+ "path": "sdk/utilities/classList",
98
+ "title": "classList",
99
+ "description": "Returns a string of space separated entries that can be used for CSS classname assignments.",
100
+ "content": "This function takes in an array of classes and returns a string of space separated entries that can be used for CSS classname assignments.\n\n## Params\n\n`classList`\n: An array containing strings or `<string,boolean>` arrays\n\n## Returns\n\nReturns a string of space separated entries that can be used for CSS classname assignments.\n\n## Examples\n\n```ts\n\ntype ClassList = Array<string | [string, boolean] | undefined>;\n\nconst classList: ClassList = ['class-1', 'class-2', 'class-3'];\n\nconst result = classes(classList);\n\nconsole.log(result); // \"class-1 class-2 class-3\"\n```\n\nYou can use a `<string,boolean>` array to control whether a class should be included or omitted from the final classes list.\n\n```ts\n\ntype ClassList = Array<string | [string, boolean] | undefined>;\n\nconst classList: ClassList = [\n 'class-1',\n ['class-2', true],\n ['class-3', false],\n 'class-4',\n];\n\nconst result = classes(classList);\n\nconsole.log(result); // \"class-1 class-2 class-4\"\n```"
101
+ },
102
+ {
103
+ "path": "sdk/utilities/debounce",
104
+ "title": "debounce",
105
+ "description": "Wrap a callback so it fires only after input stops for a chosen delay, ideal for Adobe Commerce search boxes, filters, and quantity edits that would otherwise thrash the network or UI thread.",
106
+ "content": "This function provides a way to delay callback execution or prevent overcalling a function until certain conditions are met.\n\n## Params\n\n`fn`\n: The callback function to be executed\n\n`ms`\n: Time(in milliseconds) to delay callback execution\n\n## Returns\n\nA debounce version of the original callback function.\nThis function can be treated like the original callback, except when called, the delay timer resets.\n\n## Examples\n\n```ts\n\nconst debouncedLog = debounce(console.log, 500);\n\ndebouncedLog('Do not log this string');\n\n// Wait 250ms\n\ndebouncedLog('Do not log this string'); // Resets delay timer\n\n// Wait 250ms\n\ndebouncedLog('Do not log this string'); // Resets delay timer\n\n// Wait 250ms\n\ndebouncedLog('Log this string'); // Resets delay timer\n\n// Wait 500ms\n\n// 'Log this string' is logged to the console and no other messages have been logged\n```"
107
+ },
108
+ {
109
+ "path": "sdk/utilities/deepmerge",
110
+ "title": "deepmerge",
111
+ "description": "Merges the enumerable properties of two or more objects deeply.",
112
+ "content": "Merges the enumerable properties of two or more objects deeply.\n\nSee: https://www.npmjs.com/package/deepmerge"
113
+ },
114
+ {
115
+ "path": "sdk/utilities/getCookie",
116
+ "title": "getCookie",
117
+ "description": "Read a document cookie by name for lightweight session, segmentation, or consent checks inside Storefront SDK integrations that share shopper state across drop-ins.",
118
+ "content": "Takes in a cookie name and returns the value of the cookie.\n\n## Params\n\nSignature: `getCookie(cookieName)`\n\n`cookieName`\n: The name of the cookie to get\n\n## Returns\n\nReturns the value of the cookie.\n\n## Examples\n\n```ts\n\nconst result = getCookie('foo');\n\nconsole.log(result); // \"bar\"\n```"
119
+ },
120
+ {
121
+ "path": "sdk/utilities/getFormErrors",
122
+ "title": "getFormErrors",
123
+ "description": "Scan an HTMLFormElement for validation errors after browser constraint checks so you can mirror each invalid field with inline messaging in checkout, account, and B2B forms.",
124
+ "content": "Returns every form error in an HTML form element.\n\n## Params\n\n`form`\n: An HTMLFormElement\n\n## Returns\n\nA JavaScript object containing the form errors\n\n## Examples\n\n```html\n<form>\n <input name=\"foo\" required value=\"Foo\" />\n <input name=\"bar\" required />\n <input name=\"age\" type=\"number\" value=\"uno\" />\n <input name=\"website\" type=\"url\" value=\"url\" />\n <input name=\"e-mail\" type=\"email\" value=\"email@\" />\n</form>\n```\n\n```ts\n\nconst formElement = container.querySelector('form') as HTMLFormElement;\n\nconst errors = getFormErrors(formElement);\n\nconsole.log(errors); // { bar: 'Constraints not satisfied', website: 'Constraints not satisfied', 'e-mail': 'Constraints not satisfied', }\n```"
125
+ },
126
+ {
127
+ "path": "sdk/utilities/getFormValues",
128
+ "title": "getFormValues",
129
+ "description": "Transforms the data in an HTML form element into a JavaScript object.",
130
+ "content": "Transforms the data in an HTML form element into a JavaScript object.\n\n## Params\n\n`form`\n: An HTMLFormElement\n\n## Returns\n\nA JavaScript object containing the form data\n\n## Examples\n\n```html\n<form>\n <input name=\"foo\" required value=\"Foo\" />\n <input name=\"bar\" required value=\"Bar\" />\n</form>\n```\n\n```ts\n\nconst formElement = container.querySelector('form') as HTMLFormElement;\n\nconst values = getFormValues(formElement);\n\nconsole.log(values); // { bar: 'Bar', foo: 'Foo' }\n```"
131
+ },
132
+ {
133
+ "path": "sdk/utilities/getPathValue",
134
+ "title": "getPathValue",
135
+ "description": "Safely read nested form or configuration values with dot-path keys such as address.street without writing manual null guards in SDK integrations.",
136
+ "content": "Takes in an object and a key and returns the value of the key.\n\n## Params\n\nSignature: `getPathValue(obj, key)`\n\n`obj`\n: The object to get the value from\n\n`key`\n: The key to get the value from (supports dot notation)\n\n## Returns\n\nReturns the value of the key.\n\n## Examples\n\n```ts\n\nconst obj = {\n foo: {\n bar: 'baz',\n },\n};\n\nconst result = getPathValue(obj, 'foo.bar');\n\nconsole.log(result); // \"baz\"\n```"
137
+ }
138
+ ]
139
+ }