Name the translator, and let a mobiliser write to them
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.
This commit is contained in:
parent
932bf553c7
commit
27bc18a6e1
18 changed files with 930 additions and 45 deletions
File diff suppressed because one or more lines are too long
290
data/tables/translator-org-links.json
Normal file
290
data/tables/translator-org-links.json
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
{
|
||||
"meta": {
|
||||
"title": "Who to contact — screened, fetched homepages for the organisations named in the credits",
|
||||
"generated": "2026-09-26",
|
||||
"grain": "one entry per organisation, matched to a credit string by the regexes it carries",
|
||||
"verified_orgs": 16,
|
||||
"orgs_that_did_not_answer": 0,
|
||||
"states": {
|
||||
"verified": "this script fetched it on the run that wrote this file — homepage 200, or robots.txt 200 where a bot challenge answers a datacentre IP",
|
||||
"unknown": "did not answer this run; NOT shipped, and never read as broken"
|
||||
},
|
||||
"not_linked": [
|
||||
{
|
||||
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the Driver's ruling, not this script's"
|
||||
},
|
||||
{
|
||||
"reason": "doctrine not established from published material — left out rather than guessed"
|
||||
}
|
||||
],
|
||||
"note": "Hand-screened allowlist. eBible's own homeDomain/publicationURL fields are deliberately NOT used: a refresh of someone else's catalogue must not be able to put a publisher on this map."
|
||||
},
|
||||
"held": [
|
||||
{
|
||||
"pattern": "bible society|biblical society|bibelselskab|bibelsellskap|soci(e|é)t(y|é) b[ií]blica|sociedad b[ií]blica|lembaga alkitab|united bible societies|sirk[əe]ti|bible societies",
|
||||
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the Driver's ruling, not this script's"
|
||||
},
|
||||
{
|
||||
"pattern": "artists for israel|k[äa]rnbibeln|sanskritbible|gilakmedia|tarifit|gSungrab|audiobiblia",
|
||||
"reason": "doctrine not established from published material — left out rather than guessed"
|
||||
}
|
||||
],
|
||||
"orgs": {
|
||||
"wycliffe-ca": {
|
||||
"name": "Wycliffe Bible Translators of Canada",
|
||||
"url": "https://www.wycliffe.ca/",
|
||||
"why": "Bible translation agency, Canadian office of the Wycliffe Global Alliance.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 2084732 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "wycliffe[^,]*\\bcanada\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wycliffe-au": {
|
||||
"name": "Wycliffe Bible Translators Australia",
|
||||
"url": "https://wycliffe.org.au/",
|
||||
"why": "Bible translation agency, Australian office of the Wycliffe Global Alliance.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 87857 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "wycliffe[^,]*australian?\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wycliffe": {
|
||||
"name": "Wycliffe Bible Translators",
|
||||
"url": "https://www.wycliffe.org/",
|
||||
"why": "Bible translation agency; translates the 66-book canon, no added scripture.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 367138 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bwycliffe\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"twftw": {
|
||||
"name": "The Word for the World",
|
||||
"url": "https://www.twftw.org/",
|
||||
"why": "Bible translation agency working through national translators.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 1145398 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "word for the world",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "\\bTWFTW\\b",
|
||||
"f": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"biblica": {
|
||||
"name": "Biblica",
|
||||
"url": "https://www.biblica.com/",
|
||||
"why": "Bible publisher and translator (NIV); 66-book canon.",
|
||||
"state": "verified",
|
||||
"method": "robots-txt",
|
||||
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 292 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bbiblica\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pioneer": {
|
||||
"name": "Pioneer Bible Translators",
|
||||
"url": "https://pioneerbible.org/",
|
||||
"why": "Bible translation agency working in languages with no Scripture.",
|
||||
"state": "verified",
|
||||
"method": "robots-txt",
|
||||
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 120 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "pioneer bible translators",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "traducteurs pionniers de la bible",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "\\bPBT\\b",
|
||||
"f": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"seed": {
|
||||
"name": "The Seed Company",
|
||||
"url": "https://seedcompany.com/",
|
||||
"why": "Bible translation agency, affiliate of Wycliffe USA.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 371615 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "seed company",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sim": {
|
||||
"name": "SIM",
|
||||
"url": "https://www.sim.org/",
|
||||
"why": "Evangelical mission agency; translation among its field work.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 190863 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bSIM\\b",
|
||||
"f": ""
|
||||
},
|
||||
{
|
||||
"s": "society for international ministries",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bible-league": {
|
||||
"name": "Bible League International",
|
||||
"url": "https://www.bibleleague.org/",
|
||||
"why": "Scripture publisher; absorbed the World Bible Translation Center, whose name the credits still carry.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 701341 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "world bible translation cent(er|re)",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "bible league",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "liga b[ií]blica",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"unfoldingword": {
|
||||
"name": "Door43 / unfoldingWord",
|
||||
"url": "https://door43.org/",
|
||||
"why": "Open-licensed Bible translation resources; the Door43 community platform.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 7465 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "door\\s?43",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "unfoldingword",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bridgeconn": {
|
||||
"name": "Bridge Connectivity Solutions",
|
||||
"url": "https://www.bridgeconn.com/",
|
||||
"why": "Indian Bible-translation technology and translation services group.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 20334 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "bridge conn?ectivity",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "bridge communication systems",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"btl-kenya": {
|
||||
"name": "Bible Translation and Literacy",
|
||||
"url": "https://www.btlkenya.org/",
|
||||
"why": "Kenyan Bible translation agency, Wycliffe Global Alliance member.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 76225 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "bible translation and literacy",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"kartidaya": {
|
||||
"name": "Yayasan Kartidaya",
|
||||
"url": "https://kartidaya.org/",
|
||||
"why": "Indonesian Bible translation agency, Wycliffe Global Alliance member.",
|
||||
"state": "verified",
|
||||
"method": "robots-txt",
|
||||
"detail": "homepage HTTP 429 (bot challenge from a datacentre IP), robots.txt HTTP 200, 319 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "kartidaya",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"liebenzell": {
|
||||
"name": "Liebenzell Mission",
|
||||
"url": "https://www.liebenzell.org/",
|
||||
"why": "German evangelical mission agency (Free Church tradition).",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 1281367 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "liebenzel+l? mission",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sil": {
|
||||
"name": "SIL Global",
|
||||
"url": "https://www.sil.org/",
|
||||
"why": "Language development organisation; the linguistic partner behind much of this corpus.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 415008 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bSIL\\b",
|
||||
"f": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"lockman": {
|
||||
"name": "The Lockman Foundation",
|
||||
"url": "https://www.lockman.org/",
|
||||
"why": "Scripture publisher (NASB, LBLA); 66-book canon.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 163046 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "lockman foundation",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
82
internal/check-translator-contacts.mjs
Normal file
82
internal/check-translator-contacts.mjs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* 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.`)
|
||||
|
|
@ -64,6 +64,15 @@ const GL = ['--use-gl=swiftshader', '--enable-unsafe-swiftshader', '--ignore-gpu
|
|||
// nothing about the links nobody looks at.
|
||||
const ALLOWED = /^(https:\/\/)?([a-z0-9-]+\.)*(ebible\.org|png\.bible|alkitab\.pw|git\.door43\.org|jesusfilm\.org|arc\.gt|zume\.training|bible\.is|knowgod\.com|sun\.bible|pray\.doxa\.life|fivefish\.org|scriptureearth\.org)(\/|$)/i
|
||||
|
||||
/* ★ A CONTACT IS NOT A PUBLISHER, AND THE TWO LISTS MUST NOT MERGE (2026-09-26). The credit line
|
||||
now links the translating organisation, so cr.u is a FIFTH place a URL lives in the index — the
|
||||
omission class this file already warns about, caught on the build that introduced it. These
|
||||
hosts are screened as organisations a mobiliser may CONTACT, not as publishers whose text this
|
||||
map serves, and they are spelled out here rather than read from the data file they came from:
|
||||
a screen that trusts the file it is screening is not a screen. A contact host is therefore
|
||||
still a failure if it ever appears as a resource link, because ALLOWED does not list it. */
|
||||
const ALLOWED_CONTACT = /^https:\/\/([a-z0-9-]+\.)*(wycliffe\.org|wycliffe\.ca|wycliffe\.org\.au|twftw\.org|biblica\.com|pioneerbible\.org|seedcompany\.com|sim\.org|bibleleague\.org|door43\.org|bridgeconn\.com|btlkenya\.org|kartidaya\.org|liebenzell\.org|sil\.org|lockman\.org)\/?$/i
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url))
|
||||
const REPO = resolve(HERE, '..')
|
||||
const EXPLICIT = !!(process.env.BASE_URL || process.argv[2])
|
||||
|
|
@ -122,6 +131,7 @@ const PAGE_URL = `${base}/${TREE_NAME}/gospel-resources/resources-map/index.html
|
|||
|
||||
const fails = []
|
||||
let linkCount = 0
|
||||
let contactCount = 0
|
||||
let exitCode = 0
|
||||
const browser = await chromium.launch({ headless: true, args: GL })
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } })
|
||||
|
|
@ -201,8 +211,8 @@ try {
|
|||
if (!idxRes.ok) fails.push(`pin index not served (${idxRes.status})`)
|
||||
else {
|
||||
const idx = await idxRes.json()
|
||||
const bad = []
|
||||
let scanned = 0
|
||||
const bad = [], contactBad = []
|
||||
let scanned = 0, contacts = 0
|
||||
for (const [c, v] of Object.entries(idx.codes || {})) {
|
||||
/* ⚠ ga (Words of Life) and se (Scripture Earth) were absent from this list, so every link
|
||||
from those two pools went unscreened — the doctrine boundary is the one thing that must
|
||||
|
|
@ -213,6 +223,13 @@ try {
|
|||
scanned++
|
||||
if (!ALLOWED.test(u)) bad.push(`${c} -> ${u}`)
|
||||
}
|
||||
/* The translator contact, screened against the OTHER list. Counted apart so "every link is
|
||||
on a screened publisher" keeps meaning exactly that. */
|
||||
const cu = v.cr && v.cr.u
|
||||
if (cu) {
|
||||
contacts++
|
||||
if (!ALLOWED_CONTACT.test(cu)) contactBad.push(`${c} -> ${cu}`)
|
||||
}
|
||||
}
|
||||
/* ★ THE SUN BLOCK IS A THIRD PLACE URLS LIVE (2026-09-25). The Deaf bridge is carried as a
|
||||
single top-level object rather than per-language under codes[], because SUN is one signed
|
||||
|
|
@ -225,6 +242,8 @@ try {
|
|||
}
|
||||
if (!scanned) fails.push('pin index carries no links at all — this check would pass vacuously')
|
||||
if (bad.length) fails.push(`${bad.length} link(s) on an unscreened publisher (e.g. ${bad[0]})`)
|
||||
if (contactBad.length) fails.push(`${contactBad.length} translator contact(s) on an unscreened organisation (e.g. ${contactBad[0]})`)
|
||||
contactCount = contacts
|
||||
linkCount = scanned
|
||||
|
||||
// ★ THE ACCESS TABLE IS THE OTHER HALF, AND IT WAS NEVER SCREENED (2026-09-25).
|
||||
|
|
@ -319,6 +338,7 @@ try {
|
|||
} else {
|
||||
console.log(`GOSPEL-MAP: PASS — ${st.pins} pins rendered; "${opened.pg}" (${opened.lang}) opens with`)
|
||||
console.log(` a named language; all ${linkCount} links in the index are on screened publishers;`)
|
||||
console.log(` ${contactCount} translator contact(s) are on screened organisations;`)
|
||||
console.log(` the table link carries iso=${opened.iso}; the gap filter`)
|
||||
console.log(` cuts ${filtered.before} features to ${filtered.after}, all tier 0.`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@
|
|||
"check:gallery-embed:maplibre": "node internal/check-research-gallery-embed.mjs --provider maplibre",
|
||||
"check:gospel-all": "node internal/check-gospel-all.mjs",
|
||||
"check:gospel-tables-staged": "node internal/check-gospel-tables-staged.mjs",
|
||||
"check:translator-contacts": "node internal/check-translator-contacts.mjs",
|
||||
"check:translator-contacts:poison": "node internal/check-translator-contacts.mjs --poison",
|
||||
"check:heartbeat": "node internal/check-heartbeat-threshold.mjs",
|
||||
"check:heartbeat:self-test": "node internal/check-heartbeat-threshold.mjs --self-test",
|
||||
"check:host-locale-contract": "node internal/check-host-locale-contract.mjs",
|
||||
|
|
@ -343,6 +345,7 @@
|
|||
"check:shot-fidelity": "warns when THIS machine cannot RENDER what the shots must SHOW. `bun run shots` printed 13x OK while every emoji rendered as a tofu box — a glyph fallback loads perfectly (0 console errors, 0 HTTP>=400), so the generator cannot see it. Reports by default; the `check:shot-fidelity:strict` script to block once an emoji font is installed.",
|
||||
"check:shot-fonts": "can THIS machine render the emoji the UI uses? Run BEFORE `bun run shots` — no font = tofu boxes in every committed PNG, which every other shot gate passes. Deliberately NOT in build:unlocked: it reports an ENVIRONMENT condition (this box has no emoji font), and a gate that always fails in the build makes the build useless.",
|
||||
"check:shots": "a README screenshot that predates the bundle it shows is a picture telling a lie — and it looks IDENTICAL to a current one. Compares each shot's PNG against the BUILT BUNDLE it renders (not raw sources: an unbuilt edit is not yet in any leaf). Fix: node internal/readme-shots.mjs",
|
||||
"check:translator-contacts": "the credit line LINKS the translating organisation now, and a contact URL is a link this map stands behind. Reads ALLOWED_CONTACT out of internal/verify-gospel-map.mjs rather than restating it (two copies of an allowlist drift, and the drifting copy is the one nobody reads), then asserts every cr.u is on that list, was recorded `verified` by a real fetch, is never also serving a RESOURCE (wycliffe.org is somewhere to write to, not a publisher this map reads text from), and is actually RENDERED as an anchor \u2014 the data can carry 838 URLs while the template shows none. Static, sub-second. Self-test: `bun run check:translator-contacts:poison`.",
|
||||
"check:snapshot-guard": "every gospel-resources catalogue fetcher must write through scripts/_snapshot-guard.mjs, which refuses a write that loses more than a tenth of the previous snapshot. Written after a rate-limited FCBH run silently replaced 1,666 video scopes with 1,103. Static, sub-second. Self-test: `bun run check:snapshot-guard:poison` injects a raw write and the check must fail on it.",
|
||||
"check:spa-tab-order": "the cap-5 SPA must open on Big Picture and keep the Driver's order (Big Picture, UPG-100, Prayer Progress, Gospel Resources) — asserted as PRESENCE + tab-1 + RELATIVE order on the RENDERED rail, because the rail gained three sections and now renders 12 rows with others between them. ⚠ NOT WAVE-WIRED: it FAILS rather than skips when the SPA is unreachable, deliberately (\"a gate that cannot reach its subject has not passed, it has not run\") — correct standalone, but gates:post runs on checkouts with no server. Needs `bun run serve` on 4173.",
|
||||
"check:stale-sweep-claim": "a caveat that has stopped being true is a wrong number. The gospel surfaces carry a paragraph saying which pools were searched; while the GRN sweep ran they truthfully said part of it was unread, the sweep finished, and the sentence stayed for four more commits. The DATA decides here: undecided gap codes mean the claim is allowed, all-decided means it must go, so it re-arms itself for the next slow pool. Does NOT flag a claim rendered under a v-if on the pending count — that form expires on its own. Self-test: `--poison`. Static, sub-second.",
|
||||
|
|
|
|||
|
|
@ -1661,6 +1661,12 @@ useShadowStyles(
|
|||
the way in is the footnote to it, not the headline. */
|
||||
'.gm-pg-way{display:block;font-size:12.5px;color:#3b463d;opacity:.8;margin-top:2px}' +
|
||||
'.gm-credit{font-size:12.5px;color:#6d766e;line-height:1.35}' +
|
||||
/* The contact is a LINK inside a grey credit line, so it carries the ink colour and an
|
||||
underline of its own — grey-on-grey would hide the one part of the sentence a
|
||||
mobiliser is meant to act on. */
|
||||
'.gm-org{color:#3b463d;text-decoration:underline;text-underline-offset:2px}' +
|
||||
'.gm-org:hover{color:#2c352e}' +
|
||||
'.is-dark .gm-org{color:#f3f3f1}' +
|
||||
'.is-dark .gm-credit{color:#d1d4cd}' +
|
||||
'.is-dark .gm-pg-way{color:#d1d4cd}' +
|
||||
'.gm-prose-note{opacity:.9;font-weight:600}' +
|
||||
|
|
@ -2374,7 +2380,9 @@ useShadowStyles(
|
|||
{{ t('gospel.sheet.everyResource') }} {{ curatedBridgeRes.language }} →</a>
|
||||
</div>
|
||||
<p v-if="curatedBridgeRes && curatedBridgeRes.credit" class="gm-credit" dir="ltr" style="margin:4px 0 0">
|
||||
The {{ curatedBridgeRes.language }} Scripture was translated by <b>{{ curatedBridgeRes.credit.o }}</b><template
|
||||
The {{ curatedBridgeRes.language }} Scripture was translated by <a v-if="curatedBridgeRes.credit.u"
|
||||
class="gm-org" :href="curatedBridgeRes.credit.u" target="_blank" rel="noopener noreferrer"><b>{{
|
||||
curatedBridgeRes.credit.o }}</b></a><b v-else>{{ curatedBridgeRes.credit.o }}</b><template
|
||||
v-if="curatedBridgeRes.credit.y">, {{ curatedBridgeRes.credit.y }}</template> — the likeliest people to ask
|
||||
about this language next.
|
||||
</p>
|
||||
|
|
@ -2463,7 +2471,9 @@ useShadowStyles(
|
|||
{{ t('gospel.sheet.everyResource') }} {{ candidateBridgeRes.language }} →</a>
|
||||
</div>
|
||||
<p v-if="candidateBridgeRes && candidateBridgeRes.credit" class="gm-credit" dir="ltr" style="margin:4px 0 0">
|
||||
The {{ candidateBridgeRes.language }} Scripture was translated by <b>{{ candidateBridgeRes.credit.o }}</b><template
|
||||
The {{ candidateBridgeRes.language }} Scripture was translated by <a v-if="candidateBridgeRes.credit.u"
|
||||
class="gm-org" :href="candidateBridgeRes.credit.u" target="_blank" rel="noopener noreferrer"><b>{{
|
||||
candidateBridgeRes.credit.o }}</b></a><b v-else>{{ candidateBridgeRes.credit.o }}</b><template
|
||||
v-if="candidateBridgeRes.credit.y">, {{ candidateBridgeRes.credit.y }}</template> — the likeliest people to ask
|
||||
about this language next.
|
||||
</p>
|
||||
|
|
@ -2475,12 +2485,15 @@ useShadowStyles(
|
|||
<!-- ═══ WHO TO ASK, NOT JUST WHERE TO LOOK ══════════════════════════════════════
|
||||
A link answers where the Scripture is. The organisation that produced it is
|
||||
the one with the linguists, the relationships and very often a team already in
|
||||
that country — which is what a mobiliser actually needs next. A NAME and not a
|
||||
link: the catalogue carries the publisher's domain too, and shipping it would
|
||||
put a host into this map that the doctrine screen has never seen. -->
|
||||
that country — which is what a mobiliser actually needs next, so the name is a
|
||||
LINK where one exists. Not eBible's own homeDomain field, which is still dropped:
|
||||
the URL comes from data/tables/translator-org-links.json, a hand-screened list
|
||||
whose every entry the build fetched. An org with no screened URL stays a name. -->
|
||||
<template v-if="selectedGroup.credit">
|
||||
<br><span class="gm-credit" dir="ltr">Translated by {{ selectedGroup.credit.o
|
||||
}}<template v-if="selectedGroup.credit.y">, {{ selectedGroup.credit.y }}</template></span>
|
||||
<br><span class="gm-credit" dir="ltr">Translated by <a v-if="selectedGroup.credit.u"
|
||||
class="gm-org" :href="selectedGroup.credit.u" target="_blank" rel="noopener noreferrer">{{
|
||||
selectedGroup.credit.o }}</a><template v-else>{{ selectedGroup.credit.o }}</template><template
|
||||
v-if="selectedGroup.credit.y">, {{ selectedGroup.credit.y }}</template></span>
|
||||
</template>
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -990,17 +990,47 @@ try {
|
|||
} catch { /* no SUN index in this tree: the Deaf panel stays as it was */ }
|
||||
|
||||
/**
|
||||
* WHO TRANSLATED IT. Attribution for Scripture already in the index — a name, never a new link,
|
||||
* so no publisher can enter this map through a data refresh without passing the doctrine screen.
|
||||
* It adds no resource and moves no tier; note it is attached AFTER tierOfCode is defined and
|
||||
* BEFORE nothing, precisely because it must not participate in tiering.
|
||||
* WHO TRANSLATED IT — AND HOW TO REACH THEM. Attribution for Scripture already in the index. The
|
||||
* NAME still comes from eBible exactly as eBible wrote it; the CONTACT URL comes only from
|
||||
* data/tables/translator-org-links.json, a hand-screened allowlist whose every entry this build
|
||||
* fetched. eBible's own homeDomain field is still not used, so no publisher can enter this map
|
||||
* through a data refresh without passing the doctrine screen.
|
||||
* It adds no resource and moves no tier; it is attached AFTER tierOfCode is defined precisely
|
||||
* because it must not participate in tiering.
|
||||
*/
|
||||
let credited = 0
|
||||
let credited = 0, contactable = 0
|
||||
let orgLinks = {}
|
||||
try {
|
||||
const t = JSON.parse(readFileSync(resolve(REPO, 'data/tables/translator-org-links.json'), 'utf8'))
|
||||
for (const [id, o] of Object.entries(t.orgs || {})) {
|
||||
/* Only what the verifier actually reached this run. `unknown` is not shipped and is not an
|
||||
error either — the credit simply keeps naming the org without a link. */
|
||||
if (o.state !== 'verified') continue
|
||||
orgLinks[id] = { url: o.url, match: (o.match || []).map((m) => new RegExp(m.s, m.f || '')) }
|
||||
}
|
||||
} catch { /* no org table: credits stay names, which is what they were before */ }
|
||||
|
||||
/* THE EARLIEST NAME IN THE STRING WINS, not the first pattern in the file. "Pioneer Bible
|
||||
Translators and The Word for the World International" is led by Pioneer, and a credit's lead
|
||||
organisation is the one to ask; ordering by the allowlist instead would have answered TWFTW. */
|
||||
const orgFor = (credit) => {
|
||||
let best = null
|
||||
for (const [id, o] of Object.entries(orgLinks)) {
|
||||
for (const re of o.match) {
|
||||
const m = re.exec(credit)
|
||||
if (m && (!best || m.index < best.at)) best = { id, url: o.url, at: m.index }
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
try {
|
||||
const cr = JSON.parse(readFileSync(resolve(REPO, 'data/tables/translation-credits.json'), 'utf8')).byCode || {}
|
||||
for (const [code, v] of Object.entries(cr)) {
|
||||
if (!codes[code] || !v.org) continue
|
||||
codes[code].cr = v.year ? { o: v.org, y: v.year } : { o: v.org }
|
||||
const org = orgFor(v.org)
|
||||
if (org) { codes[code].cr.u = org.url; codes[code].cr.g = org.id; contactable++ }
|
||||
credited++
|
||||
}
|
||||
} catch { /* no credits table: the sheet simply does not name a translator */ }
|
||||
|
|
@ -1041,7 +1071,7 @@ console.log(`arclight codes ${Object.keys(arc).length} (${arcOnly} new, ${upgr
|
|||
console.log(`share urls ${Object.keys(share).length}`)
|
||||
console.log(`fcbh codes ${audioCodes} (${audioNew} new codes)`)
|
||||
console.log(`gospel tracts ${gospelCodes} (Cru/GodTools presentations; depth only, tier UNCHANGED)`)
|
||||
console.log(`translators named ${credited}\t(attribution only; no new link, no tier change)`)
|
||||
console.log(`translators named ${credited}\t(${contactable} reach a screened, fetched organisation homepage; no tier change)`)
|
||||
console.log(`tiers published ${Object.keys(tiers).length}\t(one per code, so a language with no pin still has a tier)`)
|
||||
console.log(`bridge candidates ${Object.keys(bridgeCandidates).length} (computed here so the map sheet and the access table cannot disagree)`)
|
||||
console.log(`in-country bridge ${bridgeRows} (groups with NOTHING of their own; a widely-spoken language around them has something — never counted as theirs)`)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* verify-translator-org-links.mjs — turn the translator CREDIT into someone a mobiliser can reach.
|
||||
*
|
||||
* WHY. fetch-ebible-credits.mjs already answers "who translated this" and the sheet already says
|
||||
* those are "the likeliest people to ask" — but it names them and stops. A name is not a contact.
|
||||
* The mobiliser reading a zero-access group is one step from the organisation that has linguists,
|
||||
* relationships and very often a field team in that country, and the map makes them go and find it.
|
||||
*
|
||||
* ⚠ WHY THIS IS A HAND-WRITTEN ALLOWLIST AND NOT THE CATALOGUE'S OWN FIELD. eBible ships
|
||||
* homeDomain and publicationURL on every row, and fetch-ebible-credits.mjs deliberately drops
|
||||
* both: a refresh of somebody else's catalogue must never be able to put a new publisher on this
|
||||
* map, because the doctrine screen is an allowlist for exactly that reason. So the URL comes from
|
||||
* here, where every entry was screened by hand, or it does not ship at all.
|
||||
*
|
||||
* ⚠ WHAT IS DELIBERATELY ABSENT. National Bible Societies hold 89+ of the credits and NONE of
|
||||
* them is linked here. Not doubt about the Societies — the brief's filter is "only the 66-book
|
||||
* canon", and several UBS members publish editions that include the deuterocanonicals, so
|
||||
* whether a Society CONTACT is inside or outside that line is the Driver's ruling to make and not
|
||||
* this script's. They stay named-without-a-link until it is made, and the count is reported so the
|
||||
* gap is visible rather than quiet. Same for the orgs whose doctrine I could not establish: they
|
||||
* are listed in `unscreened` with the reason, never linked.
|
||||
*
|
||||
* ⚠ VERIFIED MEANS FETCHED. Every URL below was fetched by this script on the run that wrote the
|
||||
* table. Several of these hosts sit behind a bot challenge that answers a datacentre IP with 403
|
||||
* or 429 while serving a real site to a browser; for those the check falls to robots.txt, which
|
||||
* those same hosts answer, and the state records WHICH method proved it. Both controls run in band
|
||||
* and the script refuses to write if either misbehaves.
|
||||
*/
|
||||
import { writeSnapshot } from './_snapshot-guard.mjs'
|
||||
import { resolve, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url))
|
||||
const REPO = resolve(HERE, '../../..')
|
||||
const OUT = resolve(REPO, 'data/tables/translator-org-links.json')
|
||||
const UA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36'
|
||||
|
||||
/* ── THE ALLOWLIST ────────────────────────────────────────────────────────────────────────────
|
||||
`match` is tested against the credit string as eBible wrote it, in order, first hit wins — so
|
||||
the country arms of a family come BEFORE the family ("Wycliffe Bible Translators of Canada"
|
||||
must not resolve to the US office). `why` is the doctrine note: what this organisation is, in
|
||||
one line, so the screen can be re-read by a human without leaving the file. */
|
||||
const ORGS = [
|
||||
{ id: 'wycliffe-ca', name: 'Wycliffe Bible Translators of Canada', url: 'https://www.wycliffe.ca/',
|
||||
match: [/wycliffe[^,]*\bcanada\b/i], why: 'Bible translation agency, Canadian office of the Wycliffe Global Alliance.' },
|
||||
{ id: 'wycliffe-au', name: 'Wycliffe Bible Translators Australia', url: 'https://wycliffe.org.au/',
|
||||
match: [/wycliffe[^,]*australian?\b/i], why: 'Bible translation agency, Australian office of the Wycliffe Global Alliance.' },
|
||||
{ id: 'wycliffe', name: 'Wycliffe Bible Translators', url: 'https://www.wycliffe.org/',
|
||||
match: [/\bwycliffe\b/i], why: 'Bible translation agency; translates the 66-book canon, no added scripture.' },
|
||||
{ id: 'twftw', name: 'The Word for the World', url: 'https://www.twftw.org/',
|
||||
match: [/word for the world/i, /\bTWFTW\b/], why: 'Bible translation agency working through national translators.' },
|
||||
{ id: 'biblica', name: 'Biblica', url: 'https://www.biblica.com/',
|
||||
match: [/\bbiblica\b/i], why: 'Bible publisher and translator (NIV); 66-book canon.' },
|
||||
{ id: 'pioneer', name: 'Pioneer Bible Translators', url: 'https://pioneerbible.org/',
|
||||
match: [/pioneer bible translators/i, /traducteurs pionniers de la bible/i, /\bPBT\b/],
|
||||
why: 'Bible translation agency working in languages with no Scripture.' },
|
||||
{ id: 'seed', name: 'The Seed Company', url: 'https://seedcompany.com/',
|
||||
match: [/seed company/i], why: 'Bible translation agency, affiliate of Wycliffe USA.' },
|
||||
{ id: 'sim', name: 'SIM', url: 'https://www.sim.org/',
|
||||
match: [/\bSIM\b/, /society for international ministries/i], why: 'Evangelical mission agency; translation among its field work.' },
|
||||
{ id: 'bible-league', name: 'Bible League International', url: 'https://www.bibleleague.org/',
|
||||
match: [/world bible translation cent(er|re)/i, /bible league/i, /liga b[ií]blica/i],
|
||||
why: 'Scripture publisher; absorbed the World Bible Translation Center, whose name the credits still carry.' },
|
||||
/* The credit strings name Door43 itself ("Door43 World Missions Community"), and door43.org
|
||||
answers a plain GET while unfoldingword.org rate-limits a datacentre IP — so the link goes
|
||||
to the platform the credit actually names. */
|
||||
{ id: 'unfoldingword', name: 'Door43 / unfoldingWord', url: 'https://door43.org/',
|
||||
match: [/door\s?43/i, /unfoldingword/i], why: 'Open-licensed Bible translation resources; the Door43 community platform.' },
|
||||
{ id: 'bridgeconn', name: 'Bridge Connectivity Solutions', url: 'https://www.bridgeconn.com/',
|
||||
match: [/bridge conn?ectivity/i, /bridge communication systems/i],
|
||||
why: 'Indian Bible-translation technology and translation services group.' },
|
||||
{ id: 'btl-kenya', name: 'Bible Translation and Literacy', url: 'https://www.btlkenya.org/',
|
||||
match: [/bible translation and literacy/i], why: 'Kenyan Bible translation agency, Wycliffe Global Alliance member.' },
|
||||
{ id: 'kartidaya', name: 'Yayasan Kartidaya', url: 'https://kartidaya.org/',
|
||||
match: [/kartidaya/i], why: 'Indonesian Bible translation agency, Wycliffe Global Alliance member.' },
|
||||
{ id: 'liebenzell', name: 'Liebenzell Mission', url: 'https://www.liebenzell.org/',
|
||||
match: [/liebenzel+l? mission/i], why: 'German evangelical mission agency (Free Church tradition).' },
|
||||
{ id: 'sil', name: 'SIL Global', url: 'https://www.sil.org/',
|
||||
match: [/\bSIL\b/], why: 'Language development organisation; the linguistic partner behind much of this corpus.' },
|
||||
{ id: 'lockman', name: 'The Lockman Foundation', url: 'https://www.lockman.org/',
|
||||
match: [/lockman foundation/i], why: 'Scripture publisher (NASB, LBLA); 66-book canon.' },
|
||||
]
|
||||
|
||||
/* Credits that are NOT linked, and why. Counted in the table so the silence is legible. */
|
||||
const HELD = [
|
||||
{ pattern: /bible society|biblical society|bibelselskab|bibelsellskap|soci(e|é)t(y|é) b[ií]blica|sociedad b[ií]blica|lembaga alkitab|united bible societies|sirk[əe]ti|bible societies/i,
|
||||
reason: 'national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the Driver\'s ruling, not this script\'s' },
|
||||
{ pattern: /artists for israel|k[äa]rnbibeln|sanskritbible|gilakmedia|tarifit|gSungrab|audiobiblia/i,
|
||||
reason: 'doctrine not established from published material — left out rather than guessed' },
|
||||
]
|
||||
|
||||
const fetchOnce = async (url) => {
|
||||
try {
|
||||
const r = await fetch(url, { headers: { 'user-agent': UA, accept: 'text/html,*/*' }, redirect: 'follow', signal: AbortSignal.timeout(25000) })
|
||||
const body = await r.text()
|
||||
return { status: r.status, final: r.url, bytes: body.length, body }
|
||||
} catch (e) { return { status: 0, final: url, bytes: 0, body: '', error: String(e.message || e) } }
|
||||
}
|
||||
const isRobots = (b) => /(^|\n)\s*(user-agent|sitemap)\s*:/i.test(b) && b.length < 20000
|
||||
|
||||
/** Homepage first. If a bot challenge answers instead, robots.txt on the same host is the fallback. */
|
||||
const verify = async (url) => {
|
||||
const home = await fetchOnce(url)
|
||||
if (home.status === 200 && home.bytes > 400) return { state: 'verified', method: 'homepage-get', detail: `HTTP 200, ${home.bytes} bytes`, final: home.final }
|
||||
const robots = await fetchOnce(new URL('/robots.txt', url).href)
|
||||
if (robots.status === 200 && isRobots(robots.body)) {
|
||||
return { state: 'verified', method: 'robots-txt', detail: `homepage HTTP ${home.status} (bot challenge from a datacentre IP), robots.txt HTTP 200, ${robots.bytes} bytes`, final: home.final }
|
||||
}
|
||||
return { state: 'unknown', method: 'none', detail: `homepage HTTP ${home.status}${home.error ? ` (${home.error})` : ''}, robots.txt HTTP ${robots.status}`, final: home.final }
|
||||
}
|
||||
|
||||
/* ── CONTROLS, IN BAND ─────────────────────────────────────────────────────────────────────── */
|
||||
const posc = await verify('https://seedcompany.com/')
|
||||
if (posc.state !== 'verified') { console.error(`ORG-LINKS: positive control failed (${posc.detail}) — a network fault would write every org off as unreachable. Refusing.`); process.exit(1) }
|
||||
const negc = await verify('https://this-org-does-not-exist-doxa-maps.example/')
|
||||
if (negc.state === 'verified') { console.error('ORG-LINKS: negative control PASSED verification — the check cannot fail, so it proves nothing. Refusing.'); process.exit(1) }
|
||||
const robotsc = await fetchOnce('https://www.biblica.com/definitely-not-robots-xyzzy.txt')
|
||||
if (robotsc.status === 200 && isRobots(robotsc.body)) { console.error('ORG-LINKS: the robots fallback accepts a path that is not robots.txt. Refusing.'); process.exit(1) }
|
||||
console.log(`controls: positive ${posc.method}, negative "${negc.state}", robots-shape rejected non-robots path`)
|
||||
|
||||
const out = {}
|
||||
let verified = 0, unknown = 0
|
||||
for (const o of ORGS) {
|
||||
const v = await verify(o.url)
|
||||
if (v.state === 'verified') verified++; else unknown++
|
||||
out[o.id] = { name: o.name, url: o.url, why: o.why, state: v.state, method: v.method, detail: v.detail,
|
||||
/* source + flags as two fields, not a string with a suffix: a pattern is reconstructed by
|
||||
the builder, and a suffix convention is one escaping bug away from a silently different
|
||||
regex. */
|
||||
match: o.match.map((r) => ({ s: r.source, f: r.flags })) }
|
||||
console.log(` ${v.state === 'verified' ? 'ok ' : 'HELD'} ${o.id.padEnd(15)} ${v.method.padEnd(12)} ${o.url}`)
|
||||
}
|
||||
if (!verified) { console.error('ORG-LINKS: nothing verified — refusing to write.'); process.exitCode = 1 } else {
|
||||
/* THROUGH THE SNAPSHOT GUARD, and this script is the reason it exists in miniature: three of
|
||||
these hosts answered 429 on an earlier probe in the same hour. A rate-limited run would
|
||||
verify four orgs instead of sixteen and quietly take the contact off ~600 credits. */
|
||||
const body = `${JSON.stringify({
|
||||
meta: {
|
||||
title: 'Who to contact — screened, fetched homepages for the organisations named in the credits',
|
||||
generated: new Date().toISOString().slice(0, 10),
|
||||
grain: 'one entry per organisation, matched to a credit string by the regexes it carries',
|
||||
verified_orgs: verified,
|
||||
orgs_that_did_not_answer: unknown,
|
||||
states: {
|
||||
verified: 'this script fetched it on the run that wrote this file — homepage 200, or robots.txt 200 where a bot challenge answers a datacentre IP',
|
||||
unknown: 'did not answer this run; NOT shipped, and never read as broken',
|
||||
},
|
||||
not_linked: HELD.map((h) => ({ reason: h.reason })),
|
||||
note: 'Hand-screened allowlist. eBible\'s own homeDomain/publicationURL fields are deliberately NOT used: a refresh of someone else\'s catalogue must not be able to put a publisher on this map.',
|
||||
},
|
||||
held: HELD.map((h) => ({ pattern: h.pattern.source, reason: h.reason })),
|
||||
orgs: out,
|
||||
}, null, 1)}\n`
|
||||
writeSnapshot(OUT, body, (j) => Object.values(j.orgs || {}).filter((o) => o.state === 'verified').length, 'verified organisation(s)')
|
||||
console.log(`translator-org-links: ${verified} verified, ${unknown} did not answer`)
|
||||
console.log(`wrote ${OUT}`)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
290
public/dx-maps/assets/tables/translator-org-links.json
Normal file
290
public/dx-maps/assets/tables/translator-org-links.json
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
{
|
||||
"meta": {
|
||||
"title": "Who to contact — screened, fetched homepages for the organisations named in the credits",
|
||||
"generated": "2026-09-26",
|
||||
"grain": "one entry per organisation, matched to a credit string by the regexes it carries",
|
||||
"verified_orgs": 16,
|
||||
"orgs_that_did_not_answer": 0,
|
||||
"states": {
|
||||
"verified": "this script fetched it on the run that wrote this file — homepage 200, or robots.txt 200 where a bot challenge answers a datacentre IP",
|
||||
"unknown": "did not answer this run; NOT shipped, and never read as broken"
|
||||
},
|
||||
"not_linked": [
|
||||
{
|
||||
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the Driver's ruling, not this script's"
|
||||
},
|
||||
{
|
||||
"reason": "doctrine not established from published material — left out rather than guessed"
|
||||
}
|
||||
],
|
||||
"note": "Hand-screened allowlist. eBible's own homeDomain/publicationURL fields are deliberately NOT used: a refresh of someone else's catalogue must not be able to put a publisher on this map."
|
||||
},
|
||||
"held": [
|
||||
{
|
||||
"pattern": "bible society|biblical society|bibelselskab|bibelsellskap|soci(e|é)t(y|é) b[ií]blica|sociedad b[ií]blica|lembaga alkitab|united bible societies|sirk[əe]ti|bible societies",
|
||||
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the Driver's ruling, not this script's"
|
||||
},
|
||||
{
|
||||
"pattern": "artists for israel|k[äa]rnbibeln|sanskritbible|gilakmedia|tarifit|gSungrab|audiobiblia",
|
||||
"reason": "doctrine not established from published material — left out rather than guessed"
|
||||
}
|
||||
],
|
||||
"orgs": {
|
||||
"wycliffe-ca": {
|
||||
"name": "Wycliffe Bible Translators of Canada",
|
||||
"url": "https://www.wycliffe.ca/",
|
||||
"why": "Bible translation agency, Canadian office of the Wycliffe Global Alliance.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 2084732 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "wycliffe[^,]*\\bcanada\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wycliffe-au": {
|
||||
"name": "Wycliffe Bible Translators Australia",
|
||||
"url": "https://wycliffe.org.au/",
|
||||
"why": "Bible translation agency, Australian office of the Wycliffe Global Alliance.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 87857 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "wycliffe[^,]*australian?\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wycliffe": {
|
||||
"name": "Wycliffe Bible Translators",
|
||||
"url": "https://www.wycliffe.org/",
|
||||
"why": "Bible translation agency; translates the 66-book canon, no added scripture.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 367112 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bwycliffe\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"twftw": {
|
||||
"name": "The Word for the World",
|
||||
"url": "https://www.twftw.org/",
|
||||
"why": "Bible translation agency working through national translators.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 1145398 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "word for the world",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "\\bTWFTW\\b",
|
||||
"f": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"biblica": {
|
||||
"name": "Biblica",
|
||||
"url": "https://www.biblica.com/",
|
||||
"why": "Bible publisher and translator (NIV); 66-book canon.",
|
||||
"state": "verified",
|
||||
"method": "robots-txt",
|
||||
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 292 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bbiblica\\b",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pioneer": {
|
||||
"name": "Pioneer Bible Translators",
|
||||
"url": "https://pioneerbible.org/",
|
||||
"why": "Bible translation agency working in languages with no Scripture.",
|
||||
"state": "verified",
|
||||
"method": "robots-txt",
|
||||
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 120 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "pioneer bible translators",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "traducteurs pionniers de la bible",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "\\bPBT\\b",
|
||||
"f": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"seed": {
|
||||
"name": "The Seed Company",
|
||||
"url": "https://seedcompany.com/",
|
||||
"why": "Bible translation agency, affiliate of Wycliffe USA.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 371615 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "seed company",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sim": {
|
||||
"name": "SIM",
|
||||
"url": "https://www.sim.org/",
|
||||
"why": "Evangelical mission agency; translation among its field work.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 190863 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bSIM\\b",
|
||||
"f": ""
|
||||
},
|
||||
{
|
||||
"s": "society for international ministries",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bible-league": {
|
||||
"name": "Bible League International",
|
||||
"url": "https://www.bibleleague.org/",
|
||||
"why": "Scripture publisher; absorbed the World Bible Translation Center, whose name the credits still carry.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 701341 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "world bible translation cent(er|re)",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "bible league",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "liga b[ií]blica",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"unfoldingword": {
|
||||
"name": "Door43 / unfoldingWord",
|
||||
"url": "https://door43.org/",
|
||||
"why": "Open-licensed Bible translation resources; the Door43 community platform.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 7465 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "door\\s?43",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "unfoldingword",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bridgeconn": {
|
||||
"name": "Bridge Connectivity Solutions",
|
||||
"url": "https://www.bridgeconn.com/",
|
||||
"why": "Indian Bible-translation technology and translation services group.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 20334 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "bridge conn?ectivity",
|
||||
"f": "i"
|
||||
},
|
||||
{
|
||||
"s": "bridge communication systems",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"btl-kenya": {
|
||||
"name": "Bible Translation and Literacy",
|
||||
"url": "https://www.btlkenya.org/",
|
||||
"why": "Kenyan Bible translation agency, Wycliffe Global Alliance member.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 76225 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "bible translation and literacy",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"kartidaya": {
|
||||
"name": "Yayasan Kartidaya",
|
||||
"url": "https://kartidaya.org/",
|
||||
"why": "Indonesian Bible translation agency, Wycliffe Global Alliance member.",
|
||||
"state": "verified",
|
||||
"method": "robots-txt",
|
||||
"detail": "homepage HTTP 429 (bot challenge from a datacentre IP), robots.txt HTTP 200, 319 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "kartidaya",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"liebenzell": {
|
||||
"name": "Liebenzell Mission",
|
||||
"url": "https://www.liebenzell.org/",
|
||||
"why": "German evangelical mission agency (Free Church tradition).",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 1281367 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "liebenzel+l? mission",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sil": {
|
||||
"name": "SIL Global",
|
||||
"url": "https://www.sil.org/",
|
||||
"why": "Language development organisation; the linguistic partner behind much of this corpus.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 415007 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "\\bSIL\\b",
|
||||
"f": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"lockman": {
|
||||
"name": "The Lockman Foundation",
|
||||
"url": "https://www.lockman.org/",
|
||||
"why": "Scripture publisher (NASB, LBLA); 66-book canon.",
|
||||
"state": "verified",
|
||||
"method": "homepage-get",
|
||||
"detail": "HTTP 200, 163046 bytes",
|
||||
"match": [
|
||||
{
|
||||
"s": "lockman foundation",
|
||||
"f": "i"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -578,7 +578,7 @@
|
|||
"bundle": "gospel-resources",
|
||||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"output": "dx-maps/gospel-resources/gospel-resources.js",
|
||||
"integrity": "sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO",
|
||||
"integrity": "sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD",
|
||||
"elements": [
|
||||
"gospel-resources-map"
|
||||
],
|
||||
|
|
@ -598,7 +598,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -612,7 +612,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -626,7 +626,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/gospel-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -640,7 +640,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -654,7 +654,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
|
||||
<script src="../gospel-resources.js" integrity="sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-resources.js" integrity="sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
|
||||
<script src="../gospel-resources.js" integrity="sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-resources.js" integrity="sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
|
||||
<script src="../gospel-resources.js" integrity="sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-resources.js" integrity="sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
|
||||
<script src="../gospel-resources.js" integrity="sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-resources.js" integrity="sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
|
||||
<script src="../gospel-resources.js" integrity="sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-resources.js" integrity="sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -578,7 +578,7 @@
|
|||
"bundle": "gospel-resources",
|
||||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"output": "dx-maps/gospel-resources/gospel-resources.js",
|
||||
"integrity": "sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO",
|
||||
"integrity": "sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD",
|
||||
"elements": [
|
||||
"gospel-resources-map"
|
||||
],
|
||||
|
|
@ -598,7 +598,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -612,7 +612,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -626,7 +626,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/gospel-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -640,7 +640,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -654,7 +654,7 @@
|
|||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-KrZmEWirbhkdmcLBpBqHFA4pEex92noEZz+dWl5fsinGQXdJ3BZDEHhY+irkzrEO\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-EyjN92xserN7LYr4G9vqIezc9bi9IRvG+Pwa6IAwfJ4jEBECETQd/zwJ11FpmWLD\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in a new issue