Reveal URLs — Arkitektur

Dette dokumentet beskriver hvordan Reveal URLs-utvidelsen er strukturert og hvordan dens kjernelogikk fungerer, for bidragsytere og tekniske lesere. Det siterer reelle filer og symboler gjennomgående (på formen path symbol) slik at enhver påstand kan kontrolleres mot kildekoden. For den brukervendte beskrivelsen, se håndboken; for de manuelle verifiseringstrinnene, se manuell testplan.

Oversikt

Reveal URLs er en enkelt MV3-WebExtension bygget for flere motorer — Chrome, Edge, Opera, Firefox og Thunderbird — fra én delt, ren kodekjerne. (Safari er utsatt: den har et manifest og kan bygges når den navngis eksplisitt, men er ekskludert fra standardbygget og fra CI.) Utvidelsen avslører hver lenkes destinasjons-URL ved siden av lenken i en gjengitt e-post, og merker en lenke hvis synlige tekst navngir et annet registrerbart domene enn dens href — det klassiske phishing-tegnet.

Kodelageret er en pnpm/TypeScript-monorepo (pnpm-workspace.yaml, package.json med "private": true). Bunting er et enkelt esbuild-drevet skript, tooling/build.mjs, som kompilerer de delte pakkene og hver motors tynne inngangspunkter til en lastbar dist/<target>/-mappe. webextension-polyfill buntes inn i hvert skript i stedet for å baseres på som en runtime-global, slik at den samme kildekoden kjører uendret på tvers av Chromium og Gecko.

To native e-posttillegg utvider den samme deteksjonskjernen til flater WebExtension ikke kan nå: et Outlook-tillegg (Office.js-oppgaverute, som når Outlook på web/Windows/Mac/iOS/Android og Outlook.com) og et Gmail-tillegg (Apps Script CardService, som når Gmail-appene på web/Android/iOS). Begge gjenbruker kun den RENE analysen — en ny packages/core/src/findings.ts-modul (analyseAnchors/analyseHtml, en plattformnøytral Finding[]-modell) som gjenbruker den eksisterende hostMismatch-logikken og tldts-utledningen av registrerbart domene. Bare vertsadapteren (hvordan meldingsteksten leses og parses) og presentasjonen skiller seg per flate: Outlook-oppgaveruten parser meldingsteksten med sin egen DOMParser og kjører klientsiden; Gmail-tillegget parser med node-html-parser og kjører serversiden på Googles Apps Script V8-infrastruktur (gratis hosting, siden Google allerede har e-posten; distribuert lokalt-interaktivt via clasp). Fordi ingen av rammeverkene kan endre den gjengitte meldings-DOM-en i lesemodus (Office sine setAsync/prependAsync er kun for skriving; CardService gjengir kort, ikke meldings-HTML), presenterer begge tilleggene et panel/kort med funn, ikke innebygd annotering — den eksisterende linkProcessor.ts-DOM-endringsbanen og REVEAL_URLS_CSS gjenbrukes IKKE av noen av tilleggene. En relativ href løses kun mot en pålitelig <base href> i e-posten og hoppes ellers over, aldri mot postkassens/leverandørens origin (som ville fabrikkere en destinasjon).

Kodebasen skiller ansvarsområder skarpt:

Kodelagerstruktur

packages/core — den rene kjernen

Hver modul her importerer ingen utvidelses-API (browser.*/chrome.*/messenger.*) og bruker ingen streng-til-markup-DOM-sluk (innerHTML/insertAdjacentHTML). Den kontrakten håndheves mekanisk av packages/core/test/purity.test.ts, som globber hver src/**/*.ts, fjerner kommentarinnhold via sin egen stripComments (slik at docblocks som legitimt NAVNGIR de forbudte symbolene ikke utløser vakten) og deretter bekrefter at verken EXTENSION_API_PATTERN eller UNSAFE_DOM_PATTERN matcher noen modul. Den offentlige flaten re-eksporteres fra packages/core/src/index.ts. Den eneste runtime-avhengigheten er tldts (for domenesammenligning som tar hensyn til offentlige suffiks).

Modulene er:

