| `chromium.executablePath` | Optional | Full path to browser binary. `null` = auto-discover. |
| `chromium.userDataDir` | Optional | Browser profile dir for login state. |
| `chromium.maxConcurrentTabs` | Optional | Global max concurrent browser tabs across all harvest processes (default `5`). Enforced via CDP `Target.getTargets` browser self-check. |
Each source has a `concurrency` field (default `1` = serial). Set it to `2` or higher to fetch multiple articles in parallel using separate browser tabs:
The global tab limit (`chromium.maxConcurrentTabs`, default `5`) is enforced by the browser itself via CDP `Target.getTargets` — even when multiple harvest processes run simultaneously, the total open tabs never stays above the limit. A source's `concurrency` is silently capped to the global limit.
## Enable / Disable Source
...
...
@@ -50,7 +60,7 @@ A `.nhsource.json` is plain JSON:
| `source` | `{ id, name, homepageUrl, method, vaultFolder }` — the config entry, minus `vaultPath`, `enabled`, and `articleUrlPattern` |
| `source` | `{ id, name, homepageUrl, method, vaultFolder }` — the config entry, minus `vaultPath`, `enabled`, `articleUrlPattern`, and `concurrency` (all set by install) |
| `files` | Map of relative path → file content. Includes `helpers/<id>.md` (the recipe) and `helpers/<id>.fixtures.json` (if present). |
A recipe (`helpers/<id>.md`) is a YAML frontmatter file that declares the selectors, scroll behavior, and URL filters for a news source. When the harvester's `harvest.js` finds a recipe, it uses `fetcher-helper.js` (which reads the recipe's spec) instead of the legacy `fetcher-browser.js`/`fetcher-direct.js`.
A recipe (`helpers/<id>.md`) is a YAML frontmatter file that declares the selectors, scroll behavior, and URL filters for a news source. When the harvester's `harvest.js` finds a recipe, it uses `fetcher-helper.js` (which reads the recipe's spec) to drive Chromium via CDP. Browser-type sources without a recipe are no longer supported (`fetcher-browser.js` has been removed); direct-type sources fall back to `fetcher-direct.js`.
> **Precedence**: when a recipe exists, its `urlPattern`/selectors are the source of truth and the config's `articleUrlPattern` is **ignored**. A recipe also forces Chromium use regardless of `method` (since `fetcher-helper.js` drives the browser).
console.log(`\n Save to helpers/<source-id>.md (or re-run with --write <id>), then test: node scripts/preview.js --url <url> --recipe helpers/<source-id>.md --count 3`);