@girardmedia/bootspring 3.3.2 → 3.4.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.
Files changed (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: background-jobs
3
+ description: Background job patterns with BullMQ, Celery, Sidekiq, retry strategies, priority queues, and rate limiting.
4
+ ---
5
+
6
+ # Background Job Patterns
7
+
8
+ ## When to Use
9
+ Move work to background jobs when it is slow (API calls, email, PDF generation), not needed for the user's immediate response, or must be retried on failure. This skill covers job queue design across BullMQ (Node.js), Celery (Python), and Sidekiq (Ruby), plus universal patterns for retries, priorities, rate limiting, and monitoring.
10
+
11
+ ## How It Works
12
+
13
+ ### BullMQ (Node.js) -- Queue + Worker
14
+
15
+ ```typescript
16
+ import { Queue, Worker, Job } from "bullmq";
17
+ import IORedis from "ioredis";
18
+
19
+ const connection = new IORedis({ host: "localhost", port: 6379, maxRetriesPerRequest: null });
20
+ const emailQueue = new Queue("emails", { connection });
21
+
22
+ // Enqueue a job
23
+ await emailQueue.add("welcome", {
24
+ to: "alice@example.com",
25
+ template: "welcome",
26
+ variables: { name: "Alice" },
27
+ }, {
28
+ priority: 1,
29
+ attempts: 5,
30
+ backoff: { type: "exponential", delay: 5000 },
31
+ removeOnComplete: { count: 1000 },
32
+ removeOnFail: { age: 7 * 86400 },
33
+ });
34
+
35
+ // Worker -- process jobs
36
+ const worker = new Worker("emails", async (job: Job) => {
37
+ switch (job.name) {
38
+ case "welcome":
39
+ await sendEmail(job.data.to, job.data.template, job.data.variables);
40
+ break;
41
+ default:
42
+ throw new Error(`Unknown job: ${job.name}`);
43
+ }
44
+ }, {
45
+ connection,
46
+ concurrency: 5,
47
+ limiter: { max: 50, duration: 60_000 },
48
+ });
49
+
50
+ worker.on("failed", (job, err) => {
51
+ console.error(`Job ${job?.id} failed: ${err.message}`);
52
+ });
53
+ ```
54
+
55
+ ### Celery (Python) -- Distributed Tasks
56
+
57
+ ```python
58
+ from celery import Celery
59
+
60
+ app = Celery("tasks", broker="redis://localhost:6379/0")
61
+
62
+ app.conf.update(
63
+ task_serializer="json",
64
+ task_acks_late=True,
65
+ worker_prefetch_multiplier=1,
66
+ task_reject_on_worker_lost=True,
67
+ )
68
+
69
+ @app.task(bind=True, max_retries=5, autoretry_for=(ConnectionError, TimeoutError),
70
+ retry_backoff=True, retry_backoff_max=3600)
71
+ def process_payment(self, order_id: str):
72
+ order = Order.objects.get(id=order_id)
73
+ try:
74
+ charge = gateway.charge(order.total, order.payment_method)
75
+ order.mark_paid(charge.id)
76
+ except PaymentDeclined:
77
+ order.mark_declined()
78
+ raise # don't retry business failures
79
+
80
+ process_payment.apply_async(args=["order-123"], queue="payments", priority=0)
81
+ ```
82
+
83
+ ### Sidekiq (Ruby) -- Threaded Workers
84
+
85
+ ```ruby
86
+ class WelcomeEmailJob
87
+ include Sidekiq::Job
88
+
89
+ sidekiq_options queue: :mailers, retry: 5, backtrace: true
90
+
91
+ sidekiq_retry_in do |count, _exception|
92
+ (count ** 4) + 15 + (rand(10) * (count + 1))
93
+ end
94
+
95
+ def perform(user_id)
96
+ user = User.find(user_id)
97
+ UserMailer.welcome(user).deliver_now
98
+ end
99
+ end
100
+
101
+ WelcomeEmailJob.perform_async(user.id)
102
+ WelcomeEmailJob.perform_in(5.minutes, user.id)
103
+ ```
104
+
105
+ ### Retry Strategies
106
+
107
+ | Strategy | Formula | Use Case |
108
+ |----------|---------|----------|
109
+ | Fixed delay | 30s every time | Simple rate-limited APIs |
110
+ | Exponential backoff | `delay * 2^attempt` | Transient failures |
111
+ | Exponential + jitter | `delay * 2^attempt + random(0, delay)` | Prevent thundering herd |
112
+ | Custom schedule | `[10s, 30s, 2m, 10m, 1h]` | Known recovery patterns |
113
+
114
+ Do not retry: 400 Bad Request, 401/403, 404, or business logic failures (declined payment, validation error).
115
+
116
+ ### Job Design Rules
117
+
118
+ ```typescript
119
+ // GOOD: Pass IDs, not objects -- jobs are serialized to JSON
120
+ await queue.add("process-order", { orderId: "abc-123" });
121
+
122
+ // BAD: Passing the full object -- stale data, large payloads
123
+ await queue.add("process-order", { order: fullOrderObject });
124
+
125
+ // GOOD: Idempotent -- safe to run twice
126
+ async function processOrder(job: Job) {
127
+ const order = await db.orders.findUnique({ where: { id: job.data.orderId } });
128
+ if (order.status === "processed") return;
129
+ await chargeAndFulfill(order);
130
+ }
131
+ ```
132
+
133
+ ### Rate Limiting
134
+
135
+ ```typescript
136
+ // BullMQ: 100 API calls per minute per tenant
137
+ const apiQueue = new Queue("external-api", { connection });
138
+
139
+ await apiQueue.add("sync-crm", { tenantId, data }, {
140
+ group: { id: tenantId },
141
+ limiter: { max: 100, duration: 60_000 },
142
+ });
143
+ ```
144
+
145
+ ## Examples
146
+
147
+ | Framework | Language | Backend | Unique Feature |
148
+ |-----------|----------|---------|----------------|
149
+ | BullMQ | TypeScript | Redis | Flow (parent-child jobs) |
150
+ | Celery | Python | Redis/RabbitMQ | Canvas (chord, chain, group) |
151
+ | Sidekiq | Ruby | Redis | Batches (pro), threading |
152
+ | Temporal | Any (via SDK) | Temporal Server | Durable execution, sagas |
153
+
154
+ ## Checklist
155
+ - [ ] Jobs are idempotent -- safe to process the same job twice
156
+ - [ ] Jobs pass IDs, not serialized objects (re-fetch fresh data)
157
+ - [ ] Retry strategy uses exponential backoff with jitter
158
+ - [ ] Non-retryable errors (4xx, business failures) are not retried
159
+ - [ ] Dead letter queue captures permanently failed jobs
160
+ - [ ] Rate limiting configured per external API or tenant
161
+ - [ ] Priority levels defined and documented
162
+ - [ ] Job queue dashboard deployed (Bull Board, Flower, Sidekiq Web)
@@ -0,0 +1,309 @@
1
+ ---
2
+ name: browser-extensions
3
+ description: Browser extension patterns for Manifest V3, content scripts, background service workers, cross-script messaging, and storage.
4
+ ---
5
+
6
+ # Browser Extension Patterns
7
+
8
+ ## When to Use
9
+ Build a browser extension when you need to modify web page behavior, add UI overlays, intercept network requests, or provide cross-site functionality. These patterns cover Manifest V3 (required for Chrome), content scripts that run in page context, background service workers for event handling, popup and side panel UIs, secure messaging between contexts, and persistent storage.
10
+
11
+ ## How It Works
12
+
13
+ ### Manifest V3 Configuration
14
+
15
+ ```json
16
+ {
17
+ "manifest_version": 3,
18
+ "name": "My Extension",
19
+ "version": "1.0.0",
20
+ "description": "A productivity extension",
21
+ "permissions": ["storage", "activeTab", "contextMenus", "alarms"],
22
+ "host_permissions": ["https://*.example.com/*"],
23
+ "background": {
24
+ "service_worker": "background.js",
25
+ "type": "module"
26
+ },
27
+ "content_scripts": [
28
+ {
29
+ "matches": ["https://*.example.com/*"],
30
+ "js": ["content.js"],
31
+ "css": ["content.css"],
32
+ "run_at": "document_idle"
33
+ }
34
+ ],
35
+ "action": {
36
+ "default_popup": "popup.html",
37
+ "default_icon": {
38
+ "16": "icons/16.png",
39
+ "48": "icons/48.png",
40
+ "128": "icons/128.png"
41
+ }
42
+ },
43
+ "side_panel": {
44
+ "default_path": "sidepanel.html"
45
+ },
46
+ "options_page": "options.html",
47
+ "icons": {
48
+ "16": "icons/16.png",
49
+ "48": "icons/48.png",
50
+ "128": "icons/128.png"
51
+ }
52
+ }
53
+ ```
54
+
55
+ ### Background Service Worker
56
+
57
+ ```typescript
58
+ // background.ts
59
+ chrome.runtime.onInstalled.addListener((details) => {
60
+ if (details.reason === 'install') {
61
+ chrome.storage.local.set({ settings: { enabled: true, theme: 'auto' } });
62
+
63
+ // Create context menu
64
+ chrome.contextMenus.create({
65
+ id: 'analyze-selection',
66
+ title: 'Analyze "%s"',
67
+ contexts: ['selection'],
68
+ });
69
+ }
70
+ });
71
+
72
+ // Context menu click handler
73
+ chrome.contextMenus.onClicked.addListener(async (info, tab) => {
74
+ if (info.menuItemId === 'analyze-selection' && tab?.id) {
75
+ const result = await analyzeText(info.selectionText ?? '');
76
+ chrome.tabs.sendMessage(tab.id, { type: 'ANALYSIS_RESULT', data: result });
77
+ }
78
+ });
79
+
80
+ // Message handler for content script / popup communication
81
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
82
+ if (message.type === 'GET_DATA') {
83
+ fetchData(message.url)
84
+ .then((data) => sendResponse({ success: true, data }))
85
+ .catch((err) => sendResponse({ success: false, error: err.message }));
86
+ return true; // keep message channel open for async response
87
+ }
88
+
89
+ if (message.type === 'SAVE_SETTING') {
90
+ chrome.storage.sync.set({ [message.key]: message.value });
91
+ sendResponse({ success: true });
92
+ }
93
+ });
94
+
95
+ // Alarm for periodic tasks
96
+ chrome.alarms.create('sync-data', { periodInMinutes: 30 });
97
+ chrome.alarms.onAlarm.addListener(async (alarm) => {
98
+ if (alarm.name === 'sync-data') {
99
+ await syncDataToServer();
100
+ }
101
+ });
102
+ ```
103
+
104
+ ### Content Script
105
+
106
+ ```typescript
107
+ // content.ts
108
+ class PageEnhancer {
109
+ private overlay: HTMLDivElement | null = null;
110
+
111
+ constructor() {
112
+ this.init();
113
+ }
114
+
115
+ private async init() {
116
+ const { settings } = await chrome.storage.local.get('settings');
117
+ if (!settings?.enabled) return;
118
+
119
+ this.injectUI();
120
+ this.observeDOM();
121
+ }
122
+
123
+ private injectUI() {
124
+ // Use Shadow DOM to isolate styles
125
+ const host = document.createElement('div');
126
+ host.id = 'my-extension-root';
127
+ const shadow = host.attachShadow({ mode: 'closed' });
128
+
129
+ const style = document.createElement('style');
130
+ style.textContent = `
131
+ .overlay { position: fixed; bottom: 16px; right: 16px; z-index: 999999;
132
+ background: white; border-radius: 8px; padding: 16px;
133
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15); font-family: system-ui; }
134
+ .overlay button { background: #2563eb; color: white; border: none;
135
+ padding: 8px 16px; border-radius: 4px; cursor: pointer; }
136
+ `;
137
+
138
+ this.overlay = document.createElement('div');
139
+ this.overlay.className = 'overlay';
140
+ this.overlay.innerHTML = `<p>Extension Active</p><button id="action-btn">Analyze</button>`;
141
+
142
+ shadow.appendChild(style);
143
+ shadow.appendChild(this.overlay);
144
+ document.body.appendChild(host);
145
+
146
+ shadow.getElementById('action-btn')?.addEventListener('click', () => this.analyze());
147
+ }
148
+
149
+ private observeDOM() {
150
+ const observer = new MutationObserver((mutations) => {
151
+ for (const mutation of mutations) {
152
+ for (const node of mutation.addedNodes) {
153
+ if (node instanceof HTMLElement && node.matches('.target-element')) {
154
+ this.processElement(node);
155
+ }
156
+ }
157
+ }
158
+ });
159
+ observer.observe(document.body, { childList: true, subtree: true });
160
+ }
161
+
162
+ private async analyze() {
163
+ const response = await chrome.runtime.sendMessage({
164
+ type: 'GET_DATA',
165
+ url: window.location.href,
166
+ });
167
+ if (response.success) {
168
+ this.showResults(response.data);
169
+ }
170
+ }
171
+
172
+ private processElement(el: HTMLElement) { /* enhance element */ }
173
+ private showResults(data: unknown) { /* display in overlay */ }
174
+ }
175
+
176
+ // Listen for messages from background
177
+ chrome.runtime.onMessage.addListener((message) => {
178
+ if (message.type === 'ANALYSIS_RESULT') {
179
+ new PageEnhancer().showResults(message.data);
180
+ }
181
+ });
182
+
183
+ new PageEnhancer();
184
+ ```
185
+
186
+ ### Popup UI
187
+
188
+ ```html
189
+ <!-- popup.html -->
190
+ <!DOCTYPE html>
191
+ <html>
192
+ <head><link rel="stylesheet" href="popup.css" /></head>
193
+ <body>
194
+ <div id="app">
195
+ <h2>My Extension</h2>
196
+ <label><input type="checkbox" id="enabled" /> Enabled</label>
197
+ <div id="stats"></div>
198
+ <button id="analyze">Analyze Current Page</button>
199
+ </div>
200
+ <script src="popup.js" type="module"></script>
201
+ </body>
202
+ </html>
203
+ ```
204
+
205
+ ```typescript
206
+ // popup.ts
207
+ document.addEventListener('DOMContentLoaded', async () => {
208
+ const { settings } = await chrome.storage.local.get('settings');
209
+ const enabledCheckbox = document.getElementById('enabled') as HTMLInputElement;
210
+ enabledCheckbox.checked = settings?.enabled ?? true;
211
+
212
+ enabledCheckbox.addEventListener('change', () => {
213
+ chrome.storage.local.set({ settings: { ...settings, enabled: enabledCheckbox.checked } });
214
+ });
215
+
216
+ document.getElementById('analyze')?.addEventListener('click', async () => {
217
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
218
+ if (tab?.id) {
219
+ chrome.tabs.sendMessage(tab.id, { type: 'TRIGGER_ANALYSIS' });
220
+ window.close();
221
+ }
222
+ });
223
+ });
224
+ ```
225
+
226
+ ### Storage Patterns
227
+
228
+ ```typescript
229
+ // storage.ts — typed wrapper around chrome.storage
230
+ interface ExtensionStorage {
231
+ settings: { enabled: boolean; theme: 'light' | 'dark' | 'auto' };
232
+ cache: Record<string, { data: unknown; expires: number }>;
233
+ stats: { pagesAnalyzed: number; lastActive: string };
234
+ }
235
+
236
+ async function getStorage<K extends keyof ExtensionStorage>(
237
+ key: K
238
+ ): Promise<ExtensionStorage[K] | undefined> {
239
+ const result = await chrome.storage.local.get(key);
240
+ return result[key];
241
+ }
242
+
243
+ async function setStorage<K extends keyof ExtensionStorage>(
244
+ key: K,
245
+ value: ExtensionStorage[K]
246
+ ): Promise<void> {
247
+ await chrome.storage.local.set({ [key]: value });
248
+ }
249
+
250
+ // Listen for storage changes
251
+ chrome.storage.onChanged.addListener((changes, area) => {
252
+ if (area === 'local' && changes.settings) {
253
+ const newSettings = changes.settings.newValue;
254
+ applySettings(newSettings);
255
+ }
256
+ });
257
+
258
+ // Cached data with expiry
259
+ async function getCachedData(key: string): Promise<unknown | null> {
260
+ const cache = await getStorage('cache') ?? {};
261
+ const entry = cache[key];
262
+ if (!entry || entry.expires < Date.now()) return null;
263
+ return entry.data;
264
+ }
265
+
266
+ async function setCachedData(key: string, data: unknown, ttlMs: number = 3600000) {
267
+ const cache = await getStorage('cache') ?? {};
268
+ cache[key] = { data, expires: Date.now() + ttlMs };
269
+ await setStorage('cache', cache);
270
+ }
271
+ ```
272
+
273
+ ### Build Setup (Vite + CRXJS)
274
+
275
+ ```typescript
276
+ // vite.config.ts
277
+ import { defineConfig } from 'vite';
278
+ import react from '@vitejs/plugin-react';
279
+ import crx from '@crxjs/vite-plugin';
280
+ import manifest from './manifest.json';
281
+
282
+ export default defineConfig({
283
+ plugins: [react(), crx({ manifest })],
284
+ build: {
285
+ rollupOptions: {
286
+ input: { popup: 'popup.html', sidepanel: 'sidepanel.html', options: 'options.html' },
287
+ },
288
+ },
289
+ });
290
+ ```
291
+
292
+ ## Examples
293
+
294
+ | Context | Access | Communication |
295
+ |---------|--------|---------------|
296
+ | Background (service worker) | Full Chrome APIs, no DOM | `chrome.runtime.onMessage` |
297
+ | Content script | Page DOM, limited Chrome APIs | `chrome.runtime.sendMessage` |
298
+ | Popup / Side panel | Full Chrome APIs, own DOM | `chrome.tabs.sendMessage` |
299
+ | Injected script | Page JS context | `window.postMessage` |
300
+
301
+ ## Checklist
302
+ - [ ] Manifest V3 used (V2 deprecated in Chrome)
303
+ - [ ] Permissions are minimal — only request what is needed
304
+ - [ ] Content script styles isolated with Shadow DOM
305
+ - [ ] Background script handles async messages with `return true`
306
+ - [ ] Storage uses typed wrapper with `chrome.storage.local` or `sync`
307
+ - [ ] Context menus created in `onInstalled` handler, not on every load
308
+ - [ ] Popup and options pages work without content script injection
309
+ - [ ] Extension tested in Chrome, Firefox (WebExtensions API), and Edge