Skip to main content

Anilist

@magistr/anilistv2026.09.01.1· 9d agoMODELS
01README

AniList GraphQL API — search and fetch anime/manga, media details, user lists, trending, and a Telegram activity notifier with completed/dropped detection

02Release Notes

2026.09.01.1

Added

  • update-progress gained an optional customLists argument, and userlist now selects customLists so the current membership can be read first.

AniList treats customLists as an absolute set on write: it replaces the entry's entire custom-list membership with whatever is passed. Sending an omitted or partial list therefore silently drops the entry from every other custom list.

Two consequences are baked into the implementation rather than left to the caller to remember:

  • the argument is forwarded only when the caller explicitly supplies one, so an ordinary progress update cannot wipe membership as a side effect;
  • [] is the documented way to remove an entry from all custom lists.

model.version/manifest.yaml move 2026.08.30.1 -> 2026.09.01.1. Purely additive — no stored resource is reshaped.

03Models1
@magistr/anilistv2026.09.01.1extensions/models/anilist.ts

Global Arguments

ArgumentTypeDescription
mediaTypeenumDefault media type for queries
accessToken?stringAniList personal access token — required for update-progress mutations. Get at: https://anilist.co/settings/developer
clickhouseUrl?stringClickHouse HTTP base URL (e.g. http://host:8123) — required for the ingest-scores charting pipeline
clickhouseDatabasestringClickHouse database holding anilist_metadata + user_scores
clickhouseUserstringClickHouse HTTP user
clickhousePassword?stringClickHouse HTTP password (wire via vault)
fn lookup(title: string, userName?: string, limit: number)
Answer 'have I seen X, and what did I score it' from the LOCAL ClickHouse mirror instead of the AniList API — works while AniList is down or rate-limiting. Matches a title substring case-insensitively across romaji/english/native. Only covers what ingest-scores has mirrored (COMPLETED + CURRENT entries that carry a score), so an unscored or never-ingested entry reads as not-found rather than not-watched.
ArgumentTypeDescription
titlestringTitle substring, matched case-insensitively against romaji/english/native
userName?stringRestrict scores to this user (case-sensitive — user_scores ORDER BY is). Omit to show every user who scored the match.
limitnumberMax distinct media to return, most popular first
fn search(query: string, type?: enum, perPage: number, page: number, fetchAll: boolean)
Search for anime or manga by title. Set fetchAll to paginate through all results automatically.
ArgumentTypeDescription
querystringSearch term
type?enumOverride default media type
perPagenumberResults per page (ignored when fetchAll is true)
pagenumberPage number (ignored when fetchAll is true)
fetchAllbooleanFetch all pages automatically (max 5 pages / 250 results)
fn get(id: number)
Get detailed info for a specific anime/manga by AniList ID
ArgumentTypeDescription
idnumberAniList media ID
fn userlist(userName: string, type?: enum, status?: enum)
Get a user's public anime/manga list (returns all entries; AniList returns full lists in one response)
ArgumentTypeDescription
userNamestringAniList username
type?enumOverride default media type
status?enumFilter by list status
fn trending(sort: enum, type?: enum, perPage: number, page: number, fetchAll: boolean)
Get trending or popular anime/manga. Set fetchAll to paginate through all results automatically.
ArgumentTypeDescription
sortenumSort order
type?enumOverride default media type
perPagenumberResults per page (ignored when fetchAll is true)
pagenumberPage number (ignored when fetchAll is true)
fetchAllbooleanFetch all pages automatically (max 5 pages / 250 results)
fn watching(userName: string)
Get CURRENT anime list enriched with nextAiringEpisode info — shows what's airing and when.
ArgumentTypeDescription
userNamestringAniList username
fn seasonal(season?: enum, seasonYear?: number, perPage: number, page: number)
Browse anime for a specific season (WINTER/SPRING/SUMMER/FALL) and year. Defaults to current season.
ArgumentTypeDescription
season?enumSeason (defaults to current based on current month)
seasonYear?numberYear (defaults to current year)
perPagenumberResults per page
pagenumberPage number

Resources

search(1h)— Anime/manga search results
media(1h)— Detailed media info
userlist(1h)— User anime/manga list
trending(1h)— Trending or popular media
watching(1h)— CURRENT list enriched with next airing episode info
seasonal(6h)— Anime airing in a specific season/year
watchProgress(1h)— Result of update-progress or set-score mutation
activityFeed(7d)— Recent list activity across tracked users, with detected completed/dropped verdicts, formatted Telegram messages and delivery outcome
activityCursor(infinite)— Per-user dedupe cursor for recent-activity: last delivered activity id + cached AniList userId
userlistScored(30d)— Per-user score-ingest summary (observability): rows written to user_scores, media ids seen, list-chunk pagination cap
ingestRun(90d)— Score-ingest run marker across all users (observability ONLY — not a provenance gate)
metadataRefresh(90d)— Marker for a metadata-only backfill: how many score media ids lacked metadata and how many rows were written
lookupResult(7d)— Local title lookup against the ClickHouse mirror: which media matched and who scored them
04Previous Versions6
2026.08.19.1
2026.08.17.1

2026.08.17.1

Reconciles this package with the main-repo copy that had forked away from it, and adds completed/dropped verdict detection to the activity notifier.

The fork. ~/dev_tmp/swamp/extensions/models/anilist.ts — not a swamp extension source of this package, just a parallel copy — is what actually runs and what is deployed to swamp serve. The two diverged from a common ancestor at 2026.07.27.1: this package gained the AL1-AL4 hardening, JSDoc and the upgrades chain, while the main repo independently gained the #AniList heading hashtag (2026.07.30.2) and the ClickHouse lookup method (2026.08.16.1). Neither side ever had the other's work. Resolved as a real three-way merge against the 2026.07.27.1 ancestor; the four conflicts were all "both sides added code next to the same comment". Consequence worth stating plainly: production had been running without the AL1-AL4 hostile-response guards this package shipped on 2026-08-02. This release is the first time they reach the deployed notifier.

Added

  • Completed/dropped verdict detection on recent-activity. dropped activity was filtered out entirely by isConsumptionActivity (commented as "list housekeeping … noise") and completed was folded into a watch range by mergeActivities, so neither was ever a visible event — a 7-day sample over the live tracked users surfaced three real drops that had been silently discarded. New isStatusChangeActivity / isReportableActivity (which widens the consumption set by exactly one status), partitionActivities (splits verdicts out of the progress rows) and mergeStatusChanges (dedupes per (user, mediaId, status); best known score wins, since score enrichment is best-effort and a duplicate may carry null).
  • Both renderers grew an optional trailing status-changes section: buildRichMessage adds a Status changes paragraph (bold profile-linked user, verb, linked title, score) and formatActivityMessages the HTML equivalent. The footer became N users · M titles · K status changes, with users counted across BOTH sections so a drop from someone with no progress rows is not uncounted, and the titles clause dropped when only verdicts landed.
  • statusChanges + statusChangeCount on the activityFeed resource — added to the resource SCHEMA as well as the write, since the schema strips unknown keys and would otherwise have made the verdicts unqueryable while the digest still displayed them.
  • includeStatusChanges method argument (default true) — set it false to restore the previous progress-only digest with no redeploy, since it is a workflow step input rather than anything baked into the extension.

Changed

  • Both renderers take the status list as an OPTIONAL second parameter, so every pre-existing call site and assertion is untouched.
  • buildRichMessage opens with a '#AniList activity' header and sanity: model exposes exactly the N documented methods updated — they had been asserting pre-fork behavior (no hashtag, 11 methods) that the main repo superseded long ago.

Modified 1 models

2026.08.02.1

2026.08.02.1

Real fixes for the four latent bugs (AL1-AL4) filed against gql()'s request/retry path in the local anilist-latent-bugs issue-lifecycle model, plus the test-backfill work that originally characterized them (both land in this one release). anilist.ts is modified — the four fixes are localized to gql(), its rate-limit state, and the call sites that thread the new per-invocation client through; the pure-helper surface and every GraphQL query/mutation const are untouched.

Fixed

  • AL1 — a 200 response with {data:null} and no errors[] no longer null-derefs downstream with an uncaught TypeError: gql() now throws a typed Error ("200 response with null data and no errors") when both are absent. recent-activity's per-user activities fetch is now wrapped in a try/catch (mirroring the existing user-id-resolution step), so one user's hostile/malformed page is recorded in usersFailed and the fan-out continues for every other tracked user instead of aborting the whole run.
  • AL2 — a non-JSON 200 response body (e.g. a WAF/CDN error page) no longer crashes gql() with an uncaught SyntaxError: the body is now read once via response.text() and the JSON.parse is guarded, producing the same handled AniList API error: non-JSON 200 response body: <body> shape the non-ok path already used.
  • AL3 — 429-in-body detection no longer relies on an exact e.status === 429 numeric equality: a numeric-string status ("429"), or a missing status field paired with a rate-limit-shaped message ("rate limit" / "too many request"), are now both recognized and retried (sleep 60s) instead of falling through to the generic AniList GraphQL errors: throw.
  • AL4 — the module-level rateLimit object (shared by every request in the process) is gone. gql() is now built per-invocation via makeGql(authToken?), which closes over its own {remaining, resetAt} state; fetchAllPages/refreshMetadata take the caller's gql client as a parameter instead of reaching for a module-level one, and every method's execute() creates exactly one client for its own invocation. A low-remaining/future-reset response from one method call can no longer force an unrelated later call to pre-flight-sleep. Deleting the global means any un-migrated call site fails deno task check rather than silently reverting to shared state.

Adversarial suite: six pins flipped from characterizing the bug to asserting the fix (anilist_adversarial_test.ts) — the two data:null pins (search and recent-activity), the non-JSON-body pin, the two 429-in-body-shape pins (now FakeTime retry-success assertions matching the numeric-status sibling), and the module-rateLimit-coupling pin (now asserts independent per-invocation state, waited === 0). The contract-fixture, methods, coverage, and property-invariant-flow suites are unaffected by the fix (they exercise well-formed fixtures) — 144 tests before and after, same count, six assertions changed shape. Suite header comments across all five test files reworded: the four suites above still characterize unchanged behavior, and the adversarial suite now documents AL1-AL4 as fixed rather than pinned bugs.

manifest.yaml/anilist.ts version bumped to 2026.08.02.1; added an identity upgrades[] entry (toVersion: "2026.08.02.1", upgradeAttributes: (old) => old) since globalArguments is unchanged. JSDoc added to the ~23 exported symbols in anilist.ts (converted from // block comments), earning the symbols-docs quality factor now that anilist.ts is an active fix target rather than a frozen characterization surface — quality.yaml's ratchet moves from a measured 92% to a measured 100% (14/14, allPassed).

Out of scope: update-progress/set-score's mutation paths use raw fetch+resp.json() (not gql()), share AL2's non-JSON-body shape, but are not in the filed catalogue and are already null-safe via optional chaining — left unchanged.

Test backfill (wave-2c, ext-quality-bf-anilist, child of ext-quality-test-backfill)

  • Added extensions/models/anilist_test.ts (contract-fixture — pins the concrete AniList GraphQL wire shapes for all 11 methods from fixtures/*.json, plus the three ingest query-const invariants (LIST_INGEST_QUERY / USERLIST_QUERY / METADATA_INGEST_QUERY) migrated from the old file), anilist_methods_test.ts (methods — every one of the 11 methods' happy+error paths, args schema-parsed via model.methods.<m>.arguments.parse() before execute(); update-progress/

Notes trimmed at a line boundary to fit the registry's 4900-byte per-version cap. Full section: https://github.com/umag/swamp-workspace/blob/d850d8e8372d4c0008c9245959a090b37095de7a/anilist/CHANGELOG.md

2026.07.27.1

Merge pull request #61 from umag/feat/anilist-model-full

feat(anilist): bring @magistr/anilist up to the full model

Modified 1 models

2026.07.16.2

Release 2026.07.16.2 — align model versions with manifests

Maintenance release across the @magistr extensions. For most packages this carries no functional change: the only edit is the model's version: field, brought back in line with its manifest version so the published model type version and the package version no longer drift.

Functional changes in this release are limited to:

  • anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.

  • arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.

Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.

Added 1, removed 1 models

2026.05.25.1

Merge pull request #5 from umag/extensions/jscad-stl-pair

extensions: add 15 more @magistr extensions + auto-discover CI

05Stats
A
100 / 100
Downloads
1
Archive size
60.4 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License declared1/1earned
  • Verified public repository2/2earned
06Platforms
07Labels