Reveal URLs — Arhitektura
Ovaj dokument opisuje kako je strukturirano proširenje Reveal URLs i kako
radi njegova temeljna logika, za suradnike i tehničke čitatelje. U njemu se
stalno upućuje na stvarne datoteke i simbole, u obliku path symbol, kako
bi se svaka tvrdnja mogla provjeriti u izvornom kodu. Za opis iz perspektive
korisnika pogledajte manual. Za korake ručne provjere pogledajte
manual test plan.
Pregled
Reveal URLs je jedinstveni MV3 WebExtension izgrađen za više enginea, odnosno
Chrome, Edge, Opera, Firefox i Thunderbird, iz jedne zajedničke čiste jezgre
koda. Safari je odgođen: ima manifest i može se buildati kada se izričito
zatraži, ali je isključen iz zadanog builda i iz CI-ja. Proširenje otkriva
odredišni URL svake poveznice uz samu poveznicu u prikazanom emailu i
označava poveznicu čiji vidljivi tekst navodi drukčiju registrable domenu od
one u njezinu href.
Repozitorij je monorepo za pnpm i TypeScript, opisan u
pnpm-workspace.yaml i package.json, pri čemu ga "private": true
sprječava da bude javno objavljen kao paket. Bundling vodi jedna jedina
skripta pogonjena esbuildom, tooling/build.mjs, koja prevodi zajedničke
pakete i tanka entry points svake platforme u učitljiv direktorij
dist/<target>/. webextension-polyfill uključuje se u svaki script bundle
umjesto da se oslanja na runtime global, tako da se isti izvorni kod
neizmijenjen izvršava u Chromiumu i Gecku.
Dva izvorna dodatka za email proširuju istu jezgru otkrivanja na površine do
kojih WebExtension ne može doprijeti: Outlook Add-in temeljen na
Office.js task paneu i Gmail Add-on temeljen na Apps Script
CardServiceu. Oba ponovno koriste samo PURE analizu iz
packages/core/src/findings.ts, s analyseAnchors, analyseHtml,
platformski neutralnim modelom Finding[], postojećom logikom
hostMismatch i izvođenjem registrable domene preko tldts. Razlikuju se
samo host adapter, odnosno način čitanja i parsiranja tijela poruke, te
prezentacija. Outlook task pane radi parsiranje vlastitim DOMParser-om i
izvršava se client-side. Gmail add-on radi parsiranje s
node-html-parser i izvršava se server-side na Googleovoj Apps Script V8
infrastrukturi. Budući da nijedan od tih okvira ne dopušta mijenjanje
prikazanog DOM-a poruke u načinu čitanja, oba dodatka prikazuju ploču ili
karticu s nalazima, a ne inline annotation. Postojeći DOM-mutation put u
linkProcessor.ts i REVEAL_URLS_CSS ne koriste se ponovno. Relativni href
rješava se samo u odnosu na pouzdani unutar-email <base href> i inače se
preskače.
Kodna baza strogo odvaja odgovornosti:
packages/coresadrži PURE logiku, bez browser API-ja, bez storagea i bez sinkova koji upisuju markup. Može se jedinično testirati u običnom JavaScriptu i jedini je izvor istine za annotation poveznica, validaciju configa, match-pattern matching, usporedbu hostova i contrast-aware bojanje.packages/webextomata oko te jezgre WebExtension API-je: lifecycle sadržaja, dinamičku registraciju content scriptsa, UI postavki, background wiring, toolbar action i put za prikaz poruka u Thunderbirdu.extensions/<engine>drži tanka entry points po engineu i njihovemanifest.jsondatoteke.toolingsadrži skripte za build, version-stamping i generiranje ikona.featuressadrži Cucumber BDD scenarije i njihove test doubles.
Raspored repozitorija
packages/core — čista jezgra
Nijedan modul ovdje ne uvozi extension API, poput browser.*, chrome.*
ili messenger.*, i nijedan ne koristi string-to-markup DOM sink poput
innerHTML ili insertAdjacentHTML. Taj se ugovor mehanički provodi kroz
packages/core/test/purity.test.ts, koji prikuplja sve src/**/*.ts,
uklanja tijela komentara vlastitim stripComments i potom provjerava da se
ni EXTENSION_API_PATTERN ni UNSAFE_DOM_PATTERN ne podudaraju ni s jednim
modulom. Javna površina ponovno se izvozi iz packages/core/src/index.ts.
Jedina runtime ovisnost je tldts, za usporedbu domena uz razumijevanje
public suffixa.
Glavni moduli su:
packages/core/src/config.ts— schemaConfigiSiteRule, zadane vrijednosti i jedinstveni validacijski funnelnormaliseConfigpackages/core/src/matchPattern.ts— gramatika match patterna, matcher, rangiranje specifičnosti i provjera granted-origin coverageapackages/core/src/linkProcessor.ts— annotator koji radi u scopeu dokumentapackages/core/src/hostMismatch.ts— provjera nepodudaranja registrable domenepackages/core/src/styles.tsipackages/core/src/contrast.ts— injectable stylesheet i contrast-aware colouringpackages/core/src/safeColour.ts— predikatisSafeColour
packages/webext — omotači browser API-ja
Moduli ovdje SMIJU koristiti browser.* i to je jedino mjesto na kojem se
dodiruju WebExtension API-ji. Javna površina ponovno se izvozi iz
packages/webext/src/index.ts. Moduli su content.ts za content lifecycle,
contentRegistration.ts za dinamičku registraciju, storage.ts za
persistence configa, background.ts za ponašanje pri instalaciji,
toolbarAction.ts za toggle i badge, messageDisplay.ts i
messageDisplayBackground.ts za Thunderbird te options/options.ts kao
controller stranice postavki.
extensions/<engine> — tanke ulazne točke i manifesti
Svaki engine nosi vlastiti manifest.json i vlastiti icons/. Chrome,
Firefox i Thunderbird dodatno nose direktorij src/ s entry points.
Edge, Opera i Safari nose samo manifest i ikone te ponovno koriste Chromeov
src/, kako određuje build descriptor. Entry points namjerno su vrlo mali.
Na primjer, extensions/chrome/src/content.ts sadrži samo
void createContentController().bootstrap();, dok
extensions/chrome/src/background.ts poziva registerBackground,
registerContentReconciliation i registerToolbarToggle. Firefoxov
background entry isti je kao Chromeov. Thunderbirdov,
extensions/thunderbird/src/background.ts, poziva registerBackground i
registerMessageDisplay, jer nema content scripts. extensions/_template
služi kao Chromium scaffold i nije build target.
tooling, features i smoke testovi
tooling sadrži build.mjs, version.mjs i icons.mjs. features
sadrži Gherkin scenarije, njihove step definitions i vjerne test doubles u
features/support/webext.ts i features/support/world.ts. Svaki engine
također nosi extensions/<engine>/test/manifest.smoke.test.mjs, a za
_template to je template.smoke.test.mjs. Oni su autoritativni ugovor
manifesta.
Tok razotkrivanja
Put sadržaja počinje na entry pointu enginea, kao što je
extensions/chrome/src/content.ts, koji poziva
createContentController().bootstrap() iz packages/webext/src/content.ts.
Bootstrap
createContentController bootstrap izvršava se jednom po okviru.
- Sinkrono prisvaja okvir prije svakog
await, prekoclaimBootstrap, koji čita i postavlja oznakuBOOTSTRAP_MARKERnawindowokvira. Statički built-incontent_scriptsentry i preklapajući dynamic user script mogu oba ubrizgaticontent.jsu isti okvir, pa je zajednički isolated-worldwindowispravan once-only guard. - Najprije učitava spremljeni config preko
loadConfigWithRetry, s dodatnim pokušajimaBOOTSTRAP_CONFIG_RETRIESpri transient rejectionu. Tek kada čitanje uspije, ubrizgava stylesheet prekoinjectStyles, koji zapisujeREVEAL_URLS_CSSpomoćutextContent. - Ako učitavanje configa propadne u svakom pokušaju,
releaseBootstrapvraća marker prije početka annotationa, kako bi kasniji re-injection mogao ponovno pokušati. - Kada je
config.enabledistinit, poziva sestart, a u svakom se slučaju registrira listeneronConfigChanged, kako bi se config ponovno primijenio pri promjeni.
Određivanje pravila aktivnog mjesta
start, kao i applyConfig, poziva resolveSiteRule kako bi odlučio treba
li i gdje napraviti annotation. resolveSiteRule najprije pokušava s
lokacijom samog dokumenta preko resolveForHref, koji filtrira config.sites
na omogućena pravila i konačni izbor prepušta selectMostSpecific iz
jezgre, tako da pobjeđuje najspecifičniji pogodak. Ako se lokacija samog
dokumenta ne podudara ni s čim, a njegov je origin opaque ili inherited, što
provjerava hasOpaqueOrigin, tada redom slijedi fallback na TOP frame preko
readTopHref, na OPENER window preko readOpenerHref i naposljetku na
document.referrer preko readReferrer. Svako vanjsko čitanje čuva se od
cross-origin pristupa i daje kandidata samo kad se doista može pročitati.
Običan unmatched document smatra se autoritativnim i ne nasljeđuje pravilo
iz okoline.
Ograničavanje na korijen sadržaja i promatranje promjena
start zapisuje selector contentRoot pravila koje odgovara, stvara
annotator preko createAnnotator(config) i obrađuje svaki odgovarajući korijen
iz selectRoots(doc, contentRoot), koji omata querySelectorAll u
try/catch kako bi nevažeći selector sigurno završio kao []. Nakon toga
promatra doc.body, stabilan container, pomoću OBSERVER_OPTIONS.
Observer hrani processMutations, koji filtrira svaku promjenu u odnosu na
contentRoot. Target se ponovno obrađuje samo kada je Element unutar
roota preko isWithinRoot, a svaki dodani Element prolazi kroz
processAddedNode, koji annotates čvor koji je unutar roota, sam root ili
sadrži root, kao kod lazy SPA mounta. Tako su pokriveni i churn unutar
message bodyja i kasnije montiranje susjednog message bodyja, dok ostatak
aplikacijskog chromea ostaje netaknut. Null annotator je no-op.
applyConfig usklađuje živu promjenu configa. Radi teardown kada nijedno
pravilo ne odgovara ili kada je enabled isključen, radi restart kada se
promijeni razriješeni contentRoot, a inače radi reflow na mjestu pomoću
annotator.setConfig i annotator.process nad svakim odgovarajućim
korijenom bez restarta observera.
Označavanje pojedinačne poveznice
Annotator živi u packages/core/src/linkProcessor.ts. createAnnotator
vraća Annotator s closure stateom koji stranica ne može pročitati: snažan
Map vezan uz identitet anchor elementa, token data-ru po annotatoru iz
generateToken i trenutni config. Metoda process(root) radi sljedeće:
- Orezuje entries registra čiji anchor više nije povezan.
- Prikuplja kandidate anchor elemenata preko
collectAnchors, odnosno descendanta[href]i, ako je sam roota[href], i sam root. - Rješava odredište svakog anchor elementa preko
resolveAnnotatableUrl, koji vraćanullza prazan href, in-page href, href koji se ne može parsirati, schemes različite odhttp:ihttps:, ili host koji je jednak ili podređen nekom unosu izignoreHosts. - Uzima idempotent fast path kada
isAnnotationIntactpotvrdi da se render fingerprint nije promijenio i da su svi stvoreni čvorovi još uvijek povezani i na pravom mjestu. U suprotnom radi revert starog annotationa i ponovno renderiranje. - Računa mismatch pomoću
hostMismatchnad očišćenim tekstom anchor elementa, poštujeshowOnlyOnMismatchi na kraju pozivaannotateAnchor.
annotateAnchor renderira samo SAFE-DOM. U načinu "inline" na početak
umeće <span class="reveal-urls-url"> čiji se URL zapisuje preko
textContent, s prefiksom strelice i U+00A0 non-breaking space, a zatim
<br>, bez diranja djece same poveznice. U načinu "title" sprema izvorni
title, zamjenjuje ga URL-om i, kod naglašenog mismatcha, dodaje susjedni
<span class="reveal-urls-warn"> s REVEAL_URLS_WARN_LABEL. Svaki
stvoreni čvor dobiva data-ru="<token>", ali nikada sama poveznica, a svaki
entry bilježi configFingerprint, tako da promjena polja koja utječu na
render prisiljava reflow. Boje i nadjačavanja fonta primjenjuju se preko
typed element.style.* properties, nikada interpolacijom u CSS string.
revertOwned i revertAll uklanjaju čvorove u vlasništvu annotatora i
vraćaju spremljeni title.
truncateUrl radi nad Unicode code points pomoću [...displayHref], kako
rez ne bi prekinuo surrogate pair. Zadržava origin scheme-and-host i
skraćuje ostatak s trailing URL_ELLIPSIS.
Nepodudaranje hosta
U packages/core/src/hostMismatch.ts, hostMismatch uspoređuje vidljivi
tekst poveznice s njezinim href na razini registrable domene, a ne raw
hostnama. Tako se poštena sub-domain ne označava, dok se look-alike host
označava. extractHostCandidates dijeli tekst na tokene i svodi ih preko
hostCandidate, koji zahtijeva točku i ICANN public suffix koji tldts
prepoznaje. I host hrefa i svaki kandidat svode se na registrable domenu
preko tldts.getDomain. Bilo koji kandidat čija se domena razlikuje od
hrefove daje mismatch. Funkcija nikada ne baca exception.
Stilovi i kontrast
packages/core/src/styles.ts drži injectable REVEAL_URLS_CSS, odnosno
fixed layout, weight, shape i fixed bijelu potpuno opaque pozadinu chipa, te
runtime helpere applyColour, applyFontOverrides i
applyContrastBackdrop. applyContrastBackdrop čita resolved colour
elementa i prvu opaque pozadinu koju pronađe penjući se od samog elementa
prema gore preko getComputedStyle. Kada
packages/core/src/contrast.ts needsWhiteBackdrop prijavi da tekst ne
zadovoljava WCAG AA CONTRAST_THRESHOLD u odnosu na tu pozadinu i da bijela
doista pomaže, postavlja style.backgroundColor = "white".
contrast.ts daje parseColour, relativeLuminance i contrastRatio.
Podesivi hostovi i ograničavanje sadržaja
SiteRule u packages/core/src/config.ts govori GDJE se annotation izvršava,
preko match i allFrames, i KOJI ga container ograničava, preko
contentRoot, zajedno s enabled i oznakom builtin. DEFAULT_SITES
pokrivaju Gmail, Proton s allFrames i oba Outlook hosta. Config
objedinjuje globalne toggles, colours, font overrides i polje sites.
Mehanizam obrazaca podudaranja
packages/core/src/matchPattern.ts drži namjerno restrictive add-time
gramatiku MATCH_PATTERN: samo http i https, neobavezni početni *. host
wildcard, glob path, bez porta, bez scheme *, bez <all_urls>.
matchesPattern podudara URL s validiranim patternom, prepuštajući path
funkciji pathGlobMatches, gdje svaka * predstavlja proizvoljan niz
znakova. parsePatternParts dijeli validirani pattern na host, path,
scheme i wildcardHost.
Kod preklapajućih pravila vrijedi "most specific wins".
compareSiteSpecificity prvo rangira exact host iznad wildcard hosta, zatim
dulji literal host, zatim literalniji path preko literalPathLength, a na
kraju deterministic ASCII tiebreak. selectMostSpecific vraća
najspecificiranije omogućeno pravilo koje odgovara URL-u.
Provjera granted-origin coveragea zaseban je i namjerno širi matcher.
permissions.getAll() može vratiti grants u punoj gramatici WebExtensiona
(<all_urls>, *://*/*, https://*/*, *://*.host/*,
https://host/*) koje restrictive MATCH_PATTERN odbacuje.
parseGrantedOrigin ih pretvara u GrantedOriginParts, a originCovers
provjerava pokriva li granted origin ruleov match. matchAllowsOriginFallback
prijavljuje je li path pravila točno ORIGIN_FALLBACK_PATH, odnosno /*.
Dinamička registracija
packages/webext/src/contentRegistration.ts registrira jedan dynamic content
script za svako pravilo koje korisnik doda. Built-ins se isporučuju preko
static content_scripts entries i nikada se ne registriraju dinamički.
desiredContentScripts filtrira config.sites na non-builtin omogućena
pravila čiji je origin granted, preko isOriginGranted i originCovers,
mapira svako pravilo na RegisteredContentScript sa stabilnim id-jem iz
contentScriptId i postavlja matchOriginAsFallback: true samo kada to
dopušta matchAllowsOriginFallback.
reconcileContentScriptsOnce čita granted origins, spremljeni config i live
registrations te konvergira preko property-aware diffa. sameRegistration
projicira i željeni descriptor i live read-back u normalised form, primjenjujući
stvarna zadana ponašanja WebExtension API-ja, tako da freshly registered
script bude jednak descriptoru koji ga je proizveo. Budući da ne postoji
updateContentScripts, promijenjeni script uklanja se i ponovno registrira.
reconcileContentScripts omata taj tok in-flight promiseom, kako bi se dva
gotovo istodobna okidača serializirala umjesto da uđu u race.
registerContentReconciliation povezuje triggere i jednom konvergira pri
pokretanju. Uvozi se samo iz background entryja za Chrome i Firefox.
Tok izričitog dopuštenja
Static manifests deklariraju optional_host_permissions (http://*/*,
https://*/*) kako bi korisnik u runtimeu mogao odobriti proizvoljan
dodatni web host. packages/webext/src/options/options.ts
addSite validira cijelo kandidatno pravilo, odnosno match i content root,
preko normaliseSiteRule prije traženja ikakvog dopuštenja. Zatim izvodi
host origin pomoću matchOrigin, poziva browser.permissions.request iz
click gesture gumba Add i tek nakon odobrenja dodaje red builtin:false i
sprema ga canonical write putem.
Konfiguracija, pohrana i kanonski uzorak
Jedinstveni validacijski lijevak
normaliseConfig u packages/core/src/config.ts jedina je canonical read
path. Pretvara, ograničava i validira svako polje, vraćajući se na zadanu
vrijednost pri svemu neispravnom i nikada ne baca exception. Izgrađen je od
validatora po polju, poput normaliseBoolean, normaliseRenderMode,
normaliseMaxLength, normaliseIgnoreHosts, normaliseMatchColour,
normaliseMismatchColour, normaliseCssSize, normaliseFontWeight,
normaliseContentRoot, normaliseMatchPattern i normaliseSites.
normaliseSites odbacuje nevaljane entries, uklanja duplikate po match,
nameće builtin: true kada je neki match jednak built-inu, ponovno sije
built-ins koji nedostaju i sortira alfanumerički po match, proizvodeći
canonical order. configFingerprint hashira samo polja koja utječu na
render.
Pohrana
packages/webext/src/storage.ts omata WebExtension storage API. configArea
koristi browser.storage.sync kada je dostupan, kako bi postavke
putovale, i radi fallback na browser.storage.local, primjerice u
Thunderbirdu. getConfig, setConfig i onConfigChanged uvijek provlače
svoje raw vrijednosti kroz normaliseConfig, pa izobličen ili namjerno
izmijenjen store nikada ne može predati neispravan Config pozivatelju.
onConfigChanged dodatno ignorira događaje iz neaktivnog storage područja i
promjene nepovezanih ključeva.
Dva add-ona ponovno koriste isti funnel read → normaliseConfig → return nad
vlastitim host storeovima. Outlook task pane omata
Office.context.roamingSettings u extensions/outlook/src/roamingStorage.ts.
Gmail add-on omata PropertiesService.getUserProperties() u
packages/gmail/src/propertiesStorage.ts. Oba izlažu getConfig i
setConfig, zajedno s accessorima koje card UI treba, i štite unavailable
host surface posebnim errorom.
Uzorak kanonske konfiguracije i ciljanih ažuriranja
Tri write patha čitaju canonical config, mijenjaju točno jednu stvar i zatim ga ponovno zapisuju, bez spremanja unsaved form edits i bez re-renderiranja forme:
removeSiteuoptions.ts, koji čitagetConfig, pozivasetConfignad poljemsitesiz kojeg je uklonjeno to pravilo i zatim odlučuje treba li pozvatipermissions.removeaddSiteuoptions.ts, koji validira, traži dopuštenje, dodaje red i zatim pozivasaveOptionstoggleEnableduoptions.ts, gdje trenutni glavni prekidač mijenja samoenabledu spremljenom configu i ponovno ga zapisuje bez re-rendera
toggleEnabled iz packages/webext/src/toolbarAction.ts slijedi isti obrazac
sa strane backgrounda.
Build po engineu i ugovor manifesta
tooling/build.mjs vodi jedinstveni esbuild build za svaki target
WebExtensiona. Descriptor TARGETS za svaki target navodi njegov SOURCE
TARGET. Chrome, Firefox i Thunderbird imaju vlastiti src/. Edge, Opera i
Safari navode chrome kao source. Outlook add-in ima vlastiti source.
ACTIVE_TARGETS, odnosno Chrome, Edge, Firefox, Opera i Thunderbird, grade
se preko --all i --package. Safari i Outlook grade se samo kada se
izričito zatraže. Gmail add-on bundlea se zasebno naredbom
make build-gmail.
Gmail bundle cilja Apps Script V8 runtime, koji nema ni ES modules ni native
URL. Zato se gradi kao ESM, a zatim se uklanja završni export {…}, kako
bi trigger functions ostale top-level globals koje Apps Script može pozvati.
Istodobno se bundlea mali URL polyfill, koji se instalira samo kada
nedostaje. buildTarget čisti dist/<target>/, kopira per-target
manifest.json i icons/ bez izmjena, svaki source script bundlea kao IIFE
i stranicu postavki kao ESM te kopira options.html i options.css iz
packages/webext/src/options/.
tooling/version.mjs utiskuje verzije. Uzima MAJOR.MINOR iz root
package.json, pronalazi najveći postojeći BUILD number u manifestima i
dodaje ga kao treću komponentu svakom target manifestu, držeći ih u
lockstepu. Outlook add-in nosi dva versioned manifesta, manifest.json i
manifest.xml, i oba se utiskuju. Root package.json zadržava istu
verziju, ali ostaje valjan semver. tooling/icons.mjs, pokrenut preko
make icons, rasterises jedini vector source assets/icon.svg u
icon48.png i icon128.png za svaki target.
Ugovor manifesta, odnosno točna potrebna polja po engineu, definiran je u
extensions/<engine>/test/manifest.smoke.test.mjs. Ondje se također potvrđuje
da svaki referenced file doista postoji u izgrađenom distu. Za detaljan shape
polja projekt upućuje na smoke testove i odgovarajuću bilješku u
manual test plan.
Internacionalizacija (i18n)
Reveal URLs lokaliziran je na mnoge jezike osim engleskog. Skup jezika
jedinstveno proizlazi iz packages/core/src/locales.json, koji sadrži
SUPPORTED_LOCALES, native names i zadani jezik. I bundleovi proširenja i
site build u plain Nodeu čitaju odatle. Svaki neengleski string
machine-translated je i čeka ljudski pregled. Marker podrijetla razlikuje se
po formatu: _locales ga nose u description, site kataloge bilježe ga u
site/i18n/README.md, a prevedeni izvori docs nose HTML komentar u prvom
retku.
Postoje tri neovisne lokalizacijske površine, namjerno nepovezane, i svaka je podijeljena na build-time i runtime stranu.
- The options page (AD-1). Isporučuje static engleski markup, ali svaki
prevodivi čvor nosi
data-i18n="<key>"ilidata-i18n-<attr>="<key>". U runtimeupackages/webext/src/options/locale.tssluži kao apply-layer: dohvaća packaged_locales/<code>/messages.json, ponovno ispisuje svaki označeni node samo pomoćutextContenti postavlja<html lang>. Stranica se u cijelosti lokalizira, uključujući JS-built labels, gumb Remove, status line, feedback poruke i version line. Engleski fallback je_locales/en/messages.json, bundled uoptions.js, pa dynamic strings ostaju čitljivi i kad locale fetch ne uspije. Isti layer ponovno usmjerava i Online manual na pravi prevedeni doc path. - The website chrome (AD-2). Generator
tooling/site.mjsrenderira chrome sitea na engleskom, ali sdata-i18ni s<html lang="en-GB">. Zatim browser-side loadersite/scripts/i18n.mjsto mijenja na jezik posjetitelja prema spremljenom izboru,navigator.languagesi završnom fallbacku na engleski, učitavajućisite/i18n/<code>.json. Ovdje se zamjena radi sinnerHTML, jer vrijednosti chromea sadrže trusted inline markup poput<code>i<a>. - The documentation pages (AD-8/AD-9). Stranice docs renderiraju se po
localeu u build timeu i ne mijenjaju se u runtimeu. Engleski ostaje na
top-level paths. Svaki drugi locale dobiva vlastiti direktorij
<code>/i filename preveden preko sharedpackages/core/src/site-page-names.json, poputnl/handleiding.html. Kada prijevod za neki doc nedostaje, builder koristi engleski izvor, ali to izričito zapisuje. - Internal-link localisation (Finding 2). Odabrani jezik zadržava se u
navigaciji dokumentacijom. Svaki internal doc anchor koji pokazuje na
architecture.html,index.html,licence.html,manual.htmliliprivacy.htmlobilježen je sdata-doc-link="<page>", kako bi ga runtime loader mogao preusmjeriti na sestrinsku varijantu istog localea, uz očuvanje svakog#hashili?query.
Potiskivanje „Aktivnih mjesta” u klijentu e-pošte (AD-3)
Target mail clienta, danas Thunderbird, već vidi svaku prikazanu poruku, pa
je editor "Active sites" po hostu suvišan i uklanja se. Odluka se nosi kroz
dokumentirani per-target descriptor flag mailClient: true u
tooling/build.mjs, a ne kroz hardcoded target name.
- Build-time.
copyAssetsprovjeravadescriptorFor(target).mailClient. Za target mail clienta pokreće pure transformremoveSitesSection(html), koji uklanja cijeli<section class="sites">i pada sMissingSourceErrorako taj section nedostaje. - Runtime. Controller
packages/webext/src/options/options.tstolerira odsutnost sectiona.renderSites,readSites,applyConfigireadFormnajprije provjeravaju postoji li#sites-listi, ako ne postoji, ne bacajuOptionsFieldMissingError.saveOptionsvraća spremljenisitesprije normalizacije kada section nedostaje, tako da spremanje u mail clientu ne izgubi korisnički definirane hostove.guardMailClientSitesuklanja section ako je i dalje prisutan te prepoznaje mail client po prisutnosti API-ja ili featurea, a ne po target nameu.
Sigurnosni i privatnosni profil
- Nema mrežnih poziva ni izvlačenja podataka. Nijedan modul ne izvršava
fetch,XHRili drugi mrežni poziv. Proširenje čita samo spremljeni config i DOM stranice. - Samo SAFE-DOM. Annotation i injected stylesheet koriste isključivo
createElement,textContenti typedstyle.*properties, nikadainnerHTML,insertAdjacentHTMLilieval. - Sav vanjski input se normalizira. Svako čitanje i zapis prolazi kroz
normaliseConfig. Colours prolaze krozisSafeColour, match patterns krozMATCH_PATTERN, a content-root selectors ograničeni su sCONTENT_ROOT_PATTERN. - Najmanje potrebno ovlaštenje. Built-in webmail origins nalaze se u
host_permissions. Dodatni hostovi su opt-in prekooptional_host_permissionsi gesture-boundpermissions.request. Toolbar action ne deklarira popup. - Gecko prikupljanje podataka. Firefox i Thunderbird manifesti
deklariraju
browser_specific_settings.gecko.data_collection_permissions.required: ["none"]. - Licenca. Projekt je AGPL-3.0-only, kako navode
LICENSEipackage.json.
Testiranje
- Vitest unit tests pokrivaju oba paketa.
packages/core/test/*provjeravaju config, contrast, host mismatch, link processor, styles i purity guard.packages/webext/test/*provjeravaju content, storage, options, registration, toolbar, message display i jedan custom-site end-to-end test. - Cucumber BDD scenariji žive u
features/, kako određujecucumber.json, i pokreću stvarne module iz@reveal-urls/webextpreko vjernih test doubles ufeatures/support/webext.tsifeatures/support/world.ts. - Per-target manifest smoke tests u
extensions/<engine>/test/manifest.smoke.test.mjskoriste ugrađeninode:test, potvrđuju točan shape svakog manifesta i da izgrađeni dist razrješava svaki referenced file.tooling/test/dodaje build, version i definition-of-done testove. - CI u
.github/workflows/ci.ymlblokira svaki push i pull request akomake test,make bddimake lintne prođu. Version-stamping i packaging ostaju izvan toga toka i žive u zasebnom.github/workflows/release.yml, koji se aktivira tagovima.