npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

milsymbol-sidc

v0.5.0

Published

Fluent TypeScript builder for MIL-STD-2525E / APP-6 numeric (20-character) SIDC strings, for use with milsymbol.

Readme

milsymbol-sidc

CI npm version npm downloads License: MIT TypeScript

A fluent TypeScript builder for numeric SIDC strings — 20 characters by default, with opt-in positions 21–23 (extended modifiers and frame shape) for MIL-STD-2525E and APP-6 symbols — made for constructing symbols with the milsymbol library.

import ms from "milsymbol";
import { Sidc, StandardIdentity, SymbolSet } from "milsymbol-sidc";

const sidc = new Sidc()
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandUnit)
  .toString(); // "13031000000000000000"

new ms.Symbol(sidc).asSVG(); // friendly land unit icon
  • Zero runtime dependencies — TypeScript types and digit-string logic only.
  • Typed field codes — every value is a named constant carrying its literal SIDC digits, so typos are compile errors.
  • Validated output — invalid values throw; inconsistent combinations warn (or throw in strict mode), using the same rules milsymbol applies when it parses a SIDC.
  • milsymbol-ready — every generated string is accepted by milsymbol's numeric parser. Full rendering depends on the entity and modifier codes you supply, exactly as it does for any raw SIDC.

Coverage: complete structural encoding for positions 1–20 of the numeric SIDC, plus opt-in positions 21–23 (extended modifiers and frame shape) that milsymbol consumes for 2525E/APP-6 E symbols. Positions 8–10 have named universal codes; positions 11–20 accept validated raw entity and modifier codes. A membership-only code catalog ships behind the milsymbol-sidc/catalogs subpath; semantic names remain future work.

Installation

Published on npm as milsymbol-sidc. Requires Node.js >= 18.

npm install milsymbol-sidc
# or
yarn add milsymbol-sidc
# or
pnpm add milsymbol-sidc

The rendering examples use milsymbol to draw the generated SIDC. It is not a dependency of this package — milsymbol-sidc has zero runtime dependencies — so install it alongside when you want to render symbols:

npm install milsymbol

The package ships ESM with bundled TypeScript declarations (dist/).

Quick start

import {
  Sidc,
  Context,
  Standard,
  StandardIdentity,
  SymbolSet,
  Status,
  Version,
} from "milsymbol-sidc";

// A hostile planned air missile track under MIL-STD-2525E:
const sidc = new Sidc()
  .version(Version.MilStd2525E) // pos 1-2 → "13"
  .context(Context.Reality) // pos 3   → "0"
  .identity(StandardIdentity.SuspectJoker) // pos 4   → "5"
  .symbolSet(SymbolSet.AirMissile) // pos 5-6 → "02"
  .status(Status.Planned) // pos 7   → "1"
  .toString();

console.log(sidc); // "13050210000000000000"

The builder is immutable: each setter returns a new instance, so a base configuration can be safely reused:

import { Sidc, Standard, StandardIdentity } from "milsymbol-sidc";

const base = new Sidc({ standard: Standard.App6 });

const friendly = base.identity(StandardIdentity.Friend).toString();
const hostile = base.identity(StandardIdentity.SuspectJoker).toString();
// base itself is unchanged; both derived SIDCs use APP-6 E (version "14")

Anatomy of the generated SIDC

13 0 3 10 0 2 16 123456 78 90 0 0 A
│  │ │ │  │ │ │  │      │  │ │ │ │
│  │ │ │  │ │ │  │      │  │ │ │ └   frame shape (23) — `A` = no frame
│  │ │ │  │ │ │  │      │  │ │ └ │   modifier 2 extension (22)
│  │ │ │  │ │ │  │      │  │ └ │ │   modifier 1 extension (21)
│  │ │ │  │ │ │  │      │  └ │ │ │   modifier 2 (19–20)
│  │ │ │  │ │ │  │      └  │ │ │ │   modifier 1 (17–18)
│  │ │ │  │ │ │  └      │  │ │ │ │   entity code (11–16)
│  │ │ │  │ │ └  │      │  │ │ │ │   amplifier (9–10)
│  │ │ │  │ └ │  │      │  │ │ │ │   HQ/task force/feint-dummy (8)
│  │ │ │  └ │ │  │      │  │ │ │ │   status (7)
│  │ │ └  │ │ │  │      │  │ │ │ │   symbol set (5–6)
│  │ └ │  │ │ │  │      │  │ │ │ │   standard identity (4)
│  └ │ │  │ │ │  │      │  │ │ │ │   context (3)
└  │ │ │  │ │ │  │      │  │ │ │ │   version / edition (1–2)

