The credit line said who translated the Scripture and called them "the likeliest people to ask", then stopped at a name. 838 of the 929 credited groups now reach that organisation's own homepage from the sheet. The URL does not come from eBible's homeDomain field, which is still dropped: it comes from a hand-screened list of 16 organisations, every one fetched on the run that wrote the table. Where a credit names two, the one it LEADS with wins, so "Pioneer Bible Translators and The Word for the World International" reaches Pioneer and not the other way round. National Bible Societies hold 89+ credits and are deliberately linked to none of them: several UBS members publish editions carrying the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is a ruling to be made, not one to assume. They keep the name without the link, and the count says so. A contact is screened as somewhere to write to, not as a publisher this map reads text from — two separate lists, so wycliffe.org appearing as a scripture link is still a failure. Four organisations answered a datacentre IP with a bot challenge while serving browsers normally; those are proved by robots.txt and the table records which method proved which.
82 lines
4.6 KiB
JavaScript
82 lines
4.6 KiB
JavaScript
/**
|
|
* check-translator-contacts.mjs — the credit line now LINKS the translating organisation, and
|
|
* this is the gate on that link. Static, sub-second, no browser.
|
|
*
|
|
* WHAT IT WOULD CATCH. A contact URL is a link this map stands behind, so it has to clear the
|
|
* same bar as a resource link and one more besides:
|
|
* 1. Every cr.u in the pin index is on an organisation the DOCTRINE SCREEN lists. The screen's
|
|
* pattern is read out of internal/verify-gospel-map.mjs, never re-typed here — two copies of
|
|
* an allowlist drift, and the copy that drifts is the one nobody is looking at.
|
|
* 2. Every cr.u was actually FETCHED: it must appear in translator-org-links.json as `verified`.
|
|
* An org the verifier could not reach must stay a name.
|
|
* 3. No contact host is also serving a RESOURCE. wycliffe.org is a fine place to write to and
|
|
* is not a publisher this map reads text from; if one ever appears as a scripture or film
|
|
* link, that is a new publisher arriving sideways.
|
|
* 4. The leaf RENDERS it as a link. The data can carry 838 URLs while the template shows none.
|
|
* Self-test: `--poison` injects a JW contact, an unverified org, and a leaf with the anchor
|
|
* removed, and every arm must fail.
|
|
*/
|
|
import { readFileSync } from 'node:fs'
|
|
import { resolve, dirname } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
const REPO = resolve(HERE, '..')
|
|
const POISON = process.argv.includes('--poison')
|
|
|
|
const idx = JSON.parse(readFileSync(resolve(REPO, 'data/tables/people-group-pins.json'), 'utf8'))
|
|
const orgs = JSON.parse(readFileSync(resolve(REPO, 'data/tables/translator-org-links.json'), 'utf8')).orgs || {}
|
|
const gate = readFileSync(resolve(REPO, 'internal/verify-gospel-map.mjs'), 'utf8')
|
|
let leaf = readFileSync(resolve(REPO, 'plugin-designs/gospel-resources/resources-map.vue'), 'utf8')
|
|
|
|
/* The screen, lifted from the gate rather than restated. */
|
|
const m = /^const ALLOWED_CONTACT = (\/.+\/i)$/m.exec(gate)
|
|
if (!m) { console.error('TRANSLATOR-CONTACTS: verify-gospel-map.mjs no longer declares ALLOWED_CONTACT — refusing to invent one.'); process.exit(1) }
|
|
const CONTACT = new RegExp(m[1].slice(1, -2), 'i')
|
|
const RESOURCE = /^const ALLOWED = (\/.+\/i)$/m.exec(gate)
|
|
const ALLOWED = new RegExp(RESOURCE[1].slice(1, -2), 'i')
|
|
|
|
const codes = { ...(idx.codes || {}) }
|
|
if (POISON) {
|
|
codes.__p1 = { cr: { o: 'Watchtower', u: 'https://www.jw.org/' } }
|
|
codes.__p2 = { cr: { o: 'Some Org', u: 'https://this-was-never-fetched.example/' } }
|
|
codes.__p3 = { s: 'https://www.wycliffe.org/', cr: { o: 'x' } }
|
|
leaf = leaf.replace(/class="gm-org"/g, 'class="gm-plain"')
|
|
}
|
|
|
|
const verified = new Set(Object.values(orgs).filter((o) => o.state === 'verified').map((o) => o.url))
|
|
const fails = []
|
|
let contacts = 0
|
|
const contactHosts = new Set()
|
|
for (const [c, v] of Object.entries(codes)) {
|
|
const u = v.cr && v.cr.u
|
|
if (u) {
|
|
contacts++
|
|
contactHosts.add(new URL(u).host.replace(/^www\./, ''))
|
|
if (!CONTACT.test(u)) fails.push(`${c}: contact ${u} is not on the screened organisation list`)
|
|
if (!verified.has(u)) fails.push(`${c}: contact ${u} is not recorded as verified in translator-org-links.json`)
|
|
}
|
|
}
|
|
for (const [c, v] of Object.entries(codes)) {
|
|
for (const u of [v.s, v.f, v.z, v.au && v.au.u, v.bv && v.bv.u, v.ga && v.ga.u, v.se && (v.se.u || v.se)]) {
|
|
if (!u) continue
|
|
if (CONTACT.test(u) && !ALLOWED.test(u)) fails.push(`${c}: ${u} is a CONTACT host serving a resource link`)
|
|
}
|
|
}
|
|
if (!contacts) fails.push('no contact URL in the index at all — this check would pass vacuously')
|
|
if (!/class="gm-org"/.test(leaf)) fails.push('resources-map.vue renders no .gm-org anchor — the data carries contacts the sheet never shows')
|
|
if (!/credit\.u/.test(leaf)) fails.push('resources-map.vue never reads credit.u')
|
|
|
|
if (POISON) {
|
|
const want = ['not on the screened organisation list', 'not recorded as verified', 'CONTACT host serving a resource link', 'renders no .gm-org anchor']
|
|
const missed = want.filter((w) => !fails.some((f) => f.includes(w)))
|
|
if (missed.length) { console.error(`TRANSLATOR-CONTACTS --poison: these arms did NOT fire: ${missed.join(' | ')}`); process.exit(1) }
|
|
console.log(`TRANSLATOR-CONTACTS --poison: PASS — all 4 arms fired (${fails.length} failure(s)).`)
|
|
process.exit(0)
|
|
}
|
|
if (fails.length) {
|
|
console.error(`TRANSLATOR-CONTACTS: FAIL — ${fails.length} problem(s)`)
|
|
for (const f of fails.slice(0, 12)) console.error(` ${f}`)
|
|
process.exit(1)
|
|
}
|
|
console.log(`TRANSLATOR-CONTACTS: PASS — ${contacts} credit(s) link an organisation, across ${contactHosts.size} screened host(s); each one verified by fetch and rendered as a link.`)
|