- JavaScript 77.3%
- HTML 8.4%
- CSS 7.8%
- Nix 6.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| extension | ||
| nix-modules | ||
| tests | ||
| .envrc | ||
| .gitignore | ||
| flake.lock | ||
| flake.nix | ||
| LICENSE | ||
| README.md | ||
Sourcerer
A chromium extension for automatically opening tabs according to some predefined standard.
A user provides a URL to poll and a script that runs on the page data. An informative example would be one that automatically opens up MRs for a GitLab repository every 10 minutes. It of course checks that the MR isn't already open in the browser.
How it works
A job is a URL, an interval, and a script. On every interval the service
worker polls the URL, hands the result to your script inside a
manifest-sandboxed page
(the only place MV3 permits new Function on user code), and opens every URL
the script returns as a background tab — skipping URLs already open in the
browser or previously opened by that job (closing a tab does not reopen it;
use Reset seen to forget them).
Scripts are the body of a function with context bound, running in strict
mode. Return a URL, an array of URLs, or {url} objects. Relative URLs are
resolved against the polled URL.
Fetch mode — the URL is fetched (with cookies, so logged-in sessions work) and:
context.bodyis the parsed response when it is JSON, otherwise the raw textcontext.textis always the raw response bodycontext.status/context.urlare the response status and final URL
DOM mode — the URL loads in a background tab, the page settles (give
client-rendered pages time), and context.doc is the parsed page. A <base>
tag is injected so a.href resolves relative links against the polled page.
The GitLab example
MR lists on GitLab's web UI are rendered client-side, so the reliable source is the JSON API (the options page ships this example under Load GitLab example):
// Poll: https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab/merge_requests
// ?state=opened&scope=all&order_by=updated_at&per_page=5
return context.body
.filter((mr) => mr.state === 'opened')
.map((mr) => mr.web_url);
Authentication (PATs and friends)
Polling private APIs needs a token. Sourcerer does this without leaking it:
- Secrets are stored under Options → Secrets: values are never displayed
again, never placed in the polled URL (URLs land in run logs and the
seen-set), never passed to the job script (which only ever sees
{url, status, body, text}), and never synced through your browser account. - A job references a secret by name and a header (e.g. header
PRIVATE-TOKEN, or headerAuthorization+ schemeBearer). The service worker attaches the header when it fetches. If the referenced secret is missing, the run is recorded as an error instead of sending an unauthenticated request. - Lifetime — off by default, secrets live in
chrome.storage.session: in-memory only, nothing written to the profile on disk, wiped when the browser exits (so re-enter them after a restart). If you opt into persistence, the store moves tochrome.storage.local: secrets survive restarts but sit unencrypted in the browser profile on disk, readable by anything with access to that profile. Turning the toggle back off wipes them from disk.
The honest boundary: anything running as your user (or with access to your unlocked browser) can read browser memory; no extension can defend against a compromised host. Session storage keeps the token off disk and out of every other channel (logs, scripts, sync, exports).
Development
Everything runs through nix; the devshell provides all tooling (no global installs, no npm/pip):
direnv allow # enter the devshell (or: nix develop)
nix flake check # package + lint + unit + fixture e2e
nix build .#sourcerer # extension in result/share/sourcerer + web-store zip
playwright test -c tests/e2e/playwright.config.js # fixture e2e (local, headed-able)
playwright test -c tests/e2e/playwright.network.config.js # gitlab.com e2e (needs network)
To try the extension by hand: nix build .#sourcerer, then in Chromium open
chrome://extensions, enable developer mode, and Load unpacked pointing at
result/share/sourcerer. (The PNG icons are rendered from icons/icon.svg
during the build; the repo's extension/ directory alone is not loadable.)
Layout
extension/— the extension: service worker, options page, popup, sandboxextension/lib/— pure logic (validation, result normalization, dedupe), unit-testedtests/unit/—node --testunit tests, no dependenciestests/e2e/— Playwright tests:specs/run hermetically against a local fixture server (part ofnix flake check);specs-network/hit real gitlab.com and run via the devshell/CInix-modules/— flake-parts modules (package, checks, devshell)
CI lives in .forgejo/workflows/ci.yml: nix flake check, the network e2e
suite, and every locally-built store path pushed to an Attic cache via
jingus/actions.