Positions 21–23 are emitted only when an extension field is set explicitly; otherwise toString() produces the classic 20-character SIDC.

| Position | Field | API | | -------- | ------------------------- | ------------------------------------------------------------ | | 1–2 | Version / edition | Version, version() | | 3 | Context | Context, context() | | 4 | Standard identity | StandardIdentity, identity() | | 5–6 | Symbol set | SymbolSet, symbolSet() | | 7 | Status / condition | Status, status() | | 8 | HQ/task force/feint-dummy | HqTaskForceDummy, hqTaskForceDummy() | | 9–10 | Amplifier | Amplifier, amplifier() | | 11–16 | Entity code | entity() — six raw digits | | 17–18 | Modifier 1 | modifier1() — two raw digits, or extendedModifier1() | | 19–20 | Modifier 2 | modifier2() — two raw digits, or extendedModifier2() | | 21 | Modifier 1 extension | extendedModifier1() — hundreds digit of a three-digit code | | 22 | Modifier 2 extension | extendedModifier2() — hundreds digit of a three-digit code | | 23 | Frame shape | frameShape() — 0–9, or A (no frame) | | 17–18 | Modifier 1 | modifier1() — two raw digits | | 19–20 | Modifier 2 | modifier2() — two raw digits |

API

new Sidc(options?)

Creates a builder preconfigured to 2525E / Reality / Unknown / Unknown set / Present. This remains the default for backward compatibility; configure Standard.App6 to default to APP-6 E instead.

| Option | Type | Default | Description | | ----------- | -------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | strict | boolean | false | Throw on invalid field combinations during toString() instead of warning. Invalid values always throw immediately regardless of this flag. | | standard | Standard | Not configured (2525E behavior) | Select a standard family. App6 defaults the version to APP-6 E ("14"); MilStd2525 defaults it to MIL-STD-2525E ("13"). Explicit configuration also checks that later version choices belong to the selected family. | | onWarning | (problem: SidcProblem) => void | console.warn | Receive non-fatal problems instead of writing to the console. strict: true still throws instead of reporting. |

Methods

All setters validate their argument and return a new immutable Sidc.

| Method | Field | Accepts | | ---------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | standard(s) | Version default + validation rules | A Standard constant. It retains a compatible current version; otherwise it selects that family's latest edition. | | version(v) | Positions 1–2 | A Version constant or any two-digit string (escape hatch for future editions) | | context(c) | Position 3 | A Context constant | | identity(i) | Position 4 | A StandardIdentity constant | | symbolSet(s) | Positions 5–6 | A SymbolSet constant or any two-digit string | | status(s) | Position 7 | A Status constant | | hqTaskForceDummy(v) | Position 8 | An HqTaskForceDummy constant | | amplifier(v) | Positions 9–10 | An Amplifier constant | | entity(v) | Positions 11–16 | Any six-digit string; symbol-set-specific catalogs are not included | | modifier1(v) | Positions 17–18 | Any two-digit string; resets the modifier-1 extension digit to 0 when a tail exists | | modifier2(v) | Positions 19–20 | Any two-digit string; resets the modifier-2 extension digit to 0 when a tail exists | | extendedModifier1(v) | Positions 17–18 + 21 | Any three-digit string; hundreds digit lands at position 21 | | extendedModifier2(v) | Positions 19–20 + 22 | Any three-digit string; hundreds digit lands at position 22 | | frameShape(v) | Position 23 | A FrameShape constant | | withoutExtension() | Positions 21–23 | Drops the tail, keeping the two-digit modifier slots (may change rendering) | | toString() | — | Validates combinations and renders the 20- or 23-character SIDC | | problems() | — | Structured list of non-fatal problems; never throws | | isValid() | — | true when problems() is empty | | with(fields) | — | Applies a partial field record immutably; validates like the setters | | clone() | — | Copies the fields into an independent builder | | equals(other) | — | Compares encoded fields, ignoring strict and standard | | toObject() / toJSON() | — | Plain, serializable snapshot of every encoded field | | Sidc.parse(s, options?) | — | Parses a 20- or 23-character numeric SIDC; throws when invalid | | Sidc.tryParse(s, options?) | — | Parses a SIDC or returns undefined |

Enum reference

