rr-rome-client
A minimal Node.js wrapper for Reynolds & Reynolds (Rome/RIH) STAR ProcessMessage over SOAP, with WS-Security UsernameToken.
Contents
- Overview
- Install
- Supported Node Version
- Peer / External Dependencies
- Quick Start
- Configuration & Environment Variables
- Client API Methods
- RRResult Structure
- Types & IntelliSense
- Errors & Validation
- Retry Strategy
- Debug / Dump Flags
- Live Test Runner (
scripts/run-live.mjs) - Bundling & Upload Helper (
scripts/bundle-for-upload.mjs) - XML Templates & XSDs
- Logging
- Build & Development
- Design Notes / Non-goals
- License
Overview
rr-rome-client builds and sends STAR XML payloads (Customer, Service Vehicle, Combined Search, Advisors, Parts, BSM Repair Orders) inside a SOAP envelope using the STAR Transport ProcessMessage operation. It applies a WS-Security UsernameToken header and provides:
- High-level
RRClientmethods for each supported Rome operation. - JSDoc typedefs for all payloads and response shapes (usable in JS and TS).
- Structured parsing of response status blocks and operation-specific data.
- Automatic BODId & CreationDateTime generation, with ability to override.
- Basic input validation and error classification.
- Exponential backoff + jitter for retryable transport/vendor lock scenarios.
Install
npm i rr-rome-client
(You may also need peer dependencies; see below.)
Supported Node Version
package.json declares "engines": { "node": ">=22.0.0" }. The build targets modern Node 22 features (native ESM, improved performance). Earlier Node versions are not officially supported/tested.
Peer / External Dependencies
Rollup externalizes runtime libraries (they are listed in dependencies but not bundled). Ensure these are available in your environment:
Required:
- axios – HTTP transport
- fast-xml-parser – STAR XML parsing
- mustache – XML templating
- uuid – BODId generation Optional (only if you use env loader helpers or live scripts):
- dotenv
- dotenv-expand
Install (versions per package.json):
npm install axios@^1.7.7 fast-xml-parser@^4.5.0 mustache@^4.2.0 uuid@^9.0.1
# Optional env loader
npm install dotenv@^17.2.3 dotenv-expand@^12.0.3
TypeScript users (optional):
npm install -D @types/node
Browser bundling is not officially supported; you would need polyfills for core modules if attempting.
Quick Start
import { RRClient } from 'rr-rome-client';
import { loadEnv } from 'rr-rome-client/src/util/config.js'; // optional helper
// Load routing & credentials from environment variables (see below)
const { baseUrl, username, password, routing } = loadEnv();
const client = new RRClient({ baseUrl, username, password });
// Minimal createRepairOrder example
const result = await client.createRepairOrder({
customerNo: '12345',
departmentType: 'S', // DeptType
vin: '1ABCDEF2GHIJ34567',
outsdRoNo: 'EXT-RO-99'
}, { routing });
if (result.success) {
console.log('RO status:', result.data); // {status, date, time, outsdRoNo, dmsRoNo, errorMessage}
} else {
console.error('Failure:', result.statusBlocks?.transaction);
}
Configuration & Environment Variables
You can manually provide configuration or use the helper loadEnv(env) in src/util/config.js.
Recognized environment variables:
- RR_BASE_URL – SOAP endpoint URL (required)
- RR_USERNAME – WS-Security UsernameToken username (required)
- RR_PASSWORD – WS-Security UsernameToken password (required)
- RR_DEALER_NUMBER – DealerNumber for Destination (required per call)
- RR_STORE_NUMBER – StoreNumber (optional)
- RR_AREANUMBER – AreaNumber (optional)
Example .env:
RR_BASE_URL=https://rome.example.com/soap
RR_USERNAME=integratorUser
RR_PASSWORD=superSecret
RR_DEALER_NUMBER=1234
RR_STORE_NUMBER=01
RR_AREANUMBER=1
loadEnv() returns { baseUrl, username, password, routing: { dealerNumber, storeNumber, areaNumber } }.
Per-call options object shape (CallOptions):
{
routing: { dealerNumber: '1234', storeNumber?: '01', areaNumber?: '1' },
envelope?: { bodId?, creationDateTime?, sender?: { component?, task?, referenceId? } }
}
If omitted, RRClient auto-generates bodId (UUID) and creationDateTime when sending.
Client API Methods
Each method returns a Promise<RRResult<T>> where T is operation-specific data (or array). success is true for vendor SUCCESS and NO_MATCH results; FAIL triggers an RRVendorStatusError exception before a RRResult is returned.
All methods require opts.routing.dealerNumber.
combinedSearch(payload: CombinedSearchQuery, opts)
Search customer + service vehicle combinations by exactly one criterion: phone | license | vin | name | nameRecId | stkNo plus optional make, model, year, maxResults (capped at 50).
Minimal:
const res = await client.combinedSearch({ kind: 'vin', vin: '12345' }, { routing });
res.data; // Array<CombinedSearchBlock>
Errors: Throws RRValidationError if missing or multiple criteria.
insertCustomer(payload: InsertCustomerPayload, opts)
Insert a customer record. Required: lastName (or customerName). If individual (ibFlag='I' or inferred by presence of firstName), then firstName required.
const res = await client.insertCustomer({ firstName: 'Jane', lastName: 'Doe', phones:[{number:'5551234567'}] }, { routing });
res.data; // { dmsRecKey, status, statusCode }
updateCustomer(payload: UpdateCustomerPayload, opts)
Update existing customer by nameRecId plus required ibFlag. Other fields optional.
const res = await client.updateCustomer({ nameRecId:'998877', ibFlag:'I', lastName:'Doe' }, { routing });
insertServiceVehicle(payload: InsertServiceVehiclePayload, opts)
Insert a service vehicle linked to a customer. Required: vin, vehicleServInfo.customerNo.
const res = await client.insertServiceVehicle({
vin:'1HGCM82633A004352',
vehicleServInfo:{ customerNo:'12345' }
}, { routing });
res.data; // { status, statusCode }
getAdvisors(payload: GetAdvisorsParams, opts)
Fetch advisors for a department. Department values normalized: S/P/B or long names.
const res = await client.getAdvisors({ department:'SERVICE' }, { routing });
res.data; // AdvisorRow[]
createRepairOrder(payload: CreateRepairOrderPayload, opts)
Required: customerNo, departmentType, vin, outsdRoNo. Advisor optional. Complex nested labor/parts/misc blocks supported via payload.
const res = await client.createRepairOrder({
customerNo:'12345', departmentType:'S', vin:'1ABCDEF2GHIJ34567', outsdRoNo:'EXT-RO-99'
}, { routing });
res.data; // { status, date, time, outsdRoNo, dmsRoNo, errorMessage }
updateRepairOrder(payload: UpdateRepairOrderPayload, opts)
Required: finalUpdate ('Y'|'N'), outsdRoNo. May include roNo and nested sections.
const res = await client.updateRepairOrder({ finalUpdate:'N', outsdRoNo:'EXT-RO-99' }, { routing });
getParts(payload: GetPartsParams, opts)
Required: roNumber (internal ERA RO number).
const res = await client.getParts({ roNumber:'938275' }, { routing });
res.data; // PartRow[]
Payload Schema Reference
Comprehensive field-level summary sourced from src/types.js, operation builders, and validation logic. Types reflect accepted JS types (string|number where applicable). Constraints list enumerations, inference rules, and validation notes. Required = must be supplied by caller (or inferred automatically). Optional fields omitted become absent in generated XML.
CombinedSearchQuery
Only one criterion permitted; maxResults capped at 50.
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| kind | 'phone' | 'license' | 'vin' |
| phone | string|number|{phone:string} | Conditionally (if kind==='phone') | Value mapped to <Phone Num="..."/> |
| license | string|number|{license:string} | Conditionally (kind==='license') | Value mapped to <LicenseNum LicNo="..."/> |
| vin | string|number|{vin:string} | Conditionally (kind==='vin') | Value mapped to <PartVIN Vin="..."/> (partial VIN allowed) |
| name | {fname,lname,mname} or {name} | Conditionally (kind==='name') | Either FullName triple or LName only; must supply all three for FullName |
| nameRecId | string|number|{custId:string} | {nameRecId:string} | Conditionally (kind==='nameRecId') |
| stkNo | string|number|{stkNo:string} | Conditionally (kind==='stkNo') | Emits <StkNo VehId="..."/> |
| maxResults | number | No | Capped at 50 (default 50) -> MaxRecs attribute |
| make | string | No | Defaults 'ANY' -> VehData MakePfx |
| model | string|number | No | Defaults 'ANY' -> VehData Model |
| year | string|number | No | Defaults 'ANY' -> VehData Year |
InsertCustomerPayload
ibFlag inferred as 'I' if firstName present, else 'B'. Business requires lastName / customerName; individual requires firstName + last name.
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| ibFlag | 'I' | 'B' | Auto / For update must supply |
| customerType | 'R' | 'W' | 'I' |
| createdBy | string | No | Optional CreatedBy attribute |
| customerName | string | Conditional | Alias for lastName when business |
| lastName | string | Yes (unless customerName provided) |
Required base name; sanitized to A-Z0-9 space |
| firstName | string | Required when ibFlag='I' | Sanitized; required for individuals |
| midName | string | No | Sanitized |
| salut | string | No | Sanitized |
| suffix | string | No | Sanitized |
| addresses | CustomerAddress[] | No | Each entry requires line1; Type defaults 'P' |
| phones | CustomerPhone[] | No | Each entry requires number; Type defaults 'H' |
| emails | CustomerEmail[] | No | First entry used -> <Email MailTo="..."/> |
| personal.gender | 'M' | 'F' | 'U' |
| personal.otherName | string | No | Sanitized alnum/space |
| personal.anniversaryDate | string | No | Included if non-empty |
| personal.employerName | string | No | Sanitized |
| personal.employerPhone | string | No | Raw string |
| personal.occupation | string | No | Sanitized |
| personal.optOut | string | No | Pass-through |
| personal.optOutUse | string | No | Pass-through |
| personal.birthDates[].type | 'P' | 'S' | No |
| personal.birthDates[].date | string | Conditional | Included only if non-empty |
| personal.ssns[].type | 'P' | 'S' | No |
| personal.ssns[].ssn | string | Conditional | Included only if non-empty |
| personal.driver.type | 'P' | 'S' | No |
| personal.driver.licenseNumber | string | Conditional | Required to emit DriverInfo |
| personal.driver.licenseState | string | No | Optional |
| personal.driver.licenseExpDate | string | No | Optional |
| personal.children[].name | string | No | Sanitized; optional list |
| dms.taxExemptNum | string | No | Optional |
| dms.salesTerritory | string | No | Optional |
| dms.deliveryRoute | string | No | Optional |
| dms.salesmanNum | string | No | Optional |
| dms.lastContactMethod | string | No | Optional |
| dms.followups[].type | string | Conditional | Must have both type & value |
| dms.followups[].value | string | Conditional | Must have both type & value |
UpdateCustomerPayload
Extends InsertCustomerPayload plus:
| Field | Type | Required | Constraints |
|---|---|---|---|
| nameRecId | string|number | Yes | Required identifier |
| ibFlag | 'I' | 'B' | Yes |
InsertServiceVehiclePayload
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| vin | string | Yes | Must be provided |
| modelDesc | string | No | Optional attribute |
| carline | string | No | Optional attribute |
| extClrDesc | string | No | Optional attribute |
| intClrDesc | string | No | Optional attribute |
| trimDesc | string | No | Optional attribute |
| bodyStyle | string | No | Optional attribute |
| engineDesc | string | No | Optional attribute |
| transDesc | string | No | Optional attribute |
| year | string|number | No | Emits <Year> element if present |
| odometer | string|number | No | Emits <Odometer> |
| odometerUnits | string | No | Emits <OdometerUnits> |
| vehicleDetail.licNo | string | No | Emits <VehicleDetail LicNo="..."/> |
| vehicleServInfo.customerNo | string|number | Yes | Required; becomes CustomerNo attribute |
| vehicleServInfo.salesmanNo | string|number | No | Optional element |
| vehicleServInfo.inServiceDate | string|number | No | Optional element |
| vehicleServInfo.mileage | string|number | No | Optional element |
| vehicleServInfo.teamCode | string | No | Optional element |
| vehicleServInfo.vehExtWarranty.contractNumber | string | Conditional | Included if any warranty field present |
| vehicleServInfo.vehExtWarranty.expirationDate | string | Conditional | "" excluded |
| vehicleServInfo.vehExtWarranty.expirationMileage | string|number | Conditional | "" excluded |
| vehicleServInfo.advisor.contactInfo.nameRecId | string|number | No | Advisor block included only if provided |
CreateRepairOrderPayload
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| customerNo | string|number | Yes | CustNo |
| departmentType | string|number | Yes | DeptType |
| vin | string | Yes | Vin |
| outsdRoNo | string|number | Yes | External RO identifier |
| advisorNo | string|number | No | AdvNo |
| tagNo | string|number | No | TagNo |
| mileageIn | string|number | No | MileageIn |
| roComment | string | No | <RoCommentInfo RoComment="..."/> |
| estimate.parts | string|number | No | EstPartsAmt |
| estimate.labor | string|number | No | EstLaborAmt |
| estimate.total | string|number | No | EstTotalAmt |
| tax.payType | 'All' | 'Cust' | 'Intr' |
| tax.taxCode | string | No | TaxCode |
| tax.txblGrossAmt | string|number | No | TxblGrossAmt |
| tax.grossTaxAmt | string|number | No | GrossTaxAmt |
| rolabor.ops[].opCode | string | No | Optional |
| rolabor.ops[].jobNo | string|number | No | Optional |
| rolabor.ops[].custPayTypeFlag | string | No | Freeform (not validated) |
| rolabor.ops[].warrPayTypeFlag | string | No | Freeform |
| rolabor.ops[].intrPayTypeFlag | string | No | Freeform |
| rolabor.ops[].custTxblNtxblFlag | 'T' | 'N' | No |
| rolabor.ops[].warrTxblNtxblFlag | 'T' | 'N' | No |
| rolabor.ops[].intrTxblNtxblFlag | 'T' | 'N' | No |
| rolabor.ops[].vlrCode | string | No | Optional |
| rolabor.ops[].bill.payType | 'All' | 'Cust' | 'Intr' |
| rolabor.ops[].bill.jobTotalHrs | string|number | No | Optional |
| rolabor.ops[].bill.billTime | string|number | No | Optional |
| rolabor.ops[].bill.billRate | string|number | No | Optional |
| rolabor.ops[].ccc.cause | string | No | Optional |
| rolabor.ops[].ccc.complaint | string | No | Optional |
| rolabor.ops[].ccc.correction | string | No | Optional |
| rolabor.ops[].amount.payType | 'All' | 'Cust' | 'Intr' |
| rolabor.ops[].amount.amtType | string | No | Optional |
| rolabor.ops[].amount.custPrice | string|number | No | Optional |
| rolabor.ops[].amount.totalAmt | string|number | No | Optional |
| ropart.jobs[].opCode | string | No | Optional |
| ropart.jobs[].jobNo | string|number | No | Optional |
| ropart.jobs[].lines[].partNo | string | No | Optional |
| ropart.jobs[].lines[].partNoDesc | string | No | Optional |
| ropart.jobs[].lines[].partQty | string|number | No | Emits QtyOrd |
| ropart.jobs[].lines[].sale | string|number | No | Sale |
| ropart.jobs[].lines[].cost | string|number | No | Cost |
| ropart.jobs[].lines[].addDeleteFlag | string | No | AddDeleteFlag |
| rogg.ops[].lines[].breakOut | string | No | Optional |
| rogg.ops[].lines[].itemType | 'G' | 'P' | 'S' |
| rogg.ops[].lines[].itemDesc | string | No | Optional |
| rogg.ops[].lines[].custQty | string|number | No | Optional |
| rogg.ops[].lines[].warrQty | string|number | No | Optional |
| rogg.ops[].lines[].intrQty | string|number | No | Optional |
| rogg.ops[].lines[].custPayTypeFlag | string | No | Optional |
| rogg.ops[].lines[].warrPayTypeFlag | string | No | Optional |
| rogg.ops[].lines[].intrPayTypeFlag | string | No | Optional |
| rogg.ops[].lines[].custTxblNtxblFlag | 'T' | 'N' | No |
| rogg.ops[].lines[].warrTxblNtxblFlag | 'T' | 'N' | No |
| rogg.ops[].lines[].intrTxblNtxblFlag | 'T' | 'N' | No |
| rogg.ops[].lines[].amount.payType | 'All' | 'Cust' | 'Intr' |
| rogg.ops[].lines[].amount.amtType | string | No | Optional |
| rogg.ops[].lines[].amount.custPrice | string|number | No | Optional |
| rogg.ops[].lines[].amount.dlrCost | string|number | No | Optional |
| romisc.ops[].lines[].miscCode | string | No | Optional |
| romisc.ops[].lines[].custPayTypeFlag | string | No | Optional |
| romisc.ops[].lines[].warrPayTypeFlag | string | No | Optional |
| romisc.ops[].lines[].intrPayTypeFlag | string | No | Optional |
| romisc.ops[].lines[].custTxblNtxblFlag | 'T' | 'N' | No |
| romisc.ops[].lines[].warrTxblNtxblFlag | 'T' | 'N' | No |
| romisc.ops[].lines[].intrTxblNtxblFlag | 'T' | 'N' | No |
| romisc.ops[].lines[].codeAmt | string|number | No | Optional |
UpdateRepairOrderPayload
Adds required finalUpdate and supports most Create fields + mileageOut.
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| finalUpdate | 'Y' | 'N' | Yes |
| outsdRoNo | string|number | Yes | External RO identifier (validation requires present) |
| roNo | string|number | No | Optional internal RoNo |
| mileageOut | string|number | No | MileageOut |
| (other fields) | see CreateRepairOrderPayload | Conditional | Same validations apply |
GetAdvisorsParams
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| department | 'S' | 'P' | 'B' |
| advisorNumber | string|number | No | Optional filter |
| maxResults | number | Ignored | Not in XSD (builder ignores) |
GetPartsParams
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| roNumber | string|number | Yes | Required; becomes <RoInfo RoNumber="..."/> |
Common Routing & Envelope Fields
| Field | Type | Required | Constraints / Notes |
|---|---|---|---|
| routing.dealerNumber | string | Yes | Required for all requests (Destination) |
| routing.storeNumber | string | No | Optional Destination StoreNumber |
| routing.areaNumber | string | No | Optional Destination AreaNumber |
| envelope.bodId | string | Auto | UUID generated if omitted |
| envelope.creationDateTime | Date|string | Auto | Uses current time; formatted without milliseconds |
| envelope.sender.component | string | No | Defaults 'Rome' if omitted |
| envelope.sender.task | string | No | Op-specific defaults (e.g. 'CU','SV','BSMRO','CVC','RCT') |
| envelope.sender.referenceId | string | No | Op-specific defaults ('Insert','Update','Query') |
AdvisorRow (response convenience)
| Field | Type | Notes |
|---|---|---|
| advisorId | string|number|undefined | AdvisorNumber attribute |
| firstName | string|undefined | FirstName attribute |
| lastName | string|undefined | LastName attribute |
| department | 'S' | 'P' |
PartRow (response convenience)
| Field | Type | Notes |
|---|---|---|
| partNumber | string|undefined | PartNumber |
| partDescription | string|undefined | PartDescription |
| quantityOrdered | string|number|undefined | QuantityOrdered |
| quantityShipped | string|number|undefined | QuantityShipped |
| price | string|number|undefined | Price |
| cost | string|number|undefined | Cost |
| processedFlag | string|undefined | ProcessedFlag |
| addOrDelete | string|undefined | AddOrDelete |
CombinedSearch Response Shapes
Hierarchical breakdown of the parsed array returned under RRResult.data for combinedSearch (each entry is a CombinedSearchBlock). Attribute-centric; absent fields are simply omitted.
CombinedSearchBlock
| Field | Type | Notes |
|---|---|---|
| NameContactId | CombinedSearchNameContactId | Customer name/contact composite |
| ServVehicle | CombinedSearchServVehicle[] | Zero or more service vehicle blocks |
| Message | CombinedSearchMessage[] | Optional informational messages |
NameContactId
| Field | Type | Notes |
|---|---|---|
| NameId | CombinedSearchNameId | Includes identifiers & individual/business name choice |
| Address | Object[] | Each has address-related attributes (lines, city, state, zip, etc.) |
| ContactOptions | Object[] | Arbitrary contact option attributes as present |
| Phone | Object[] | Each phone entry carries attributes like Type, Num, Ext |
| Object[] | Each email entry carries attributes (MailTo, etc.) |
NameId
| Field | Type | Notes |
|---|---|---|
| NameRecId | string|number | Identifier used for subsequent operations |
| IBFlag | 'I' | 'B' |
| IndName | Object | Present for individual: attributes like FName, LName, MName |
| BusName | Object | Present for business: attributes like Name (business name) |
ServVehicle (CombinedSearchServVehicle)
| Field | Type | Notes |
|---|---|---|
| Vehicle | CombinedSearchVehicle | VIN + descriptive attributes |
| VehicleServInfo | CombinedSearchVehicleServInfo | Service info attributes & nested warranty/advisor/comments |
Vehicle (CombinedSearchVehicle)
| Attribute | Type | Notes |
|---|---|---|
| Vin | string | Vehicle identification number (may be partial in search results) |
| VehicleMake | string | Make code / description |
| VehicleYr | string|number | Year |
| MdlNo | string | Model code |
| ModelDesc | string | Model description |
| Carline | string | Carline description |
| ExtClrDesc | string | Exterior color |
| IntClrDesc | string | Interior color |
| MakeName | string | Full make name |
| VehicleDetail.LicNo | string | License plate (if present) |
VehicleServInfo (CombinedSearchVehicleServInfo)
| Attribute | Type | Notes |
|---|---|---|
| CustomerNo | string|number | Linked customer number |
| SalesmanNo | string|number | Optional salesman number |
| InServiceDate | string|number | Date vehicle placed in service |
| Mileage | string|number | Current mileage |
| TeamCode | string | Team/department code |
| VehExtWarranty.ContractNumber | string | Extended warranty contract number |
| VehExtWarranty.ExpirationDate | string | Warranty expiration date |
| VehExtWarranty.ExpirationMileage | string|number | Warranty mileage limit |
| Advisor.ContactInfo.NameRecId | string|number | Advisor reference (if present) |
| VehServComments[] | string[] | Freeform service comments (array of raw text) |
Message (CombinedSearchMessage)
| Field | Type | Notes |
|---|---|---|
| MessageNo | string|number | Optional message number (if provided) |
| Text | string | Message text content |
CustomerResponseData
Returned as result.data for insertCustomer / updateCustomer.
| Field | Type | Notes |
|---|---|---|
| dmsRecKey | string|undefined | DMS record key identifier (if provided in TransStatus) |
| status | string|undefined | Vendor status string |
| statusCode | string|undefined | Vendor status code |
ServiceVehicleResponseData
Returned as result.data for insertServiceVehicle.
| Field | Type | Notes |
|---|---|---|
| status | string|undefined | GenTransStatus Status |
| statusCode | string|undefined | GenTransStatus StatusCode |
RepairOrderData
Returned as result.data for createRepairOrder / updateRepairOrder (parsed from RoRecordStatus).
| Field | Type | Notes |
|---|---|---|
| status | string|undefined | RoRecordStatus Status |
| date | string|undefined | Date attribute/text |
| time | string|undefined | Time attribute/text |
| outsdRoNo | string|undefined | External RO number (OutsdRoNo) |
| dmsRoNo | string|undefined | Internal DMS RO number (DMSRoNo) |
| errorMessage | string|undefined | ErrorMessage if provided |
RRResult Structure
Each successful call resolves to:
interface RRResult<T> {
success: boolean; // SUCCESS or NO_MATCH
data?: T; // op-specific parsed convenience data
parsed: any; // entire parsed STAR payload root
xml: { request: string; response: string }; // raw SOAP envelopes
statusBlocks?: { transaction?: {status,statusCode,message}; roRecord?: {status,date,time,outsdRoNo,dmsRoNo,errorMessage} };
applicationArea?: any; // raw ApplicationArea node
}
Use statusBlocks.transaction for generic status; data for normalized op output.
Convenience data refers to the distilled, operation-specific subset placed on RRResult.data by a dedicated postParse function (e.g., extracting only RoRecordStatus identifiers or customer DMS keys). It is intentionally smaller and flatter than RRResult.parsed, which contains the entire parsed STAR payload tree. Use data for common identifiers/status checks; fall back to parsed when you need full raw XML-derived detail.
Types & IntelliSense
Rich JSDoc typedefs ship with the package.
- ESM:
import { RRClient } from 'rr-rome-client';optionally import'rr-rome-client/types'to prompt editor indexing. - CJS:
const { RRClient } = require('rr-rome-client'); require('rr-rome-client/types');
Selected typedef categories (see src/types.js):
- Routing / Envelope / CallOptions
- Customer / Service Vehicle / Repair Order payload blocks
- Combined Search structures
- Advisor and Parts row shapes
RRResult<T>generic helper
The build also emits a TypeScript declaration bundle (dist/types/index.d.ts) generated via tsconfig.types.json (processing only src/types.js).
Errors & Validation
Three custom error classes (src/errors.js):
RRTransportError– Non-2xx HTTP status or network failure;meta.status/meta.bodymay be attached.RRVendorStatusError– Vendor FAIL status (non-success & not NO_MATCH). Includesmeta.status(raw status object) and full response XML.retryablemay be set (currently determined by vendor message lock wording or explicit flag).RRValidationError– Input validation failures (missing required fields, invalid enumeration values, etc.).
Enumerated validations (examples):
- Customer:
ibFlag, requiredfirstNameif individual, allowedcustomerTypevalues. - Service Vehicle: mandatory
vinandvehicleServInfo.customerNo. - Repair Orders: required header fields; enumerations for tax pay type (
All|Cust|Intr|Warr), taxable flags (T|N), item types (G|P|S|F).
Error Handling Example:
try {
await client.createRepairOrder(/* ... */);
} catch (e) {
if (e instanceof RRVendorStatusError) {
console.error('Vendor fail', e.meta.status);
} else if (e instanceof RRValidationError) {
console.error('Bad input', e.message);
} else if (e instanceof RRTransportError) {
console.error('HTTP/Network', e.message, e.meta.status);
} else {
console.error('Unexpected', e);
}
}
Retry Strategy
withBackoff(fn, {max, logger}) (used internally) retries on:
- Transport/network errors (
RRTransportError). - Vendor status errors indicating record lock/in use (message matching
/lock|in use|record.*busy/i) or explicitretryableflag. Backoff: exponential starting at 400ms, capped at 10s, plus up to 250ms jitter. Configure vianew RRClient({ retries: { max: 5 }, ... }).
Debug / Dump Flags
Set these environment variables to inspect request/response internals:
RR_DEBUG– Enables debug logging (defaultLogger.debug).RR_DUMP_ENVELOPE=1– Prints outgoing SOAP envelope.RR_DUMP_XML=1– Prints full response XML.RR_DUMP_STATUS=1– Logs parsed status blocks.RR_DUMP_APPLICATION=1– Logs raw ApplicationArea block.RR_DUMP_DATA=1– Logsresult.data(postParse convenience output).
Live Test Runner (scripts/run-live.mjs)
Provides curated integration tests against a live Rome system using Vitest. Loads .env then .env.local (override). Usage:
node scripts/run-live.mjs <test> [flags] [-- ...vitestArgs]
Available tests (see script for full list & flags):
combinedSearch– VIN or phone search.insertCustomer,updateCustomer– Customer record operations (use--writeto enable live writes).insertServiceVehicle– Add vehicle (requires or discovers customer).getAdvisors– Advisor listing by department.createRepairOrder,updateRepairOrder– BSM RO operations.getParts– Retrieve RO parts lines.all– Run all sequentially.
Common flags:
--dump→RR_DUMP_ENVELOPE--write→RR_LIVE_WRITES(enables writes)- Operation-specific flags map to
RR_TEST_*env variables (see script comments for details). - Arbitrary env:
--set=KEY=VALUE.
Example:
node scripts/run-live.mjs createRepairOrder --write --dump --customerNo=1134485 --vin=1ABCDEF2GHIJ34567 --ro=BSM123
Bundling & Upload Helper (scripts/bundle-for-upload.mjs)
Creates text bundles of project files for transport/support purposes.
- Includes key directories (
src/,test/,schemas/) and specific root files. - Excludes large/irrelevant directories (
node_modules,dist, etc.). - Options:
--max-bytes,--pattern=globish,--with-env(include.env– caution),--list-only. Generatesbundles/bundle-<uuid>-N.txtwith file boundary markers.
XML Templates & XSDs
Templates reside in src/templates/templateMap.js using Mustache. Each operation builder renders a STAR root element plus ApplicationArea. XSD files (under schemas/) accompany operations:
- Customer Insert/Update:
rey_RomeCustomerInsertReq.xsd,rey_RomeCustomerUpdateReq.xsd - Service Vehicle Insert:
rey_RomeServVehicleInsertReq.xsd - Combined Search:
rey_RomeCustServVehCombReq.xsd - Advisors:
rey_RomeGetAdvisorsReq.xsd - Parts:
rey_RomeGetPartsReq.xsd - Repair Orders Create/Update:
rey_RomeCreateBSMRepairOrderReq.xsd,rey_RomeUpdateBSMRepairOrderReq.xsdResponse XSDs also present for repair orders and others.
Builders attach an xsdFilename hint and elementName; no runtime XSD validation is performed (the project does not contain a validation module beyond these references).
Logging
Default logger (src/logger.js): logs to console via info, warn, error; debug gated by RR_DEBUG.
Provide a custom logger with matching method names in RRClientConfig:
const logger = { info:()=>{}, warn:()=>{}, error:console.error, debug:()=>{} };
const client = new RRClient({ baseUrl, username, password, logger });
Build & Development
Scripts:
npm run build– Rollup buildsdist/index.cjs&dist/index.mjs, copies JSDoc types (scripts/postbuild-copy-types.mjs), emits.d.ts(viatsc -p tsconfig.types.json).npm test– Runs unit tests (vitestwithvitest.config.unit.mjs).npm run bundle– Invoke bundle creation (see above).npm run live:<op>– Convenience commands mapping toscripts/run-live.mjs(e.g.npm run live:getParts).
Rollup configuration (rollup.config.mjs):
- Externalizes Node core modules and listed dependencies.
- Applies terser minification (2 passes, hoisting) for compact output.
- Generates both CJS and ESM entrypoints.
Tree-shaking is enabled (treeshake.moduleSideEffects = false). sideEffects: false in package.json allows downstream bundlers to drop unused exports.
Design Notes / Non-goals
- WS-Security: Implements only UsernameToken with password type Text or Digest (configurable via
wssePasswordType). No nonce or timestamp included. - Idempotency:
bodIdgenerated per request unless provided;ensureBodAndDatescentralizes creation. - Parsing: Focused on extracting status and op-specific identifiers; raw parsed STAR tree still accessible via
result.parsed. - Status Handling: Treats
NO_MATCH(codes like 2 or 213) as non-error withsuccess=trueso callers can differentiate empty results from failures. - Validation: Enforces only practical minimum & enumerations; does not attempt full schema compliance.
- No automatic
fetchtransport fallback yet (axios chosen for reliability). You can replacepostSoapif providing a compatible function returning raw response text and error semantics. - No built-in XSD validator; XSDs present for reference only.