@idfkit/core¶
The portable surface: parsing, writing, and the object model. Every export here
is synchronous and takes strings, so the same code runs in Node, a browser, a
worker, and an edge runtime. File and network access lives in
@idfkit/core/node and SchemaBundle.
IdfCollection
¶
Name-indexed collection of objects of one type.
Iterable, so for (const zone of doc.all('Zone')) works, and array-like
enough that [...collection], .map, .filter read naturally. Lookup by
name is O(1) and case-insensitive, matching EnergyPlus semantics.
Insertion order is preserved. IDF files are hand-edited and diffed, so reordering objects on a round-trip would produce noisy diffs for no reason.
IdfDocument
¶
An EnergyPlus model.
Holds collections keyed by object type, a live reference graph, and the schema for one specific EnergyPlus version. Every document is bound to a version at construction; there is no version-agnostic mode, because field order and reference lists genuinely differ between releases.
The optional M parameter attaches a generated type map, which makes field
access statically checked without changing anything at runtime. See
typemap.ts.
schema
¶
references
¶
size
¶
version
¶
add
¶
addRaw
¶
all
¶
Every object of one type.
The type name is matched case-insensitively, because EnergyPlus matches it
that way: all('zone'), all('ZONE') and all('Zone') are one
collection, whatever casing the source file used. Every type-name-keyed
entry point resolves identically, so get, require, has and remove
cannot disagree with this one.
When the document carries a generated type map, the argument completes among that version's type names and the result is narrowed to the matching field interface. Unknown names still work and simply stay untyped, which is what version-generic code needs.
Why an unknown type name returns empty rather than throwing¶
all('Zoen') returns an empty collection. Throwing would catch the typo,
and it was weighed and rejected. has() must answer false rather than
throw, and an all() that throws where has() does not is two rules for
one question. The version-generic contract above depends on an unknown name
being answerable: code written against no particular release asks for types
that exist in some versions and not others, and that is not an error. And
idfkit, the Python library this API is unified with, cannot throw here at
all, because a document may carry no schema and then nothing distinguishes
a typo from a valid type; a rule that holds only when a schema happens to
be loaded is not one rule.
The typo is caught on the paths that write, which can afford to be strict:
add() throws for a type in no schema, so does attach(), and the IDF and
epJSON parsers reject one. To ask the question directly, call
document.schema.has(name).
What this must never do, and no longer does, is store the empty
collection it hands back. A read that mutates the document is wrong on its
own terms: probing five misspelled names used to leave five junk keys in
types(), visible to every later iteration and to toJSON. The collection
returned for an absent type is detached from the document.
idfkit resolves and declines to store on exactly the same terms, in
IDFDocument.__getitem__.
attach
¶
Attach an existing detached object, e.g. one produced by clone().
Repeats the checks addRaw makes rather than trusting the object. An
object carries its own schema definition, so one cloned out of a document
on a different EnergyPlus version would otherwise be written using that
version's field order under this document's Version header, which
mis-maps every field on reload instead of failing.
danglingReferences
¶
get
¶
has
¶
objects
¶
onFieldChanged
¶
onNameChanged
¶
remove
¶
rename
¶
require
¶
toJSON
¶
types
¶
IdfObject
¶
A single EnergyPlus object.
Field access is via real accessors installed on a per-type prototype, so
zone.ceiling_height is an ordinary property read that TypeScript can see
(given the generated interfaces) and V8 can inline. See shape.ts.
Field names are epJSON names (zone_name, outside_boundary_condition),
not the space-separated IDD names. That is a deliberate break from the Python
library's IDF-to-Python conversion: epJSON names are already valid JS
identifiers and valid TS interface keys, so using them directly means the
on-disk name, the runtime key, and the static type all agree.
ObjectShape
¶
Per-object-type prototype carrying real accessors for every field.
The Python library resolves zone.ceiling_height through __getattr__. The
mechanical translation of that is a Proxy, which we deliberately do not use:
proxies defeat V8's inline caches, and more importantly they are invisible to
TypeScript, so nothing would autocomplete. Instead each object type gets one
prototype with Object.defineProperty accessors, built once and shared by
every instance of that type. Property access is then an ordinary monomorphic
lookup, and the generated .d.ts interfaces describe it statically.
Shapes are keyed by the schema definition object rather than by type name.
Because the schema bundle is content-addressed, Zone in 9.4.0 and Zone in
26.1.0 are the same frozen definition, so they share one shape and one
prototype. Cross-version documents stay monomorphic for free.
extensibleKey
¶
Extensible array key (vertices), if this type has one.
extensibleRefFields
¶
Fields inside the extensible group that point into a reference list.
Kept separate from refFields because these live in type.x.fields, not
the positional field list, and so need the repeat index to address them.
Ignoring them is not cosmetic: ZoneList, Branch, and the supply/return
paths carry all of their references here, so leaving them out of the graph
makes rename() silently produce a broken model.
fields
¶
Field names in IDF positional order, excluding the name field.
keyFields
¶
Fields whose value declares a name other objects may reference.
named
¶
Whether the object carries a name (most do; Version does not).
proto
¶
refFields
¶
Fields that point into a reference list, i.e. foreign keys.
type
¶
typeName
¶
ReferenceGraph
¶
Live index of every name-to-name reference in a document.
Kept current by the document as objects are added, removed, renamed, and
edited, so referencing() is a lookup rather than a scan. EnergyPlus models
are dense with references (every surface names a zone and a construction,
every construction names materials), and the rename-propagation behaviour
that makes the library useful depends on this being exact.
Names are matched case-insensitively, because EnergyPlus resolves them that way, but the original casing is preserved for round-tripping.
Schema
¶
A single EnergyPlus version's schema, backed by a shared blob store.
Type definitions are hydrated lazily and cached in the store, so loading a second version only pays for the definitions that version does not already share with one in memory. In practice that is a couple hundred out of 858.
SchemaBundle
¶
Loads schemas from a bundle, sharing one blob store across every version.
Hold one of these for the lifetime of the process. Loading 26.1.0 and then 9.4.0 costs far less than twice one version, because most definitions are byte-identical and already hydrated.
BundleSource
¶
Where bundle files come from.
The only runtime-specific part of this package. Node reads from disk, the
browser fetches over HTTP, and a bundler-driven app can supply its own
resolver backed by import(). Everything above this interface is portable.
read
¶
DocsUrl
¶
FieldDescription
¶
Description of a single field in an EnergyPlus object type.
The field set mirrors Python's idfkit.introspection.FieldDescription
one-for-one, snake_case renamed to camelCase. Every member is always present,
so a description can be diffed against the Python dataclass key by key;
Python's str | None becomes string | undefined.
default
¶
enumValues
¶
Permitted values for a choice field.
Numbers rather than strings for the handful of fields that express a choice numerically, matching Python, which also hands back the raw JSON values.
exclusiveMaximum
¶
Exclusive maximum. Number or boolean, exactly as exclusiveMinimum.
exclusiveMinimum
¶
Exclusive minimum, as the schema for this version declares it.
true rather than a number on 8.9.0 through 9.5.0, whose draft-04 schemas
use the keyword as a flag qualifying minimum instead of as a bound. Python
reports the same raw value, so a caller comparing the two sides sees no
difference; a caller comparing a value against it must check the type first.
fieldType
¶
epJSON JSON-Schema type: "number", "string", "integer", "array", or
a pipe-delimited union in declaration order for heterogeneous anyOf
fields, e.g. "number|string" for Schedule:Compact's extensible field.
undefined when the field appears in the positional order but carries no
schema definition at all.
isReference
¶
Whether this field points into another object's reference list.
maximum
¶
minimum
¶
name
¶
Field name in epJSON spelling, e.g. x_origin.
note
¶
Field documentation note.
Always undefined here. The slim schema bundle deliberately drops note
(see the header of @idfkit/schemas's types.ts), and no other faithful
source for it exists in this package. It is kept in the type because the
naming register requires the same field set on both sides, and because
inventing a value from the field name would be worse than admitting the
absence.
objectList
¶
Reference list names this field points into.
required
¶
Whether the field is listed in the object's required array.
units
¶
SI units, e.g. "m", "W/m-K".
ObjectDescription
¶
Description of an EnergyPlus object type.
Field set mirrors Python's idfkit.introspection.ObjectDescription.
extensibleSize
¶
Number of fields in one extensible repeat group.
fields
¶
hasName
¶
Whether the type carries a name field. Version and friends do not.
isExtensible
¶
Whether the type has a repeating extensible group.
memo
¶
Object memo from the schema.
Always undefined here, for the same reason as FieldDescription.note:
the slim bundle drops memo, and this package has no faithful source for
it. See that member's note.
objType
¶
Canonical object type name, e.g. "Zone".
requiredFields
¶
Required field names, in schema order.
ObjectOwner
¶
ParseDiagnostic
¶
ParseOptions
¶
RawObject
¶
ReferenceEdge
¶
SchemaDelta
¶
SlimField
¶
auto
¶
Field is an anyOf of a numeric branch and a string branch, in that order.
Set for every one of the 13060 such fields across the 17 bundled versions.
The numeric branch's type, enum and bounds are hoisted onto this record;
the string branch survives as se.
d
¶
Schema default, applied on write when the field is absent.
e
¶
Permitted values for a choice field.
Numbers, not strings, on the 68 fields across the versions that express a
choice numerically (e: [1, 3] on Site:GroundDomain:Slab.phase). Compare
strings case-insensitively and numbers by value.
max
¶
min
¶
ol
¶
Names of reference lists this field points into (i.e. it is a foreign key).
rc
¶
Value is case-sensitive and must not be normalized.
ref
¶
Names of reference lists this field contributes to (i.e. it is a key).
se
¶
String literals the anyOf string branch accepts, verbatim from the schema.
Only meaningful together with auto. Absent while auto is set means the
string branch carries no enum at all and ANY string is legal there, which is
the shape of 646 fields including Schedule:Compact's extensible field.
That is why the empty string is kept here rather than filtered out the way
e filters it: se: [''] (the whole string branch of the 68 fields whose
number branch carries a numeric enum) and no se at all mean the opposite
of one another.
The sentinel is not a constant: 10565 fields take Autosize and 1781 take
Autocalculate, so a validator that accepts either everywhere accepts a
value EnergyPlus rejects.
t
¶
Storage class.
u
¶
SI units, used by the unit-conversion helpers.
xmax
¶
Exclusive maximum. Number or boolean, exactly as xmin.
xmin
¶
Exclusive minimum, in whichever JSON Schema dialect the version shipped.
A number is the bound itself (draft-06+, 9.6.0 onwards). The boolean true
qualifies the sibling min, making it exclusive (draft-04, 8.9.0 through
9.5.0). Measured across the bundled schemas: xmin is boolean 9013 times
in the older seven versions and numeric 13840 times in the newer ten;
xmax behaves identically, and no version mixes the two. Branch on the
value's type, never on the version.
SlimType
¶
anon
¶
Object has no name field at all, e.g. Version, GlobalGeometryRules.
f
¶
All field names in IDF positional order, from legacy_idd.fields.
g
¶
IDD group, e.g. Thermal Zones and Surfaces.
nref
¶
Reference lists the object's name contributes to.
nreq
¶
Object's name is required.
p
¶
Field definitions, keyed by epJSON field name.
r
¶
Required field names.
s
¶
Object is a singleton (maxProperties: 1), e.g. Version, Building.
x
¶
Extensible group definition, if the object has one.
ValidationError
¶
One validation finding.
code is the machine-readable part and is shared with the Python library
verbatim (E001…E010, W002, W003). message is for a human and is
not guaranteed identical across the two languages: number formatting and
type names differ between the runtimes. Match on code.
code
¶
Machine-readable code, stable across languages.
field
¶
Field the finding concerns, or undefined when it concerns the object.
message
¶
Human-readable description.
objName
¶
Object name the finding was found on. Empty for anonymous objects.
objType
¶
Object type the finding was found on.
severity
¶
How serious the finding is.
ValidationResult
¶
Everything one validation run found, split by severity.
isValid and totalIssues are computed once when the result is built rather
than being live properties, because the arrays are read-only.
WriteIdfOptions
¶
commentColumn
¶
Column the field-name comments are aligned to.
comments
¶
Emit !- Field Name comments after each field.
indent
¶
Indent for field lines.
versionFirst
¶
Write Version first regardless of insertion order. EnergyPlus does not
require it, but every tool in the ecosystem expects it and diffs are
cleaner when it is stable.
AnyTypeMap
¶
Base constraint: any map from object type name to its field interface.
EpJson
¶
epJSON document shape: type -> name -> field values.
ExtensibleGroup
¶
One repeat of an extensible group, e.g. a single vertex.
FieldValue
¶
A scalar field value. undefined means the field is absent.
FieldValues
¶
Field values accepted when constructing or updating an object.
ObjectOf
¶
Field interface for a type name, or an empty object for unknown names.
Severity
¶
Severity of a validation finding.
Both a value and a type. The value gives the Python original's
Severity.ERROR spelling; the type is the string union that a 'error'
literal satisfies, which is how the same three strings reach the wire in both
languages. The strings themselves are load-bearing: the conformance corpus
compares them across the two implementations.
StoredValue
¶
Anything storable in a field slot.
TypeNameOf
¶
Accepted type names for a map.
The string & {} arm is what keeps literal completion alive while still
accepting arbitrary strings: without it TypeScript widens the parameter to
string and the suggestions disappear.
UntypedMap
¶
A document with no version types attached.
ValuesOf
¶
Field values accepted when creating an object of a given type.
Deliberately not ObjectOf. For a known type name this is the exact field
interface, so TypeScript's excess-property check rejects a misspelled field
in an object literal. For anything else it widens to the permissive
FieldValues, which is what version-generic code and untyped documents need.
Using one type for both would force a choice between catching typos and
allowing dynamic field names; using two costs nothing and gives both.
CONFORMANCE_LEVEL
¶
The conformance corpus level this release is checked against, as an immutable tag in idfkit/idfkit-conformance. A release asserts this claim in its own checks (FR-024): the corpus at this tag passes against this library, or the release does not ship.
This is not a version number and it is not compared to one. Two installed libraries agree on the formats when they declare the same level, whatever their own versions say (FR-025).
Severity
¶
Severity of a validation finding.
Both a value and a type. The value gives the Python original's
Severity.ERROR spelling; the type is the string union that a 'error'
literal satisfies, which is how the same three strings reach the wire in both
languages. The strings themselves are load-bearing: the conformance corpus
compares them across the two implementations.