Deterministic 3D cartonization for Node.js. It uses the optional native engine when available and automatically falls back to the bundled JavaScript implementation.
Full documentation, the constraint reference and benchmarks live at packvium.com.
npm install @packvium/engineNode.js 16 or later is required. Use a currently supported Node.js release in production.
import { backend, commerce, pack } from '@packvium/engine';
const result = pack({
items: [{
id: 'book', quantity: 4,
dimensions: { length: '210', width: '140', height: '30' },
}],
containers: [{
id: 'carton',
inner_dimensions: { length: '400', width: '300', height: '250' },
}],
});
console.log(backend()); // "rust" or "javascript"
console.log(result.status); // "feasible"
console.log(result.containers);
const commerceDocument = { tariffs: [{
carrier_id: 'acme', service_id: 'ground',
versions: [{
effective_at: 0, dimensional_weight_divisor: 5000,
cost_per_dimensional_kg_minor: { 'zone-a': 450 },
minimum_charge_minor: 900, fuel_surcharge_permille: 120,
}],
}] };
const quote = commerce.quote(commerceDocument, {
carrier_id: 'acme', service_id: 'ground', tariff_version: 1,
zone: 'zone-a', actual_weight_g: 1200, volume_mm3: 6000000,
});
console.log(quote.quote.total_minor);commerce has three functions, all deterministic and all over one document you supply —
no clock, no network, no hidden state. A history is a list and a version's number is its
position in that list starting at 1, so tariff_version: 2 always means "the second
entry under this carrier and service".
import { commerce } from '@packvium/engine';
// Which version applies: pin it, or ask what was in force at an instant. Never both.
commerce.quote(document, { /* ... */ tariff_version: 1 });
commerce.quote(document, { /* ... */ as_of: 1500 });
// The decision, and the rule id and version that made it.
const { decision } = commerce.evaluatePolicy(document, {
scope: 'hazmat', context: { un_class: '1.4' }, as_of: 0,
});
decision.allowed; // false
decision.citation.rule_id; // "no-hazmat-air"
// Which catalog version a pin resolves to, what it holds, whether it was a rollback.
const { catalog } = commerce.catalogVersionInfo(document, {
catalog_id: 'dc-12', version: 2, resolved_at: 1700,
});
catalog.entry_counts; // { items: 1, cartons: 1, pallets: 0, ... }
catalog.rolled_back_from; // 1, or null for an ordinary publication
// Store, log and compare results in the canonical form, not JSON.stringify.
commerce.canonicalJson(result);Two kinds of failure, and they are not interchangeable:
- a malformed document or request is your bug and throws
CommerceInputError; - a request the model simply cannot answer — no tariff effective at that instant, no
rate for that zone — is a successful call returning
"status": "rejected"with a code from a closed set and structured fields naming what was missing.
commerce.backend() reports whether the native addon or the JavaScript implementation
answered; both return the same result for the same input. A runnable walk-through of all
three functions is in examples/commerce.mjs, and the full
contract — document format, every result shape, all ten rejection codes, complexity and
limitations — is docs/COMMERCE-API.md.
Runnable, in examples/. Each one is a single file you can read top to bottom
and execute without a project around it.
| File | What it shows |
|---|---|
basic.mjs |
Pack an order, read placements, and see why an item was refused. |
objectives.mjs |
All six objectives on scenes where they genuinely disagree — the same scores the Python, PHP and Rust engines print for the same request. |
shapes.mjs |
Items that are not their box: complementary wedges sharing one crate as convex_hull, and a cushion that compresses under load until the crush limit refuses it. |
constraints.mjs |
Stacking caps, incompatible tags and atomic groups — each shown with and without the rule, plus how to read the structured refusal. |
units.mjs |
Why lengths travel as strings: fractional inches kept exact, one tick deciding a fit, and the point where a JavaScript number stops being exact and a quote is refused rather than rounded. |
commerce.mjs |
Rate a shipment, apply an eligibility rule, and pin a catalog version. |
node examples/basic.mjs- Exact, deterministic placement with no floating-point geometry decisions.
- Rotation, payload, stackability, support, clearance, obstacle and tag constraints.
- Multiple container types and clear explanations for unpacked items.
- JSON input/output through
pack()orpackJson(). - Optional payload rebalancing with
rebalanceWeight(). - Loading and removal sequence helpers for already placed boxes.
- Deterministic carrier quotes, policy evaluation and effective-dated catalog lookup
through
commerce.
The native addon is optional. npm install works on unsupported platforms too; call
backend() if your application needs to know which implementation handled a request.
One request and result contract, implemented independently in four engines (Rust, Python, PHP, JavaScript) and held to identical placements on a shared fixture set. Pick the package for your stack; mixing them in one system is safe.
Documentation, the constraint reference and the benchmarks are at packvium.com.
| Package | Install | Source |
|---|---|---|
Python — packvium |
pip install packvium |
packvium-python |
PHP — packvium/packvium |
composer require packvium/packvium |
packvium-php |
Rust — packvium |
packvium = "1.0" |
packvium-rust |
Node.js — @packvium/engine |
npm install @packvium/engine |
packvium-node |
Browser / WebAssembly — @packvium/browser |
npm install @packvium/browser |
packvium-wasm |
PHP FFI bridge — packvium/native-bridge |
composer require packvium/native-bridge |
packvium-php-bridge |
Python native selector — packvium-native |
from source until the native wheels ship | packvium-python-adapter |
TypeScript declarations are included. See the package's index.d.ts for the complete
request and result types, and docs/COMMERCE-API.md for the commercial/control-plane
contract. Report security issues through SECURITY.md.
MIT. See LICENSE.