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