packages/webext — nettleser-API-innpakningene

Moduler her KAN bruke browser.* og er det eneste stedet WebExtension-API-ene berøres. Den offentlige flaten re-eksporteres fra packages/webext/src/index.ts. Modulene er: content.ts (innholdslivssyklus), contentRegistration.ts (dynamisk registrering), storage.ts (konfigurasjonspersistens), background.ts (oppførsel ved installasjon), toolbarAction.ts (veksling + merke), messageDisplay.ts og messageDisplayBackground.ts (Thunderbird), og options/options.ts (kontrolleren for innstillingssiden).

extensions/<engine> — tynne inngangspunkter og manifester

Hver motor bærer sin egen manifest.json og icons/. Chrome, Firefox og Thunderbird leverer i tillegg en src/ med inngangspunkter; Edge, Opera og Safari bærer kun et manifest og ikoner og GJENBRUKER Chromes src/ (deklarert av bygge- beskrivelsen — se nedenfor). Inngangspunktene er bevisst bittesmå: for eksempel er extensions/chrome/src/content.ts bare void createContentController().bootstrap();, og extensions/chrome/src/background.ts kaller registerBackground, registerContentReconciliation og registerToolbarToggle. Firefox' bakgrunns- inngang er identisk med Chromes; Thunderbirds (extensions/thunderbird/src/background.ts) kaller i stedet registerBackground og registerMessageDisplay (den har ingen innholdsskript). extensions/_template er et kopiklart Chromium-stillas, ikke et byggemål.

tooling, features, og røyktestene

tooling inneholder build.mjs, version.mjs og icons.mjs. features inneholder Gherkin-scenariene, deres trinndefinisjoner og de tro testdoblene i features/support/webext.ts og features/support/world.ts. Hver motor bærer også en extensions/<engine>/test/manifest.smoke.test.mjs (_template sin er template.smoke.test.mjs) — disse er den autoritative manifestkontrakten (se «Bygg per motor og manifestkontrakten» nedenfor).

Avsløringspipelinen

Innholdsbanen begynner ved et motorinngangspunkt som extensions/chrome/src/content.ts, som kaller createContentController().bootstrap() fra packages/webext/src/content.ts.

Bootstrap

createContentController bootstrap kjører én gang per ramme:

  1. Den hevder rammen synkront, før noe await, via claimBootstrap, som leser/setter BOOTSTRAP_MARKER-flagget på rammens window. En statisk innebygd content_scripts-oppføring og et overlappende dynamisk brukerskript kan begge injisere content.js i den samme rammen; den delte isolerte-verden- window gjør dette til den riktige engangsvakten, og å hevde før noe await betyr at to nær-samtidige injeksjoner ikke begge kan passere den.
  2. Den laster den lagrede konfigurasjonen FØRST via loadConfigWithRetry (BOOTSTRAP_CONFIG_RETRIES ekstra forsøk ved en forbigående avvisning), og holder markøren på tvers av forsøkene slik at en overlappende injeksjon som allerede ble en no-op ikke blir strandet. Først når lesingen lykkes injiserer den stilarket (injectStyles, som tilordner REVEAL_URLS_CSS via textContent), slik at en mislykket lesing ikke legger til noe <style> og et nytt forsøk ikke kan duplisere det.
  3. Hvis konfigurasjonslasting mislykkes ved hvert forsøk, ruller releaseBootstrap markøren tilbake (kun her, før annoteringen starter) slik at en senere reinjeksjon kan prøve på nytt; når start har kjørt må markøren bestå, fordi en ny annoterer ikke ville eie nodene fra forrige gjennomgang.
  4. Når config.enabled kaller den start, og den registrerer alltid en onConfigChanged-lytter som tar konfigurasjonen i bruk på nytt (som dekker overgangen fra deaktivert til aktivert).

Bestemme den aktive nettstedsregelen

