@principal-ai/principal-view-core 0.5.6

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.
Files changed (204) hide show
  1. package/README.md +126 -0
  2. package/dist/ConfigurationLoader.d.ts +76 -0
  3. package/dist/ConfigurationLoader.d.ts.map +1 -0
  4. package/dist/ConfigurationLoader.js +144 -0
  5. package/dist/ConfigurationLoader.js.map +1 -0
  6. package/dist/ConfigurationValidator.d.ts +31 -0
  7. package/dist/ConfigurationValidator.d.ts.map +1 -0
  8. package/dist/ConfigurationValidator.js +242 -0
  9. package/dist/ConfigurationValidator.js.map +1 -0
  10. package/dist/EventProcessor.d.ts +49 -0
  11. package/dist/EventProcessor.d.ts.map +1 -0
  12. package/dist/EventProcessor.js +215 -0
  13. package/dist/EventProcessor.js.map +1 -0
  14. package/dist/EventRecorderService.d.ts +305 -0
  15. package/dist/EventRecorderService.d.ts.map +1 -0
  16. package/dist/EventRecorderService.js +463 -0
  17. package/dist/EventRecorderService.js.map +1 -0
  18. package/dist/LibraryLoader.d.ts +63 -0
  19. package/dist/LibraryLoader.d.ts.map +1 -0
  20. package/dist/LibraryLoader.js +188 -0
  21. package/dist/LibraryLoader.js.map +1 -0
  22. package/dist/PathBasedEventProcessor.d.ts +90 -0
  23. package/dist/PathBasedEventProcessor.d.ts.map +1 -0
  24. package/dist/PathBasedEventProcessor.js +239 -0
  25. package/dist/PathBasedEventProcessor.js.map +1 -0
  26. package/dist/SessionManager.d.ts +194 -0
  27. package/dist/SessionManager.d.ts.map +1 -0
  28. package/dist/SessionManager.js +299 -0
  29. package/dist/SessionManager.js.map +1 -0
  30. package/dist/ValidationEngine.d.ts +31 -0
  31. package/dist/ValidationEngine.d.ts.map +1 -0
  32. package/dist/ValidationEngine.js +158 -0
  33. package/dist/ValidationEngine.js.map +1 -0
  34. package/dist/helpers/GraphInstrumentationHelper.d.ts +93 -0
  35. package/dist/helpers/GraphInstrumentationHelper.d.ts.map +1 -0
  36. package/dist/helpers/GraphInstrumentationHelper.js +248 -0
  37. package/dist/helpers/GraphInstrumentationHelper.js.map +1 -0
  38. package/dist/index.d.ts +33 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +34 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/rules/config.d.ts +57 -0
  43. package/dist/rules/config.d.ts.map +1 -0
  44. package/dist/rules/config.js +382 -0
  45. package/dist/rules/config.js.map +1 -0
  46. package/dist/rules/engine.d.ts +70 -0
  47. package/dist/rules/engine.d.ts.map +1 -0
  48. package/dist/rules/engine.js +252 -0
  49. package/dist/rules/engine.js.map +1 -0
  50. package/dist/rules/implementations/connection-type-references.d.ts +7 -0
  51. package/dist/rules/implementations/connection-type-references.d.ts.map +1 -0
  52. package/dist/rules/implementations/connection-type-references.js +104 -0
  53. package/dist/rules/implementations/connection-type-references.js.map +1 -0
  54. package/dist/rules/implementations/dead-end-states.d.ts +17 -0
  55. package/dist/rules/implementations/dead-end-states.d.ts.map +1 -0
  56. package/dist/rules/implementations/dead-end-states.js +72 -0
  57. package/dist/rules/implementations/dead-end-states.js.map +1 -0
  58. package/dist/rules/implementations/index.d.ts +24 -0
  59. package/dist/rules/implementations/index.d.ts.map +1 -0
  60. package/dist/rules/implementations/index.js +62 -0
  61. package/dist/rules/implementations/index.js.map +1 -0
  62. package/dist/rules/implementations/library-node-type-match.d.ts +17 -0
  63. package/dist/rules/implementations/library-node-type-match.d.ts.map +1 -0
  64. package/dist/rules/implementations/library-node-type-match.js +123 -0
  65. package/dist/rules/implementations/library-node-type-match.js.map +1 -0
  66. package/dist/rules/implementations/minimum-node-sources.d.ts +22 -0
  67. package/dist/rules/implementations/minimum-node-sources.d.ts.map +1 -0
  68. package/dist/rules/implementations/minimum-node-sources.js +54 -0
  69. package/dist/rules/implementations/minimum-node-sources.js.map +1 -0
  70. package/dist/rules/implementations/no-unknown-fields.d.ts +7 -0
  71. package/dist/rules/implementations/no-unknown-fields.d.ts.map +1 -0
  72. package/dist/rules/implementations/no-unknown-fields.js +211 -0
  73. package/dist/rules/implementations/no-unknown-fields.js.map +1 -0
  74. package/dist/rules/implementations/orphaned-edge-types.d.ts +7 -0
  75. package/dist/rules/implementations/orphaned-edge-types.d.ts.map +1 -0
  76. package/dist/rules/implementations/orphaned-edge-types.js +47 -0
  77. package/dist/rules/implementations/orphaned-edge-types.js.map +1 -0
  78. package/dist/rules/implementations/orphaned-node-types.d.ts +7 -0
  79. package/dist/rules/implementations/orphaned-node-types.d.ts.map +1 -0
  80. package/dist/rules/implementations/orphaned-node-types.js +50 -0
  81. package/dist/rules/implementations/orphaned-node-types.js.map +1 -0
  82. package/dist/rules/implementations/required-metadata.d.ts +7 -0
  83. package/dist/rules/implementations/required-metadata.d.ts.map +1 -0
  84. package/dist/rules/implementations/required-metadata.js +57 -0
  85. package/dist/rules/implementations/required-metadata.js.map +1 -0
  86. package/dist/rules/implementations/state-transition-references.d.ts +7 -0
  87. package/dist/rules/implementations/state-transition-references.d.ts.map +1 -0
  88. package/dist/rules/implementations/state-transition-references.js +135 -0
  89. package/dist/rules/implementations/state-transition-references.js.map +1 -0
  90. package/dist/rules/implementations/unreachable-states.d.ts +7 -0
  91. package/dist/rules/implementations/unreachable-states.d.ts.map +1 -0
  92. package/dist/rules/implementations/unreachable-states.js +80 -0
  93. package/dist/rules/implementations/unreachable-states.js.map +1 -0
  94. package/dist/rules/implementations/valid-action-patterns.d.ts +17 -0
  95. package/dist/rules/implementations/valid-action-patterns.d.ts.map +1 -0
  96. package/dist/rules/implementations/valid-action-patterns.js +109 -0
  97. package/dist/rules/implementations/valid-action-patterns.js.map +1 -0
  98. package/dist/rules/implementations/valid-color-format.d.ts +7 -0
  99. package/dist/rules/implementations/valid-color-format.d.ts.map +1 -0
  100. package/dist/rules/implementations/valid-color-format.js +91 -0
  101. package/dist/rules/implementations/valid-color-format.js.map +1 -0
  102. package/dist/rules/implementations/valid-edge-types.d.ts +7 -0
  103. package/dist/rules/implementations/valid-edge-types.d.ts.map +1 -0
  104. package/dist/rules/implementations/valid-edge-types.js +244 -0
  105. package/dist/rules/implementations/valid-edge-types.js.map +1 -0
  106. package/dist/rules/implementations/valid-node-types.d.ts +7 -0
  107. package/dist/rules/implementations/valid-node-types.d.ts.map +1 -0
  108. package/dist/rules/implementations/valid-node-types.js +175 -0
  109. package/dist/rules/implementations/valid-node-types.js.map +1 -0
  110. package/dist/rules/index.d.ts +28 -0
  111. package/dist/rules/index.d.ts.map +1 -0
  112. package/dist/rules/index.js +45 -0
  113. package/dist/rules/index.js.map +1 -0
  114. package/dist/rules/types.d.ts +309 -0
  115. package/dist/rules/types.d.ts.map +1 -0
  116. package/dist/rules/types.js +35 -0
  117. package/dist/rules/types.js.map +1 -0
  118. package/dist/types/canvas.d.ts +409 -0
  119. package/dist/types/canvas.d.ts.map +1 -0
  120. package/dist/types/canvas.js +70 -0
  121. package/dist/types/canvas.js.map +1 -0
  122. package/dist/types/index.d.ts +311 -0
  123. package/dist/types/index.d.ts.map +1 -0
  124. package/dist/types/index.js +13 -0
  125. package/dist/types/index.js.map +1 -0
  126. package/dist/types/library.d.ts +185 -0
  127. package/dist/types/library.d.ts.map +1 -0
  128. package/dist/types/library.js +15 -0
  129. package/dist/types/library.js.map +1 -0
  130. package/dist/types/path-based-config.d.ts +230 -0
  131. package/dist/types/path-based-config.d.ts.map +1 -0
  132. package/dist/types/path-based-config.js +9 -0
  133. package/dist/types/path-based-config.js.map +1 -0
  134. package/dist/utils/CanvasConverter.d.ts +118 -0
  135. package/dist/utils/CanvasConverter.d.ts.map +1 -0
  136. package/dist/utils/CanvasConverter.js +315 -0
  137. package/dist/utils/CanvasConverter.js.map +1 -0
  138. package/dist/utils/GraphConverter.d.ts +18 -0
  139. package/dist/utils/GraphConverter.d.ts.map +1 -0
  140. package/dist/utils/GraphConverter.js +61 -0
  141. package/dist/utils/GraphConverter.js.map +1 -0
  142. package/dist/utils/LibraryConverter.d.ts +113 -0
  143. package/dist/utils/LibraryConverter.d.ts.map +1 -0
  144. package/dist/utils/LibraryConverter.js +166 -0
  145. package/dist/utils/LibraryConverter.js.map +1 -0
  146. package/dist/utils/PathMatcher.d.ts +55 -0
  147. package/dist/utils/PathMatcher.d.ts.map +1 -0
  148. package/dist/utils/PathMatcher.js +172 -0
  149. package/dist/utils/PathMatcher.js.map +1 -0
  150. package/dist/utils/YamlParser.d.ts +36 -0
  151. package/dist/utils/YamlParser.d.ts.map +1 -0
  152. package/dist/utils/YamlParser.js +63 -0
  153. package/dist/utils/YamlParser.js.map +1 -0
  154. package/package.json +47 -0
  155. package/src/ConfigurationLoader.test.ts +490 -0
  156. package/src/ConfigurationLoader.ts +185 -0
  157. package/src/ConfigurationValidator.test.ts +200 -0
  158. package/src/ConfigurationValidator.ts +283 -0
  159. package/src/EventProcessor.test.ts +405 -0
  160. package/src/EventProcessor.ts +250 -0
  161. package/src/EventRecorderService.test.ts +541 -0
  162. package/src/EventRecorderService.ts +744 -0
  163. package/src/LibraryLoader.ts +215 -0
  164. package/src/PathBasedEventProcessor.test.ts +567 -0
  165. package/src/PathBasedEventProcessor.ts +332 -0
  166. package/src/SessionManager.test.ts +424 -0
  167. package/src/SessionManager.ts +470 -0
  168. package/src/ValidationEngine.test.ts +371 -0
  169. package/src/ValidationEngine.ts +196 -0
  170. package/src/helpers/GraphInstrumentationHelper.test.ts +340 -0
  171. package/src/helpers/GraphInstrumentationHelper.ts +326 -0
  172. package/src/index.ts +85 -0
  173. package/src/rules/config.test.ts +278 -0
  174. package/src/rules/config.ts +459 -0
  175. package/src/rules/engine.test.ts +332 -0
  176. package/src/rules/engine.ts +318 -0
  177. package/src/rules/implementations/connection-type-references.ts +117 -0
  178. package/src/rules/implementations/dead-end-states.ts +101 -0
  179. package/src/rules/implementations/index.ts +73 -0
  180. package/src/rules/implementations/library-node-type-match.ts +148 -0
  181. package/src/rules/implementations/minimum-node-sources.ts +82 -0
  182. package/src/rules/implementations/no-unknown-fields.ts +342 -0
  183. package/src/rules/implementations/orphaned-edge-types.ts +55 -0
  184. package/src/rules/implementations/orphaned-node-types.ts +58 -0
  185. package/src/rules/implementations/required-metadata.ts +64 -0
  186. package/src/rules/implementations/state-transition-references.ts +151 -0
  187. package/src/rules/implementations/unreachable-states.ts +94 -0
  188. package/src/rules/implementations/valid-action-patterns.ts +136 -0
  189. package/src/rules/implementations/valid-color-format.ts +140 -0
  190. package/src/rules/implementations/valid-edge-types.ts +258 -0
  191. package/src/rules/implementations/valid-node-types.ts +189 -0
  192. package/src/rules/index.ts +95 -0
  193. package/src/rules/types.ts +426 -0
  194. package/src/types/canvas.ts +496 -0
  195. package/src/types/index.ts +382 -0
  196. package/src/types/library.ts +233 -0
  197. package/src/types/path-based-config.ts +281 -0
  198. package/src/utils/CanvasConverter.ts +431 -0
  199. package/src/utils/GraphConverter.test.ts +195 -0
  200. package/src/utils/GraphConverter.ts +71 -0
  201. package/src/utils/LibraryConverter.ts +245 -0
  202. package/src/utils/PathMatcher.test.ts +148 -0
  203. package/src/utils/PathMatcher.ts +183 -0
  204. package/src/utils/YamlParser.ts +75 -0
