@idfkit/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/collection.d.ts +48 -0
  4. package/dist/collection.d.ts.map +1 -0
  5. package/dist/collection.js +100 -0
  6. package/dist/collection.js.map +1 -0
  7. package/dist/document.d.ts +100 -0
  8. package/dist/document.d.ts.map +1 -0
  9. package/dist/document.js +0 -0
  10. package/dist/document.js.map +1 -0
  11. package/dist/index.d.ts +30 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +21 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/internal.d.ts +25 -0
  16. package/dist/internal.d.ts.map +1 -0
  17. package/dist/internal.js +25 -0
  18. package/dist/internal.js.map +1 -0
  19. package/dist/node.d.ts +40 -0
  20. package/dist/node.d.ts.map +1 -0
  21. package/dist/node.js +70 -0
  22. package/dist/node.js.map +1 -0
  23. package/dist/object.d.ts +114 -0
  24. package/dist/object.d.ts.map +1 -0
  25. package/dist/object.js +224 -0
  26. package/dist/object.js.map +1 -0
  27. package/dist/parse/epjson.d.ts +16 -0
  28. package/dist/parse/epjson.d.ts.map +1 -0
  29. package/dist/parse/epjson.js +91 -0
  30. package/dist/parse/epjson.js.map +1 -0
  31. package/dist/parse/idf.d.ts +44 -0
  32. package/dist/parse/idf.d.ts.map +1 -0
  33. package/dist/parse/idf.js +170 -0
  34. package/dist/parse/idf.js.map +1 -0
  35. package/dist/parse/lexer.d.ts +37 -0
  36. package/dist/parse/lexer.d.ts.map +1 -0
  37. package/dist/parse/lexer.js +105 -0
  38. package/dist/parse/lexer.js.map +1 -0
  39. package/dist/references.d.ts +56 -0
  40. package/dist/references.d.ts.map +1 -0
  41. package/dist/references.js +147 -0
  42. package/dist/references.js.map +1 -0
  43. package/dist/shape.d.ts +54 -0
  44. package/dist/shape.d.ts.map +1 -0
  45. package/dist/shape.js +114 -0
  46. package/dist/shape.js.map +1 -0
  47. package/dist/typemap.d.ts +43 -0
  48. package/dist/typemap.d.ts.map +1 -0
  49. package/dist/typemap.js +18 -0
  50. package/dist/typemap.js.map +1 -0
  51. package/dist/types/v26-1.d.ts +65896 -0
  52. package/dist/types/v26-1.d.ts.map +1 -0
  53. package/dist/types/v26-1.js +5 -0
  54. package/dist/types/v26-1.js.map +1 -0
  55. package/dist/types/v9-4.d.ts +61414 -0
  56. package/dist/types/v9-4.d.ts.map +1 -0
  57. package/dist/types/v9-4.js +5 -0
  58. package/dist/types/v9-4.js.map +1 -0
  59. package/dist/versions.d.ts +17 -0
  60. package/dist/versions.d.ts.map +1 -0
  61. package/dist/versions.js +34 -0
  62. package/dist/versions.js.map +1 -0
  63. package/dist/write/epjson.d.ts +15 -0
  64. package/dist/write/epjson.d.ts.map +1 -0
  65. package/dist/write/epjson.js +10 -0
  66. package/dist/write/epjson.js.map +1 -0
  67. package/dist/write/idf.d.ts +47 -0
  68. package/dist/write/idf.d.ts.map +1 -0
  69. package/dist/write/idf.js +149 -0
  70. package/dist/write/idf.js.map +1 -0
  71. package/package.json +55 -0
