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:

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/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:

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:

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.

Segurtasun eta pribatutasun jarrera

Probak