@@ -0,0 +1,340 @@
1
+ import { GraphInstrumentationHelper } from './GraphInstrumentationHelper';
2
+ import type { GraphConfiguration, GraphEvent } from '../types';
3
+
4
+ describe('GraphInstrumentationHelper', () => {
5
+ let testConfig: GraphConfiguration;
6
+ let capturedEvents: GraphEvent[];
7
+
8
+ beforeEach(() => {
9
+ testConfig = {
10
+ metadata: {
11
+ name: 'Test System',
12
+ version: '1.0.0',
13
+ },
14
+ nodeTypes: {
15
+ user: {
16
+ shape: 'circle',
17
+ color: '#4CAF50',
18
+ dataSchema: {
19
+ userId: { type: 'string', required: true },
20
+ },
21
+ },
22
+ server: {
23
+ shape: 'hexagon',
24
+ color: '#9C27B0',
25
+ dataSchema: {
26
+ uptime: { type: 'number' },
27
+ },
28
+ },
29
+ },
30
+ edgeTypes: {
31
+ connection: {
32
+ style: 'solid',
33
+ directed: true,
34
+ },
35
+ },
36
+ allowedConnections: [{ from: 'user', to: 'server', via: 'connection' }],
37
+ };
38
+
39
+ capturedEvents = [];
40
+ });
41
+
42
+ describe('Event Emission', () => {
43
+ it('should emit events through callback', () => {
44
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
45
+ capturedEvents.push(event);
46
+ });
47
+
48
+ helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
49
+
50
+ expect(capturedEvents).toHaveLength(1);
51
+ expect(capturedEvents[0].type).toBe('node_created');
52
+ });
53
+
54
+ it('should allow setting event emitter after construction', () => {
55
+ const helper = new GraphInstrumentationHelper(testConfig);
56
+
57
+ helper.setEventEmitter((event) => {
58
+ capturedEvents.push(event);
59
+ });
60
+
61
+ helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
62
+
63
+ expect(capturedEvents).toHaveLength(1);
64
+ });
65
+ });
66
+
67
+ describe('Node Event Helpers', () => {
68
+ it('should emit node created event', () => {
69
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
70
+ capturedEvents.push(event);
71
+ });
72
+
73
+ const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
74
+
75
+ expect(event.category).toBe('node');
76
+ expect(event.operation).toBe('create');
77
+ expect(event.type).toBe('node_created');
78
+ expect(event.expected).toBe(true);
79
+
80
+ const payload = event.payload as any;
81
+ expect(payload.nodeId).toBe('user-1');
82
+ expect(payload.nodeType).toBe('user');
83
+ expect(payload.data.userId).toBe('alice');
84
+ });
85
+
86
+ it('should emit node created event with position', () => {
87
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
88
+ capturedEvents.push(event);
89
+ });
90
+
91
+ const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' }, {
92
+ position: { x: 100, y: 200 },
93
+ });
94
+
95
+ const payload = event.payload as any;
96
+ expect(payload.position).toEqual({ x: 100, y: 200 });
97
+ });
98
+
99
+ it('should emit node updated event', () => {
100
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
101
+ capturedEvents.push(event);
102
+ });
103
+
104
+ const event = helper.emitNodeUpdated('user-1', { status: 'online' });
105
+
106
+ expect(event.category).toBe('node');
107
+ expect(event.operation).toBe('update');
108
+ expect(event.type).toBe('node_updated');
109
+
110
+ const payload = event.payload as any;
111
+ expect(payload.nodeId).toBe('user-1');
112
+ expect(payload.data.status).toBe('online');
113
+ });
114
+
115
+ it('should emit node deleted event', () => {
116
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
117
+ capturedEvents.push(event);
118
+ });
119
+
120
+ const event = helper.emitNodeDeleted('user-1');
121
+
122
+ expect(event.category).toBe('node');
123
+ expect(event.operation).toBe('delete');
124
+ expect(event.type).toBe('node_deleted');
125
+
126
+ const payload = event.payload as any;
127
+ expect(payload.nodeId).toBe('user-1');
128
+ });
129
+
130
+ it('should mark events as unexpected when specified', () => {
131
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
132
+ capturedEvents.push(event);
133
+ });
134
+
135
+ const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' }, {
136
+ expected: false,
137
+ });
138
+
139
+ expect(event.expected).toBe(false);
140
+ });
141
+ });
142
+
143
+ describe('Edge Event Helpers', () => {
144
+ it('should emit edge created event', () => {
145
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
146
+ capturedEvents.push(event);
147
+ });
148
+
149
+ const event = helper.emitEdgeCreated('conn-1', 'connection', 'user-1', 'server-1');
150
+
151
+ expect(event.category).toBe('edge');
152
+ expect(event.operation).toBe('create');
153
+ expect(event.type).toBe('edge_created');
154
+
155
+ const payload = event.payload as any;
156
+ expect(payload.edgeId).toBe('conn-1');
157
+ expect(payload.edgeType).toBe('connection');
158
+ expect(payload.from).toBe('user-1');
159
+ expect(payload.to).toBe('server-1');
160
+ });
161
+
162
+ it('should emit edge created event with data', () => {
163
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
164
+ capturedEvents.push(event);
165
+ });
166
+
167
+ const event = helper.emitEdgeCreated('conn-1', 'connection', 'user-1', 'server-1', {
168
+ data: { bandwidth: '1gbps' },
169
+ });
170
+
171
+ const payload = event.payload as any;
172
+ expect(payload.data.bandwidth).toBe('1gbps');
173
+ });
174
+
175
+ it('should emit edge animated event', () => {
176
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
177
+ capturedEvents.push(event);
178
+ });
179
+
180
+ const event = helper.emitEdgeAnimated('msg-1', 'message', 'user-1', 'user-2', {
181
+ duration: 500,
182
+ direction: 'forward',
183
+ });
184
+
185
+ expect(event.category).toBe('edge');
186
+ expect(event.operation).toBe('animate');
187
+ expect(event.type).toBe('edge_animated');
188
+
189
+ const payload = event.payload as any;
190
+ expect(payload.animation?.duration).toBe(500);
191
+ expect(payload.animation?.direction).toBe('forward');
192
+ });
193
+
194
+ it('should emit edge deleted event', () => {
195
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
196
+ capturedEvents.push(event);
197
+ });
198
+
199
+ const event = helper.emitEdgeDeleted('conn-1');
200
+
201
+ expect(event.category).toBe('edge');
202
+ expect(event.operation).toBe('delete');
203
+ expect(event.type).toBe('edge_deleted');
204
+ });
205
+ });
206
+
207
+ describe('State Event Helpers', () => {
208
+ it('should emit state change event', () => {
209
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
210
+ capturedEvents.push(event);
211
+ });
212
+
213
+ const event = helper.emitStateChange('user-1', 'online');
214
+
215
+ expect(event.category).toBe('state');
216
+ expect(event.type).toBe('state_changed');
217
+
218
+ const payload = event.payload as any;
219
+ expect(payload.nodeId).toBe('user-1');
220
+ expect(payload.newState).toBe('online');
221
+ });
222
+
223
+ it('should emit state change with previous state', () => {
224
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
225
+ capturedEvents.push(event);
226
+ });
227
+
228
+ const event = helper.emitStateChange('user-1', 'online', {
229
+ previousState: 'offline',
230
+ });
231
+
232
+ const payload = event.payload as any;
233
+ expect(payload.previousState).toBe('offline');
234
+ expect(payload.newState).toBe('online');
235
+ });
236
+
237
+ it('should emit state change with additional data', () => {
238
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
239
+ capturedEvents.push(event);
240
+ });
241
+
242
+ const event = helper.emitStateChange('user-1', 'online', {
243
+ data: { lastSeen: Date.now() },
244
+ });
245
+
246
+ const payload = event.payload as any;
247
+ expect(payload.data.lastSeen).toBeDefined();
248
+ });
249
+ });
250
+
251
+ describe('System Event Helpers', () => {
252
+ it('should emit reset event', () => {
253
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
254
+ capturedEvents.push(event);
255
+ });
256
+
257
+ const event = helper.emitReset();
258
+
259
+ expect(event.category).toBe('system');
260
+ expect(event.type).toBe('system_reset');
261
+
262
+ const payload = event.payload as any;
263
+ expect(payload.action).toBe('reset');
264
+ });
265
+
266
+ it('should emit pause event', () => {
267
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
268
+ capturedEvents.push(event);
269
+ });
270
+
271
+ const event = helper.emitPause();
272
+
273
+ expect(event.category).toBe('system');
274
+ expect(event.type).toBe('system_pause');
275
+
276
+ const payload = event.payload as any;
277
+ expect(payload.action).toBe('pause');
278
+ });
279
+
280
+ it('should emit resume event', () => {
281
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
282
+ capturedEvents.push(event);
283
+ });
284
+
285
+ const event = helper.emitResume();
286
+
287
+ expect(event.category).toBe('system');
288
+ expect(event.type).toBe('system_resume');
289
+
290
+ const payload = event.payload as any;
291
+ expect(payload.action).toBe('resume');
292
+ });
293
+ });
294
+
295
+ describe('Event IDs', () => {
296
+ it('should generate unique event IDs', () => {
297
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
298
+ capturedEvents.push(event);
299
+ });
300
+
301
+ helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
302
+ helper.emitNodeCreated('user-2', 'user', { userId: 'bob' });
303
+
304
+ expect(capturedEvents[0].id).not.toBe(capturedEvents[1].id);
305
+ });
306
+
307
+ it('should include timestamp in event', () => {
308
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
309
+ capturedEvents.push(event);
310
+ });
311
+
312
+ const before = Date.now();
313
+ helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
314
+ const after = Date.now();
315
+
316
+ expect(capturedEvents[0].timestamp).toBeGreaterThanOrEqual(before);
317
+ expect(capturedEvents[0].timestamp).toBeLessThanOrEqual(after);
318
+ });
319
+ });
320
+
321
+ describe('Metadata Support', () => {
322
+ it('should include metadata in events', () => {
323
+ const helper = new GraphInstrumentationHelper(testConfig, (event) => {
324
+ capturedEvents.push(event);
325
+ });
326
+
327
+ const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' }, {
328
+ metadata: {
329
+ source: 'test:line:42',
330
+ tags: ['important', 'user-creation'],
331
+ description: 'Creating test user',
332
+ },
333
+ });
334
+
335
+ expect(event.metadata?.source).toBe('test:line:42');
336
+ expect(event.metadata?.tags).toEqual(['important', 'user-creation']);
337
+ expect(event.metadata?.description).toBe('Creating test user');
338
+ });
339
+ });
340
+ });
@@ -0,0 +1,326 @@
1
+ import type { GraphConfiguration, GraphEvent, NodeEvent, EdgeEvent, StateEvent } from '../types';
2
+
3
+ /**
4
+ * Helper class for instrumenting tests with graph events
5
+ * Makes it easy to emit events from tests
6
+ */
7
+ export class GraphInstrumentationHelper {
8
+ private configuration: GraphConfiguration;
9
+ private eventEmitter?: (event: GraphEvent) => void;
10
+ private eventCounter = 0;
11
+
12
+ constructor(
13
+ configuration: GraphConfiguration,
14
+ eventEmitter?: (event: GraphEvent) => void
15
+ ) {
16
+ this.configuration = configuration;
17
+ this.eventEmitter = eventEmitter;
18
+ }
19
+
20
+ /**
21
+ * Set the event emitter callback
22
+ */
23
+ setEventEmitter(emitter: (event: GraphEvent) => void): void {
24
+ this.eventEmitter = emitter;
25
+ }
26
+
27
+ /**
28
+ * Emit a node creation event
29
+ */
30
+ emitNodeCreated(
31
+ id: string,
32
+ type: string,
33
+ data: Record<string, any>,
34
+ options: {
35
+ expected?: boolean;
36
+ position?: { x: number; y: number };
37
+ metadata?: Record<string, any>;
38
+ } = {}
39
+ ): GraphEvent {
40
+ const event: GraphEvent = {
41
+ id: this.generateEventId(),
42
+ type: 'node_created',
43
+ timestamp: Date.now(),
44
+ category: 'node',
45
+ operation: 'create',
46
+ payload: {
47
+ operation: 'create',
48
+ nodeId: id,
49
+ nodeType: type,
50
+ data,
51
+ position: options.position,
52
+ } as NodeEvent,
53
+ expected: options.expected ?? true,
54
+ metadata: options.metadata,
55
+ };
56
+
57
+ this.emit(event);
58
+ return event;
59
+ }
60
+
61
+ /**
62
+ * Emit a node update event
63
+ */
64
+ emitNodeUpdated(
65
+ id: string,
66
+ updates: Record<string, any>,
67
+ options: { expected?: boolean; metadata?: Record<string, any> } = {}
68
+ ): GraphEvent {
69
+ const event: GraphEvent = {
70
+ id: this.generateEventId(),
71
+ type: 'node_updated',
72
+ timestamp: Date.now(),
73
+ category: 'node',
74
+ operation: 'update',
75
+ payload: {
76
+ operation: 'update',
77
+ nodeId: id,
78
+ nodeType: '', // Will be determined from state
79
+ data: updates,
80
+ } as NodeEvent,
81
+ expected: options.expected ?? true,
82
+ metadata: options.metadata,
83
+ };
84
+
85
+ this.emit(event);
86
+ return event;
87
+ }
88
+
89
+ /**
90
+ * Emit a node deletion event
91
+ */
92
+ emitNodeDeleted(
93
+ id: string,
94
+ options: { expected?: boolean; metadata?: Record<string, any> } = {}
95
+ ): GraphEvent {
96
+ const event: GraphEvent = {
97
+ id: this.generateEventId(),
98
+ type: 'node_deleted',
99
+ timestamp: Date.now(),
100
+ category: 'node',
101
+ operation: 'delete',
102
+ payload: {
103
+ operation: 'delete',
104
+ nodeId: id,
105
+ nodeType: '', // Will be determined from state
106
+ } as NodeEvent,
107
+ expected: options.expected ?? true,
108
+ metadata: options.metadata,
109
+ };
110
+
111
+ this.emit(event);
112
+ return event;
113
+ }
114
+
115
+ /**
116
+ * Emit an edge creation event
117
+ */
118
+ emitEdgeCreated(
119
+ id: string,
120
+ type: string,
121
+ from: string,
122
+ to: string,
123
+ options: {
124
+ expected?: boolean;
125
+ data?: Record<string, any>;
126
+ metadata?: Record<string, any>;
127
+ } = {}
128
+ ): GraphEvent {
129
+ const event: GraphEvent = {
130
+ id: this.generateEventId(),
131
+ type: 'edge_created',
132
+ timestamp: Date.now(),
133
+ category: 'edge',
134
+ operation: 'create',
135
+ payload: {
136
+ operation: 'create',
137
+ edgeId: id,
138
+ edgeType: type,
139
+ from,
140
+ to,
141
+ data: options.data,
142
+ } as EdgeEvent,
143
+ expected: options.expected ?? true,
144
+ metadata: options.metadata,
145
+ };
146
+
147
+ this.emit(event);
148
+ return event;
149
+ }
150
+
151
+ /**
152
+ * Emit an edge animation event
153
+ */
154
+ emitEdgeAnimated(
155
+ id: string,
156
+ type: string,
157
+ from: string,
158
+ to: string,
159
+ options: {
160
+ duration?: number;
161
+ direction?: 'forward' | 'backward' | 'bidirectional';
162
+ metadata?: Record<string, any>;
163
+ } = {}
164
+ ): GraphEvent {
165
+ const event: GraphEvent = {
166
+ id: this.generateEventId(),
167
+ type: 'edge_animated',
168
+ timestamp: Date.now(),
169
+ category: 'edge',
170
+ operation: 'animate',
171
+ payload: {
172
+ operation: 'animate',
173
+ edgeId: id,
174
+ edgeType: type,
175
+ from,
176
+ to,
177
+ animation: {
178
+ duration: options.duration,
179
+ direction: options.direction || 'forward',
180
+ },
181
+ } as EdgeEvent,
182
+ expected: true,
183
+ metadata: options.metadata,
184
+ };
185
+
186
+ this.emit(event);
187
+ return event;
188
+ }
189
+
190
+ /**
191
+ * Emit an edge deletion event
192
+ */
193
+ emitEdgeDeleted(
194
+ id: string,
195
+ options: { expected?: boolean; metadata?: Record<string, any> } = {}
196
+ ): GraphEvent {
197
+ const event: GraphEvent = {
198
+ id: this.generateEventId(),
199
+ type: 'edge_deleted',
200
+ timestamp: Date.now(),
201
+ category: 'edge',
202
+ operation: 'delete',
203
+ payload: {
204
+ operation: 'delete',
205
+ edgeId: id,
206
+ edgeType: '', // Will be determined from state
207
+ from: '',
208
+ to: '',
209
+ } as EdgeEvent,
210
+ expected: options.expected ?? true,
211
+ metadata: options.metadata,
212
+ };
213
+
214
+ this.emit(event);
215
+ return event;
216
+ }
217
+
218
+ /**
219
+ * Emit a state change event
220
+ */
221
+ emitStateChange(
222
+ nodeId: string,
223
+ newState: string,
224
+ options: {
225
+ previousState?: string;
226
+ expected?: boolean;
227
+ data?: Record<string, any>;
228
+ metadata?: Record<string, any>;
229
+ } = {}
230
+ ): GraphEvent {
231
+ const event: GraphEvent = {
232
+ id: this.generateEventId(),
233
+ type: 'state_changed',
234
+ timestamp: Date.now(),
235
+ category: 'state',
236
+ operation: 'update',
237
+ payload: {
238
+ nodeId,
239
+ previousState: options.previousState,
240
+ newState,
241
+ data: options.data,
242
+ } as StateEvent,
243
+ expected: options.expected ?? true,
244
+ metadata: options.metadata,
245
+ };
246
+
247
+ this.emit(event);
248
+ return event;
249
+ }
250
+
251
+ /**
252
+ * Emit a system reset event
253
+ */
254
+ emitReset(): GraphEvent {
255
+ const event: GraphEvent = {
256
+ id: this.generateEventId(),
257
+ type: 'system_reset',
258
+ timestamp: Date.now(),
259
+ category: 'system',
260
+ operation: 'update',
261
+ payload: {
262
+ action: 'reset',
263
+ },
264
+ expected: true,
265
+ };
266
+
267
+ this.emit(event);
268
+ return event;
269
+ }
270
+
271
+ /**
272
+ * Emit a system pause event
273
+ */
274
+ emitPause(): GraphEvent {
275
+ const event: GraphEvent = {
276
+ id: this.generateEventId(),
277
+ type: 'system_pause',
278
+ timestamp: Date.now(),
279
+ category: 'system',
280
+ operation: 'update',
281
+ payload: {
282
+ action: 'pause',
283
+ },
284
+ expected: true,
285
+ };
286
+
287
+ this.emit(event);
288
+ return event;
289
+ }
290
+
291
+ /**
292
+ * Emit a system resume event
293
+ */
294
+ emitResume(): GraphEvent {
295
+ const event: GraphEvent = {
296
+ id: this.generateEventId(),
297
+ type: 'system_resume',
298
+ timestamp: Date.now(),
299
+ category: 'system',
300
+ operation: 'update',
301
+ payload: {
302
+ action: 'resume',
303
+ },
304
+ expected: true,
305
+ };
306
+
307
+ this.emit(event);
308
+ return event;
309
+ }
310
+
311
+ /**
312
+ * Generate a unique event ID
313
+ */
314
+ private generateEventId(): string {
315
+ return `evt-${++this.eventCounter}-${Date.now()}`;
316
+ }
317
+
318
+ /**
319
+ * Emit an event
320
+ */
321
+ private emit(event: GraphEvent): void {
322
+ if (this.eventEmitter) {
323
+ this.eventEmitter(event);
324
+ }
325
+ }
326
+ }