@fairfox/polly 0.1.2 → 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.
Files changed (68) hide show
  1. package/{cli/polly.ts → dist/cli/polly.js} +100 -206
  2. package/dist/cli/polly.js.map +10 -0
  3. package/dist/scripts/build-extension.js +137 -0
  4. package/dist/scripts/build-extension.js.map +10 -0
  5. package/dist/vendor/verify/src/cli.js +2089 -0
  6. package/dist/vendor/verify/src/cli.js.map +16 -0
  7. package/dist/vendor/visualize/src/cli.js +2204 -0
  8. package/dist/vendor/visualize/src/cli.js.map +19 -0
  9. package/package.json +12 -12
  10. package/vendor/analysis/src/extract/adr.ts +0 -212
  11. package/vendor/analysis/src/extract/architecture.ts +0 -160
  12. package/vendor/analysis/src/extract/contexts.ts +0 -298
  13. package/vendor/analysis/src/extract/flows.ts +0 -309
  14. package/vendor/analysis/src/extract/handlers.ts +0 -321
  15. package/vendor/analysis/src/extract/index.ts +0 -9
  16. package/vendor/analysis/src/extract/integrations.ts +0 -329
  17. package/vendor/analysis/src/extract/manifest.ts +0 -298
  18. package/vendor/analysis/src/extract/types.ts +0 -389
  19. package/vendor/analysis/src/index.ts +0 -7
  20. package/vendor/analysis/src/types/adr.ts +0 -53
  21. package/vendor/analysis/src/types/architecture.ts +0 -245
  22. package/vendor/analysis/src/types/core.ts +0 -210
  23. package/vendor/analysis/src/types/index.ts +0 -18
  24. package/vendor/verify/src/adapters/base.ts +0 -164
  25. package/vendor/verify/src/adapters/detection.ts +0 -281
  26. package/vendor/verify/src/adapters/event-bus/index.ts +0 -480
  27. package/vendor/verify/src/adapters/web-extension/index.ts +0 -508
  28. package/vendor/verify/src/adapters/websocket/index.ts +0 -486
  29. package/vendor/verify/src/cli.ts +0 -430
  30. package/vendor/verify/src/codegen/config.ts +0 -354
  31. package/vendor/verify/src/codegen/tla.ts +0 -719
  32. package/vendor/verify/src/config/parser.ts +0 -303
  33. package/vendor/verify/src/config/types.ts +0 -113
  34. package/vendor/verify/src/core/model.ts +0 -267
  35. package/vendor/verify/src/core/primitives.ts +0 -106
  36. package/vendor/verify/src/extract/handlers.ts +0 -2
  37. package/vendor/verify/src/extract/types.ts +0 -2
  38. package/vendor/verify/src/index.ts +0 -150
  39. package/vendor/verify/src/primitives/index.ts +0 -102
  40. package/vendor/verify/src/runner/docker.ts +0 -283
  41. package/vendor/verify/src/types.ts +0 -51
  42. package/vendor/visualize/src/cli.ts +0 -365
  43. package/vendor/visualize/src/codegen/structurizr.ts +0 -770
  44. package/vendor/visualize/src/index.ts +0 -13
  45. package/vendor/visualize/src/runner/export.ts +0 -235
  46. package/vendor/visualize/src/viewer/server.ts +0 -485
  47. /package/dist/{background → src/background}/index.js +0 -0
  48. /package/dist/{background → src/background}/index.js.map +0 -0
  49. /package/dist/{background → src/background}/message-router.js +0 -0
  50. /package/dist/{background → src/background}/message-router.js.map +0 -0
  51. /package/dist/{index.js → src/index.js} +0 -0
  52. /package/dist/{index.js.map → src/index.js.map} +0 -0
  53. /package/dist/{shared → src/shared}/adapters/index.js +0 -0
  54. /package/dist/{shared → src/shared}/adapters/index.js.map +0 -0
  55. /package/dist/{shared → src/shared}/lib/context-helpers.js +0 -0
  56. /package/dist/{shared → src/shared}/lib/context-helpers.js.map +0 -0
  57. /package/dist/{shared → src/shared}/lib/errors.js +0 -0
  58. /package/dist/{shared → src/shared}/lib/errors.js.map +0 -0
  59. /package/dist/{shared → src/shared}/lib/message-bus.js +0 -0
  60. /package/dist/{shared → src/shared}/lib/message-bus.js.map +0 -0
  61. /package/dist/{shared → src/shared}/lib/state.js +0 -0
  62. /package/dist/{shared → src/shared}/lib/state.js.map +0 -0
  63. /package/dist/{shared → src/shared}/lib/test-helpers.js +0 -0
  64. /package/dist/{shared → src/shared}/lib/test-helpers.js.map +0 -0
  65. /package/dist/{shared → src/shared}/state/app-state.js +0 -0
  66. /package/dist/{shared → src/shared}/state/app-state.js.map +0 -0
  67. /package/dist/{shared → src/shared}/types/messages.js +0 -0
  68. /package/dist/{shared → src/shared}/types/messages.js.map +0 -0
