A comprehensive JSON-based rules engine for validating commercial vehicle permit applications against British Columbia's transportation policy regulations.
The onRouteBC Policy Engine is a sophisticated validation library designed to ensure commercial vehicle permit applications comply with British Columbia's transportation regulations. It provides a flexible, rule-based system that can validate complex vehicle configurations, weight distributions, axle arrangements, and permit requirements.
- Comprehensive Vehicle Validation: Validates vehicle configurations, weights, dimensions, and axle arrangements
- Policy-Driven Rules: Uses JSON-based policy definitions for flexible rule configuration
- Bridge Formula Calculations: Implements provincial bridge formula requirements for axle group weight limits
- Tire Load Validation: Ensures tire configurations meet safety and regulatory requirements
- Weight Distribution Checks: Validates proper weight distribution across vehicle axles
- Permit Type Support: Handles multiple permit types including Term Oversize, Single Trip Oversize, and Motive Fuel
- Commodity-Based Validation: Supports commodity-specific permit requirements
- Extensible Architecture: Built on json-rules-engine with custom operators for complex validations
npm install onroute-policy-engineimport Policy from 'onroute-policy-engine';
// Load your policy definition
const policyDefinition = require('./policy-definition.json');
// Create policy instance
const policy = new Policy(policyDefinition);
// Validate a permit application
const permitApplication = {
permitType: 'TROS',
permitData: {
// Your permit data here
},
};
const results = await policy.validate(permitApplication);
console.log(results);The policy engine uses a JSON-based policy definition that describes:
- Permit Types: Different types of permits (TROS, STOS, MFP, etc.)
- Vehicle Types: Power units and trailers with their characteristics
- Validation Rules: Complex rules for weight, dimension, and configuration validation
- Commodities: Cargo types that affect permit requirements
A vehicle configuration consists of:
- Power Units: The primary vehicle (truck, tractor, etc.)
- Trailers: Additional units being towed
- Axle Configurations: Detailed axle arrangements with weights and dimensions
The engine returns comprehensive validation results including:
- Policy Violations: Specific rule violations with detailed messages
- Informational Messages: Guidance and recommendations
- Axle Calculations: Bridge formula and weight distribution results
class Policy {
constructor(policyDefinition: PolicyDefinition);
// Core validation
validate(permitApplication: PermitApplication): Promise<ValidationResults>;
// Vehicle configuration validation
runAxleCalculation(
vehicleConfiguration: string[],
axleConfiguration: AxleConfiguration[],
licensedGVW: number,
commodityId: string,
): AxleCalcResults;
// Policy information
getPermitTypes(): Map<string, string>;
getCommodities(permitTypeId?: string): Map<string, string>;
getPowerUnitTypes(): Map<string, string>;
getTrailerTypes(): Map<string, string>;
// Vehicle configuration helpers
getNextPermittableVehicles(
permitTypeId: string,
commodityId: string,
currentConfiguration: string[],
): Map<string, string>;
// Weight and dimension calculations
getDefaultPowerUnitWeight(
vehicleType: string,
axleCode: number,
): WeightDimension[];
getDefaultTrailerWeight(
vehicleType: string,
axles: number,
): TrailerWeightDimension[];
calculateBridge(
axleConfiguration: AxleConfiguration[],
): BridgeCalculationResult[];
}interface PermitApplication {
permitType: string;
permitData: any;
}
interface ValidationResults {
results: ValidationResult[];
summary: {
totalViolations: number;
totalWarnings: number;
totalInfo: number;
};
}
interface AxleConfiguration {
numberOfAxles: number;
numberOfTires?: number;
tireSize?: number;
axleUnitWeight: number;
axleSpread?: number;
}import Policy from 'onroute-policy-engine';
const policy = new Policy(policyDefinition);
const permitApp = {
permitType: 'TROS',
permitData: {
vehicleConfiguration: [
POWER_UNITS.TRUCK_TRACTORS.id,
TRAILERS.SEMI_TRAILERS.id,
],
axleConfiguration: [
{
numberOfAxles: 2,
axleUnitWeight: 12000,
numberOfTires: 4,
tireSize: 445,
},
{
numberOfAxles: 3,
axleUnitWeight: 34000,
numberOfTires: 12,
tireSize: 445,
},
{
numberOfAxles: 3,
axleUnitWeight: 34000,
numberOfTires: 12,
tireSize: 445,
},
],
licensedGVW: 63500,
},
};
const results = await policy.validate(permitApp);
console.log(
`Validation completed with ${results.violations.length} violations`,
);// Validate axle configuration against policy rules
const axleResults = policy.runAxleCalculation(
[POWER_UNITS.TRUCK_TRACTORS.id, TRAILERS.SEMI_TRAILERS.id],
[
{
numberOfAxles: 2,
axleUnitWeight: 12000,
numberOfTires: 4,
tireSize: 445,
},
{
numberOfAxles: 3,
axleUnitWeight: 34000,
numberOfTires: 12,
tireSize: 445,
},
{
numberOfAxles: 3,
axleUnitWeight: 34000,
numberOfTires: 12,
tireSize: 445,
},
],
63500,
'NONREDU', // Selected commodity ID
);
console.log('Axle validation results:', axleResults.results);The fourth argument is required and must identify a configured commodity. Use
XXXXXXX only when None was explicitly selected. Matching commodity weight rows
replace inherited rows for that axle layout; other layouts retain their defaults.
validate(permit) reads the same selection from
permitData.permittedCommodity.commodityType. Invalid or missing required
commodity selections retain configuration violations and omit axle calculations.
This API change requires coordinated engine and frontend deployment. Publish the
commodity weight configuration with the engine release and set its minPEVersion
to that released version; older engines ignore commodity weight rows. The vehicles
backend continues passing commodity in the existing permit payload to validate.
// Get all available permit types
const permitTypes = policy.getPermitTypes();
console.log('Available permit types:', Array.from(permitTypes.entries()));
// Get commodities for a specific permit type
const commodities = policy.getCommodities('TROS');
console.log('TROS commodities:', Array.from(commodities.entries()));
// Get valid next vehicles for a configuration
const nextVehicles = policy.getNextPermittableVehicles('STOS', 'IMCONTN', [
POWER_UNITS.TRUCK_TRACTORS.id,
]);
console.log('Valid next vehicles:', Array.from(nextVehicles.entries()));The engine implements several key validation rules:
- Bridge Formula Check: Validates axle group weights based on axle spacing
- Number of Wheels Per Axle: Ensures valid tire counts per axle unit
- Permittable Weight Check: Validates axle unit weights against limits
- Minimum Steer Axle Weight: Ensures proper front axle weight distribution
- Minimum Drive Axle Weight: Validates drive axle weight requirements
- Maximum Tire Load: Checks tire load capacity based on size and quantity
For detailed information about these rules, see Policy Check Rules Documentation.
- Node.js 18+
- TypeScript 5.7+
- npm or yarn
# Clone the repository
git clone https://github.com/bcgov/onroutebc-policy-engine.git
cd onroutebc-policy-engine
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
# Run linting
npm run lintsrc/
├── enum/ # Enumerations and constants
├── helper/ # Helper functions and utilities
├── types/ # TypeScript type definitions
├── _examples/ # Usage examples and demos
├── _test/ # Test data and configurations
├── policy-engine.ts # Main policy engine implementation
├── validation-result.ts # Validation result types
└── index.ts # Public API exports
# Clean build
npm run clean-build
# Development build
npm run build- Policy Configuration Reference - Detailed policy definition format
- Validation Result Reference - Understanding validation results
- Basic Client-Server Flow - Integration patterns
- Policy Check Rules - Detailed explanation of validation rules
- Fork the repository
- Create a feature branch (
git checkout -b feature/new-feature) - Commit your changes (
git commit -m 'Add new feature') - Push to the branch (
git push origin feature/new-feature) - Open a Pull Request
- Follow TypeScript best practices
- Use ESLint and Prettier for code formatting
- Write comprehensive tests for new features
- Update documentation for API changes
# Run all tests
npm test
# Run tests with coverage
npm test -- --coverage
# Run specific test file
npm test -- validate-tros.spec.tsUse this when you want this frontend example, including the permit form application flow, to pick up local onroute-policy-engine changes without publishing a package.
# policy engine repo route
# note: linking only works the BUILT files! so you must run `npm run build` after pulling down any PR, making any code changes, etc, to the policy engine
npm run build
# npm run build -- --watch # optional for active library development; rebuilds dist on change
npm link
# this example app
cd src/_examples/usage/react-frontend-example
npm link onroute-policy-engine
npm run dev
# OPTIONAL - Now assume you have further changes to make to policy engine, or are switching policy engine branches, etc. To bring those changes in, you would:
# in PE repo, after changes are made
npm run build
# in application repo, after build is done
kill the npm run dev
npm run dev
# now that you've built + restarted the dev server, changes should be represented.If Chrome DevTools shows import/module resolution issues after linking, add this to vite.config.ts:
export default defineConfig({
resolve: {
preserveSymlinks: true,
},
});This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
For questions, issues, or contributions:
- Issues: GitHub Issues
- Documentation: Check the docs directory
- Examples: See src/_examples for usage examples
- json-rules-engine - Core rules engine functionality
- onRouteBC - British Columbia's commercial vehicle permitting system
Note: This policy engine is specifically designed for British Columbia's commercial vehicle regulations. Ensure compliance with local regulations when adapting for other jurisdictions.