@zigrivers/scaffold 3.8.0 → 3.9.1

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 (70) hide show
  1. package/README.md +73 -8
  2. package/content/knowledge/browser-extension/browser-extension-architecture.md +195 -0
  3. package/content/knowledge/browser-extension/browser-extension-content-scripts.md +264 -0
  4. package/content/knowledge/browser-extension/browser-extension-conventions.md +156 -0
  5. package/content/knowledge/browser-extension/browser-extension-cross-browser.md +229 -0
  6. package/content/knowledge/browser-extension/browser-extension-dev-environment.md +247 -0
  7. package/content/knowledge/browser-extension/browser-extension-manifest.md +220 -0
  8. package/content/knowledge/browser-extension/browser-extension-project-structure.md +183 -0
  9. package/content/knowledge/browser-extension/browser-extension-requirements.md +107 -0
  10. package/content/knowledge/browser-extension/browser-extension-security.md +202 -0
  11. package/content/knowledge/browser-extension/browser-extension-service-workers.md +265 -0
  12. package/content/knowledge/browser-extension/browser-extension-store-submission.md +155 -0
  13. package/content/knowledge/browser-extension/browser-extension-testing.md +270 -0
  14. package/content/knowledge/data-pipeline/data-pipeline-architecture.md +175 -0
  15. package/content/knowledge/data-pipeline/data-pipeline-batch-patterns.md +263 -0
  16. package/content/knowledge/data-pipeline/data-pipeline-conventions.md +176 -0
  17. package/content/knowledge/data-pipeline/data-pipeline-dev-environment.md +350 -0
  18. package/content/knowledge/data-pipeline/data-pipeline-orchestration.md +291 -0
  19. package/content/knowledge/data-pipeline/data-pipeline-project-structure.md +257 -0
  20. package/content/knowledge/data-pipeline/data-pipeline-quality.md +324 -0
  21. package/content/knowledge/data-pipeline/data-pipeline-requirements.md +145 -0
  22. package/content/knowledge/data-pipeline/data-pipeline-schema-management.md +295 -0
  23. package/content/knowledge/data-pipeline/data-pipeline-security.md +326 -0
  24. package/content/knowledge/data-pipeline/data-pipeline-streaming-patterns.md +280 -0
  25. package/content/knowledge/data-pipeline/data-pipeline-testing.md +406 -0
  26. package/content/knowledge/ml/ml-architecture.md +172 -0
  27. package/content/knowledge/ml/ml-conventions.md +209 -0
  28. package/content/knowledge/ml/ml-dev-environment.md +299 -0
  29. package/content/knowledge/ml/ml-experiment-tracking.md +285 -0
  30. package/content/knowledge/ml/ml-model-evaluation.md +256 -0
  31. package/content/knowledge/ml/ml-observability.md +253 -0
  32. package/content/knowledge/ml/ml-project-structure.md +216 -0
  33. package/content/knowledge/ml/ml-requirements.md +138 -0
  34. package/content/knowledge/ml/ml-security.md +188 -0
  35. package/content/knowledge/ml/ml-serving-patterns.md +243 -0
  36. package/content/knowledge/ml/ml-testing.md +301 -0
  37. package/content/knowledge/ml/ml-training-patterns.md +269 -0
  38. package/content/methodology/browser-extension-overlay.yml +82 -0
  39. package/content/methodology/data-pipeline-overlay.yml +70 -0
  40. package/content/methodology/ml-overlay.yml +70 -0
  41. package/dist/cli/commands/init.d.ts +13 -0
  42. package/dist/cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/commands/init.js +122 -2
  44. package/dist/cli/commands/init.js.map +1 -1
  45. package/dist/cli/commands/init.test.js +120 -0
  46. package/dist/cli/commands/init.test.js.map +1 -1
  47. package/dist/config/schema.d.ts +864 -48
  48. package/dist/config/schema.d.ts.map +1 -1
  49. package/dist/config/schema.js +53 -0
  50. package/dist/config/schema.js.map +1 -1
  51. package/dist/config/schema.test.js +166 -3
  52. package/dist/config/schema.test.js.map +1 -1
  53. package/dist/core/assembly/overlay-loader.test.js +33 -0
  54. package/dist/core/assembly/overlay-loader.test.js.map +1 -1
  55. package/dist/e2e/project-type-overlays.test.d.ts +2 -2
  56. package/dist/e2e/project-type-overlays.test.js +499 -33
  57. package/dist/e2e/project-type-overlays.test.js.map +1 -1
  58. package/dist/types/config.d.ts +10 -1
  59. package/dist/types/config.d.ts.map +1 -1
  60. package/dist/wizard/questions.d.ts +17 -1
  61. package/dist/wizard/questions.d.ts.map +1 -1
  62. package/dist/wizard/questions.js +75 -1
  63. package/dist/wizard/questions.js.map +1 -1
  64. package/dist/wizard/questions.test.js +167 -0
  65. package/dist/wizard/questions.test.js.map +1 -1
  66. package/dist/wizard/wizard.d.ts +13 -0
  67. package/dist/wizard/wizard.d.ts.map +1 -1
  68. package/dist/wizard/wizard.js +17 -1
  69. package/dist/wizard/wizard.js.map +1 -1
  70. package/package.json +1 -1