start (og applyConfig) kaller resolveSiteRule for å avgjøre om — og hvor — det skal annoteres. resolveSiteRule prøver først det kjørende dokumentets EGEN plassering via resolveForHref, som filtrerer config.sites til de aktiverte reglene og overlater til kjernens selectMostSpecific slik at det mest spesifikke samsvaret vinner (en spesifikk innebygd regel slår et bredt bruker-jokertegn). Når den egne plasseringen ikke matcher noe OG dokumentets egen origin er ugjennomsiktig eller arvet — styrt av hasOpaqueOrigin, sann kun for about:blank/about:srcdoc (arvet via INHERITED_ORIGIN_URLS) eller blob:/data: (OPAQUE_ORIGIN_SCHEMES) — faller den tilbake, i rekkefølge, til TOPP-rammen (readTopHref, det innebygde Proton-meldings-iframe-tilfellet), ÅPNER- vinduet (readOpenerHref, Outlooks utsprett-lesevindu åpnet via window.open("about:blank")) og til slutt document.referrer (readReferrer). Hver ekstern lesing er beskyttet mot kryss-origin-tilgang (som kaster) og bidrar med en kandidat kun når den er lesbar. Et VANLIG dokument uten samsvar er autoritativt og arver aldri en regel fra omgivelseskonteksten, slik at kontrolleren forblir inaktiv.

Avgrensning til innholdsroten og observering av mutasjoner

start registrerer den matchede regelens contentRoot-velger, oppretter annotereren via createAnnotator(config), og behandler hver matchede rot fra selectRoots(doc, contentRoot) (en querySelectorAll pakket i try/catch slik at en ugyldig velger feiler sikkert til []). Den observerer deretter doc.body (en stabil beholder) med OBSERVER_OPTIONS ({ childList: true, subtree: true }).

Observatøren mater processMutations, som styrer hver mutasjon på innholds- roten: den behandler en mutasjons-target på nytt kun når den er et Element innenfor en rot (isWithinRoot, via closest), og ruter hvert tilføyde Element gjennom processAddedNode, som annoterer en node som er inne i en rot, ER en rot, eller INNEHOLDER en (en lat SPA-montering). Så både endringer inne i roten og en søsken- meldingstekst som monteres senere fanges opp, mens app-rammeverk utenfor røttene forblir urørt. En null-annoterer (utvidelsen er stoppet) er en no-op.

applyConfig forener en levende konfigurasjonsendring: den river ned når ingen regel matcher eller aktivert-flagget er av; starter på nytt (tilbakestill + observer på nytt) når den bestemte contentRoot endret seg; og ellers flyter den om på stedet — annotator.setConfig deretter annotator.process over hver matchede rot — uten å starte observatøren på nytt.

Annotere et enkelt anker

Annotereren bor i packages/core/src/linkProcessor.ts. createAnnotator returnerer en Annotator over closure-tilstand siden ikke kan lese: et sterkt Map nøklet på anker-identitet, et data-ru-token per annoterer fra generateToken (crypto.getRandomValues), og den gjeldende konfigurasjonen. Dens process(root)-metode:

  1. Beskjærer registeroppføringer hvis anker ikke lenger er tilkoblet.
  2. Samler kandidatankre med collectAnchors (etterkommer-a[href] pluss selve roten når den er en a[href], deduplisert).
  3. Løser hvert ankers destinasjon med resolveAnnotatableUrl, som returnerer null (hopp over) for en tom/på-siden-href, en uparsbar href, ethvert skjema annet enn http:/https:, eller en vert lik eller under en ignoreHosts-oppføring; den løser relative href-er mot baseURI slik at resultatet er absolutt.
  4. Tar den idempotente raske banen når isAnnotationIntact bekrefter at gjengivelsens fingeravtrykk er uendret og hver opprettet node fortsatt er tilkoblet og i posisjon; ellers tilbakestiller den den foreldede annoteringen og gjengir på nytt.
  5. Beregner avvik med hostMismatch på den rene ankerteksten, respekterer showOnlyOnMismatch (lar ærlige lenker være urørt), og kaller annotateAnchor.

