@ifc-lite/cli 0.20.0 → 0.21.0

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.
@@ -0,0 +1,324 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * `ifc-lite gym (--model <file.ifc> | --seed <n>) [--checks schema,clash,ids] [--ids <rules.xml>]`
6
+ *
7
+ * A reset/step/reward environment loop over the existing headless checks:
8
+ * the skeleton of an RLVR environment for buildings (docs/vision/moonshots-tech.md
9
+ * M2, docs/vision/moonshots-execution-plan.md B0.4). The environment wraps a
10
+ * model - either a fixed file (`--model`) or a procedurally generated World
11
+ * Gym episode (`--seed`, see `gym/episode.ts`) - and lets an agent apply
12
+ * data-mutation ops, scoring each step against the same schema/clash/ids
13
+ * checks the `validate`, `clash`, and `ids` commands already run.
14
+ * Geometry-creating ops are out of scope for v0, see "op vocabulary gaps"
15
+ * below.
16
+ *
17
+ * Protocol (newline-delimited JSON on stdout, one JSON command per stdin line):
18
+ *
19
+ * -> (on start) {"type":"reset","observation":{...},"channels":{...}}
20
+ * <- {"type":"step","ops":[{"op":"setProperty","expressId":42,"psetName":"Pset_WallCommon","propName":"IsExternal","value":true}]}
21
+ * -> {"type":"reward","channels":{...},"done":false}
22
+ * <- {"type":"reset"}
23
+ * -> {"type":"reset","observation":{...},"channels":{...}}
24
+ * <- {"type":"close"}
25
+ * -> (process exits 0, no reply line)
26
+ *
27
+ * Malformed input never crashes the process: it replies with a structured
28
+ * {"type":"error","message":"..."} line and keeps reading. A `step` batch is
29
+ * ATOMIC: it either fully applies (one `reward` line) or leaves the session
30
+ * exactly as it was (one `error` line) - a malformed op mid-batch never
31
+ * leaves earlier ops of the same batch applied.
32
+ *
33
+ * Episode factory (B2.2): instead of `--model`, `--seed <n>` generates a
34
+ * World Gym benchmark model in-process (tools/world-gym/generator.mjs,
35
+ * dynamically imported from the repo checkout - the published npm package
36
+ * does not ship the generator, so `--seed` fails there with a clear error
37
+ * while `--model` keeps working). `[--family frame|office|auto]` pins the
38
+ * family; corruption follows the benchmark's deterministic Bernoulli draw at
39
+ * the spec's corrupt rate (tools/world-gym/benchmark/splits.mjs) unless
40
+ * `--corrupt` / `--no-corrupt` forces it or `--corrupt-rate <p>` overrides
41
+ * the rate (forcing and a rate are mutually exclusive). Mid-session,
42
+ * `{"type":"reset","seed":8}` (plus optional "family"/"corrupt"/"corruptRate"
43
+ * fields) swaps to a fresh generated episode, so an RL consumer can stream
44
+ * the whole benchmark through one gym process without touching generator
45
+ * internals. Generated-episode reset lines carry an extra `episode` field:
46
+ * {seed, family, corrupted}. (The `corrupted` flag is deliberately exposed:
47
+ * the gym is the TRAINING surface; benchmark ground truth is regenerable
48
+ * from the seed anyway - see tools/world-gym/benchmark/BENCHMARK.md.)
49
+ *
50
+ * Determinism: the same model plus the same op sequence must yield
51
+ * byte-identical reward lines - see `gym/channels.ts` for the sorting and
52
+ * rounding contract. Reward shaping: every channel's `score` is in [0, 1]
53
+ * with higher-is-better (the clash channel scores 1 for clash-free and
54
+ * decreases as the clash count grows; the raw count is `totalClashes`).
55
+ *
56
+ * Mutation surface: v0 supports `setProperty` / `setAttribute` /
57
+ * `deleteProperty`, mirroring `bim.mutate`'s method names exactly (see
58
+ * `gym/ops.ts`). Ops are applied via `MutablePropertyView` + `StepExporter`
59
+ * (the same classes `ifc-lite mutate` uses) rather than through `bim.mutate`
60
+ * itself, because `HeadlessBackend.mutate` (packages/cli/src/headless-backend.ts)
61
+ * is currently a no-op stub: `bim.mutate.setProperty()` silently does
62
+ * nothing in headless mode. Wiring that backend up to the same
63
+ * MutablePropertyView this file drives is a mechanical follow-up, not a
64
+ * redesign, since the op vocabulary already matches.
65
+ *
66
+ * Each step re-exports the accumulated mutation overlay to STEP text and
67
+ * re-parses it into a fresh `IfcDataStore` before running checks. This is
68
+ * deliberate, not a perf shortcut we forgot to remove: `ids`/`clash`/the
69
+ * schema checks all read directly from an `IfcDataStore` (via
70
+ * `createDataAccessor(store)` / `EntityNode(store, id)`), with no
71
+ * mutation-overlay awareness, so a live overlay would silently be invisible
72
+ * to every check. Re-parsing after export guarantees the checks see exactly
73
+ * what a human re-opening the mutated file would see.
74
+ *
75
+ * Op vocabulary gaps (v0, tracked for a follow-up bet, not fixed here):
76
+ * - Ops target entities by `expressId`, not `GlobalId`. Stable within one
77
+ * gym session (StepExporter never renumbers existing entities), but a
78
+ * GlobalId-keyed op format would be more robust for agents that only
79
+ * ever see a model's IFC-standard identifiers.
80
+ * - `observation.bounds` is always `null`: no geometry pass runs on
81
+ * `reset`, and even the `clash` channel's mesh pass never feeds a
82
+ * bounding box back into the observation.
83
+ * - No entity-creation ops (new walls/slabs/etc. via `packages/create`) and
84
+ * no episode-termination signal (`done` is always `false`); both need a
85
+ * real reward-shaping design, not just wiring.
86
+ * - `bim.mutate.setProperty/setAttribute/deleteProperty` are no-ops on
87
+ * `HeadlessBackend` (see above); this file works around that directly
88
+ * rather than fixing the backend, to keep this bet's diff scoped to one
89
+ * new command.
90
+ */
91
+ import { createInterface } from 'node:readline';
92
+ import { readFile } from 'node:fs/promises';
93
+ import { basename } from 'node:path';
94
+ import { loadIfcFile, loadIfcBytes } from '../loader.js';
95
+ import { getFlag, fatal } from '../output.js';
96
+ import { extractPropertiesOnDemand } from '@ifc-lite/parser';
97
+ import { MutablePropertyView } from '@ifc-lite/mutations';
98
+ import { StepExporter } from '@ifc-lite/export';
99
+ import { GeometryProcessor } from '@ifc-lite/geometry';
100
+ import { IDSNamespace } from '@ifc-lite/sdk';
101
+ import { KNOWN_CHECKS, computeSchemaChannel, computeClashChannel, computeIdsChannel, computeObservation, } from './gym/channels.js';
102
+ import { parseOp, applyOp } from './gym/ops.js';
103
+ import { generateEpisode, parseSeed, parseCorruptRate, } from './gym/episode.js';
104
+ const USAGE = 'Usage: ifc-lite gym (--model <file.ifc> | --seed <n> [--family frame|office|auto] [--corrupt|--no-corrupt|--corrupt-rate <p>]) [--checks schema,clash,ids] [--ids <rules.xml>] [--locale en|de|fr]';
105
+ const SUPPORTED_LOCALES = ['en', 'de', 'fr'];
106
+ function parseChecks(raw, idsPath) {
107
+ if (raw === undefined) {
108
+ return idsPath ? new Set(['schema', 'clash', 'ids']) : new Set(['schema', 'clash']);
109
+ }
110
+ const result = new Set();
111
+ for (const part of raw.split(',').map(s => s.trim()).filter(Boolean)) {
112
+ if (!KNOWN_CHECKS.includes(part)) {
113
+ fatal(`Unknown check "${part}" in --checks (supported: ${KNOWN_CHECKS.join(', ')})`);
114
+ }
115
+ result.add(part);
116
+ }
117
+ return result;
118
+ }
119
+ function parseLocale(raw) {
120
+ if (raw === undefined)
121
+ return 'en';
122
+ if (!SUPPORTED_LOCALES.includes(raw)) {
123
+ fatal(`Unsupported --locale "${raw}" (supported: ${SUPPORTED_LOCALES.join(', ')})`);
124
+ }
125
+ return raw;
126
+ }
127
+ export async function gymCommand(args, io = {}) {
128
+ const modelPath = getFlag(args, '--model');
129
+ const seedFlag = getFlag(args, '--seed');
130
+ if (!modelPath && seedFlag === undefined)
131
+ fatal(USAGE);
132
+ if (modelPath && seedFlag !== undefined)
133
+ fatal(`--model and --seed are mutually exclusive\n${USAGE}`);
134
+ const idsPath = getFlag(args, '--ids');
135
+ const checks = parseChecks(getFlag(args, '--checks'), idsPath);
136
+ const locale = parseLocale(getFlag(args, '--locale'));
137
+ const output = io.output ?? process.stdout;
138
+ const input = io.input ?? process.stdin;
139
+ function send(msg) {
140
+ output.write(`${JSON.stringify(msg)}\n`);
141
+ }
142
+ let modelId;
143
+ let originalStore;
144
+ let episode = null;
145
+ async function loadEpisode(spec) {
146
+ const { model, episode: info } = await generateEpisode(spec);
147
+ originalStore = await loadIfcBytes(new TextEncoder().encode(model.content), `gym-seed-${info.seed}.ifc`);
148
+ episode = info;
149
+ modelId = `gym-seed-${info.seed}.ifc`;
150
+ }
151
+ if (modelPath) {
152
+ modelId = basename(modelPath);
153
+ originalStore = await loadIfcFile(modelPath);
154
+ }
155
+ else {
156
+ const forceCorrupt = args.includes('--corrupt') ? true : args.includes('--no-corrupt') ? false : undefined;
157
+ const corruptRateFlag = getFlag(args, '--corrupt-rate');
158
+ if (forceCorrupt !== undefined && corruptRateFlag !== undefined) {
159
+ fatal(`--corrupt/--no-corrupt and --corrupt-rate are mutually exclusive\n${USAGE}`);
160
+ }
161
+ await loadEpisode({
162
+ seed: parseSeed(seedFlag, '--seed'),
163
+ family: getFlag(args, '--family') ?? 'auto',
164
+ forceCorrupt,
165
+ corruptRate: corruptRateFlag !== undefined ? parseCorruptRate(corruptRateFlag, '--corrupt-rate') : undefined,
166
+ });
167
+ }
168
+ const ids = new IDSNamespace();
169
+ let idsDoc = null;
170
+ if (idsPath) {
171
+ const idsXml = await readFile(idsPath, 'utf-8');
172
+ idsDoc = await ids.parse(idsXml);
173
+ }
174
+ // Lazily initialised: wasm geometry init is only worth paying for when
175
+ // the "clash" channel is actually requested.
176
+ let processor = null;
177
+ async function getProcessor() {
178
+ if (!processor) {
179
+ // Assign only after init() succeeds: caching the instance before a
180
+ // failed init would hand every later call an uninitialized processor
181
+ // instead of retrying (the init throw itself surfaces as a structured
182
+ // error line on the step that requested the clash channel).
183
+ const p = new GeometryProcessor();
184
+ await p.init();
185
+ processor = p;
186
+ }
187
+ return processor;
188
+ }
189
+ function createMutationView() {
190
+ const view = new MutablePropertyView(null, 'default');
191
+ view.setOnDemandExtractor((entityId) => extractPropertiesOnDemand(originalStore, entityId));
192
+ return view;
193
+ }
194
+ /**
195
+ * The committed op journal: every op of every successfully rewarded step,
196
+ * in order. `step` batches are atomic - a failing batch (malformed op,
197
+ * export/parse failure, channel failure) must leave the session exactly as
198
+ * it was - so the journal is the single source of truth and the view is
199
+ * rebuilt from it whenever a batch fails partway through.
200
+ */
201
+ let journal = [];
202
+ let mutationView = createMutationView();
203
+ function rebuildViewFromJournal() {
204
+ mutationView = createMutationView();
205
+ for (const op of journal)
206
+ applyOp(mutationView, op);
207
+ }
208
+ /**
209
+ * Materialise the current mutation overlay into a fresh, independently
210
+ * parsed store. See the module doc for why re-export + re-parse is
211
+ * required rather than reading through the overlay directly.
212
+ */
213
+ async function materializeStore() {
214
+ const schema = (originalStore.schemaVersion ?? 'IFC4');
215
+ const exporter = new StepExporter(originalStore, mutationView);
216
+ const result = exporter.export({ schema, applyMutations: true });
217
+ return loadIfcBytes(result.content, modelId);
218
+ }
219
+ async function computeChannels(store) {
220
+ const channels = {};
221
+ // Fixed canonical key order regardless of --checks argument order, so
222
+ // two runs requesting the same set in a different order still produce
223
+ // byte-identical JSON.
224
+ if (checks.has('schema'))
225
+ channels.schema = computeSchemaChannel(store);
226
+ if (checks.has('clash'))
227
+ channels.clash = await computeClashChannel(store, await getProcessor(), modelId);
228
+ if (checks.has('ids'))
229
+ channels.ids = await computeIdsChannel(store, ids, idsDoc, locale);
230
+ return channels;
231
+ }
232
+ /** Apply a step batch atomically: on ANY failure, restore the pre-batch state. */
233
+ async function stepBatch(rawOps) {
234
+ // Phase 1: validate the whole batch before anything is applied.
235
+ const ops = rawOps.map(raw => parseOp(raw));
236
+ // Phase 2: apply + materialize + score; roll back to the journal on failure.
237
+ try {
238
+ for (const op of ops)
239
+ applyOp(mutationView, op);
240
+ const store = await materializeStore();
241
+ const channels = await computeChannels(store);
242
+ journal.push(...ops);
243
+ return channels;
244
+ }
245
+ catch (err) {
246
+ rebuildViewFromJournal();
247
+ throw err;
248
+ }
249
+ }
250
+ async function emitReset() {
251
+ journal = [];
252
+ mutationView = createMutationView();
253
+ const observation = computeObservation(originalStore);
254
+ const channels = await computeChannels(originalStore);
255
+ // `episode` only exists for generated episodes; --model resets keep the
256
+ // exact v0 payload shape for backward compatibility.
257
+ send(episode ? { type: 'reset', episode, observation, channels } : { type: 'reset', observation, channels });
258
+ }
259
+ await emitReset();
260
+ const rl = createInterface({ input, terminal: false, crlfDelay: Infinity });
261
+ // A broken pipe on stdout or a stdin failure must end the loop cleanly,
262
+ // not take the process down with an uncaught 'error' emitter event.
263
+ const onStreamError = () => {
264
+ rl.close();
265
+ };
266
+ input.on('error', onStreamError);
267
+ output.on('error', onStreamError);
268
+ for await (const line of rl) {
269
+ const trimmed = line.trim();
270
+ if (!trimmed)
271
+ continue;
272
+ let msg;
273
+ try {
274
+ msg = JSON.parse(trimmed);
275
+ }
276
+ catch (err) {
277
+ send({ type: 'error', message: `Malformed JSON: ${err.message}` });
278
+ continue;
279
+ }
280
+ const type = typeof msg === 'object' && msg !== null ? msg.type : undefined;
281
+ try {
282
+ if (type === 'step') {
283
+ const ops = msg.ops;
284
+ if (!Array.isArray(ops))
285
+ throw new Error('"step" command needs an "ops" array');
286
+ const channels = await stepBatch(ops);
287
+ send({ type: 'reward', channels, done: false });
288
+ }
289
+ else if (type === 'reset') {
290
+ const m = msg;
291
+ if (m.seed !== undefined) {
292
+ // New generated episode over the same protocol (episode factory).
293
+ if (m.family !== undefined && typeof m.family !== 'string') {
294
+ throw new Error('reset field "family" must be a string (frame|office|auto)');
295
+ }
296
+ if (m.corrupt !== undefined && typeof m.corrupt !== 'boolean') {
297
+ throw new Error('reset field "corrupt" must be a boolean');
298
+ }
299
+ if (m.corrupt !== undefined && m.corruptRate !== undefined) {
300
+ throw new Error('reset fields "corrupt" and "corruptRate" are mutually exclusive');
301
+ }
302
+ await loadEpisode({
303
+ seed: parseSeed(m.seed, 'reset field "seed"'),
304
+ family: m.family ?? 'auto',
305
+ forceCorrupt: m.corrupt,
306
+ corruptRate: m.corruptRate !== undefined ? parseCorruptRate(m.corruptRate, 'reset field "corruptRate"') : undefined,
307
+ });
308
+ }
309
+ await emitReset();
310
+ }
311
+ else if (type === 'close') {
312
+ rl.close();
313
+ return;
314
+ }
315
+ else {
316
+ send({ type: 'error', message: `Unknown command type: ${JSON.stringify(type ?? null)}` });
317
+ }
318
+ }
319
+ catch (err) {
320
+ send({ type: 'error', message: err.message });
321
+ }
322
+ }
323
+ }
324
+ //# sourceMappingURL=gym.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gym.js","sourceRoot":"","sources":["../../src/commands/gym.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,42 @@
1
+ import { type IfcDataStore } from '@ifc-lite/parser';
2
+ export interface ValidationIssue {
3
+ severity: 'error' | 'warning' | 'info';
4
+ rule: string;
5
+ message: string;
6
+ /** Referencing entity's expressId (set by per-reference rules like reference-integrity). */
7
+ entityId?: number;
8
+ /** Zero-based top-level attribute slot of the offending value within the referencing entity. */
9
+ attributeIndex?: number;
10
+ /** The referenced expressId that does not exist in the file. */
11
+ target?: number;
12
+ }
13
+ /** One `#N` reference whose target expressId does not exist in the file. */
14
+ interface DanglingReference {
15
+ /** expressId of the entity containing the reference. */
16
+ entityId: number;
17
+ /** STEP type name of the referencing entity (UPPERCASE, as scanned). */
18
+ entityType: string;
19
+ /** Zero-based top-level attribute slot the reference sits in (nested refs report their containing slot). */
20
+ attributeIndex: number;
21
+ /** The missing expressId being referenced. */
22
+ target: number;
23
+ }
24
+ /**
25
+ * Walk every indexed entity record and collect `#N` references whose target
26
+ * does not exist in the file (rule `reference-integrity`). Uses the parsed
27
+ * entity index for both iteration and existence checks; entities held in the
28
+ * deferred index (lazily-parsed property entities) count as existing and are
29
+ * scanned too.
30
+ */
31
+ export declare function collectDanglingReferences(store: IfcDataStore): DanglingReference[];
32
+ /**
33
+ * Run the structural validation checks (required entities, storeys, GlobalId
34
+ * uniqueness, naming, schema version, quantity completeness, reference
35
+ * integrity) against an already-parsed store. Pulled out of
36
+ * {@link validateCommand} so other consumers (tests, harnesses) reuse the
37
+ * exact same rules instead of re-implementing them.
38
+ */
39
+ export declare function computeValidationIssues(store: IfcDataStore): ValidationIssue[];
1
40
  export declare function validateCommand(args: string[]): Promise<void>;
