Skip to content

Commit 4f045b5

Browse files
authored
Improve dev env, especially gappiness (#64 closes #55)
2 parents aa3388b + f070cdb commit 4f045b5

File tree

5 files changed

+57
-80
lines changed

5 files changed

+57
-80
lines changed

.claude/agents/corpus-fixer.md

Lines changed: 0 additions & 39 deletions
This file was deleted.

.claude/commands/corpus-loop.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
argument-hint: [corpus_slug]
3-
description: uses Playwright MCP and the `corpus:view` to parse page elements
3+
description: uses Playwright MCP and the `corpus` to parse page elements
44
---
55

66
- using Playwright MCP, navigate to `http://localhost:3001/corpus/$1/gitcasso`
@@ -54,4 +54,13 @@ If you see `"title": "TODO_TITLE"` or similar hardcoded `TODO` values in the JSO
5454

5555
- Don't hedge your bets and write lots of fallback code or strings of `?.`. Have a specific piece of data you want to get, use non-null `!` assertions where necessary to be clear about getting.
5656
- If a field is empty, represent it with an empty string. Don't use placeholders when extracting data.
57-
- The pages we are scraping are going to change over time, and it's easier to fix broken ones if we know exactly what used to work. If the code has lots of branching paths, it's harder to tell what it was doing.
57+
- The pages we are scraping are going to change over time, and it's easier to fix broken ones if we know exactly what used to work. If the code has lots of branching paths, it's harder to tell what it was doing.
58+
59+
## Troubleshooting
60+
61+
- If you see `"spot": "NO_SPOT"` but expect an enhancer to match:
62+
- Check console logs for enhancer attempts (e.g., `"eE examing url"`)
63+
- Look for specific rejection reasons in the enhancer's `tryToEnhance` method
64+
- If multiple enhancers are conflicting:
65+
- Check the order of enhancer registration in `registries.ts`
66+
- Ensure proper exclusion logic (e.g., checking for specific containers)

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ When the `textarea` gets removed from the page, the `TextareaRegistry` is notifi
7979
## Testing
8080

8181
- `pnpm playground` gives you a test environment where you can tinker with the popup with various test data, supports hot reload
82-
- `pnpm corpus:view` gives you recordings of various web pages which you can see with and without enhancement by the browser extension
82+
- `pnpm corpus` gives you recordings of various web pages which you can see with and without enhancement by the browser extension
8383

8484
### Test Corpus
8585

@@ -114,7 +114,7 @@ We maintain a corpus of test pages in two formats for testing the browser extens
114114

115115
#### Viewing Corpus Files
116116

117-
- Run `pnpm corpus:view` to start the test server at http://localhost:3001
117+
- Run `pnpm corpus` to start the test server at http://localhost:3001
118118
- Select any corpus file to view in two modes:
119119
- **Clean**: Original page without extension
120120
- **Gitcasso**: Page with extension injected for testing

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"playground": "vite --config vite.playground.config.ts",
6565
"playground:build": "vite build --config vite.playground.config.ts",
6666
"corpus:har:record": "tsx tests/corpus-har-record.ts",
67-
"corpus:view": "tsx tests/corpus-view.ts"
67+
"corpus": "tsx tests/corpus-view.ts"
6868
},
6969
"type": "module",
7070
"version": "0.0.1"

tests/corpus-view.ts

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ const WEBEXTENSION_POLYFILL_REPLACEMENT =
3939
'console.warn("Webextension-polyfill check bypassed for corpus testing")'
4040
const BROWSER_API_MOCKS =
4141
'window.chrome=window.chrome||{runtime:{getURL:path=>"chrome-extension://gitcasso-test/"+path,onMessage:{addListener:()=>{}},sendMessage:()=>Promise.resolve(),id:"gitcasso-test"}};window.browser=window.chrome;'
42-
const PERMISSIVE_CSP = "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: http: https:;"
42+
const PERMISSIVE_CSP =
43+
"default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: http: https:; connect-src 'self' http: https:; script-src 'self' 'unsafe-inline' 'unsafe-eval';"
4344

