openvibers/openvibe.directory Commits
Omit unverified OpenStreetMap phone tags (#3)
Changes
@@@ -2,6 +2,6 @@Source-linked directories for OpenVibe sites and tools, AI models and providers, open-source projects, and local businesses from OpenStreetMap. Every entry has a source and a last checked date. Public sources are refreshed at startup and every six hours; a failed source keeps its last successful in-memory snapshot and exposes the failure in `/api/v1/kinds`.--Business listings are a bounded San Francisco pilot sourced from OpenStreetMap under ODbL 1.0. Phone numbers appear only when published on the mapped business. Private-person searches and reverse lookups are excluded. The people directory has no imported entries and remains empty until the opt-in claim flow is available.++Business listings are a bounded San Francisco pilot sourced from OpenStreetMap under ODbL 1.0. Unverified OpenStreetMap phone tags are omitted; a business number needs a business-owned source before publication. Private-person searches and reverse lookups are excluded. The people directory has no imported entries and remains empty until the opt-in claim flow is available.Run `npm ci`, then `node server/index.js`. Check `/api/v1/kinds`, `/api/v1/entries`, `/sitemap.xml`, and `/llms.txt`.@@@ -46,8 +46,8 @@ function businesses(xml, checked) {const id=`${type}-${n.id}`;const source=`https://www.openstreetmap.org/${type}/${n.id}`;const website=safeUrl(tags.website)||safeUrl(tags['contact:website'])||source;-- const phone=tags['contact:phone']||tags.phone||null;-- out.push(entry('businesses',id,tags.name,`${tags.shop||tags.amenity} in San Francisco, mapped in OpenStreetMap.`,website,source,checked,{group:tags.shop||tags.amenity,phone:phone&&String(phone).slice(0,80),osm_attribution:'© OpenStreetMap contributors, ODbL 1.0'}));++ // OSM contact tags can be contributed by anyone; show a number only after checking a business-owned source.++ out.push(entry('businesses',id,tags.name,`${tags.shop||tags.amenity} in San Francisco, mapped in OpenStreetMap.`,website,source,checked,{group:tags.shop||tags.amenity,osm_attribution:'© OpenStreetMap contributors, ODbL 1.0'}));}return out.filter(Boolean);}@@@ -3,10 +3,10 @@ const {test}=require('node:test');const assert=require('node:assert/strict');const {createStore,businesses}=require('../server/sources');const {createApp}=require('../server/app');--test('business phone is only read from business OSM tags and source is attributed',()=>{++test('unverified OSM phone tags are not published and business source is attributed',()=>{const xml='<osm><node id="1"><tag k="shop" v="books"/><tag k="name" v="Bookshop"/><tag k="phone" v="+1 415 555 0100"/></node><node id="2"><tag k="name" v="Jane Smith"/><tag k="phone" v="+1 415 555 0101"/></node></osm>';const rows=businesses(xml,'2026-11-10T00:00:00.000Z');-- assert.equal(rows.length,1);assert.equal(rows[0].phone,'+1 415 555 0100');assert.match(rows[0].sources[0].url,/openstreetmap/);assert.match(rows[0].osm_attribution,/ODbL/);++ assert.equal(rows.length,1);assert.equal(rows[0].phone,undefined);assert.match(rows[0].sources[0].url,/openstreetmap/);assert.match(rows[0].osm_attribution,/ODbL/);});test('failed refresh retains the last successful source and reports stale status',async()=>{let fail=false;const fetchImpl=async(url)=>{if(fail)throw Error('source down');return {ok:true,json:async()=>url.includes('frame?')?{nav:[{id:'live',name:'Live',url:'https://openvibe.live/'}]}:{tools:[{id:'yaml',name:'YAML',docs:'https://openvibe.tools/tool/yaml'}],things:[{id:'a/b',name:'B',maker:'A',url:'https://example.com'}],items:[{full_name:'OpenVibers/Test',name:'Test',html_url:'https://github.com/OpenVibers/Test'}]},text:async()=>'<osm><node id="1"><tag k="shop" v="books"/><tag k="name" v="Books"/></node></osm>'};};
@@ -2,6 +2,6 @@
@@ -2,6 +2,6 @@
Source-linked directories for OpenVibe sites and tools, AI models and providers, open-source projects, and local businesses from OpenStreetMap. Every entry has a source and a last checked date. Public sources are refreshed at startup and every six hours; a failed source keeps its last successful in-memory snapshot and exposes the failure in `/api/v1/kinds`.
Source-linked directories for OpenVibe sites and tools, AI models and providers, open-source projects, and local businesses from OpenStreetMap. Every entry has a source and a last checked date. Public sources are refreshed at startup and every six hours; a failed source keeps its last successful in-memory snapshot and exposes the failure in `/api/v1/kinds`.
-Business listings are a bounded San Francisco pilot sourced from OpenStreetMap under ODbL 1.0. Phone numbers appear only when published on the mapped business. Private-person searches and reverse lookups are excluded. The people directory has no imported entries and remains empty until the opt-in claim flow is available.
+Business listings are a bounded San Francisco pilot sourced from OpenStreetMap under ODbL 1.0. Unverified OpenStreetMap phone tags are omitted; a business number needs a business-owned source before publication. Private-person searches and reverse lookups are excluded. The people directory has no imported entries and remains empty until the opt-in claim flow is available.
Run `npm ci`, then `node server/index.js`. Check `/api/v1/kinds`, `/api/v1/entries`, `/sitemap.xml`, and `/llms.txt`.
Run `npm ci`, then `node server/index.js`. Check `/api/v1/kinds`, `/api/v1/entries`, `/sitemap.xml`, and `/llms.txt`.
@@ -46,8 +46,8 @@ function businesses(xml, checked) {
@@ -46,8 +46,8 @@ function businesses(xml, checked) {
const id=`${type}-${n.id}`;
const id=`${type}-${n.id}`;
const source=`https://www.openstreetmap.org/${type}/${n.id}`;
const source=`https://www.openstreetmap.org/${type}/${n.id}`;
const website=safeUrl(tags.website)||safeUrl(tags['contact:website'])||source;
const website=safeUrl(tags.website)||safeUrl(tags['contact:website'])||source;
- const phone=tags['contact:phone']||tags.phone||null;
- out.push(entry('businesses',id,tags.name,`${tags.shop||tags.amenity} in San Francisco, mapped in OpenStreetMap.`,website,source,checked,{group:tags.shop||tags.amenity,phone:phone&&String(phone).slice(0,80),osm_attribution:'© OpenStreetMap contributors, ODbL 1.0'}));
+ // OSM contact tags can be contributed by anyone; show a number only after checking a business-owned source.
+ out.push(entry('businesses',id,tags.name,`${tags.shop||tags.amenity} in San Francisco, mapped in OpenStreetMap.`,website,source,checked,{group:tags.shop||tags.amenity,osm_attribution:'© OpenStreetMap contributors, ODbL 1.0'}));
}
}
return out.filter(Boolean);
return out.filter(Boolean);
}
}
@@ -3,10 +3,10 @@ const {test}=require('node:test');
@@ -3,10 +3,10 @@ const {test}=require('node:test');
const assert=require('node:assert/strict');
const assert=require('node:assert/strict');
const {createStore,businesses}=require('../server/sources');
const {createStore,businesses}=require('../server/sources');
const {createApp}=require('../server/app');
const {createApp}=require('../server/app');
-test('business phone is only read from business OSM tags and source is attributed',()=>{
+test('unverified OSM phone tags are not published and business source is attributed',()=>{
const xml='<osm><node id="1"><tag k="shop" v="books"/><tag k="name" v="Bookshop"/><tag k="phone" v="+1 415 555 0100"/></node><node id="2"><tag k="name" v="Jane Smith"/><tag k="phone" v="+1 415 555 0101"/></node></osm>';
const xml='<osm><node id="1"><tag k="shop" v="books"/><tag k="name" v="Bookshop"/><tag k="phone" v="+1 415 555 0100"/></node><node id="2"><tag k="name" v="Jane Smith"/><tag k="phone" v="+1 415 555 0101"/></node></osm>';
const rows=businesses(xml,'2026-11-10T00:00:00.000Z');
const rows=businesses(xml,'2026-11-10T00:00:00.000Z');
- assert.equal(rows.length,1);assert.equal(rows[0].phone,'+1 415 555 0100');assert.match(rows[0].sources[0].url,/openstreetmap/);assert.match(rows[0].osm_attribution,/ODbL/);
+ assert.equal(rows.length,1);assert.equal(rows[0].phone,undefined);assert.match(rows[0].sources[0].url,/openstreetmap/);assert.match(rows[0].osm_attribution,/ODbL/);
});
});
test('failed refresh retains the last successful source and reports stale status',async()=>{
test('failed refresh retains the last successful source and reports stale status',async()=>{
let fail=false;const fetchImpl=async(url)=>{if(fail)throw Error('source down');return {ok:true,json:async()=>url.includes('frame?')?{nav:[{id:'live',name:'Live',url:'https://openvibe.live/'}]}:{tools:[{id:'yaml',name:'YAML',docs:'https://openvibe.tools/tool/yaml'}],things:[{id:'a/b',name:'B',maker:'A',url:'https://example.com'}],items:[{full_name:'OpenVibers/Test',name:'Test',html_url:'https://github.com/OpenVibers/Test'}]},text:async()=>'<osm><node id="1"><tag k="shop" v="books"/><tag k="name" v="Books"/></node></osm>'};};
let fail=false;const fetchImpl=async(url)=>{if(fail)throw Error('source down');return {ok:true,json:async()=>url.includes('frame?')?{nav:[{id:'live',name:'Live',url:'https://openvibe.live/'}]}:{tools:[{id:'yaml',name:'YAML',docs:'https://openvibe.tools/tool/yaml'}],things:[{id:'a/b',name:'B',maker:'A',url:'https://example.com'}],items:[{full_name:'OpenVibers/Test',name:'Test',html_url:'https://github.com/OpenVibers/Test'}]},text:async()=>'<osm><node id="1"><tag k="shop" v="books"/><tag k="name" v="Books"/></node></osm>'};};