npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@adobe-commerce/aio-toolkit

v1.0.14

Published

A comprehensive TypeScript toolkit for Adobe App Builder applications providing standardized Adobe Commerce integrations, I/O Events orchestration, file storage utilities, authentication helpers, and robust backend development tools with 100% test coverag

Readme

@adobe-commerce/aio-toolkit

Overview

A comprehensive TypeScript toolkit for Adobe App Builder applications providing standardized Adobe Commerce integrations, I/O Events orchestration, file storage utilities, authentication helpers, and robust backend development tools with 100% test coverage and enterprise-grade reliability.

Installation

npm install @adobe-commerce/aio-toolkit

Usage

The toolkit is organized into five main modules:

🛠️ Framework Components

Core infrastructure for Adobe App Builder applications

RuntimeAction

HTTP request handling and business logic execution for Adobe I/O Runtime.

const {
  RuntimeAction,
  RuntimeActionResponse,
  HttpMethod,
  HttpStatus,
} = require('@adobe-commerce/aio-toolkit');

// Create a simple action
const myAction = RuntimeAction.execute(
  'process-user', // Action name
  [HttpMethod.POST], // Allowed HTTP methods
  ['userId'], // Required parameters
  ['authorization'], // Required headers
  async (params, ctx) => {
    // Your business logic here
    const { userId } = params;
    const { logger } = ctx;

    logger.info(`Processing request for user: ${userId}`);

    // Return success response
    return RuntimeActionResponse.success({
      message: 'Action completed successfully',
      userId: userId,
    });
  }
);

// Export for Adobe I/O Runtime
exports.main = myAction;

EventConsumerAction

Event-driven processing for Adobe I/O Events with automatic validation.

const { 
  EventConsumerAction,
  RuntimeActionResponse,
  HttpStatus
} = require('@adobe-commerce/aio-toolkit');

// Create a simple event consumer
const myEventConsumer = EventConsumerAction.execute(
  'commerce-event-handler',
  ['eventType'], // Required parameters
  ['x-adobe-signature'], // Required headers
  async (params, ctx) => {
    const { eventType } = params;
    const { logger } = ctx;

    logger.info(`Processing event: ${eventType}`);

    // Process the event
    await processCommerceEvent(eventType, params);

    // Return success response
    return RuntimeActionResponse.success({
      message: 'Event processed successfully',
      eventType: eventType,
      processedAt: new Date().toISOString()
    });
  }
);

// Export for Adobe I/O Runtime
exports.main = myEventConsumer;

WebhookAction

Secure webhook request handler with built-in Adobe Commerce signature verification.

const { 
  WebhookAction,
  WebhookActionResponse,
  SignatureVerification
} = require('@adobe-commerce/aio-toolkit');

// Create a webhook action with signature verification enabled
const myWebhook = WebhookAction.execute(
  'order-webhook',
  ['orderId'], // Required parameters
  ['x-adobe-commerce-webhook-id'], // Required headers
  SignatureVerification.ENABLED, // Enable signature verification
  async (params, ctx) => {
    const { orderId } = params;
    const { logger } = ctx;

    logger.info(`Processing order webhook: ${orderId}`);

    // Your webhook logic here
    // Return structured webhook response
    return [
      WebhookActionResponse.add('result', {
        orderId: orderId,
        status: 'processed',
        timestamp: new Date().toISOString()
      }),
      WebhookActionResponse.success()
    ];
  }
);

// Export for Adobe I/O Runtime
exports.main = myWebhook;

// Disable signature verification for testing
const testWebhook = WebhookAction.execute(
  'test-webhook',
  ['data'],
  [],
  SignatureVerification.DISABLED,
  async (params, ctx) => {
    return WebhookActionResponse.success();
  }
);

WebhookActionResponse Operations:

  • success(): Indicates successful webhook processing
  • exception(message?, exceptionClass?): Returns error response
  • add(path, value, instance?): Adds data to response
  • replace(path, value, instance?): Replaces data in response
  • remove(path): Removes data from response

PublishEvent

Event publishing component for Adobe I/O Events with CloudEvents support.

const { PublishEvent } = require('@adobe-commerce/aio-toolkit');

// Initialize the publisher
const publishEvent = new PublishEvent(
  'your-ims-org-id@AdobeOrg',
  'your-api-key',
  'your-access-token',
  logger // Optional custom logger
);

// Publish a simple event
const result = await publishEvent.execute(
  'your-provider-id',
  'commerce.order.created',
  {
    orderId: 'ORD-123456',
    customerId: 'CUST-789',
    amount: 199.99,
    currency: 'USD'
  }
);

console.log(`Event published: ${result.eventId}, Status: ${result.status}`);

// Publish an event with subject
const orderResult = await publishEvent.execute(
  'commerce-provider',
  'com.adobe.commerce.order.shipped',
  {
    orderId: 'ORD-123456',
    trackingNumber: 'TRK-789',
    carrier: 'UPS',
    estimatedDelivery: '2023-12-05T18:00:00Z'
  },
  'orders/ORD-123456'
);

// Handle publishing results
if (orderResult.status === 'published') {
  console.log(`Order shipped event published successfully: ${orderResult.eventId}`);
} else {
  console.error(`Failed to publish event: ${orderResult.error}`);
}

GraphQlAction

GraphQL server implementation with schema validation and introspection control.

const { GraphQlAction } = require('@adobe-commerce/aio-toolkit');

