[object Object]

← back to Commercialrealestate

chore: lint, refactor (shared normalizeCrexiId), v0.26.1 (session close TK-12033)

5ddda1e259ae837eb9d837f3e24bd88a2812b71b · 2026-09-23 12:44:11 -0700 · Steve Abrams

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Files touched

Diff

commit 5ddda1e259ae837eb9d837f3e24bd88a2812b71b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 23 12:44:11 2026 -0700

    chore: lint, refactor (shared normalizeCrexiId), v0.26.1 (session close TK-12033)
    
    Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---
 package-lock.json                 | 4 ++--
 package.json                      | 2 +-
 scripts/refresh-listings-multi.js | 4 ++--
 scripts/sources/firms.js          | 9 ++++++---
 4 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index fee7b5e..c339a6e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "commercialrealestate",
-  "version": "0.26.0",
+  "version": "0.26.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "commercialrealestate",
-      "version": "0.26.0",
+      "version": "0.26.1",
       "dependencies": {
         "better-sqlite3": "^12.11.1",
         "express": "^4.22.2",
diff --git a/package.json b/package.json
index cad439f..c9d0caf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "commercialrealestate",
-  "version": "0.26.0",
+  "version": "0.26.1",
   "private": true,
   "description": "LA County CRE investment explorer — multi-firm sourced, Census + assessor enriched, Qwen-analyzed",
   "scripts": {
diff --git a/scripts/refresh-listings-multi.js b/scripts/refresh-listings-multi.js
index 1403932..cb843b1 100644
--- a/scripts/refresh-listings-multi.js
+++ b/scripts/refresh-listings-multi.js
@@ -14,6 +14,7 @@
 const fs = require('fs');
 const path = require('path');
 const { chromium } = require('playwright-core');
+const { normalizeCrexiId } = require('./sources/firms');
 
 const ROOT = path.join(__dirname, '..');
 const CFG = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', 'metro-listings-config.json'), 'utf8'));
@@ -61,8 +62,7 @@ async function makeBrowser() {
             : (a.address && typeof a.address === 'object' ? a.address : ((a.locations && a.locations[0]) || {}));
           const st = String(loc.stateCode || loc.state || loc.stateName || '').toUpperCase();
           if (st && st !== 'CA' && st !== 'CALIFORNIA') continue;
-          const rawId = a.id || a.assetId;   // "sales-N" -> "N" (canonical detail URL + crxN id namespace, TK-12033)
-          const id = rawId == null ? null : String(rawId).replace(/^(?:sales|lease|auction)-/i, "");
+          const id = normalizeCrexiId(a.id || a.assetId);   // "sales-N" -> "N" (canonical detail URL + crxN id namespace, TK-12033)
           const price = (a.propertyPrice && a.propertyPrice.total) || a.askingPrice || a.price;
           const city = loc.city || loc.cityName;
           const address = loc.streetAddress || loc.address || loc.fullAddress || a.propertyName || a.name || '';
diff --git a/scripts/sources/firms.js b/scripts/sources/firms.js
index 86ba78f..301344c 100644
--- a/scripts/sources/firms.js
+++ b/scripts/sources/firms.js
@@ -110,6 +110,10 @@ function genericExtract(json) {
   return out;
 }
 
+// Crexi v2 ids arrive as "sales-2066415" / "lease-…"; the canonical detail URL and our historical crxN ids use the
+// bare number. Shared by crexiExtract (this file) and the paid-path inline extractor in refresh-listings-multi.js.
+function normalizeCrexiId(raw) { return raw == null ? null : String(raw).replace(/^(?:sales|lease|auction)-/i, ""); }
+
 // Crexi's verified extractor — its API matches genericExtract anyway, but we keep it explicit so the
 // reference source is never silently changed by a heuristic tweak.
 function crexiExtract(json) {
@@ -131,8 +135,7 @@ function crexiExtract(json) {
     // v2 ids arrive as "sales-2066415" / "lease-…" but the canonical detail URL (and our historical crxN ids)
     // use the bare number — /properties/2066415/<urlSlug>. Keeping the prefix 302s to a dead "brokerage search"
     // page, forks the id namespace, and fails crcp-scope.js isCalifornia() closed (TK-12033).
-    const rawId = a.id || a.assetId;
-    const id = rawId == null ? null : String(rawId).replace(/^(?:sales|lease|auction)-/i, "");
+    const id = normalizeCrexiId(a.id || a.assetId);
     const price = (a.propertyPrice && a.propertyPrice.total) || a.askingPrice || a.price;
     const city = loc.city || loc.cityName;
     const address = loc.streetAddress || loc.address || loc.fullAddress || a.propertyName || a.name || '';
@@ -296,4 +299,4 @@ function toListing(a, firm) {
 // Apply the SFV + price-band + plausibility gate to a batch of intermediate assets.
 const inBand = (a) => a && a.city && SFV.has(a.city.toLowerCase()) && a.price >= MINP && a.price <= MAXP && a.address;
 
-module.exports = { SFV, MINP, MAXP, TYPES, norm, today, FIRMS, byKey, genericExtract, toListing, inBand };
+module.exports = { SFV, MINP, MAXP, TYPES, norm, today, FIRMS, byKey, genericExtract, toListing, inBand, normalizeCrexiId };

← 0aa4c7e auto-data-snapshot: 2026-09-23T12:42:16 (8 data files) — dat  ·  back to Commercialrealestate  ·  afternoon CRE update 2026-09-23 0cc031f →