Thank you for helping improve @addon-core/inject-script.
This package provides one typed script-injection contract across Manifest V2 and Manifest V3. Contributions should preserve that cross-manifest boundary, keep unsupported capabilities explicit, and include verification for every affected adapter.
By participating, you agree to follow our Code of Conduct. Report vulnerabilities through the private process described in SECURITY.md, not through a public issue.
The repository uses a simplified GitFlow model:
maincontains released code.developis the integration branch and the normal pull-request target.feature/<short-name>branches start fromdevelop.- Releases are prepared by merging
developintomain. - After a successful release, the workflow syncs
mainback intodevelop.
Create a branch from the latest develop:
git switch develop
git pull --ff-only
git switch -c feature/<short-name>Node.js 20 is the default CI environment. Pull requests into main and the release gate also exercise Node.js 18, 20, and 22.
git clone git@github.com:addon-stack/inject-script.git
cd inject-script
npm ciUseful commands:
| Command | Purpose |
|---|---|
npm run dev |
Build continuously with tsup |
npm run build |
Create ESM, CJS, and declaration outputs |
npm run format |
Format supported files with Biome |
npm run format:check |
Check formatting without writing |
npm run lint |
Run Biome formatting and lint checks |
npm run lint:fix |
Apply safe Biome fixes |
npm run lint:fix:unsafe |
Apply safe and unsafe Biome fixes |
npm run typecheck |
Type-check package sources |
npm run test:types |
Type-check public API contract fixtures |
npm run test |
Build and run the Jest suite |
npm run test:ci |
Build, run Jest with coverage, and check dependency-release integration |
npm run test:tooling |
Verify Renovate, commitlint, and changelog integration |
npm run release |
Perform a real maintainer release through release-it |
npm run release is not a dry run. Do not execute it unless you are intentionally publishing a release and have the required maintainer access.
Keep changes focused and preserve the package's public design:
- Every operation has one explicit
target. - Target selectors remain mutually exclusive.
- Unsupported targets and options fail explicitly; they are never removed silently.
run()uses best-effort target isolation and returns package-owned success/failure outcomes instead of raw browser results.- Explicit target calls start synchronously before the first result is awaited so user activation is preserved.
- Callback arguments and results remain strictly JSON-compatible.
- MV2 injected code must stay self-contained because imports and caller closures do not cross the injection boundary.
- Runtime code must not introduce
evalornew Function. - Frame discovery, RPC fan-out, concurrency queues, and application-specific aggregation remain outside this package.
When changing public behavior, update the implementation, types, tests, and README together.
The test suite covers the following contracts:
tests/types.test.tsverifies compile-time API behavior.tests/inject-script.test.cjsverifies MV2/MV3 runtime behavior against the built package.tests/release-it.test.cjsverifies release versioning policy.tests/dependency-release.test.mjsverifies the real parser, commitlint CLI, and generated dependency release notes.
For adapter changes, cover the affected combinations where relevant:
- Manifest V2 and Manifest V3;
- callback-based
global.chromeand Promise-basedglobal.browser; - top frame,
allFrames, explicitframeIds, anddocumentIds; - success and failure outcomes, including execution, delivery, timeout, and target-gone errors;
- input ordering and synchronous initiation of explicit target batches;
- valid JSON data and runtime-only invalid values;
- preparation errors, native delivery failures, and timeouts.
MV2 payload tests execute the generated code in a separate process. Keep this path covered when changing serialized injected logic.
Before opening a pull request, run:
npm run lint
npm run typecheck
npm run test:citest:ci already runs type-contract tests and a production build before Jest.
Local hooks provide additional protection:
pre-commitrunslint-staged, which formats and lints supported staged source/config files.commit-msgvalidates Conventional Commits with commitlint.pre-pushrunstypecheckand the fulltest:cicommand.
GitHub Actions repeats lint, typecheck, tests, coverage, and production build checks. Pull requests into main and release runs use the full operating-system and Node.js matrix. The aggregate CI passed check also requires strict Renovate configuration validation; failed, cancelled, or skipped prerequisites fail this check.
All commits must follow Conventional Commits:
<type>(optional scope): <short summary>
[optional body]
[optional footer]
Common examples:
feat(target): add document targeting
fix(mv2): preserve partial timeout results
deps: update dependencies
ci: update workflow actions
refactor(results): centralize native normalization
test(types): cover interface arguments
docs: simplify the quick start
Use ! or a BREAKING CHANGE: footer for an incompatible public change:
feat!: replace the legacy target options
Commit messages are enforced by .husky/commit-msg and .commitlintrc.json.
Release versions are derived from commit history by release-it and @release-it/conventional-changelog:
- A breaking change increments major at
1.xand newer. - A breaking change increments minor while the package is on
0.x. featandrevertincrement minor.fix,deps,perf, andrefactorincrement patch.docs,test,build,chore,ci, andstyledo not trigger a release by themselves.- When several changes are present, the highest applicable increment wins.
Do not edit released changelog entries manually. CHANGELOG.md is generated from Conventional Commits during release.
The Update Dependencies workflow runs every Sunday at 02:15 America/Los_Angeles and can also be started from Actions → Update Dependencies → Run workflow. The time follows Los Angeles daylight saving rules.
Renovate reads renovate.json from main and updates only @addon-core/browser and @types/chrome, together with their required lockfile changes. Stable updates of any size are grouped in one deps/update-dependencies branch and one PR into main, including Chrome-types-only updates. Other direct dependencies and standalone lockfile maintenance are disabled.
On each run, Renovate refreshes its branch from the current main when needed. An open bot-managed PR is updated in place. After merging, a future update can recreate the same branch name. No available update means no empty PR. Manually committing to an open bot branch pauses automatic updates to protect those commits.
The bot creates deps: update dependencies commits. Ordinary dependency-only changes trigger a patch release and appear under 📦 Dependencies in the changelog and GitHub release notes. Keep the deps: ... title when squashing. Breaking markers and other commits still follow the normal release policy.
The App token triggers the normal PR checks: lint, source and public API type checks, build, tests, and strict Renovate configuration validation. Merging remains manual. Failed checks leave the PR open and do not publish a release. After merge, the existing Release workflow repeats the full checks, publishes the package, and syncs main back into develop.
Keep conventional-commits-parser 6.x as an explicit development dependency. The release tooling needs parser 6.x through optional peers, while commitlint needs parser 5.x. Explicitly declaring parser 6.x prevents npm lockfile-only regeneration from dropping the release parser. After changing it, regenerate the lockfile in a clean directory and verify npm ci and the release tooling tests.
Reuse the existing organization App addon-stack-dependency-updates; each package keeps its own workflow and dependency rules.
- In the
addon-stackorganization settings, open GitHub Apps → Configure for the existing installation and add inject-script under Only select repositories. Keep existing repositories selected. The existing App permissions are sufficient; no new App or key is needed. - Under Secrets and variables → Actions, add
inject-scriptto the selected repositories for both the organization variableRENOVATE_APP_CLIENT_IDand the organization secretRENOVATE_APP_PRIVATE_KEY. The workflow creates a short-lived token scoped to this repository only. Renovate does not need an npm publishing token. - Merge the setup from
developintomain. Both scheduled runs and the manual workflow entry need the workflow file on the default branch. - Run Update Dependencies manually and inspect the first PR. Configure the PR check CI passed as required for
mainand require an up-to-date branch. Do not give the dependency App a branch-protection bypass. - Account for release automation before enforcing branch rules:
release-itpushes version/changelog commits tomain, and the sync step writes todevelop. Rules requiring PRs for all writes need a suitable exception for the release writer; the dependency App is not the release writer.
For another package, repeat the installation and organization-variable/secret access steps, then add its workflow, dependency allowlist, release policy, tests, and documentation. GitHub supports changing the repositories available to an installed App and sharing organization secrets with selected repositories.
Open regular pull requests against develop and include:
- a concise explanation of the problem and the chosen solution;
- tests for changed behavior;
- documentation for public API or contract changes;
- migration notes for breaking changes;
- links to related issues when available.
Pull-request checklist:
- The change is focused and contains no unrelated edits.
- MV2 and MV3 implications have been considered.
-
npm run lintpasses. -
npm run typecheckpasses. -
npm run test:cipasses. - Public documentation is updated when necessary.
- Commit messages follow Conventional Commits.
Maintainers create release pull requests from develop into main.
The release workflow runs on pushes to main and through manual dispatch:
- Run the full CI matrix.
- Calculate the next version from Conventional Commits unless an exact version was provided.
- Update
package.jsonandCHANGELOG.md. - Create the release commit and Git tag.
- Create a GitHub Release.
- Publish the public npm package with provenance through trusted publishing.
- Sync
mainback intodevelop.
The workflow accepts optional prerelease and npm dist-tag inputs. It uses GitHub OIDC permissions for npm provenance and does not depend on a documented contributor NPM_TOKEN flow.
Publishing is a maintainer operation. Contributors only need to prepare a complete, verified pull request into develop.
By contributing, you agree that your contributions are licensed under the project's MIT License.