const schema = `
  type Query {
    hello: String
    user(id: ID!): User
  }
  
  type User {
    id: ID!
    name: String!
    email: String!
  }
`;

const resolvers = async ctx => {
  const { logger } = ctx;

  return {
    hello: () => 'Hello, World!',
    user: ({ id }) => {
      logger.debug(`Fetching user: ${id}`);
      return {
        id,
        name: 'John Doe',
        email: '[email protected]'
      };
    }
  };
};

// Create and export GraphQL endpoint
exports.main = GraphQlAction.execute(schema, resolvers);

OpenWhisk & OpenWhiskAction

OpenWhisk client for serverless action invocation and integration. OpenWhisk action wrapper with enhanced logging and error handling.

const { OpenWhisk, OpenwhiskAction, RuntimeActionResponse } = require('@adobe-commerce/aio-toolkit');

// Invoke another action
const invokeAction = async (params) => {
  try {
    // Initialize OpenWhisk client
    const openwhisk = new Openwhisk(params.API_HOST, params.API_AUTH)

    const result = await openwhisk.execute('hello-world', {
      name: 'World'
    });
    
    console.log('Action result:', result);
    return result;
  } catch (error) {
    console.error('Action invocation failed:', error);
    throw error;
  }
};

// Simple action with logging
const helloWorldAction = OpenwhiskAction.execute('hello-world', async (params, ctx) => {
  const { logger } = ctx;
  const { name = 'World' } = params;

  logger.info(`Greeting request for: ${name}`);

  const message = `Hello, ${name}!`;

  logger.info(`Generated greeting: ${message}`);

  return RuntimeActionResponse.success({
    message,
    timestamp: new Date().toISOString(),
    action: 'hello-world'
  });
});

// Export for Adobe I/O Runtime
exports.main = helloWorldAction;

Telemetry

OpenTelemetry integration for enterprise-grade observability with distributed tracing, metrics collection, and structured logging.

Features:

  • Automatic telemetry instrumentation for all framework action classes
  • Request ID correlation across all log messages (x-adobe-commerce-request-id)
  • Action type identification for better filtering (action.type)
  • Access to current OpenTelemetry span via ctx.telemetry for custom instrumentation
  • telemetry.instrument() method for wrapping functions with automatic span creation
  • Conditional span access based on ENABLE_TELEMETRY flag
  • Multi-provider support (New Relic implemented, Grafana ready)
  • Graceful fallback when telemetry is not configured
  • Zero performance impact when disabled (opt-in via feature flags)

Configuration:

Configure telemetry in your app.config.yaml:

runtime-action:
  function: actions/runtime-action/index.js
  web: 'yes'
  runtime: nodejs:22
  inputs:
    LOG_LEVEL: debug
    ENVIRONMENT: $ENVIRONMENT
    ENABLE_TELEMETRY: true
    NEW_RELIC_TELEMETRY: true
    NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
    NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
  annotations:
    require-adobe-auth: true
    final: true

Set environment variables in your .env file:

# Telemetry configuration
ENVIRONMENT=production
NEW_RELIC_SERVICE_NAME=my-app-builder-app
NEW_RELIC_LICENSE_KEY=your-license-key-here
NEW_RELIC_URL=https://otlp.nr-data.net:4318  # Optional

Usage Example:

Telemetry is automatically initialized when you use framework action classes:

/*
 * <license header>
 */

const { RuntimeAction, RuntimeActionResponse, HttpMethod } = require("@adobe-commerce/aio-toolkit");

// Custom function with telemetry
const processData = (data, telemetry, logger) => {
  const span = telemetry?.getCurrentSpan?.();
  if (span) {
    span.setAttribute('data.size', data.length);
  }
  logger.info('Processing data');
  return { processed: true };
};

exports.main = RuntimeAction.execute(
  'my-action',
  [HttpMethod.POST],
  ['data'],
  ['Authorization'],
  async (params, ctx) => {
    const { logger, telemetry } = ctx;

    // Logger automatically includes x-adobe-commerce-request-id and action.type
    logger.info('Action started');

    // Wrap function to create child span
    const instrumented = telemetry?.instrument?.('data.process', processData) || processData;
    const result = instrumented(params.data, telemetry, logger);

    return RuntimeActionResponse.success(result);
  }
);

Key Features:

  • telemetry.getCurrentSpan(params) - Access current span for custom attributes and events
  • telemetry.instrument(spanName, fn) - Wrap functions to create child spans automatically
  • Safe with optional chaining - gracefully degrades when telemetry is disabled
  • Logger automatically includes request ID and action type for correlation

What Gets Logged Automatically:

All log messages automatically include:

  • x-adobe-commerce-request-id - Extracted from webhook headers for request correlation
  • action.type - Identifies the action type (runtime-action, webhook-action, event-consumer-action, openwhisk-action)
  • Action lifecycle events (start, parameters, completion, errors)
  • Full stack traces for errors

Viewing in New Relic:

Once configured, you can query your logs in New Relic using these attributes:

-- Filter by request ID
SELECT * FROM Log WHERE `x-adobe-commerce-request-id` = 'request-123'

-- Filter by action type
SELECT * FROM Log WHERE `action.type` = 'webhook-action'

-- Find errors for a specific request
SELECT * FROM Log WHERE `x-adobe-commerce-request-id` = 'request-123' AND level = 'error'

