A lightweight, zero-dependency TypeScript/JavaScript library for parsing Telcordia SR-4731 Optical Time Domain Reflectometer (
.sor) binary files.
- β‘ Fast & Lightweight: Zero runtime dependencies.
- π¦ Dual Module Format: Works seamlessly in ESM and CommonJS environments (Node.js & Browsers).
- π§© Object-Oriented Design: Encapsulated
SorParserandSorDatamodels. - π JSON Export: Export parsed OTDR traces directly to JSON strings using
.toJson(). - π οΈ Fully Typed: Written in TypeScript with complete type definitions included.
npm install @diolan12/js-otdror with yarn / pnpm / bun:
yarn add @diolan12/js-otdr
# or
pnpm add @diolan12/js-otdr
# or
bun add @diolan12/js-otdrYou can also use @diolan12/js-otdr directly in the browser without a bundler via jsDelivr or unpkg:
<script src="https://cdn.jsdelivr.net/npm/@diolan12/js-otdr/dist/index.global.js"></script>
<script>
// Access via global JsOtdr namespace
const parser = new JsOtdr.SorParser(arrayBuffer);
const sorData = parser.parse();
console.log(sorData.toJson(true));
</script><script type="module">
import { SorParser } from 'https://cdn.jsdelivr.net/npm/@diolan12/js-otdr/+esm';
const parser = new SorParser(arrayBuffer);
const sorData = parser.parse();
</script>Pass an ArrayBuffer to SorParser to extract structured OTDR metadata:
import { SorParser } from 'js-otdr';
import * as fs from 'node:fs';
// Read SOR binary file in Node.js
const fileBuffer = fs.readFileSync('trace.sor');
const arrayBuffer = fileBuffer.buffer.slice(
fileBuffer.byteOffset,
fileBuffer.byteOffset + fileBuffer.byteLength
);
// Instantiate parser and parse
const parser = new SorParser(arrayBuffer);
const sorData = parser.parse();
console.log(`Cable ID: ${sorData.cableId}`);
console.log(`Wavelength: ${sorData.wavelengthNm} nm`);
console.log(`Events Found: ${sorData.events.length}`);Convert parsed OTDR data into a JSON string directly from SorParser or SorData:
import { SorParser } from 'js-otdr';
const parser = new SorParser(arrayBuffer);
// Format with 2-space indentation
const prettyJson = parser.toJson(true);
console.log(prettyJson);
// Compact JSON string
const compactJson = parser.toJson(false);You can also call .toJson() on an existing SorData instance:
const sorData = parser.parse();
const jsonOutput = sorData.toJson(true);Build an OTDR-style events table β a launch row, a fiber-section row between consecutive events, and the last event rendered as end-of-fiber β ready to feed straight into a UI table:
const sorData = parser.parse();
for (const row of sorData.getEventsTable()) {
console.log(row.type, row.distanceMeters, row.sectionLengthMeters, row.lossDb, row.reflectanceDb);
}| # | distance | section length | type | loss | reflectance |
|---|---|---|---|---|---|
| 0 | 0.000 km | β | launch |
β | β |
| β | β | 16.066 km | fiber-section |
β | β |
| 1 | 16.066 km | β | splice |
0.193 dB | β |
| β | β | 4.895 km | fiber-section |
β | β |
| 2 | 20.961 km | β | splice |
1.967 dB | β |
| β¦ | |||||
| 5 | 39.490 km | β | end-of-fiber |
β | -49.298 dB |
Each row is a SorEventsTableRow:
interface SorEventsTableRow {
eventNumber: number | null; // null for fiber-section rows, 0 for the launch row
type: 'launch' | 'fiber-section' | 'splice' | 'connector' | 'saturated' | 'end-of-fiber' | 'unknown';
distanceMeters: number | null; // null for fiber-section rows
sectionLengthMeters: number | null; // only set on fiber-section rows
lossDb: number | null;
reflectanceDb: number | null;
}class SorParser {
constructor(buffer: ArrayBuffer)
// Parses the buffer and returns a SorData instance
public parse(): SorData
// Conveniently converts parsed SOR data directly into JSON
public toJson(pretty?: boolean): string
}class SorData implements SorMetadata {
cableId: string;
fiberId: string;
wavelengthNm: number;
pulseWidthNs: number;
rangeMeters: number;
refractiveIndex: number;
endToEndLossDb: number;
opticalReturnLossDb: number;
events: SorEvent[];
dataPoints: number[];
// Formats data as JSON string
public toJson(pretty?: boolean): string;
// Returns plain JavaScript object clone
public toObject(): SorMetadata;
// Builds an OTDR-style events table (launch / fiber sections / events / end-of-fiber)
public getEventsTable(): SorEventsTableRow[];
}We use Vitest for unit testing and tsup for TypeScript bundling.
# Run unit test suite once
npm test
# Run tests in watch mode during development
npm run test:watchtests/
βββ fixtures/
β βββ Core-47.sor # Sample .sor binary test files
βββ parser.test.ts # SorParser and SorData test suite
When contributing new features or parsing capabilities, add corresponding unit test cases in tests/parser.test.ts:
import { describe, it, expect } from 'vitest';
import { SorParser } from '../src/parser';
describe('My New Feature', () => {
it('should parse custom SOR attributes', () => {
// Write test expectations here
});
});To bundle ESM (dist/index.js), CommonJS (dist/index.cjs), and declaration files (dist/index.d.ts):
npm run buildContributions are welcome! Please follow these steps:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/my-feature). - Write clean, readable TypeScript code following OOP principles.
- Add unit tests for your changes and verify with
npm test. - Open a Pull Request detailing your changes.
This project is licensed under the MIT License.