@sigloch/contracts 6.0.0 → 6.3.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.
@@ -1,10 +1,13 @@
1
+ import { indexOf } from './graph-index.js';
1
2
  // ---------------------------------------------------------------------------
2
3
  // UC-01: UC must have compose→REQ trace (CR-165: satisfy UC→REQ is invalid per meta-model)
3
4
  // ---------------------------------------------------------------------------
4
5
  export function uc01HasRequirements(graph) {
5
- return graph.elements
6
- .filter(e => e.type === 'UC')
7
- .filter(uc => !graph.traces.some(t => t.source === uc.id && t.type === 'compose' && graph.elements.some(e => e.id === t.target && e.type === 'REQ')))
6
+ // CR-SM-264: compose-Kanten je UC aus dem Index. Vorher ein Vollscan ueber alle Traces je UC,
7
+ // darin ein Vollscan ueber alle Elemente je Treffer — `UC x T x E`.
8
+ const idx = indexOf(graph);
9
+ return idx.elementsOfType('UC')
10
+ .filter(uc => !idx.out(uc.id, 'compose').some(t => idx.typeOf(t.target) === 'REQ'))
8
11
  .map(uc => ({
9
12
  rule_id: 'UC-01',
10
13
  severity: 'error',
@@ -18,14 +21,31 @@ export function uc01HasRequirements(graph) {
18
21
  // UC-02: UC FCHAIN must have an ACTOR with io trace
19
22
  // ---------------------------------------------------------------------------
20
23
  export function uc02HasActor(graph) {
21
- return graph.elements
22
- .filter(e => e.type === 'UC')
23
- .filter(uc => {
24
- // Check if any ACTOR has io trace to this UC or to a FCHAIN/FLOW in this UC
25
- return !graph.traces.some(t => t.type === 'io' &&
26
- graph.elements.some(e => e.id === t.source && e.type === 'ACTOR') &&
27
- (t.target === uc.id || graph.traces.some(ct => ct.source === uc.id && ct.type === 'compose' && ct.target === t.target)));
28
- })
24
+ // CR-SM-261: dieselbe Frage, einmal statt je UC neu.
25
+ //
26
+ // Die alte Fassung scannte je UC alle Traces, darin je io-Trace alle ELEMENTE (die
27
+ // ACTOR-Suche) und noch einmal alle Traces (die compose-Suche) `UC × T × (E + T)`. Das
28
+ // Profil aus CR-SM-260 hat sie mit 50,8 % des gesamten Regellaufs und einem Wachstum von
29
+ // n^3,05 gemessen, kubisch statt quadratisch und damit die teuerste Regel des Katalogs.
30
+ //
31
+ // Die Umkehrung: erst die Menge der io-Ziele bilden, die von einem ACTOR ausgehen (einmal
32
+ // ueber Elemente und Traces), dann je UC nur noch Mitgliedschaft pruefen. Dieselbe Aussage,
33
+ // dieselben Befunde in derselben Reihenfolge — die Iteration ueber `graph.elements` bleibt
34
+ // die aeussere Schleife.
35
+ const idx = indexOf(graph);
36
+ const actorIds = idx.idsOfType('ACTOR');
37
+ const actorIoTargets = new Set(idx.tracesOfType('io').filter(t => actorIds.has(t.source)).map(t => t.target));
38
+ const composeTargets = new Map();
39
+ for (const t of idx.tracesOfType('compose')) {
40
+ const list = composeTargets.get(t.source);
41
+ if (list)
42
+ list.push(t.target);
43
+ else
44
+ composeTargets.set(t.source, [t.target]);
45
+ }
46
+ return idx.elementsOfType('UC')
47
+ .filter(uc => !actorIoTargets.has(uc.id) &&
48
+ !(composeTargets.get(uc.id) ?? []).some(target => actorIoTargets.has(target)))
29
49
  .map(uc => ({
30
50
  rule_id: 'UC-02',
31
51
  severity: 'error',
@@ -39,10 +59,9 @@ export function uc02HasActor(graph) {
39
59
  // UC-03: UC must have compose→FCHAIN (scenario)
40
60
  // ---------------------------------------------------------------------------
41
61
  export function uc03HasScenario(graph) {
42
- return graph.elements
43
- .filter(e => e.type === 'UC')
44
- .filter(uc => !graph.traces.some(t => t.source === uc.id && t.type === 'compose' &&
45
- graph.elements.some(e => e.id === t.target && e.type === 'FCHAIN')))
62
+ const idx = indexOf(graph);
63
+ return idx.elementsOfType('UC')
64
+ .filter(uc => !idx.out(uc.id, 'compose').some(t => idx.typeOf(t.target) === 'FCHAIN'))
46
65
  .map(uc => ({
47
66
  rule_id: 'UC-03',
48
67
  severity: 'warning',
@@ -77,10 +96,12 @@ export function uc04GoalDefined(graph) {
77
96
  // UC-05: UC should have postcondition REQ (CR-165: compose only, satisfy UC→REQ invalid)
78
97
  // ---------------------------------------------------------------------------
79
98
  export function uc05HasPostcondition(graph) {
80
- return graph.elements
81
- .filter(e => e.type === 'UC')
82
- .filter(uc => !graph.traces.some(t => t.source === uc.id && t.type === 'compose' &&
83
- graph.elements.some(e => e.id === t.target && e.type === 'REQ' && e.kinds?.includes('postcondition'))))
99
+ const idx = indexOf(graph);
100
+ return idx.elementsOfType('UC')
101
+ .filter(uc => !idx.out(uc.id, 'compose').some(t => {
102
+ const target = idx.byId.get(t.target);
103
+ return target?.type === 'REQ' && target.kinds?.includes('postcondition');
104
+ }))
84
105
  .map(uc => ({
85
106
  rule_id: 'UC-05',
86
107
  severity: 'info',
@@ -94,10 +115,12 @@ export function uc05HasPostcondition(graph) {
94
115
  // UC-06: UC should have precondition REQ (CR-165: compose only, satisfy UC→REQ invalid)
95
116
  // ---------------------------------------------------------------------------
96
117
  export function uc06HasPrecondition(graph) {
97
- return graph.elements
98
- .filter(e => e.type === 'UC')
99
- .filter(uc => !graph.traces.some(t => t.source === uc.id && t.type === 'compose' &&
100
- graph.elements.some(e => e.id === t.target && e.type === 'REQ' && e.kinds?.includes('precondition'))))
118
+ const idx = indexOf(graph);
119
+ return idx.elementsOfType('UC')
120
+ .filter(uc => !idx.out(uc.id, 'compose').some(t => {
121
+ const target = idx.byId.get(t.target);
122
+ return target?.type === 'REQ' && target.kinds?.includes('precondition');
123
+ }))
101
124
  .map(uc => ({
102
125
  rule_id: 'UC-06',
103
126
  severity: 'info',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigloch/contracts",
3
- "version": "6.0.0",
3
+ "version": "6.3.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",