-- Group by action type
SELECT count(*) FROM Log FACET `action.type` SINCE 1 hour ago

Supported Action Classes:

Telemetry is automatically integrated with all framework action classes:

  • RuntimeAction - HTTP request handling with telemetry
  • WebhookAction - Webhook processing with telemetry
  • EventConsumerAction - Event-driven processing with telemetry
  • OpenwhiskAction - OpenWhisk action handling with telemetry

All action classes provide structured logging with automatic request ID and action type tracking.

RuntimeApiGatewayService

Flexible Adobe I/O Runtime API Gateway client that accepts bearer tokens for authenticated requests.

const { RuntimeApiGatewayService, AdobeAuth } = require('@adobe-commerce/aio-toolkit');

// Step 1: Generate token using AdobeAuth (implement caching in your application)
const token = await AdobeAuth.getToken(
  'your-client-id',
  'your-client-secret',
  'your-technical-account-id',
  '[email protected]',
  'your-ims-org-id@AdobeOrg',
  ['openid', 'AdobeID', 'adobeio_api']
);

// Step 2: Initialize the service with the token
const apiGatewayService = new RuntimeApiGatewayService(
  'your-namespace-12345',          // Runtime namespace
  'your-ims-org-id@AdobeOrg',      // IMS Org ID
  token,                            // Bearer token
  logger                            // Optional custom logger
);

// GET request
const data = await apiGatewayService.get('v1/web/my-package/my-action');
console.log('Action response:', data);

// POST request with payload
const result = await apiGatewayService.post('v1/web/my-package/process', {
  orderId: 'ORD-123',
  action: 'process'
});

// PUT request
const updateResult = await apiGatewayService.put('v1/web/my-package/update', {
  id: '123',
  status: 'completed'
});

// DELETE request
const deleteResult = await apiGatewayService.delete('v1/web/my-package/remove/123');

Key Features:

  • Built-in authentication headers
  • Support for GET, POST, PUT, DELETE methods
  • Flexible endpoint configuration
  • Comprehensive error handling
  • Accepts pre-generated bearer tokens (implement your own caching strategy)
  • CustomLogger integration

💡 Best Practice: Implement token caching at the application level (Redis, Memcached, in-memory) and validate tokens with BearerToken.info() before use to avoid unnecessary token generation calls.

FileRepository

File-based storage with CRUD operations for Adobe I/O Runtime applications.

Key Methods:

  • save(payload, id?, overwrite?): Saves data with optional ID parameter and overwrite flag. The id parameter takes precedence over payload.id. IDs are automatically sanitized to alphanumeric + underscore characters. Set overwrite: true to replace entire file, or false (default) to merge with existing data.
  • load(id): Loads data by ID with file system timestamps (createdAt, updatedAt)
  • list(): Lists all stored records with file system timestamps
  • metadata(id?): Retrieves file metadata (size, timestamps) without reading content - faster than list() for large datasets
  • delete(ids): Deletes records by ID array

New in v1.0.4:

  • File System Timestamps: createdAt and updatedAt are now retrieved from actual file system properties instead of being stored in file content
  • Metadata Method: New metadata() method for efficient file property retrieval without reading file content
  • Overwrite Flag: Control file update strategy with save(payload, id, overwrite) - merge (default) or replace entire file

Best Practice: Create custom repository classes that extend FileRepository for specific entities.

1. Define Entity Repository

Create a custom repository class extending FileRepository:

const { FileRepository } = require("@adobe-commerce/aio-toolkit");

class EntityRepository extends FileRepository {
    /**
     * @constructor
     */
    constructor() {
        super("/toolkit-demo/entity");
    }
}

module.exports = EntityRepository;
2. List Action

Retrieve all entities from the repository:

const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");

exports.main = RuntimeAction.execute(
    "entity-list",
    [HttpMethod.POST],
    [],
    ['Authorization'],
    async (params) => {
        const entityRepository = new EntityRepository();
        return RuntimeActionResponse.success(await entityRepository.list());
    }
);
3. Load Action

Load a specific entity by ID:

const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");

exports.main = RuntimeAction.execute(
    "entity-load",
    [HttpMethod.POST],
    ['id'],
    ['Authorization'],
    async (params) => {
        const entityRepository = new EntityRepository();
        return RuntimeActionResponse.success(await entityRepository.load(params.id));
    }
);
4. Save Action

Save entity data with flexible ID handling using the new optional ID parameter:

const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");

const requiredParams = [
    "name",
    "status"
];

exports.main = RuntimeAction.execute(
    "entity-save",
    [HttpMethod.POST],
    requiredParams,
    ['Authorization'],
    async (params) => {
        const entityRepository = new EntityRepository();

        // Build payload with required fields
        let payload = {};
        for (const fieldName in requiredParams) {
            payload[requiredParams[fieldName]] = params[requiredParams[fieldName]];
        }

        // Extract ID parameter for prioritized handling
        const explicitId = params.id || params.customId || null;
        
        // Save with optional ID parameter - it takes precedence over payload.id
        const savedId = await entityRepository.save(payload, explicitId);

        return RuntimeActionResponse.success({
            id: savedId,
            message: 'Entity saved successfully'
        });
    }
);
5. Save with Overwrite Flag

Control file update strategy with the overwrite parameter:

const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");

