npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

f977zone

v1.0.0

Published

Zero-dependency scraping modules for game-forum cataloging — thread import and the forum's SAM catalog API. Framework-agnostic; works in Node.js >= 18.17, Bun, or any JS runtime with global fetch.

Readme

f977zone

Zero-dependency scraping modules for game-forum cataloging — thread import and the forum's SAM catalog API. Minimal by design: pure fetch, JSON, and hardened HTML parsing — nothing else.

These modules handle the hard parts for you:

  • forum — XenForo thread import: title cleanup, overview, developer/version/OS fields, engine & platform detection, genre spoilers, download links (allowlisted hosters), hotlinked screenshots
  • sam — the forum's own "Latest Games & Updates" catalog API: the full game list (30 items/page) with cover + screens, no login required

Every function returns normalized, sanitized data — safe to render in your own UI.


Features

  • 🔌 Zero dependencies — pure Node.js fetch (the thread parser is regex-based; no cheerio/jsdom). Runs on Node ≥ 18.17, Bun, Deno, and serverless runtimes
  • 🧩 Modular — import one module or all of them
  • 🔐 Security built-in — download links allowlisted by hoster, URL scheme filtering, HTML stripping, SSRF-safe URL helper
  • 🛡️ Hardened parsing — survives markup changes, dedupes, discards ads/tracking, strips category prefixes
  • 🌐 Configurable — user agent + session cookie via env vars
  • 📦 Tree-shakeable ESM — import only what you need

Supported sources

| Codename | Type | Auth | Export path | | --- | --- | --- | --- | | forum | Game forum (XenForo thread import) | session cookie (optional) | f977zone/f95zone | | sam | Forum catalog API (latest_data.php?cmd=list) | none | f977zone/sam |

