Reveal URLs — Arkitektura
Dokumentu honek Reveal URLs nola egituratuta dagoen eta haren oinarrizko
logikak nola funtzionatzen duen azaltzen du, laguntzaileentzat eta irakurle
teknikoentzat. Testuan zehar fitxategi eta sinbolo errealak aipatzen dira
path symbol formatuan, baieztapen bakoitza iturburu-kodean egiaztatu ahal
izateko. Erabiltzaileentzako azalpena manual dokumentuan dago, eta
eskuzko egiaztapenaren urratsak
manual test plan
dokumentuan.
Ikuspegi orokorra
Reveal URLs MV3 WebExtension bakar bat da, Chrome, Edge, Opera, Firefox eta
Thunderbird-entzat eraikia, nukleo komun eta garbi beretik abiatuta. Safari
gerorako utzita dago: badu manifestua eta espresuki izendatzen denean eraiki
daiteke, baina ez dago lehenetsitako build-ean ezta CI-n ere. Hedapenak esteka
bakoitzaren helmugako URLa erakusten du rendered email edukian bertan, eta
esteka markatzen du testu ikusgarriak href-ean dagoen registrable domeinutik
bestelako bat aipatzen duenean.
Biltegia pnpm eta TypeScript monorepo bat da, pnpm-workspace.yaml eta
package.json fitxategiek deskribatua. "private": true ezarpenak pakete gisa
argitaratzea saihesten du. Bundling-a tooling/build.mjs script-ak egiten du:
partekatutako paketeak eta engine bakoitzeko entry point meheak konpilatzen
ditu dist/<target>/ direktorio kargagarri batera. webextension-polyfill
script bakoitzean bundleatzen da, runtime global baten menpe utzi ordez.
Posta elektronikoko bi native gehigarrik detekzio-nukleo bera zabaltzen dute
WebExtension-ek iritsi ezin dituen gainazaletara: Outlook Add-in Office.js
gainean eta Gmail Add-on Apps Script CardService gainean. Biek
packages/core/src/findings.ts-eko PURE analisia baino ez dute berrerabiltzen,
analyseAnchors, analyseHtml, Finding[] eredu neutrala, hostMismatch
logika eta tldts bidezko registrable domeinuaren eratorpena barne. Host
egokitzailea eta aurkezpena baino ez dira aldatzen. Outlook-ek DOMParser
erabiltzen du eta client-side exekutatzen da. Gmail-ek
node-html-parser erabiltzen du eta server-side, Google Apps Script V8
azpiegituran exekutatzen da. Bi framework horiek ezin dutenez mezuaren DOM
renderizatua aldatu, biek aurkikuntzen panel edo txartel bat erakusten dute,
eta ez inline annotation. linkProcessor.ts-eko DOM mutazio bidea eta
REVEAL_URLS_CSS ez dira hor erabiltzen. Href erlatibo bat mezu barruko
<base href> fidagarri baten aurka bakarrik ebazten da; bestela, baztertu
egiten da.
Kode-oinarriak erantzukizunak argi bereizten ditu:
packages/core-ek PURE logika gordetzen du, browser API-rik gabe, storage-rik gabe eta markup sink-ik gabe. Hori da annotation logikarako, config balidaziorako eta host konparaziorako egia-iturri bakarra.packages/webext-ek nukleo hori WebExtension API-ekin, content lifecycle-arekin, dynamic registration-arekin, options UI-arekin eta background wiring-arekin biltzen du.extensions/<engine>-ek engine bakoitzeko entry point meheak eta beremanifest.jsongordetzen ditu.tooling-ek build, version-stamping eta ikonoen script-ak gordetzen ditu.features-ek Cucumber BDD eszenarioak eta test doubles-ak gordetzen ditu.
Biltegiaren egitura
packages/core — nukleo purua
Hemengo moduluek ez dute extension API-rik inportatzen, hala nola
browser.*, chrome.* edo messenger.*, eta ez dute string-to-markup DOM
sink-ik erabiltzen, hala nola innerHTML edo insertAdjacentHTML. Kontratu
hori mekanikoki betearazten da packages/core/test/purity.test.ts fitxategian:
src/**/*.ts ibilbidea zeharkatzen du, iruzkinen gorputzak stripComments
bidez kentzen ditu eta egiaztatzen du ez EXTENSION_API_PATTERN ez
UNSAFE_DOM_PATTERN ez datozela bat moduluren batekin. Azalera publikoa
packages/core/src/index.ts-tik berriz esportatzen da, eta runtime
dependentzia bakarra tldts da.
Modulu nagusiak hauek dira:
packages/core/src/config.ts—ConfigetaSiteRuleeskemak, lehenetsitako balioak eta balidazio-inbutuanormaliseConfigpackages/core/src/matchPattern.ts— pattern-aren gramatika, matcher-a eta espezifikotasun-arauakpackages/core/src/linkProcessor.ts— dokumentu mailako annotator-apackages/core/src/hostMismatch.ts— registrable domeinuaren desadostasun egiaztapenapackages/core/src/styles.tsetapackages/core/src/contrast.ts— injectable stylesheet-a eta kontraste-logikapackages/core/src/safeColour.ts—isSafeColourpredikatua
packages/webext — browser API bilgarriak
Modulu hauek browser.* erabil dezakete, eta hori da WebExtension APIak ukitzen
diren toki bakarra. Azalera publikoa packages/webext/src/index.ts-tik
berriz esportatzen da. Hemen daude content.ts, contentRegistration.ts,
storage.ts, background.ts, toolbarAction.ts, messageDisplay.ts,
messageDisplayBackground.ts eta options/options.ts.
extensions/<engine> — sarrera-puntu meheak eta manifestuak
Engine bakoitzak bere manifest.json eta bere icons/ dauzka. Chrome,
Firefox eta Thunderbird-ek gainera src/ direktorioa dute sarrera-puntuekin.
Edge, Opera eta Safari-k manifestua eta ikonoak bakarrik dituzte, eta
Chrome-ren src/ berrerabiltzen dute. Adibidez,
extensions/chrome/src/content.ts fitxategiak hau baino ez dauka:
void createContentController().bootstrap();, eta
extensions/chrome/src/background.ts fitxategiak registerBackground,
registerContentReconciliation eta registerToolbarToggle deitzen ditu.
extensions/thunderbird/src/background.ts fitxategiak registerBackground eta
registerMessageDisplay deitzen ditu. extensions/_template scaffold bat da,
ez build target bat.
tooling, features eta smoke testak
tooling-ek build.mjs, version.mjs eta icons.mjs biltzen ditu.
features-ek Gherkin eszenarioak, step definition-ak eta
features/support/webext.ts eta features/support/world.ts fitxategietako
doubles fidelak biltzen ditu. Engine bakoitzak
extensions/<engine>/test/manifest.smoke.test.mjs ere badu; _template-ek,
berriz, template.smoke.test.mjs erabiltzen du.
Agerrarazte-fluxua
Edukien bidea extensions/chrome/src/content.ts bezalako engine entry point
batean hasten da; handik createContentController().bootstrap() deitzen da
packages/webext/src/content.ts fitxategitik.
Bootstrap
createContentController-en bootstrap frame bakoitzeko behin exekutatzen da.
claimBootstrap bidez frame-a bereganatzen du await bakoitzaren aurretik,
loadConfigWithRetry bidez config-a kargatzen du, injectStyles bidez
stylesheet-a irakurketa arrakastatsuaren ondoren bakarrik txertatzen du, eta
releaseBootstrap bidez markatzailea leheneratzen du karga behin betiko huts
egiten badu. config.enabled aktibo dagoenean start deitzen du, eta
nolanahi ere onConfigChanged listener-a erregistratzen du.
Gune aktiboaren araua ebaztea
start eta applyConfig-ek resolveSiteRule erabiltzen dute annotation egin
behar den eta non egin behar den erabakitzeko. Lehenik dokumentuaren kokapena
bera probatzen da resolveForHref bidez; horrek config.sites-etik arau
aktiboak bakarrik uzten ditu, eta azken hautaketa selectMostSpecific-en esku
uzten du. Dokumentua bat ez badator eta haren jatorria opaque edo inherited
bada, hasOpaqueOrigin funtzioak egiaztatzen duen bezala, fallback bat egiten
da readTopHref, readOpenerHref eta readReferrer erabiliz, cross-origin
irakurketen aurkako babesarekin.
Edukiaren erroraino mugatzea eta aldaketak behatzea
start-ek contentRoot gogoratzen du, createAnnotator(config) bidez
annotator-a sortzen du eta selectRoots(doc, contentRoot)-eko erro bakoitza
prozesatzen du; funtzio horrek querySelectorAll try/catch barruan biltzen du,
selector oker batek [] itzuli dezan modu seguruan. Gero doc.body behatzen
du OBSERVER_OPTIONS erabiliz. processMutations-ek aldaketa bakoitza root-aren
arabera iragazten du, eta processAddedNode-ek root barruko nodoak, root bera
eta root-a edukitzen duten nodoak hartzen ditu. applyConfig-ek teardown,
restart edo reflow egiten du arau aktiboaren eta contentRoot-aren aldaketaren
arabera.
Esteka bakar bat annotatzea
Annotator-a packages/core/src/linkProcessor.ts fitxategian dago.
createAnnotator-ek Annotator bat itzultzen du Map, generateToken-etik
ateratako data-ru token-a eta config aktiboa dituela. Haren process(root)
metodoak deskonektatutako erregistroak garbitzen ditu, collectAnchors
erabiliz a[href] elementuak biltzen ditu, resolveAnnotatableUrl bidez URLa
ebazten du, render-a aldatu ez denean isAnnotationIntact bide azkarra
erabiltzen du eta mismatch-a hostMismatch bidez kalkulatzen du
annotateAnchor deitu aurretik.
annotateAnchor-ek SAFE-DOM soilik renderizatzen du. "inline" moduan
<span class="reveal-urls-url"> bat gehitzen du textContent-ekin eta
<br> batekin. "title" moduan lehendik dagoen title gordetzen du, URLa
ezartzen du, eta mismatch-a azpimarratzen denean
<span class="reveal-urls-warn"> gehitzen du REVEAL_URLS_WARN_LABEL
balioarekin. Sortutako nodoek data-ru="<token>" jasotzen dute, eta
configFingerprint-ek reflow behartzeko balio du. truncateUrl-ek
[...displayHref] gainean lan egiten du eta URL_ELLIPSIS erabiltzen du.
Host desadostasuna
packages/core/src/hostMismatch.ts fitxategiko hostMismatch-ek estekaren
testu ikusgarria eta haren href alderatzen ditu registrable domeinu mailan,
ez host gordinean. extractHostCandidates-ek testua tokenetan zatitzen du eta
hostCandidate bidez murrizten du; hark puntu bat eta tldts-ek aitortutako
public suffix bat eskatzen ditu. Href-eko hosta eta hautagaiak
tldts.getDomain bidez murrizten dira. Domeinu desberdina duen edozein
hautagaik mismatch sortzen du.
Estiloak eta kontrastea
packages/core/src/styles.ts fitxategiak REVEAL_URLS_CSS eta
applyColour, applyFontOverrides eta applyContrastBackdrop laguntzaileak
gordetzen ditu. applyContrastBackdrop-ek elementuaren kolore kalkulatua eta
getComputedStyle bidez aurkitzen duen lehen opaque atzeko planoa irakurtzen
ditu, eta gero packages/core/src/contrast.ts-eko needsWhiteBackdrop
erabiltzen du style.backgroundColor = "white" ezarri behar den erabakitzeko.
contrast.ts-ek parseColour, relativeLuminance eta contrastRatio
eskaintzen ditu.
Host konfiguragarriak eta edukiaren esparrua
packages/core/src/config.ts fitxategiko SiteRule-ek zehazten du annotation
NON exekutatzen den match eta allFrames bidez, eta ZEIN container-ek
mugatzen duen contentRoot erabiliz, enabled eta builtin markarekin batera.
DEFAULT_SITES multzoak Gmail, Proton allFrames-ekin eta Outlook-eko bi
hostak hartzen ditu. Config-ek toggle globalak, colours, font overrides eta
sites zerrenda biltzen ditu.
Match pattern motorea
packages/core/src/matchPattern.ts fitxategiak MATCH_PATTERN gramatika
murriztailea dauka: http eta https bakarrik, aukerako *. wildcard host bat,
glob path bat, baina porturik gabe, * scheme-rik gabe eta <all_urls> gabe.
matchesPattern-ek URL bat pattern balidatu baten aurka konparatzen du eta
path zatia pathGlobMatches-era delegatzen du. parsePatternParts-ek pattern-a
host, path, scheme eta wildcardHost ataletan banatzen du.
Arauak gainjartzen direnean, "most specific wins" araua aplikatzen da.
compareSiteSpecificity-ek host zehatza lehenesten du wildcard host-aren
gainetik, gero host literal luzeena, gero path literalagoa
literalPathLength bidez, eta azkenik ASCII tiebreak bat erabiltzen du.
selectMostSpecific-ek URL bati dagokion arau aktibo espezifikoena itzultzen
du.
permissions.getAll()-ek WebExtension gramatika osoko grant-ak eman ditzake,
hala nola <all_urls>, *://*/*, https://*/*, *://*.host/* eta
https://host/*) eta MATCH_PATTERN-ek baztertuko lituzke.
parseGrantedOrigin-ek horiek GrantedOriginParts bihurtzen ditu,
originCovers-ek match-en gaineko estaldura egiaztatzen du, eta
matchAllowsOriginFallback-ek adierazten du path-a zehazki
ORIGIN_FALLBACK_PATH den ala ez, hau da /*.
Erregistro dinamikoa
packages/webext/src/contentRegistration.ts-ek dynamic content script bat
erregistratzen du erabiltzaileak gehitutako arau bakoitzeko. Built-in arauak
static content_scripts sarreren bidez zerbitzatzen dira, eta inoiz ez dira
dinamikoki erregistratzen. desiredContentScripts-ek config.sites iragazten
du, isOriginGranted, originCovers eta contentScriptId erabiltzen ditu eta
matchOriginAsFallback: true ezartzen du soilik
matchAllowsOriginFallback(rule.match) betetzen denean.
reconcileContentScriptsOnce-ek emandako jatorriak, gordetako config-a eta
erregistro aktiboak irakurtzen ditu, eta property-aware diff baten bidez
harmonizatzen ditu. sameRegistration-ek nahi den descriptor-a eta live
read-back-a normalizatzen ditu. updateContentScripts existitzen ez denez,
aldatutako script bat deserregistratu eta gero berriz erregistratzen da.
reconcileContentScripts-ek inFlight promise-a erabiltzen du ia aldi berean
gerta daitezkeen trigger-ak serializatzeko.
Baimen esplizituaren fluxua
Manifest static-ek optional_host_permissions
(http://*/*, https://*/*) deklaratzen dituzte, erabiltzaileak runtime-an
host gehigarri baterako baimena eman ahal izan dezan.
packages/webext/src/options/options.ts fitxategiko addSite-ek hautagai den
arau osoa balidatzen du normaliseSiteRule bidez, host origin-a matchOrigin
bidez eratorri eta browser.permissions.request deitzen du Add botoiaren
click gesture-etik. Oniritzia jaso ondoren bakarrik eransten du
builtin:false errenkada.
Konfigurazioa, biltegiratzea eta eredu kanonikoa
Balidazio-inbutu bakarra
packages/core/src/config.ts fitxategiko normaliseConfig da irakurketa-bide
kanoniko bakarra. Eremu bakoitza behartzen eta mugatzen du, sarrera okerra
dagoenean balio lehenetsira itzultzen da eta inoiz ez du exception jaurtitzen.
Eremuz eremuko validator-ek osatzen dute, hala nola normaliseBoolean,
normaliseRenderMode, normaliseMaxLength, normaliseIgnoreHosts,
normaliseMatchColour, normaliseMismatchColour, normaliseCssSize,
normaliseFontWeight, normaliseContentRoot, normaliseMatchPattern eta
normaliseSites. normaliseSites-ek sarrera okerrak baztertzen ditu,
match-en araberako bikoiztuak kentzen ditu, builtin: true behartzen du
araua built-in arau batekin bat datorrenean eta falta diren sarrera integratu
guztiak leheneratzen ditu.
Biltegiratzea
packages/webext/src/storage.ts fitxategiak WebExtension storage API biltzen
du. configArea-k browser.storage.sync erabiltzen du eskuragarri dagoenean,
eta browser.storage.local-era jotzen du, adibidez Thunderbirden.
getConfig, setConfig eta onConfigChanged-ek beti igarotzen dituzte
balioak normaliseConfig-etik; horrela, storage hondatu batek inoiz ezin du
Config baliogabe bat itzuli. Add-on biek funnel bera erabiltzen dute beren
host storage-an.
Konfigurazio kanonikoa eta eguneratze zuzenduak
Hiru write bidek config kanonikoa irakurtzen dute, gauza bakarra aldatzen dute eta berriz idazten dute, amaitu gabeko form edits-ak gorde gabe:
removeSiteoptions.tsfitxategianaddSiteoptions.tsfitxategiantoggleEnabledoptions.tsfitxategian
packages/webext/src/toolbarAction.ts fitxategiko toggleEnabled-ek ere eredu
bera jarraitzen du background aldetik.
Engine bakoitzeko build-a eta manifestu-kontratua
tooling/build.mjs-ek esbuild build komun bat exekutatzen du WebExtension
target bakoitzerako. TARGETS descriptor-ak target bakoitzaren SOURCE TARGET-a
adierazten du. Chrome, Firefox eta Thunderbird-ek beren src/ propioa dute.
Edge, Opera eta Safari-k chrome erabiltzen dute iturri gisa. Outlook add-in-ek
iturri bereizia dauka. ACTIVE_TARGETS, hau da, Chrome, Edge, Firefox, Opera
eta Thunderbird, --all eta --package bidez eraikitzen direnak dira. Safari
eta Outlook espresuki izendatzen direnean bakarrik eraikitzen dira, eta
Gmail-ek make build-gmail erabiltzen du.
Gmail bundle-ak Apps Script V8 runtime-a du jomuga, eta horrek ez du ez ES
modules ez native URL. Horregatik, ESM gisa eraikitzen da eta, gero,
amaierako export {…} kentzen zaio trigger funtzioak global izaten jarrai
dezan. URL polyfill txiki bat bakarrik sartzen da behar denean. buildTarget-ek
dist/<target>/ garbitzen du, manifest.json eta icons/ kopiatzen ditu,
script-ak IIFE gisa bundleatzen ditu, options orria ESM gisa eta
options.html eta options.css kopiatzen ditu.
tooling/version.mjs-ek bertsioak zigilatzen ditu. Erroko package.json-eko
MAJOR.MINOR hartzen du, BUILD zenbaki handiena aurkitzen du eta target
manifest bakoitzari gehitzen dio. Outlook add-in-ek manifest.json eta
manifest.xml ditu. tooling/icons.mjs-ek, make icons bidez exekutatuta,
assets/icon.svg rasterizatzen du icon48.png eta icon128.png fitxategietara.
Manifestu-kontratua extensions/<engine>/test/manifest.smoke.test.mjs
fitxategietan definitzen da; han manifestu bakoitzaren forma eta referenced
file bakoitzaren existentzia egiaztatzen dira.
Nazioartekotzea (i18n)
Reveal URLs ingelesa ez den hizkuntza askotara lokalizatuta dago. Multzo
horren iturri bakarra packages/core/src/locales.json da; han daude
SUPPORTED_LOCALES, bertako izenak eta hizkuntza lehenetsia. Ingelesez ez
dagoen string bakoitza machine-translated da eta giza berrikuspenaren zain
dago. Jatorri-marka formatuaren araberakoa da: _locales katalogoek
description eremuan daramate, site catalogues-ek site/i18n/README.md
fitxategian jasotzen dute, eta docs itzuliek lehen lerroko HTML iruzkina dute.
Hiru lokalizazio-azalera independente daude:
- The options page (AD-1). Markup static-a ingelesez bidaltzen da, baina
itzul daitekeen nodo bakoitzak
data-i18n="<key>"edodata-i18n-<attr>="<key>"darama. Runtime-an,packages/webext/src/options/locale.ts-ek_locales/<code>/messages.jsonkargatzen du, testuatextContentbidez berridazten du eta<html lang>ezartzen du. - The website chrome (AD-2).
tooling/site.mjs-ek site chrome-a ingelesez renderizatzen dudata-i18neta<html lang="en-GB">erabiliz. Ondorensite/scripts/i18n.mjs-ek bisitariaren hizkuntzara aldatzen dusite/i18n/<code>.jsonbidez. - The documentation pages (AD-8/AD-9). Dokumentazio-orriak locale
bakoitzerako renderizatzen dira build unean, eta
packages/core/src/site-page-names.json-eko fitxategi-izen lokalizatuak erabiltzen dituzte, adibideznl/handleiding.html. - Internal-link localisation (Finding 2). Barneko docs estekek
data-doc-link="<page>"daramate, eta runtime loader-ak locale bereko sibling aldaerara berridazten ditu,#hasheta?querygordeta.
Posta-bezeroko «Active sites» atalaren ezkutatzea (AD-3)
Thunderbird bezalako mail client target batek rendered messages guztiak ikusten
ditu dagoeneko; beraz, host-based «Active sites» editorea soberan dago. Erabaki
hori tooling/build.mjs-eko mailClient: true eremuaren bidez pasatzen da.
- Build-time.
copyAssets-ekdescriptorFor(target).mailClientegiaztatzen du eta, aktibo dagoenean,<section class="sites">kentzen duremoveSitesSection(html)bidez. - Runtime.
packages/webext/src/options/options.ts-ek atal hori ez egotea onartzen du, ez duOptionsFieldMissingErrorjaurtitzen eta gordetakositesbalioa berrezartzen du normalisation egin aurretik.
Segurtasun eta pribatutasun jarrera
- Sareko deirik eta datu-ihesik ez. Modulu batek ere ez du
fetch,XHRedo bestelako sare-deirik egiten. Hedapenak gordetako config-a eta orriaren DOM-a baino ez ditu irakurtzen. - SAFE-DOM bakarrik. Annotation-ek eta injected stylesheet-ak
createElement,textContentetastyle.*baino ez dituzte erabiltzen, eta ezinnerHTML,insertAdjacentHTMLedoeval. - Kanpoko sarrera oro normalizatzen da. Irakurketa eta idazketa guztiak
normaliseConfig-etik pasatzen dira. ColoursisSafeColourbidez egiaztatzen dira, pattern-akMATCH_PATTERNbidez eta content-root selectorsCONTENT_ROOT_PATTERNbidez mugatzen dira. - Pribilegio minimoak. Webmail jatorri integratuak
host_permissionsdira. Host gehigarriak opt-in diraoptional_host_permissionsetapermissions.requestbidez. - Datu-bilketa Gecko ingurunean. Firefox eta Thunderbird manifestuek hau
deklaratzen dute:
browser_specific_settings.gecko.data_collection_permissions.required: ["none"]. - Lizentzia. Proiektua AGPL-3.0-only da,
LICENSEetapackage.jsonfitxategietan adierazten den bezala.
Probak
- Vitest unit tests-ek
packages/core/test/*etapackages/webext/test/*estaltzen dituzte. - Cucumber BDD eszenarioak
features/direktorioan bizi dira etafeatures/support/webext.tsetafeatures/support/world.tserabiltzen dituzte. - Per-target manifest smoke tests-ek
extensions/<engine>/test/manifest.smoke.test.mjsfitxategietannode:testerabiltzen dute, manifestuaren forma eta referenced files egiaztatzeko. - CI
.github/workflows/ci.ymlfitxategian push eta pull request guztiak gelditzen ditumake test,make bddetamake lintgainditzen ez badira. Version-stamping eta packaging.github/workflows/release.ymlfitxategian daude.