@dr2rai/raid-canvas 0.1.0 → 0.2.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/src/X6Shapes.ts CHANGED
@@ -9,12 +9,14 @@
9
9
  * - Cascais Heraldry design tokens.
10
10
  */
11
11
 
12
- import { Graph, Node, Edge } from '@antv/x6';
12
+ import { Graph, Shape, Node, Edge } from '@antv/x6';
13
13
  import type {
14
+ AimOntologyKind,
14
15
  AimEdgeKind,
15
16
  RaidNodeData,
16
17
  RaidEdgeData,
17
18
  OrthogonalPortId,
19
+ Bounds,
18
20
  } from './types.js';
19
21
 
20
22
  /**
@@ -105,9 +107,9 @@ export function registerAimShapes(): void {
105
107
  }
106
108
 
107
109
  // 1. AimUseCaseNode ('uc') — Ellipse with Net Gold border
108
- Node.define({
110
+ Shape.Ellipse.define({
109
111
  shape: 'aim-uc',
110
- inherit: 'ellipse',
112
+ overwrite: true,
111
113
  width: 140,
112
114
  height: 70,
113
115
  attrs: {
@@ -115,8 +117,6 @@ export function registerAimShapes(): void {
115
117
  fill: CascaisPalette.ChalkWhite,
116
118
  stroke: CascaisPalette.NetGold,
117
119
  strokeWidth: 2,
118
- rx: 70,
119
- ry: 35,
120
120
  class: 'aim-node aim-uc',
121
121
  },
122
122
  label: {
@@ -133,9 +133,9 @@ export function registerAimShapes(): void {
133
133
  });
134
134
 
135
135
  // 2. AimActivityNode ('act') — Rounded rectangle with Heraldic Green border
136
- Node.define({
136
+ Shape.Rect.define({
137
137
  shape: 'aim-act',
138
- inherit: 'rect',
138
+ overwrite: true,
139
139
  width: 150,
140
140
  height: 60,
141
141
  attrs: {
@@ -161,9 +161,9 @@ export function registerAimShapes(): void {
161
161
  });
162
162
 
163
163
  // 3. AimClassNode ('cls') — Compartmentalized class card
164
- Node.define({
164
+ Shape.Rect.define({
165
165
  shape: 'aim-cls',
166
- inherit: 'rect',
166
+ overwrite: true,
167
167
  width: 180,
168
168
  height: 100,
169
169
  markup: [
@@ -258,9 +258,9 @@ export function registerAimShapes(): void {
258
258
  });
259
259
 
260
260
  // 4. AimObjectNode ('obj') — Instance card with underlined title
261
- Node.define({
261
+ Shape.Rect.define({
262
262
  shape: 'aim-obj',
263
- inherit: 'rect',
263
+ overwrite: true,
264
264
  width: 160,
265
265
  height: 80,
266
266
  attrs: {
@@ -271,7 +271,7 @@ export function registerAimShapes(): void {
271
271
  class: 'aim-node aim-obj',
272
272
  },
273
273
  label: {
274
- text: '<u>instance: Type</u>',
274
+ text: 'instance: Type',
275
275
  fill: CascaisPalette.TextPrimary,
276
276
  fontSize: 12,
277
277
  fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
@@ -283,9 +283,9 @@ export function registerAimShapes(): void {
283
283
  });
284
284
 
285
285
  // 5. AimPersonNode ('per') — Person / Actor role card
286
- Node.define({
286
+ Shape.Rect.define({
287
287
  shape: 'aim-per',
288
- inherit: 'rect',
288
+ overwrite: true,
289
289
  width: 120,
290
290
  height: 70,
291
291
  attrs: {
@@ -311,9 +311,9 @@ export function registerAimShapes(): void {
311
311
  });
312
312
 
313
313
  // 6. AimEdge — Orthogonal Manhattan edge with rounded corners
314
- Edge.define({
314
+ Shape.Edge.define({
315
315
  shape: 'aim-edge',
316
- inherit: 'edge',
316
+ overwrite: true,
317
317
  router: {
318
318
  name: 'manhattan',
319
319
  args: {
@@ -509,8 +509,11 @@ function getEdgeStyling(kind: AimEdgeKind): Record<string, unknown> {
509
509
  strokeWidth: 1.5,
510
510
  strokeDasharray: '5,5',
511
511
  targetMarker: {
512
- name: 'open',
513
- size: 8,
512
+ name: 'block',
513
+ args: {
514
+ size: 8,
515
+ open: true,
516
+ },
514
517
  },
515
518
  };
516
519
 
@@ -573,3 +576,48 @@ function getEdgeStyling(kind: AimEdgeKind): Record<string, unknown> {
573
576
  };
574
577
  }
575
578
  }
579
+
580
+ /**
581
+ * Returns default Cartesian bounds for an instantiated AOAIM archetype.
582
+ */
583
+ export function getDefaultNodeBounds(
584
+ kind: AimOntologyKind | string,
585
+ x: number = 100,
586
+ y: number = 100,
587
+ ): Bounds {
588
+ switch (kind) {
589
+ case 'uc':
590
+ return { x, y, width: 140, height: 70 };
591
+ case 'act':
592
+ return { x, y, width: 150, height: 60 };
593
+ case 'cls':
594
+ return { x, y, width: 180, height: 110 };
595
+ case 'obj':
596
+ return { x, y, width: 160, height: 80 };
597
+ case 'per':
598
+ return { x, y, width: 120, height: 70 };
599
+ default:
600
+ return { x, y, width: 140, height: 60 };
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Returns a canonical default display name for an instantiated AOAIM archetype.
606
+ */
607
+ export function getDefaultNodeName(kind: AimOntologyKind | string): string {
608
+ switch (kind) {
609
+ case 'uc':
610
+ return 'New UseCase';
611
+ case 'act':
612
+ return 'New Activity';
613
+ case 'cls':
614
+ return 'NewClass';
615
+ case 'obj':
616
+ return 'new Object';
617
+ case 'per':
618
+ return 'Actor';
619
+ default:
620
+ return 'Entity';
621
+ }
622
+ }
623
+
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@
7
7
  * - 4-way orthogonal ports and Manhattan router integration.
8
8
  * - Cascais Heraldry design tokens and theme styling.
9
9
  * - Bidirectional RaiBridge for hydrating from and serializing to the aim-* SVG contract.
10
+ * - Anti-entropy semantic connection validation and wiring rules.
11
+ * - Reusable <RaidCanvas /> React component with Dnd and imperative control handle.
10
12
  */
11
13
 
12
14
  // Core Metamodel & SVG Contract Types
@@ -33,7 +35,28 @@ export {
33
35
  configureAimGraph,
34
36
  createAimNode,
35
37
  createAimEdge,
38
+ getDefaultNodeBounds,
39
+ getDefaultNodeName,
36
40
  } from './X6Shapes.js';
37
41
 
42
+ // Anti-Entropy Semantic Connection Rules
43
+ export {
44
+ SEMANTIC_RULES_MATRIX,
45
+ validateSemanticConnection,
46
+ getSemanticEdgeKind,
47
+ getSemanticEdgeStereotype,
48
+ getSemanticRuleDescription,
49
+ getAvailableStereotypes,
50
+ type SemanticRule,
51
+ type StereotypeOption,
52
+ } from './semanticRules.js';
53
+
38
54
  // Bidirectional SVG <-> X6 Synchronization Bridge
39
55
  export { RaiBridge } from './RaiBridge.js';
56
+
57
+ // Reusable React Canvas Component & Ref Handle
58
+ export {
59
+ RaidCanvas,
60
+ type RaidCanvasProps,
61
+ type RaidCanvasHandle,
62
+ } from './RaidCanvas.js';
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @file semanticRules.ts
3
+ * @description Anti-entropy semantic connection validation and wiring rules
4
+ * for the AOAIM (Activity-Object-AI Model) graphical modeling contract.
5
+ *
6
+ * Enforces ontological integrity during edge creation:
7
+ * - 'per' -> 'uc' : 'association' («initiates», «owns» [0..1], «participates» [0..*])
8
+ * - 'per' -> 'act' : 'association' («executes»)
9
+ * - 'uc' -> 'uc' : 'dependency' («includes», «extends», «dependsOn»)
10
+ * - 'uc' -> 'act' : 'dependency' («includes»)
11
+ * - 'act' -> 'act' : 'association' («sequence» / process flow)
12
+ * - 'cls' -> 'cls' : 'association' («relatesTo»), 'generalization', 'composition', 'aggregation'
13
+ * - 'cls' -> 'obj' : 'dependency' («instantiates»)
14
+ * - 'obj' -> 'obj' : 'association' («link»)
15
+ */
16
+
17
+ import type { AimOntologyKind, AimEdgeKind } from './types.js';
18
+
19
+ export interface StereotypeOption {
20
+ readonly stereotype: string;
21
+ readonly defaultCardinality?: string;
22
+ readonly description: string;
23
+ }
24
+
25
+ export interface SemanticRule {
26
+ readonly valid: boolean;
27
+ readonly defaultEdgeKind: AimEdgeKind;
28
+ readonly defaultStereotype: string;
29
+ readonly defaultCardinality?: string;
30
+ readonly availableStereotypes: readonly StereotypeOption[];
31
+ readonly description: string;
32
+ }
33
+
34
+ /**
35
+ * Ontological connection rules matrix.
36
+ * Key: `${sourceKind}->${targetKind}`
37
+ */
38
+ export const SEMANTIC_RULES_MATRIX: Readonly<Record<string, SemanticRule>> = {
39
+ 'per->uc': {
40
+ valid: true,
41
+ defaultEdgeKind: 'association',
42
+ defaultStereotype: '«initiates»',
43
+ description: 'Actor initiates, owns, or participates in UseCase',
44
+ availableStereotypes: [
45
+ {
46
+ stereotype: '«initiates»',
47
+ defaultCardinality: '1',
48
+ description: 'Initiating role triggering the UseCase flow',
49
+ },
50
+ {
51
+ stereotype: '«owns»',
52
+ defaultCardinality: '0..1',
53
+ description: 'Ownership relationship representing responsible party (cardinality 0..1)',
54
+ },
55
+ {
56
+ stereotype: '«participates»',
57
+ defaultCardinality: '0..*',
58
+ description: 'Participating role supporting the UseCase execution (cardinality 0..*)',
59
+ },
60
+ ],
61
+ },
62
+ 'per->act': {
63
+ valid: true,
64
+ defaultEdgeKind: 'association',
65
+ defaultStereotype: '«executes»',
66
+ description: 'Actor directly performs or executes Activity',
67
+ availableStereotypes: [
68
+ {
69
+ stereotype: '«executes»',
70
+ description: 'Actor performs this process step',
71
+ },
72
+ {
73
+ stereotype: '«approves»',
74
+ description: 'Actor reviews or approves activity completion',
75
+ },
76
+ ],
77
+ },
78
+ 'uc->uc': {
79
+ valid: true,
80
+ defaultEdgeKind: 'dependency',
81
+ defaultStereotype: '«includes»',
82
+ description: 'UseCase includes or extends sub-UseCase',
83
+ availableStereotypes: [
84
+ {
85
+ stereotype: '«includes»',
86
+ description: 'Mandatory sub-flow inclusion dependency',
87
+ },
88
+ {
89
+ stereotype: '«extends»',
90
+ description: 'Optional conditional extension point',
91
+ },
92
+ {
93
+ stereotype: '«dependsOn»',
94
+ description: 'Prerequisite functional dependency',
95
+ },
96
+ ],
97
+ },
98
+ 'uc->act': {
99
+ valid: true,
100
+ defaultEdgeKind: 'dependency',
101
+ defaultStereotype: '«includes»',
102
+ description: 'UseCase elaborates downflow process Activity',
103
+ availableStereotypes: [
104
+ {
105
+ stereotype: '«includes»',
106
+ description: 'Process activity required by UseCase',
107
+ },
108
+ {
109
+ stereotype: '«triggers»',
110
+ description: 'UseCase execution triggers activity',
111
+ },
112
+ ],
113
+ },
114
+ 'act->act': {
115
+ valid: true,
116
+ defaultEdgeKind: 'association',
117
+ defaultStereotype: '«sequence»',
118
+ description: 'Activity transitions to next process step',
119
+ availableStereotypes: [
120
+ {
121
+ stereotype: '«sequence»',
122
+ description: 'Sequential process transition',
123
+ },
124
+ {
125
+ stereotype: '«branch»',
126
+ description: 'Conditional decision branch',
127
+ },
128
+ ],
129
+ },
130
+ 'cls->cls': {
131
+ valid: true,
132
+ defaultEdgeKind: 'association',
133
+ defaultStereotype: '«relatesTo»',
134
+ description: 'Domain structural relationship between Classes',
135
+ availableStereotypes: [
136
+ {
137
+ stereotype: '«relatesTo»',
138
+ defaultCardinality: '1..*',
139
+ description: 'General domain association',
140
+ },
141
+ {
142
+ stereotype: '«generalization»',
143
+ description: 'Inheritance classification (subclass is-a superclass)',
144
+ },
145
+ {
146
+ stereotype: '«composition»',
147
+ defaultCardinality: '1',
148
+ description: 'Composite whole-part lifecycle binding',
149
+ },
150
+ {
151
+ stereotype: '«aggregation»',
152
+ defaultCardinality: '0..*',
153
+ description: 'Shared aggregation relationship',
154
+ },
155
+ ],
156
+ },
157
+ 'cls->obj': {
158
+ valid: true,
159
+ defaultEdgeKind: 'dependency',
160
+ defaultStereotype: '«instantiates»',
161
+ description: 'Class specifies runtime Object instance',
162
+ availableStereotypes: [
163
+ {
164
+ stereotype: '«instantiates»',
165
+ description: 'Class defines schema for runtime instance',
166
+ },
167
+ ],
168
+ },
169
+ 'obj->obj': {
170
+ valid: true,
171
+ defaultEdgeKind: 'association',
172
+ defaultStereotype: '«link»',
173
+ description: 'Runtime communication link between Objects',
174
+ availableStereotypes: [
175
+ {
176
+ stereotype: '«link»',
177
+ description: 'Runtime object collaboration message link',
178
+ },
179
+ ],
180
+ },
181
+ };
182
+
183
+ /**
184
+ * Validates whether an edge connection between source and target archetypes is ontologically allowed.
185
+ */
186
+ export function validateSemanticConnection(
187
+ sourceKind: AimOntologyKind | string,
188
+ targetKind: AimOntologyKind | string,
189
+ ): boolean {
190
+ if (!sourceKind || !targetKind) return false;
191
+ const key = `${sourceKind}->${targetKind}`;
192
+ return SEMANTIC_RULES_MATRIX[key]?.valid ?? false;
193
+ }
194
+
195
+ /**
196
+ * Determines the canonical default relationship edge kind for a connected pair.
197
+ */
198
+ export function getSemanticEdgeKind(
199
+ sourceKind: AimOntologyKind | string,
200
+ targetKind: AimOntologyKind | string,
201
+ ): AimEdgeKind {
202
+ const key = `${sourceKind}->${targetKind}`;
203
+ return SEMANTIC_RULES_MATRIX[key]?.defaultEdgeKind ?? 'association';
204
+ }
205
+
206
+ /**
207
+ * Determines the canonical default stereotype annotation for a connected pair.
208
+ */
209
+ export function getSemanticEdgeStereotype(
210
+ sourceKind: AimOntologyKind | string,
211
+ targetKind: AimOntologyKind | string,
212
+ ): string {
213
+ const key = `${sourceKind}->${targetKind}`;
214
+ return SEMANTIC_RULES_MATRIX[key]?.defaultStereotype ?? '';
215
+ }
216
+
217
+ /**
218
+ * Returns available ontological stereotypes and their default cardinalities for a pair.
219
+ */
220
+ export function getAvailableStereotypes(
221
+ sourceKind: AimOntologyKind | string,
222
+ targetKind: AimOntologyKind | string,
223
+ ): readonly StereotypeOption[] {
224
+ const key = `${sourceKind}->${targetKind}`;
225
+ return SEMANTIC_RULES_MATRIX[key]?.availableStereotypes ?? [];
226
+ }
227
+
228
+ /**
229
+ * Returns a human-readable description explaining the ontological relationship.
230
+ */
231
+ export function getSemanticRuleDescription(
232
+ sourceKind: AimOntologyKind | string,
233
+ targetKind: AimOntologyKind | string,
234
+ ): string {
235
+ const key = `${sourceKind}->${targetKind}`;
236
+ return SEMANTIC_RULES_MATRIX[key]?.description ?? 'Invalid ontological connection';
237
+ }