@ifc-lite/cli 0.21.0 → 0.22.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.
Files changed (42) hide show
  1. package/dist/commands/diagnose-geometry.d.ts.map +1 -1
  2. package/dist/commands/diagnose-geometry.js +28 -22
  3. package/dist/commands/diagnose-geometry.js.map +1 -1
  4. package/dist/commands/diff-content.d.ts +11 -0
  5. package/dist/commands/diff-content.d.ts.map +1 -0
  6. package/dist/commands/diff-content.js +270 -0
  7. package/dist/commands/diff-content.js.map +1 -0
  8. package/dist/commands/diff-engine.d.ts +23 -0
  9. package/dist/commands/diff-engine.d.ts.map +1 -0
  10. package/dist/commands/diff-engine.js +159 -0
  11. package/dist/commands/diff-engine.js.map +1 -0
  12. package/dist/commands/diff-scope.d.ts +77 -0
  13. package/dist/commands/diff-scope.d.ts.map +1 -0
  14. package/dist/commands/diff-scope.js +160 -0
  15. package/dist/commands/diff-scope.js.map +1 -0
  16. package/dist/commands/diff-test-helpers.d.ts +70 -0
  17. package/dist/commands/diff-test-helpers.d.ts.map +1 -0
  18. package/dist/commands/diff-test-helpers.js +203 -0
  19. package/dist/commands/diff-test-helpers.js.map +1 -0
  20. package/dist/commands/diff.d.ts +1 -0
  21. package/dist/commands/diff.d.ts.map +1 -1
  22. package/dist/commands/diff.js +71 -25
  23. package/dist/commands/diff.js.map +1 -1
  24. package/dist/commands/extract-entities.d.ts.map +1 -1
  25. package/dist/commands/extract-entities.js +8 -2
  26. package/dist/commands/extract-entities.js.map +1 -1
  27. package/dist/commands/gym.d.ts.map +1 -1
  28. package/dist/commands/gym.js +77 -62
  29. package/dist/commands/gym.js.map +1 -1
  30. package/dist/commands/validate.d.ts.map +1 -1
  31. package/dist/commands/validate.js +26 -2
  32. package/dist/commands/validate.js.map +1 -1
  33. package/dist/hbjson-export.d.ts +37 -0
  34. package/dist/hbjson-export.d.ts.map +1 -0
  35. package/dist/hbjson-export.js +39 -0
  36. package/dist/hbjson-export.js.map +1 -0
  37. package/dist/headless-backend.d.ts.map +1 -1
  38. package/dist/headless-backend.js +18 -20
  39. package/dist/headless-backend.js.map +1 -1
  40. package/dist/index.js +3 -0
  41. package/dist/index.js.map +1 -1
  42. package/package.json +17 -16