exports.main = RuntimeAction.execute(
    "entity-save-overwrite",
    [HttpMethod.POST],
    ['name', 'status'],
    ['Authorization'],
    async (params) => {
        const entityRepository = new EntityRepository();

        const payload = {
            name: params.name,
            status: params.status
        };

        // Replace entire file instead of merging
        const savedId = await entityRepository.save(payload, params.id, true);

        return RuntimeActionResponse.success({
            id: savedId,
            message: 'Entity replaced successfully'
        });
    }
);
6. Metadata Action

Retrieve file metadata without reading content (faster for large datasets):

const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");

exports.main = RuntimeAction.execute(
    "entity-metadata",
    [HttpMethod.POST],
    [],
    ['Authorization'],
    async (params) => {
        const entityRepository = new EntityRepository();
        
        // Get metadata for all files
        const allMetadata = await entityRepository.metadata();
        
        // Or get metadata for specific file
        const singleMetadata = params.id 
            ? await entityRepository.metadata(params.id)
            : null;

        return RuntimeActionResponse.success({
            all: allMetadata,
            single: singleMetadata
        });
    }
);
7. Delete Action

Delete entities by providing an array of IDs:

const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");

exports.main = RuntimeAction.execute(
    "entity-delete",
    [HttpMethod.POST],
    ['ids'],
    ['Authorization'],
    async (params) => {
        const entityRepository = new EntityRepository();
        return RuntimeActionResponse.success(await entityRepository.delete(params.ids));
    }
);

This approach provides:

  • Separation of concerns: Each CRUD operation has its own action file
  • Reusable repository: Custom repository can be shared across actions
  • Proper validation: Required parameters and headers are enforced
  • Consistent responses: All actions use RuntimeActionResponse for standardized output
  • Flexible ID management: Support for explicit IDs, payload IDs, and auto-generation
  • Automatic sanitization: IDs are cleaned to ensure file system compatibility

🏪 Commerce Components

Adobe Commerce API integration and authentication

AdobeAuth

Adobe IMS authentication and token management.

const { AdobeAuth } = require('@adobe-commerce/aio-toolkit');

// Get authentication token
const token = await AdobeAuth.getToken(
  'your-client-id',
  'your-client-secret', 
  'your-technical-account-id',
  'your-technical-account-email',
  'your-ims-org-id',
  ['AdobeID', 'openid', 'adobeio_api'] // Scopes
);

console.log('Authentication token:', token);

AdobeCommerceClient

HTTP client for Adobe Commerce API integration with multiple authentication methods.

OAuth 1.0a Authentication

const { AdobeCommerceClient, Oauth1aConnection } = require('@adobe-commerce/aio-toolkit');

const connection = new Oauth1aConnection(
  'consumer-key',
  'consumer-secret',
  'access-token',
  'access-token-secret',
  logger // Optional custom logger
);

// Create client
const client = new AdobeCommerceClient('https://your-commerce-store.com/rest', connection);

// Make API calls
const products = await client.get('V1/products');
const newProduct = await client.post('V1/products', {}, productData);

IMS (Identity Management System) Authentication

const { AdobeCommerceClient, ImsConnection, AdobeAuth, BearerToken } = require('@adobe-commerce/aio-toolkit');

// Step 1: Generate token using AdobeAuth (implement caching in your application)
// Check your application cache first
let token = await yourAppCache.get('commerce_ims_token');

if (!token || !BearerToken.info(token).isValid) {
  // Generate new token
  token = await AdobeAuth.getToken(
    'client-id',
    'client-secret',
    'technical-account-id',
    'technical-account-email',
    'ims-org-id@AdobeOrg',
    ['AdobeID', 'openid', 'adobeio_api']
  );

  // Store in your application cache with appropriate TTL
  const tokenInfo = BearerToken.info(token);
  if (tokenInfo.isValid && tokenInfo.timeUntilExpiry) {
    const ttl = Math.floor(tokenInfo.timeUntilExpiry / 1000) - 600; // 10-min buffer
    await yourAppCache.set('commerce_ims_token', token, ttl);
  }
}

// Step 2: Create connection with token
const connection = new ImsConnection(token, logger); // Optional logger

// Step 3: Create client with IMS authentication
const client = new AdobeCommerceClient('https://your-commerce-store.com', connection);

// Make API calls
const products = await client.get('V1/products');
const newProduct = await client.post('V1/products', {}, productData);

💡 Best Practice: Implement token caching at the application level (Redis, Memcached, in-memory) to avoid unnecessary token generation calls.

⚠️ DEPRECATED: ImsToken Class

This class is deprecated due to built-in caching mechanism issues:

  • Relies on Adobe I/O Runtime State API which has reliability issues
  • State API may not be available in all runtime environments
  • Hard-coded TTL buffers and minimum TTL are inflexible
  • Limited control over caching strategy

Migration Required: Use AdobeAuth directly and implement caching at the application level.

// ❌ OLD (Deprecated) - ImsToken with built-in caching
const { ImsToken } = require('@adobe-commerce/aio-toolkit');

const imsToken = new ImsToken(
  'client-id',
  'client-secret',
  'technical-account-id',
  'technical-account-email',
  'ims-org-id@AdobeOrg',
  ['AdobeID', 'openid', 'adobeio_api'],
  logger // optional
);

const token = await imsToken.execute();
// ✅ NEW (Recommended) - AdobeAuth with application-level caching
const { AdobeAuth, BearerToken } = require('@adobe-commerce/aio-toolkit');

