@ifc-lite/clash 1.9.2 → 2.1.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/index.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
  export * from './types.js';
11
11
  export { matchesSelector } from './selectors.js';
12
+ export { clashMemberKey, clashMemberSet, inClashSet } from './members.js';
12
13
  export { DISCIPLINES, CLASH_RULE_PRESETS, inferClashSeverity, disciplineMatrixRules, rulesFromPresets, type Discipline, type DisciplineInfo, type ClashRulePreset, } from './disciplines.js';
13
14
  export { createClashEngine, type ClashEngine, type ClashBackend, type CreateClashEngineOptions } from './engine.js';
14
15
  export { makeExclusionSet, isExcluded, pairKey, qualifiedKey } from './exclude.js';
@@ -17,7 +18,8 @@ export { groupClashes, isClusterGroupingIneffective, type GroupOptions } from '.
17
18
  export { groupDuplicateSets } from './duplicate-sets.js';
18
19
  export { clashReviewKey, aggregateReviewStatus, reviewStatusToBcfTopicStatus, } from './review.js';
19
20
  export { compareClashRuns, type ClashRevisionDiff } from './lifecycle.js';
20
- export { SEVERITY_RANK, TOUCHING_EPSILON, penetrationDepth, isTouching, sortClashes, summarizeClashes, ruleHadNoMatch, classifyRuleCoverage, type ClashSortBy, type RuleCoverageOutcome, } from './analysis.js';
21
+ export { compareClashRevisions, type ClashRevisionSide, type ClashRevisionComparison, type ClashRevisionReasons, } from './revision.js';
22
+ export { SEVERITY_RANK, TOUCHING_EPSILON, penetrationDepth, isTouching, sortClashes, summarizeClashes, ruleHadNoMatch, classifyRuleCoverage, describeEmptyRuleSides, type ClashSortBy, type RuleCoverageOutcome, } from './analysis.js';
21
23
  export { findDuplicates, DUPLICATES_RULE, type DuplicateOptions, } from './duplicates.js';
22
24
  /**
23
25
  * Per-triangle mesh analysis, re-exported for consumers outside this package
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACpH,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,4BAA4B,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB;;;;;;;GAOG;AACH,OAAO,EACL,YAAY,EACZ,KAAK,QAAQ,GACd,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACpH,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,4BAA4B,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB;;;;;;;GAOG;AACH,OAAO,EACL,YAAY,EACZ,KAAK,QAAQ,GACd,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@
12
12
  */
13
13
  export * from './types.js';
14
14
  export { matchesSelector } from './selectors.js';
15
+ export { clashMemberKey, clashMemberSet, inClashSet } from './members.js';
15
16
  export { DISCIPLINES, CLASH_RULE_PRESETS, inferClashSeverity, disciplineMatrixRules, rulesFromPresets, } from './disciplines.js';
16
17
  export { createClashEngine } from './engine.js';
17
18
  export { makeExclusionSet, isExcluded, pairKey, qualifiedKey } from './exclude.js';
@@ -20,7 +21,8 @@ export { groupClashes, isClusterGroupingIneffective } from './grouping.js';
20
21
  export { groupDuplicateSets } from './duplicate-sets.js';
21
22
  export { clashReviewKey, aggregateReviewStatus, reviewStatusToBcfTopicStatus, } from './review.js';
22
23
  export { compareClashRuns } from './lifecycle.js';
23
- export { SEVERITY_RANK, TOUCHING_EPSILON, penetrationDepth, isTouching, sortClashes, summarizeClashes, ruleHadNoMatch, classifyRuleCoverage, } from './analysis.js';
24
+ export { compareClashRevisions, } from './revision.js';
25
+ export { SEVERITY_RANK, TOUCHING_EPSILON, penetrationDepth, isTouching, sortClashes, summarizeClashes, ruleHadNoMatch, classifyRuleCoverage, describeEmptyRuleSides, } from './analysis.js';
24
26
  export { findDuplicates, DUPLICATES_RULE, } from './duplicates.js';