annotateAnchor gjengir kun SIKKER-DOM. I "inline"-modus setter den inn foran et <span class="reveal-urls-url"> hvis URL settes via textContent (prefikset med en pil og et U+00A0-hardt mellomrom, avkortet av truncateUrl) etterfulgt av en <br>, og lar lenkens egne barn være urørt; det tilkoblede spennet får deretter en kontrastbakgrunn via applyContrastBackdrop. I "title"-modus lagrer den den opprinnelige title (TitleSave), overskriver den med URL-en — den eneste skrivingen til ankeret — og, ved et fremhevet avvik, legger den til et søsken- <span class="reveal-urls-warn">-merke som bærer REVEAL_URLS_WARN_LABEL. Hver opprettet node er merket data-ru="<token>" (aldri ankeret), og hver oppføring registrerer sin configFingerprint slik at en endring i gjengivelseskonfigurasjonen tvinger en omflyt. Farger og skriftoverstyringer brukes gjennom typede element.style.*-egenskaper (applyColour/applyFontOverrides), aldri interpolert inn i en CSS-streng. revertOwned fjerner opprettede noder ved referanse og gjenoppretter den lagrede tittelen; revertAll gjør dette for hvert eid anker og tømmer registeret.

truncateUrl opererer over Unicode-KODEPUNKTER ([...displayHref]), slik at et kutt aldri deler et surrogatpar; den bevarer skjema-og-vert-originen og forkorter resten med en avsluttende URL_ELLIPSIS.

Vertsavvik

packages/core/src/hostMismatch.ts hostMismatch sammenligner lenkens synlige tekst mot dens href etter registrerbart domene, ikke rått vertsnavn, slik at en honest underdomene ikke merkes mens en etterligning blir det. extractHostCandidates deler teksten og reduserer hvert token via hostCandidate (som krever et punktum og et tldts-gjenkjent ICANN-offentlig suffiks, og avviser vanlig punktert tekst som e.g). Både href-verten og hver kandidat reduseres til sitt registrerbare domene med tldts.getDomain, og ENHVER kandidat hvis domene avviker fra href-ens gir et avvik — slik at det å navngi den ekte ondsinnede verten ved siden av en lokkevert ikke kan undertrykke advarselen. Den kaster aldri.

Stiler og kontrast

