Skip to content

v0.2.0-rc.1

Pre-release
Pre-release

Choose a tag to compare

@samuelduchesne samuelduchesne released this 04 Sep 02:19
· 4 commits to main since this release
13fb45c

The first release candidate for 0.2.0, the release that unifies the JavaScript and Python APIs under one vocabulary and brings the Python library's validation, introspection and documentation helpers to JavaScript for the first time.

This is a prerelease, published under the next dist-tag. npm install @idfkit/core still gives you 0.1.0. Opt in explicitly:

npm install @idfkit/core@next @idfkit/schemas@next

It is breaking. Five renames, all mechanical, all named exports, so a bundler tells you exactly which one you missed rather than failing at run time.

Validation, introspection and documentation URLs

The largest addition, and the reason to try this RC. @idfkit/core now carries the checks the Python library has always had.

validateDocument(doc) and validateObject(obj, schema) check a model against its schema. Nothing throws: a finding is a record with a severity, the object it was found on, the field it concerns, a message, and a code. Match on code and never on message — the codes are shared verbatim with Python (E001 required field missing, E003 wrong type, E004 value not among the permitted values, E005 to E008 the four bounds, E009 dangling reference, E010 singleton violation, W002/W003 unknown type and unknown field), while the messages differ between the runtimes because number formatting and type names do.

Validation is on demand. Parsing never triggers it, so reading a file costs exactly what it did before.

validateObject takes its schema as an argument rather than reading it off a document, which means you can check an object while it is still detached — before it goes anywhere near your model:

import { validateObject } from '@idfkit/core';

const candidate = zone.clone();
candidate.set('ceiling_height', -1);

const findings = validateObject(candidate, doc.schema);
// [{ code: 'E005', field: 'ceiling_height',
//    message: 'Value -1 is below minimum 0', severity: 'error', ... }]

describeObjectType(schema, typeName) reports what a type declares: every field with its type, whether it is required, its default, its units, its permitted values and its bounds, plus whether the type carries a name and whether it has an extensible group. It is the schema read as a description rather than as storage, which is what a form generator or a field editor wants.

docsUrlForObject, ioReferenceUrl, engineeringReferenceUrl and searchUrl return links into the EnergyPlus documentation, so a tool can point at the reference instead of embedding it.

⚠️ Breaking changes

1. IDFDocument is now IdfDocument

The last exported type spelling the acronym in full caps, out of step with IdfObject, IdfCollection and IdfParseError.

// Before
import type { IDFDocument } from '@idfkit/core';
// After
import type { IdfDocument } from '@idfkit/core';

2. detectVersion() and detectEpJsonVersion() are now getIdfVersion() and getEpJsonVersion()

Python spells the same operation get_idf_version, and the shared naming register maps a Python get_* accessor to a TypeScript get* one. No alias is kept: a second public name for one concept is what the register exists to prevent.

// Before
const v = detectVersion(text);
// After
const v = getIdfVersion(text);

3. doc.collection(type) is no longer exported

Use all(), which returns the same collection and is the name the register carries.

// Before
const zones = doc.collection('Zone');
// After
const zones = doc.all('Zone');

4. The @idfkit/core/types subpath is gone

The generated per-version interfaces now ship as opt-in packages. This is why @idfkit/core fell from 6.7 MB unpacked to 286 KB: the two type maps were 5.3 MB of it and every reader paid for both whether or not they parameterised a document. They are types only, erased at build time, so runtime behaviour is identical either way.

// Before
import type { TypeMap } from '@idfkit/core/types';
// After  (npm install @idfkit/types-v26-1@next)
import type { TypeMap } from '@idfkit/types-v26-1';

5. SlimField.xmin, xmax and e were typed wrongly and are now honest

Only affects code reading SlimField directly. xmin and xmax were typed number but hold a boolean on EnergyPlus 8.9.0 through 9.5.0, where draft-04 makes the keyword a flag qualifying its sibling bound. Code trusting the old type computed value <= true and rejected everything at or below 1 in a positive-bounded field. e was typed string[] but holds numbers for the 68 fields carrying a numeric enum.

If you were evaluating bounds yourself, this is a good moment to stop and call validateObject instead. That case is exactly what it gets right and hand-rolled checks get wrong.

IdfCollection.insert(), delete() and rekey() are also no longer importable. All three were already tagged @internal, but no tsconfig set stripInternal, so they shipped as public by accident.

Fixes

  • Validation reads the constraints inside an anyOf field instead of discarding them. The bundle used to collapse an anyOf to its numeric branch and throw the string branch away, so nothing recorded which sentinel a field accepts — and 1,781 fields take Autocalculate rather than Autosize, while 646 accept any string at all. SlimField gains se, the string branch's enum verbatim. The bundle grows 5,416 bytes, 0.52%.

    The same change removes reference-check false positives from lists nothing can populate, ZoneList-expanded names, and implicit remainder spaces. Across the 760 EnergyPlus example files that is 19,793 spurious findings gone, and files that validate cleanly rise from 149 to 470.

  • Reading an unknown object type no longer modifies the document. doc.all('Zoen') used to store the new empty collection, permanently adding a junk key as a side effect of a read. Unknown names still return empty rather than throwing, matching Python.

  • A blank Name is no longer given an invented name. A type with an optional Name left blank keeps the blank as the epJSON key ""; the synthetic "<Type> N" key is reserved for types with no Name field at all. Previously a document round-tripped through epJSON came back with an object nobody had named. (#7)

Also new

  • CONFORMANCE_LEVEL, exported from @idfkit/core, names the cross-language conformance corpus level this release is checked against. It is not a version number and is not comparable to one: two installed libraries agree about the formats when they report the same level, whatever their own versions say.

  • The idfkit facade package is built and gated but not published in this RC. npm's similarity filter rejected the name and an appeal is pending, so npm install idfkit does not work yet. Keep using the scoped packages.

Upgrade notes

Nothing upgrades transparently — this RC is behind the next tag by design, so you have to ask for it.

Work through the five renames in order; they are all named exports, so tsc, rollup, esbuild and Vite each report the ones you missed:

"IDFDocument" is not exported by "node_modules/@idfkit/core/dist/index.js"

The one change that can pass a build and still be wrong is number 5, SlimField's corrected types. If you read xmin, xmax or e directly, review those call sites by hand.

Feedback on the validation API in particular is what this candidate is for. Report anything at idfkit-js/issues; the full change list is in CHANGELOG.md, and the work landed in #29.