Files
chemenu/sites/CONTRACT.md
T
torbenandClaude Opus 5.5 5994b2a88c
CI / verify (push) Successful in 8m8s
CI / pwsh (push) Successful in 2m40s
Release / release (push) Successful in 35s
Release / wiki-mirror (push) Skipped
raw: fetch behind a login - raw login stores the user's session as a secret outside the tree, fetch.routes sends a host through it or to an external fetcher, a handler's render goes through the optional browser component (preflight --with browser) (#169); 10.0.0-beta.8
Files changed:
- CHANGES.md
- INSTALL.md
- VERSION
- docs/how-to/README.md
- docs/how-to/fetch-pages-behind-a-login.md
- docs/how-to/ingest-a-source.md
- docs/reference/commands/README.md
- docs/reference/commands/instance-health.md
- docs/reference/commands/raw-material-and-uploads.md
- docs/reference/configuration.md
- docs/reference/exit-codes.md
- instructions/preflight.md
- instructions/wiki-ingest/SKILL.md
- raw/CONTRACT.md
- sites/CONTRACT.md
- tools/CONTRACT.md
- tools/README.md
- tools/chemenu/browser.py
- tools/chemenu/cli_contract.py
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/raw/__init__.py
- tools/chemenu/commands/raw/fetch.py
- tools/chemenu/commands/raw/login.py
- tools/chemenu/config_shapes.py
- tools/chemenu/fetch_routes.py
- tools/chemenu/reference_doc.py
- tools/chemenu/settings.py
- tools/chemenu/sites/__init__.py
- tools/chemenu/sites/data.py
- tools/chemenu/sites/site.schema.json
- tools/chemenu/tests/test_cli.py
- tools/chemenu/tests/test_config_shapes.py
- tools/chemenu/tests/test_doctor.py
- tools/chemenu/tests/test_preflight.py
- tools/chemenu/tests/test_preflight_pwsh.py
- tools/chemenu/tests/test_raw_fetch_routes.py
- tools/chemenu/tests/test_raw_login.py
- tools/chemenu/tests/test_site_files.py
- tools/chemenu/web_capture.py
- tools/chemenu/web_session.py
- tools/preflight.ps1
- tools/preflight.sh
- tools/requirements-browser.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
2026-10-11 20:50:23 +02:00

92 lines
6.7 KiB
Markdown

# sites/ - Handlers of this Instance
One JSON file per website, read by `tools/wikitool raw fetch`: what this instance knows about how
a website serves its pages, beyond what the stack's own handlers know. This file is the stack's;
the handler files beside it are the instance's own, and no release touches them. They are
tracked like `kb/` and `raw/`, so every clone fetches a website the same way.
The stack's handlers are the same kind of file, under `tools/chemenu/sites/handlers/` - open one
to see a working example, copy it to `sites/<name>.json` to start from it.
## A handler file
The file name without `.json` is the handler's name: lower-case letters, digits and hyphens. It is
what `disabled` in the `sites` section of the settings names, and what the capture header shows
as `site: <name> (instance)`.
| Field | Meaning |
|-------|---------|
| `host` | Required. The website's host name, lower case, matched exactly |
| `summary` | Required. One sentence on what the handler does - what a reader of the file needs |
| `path` | Optional. A regular expression matched against the start of the URL path; among the handlers of a host, the longest matching `path` wins. Without it the handler takes every path of the host |
| `parent` | Optional. The name of a handler for the same host whose `rewrites`, `drop` rules and `checks` this one starts with |
| `rewrites` | Optional, at most 5. Each `{"pattern", "replacement"}` is applied to the URL once, in order, as `re.sub(pattern, replacement, url, count=1)`; `{locale}` in a replacement is the `locale` of the `sites` section. The result is the standard form that is fetched; the URL as typed goes into the header as `requested_url` |
| `drop` | Optional, at most 40. Selectors for the page furniture to leave out of the derived text; the `.html` stays as received |
| `checks` | Optional, at most 3 - counting the parent's. Hint lines printed as `CHECK` when they apply to the page |
| `subpages` | Optional. Which links of a page are its subpages, so `raw fetch --scope` can fetch the page and them as one bundle. A child inherits the parent's block unless it has its own |
| `session` | Optional. For a website behind a login: the page `raw login` opens for the user to sign in on, and how a fetched page shows that it arrived signed in. A child inherits the parent's block unless it has its own |
| `render` | Optional, `true` or `false` (the default). `true` fetches the handler's pages through a headless browser, because they draw their content with JavaScript; the header then says `rendered: true`. A child inherits `true` from its parent |
A selector in `drop`, and the `count` of a check, is an object with any of `tag`, `id`, `class`
(one token of the element's classes), `attr` (an attribute that must be present, equal to `value`
when that is given) and `inside` (the name of an ancestor element). At least one of `tag`, `id`,
`class`, `attr` is required, and `value` needs `attr`; every field given must match.
A check is `{"text", "meta"?, "count"?}`. With `meta`, the line applies only when the page has a
`<meta name="...">` with that name and content, which replaces `{meta}` in the text. With `count`,
it applies only when at least one element matches the selector, and `{count}` is how many. With
neither, it applies to every page of the handler. A text that uses `{meta}` or `{count}` must have
the field that fills it.
`subpages` is `{"links", "prefix"?}`. `links` is a selector, as above, for the `<a>` elements whose
`href` names a subpage; `prefix` limits the path a subpage may have: `{page}` (the default) is the
path of the page itself, with a trailing slash, and a value starting with `/` is a literal path.
A link is resolved against the page's URL, loses its fragment and counts only on the page's own
host, under the prefix, and once; the page itself is never its own subpage. Nothing is fetched
because of it unless the user runs `raw fetch <url> --scope`; without it the page prints one
`CHECK` line with the number of subpages. At most `max_pages` of the `sites` section are fetched.
`session` is `{"login", "signed_in"?, "signed_out"?}`. `login` is the `http(s)` URL of the login
page. `signed_in` is a selector, as above, that matches only on a signed-in page - a logout link,
an account menu; `signed_out` one that matches only on a page that was not - a paywall teaser, a
"subscribe to read on" box. At least one of the two is required, and with both, both must hold. A
page fetched with the user's session that does not show the sign stops `raw fetch` with exit 42
and writes nothing, so pick a sign that the full article always carries, or its teaser always
does. The block alone changes nothing: the pages of the host are fetched with the session only
once the instance routes the host there (`fetch.routes` in the settings). Which website allows
its pages to be fetched this way is the user's to judge, and a handler records nothing about it.
`render` changes what the `.html` is: the page as the browser drew it, not the bytes the server
sent. Declare it only for a website whose text is missing from the HTML it sends. `raw login` and
a rendered fetch need the optional browser component (`tools/preflight.sh --with browser`).
`tools/chemenu/sites/site.schema.json` is the exact shape; every field outside the table above is
refused.
## Which handler applies
A handler of this instance for the same `host` and `path` as a stack handler **replaces** it - the
stack's is not applied at all for that host and path, and `wikitool doctor` names the replacement.
Handlers of the stack that inherited from the replaced one keep what they inherited. To keep what
the stack knows and add to it, give the file a `parent` instead and a `path` of its own.
A file named like a stack handler but for another host or path is refused, and so are two files
with the same host and path. A `parent` is a handler of the stack or of this instance; a missing
one, or a cycle, refuses the file.
## A file that cannot be used
A file that is not valid JSON, breaks the schema, has a pattern that does not compile, or breaks
a rule above is not applied. `wikitool doctor` fails and names the file and the reason. A
`raw fetch` whose URL has the host such a file names - or any URL, when no host can be read from
it - exits 1 before it fetches or writes anything: a handler that silently did not apply would
leave furniture in the text and a locale-specific page in the source. A broken file for another
host does not stop the fetch.
## Safety
A pattern runs only against the URL and its path, never against the page. A handler cannot run
code, read a file or reach another host: it is data, not a program. A `session` holds no
credential either - only where the user signs in and what a signed-in page looks like; the
session itself is stored outside the working tree by `raw login`.