import type { SlimField, SlimType } from '@idfkit/schemas'; import { DATA, KEY, NAME, ORIGIN, OWNER, SHAPE, SOURCE } from './internal.js'; import { type ObjectShape } from './shape.js'; /** A scalar field value. `undefined` means the field is absent. */ export type FieldValue = string | number | undefined; /** One repeat of an extensible group, e.g. a single vertex. */ export type ExtensibleGroup = Record; /** Anything storable in a field slot. */ export type StoredValue = string | number | ExtensibleGroup[]; /** Field values accepted when constructing or updating an object. */ export type FieldValues = Record; /** * Something that wants to know when an object changes. * * Implemented by `IdfDocument`. Declared as an interface so a detached object * has no dependency on the document at all. */ export interface ObjectOwner { onFieldChanged(obj: IdfObject, field: string, previous: unknown, next: unknown): void; onNameChanged(obj: IdfObject, previous: string, next: string): void; /** * Whether an in-place edit to an extensible repeat has to be heard. * * True only while the document carries a retained source, because that is the only time there is * a touched record to maintain. Hearing it costs an accessor on every field of every repeat, and * an accessor read is far dearer than a plain one over the vertices of a real model. */ tracksExtensibleEdits(): boolean; } /** * 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. */ export declare class IdfObject { readonly [DATA]: Record; readonly [SHAPE]: ObjectShape; [OWNER]: ObjectOwner | undefined; [NAME]: string; [KEY]: string; /** Index into the document's preserved anchors, or `undefined` once anything has changed this. */ [SOURCE]: number | undefined; [ORIGIN]: number | undefined; /** * Objects are built through `IdfObject.create`, never `new`, because each * instance's prototype depends on its object type. */ private constructor(); static create(typeName: string, type: SlimType, name: string, values?: FieldValues): IdfObject; /** Canonical object type name, e.g. `BuildingSurface:Detailed`. */ get typeName(): string; /** Schema definition for this object's type. */ get schema(): SlimType; /** * The object's name, i.e. its key within its collection. * * Assigning propagates through the document: the collection is re-keyed and * every field elsewhere in the model that referenced the old name is updated. */ get name(): string; set name(value: string); /** * The object's slot in its collection. * * Equals `name` for ordinary objects. Objects with no name, or with a blank * name, get a synthetic key so they can still be stored and addressed. */ get key(): string; /** Whether this object type carries a name at all. */ get isNamed(): boolean; /** Read a field by name. Untyped escape hatch for version-generic code. */ get(field: string): StoredValue | undefined; /** Write a field by name, going through the same hooks as property access. */ set(field: string, value: StoredValue | null | undefined): void; /** Apply several fields at once. */ update(values: FieldValues): this; /** Whether the schema defines this field for this object type. */ hasField(field: string): boolean; /** Schema definition for one field. */ fieldSchema(field: string): SlimField | undefined; /** Field names in IDF positional order, excluding the name. */ get fieldNames(): readonly string[]; /** Field names that are actually set on this object. */ setFieldNames(): string[]; /** * Repeat groups of the extensible section, e.g. the vertices of a surface. * * Returns a live array: pushing to it mutates the object, and so does writing a field of one of * its repeats. Both now tell the document, which they did not before: the array handed back was * the object's own data, so a pushed vertex reached the object without passing any accessor and * a preserving write discarded the edit in a file that loads. * * It is still an array to everything that reads it. `Array.isArray` is true, indexing and * iteration are unchanged, `map` and `filter` hand back plain arrays, and a repeat spreads and * compares exactly as the plain object it replaces. * * **Only when the document carries a retained source.** A repeat's fields are own accessors, and * an accessor read is roughly thirty times the cost of a plain property read; over the 33,000 * vertices of a real model that is measurable, and charging every vertex read is exactly what * rejecting a `Proxy` was meant to avoid. A document read without preservation has no touched * record to maintain, so it keeps the plain array and today's speed. A document read WITH * preservation is being edited, and an edit that reaches the file matters more there than the * throughput of reading a coordinate. * * The wrapper is built once and kept, so a read of a preserving document costs one `instanceof` * after the first. * * One spelling is not heard: replacing a whole repeat by index, `obj.extensible[0] = {...}`, * writes through the array's own index slot, which cannot be caught without charging every * vertex read. Writing the repeat's fields and `splice` both are heard. See `extensible.ts`. */ get extensible(): ExtensibleGroup[]; /** * Names this object's fields point at, paired with the field holding them. * * Extensible groups are included, with `index` naming the repeat the value * sits in. Half the references in a real model live there. */ outgoingReferences(): Array<{ field: string; target: string; index?: number; }>; /** * Names this object contributes to the model's reference lists. * * Usually just `name`, but anonymous types like `FluidProperties:Name` carry * their identity in an ordinary field instead, and other objects reference * that value. Treating those as nameless makes every pointer at them look * dangling. */ declaredNames(): string[]; /** Detached deep copy, optionally renamed. Not attached to any document. */ clone(name?: string): IdfObject; /** * Plain epJSON body: field values only, without the name. * * Choice values are emitted in the casing the schema declares, whatever casing the field was * written or set in. This is the one place the two formats want different answers and the * reason canonicalisation lives here rather than at parse: IDF matches a choice * case-insensitively, so `writeIdf` keeps the author's `CounterClockWise` and the file still * runs, while the epJSON reader matches the JSON Schema `enum` exactly and fatals on anything * else. Canonicalising on the way in would repair the object notation by rewriting the text * format's output, which is not this library's to rewrite. * * The rule is `ConvertInputFormat`'s own, taken from `IdfParser::parse_value`: a value is * matched against the field's choice list case-insensitively and replaced by the member it * matched. Note what that code does NOT consult, since a reader will look for it here: the * schema's `retaincase` (`SlimField.rc`) does not exempt a choice field, and the seven fields * across the bundled versions that carry both are canonicalised like any other. `retaincase` * governs the values EnergyPlus's own reader would otherwise upper-case, and this library * upper-cases nothing. */ toJSON(): Record; toString(): string; } //# sourceMappingURL=object.d.ts.map