Standard

Values match milsymbol's standard option and ms.setStandard() API, so the same constant can configure both libraries.

| Constant | Value | Standard family | | ------------ | -------- | --------------------------------------------------- | | MilStd2525 | "2525" | US MIL-STD-2525 (default behavior when omitted) | | App6 | "APP6" | NATO APP-6 |

HqTaskForceDummy

Position 8 values identify headquarters, task force, and feint/dummy variants.

| Constant | Code | Meaning | | --------------------------------- | ----- | ----------------------------------- | | None | "0" | None / not applicable | | FeintDummy | "1" | Feint/dummy | | Headquarters | "2" | Headquarters | | FeintDummyHeadquarters | "3" | Feint/dummy headquarters | | TaskForce | "4" | Task force | | FeintDummyTaskForce | "5" | Feint/dummy task force | | TaskForceHeadquarters | "6" | Task-force headquarters | | FeintDummyTaskForceHeadquarters | "7" | Feint/dummy task-force headquarters |

Amplifier

Position 9–10 values identify echelon, mobility, leadership, or auxiliary amplifiers. None writes the zero/no-amplifier code "00".

| Constant | Code | Meaning | | ---------------------------- | ------ | ------------------------------- | | None | "00" | None / not specified | | TeamCrew | "11" | Team/crew | | Squad | "12" | Squad | | Section | "13" | Section | | PlatoonDetachment | "14" | Platoon/detachment | | CompanyBatteryTroop | "15" | Company/battery/troop | | BattalionSquadron | "16" | Battalion/squadron | | RegimentGroup | "17" | Regiment/group | | Brigade | "18" | Brigade | | Division | "21" | Division | | CorpsMef | "22" | Corps/MEF | | Army | "23" | Army | | ArmyGroupFront | "24" | Army group/front | | RegionTheater | "25" | Region/theater | | Command | "26" | Command | | WheeledLimitedCrossCountry | "31" | Wheeled, limited cross-country | | WheeledCrossCountry | "32" | Wheeled, cross-country | | Tracked | "33" | Tracked | | WheeledTrackedCombination | "34" | Wheeled and tracked combination | | Towed | "35" | Towed | | Rail | "36" | Rail | | PackAnimals | "37" | Pack animals | | OverSnowPrimeMover | "41" | Over snow, prime mover | | Sled | "42" | Sled | | Barge | "51" | Barge | | Amphibious | "52" | Amphibious | | ShortTowedArray | "61" | Short towed array | | LongTowedArray | "62" | Long towed array | | LeaderIndividual | "71" | Leader individual | | DeputyIndividual | "72" | Deputy individual |

Entity and modifier setters intentionally accept raw digit strings so callers can use codes specific to their symbol set and edition. They validate width and ASCII digits but do not validate catalog membership.

FrameShape

Position 23, consumed by milsymbol for E-edition shape overrides (1–9 are ignored upstream unless the version is E; A applies on any edition). Selectors are named after the frame they select; NoFrame (A) is the only letter-valued code in the SIDC.

| Constant | Code | Meaning | | -------------------------- | ----- | -------------------------------------------------- | | Default | "0" | No override (implied when no extension is set) | | Space | "1" | Space frame | | Air | "2" | Air frame | | LandUnit | "3" | Land unit frame | | LandEquipmentSeaSurface | "4" | Land equipment / sea surface frame | | Installation | "5" | Installation frame | | LandDismountedIndividual | "6" | Dismounted individual frame | | SeaSubsurface | "7" | Sea subsurface frame | | ActivityEvent | "8" | Activity / event frame | | Cyberspace | "9" | Cyberspace frame | | NoFrame | "A" | Suppress the frame entirely |

Version

| Constant | Code | Standard | | ------------- | ------ | --------------------------- | | MilStd2525D | "10" | MIL-STD-2525D | | App6D | "11" | APP-6 D | | MilStd2525E | "13" | MIL-STD-2525E (default) | | App6E | "14" | APP-6 E |

Context

| Constant | Code | Meaning | | ------------ | ----- | ---------------------------------- | | Reality | "0" | Real-world operation (default) | | Exercise | "1" | Training/exercise | | Simulation | "2" | Simulation |

StandardIdentity