@@ -0,0 +1,202 @@
1
+ ---
2
+ name: browser-extension-security
3
+ description: Content Security Policy for extensions, prohibitions on eval and inline scripts, host permissions principle of least privilege, and XSS prevention in extension UIs
4
+ topics: [browser-extension, security, csp, xss, permissions, least-privilege, eval]
5
+ ---
6
+
7
+ Browser extensions run with elevated browser privileges compared to ordinary web pages. A compromised extension can read browsing history, intercept network requests, steal cookies, and inject content into any page it has permission to access. Security is not optional — it is a first-class requirement enforced by the browser, the store review process, and the trust of every user who installs the extension.
8
+
9
+ ## Summary
10
+
11
+ Manifest V3 enforces a strict Content Security Policy by default that prohibits `eval()`, `new Function()`, and inline scripts — comply with this unconditionally. Never request more host permissions than features require; prefer `activeTab` over broad host permissions. Extension UI pages (popup, options) are vulnerable to XSS if they render untrusted content — sanitize all dynamic HTML, prefer textContent over innerHTML, and use a trusted types policy where supported. Regularly audit permissions and remove any that are no longer needed.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Content Security Policy for Extensions
16
+
17
+ Manifest V3 enforces a strict default CSP for extension pages (popup, options, background):
18
+
19
+ ```
20
+ script-src 'self'; object-src 'self'
21
+ ```
22
+
23
+ This means:
24
+ - No inline `<script>` blocks in popup or options HTML.
25
+ - No `eval()`, `setTimeout("string")`, `new Function("string")`, or any other string-to-code evaluation.
26
+ - No scripts loaded from external URLs.
27
+ - All scripts must be bundled into extension package files referenced by `src=` attributes.
28
+
29
+ **Customizing the extension CSP** (only add what is absolutely necessary):
30
+
31
+ ```json
32
+ // manifest.json
33
+ {
34
+ "content_security_policy": {
35
+ "extension_pages": "script-src 'self'; object-src 'none'",
36
+ "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval'"
37
+ }
38
+ }
39
+ ```
40
+
41
+ `"sandbox"` is a separate policy for sandboxed extension pages. If you need `eval` for a legitimate reason (e.g., a code editor that evaluates JavaScript), use a sandboxed page — `chrome.runtime.getURL('sandbox.html')` — which is isolated from the extension's privileged context.
42
+
43
+ **Inline scripts prohibition — practical implications:**
44
+
45
+ ```html
46
+ <!-- PROHIBITED — inline script blocked by CSP -->
47
+ <button onclick="doThing()">Click me</button>
48
+ <script>window.onload = function() { init(); }</script>
49
+
50
+ <!-- CORRECT — event listeners in external JS file -->
51
+ <button id="my-btn">Click me</button>
52
+ <!-- popup.js (bundled, loaded as external file) -->
53
+ ```
54
+
55
+ ```typescript
56
+ // popup.ts — attach all event handlers here
57
+ document.getElementById('my-btn')?.addEventListener('click', doThing);
58
+ document.addEventListener('DOMContentLoaded', init);
59
+ ```
60
+
61
+ **Audit your build output:** Run a build in production mode and inspect the output. If your popup HTML contains any `<script>` tags with inline content or `onclick` attributes, your bundler is misconfigured.
62
+
63
+ ### Prohibiting eval and String-to-Code Patterns
64
+
65
+ `eval()` and related patterns are the primary mechanism for code injection attacks. Manifest V3 prohibits them in extension pages — but you must also avoid them in content scripts (where they would be prohibited by the host page's CSP in `document_start` injection, and represent a security risk in any injection mode).
66
+
67
+ **Patterns to avoid:**
68
+
69
+ ```typescript
70
+ // PROHIBITED
71
+ eval('doThing()');
72
+ setTimeout('doThing()', 1000); // String argument form only
73
+ setInterval('doThing()', 1000);
74
+ new Function('return doThing()')();
75
+ document.write('<script>doThing()</script>'); // DOM-based code injection
76
+
77
+ // Safe alternative
78
+ setTimeout(doThing, 1000); // Function reference
79
+ setInterval(doThing, 1000);
80
+ ```
81
+
82
+ **Dynamic template rendering without eval:**
83
+
84
+ If you need to render dynamic content (e.g., user-defined templates), use a CSP-compliant template engine that does not use `eval`. Mustache, Handlebars (with the `noEscape` option disabled), or a custom string-interpolation function all work without `eval`.
85
+
86
+ ### Host Permissions: Principle of Least Privilege
87
+
88
+ Every host permission you declare expands the attack surface of the extension. A compromised extension with `<all_urls>` can inject code into banking websites, steal session cookies from any domain, and exfiltrate browsing history.
89
+
90
+ **Permission escalation order (prefer the lowest that works):**
91
+
92
+ 1. **No host permission** — Works for extensions that only modify the browser UI (new tab page, toolbar button with popup) without touching page content.
93
+ 2. **`activeTab`** — Grants access to the current tab's URL and content only when the user explicitly invokes the extension. No install-time warning. No persistent access. Best for "apply this action to the current page" use cases.
94
+ 3. **Specific origins** — `"host_permissions": ["https://api.example.com/*"]` — Access only to the declared origins. For extensions that call a specific backend API from content scripts.
95
+ 4. **Pattern-based** — `"https://*.github.com/*"` — All subdomains of a specific domain. For extensions that enhance a specific service.
96
+ 5. **`<all_urls>`** — Access to every URL. Only justifiable for: ad blockers, password managers, developer tools, reading mode, and similar utilities that genuinely need to operate on any site.
97
+
98
+ **Optional host permissions for progressive disclosure:**
99
+
100
+ ```json
101
+ // manifest.json
102
+ {
103
+ "optional_host_permissions": ["https://*.github.com/*", "https://*.gitlab.com/*"]
104
+ }
105
+ ```
106
+
107
+ ```typescript
108
+ // Request GitHub permission when user enables GitHub integration
109
+ async function enableGitHubIntegration(): Promise<boolean> {
110
+ return chrome.permissions.request({
111
+ origins: ['https://*.github.com/*'],
112
+ });
113
+ }
114
+
115
+ // Check before using
116
+ async function isGitHubAccessGranted(): Promise<boolean> {
117
+ return chrome.permissions.contains({
118
+ origins: ['https://*.github.com/*'],
119
+ });
120
+ }
121
+ ```
122
+
123
+ Progressive permission requests reduce the number of users who abandon the install due to scary permission warnings, and follow the principle of least privilege by only requesting what the user actively needs.
124
+
125
+ ### XSS Prevention in Extension UIs
126
+
127
+ Extension popup and options pages are HTML documents that can render dynamic content. If that content includes unsanitized user-controlled strings or data from web pages, XSS is possible — and in an extension context, XSS in a privileged page means XSS with full `chrome.*` API access.
128
+
129
+ **The core rule: never use innerHTML with untrusted content.**
130
+
131
+ ```typescript
132
+ // DANGEROUS — XSS if pageTitle contains <script> or event handlers
133
+ element.innerHTML = `<div class="title">${pageTitle}</div>`;
134
+
135
+ // Safe — textContent is never interpreted as HTML
136
+ const titleEl = document.createElement('div');
137
+ titleEl.className = 'title';
138
+ titleEl.textContent = pageTitle;
139
+ element.appendChild(titleEl);
140
+
141
+ // Also safe — explicit sanitization
142
+ import DOMPurify from 'dompurify';
143
+ element.innerHTML = DOMPurify.sanitize(htmlContent);
144
+ ```
145
+
146
+ **DOMPurify for legitimate HTML rendering:**
147
+
148
+ When you genuinely need to render HTML from a trusted but potentially malicious source (e.g., a page's meta description):
149
+
150
+ ```typescript
151
+ import DOMPurify from 'dompurify';
152
+
153
+ // Sanitize before rendering
154
+ const clean = DOMPurify.sanitize(userHtml, {
155
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
156
+ ALLOWED_ATTR: [], // No attributes — prevents event handler injection
157
+ });
158
+ element.innerHTML = clean;
159
+ ```
160
+
161
+ **Content retrieved from web pages:** Any data that comes from a web page — tab title, page content, metadata, cookies — must be treated as potentially malicious. Apply the same sanitization discipline you would apply to user input in a web application.
162
+
163
+ **Message data from postMessage:** Content received via `window.postMessage` from page scripts must be treated as untrusted. Never render raw postMessage data as HTML.
164
+
165
+ ### Securing Cross-Context Communication
166
+
167
+ **Validate message senders:**
168
+
169
+ ```typescript
170
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
171
+ // Reject messages from web pages (non-extension senders)
172
+ if (!sender.id || sender.id !== chrome.runtime.id) {
173
+ console.warn('Message from unexpected sender:', sender);
174
+ return;
175
+ }
176
+
177
+ // Process message from known extension context
178
+ handleMessage(message, sender, sendResponse);
179
+ });
180
+ ```
181
+
182
+ **Be cautious with tabs.sendMessage targets:**
183
+
184
+ When using `chrome.tabs.sendMessage` to push data to a content script, a malicious page may have replaced the content script's context. Validate the sender in the content script:
185
+
186
+ ```typescript
187
+ // Content script — validate incoming messages from background
188
+ chrome.runtime.onMessage.addListener((message, sender) => {
189
+ // Only accept messages from the extension itself
190
+ if (sender.id !== chrome.runtime.id) return;
191
+ handleBackgroundMessage(message);
192
+ });
193
+ ```
194
+
195
+ ### Secrets and API Keys
196
+
197
+ Never bundle API secrets or private keys in extension source code:
198
+
199
+ - Extension source code is accessible to any user who installs it (via `chrome://extensions` → source in developer mode, or by unpacking the CRX/ZIP file).
200
+ - The Chrome Web Store stores submitted ZIP files — assume the contents are readable.
201
+ - Use a backend proxy service for API calls requiring secret credentials. The extension authenticates to your backend; your backend authenticates to the third-party API.
202
+ - If a third-party API supports per-user OAuth tokens, use `chrome.identity.getAuthToken()` or the full OAuth2 flow — user tokens are not bundled secrets.
@@ -0,0 +1,265 @@
1
+ ---
2
+ name: browser-extension-service-workers
3
+ description: Extension service worker lifecycle (install/activate), event-driven programming model, alarms API for recurring tasks, and persistent state via chrome.storage
4
+ topics: [browser-extension, service-worker, lifecycle, alarms, chrome-storage, event-driven, background]
5
+ ---
6
+
7
+ The Manifest V3 background service worker is the most architecturally disruptive change from MV2. The persistent background page that could hold state indefinitely is gone. Service workers are event-driven and ephemeral — Chrome terminates them when idle and restarts them when events arrive. Every design decision for background logic must account for this constraint.
8
+
9
+ ## Summary
10
+
11
+ Extension service workers follow a lifecycle of install, activate, and per-event wake-up/terminate cycles. All persistent state must be in `chrome.storage` — never rely on module-level variables surviving between events. Use `chrome.alarms` for recurring background tasks (never `setInterval`). Listen for `chrome.runtime.onInstalled` to perform first-run setup and migration. Structure the service worker as a pure event dispatcher that routes to handler functions, keeping the top-level script lean so it parses and registers listeners quickly on each wake-up.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Service Worker Lifecycle
16
+
17
+ The extension service worker has three lifecycle phases:
18
+
19
+ **1. Install**
20
+
21
+ Fires once when the extension is first installed or when an update changes the service worker script. This is where you set default state:
22
+
23
+ ```typescript
24
+ chrome.runtime.onInstalled.addListener(async ({ reason, previousVersion }) => {
25
+ switch (reason) {
26
+ case chrome.runtime.OnInstalledReason.INSTALL:
27
+ // First-time install — set defaults
28
+ await chrome.storage.sync.set({
29
+ enabled: true,
30
+ theme: 'auto',
31
+ blocklist: [],
32
+ });
33
+ // Open onboarding page
34
+ await chrome.tabs.create({
35
+ url: chrome.runtime.getURL('options/index.html?onboarding=true'),
36
+ });
37
+ break;
38
+
39
+ case chrome.runtime.OnInstalledReason.UPDATE:
40
+ // Extension updated — run migration if needed
41
+ if (previousVersion && isOlderThan(previousVersion, '2.0.0')) {
42
+ await migrateV1ToV2();
43
+ }
44
+ break;
45
+
46
+ case chrome.runtime.OnInstalledReason.CHROME_UPDATE:
47
+ // Chrome updated — rarely needs handling
48
+ break;
49
+ }
50
+ });
51
+ ```
52
+
53
+ **2. Activate**
54
+
55
+ For extension service workers, `activate` fires immediately after `install`. In the web service worker model, `activate` is used for cache cleanup; extension service workers rarely need custom `activate` logic. Chrome handles the transition automatically.
56
+
57
+ **3. Event-driven execution**
58
+
59
+ After install/activate, the service worker is idle. Chrome terminates it after 30 seconds of inactivity. It is restarted when:
60
+ - A message arrives via `chrome.runtime.onMessage` or a port connection via `chrome.runtime.onConnect`.
61
+ - An alarm fires via `chrome.alarms.onAlarm`.
62
+ - A browser event fires: `chrome.tabs.onActivated`, `chrome.tabs.onUpdated`, `chrome.storage.onChanged`, etc.
63
+ - The user clicks the extension action (toolbar button).
64
+
65
+ **Critical implication:** Every event handler must be registered at the top level of the service worker script, not inside async callbacks or deferred initialization. Chrome only keeps the service worker alive long enough to process the current event — if a listener is registered in a `setTimeout` or inside a `Promise.then`, it may never execute because the service worker was terminated before the deferred code ran.
66
+
67
+ ### Event-Driven Architecture Pattern
68
+
69
+ The service worker entry point should be a lean dispatcher. Heavy logic belongs in imported handler modules:
70
+
71
+ ```typescript
72
+ // background/index.ts — the service worker entry point
73
+
74
+ // Import handlers (all imports resolve synchronously at parse time)
75
+ import { handleMessage } from './message-router';
76
+ import { handleAlarm } from './alarm-handlers';
77
+ import { handleTabUpdate } from './tab-handlers';
78
+ import { handleInstalled } from './install';
79
+
80
+ // Register ALL event listeners at the TOP LEVEL immediately
81
+ // Chrome requires listeners to be registered synchronously during
82
+ // the service worker's initial execution
83
+ chrome.runtime.onInstalled.addListener(handleInstalled);
84
+ chrome.runtime.onMessage.addListener(handleMessage);
85
+ chrome.alarms.onAlarm.addListener(handleAlarm);
86
+ chrome.tabs.onUpdated.addListener(handleTabUpdate);
87
+
88
+ // DO NOT do this — the listener may never be registered
89
+ async function init() {
90
+ await loadConfig(); // ← If this takes time, Chrome may terminate the SW first
91
+ chrome.runtime.onMessage.addListener(handleMessage); // ← Never reached
92
+ }
93
+ init();
94
+ ```
95
+
96
+ **Message router pattern:**
97
+
98
+ ```typescript
99
+ // background/message-router.ts
100
+ export function handleMessage(
101
+ message: ExtensionMessage,
102
+ sender: chrome.runtime.MessageSender,
103
+ sendResponse: (response?: unknown) => void,
104
+ ): boolean | undefined {
105
+ switch (message.type) {
106
+ case Messages.POPUP_GET_STATUS:
107
+ getStatus().then(sendResponse);
108
+ return true; // Async response
109
+
110
+ case Messages.POPUP_TOGGLE_ENABLED:
111
+ toggleEnabled(message.payload.enabled).then(() => sendResponse());
112
+ return true;
113
+
114
+ case Messages.CONTENT_PAGE_LOADED:
115
+ handlePageLoaded(message.payload, sender.tab?.id);
116
+ // No response needed — fall through (returns undefined → synchronous)
117
+ break;
118
+
119
+ default:
120
+ console.warn('Unhandled message type:', message.type);
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Avoiding State Loss on Service Worker Termination
126
+
127
+ **Anti-pattern — in-memory cache:**
128
+
129
+ ```typescript
130
+ // WRONG — this cache is lost when the service worker is terminated
131
+ const configCache = new Map<string, SiteConfig>();
132
+
133
+ chrome.runtime.onMessage.addListener((message) => {
134
+ if (message.type === Messages.CONTENT_REQUEST_CONFIG) {
135
+ // May return undefined if SW was terminated and cache was cleared
136
+ return configCache.get(message.payload.url);
137
+ }
138
+ });
139
+ ```
140
+
141
+ **Correct pattern — always read from storage:**
142
+
143
+ ```typescript
144
+ // CORRECT — storage persists across service worker terminations
145
+ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
146
+ if (message.type === Messages.CONTENT_REQUEST_CONFIG) {
147
+ getConfigForUrl(message.payload.url).then(sendResponse);
148
+ return true;
149
+ }
150
+ });
151
+
152
+ async function getConfigForUrl(url: string): Promise<SiteConfig | null> {
153
+ const { sitesConfig } = await chrome.storage.sync.get('sitesConfig');
154
+ return sitesConfig?.find((c: SiteConfig) => urlMatches(url, c.pattern)) ?? null;
155
+ }
156
+ ```
157
+
158
+ **chrome.storage.session for within-session state:**
159
+
160
+ Chrome 102+ provides `chrome.storage.session` — storage that persists across service worker restarts within a browser session but is cleared when the browser closes:
161
+
162
+ ```typescript
163
+ // Track whether we're mid-operation, survives SW restart within session
164
+ await chrome.storage.session.set({ processingTabId: tabId });
165
+
166
+ // On SW restart, check if interrupted work needs resuming
167
+ const { processingTabId } = await chrome.storage.session.get('processingTabId');
168
+ if (processingTabId) {
169
+ await resumeProcessing(processingTabId);
170
+ }
171
+ ```
172
+
173
+ ### Alarms API for Recurring Tasks
174
+
175
+ `setInterval` and `setTimeout` do not survive service worker termination. Use `chrome.alarms` for any recurring background work.
176
+
177
+ **Registering alarms (must be idempotent):**
178
+
179
+ ```typescript
180
+ // Register in onInstalled AND when the service worker starts up
181
+ // (In case alarms were cleared by a browser update)
182
+ async function ensureAlarmsRegistered(): Promise<void> {
183
+ const existing = await chrome.alarms.get('sync-check');
184
+ if (!existing) {
185
+ chrome.alarms.create('sync-check', {
186
+ periodInMinutes: 15,
187
+ delayInMinutes: 1, // First fire after 1 minute
188
+ });
189
+ }
190
+
191
+ const existingDaily = await chrome.alarms.get('daily-cleanup');
192
+ if (!existingDaily) {
193
+ chrome.alarms.create('daily-cleanup', {
194
+ periodInMinutes: 60 * 24,
195
+ when: Date.now() + 60 * 60 * 1000, // First fire in 1 hour
196
+ });
197
+ }
198
+ }
199
+
200
+ // Call on install and at service worker startup
201
+ chrome.runtime.onInstalled.addListener(ensureAlarmsRegistered);
202
+ // Also call when SW starts (in case alarms were lost)
203
+ ensureAlarmsRegistered();
204
+ ```
205
+
206
+ **Alarm handler:**
207
+
208
+ ```typescript
209
+ chrome.alarms.onAlarm.addListener(async (alarm) => {
210
+ switch (alarm.name) {
211
+ case 'sync-check':
212
+ await performSyncCheck();
213
+ break;
214
+ case 'daily-cleanup':
215
+ await performDailyCleanup();
216
+ break;
217
+ default:
218
+ console.warn('Unknown alarm:', alarm.name);
219
+ }
220
+ });
221
+ ```
222
+
223
+ **Minimum alarm interval:** Chrome enforces a minimum alarm interval of 1 minute (or 30 seconds in some contexts). Do not attempt sub-minute recurring tasks with alarms — use a polling loop within a single alarm handler if sub-minute work is needed, and be aware this will keep the service worker alive.
224
+
225
+ ### Keeping the Service Worker Alive for Long Operations
226
+
227
+ For long-running operations that must not be interrupted (e.g., downloading and processing a large file), use strategies to prevent premature termination:
228
+
229
+ **Strategy 1 — Chain promises to stay active:**
230
+
231
+ Chrome extends the service worker's lifetime as long as there are pending promises created within the current event handler. Keep a promise chain alive for the duration of the work:
232
+
233
+ ```typescript
234
+ chrome.runtime.onMessage.addListener((_msg, _sender, sendResponse) => {
235
+ // The returned `true` and the pending `sendResponse` keep the SW alive
236
+ runLongOperation()
237
+ .then(result => sendResponse({ success: true, result }))
238
+ .catch(err => sendResponse({ success: false, error: err.message }));
239
+ return true;
240
+ });
241
+ ```
242
+
243
+ **Strategy 2 — Use the service worker's `waitUntil` equivalent:**
244
+
245
+ Extension service workers do not have `event.waitUntil()` like web service workers. The closest equivalent is to keep an event's `sendResponse` callback pending, or to hold a port connection open.
246
+
247
+ **Strategy 3 — Checkpoint progress to storage:**
248
+
249
+ For operations that genuinely need more time than the service worker lifetime allows, checkpoint progress to `chrome.storage.session` and resume on the next alarm or message:
250
+
251
+ ```typescript
252
+ async function processInChunks(items: Item[]): Promise<void> {
253
+ const { processedIndex = 0 } = await chrome.storage.session.get('processedIndex');
254
+
255
+ for (let i = processedIndex; i < items.length; i++) {
256
+ await processItem(items[i]);
257
+ // Checkpoint every 10 items in case SW is terminated
258
+ if (i % 10 === 0) {
259
+ await chrome.storage.session.set({ processedIndex: i });
260
+ }
261
+ }
262
+
263
+ await chrome.storage.session.remove('processedIndex');
264
+ }
265
+ ```
@@ -0,0 +1,155 @@
1
+ ---
2
+ name: browser-extension-store-submission
3
+ description: Chrome Web Store review process, AMO submission, screenshot and promotional image requirements, listing optimization, and managing version updates
4
+ topics: [browser-extension, store-submission, chrome-web-store, amo, listing-optimization, review-process, screenshots]
5
+ ---
6
+
7
+ Store submission is the deployment step for browser extensions. Unlike web applications where you push to a server, extensions must pass store review before reaching users. Review timelines, policy requirements, and listing quality directly affect user acquisition and approval success. Understanding the process before writing the first line of code prevents costly late-stage pivots.
8
+
9
+ ## Summary
10
+
11
+ Chrome Web Store review takes 1–3 days for automated review, up to 2 weeks for manual review triggered by sensitive permissions. AMO (Mozilla Add-ons) requires source code submission alongside minified builds and enforces a no-obfuscation policy. Store listing quality (screenshots, descriptions, icon) significantly affects organic search and conversion. Version updates that add or change permissions trigger re-review. Prepare all store assets before the first submission to avoid resubmission delays.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Chrome Web Store Review Process
16
+
17
+ The Chrome Web Store has a two-tier review system:
18
+
19
+ **Automated review (most extensions):**
20
+ - Triggered for extensions without sensitive permissions.
21
+ - Typically completes in 1–3 business days.
22
+ - Checks for policy violations: remote code execution, deceptive behavior, malware patterns.
23
+ - An extension that passes automated review is published automatically.
24
+
25
+ **Manual review (sensitive permissions or policy concerns):**
26
+ - Triggered by: `webRequest`, `nativeMessaging`, `history`, `cookies`, `unlimitedStorage`, `<all_urls>` or very broad host patterns, and any permission not commonly seen.
27
+ - Also triggered by: policy violation flags, user reports, or random sampling of extensions.
28
+ - Takes 1–4 weeks. During this period, your extension is "pending review" and not publicly accessible.
29
+ - Reviewers may request additional justification via email. Respond within 5 business days or the review is cancelled.
30
+
31
+ **New developer accounts** face stricter scrutiny. New accounts submitting extensions with sensitive permissions often go to manual review regardless of the permissions requested. Plan for a 2-week review window for a new developer account's first submission.
32
+
33
+ **Preparing for review:**
34
+ - Write a detailed "Single Purpose" description in the "Developer Notes" field in the developer console. Explain exactly what each permission is used for.
35
+ - Every permission must be clearly mentioned in the public store description.
36
+ - The extension must work as described. Reviewers install and test extensions.
37
+ - If the extension requires authentication or a specific URL to function, provide test credentials in the Developer Notes.
38
+
39
+ ### AMO (Mozilla Add-ons) Submission
40
+
41
+ AMO review differs significantly from Chrome's process:
42
+
43
+ **Source code submission:**
44
+ - When submitting a minified/bundled extension, AMO requires the corresponding source code so reviewers can audit the logic.
45
+ - Submit a ZIP of your source code alongside the extension ZIP.
46
+ - Include a `build.md` with exact instructions to reproduce the submitted build from source.
47
+ - Reviewers execute your build instructions and compare the output to the submitted artifacts. If the build does not reproduce, the submission is rejected.
48
+
49
+ **No obfuscation policy:**
50
+ - Minification (whitespace removal, variable shortening) is acceptable.
51
+ - Obfuscation (intentionally making code unreadable with tools like obfuscator.io) is grounds for rejection.
52
+ - Webpack/Vite production builds are acceptable — their output is minified but not obfuscated.
53
+
54
+ **Review timeline:**
55
+ - New extensions: 1–2 weeks for full review.
56
+ - Updates to reviewed extensions: Can be faster if no new permissions are added.
57
+ - Extensions with high install counts receive priority review for security updates.
58
+
59
+ **Listing vs. self-distribution:**
60
+ - AMO-listed extensions are reviewed, signed, and discoverable in the Firefox add-ons store.
61
+ - Self-distributed extensions are signed by Mozilla (required for permanent install in Firefox) but not reviewed or listed.
62
+ - Choose "Listed" for all consumer-facing extensions. "Self-distributed" is for enterprise or internal extensions.
63
+
64
+ ### Extension Icon Requirements
65
+
66
+ Store icons must meet specific requirements to avoid rejection or poor presentation:
67
+
68
+ **Manifest icons (required):**
69
+ - 16×16 — Toolbar button (the most important size — must be recognizable at this scale).
70
+ - 32×32 — Windows HiDPI toolbar; extension management page.
71
+ - 48×48 — Extension management page (`chrome://extensions`).
72
+ - 128×128 — Chrome Web Store listing page.
73
+
74
+ **Chrome Web Store store icon:**
75
+ - 128×128 PNG — Separate from the manifest icon, uploaded in the developer console.
76
+ - Must not have rounded corners (Chrome applies rounding in the store UI).
77
+ - Must not contain text if the text is too small to read at 128×128.
78
+
79
+ **Firefox icon:**
80
+ - AMO accepts PNG or SVG for the store icon.
81
+ - Recommended: submit SVG for the store icon; PNG for manifest icons.
82
+
83
+ **Design for legibility at 16×16:** The toolbar icon at 16×16 is the primary user-facing brand element of your extension. A complex illustration or thin text will be unrecognizable. Use simple, bold shapes and high contrast. Test actual rendering at 16×16 before finalizing.
84
+
85
+ ### Screenshots and Promotional Assets
86
+
87
+ Screenshots are the single biggest factor in store listing conversion rates — they are the first thing users see when evaluating your extension.
88
+
89
+ **Chrome Web Store requirements:**
90
+ - 1280×800 or 640×400 PNG or JPEG screenshots.
91
+ - Minimum 1 screenshot, maximum 5.
92
+ - Screenshots must accurately represent the extension's UI and behavior. Staged screenshots are fine; misleading screenshots are grounds for removal.
93
+
94
+ **Promotional tile (Chrome Web Store):**
95
+ - Small tile: 440×280 PNG — Required for "Featured" promotion.
96
+ - Large tile: 920×680 PNG — Optional.
97
+ - Marquee banner: 1400×560 PNG — Optional, for featured placement.
98
+
99
+ **AMO screenshot requirements:**
100
+ - Any resolution PNG or JPEG.
101
+ - Recommended: 1280×800.
102
+ - Up to 10 screenshots.
103
+
104
+ **Screenshot best practices:**
105
+ - Show the extension's core value proposition in the first screenshot.
106
+ - Use real content, not placeholder text.
107
+ - Include captions (add them in the developer console, not in the image) explaining what is shown.
108
+ - Show both the extension UI and the page it is enhancing — side by side or with the popup visible over the page.
109
+ - Provide dark mode screenshots if the extension supports dark mode.
110
+
111
+ ### Store Listing Optimization
112
+
113
+ **Title:**
114
+ - Chrome: 45 characters max.
115
+ - Include the primary keyword users would search for. "Ad Blocker for Chrome" ranks for "ad blocker".
116
+ - Do not use competitor brand names in the title.
117
+
118
+ **Short description (Chrome / AMO summary):**
119
+ - Chrome: 132 characters. This appears in search results.
120
+ - Lead with the primary benefit: "Block ads, trackers, and popups on every website."
121
+ - Avoid generic phrasing: "A great extension for better browsing" is not helpful.
122
+
123
+ **Full description:**
124
+ - Explain every permission in plain language.
125
+ - List key features as bullet points — users scan, not read.
126
+ - Include keywords that describe the use case naturally. Do not keyword-stuff.
127
+ - Link to privacy policy, support page, and source code repository.
128
+
129
+ **Privacy Policy:**
130
+ - Required for any extension that collects, processes, or transmits user data.
131
+ - Even if the extension stores data only locally, provide a privacy policy that states this.
132
+ - Host the privacy policy at a stable URL (not a GitHub README — it can be moved; use a dedicated page).
133
+
134
+ ### Managing Version Updates
135
+
136
+ **Version number requirements:**
137
+ - Each update must have a strictly higher version number than the previous published version.
138
+ - Chrome uses the four-part format: `MAJOR.MINOR.PATCH.BUILD` (e.g., `1.2.3.0`).
139
+ - Once a version number is used, it cannot be reused even if the submission was rejected.
140
+
141
+ **Permission changes in updates:**
142
+ - Adding new permissions to an existing extension triggers re-review.
143
+ - Users with the extension installed are shown a permission update prompt — they must re-approve before the extension updates.
144
+ - This is a significant UX disruption. Batch permission additions into major version updates and communicate the changes clearly.
145
+ - Never remove permissions without considering whether existing features depend on them — silent feature breakage is worse than a permission prompt.
146
+
147
+ **Update rollout:**
148
+ - Chrome supports staged rollouts: publish to a percentage of users and increase over time.
149
+ - Configure this in the developer console under "Distribution" after submitting the update.
150
+ - Use staged rollout for major updates or updates with new permissions to catch issues before full release.
151
+
152
+ **Expedited review:**
153
+ - The Chrome Web Store provides an "expedite review" option in the developer console for critical security fixes.
154
+ - Use sparingly — you are granted approximately two expedited reviews per year per developer account.
155
+ - Include a detailed explanation of the security issue and urgency in the expedite request.