@unchainedshop/core
v4.6.2
Published
Core orchestration package for the Unchained Engine with business services and directors
Downloads
1,204
Readme
@unchainedshop/core
Core orchestration package for the Unchained Engine. Integrates all core-* modules, provides business services, directors for plugins, and bulk import functionality.
Installation
npm install @unchainedshop/coreUsage
import { initCore, type UnchainedCore } from '@unchainedshop/core';
const unchainedCore = await initCore({
db,
migrationRepository,
options: {
// Module-specific options
},
});
// Access modules
const products = await unchainedCore.modules.products.findProducts({});
// Use services
const pricing = await unchainedCore.services.orders.pricingSheet(order);
// Bulk import
await unchainedCore.bulkImporter.prepare('PRODUCT');API Overview
Initialization
| Export | Description |
|--------|-------------|
| initCore | Initialize the core with all modules and services |
| getAllAdapters | Get all registered adapters across all directors |
Modules
The modules object provides access to all core-* module APIs:
| Module | Description |
|--------|-------------|
| products | Product management |
| assortments | Category/assortment management |
| filters | Product filtering and search |
| orders | Order management |
| users | User management |
| payment | Payment providers |
| delivery | Delivery providers |
| warehousing | Inventory and stock |
| enrollments | Subscriptions |
| quotations | Quote management |
| bookmarks | User bookmarks |
| countries | Country management |
| currencies | Currency management |
| languages | Language management |
| files | File management |
| events | Event history |
| worker | Background jobs |
Services
Business logic services that orchestrate multiple modules:
| Service | Description |
|---------|-------------|
| orders | Order pricing, checkout workflows |
| products | Product pricing calculations |
| users | User-related operations |
| files | File operations with adapters |
Directors
Plugin directors provide extensibility through the Director/Adapter pattern. Each director manages a collection of adapters that implement specific behaviors.
| Director | Description |
|----------|-------------|
| PaymentDirector | Payment processing adapters |
| DeliveryDirector | Delivery/shipping adapters |
| WarehousingDirector | Inventory and stock adapters |
| WorkerDirector | Background job workers |
| FilterDirector | Product search and filtering |
| EnrollmentDirector | Subscription plan handling |
| QuotationDirector | Quote/RFQ processing |
| ProductPricingDirector | Product price calculations |
| OrderPricingDirector | Order total calculations |
| DeliveryPricingDirector | Delivery fee calculations |
| PaymentPricingDirector | Payment fee calculations |
| ProductDiscountDirector | Product-level discounts |
| OrderDiscountDirector | Order-level discounts |
| MessagingDirector | Email/notification templates |
Director/Adapter Architecture
The Unchained Engine uses a Director/Adapter pattern for extensibility. Directors manage collections of adapters, and adapters implement specific behaviors.
Base Classes
import { BaseAdapter, BaseDirector } from '@unchainedshop/utils';- BaseDirector: Factory function that creates a generic director with methods to register, unregister, and retrieve adapters
- BaseAdapter: Base implementation providing logging and utility methods for all adapters
Creating a Custom Adapter
All adapters extend from BaseAdapter and must implement:
const MyAdapter = {
key: 'my-adapter', // Unique identifier
label: 'My Custom Adapter', // Human-readable label
version: '1.0.0', // Adapter version
// Adapter-specific methods...
};
// Register with the appropriate director
SomeDirector.registerAdapter(MyAdapter);Payment Director
Manages payment processing and orchestrates payment adapters.
import { PaymentDirector, type IPaymentAdapter } from '@unchainedshop/core';
const MyPaymentAdapter: IPaymentAdapter = {
key: 'my-payment',
label: 'My Payment Gateway',
version: '1.0.0',
typeSupported(type) {
return type === 'CARD';
},
actions(config, context) {
return {
configurationError() { return null; },
isActive() { return true; },
isPayLaterAllowed() { return false; },
async charge() {
// Process payment charge
return { transactionId: '...' };
},
async confirm() {
// Confirm payment
return { transactionId: '...' };
},
async cancel() {
// Cancel payment
return true;
},
async register() {
// Register payment method
return { token: '...' };
},
async sign() {
// Sign payment request
return '...';
},
async validate(token) {
// Validate payment token
return true;
},
};
},
};
PaymentDirector.registerAdapter(MyPaymentAdapter);Delivery Director
Manages delivery operations and coordinates shipping adapters.
import { DeliveryDirector, type IDeliveryAdapter } from '@unchainedshop/core';
const MyDeliveryAdapter: IDeliveryAdapter = {
key: 'my-delivery',
label: 'My Shipping Provider',
version: '1.0.0',
typeSupported(type) {
return type === 'SHIPPING';
},
actions(config, context) {
return {
configurationError() { return null; },
isActive() { return true; },
isAutoReleaseAllowed() { return false; },
async send() {
// Trigger delivery
return { trackingNumber: '...' };
},
estimatedDeliveryThroughput(warehousingTime) {
// Return estimated delivery time in ms
return 3 * 24 * 60 * 60 * 1000; // 3 days
},
async pickUpLocations() {
// Return available pickup locations
return [];
},
async pickUpLocationById(locationId) {
return null;
},
};
},
};
DeliveryDirector.registerAdapter(MyDeliveryAdapter);Warehousing Director
Manages inventory and stock operations, including NFT/token support.
import { WarehousingDirector, type IWarehousingAdapter } from '@unchainedshop/core';
const MyWarehousingAdapter: IWarehousingAdapter = {
key: 'my-warehouse',
label: 'My Inventory System',
version: '1.0.0',
typeSupported(type) {
return type === 'PHYSICAL';
},
actions(config, context) {
return {
configurationError() { return null; },
isActive() { return true; },
async stock(referenceDate) {
// Return current stock quantity
return 100;
},
async productionTime(quantity) {
// Return production time in ms
return 0;
},
async commissioningTime(quantity) {
// Return commissioning time in ms
return 24 * 60 * 60 * 1000; // 1 day
},
async estimatedStock() {
return 100;
},
async estimatedDispatch() {
return new Date();
},
// For tokenized products (NFTs):
async tokenize() { return []; },
async tokenMetadata(serial, date) { return {}; },
async isInvalidateable(serial, date) { return false; },
};
},
};
WarehousingDirector.registerAdapter(MyWarehousingAdapter);Worker Director
Manages background job processing and scheduled tasks.
import { WorkerDirector, type IWorkerAdapter } from '@unchainedshop/core';
interface MyInput { email: string; subject: string; }
interface MyOutput { messageId: string; }
const MyWorkerAdapter: IWorkerAdapter<MyInput, MyOutput> = {
key: 'my-worker',
label: 'My Background Worker',
version: '1.0.0',
type: 'MY_WORK_TYPE', // Work type identifier
external: false, // Runs in-process
maxParallelAllocations: 10, // Max concurrent executions
async doWork(input, unchainedAPI, workId) {
// Process the work item
const { email, subject } = input;
// Return result
return {
success: true,
result: { messageId: 'msg-123' },
};
},
};
WorkerDirector.registerAdapter(MyWorkerAdapter);
// Schedule recurring work
WorkerDirector.configureAutoscheduling({
type: 'MY_WORK_TYPE',
input: { email: '[email protected]', subject: 'Test' },
schedule: '0 * * * *', // Every hour (cron syntax)
});Pricing Directors
Pricing directors calculate prices using a chain of adapters. Each adapter can add, modify, or discount prices.
import { ProductPricingDirector, type IProductPricingAdapter } from '@unchainedshop/core';
const MyPricingAdapter: IProductPricingAdapter = {
key: 'my-pricing',
label: 'My Pricing Logic',
version: '1.0.0',
orderIndex: 10, // Lower numbers run first
isActivatedFor(context) {
// Return true if this adapter should apply
return true;
},
actions(params) {
return {
calculate() {
// Add price calculations
this.calculation.addItem({
category: 'BASE',
amount: 1000, // in smallest currency unit
});
},
};
},
};
ProductPricingDirector.registerAdapter(MyPricingAdapter);Discount Directors
Discount directors manage coupon codes and automatic discounts.
import { OrderDiscountDirector, type IDiscountAdapter } from '@unchainedshop/core';
const MyDiscountAdapter: IDiscountAdapter = {
key: 'my-discount',
label: 'My Discount System',
version: '1.0.0',
orderIndex: 10,
isManualAdditionAllowed(code) {
return code.startsWith('PROMO');
},
isManualRemovalAllowed() {
return true;
},
actions(context) {
return {
isValidForSystemTriggering() {
// Auto-apply discount?
return false;
},
isValidForCodeTriggering(code) {
// Apply when code entered?
return code === 'PROMO10';
},
discountForPricingAdapterKey(params) {
// Return discount configuration for pricing adapter
return {
isNetPrice: false,
rate: 0.1, // 10% off
};
},
async reserve(code) {
// Reserve discount (e.g., decrement coupon balance)
},
async release() {
// Release reservation on order cancellation
},
};
},
};
OrderDiscountDirector.registerAdapter(MyDiscountAdapter);Filter Director
Manages product filtering and search functionality.
import { FilterDirector, type IFilterAdapter } from '@unchainedshop/core';
const MyFilterAdapter: IFilterAdapter = {
key: 'my-filter',
label: 'My Search Filter',
version: '1.0.0',
orderIndex: 10,
actions(context) {
return {
async aggregateProductIds(params) {
// Return product IDs matching filter
return ['product-1', 'product-2'];
},
async searchProducts(params, options) {
// Search products
return { productIds: [], totalCount: 0 };
},
async searchAssortments(params, options) {
// Search assortments
return { assortmentIds: [], totalCount: 0 };
},
transformProductSelector(selector, options) {
// Modify MongoDB product selector
return selector;
},
transformFilterSelector(selector, options) {
// Modify MongoDB filter selector
return selector;
},
transformSortStage(sort, options) {
// Modify MongoDB sort stage
return sort;
},
};
},
};
FilterDirector.registerAdapter(MyFilterAdapter);Messaging Director
The Messaging Director uses a template resolver pattern instead of traditional adapters.
import { MessagingDirector } from '@unchainedshop/core';
// Register a message template
MessagingDirector.registerTemplate('ORDER_CONFIRMATION', async (context) => {
const { order, user } = context;
return [
{
type: 'EMAIL',
input: {
to: user.email,
subject: `Order Confirmation #${order.orderNumber}`,
html: '<h1>Thank you for your order!</h1>',
},
},
{
type: 'SMS',
input: {
to: user.phone,
text: `Order #${order.orderNumber} confirmed!`,
},
},
];
});Quotation Director
Handles quotation/RFQ (Request for Quote) operations.
import { QuotationDirector, type IQuotationAdapter } from '@unchainedshop/core';
const MyQuotationAdapter: IQuotationAdapter = {
key: 'my-quotation',
label: 'My Quote System',
version: '1.0.0',
isActivatedFor(quotationContext, unchainedAPI) {
return true;
},
actions(context) {
return {
configurationError() { return null; },
isManualProposalRequired() { return true; },
isManualRequestVerificationRequired() { return false; },
async quote() {
// Generate quote
return { price: 1000, currency: 'CHF' };
},
async submitRequest(quotationContext) {
// Submit RFQ
},
async verifyRequest(quotationContext) {
// Verify RFQ
},
async rejectRequest(quotationContext) {
// Reject RFQ
},
transformItemConfiguration(params) {
return params.configuration;
},
};
},
};
QuotationDirector.registerAdapter(MyQuotationAdapter);Enrollment Director
Manages subscription/enrollment plans and recurring billing.
import { EnrollmentDirector, type IEnrollmentAdapter } from '@unchainedshop/core';
const MyEnrollmentAdapter: IEnrollmentAdapter = {
key: 'my-enrollment',
label: 'My Subscription System',
version: '1.0.0',
isActivatedFor(productPlan) {
return productPlan.type === 'PLAN_PRODUCT';
},
transformOrderItemToEnrollmentPlan(orderPosition, unchainedAPI) {
return {
configuration: orderPosition.configuration,
};
},
actions(context) {
return {
async nextPeriod() {
// Calculate next billing period
return {
start: new Date(),
end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
};
},
isValidForActivation() {
return true;
},
isOverdue() {
return false;
},
async configurationForOrder(period) {
// Return order configuration for period
return {};
},
};
},
};
EnrollmentDirector.registerAdapter(MyEnrollmentAdapter);Bulk Importer
| Method | Description |
|--------|-------------|
| bulkImporter.prepare | Prepare bulk import for entity type |
| bulkImporter.process | Process prepared import data |
Types
| Export | Description |
|--------|-------------|
| UnchainedCore | Core instance type |
| UnchainedCoreOptions | Initialization options |
| Modules | All modules type |
| Services | All services type |
| BulkImporter | Bulk importer type |
Configuration
const core = await initCore({
db,
migrationRepository,
modules: {
// Custom modules
},
services: {
// Custom services
},
bulkImporter: {
handlers: {
// Custom import handlers
},
},
options: {
// Module-specific options
},
});License
EUPL-1.2