| Constant | Code | Frame drawn | | --------------- | ----- | ------------------------------------------------------------------ | | Pending | "0" | Unknown shape, dashed | | Unknown | "1" | Yellow octagonal frame (default) | | AssumedFriend | "2" | Blue frame, dashed | | Friend | "3" | Blue frame | | Neutral | "4" | Green frame | | SuspectJoker | "5" | Red frame, dashed — Suspect in reality, Joker in exercises | | HostileFaker | "6" | Red frame — Hostile in reality, Faker in exercises |

SymbolSet

Only sets that milsymbol can render are listed.

| Constant | Code | Domain | | ------------------------------- | ------ | ------------------------------------ | | Unknown | "00" | Unknown | | Air | "01" | Air tracks | | AirMissile | "02" | Air missiles | | Space | "05" | Space | | SpaceMissile | "06" | Space missiles | | LandUnit | "10" | Land units | | LandCivilianUnit | "11" | Land civilian units | | LandEquipment | "15" | Land equipment | | Installation | "20" | Installations | | ControlMeasure | "25" | Tactical graphics / control measures | | LandDismountedIndividual | "27" | Dismounted individuals | | SeaSurface | "30" | Sea surface tracks | | SeaSubsurface | "35" | Subsurface tracks | | MineWarfare | "36" | Sea mines | | Activity | "40" | Activities/events | | SignalsIntelligenceSpace | "50" | SIGINT space | | SignalsIntelligenceAir | "51" | SIGINT air | | SignalsIntelligenceLand | "52" | SIGINT land | | SignalsIntelligenceSeaSurface | "53" | SIGINT sea surface | | SignalsIntelligenceSubsurface | "54" | SIGINT subsurface | | Cyberspace | "60" | Cyberspace |

Codes "12" and "39" have no named constant but are recognized via the raw string escape hatch.

Status

| Constant | Code | Meaning | | ---------------- | ----- | ------------------------------------ | | Present | "0" | Present / actual (default) | | Planned | "1" | Planned / anticipated (dashed frame) | | FullyCapable | "2" | Condition bar: fully capable | | Damaged | "3" | Condition bar: damaged | | Destroyed | "4" | Condition bar: destroyed | | FullToCapacity | "5" | Condition bar: full to capacity |

Validation and error handling

Two layers of validation run at different times:

  1. Setter-time (SidcValidationError) — thrown immediately for malformed input (wrong number of digits) or unknown enum codes. Values must be strings: passing a number or other non-string value throws rather than being coerced. This catches bugs at the call site rather than deep inside rendering code.
  2. toString()-time combination checks — cross-field rules mirroring milsymbol's parser. By default problems are emitted with console.warn; with { strict: true } they throw SidcCombinationError.

Active combination rules:

  • Condition statuses (fully capable … full to capacity) do not apply to control measures.
  • Exercise symbols on the unknown symbol set lose their affiliation unless the identity is pending/unknown.
  • Symbol set 27 is unsupported in MIL-STD-2525D; symbol set 60 is unsupported in APP-6 D.
  • Non-default extension values (nonzero modifier extension digits or a frame shape other than Default) are reported for D-edition versions "10", "11", and "12". This is the library's support policy, not a milsymbol restriction: upstream consumes positions 21–22 and A on any edition, while shape selectors 1–9 only take effect for E editions. Explicit all-zero tails never warn.
  • When a Standard is explicitly configured, official version codes from the other standard family are reported.
  • Raw version/symbol-set codes outside milsymbol's known tables are reported.
import {
  Amplifier,
  Context,
  HqTaskForceDummy,
  Sidc,
  SidcCombinationError,
  SidcValidationError,
  Standard,
  StandardIdentity,
  Status,
  SymbolSet,
} from "milsymbol-sidc";

// Throws immediately: "9" is not a valid identity code.
new Sidc().identity("9" as never); // SidcValidationError

// Suspect/Joker and Hostile/Faker are dual-use: valid in reality AND exercise.
new Sidc({ strict: true })
  .identity(StandardIdentity.SuspectJoker) // Suspect in reality, Joker in exercises
  .symbolSet(SymbolSet.LandUnit)
  .toString(); // "13051000000000000000", no warnings

new Sidc({ strict: true })
  .context(Context.Exercise)
  .identity(StandardIdentity.HostileFaker) // Faker in exercises
  .symbolSet(SymbolSet.LandUnit)
  .toString(); // "13161000000000000000", no warnings