@@ -1,309 +0,0 @@
1
- // Message flow analysis - trace messages between contexts
2
-
3
- import { Project, Node } from "ts-morph";
4
- import type { MessageFlow, MessageStep } from "../types/architecture";
5
- import type { MessageHandler } from "../types/core";
6
-
7
- export class FlowAnalyzer {
8
- private project: Project;
9
- private handlers: MessageHandler[];
10
-
11
- constructor(tsConfigPath: string, handlers: MessageHandler[]) {
12
- this.project = new Project({
13
- tsConfigFilePath: tsConfigPath,
14
- });
15
- this.handlers = handlers;
16
- }
17
-
18
- /**
19
- * Analyze message flows between contexts
20
- */
21
- analyzeFlows(): MessageFlow[] {
22
- const flows: MessageFlow[] = [];
23
-
24
- // Group handlers by message type
25
- const handlersByType = new Map<string, MessageHandler[]>();
26
- for (const handler of this.handlers) {
27
- if (!handlersByType.has(handler.messageType)) {
28
- handlersByType.set(handler.messageType, []);
29
- }
30
- handlersByType.get(handler.messageType)!.push(handler);
31
- }
32
-
33
- // For each message type, trace the flow
34
- for (const [messageType, handlers] of handlersByType) {
35
- // Find senders
36
- const senders = this.findMessageSenders(messageType);
37
-
38
- // For each sender, create a flow
39
- for (const sender of senders) {
40
- const recipients = handlers.map((h) => h.node);
41
-
42
- // Build sequence of steps
43
- const sequence = this.buildSequence(messageType, sender, handlers);
44
-
45
- // Extract flow metadata
46
- const flowMetadata = this.extractFlowMetadata(sender.file, sender.line);
47
-
48
- flows.push({
49
- messageType,
50
- from: sender.context,
51
- to: recipients,
52
- trigger: flowMetadata.trigger,
53
- flowName: flowMetadata.flowName,
54
- description: flowMetadata.description,
55
- sequence,
56
- });
57
- }
58
- }
59
-
60
- return flows;
61
- }
62
-
63
- /**
64
- * Find all places where a message is sent
65
- */
66
- private findMessageSenders(messageType: string): Array<{
67
- context: string;
68
- file: string;
69
- line: number;
70
- }> {
71
- const senders: Array<{ context: string; file: string; line: number }> = [];
72
-
73
- for (const sourceFile of this.project.getSourceFiles()) {
74
- const filePath = sourceFile.getFilePath();
75
- const context = this.inferContext(filePath);
76
-
77
- sourceFile.forEachDescendant((node) => {
78
- if (Node.isCallExpression(node)) {
79
- const expression = node.getExpression();
80
-
81
- // Check for .send() or .emit() calls
82
- if (Node.isPropertyAccessExpression(expression)) {
83
- const methodName = expression.getName();
84
-
85
- if (methodName === "send" || methodName === "emit") {
86
- const args = node.getArguments();
87
- if (args.length > 0) {
88
- const firstArg = args[0];
89
-
90
- let msgType: string | undefined;
91
-
92
- // Check if first argument is a string literal: send("MESSAGE")
93
- if (Node.isStringLiteral(firstArg)) {
94
- msgType = firstArg.getLiteralValue();
95
- }
96
- // Check if first argument is an object literal: send({ type: "MESSAGE" })
97
- else if (Node.isObjectLiteralExpression(firstArg)) {
98
- const typeProperty = firstArg.getProperty("type");
99
- if (typeProperty && Node.isPropertyAssignment(typeProperty)) {
100
- const initializer = typeProperty.getInitializer();
101
- if (initializer && Node.isStringLiteral(initializer)) {
102
- msgType = initializer.getLiteralValue();
103
- }
104
- }
105
- }
106
-
107
- if (msgType === messageType) {
108
- senders.push({
109
- context,
110
- file: filePath,
111
- line: node.getStartLineNumber(),
112
- });
113
- }
114
- }
115
- }
116
- }
117
- }
118
- });
119
- }
120
-
121
- return senders;
122
- }
123
-
124
- /**
125
- * Build sequence of steps for a message flow
126
- */
127
- private buildSequence(
128
- messageType: string,
129
- sender: { context: string; file: string; line: number },
130
- handlers: MessageHandler[]
131
- ): MessageStep[] {
132
- const steps: MessageStep[] = [];
133
- let stepNumber = 1;
134
-
135
- // Step 1: Send message
136
- steps.push({
137
- step: stepNumber++,
138
- action: `${sender.context}.send(${messageType})`,
139
- context: sender.context,
140
- location: {
141
- file: sender.file,
142
- line: sender.line,
143
- },
144
- });
145
-
146
- // Step 2+: Handle in each recipient context
147
- for (const handler of handlers) {
148
- steps.push({
149
- step: stepNumber++,
150
- action: `${handler.node}.handle(${messageType})`,
151
- context: handler.node,
152
- location: handler.location,
153
- });
154
-
155
- // Add substeps for any messages sent by this handler
156
- const subsends = this.findMessagesInHandler(handler);
157
- for (const subsend of subsends) {
158
- steps.push({
159
- step: stepNumber++,
160
- action: `${handler.node}.send(${subsend.messageType})`,
161
- context: handler.node,
162
- location: subsend.location,
163
- });
164
- }
165
- }
166
-
167
- return steps;
168
- }
169
-
170
- /**
171
- * Find messages sent within a handler
172
- */
173
- private findMessagesInHandler(handler: MessageHandler): Array<{
174
- messageType: string;
175
- location: { file: string; line: number };
176
- }> {
177
- const sends: Array<{ messageType: string; location: { file: string; line: number } }> = [];
178
-
179
- const sourceFile = this.project.getSourceFile(handler.location.file);
180
- if (!sourceFile) return sends;
181
-
182
- // Find the handler function at the given line
183
- const targetLine = handler.location.line;
184
-
185
- sourceFile.forEachDescendant((node) => {
186
- if (Node.isCallExpression(node)) {
187
- const line = node.getStartLineNumber();
188
-
189
- // Rough heuristic: if it's near the handler line, it's probably in the handler
190
- if (Math.abs(line - targetLine) < 20) {
191
- const expression = node.getExpression();
192
-
193
- if (Node.isPropertyAccessExpression(expression)) {
194
- const methodName = expression.getName();
195
-
196
- if (methodName === "send" || methodName === "emit") {
197
- const args = node.getArguments();
198
- if (args.length > 0) {
199
- const firstArg = args[0];
200
- let messageType: string | undefined;
201
-
202
- // Check if first argument is a string literal: send("MESSAGE")
203
- if (Node.isStringLiteral(firstArg)) {
204
- messageType = firstArg.getLiteralValue();
205
- }
206
- // Check if first argument is an object literal: send({ type: "MESSAGE" })
207
- else if (Node.isObjectLiteralExpression(firstArg)) {
208
- const typeProperty = firstArg.getProperty("type");
209
- if (typeProperty && Node.isPropertyAssignment(typeProperty)) {
210
- const initializer = typeProperty.getInitializer();
211
- if (initializer && Node.isStringLiteral(initializer)) {
212
- messageType = initializer.getLiteralValue();
213
- }
214
- }
215
- }
216
-
217
- if (messageType) {
218
- sends.push({
219
- messageType,
220
- location: {
221
- file: handler.location.file,
222
- line,
223
- },
224
- });
225
- }
226
- }
227
- }
228
- }
229
- }
230
- }
231
- });
232
-
233
- return sends;
234
- }
235
-
236
- /**
237
- * Extract flow metadata from JSDoc annotations
238
- */
239
- private extractFlowMetadata(
240
- filePath: string,
241
- lineNumber: number
242
- ): {
243
- trigger?: string;
244
- flowName?: string;
245
- description?: string;
246
- } {
247
- const sourceFile = this.project.getSourceFile(filePath);
248
- if (!sourceFile) return {};
249
-
250
- // Find the node at this line
251
- let targetNode: any = null;
252
-
253
- sourceFile.forEachDescendant((node) => {
254
- if (node.getStartLineNumber() === lineNumber) {
255
- targetNode = node;
256
- }
257
- });
258
-
259
- if (!targetNode) return {};
260
-
261
- // Look for JSDoc comments
262
- const jsDocs = targetNode.getJsDocs?.() || [];
263
- if (jsDocs.length === 0) return {};
264
-
265
- const comment = jsDocs[0].getText();
266
-
267
- // Extract @flow annotation
268
- const flowMatch = comment.match(/@flow\s+([^\s]+)/);
269
- const flowName = flowMatch ? flowMatch[1] : undefined;
270
-
271
- // Extract @trigger annotation
272
- const triggerMatch = comment.match(/@trigger\s+(.+?)(?:\n|$)/);
273
- const trigger = triggerMatch ? triggerMatch[1].trim() : undefined;
274
-
275
- // Extract @description
276
- const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
277
- const description = descMatch ? descMatch[1].trim() : undefined;
278
-
279
- return { trigger, flowName, description };
280
- }
281
-
282
- /**
283
- * Infer context from file path
284
- */
285
- private inferContext(filePath: string): string {
286
- const path = filePath.toLowerCase();
287
-
288
- if (path.includes("/background/") || path.includes("\\background\\")) {
289
- return "background";
290
- }
291
- if (path.includes("/content/") || path.includes("\\content\\")) {
292
- return "content";
293
- }
294
- if (path.includes("/popup/") || path.includes("\\popup\\")) {
295
- return "popup";
296
- }
297
- if (path.includes("/devtools/") || path.includes("\\devtools\\")) {
298
- return "devtools";
299
- }
300
- if (path.includes("/options/") || path.includes("\\options\\")) {
301
- return "options";
302
- }
303
- if (path.includes("/offscreen/") || path.includes("\\offscreen\\")) {
304
- return "offscreen";
305
- }
306
-
307
- return "unknown";
308
- }
309
- }
@@ -1,321 +0,0 @@
1
- // Handler extraction from TypeScript code
2
- // Extracts message handlers and their state mutations
3
-
4
- import { Project, type SourceFile, SyntaxKind, Node } from "ts-morph";
5
- import type { MessageHandler, StateAssignment, VerificationCondition } from "../types";
6
-
7
- export interface HandlerAnalysis {
8
- handlers: MessageHandler[];
9
- messageTypes: Set<string>;
10
- }
11
-
12
- export class HandlerExtractor {
13
- private project: Project;
14
-
15
- constructor(tsConfigPath: string) {
16
- this.project = new Project({
17
- tsConfigFilePath: tsConfigPath,
18
- });
19
- }
20
-
21
- /**
22
- * Extract all message handlers from the codebase
23
- */
24
- extractHandlers(): HandlerAnalysis {
25
- const handlers: MessageHandler[] = [];
26
- const messageTypes = new Set<string>();
27
-
28
- // Find all source files
29
- const sourceFiles = this.project.getSourceFiles();
30
-
31
- for (const sourceFile of sourceFiles) {
32
- const fileHandlers = this.extractFromFile(sourceFile);
33
- handlers.push(...fileHandlers);
34
-
35
- for (const handler of fileHandlers) {
36
- messageTypes.add(handler.messageType);
37
- }
38
- }
39
-
40
- return {
41
- handlers,
42
- messageTypes,
43
- };
44
- }
45
-
46
- /**
47
- * Extract handlers from a single source file
48
- */
49
- private extractFromFile(sourceFile: SourceFile): MessageHandler[] {
50
- const handlers: MessageHandler[] = [];
51
- const filePath = sourceFile.getFilePath();
52
-
53
- // Determine context from file path
54
- const context = this.inferContext(filePath);
55
-
56
- // Find all .on() call expressions
57
- sourceFile.forEachDescendant((node) => {
58
- if (Node.isCallExpression(node)) {
59
- const expression = node.getExpression();
60
-
61
- // Check if this is a .on() call
62
- if (Node.isPropertyAccessExpression(expression)) {
63
- const methodName = expression.getName();
64
-
65
- if (methodName === "on") {
66
- const handler = this.extractHandler(node, context, filePath);
67
- if (handler) {
68
- handlers.push(handler);
69
- }
70
- }
71
- }
72
- }
73
- });
74
-
75
- return handlers;
76
- }
77
-
78
- /**
79
- * Extract handler details from a .on() call expression
80
- */
81
- private extractHandler(callExpr: any, context: string, filePath: string): MessageHandler | null {
82
- const args = callExpr.getArguments();
83
-
84
- if (args.length < 2) {
85
- return null;
86
- }
87
-
88
- // First argument should be the message type (string literal)
89
- const messageTypeArg = args[0];
90
- let messageType: string | null = null;
91
-
92
- if (Node.isStringLiteral(messageTypeArg)) {
93
- messageType = messageTypeArg.getLiteralValue();
94
- } else if (Node.isTemplateExpression(messageTypeArg)) {
95
- // Handle template literals if needed
96
- messageType = messageTypeArg.getText().replace(/[`'"]/g, "");
97
- }
98
-
99
- if (!messageType) {
100
- return null;
101
- }
102
-
103
- // Second argument is the handler function
104
- const handlerArg = args[1];
105
- const assignments: StateAssignment[] = [];
106
- const preconditions: VerificationCondition[] = [];
107
- const postconditions: VerificationCondition[] = [];
108
-
109
- // Parse the handler function for state assignments and verification conditions
110
- if (Node.isArrowFunction(handlerArg) || Node.isFunctionExpression(handlerArg)) {
111
- this.extractAssignments(handlerArg, assignments);
112
- this.extractVerificationConditions(handlerArg, preconditions, postconditions);
113
- }
114
-
115
- const line = callExpr.getStartLineNumber();
116
-
117
- return {
118
- messageType,
119
- node: context, // Renamed from 'context' to 'node' for generalization
120
- assignments,
121
- preconditions,
122
- postconditions,
123
- location: {
124
- file: filePath,
125
- line,
126
- },
127
- };
128
- }
129
-
130
- /**
131
- * Extract state assignments from a handler function
132
- */
133
- private extractAssignments(funcNode: any, assignments: StateAssignment[]): void {
134
- funcNode.forEachDescendant((node: any) => {
135
- // Look for assignment expressions: state.field = value
136
- if (Node.isBinaryExpression(node)) {
137
- const operator = node.getOperatorToken().getText();
138
-
139
- if (operator === "=") {
140
- const left = node.getLeft();
141
- const right = node.getRight();
142
-
143
- // Check if left side is a state property access
144
- if (Node.isPropertyAccessExpression(left)) {
145
- const fieldPath = this.getPropertyPath(left);
146
-
147
- // Check if this is a state access
148
- if (fieldPath.startsWith("state.")) {
149
- const field = fieldPath.substring(6); // Remove "state." prefix
150
- const value = this.extractValue(right);
151
-
152
- if (value !== undefined) {
153
- assignments.push({
154
- field,
155
- value,
156
- });
157
- }
158
- }
159
- }
160
- }
161
- }
162
- });
163
- }
164
-
165
- /**
166
- * Extract verification conditions (requires/ensures) from a handler function
167
- */
168
- private extractVerificationConditions(
169
- funcNode: any,
170
- preconditions: VerificationCondition[],
171
- postconditions: VerificationCondition[]
172
- ): void {
173
- const body = funcNode.getBody();
174
-
175
- // Get all statements in the function body
176
- const statements = Node.isBlock(body) ? body.getStatements() : [body];
177
-
178
- statements.forEach((statement: any, index: number) => {
179
- // Look for expression statements that are function calls
180
- if (Node.isExpressionStatement(statement)) {
181
- const expr = statement.getExpression();
182
-
183
- if (Node.isCallExpression(expr)) {
184
- const callee = expr.getExpression();
185
-
186
- if (Node.isIdentifier(callee)) {
187
- const functionName = callee.getText();
188
-
189
- if (functionName === "requires") {
190
- // Extract precondition
191
- const condition = this.extractCondition(expr);
192
- if (condition) {
193
- preconditions.push(condition);
194
- }
195
- } else if (functionName === "ensures") {
196
- // Extract postcondition
197
- const condition = this.extractCondition(expr);
198
- if (condition) {
199
- postconditions.push(condition);
200
- }
201
- }
202
- }
203
- }
204
- }
205
- });
206
- }
207
-
208
- /**
209
- * Extract condition from a requires() or ensures() call
210
- */
211
- private extractCondition(callExpr: any): VerificationCondition | null {
212
- const args = callExpr.getArguments();
213
-
214
- if (args.length === 0) {
215
- return null;
216
- }
217
-
218
- // First argument is the condition expression
219
- const conditionArg = args[0];
220
- const expression = conditionArg.getText();
221
-
222
- // Second argument (optional) is the message
223
- let message: string | undefined;
224
- if (args.length >= 2 && Node.isStringLiteral(args[1])) {
225
- message = args[1].getLiteralValue();
226
- }
227
-
228
- const line = callExpr.getStartLineNumber();
229
- const column = callExpr.getStartLinePos();
230
-
231
- return {
232
- expression,
233
- message,
234
- location: {
235
- line,
236
- column,
237
- },
238
- };
239
- }
240
-
241
- /**
242
- * Get the full property access path (e.g., "state.user.loggedIn")
243
- */
244
- private getPropertyPath(node: any): string {
245
- const parts: string[] = [];
246
-
247
- let current = node;
248
- while (Node.isPropertyAccessExpression(current)) {
249
- parts.unshift(current.getName());
250
- current = current.getExpression();
251
- }
252
-
253
- // Add the base identifier
254
- if (Node.isIdentifier(current)) {
255
- parts.unshift(current.getText());
256
- }
257
-
258
- return parts.join(".");
259
- }
260
-
261
- /**
262
- * Extract a literal value from an expression
263
- */
264
- private extractValue(node: any): string | boolean | number | null | undefined {
265
- if (Node.isStringLiteral(node)) {
266
- return node.getLiteralValue();
267
- }
268
-
269
- if (Node.isNumericLiteral(node)) {
270
- return node.getLiteralValue();
271
- }
272
-
273
- if (node.getKind() === SyntaxKind.TrueKeyword) {
274
- return true;
275
- }
276
-
277
- if (node.getKind() === SyntaxKind.FalseKeyword) {
278
- return false;
279
- }
280
-
281
- if (node.getKind() === SyntaxKind.NullKeyword) {
282
- return null;
283
- }
284
-
285
- // For complex expressions, return undefined (can't extract)
286
- return undefined;
287
- }
288
-
289
- /**
290
- * Infer the context (background, content, popup, etc.) from file path
291
- */
292
- private inferContext(filePath: string): string {
293
- const path = filePath.toLowerCase();
294
-
295
- if (path.includes("/background/") || path.includes("\\background\\")) {
296
- return "background";
297
- }
298
- if (path.includes("/content/") || path.includes("\\content\\")) {
299
- return "content";
300
- }
301
- if (path.includes("/popup/") || path.includes("\\popup\\")) {
302
- return "popup";
303
- }
304
- if (path.includes("/devtools/") || path.includes("\\devtools\\")) {
305
- return "devtools";
306
- }
307
- if (path.includes("/options/") || path.includes("\\options\\")) {
308
- return "options";
309
- }
310
- if (path.includes("/offscreen/") || path.includes("\\offscreen\\")) {
311
- return "offscreen";
312
- }
313
-
314
- return "unknown";
315
- }
316
- }
317
-
318
- export function extractHandlers(tsConfigPath: string): HandlerAnalysis {
319
- const extractor = new HandlerExtractor(tsConfigPath);
320
- return extractor.extractHandlers();
321
- }
@@ -1,9 +0,0 @@
1
- // Export all extraction utilities
2
- export * from "./types";
3
- export * from "./handlers";
4
- export * from "./manifest";
5
- export * from "./contexts";
6
- export * from "./flows";
7
- export * from "./integrations";
8
- export * from "./adr";
9
- export * from "./architecture";