JSPM

@classytic/flow

0.1.5
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 261
  • Score
    100M100P100Q52431F
  • License MIT

Production-grade inventory kernel and supply chain engine for MongoDB — locations, moves, quants, reservations, valuation, routing, traceability

Package Exports

  • @classytic/flow
  • @classytic/flow/counting
  • @classytic/flow/domain
  • @classytic/flow/domain/contracts
  • @classytic/flow/domain/enums
  • @classytic/flow/domain/policies
  • @classytic/flow/events
  • @classytic/flow/models
  • @classytic/flow/packaging
  • @classytic/flow/procurement
  • @classytic/flow/reporting
  • @classytic/flow/repositories
  • @classytic/flow/reservations
  • @classytic/flow/routing
  • @classytic/flow/scanning
  • @classytic/flow/services
  • @classytic/flow/traceability
  • @classytic/flow/types
  • @classytic/flow/valuation

Readme

@classytic/flow

Production-grade inventory kernel and supply chain engine for MongoDB.

Double-entry inventory, location-based stock tracking, reservations, FIFO/FEFO/WAC valuation, lot/serial traceability, putaway/removal routing, wave picking with serpentine path optimization, cross-dock multi-destination routing, procurement, cycle counting, and replenishment — all as pure domain services with zero framework coupling.

Quick Start

import { createFlowEngine } from '@classytic/flow';
import mongoose from 'mongoose';

// 1. Create the engine
const flow = createFlowEngine({
  mongoose: mongoose.connection,
  mode: 'standard',
  catalog: {
    resolveSku: async (skuRef) => productService.findBySku(skuRef),
  },
});

// 2. Access models
const { InventoryNode, Location, StockMoveGroup, StockMove, StockQuant, Reservation } = flow.models;

// 3. Context carries tenant + actor — never mixed into domain inputs
const ctx = { organizationId: 'org_1', actorId: 'user_1' };

// 4. Use services (pure async functions — no HTTP, no framework)
const availability = await flow.services.quant.getAvailability({
  skuRef: 'SKU-RED-M',
  nodeId: 'warehouse_1',
}, ctx);

// 5. Create and execute a transfer
const group = await flow.services.moveGroup.create({
  groupType: 'transfer',
  sourceNodeId: 'warehouse_1',
  destinationNodeId: 'store_1',
  items: [{
    moveGroupId: '',            // auto-assigned
    operationType: 'transfer',
    skuRef: 'SKU-RED-M',
    sourceLocationId: 'loc_storage_wh1',
    destinationLocationId: 'loc_storage_store1',
    quantityPlanned: 50,
  }],
}, ctx);

await flow.services.moveGroup.executeAction(group._id, 'confirm', {}, ctx);
await flow.services.moveGroup.executeAction(group._id, 'dispatch', {}, ctx);
await flow.services.moveGroup.executeAction(group._id, 'receive', {}, ctx);

// 6. Subscribe to events (bridge to your ledger, notifications, etc.)
flow.events.on('inventory.move.done', async (payload) => {
  // payload: { organizationId, moveId, skuRef, quantityDone, ... }
  await ledger.postInventoryMovement(payload);
});

// 7. Query active config at runtime
flow.services.mode;      // 'standard'
flow.services.routing;   // { putaway: false, removal: false, crossDock: false }
flow.services.valuation; // { method: 'wac' }

Simple Mode (single store, basic stock)

Flow scales down to simple apps. You don't need locations, routing, or lots — just stock queries and moves:

const ctx = { organizationId: 'org_1', actorId: 'user_1' };

// Check availability
const stock = await flow.services.quant.getAvailability({ skuRef: 'SKU-RED-M' }, ctx);
// → { quantityOnHand: 50, quantityReserved: 5, quantityAvailable: 45, quantityIncoming: 20, quantityOutgoing: 0 }

// Reserve stock for an order
await flow.services.reservation.reserve({
  reservationType: 'hard',
  skuRef: 'SKU-RED-M',
  locationId: 'loc_default',
  quantity: 3,
  ownerType: 'order',
  ownerId: 'order_123',
}, ctx);