// Check your application cache first (Redis, Memcached, in-memory, etc.)
let token = await yourAppCache.get('ims_token');

// Validate cached token
if (!token || !BearerToken.info(token).isValid) {
  // Generate new token using AdobeAuth
  token = await AdobeAuth.getToken(
    'client-id',
    'client-secret',
    'technical-account-id',
    'technical-account-email',
    'ims-org-id@AdobeOrg',
    ['AdobeID', 'openid', 'adobeio_api']
  );

  // Store in your application cache with appropriate TTL
  const tokenInfo = BearerToken.info(token);
  if (tokenInfo.isValid && tokenInfo.timeUntilExpiry) {
    const ttl = Math.floor(tokenInfo.timeUntilExpiry / 1000) - 600; // 10-min buffer
    await yourAppCache.set('ims_token', token, ttl);
  }
}

// Use the token
console.log('Token:', token);

Why Migrate?

  • ✅ Full control over caching strategy (Redis, Memcached, in-memory, etc.)
  • ✅ Better error handling and retry logic
  • ✅ Works in any environment (not just Adobe I/O Runtime)
  • ✅ Easier to test and mock
  • ✅ Flexible TTL management based on your needs

Basic Authentication

const { AdobeCommerceClient, BasicAuthConnection } = require('@adobe-commerce/aio-toolkit');

const connection = new BasicAuthConnection(
  'username',
  'password',
  logger // Optional custom logger
);

// Create client
const client = new AdobeCommerceClient('https://your-commerce-store.com/rest', connection);

// Make API calls
const products = await client.get('V1/products');

ShippingCarrier

Fluent builder for creating custom shipping carriers for Adobe Commerce webhook extensibility.

const { 
  ShippingCarrier,
  ShippingCarrierResponse
} = require('@adobe-commerce/aio-toolkit');

// Create a custom shipping carrier with methods
const carrier = new ShippingCarrier('fedex', (carrier) => {
  carrier
    .setTitle('FedEx Express')
    .setStores(['default', 'store1'])
    .setCountries(['US', 'CA', 'MX'])
    .setSortOrder(10)
    .setActive(true)
    .setTrackingAvailable(true)
    .setShippingLabelsAvailable(true)
    .addMethod('standard', (method) => {
      method
        .setMethodTitle('Standard Shipping')
        .setPrice(9.99)
        .setCost(5.00)
        .addAdditionalData('delivery_time', '3-5 business days')
        .addAdditionalData('tracking_available', true);
    })
    .addMethod('express', (method) => {
      method
        .setMethodTitle('Express Shipping')
        .setPrice(19.99)
        .setCost(12.00)
        .addAdditionalData('delivery_time', '1-2 business days');
    })
    .removeMethod('overnight'); // Remove a method
});

// Get carrier configuration
const carrierData = carrier.getData();
console.log(carrierData);

// Access added and removed methods
const addedMethods = carrier.getAddedMethods();   // ['standard', 'express']
const removedMethods = carrier.getRemovedMethods(); // ['overnight']

// Generate webhook response operations
const response = new ShippingCarrierResponse(carrier);
const operations = response.generate();
return operations; // Use in webhook action

// Update carrier data (code is immutable)
carrier.setData({
  code: 'ups', // This will be ignored - code remains 'fedex'
  title: 'Demo Postal Service',
  stores: ['default'],
  countries: ['US', 'CA'],
  sort_order: 10,
  active: true,
  tracking_available: true,
  shipping_labels_available: true
});

// Code property is immutable - create new instance if you need different code
const newCarrier = new ShippingCarrier('ups', (c) => {
  c.addMethod('ground', (m) => {
    m.setMethodTitle('UPS Ground').setPrice(12.99);
  });
});

Key Features:

  • Builder pattern with method chaining
  • Validation for carrier and method codes (alphanumeric and underscores only)
  • Add and remove shipping methods dynamically
  • Configure carrier properties (title, stores, countries, sort order, etc.)
  • Immutable code property - prevents accidental carrier identity changes
  • Public getter methods: getAddedMethods() and getRemovedMethods()
  • Generate webhook response operations
  • Type-safe TypeScript interfaces

Validation Rules:

  • Carrier and method codes must contain only alphanumeric characters and underscores
  • No spaces, hyphens, dots, or special characters allowed
  • Empty or whitespace-only codes throw errors
  • Code property cannot be changed after initialization

OnboardCommerce

Complete Adobe Commerce I/O Events configuration orchestration with automated provider setup and event subscription management.

const { 
  OnboardCommerce, 
  AdobeCommerceClient, 
  ImsConnection 
} = require('@adobe-commerce/aio-toolkit');
const Core = require('@adobe/aio-sdk').Core;

// Generate token using AdobeAuth (implement caching in your application)
const token = await AdobeAuth.getToken(
  'client-id',
  'client-secret',
  'technical-account-id',
  'technical-account-email',
  'ims-org-id@AdobeOrg',
  ['AdobeID', 'openid', 'adobeio_api']
);

// Initialize Adobe Commerce client
const connection = new ImsConnection(token, logger);
const adobeCommerceClient = new AdobeCommerceClient(
  'https://your-commerce-store.com',
  connection
);

// Initialize logger
const logger = Core.Logger('onboard-client', {
  level: 'debug'
});