41
+ export {};
2
42
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AA0BA,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgInE"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAgC,KAAK,YAAY,EAAkB,MAAM,kBAAkB,CAAC;AAEnG,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gGAAgG;IAChG,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,4EAA4E;AAC5E,UAAU,iBAAiB;IACzB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;CAChB;AAuFD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,EAAE,CAmBlF;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,eAAe,EAAE,CA2H9E;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCnE"}
@@ -10,17 +10,132 @@
10
10
  * - Spatial structure completeness
11
11
  * - Orphan entity detection
12
12
  * - GlobalId uniqueness
13
+ * - Reference integrity (#N references that point at no entity)
13
14
  */
14
15
  import { loadIfcFile } from '../loader.js';
15
16
  import { hasFlag, fatal, printJson } from '../output.js';
16
17
  import { EntityNode } from '@ifc-lite/query';
17
18
  import { getInheritanceChainForEntity } from '@ifc-lite/parser';
18
- export async function validateCommand(args) {
19
- const filePath = args.find(a => !a.startsWith('-'));
20
- if (!filePath)
21
- fatal('Usage: ifc-lite validate <file.ifc> [--json]');
22
- const jsonOutput = hasFlag(args, '--json');
23
- const store = await loadIfcFile(filePath);
19
+ /** Cap on individually-reported dangling references; the remainder is rolled into one summary issue. */
20
+ const DANGLING_REF_ISSUE_CAP = 50;
21
+ const CH_QUOTE = 0x27; // '
22
+ const CH_LPAREN = 0x28; // (
23
+ const CH_RPAREN = 0x29; // )
24
+ const CH_COMMA = 0x2c; // ,
25
+ const CH_HASH = 0x23; // #
26
+ const CH_SLASH = 0x2f; // /
27
+ const CH_STAR = 0x2a; // *
28
+ /**
29
+ * Scan one entity record's source bytes for `#N` attribute references and
30
+ * report the ones whose target expressId is not indexed. Operates directly on
31
+ * the raw bytes the parser already indexed (`store.entityIndex.byId` spans),
32
+ * so it is a single pass over each record with no string allocation. Quote
33
+ * tracking (including STEP's doubled-quote escape) keeps `#N` sequences inside
34
+ * string literals from being read as references; embedded STEP comments are
35
+ * skipped.
36
+ */
37
+ function scanEntityForDanglingRefs(source, ref, exists, out) {
38
+ const end = ref.byteOffset + ref.byteLength;
39
+ // References only occur inside the attribute list; skip the "#id = TYPE" prefix.
40
+ let i = ref.byteOffset;
41
+ while (i < end && source[i] !== CH_LPAREN)
42
+ i++;
43
+ if (i >= end)
44
+ return; // malformed record, nothing to scan
45
+ let depth = 1; // we are inside the outermost '(' now
46
+ let attrIndex = 0;
47
+ i++;
48
+ while (i < end) {
49
+ const c = source[i];
50
+ if (c === CH_QUOTE) {
51
+ // String literal: skip to the closing quote, honouring '' escapes.
52
+ i++;
53
+ while (i < end) {
54
+ if (source[i] === CH_QUOTE) {
55
+ if (i + 1 < end && source[i + 1] === CH_QUOTE) {
56
+ i += 2; // escaped quote, still inside the string
57
+ continue;
58
+ }
59
+ break; // closing quote
60
+ }
61
+ i++;
62
+ }
63
+ }
64
+ else if (c === CH_SLASH && i + 1 < end && source[i + 1] === CH_STAR) {
65
+ // STEP comment inside a record (rare but legal): skip to closing marker.
66
+ i += 2;
67
+ while (i + 1 < end && !(source[i] === CH_STAR && source[i + 1] === CH_SLASH))
68
+ i++;
69
+ i++; // lands on '/', loop increment moves past it
70
+ }
71
+ else if (c === CH_LPAREN) {
72
+ depth++;
73
+ }
74
+ else if (c === CH_RPAREN) {
75
+ depth--;
76
+ if (depth === 0)
77
+ return; // end of attribute list
78
+ }
79
+ else if (c === CH_COMMA && depth === 1) {
80
+ attrIndex++;
81
+ }
82
+ else if (c === CH_HASH) {
83
+ let id = 0;
84
+ let digits = 0;
85
+ let j = i + 1;
86
+ while (j < end) {
87
+ const d = source[j] - 0x30;
88
+ if (d < 0 || d > 9)
89
+ break;
90
+ id = id * 10 + d;
91
+ digits++;
92
+ j++;
93
+ }
94
+ if (digits > 0) {
95
+ if (!exists(id)) {
96
+ out.push({ entityId: ref.expressId, entityType: ref.type, attributeIndex: attrIndex, target: id });
97
+ }
98
+ i = j - 1; // loop increment moves past the last digit
99
+ }
100
+ }
101
+ i++;
102
+ }
103
+ }
104
+ /**
105
+ * Walk every indexed entity record and collect `#N` references whose target
106
+ * does not exist in the file (rule `reference-integrity`). Uses the parsed
107
+ * entity index for both iteration and existence checks; entities held in the
108
+ * deferred index (lazily-parsed property entities) count as existing and are
109
+ * scanned too.
110
+ */
111
+ export function collectDanglingReferences(store) {
112
+ const source = store.source;
113
+ const byId = store.entityIndex.byId;
114
+ const deferred = store.deferredEntityIndex;
115
+ if (!source || source.byteLength === 0 || !byId)
116
+ return [];
117
+ const exists = (id) => byId.has(id) || deferred?.has(id) === true;
118
+ const out = [];
119
+ for (const ref of byId.values()) {
120
+ scanEntityForDanglingRefs(source, ref, exists, out);
121
+ }
122
+ if (deferred) {
123
+ for (const ref of deferred.values()) {
124
+ if (byId.has(ref.expressId))
125
+ continue; // already scanned above
126
+ scanEntityForDanglingRefs(source, ref, exists, out);
127
+ }
128
+ }
129
+ return out;
130
+ }
131
+ /**
132
+ * Run the structural validation checks (required entities, storeys, GlobalId
133
+ * uniqueness, naming, schema version, quantity completeness, reference
134
+ * integrity) against an already-parsed store. Pulled out of
135
+ * {@link validateCommand} so other consumers (tests, harnesses) reuse the
136
+ * exact same rules instead of re-implementing them.
137
+ */
138
+ export function computeValidationIssues(store) {
24
139
  const issues = [];
25
140
  // 1. Check required spatial entities
26
141
  const requiredTypes = ['IFCPROJECT', 'IFCSITE', 'IFCBUILDING'];
@@ -114,6 +229,40 @@ export async function validateCommand(args) {
114
229
  message: `${withoutQuantities}/${totalQuantifiable} building elements (${pct}%) have no quantity sets — quantity-based analysis may be incomplete`,
115
230
  });
116
231
  }
232
+ // 7. Reference integrity: every #N attribute reference must resolve to an
233
+ // entity that exists in the file. A dangling reference is structurally
234
+ // broken data (consumers following it get nothing, or worse, garbage), so
235
+ // it reports as an error like unique-globalid does.
236
+ const dangling = collectDanglingReferences(store);
237
+ for (const d of dangling.slice(0, DANGLING_REF_ISSUE_CAP)) {
238
+ // Display names render in IFC PascalCase (IfcWall, not IFCWALL); the
239
+ // collector carries the raw STEP token, so resolve through the table.
240
+ const typeName = store.entities.getTypeName(d.entityId) || d.entityType;
241
+ issues.push({
242
+ severity: 'error',
243
+ rule: 'reference-integrity',
244
+ message: `#${d.entityId} ${typeName} attribute ${d.attributeIndex} references missing entity #${d.target}`,
245
+ entityId: d.entityId,
246
+ attributeIndex: d.attributeIndex,
247
+ target: d.target,
248
+ });
249
+ }
250
+ if (dangling.length > DANGLING_REF_ISSUE_CAP) {
251
+ issues.push({
252
+ severity: 'error',
253
+ rule: 'reference-integrity',
254
+ message: `${dangling.length - DANGLING_REF_ISSUE_CAP} more dangling reference(s) not listed (${dangling.length} total)`,
255
+ });
256
+ }
257
+ return issues;
258
+ }
259
+ export async function validateCommand(args) {
260
+ const filePath = args.find(a => !a.startsWith('-'));
261
+ if (!filePath)
262
+ fatal('Usage: ifc-lite validate <file.ifc> [--json]');
263
+ const jsonOutput = hasFlag(args, '--json');
264
+ const store = await loadIfcFile(filePath);
265
+ const issues = computeValidationIssues(store);
117
266
  const summary = {
118
267
  file: filePath,
119
268
  schema: store.schemaVersion,
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}