25
27
  /**
26
28
  * Per-triangle mesh analysis, re-exported for consumers outside this package
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAsE,MAAM,aAAa,CAAC;AACpH,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,4BAA4B,EAAqB,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAA0B,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,oBAAoB,GAGrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,eAAe,GAEhB,MAAM,iBAAiB,CAAC;AACzB;;;;;;;GAOG;AACH,OAAO,EACL,YAAY,GAEb,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAsE,MAAM,aAAa,CAAC;AACpH,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,4BAA4B,EAAqB,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAA0B,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EACL,qBAAqB,GAItB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,sBAAsB,GAGvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,eAAe,GAEhB,MAAM,iBAAiB,CAAC;AACzB;;;;;;;GAOG;AACH,OAAO,EACL,YAAY,GAEb,MAAM,uBAAuB,CAAC"}
@@ -1,14 +1,3 @@
1
- /**
2
- * Clash lifecycle across model revisions (Phase 5).
3
- *
4
- * Compares two clash runs and partitions their clashes into added / resolved /
5
- * persistent buckets. Matching is by the stable `clash.id`, which is durable
6
- * across revisions because it derives from the elements' durable keys
7
- * (IfcGUID / USD prim path) plus the rule id — not from runtime refs that
8
- * change between loads. This makes the diff stable: a clash that survives a
9
- * revision keeps its id and is reported as `persistent` rather than as a
10
- * resolve-plus-add churn.
11
- */
12
1
  import type { Clash, ClashResult } from './types.js';
13
2
  /**
14
3
  * The result of comparing a previous clash run to a later ("next") one.
@@ -17,6 +6,9 @@ import type { Clash, ClashResult } from './types.js';
17
6
  * - `persistent` clashes present in both runs (still open; the `next` Clash)
18
7
  * - `resolved` clashes present in `previous` but not in `next` (fixed/removed)
19
8
  *
9
+ * "Present in" counts occurrences, not just review keys: two clashes sharing a
10
+ * review key in one run need a counterpart each to both be `persistent`.
11
+ *
20
12
  * Each array is sorted by `clash.id` for deterministic, diff-friendly output.
21
13
  */
22
14
  export interface ClashRevisionDiff {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,UAAU,EAAE,KAAK,EAAE,CAAC;IACpB,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAyBD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,iBAAiB,CAsC5F"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,UAAU,EAAE,KAAK,EAAE,CAAC;IACpB,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAsID;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,iBAAiB,CA8B5F"}
package/dist/lifecycle.js CHANGED
@@ -1,6 +1,35 @@
1
1
  /* This Source Code Form is subject to the terms of the Mozilla Public
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Clash lifecycle across model revisions (Phase 5).
6
+ *
7
+ * Compares two clash runs and partitions their clashes into added / resolved /
8
+ * persistent buckets. Matching is by `clashReviewKey` (review.ts) — the rule id
9
+ * plus the two elements' durable keys (IfcGUID / USD prim path), order-
10
+ * independent — NOT by the raw `clash.id`. `clash.id` (`engine-ts/orchestrator.
11
+ * ts`'s `clashId()`) also folds in `ClashElement.model`, which review.ts
12
+ * documents as "an ephemeral per-load id in the viewer": two loads of the
13
+ * identical geometry — exactly the "revision" scenario this module exists to
14
+ * diff — get two different `model` values and therefore two different
15
+ * `clash.id`s for the same real-world clash. Matching on the review key keeps
16
+ * the diff stable across loads: a clash that survives a revision is reported
17
+ * as `persistent` rather than as a resolve-plus-add churn.
18
+ *
19
+ * The review key is NOT unique within a run, and the matching below is built
20
+ * around that. Dropping `model` is what makes the key durable, but the engine
21
+ * treats `(model, key)` as element identity — `orchestrator.ts` skips a pair
22
+ * only when `elA.key === elB.key && elA.model === elB.model` — and
23
+ * `adapters/ifcx.ts` keys on the bare USD prim path while `adapters/step.ts`
24
+ * keys on the bare IfcGUID (only its `syntheticKey` fallback folds in the
25
+ * model id). A federated run gathers every loaded model, so one run can
26
+ * legitimately hold several DISTINCT clashes under one review key: `/Duct` in
27
+ * layer-a and `/Duct` in layer-b each hitting the same wall. Matching by key
28
+ * membership alone would collapse those, silently swallowing a resolved clash
29
+ * and reporting a genuinely new one as pre-existing, so occurrences are
30
+ * grouped per key and paired (see `pairGroup`) instead.
31
+ */
32
+ import { clashReviewKey } from './review.js';
4
33
  /** Stable string compare for ids (ASCII/Unicode code-point order). */