@@ -1 +1 @@
1
- {"version":3,"file":"diagnose-geometry.d.ts","sourceRoot":"","sources":["../../src/commands/diagnose-geometry.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAMjF,KAAK,SAAS,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAAE,EAClB,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,SAAS,EAAE,CAMb;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E3E"}
1
+ {"version":3,"file":"diagnose-geometry.d.ts","sourceRoot":"","sources":["../../src/commands/diagnose-geometry.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAMjF,KAAK,SAAS,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAAE,EAClB,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,SAAS,EAAE,CAMb;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkF3E"}
@@ -52,31 +52,37 @@ export async function diagnoseGeometryCommand(args) {
52
52
  const buf = await readFile(filePath);
53
53
  const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
54
54
  const gp = new GeometryProcessor();
55
- await gp.init();
56
- let diag = gp.diagnoseGeometry(bytes);
57
- if (diag && (productArg !== undefined || typeArg !== undefined)) {
58
- let productId;
59
- if (productArg !== undefined) {
60
- if (isExpressId(productArg)) {
61
- productId = parseInt(productArg, 10);
62
- }
63
- else {
64
- // GlobalId: resolve via the columnar parser's entity table (the wasm
65
- // diagnostics pass never surfaces GlobalIds, only express IDs). Parse
66
- // the bytes ALREADY in memory rather than re-reading the file from disk
67
- // the geometry pass above already loaded them, so this avoids a
68
- // redundant disk read (the wasm IfcAPI exposes no GlobalId lookup, so a
69
- // columnar parse is still required to map GlobalId expressId).
70
- const store = await loadIfcBytes(bytes, filePath);
71
- const resolved = store.entities.getExpressIdByGlobalId(productArg);
72
- if (resolved === -1) {
73
- fatal(`--product: no entity found with GlobalId "${productArg}"`);
74
- return;
55
+ let diag;
56
+ try {
57
+ await gp.init();
58
+ diag = gp.diagnoseGeometry(bytes);
59
+ if (diag && (productArg !== undefined || typeArg !== undefined)) {
60
+ let productId;
61
+ if (productArg !== undefined) {
62
+ if (isExpressId(productArg)) {
63
+ productId = parseInt(productArg, 10);
64
+ }
65
+ else {
66
+ // GlobalId: resolve via the columnar parser's entity table (the wasm
67
+ // diagnostics pass never surfaces GlobalIds, only express IDs). Parse
68
+ // the bytes ALREADY in memory rather than re-reading the file from disk
69
+ // the geometry pass above already loaded them, so this avoids a
70
+ // redundant disk read (the wasm IfcAPI exposes no GlobalId lookup, so a
71
+ // columnar parse is still required to map GlobalId → expressId).
72
+ const store = await loadIfcBytes(bytes, filePath);
73
+ const resolved = store.entities.getExpressIdByGlobalId(productArg);
74
+ if (resolved === -1) {
75
+ fatal(`--product: no entity found with GlobalId "${productArg}"`);
76
+ return;
77
+ }
78
+ productId = resolved;
75
79
  }
76
- productId = resolved;
77
80
  }
81
+ diag = { ...diag, worstHosts: filterWorstHosts(diag.worstHosts, { productId, ifcType: typeArg }) };
78
82
  }
79
- diag = { ...diag, worstHosts: filterWorstHosts(diag.worstHosts, { productId, ifcType: typeArg }) };
83
+ }
84
+ finally {
85
+ gp.dispose();
80
86
  }
81
87
  if (asJson || outPath) {
82
88
  const json = JSON.stringify(diag ?? null, null, 2);
@@ -1 +1 @@
1
- {"version":3,"file":"diagnose-geometry.js","sourceRoot":"","sources":["../../src/commands/diagnose-geometry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diagnose-geometry.js","sourceRoot":"","sources":["../../src/commands/diagnose-geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface ContentDiffOptions {
2
+ basePath: string;
3
+ headPath: string;
4
+ /** `--identity-in`: a sidecar to replay as key aliases. */
5
+ identityIn?: string;
6
+ /** `--identity-out`: where to write the sidecar this run establishes. */
7
+ identityOut?: string;
8
+ json: boolean;
9
+ }
10
+ export declare function contentDiffCommand(options: ContentDiffOptions): Promise<void>;
11
+ //# sourceMappingURL=diff-content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-content.d.ts","sourceRoot":"","sources":["../../src/commands/diff-content.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFnF"}
@@ -0,0 +1,270 @@
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 diff --by-content` — the engine-backed half of the diff command
6
+ * (issue #1891), plus the identity-map sidecar it can emit and consume.
7
+ *
8
+ * The loop this closes: run it once, review the `renamed` matches, keep the
9
+ * sidecar. On the next run the accepted claims come back in as key aliases, the
10
+ * re-GUIDed elements are matched by key, and they never show up as churn again.
11
+ * See `diff-engine.ts` for why this path is data-scope only.
12
+ */
13
+ import { readFile, stat, writeFile } from 'node:fs/promises';
14
+ import { resolve } from 'node:path';
15
+ import { createIdentityMapSidecar, diffModels, identityMapFromContentMatches, identityMapSidecarMismatches, keyAliasesFromSidecar, parseIdentityMapSidecar, serializeIdentityMapSidecar, } from '@ifc-lite/diff';
16
+ import { loadIfcBytes } from '../loader.js';
17
+ import { fatal, printJson } from '../output.js';
18
+ import { buildFileFingerprints, modelIdentityOf } from './diff-engine.js';
19
+ export async function contentDiffCommand(options) {
20
+ const { basePath, headPath } = options;
21
+ // Before anything is read, and long before anything is written.
22
+ await refuseOverwritingAnInput(options);
23
+ const baseBytes = await readModel(basePath);
24
+ const headBytes = await readModel(headPath);
25
+ const baseIdentity = modelIdentityOf(basePath, baseBytes);
26
+ const headIdentity = modelIdentityOf(headPath, headBytes);
27
+ const incoming = options.identityIn
28
+ ? await readVerifiedSidecar(options.identityIn, baseIdentity, headIdentity)
29
+ : undefined;
30
+ process.stderr.write('Loading files...\n');
31
+ const baseStore = await loadIfcBytes(baseBytes, basePath);
32
+ const headStore = await loadIfcBytes(headBytes, headPath);
33
+ const diff = diffModels(buildFileFingerprints(baseStore), buildFileFingerprints(headStore), {
34
+ // No meshes in Node: `data` is the honest description of what this path can
35
+ // compare. See diff-engine.ts.
36
+ scope: 'data',
37
+ matchUnpairedByContent: true,
38
+ keyAliases: incoming ? keyAliasesFromSidecar(incoming) : undefined,
39
+ });
40
+ const applied = diff.appliedKeyAliases ?? new Map();
41
+ const ignored = incoming ? countClaims(incoming) - applied.size : 0;
42
+ let written;
43
+ if (options.identityOut) {
44
+ const entries = mergeIdentityClaims(incoming, applied, diff.contentMatches);
45
+ const sidecar = createIdentityMapSidecar({
46
+ base: baseIdentity,
47
+ head: headIdentity,
48
+ entries,
49
+ // Reproducible by construction: identical inputs write identical bytes.
50
+ // The builder sorts and de-duplicates so a checked-in sidecar produces an
51
+ // empty git diff when nothing changed, and stamping `Date.now()` here
52
+ // would throw that away — every rerun, including the
53
+ // `--identity-in x --identity-out x` carry-forward, would show a one-line
54
+ // diff that says nothing about the claims. An incoming `created` is
55
+ // preserved instead of refreshed: it dates the claims, not the rewrite,
56
+ // and the sidecar is addressed by the two model digests either way. There
57
+ // is deliberately no flag to request a fresh stamp — it would add CLI
58
+ // surface for a field the format already documents as informational and
59
+ // optional, and `git log` dates a reviewed, committed artifact better
60
+ // than a self-reported timestamp does.
61
+ created: incoming?.created,
62
+ });
63
+ await writeFile(options.identityOut, serializeIdentityMapSidecar(sidecar), 'utf-8');
64
+ written = { path: options.identityOut, entries: sidecar.entries };
65
+ }
66
+ if (options.json) {
67
+ printJson({
68
+ base: { path: basePath, hash: baseIdentity.hash, schema: baseStore.schemaVersion },
69
+ head: { path: headPath, hash: headIdentity.hash, schema: headStore.schemaVersion },
70
+ scope: diff.scope,
71
+ counts: diff.counts,
72
+ contentMatches: (diff.contentMatches ?? []).map((match) => ({
73
+ kind: match.kind,
74
+ base: match.base.map((entity) => entity.key),
75
+ head: match.head.map((entity) => entity.key),
76
+ })),
77
+ identityMap: {
78
+ in: options.identityIn
79
+ ? { path: options.identityIn, applied: applied.size, ignored }
80
+ : undefined,
81
+ out: written ? { path: written.path, entries: written.entries.length } : undefined,
82
+ },
83
+ });
84
+ return;
85
+ }
86
+ printReport({
87
+ basePath,
88
+ headPath,
89
+ diff,
90
+ appliedCount: applied.size,
91
+ ignoredCount: ignored,
92
+ identityIn: options.identityIn,
93
+ written,
94
+ });
95
+ }
96
+ /**
97
+ * Refuse to run at all if `--identity-out` names one of the two input models.
98
+ *
99
+ * The sidecar is a JSON document. Writing it over an IFC file destroys the
100
+ * user's model, and a mistyped or shell-completed path is all it takes — the
101
+ * two arguments right before it are IFC paths. So this is checked first, and
102
+ * the command exits without reading, comparing, or writing anything.
103
+ *
104
+ * Two independent tests, because a path string is not a file:
105
+ *
106
+ * 1. Resolved paths are equal. Catches `./v1.ifc` vs `v1.ifc` vs `sub/../v1.ifc`
107
+ * with no filesystem access, and is the whole answer on a platform where
108
+ * `stat` reports no usable inode.
109
+ * 2. The output already exists AND is the same file as an input, by device +
110
+ * inode. This is the exhaustive test: it catches a symlink, a hard link, a
111
+ * bind mount, and `V1.IFC` on a case-insensitive filesystem — every way two
112
+ * different strings can name one file. It is also sufficient on its own for
113
+ * the destructive case, because a path that does not resolve to an existing
114
+ * file cannot be overwriting an input: the inputs must exist to be read.
115
+ *
116
+ * `--identity-in` and `--identity-out` naming the SAME sidecar is not checked
117
+ * here, because that is the carry-forward workflow this feature is built around
118
+ * (read the accepted claims, write back the ones that still held).
119
+ */
120
+ async function refuseOverwritingAnInput(options) {
121
+ const { identityOut } = options;
122
+ if (identityOut === undefined)
123
+ return;
124
+ const out = resolve(identityOut);
125
+ const outStat = await statOrUndefined(identityOut);
126
+ for (const [label, input] of [
127
+ ['base model', options.basePath],
128
+ ['head model', options.headPath],
129
+ ]) {
130
+ if (resolve(input) !== out && !isSameFile(outStat, await statOrUndefined(input)))
131
+ continue;
132
+ fatal(`--identity-out ${identityOut} is the ${label} (${input}). ` +
133
+ 'Writing the identity map there would overwrite the input file.');
134
+ }
135
+ }
136
+ async function statOrUndefined(path) {
137
+ // A missing or unreadable path cannot be an input file being overwritten;
138
+ // reading the models is what reports it, with its own message.
139
+ try {
140
+ return await stat(path);
141
+ }
142
+ catch {
143
+ return undefined;
144
+ }
145
+ }
146
+ function isSameFile(a, b) {
147
+ return a !== undefined && b !== undefined && a.dev === b.dev && a.ino === b.ino;
148
+ }
149
+ /**
150
+ * Read one of the two input models, reporting a missing or unreadable path the
151
+ * way the rest of the command reports problems rather than throwing a raw
152
+ * `ENOENT` stack at the user — the same treatment `readVerifiedSidecar` already
153
+ * gives the sidecar.
154
+ */
155
+ async function readModel(path) {
156
+ try {
157
+ return await readFile(path);
158
+ }
159
+ catch (error) {
160
+ return fatal(`Cannot read ${path}: ${error.message}`);
161
+ }
162
+ }
163
+ /**
164
+ * Read a sidecar and refuse it unless it was verified against exactly these two
165
+ * files.
166
+ *
167
+ * This is the whole reason the sidecar pins both content digests. A rename list
168
+ * with no idea which revisions a human looked at when accepting it is not a
169
+ * claim, it is a guess with a filename; replayed against the wrong pair it
170
+ * either silently does nothing or asserts an identity nobody reviewed. There is
171
+ * deliberately no override flag: the fix for a mismatch is to re-run the
172
+ * comparison that produced the claims, which is one command.
173
+ */
174
+ async function readVerifiedSidecar(path, base, head) {
175
+ let text;
176
+ try {
177
+ text = await readFile(path, 'utf-8');
178
+ }
179
+ catch (error) {
180
+ return fatal(`Cannot read identity map ${path}: ${error.message}`);
181
+ }
182
+ let sidecar;
183
+ try {
184
+ sidecar = parseIdentityMapSidecar(text);
185
+ }
186
+ catch (error) {
187
+ return fatal(error.message);
188
+ }
189
+ const problems = identityMapSidecarMismatches(sidecar, { base, head });
190
+ if (problems.length > 0) {
191
+ return fatal(`Identity map ${path} was not verified against these files:\n ${problems.join('\n ')}`);
192
+ }
193
+ return sidecar;
194
+ }
195
+ /** Distinct head keys a sidecar actually claims (self-claims and repeats are
196
+ * no-ops the consumer drops, so counting raw entries would overstate what
197
+ * could have applied). */
198
+ function countClaims(sidecar) {
199
+ return keyAliasesFromSidecar(sidecar).size;
200
+ }
201
+ /**
202
+ * The claims the output sidecar should carry: the incoming ones that still
203
+ * applied, plus the ones this run's content matching established.
204
+ *
205
+ * Carrying the incoming claims forward is not bookkeeping, it is the point.
206
+ * An applied alias matches its pair by key, so that pair is no longer in
207
+ * `contentMatches` — deriving the output from the matches alone would drop
208
+ * exactly the claims that worked, and `--identity-in x --identity-out x` would
209
+ * erase the map a little more on every run. Their original `reason` is
210
+ * preserved so a hand-written claim does not silently become an engine-derived
211
+ * one. Incoming claims that did NOT apply (stale, or colliding) are dropped:
212
+ * the sidecar records what held against these two files, not what someone once
213
+ * hoped.
214
+ */
215
+ function mergeIdentityClaims(incoming, applied, matches) {
216
+ const entries = [];
217
+ if (incoming) {
218
+ const reasons = new Map();
219
+ for (const entry of incoming.entries) {
220
+ if (!reasons.has(entry.here))
221
+ reasons.set(entry.here, entry.reason);
222
+ }
223
+ for (const [here, base] of applied) {
224
+ entries.push({ base, here, reason: reasons.get(here) ?? 'carried forward' });
225
+ }
226
+ }
227
+ entries.push(...identityMapFromContentMatches(matches));
228
+ return entries;
229
+ }
230
+ function printReport(report) {
231
+ const { diff } = report;
232
+ const out = (line) => {
233
+ process.stdout.write(`${line}\n`);
234
+ };
235
+ out('');
236
+ out(` Base: ${report.basePath}`);
237
+ out(` Head: ${report.headPath}`);
238
+ out(` Scope: data (the CLI has no geometry pipeline)`);
239
+ out('');
240
+ out(` Unchanged: ${diff.counts.unchanged}`);
241
+ out(` Modified: ${diff.counts.modified}`);
242
+ out(` Added: ${diff.counts.added}`);
243
+ out(` Deleted: ${diff.counts.deleted}`);
244
+ const byKind = new Map();
245
+ for (const match of diff.contentMatches ?? []) {
246
+ byKind.set(match.kind, (byKind.get(match.kind) ?? 0) + 1);
247
+ }
248
+ out('');
249
+ if (byKind.size === 0) {
250
+ out(' Content matches: none');
251
+ }
252
+ else {
253
+ out(' Content matches:');
254
+ for (const [kind, count] of [...byKind].sort()) {
255
+ out(` ${kind.padEnd(13)} ${count}`);
256
+ }
257
+ out(' (renamed / moved / reshaped are resolved; the rest need a human)');
258
+ }
259
+ if (report.identityIn) {
260
+ out('');
261
+ out(` Identity map in: ${report.identityIn}`);
262
+ out(` applied: ${report.appliedCount}, ignored: ${report.ignoredCount}`);
263
+ }
264
+ if (report.written) {
265
+ out('');
266
+ out(` Identity map out: ${report.written.path} (${report.written.entries.length} claims)`);
267
+ }
268
+ out('');
269
+ }
270
+ //# sourceMappingURL=diff-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-content.js","sourceRoot":"","sources":["../../src/commands/diff-content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type EntityFingerprint, type ModelIdentity } from '@ifc-lite/diff';
2
+ import { type IfcDataStore } from '@ifc-lite/parser';
3
+ /** Adapter handle threaded through the diff: the entity's express id. */
4
+ export type DiffRef = number;
5
+ /**
6
+ * Content digest of a model's bytes, as written into the sidecar.
7
+ *
8
+ * The digest is over the file **as it sits on disk**, before any `.ifcZIP`
9
+ * unwrapping — that is the thing a user can reproduce with `shasum`, and the
10
+ * thing that changes when someone re-exports.
11
+ */
12
+ export declare function modelIdentityOf(path: string, bytes: Uint8Array): ModelIdentity;
13
+ /**
14
+ * Build one {@link EntityFingerprint} per `IfcObjectDefinition` in a store.
15
+ *
16
+ * `components` is populated as well as `dataHash`: the content pass's only
17
+ * defence against a `dataHash` collision retiring an unrelated add/delete pair
18
+ * is agreement on `ifcType` and on every component sub-hash, and the second
19
+ * check is inert unless both sides supply them (see the "Hash collisions"
20
+ * section of `docs/guide/model-diff.md`).
21
+ */
22
+ export declare function buildFileFingerprints(store: IfcDataStore): EntityFingerprint<DiffRef>[];
23
+ //# sourceMappingURL=diff-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-engine.d.ts","sourceRoot":"","sources":["../../src/commands/diff-engine.ts"],"names":[],"mappings":"AA8BA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAML,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAG1B,yEAAyE;AACzE,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,CAG9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAavF"}
@@ -0,0 +1,159 @@
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
+ * The CLI's adapter onto the real `@ifc-lite/diff` engine (issue #1891).
6
+ *
7
+ * `ifc-lite diff` on its own answers "what changed at the type and identity
8
+ * level" — counts per type, GlobalIds added and removed. That is useless the
9
+ * moment the two files came from a from-scratch re-export, because every
10
+ * GlobalId is new and the whole model reads as deleted-and-added. `--by-content`
11
+ * routes the same two files through the engine's content-keyed matching pass
12
+ * instead, and lets the accepted matches be written to (and replayed from) an
13
+ * identity-map sidecar.
14
+ *
15
+ * **Data scope only, on purpose.** The Node CLI has no geometry pipeline: no
16
+ * meshes, so no world geometry hash and no bounding box. Rather than pretend
17
+ * otherwise, it passes `scope: 'data'`, which is the honest description of what
18
+ * it can see — the engine then classifies every unambiguous 1:1 content match as
19
+ * `renamed` and reports every genuinely ambiguous group as a group, exactly as
20
+ * it does for a viewer session whose geometry hashing was unavailable.
21
+ *
22
+ * **Scope of the comparison: every `IfcObjectDefinition` in the file**, decided
23
+ * from the schema registry's inheritance chain rather than from what the
24
+ * columnar parser put in its `EntityTable`. That walk is `diff-scope.ts`, which
25
+ * documents the rule and the two silent defects it fixes; `--by-entity` runs on
26
+ * the same walk, so both modes of the command answer about the same entities.
27
+ */
28
+ import { createHash } from 'node:crypto';
29
+ import { buildComponentFingerprints, buildDataFingerprint, } from '@ifc-lite/diff';
30
+ import { RelationshipType } from '@ifc-lite/data';
31
+ import { EntityExtractor, extractAllEntityAttributes, extractPropertiesOnDemand, extractQuantitiesOnDemand, getAttributeNamesAcrossSchemas, } from '@ifc-lite/parser';
32
+ import { comparableEntities } from './diff-scope.js';
33
+ /**
34
+ * Content digest of a model's bytes, as written into the sidecar.
35
+ *
36
+ * The digest is over the file **as it sits on disk**, before any `.ifcZIP`
37
+ * unwrapping — that is the thing a user can reproduce with `shasum`, and the
38
+ * thing that changes when someone re-exports.
39
+ */
40
+ export function modelIdentityOf(path, bytes) {
41
+ const digest = createHash('sha256').update(bytes).digest('hex');
42
+ return { hash: `sha256:${digest}`, path };
43
+ }
44
+ /**
45
+ * Build one {@link EntityFingerprint} per `IfcObjectDefinition` in a store.
46
+ *
47
+ * `components` is populated as well as `dataHash`: the content pass's only
48
+ * defence against a `dataHash` collision retiring an unrelated add/delete pair
49
+ * is agreement on `ifcType` and on every component sub-hash, and the second
50
+ * check is inert unless both sides supply them (see the "Hash collisions"
51
+ * section of `docs/guide/model-diff.md`).
52
+ */
53
+ export function buildFileFingerprints(store) {
54
+ const fingerprints = [];
55
+ for (const { expressId, globalId, ifcType, source, isTypeObject } of comparableEntities(store)) {
56
+ const input = buildDataInput(store, expressId, ifcType, source, isTypeObject);
57
+ fingerprints.push({
58
+ key: globalId,
59
+ ifcType,
60
+ dataHash: buildDataFingerprint(input),
61
+ components: buildComponentFingerprints(input),
62
+ ref: expressId,
63
+ });
64
+ }
65
+ return fingerprints;
66
+ }
67
+ /**
68
+ * Assemble the canonical {@link DataFingerprintInput} for one entity.
69
+ *
70
+ * Mirrors the viewer adapter (`apps/viewer/src/lib/compare/buildFingerprints.ts`)
71
+ * minus its geometry-data filtering: that filter exists to keep placement data
72
+ * out of the *data* hash so a pure move reads as a geometry-only change, and
73
+ * this path has no geometry hash for such a change to land in. Dropping the
74
+ * filter here would make a moved element look unchanged.
75
+ */
76
+ function buildDataInput(store, expressId, ifcType,
77
+ /** Set only when the entity is absent from the columnar `EntityTable`, whose
78
+ * accessors then answer '' for every display attribute. */
79
+ source,
80
+ /** `IfcTypeObject` subtype? Gates `Tag` into the fingerprint (issue #2021). */
81
+ isTypeObject) {
82
+ const predefinedType = extractAllEntityAttributes(store, expressId).find((attribute) => attribute.name === 'PredefinedType')?.value;
83
+ // `Tag` for a TYPE OBJECT only. On an occurrence it is the authoring tool's
84
+ // element id, which changes across producers while the design does not, and
85
+ // `dataHash` is the content bucket key — hashing it there would stop the
86
+ // re-export matching this whole path exists for. On a type object it is the
87
+ // only thing separating same-named types with no geometry hash to fall back
88
+ // on (issue #2021, and `DataFingerprintInput.tag` for the full argument).
89
+ const tag = isTypeObject ? attributeAcrossSchemas(store, expressId, ifcType, 'Tag') : undefined;
90
+ const propertySets = extractPropertiesOnDemand(store, expressId).map((set) => ({
91
+ name: set.name,
92
+ properties: set.properties.map((property) => ({ name: property.name, value: property.value })),
93
+ }));
94
+ const quantitySets = extractQuantitiesOnDemand(store, expressId).map((set) => ({
95
+ name: set.name,
96
+ quantities: set.quantities.map((quantity) => ({
97
+ name: quantity.name,
98
+ // Rounded to 4 dp, matching the viewer: re-exporting a model with
99
+ // sub-tolerance float jitter must not flip the data hash on an otherwise
100
+ // identical element, which on this path would cost the pair its match.
101
+ value: roundQuantity(quantity.value),
102
+ })),
103
+ }));
104
+ const typeAssignments = store.relationships
105
+ .getRelated(expressId, RelationshipType.DefinesByType, 'inverse')
106
+ .map((typeId) => ({
107
+ globalId: store.entities.getGlobalId(typeId) || undefined,
108
+ name: store.entities.getName(typeId) || undefined,
109
+ type: store.entities.getTypeName(typeId) || undefined,
110
+ }));
111
+ return {
112
+ ifcType,
113
+ name: store.entities.getName(expressId) || source?.name || undefined,
114
+ description: store.entities.getDescription(expressId) || source?.description || undefined,
115
+ objectType: store.entities.getObjectType(expressId) || source?.objectType || undefined,
116
+ predefinedType: predefinedType != null ? String(predefinedType) : undefined,
117
+ tag: tag != null ? String(tag) : undefined,
118
+ propertySets,
119
+ quantitySets,
120
+ typeAssignments,
121
+ };
122
+ }
123
+ function roundQuantity(value) {
124
+ return Number.isFinite(value) ? Math.round(value * 1e4) / 1e4 : value;
125
+ }
126
+ /**
127
+ * One named attribute, read positionally through the **cross-schema** attribute
128
+ * list (issue #2021).
129
+ *
130
+ * `extractAllEntityAttributes` names attributes through the parser's IFC4
131
+ * codegen pin, which answers an EMPTY list for a class the pin does not carry —
132
+ * so a `.find(name === 'Tag')` over it silently finds nothing on every
133
+ * IFC4X3-only type object (`IfcRailType`, `IfcTrackElementType`,
134
+ * `IfcSignalType`, …) while working perfectly on IFC2X3 and IFC4. That is a
135
+ * no-op nobody would notice: the entity is in scope, its class name is right,
136
+ * `isTypeObject` is right, and only the evidence is missing.
137
+ *
138
+ * This is the same pinned-registry family as the membership defect `#2001`
139
+ * fixed, and it has to be answered from the same place: the inheritance chain
140
+ * decides *whether* to read a `Tag`, so the attribute list that decides *where*
141
+ * it sits must span the same schemas. `getAttributeNamesAcrossSchemas` returns
142
+ * the pinned result unchanged for every class the pin does know, so this is
143
+ * additive — no IFC2X3 or IFC4 entity's hash moves because of it.
144
+ *
145
+ * Reads the raw STEP slot rather than reusing `extractAllEntityAttributes`'
146
+ * display normalization: this value is hashed, not shown, so `$` (absent) is
147
+ * the only case that needs interpreting and it arrives as null.
148
+ */
149
+ function attributeAcrossSchemas(store, expressId, ifcType, attributeName) {
150
+ const index = getAttributeNamesAcrossSchemas(ifcType).indexOf(attributeName);
151
+ if (index < 0)
152
+ return undefined;
153
+ const ref = store.entityIndex.byId.get(expressId);
154
+ if (!ref)
155
+ return undefined;
156
+ const raw = new EntityExtractor(store.source).extractEntity(ref)?.attributes?.[index];
157
+ return typeof raw === 'string' || typeof raw === 'number' ? String(raw) : undefined;
158
+ }
159
+ //# sourceMappingURL=diff-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-engine.js","sourceRoot":"","sources":["../../src/commands/diff-engine.ts"],"names":[],"mappings":"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"}