// Initialize OnboardCommerce
const onboardCommerce = new OnboardCommerce(
  adobeCommerceClient,
  process.env.COMMERCE_ADOBE_IO_EVENTS_MERCHANT_ID || '',
  process.env.COMMERCE_ADOBE_IO_EVENTS_ENVIRONMENT_ID || '',
  logger,
  false // isPaaS: set to true for PaaS instances (defaults to false)
);

// Define commerce provider
const commerceProvider = {
  raw: {
    id: 'commerce-provider-id',
    label: 'Commerce Events Provider',
    description: 'Provider for Adobe Commerce events',
    docsUrl: 'https://developer.adobe.com/commerce/events'
  }
};

// Define workspace configuration
const workspaceConfig = {
  project: {
    id: process.env.ADOBE_PROJECT_ID,
    name: 'My Commerce Project',
    title: 'Commerce Integration'
  },
  workspace: {
    id: process.env.ADOBE_WORKSPACE_ID,
    name: 'Production'
  }
};

// Define commerce events configuration
const commerceEventsConfig = [
  {
    event: {
      name: 'com.adobe.commerce.observer.catalog_product_save_after',
      label: 'Product Saved',
      description: 'Triggered when a product is saved'
    }
  },
  {
    event: {
      name: 'com.adobe.commerce.observer.sales_order_save_after',
      label: 'Order Saved',
      description: 'Triggered when an order is saved'
    }
  }
];

// Execute configuration
const result = await onboardCommerce.process(
  commerceProvider.raw,
  workspaceConfig,
  commerceEventsConfig
);

if (result.success) {
  console.log('✅ Commerce events configured successfully');
  console.log(`Provider: ${result.provider?.label}`);
} else {
  console.error('❌ Configuration failed:', result.error);
}

Key Features:

  • Automated provider configuration with validation
  • Event subscription management with duplicate detection
  • Intelligent event metadata validation against supported Commerce events
  • PaaS support for Adobe Commerce Cloud instances with native event handling
  • Structured logging with prefixes: [START], [CREATE], [SKIP], [ERROR], [IMPORTANT]
  • Comprehensive summary reporting with event subscription statistics
  • Integration with Adobe Commerce API for event discovery
  • Automatic post-subscription CLI instructions for PaaS instances
  • 100% test coverage

PaaS Support:

For Adobe Commerce PaaS (Platform as a Service) instances, enable PaaS mode to support native events:

// Initialize OnboardCommerce for PaaS instances
const onboardCommercePaaS = new OnboardCommerce(
  adobeCommerceClient,
  merchantId,
  environmentId,
  logger,
  true // Enable PaaS mode
);

// Define native PaaS events (observer.*, plugin.*)
const paasEventsConfig = [
  {
    event: {
      name: 'observer.catalog_product_save_after',
      label: 'Product Saved',
      description: 'Native observer event'
    }
  },
  {
    event: {
      name: 'plugin.magento.catalog.model.product.save',
      label: 'Product Save Plugin',
      description: 'Native plugin event'
    }
  }
];

// Process PaaS events
const result = await onboardCommercePaaS.process(
  commerceProvider.raw,
  workspaceConfig,
  paasEventsConfig
);

// PaaS mode automatically:
// - Skips supported events validation for better performance
// - Handles native events without the 'parent' field
// - Displays post-subscription Magento CLI commands

After successful event subscription on PaaS, the system displays:

[IMPORTANT] ⚠️  Post-Subscription Steps for PaaS:
[IMPORTANT] 1. Run: bin/magento events:generate:module to generate module after successful event subscription
[IMPORTANT] 2. Run: bin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush to install the generated module

🎨 Experience Components

Adobe Commerce Admin UI extension and user experience tools

AdminUiSdk

Create and manage Adobe Commerce Admin UI extensions with menu items, sections, and page configurations.

const { AdminUiSdk } = require('@adobe-commerce/aio-toolkit');

const sdk = new AdminUiSdk('dataMappingTool');

// Add menu section with external parent
sdk.addMenuSection(
  'dataMappingTool::checkout_integration',
  'Checkout Integration',
  100,
  'Magento_Backend::system'
);

// Add menu item
sdk.addMenuItem(
  'dataMappingTool::application',
  'Application',
  1,
  'dataMappingTool::checkout_integration'
);

// Set page title and get registration
sdk.addPage('Data Mapping Tool Dashboard');
const registration = sdk.getRegistration();

🔗 Integration Components

External API integration and utility functions

RestClient

HTTP client for external API integration with support for various payload types.

Basic Usage

const { RestClient } = require('@adobe-commerce/aio-toolkit');

const client = new RestClient();

// GET request
const response = await client.get('https://api.example.com/data', {
  'Authorization': 'Bearer token'
});

JSON Payloads (default)

// POST with JSON (automatic Content-Type: application/json)
const jsonData = { name: 'Product', price: 99.99 };
const response = await client.post('https://api.example.com/products', {
  'Authorization': 'Bearer token'
}, jsonData);

Form-Encoded Requests

// URLSearchParams for form-encoded data (automatic Content-Type: application/x-www-form-urlencoded)
const formData = new URLSearchParams({
  grant_type: 'client_credentials',
  client_id: 'your-client-id',
  client_secret: 'your-client-secret'
});

const tokenResponse = await client.post('https://auth.example.com/token', {
  Accept: 'application/json'
}, formData);

File Upload

// FormData for file uploads (Content-Type boundary handled automatically)
const uploadData = new FormData();
uploadData.append('file', fileBuffer, 'document.pdf');
uploadData.append('description', 'Important document');

