@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.
- package/dist/commands/diagnose-geometry.d.ts.map +1 -1
- package/dist/commands/diagnose-geometry.js +28 -22
- package/dist/commands/diagnose-geometry.js.map +1 -1
- package/dist/commands/diff-content.d.ts +11 -0
- package/dist/commands/diff-content.d.ts.map +1 -0
- package/dist/commands/diff-content.js +270 -0
- package/dist/commands/diff-content.js.map +1 -0
- package/dist/commands/diff-engine.d.ts +23 -0
- package/dist/commands/diff-engine.d.ts.map +1 -0
- package/dist/commands/diff-engine.js +159 -0
- package/dist/commands/diff-engine.js.map +1 -0
- package/dist/commands/diff-scope.d.ts +77 -0
- package/dist/commands/diff-scope.d.ts.map +1 -0
- package/dist/commands/diff-scope.js +160 -0
- package/dist/commands/diff-scope.js.map +1 -0
- package/dist/commands/diff-test-helpers.d.ts +70 -0
- package/dist/commands/diff-test-helpers.d.ts.map +1 -0
- package/dist/commands/diff-test-helpers.js +203 -0
- package/dist/commands/diff-test-helpers.js.map +1 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +71 -25
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/extract-entities.d.ts.map +1 -1
- package/dist/commands/extract-entities.js +8 -2
- package/dist/commands/extract-entities.js.map +1 -1
- package/dist/commands/gym.d.ts.map +1 -1
- package/dist/commands/gym.js +77 -62
- package/dist/commands/gym.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +26 -2
- package/dist/commands/validate.js.map +1 -1
- package/dist/hbjson-export.d.ts +37 -0
- package/dist/hbjson-export.d.ts.map +1 -0
- package/dist/hbjson-export.js +39 -0
- package/dist/hbjson-export.js.map +1 -0
- package/dist/headless-backend.d.ts.map +1 -1
- package/dist/headless-backend.js +18 -20
- package/dist/headless-backend.js.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- 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,
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (productArg !== undefined) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,4DAA4D;AAE5D;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAA4B,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIlF,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAkB,EAClB,IAA8C;IAE9C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QACjF,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAAc;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,KAAK,CACH,0EAA0E;YACxE,qDAAqD,CACxD,CAAC;QACF,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAExC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAEzE,MAAM,EAAE,GAAG,IAAI,iBAAiB,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"diagnose-geometry.js","sourceRoot":"","sources":["../../src/commands/diagnose-geometry.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,4DAA4D;AAE5D;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAA4B,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIlF,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAkB,EAClB,IAA8C;IAE9C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QACjF,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAAc;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,KAAK,CACH,0EAA0E;YACxE,qDAAqD,CACxD,CAAC;QACF,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAExC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAEzE,MAAM,EAAE,GAAG,IAAI,iBAAiB,EAAE,CAAC;IACnC,IAAI,IAA4C,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAElC,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,CAAC,EAAE,CAAC;YAChE,IAAI,SAA6B,CAAC;YAClC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC5B,SAAS,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBACvC,CAAC;qBAAM,CAAC;oBACN,qEAAqE;oBACrE,sEAAsE;oBACtE,wEAAwE;oBACxE,kEAAkE;oBAClE,wEAAwE;oBACxE,iEAAiE;oBACjE,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAC;oBACnE,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;wBACpB,KAAK,CAAC,6CAA6C,UAAU,GAAG,CAAC,CAAC;wBAClE,OAAO;oBACT,CAAC;oBACD,SAAS,GAAG,QAAQ,CAAC;gBACvB,CAAC;YACH,CAAC;YACD,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACrG,CAAC;IACH,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,OAAO,EAAE,CAAC;IACf,CAAC;IAED,IAAI,MAAM,IAAI,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACnD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,wBAAwB,OAAO,EAAE,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACpC,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;QACjD,OAAO;IACT,CAAC;IACD,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,8EAA8E;IAC9E,qEAAqE;IACrE,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,MAAM;YACJ,MAAM;YACN,6EAA6E;YAC7E,kFAAkF;YAClF,kCAAkC,CACrC,CAAC;QACF,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -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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"}
|