@@ -0,0 +1,170 @@
1
+ import { IDFDocument } from '../document.js';
2
+ import { lex } from './lexer.js';
3
+ /**
4
+ * Parse IDF text into a document.
5
+ *
6
+ * Synchronous and pure: text in, document out, no I/O. Everything that touches
7
+ * the filesystem or network lives at the edges (`@idfkit/core/node`), so this
8
+ * function behaves identically in Node, a browser, and a worker. The schema
9
+ * must already be loaded, which is the one genuinely async step.
10
+ */
11
+ export function parseIdf(text, schema, options = {}) {
12
+ const strict = options.strict ?? true;
13
+ const diagnostics = [];
14
+ const report = (diagnostic) => {
15
+ if (strict) {
16
+ throw new IdfParseError(diagnostic);
17
+ }
18
+ diagnostics.push(diagnostic);
19
+ options.onDiagnostic?.(diagnostic);
20
+ };
21
+ const raw = lex(text, { onDiagnostic: report });
22
+ const document = new IDFDocument(schema);
23
+ for (const object of raw) {
24
+ const canonical = schema.resolve(object.typeName);
25
+ if (canonical === undefined) {
26
+ report({
27
+ message: `Unknown object type "${object.typeName}" in EnergyPlus ${schema.version}`,
28
+ line: object.line,
29
+ typeName: object.typeName,
30
+ });
31
+ continue;
32
+ }
33
+ const definition = schema.require(canonical);
34
+ try {
35
+ const { name, values } = interpret(definition, object);
36
+ document.addRaw(canonical, definition.anon === 1 ? null : name, values);
37
+ }
38
+ catch (error) {
39
+ report({
40
+ message: error instanceof Error ? error.message : String(error),
41
+ line: object.line,
42
+ typeName: canonical,
43
+ });
44
+ }
45
+ }
46
+ return { document, diagnostics };
47
+ }
48
+ /** Map positional IDF values onto named schema fields. */
49
+ function interpret(definition, object) {
50
+ const order = definition.f;
51
+ const named = definition.anon !== 1 && order[0] === 'name';
52
+ const values = {};
53
+ let cursor = 0;
54
+ let name = '';
55
+ if (named) {
56
+ name = object.values[0]?.trim() ?? '';
57
+ if (name === '' && definition.nreq === 1) {
58
+ throw new Error(`${object.typeName} requires a name`);
59
+ }
60
+ cursor = 1;
61
+ }
62
+ const fixed = named ? order.slice(1) : order;
63
+ for (const field of fixed) {
64
+ const raw = object.values[cursor++];
65
+ if (raw === undefined)
66
+ break;
67
+ if (raw === '')
68
+ continue;
69
+ const coerced = coerce(definition, field, raw);
70
+ if (coerced !== undefined)
71
+ values[field] = coerced;
72
+ }
73
+ // Everything past the fixed fields belongs to the extensible section, read in
74
+ // repeats of the group width. A trailing partial group is kept rather than
75
+ // dropped: files in the wild are truncated, and losing data on a round-trip
76
+ // is worse than carrying an incomplete group.
77
+ const extensible = definition.x;
78
+ if (extensible !== undefined) {
79
+ const width = extensible.fields.length;
80
+ const groups = [];
81
+ let lastPopulated = -1;
82
+ while (cursor < object.values.length) {
83
+ const group = {};
84
+ let populated = false;
85
+ for (let offset = 0; offset < width; offset += 1) {
86
+ const raw = object.values[cursor++];
87
+ if (raw === undefined || raw === '')
88
+ continue;
89
+ const field = extensible.fields[offset];
90
+ const coerced = coerceExtensible(definition, field, raw);
91
+ if (coerced !== undefined) {
92
+ group[field] = coerced;
93
+ populated = true;
94
+ }
95
+ }
96
+ if (populated)
97
+ lastPopulated = groups.length;
98
+ groups.push(group);
99
+ }
100
+ // An all-blank repeat in the middle is kept, because the section is
101
+ // positional: dropping it pulls every later group down a slot, so a surface
102
+ // silently loses a vertex and its `number_of_vertices` stops matching.
103
+ // Trailing blanks are padding rather than data, and are dropped.
104
+ groups.length = lastPopulated + 1;
105
+ if (groups.length > 0)
106
+ values[extensible.key] = groups;
107
+ }
108
+ return { name, values };
109
+ }
110
+ function coerce(definition, field, raw) {
111
+ return coerceValue(definition.p[field]?.t, raw);
112
+ }
113
+ function coerceExtensible(definition, field, raw) {
114
+ const value = coerceValue(definition.x?.p[field]?.t, raw);
115
+ return Array.isArray(value) ? undefined : value;
116
+ }
117
+ /**
118
+ * Convert IDF text to a stored value.
119
+ *
120
+ * Numeric fields that hold `Autosize`, `Autocalculate`, or anything else
121
+ * non-numeric stay as strings. EnergyPlus accepts them and silently coercing
122
+ * to `NaN` would destroy the model on write.
123
+ */
124
+ function coerceValue(kind, raw) {
125
+ if (kind === 'n' || kind === 'i') {
126
+ const parsed = Number(raw);
127
+ if (!Number.isNaN(parsed) && raw.trim() !== '') {
128
+ const value = kind === 'i' ? Math.trunc(parsed) : parsed;
129
+ // Normalize -0 to 0. IDF files do contain `-0`, and JavaScript keeps the
130
+ // sign, so without this a value round-trips to a different (if equal)
131
+ // number and every strict comparison downstream reports a spurious diff.
132
+ return value === 0 ? 0 : value;
133
+ }
134
+ }
135
+ return raw;
136
+ }
137
+ export class IdfParseError extends Error {
138
+ line;
139
+ typeName;
140
+ constructor(diagnostic) {
141
+ super(`${diagnostic.message} (line ${diagnostic.line})`);
142
+ this.name = 'IdfParseError';
143
+ this.line = diagnostic.line;
144
+ this.typeName = diagnostic.typeName;
145
+ }
146
+ }
147
+ /**
148
+ * Read the version identifier from IDF text without a schema.
149
+ *
150
+ * Chicken-and-egg: choosing the schema requires knowing the version, and the
151
+ * version lives inside the file. This does the minimum scan needed to break
152
+ * the cycle, and does not validate anything else.
153
+ */
154
+ export function detectVersion(text) {
155
+ // Strip comments first. Trying to tolerate them inside the pattern means
156
+ // guessing how many comment lines sit between the comma and the value, which
157
+ // is exactly the kind of thing that works on the file you tested and fails on
158
+ // the next one.
159
+ const stripped = text.replace(/!.*$/gm, '');
160
+ // Anchored to a statement boundary so a field value containing the word
161
+ // "Version" cannot be mistaken for the object.
162
+ const match = /(?:^|;)\s*Version\s*,\s*([\d.]+)\s*;/i.exec(stripped);
163
+ const raw = match?.[1];
164
+ if (raw === undefined)
165
+ return undefined;
166
+ const parts = raw.split('.').map((p) => Number(p) || 0);
167
+ // EnergyPlus writes `Version, 26.1;` but schemas are keyed `26.1.0`.
168
+ return `${parts[0] ?? 0}.${parts[1] ?? 0}.${parts[2] ?? 0}`;
169
+ }
170
+ //# sourceMappingURL=idf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idf.js","sourceRoot":"","sources":["../../src/parse/idf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /** A raw object as it appears in the file, before schema interpretation. */
2
+ export interface RawObject {
3
+ /** Type name exactly as written, e.g. `BuildingSurface:Detailed`. */
4
+ typeName: string;
5
+ /** Comma-separated values after the type name, trimmed, comments stripped. */
6
+ values: string[];
7
+ /** 1-based line where the object starts, for diagnostics. */
8
+ line: number;
9
+ }
10
+ export interface LexDiagnostic {
11
+ message: string;
12
+ line: number;
13
+ }
14
+ export interface LexOptions {
15
+ /** Report a problem instead of throwing. */
16
+ onDiagnostic?: (diagnostic: LexDiagnostic) => void;
17
+ }
18
+ /**
19
+ * Split IDF text into raw objects.
20
+ *
21
+ * A hand-written character scan rather than a regex. The Python library matches
22
+ * objects with a `(?:[^;!]*(?:![^\n]*\n)?)*?` inner loop; that is a nested
23
+ * quantifier, so it backtracks badly on malformed input and cannot report where
24
+ * the problem was. A scanner is about the same amount of code, is linear in the
25
+ * input, and always knows its line number.
26
+ *
27
+ * The grammar is small:
28
+ * - `!` starts a comment running to end of line
29
+ * - `,` separates fields
30
+ * - `;` terminates an object
31
+ * - everything else is field text, trimmed
32
+ *
33
+ * There are no string literals and no escape sequences, so a comma cannot occur
34
+ * inside a field value. Real files depend on that.
35
+ */
36
+ export declare function lex(text: string, options?: LexOptions): RawObject[];
37
+ //# sourceMappingURL=lexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/parse/lexer.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;CACpD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,SAAS,EAAE,CAkGvE"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Split IDF text into raw objects.
3
+ *
4
+ * A hand-written character scan rather than a regex. The Python library matches
5
+ * objects with a `(?:[^;!]*(?:![^\n]*\n)?)*?` inner loop; that is a nested
6
+ * quantifier, so it backtracks badly on malformed input and cannot report where
7
+ * the problem was. A scanner is about the same amount of code, is linear in the
8
+ * input, and always knows its line number.
9
+ *
10
+ * The grammar is small:
11
+ * - `!` starts a comment running to end of line
12
+ * - `,` separates fields
13
+ * - `;` terminates an object
14
+ * - everything else is field text, trimmed
15
+ *
16
+ * There are no string literals and no escape sequences, so a comma cannot occur
17
+ * inside a field value. Real files depend on that.
18
+ */
19
+ export function lex(text, options = {}) {
20
+ const objects = [];
21
+ const report = options.onDiagnostic;
22
+ const length = text.length;
23
+ /** Field text pieces, split whenever a comment interrupts a field. */
24
+ let chunks = [];
25
+ /** Fields of the object being read; index 0 ends up being the type name. */
26
+ let values = [];
27
+ let index = 0;
28
+ let line = 1;
29
+ let fieldStart = 0;
30
+ let objectLine = 1;
31
+ let objectStarted = false;
32
+ const endField = (end) => {
33
+ chunks.push(text.slice(fieldStart, end));
34
+ const value = chunks.join('').trim();
35
+ chunks = [];
36
+ return value;
37
+ };
38
+ while (index < length) {
39
+ const char = text[index];
40
+ if (char === '!') {
41
+ // Preserve any field text seen before the comment, then resume after the
42
+ // newline. This is what lets `Zone1, !- Name` work: the comment is not
43
+ // part of the value, but the value is not finished either.
44
+ chunks.push(text.slice(fieldStart, index));
45
+ const newline = text.indexOf('\n', index);
46
+ if (newline === -1) {
47
+ index = length;
48
+ fieldStart = length;
49
+ break;
50
+ }
51
+ index = newline + 1;
52
+ fieldStart = index;
53
+ line += 1;
54
+ if (!objectStarted && chunks.join('').trim() === '') {
55
+ chunks = [];
56
+ objectLine = line;
57
+ }
58
+ continue;
59
+ }
60
+ if (char === ',') {
61
+ values.push(endField(index));
62
+ objectStarted = true;
63
+ index += 1;
64
+ fieldStart = index;
65
+ continue;
66
+ }
67
+ if (char === ';') {
68
+ values.push(endField(index));
69
+ index += 1;
70
+ fieldStart = index;
71
+ const typeName = values.shift() ?? '';
72
+ if (typeName === '') {
73
+ report?.({ message: 'Object with no type name', line: objectLine });
74
+ }
75
+ else {
76
+ objects.push({ typeName, values, line: objectLine });
77
+ }
78
+ values = [];
79
+ objectStarted = false;
80
+ objectLine = line;
81
+ continue;
82
+ }
83
+ if (char === '\n') {
84
+ line += 1;
85
+ if (!objectStarted &&
86
+ chunks.join('').trim() === '' &&
87
+ text.slice(fieldStart, index).trim() === '') {
88
+ // Blank line before any object content: keep the start line current.
89
+ chunks = [];
90
+ fieldStart = index + 1;
91
+ objectLine = line;
92
+ }
93
+ }
94
+ index += 1;
95
+ }
96
+ const trailing = (chunks.join('') + text.slice(fieldStart, length)).trim();
97
+ if (trailing !== '' || values.length > 0) {
98
+ report?.({
99
+ message: `Unterminated object near "${trailing.slice(0, 40) || values[0]}" (missing ";")`,
100
+ line: objectLine,
101
+ });
102
+ }
103
+ return objects;
104
+ }
105
+ //# sourceMappingURL=lexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexer.js","sourceRoot":"","sources":["../../src/parse/lexer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { IdfObject } from './object.js';
2
+ /** One field of one object pointing at a name. */
3
+ export interface ReferenceEdge {
4
+ readonly from: IdfObject;
5
+ readonly field: string;
6
+ readonly target: string;
7
+ /**
8
+ * Repeat index, when the field lives inside an extensible group. Absent for
9
+ * ordinary positional fields, which is what distinguishes the two.
10
+ */
11
+ readonly index?: number;
12
+ }
13
+ /**
14
+ * Live index of every name-to-name reference in a document.
15
+ *
16
+ * Kept current by the document as objects are added, removed, renamed, and
17
+ * edited, so `referencing()` is a lookup rather than a scan. EnergyPlus models
18
+ * are dense with references (every surface names a zone and a construction,
19
+ * every construction names materials), and the rename-propagation behaviour
20
+ * that makes the library useful depends on this being exact.
21
+ *
22
+ * Names are matched case-insensitively, because EnergyPlus resolves them that
23
+ * way, but the original casing is preserved for round-tripping.
24
+ */
25
+ export declare class ReferenceGraph {
26
+ #private;
27
+ /** Record that `obj.field` points at `target`. */
28
+ add(obj: IdfObject, field: string, target: string, index?: number): void;
29
+ /** Index every reference field of an object at once. */
30
+ addObject(obj: IdfObject): void;
31
+ /** Drop every edge originating from an object. */
32
+ removeObject(obj: IdfObject): void;
33
+ /** Update the edge for a single field after its value changed. */
34
+ updateField(obj: IdfObject, field: string, previous: unknown, next: unknown): void;
35
+ /** Edges pointing at a name. */
36
+ referencing(name: string): ReferenceEdge[];
37
+ /** Objects that reference a name, deduplicated. */
38
+ referencingObjects(name: string): IdfObject[];
39
+ /** Names an object points at. */
40
+ referencedBy(obj: IdfObject): string[];
41
+ /** Whether anything points at this name. */
42
+ isReferenced(name: string): boolean;
43
+ /**
44
+ * Rewrite every edge pointing at `previous` to point at `next`.
45
+ *
46
+ * Only updates the index. The document is responsible for writing the new
47
+ * value into the referencing objects' fields, which it does without going
48
+ * back through the setter hook to avoid re-entering this method.
49
+ */
50
+ retarget(previous: string, next: string): ReferenceEdge[];
51
+ /** Edges whose target does not exist. `valid` holds lowercased names. */
52
+ dangling(valid: ReadonlySet<string>): ReferenceEdge[];
53
+ clear(): void;
54
+ get size(): number;
55
+ }
56
+ //# sourceMappingURL=references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;;IAMzB,kDAAkD;IAClD,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAoBxE,wDAAwD;IACxD,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAM/B,kDAAkD;IAClD,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAalC,kEAAkE;IAClE,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAqBlF,gCAAgC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE;IAK1C,mDAAmD;IACnD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IAM7C,iCAAiC;IACjC,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,EAAE;IAKtC,4CAA4C;IAC5C,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE;IAiBzD,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,aAAa,EAAE;IASrD,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI,MAAM,CAIjB;CACF"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Live index of every name-to-name reference in a document.
3
+ *
4
+ * Kept current by the document as objects are added, removed, renamed, and
5
+ * edited, so `referencing()` is a lookup rather than a scan. EnergyPlus models
6
+ * are dense with references (every surface names a zone and a construction,
7
+ * every construction names materials), and the rename-propagation behaviour
8
+ * that makes the library useful depends on this being exact.
9
+ *
10
+ * Names are matched case-insensitively, because EnergyPlus resolves them that
11
+ * way, but the original casing is preserved for round-tripping.
12
+ */
13
+ export class ReferenceGraph {
14
+ /** lowercased target name -> edges pointing at it. */
15
+ #incoming = new Map();
16
+ /** object -> edges originating from it. */
17
+ #outgoing = new Map();
18
+ /** Record that `obj.field` points at `target`. */
19
+ add(obj, field, target, index) {
20
+ if (target === '')
21
+ return;
22
+ const edge = Object.freeze({ from: obj, field, target, index });
23
+ const key = target.toLowerCase();
24
+ let incoming = this.#incoming.get(key);
25
+ if (incoming === undefined) {
26
+ incoming = new Set();
27
+ this.#incoming.set(key, incoming);
28
+ }
29
+ incoming.add(edge);
30
+ let outgoing = this.#outgoing.get(obj);
31
+ if (outgoing === undefined) {
32
+ outgoing = new Set();
33
+ this.#outgoing.set(obj, outgoing);
34
+ }
35
+ outgoing.add(edge);
36
+ }
37
+ /** Index every reference field of an object at once. */
38
+ addObject(obj) {
39
+ for (const { field, target, index } of obj.outgoingReferences()) {
40
+ this.add(obj, field, target, index);
41
+ }
42
+ }
43
+ /** Drop every edge originating from an object. */
44
+ removeObject(obj) {
45
+ const outgoing = this.#outgoing.get(obj);
46
+ if (outgoing === undefined)
47
+ return;
48
+ for (const edge of outgoing) {
49
+ const key = edge.target.toLowerCase();
50
+ const incoming = this.#incoming.get(key);
51
+ if (incoming === undefined)
52
+ continue;
53
+ incoming.delete(edge);
54
+ if (incoming.size === 0)
55
+ this.#incoming.delete(key);
56
+ }
57
+ this.#outgoing.delete(obj);
58
+ }
59
+ /** Update the edge for a single field after its value changed. */
60
+ updateField(obj, field, previous, next) {
61
+ if (typeof previous === 'string' && previous !== '') {
62
+ const key = previous.toLowerCase();
63
+ const incoming = this.#incoming.get(key);
64
+ const outgoing = this.#outgoing.get(obj);
65
+ if (incoming !== undefined && outgoing !== undefined) {
66
+ for (const edge of outgoing) {
67
+ // `index !== undefined` marks an edge inside an extensible group.
68
+ // Those are re-indexed wholesale, so a positional-field update must
69
+ // not consume one that happens to share the field name.
70
+ if (edge.field !== field || edge.index !== undefined)
71
+ continue;
72
+ outgoing.delete(edge);
73
+ incoming.delete(edge);
74
+ break;
75
+ }
76
+ if (incoming.size === 0)
77
+ this.#incoming.delete(key);
78
+ }
79
+ }
80
+ if (typeof next === 'string' && next !== '')
81
+ this.add(obj, field, next);
82
+ }
83
+ /** Edges pointing at a name. */
84
+ referencing(name) {
85
+ const edges = this.#incoming.get(name.toLowerCase());
86
+ return edges === undefined ? [] : [...edges];
87
+ }
88
+ /** Objects that reference a name, deduplicated. */
89
+ referencingObjects(name) {
90
+ const seen = new Set();
91
+ for (const edge of this.referencing(name))
92
+ seen.add(edge.from);
93
+ return [...seen];
94
+ }
95
+ /** Names an object points at. */
96
+ referencedBy(obj) {
97
+ const edges = this.#outgoing.get(obj);
98
+ return edges === undefined ? [] : [...new Set([...edges].map((e) => e.target))];
99
+ }
100
+ /** Whether anything points at this name. */
101
+ isReferenced(name) {
102
+ return (this.#incoming.get(name.toLowerCase())?.size ?? 0) > 0;
103
+ }
104
+ /**
105
+ * Rewrite every edge pointing at `previous` to point at `next`.
106
+ *
107
+ * Only updates the index. The document is responsible for writing the new
108
+ * value into the referencing objects' fields, which it does without going
109
+ * back through the setter hook to avoid re-entering this method.
110
+ */
111
+ retarget(previous, next) {
112
+ const key = previous.toLowerCase();
113
+ const edges = this.#incoming.get(key);
114
+ if (edges === undefined)
115
+ return [];
116
+ const affected = [...edges];
117
+ this.#incoming.delete(key);
118
+ for (const edge of affected) {
119
+ this.#outgoing.get(edge.from)?.delete(edge);
120
+ }
121
+ for (const edge of affected) {
122
+ this.add(edge.from, edge.field, next, edge.index);
123
+ }
124
+ return affected;
125
+ }
126
+ /** Edges whose target does not exist. `valid` holds lowercased names. */
127
+ dangling(valid) {
128
+ const out = [];
129
+ for (const [key, edges] of this.#incoming) {
130
+ if (valid.has(key))
131
+ continue;
132
+ out.push(...edges);
133
+ }
134
+ return out;
135
+ }
136
+ clear() {
137
+ this.#incoming.clear();
138
+ this.#outgoing.clear();
139
+ }
140
+ get size() {
141
+ let total = 0;
142
+ for (const edges of this.#incoming.values())
143
+ total += edges.size;
144
+ return total;
145
+ }
146
+ }
147
+ //# sourceMappingURL=references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.js","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { SlimType } from '@idfkit/schemas';
2
+ import type { IdfObject } from './object.js';
3
+ /**
4
+ * Per-object-type prototype carrying real accessors for every field.
5
+ *
6
+ * The Python library resolves `zone.ceiling_height` through `__getattr__`. The
7
+ * mechanical translation of that is a `Proxy`, which we deliberately do not use:
8
+ * proxies defeat V8's inline caches, and more importantly they are invisible to
9
+ * TypeScript, so nothing would autocomplete. Instead each object type gets one
10
+ * prototype with `Object.defineProperty` accessors, built once and shared by
11
+ * every instance of that type. Property access is then an ordinary monomorphic
12
+ * lookup, and the generated `.d.ts` interfaces describe it statically.
13
+ *
14
+ * Shapes are keyed by the schema definition object rather than by type name.
15
+ * Because the schema bundle is content-addressed, `Zone` in 9.4.0 and `Zone` in
16
+ * 26.1.0 are the same frozen definition, so they share one shape and one
17
+ * prototype. Cross-version documents stay monomorphic for free.
18
+ */
19
+ export declare class ObjectShape {
20
+ readonly typeName: string;
21
+ readonly type: SlimType;
22
+ readonly proto: object;
23
+ /** Field names in IDF positional order, excluding the name field. */
24
+ readonly fields: readonly string[];
25
+ /** Fields that point into a reference list, i.e. foreign keys. */
26
+ readonly refFields: readonly string[];
27
+ /**
28
+ * Fields *inside* the extensible group that point into a reference list.
29
+ *
30
+ * Kept separate from `refFields` because these live in `type.x.fields`, not
31
+ * the positional field list, and so need the repeat index to address them.
32
+ * Ignoring them is not cosmetic: `ZoneList`, `Branch`, and the supply/return
33
+ * paths carry all of their references here, so leaving them out of the graph
34
+ * makes `rename()` silently produce a broken model.
35
+ */
36
+ readonly extensibleRefFields: readonly string[];
37
+ /** Fields whose value declares a name other objects may reference. */
38
+ readonly keyFields: readonly string[];
39
+ /** Extensible array key (`vertices`), if this type has one. */
40
+ readonly extensibleKey: string | undefined;
41
+ /** Whether the object carries a name (most do; `Version` does not). */
42
+ readonly named: boolean;
43
+ constructor(typeName: string, type: SlimType, base: object);
44
+ }
45
+ /**
46
+ * Get (or build) the shape for an object type.
47
+ *
48
+ * `base` is `IdfObject.prototype`, threaded in rather than imported to keep
49
+ * this module free of a cycle with `object.ts`.
50
+ */
51
+ export declare function shapeFor(typeName: string, type: SlimType, base: object): ObjectShape;
52
+ /** Number of distinct shapes built, for tests and diagnostics. */
53
+ export declare function shapeOf(obj: IdfObject): ObjectShape;
54
+ //# sourceMappingURL=shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../src/shape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,aAAa,CAAC;AAE1D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM;CA4B3D;AA0BD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAYpF;AAED,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,WAAW,CAEnD"}