equipay

Project: equiPay

Objective

equiPay is an open-source Chrome extension that streamlines reporting Pay Transparency Law violations to the New York State Department of Labor (NYS DOL). Without it, a reporter has to manually screenshot a job posting, convert it to PDF, navigate to the DOL’s JavaServer Faces (.faces) complaint form, and hand-fill every field. equiPay automates evidence capture and pre-fills the complaint form, leaving the user to review, look up the employer’s business address, and submit.

License

MIT License

End-to-end flow

  1. User clicks the equiPay toolbar icon on a job posting (LinkedIn, Indeed, Glassdoor, etc.).
  2. content.js is injected into the posting tab. It identifies the job-description element via a per-site parser, temporarily neutralizes overflow/height on the JD’s scroll ancestors so the content flows into the natural document, and rasterizes the element with html2canvas.
  3. The rasterized PNG is composed into a PDF via jsPDF: a metadata header (URL, timestamp, employer, job title, listed location) followed by the screenshot paginated across letter-sized pages.
  4. The PDF is saved to the user’s Downloads folder (NYS_Violation_[Company].pdf) and kept as a base64 data URL so it can be reused later for auto-upload.
  5. content.js messages background.js with the extracted metadata + the PDF data URL. The service worker stashes everything in chrome.storage.local and opens a new tab to the NYS DOL complaint form.
  6. On that tab’s status: complete, background.js injects the bundled dist/formfill.js. The orchestrator picks a state adapter by window.location.host, loads the capture data + user profile from storage, runs the adapter’s declared text-field / radio / explanation / upload / comments mappings against the DOM, and renders a dismissable review panel built from the adapter’s reviewPanel config (requirements checklist, law links, business-address lookup helpers).

Architecture & Technical Decisions

Manifest V3

Activation model

Evidence capture (content.js)

LinkedIn URL normalization

Form-fill (formfill/dist/formfill.js)

Build (esbuild)

User profile (options.html / options.js)

What the extension deliberately does not do

File layout

File Purpose
manifest.json MV3 config, permissions, action + options page
background.js Service worker: action click → inject capture; handle CAPTURE_COMPLETE → open DOL tab + inject formfill
content.js Parser registry, DOM expansion, html2canvas capture, jsPDF composition
formfill/ (source) + dist/formfill.js (built) State-adapter registry, library helpers, orchestrator; built via esbuild
options.html / options.js Claimant profile editor
icons/icon-{16,48,128}.png + icon.svg Toolbar + Web Store icons (generated via npm run build-icons)
vendor/jspdf.umd.min.js 3rd-party PDF engine (vendored from jspdf)
vendor/html2canvas.min.js 3rd-party DOM-to-canvas rasterizer (vendored from html2canvas)
scripts/build-icons.mjs Build-time rasterizer for the icon SVG
docs/claude.md, docs/STORE_LISTING.md, docs/ADDING_A_STATE.md Design notes, Web Store copy, per-state adapter playbook
package.json npm deps + sync-lib + build-icons scripts

Permissions

Permission Why
activeTab Inject capture scripts on whichever tab the user clicks equiPay on
scripting executeScript into the capture tab and the DOL form tab
storage Claimant profile + per-capture data (chrome.storage.local)
tabs Open the DOL tab + listen for its status: complete
unlimitedStorage PDF data URLs can exceed the default 10MB quota on image-heavy postings
host_permissions: https://apps.labor.ny.gov/* Inject the form-fill bundle into the programmatically-opened NY DOL form tab. Additional states’ hosts are added here as adapters are registered.

Future Roadmap