const uploadResponse = await client.post('https://api.example.com/upload', {
  'Authorization': 'Bearer token'
}, uploadData);

Text/XML Payloads

// String payloads with custom content type
const xmlData = '<?xml version="1.0"?><order><id>123</id></order>';
const xmlResponse = await client.post('https://api.example.com/orders', {
  'Authorization': 'Bearer token',
  'Content-Type': 'application/xml'
}, xmlData);

BearerToken

Bearer token extraction and JWT analysis utility. Supports both standard HTTP headers and OpenWhisk format for maximum portability.

Extract from Standard HTTP Headers

const { BearerToken } = require('@adobe-commerce/aio-toolkit');

const headers = { authorization: 'Bearer abc123token' };
const tokenInfo = BearerToken.extract(headers);
console.log(tokenInfo);
// Output: {
//   token: 'abc123token',
//   tokenLength: 11,
//   isValid: true,
//   expiry: '2024-01-02T12:00:00.000Z',
//   timeUntilExpiry: 86400000
// }

Extract from OpenWhisk Params (Backward Compatibility)

const params = { __ow_headers: { authorization: 'Bearer abc123token' } };
const owTokenInfo = BearerToken.extract(params);
console.log(owTokenInfo); // Same output format as above

Direct Token Analysis

const directInfo = BearerToken.info('jwt-token-string');
if (directInfo.isValid) {
  console.log(`Token expires at: ${directInfo.expiry}`);
  console.log(`Time until expiry: ${directInfo.timeUntilExpiry}ms`);
} else {
  console.log('Token is invalid or expired');
}

Methods:

  • extract(headersOrParams) - Extracts Bearer token from headers or OpenWhisk params
  • info(token) - Analyzes token string and returns validation/expiry details

InfiniteLoopBreaker

Detect and prevent infinite loops in event-driven applications.

const { InfiniteLoopBreaker } = require('@adobe-commerce/aio-toolkit');

// Basic infinite loop detection
const isLoop = await InfiniteLoopBreaker.isInfiniteLoop({
  keyFn: 'order-processing-key',
  fingerprintFn: orderData,
  eventTypes: ['order.created', 'order.updated'],
  event: 'order.created'
});

if (isLoop) {
  console.log('Infinite loop detected, skipping processing');
  return;
}

// Process the event
await processOrderEvent(orderData);

// Store fingerprint to prevent future loops
await InfiniteLoopBreaker.storeFingerPrint(
  'order-processing-key',
  orderData,
  300 // 5 minutes TTL
);

OnboardIOEvents (formerly OnboardEvents)

Complete onboarding orchestration for Adobe I/O Events (providers, metadata, and registrations).

Note: OnboardEvents is deprecated and will be removed in v2.0.0. Please use OnboardIOEvents instead.

const { OnboardIOEvents } = require('@adobe-commerce/aio-toolkit');

// ✅ Recommended - Use OnboardIOEvents
const onboardEvents = new OnboardIOEvents(
  'My E-commerce Store',
  process.env.ADOBE_CONSUMER_ID!,
  process.env.ADOBE_PROJECT_ID!,
  process.env.ADOBE_WORKSPACE_ID!,
  process.env.ADOBE_API_KEY!,
  process.env.ADOBE_ACCESS_TOKEN!
);

const basicOnboardingExample = async () => {
  const input = {
    providers: [
      {
        key: 'ecommerce-provider',
        label: 'E-commerce Events Provider',
        description: 'Provider for e-commerce platform events',
        docsUrl: 'https://docs.mystore.com/events',
        registrations: [
          {
            key: 'order-events',
            label: 'Order Events Registration',
            description: 'Registration for order-related events',
            events: [
              {
                eventCode: 'com.mystore.order.placed',
                runtimeAction: 'mystore/order-placed-handler',
                deliveryType: 'webhook',
                sampleEventTemplate: {
                  orderId: 'ord_123456',
                  customerId: 'cust_789',
                  totalAmount: 99.99,
                  currency: 'USD',
                  status: 'placed',
                  timestamp: '2023-01-01T12:00:00Z'
                }
              },
              {
                eventCode: 'com.mystore.order.shipped',
                runtimeAction: 'mystore/order-shipped-handler',
                deliveryType: 'journal',
                sampleEventTemplate: {
                  orderId: 'ord_123456',
                  trackingNumber: 'TN123456789',
                  carrier: 'UPS',
                  shippedAt: '2023-01-02T10:00:00Z'
                }
              }
            ]
          }
        ]
      }
    ]
  };

  try {
    const result = await onboardEvents.process(input);
    
    console.log('Onboarding completed successfully!');
    console.log(`Providers: ${result.createdProviders.length} processed`);
    console.log(`Events: ${result.createdEvents.length} processed`);
    console.log(`Registrations: ${result.createdRegistrations.length} processed`);
    
    // Check results
    result.createdProviders.forEach(provider => {
      if (provider.created) {
        console.log(`✅ Created provider: ${provider.provider.label}`);
      } else if (provider.skipped) {
        console.log(`⏭️ Skipped existing provider: ${provider.provider.label}`);
      } else {
        console.log(`❌ Failed to create provider: ${provider.error}`);
      }
    });
    
    return result;
  } catch (error) {
    console.error('Onboarding failed:', error.message);
    return null;
  }
};