4445
// UI Styles
4546
const REBUILD_BUTTON_STYLES = `
@@ -125,19 +126,20 @@ app.get('/', async (_req, res) => {
125126
const links = Object.entries(CORPUS)
126127
.map(([key, entry]) => {
127128
const description = entry.description
128-
? `<div style="color: #666; font-size: 0.9em; margin-top: 5px;">${entry.description}</div>`
129+
? `<div style="color: #666; font-size: 0.9em;">${entry.description}</div>`
129130
: ''
130131
return `
131132
<li>
132-
<div style="margin-bottom: 10px;">
133-
<div style="font-weight: bold; color: #555;">${key}</div>
134-
<div style="font-size: 0.9em; color: #888;">${entry.type.toUpperCase()}</div>
135-
${description}
136-
</div>
137-
<div style="display: flex; gap: 10px;">
138-
<a href="/corpus/${key}/clean" style="flex: 1; text-align: center;">🔍 Clean</a>
139-
<a href="/corpus/${key}/gitcasso" style="flex: 1; text-align: center;">🚀 Gitcasso</a>
133+
<div style="display: flex; justify-content: space-between; align-items: center;">
134+
<div>
135+
<strong>${key}</strong> <span style="color: #888;">${entry.type.toLowerCase()}</span>
136+
</div>
137+
<div style="display: flex; gap: 15px;">
138+
<a href="/corpus/${key}/clean">🔍 clean</a>
139+
<a href="/corpus/${key}/gitcasso">🚀 gitcasso</a>
140+
</div>
140141
</div>
142+
${description}
141143
</li>
142144
`
143145
})
@@ -150,36 +152,27 @@ app.get('/', async (_req, res) => {
150152
<title>Corpus Viewer</title>
151153
<style>
152154
body {
153-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
154-
max-width: 700px;
155-
margin: 50px auto;
156-
padding: 20px;
155+
font-family: system-ui, sans-serif;
156+
max-width: 600px;
157+
margin: 20px auto;
158+
padding: 10px;
157159
}
158-
h1 { color: #333; }
159-
ul { list-style: none; padding: 0; }
160-
li { margin: 20px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; }
160+
h1 { margin-bottom: 10px; }
161+
ul { list-style: none; padding: 0; margin: 0; }
162+
li { margin: 4px 0; padding: 8px 12px; background: #f9f9f9; }
161163
a {
162-
display: block;
163-
padding: 12px 20px;
164-
background: #fff;
164+
color: #0066cc;
165165
text-decoration: none;
166-
color: #333;
167-
border-radius: 6px;
168-
border: 1px solid #dee2e6;
169-
transition: all 0.2s;
166+
font-size: 0.9em;
170167
}
171-
a:hover:not([style*="pointer-events: none"]) { background: #e9ecef; transform: translateY(-1px); }
172-
code { background: #f1f3f4; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
168+
a:hover { text-decoration: underline; }
173169
</style>
174170
</head>
175171
<body>
176-
<h1>📄 Corpus Viewer</h1>
177-
<p>Select a recorded page to view:</p>
172+
<h1>Corpus Viewer</h1>
178173
<ul>${links}</ul>
179-
<div style="margin-top: 40px; padding: 20px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #007acc;">
180-
<h3>Corpus Types</h3>
181-
<p><strong>HAR:</strong> Automated network captures of initial page loads</p>
182-
<p><strong>HTML:</strong> Manual SingleFile captures of post-interaction states</p>
174+
<div style="margin-top: 20px; padding: 10px; background: #f9f9f9;">
175+
<strong>HAR:</strong> Network captures | <strong>HTML:</strong> Manual captures
183176
</div>
184177
</body>
185178
</html>
@@ -237,6 +230,10 @@ app.get('/corpus/:key/:mode(clean|gitcasso)', async (req, res) => {
237230

238231
// Replace external URLs with local asset URLs
239232
let html = mainEntry.response.content.text!
233+
234+
// Strip CSP headers that might block our injected scripts
235+
html = stripCSPFromHTML(html)
236+
240237
domains.forEach((domain) => {
241238
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
242239
const regex = new RegExp(`https?://${escapedDomain}`, 'g')
@@ -245,6 +242,13 @@ app.get('/corpus/:key/:mode(clean|gitcasso)', async (req, res) => {
245242
if (mode === 'gitcasso') {
246243
html = injectGitcassoScriptForHAR(key, html)
247244
}
245+
246+
// Set permissive headers for HAR corpus to allow rebuild requests
247+
res.set({
248+
'Content-Security-Policy': PERMISSIVE_CSP,
249+
'X-Content-Type-Options': 'nosniff',
250+
})
251+
248252
return res.send(html)
249253
} else if (entry.type === 'html') {
250254
// Handle HTML corpus
@@ -385,12 +389,15 @@ app.listen(PORT, () => {
385389

386390
// Strip CSP meta tags and headers from HTML that might block our scripts
387391
function stripCSPFromHTML(html: string): string {
388-
// Remove CSP meta tags
389-
html = html.replace(/<meta[^>]*http-equiv\s*=\s*["']content-security-policy["'][^>]*>/gi, '')
390-
html = html.replace(/<meta[^>]*name\s*=\s*["']content-security-policy["'][^>]*>/gi, '')
392+
// Remove CSP meta tags - more comprehensive patterns
393+
html = html.replace(/<meta[^>]*http-equiv\s*=\s*["']?content-security-policy["']?[^>]*>/gi, '')
394+
html = html.replace(/<meta[^>]*name\s*=\s*["']?content-security-policy["']?[^>]*>/gi, '')
395+
396+
// Also match patterns where content-security-policy appears anywhere in the meta tag
397+
html = html.replace(/<meta[^>]*content-security-policy[^>]*>/gi, '')
391398

392399
// Remove any other restrictive security meta tags
393-
html = html.replace(/<meta[^>]*http-equiv\s*=\s*["']x-content-type-options["'][^>]*>/gi, '')
400+
html = html.replace(/<meta[^>]*http-equiv\s*=\s*["']?x-content-type-options["']?[^>]*>/gi, '')
394401

395402
return html
396403
}

0 commit comments

Comments
 (0)