@fairfox/polly 0.1.3 → 0.1.4
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/{cli/polly.ts → dist/cli/polly.js} +100 -206
- package/dist/cli/polly.js.map +10 -0
- package/dist/scripts/build-extension.js +137 -0
- package/dist/scripts/build-extension.js.map +10 -0
- package/dist/vendor/verify/src/cli.js +2089 -0
- package/dist/vendor/verify/src/cli.js.map +16 -0
- package/dist/vendor/visualize/src/cli.js +2204 -0
- package/dist/vendor/visualize/src/cli.js.map +19 -0
- package/package.json +12 -12
- package/vendor/analysis/src/extract/adr.ts +0 -212
- package/vendor/analysis/src/extract/architecture.ts +0 -160
- package/vendor/analysis/src/extract/contexts.ts +0 -298
- package/vendor/analysis/src/extract/flows.ts +0 -309
- package/vendor/analysis/src/extract/handlers.ts +0 -321
- package/vendor/analysis/src/extract/index.ts +0 -9
- package/vendor/analysis/src/extract/integrations.ts +0 -329
- package/vendor/analysis/src/extract/manifest.ts +0 -298
- package/vendor/analysis/src/extract/types.ts +0 -389
- package/vendor/analysis/src/index.ts +0 -7
- package/vendor/analysis/src/types/adr.ts +0 -53
- package/vendor/analysis/src/types/architecture.ts +0 -245
- package/vendor/analysis/src/types/core.ts +0 -210
- package/vendor/analysis/src/types/index.ts +0 -18
- package/vendor/verify/src/adapters/base.ts +0 -164
- package/vendor/verify/src/adapters/detection.ts +0 -281
- package/vendor/verify/src/adapters/event-bus/index.ts +0 -480
- package/vendor/verify/src/adapters/web-extension/index.ts +0 -508
- package/vendor/verify/src/adapters/websocket/index.ts +0 -486
- package/vendor/verify/src/cli.ts +0 -430
- package/vendor/verify/src/codegen/config.ts +0 -354
- package/vendor/verify/src/codegen/tla.ts +0 -719
- package/vendor/verify/src/config/parser.ts +0 -303
- package/vendor/verify/src/config/types.ts +0 -113
- package/vendor/verify/src/core/model.ts +0 -267
- package/vendor/verify/src/core/primitives.ts +0 -106
- package/vendor/verify/src/extract/handlers.ts +0 -2
- package/vendor/verify/src/extract/types.ts +0 -2
- package/vendor/verify/src/index.ts +0 -150
- package/vendor/verify/src/primitives/index.ts +0 -102
- package/vendor/verify/src/runner/docker.ts +0 -283
- package/vendor/verify/src/types.ts +0 -51
- package/vendor/visualize/src/cli.ts +0 -365
- package/vendor/visualize/src/codegen/structurizr.ts +0 -770
- package/vendor/visualize/src/index.ts +0 -13
- package/vendor/visualize/src/runner/export.ts +0 -235
- package/vendor/visualize/src/viewer/server.ts +0 -485
- /package/dist/{background → src/background}/index.js +0 -0
- /package/dist/{background → src/background}/index.js.map +0 -0
- /package/dist/{background → src/background}/message-router.js +0 -0
- /package/dist/{background → src/background}/message-router.js.map +0 -0
- /package/dist/{index.js → src/index.js} +0 -0
- /package/dist/{index.js.map → src/index.js.map} +0 -0
- /package/dist/{shared → src/shared}/adapters/index.js +0 -0
- /package/dist/{shared → src/shared}/adapters/index.js.map +0 -0
- /package/dist/{shared → src/shared}/lib/context-helpers.js +0 -0
- /package/dist/{shared → src/shared}/lib/context-helpers.js.map +0 -0
- /package/dist/{shared → src/shared}/lib/errors.js +0 -0
- /package/dist/{shared → src/shared}/lib/errors.js.map +0 -0
- /package/dist/{shared → src/shared}/lib/message-bus.js +0 -0
- /package/dist/{shared → src/shared}/lib/message-bus.js.map +0 -0
- /package/dist/{shared → src/shared}/lib/state.js +0 -0
- /package/dist/{shared → src/shared}/lib/state.js.map +0 -0
- /package/dist/{shared → src/shared}/lib/test-helpers.js +0 -0
- /package/dist/{shared → src/shared}/lib/test-helpers.js.map +0 -0
- /package/dist/{shared → src/shared}/state/app-state.js +0 -0
- /package/dist/{shared → src/shared}/state/app-state.js.map +0 -0
- /package/dist/{shared → src/shared}/types/messages.js +0 -0
- /package/dist/{shared → src/shared}/types/messages.js.map +0 -0
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2
|
-
// Event Bus Adapter
|
|
3
|
-
// ═══════════════════════════════════════════════════════════════
|
|
4
|
-
//
|
|
5
|
-
// Extracts verification model from event-driven systems using EventEmitter.
|
|
6
|
-
// Recognizes:
|
|
7
|
-
// - EventEmitter instances (Node.js EventEmitter, mitt, etc.)
|
|
8
|
-
// - emitter.on(event, handler) pattern
|
|
9
|
-
// - emitter.emit(event, data) pattern
|
|
10
|
-
// - State mutations via state.field = value
|
|
11
|
-
// - Verification primitives (requires, ensures)
|
|
12
|
-
|
|
13
|
-
import { Project, type SourceFile, SyntaxKind, Node } from "ts-morph";
|
|
14
|
-
import type {
|
|
15
|
-
CoreVerificationModel,
|
|
16
|
-
MessageHandler,
|
|
17
|
-
StateAssignment,
|
|
18
|
-
VerificationCondition,
|
|
19
|
-
NodeDefinition,
|
|
20
|
-
MessageType,
|
|
21
|
-
RoutingRule,
|
|
22
|
-
} from "../../core/model";
|
|
23
|
-
import type { AdapterConfig, RoutingAdapter } from "../base";
|
|
24
|
-
|
|
25
|
-
// ─────────────────────────────────────────────────────────────────
|
|
26
|
-
// Configuration
|
|
27
|
-
// ─────────────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
export interface EventBusAdapterConfig extends AdapterConfig {
|
|
30
|
-
/** EventEmitter library being used (default: "events") */
|
|
31
|
-
emitterLibrary?: "events" | "mitt" | "eventemitter3" | "custom";
|
|
32
|
-
|
|
33
|
-
/** Pattern to match emitter instances (default: /emitter|bus|events/i) */
|
|
34
|
-
emitterPattern?: RegExp;
|
|
35
|
-
|
|
36
|
-
/** Maximum concurrent events to model (default: 5) */
|
|
37
|
-
maxInFlight?: number;
|
|
38
|
-
|
|
39
|
-
/** Maximum number of emitter instances (default: 3) */
|
|
40
|
-
maxEmitters?: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ─────────────────────────────────────────────────────────────────
|
|
44
|
-
// Event Bus Adapter Implementation
|
|
45
|
-
// ─────────────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
export class EventBusAdapter implements RoutingAdapter<EventBusAdapterConfig> {
|
|
48
|
-
readonly name = "event-bus";
|
|
49
|
-
readonly config: EventBusAdapterConfig;
|
|
50
|
-
private project: Project;
|
|
51
|
-
|
|
52
|
-
constructor(config: EventBusAdapterConfig) {
|
|
53
|
-
this.config = {
|
|
54
|
-
emitterLibrary: "events",
|
|
55
|
-
emitterPattern: /emitter|bus|events/i,
|
|
56
|
-
maxInFlight: 5,
|
|
57
|
-
maxEmitters: 3,
|
|
58
|
-
...config,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
this.project = new Project({
|
|
62
|
-
tsConfigFilePath: config.tsConfigPath,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Extract the complete verification model from the event-driven codebase
|
|
68
|
-
*/
|
|
69
|
-
extractModel(): CoreVerificationModel {
|
|
70
|
-
const sourceFiles = this.project.getSourceFiles();
|
|
71
|
-
|
|
72
|
-
// Extract all handlers
|
|
73
|
-
const handlers: MessageHandler[] = [];
|
|
74
|
-
const messageTypeNames = new Set<string>();
|
|
75
|
-
const emitterNames = new Set<string>();
|
|
76
|
-
|
|
77
|
-
for (const sourceFile of sourceFiles) {
|
|
78
|
-
const fileHandlers = this.extractHandlersFromFile(sourceFile);
|
|
79
|
-
handlers.push(...fileHandlers);
|
|
80
|
-
|
|
81
|
-
for (const handler of fileHandlers) {
|
|
82
|
-
messageTypeNames.add(handler.messageType);
|
|
83
|
-
emitterNames.add(handler.node);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Define nodes (emitters)
|
|
88
|
-
const nodes: NodeDefinition[] = [];
|
|
89
|
-
|
|
90
|
-
// Central event bus node (mediator pattern)
|
|
91
|
-
nodes.push({
|
|
92
|
-
id: "central-bus",
|
|
93
|
-
type: "event-bus",
|
|
94
|
-
canSendTo: ["*"],
|
|
95
|
-
canReceiveFrom: ["*"],
|
|
96
|
-
metadata: {
|
|
97
|
-
isMediator: true,
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Individual emitter nodes
|
|
102
|
-
for (const emitterName of emitterNames) {
|
|
103
|
-
if (emitterName !== "central-bus") {
|
|
104
|
-
nodes.push({
|
|
105
|
-
id: emitterName,
|
|
106
|
-
type: "emitter",
|
|
107
|
-
canSendTo: ["central-bus", "*"],
|
|
108
|
-
canReceiveFrom: ["central-bus", "*"],
|
|
109
|
-
metadata: {
|
|
110
|
-
isEmitter: true,
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Define message types
|
|
117
|
-
const messageTypes: MessageType[] = Array.from(messageTypeNames).map((name) => ({
|
|
118
|
-
name,
|
|
119
|
-
payload: {
|
|
120
|
-
name: "unknown",
|
|
121
|
-
kind: "unknown",
|
|
122
|
-
nullable: false,
|
|
123
|
-
},
|
|
124
|
-
routing: {
|
|
125
|
-
from: ["*"],
|
|
126
|
-
to: ["*"],
|
|
127
|
-
},
|
|
128
|
-
}));
|
|
129
|
-
|
|
130
|
-
// Define routing rules (event bus uses broadcast pattern)
|
|
131
|
-
const routingRules: RoutingRule[] = [
|
|
132
|
-
{
|
|
133
|
-
pattern: "broadcast",
|
|
134
|
-
messageTypes: Array.from(messageTypeNames),
|
|
135
|
-
description: "Event bus broadcast pattern - one-to-many messaging",
|
|
136
|
-
},
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
nodes,
|
|
141
|
-
messageTypes,
|
|
142
|
-
routingRules,
|
|
143
|
-
state: {}, // Populated by user configuration
|
|
144
|
-
handlers,
|
|
145
|
-
bounds: {
|
|
146
|
-
maxConcurrentMessages: this.config.maxInFlight!,
|
|
147
|
-
maxNodes: Math.min(nodes.length, this.config.maxEmitters! + 1), // +1 for central bus
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Recognize message handler registration: emitter.on("event", handler)
|
|
154
|
-
*/
|
|
155
|
-
recognizeMessageHandler(node: Node): MessageHandler | null {
|
|
156
|
-
if (!Node.isCallExpression(node)) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const expression = node.getExpression();
|
|
161
|
-
|
|
162
|
-
// Check if this is a .on() or .addListener() call
|
|
163
|
-
if (!Node.isPropertyAccessExpression(expression)) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const methodName = expression.getName();
|
|
168
|
-
if (methodName !== "on" && methodName !== "addListener" && methodName !== "addEventListener") {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return this.extractHandlerFromOnCall(node);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Recognize state mutation: state.field = value
|
|
177
|
-
*/
|
|
178
|
-
recognizeStateUpdate(node: Node): StateAssignment | null {
|
|
179
|
-
if (!Node.isBinaryExpression(node)) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const operator = node.getOperatorToken().getText();
|
|
184
|
-
if (operator !== "=") {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const left = node.getLeft();
|
|
189
|
-
const right = node.getRight();
|
|
190
|
-
|
|
191
|
-
// Check if left side is a state property access
|
|
192
|
-
if (!Node.isPropertyAccessExpression(left)) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const fieldPath = this.getPropertyPath(left);
|
|
197
|
-
|
|
198
|
-
// Check if this is a state access
|
|
199
|
-
if (!fieldPath.startsWith("state.")) {
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const field = fieldPath.substring(6); // Remove "state." prefix
|
|
204
|
-
const value = this.extractValue(right);
|
|
205
|
-
|
|
206
|
-
if (value === undefined) {
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
field,
|
|
212
|
-
value,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Recognize verification condition: requires() or ensures()
|
|
218
|
-
*/
|
|
219
|
-
recognizeVerificationCondition(
|
|
220
|
-
node: Node,
|
|
221
|
-
type: "precondition" | "postcondition"
|
|
222
|
-
): VerificationCondition | null {
|
|
223
|
-
if (!Node.isCallExpression(node)) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const callee = node.getExpression();
|
|
228
|
-
if (!Node.isIdentifier(callee)) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const functionName = callee.getText();
|
|
233
|
-
const expectedName = type === "precondition" ? "requires" : "ensures";
|
|
234
|
-
|
|
235
|
-
if (functionName !== expectedName) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return this.extractCondition(node);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ─────────────────────────────────────────────────────────────────
|
|
243
|
-
// Private Helper Methods
|
|
244
|
-
// ─────────────────────────────────────────────────────────────────
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Extract all handlers from a source file
|
|
248
|
-
*/
|
|
249
|
-
private extractHandlersFromFile(sourceFile: SourceFile): MessageHandler[] {
|
|
250
|
-
const handlers: MessageHandler[] = [];
|
|
251
|
-
|
|
252
|
-
sourceFile.forEachDescendant((node) => {
|
|
253
|
-
const handler = this.recognizeMessageHandler(node);
|
|
254
|
-
if (handler) {
|
|
255
|
-
handlers.push(handler);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
return handlers;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Extract handler details from a .on() call expression
|
|
264
|
-
*/
|
|
265
|
-
private extractHandlerFromOnCall(callExpr: Node): MessageHandler | null {
|
|
266
|
-
if (!Node.isCallExpression(callExpr)) {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const args = callExpr.getArguments();
|
|
271
|
-
|
|
272
|
-
if (args.length < 2) {
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// First argument should be the event name (string literal)
|
|
277
|
-
const eventNameArg = args[0];
|
|
278
|
-
let eventName: string | null = null;
|
|
279
|
-
|
|
280
|
-
if (Node.isStringLiteral(eventNameArg)) {
|
|
281
|
-
eventName = eventNameArg.getLiteralValue();
|
|
282
|
-
} else if (Node.isTemplateExpression(eventNameArg)) {
|
|
283
|
-
eventName = eventNameArg.getText().replace(/[`'"]/g, "");
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (!eventName) {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Second argument is the handler function
|
|
291
|
-
const handlerArg = args[1];
|
|
292
|
-
const assignments: StateAssignment[] = [];
|
|
293
|
-
const preconditions: VerificationCondition[] = [];
|
|
294
|
-
const postconditions: VerificationCondition[] = [];
|
|
295
|
-
|
|
296
|
-
// Parse the handler function for state assignments and verification conditions
|
|
297
|
-
if (Node.isArrowFunction(handlerArg) || Node.isFunctionExpression(handlerArg)) {
|
|
298
|
-
this.extractAssignmentsFromFunction(handlerArg, assignments);
|
|
299
|
-
this.extractVerificationConditionsFromFunction(handlerArg, preconditions, postconditions);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Infer emitter name from the call expression
|
|
303
|
-
const expression = callExpr.getExpression();
|
|
304
|
-
let emitterName = "central-bus";
|
|
305
|
-
if (Node.isPropertyAccessExpression(expression)) {
|
|
306
|
-
const emitterExpr = expression.getExpression();
|
|
307
|
-
if (Node.isIdentifier(emitterExpr)) {
|
|
308
|
-
emitterName = emitterExpr.getText();
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const sourceFile = callExpr.getSourceFile();
|
|
313
|
-
const line = callExpr.getStartLineNumber();
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
messageType: eventName,
|
|
317
|
-
node: emitterName,
|
|
318
|
-
assignments,
|
|
319
|
-
preconditions,
|
|
320
|
-
postconditions,
|
|
321
|
-
location: {
|
|
322
|
-
file: sourceFile.getFilePath(),
|
|
323
|
-
line,
|
|
324
|
-
},
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Extract state assignments from a handler function
|
|
330
|
-
*/
|
|
331
|
-
private extractAssignmentsFromFunction(funcNode: Node, assignments: StateAssignment[]): void {
|
|
332
|
-
funcNode.forEachDescendant((node) => {
|
|
333
|
-
const assignment = this.recognizeStateUpdate(node);
|
|
334
|
-
if (assignment) {
|
|
335
|
-
assignments.push(assignment);
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Extract verification conditions from a handler function
|
|
342
|
-
*/
|
|
343
|
-
private extractVerificationConditionsFromFunction(
|
|
344
|
-
funcNode: Node,
|
|
345
|
-
preconditions: VerificationCondition[],
|
|
346
|
-
postconditions: VerificationCondition[]
|
|
347
|
-
): void {
|
|
348
|
-
const body =
|
|
349
|
-
Node.isArrowFunction(funcNode) || Node.isFunctionExpression(funcNode)
|
|
350
|
-
? funcNode.getBody()
|
|
351
|
-
: funcNode;
|
|
352
|
-
|
|
353
|
-
if (!body) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Get all statements in the function body
|
|
358
|
-
const statements = Node.isBlock(body) ? body.getStatements() : [body];
|
|
359
|
-
|
|
360
|
-
for (const statement of statements) {
|
|
361
|
-
// Look for expression statements that are function calls
|
|
362
|
-
if (Node.isExpressionStatement(statement)) {
|
|
363
|
-
const expr = statement.getExpression();
|
|
364
|
-
|
|
365
|
-
const precond = this.recognizeVerificationCondition(expr, "precondition");
|
|
366
|
-
if (precond) {
|
|
367
|
-
preconditions.push(precond);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const postcond = this.recognizeVerificationCondition(expr, "postcondition");
|
|
371
|
-
if (postcond) {
|
|
372
|
-
postconditions.push(postcond);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Extract condition from a requires() or ensures() call
|
|
380
|
-
*/
|
|
381
|
-
private extractCondition(callExpr: Node): VerificationCondition | null {
|
|
382
|
-
if (!Node.isCallExpression(callExpr)) {
|
|
383
|
-
return null;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const args = callExpr.getArguments();
|
|
387
|
-
|
|
388
|
-
if (args.length === 0) {
|
|
389
|
-
return null;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// First argument is the condition expression
|
|
393
|
-
const conditionArg = args[0];
|
|
394
|
-
const expression = conditionArg.getText();
|
|
395
|
-
|
|
396
|
-
// Second argument (optional) is the message
|
|
397
|
-
let message: string | undefined;
|
|
398
|
-
if (args.length >= 2 && Node.isStringLiteral(args[1])) {
|
|
399
|
-
message = args[1].getLiteralValue();
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const line = callExpr.getStartLineNumber();
|
|
403
|
-
const column = callExpr.getStart();
|
|
404
|
-
|
|
405
|
-
return {
|
|
406
|
-
expression,
|
|
407
|
-
message,
|
|
408
|
-
location: {
|
|
409
|
-
line,
|
|
410
|
-
column,
|
|
411
|
-
},
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Get the full property access path (e.g., "state.user.loggedIn")
|
|
417
|
-
*/
|
|
418
|
-
private getPropertyPath(node: Node): string {
|
|
419
|
-
const parts: string[] = [];
|
|
420
|
-
|
|
421
|
-
let current: Node = node;
|
|
422
|
-
while (Node.isPropertyAccessExpression(current)) {
|
|
423
|
-
parts.unshift(current.getName());
|
|
424
|
-
current = current.getExpression();
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Add the base identifier
|
|
428
|
-
if (Node.isIdentifier(current)) {
|
|
429
|
-
parts.unshift(current.getText());
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return parts.join(".");
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Extract a literal value from an expression
|
|
437
|
-
*/
|
|
438
|
-
private extractValue(node: Node): string | boolean | number | null | undefined {
|
|
439
|
-
if (Node.isStringLiteral(node)) {
|
|
440
|
-
return node.getLiteralValue();
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (Node.isNumericLiteral(node)) {
|
|
444
|
-
return node.getLiteralValue();
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
if (node.getKind() === SyntaxKind.TrueKeyword) {
|
|
448
|
-
return true;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (node.getKind() === SyntaxKind.FalseKeyword) {
|
|
452
|
-
return false;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (node.getKind() === SyntaxKind.NullKeyword) {
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// For complex expressions, return undefined (can't extract)
|
|
460
|
-
return undefined;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Custom invariants specific to event buses
|
|
465
|
-
*/
|
|
466
|
-
customInvariants(): Array<[name: string, tlaExpression: string]> {
|
|
467
|
-
return [
|
|
468
|
-
[
|
|
469
|
-
"CentralBusAlwaysAvailable",
|
|
470
|
-
'ports["central-bus"] = "connected" \\* Central event bus should always be available',
|
|
471
|
-
],
|
|
472
|
-
[
|
|
473
|
-
"BroadcastOrdering",
|
|
474
|
-
"\\A msg1, msg2 \\in Range(messages) : " +
|
|
475
|
-
'(msg1.msgType = msg2.msgType /\\ msg1.status = "delivered" /\\ msg2.status = "pending") => ' +
|
|
476
|
-
"msg1.id < msg2.id \\* Events of the same type are delivered in order",
|
|
477
|
-
],
|
|
478
|
-
];
|
|
479
|
-
}
|
|
480
|
-
}
|