// A complete structural 20-position SIDC using raw entity/modifier codes.
new Sidc({ standard: Standard.App6, strict: true })
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandUnit)
  .hqTaskForceDummy(HqTaskForceDummy.Headquarters)
  .amplifier(Amplifier.BattalionSquadron)
  .entity("123456")
  .modifier1("01")
  .modifier2("09")
  .toString(); // "14031002161234560109"

// A real combination problem: condition status on a control measure.
new Sidc({ strict: true })
  .symbolSet(SymbolSet.ControlMeasure)
  .status(Status.Destroyed)
  .toString(); // SidcCombinationError

// Capturing warnings programmatically (e.g. in tests):
const warnings: string[] = [];
const originalWarn = console.warn;
console.warn = (message: string) => warnings.push(message);
try {
  new Sidc()
    .symbolSet(SymbolSet.ControlMeasure)
    .status(Status.Destroyed)
    .toString();
} finally {
  console.warn = originalWarn;
}

Error classes: SidcError (base) → SidcValidationError, SidcCombinationError.

Reading and reporting problems

console.warn is only the default. Inspect problems without side effects with problems() and isValid(), or route them anywhere with onWarning:

import { Sidc, SymbolSet, Status } from "milsymbol-sidc";

const sidc = new Sidc()
  .symbolSet(SymbolSet.ControlMeasure)
  .status(Status.Destroyed);

sidc.problems();
// [{ code: "condition-status-control-measure", message: "…" }]
sidc.isValid(); // false

new Sidc({ onWarning: (problem) => log.warn(problem.code) })
  .symbolSet(SymbolSet.ControlMeasure)
  .status(Status.Destroyed)
  .toString(); // no console output; onWarning is called once

Every problem carries a stable code and the exact legacy warning message. problems() never throws, even for builders created with { strict: true }; toString() still throws SidcCombinationError in strict mode.

Parsing a SIDC

Sidc.parse() turns a 20- or 23-character numeric SIDC back into a builder so you can validate, edit, and re-render existing codes. Sidc.tryParse() returns undefined instead of throwing.

import { Sidc } from "milsymbol-sidc";

const sidc = Sidc.parse("14031002161234560109");
sidc.toObject().entity; // "123456"

sidc.with({ modifier1: "02" }).toString(); // "14031002161234560209"
Sidc.tryParse("not-a-sidc"); // undefined

Round-tripping is guaranteed: Sidc.parse(s).toString() === s for any string this library produces, including explicit zero tails (…000 stays 23 characters, because tail presence is data). A 20-character SIDC and the same code padded with a zero tail render identically in milsymbol but compare as unequal builders, mirroring the different strings. Only the 20- and 23-character numeric forms are supported; letter-based SIDCs, 21/22-character input, and positions 24–30 are not.

Catalogs (optional)

The milsymbol-sidc/catalogs subpath exposes a membership catalog generated from milsymbol's numeric symbol data: which entity and modifier codes milsymbol registers for each symbol set. It is a separate entry point, so the core builder stays data-free unless you import it.

import { entityCodes, isKnownEntityCode } from "milsymbol-sidc/catalogs";

isKnownEntityCode("10", "121100"); // true — a registered land-unit entity
isKnownEntityCode("10", "999999"); // false
entityCodes("10").length; // number of registered land-unit codes

// Three-digit (extended) modifiers are exposed separately:
import {
  extendedModifier1Codes,
  isKnownExtendedModifier1Code,
} from "milsymbol-sidc/catalogs";

isKnownExtendedModifier1Code("10", "100"); // true — the common UAV modifier
isKnownExtendedModifier1Code("10", "199"); // false
extendedModifier1Codes("10").length; // registered three-digit modifier 1 codes

The catalog is membership only: it carries no semantic names and is never consulted by toString(), so raw entity and modifier values still encode without recognition warnings. Regenerate it with npm run generate:catalogs; the milsymbol version it was derived from is exported as CATALOG_SOURCE.

Using with milsymbol

milsymbol routes any SIDC whose first two characters are digits to its numeric parser, so codes from this package work without any configuration. The standard style option only selects NATO vs US styling details:

Node.js (ESM)

import ms from "milsymbol"; // bundles all numeric standards
import { Sidc, Standard, StandardIdentity, SymbolSet } from "milsymbol-sidc";

const standard = Standard.App6;
ms.setStandard(standard); // "APP6": use NATO styling

const sidc = new Sidc({ standard }) // defaults to APP-6 E (version "14")
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandUnit)
  .toString();

const symbol = new ms.Symbol(sidc, { size: 32 });
if (!symbol.isValid()) {
  console.warn(`milsymbol could not fully render ${sidc}`);
}
const svg = symbol.asSVG();