packages/core/src/styles.ts inneholder det injiserbare REVEAL_URLS_CSS (fast layout, vekt, form og en fast hvit, fullstendig ugjennomsiktig chip-bakgrunn — background: #ffffff, opacity: 1; farger interpoleres ALDRI inn i det) og runtime- hjelperne applyColour/applyFontOverrides/applyContrastBackdrop. applyContrastBackdrop leser elementets bestemte farge og den første ugjennomsiktige bakgrunnen funnet ved å gå fra elementet SELV og oppover (dets egen bakgrunn — chipens faste hvite standard eller en vertssides mer spesifikke overstyring — før noen forfedre) gjennom standard getComputedStyle (hentet fra element.ownerDocument?.defaultView, slik at den er ramme-sikker og kan stubbes) og, når packages/core/src/contrast.ts needsWhiteBackdrop rapporterer at teksten ikke består WCAG AA (CONTRAST_THRESHOLD) mot den bakgrunnen OG hvitt genuint hjelper, setter style.backgroundColor = "white". contrast.ts tilbyr parseColour, relativeLuminance og contrastRatio; den parser rgb()/rgba()/hex-formene getComputedStyle returnerer og behandler en fullstendig gjennomsiktig farge som «ikke funnet».

Konfigurerbare verter og innholdsavgrensning

En SiteRule (packages/core/src/config.ts) sier HVOR annotering kjører (match, allFrames) og HVILKEN beholder som avgrenser den (contentRoot), pluss enabled og et builtin-flagg. De medfølgende DEFAULT_SITES dekker Gmail, Proton (med allFrames) og begge Outlook-vertene. Config samler de globale bryterne, fargene, skriftoverstyringene og sites-tabellen.

Samsvarsmønster-motoren

packages/core/src/matchPattern.ts inneholder en bevisst RESTRIKTIV grammatikk for innleggingstidspunkt MATCH_PATTERN (kun http/https, valgfritt innledende *.- vertsjokertegn, glob-sti; ingen port, intet *-skjema, ingen <all_urls>). matchesPattern matcher en URL mot et validert mønster, og delegerer stien til pathGlobMatches (hver * matcher en hvilken som helst tegnsekvens); den feiler sikkert til false. parsePatternParts deler et validert mønster inn i host/path/scheme/ wildcardHost.

Overlappende regler løses ved «mest spesifikke vinner»: compareSiteSpecificity rangerer etter (1) eksakt vert over jokertegn-vert, (2) lengre bokstavelig vert, (3) mer bokstavelig sti (literalPathLength), deretter (4) en deterministisk ASCII-avgjørelse; selectMostSpecific returnerer den mest spesifikke aktiverte regelen som matcher en URL.

Dekning av tildelte origins er en SEPARAT, bevisst BREDERE matcher. permissions.getAll() kan rapportere tildelinger i den fulle WebExtension-grammatikken (<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*) som det restriktive MATCH_PATTERN ville avvise. parseGrantedOrigin parser disse til GrantedOriginParts, og originCovers rapporterer hvorvidt en tildelt origin dekker en regels match (f.eks. dekker en bred https://*/* tildeling genuint hver https-regel). matchAllowsOriginFallback rapporterer hvorvidt en regels sti er nøyaktig ORIGIN_FALLBACK_PATH (/*) — det sikre snittet av Chromium- og Gecko/Firefox 128-begrensningene på den dynamiske ugjennomsiktig-origin-reserven.

Dynamisk registrering

packages/webext/src/contentRegistration.ts registrerer ett dynamisk innholdsskript per brukertilføyd regel. Innebygde regler betjenes av de statiske content_scripts- oppføringene og registreres ALDRI dynamisk. desiredContentScripts filtrerer config.sites til ikke-innebygde, aktiverte regler hvis origin er isOriginGranted (delegerer til originCovers), mapper hver til et RegisteredContentScript med en stabil id (contentScriptId, en FNV-1a-hash av samsvaret slik at id-en kun bruker det API-sikre tegnsettet), og setter matchOriginAsFallback: true KUN når matchAllowsOriginFallback(rule.match) holder (en /*-sti). Å styre flagget på denne måten hindrer at én regel med ikke-konform sti får hele batch-registreringen til å bli avvist; en slik regel registreres uten utsprett-dekning ved design.

reconcileContentScriptsOnce leser de tildelte originene, den lagrede konfigurasjonen og de levende registreringene, og konvergerer deretter ved en EGENSKAPSBEVISST diff: sameRegistration projiserer både den ønskede deskriptoren og den levende tilbakelesingen på en normalisert form (ved å bruke hvert felts reelle WebExtension-standard — allFrames/matchOriginAsFallback har standard false, persistAcrossSessions har standard TRUE, runAt har standard document_idle) slik at et nyregistrert skript sammenlignes som LIK deskriptoren som produserte det og ingen reregistreringsløkke kan oppstå. Fordi det ikke finnes noen updateContentScripts, avregistreres et endret skript og registreres deretter på nytt. reconcileContentScripts pakker dette med et underveis-løfte (inFlight) slik at to nær-samtidige utløsere (en konfigurasjonsendring OG permissions.onAdded som begge utløses ved en nettstedstilføyelse) serialiseres i stedet for å kappløpe. registerContentReconciliation kobler utløserne (permissions.onAdded/onRemoved, onConfigChanged) og konvergerer én gang ved oppstart; den importeres kun av Chrome- og Firefox-bakgrunnsinngangene.

Opt-in-tillatelsesflyten

De statiske manifestene deklarerer optional_host_permissions (http://*/*, https://*/*) slik at en bruker kan tildele en vilkårlig ekstra nettvert under kjøring. packages/webext/src/options/options.ts addSite validerer HELE kandidatregelen (samsvar OG innholdsrot) gjennom normaliseSiteRule FØR den ber om noen tillatelse, utleder vertsoriginen med matchOrigin, og kaller browser.permissions.request innenfra Legg til-knappens klikkgest; kun ved tildeling legger den til en builtin:false-rad og persisterer gjennom den kanoniske skrivebanen.

Konfigurasjon, lagring og det kanoniske mønsteret

Den enkle valideringstrakten

normaliseConfig i packages/core/src/config.ts er den ene kanoniske lesebanen: den tvinger og begrenser hvert felt, faller tilbake til standarden ved alt ugyldig, og kaster aldri. Den er bygget fra validatorer per felt — normaliseBoolean, normaliseRenderMode, normaliseMaxLength, normaliseIgnoreHosts (reduserer hver oppføring til et bart punycode-vertsnavn via bareHostname), normaliseMatchColour/normaliseMismatchColour (styrt av safeColour.ts isSafeColour), normaliseCssSize, normaliseFontWeight, normaliseContentRoot (begrenset av CONTENT_ROOT_PATTERN/CONTENT_ROOT_MAX_LENGTH), normaliseMatchPattern (styrt av MATCH_PATTERN) og normaliseSites. normaliseSites forkaster avvisninger, dedupliserer etter match, TVINGER builtin: true på enhver regel hvis samsvar er likt en innebygd regels (lukker vektoren eksakt-samsvar-skygger-innebygd / dobbeltinjeksjon), reseder enhver manglende innebygd regel (slik at et manipulert lager ikke kan slippe en kjerneleverandør) og sorterer alfanumerisk etter match for en kanonisk rekkefølge. configFingerprint hasher kun de gjengivelsespåvirkende feltene (ekskluderer enabled, ignoreHosts og sites, som driver start/stopp og hopp i stedet for omflyt) via fnv1a.

Lagring

packages/webext/src/storage.ts pakker WebExtension-lagrings-API-et. configArea bruker browser.storage.sync når den er tilgjengelig (slik at innstillinger roamer) og faller tilbake til browser.storage.local (f.eks. på Thunderbird); configAreaName rapporterer hvilken som er aktiv. getConfig, setConfig og onConfigChanged trakter alle sin rå verdi gjennom normaliseConfig, slik at et misdannet eller manipulert lager aldri kan overlevere en ugyldig Config til en kaller. onConfigChanged ignorerer i tillegg hendelser fra det INAKTIVE lagringsområdet og endringer i urelaterte nøkler.

De to tilleggene gjenbruker den samme les -> normaliseConfig -> returner-trakten over sine egne vertslagre. Outlook-oppgaverutens extensions/outlook/src/roamingStorage.ts pakker Office.context.roamingSettings (synkron get/set, forpliktet med saveAsync); Gmail-tilleggets packages/gmail/src/propertiesStorage.ts pakker PropertiesService.getUserProperties() (per bruker, roamer på tvers av den brukerens enheter, JSON-kodet under én nøkkel, og trenger INGEN ekstra OAuth-omfang — aldri den delte ScriptProperties). Begge eksponerer getConfig/setConfig pluss de kort-relevante getIgnoreHosts/getHighlightMismatch-aksessorene og beskytter en utilgjengelig vertsflate med en dedikert feil. Gmail-hjemmesideutløseren gjengir et CardService- innstillingskort fra getConfig, og onSaveSettings-skjemainnsendingshåndtereren slår de parsede ignoreHosts/highlightMismatch sammen med den gjeldende konfigurasjonen og persisterer den gjennom setConfig; den kontekstuelle utløseren tråder ignoreHosts inn i adapteren og highlightMismatch inn i kortbyggeren, og leser defensivt slik at en lagrings- feil faller tilbake til standardverdier i stedet for å bryte meldingsanalysen.

Mønsteret kanonisk-konfigurasjon + målrettet-oppdatering

Tre skrivebaner leser den kanoniske konfigurasjonen, endrer nøyaktig én ting og skriver den tilbake — uten noensinne å forplikte ulagrede skjemaredigeringer eller gjengi skjemaet på nytt:

Verktøylinjehandlingens egen toggleEnabled (packages/webext/src/toolbarAction.ts) følger det samme mønsteret fra bakgrunnssiden.

Bygg per motor og manifestkontrakten

tooling/build.mjs driver et enkelt esbuild-bygg for hvert WebExtension-mål. TARGETS- beskrivelsen navngir hvert måls KILDEMÅL: Chrome, Firefox og Thunderbird leverer sin egen src/; Edge, Opera og Safari deklarerer chrome som sin kilde; Outlook-tillegget (Office.js) er sin egen kilde. ACTIVE_TARGETS (Chrome, Edge, Firefox, Opera og Thunderbird — Safari og Outlook er ekskludert) er det --all/--package bygger; Safari og Outlook bygges kun når de navngis eksplisitt, og Gmail-tillegget (Apps Script) er en separat esbuild-bunt (make build-gmail) utenfor --all-løkken. Den Gmail-bunten retter seg mot Apps Scripts V8-runtime, som verken har ES-moduler eller en nativ URL: den bygges som ESM og får deretter sin avsluttende export {…}-setning strippet (slik at utløserfunksjonene forblir globaler på toppnivå som Apps Script kan kalle), og den bunter en liten URL-polyfill (installert kun når URL er fraværende) slik at den delte kjernens new URL(...) løser lenker på serversiden. buildTarget rydder dist/<target>/, kopierer mål-spesifikk manifest.json og icons/ ordrett, bunter hvert kildeskript som IIFE (de injiserte INJECTED_SCRIPTS content.ts/messageDisplay.ts kan ikke være ES-moduler, og bakgrunner er klassiske arbeidere/hendelsessider også) og den delte innstillingssiden som ESM, og kopierer options.html/options.css fra packages/webext/src/options/. webextension-polyfill buntes inn i hvert skript.

tooling/version.mjs stempler versjoner: den tar MAJOR.MINOR fra rot- package.json og legger til et auto-inkrementerende BUILD-nummer (lest fra den høyeste eksisterende tredje komponenten på tvers av målmanifestene) inn i hvert mål- manifest, og holder dem i takt. De fleste mål bærer et enkelt manifest.json; the Outlook-tillegget bærer to versjonerte manifester (manifest.json og manifest.xml) og begge stemples, og _template-stillaset stemples i takt også. Rot-package.json holdes på samme versjon, men forblir gyldig semver (en firedelt versjon er tillatt i et manifest, aldri i package.json). tooling/icons.mjs (kjørt via make icons) rasteriserer den enkle vektorkilden assets/icon.svg til hvert måls icons/icon48.png og icons/icon128.png, og prøver cairosvg, deretter inkscape, deretter ImageMagick.

Manifestkontrakten — de nøyaktige påkrevde feltene per motor — er den eneste sannhetskilden i hver extensions/<engine>/test/manifest.smoke.test.mjs (f.eks. extensions/chrome/test/manifest.smoke.test.mjs sin assertManifestShape, som også bygger målet og bekrefter at hver refererte fil løses i dist-en). I henhold til prosjektets dedupliseringskonvensjon er ikke felt-for-felt-formen oppregnet på nytt her; se de røyktestene og «Manifest contract»- notatet i manuell testplan.

Internasjonalisering (i18n)

Reveal URLs er lokalisert til mange språk i tillegg til engelsk. Settet er enkeltkildet i packages/core/src/locales.json (SUPPORTED_LOCALES, innfødte navn og standarden), som både utvidelsen bunter (via den løste JSON- importen) og det rene Node-nettstedbygget leser. Hver ikke-engelsk streng er maskinoversatt og venter på menneskelig gjennomgang; opphavsmarkøren er per format (_locales bruker hver meldings description, nettstedets chrome-ordbøker registrerer det i site/i18n/README.md, og oversatte dokumentkilder bærer en HTML- kommentar på første linje).

Det finnes tre uavhengige lokaliseringsflater, bevisst ikke krysskoblet, og hver er delt langs en byggetid/runtime-linje.

Undertrykking av «Aktive nettsteder» for e-postklient (AD-3)

Et e-postklient-mål (Thunderbird i dag) ser allerede hver gjengitt melding, så «Aktive nettsteder»-redigeringsverktøyet per vert er overflødig der og fjernes. Beslutningen bæres av et dokumentert beskrivelsesflagg per mål, mailClient: true, på tooling/build.mjs sin TARGETS.thunderbird — aldri et hardkodet målnavn i bygge- eller UI-logikk — og er implementert i to halvdeler:

Sikkerhets- og personvernholdning

Testing