@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.
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,329 +0,0 @@
1
- // External integration detection - find external APIs, services, etc.
2
-
3
- import { Project, Node } from "ts-morph";
4
- import type { ExternalIntegration, ExternalAPICall } from "../types/architecture";
5
-
6
- export class IntegrationAnalyzer {
7
- private project: Project;
8
-
9
- constructor(tsConfigPath: string) {
10
- this.project = new Project({
11
- tsConfigFilePath: tsConfigPath,
12
- });
13
- }
14
-
15
- /**
16
- * Analyze external integrations in the codebase
17
- */
18
- analyzeIntegrations(): ExternalIntegration[] {
19
- const integrations = new Map<string, ExternalIntegration>();
20
-
21
- // Find all fetch() calls
22
- const fetchCalls = this.findFetchCalls();
23
- for (const call of fetchCalls) {
24
- this.addOrMergeIntegration(integrations, this.createAPIIntegration(call));
25
- }
26
-
27
- // Find WebSocket connections
28
- const websockets = this.findWebSockets();
29
- for (const ws of websockets) {
30
- this.addOrMergeIntegration(integrations, this.createWebSocketIntegration(ws));
31
- }
32
-
33
- // Find external script imports
34
- const externalScripts = this.findExternalScripts();
35
- for (const script of externalScripts) {
36
- this.addOrMergeIntegration(integrations, script);
37
- }
38
-
39
- return Array.from(integrations.values());
40
- }
41
-
42
- /**
43
- * Find all fetch() API calls
44
- */
45
- private findFetchCalls(): Array<{
46
- url: string;
47
- method: string;
48
- file: string;
49
- line: number;
50
- description?: string;
51
- }> {
52
- const calls: Array<{
53
- url: string;
54
- method: string;
55
- file: string;
56
- line: number;
57
- description?: string;
58
- }> = [];
59
-
60
- for (const sourceFile of this.project.getSourceFiles()) {
61
- sourceFile.forEachDescendant((node) => {
62
- if (Node.isCallExpression(node)) {
63
- const expression = node.getExpression();
64
-
65
- // Check for fetch() calls
66
- if (Node.isIdentifier(expression) && expression.getText() === "fetch") {
67
- const args = node.getArguments();
68
- if (args.length > 0) {
69
- // Extract URL
70
- const urlArg = args[0];
71
- let url: string | null = null;
72
-
73
- if (Node.isStringLiteral(urlArg)) {
74
- url = urlArg.getLiteralValue();
75
- } else if (Node.isTemplateExpression(urlArg)) {
76
- // Try to extract base URL from template
77
- url = this.extractBaseURL(urlArg.getText());
78
- }
79
-
80
- if (url) {
81
- // Extract method
82
- let method = "GET"; // Default
83
- if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
84
- const options = args[1];
85
- const methodProp = options.getProperty("method");
86
- if (methodProp && Node.isPropertyAssignment(methodProp)) {
87
- const initializer = methodProp.getInitializer();
88
- if (initializer && Node.isStringLiteral(initializer)) {
89
- method = initializer.getLiteralValue().toUpperCase();
90
- }
91
- }
92
- }
93
-
94
- // Extract description from JSDoc
95
- const description = this.extractJSDocDescription(node);
96
-
97
- calls.push({
98
- url,
99
- method,
100
- file: sourceFile.getFilePath(),
101
- line: node.getStartLineNumber(),
102
- description,
103
- });
104
- }
105
- }
106
- }
107
- }
108
- });
109
- }
110
-
111
- return calls;
112
- }
113
-
114
- /**
115
- * Find WebSocket connections
116
- */
117
- private findWebSockets(): Array<{
118
- url: string;
119
- file: string;
120
- line: number;
121
- description?: string;
122
- }> {
123
- const websockets: Array<{
124
- url: string;
125
- file: string;
126
- line: number;
127
- description?: string;
128
- }> = [];
129
-
130
- for (const sourceFile of this.project.getSourceFiles()) {
131
- sourceFile.forEachDescendant((node) => {
132
- if (Node.isNewExpression(node)) {
133
- const expression = node.getExpression();
134
-
135
- // Check for new WebSocket()
136
- if (Node.isIdentifier(expression) && expression.getText() === "WebSocket") {
137
- const args = node.getArguments();
138
- if (args.length > 0 && Node.isStringLiteral(args[0])) {
139
- const url = args[0].getLiteralValue();
140
- const description = this.extractJSDocDescription(node);
141
-
142
- websockets.push({
143
- url,
144
- file: sourceFile.getFilePath(),
145
- line: node.getStartLineNumber(),
146
- description,
147
- });
148
- }
149
- }
150
- }
151
- });
152
- }
153
-
154
- return websockets;
155
- }
156
-
157
- /**
158
- * Find external script dependencies (from imports)
159
- */
160
- private findExternalScripts(): ExternalIntegration[] {
161
- const scripts: ExternalIntegration[] = [];
162
- const seen = new Set<string>();
163
-
164
- for (const sourceFile of this.project.getSourceFiles()) {
165
- for (const importDecl of sourceFile.getImportDeclarations()) {
166
- const moduleSpecifier = importDecl.getModuleSpecifierValue();
167
-
168
- // Only consider external packages (not relative imports)
169
- if (!moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/")) {
170
- // Extract package name (handle scoped packages)
171
- const packageName = moduleSpecifier.startsWith("@")
172
- ? moduleSpecifier.split("/").slice(0, 2).join("/")
173
- : moduleSpecifier.split("/")[0];
174
-
175
- if (!seen.has(packageName)) {
176
- seen.add(packageName);
177
-
178
- scripts.push({
179
- type: "external-script",
180
- name: packageName,
181
- technology: "npm package",
182
- usedIn: [sourceFile.getFilePath()],
183
- description: `External dependency: ${packageName}`,
184
- });
185
- }
186
- }
187
- }
188
- }
189
-
190
- return scripts;
191
- }
192
-
193
- /**
194
- * Create API integration from fetch call
195
- */
196
- private createAPIIntegration(call: {
197
- url: string;
198
- method: string;
199
- file: string;
200
- line: number;
201
- description?: string;
202
- }): ExternalIntegration {
203
- // Extract base URL
204
- const baseURL = this.extractBaseURL(call.url);
205
-
206
- // Infer name from URL
207
- const name = this.inferAPIName(baseURL);
208
-
209
- return {
210
- type: "api",
211
- name,
212
- technology: "REST API",
213
- url: baseURL,
214
- usedIn: [call.file],
215
- description: call.description || `External API: ${name}`,
216
- calls: [
217
- {
218
- method: call.method,
219
- endpoint: call.url,
220
- location: {
221
- file: call.file,
222
- line: call.line,
223
- },
224
- description: call.description,
225
- },
226
- ],
227
- };
228
- }
229
-
230
- /**
231
- * Create WebSocket integration
232
- */
233
- private createWebSocketIntegration(ws: {
234
- url: string;
235
- file: string;
236
- line: number;
237
- description?: string;
238
- }): ExternalIntegration {
239
- const name = this.inferAPIName(ws.url);
240
-
241
- return {
242
- type: "websocket",
243
- name,
244
- technology: "WebSocket",
245
- url: ws.url,
246
- usedIn: [ws.file],
247
- description: ws.description || `WebSocket connection: ${name}`,
248
- };
249
- }
250
-
251
- /**
252
- * Extract base URL from full URL or template
253
- */
254
- private extractBaseURL(url: string): string {
255
- // Remove template parts
256
- url = url.replace(/\$\{[^}]+\}/g, "");
257
- url = url.replace(/`/g, "");
258
-
259
- try {
260
- const parsed = new URL(url);
261
- return `${parsed.protocol}//${parsed.host}`;
262
- } catch {
263
- // If parsing fails, try to extract domain
264
- const match = url.match(/https?:\/\/([^/]+)/);
265
- return match ? match[0] : url;
266
- }
267
- }
268
-
269
- /**
270
- * Infer API name from URL
271
- */
272
- private inferAPIName(url: string): string {
273
- try {
274
- const parsed = new URL(url);
275
- const hostname = parsed.hostname;
276
-
277
- // Remove www. prefix
278
- const cleanHost = hostname.replace(/^www\./, "");
279
-
280
- // Take first part of domain
281
- const parts = cleanHost.split(".");
282
- if (parts.length > 0) {
283
- // Capitalize first letter
284
- return parts[0].charAt(0).toUpperCase() + parts[0].slice(1) + " API";
285
- }
286
- } catch {
287
- // Fallback
288
- }
289
-
290
- return "External API";
291
- }
292
-
293
- /**
294
- * Add or merge integration into map
295
- */
296
- private addOrMergeIntegration(
297
- map: Map<string, ExternalIntegration>,
298
- integration: ExternalIntegration
299
- ): void {
300
- const key = `${integration.type}:${integration.name}`;
301
-
302
- if (map.has(key)) {
303
- const existing = map.get(key)!;
304
-
305
- // Merge usedIn
306
- existing.usedIn = [...new Set([...existing.usedIn, ...integration.usedIn])];
307
-
308
- // Merge calls
309
- if (integration.calls && existing.calls) {
310
- existing.calls.push(...integration.calls);
311
- } else if (integration.calls) {
312
- existing.calls = integration.calls;
313
- }
314
- } else {
315
- map.set(key, integration);
316
- }
317
- }
318
-
319
- /**
320
- * Extract JSDoc description from node
321
- */
322
- private extractJSDocDescription(node: any): string | undefined {
323
- const jsDocs = node.getJsDocs?.() || [];
324
- if (jsDocs.length === 0) return undefined;
325
-
326
- const comment = jsDocs[0].getDescription().trim();
327
- return comment || undefined;
328
- }
329
- }
@@ -1,298 +0,0 @@
1
- // Manifest.json parser for Chrome extensions
2
-
3
- import * as fs from "node:fs";
4
- import * as path from "node:path";
5
- import type { ManifestInfo, ContextInfo } from "../types/architecture";
6
-
7
- /**
8
- * Parse manifest.json and extract context information
9
- */
10
- export class ManifestParser {
11
- private manifestPath: string;
12
- private manifestData: any;
13
- private baseDir: string;
14
-
15
- constructor(projectRoot: string) {
16
- this.baseDir = projectRoot;
17
- this.manifestPath = path.join(projectRoot, "manifest.json");
18
-
19
- if (!fs.existsSync(this.manifestPath)) {
20
- throw new Error(`manifest.json not found at ${this.manifestPath}`);
21
- }
22
-
23
- try {
24
- const content = fs.readFileSync(this.manifestPath, "utf-8");
25
- this.manifestData = JSON.parse(content);
26
- } catch (error) {
27
- throw new Error(`Failed to parse manifest.json: ${error}`);
28
- }
29
- }
30
-
31
- /**
32
- * Parse manifest and extract all information
33
- */
34
- parse(): ManifestInfo {
35
- const manifest = this.manifestData;
36
-
37
- return {
38
- name: manifest.name || "Unknown Extension",
39
- version: manifest.version || "0.0.0",
40
- description: manifest.description,
41
- manifestVersion: manifest.manifest_version || 2,
42
- background: this.parseBackground(),
43
- contentScripts: this.parseContentScripts(),
44
- popup: this.parsePopup(),
45
- options: this.parseOptions(),
46
- devtools: this.parseDevtools(),
47
- permissions: manifest.permissions || [],
48
- hostPermissions: manifest.host_permissions || [],
49
- };
50
- }
51
-
52
- /**
53
- * Get context entry points from manifest
54
- */
55
- getContextEntryPoints(): Record<string, string> {
56
- const entryPoints: Record<string, string> = {};
57
-
58
- // Background
59
- const background = this.parseBackground();
60
- if (background) {
61
- // Take first file as entry point
62
- const entryFile = background.files[0];
63
- if (entryFile) {
64
- entryPoints.background = this.findSourceFile(entryFile);
65
- }
66
- }
67
-
68
- // Content scripts
69
- const contentScripts = this.parseContentScripts();
70
- if (contentScripts && contentScripts.length > 0) {
71
- const firstScript = contentScripts[0].js[0];
72
- if (firstScript) {
73
- entryPoints.content = this.findSourceFile(firstScript);
74
- }
75
- }
76
-
77
- // Popup
78
- const popup = this.parsePopup();
79
- if (popup) {
80
- // For HTML, we need to find the associated JS/TS file
81
- const htmlPath = path.join(this.baseDir, popup.html);
82
- const jsPath = this.findAssociatedJS(htmlPath);
83
- if (jsPath) {
84
- entryPoints.popup = jsPath;
85
- }
86
- }
87
-
88
- // Options
89
- const options = this.parseOptions();
90
- if (options) {
91
- const htmlPath = path.join(this.baseDir, options.page);
92
- const jsPath = this.findAssociatedJS(htmlPath);
93
- if (jsPath) {
94
- entryPoints.options = jsPath;
95
- }
96
- }
97
-
98
- // DevTools
99
- const devtools = this.parseDevtools();
100
- if (devtools) {
101
- const htmlPath = path.join(this.baseDir, devtools.page);
102
- const jsPath = this.findAssociatedJS(htmlPath);
103
- if (jsPath) {
104
- entryPoints.devtools = jsPath;
105
- }
106
- }
107
-
108
- return entryPoints;
109
- }
110
-
111
- /**
112
- * Parse background configuration
113
- */
114
- private parseBackground(): ManifestInfo["background"] {
115
- const bg = this.manifestData.background;
116
- if (!bg) return undefined;
117
-
118
- // Manifest V3 - service worker
119
- if (bg.service_worker) {
120
- return {
121
- type: "service_worker",
122
- files: [bg.service_worker],
123
- };
124
- }
125
-
126
- // Manifest V2 - scripts
127
- if (bg.scripts) {
128
- return {
129
- type: "script",
130
- files: bg.scripts,
131
- };
132
- }
133
-
134
- // Manifest V2 - page
135
- if (bg.page) {
136
- return {
137
- type: "script",
138
- files: [bg.page],
139
- };
140
- }
141
-
142
- return undefined;
143
- }
144
-
145
- /**
146
- * Parse content scripts configuration
147
- */
148
- private parseContentScripts(): ManifestInfo["contentScripts"] {
149
- const cs = this.manifestData.content_scripts;
150
- if (!cs || !Array.isArray(cs)) return undefined;
151
-
152
- return cs.map((script) => ({
153
- matches: script.matches || [],
154
- js: script.js || [],
155
- css: script.css,
156
- }));
157
- }
158
-
159
- /**
160
- * Parse popup configuration
161
- */
162
- private parsePopup(): ManifestInfo["popup"] {
163
- const action = this.manifestData.action || this.manifestData.browser_action;
164
- if (!action) return undefined;
165
-
166
- if (action.default_popup) {
167
- return {
168
- html: action.default_popup,
169
- default: true,
170
- };
171
- }
172
-
173
- return undefined;
174
- }
175
-
176
- /**
177
- * Parse options configuration
178
- */
179
- private parseOptions(): ManifestInfo["options"] {
180
- const options = this.manifestData.options_ui || this.manifestData.options_page;
181
- if (!options) return undefined;
182
-
183
- if (typeof options === "string") {
184
- return {
185
- page: options,
186
- openInTab: false,
187
- };
188
- }
189
-
190
- return {
191
- page: options.page,
192
- openInTab: options.open_in_tab,
193
- };
194
- }
195
-
196
- /**
197
- * Parse devtools configuration
198
- */
199
- private parseDevtools(): ManifestInfo["devtools"] {
200
- const devtools = this.manifestData.devtools_page;
201
- if (!devtools) return undefined;
202
-
203
- return {
204
- page: devtools,
205
- };
206
- }
207
-
208
- /**
209
- * Find source file from manifest reference
210
- * Tries multiple locations: exact path, src/ directory, .ts extension
211
- */
212
- private findSourceFile(manifestPath: string): string {
213
- const candidates = [
214
- // Exact path from manifest
215
- path.join(this.baseDir, manifestPath),
216
- // Same path with .ts extension
217
- path.join(this.baseDir, manifestPath.replace(/\.js$/, ".ts")),
218
- // In src/ directory
219
- path.join(this.baseDir, "src", manifestPath),
220
- path.join(this.baseDir, "src", manifestPath.replace(/\.js$/, ".ts")),
221
- // In src/ with .tsx extension
222
- path.join(this.baseDir, "src", manifestPath.replace(/\.js$/, ".tsx")),
223
- ];
224
-
225
- for (const candidate of candidates) {
226
- if (fs.existsSync(candidate)) {
227
- return candidate;
228
- }
229
- }
230
-
231
- // Fallback to manifest path (will error later if not found)
232
- return path.join(this.baseDir, manifestPath);
233
- }
234
-
235
- /**
236
- * Find associated JavaScript/TypeScript file for an HTML file
237
- */
238
- private findAssociatedJS(htmlPath: string): string | null {
239
- if (!fs.existsSync(htmlPath)) {
240
- return null;
241
- }
242
-
243
- // Read HTML and look for script tags
244
- const html = fs.readFileSync(htmlPath, "utf-8");
245
- const scriptMatch = html.match(/<script[^>]+src=["']([^"']+)["']/i);
246
-
247
- if (scriptMatch && scriptMatch[1]) {
248
- const scriptPath = scriptMatch[1];
249
- const fullPath = path.resolve(path.dirname(htmlPath), scriptPath);
250
-
251
- // Try with and without .js/.ts extension
252
- if (fs.existsSync(fullPath)) return fullPath;
253
- if (fs.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
254
- return fullPath.replace(/\.js$/, ".ts");
255
- }
256
- if (fs.existsSync(fullPath.replace(/\.js$/, ".tsx"))) {
257
- return fullPath.replace(/\.js$/, ".tsx");
258
- }
259
- }
260
-
261
- // Fallback: look for convention-based files
262
- const baseName = path.basename(htmlPath, ".html");
263
- const dir = path.dirname(htmlPath);
264
-
265
- const candidates = [
266
- path.join(dir, `${baseName}.ts`),
267
- path.join(dir, `${baseName}.tsx`),
268
- path.join(dir, `${baseName}.js`),
269
- path.join(dir, "index.ts"),
270
- path.join(dir, "index.tsx"),
271
- path.join(dir, "index.js"),
272
- ];
273
-
274
- for (const candidate of candidates) {
275
- if (fs.existsSync(candidate)) {
276
- return candidate;
277
- }
278
- }
279
-
280
- return null;
281
- }
282
- }
283
-
284
- /**
285
- * Parse manifest.json and extract information
286
- */
287
- export function parseManifest(projectRoot: string): ManifestInfo {
288
- const parser = new ManifestParser(projectRoot);
289
- return parser.parse();
290
- }
291
-
292
- /**
293
- * Get context entry points from manifest.json
294
- */
295
- export function getContextEntryPoints(projectRoot: string): Record<string, string> {
296
- const parser = new ManifestParser(projectRoot);
297
- return parser.getContextEntryPoints();
298
- }