-
Notifications
You must be signed in to change notification settings - Fork 0
Document.Working with SLEF
@elite-dangerous-almanac/core / Working with SLEF
SLEF — the Ship Loadout Export Format — is how Inara, EDSY, Coriolis and the rest pass
builds around. A SLEF payload is an array of entries, each a header naming the
producer and a data half that is a journal Loadout event.
import { parseSlef } from '@elite-dangerous-almanac/core/ships/slef';
declare const slefJsonString: string;
const [entry] = parseSlef(slefJsonString);
entry?.data.Ship; // the hull symbol
entry?.header.appName; // which tool wrote itparseSlef accepts a JSON string or an already-parsed value, and it also accepts a bare
Loadout event — a journal line pasted straight in is a valid input, not a special case.
To go from a payload to something you can ask questions of, hand it to ShipLoadout instead:
import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
declare const slefJsonString: string;
const build = ShipLoadout.fromSlef(slefJsonString); // first entry by default
const second = ShipLoadout.fromSlef(slefJsonString, 1); // or pick oneThey differ in what a bad entry costs you.
import { inspectSlef, parseSlef } from '@elite-dangerous-almanac/core/ships/slef';
declare const mixed: string; // some entries good, some malformed
parseSlef(mixed); // throws TypeError — the whole payload is rejected
const seen = inspectSlef(mixed);
seen.entries; // the entries that did parse
seen.diagnostics; // one per rejected entry, with its index and a stable codeUse parseSlef when the payload is yours and a malformed entry is a bug you want to
hear about. Use inspectSlef when you are importing a file a user handed you and would
rather show them which of their five builds failed than reject all five.
Inspection validates the SLEF structure, not catalogue support. A structurally valid
entry whose Ship is absent from the hull catalogue remains in entries, but
ShipLoadout.fromSlef rejects it when selected. Catch that TypeError when converting
each inspected entry into a build.
Neither survives input that is not JSON. Both call JSON.parse on a string first,
so a truncated or non-JSON file throws SyntaxError from both — catch that separately.
import { inspectSlef } from '@elite-dangerous-almanac/core/ships/slef';
declare const bytes: string;
try {
inspectSlef(bytes);
} catch (error) {
if (error instanceof SyntaxError) {
// not JSON at all
}
}import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
declare const build: ShipLoadout;
build.toSlefString({ header: { appName: 'MyApp', appVersion: '1.0.0' } });
build.toSlef({ header: { appName: 'MyApp', appVersion: '1.0.0' } }); // the object form
build.toLoadoutEvent(); // just the journal eventThe header is required, and naming your own app in it is the point: a downstream reader
needs to know which tool produced the build. If you re-export a build you imported, say
so — the SLEF specification expects the exporting application to identify itself, and
this repository's data/ships/SOURCES.md records the same requirement for captures it
redistributes.
This is the part most consumers get wrong, so the library keeps the two apart.
Everything the library computes is catalogue retail — a property of the fit. What a
capture states it paid is provenance about that capture: it carries station discounts,
it can price only part of the build, and two producers do not even agree on whether
HullValue includes the hull's stock fittings.
import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
declare const build: ShipLoadout;
build.toLoadoutEvent(); // retail: hull cost plus every module's list price
build.toLoadoutEvent({ credits: 'source' }); // the capture's figures, less what was narrowedThe captured figures live on a read-only record that no edit changes:
import type { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
import { getSourceModuleValue } from '@elite-dangerous-almanac/core/ships/source-purchase';
declare const build: ShipLoadout;
const paid = build.sourcePurchase; // null for a build you assembled yourself
paid?.hullValue; // -> 37472252 as the capture stated it
paid && getSourceModuleValue(paid, 'FrameShiftDrive')?.value; // -> 4976355
paid && getSourceModuleValue(paid, 'ShipCockpit'); // -> null — unpriced is not "free"Each captured figure stays pinned to the article it was paid for, so losing an article
narrows the source export rather than staling it. Swap or remove a module and it
exports unpriced, taking ModulesValue and Rebuy with it; engineer a module or fill an
empty mount and both still stand. HullValue always stands, because it names no slot to
narrow.
Import normalization narrows it the same way before you have edited anything, and on one
more ground than an edit: a module the catalogue cannot resolve, one a fixed mount cannot
hold, and a core internal the capture named no module for are all discarded or stocked
from the hull defaults, which leaves that slot unpriced and drops the two totals. Filling
an empty mount yourself leaves them standing because you can see the change; this one you
did not make. Free articles are the exception: a bulkhead stocked from absence, and a
cargo hatch the capture left unpriced or priced at zero, leave the totals alone.
build.importOutcomes says which — see
Reading a player journal.
One limit worth knowing: what a capture never priced, it also never explains — so losing an unpriced module, to a removal or a replacement, cannot be detected. LoadoutExportOptions.credits records that and the other boundary cases.
Frontier writes FrameShiftDrive; Inara writes frameshiftdrive, as the SLEF
specification's own example does. Both name the same mount, and lookups are
case-insensitive in both directions. What a build already carries is never rewritten, so
re-exporting an import returns the producer's own spelling untouched.
Guides
astro
Classes (1)
ProceduralSystem
Properties
Accessors
Methods
Interfaces (16)
Type Aliases (3)
Variables (11)
Functions (37)
- absoluteBoxelToBoxelCode
- boxelCodeToAbsoluteBoxel
- boxelCodeToLetters
- boxelEdgeLy
- boxelInternalSize
- canonicalizeSectorName
- canonicalizeSystemName
- decodeModSystemAddress
- decodeSystemAddress
- encodeModSystemAddress
- encodeSystemAddress
- findHandAuthoredRegionAt
- formatSystemName
- getCodexRegion
- getCodexRegionByName
- getHandAuthoredRegionOrigin
- getNebulaByName
- isPermitLockedRegionName
- isPermitLockedSystemName
- isProceduralSystemName
- lettersToBoxelCode
- massCodeToSizeClass
- nearestNebulae
- nebulaeWithin
- parseSystemName
- permitLockedRegionForSystemName
- permitLockedSystemForAddress
- permitLockedSystemForName
- permitLockForSystemName
- resolveNamingRegionOrigin
- sectorGridPositionFromGalacticPosition
- sectorGridPositionFromName
- sectorNameFromGalacticPosition
- sectorNameFromGridPosition
- sizeClassToMassCode
- toSystemAddress
- tryToSystemAddress
commodities
Interfaces (1)
Type Aliases (1)
Variables (3)
Functions (3)
equipment
Interfaces (6)
Type Aliases (8)
Variables (3)
Functions (10)
Subpath modules (2)
i18n
Interfaces (1)
Type Aliases (1)
Functions (17)
- getBlueprintName
- getCalculationIssueMessage
- getEngineeringGroupName
- getExperimentalEffectDescription
- getExperimentalEffectName
- getLoadoutEditErrorMessage
- getLoadoutIssueMessage
- getLoadoutSlotName
- getMaterialName
- getMicroResourceName
- getModuleName
- getOutfittingFamilyName
- getPreEngineeredVariantName
- getShipManufacturer
- getShipName
- getSlefDiagnosticMessage
- getSlotRestrictionLabel
materials
Enumerations (2)
Interfaces (2)
Type Aliases (2)
Variables (9)
ships
Classes (3)
BuildMetrics
Methods
- armourMetrics()
- buildCost()
- buildMass()
- cellBanks()
- distributorMetrics()
- distributorMetricsResult()
- frameShiftDrive()
- frameShiftDriveMassFactor()
- fuelPerJump()
- heatMetrics()
- heatMetricsResult()
- jumpRange()
- jumpRangeSummary()
- ladenJumpRange()
- loadout()
- maxJumpRange()
- mobilityCapacitorMetrics()
- mobilityCapacitorMetricsResult()
- mobilityMetrics()
- mobilityMetricsResult()
- powerBudget()
- shieldCapacitorMetrics()
- shieldCapacitorMetricsResult()
- shieldMetrics()
- shieldMetricsResult()
- shieldRecovery()
- shieldRecoveryResult()
- standardLoad()
- standardLoadResult()
- thrusters()
- totalRange()
- weaponMetrics()
- weaponsCapacitorMetrics()
- of()
LoadoutEditError
Constructors
Properties
Methods
ShipLoadout
Accessors
- cargoCapacity
- fuelCapacity
- hullValue
- importOutcomes
- modulesValue
- rebuy
- shipIdent
- shipName
- shipSymbol
- sourcePurchase
- unladenMass
Methods
- applyBlueprint()
- availableBlueprints()
- availableExperimentalEffects()
- clearEngineering()
- completeEngineeringGrade()
- fittedModuleAt()
- fittedModules()
- modulesForSlot()
- removeModule()
- repairFixedMount()
- setExperimentalEffect()
- setModule()
- setModuleEnabled()
- setModulePriority()
- setPreEngineeredVariant()
- slots()
- toLoadoutEvent()
- toSlef()
- toSlefString()
- validation()
- default()
- empty()
- fromLoadout()
- fromSlef()
Interfaces (125)
- AmmunitionCapacity
- AmmunitionStats
- ApplyBlueprintOptions
- ArmourInput
- ArmourMetrics
- AvailableBlueprint
- Blueprint
- BlueprintFeature
- BlueprintGrade
- BlueprintModuleEngineering
- BuildCost
- BuildCredits
- BuildMass
- BuildSlotBase
- BuildWeaponMetrics
- BulkheadParams
- CalculationIssue
- CellBankInput
- CellBankMetrics
- CellBankSummary
- CoreBuildSlot
- CoreSlots
- DamageComponents
- DamageDistribution
- DamageResistanceParams
- DamageSplit
- DamageTypeValues
- DistributorCapacitorMetrics
- DistributorInput
- DistributorMetrics
- DistributorOptions
- DistributorPips
- EngineeringMaterial
- EngineeringModifier
- EngineeringNormalizationUnchanged
- EngineeringNormalizationUnsupported
- EngineeringNormalized
- EngineeringOptionGroup
- ExperimentalContribution
- ExperimentalEffect
- ExperimentalEffectUnchanged
- ExperimentalEffectUnsupported
- ExperimentalEffectUpdated
- FittedModule
- FittedWeaponMetrics
- FrameShiftDriveJumpStats
- FrameShiftDriveParams
- FuelCapacity
- GunsightPoint
- HardpointBuildSlot
- HardpointSlotSpec
- HeatInput
- HeatMetrics
- HeatState
- HeatWeapon
- HullReinforcementParams
- JumpOptions
- JumpRangeSummary
- LoadoutCalculationModule
- LoadoutEvent
- LoadoutExportOptions
- LoadoutIssue
- LoadoutMass
- LoadoutModule
- LoadoutValidation
- LoadoutValidationInput
- MassCurveStats
- MobilityCapacitorInput
- MobilityCapacitorMetrics
- MobilityCapacitorOptions
- MobilityInput
- MobilityMetrics
- ModuleLimitEntry
- ModuleLimitIncrease
- ModuleLimitUsage
- ModuleReinforcementParams
- OptionalBuildSlot
- OptionalSlotSpec
- OutfittingModule
- OutfittingModuleIdentity
- OutfittingModuleStats
- ParsedSlot
- PowerBand
- PowerBudget
- PowerConsumer
- PowerConsumerResult
- PowerDistributorStats
- PowerGenerationStats
- PreEngineeredModifier
- PreEngineeredVariant
- ProjectileRangeBoundaries
- ShieldBoosterParams
- ShieldCapacitorInput
- ShieldCapacitorMetrics
- ShieldCapacitorOptions
- ShieldGeneratorParams
- ShieldInput
- ShieldMetrics
- ShieldRecovery
- ShieldRecoveryInput
- ShieldRecoveryOptions
- ShieldRegenerationStats
- Ship
- ShipSlots
- SimpleBuildSlot
- SlefDiagnostic
- SlefEntry
- SlefExportOptions
- SlefHeader
- SlefInspection
- SlefStringifyOptions
- SourceModuleValue
- SourcePurchaseRecord
- StandardLoadInputs
- ThrusterCurveParams
- ThrusterParams
- TotalRangeDetails
- ValidationModule
- WeaponDamageStats
- WeaponMetrics
- WeaponsCapacitorInput
- WeaponsCapacitorMetrics
- WeaponsOptions
- WeaponStats
- WeaponTotals
Type Aliases (44)
- BlueprintGrades
- BuildSlot
- CalculationIssueReason
- CalculationResult
- CoreSlotType
- DamageResistances
- DamageType
- EngineeringGroupId
- EngineeringNormalizationCode
- EngineeringNormalizationResult
- ExperimentalEffectMutationCode
- ExperimentalEffectMutationResult
- FixedMountRepairResult
- GunsightOffset
- HardpointRestriction
- ImmovableReason
- LoadoutEditErrorCode
- LoadoutImportOutcome
- LoadoutIssueCode
- LoadoutIssueParam
- LoadoutIssueParams
- LoadoutSlot
- ModifierMethod
- ModuleCategory
- ModuleEngineering
- ModuleExclusionGroup
- ModuleFitConstraint
- ModuleGuidance
- ModuleLimitGroup
- ModuleMount
- ModuleRating
- ModuleSlot
- OptionalRestriction
- OutfittingFamilyId
- PreEngineeredAcquisition
- ShipGunsight
- ShipGunsightCatalogue
- Slef
- SlefConstraint
- SlefDiagnosticCode
- SlotKind
- SlotRestriction
- StandardLoad
- ThrusterLoad
Variables (10)
Functions (85)
- ammunitionCapacity
- armourMetrics
- armourPiercingFactor
- calculateCargoCapacity
- calculateFuelCapacity
- calculateModuleLimits
- calculateUnladenMass
- cellBankSummary
- combinedRateOfFire
- computeModifiers
- damageFalloff
- damagePerSecond
- distributorMetrics
- effectiveHitPoints
- effectiveWeaponThermalLoad
- energyPerSecond
- enumerateSlots
- equilibriumHeatLevel
- frameShiftDriveMassFactor
- fuelPerJump
- getBlueprint
- getBlueprintGrade
- getBlueprintsForModule
- getBulkheadsForShip
- getEngineeringGroup
- getExperimentalEffect
- getExperimentalsForBlueprint
- getExperimentalsForModule
- getLoadoutModifier
- getModuleBySymbol
- getModulesByName
- getPreEngineeredJournalModifiers
- getPreEngineeredModifiers
- getPreEngineeredStats
- getPreEngineeredVariants
- getShipByName
- getShipBySymbol
- getShipGunsight
- getShipSlots
- getSourceModuleValue
- hasFrameShiftDriveJumpStats
- hasMassCurveStats
- hasPowerDistributorStats
- hasPowerGenerationStats
- hasShieldRegenerationStats
- hasWeaponDamageStats
- heatLevelAtTime
- heatMetrics
- heatPerSecond
- identifyPreEngineeredVariant
- inspectSlef
- isPreEngineered
- mapDamageTypes
- mobilityCapacitorMetrics
- mobilityMetrics
- parseSlef
- parseSlotName
- powerBudget
- projectGunsight
- resolveBlueprintForModule
- secondsToHeatLevel
- shieldCapacitorMetrics
- shieldMassCurveMultiplier
- shieldMetrics
- shieldRecovery
- shieldStrength
- singleJumpRange
- sourcePurchaseFromLoadout
- splitDamage
- stackArmourResistance
- stackShieldResistance
- stringifySlef
- sumMaterials
- sumSourceModuleValues
- sumWeaponMetrics
- sustainedDamagePerSecond
- sustainedFireFactor
- systemsResistance
- thrusterMassCurveMultiplier
- toSlef
- totalRange
- unresolvedModifiers
- validateLoadout
- weaponMetrics
- weaponsCapacitorMetrics