Actual base URLs live in src/*.js (as exported constants F95_BASE / SAM_BASE) — see .env.example for the cookie setup.


Requirements

  • Node.js ≥ 18.17 (global fetch required) — or any runtime that provides fetch
  • forum source (optional): a session cookie (F95_COOKIE) for login-gated content (genre spoilers, download buttons)

Installation

npm install github:bagasss859-sketch/f977zone

No build step, no transitive dependencies.


Quick start

import { f95ImportFromUrl } from 'f977zone';
import { fetchSamPage, fetchSamMeta } from 'f977zone/sam';

// 1. Import a single thread (needs F95_COOKIE for full data)
const game = await f95ImportFromUrl('https://f95zone.to/threads/game-name.123456/');
console.log(game.title, '|', game.developer, '|', game.downloads.length, 'links');

// 2. Browse the whole catalog — no login
const meta = await fetchSamMeta();                 // tag/prefix names
const { items, count } = await fetchSamPage('games', 1);
console.log(count, 'games — first:', items[0]?.title);

Configuration

Credentials — where to get the secret

F95_COOKIE (optional but recommended)

Some forum content (genre spoilers, download buttons) is hidden from guests:

  1. Log in to the forum with a regular account.
  2. Press F12Network → open any thread page.
  3. Under Request Headers, copy the entire Cookie value (starts with xf_csrf=...; xf_session=...).
  4. Paste it into .env as F95_COOKIE="xf_csrf=...; xf_session=...".

Cookies expire — refresh when imports report missing data. The SAM catalog API (f977zone/sam) works without any login.

Environment variables

| Variable | Default | Description | | --- | --- | --- | | F95_COOKIE | — | forum session cookie (xf_csrf=...; xf_session=...) | | F95_USER_AGENT | Chrome UA | forum user agent |


How the scraping works

forum (src/f95zone.js) — XenForo thread import

  1. Fetches the thread page with browser-mirroring headers (User-Agent, Accept-Language, Referer) plus the optional session cookie.
  2. Isolates the first post (bbWrapper block) — everything else (sidebar, replies, ads) is discarded.
  3. Title cleanup: strips the | F95zone suffix and layered category prefixes (VN - Ren'Py - , Android - ... - ), then decodes HTML entities.
  4. Field extraction from the post text: Overview: description, then labeled lines (Developer / Version / OS / Language / Censored) with a label-anchored regex.
  5. Engine & platform detection by keyword (Ren'Py, RPG Maker, Unity, ... → Windows/Android/Mac/Linux/iOS; defaults to Windows).
  6. Genres: spoiler bbCodeBlock-content (needs login) → inline Genre: x, y → bracketed title parts as fallback.
  7. Download links: anchors to allowlisted hosters (Mega, Google Drive, MediaFire, PixelDrain, WorkUpload, GoFile, 1Fichier, UploadHaven, KatFile, RapidGator, NitroFlare, ...) plus forum attachment/thread-download links. Labels come from anchor text (e.g. MEGA) or the hoster name.
  8. Screenshots: hotlinked — the full-size URL is derived from the /thumb/ URL; banners/logos/gifs are filtered out (max 10). Cover from og:image, falling back to the first screenshot.
  9. login_required flag set when the page says links are hidden from guests.

sam (src/sam.js) — catalog API

The forum's own "Latest Games & Updates" app exposes latest_data.php?cmd=list — a JSON API returning the entire game catalog (26k+ items across categories) sorted by date, 30 per page, with cover + screenshots. No login. Metadata (tag/prefix names) is injected into the app's HTML as var latestUpdates = {...}; — parsed once and used to decorate items (tagName(), prefixName(), prefixIsEngine()).


API reference

forum — f977zone/f95zone

| Function | Description | | --- | --- | | f95ImportFromUrl(url) | Full thread import → game data (or { error }) | | f95ParseThread(html, threadUrl) | Parse thread HTML → normalized game data | | f95Fetch(url, timeoutMs) | Browser-mirroring fetch (cookie + UA) → { html?, error?, finalUrl? }, never throws | | f95GetFirstPost(html) | Extract the first post body (bbWrapper) | | f95HtmlToText(html) | HTML → clean text | | f95FieldValue(text, label) | Value after a label, e.g. Developer: | | f95GetAttachments(postHtml) | All attachment image URLs | | f95ThumbToFull(url) | Thumb URL → full-size URL | | f95DetectEngine(title, text) | Engine detection (Ren'Py, RPG Maker, Unity, ...) | | f95DetectPlatforms(osText, titleText) | Platform detection (Windows/Android/Mac/Linux/iOS) | | f95ExtractScreenshots(postHtml) | Full-size screenshot hotlinks (max 10) | | f95ExtractDownloads(postHtml) | Download links (allowlisted hosters) | | isF95ThreadUrl(url) | Thread URL validation |

sam — f977zone/sam

| Function | Description | | --- | --- | | fetchSamMeta() | Catalog metadata (tags/prefixes) from the app page | | fetchSamPage(cat, page) | One catalog page (30 items) — no login | | totalPages(count) | Page count for a category | | tagName(meta, id) / prefixName(meta, id) | Decorate items with tag/prefix names | | prefixIsEngine(meta, id) | Is the prefix an engine? | | slugFromTitle(title) / threadUrlFromId(id, title) | Placeholder thread URL helpers |


Security

  • Download links are allowlisted by hoster; forum-internal download/attachment URLs are the only non-hoster exception.
  • safeHttpUrl() forces http(s):// and rejects javascript:, data:, vbscript: — applied to covers/attachments before they leave the module.
  • stripHtml() removes tags/scripts/styles from external text (titles, overviews, labels).
  • Screenshots are hotlinked, never downloaded by this library.
import { safeHttpUrl, stripHtml, isSafeExternalUrl } from 'f977zone';

Error handling

f95Fetch never throws — it returns { error }. f95ImportFromUrl returns { error } with a descriptive message (invalid URL, fetch failure, or login-required). Other helpers throw plain Errors on invalid input. Network timeouts abort via AbortSignal.timeout (default 25 s).

Examples

Plain Node script — catalog → detail

import { fetchSamPage, fetchSamMeta, f95Fetch, f95ParseThread, threadUrlFromId } from 'f977zone';

const meta = await fetchSamMeta();
const { items } = await fetchSamPage('games', 1);
const first = items[0];

// Fetch the thread and parse it
const url = threadUrlFromId(first.thread_id, first.title);
const res = await f95Fetch(url);
if (res.html) {
  const data = f95ParseThread(res.html, url);
  console.log(data.title, '|', data.developer, '|', data.downloads.length, 'links');
}

Testing

npm test
  • test/import.test.mjs — all exports/subpaths resolve
  • test/f95.test.mjs — offline thread-parser tests (HTML fixture, no network)

Disclaimer

This library is for educational and personal-use purposes only. The scraped sources are third-party websites; this project is not affiliated with, endorsed by, or connected to any of them. You are responsible for:

  • Complying with the terms of service of the sites you scrape
  • Complying with the laws of your jurisdiction regarding adult content
  • Respecting rate limits — cache responses on your side; the built-in helpers keep request volume low

License

MIT © Bagas