@fairfox/polly 0.1.0
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/LICENSE +21 -0
- package/README.md +322 -0
- package/cli/polly.ts +564 -0
- package/dist/background/api-client.d.ts +7 -0
- package/dist/background/context-menu.d.ts +7 -0
- package/dist/background/index.d.ts +31 -0
- package/dist/background/index.js +1309 -0
- package/dist/background/index.js.map +25 -0
- package/dist/background/log-store.d.ts +22 -0
- package/dist/background/message-router.d.ts +30 -0
- package/dist/background/message-router.js +1300 -0
- package/dist/background/message-router.js.map +24 -0
- package/dist/background/offscreen-manager.d.ts +10 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1471 -0
- package/dist/index.js.map +27 -0
- package/dist/shared/adapters/chrome/context-menus.chrome.d.ts +8 -0
- package/dist/shared/adapters/chrome/offscreen.chrome.d.ts +6 -0
- package/dist/shared/adapters/chrome/runtime.chrome.d.ts +13 -0
- package/dist/shared/adapters/chrome/storage.chrome.d.ts +8 -0
- package/dist/shared/adapters/chrome/tabs.chrome.d.ts +16 -0
- package/dist/shared/adapters/chrome/window.chrome.d.ts +6 -0
- package/dist/shared/adapters/context-menus.adapter.d.ts +22 -0
- package/dist/shared/adapters/fetch.adapter.d.ts +6 -0
- package/dist/shared/adapters/index.d.ts +34 -0
- package/dist/shared/adapters/index.js +298 -0
- package/dist/shared/adapters/index.js.map +18 -0
- package/dist/shared/adapters/logger.adapter.d.ts +44 -0
- package/dist/shared/adapters/offscreen.adapter.d.ts +20 -0
- package/dist/shared/adapters/runtime.adapter.d.ts +66 -0
- package/dist/shared/adapters/storage.adapter.d.ts +29 -0
- package/dist/shared/adapters/tabs.adapter.d.ts +39 -0
- package/dist/shared/adapters/window.adapter.d.ts +14 -0
- package/dist/shared/lib/context-helpers.d.ts +64 -0
- package/dist/shared/lib/context-helpers.js +1086 -0
- package/dist/shared/lib/context-helpers.js.map +24 -0
- package/dist/shared/lib/context-specific-helpers.d.ts +160 -0
- package/dist/shared/lib/errors.d.ts +67 -0
- package/dist/shared/lib/errors.js +94 -0
- package/dist/shared/lib/errors.js.map +10 -0
- package/dist/shared/lib/handler-execution-tracker.d.ts +24 -0
- package/dist/shared/lib/message-bus.d.ts +233 -0
- package/dist/shared/lib/message-bus.js +1033 -0
- package/dist/shared/lib/message-bus.js.map +23 -0
- package/dist/shared/lib/state.d.ts +102 -0
- package/dist/shared/lib/state.js +1265 -0
- package/dist/shared/lib/state.js.map +24 -0
- package/dist/shared/lib/test-helpers.d.ts +133 -0
- package/dist/shared/lib/test-helpers.js +136 -0
- package/dist/shared/lib/test-helpers.js.map +10 -0
- package/dist/shared/state/app-state.d.ts +8 -0
- package/dist/shared/state/app-state.js +1272 -0
- package/dist/shared/state/app-state.js.map +25 -0
- package/dist/shared/types/messages.d.ts +341 -0
- package/dist/shared/types/messages.js +25 -0
- package/dist/shared/types/messages.js.map +10 -0
- package/package.json +110 -0
package/cli/polly.ts
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Polly CLI
|
|
4
|
+
*
|
|
5
|
+
* Command-line tool for building multi-execution-context applications
|
|
6
|
+
* with reactive state and cross-context messaging.
|
|
7
|
+
*
|
|
8
|
+
* Supports: Chrome extensions, PWAs, Node/Bun/Deno apps with workers
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* polly init [name] Create a new project
|
|
12
|
+
* polly check Run all checks (typecheck, lint, test, build)
|
|
13
|
+
* polly build [options] Build the project
|
|
14
|
+
* polly dev Build with watch mode
|
|
15
|
+
* polly typecheck Type check your code
|
|
16
|
+
* polly lint [--fix] Lint your code
|
|
17
|
+
* polly format Format your code
|
|
18
|
+
* polly test [args] Run tests (requires bun test)
|
|
19
|
+
* polly verify [args] Run formal verification
|
|
20
|
+
* polly visualize [args] Generate architecture diagrams
|
|
21
|
+
* polly help Show help
|
|
22
|
+
*
|
|
23
|
+
* Options:
|
|
24
|
+
* --prod Build for production (minified)
|
|
25
|
+
* --config <path> Path to config file (default: polly.config.ts)
|
|
26
|
+
* --fix Auto-fix lint/format issues
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// Use Bun built-ins instead of Node.js APIs
|
|
30
|
+
const __dirname = import.meta.dir;
|
|
31
|
+
|
|
32
|
+
const command = process.argv[2];
|
|
33
|
+
const commandArgs = process.argv.slice(3);
|
|
34
|
+
const cwd = process.cwd();
|
|
35
|
+
|
|
36
|
+
// Parse arguments
|
|
37
|
+
const args = {
|
|
38
|
+
prod: process.argv.includes("--prod"),
|
|
39
|
+
config: process.argv.includes("--config")
|
|
40
|
+
? process.argv[process.argv.indexOf("--config") + 1]
|
|
41
|
+
: undefined,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load user's configuration
|
|
46
|
+
*/
|
|
47
|
+
async function loadConfig() {
|
|
48
|
+
const configPaths = [
|
|
49
|
+
args.config,
|
|
50
|
+
`${cwd}/polly.config.ts`,
|
|
51
|
+
`${cwd}/polly.config.js`,
|
|
52
|
+
`${cwd}/polly.config.mjs`,
|
|
53
|
+
].filter(Boolean) as string[];
|
|
54
|
+
|
|
55
|
+
for (const configPath of configPaths) {
|
|
56
|
+
// Use Bun.file().exists() instead of existsSync
|
|
57
|
+
if (await Bun.file(configPath).exists()) {
|
|
58
|
+
try {
|
|
59
|
+
const config = await import(configPath);
|
|
60
|
+
return config.default || config;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(`❌ Failed to load config: ${configPath}`);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
srcDir: "src",
|
|
69
|
+
distDir: "dist",
|
|
70
|
+
manifest: "manifest.json",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build command - build the extension
|
|
76
|
+
*/
|
|
77
|
+
async function build() {
|
|
78
|
+
const config = await loadConfig();
|
|
79
|
+
|
|
80
|
+
// Import the build script from framework
|
|
81
|
+
const buildScriptPath = `${__dirname}/../scripts/build-extension.ts`;
|
|
82
|
+
|
|
83
|
+
// Pass config via environment
|
|
84
|
+
process.env["WEB_EXT_SRC"] = `${cwd}/${config.srcDir || "src"}`;
|
|
85
|
+
process.env["WEB_EXT_DIST"] = `${cwd}/${config.distDir || "dist"}`;
|
|
86
|
+
process.env["WEB_EXT_MANIFEST"] = `${cwd}/${config.manifest || "manifest.json"}`;
|
|
87
|
+
process.env["WEB_EXT_CWD"] = cwd;
|
|
88
|
+
process.env["WEB_EXT_PROD"] = args.prod ? "true" : "false";
|
|
89
|
+
|
|
90
|
+
// Run build
|
|
91
|
+
const proc = Bun.spawn(["bun", buildScriptPath], {
|
|
92
|
+
cwd,
|
|
93
|
+
stdout: "inherit",
|
|
94
|
+
stderr: "inherit",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const exitCode = await proc.exited;
|
|
98
|
+
if (exitCode !== 0) {
|
|
99
|
+
process.exit(exitCode);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Dev command - build with watch mode
|
|
105
|
+
*/
|
|
106
|
+
async function dev() {
|
|
107
|
+
await build();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Verify command - delegate to @fairfox/web-ext-verify
|
|
112
|
+
*/
|
|
113
|
+
async function verify() {
|
|
114
|
+
const verifyCli = `${__dirname}/../../verify/src/cli.ts`;
|
|
115
|
+
|
|
116
|
+
const proc = Bun.spawn(["bun", verifyCli, ...commandArgs], {
|
|
117
|
+
cwd,
|
|
118
|
+
stdout: "inherit",
|
|
119
|
+
stderr: "inherit",
|
|
120
|
+
stdin: "inherit",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const exitCode = await proc.exited;
|
|
124
|
+
if (exitCode !== 0) {
|
|
125
|
+
throw new Error(`Verification failed with exit code ${exitCode}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Visualize command - delegate to @fairfox/web-ext-visualize
|
|
131
|
+
*/
|
|
132
|
+
async function visualize() {
|
|
133
|
+
const visualizeCli = `${__dirname}/../../visualize/src/cli.ts`;
|
|
134
|
+
|
|
135
|
+
const proc = Bun.spawn(["bun", visualizeCli, ...commandArgs], {
|
|
136
|
+
cwd,
|
|
137
|
+
stdout: "inherit",
|
|
138
|
+
stderr: "inherit",
|
|
139
|
+
stdin: "inherit",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const exitCode = await proc.exited;
|
|
143
|
+
if (exitCode !== 0) {
|
|
144
|
+
throw new Error(`Visualization failed with exit code ${exitCode}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Typecheck command - run TypeScript type checking
|
|
150
|
+
*/
|
|
151
|
+
async function typecheck() {
|
|
152
|
+
const proc = Bun.spawn(["bunx", "tsc", "--noEmit"], {
|
|
153
|
+
cwd,
|
|
154
|
+
stdout: "inherit",
|
|
155
|
+
stderr: "inherit",
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const exitCode = await proc.exited;
|
|
159
|
+
if (exitCode !== 0) {
|
|
160
|
+
throw new Error(`Type checking failed with exit code ${exitCode}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Lint command - run Biome linter
|
|
166
|
+
*/
|
|
167
|
+
async function lint() {
|
|
168
|
+
const fix = commandArgs.includes("--fix");
|
|
169
|
+
const lintArgs = fix ? ["check", "--write", "."] : ["check", "."];
|
|
170
|
+
|
|
171
|
+
const proc = Bun.spawn(["bunx", "@biomejs/biome", ...lintArgs], {
|
|
172
|
+
cwd,
|
|
173
|
+
stdout: "inherit",
|
|
174
|
+
stderr: "inherit",
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const exitCode = await proc.exited;
|
|
178
|
+
if (exitCode !== 0) {
|
|
179
|
+
throw new Error(`Linting failed with exit code ${exitCode}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Format command - run Biome formatter
|
|
185
|
+
*/
|
|
186
|
+
async function format() {
|
|
187
|
+
const proc = Bun.spawn(["bunx", "@biomejs/biome", "format", "--write", "."], {
|
|
188
|
+
cwd,
|
|
189
|
+
stdout: "inherit",
|
|
190
|
+
stderr: "inherit",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const exitCode = await proc.exited;
|
|
194
|
+
if (exitCode !== 0) {
|
|
195
|
+
throw new Error(`Formatting failed with exit code ${exitCode}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Test command - run Bun tests
|
|
201
|
+
*/
|
|
202
|
+
async function test() {
|
|
203
|
+
const proc = Bun.spawn(["bun", "test", ...commandArgs], {
|
|
204
|
+
cwd,
|
|
205
|
+
stdout: "pipe",
|
|
206
|
+
stderr: "pipe",
|
|
207
|
+
stdin: "inherit",
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const stdout = await new Response(proc.stdout).text();
|
|
211
|
+
const stderr = await new Response(proc.stderr).text();
|
|
212
|
+
|
|
213
|
+
// Output the results
|
|
214
|
+
if (stdout) process.stdout.write(stdout);
|
|
215
|
+
if (stderr) process.stderr.write(stderr);
|
|
216
|
+
|
|
217
|
+
const exitCode = await proc.exited;
|
|
218
|
+
|
|
219
|
+
// Check if no tests were found (not a failure)
|
|
220
|
+
if (stderr.includes("0 test files matching")) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (exitCode !== 0) {
|
|
225
|
+
throw new Error(`Tests failed with exit code ${exitCode}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check command - run all quality checks in sequence
|
|
231
|
+
*/
|
|
232
|
+
async function check() {
|
|
233
|
+
const checks = [
|
|
234
|
+
{ name: "Type checking", fn: typecheck },
|
|
235
|
+
{ name: "Linting", fn: lint },
|
|
236
|
+
{ name: "Testing", fn: test },
|
|
237
|
+
{ name: "Building", fn: build },
|
|
238
|
+
{ name: "Verification", fn: verify, optional: true },
|
|
239
|
+
{ name: "Visualization", fn: visualize, optional: true },
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
for (const { name, fn, optional } of checks) {
|
|
243
|
+
try {
|
|
244
|
+
await fn();
|
|
245
|
+
} catch (_error) {
|
|
246
|
+
if (optional) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
console.error(`\n\x1b[31m✗ ${name} failed\x1b[0m\n`);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Init command - scaffold a new extension
|
|
257
|
+
*/
|
|
258
|
+
async function init() {
|
|
259
|
+
const projectName = commandArgs[0] || "my-extension";
|
|
260
|
+
const projectPath = `${cwd}/${projectName}`;
|
|
261
|
+
|
|
262
|
+
// Check if directory already exists
|
|
263
|
+
const { existsSync, mkdirSync, writeFileSync } = await import("node:fs");
|
|
264
|
+
|
|
265
|
+
if (existsSync(projectPath)) {
|
|
266
|
+
console.error(`\x1b[31m✗ Directory '${projectName}' already exists\x1b[0m\n`);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Create project structure
|
|
271
|
+
const dirs = [
|
|
272
|
+
projectPath,
|
|
273
|
+
`${projectPath}/src`,
|
|
274
|
+
`${projectPath}/src/background`,
|
|
275
|
+
`${projectPath}/src/popup`,
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
for (const dir of dirs) {
|
|
279
|
+
mkdirSync(dir, { recursive: true });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Create package.json
|
|
283
|
+
const packageJson = {
|
|
284
|
+
name: projectName,
|
|
285
|
+
version: "0.1.0",
|
|
286
|
+
type: "module",
|
|
287
|
+
scripts: {
|
|
288
|
+
check: "web-ext check",
|
|
289
|
+
build: "web-ext build",
|
|
290
|
+
"build:prod": "web-ext build --prod",
|
|
291
|
+
typecheck: "web-ext typecheck",
|
|
292
|
+
lint: "web-ext lint",
|
|
293
|
+
"lint:fix": "web-ext lint --fix",
|
|
294
|
+
format: "web-ext format",
|
|
295
|
+
test: "web-ext test",
|
|
296
|
+
verify: "web-ext verify",
|
|
297
|
+
"verify:setup": "web-ext verify --setup",
|
|
298
|
+
visualize: "web-ext visualize",
|
|
299
|
+
"visualize:export": "web-ext visualize --export",
|
|
300
|
+
"visualize:serve": "web-ext visualize --serve",
|
|
301
|
+
},
|
|
302
|
+
dependencies: {
|
|
303
|
+
"@fairfox/web-ext": "*",
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
writeFileSync(`${projectPath}/package.json`, JSON.stringify(packageJson, null, 2));
|
|
308
|
+
|
|
309
|
+
// Create manifest.json at root
|
|
310
|
+
const manifest = {
|
|
311
|
+
manifest_version: 3,
|
|
312
|
+
name: projectName,
|
|
313
|
+
version: "0.1.0",
|
|
314
|
+
description: "A Chrome extension built with @fairfox/web-ext",
|
|
315
|
+
background: {
|
|
316
|
+
service_worker: "background/index.js",
|
|
317
|
+
type: "module",
|
|
318
|
+
},
|
|
319
|
+
action: {
|
|
320
|
+
default_popup: "popup/popup.html",
|
|
321
|
+
},
|
|
322
|
+
permissions: ["storage"],
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
writeFileSync(`${projectPath}/manifest.json`, JSON.stringify(manifest, null, 2));
|
|
326
|
+
|
|
327
|
+
// Create background script
|
|
328
|
+
const backgroundScript = `/**
|
|
329
|
+
* Background Service Worker
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
import { getMessageBus } from "@fairfox/web-ext/message-bus";
|
|
333
|
+
import { MessageRouter } from "@fairfox/web-ext/message-router";
|
|
334
|
+
|
|
335
|
+
const bus = getMessageBus("background");
|
|
336
|
+
new MessageRouter(bus);
|
|
337
|
+
|
|
338
|
+
// Add your message handlers here
|
|
339
|
+
bus.on("PING", async () => {
|
|
340
|
+
return { success: true, message: "pong" };
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
console.log("Background service worker initialized");
|
|
344
|
+
`;
|
|
345
|
+
|
|
346
|
+
writeFileSync(`${projectPath}/src/background/index.ts`, backgroundScript);
|
|
347
|
+
|
|
348
|
+
// Create popup HTML in src/popup
|
|
349
|
+
const popupHtml = `<!DOCTYPE html>
|
|
350
|
+
<html>
|
|
351
|
+
<head>
|
|
352
|
+
<meta charset="utf-8" />
|
|
353
|
+
<title>${projectName}</title>
|
|
354
|
+
</head>
|
|
355
|
+
<body>
|
|
356
|
+
<div id="root"></div>
|
|
357
|
+
<script type="module" src="./index.js"></script>
|
|
358
|
+
</body>
|
|
359
|
+
</html>
|
|
360
|
+
`;
|
|
361
|
+
|
|
362
|
+
writeFileSync(`${projectPath}/src/popup/popup.html`, popupHtml);
|
|
363
|
+
|
|
364
|
+
// Create popup script
|
|
365
|
+
const popupScript = `/**
|
|
366
|
+
* Popup UI
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
import { getMessageBus } from "@fairfox/web-ext/message-bus";
|
|
370
|
+
|
|
371
|
+
const bus = getMessageBus("popup");
|
|
372
|
+
|
|
373
|
+
// Simple example without UI framework
|
|
374
|
+
const root = document.getElementById("root");
|
|
375
|
+
|
|
376
|
+
if (root) {
|
|
377
|
+
root.innerHTML = \`
|
|
378
|
+
<div style="padding: 16px; min-width: 200px;">
|
|
379
|
+
<h1 style="margin: 0 0 8px 0; font-size: 18px;">${projectName}</h1>
|
|
380
|
+
<button id="ping-btn" style="padding: 8px 16px;">Ping Background</button>
|
|
381
|
+
<p id="response" style="margin-top: 8px; font-size: 14px;"></p>
|
|
382
|
+
</div>
|
|
383
|
+
\`;
|
|
384
|
+
|
|
385
|
+
const btn = document.getElementById("ping-btn");
|
|
386
|
+
const response = document.getElementById("response");
|
|
387
|
+
|
|
388
|
+
btn?.addEventListener("click", async () => {
|
|
389
|
+
const result = await bus.send({ type: "PING" });
|
|
390
|
+
if (response) {
|
|
391
|
+
response.textContent = JSON.stringify(result);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
`;
|
|
396
|
+
|
|
397
|
+
writeFileSync(`${projectPath}/src/popup/index.ts`, popupScript);
|
|
398
|
+
|
|
399
|
+
// Create tsconfig.json
|
|
400
|
+
const tsconfig = {
|
|
401
|
+
compilerOptions: {
|
|
402
|
+
target: "ES2022",
|
|
403
|
+
module: "ESNext",
|
|
404
|
+
lib: ["ES2022", "DOM"],
|
|
405
|
+
moduleResolution: "bundler",
|
|
406
|
+
strict: true,
|
|
407
|
+
esModuleInterop: true,
|
|
408
|
+
skipLibCheck: true,
|
|
409
|
+
forceConsistentCasingInFileNames: true,
|
|
410
|
+
resolveJsonModule: true,
|
|
411
|
+
allowSyntheticDefaultImports: true,
|
|
412
|
+
jsx: "react-jsx",
|
|
413
|
+
jsxImportSource: "preact",
|
|
414
|
+
},
|
|
415
|
+
include: ["src/**/*"],
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
writeFileSync(`${projectPath}/tsconfig.json`, JSON.stringify(tsconfig, null, 2));
|
|
419
|
+
|
|
420
|
+
// Create biome.json
|
|
421
|
+
const biomeConfig = {
|
|
422
|
+
files: {
|
|
423
|
+
includes: ["src/**/*.ts", "src/**/*.tsx"],
|
|
424
|
+
ignoreUnknown: true,
|
|
425
|
+
},
|
|
426
|
+
linter: {
|
|
427
|
+
enabled: true,
|
|
428
|
+
rules: {
|
|
429
|
+
recommended: true,
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
formatter: {
|
|
433
|
+
enabled: true,
|
|
434
|
+
indentStyle: "space",
|
|
435
|
+
indentWidth: 2,
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
writeFileSync(`${projectPath}/biome.json`, JSON.stringify(biomeConfig, null, 2));
|
|
440
|
+
|
|
441
|
+
// Create README
|
|
442
|
+
const readme = `# ${projectName}
|
|
443
|
+
|
|
444
|
+
A Chrome extension built with [@fairfox/web-ext](https://github.com/fairfox/web-ext).
|
|
445
|
+
|
|
446
|
+
## Getting Started
|
|
447
|
+
|
|
448
|
+
1. Install dependencies:
|
|
449
|
+
\`\`\`bash
|
|
450
|
+
bun install
|
|
451
|
+
\`\`\`
|
|
452
|
+
|
|
453
|
+
2. Build the extension:
|
|
454
|
+
\`\`\`bash
|
|
455
|
+
bun run build
|
|
456
|
+
\`\`\`
|
|
457
|
+
|
|
458
|
+
3. Load the extension in Chrome:
|
|
459
|
+
- Open \`chrome://extensions\`
|
|
460
|
+
- Enable "Developer mode"
|
|
461
|
+
- Click "Load unpacked"
|
|
462
|
+
- Select the \`dist/\` folder
|
|
463
|
+
|
|
464
|
+
## Development
|
|
465
|
+
|
|
466
|
+
- \`bun run build\` - Build the extension
|
|
467
|
+
- \`bun run check\` - Run all checks (typecheck, lint, test, build)
|
|
468
|
+
- \`bun run typecheck\` - Type check your code
|
|
469
|
+
- \`bun run lint\` - Lint your code
|
|
470
|
+
- \`bun run format\` - Format your code
|
|
471
|
+
- \`bun run verify\` - Run formal verification
|
|
472
|
+
- \`bun run visualize\` - Generate architecture diagrams
|
|
473
|
+
|
|
474
|
+
## Project Structure
|
|
475
|
+
|
|
476
|
+
\`\`\`
|
|
477
|
+
${projectName}/
|
|
478
|
+
├── src/
|
|
479
|
+
│ ├── background/
|
|
480
|
+
│ │ └── index.ts # Background service worker
|
|
481
|
+
│ └── popup/
|
|
482
|
+
│ ├── popup.html # Popup HTML
|
|
483
|
+
│ └── index.ts # Popup script
|
|
484
|
+
├── manifest.json # Extension manifest
|
|
485
|
+
├── dist/ # Build output (load this in Chrome)
|
|
486
|
+
├── package.json
|
|
487
|
+
├── tsconfig.json
|
|
488
|
+
└── biome.json
|
|
489
|
+
\`\`\`
|
|
490
|
+
`;
|
|
491
|
+
|
|
492
|
+
writeFileSync(`${projectPath}/README.md`, readme);
|
|
493
|
+
|
|
494
|
+
// Create .gitignore
|
|
495
|
+
const gitignore = `node_modules
|
|
496
|
+
dist
|
|
497
|
+
docs
|
|
498
|
+
.DS_Store
|
|
499
|
+
`;
|
|
500
|
+
|
|
501
|
+
writeFileSync(`${projectPath}/.gitignore`, gitignore);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Help command
|
|
506
|
+
*/
|
|
507
|
+
function help() {
|
|
508
|
+
// Help is shown automatically via commander
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Main entry point
|
|
513
|
+
*/
|
|
514
|
+
async function main() {
|
|
515
|
+
try {
|
|
516
|
+
switch (command) {
|
|
517
|
+
case "init":
|
|
518
|
+
await init();
|
|
519
|
+
break;
|
|
520
|
+
case "check":
|
|
521
|
+
await check();
|
|
522
|
+
break;
|
|
523
|
+
case "build":
|
|
524
|
+
await build();
|
|
525
|
+
break;
|
|
526
|
+
case "dev":
|
|
527
|
+
await dev();
|
|
528
|
+
break;
|
|
529
|
+
case "typecheck":
|
|
530
|
+
await typecheck();
|
|
531
|
+
break;
|
|
532
|
+
case "lint":
|
|
533
|
+
await lint();
|
|
534
|
+
break;
|
|
535
|
+
case "format":
|
|
536
|
+
await format();
|
|
537
|
+
break;
|
|
538
|
+
case "test":
|
|
539
|
+
await test();
|
|
540
|
+
break;
|
|
541
|
+
case "verify":
|
|
542
|
+
await verify();
|
|
543
|
+
break;
|
|
544
|
+
case "visualize":
|
|
545
|
+
await visualize();
|
|
546
|
+
break;
|
|
547
|
+
case "help":
|
|
548
|
+
case "--help":
|
|
549
|
+
case "-h":
|
|
550
|
+
case undefined:
|
|
551
|
+
help();
|
|
552
|
+
break;
|
|
553
|
+
default:
|
|
554
|
+
console.error(`❌ Unknown command: ${command}\n`);
|
|
555
|
+
help();
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
558
|
+
} catch (error) {
|
|
559
|
+
console.error("\n❌ Command failed:", error);
|
|
560
|
+
process.exit(1);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
main();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MessageBus } from "../shared/lib/message-bus";
|
|
2
|
+
import type { BaseMessage, ExtensionMessage } from "../shared/types/messages";
|
|
3
|
+
/**
|
|
4
|
+
* Initialize background script with message router.
|
|
5
|
+
*
|
|
6
|
+
* This is the recommended way to setup your background script.
|
|
7
|
+
* It automatically creates the message bus and router.
|
|
8
|
+
*
|
|
9
|
+
* @returns MessageBus instance for registering handlers
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // src/background/index.ts
|
|
14
|
+
* import { createBackground } from '@fairfox/web-ext/background'
|
|
15
|
+
*
|
|
16
|
+
* const bus = createBackground()
|
|
17
|
+
*
|
|
18
|
+
* bus.on('MY_MESSAGE', async (payload) => {
|
|
19
|
+
* return { success: true }
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example With custom message types
|
|
24
|
+
* ```typescript
|
|
25
|
+
* type MyMessages = { type: 'MY_MESSAGE'; data: string }
|
|
26
|
+
* const bus = createBackground<MyMessages>()
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function createBackground<TMessage extends BaseMessage = ExtensionMessage>(): MessageBus<TMessage>;
|
|
30
|
+
export { MessageRouter } from "./message-router";
|
|
31
|
+
export { getMessageBus } from "../shared/lib/message-bus";
|