Features

Feature Description
Double-entry inventory Every stock change is a move between locations — never direct mutation
Location hierarchy Warehouse > zone > aisle > bin. Virtual locations for vendor, customer, scrap, transit
Stock quants Materialized read model of on-hand, reserved, available, incoming, outgoing
Reservations First-class records with expiry, partial consumption, overconsumption guard, and pluggable allocation
Valuation WAC (default), FIFO, FEFO, specific identification, standard cost, landed cost with penny-leak prevention
Lot/serial tracking Full traceability with expiry, recall support, and FEFO allocation
Routing engine Putaway strategies, removal strategies, route expansion
Wave picking Serpentine (boustrophedon) path optimization with 3D coordinates, weight/volume/item cart batching with oversized pick splitting
Cross-dock routing Multi-destination assignments — incoming stock auto-routed to waiting outbound orders in FIFO order
Procurement Purchase order lifecycle, receiving, vendor management
Cycle counting Full/cycle/blind counts with auto-reconciliation
Replenishment Min/max, reorder point, safety stock, EOQ, multi-echelon
Scan resolution + GS1-128 Device-agnostic barcode/QR/RFID → entity resolution. Built-in GS1-128 parser for GTIN, lot, expiry, serial.
Stock states Sellable, damaged, quarantine, hold, returns, expired, in-transit
UoM conversion Each/case/carton/pallet with per-SKU conversion factors
Packaging StockPackage with parent/child nesting (carton → case → pallet), weight calculation
Stock ownership ownerRef on quants — owned vs consignment/third-party in same location
QC workflow Quality hold location + inspection route step
Multi-tenant Organization-scoped via MongoKit multi-tenant plugin
Events In-process emitter — bridge to Redis, webhooks, ledger, notifications
Capability tiers simple / standard / enterprise — config-driven feature gating

Safety & Data Integrity

Flow enforces invariants at the engine level so consumers don't have to:

Guard Behavior
Atomic quant updates quantityAvailable = quantityOnHand - quantityReserved computed in a single MongoDB aggregation pipeline — no race window
Negative stock prevention postMove() checks location.allowNegativeStock before decrementing; throws NegativeStockError if insufficient. Virtual sources (returns, receipts) auto-bypass.
Canonical posting path receiveGroup() delegates to PostingService.postMove() per move — negative stock guard, move transition validation, and inventory.move.done events fire for every move
Reservation lifecycle expire() and consume() both release quantityReserved from the quant. consume() validates quantity > 0 and caps at remaining — no overconsumption. Fulfillment releases all reservations.
Input validation quantityDone > 0, quantity > 0, non-empty items[] enforced at service entry
Tenant-scoped idempotency All idempotency keys prefixed with organizationId: (reservation, move, procurement) — no cross-tenant collision
WAC precision Weighted average cost rounded to 4 decimal places; freezes at last known cost when stock goes negative
Penny-leak prevention Landed cost post-capitalization uses remainder absorption — COGS + remaining = exactly the allocated cost per item, with cross-item correction on the last item
Concurrent allocation safety Parallel availability check + batchUpsert for quant reservation locks within a single transaction
Event error isolation Handler errors logged but never propagate to emitter
GS1 year rollover Dynamic sliding window (±50 years from current year) instead of hardcoded cutoff
FIFO/FEFO zero-qty guard Valuation engines return empty result for quantity <= 0
Partially done moves receiveGroup() reconciles partially_done moves — completes remaining quantity instead of skipping them

Subpath Exports