5
34
  function byId(a, b) {
6
35
  if (a.id < b.id)
@@ -10,19 +39,108 @@ function byId(a, b) {
10
39
  return 0;
11
40
  }
12
41
  /**
13
- * Index a run's clashes by their stable id for O(1) membership checks. The
14
- * engine dedups clash ids within a run, so ids are unique here. If a caller
15
- * passed a hand-built run with a repeated id, the map keeps the last occurrence
16
- * for membership tests, while the added/persistent/resolved buckets below
17
- * iterate the raw clash arrays so a duplicate id would appear once per
18
- * occurrence in its bucket and in the counts.
42
+ * Group a run's clashes by their durable review key (`clashReviewKey`, not
43
+ * `clash.id` see the module docstring), keeping EVERY occurrence rather than
44
+ * the last one. The key is deliberately not unique within a run (module
45
+ * docstring), so the group length is the multiplicity that `pairGroup` needs
46
+ * in order to tell "the same clash, seen again" from "a second, different
47
+ * clash that happens to share a review key".
19
48
  */
20
- function indexById(run) {
21
- const byKey = new Map();
49
+ function groupByReviewKey(run) {
50
+ const groups = new Map();
51
+ for (const clash of run.clashes) {
52
+ const key = clashReviewKey(clash);
53
+ const group = groups.get(key);
54
+ if (group)
55
+ group.push(clash);
56
+ else
57
+ groups.set(key, [clash]);
58
+ }
59
+ return groups;
60
+ }
61
+ /** Every `model` id that appears on any clash in a run. `toRef`
62
+ * (`engine-ts/orchestrator.ts`) carries `model` onto both of a `Clash`'s
63
+ * element refs, so this is readable from a `ClashResult` alone. A model that
64
+ * produced no clash at all in the run is invisible here — see `pairGroup`. */
65
+ function modelsInRun(run) {
66
+ const models = new Set();
22
67
  for (const clash of run.clashes) {
23
- byKey.set(clash.id, clash);
68
+ models.add(clash.a.model);
69
+ models.add(clash.b.model);
70
+ }
71
+ return models;
72
+ }
73
+ /** True when both of a clash's elements sit in models the other run also
74
+ * clashed in, i.e. that clash's `clash.id` was still mintable there. */
75
+ function bothModelsIn(clash, models) {
76
+ return models.has(clash.a.model) && models.has(clash.b.model);
77
+ }
78
+ /** Split into [matching, rest], preserving run order in both. */
79
+ function partition(clashes, keep) {
80
+ const yes = [];
81
+ const no = [];
82
+ for (const clash of clashes)
83
+ (keep(clash) ? yes : no).push(clash);
84
+ return [yes, no];
85
+ }
86
+ /**
87
+ * Pair one review key's previous-run occurrences against its next-run ones.
88
+ *
89
+ * Equal `clash.id`s are paired first. `clashId()` folds both elements' `model`
90
+ * into the id, so an id match is the same clash between the same two loaded
91
+ * models — which covers the whole of the "federated session that did not
92
+ * reload" case, pairing every surviving clash with ITSELF.
93
+ *
94
+ * A leftover is the same real-world clash seen again only if `model` was
95
+ * re-minted between the runs, and the data says whether it was: a previous-run
96
+ * leftover whose two models BOTH still clash somewhere in the next run was not
97
+ * re-minted, so had that clash survived it would have produced the same id and
98
+ * paired above — it is genuinely `resolved`. The mirror of that is genuinely
99
+ * `added`. Only leftovers whose models the other run no longer shows — the
100
+ * re-load this whole module exists to diff — are paired with each other, in
101
+ * run order, and the surplus on either side is `added` / `resolved`. Without
102
+ * that model test, two runs of one federated session in which the wall stops
103
+ * hitting `/Duct` in layer-a and starts hitting `/Duct` in layer-b would pair
104
+ * on count alone: the fixed clash would vanish from `resolved` and the new one
105
+ * would be reported as pre-existing.
106
+ *
107
+ * The model ids are read off the two runs' clashes, so a model that produced
108
+ * no clash at all in a run is invisible to the test and its clashes count as
109
+ * re-minted. That errs towards pairing, i.e. towards `persistent`: the
110
+ * churn-free reading, not a claim that something was fixed.
111
+ *
112
+ * `persistent` always receives the NEXT run's Clash (current geometry).
113
+ */
114
+ function pairGroup(previous, next, models, out) {
115
+ const prevIndicesById = new Map();
116
+ previous.forEach((clash, i) => {
117
+ const slot = prevIndicesById.get(clash.id);
118
+ if (slot)
119
+ slot.push(i);
120
+ else
121
+ prevIndicesById.set(clash.id, [i]);
122
+ });
123
+ const matchedPrev = new Array(previous.length).fill(false);
124
+ const unmatchedNext = [];
125
+ for (const clash of next) {
126
+ const index = prevIndicesById.get(clash.id)?.shift();
127
+ if (index === undefined) {
128
+ unmatchedNext.push(clash);
129
+ }
130
+ else {
131
+ matchedPrev[index] = true;
132
+ out.persistent.push(clash);
133
+ }
24
134
  }
25
- return byKey;
135
+ const unmatchedPrev = previous.filter((_, i) => !matchedPrev[i]);
136
+ const [reloadedPrev, stillLivePrev] = partition(unmatchedPrev, (clash) => !bothModelsIn(clash, models.next));
137
+ const [reloadedNext, stillLiveNext] = partition(unmatchedNext, (clash) => !bothModelsIn(clash, models.previous));
138
+ out.resolved.push(...stillLivePrev);
139
+ out.added.push(...stillLiveNext);
140
+ const paired = Math.min(reloadedPrev.length, reloadedNext.length);
141
+ out.persistent.push(...reloadedNext.slice(0, paired));
142
+ out.added.push(...reloadedNext.slice(paired));
143
+ out.resolved.push(...reloadedPrev.slice(paired));
26
144
  }
27
145
  /**
28
146
  * Compare two clash runs and partition their clashes by lifecycle state.
@@ -33,26 +151,18 @@ function indexById(run) {
33
151
  * caller can render the up-to-date state. Each array is sorted by id.
34
152
  */
35
153
  export function compareClashRuns(previous, next) {
36
- const prevById = indexById(previous);
37
- const nextById = indexById(next);
38
- const added = [];
39
- const persistent = [];
40
- const resolved = [];
41
- for (const clash of next.clashes) {
42
- if (prevById.has(clash.id)) {
43
- // Present in both runs: still open. Report the next run's Clash so the
44
- // caller sees current geometry, not the stale previous-revision copy.
45
- persistent.push(clash);
46
- }
47
- else {
48
- added.push(clash);
49
- }
154
+ const prevGroups = groupByReviewKey(previous);
155
+ const nextGroups = groupByReviewKey(next);
156
+ const models = { previous: modelsInRun(previous), next: modelsInRun(next) };
157
+ const buckets = { added: [], persistent: [], resolved: [] };
158
+ for (const [key, nextGroup] of nextGroups) {
159
+ pairGroup(prevGroups.get(key) ?? [], nextGroup, models, buckets);
50
160
  }
51
- for (const clash of previous.clashes) {
52
- if (!nextById.has(clash.id)) {
53
- resolved.push(clash);
54
- }
161
+ for (const [key, prevGroup] of prevGroups) {
162
+ if (!nextGroups.has(key))
163
+ buckets.resolved.push(...prevGroup);
55
164
  }
165
+ const { added, persistent, resolved } = buckets;
56
166
  added.sort(byId);
57
167
  persistent.sort(byId);
58
168
  resolved.sort(byId);
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAgC/D,sEAAsE;AACtE,SAAS,IAAI,CAAC,CAAQ,EAAE,CAAQ;IAC9B,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,SAAS,CAAC,GAAgB;IACjC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAiB,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAChC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAqB,EAAE,IAAiB;IACvE,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAEjC,MAAM,KAAK,GAAY,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAY,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAY,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3B,uEAAuE;YACvE,sEAAsE;YACtE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEpB,OAAO;QACL,KAAK;QACL,UAAU;QACV,QAAQ;QACR,OAAO,EAAE;YACP,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,UAAU,EAAE,UAAU,CAAC,MAAM;YAC7B,QAAQ,EAAE,QAAQ,CAAC,MAAM;SAC1B;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ClashElement } from './types.js';
2
+ /**
3
+ * The membership identity of one element: its model plus its `ref`, encoded
4
+ * with the engine's existing model-qualified scheme (`qualifiedKey`) so there
5
+ * is one such encoding in this package rather than two.
6
+ */
7
+ export declare function clashMemberKey(model: string, ref: number): string;
8
+ /** Index a rule's member list; `null` when the side has no explicit members. */
9
+ export declare function clashMemberSet(members: readonly string[] | undefined): ReadonlySet<string> | null;
10
+ /** Whether one element belongs to a rule side, by membership when the side has
11
+ * an explicit member set and by type selector otherwise. */
12
+ export declare function inClashSet(element: Pick<ClashElement, 'model' | 'ref' | 'tag'>, selector: string, members: ReadonlySet<string> | null): boolean;
13
+ //# sourceMappingURL=members.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../src/members.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACrC,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAE5B;AAED;6DAC6D;AAC7D,wBAAgB,UAAU,CACxB,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,KAAK,GAAG,KAAK,CAAC,EACpD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,GAClC,OAAO,CAGT"}
@@ -0,0 +1,46 @@
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
+ * Explicit MEMBERSHIP for a rule's A / B set.
6
+ *
7
+ * `ClashRule.a` / `.b` are type-name selectors, and a `ClashElement` carries
8
+ * nothing but its type (`tag`), name and storey — so a selector cannot express
9
+ * "walls whose `Pset_WallCommon.IsExternal` is true". A caller that DOES have
10
+ * the model (the viewer resolves its advanced filter against the parsed store)
11
+ * resolves such a set to element identities and hands them over as
12
+ * `membersA` / `membersB`; the engine then partitions by membership instead of
13
+ * by the selector, and everything downstream — severity, exclusions, dedup,
14
+ * coverage, both kernels — is untouched.
15
+ *
16
+ * The identity is `(model, ref)`, not `key`. `ref` is what a caller holding a
17
+ * `(modelId, expressId)` pair can compute directly, while `key` is built inside
18
+ * the adapter out of the stored GlobalId plus an occurrence suffix and cannot
19
+ * be reconstructed from the store alone. Two GPU-instanced occurrences of one
20
+ * entity share a `ref` and both land in the set, which is the intended reading
21
+ * of "this entity is in set A".
22
+ *
23
+ * An EMPTY member list is not an absent one — see `ClashRule.membersA`.
24
+ */
25
+ import { qualifiedKey } from './exclude.js';
26
+ import { matchesSelector } from './selectors.js';
27
+ /**
28
+ * The membership identity of one element: its model plus its `ref`, encoded
29
+ * with the engine's existing model-qualified scheme (`qualifiedKey`) so there
30
+ * is one such encoding in this package rather than two.
31
+ */
32
+ export function clashMemberKey(model, ref) {
33
+ return qualifiedKey(model, String(ref));
34
+ }
35
+ /** Index a rule's member list; `null` when the side has no explicit members. */
36
+ export function clashMemberSet(members) {
37
+ return members ? new Set(members) : null;
38
+ }
39
+ /** Whether one element belongs to a rule side, by membership when the side has
40
+ * an explicit member set and by type selector otherwise. */
41
+ export function inClashSet(element, selector, members) {
42
+ if (members)
43
+ return members.has(clashMemberKey(element.model, element.ref));
44
+ return matchesSelector(element.tag, selector);
45
+ }
46
+ //# sourceMappingURL=members.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"members.js","sourceRoot":"","sources":["../src/members.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,GAAW;IACvD,OAAO,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAC5B,OAAsC;IAEtC,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3C,CAAC;AAED;6DAC6D;AAC7D,MAAM,UAAU,UAAU,CACxB,OAAoD,EACpD,QAAgB,EAChB,OAAmC;IAEnC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,OAAO,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { Clash, ClashResult } from './types.js';
2
+ /**
3
+ * One side of a revision comparison: a completed run plus the durable
4
+ * identity of every model it drew elements from.
5
+ *
6
+ * `ClashElementRef.model` (carried on `Clash.a`/`Clash.b`) is the federation's
7
+ * per-LOAD id — `crypto.randomUUID()` in the viewer — so it is deliberately
8
+ * useless for asking "is this model still part of the comparison". `modelNames`
9
+ * is the caller-supplied bridge: a map from that ephemeral id, AS IT APPEARS ON
10
+ * THIS RESULT'S OWN CLASHES, to a durable identity (the model's display name /
11
+ * filename in the viewer). Two different `model` ids that map to the same name
12
+ * are treated as the same model reloaded; a name present in `previous` but
13
+ * absent from `next` is treated as a model the comparison lost entirely.
14
+ *
15
+ * This is a heuristic, not a proof: renaming a model between runs makes it
16
+ * look "missing" (its old name has no counterpart), and two distinctly loaded
17
+ * files that happen to share a display name look like one. Both failure modes
18
+ * point the same way — toward `unretested` rather than a wrongly-confident
19
+ * `resolved` — which is the direction this module exists to be safe in.
20
+ */
21
+ export interface ClashRevisionSide {
22
+ result: ClashResult;
23
+ /** Ephemeral `ClashElementRef.model` id → durable display identity. */
24
+ modelNames: Readonly<Record<string, string>>;
25
+ }
26
+ export interface ClashRevisionReasons {
27
+ /** Rule ids the baseline ran that the current run's `rulesRun` omits entirely. */
28
+ skippedRuleIds: string[];
29
+ /** Rule ids the current run ran but whose coverage matched 0 elements on a
30
+ * side (`ruleHadNoMatch`) — it ran, but compared nothing. */
31
+ noMatchRuleIds: string[];
32
+ /** Durable model names present in the baseline that have no counterpart
33
+ * (by name) among the current run's models. */
34
+ missingModelNames: string[];
35
+ }
36
+ export interface ClashRevisionComparison {
37
+ /** In the current run, not the baseline: new issues. */
38
+ added: Clash[];
39
+ /** In both runs: still open. Carries the current run's `Clash`. */
40
+ persistent: Clash[];
41
+ /** In the baseline, not the current run, AND safe to call fixed: every rule
42
+ * and model the clash depended on was genuinely re-checked. */
43
+ resolved: Clash[];
44
+ /** In the baseline, not the current run, but NOT safe to call fixed: the
45
+ * current run could not have found it even if it still existed (a skipped
46
+ * rule, an empty-match rule, or a model no longer in the comparison). Never
47
+ * silently folded into `resolved`. */
48
+ unretested: Clash[];
49
+ /** Why each `unretested` clash landed there, for a coordinator's UI banner. */
50
+ reasons: ClashRevisionReasons;
51
+ summary: {
52
+ added: number;
53
+ persistent: number;
54
+ resolved: number;
55
+ unretested: number;
56
+ };
57
+ }
58
+ /**
59
+ * Compare a saved baseline run to a current run, safely.
60
+ *
61
+ * Pure and deterministic: depends only on the two inputs. `resolved` only
62
+ * receives a clash whose rule ran (with real matches on both sides) in the
63
+ * current run AND whose two models both still have a same-named counterpart
64
+ * there — everything else that `compareClashRuns` would have called
65
+ * `resolved` moves to `unretested` instead.
66
+ */
67
+ export declare function compareClashRevisions(previous: ClashRevisionSide, next: ClashRevisionSide): ClashRevisionComparison;
68
+ //# sourceMappingURL=revision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revision.d.ts","sourceRoot":"","sources":["../src/revision.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,uEAAuE;IACvE,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;kEAC8D;IAC9D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;oDACgD;IAChD,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,wDAAwD;IACxD,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,mEAAmE;IACnE,UAAU,EAAE,KAAK,EAAE,CAAC;IACpB;oEACgE;IAChE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB;;;2CAGuC;IACvC,UAAU,EAAE,KAAK,EAAE,CAAC;IACpB,+EAA+E;IAC/E,OAAO,EAAE,oBAAoB,CAAC;IAC9B,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACtF;AAkGD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,iBAAiB,GACtB,uBAAuB,CAoEzB"}