basicOnboardingExample();

📡 I/O Events Components

Adobe I/O Events management and orchestration

EventMetadataManager

Manage event metadata for Adobe I/O Events providers.

const { EventMetadataManager } = require('@adobe-commerce/aio-toolkit');

const manager = new EventMetadataManager({
  apiKey: 'your-api-key',
  accessToken: 'your-access-token',
  consumerId: 'your-consumer-id',
  projectId: 'your-project-id',
  workspaceId: 'your-workspace-id'
});

// Create event metadata
const eventMetadata = await manager.create({
  providerId: 'your-provider-id',
  eventCode: 'commerce.order.created',
  label: 'Order Created',
  description: 'Triggered when a new order is created in the commerce system',
  sampleEventTemplate: {
    orderId: 'ORD-123456',
    customerId: 'CUST-789',
    amount: 199.99,
    currency: 'USD',
    items: [
      {
        sku: 'PRODUCT-001',
        name: 'Premium Widget',
        quantity: 2,
        price: 99.99
      }
    ],
    timestamp: '2023-12-01T10:30:00Z'
  }
});

// Get event metadata
const metadata = await manager.get('your-provider-id', 'commerce.order.created');

// List all event metadata for a provider
const allMetadata = await manager.list('your-provider-id');

// Delete specific event metadata
await manager.delete('your-provider-id', 'commerce.order.created');

// Delete all event metadata for a provider
await manager.delete('your-provider-id');

ProviderManager

Manage event providers for Adobe I/O Events.

const { ProviderManager } = require('@adobe-commerce/aio-toolkit');

const manager = new ProviderManager({
  apiKey: 'your-api-key',
  accessToken: 'your-access-token',
  consumerId: 'your-consumer-id',
  projectId: 'your-project-id',
  workspaceId: 'your-workspace-id'
});

// Create a new provider
const provider = await manager.create({
  label: 'E-commerce Events Provider',
  description: 'Provides events from our e-commerce platform including orders, customers, products, and inventory updates',
  docsUrl: 'https://docs.mystore.com/events-api',
  providerMetadata: {
    region: 'us-east-1',
    environment: 'production',
    version: '2.1.0'
  }
});

// Get provider details
const existingProvider = await manager.get('your-provider-id', {
  eventmetadata: true // Include event metadata in response
});

// List all providers
const providers = await manager.list({
  eventmetadata: true, // Include event metadata
  providerMetadataIds: ['provider-1', 'provider-2'] // Filter by specific providers
});

// List providers with advanced filtering
const filteredProviders = await manager.list({
  instanceId: 'your-instance-id',
  providerMetadataId: 'specific-provider-metadata-id'
});

// Delete provider
await manager.delete('your-provider-id');

RegistrationManager

Manage event registrations and subscriptions for Adobe I/O Events.

const { RegistrationManager } = require('@adobe-commerce/aio-toolkit');

const manager = new RegistrationManager({
  apiKey: 'your-api-key',
  accessToken: 'your-access-token',
  consumerId: 'your-consumer-id',
  projectId: 'your-project-id',
  workspaceId: 'your-workspace-id'
});

// Create event registration
const registration = await manager.create({
  name: 'Order Processing Registration',
  description: 'Handles order-related events',
  delivery: {
    type: 'webhook',
    url: 'https://your-app.com/webhook/orders'
  },
  eventsOfInterest: [
    {
      providerId: 'your-provider-id',
      eventCode: 'commerce.order.created'
    },
    {
      providerId: 'your-provider-id', 
      eventCode: 'commerce.order.updated'
    }
  ]
});

// Get registration details
const existingRegistration = await manager.get('your-registration-id');

// List all registrations
const registrations = await manager.list();

// Delete registration
await manager.delete('your-registration-id');

Environment Variables

Common environment variables used across components:

# Adobe commerce credentials
COMMERCE_BASE_URL=commerce-base-url
COMMERCE_CONSUMER_KEY=commerce-consumer-key
COMMERCE_CONSUMER_SECRET=commerce-consumer-secret
COMMERCE_ACCESS_TOKEN=commerce-access-token
COMMERCE_ACCESS_TOKEN_SECRET=commerce-access-token-secret
# Environment from DIST file
OAUTH_BASE_URL=https://ims-na1.adobelogin.com/ims/token/
IO_MANAGEMENT_BASE_URL=https://api.adobe.io/events/
# OAuth configs
# The following values can be copied from the Credential details page in AppBuilder under Organization > Project > Workspace > OAuth Server-to-Server
OAUTH_CLIENT_ID=client-id
OAUTH_CLIENT_SECRET=client-secret
OAUTH_TECHNICAL_ACCOUNT_ID=technical-account-id
OAUTH_TECHNICAL_ACCOUNT_EMAIL=technical-account-email
OAUTH_ORG_ID=org-id
OAUTH_SCOPES=scopes
# OAUTH_HOST=<string>
# Workspace configs
# The following values can be copied from the JSON downloadable in AppBuilder from Organization > Project > Workspace
# IO_CONSUMER corresponds to project.org.id
# IO_PROJECT_ID corresponds to project.id
# IO_WORKSPACE_ID corresponds to project.workspace.id
IO_CONSUMER_ID=consumer-id
IO_PROJECT_ID=project-id
IO_WORKSPACE_ID=workspace-id

License

See the LICENSE file (in package) for license rights and limitations.