Node.js (CommonJS)

milsymbol-sidc is ESM-only. From CommonJS, load it with dynamic import(). milsymbol itself can be required directly:

const ms = require("milsymbol");

async function render() {
  const { Sidc, Standard, StandardIdentity, SymbolSet } =
    await import("milsymbol-sidc");

  const sidc = new Sidc({ standard: Standard.App6 })
    .identity(StandardIdentity.Friend)
    .symbolSet(SymbolSet.LandUnit)
    .toString();

  return new ms.Symbol(sidc, { size: 32 }).asSVG();
}

Node.js 20.19+ and 22.12+ can also require("milsymbol-sidc") directly via require(esm); earlier Node 18/20 releases must use import().

Browser

<script src="https://unpkg.com/milsymbol@3/dist/milsymbol.js"></script>
<script type="module">
  import {
    Sidc,
    Standard,
    StandardIdentity,
    SymbolSet,
  } from "https://unpkg.com/milsymbol-sidc/dist/src/index.js";

  const sidc = new Sidc({ standard: Standard.App6 })
    .identity(StandardIdentity.Neutral)
    .symbolSet(SymbolSet.SeaSurface)
    .toString();

  document.body.innerHTML = new ms.Symbol(sidc, {
    standard: Standard.App6,
  }).asSVG();
</script>

Recipes

import {
  Amplifier,
  Context,
  FrameShape,
  HqTaskForceDummy,
  Sidc,
  Standard,
  StandardIdentity,
  Status,
  SymbolSet,
  Version,
} from "milsymbol-sidc";

// Hostile planned armored unit
new Sidc()
  .identity(StandardIdentity.SuspectJoker)
  .symbolSet(SymbolSet.LandUnit)
  .status(Status.Planned)
  .toString(); // "13051010000000000000"

// Neutral damaged installation
new Sidc()
  .identity(StandardIdentity.Neutral)
  .symbolSet(SymbolSet.Installation)
  .status(Status.Damaged)
  .toString(); // "13042030000000000000"

// Friendly destroyed mine (mine warfare set)
new Sidc()
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.MineWarfare)
  .status(Status.Destroyed)
  .toString(); // "13033640000000000000"

// Exercise joker submarine under APP-6E
new Sidc({ standard: Standard.App6, strict: true })
  .context(Context.Exercise)
  .identity(StandardIdentity.SuspectJoker)
  .symbolSet(SymbolSet.SeaSubsurface)
  .toString(); // "14153500000000000000"

// APP-6 unit with structural entity and modifier fields
new Sidc({ standard: Standard.App6, strict: true })
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandUnit)
  .entity("123456")
  .modifier1("01")
  .modifier2("09")
  .toString(); // "14031000001234560109"

// 2525E UAV modifier (extended modifier 1 "100") plus the airborne modifier
new Sidc({ strict: true })
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandUnit)
  .entity("110000")
  .extendedModifier1("100")
  .extendedModifier2("100")
  .toString(); // "13031000001100000000110"

// Frame shape "A" suppresses the frame for E-edition symbols
new Sidc({ strict: true })
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandUnit)
  .frameShape(FrameShape.NoFrame)
  .toString(); // "1303100000000000000000A"

// APP-6 D configuration retains an explicitly selected compatible edition
new Sidc({ standard: Standard.App6, strict: true })
  .version(Version.App6D)
  .identity(StandardIdentity.Friend)
  .symbolSet(SymbolSet.LandDismountedIndividual)
  .toString(); // "11032700000000000000"

Development

npm install
npm test                  # compiles with tsc, then runs node --test against dist/
npm run build
npm run lint              # eslint
npm run format:check      # prettier
npm run generate:catalogs # regenerate src/catalogs.generated.ts from milsymbol

CI runs the test suite on Node 18/20/22/24, plus lint, formatting, markdownlint, coverage, and a check that the generated catalog is current.

Test coverage includes per-field offset encoding for every enum member, defaults, immutability, setter validation errors, extended-field offsets, all combination rules in both warn and strict modes, raw-code escape hatches, and error class hierarchy.

Roadmap

  • Semantic entity and modifier names, layered on the membership catalog.
  • Official positions 24–30 / Set C extension data (country codes and beyond). Positions 21–23 shipped: see extendedModifier1(), extendedModifier2(), and frameShape().

License

MIT