@ifc-lite/cli 0.26.0 → 0.28.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/README.md +1 -1
- package/dist/commands/ask.d.ts +8 -0
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +10 -9
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/bsdd.d.ts.map +1 -1
- package/dist/commands/bsdd.js +49 -13
- package/dist/commands/bsdd.js.map +1 -1
- package/dist/commands/delivery-checks.d.ts +56 -0
- package/dist/commands/delivery-checks.d.ts.map +1 -0
- package/dist/commands/delivery-checks.js +222 -0
- package/dist/commands/delivery-checks.js.map +1 -0
- package/dist/commands/delivery-recipe.d.ts +23 -0
- package/dist/commands/delivery-recipe.d.ts.map +1 -0
- package/dist/commands/delivery-recipe.js +104 -0
- package/dist/commands/delivery-recipe.js.map +1 -0
- package/dist/commands/delivery-report.d.ts +51 -0
- package/dist/commands/delivery-report.d.ts.map +1 -0
- package/dist/commands/delivery-report.js +114 -0
- package/dist/commands/delivery-report.js.map +1 -0
- package/dist/commands/delivery.d.ts +2 -0
- package/dist/commands/delivery.d.ts.map +1 -0
- package/dist/commands/delivery.js +127 -0
- package/dist/commands/delivery.js.map +1 -0
- package/dist/commands/diff-classification-label.d.ts +12 -0
- package/dist/commands/diff-classification-label.d.ts.map +1 -0
- package/dist/commands/diff-classification-label.js +20 -0
- package/dist/commands/diff-classification-label.js.map +1 -0
- package/dist/commands/diff-engine.d.ts.map +1 -1
- package/dist/commands/diff-engine.js +33 -11
- package/dist/commands/diff-engine.js.map +1 -1
- package/dist/commands/diff-test-helpers.d.ts +12 -0
- package/dist/commands/diff-test-helpers.d.ts.map +1 -1
- package/dist/commands/diff-test-helpers.js +68 -0
- package/dist/commands/diff-test-helpers.js.map +1 -1
- package/dist/commands/diff-unit-scale-fixtures.d.ts +34 -0
- package/dist/commands/diff-unit-scale-fixtures.d.ts.map +1 -0
- package/dist/commands/diff-unit-scale-fixtures.js +94 -0
- package/dist/commands/diff-unit-scale-fixtures.js.map +1 -0
- package/dist/commands/export.d.ts +11 -0
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +9 -14
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/gym.js +2 -2
- package/dist/commands/merge.d.ts.map +1 -1
- package/dist/commands/merge.js +8 -0
- package/dist/commands/merge.js.map +1 -1
- package/dist/commands/query-aggregation.d.ts +27 -4
- package/dist/commands/query-aggregation.d.ts.map +1 -1
- package/dist/commands/query-aggregation.js +67 -6
- package/dist/commands/query-aggregation.js.map +1 -1
- package/dist/commands/query-output.d.ts +7 -0
- package/dist/commands/query-output.d.ts.map +1 -1
- package/dist/commands/query-output.js +63 -27
- package/dist/commands/query-output.js.map +1 -1
- package/dist/commands/query-storey.d.ts +24 -0
- package/dist/commands/query-storey.d.ts.map +1 -0
- package/dist/commands/query-storey.js +71 -0
- package/dist/commands/query-storey.js.map +1 -0
- package/dist/commands/query.d.ts +2 -25
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +39 -158
- package/dist/commands/query.js.map +1 -1
- package/dist/commands/schedule-aggregate.d.ts +51 -0
- package/dist/commands/schedule-aggregate.d.ts.map +1 -0
- package/dist/commands/schedule-aggregate.js +121 -0
- package/dist/commands/schedule-aggregate.js.map +1 -0
- package/dist/commands/schedule-columns.d.ts +15 -0
- package/dist/commands/schedule-columns.d.ts.map +1 -0
- package/dist/commands/schedule-columns.js +63 -0
- package/dist/commands/schedule-columns.js.map +1 -0
- package/dist/commands/schedule-group.d.ts +65 -0
- package/dist/commands/schedule-group.d.ts.map +1 -0
- package/dist/commands/schedule-group.js +0 -0
- package/dist/commands/schedule-group.js.map +1 -0
- package/dist/commands/schedule-presets.d.ts +21 -0
- package/dist/commands/schedule-presets.d.ts.map +1 -0
- package/dist/commands/schedule-presets.js +62 -0
- package/dist/commands/schedule-presets.js.map +1 -0
- package/dist/commands/schedule-render-html.d.ts +13 -0
- package/dist/commands/schedule-render-html.d.ts.map +1 -0
- package/dist/commands/schedule-render-html.js +100 -0
- package/dist/commands/schedule-render-html.js.map +1 -0
- package/dist/commands/schedule-render-md.d.ts +12 -0
- package/dist/commands/schedule-render-md.d.ts.map +1 -0
- package/dist/commands/schedule-render-md.js +77 -0
- package/dist/commands/schedule-render-md.js.map +1 -0
- package/dist/commands/schedule-render.d.ts +51 -0
- package/dist/commands/schedule-render.d.ts.map +1 -0
- package/dist/commands/schedule-render.js +205 -0
- package/dist/commands/schedule-render.js.map +1 -0
- package/dist/commands/schedule-spec.d.ts +29 -0
- package/dist/commands/schedule-spec.d.ts.map +1 -0
- package/dist/commands/schedule-spec.js +111 -0
- package/dist/commands/schedule-spec.js.map +1 -0
- package/dist/commands/schedule.d.ts +23 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/schedule.js +249 -0
- package/dist/commands/schedule.js.map +1 -0
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +192 -17
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/stats-aggregation.d.ts +31 -6
- package/dist/commands/stats-aggregation.d.ts.map +1 -1
- package/dist/commands/stats-aggregation.js +79 -11
- package/dist/commands/stats-aggregation.js.map +1 -1
- package/dist/commands/stats.d.ts.map +1 -1
- package/dist/commands/stats.js +4 -4
- package/dist/commands/stats.js.map +1 -1
- package/dist/commands/validate.d.ts +7 -4
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +9 -6
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/where-filter.d.ts +42 -0
- package/dist/commands/where-filter.d.ts.map +1 -0
- package/dist/commands/where-filter.js +88 -0
- package/dist/commands/where-filter.js.map +1 -0
- package/dist/energy-export.d.ts.map +1 -1
- package/dist/energy-export.js +12 -2
- package/dist/energy-export.js.map +1 -1
- package/dist/geometry-report.d.ts.map +1 -1
- package/dist/geometry-report.js +15 -3
- package/dist/geometry-report.js.map +1 -1
- package/dist/headless-backend.d.ts +20 -5
- package/dist/headless-backend.d.ts.map +1 -1
- package/dist/headless-backend.js +131 -90
- package/dist/headless-backend.js.map +1 -1
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +13 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +21 -0
- package/dist/output.js.map +1 -1
- package/dist/property-filter-match.d.ts +12 -0
- package/dist/property-filter-match.d.ts.map +1 -0
- package/dist/property-filter-match.js +17 -0
- package/dist/property-filter-match.js.map +1 -0
- package/dist/query-overlay-relations.d.ts +29 -0
- package/dist/query-overlay-relations.d.ts.map +1 -0
- package/dist/query-overlay-relations.js +88 -0
- package/dist/query-overlay-relations.js.map +1 -0
- package/dist/query-overlay.d.ts +44 -0
- package/dist/query-overlay.d.ts.map +1 -0
- package/dist/query-overlay.js +100 -0
- package/dist/query-overlay.js.map +1 -0
- package/package.json +21 -21
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-recipe.d.ts","sourceRoot":"","sources":["../../src/commands/delivery-recipe.ts"],"names":[],"mappings":"AAuCA,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAED,4EAA4E;AAC5E,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAQD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAiEtF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
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 saved recipe `ifc-lite delivery` runs.
|
|
6
|
+
*
|
|
7
|
+
* A recipe is a plain JSON file naming one or more model files and the
|
|
8
|
+
* checks to run against every one of them — `structural` (the same rules
|
|
9
|
+
* `ifc-lite validate` runs) and/or one or more `ids` rule files (the same
|
|
10
|
+
* validator `ifc-lite ids` runs). It exists so a delivery check is a single,
|
|
11
|
+
* versioned, reviewable artifact a team commits and re-runs, rather than a
|
|
12
|
+
* remembered shell invocation.
|
|
13
|
+
*
|
|
14
|
+
* ```json
|
|
15
|
+
* {
|
|
16
|
+
* "models": ["model.ifc"],
|
|
17
|
+
* "structural": true,
|
|
18
|
+
* "ids": ["door-rules.ids"]
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Every `models`/`ids` path is resolved relative to the DIRECTORY CONTAINING
|
|
23
|
+
* THE RECIPE FILE, not the process's current working directory — a recipe
|
|
24
|
+
* committed alongside its fixtures stays runnable from anywhere.
|
|
25
|
+
*
|
|
26
|
+
* A malformed recipe is a `fatal(...)`, matching every other saved-definition
|
|
27
|
+
* loader in this CLI (`schedule-spec.ts`): not valid JSON, not a JSON object,
|
|
28
|
+
* an unrecognised field, a field of the wrong type, an empty/missing
|
|
29
|
+
* `models` list, or declaring NEITHER `structural: true` NOR a non-empty
|
|
30
|
+
* `ids` list (a recipe with zero applicable checks can never produce a
|
|
31
|
+
* meaningful delivery verdict, so it is rejected up front rather than
|
|
32
|
+
* silently reporting an empty "pass").
|
|
33
|
+
*/
|
|
34
|
+
import { readFile } from 'node:fs/promises';
|
|
35
|
+
import { dirname, resolve } from 'node:path';
|
|
36
|
+
import { fatal } from '../output.js';
|
|
37
|
+
const RECIPE_FIELDS = ['models', 'structural', 'ids'];
|
|
38
|
+
function isStringArray(value) {
|
|
39
|
+
return Array.isArray(value) && value.every(v => typeof v === 'string');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Load and validate a delivery recipe, resolving every declared path
|
|
43
|
+
* relative to the recipe file's own directory. Every failure mode is a
|
|
44
|
+
* `fatal(...)` with a specific reason — never a silently empty or
|
|
45
|
+
* partially-applied recipe.
|
|
46
|
+
*/
|
|
47
|
+
export async function loadDeliveryRecipe(path) {
|
|
48
|
+
let content;
|
|
49
|
+
try {
|
|
50
|
+
content = await readFile(path, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
fatal(`Recipe "${path}" could not be read: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(content);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
fatal(`Recipe "${path}" is not valid JSON: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
63
|
+
fatal(`Recipe "${path}" must be a JSON object with "models" and at least one of "structural"/"ids", got ${Array.isArray(parsed) ? 'an array' : typeof parsed}.`);
|
|
64
|
+
}
|
|
65
|
+
const obj = parsed;
|
|
66
|
+
const unknownKeys = Object.keys(obj).filter(k => !RECIPE_FIELDS.includes(k));
|
|
67
|
+
if (unknownKeys.length > 0) {
|
|
68
|
+
fatal(`Recipe "${path}" has unrecognised field(s): ${unknownKeys.join(', ')}. Valid fields: ${RECIPE_FIELDS.join(', ')}.`);
|
|
69
|
+
}
|
|
70
|
+
if (!('models' in obj) || !isStringArray(obj.models) || obj.models.length === 0) {
|
|
71
|
+
fatal(`Recipe "${path}": "models" must be a non-empty array of file paths.`);
|
|
72
|
+
}
|
|
73
|
+
const models = obj.models;
|
|
74
|
+
if ('structural' in obj && typeof obj.structural !== 'boolean') {
|
|
75
|
+
fatal(`Recipe "${path}": "structural" must be a boolean, got ${typeof obj.structural}.`);
|
|
76
|
+
}
|
|
77
|
+
const structural = obj.structural ?? false;
|
|
78
|
+
if ('ids' in obj && !isStringArray(obj.ids)) {
|
|
79
|
+
fatal(`Recipe "${path}": "ids" must be an array of file paths.`);
|
|
80
|
+
}
|
|
81
|
+
const ids = obj.ids ?? [];
|
|
82
|
+
if (!structural && ids.length === 0) {
|
|
83
|
+
fatal(`Recipe "${path}" declares no applicable checks: set "structural": true and/or a non-empty "ids" list. A zero-check recipe can never produce a delivery verdict.`);
|
|
84
|
+
}
|
|
85
|
+
// Duplicate model/ids entries are rejected rather than silently
|
|
86
|
+
// deduplicated or run twice under one collapsed key: a repeated path is
|
|
87
|
+
// almost certainly a copy-paste mistake in the recipe, and either
|
|
88
|
+
// response (silent dedup, or a doubled report entry) would misrepresent
|
|
89
|
+
// what the recipe actually says it checks.
|
|
90
|
+
const dupModels = models.filter((m, i) => models.indexOf(m) !== i);
|
|
91
|
+
if (dupModels.length > 0) {
|
|
92
|
+
fatal(`Recipe "${path}": "models" lists the same path more than once: ${[...new Set(dupModels)].join(', ')}.`);
|
|
93
|
+
}
|
|
94
|
+
const dupIds = ids.filter((m, i) => ids.indexOf(m) !== i);
|
|
95
|
+
if (dupIds.length > 0) {
|
|
96
|
+
fatal(`Recipe "${path}": "ids" lists the same path more than once: ${[...new Set(dupIds)].join(', ')}.`);
|
|
97
|
+
}
|
|
98
|
+
const recipePath = resolve(path);
|
|
99
|
+
const baseDir = dirname(recipePath);
|
|
100
|
+
const resolvedModels = models.map(m => resolve(baseDir, m));
|
|
101
|
+
const resolvedIds = ids.map(i => resolve(baseDir, i));
|
|
102
|
+
return { recipePath, models, structural, ids, resolvedModels, resolvedIds };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=delivery-recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-recipe.js","sourceRoot":"","sources":["../../src/commands/delivery-recipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consolidated report assembly and HTML rendering for `ifc-lite delivery`.
|
|
3
|
+
*
|
|
4
|
+
* Determinism contract: `buildDeliveryReport` never reads the wall clock,
|
|
5
|
+
* never iterates a `Map`/object in insertion-order-dependent ways, and never
|
|
6
|
+
* keys a result by a derived string (model path, IDS file name, check type)
|
|
7
|
+
* — every check is one entry in a flat, declared-order ARRAY. Two collisions
|
|
8
|
+
* this codebase has actually shipped (a subtotal silently overwritten in
|
|
9
|
+
* `schedule`'s output, a duplicate `--columns` header collapsing a row in
|
|
10
|
+
* JSON) both came from keying a result object by a name that two distinct
|
|
11
|
+
* inputs could share; an array keeps every check's own model/type/source
|
|
12
|
+
* fields as data instead of forcing them into a Map/object key that could
|
|
13
|
+
* collide. The result: the SAME recipe run twice, with no model or IDS file
|
|
14
|
+
* changed on disk, produces byte-identical JSON (`tool.version` and the
|
|
15
|
+
* `sha256` fingerprints are the only fields that could ever legitimately
|
|
16
|
+
* differ between two runs, and neither depends on wall-clock time or
|
|
17
|
+
* iteration order).
|
|
18
|
+
*/
|
|
19
|
+
import type { StructuralCheckResult, IdsCheckResult } from './delivery-checks.js';
|
|
20
|
+
import type { ResolvedDeliveryRecipe } from './delivery-recipe.js';
|
|
21
|
+
export type DeliveryCheckResult = StructuralCheckResult | IdsCheckResult;
|
|
22
|
+
export interface DeliveryModelEntry {
|
|
23
|
+
/** Path exactly as declared in the recipe (relative, for a readable report). */
|
|
24
|
+
path: string;
|
|
25
|
+
/** SHA-256 of the model's bytes, or undefined when the model could not be loaded. */
|
|
26
|
+
sha256?: string;
|
|
27
|
+
/** Set when the model could not be loaded; every check for this model then reports `error`. */
|
|
28
|
+
loadError?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface DeliveryReport {
|
|
31
|
+
tool: {
|
|
32
|
+
name: 'ifc-lite';
|
|
33
|
+
version: string;
|
|
34
|
+
};
|
|
35
|
+
recipe: string;
|
|
36
|
+
models: DeliveryModelEntry[];
|
|
37
|
+
checks: DeliveryCheckResult[];
|
|
38
|
+
/** `pass` iff every check in `checks` is `pass` and `checks` is non-empty. */
|
|
39
|
+
verdict: 'pass' | 'fail';
|
|
40
|
+
summary: {
|
|
41
|
+
total: number;
|
|
42
|
+
pass: number;
|
|
43
|
+
fail: number;
|
|
44
|
+
error: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Build the consolidated report from a resolved recipe, its per-model load outcomes, and every check result. */
|
|
48
|
+
export declare function buildDeliveryReport(recipe: ResolvedDeliveryRecipe, version: string, models: DeliveryModelEntry[], checks: DeliveryCheckResult[]): DeliveryReport;
|
|
49
|
+
/** Render the report as a standalone HTML document — no external assets, safe to open from disk. */
|
|
50
|
+
export declare function renderDeliveryHtml(report: DeliveryReport): string;
|
|
51
|
+
//# sourceMappingURL=delivery-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-report.d.ts","sourceRoot":"","sources":["../../src/commands/delivery-report.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAe,MAAM,sBAAsB,CAAC;AAC/F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,GAAG,cAAc,CAAC;AAEzE,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,iHAAiH;AACjH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,EAAE,EAC5B,MAAM,EAAE,mBAAmB,EAAE,GAC5B,cAAc,CAchB;AA+BD,oGAAoG;AACpG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAoEjE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
/** Build the consolidated report from a resolved recipe, its per-model load outcomes, and every check result. */
|
|
5
|
+
export function buildDeliveryReport(recipe, version, models, checks) {
|
|
6
|
+
const counts = { total: checks.length, pass: 0, fail: 0, error: 0 };
|
|
7
|
+
for (const c of checks)
|
|
8
|
+
counts[c.status]++;
|
|
9
|
+
const verdict = checks.length > 0 && counts.fail === 0 && counts.error === 0 ? 'pass' : 'fail';
|
|
10
|
+
return {
|
|
11
|
+
tool: { name: 'ifc-lite', version },
|
|
12
|
+
recipe: recipe.recipePath,
|
|
13
|
+
models,
|
|
14
|
+
checks,
|
|
15
|
+
verdict,
|
|
16
|
+
summary: counts,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function escapeHtml(value) {
|
|
20
|
+
return value
|
|
21
|
+
.replace(/&/g, '&')
|
|
22
|
+
.replace(/</g, '<')
|
|
23
|
+
.replace(/>/g, '>')
|
|
24
|
+
.replace(/"/g, '"')
|
|
25
|
+
.replace(/'/g, ''');
|
|
26
|
+
}
|
|
27
|
+
function statusBadge(status) {
|
|
28
|
+
const label = status.toUpperCase();
|
|
29
|
+
return `<span class="badge badge-${status}">${label}</span>`;
|
|
30
|
+
}
|
|
31
|
+
function describeCheck(check) {
|
|
32
|
+
if (check.type === 'structural') {
|
|
33
|
+
return `${check.errorCount} error(s), ${check.warningCount} warning(s), ${check.infoCount} info`;
|
|
34
|
+
}
|
|
35
|
+
const parts = [];
|
|
36
|
+
if (check.totalSpecifications !== undefined) {
|
|
37
|
+
parts.push(`${check.passedSpecifications ?? 0}/${check.totalSpecifications} specification(s) passed`);
|
|
38
|
+
}
|
|
39
|
+
if (check.failedEntities !== undefined && check.totalEntities !== undefined) {
|
|
40
|
+
parts.push(`${check.failedEntities}/${check.totalEntities} entities failed`);
|
|
41
|
+
}
|
|
42
|
+
if (check.error)
|
|
43
|
+
parts.push(check.error);
|
|
44
|
+
return parts.join(' — ') || (check.error ?? '');
|
|
45
|
+
}
|
|
46
|
+
/** Render the report as a standalone HTML document — no external assets, safe to open from disk. */
|
|
47
|
+
export function renderDeliveryHtml(report) {
|
|
48
|
+
const rows = report.checks
|
|
49
|
+
.map(check => {
|
|
50
|
+
const source = check.type === 'structural' ? 'structural' : check.source;
|
|
51
|
+
return ` <tr class="row-${check.status}">
|
|
52
|
+
<td>${escapeHtml(check.model)}</td>
|
|
53
|
+
<td>${escapeHtml(check.type)}</td>
|
|
54
|
+
<td>${escapeHtml(source)}</td>
|
|
55
|
+
<td>${statusBadge(check.status)}</td>
|
|
56
|
+
<td>${escapeHtml(describeCheck(check))}</td>
|
|
57
|
+
</tr>`;
|
|
58
|
+
})
|
|
59
|
+
.join('\n');
|
|
60
|
+
const modelRows = report.models
|
|
61
|
+
.map(m => ` <tr>
|
|
62
|
+
<td>${escapeHtml(m.path)}</td>
|
|
63
|
+
<td><code>${escapeHtml(m.sha256 ?? '')}</code></td>
|
|
64
|
+
<td>${m.loadError ? `<span class="badge badge-error">ERROR</span> ${escapeHtml(m.loadError)}` : 'OK'}</td>
|
|
65
|
+
</tr>`)
|
|
66
|
+
.join('\n');
|
|
67
|
+
return `<!doctype html>
|
|
68
|
+
<html lang="en">
|
|
69
|
+
<head>
|
|
70
|
+
<meta charset="utf-8">
|
|
71
|
+
<title>IFC Delivery Check Report</title>
|
|
72
|
+
<style>
|
|
73
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 2rem; color: #1a1a1a; }
|
|
74
|
+
h1 { font-size: 1.4rem; }
|
|
75
|
+
.verdict { font-size: 1.1rem; font-weight: 600; padding: 0.5rem 1rem; border-radius: 4px; display: inline-block; }
|
|
76
|
+
.verdict-pass { background: #e6f4ea; color: #1e7e34; }
|
|
77
|
+
.verdict-fail { background: #fce8e6; color: #c5221f; }
|
|
78
|
+
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
|
|
79
|
+
th, td { border: 1px solid #ddd; padding: 0.4rem 0.6rem; text-align: left; font-size: 0.9rem; }
|
|
80
|
+
th { background: #f5f5f5; }
|
|
81
|
+
.badge { display: inline-block; padding: 0.1rem 0.5rem; border-radius: 3px; font-size: 0.75rem; font-weight: 600; }
|
|
82
|
+
.badge-pass { background: #e6f4ea; color: #1e7e34; }
|
|
83
|
+
.badge-fail { background: #fce8e6; color: #c5221f; }
|
|
84
|
+
.badge-error { background: #fef7e0; color: #8a6d00; }
|
|
85
|
+
.row-error td { background: #fffdf5; }
|
|
86
|
+
</style>
|
|
87
|
+
</head>
|
|
88
|
+
<body>
|
|
89
|
+
<h1>IFC Delivery Check Report</h1>
|
|
90
|
+
<p><strong>Tool:</strong> ${escapeHtml(report.tool.name)} ${escapeHtml(report.tool.version)}<br>
|
|
91
|
+
<strong>Recipe:</strong> <code>${escapeHtml(report.recipe)}</code></p>
|
|
92
|
+
<p class="verdict verdict-${report.verdict}">Verdict: ${report.verdict.toUpperCase()}</p>
|
|
93
|
+
<p>${report.summary.pass} pass, ${report.summary.fail} fail, ${report.summary.error} error (of ${report.summary.total} check(s))</p>
|
|
94
|
+
|
|
95
|
+
<h2>Models</h2>
|
|
96
|
+
<table>
|
|
97
|
+
<thead><tr><th>Path</th><th>SHA-256</th><th>Status</th></tr></thead>
|
|
98
|
+
<tbody>
|
|
99
|
+
${modelRows}
|
|
100
|
+
</tbody>
|
|
101
|
+
</table>
|
|
102
|
+
|
|
103
|
+
<h2>Checks</h2>
|
|
104
|
+
<table>
|
|
105
|
+
<thead><tr><th>Model</th><th>Type</th><th>Source</th><th>Status</th><th>Evidence</th></tr></thead>
|
|
106
|
+
<tbody>
|
|
107
|
+
${rows}
|
|
108
|
+
</tbody>
|
|
109
|
+
</table>
|
|
110
|
+
</body>
|
|
111
|
+
</html>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=delivery-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-report.js","sourceRoot":"","sources":["../../src/commands/delivery-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../src/commands/delivery.ts"],"names":[],"mappings":"AAyCA,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EnE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
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 delivery <recipe.json> [--json] [--out <report.json>] [--html <report.html>]
|
|
6
|
+
*
|
|
7
|
+
* Run a repeatable, reviewable model-delivery check from a saved recipe
|
|
8
|
+
* (see `delivery-recipe.ts`): structural validation (the same rules
|
|
9
|
+
* `ifc-lite validate` runs) and/or IDS validation (the same validator
|
|
10
|
+
* `ifc-lite ids` runs), against one or more model files, in one invocation.
|
|
11
|
+
*
|
|
12
|
+
* Every check reports one of `pass` / `fail` / `error` — never folded
|
|
13
|
+
* together (see `delivery-checks.ts`). The overall verdict is `pass` only
|
|
14
|
+
* when every declared check on every declared model passed; an unreadable
|
|
15
|
+
* model, an unreadable/empty IDS ruleset, or any failed check all produce a
|
|
16
|
+
* `fail` verdict, matching the boundary in issue #3931: "Zero applicable
|
|
17
|
+
* checks or an unreadable model must not be a successful delivery verdict."
|
|
18
|
+
*
|
|
19
|
+
* Output: a human-readable summary to stdout by default, `--json` for the
|
|
20
|
+
* consolidated machine-readable report instead (or `--out <file>` to write
|
|
21
|
+
* that JSON to disk rather than stdout), and `--html <file>` to additionally
|
|
22
|
+
* write a standalone HTML report — the two forms the issue asks for,
|
|
23
|
+
* produced by one invocation.
|
|
24
|
+
*
|
|
25
|
+
* Determinism: given the same recipe and the same bytes on disk, running
|
|
26
|
+
* this command twice produces byte-identical `--json` output (see the
|
|
27
|
+
* contract documented in `delivery-report.ts`).
|
|
28
|
+
*/
|
|
29
|
+
import { getFlag, hasFlag, fatal, printJson, writeOutput } from '../output.js';
|
|
30
|
+
import { readCliVersion } from '../version.js';
|
|
31
|
+
import { fileURLToPath } from 'node:url';
|
|
32
|
+
import { dirname, join, relative } from 'node:path';
|
|
33
|
+
import { loadDeliveryRecipe } from './delivery-recipe.js';
|
|
34
|
+
import { loadModelForDelivery, runStructuralCheck, runIdsCheck } from './delivery-checks.js';
|
|
35
|
+
import { buildDeliveryReport, renderDeliveryHtml } from './delivery-report.js';
|
|
36
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
const CLI_VERSION = readCliVersion(join(__dirname, '..', '..', 'package.json'));
|
|
38
|
+
export async function deliveryCommand(args) {
|
|
39
|
+
const recipePath = args.find(a => !a.startsWith('-'));
|
|
40
|
+
if (!recipePath) {
|
|
41
|
+
fatal('Usage: ifc-lite delivery <recipe.json> [--json] [--out report.json] [--html report.html]');
|
|
42
|
+
}
|
|
43
|
+
const jsonOutput = hasFlag(args, '--json');
|
|
44
|
+
const outPath = getFlag(args, '--out');
|
|
45
|
+
const htmlPath = getFlag(args, '--html');
|
|
46
|
+
const recipe = await loadDeliveryRecipe(recipePath);
|
|
47
|
+
const models = [];
|
|
48
|
+
const checks = [];
|
|
49
|
+
// Declared order throughout: models in recipe order, and within each
|
|
50
|
+
// model, structural first (when requested) then every --ids file in
|
|
51
|
+
// recipe order. No Map/object iteration anywhere in this loop — see
|
|
52
|
+
// delivery-report.ts's determinism contract.
|
|
53
|
+
for (let i = 0; i < recipe.resolvedModels.length; i++) {
|
|
54
|
+
const declaredPath = recipe.models[i];
|
|
55
|
+
const absPath = recipe.resolvedModels[i];
|
|
56
|
+
const loaded = await loadModelForDelivery(absPath);
|
|
57
|
+
if ('error' in loaded) {
|
|
58
|
+
models.push({ path: declaredPath, loadError: loaded.error });
|
|
59
|
+
// The model could not be loaded: every check the recipe declares for
|
|
60
|
+
// it is unevaluable, and reports `error` — never silently omitted
|
|
61
|
+
// (which would shrink the check count and could flip an
|
|
62
|
+
// otherwise-failing recipe to a false "pass") and never `pass`.
|
|
63
|
+
if (recipe.structural) {
|
|
64
|
+
checks.push({
|
|
65
|
+
type: 'structural', model: declaredPath, status: 'error',
|
|
66
|
+
errorCount: 0, warningCount: 0, infoCount: 0, issues: [],
|
|
67
|
+
error: `model ${loaded.error}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
for (const idsDeclared of recipe.ids) {
|
|
71
|
+
checks.push({
|
|
72
|
+
type: 'ids', model: declaredPath, source: idsDeclared, status: 'error',
|
|
73
|
+
error: `model ${loaded.error}`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
models.push({ path: declaredPath, sha256: loaded.sha256 });
|
|
79
|
+
if (recipe.structural) {
|
|
80
|
+
checks.push(runStructuralCheck(declaredPath, loaded.store));
|
|
81
|
+
}
|
|
82
|
+
for (let j = 0; j < recipe.resolvedIds.length; j++) {
|
|
83
|
+
const result = await runIdsCheck(declaredPath, loaded.store, recipe.resolvedIds[j]);
|
|
84
|
+
checks.push({ ...result, source: recipe.ids[j] });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const report = buildDeliveryReport(recipe, CLI_VERSION, models, checks);
|
|
88
|
+
if (htmlPath) {
|
|
89
|
+
await writeOutput(renderDeliveryHtml(report), htmlPath);
|
|
90
|
+
}
|
|
91
|
+
if (jsonOutput || outPath) {
|
|
92
|
+
if (outPath) {
|
|
93
|
+
await writeOutput(JSON.stringify(report, null, 2), outPath);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
printJson(report);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
printHumanSummary(report, recipePath);
|
|
101
|
+
}
|
|
102
|
+
if (report.verdict !== 'pass')
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
function printHumanSummary(report, recipePath) {
|
|
106
|
+
process.stdout.write(`\n Delivery check: ${recipePath}\n`);
|
|
107
|
+
process.stdout.write(` ifc-lite ${report.tool.version}\n\n`);
|
|
108
|
+
for (const m of report.models) {
|
|
109
|
+
const rel = relative(process.cwd(), m.path);
|
|
110
|
+
if (m.loadError) {
|
|
111
|
+
process.stdout.write(` [ERR] ${rel}: ${m.loadError}\n`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
process.stdout.write(` [OK] ${rel} (sha256 ${m.sha256?.slice(0, 12)}…)\n`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
process.stdout.write('\n');
|
|
118
|
+
for (const c of report.checks) {
|
|
119
|
+
const icon = c.status === 'pass' ? 'PASS' : c.status === 'fail' ? 'FAIL' : 'ERR ';
|
|
120
|
+
const source = c.type === 'structural' ? 'structural' : c.source;
|
|
121
|
+
const detail = c.error ? ` — ${c.error}` : '';
|
|
122
|
+
process.stdout.write(` [${icon}] ${relative(process.cwd(), c.model)} :: ${source}${detail}\n`);
|
|
123
|
+
}
|
|
124
|
+
process.stdout.write(`\n ${report.summary.pass} pass, ${report.summary.fail} fail, ${report.summary.error} error (of ${report.summary.total})\n`);
|
|
125
|
+
process.stdout.write(` Verdict: ${report.verdict.toUpperCase()}\n\n`);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../src/commands/delivery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ClassificationInfo } from '@ifc-lite/parser';
|
|
2
|
+
/**
|
|
3
|
+
* Project one resolved classification reference to the single string
|
|
4
|
+
* `DataFingerprintInput.classifications` hashes: `system:identification`,
|
|
5
|
+
* falling back to `name` when the reference carries no identification code.
|
|
6
|
+
* Both blank is "no label" — `''` lets the fingerprint's blank filter drop
|
|
7
|
+
* it, the same rule a blank material name gets. The MCP server's
|
|
8
|
+
* (`packages/mcp/src/tools/diff-fingerprints.ts`) byte-identical twin — must
|
|
9
|
+
* stay one.
|
|
10
|
+
*/
|
|
11
|
+
export declare function classificationLabel(info: ClassificationInfo): string;
|
|
12
|
+
//# sourceMappingURL=diff-classification-label.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-classification-label.d.ts","sourceRoot":"","sources":["../../src/commands/diff-classification-label.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAKpE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
* Project one resolved classification reference to the single string
|
|
6
|
+
* `DataFingerprintInput.classifications` hashes: `system:identification`,
|
|
7
|
+
* falling back to `name` when the reference carries no identification code.
|
|
8
|
+
* Both blank is "no label" — `''` lets the fingerprint's blank filter drop
|
|
9
|
+
* it, the same rule a blank material name gets. The MCP server's
|
|
10
|
+
* (`packages/mcp/src/tools/diff-fingerprints.ts`) byte-identical twin — must
|
|
11
|
+
* stay one.
|
|
12
|
+
*/
|
|
13
|
+
export function classificationLabel(info) {
|
|
14
|
+
const system = info.system?.trim() ?? '';
|
|
15
|
+
const code = (info.identification || info.name || '').trim();
|
|
16
|
+
if (!system && !code)
|
|
17
|
+
return '';
|
|
18
|
+
return `${system}:${code}`;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=diff-classification-label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-classification-label.js","sourceRoot":"","sources":["../../src/commands/diff-classification-label.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAI/D;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAwB;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -1 +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,
|
|
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,EAWL,KAAK,YAAY,EAElB,MAAM,kBAAkB,CAAC;AAI1B,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,CAsBvF"}
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
import { createHash } from 'node:crypto';
|
|
29
29
|
import { buildComponentFingerprints, buildDataFingerprint, } from '@ifc-lite/diff';
|
|
30
30
|
import { RelationshipType } from '@ifc-lite/data';
|
|
31
|
-
import { EntityExtractor, extractAllEntityAttributes, extractPropertiesOnDemand, extractQuantitiesOnDemand, getAttributeNamesAcrossSchemas, } from '@ifc-lite/parser';
|
|
31
|
+
import { EntityExtractor, extractAllEntityAttributes, extractClassificationsOnDemand, extractProjectUnits, extractPropertiesOnDemand, extractQuantitiesOnDemand, getAttributeNamesAcrossSchemas, quantitySiScale, roundToScale, scaledPropertyValue, } from '@ifc-lite/parser';
|
|
32
|
+
import { classificationLabel } from './diff-classification-label.js';
|
|
32
33
|
import { comparableEntities } from './diff-scope.js';
|
|
33
34
|
/**
|
|
34
35
|
* Content digest of a model's bytes, as written into the sidecar.
|
|
@@ -51,9 +52,18 @@ export function modelIdentityOf(path, bytes) {
|
|
|
51
52
|
* section of `docs/guide/model-diff.md`).
|
|
52
53
|
*/
|
|
53
54
|
export function buildFileFingerprints(store) {
|
|
55
|
+
// Resolved once per store, not per entity: the same `IFCUNITASSIGNMENT` walk
|
|
56
|
+
// every property in the file would otherwise repeat, and both `quantitySiScale`
|
|
57
|
+
// and `scaleMeasureValue` are pure over it. An IfcQuantityLength/Area/Volume
|
|
58
|
+
// is stored in the project's raw author unit, exactly like a measure-typed
|
|
59
|
+
// property, so both need the same base-SI conversion before hashing —
|
|
60
|
+
// otherwise a model re-authored in a different project length unit, with no
|
|
61
|
+
// physical quantity actually changed, reports every quantified/measured
|
|
62
|
+
// entity as `modified` (see `buildDataInput`'s scaling comment).
|
|
63
|
+
const units = extractProjectUnits(store.source, store.entityIndex);
|
|
54
64
|
const fingerprints = [];
|
|
55
65
|
for (const { expressId, globalId, ifcType, source, isTypeObject } of comparableEntities(store)) {
|
|
56
|
-
const input = buildDataInput(store, expressId, ifcType, source, isTypeObject);
|
|
66
|
+
const input = buildDataInput(store, expressId, ifcType, source, isTypeObject, units);
|
|
57
67
|
fingerprints.push({
|
|
58
68
|
key: globalId,
|
|
59
69
|
ifcType,
|
|
@@ -78,7 +88,11 @@ function buildDataInput(store, expressId, ifcType,
|
|
|
78
88
|
* accessors then answer '' for every display attribute. */
|
|
79
89
|
source,
|
|
80
90
|
/** `IfcTypeObject` subtype? Gates `Tag` into the fingerprint (issue #2021). */
|
|
81
|
-
isTypeObject
|
|
91
|
+
isTypeObject,
|
|
92
|
+
/** The file's declared units, resolved once by {@link buildFileFingerprints},
|
|
93
|
+
* for scaling Qto_ Length/Area/Volume quantities and measure-typed Pset
|
|
94
|
+
* properties to base SI before hashing. */
|
|
95
|
+
units) {
|
|
82
96
|
const predefinedType = extractAllEntityAttributes(store, expressId).find((attribute) => attribute.name === 'PredefinedType')?.value;
|
|
83
97
|
// `Tag` for a TYPE OBJECT only. On an occurrence it is the authoring tool's
|
|
84
98
|
// element id, which changes across producers while the design does not, and
|
|
@@ -89,16 +103,21 @@ isTypeObject) {
|
|
|
89
103
|
const tag = isTypeObject ? attributeAcrossSchemas(store, expressId, ifcType, 'Tag') : undefined;
|
|
90
104
|
const propertySets = extractPropertiesOnDemand(store, expressId).map((set) => ({
|
|
91
105
|
name: set.name,
|
|
92
|
-
properties: set.properties.map((property) => ({
|
|
106
|
+
properties: set.properties.map((property) => ({
|
|
107
|
+
name: property.name,
|
|
108
|
+
value: scaledPropertyValue(property.value, property.dataType, units),
|
|
109
|
+
})),
|
|
93
110
|
}));
|
|
94
111
|
const quantitySets = extractQuantitiesOnDemand(store, expressId).map((set) => ({
|
|
95
112
|
name: set.name,
|
|
96
113
|
quantities: set.quantities.map((quantity) => ({
|
|
97
114
|
name: quantity.name,
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
115
|
+
// Scaled to base SI first (a Qto_ value is stored in the project's raw
|
|
116
|
+
// author unit, see `quantitySiScale`), then rounded to 4 dp, matching
|
|
117
|
+
// the viewer: re-exporting a model with sub-tolerance float jitter must
|
|
118
|
+
// not flip the data hash on an otherwise identical element, which on
|
|
119
|
+
// this path would cost the pair its match.
|
|
120
|
+
value: roundToScale(quantity.value * quantitySiScale(quantity, units)),
|
|
102
121
|
})),
|
|
103
122
|
}));
|
|
104
123
|
const typeAssignments = store.relationships
|
|
@@ -108,6 +127,11 @@ isTypeObject) {
|
|
|
108
127
|
name: store.entities.getName(typeId) || undefined,
|
|
109
128
|
type: store.entities.getTypeName(typeId) || undefined,
|
|
110
129
|
}));
|
|
130
|
+
// Resolved classification references (never entity references — see
|
|
131
|
+
// `DataFingerprintInput.classifications`): an `IfcRelAssociatesClassification`
|
|
132
|
+
// has no channel of its own here, so a re-coded element — geometry and every
|
|
133
|
+
// property untouched — previously read as unchanged on every path.
|
|
134
|
+
const classifications = extractClassificationsOnDemand(store, expressId).map(classificationLabel);
|
|
111
135
|
return {
|
|
112
136
|
ifcType,
|
|
113
137
|
name: store.entities.getName(expressId) || source?.name || undefined,
|
|
@@ -118,11 +142,9 @@ isTypeObject) {
|
|
|
118
142
|
propertySets,
|
|
119
143
|
quantitySets,
|
|
120
144
|
typeAssignments,
|
|
145
|
+
classifications,
|
|
121
146
|
};
|
|
122
147
|
}
|
|
123
|
-
function roundQuantity(value) {
|
|
124
|
-
return Number.isFinite(value) ? Math.round(value * 1e4) / 1e4 : value;
|
|
125
|
-
}
|
|
126
148
|
/**
|
|
127
149
|
* One named attribute, read positionally through the **cross-schema** attribute
|
|
128
150
|
* list (issue #2021).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-engine.js","sourceRoot":"","sources":["../../src/commands/diff-engine.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GAIrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,8BAA8B,
|
|
1
|
+
{"version":3,"file":"diff-engine.js","sourceRoot":"","sources":["../../src/commands/diff-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -94,4 +94,16 @@ export declare function infraRevisionModel(revision: 1 | 2): string;
|
|
|
94
94
|
export declare const BASE_MODEL: string;
|
|
95
95
|
/** Same building, re-exported: the two walls carry brand-new GlobalIds. */
|
|
96
96
|
export declare const HEAD_MODEL: string;
|
|
97
|
+
/** A minimal model with one wall carrying one `Qto_` length quantity, under a
|
|
98
|
+
* chosen project length unit and a chosen raw quantity value — for the
|
|
99
|
+
* unit-of-measure oracle case (a Qto_ value is stored in the project's raw
|
|
100
|
+
* author unit, not base SI; see `quantitySiScale`). */
|
|
101
|
+
export declare function quantityModel(lengthUnit: 'METRE' | 'MILLIMETRE', qtyValue: number, explicitQuantityUnit?: 'METRE' | 'MILLIMETRE'): string;
|
|
102
|
+
/**
|
|
103
|
+
* A minimal model with one wall associated to one Uniclass-style
|
|
104
|
+
* classification reference, under a chosen identification code — for the
|
|
105
|
+
* classification oracle case (an `IfcRelAssociatesClassification` re-coding
|
|
106
|
+
* with no geometry, property or quantity edit).
|
|
107
|
+
*/
|
|
108
|
+
export declare function classificationModel(identification: string): string;
|
|
97
109
|
//# sourceMappingURL=diff-test-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-test-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/diff-test-helpers.ts"],"names":[],"mappings":"AAIA,kFAAkF;AAElF,yDAAyD;AACzD,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;wDAEwD;AACxD,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA4B1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA+BtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA0B5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAwBrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAmBtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CA+B1D;AAED,eAAO,MAAM,UAAU,QAAoC,CAAC;AAC5D,2EAA2E;AAC3E,eAAO,MAAM,UAAU,QAAoC,CAAC"}
|
|
1
|
+
{"version":3,"file":"diff-test-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/diff-test-helpers.ts"],"names":[],"mappings":"AAIA,kFAAkF;AAElF,yDAAyD;AACzD,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;wDAEwD;AACxD,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA4B1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA+BtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA0B5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAwBrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAmBtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CA+B1D;AAED,eAAO,MAAM,UAAU,QAAoC,CAAC;AAC5D,2EAA2E;AAC3E,eAAO,MAAM,UAAU,QAAoC,CAAC;AAE5D;;;wDAGwD;AACxD,wBAAgB,aAAa,CAC3B,UAAU,EAAE,OAAO,GAAG,YAAY,EAClC,QAAQ,EAAE,MAAM,EAChB,oBAAoB,CAAC,EAAE,OAAO,GAAG,YAAY,GAC5C,MAAM,CAiCR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAwBlE"}
|