@adobe-commerce/aio-toolkit
v1.0.18
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
Downloads
453
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-toolkitUsage
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 processingexception(message?, exceptionClass?): Returns error responseadd(path, value, instance?): Adds data to responsereplace(path, value, instance?): Replaces data in responseremove(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'
},
'eventId'
);
console.log(`Event published: ${result.eventId}, Status: ${result.status}`);
// Publish an event with subject (eventId is optional; pass undefined to use auto-generated ID)
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'
},
undefined,
'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.telemetryfor custom instrumentation telemetry.instrument()method for wrapping functions with automatic span creation- Conditional span access based on
ENABLE_TELEMETRYflag - 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: trueSet 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 # OptionalUsage 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 eventstelemetry.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 correlationaction.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 agoSupported Action Classes:
Telemetry is automatically integrated with all framework action classes:
RuntimeAction- HTTP request handling with telemetryWebhookAction- Webhook processing with telemetryEventConsumerAction- Event-driven processing with telemetryOpenwhiskAction- 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. Theidparameter takes precedence overpayload.id. IDs are automatically sanitized to alphanumeric + underscore characters. Setoverwrite: trueto replace entire file, orfalse(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 timestampsmetadata(id?): Retrieves file metadata (size, timestamps) without reading content - faster thanlist()for large datasetsdelete(ids): Deletes records by ID array
New in v1.0.4:
- File System Timestamps:
createdAtandupdatedAtare 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
AdobeAuthdirectly 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()andgetRemovedMethods() - 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 commandsAfter 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 aboveDirect 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 paramsinfo(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:
OnboardEventsis deprecated and will be removed in v2.0.0. Please useOnboardIOEventsinstead.
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');🛠️ CLI Tools
aio-toolkit-cursor-context
Command-line tool for managing Cursor IDE context files (rules and commands) in your Adobe App Builder projects. This CLI automatically sets up Cursor IDE-specific contexts and configures the Model Context Protocol (MCP) server for enhanced development experience.
⚠️ Security Notice - MCP Server Integration
The MCP (Model Context Protocol) server feature requires the optional package
@adobe-commerce/commerce-extensibility-tools. This package is not installed by default due to known security vulnerabilities in its dependencies.If you want to use MCP integration:
npm install @adobe-commerce/commerce-extensibility-toolsKnown Vulnerabilities:
- ReDoS (Regular Expression Denial of Service) vulnerability in
@modelcontextprotocol/sdk- DNS rebinding protection disabled by default in
@modelcontextprotocol/sdkRecommendation: Only install this package if you specifically need MCP server integration for Cursor IDE. Monitor the package repository for security updates.
All other toolkit features work normally without this optional dependency.
Commands
check
Validates that Cursor contexts are properly set up in your project.
npx aio-toolkit-cursor-context checkFeatures:
- ✅ Checks for
.cursor/rulesand.cursor/commandsdirectories - ✅ Validates MCP server configuration
- ✅ Detects if running in Cursor IDE
- ✅ Reports missing files or configurations
Output Example:
✅ Cursor IDE detected
✅ MCP server configured
✅ Found 3 context files in .cursor/rules
✅ Found 4 context files in .cursor/commands
✅ Cursor contexts are properly set upapply
Copies Cursor context files from the package to your project.
# Standard mode - copies missing files only
npx aio-toolkit-cursor-context apply
# Force mode - overwrites existing files and bypasses IDE detection
npx aio-toolkit-cursor-context apply -f
npx aio-toolkit-cursor-context apply --forceFeatures:
- 📁 Copies context files to
.cursor/rulesand.cursor/commands - 🔄 Creates MCP configuration in
.cursor/mcp.json - 🛡️ IDE detection prevents accidental application in non-Cursor environments
- ⚡ Force mode for CI/CD pipelines or when IDE detection fails
- 📊 Reports copied, skipped, and overwritten files
Standard Mode Output:
Copied 3 new context files to .cursor/rules
Skipped 2 files (already exist)
✅ MCP server configured
✅ All cursor contexts applied successfullyForce Mode Output:
Overwritten 5 existing files in .cursor/rules
Copied 4 new files to .cursor/commands
✅ MCP server configured
✅ All cursor contexts applied successfully (force mode)help
Displays help information for the CLI.
npx aio-toolkit-cursor-context helpContext Files Included
The CLI provides the following context files:
Rules (.cursor/rules/):
aio-toolkit-create-adobe-commerce-client.mdc- Creating Adobe Commerce client operationsaio-toolkit-oop-best-practices.mdc- Object-oriented programming best practicesaio-toolkit-setup-new-relic-telemetry.mdc- Setting up New Relic telemetry
Commands (.cursor/commands/):
aio-toolkit-create-event-consumer-action.md- Create event consumer actionsaio-toolkit-create-graphql-action.md- Create GraphQL actionsaio-toolkit-create-runtime-action.md- Create runtime actionsaio-toolkit-create-webhook-action.md- Create webhook actions
Use Cases
- Initial Project Setup: Run
applyto set up all Cursor contexts - Continuous Integration: Use
apply -fin CI/CD pipelines - Verification: Run
checkto ensure contexts are properly configured - Updates: Run
apply -fafter toolkit updates to get latest contexts
aio-toolkit-onboard-events
Command-line tool for managing Adobe I/O Events and Commerce event integrations. This CLI automates the process of onboarding event providers, registrations, and subscriptions, as well as cleaning up resources from test/development environments.
Commands
execute
Onboards Adobe I/O Events providers, registrations, and Commerce event subscriptions based on configuration file.
# Execute with default config (onboard.config.yaml)
npx aio-toolkit-onboard-events executeFeatures:
- ✅ Creates Adobe I/O Events providers with automatic duplicate detection
- ✅ Sets up event metadata for custom events
- ✅ Configures event registrations with webhook or journal delivery
- ✅ Subscribes to Adobe Commerce events (supports both OAuth and IMS authentication)
- ✅ Validates environment variables upfront with clear error messages
- ✅ Structured logging with detailed progress tracking
cleanup
Removes Adobe I/O Events resources and Commerce event subscriptions defined in configuration file.
# Cleanup with default config
npx aio-toolkit-onboard-events cleanupFeatures:
- 🗑️ Deletes event registrations
- 🗑️ Removes event metadata
- 🗑️ Deletes event providers
- 🗑️ Unsubscribes from Commerce events and removes providers
- ⚡ Respects dependency order (registrations → metadata → providers)
- 📊 Provides detailed cleanup summary
help
Displays help information for the CLI.
npx aio-toolkit-onboard-events helpConfiguration File
The CLI uses onboard.config.yaml to define resources. Default template:
# Configuration file version
version: "1.0"
# Application metadata
application:
name: "My App Builder Application"
# Description is optional
# description: "My App Builder Application for Adobe Commerce"
# Events configuration
events:
# Adobe I/O Events configuration
io:
config: io-events.json # File path to I/O events configuration
# Adobe Commerce Events configuration
commerce:
providerKey: commerce
config: commerce-events.json # File path to Commerce events configuration
# Deployment settings
deployment:
# Instance type: paas | cloud
# paas = Adobe Commerce Cloud/On-premise (OAuth)
# cloud = Adobe Commerce as Cloud Service (IMS)
type: paas
# Authentication type (optional, auto-detected from type if not specified)
# Allowed values: oauth | ims
# Auto-detection: paas → oauth, cloud → ims
# authType: oauthUse Cases
- Development Onboarding: Run
executeto quickly set up event integrations - Environment Cleanup: Run
cleanupto remove test resources before production deployment - CI/CD Integration: Automate event configuration in deployment pipelines
- Multi-Environment Setup: Use different config files for dev/staging/production environments
Environment Variables
Common environment variables used across components:
# Adobe I/O Events OAuth (Required for aio-toolkit-onboard-events)
# 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=openid, AdobeID, adobeio_api
# Adobe I/O Workspace (Required for aio-toolkit-onboard-events)
# 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
# Workspace Configuration (Required for aio-toolkit-onboard-events)
IO_WORKSPACE_CONFIG=path/to/onboard.config.yaml
# Adobe Commerce Base Configuration (Required for Commerce events)
COMMERCE_BASE_URL=https://your-commerce-store.com
COMMERCE_ADOBE_IO_EVENTS_MERCHANT_ID=merchant-id
COMMERCE_ADOBE_IO_EVENTS_ENVIRONMENT_ID=environment-id
# Commerce OAuth 1.0a Authentication (Use when authType is "oauth1a")
COMMERCE_OAUTH_CONSUMER_KEY=consumer-key
COMMERCE_OAUTH_CONSUMER_SECRET=consumer-secret
COMMERCE_OAUTH_ACCESS_TOKEN=access-token
COMMERCE_OAUTH_ACCESS_TOKEN_SECRET=access-token-secret
# Commerce IMS Authentication (Use when authType is "ims")
COMMERCE_IMS_CLIENT_ID=client-id
COMMERCE_IMS_CLIENT_SECRET=client-secret
COMMERCE_IMS_TECHNICAL_ACCOUNT_ID=tech-account-id
COMMERCE_IMS_TECHNICAL_ACCOUNT_EMAIL=tech-email
COMMERCE_IMS_ORG_ID=org-id
COMMERCE_IMS_SCOPES=openid, AdobeID, adobeio_api
# Legacy Environment Variables (Still supported for backward compatibility)
OAUTH_BASE_URL=https://ims-na1.adobelogin.com/ims/token/
IO_MANAGEMENT_BASE_URL=https://api.adobe.io/events/
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-secretLicense
See the LICENSE file (in package) for license rights and limitations.
