← back to Commercialrealestate
chore: lint, refactor, v0.9.0 (session close)
87cbbdedbf790617eb322ec3344f6803022fe251 · 2026-07-13 09:56:31 -0700 · Steve Abrams
- email-hunt.js: rename blockedOrEmpty→shouldEscalate + clearer comment (no behavior change)
- serve.js: find-email timeout message now reflects the real cap (45s/75s) instead of hardcoded 45s
- crcp.html: add esc() helper + apply in webLink() (XSS-harden the site-name link)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Files touched
M package.jsonM public/crcp.htmlM scripts/lib/email-hunt.jsM scripts/serve.js
Diff
commit 87cbbdedbf790617eb322ec3344f6803022fe251
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jul 13 09:56:31 2026 -0700
chore: lint, refactor, v0.9.0 (session close)
- email-hunt.js: rename blockedOrEmpty→shouldEscalate + clearer comment (no behavior change)
- serve.js: find-email timeout message now reflects the real cap (45s/75s) instead of hardcoded 45s
- crcp.html: add esc() helper + apply in webLink() (XSS-harden the site-name link)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
package.json | 2 +-
public/crcp.html | 3 ++-
scripts/lib/email-hunt.js | 9 +++++----
scripts/serve.js | 2 +-
4 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/package.json b/package.json
index 858c974..60cfa8b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "commercialrealestate",
- "version": "0.8.0",
+ "version": "0.9.0",
"private": true,
"description": "LA County CRE investment explorer — multi-firm sourced, Census + assessor enriched, Qwen-analyzed",
"scripts": {
diff --git a/public/crcp.html b/public/crcp.html
index 50560b8..bcfd553 100644
--- a/public/crcp.html
+++ b/public/crcp.html
@@ -229,7 +229,8 @@ function srt(arr,col,dir){ return arr.slice().sort((a,b)=>{ let x=a[col],y=b[col
function markHdr(tbl,sort){ document.querySelectorAll('#'+tbl+' th.srt').forEach(th=>{ th.classList.remove('asc','desc'); if(th.dataset.col===sort.col) th.classList.add(sort.dir>0?'asc':'desc'); }); }
// Web column shows the SITE NAME (domain), not a bare "site↗" — Steve 2026-07-13.
const siteName=u=>{ try{ return new URL(/^https?:/.test(u)?u:'https://'+u).hostname.replace(/^www\./,''); }catch(e){ return u; } };
-const webLink=u=>{ if(!u) return '<span class="miss">—</span>'; const h=/^https?:/.test(u)?u:'https://'+u; return `<a href="${h}" target="_blank" rel="noopener noreferrer" style="color:var(--gold)" title="${h}">${siteName(u)}↗</a>`; };
+const esc=s=>(s==null?'':String(s)).replace(/&/g,'&').replace(/</g,'<').replace(/"/g,'"');
+const webLink=u=>{ if(!u) return '<span class="miss">—</span>'; const h=/^https?:/.test(u)?u:'https://'+u; return `<a href="${esc(h)}" target="_blank" rel="noopener noreferrer" style="color:var(--gold)" title="${esc(h)}">${esc(siteName(u))}↗</a>`; };
// On-demand email hunt (the "✉ find email" button). hunt[] survives the 3s poll re-renders;
// a found email also lands in broker.email server-side, so the next poll carries it natively.
const hunt={};
diff --git a/scripts/lib/email-hunt.js b/scripts/lib/email-hunt.js
index 4003b76..bd55bf0 100644
--- a/scripts/lib/email-hunt.js
+++ b/scripts/lib/email-hunt.js
@@ -158,10 +158,11 @@ async function huntEmails(b, opts = {}) {
}
}
let { email, basis, pool } = pickBest([...mailtos], [...texts], dom, b.name);
- // Escalate to the real browser only when plain fetch struck out (blocked or JS-rendered) AND we
- // don't already have a confident hit. Renders JS + carries a genuine browser fingerprint.
- const blockedOrEmpty = !email && (tried.some(t => t.status === 403 || t.status === 0) || tried.every(t => t.status >= 200));
- if (opts.browserHtml && blockedOrEmpty) {
+ // Escalate to the real browser when no email found: either the site blocked us (403/0) or it
+ // rendered fine but published no email (JS-rendered contact widgets). Renders JS + carries a
+ // genuine browser fingerprint.
+ const shouldEscalate = !email && (tried.some(t => t.status === 403 || t.status === 0) || tried.every(t => t.status >= 200));
+ if (opts.browserHtml && shouldEscalate) {
const bh = await opts.browserHtml(b.website);
tried.push({ url: b.website + ' [browser]', status: bh.status, err: bh.err });
if (bh.ok) {
diff --git a/scripts/serve.js b/scripts/serve.js
index 6cbad78..ee3646b 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -874,7 +874,7 @@ app.post('/api/broker/find-email', async (req, res) => {
site: emailHunt.siteDomain(website),
reason: r.found ? null
: r.basis === 'ambiguous-shared-page' ? `shared page — ${(r.all || []).length} other agents' emails, none match ${name}; refused to guess`
- : r.basis === 'timeout' ? 'site too slow (45s cap)'
+ : r.basis === 'timeout' ? `site too slow (${cap / 1000}s cap)`
: reachedOk ? 'reached the site but no email is published on it'
: 'site unreachable/blocked (' + (r.tried || []).map(t => t.status || t.err || '?').join('/') + ')',
cost: '$0 (local fetch)'
← 96492dc auto-save: 2026-07-13T09:54:06 (1 files) — data/alerts-state
·
back to Commercialrealestate
·
data: CRCP listings sweep +2 (palm-beach) from launchd kicks 701f6cd →