Import Contents
@classytic/flow createFlowEngine() factory + full API
@classytic/flow/domain Entities, value objects, errors
@classytic/flow/domain/contracts CatalogBridge, AuthContext interfaces
@classytic/flow/domain/enums LocationType, MoveStatus, OperationType, etc.
@classytic/flow/domain/policies AllocationPolicy, RemovalPolicy, ValuationPolicy
@classytic/flow/models Mongoose schemas
@classytic/flow/repositories Repository factories (MongoKit)
@classytic/flow/services All domain services
@classytic/flow/valuation Cost layers, WAC/FIFO/FEFO engine, landed cost
@classytic/flow/routing Putaway, removal, wave engine, cross-dock engine
@classytic/flow/reservations Allocation strategies and reservation service
@classytic/flow/procurement Procurement order lifecycle
@classytic/flow/counting Inventory count and reconciliation
@classytic/flow/traceability Lot/serial trace queries
@classytic/flow/scanning Scan resolution + GS1-128 parser
@classytic/flow/packaging StockPackage service
@classytic/flow/reporting Read models, metrics, stock aging
@classytic/flow/events Event definitions and emitter
@classytic/flow/types All TypeScript types

Configuration

const flow = createFlowEngine({
  // Required
  mongoose: connection,

  // Required — how Flow resolves product details
  catalog: {
    resolveSku: async (skuRef) => ({ sku: skuRef, displayName: '...', trackingMode: 'none', uom: 'unit' }),
  },

  // Optional — deployment mode (default: 'standard')
  mode: 'standard',

  // Optional — multi-tenant (omit for single-tenant)
  multiTenant: { orgField: 'organizationId' },

  // Optional — valuation method (default: 'wac')
  valuation: { method: 'wac' },

  // Optional — allocation policy (default: 'fifo') — resolves to built-in FifoStrategy/FefoStrategy/LifoStrategy
  allocation: { defaultPolicy: 'fifo' },

  // Optional — routing (enterprise mode)
  routing: { putaway: true, removal: true, crossDock: false },

  // Optional — custom allocation policy (overrides allocation.defaultPolicy)
  policies: { allocation: myCustomPolicy },

  // Optional — custom event emitter (default: in-process)
  events: { adapter: myRedisEventBus },

  // Optional — idempotency store
  idempotency: myRedisIdempotency,

  // Optional — MongoKit plugins per repository
  plugins: {
    quant: [cachePlugin({ adapter: redis, ttl: 30 })],
    move: [auditTrailPlugin()],
  },
});
Option Type Default Description
mongoose Connection required Mongoose connection
catalog CatalogBridge required Product/SKU resolution
mode FlowMode 'standard' simple, standard, or enterprise
multiTenant object undefined { orgField } for org-scoped isolation
valuation.method string 'wac' wac, fifo, fefo, specific, standard
allocation.defaultPolicy string 'fifo' fifo, fefo, lifo — maps to built-in strategy
routing.putaway boolean false Enable putaway strategy engine
routing.removal boolean false Enable removal strategy engine
routing.crossDock boolean false Enable cross-dock multi-destination routing

What Flow Returns

const flow = createFlowEngine(config);

flow.models         // All Mongoose models
flow.repositories   // All MongoKit repositories
flow.services       // All domain services + active config (mode, routing, valuation)
flow.events         // In-process event emitter
flow.registerPolicy // Register custom allocation policies at runtime

What Flow Does NOT Include

Flow is a pure domain kernel. These are consumer responsibilities:

  • HTTP routes — use Arc, Express, Fastify, NestJS, Next.js
  • Durable workflows — use Streamline, BullMQ, cron
  • Agent tooling — wrap flow.services in your agent tools or MCP servers
  • Ledger integration — subscribe to flow.events
  • Authentication — pass AuthContext to service methods

Dependencies

Dependency Type Required
mongoose peer Yes (>=9.0.0)
@classytic/mongokit peer Yes (>=3.0.0) — provides repository pattern, pagination, plugins

Reference Docs

Doc Description
Architecture Domain model, clean architecture layers, invariants, capability tiers
Algorithms Valuation, allocation, routing, replenishment formulas
Workflows Inbound, outbound, transfer, counting, return flows
Plugins & Adapters Scan, events, labels, catalog, policies, workflow adapters
Integration MongoKit, Arc, Streamline, framework examples
API Reference Service methods, entity fields, events, errors
Test Strategy Test layers, coverage targets, fixtures
Migration Plan Step-by-step guide to integrate Flow into an Arc backend
Changelog Version history and bug fixes

License

MIT