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

tu-scraper

v1.2.1

Published

Production-ready TypeScript scraper and npm package to fetch official Tribhuvan University (TU) notices across all 9 verified institute, faculty and central office portals.

Readme


Overview

tu-scraper provides a clean, unified, zero-boilerplate API for developers, students, and institutions to programmatically access official Tribhuvan University notices. It eliminates manual HTML parsing, handles faculty-specific DOM variance, provides in-memory caching, and standardizes notice records into strict TypeScript interfaces.


Installation

# npm
npm install tu-scraper

# pnpm
pnpm add tu-scraper

# yarn
yarn add tu-scraper

# bun
bun add tu-scraper

Quick Start

import { getNotices, getLatest, searchNotices } from "tu-scraper";

// 1. Fetch notices from Institute of Science and Technology (IOST)
const iostNotices = await getNotices("iost");
console.log(iostNotices);

// 2. Fetch the single most recent notice from Institute of Engineering (IOE)
const latestIoe = await getLatest("ioe");
console.log(`Latest IOE Notice: ${latestIoe?.title} (${latestIoe?.date})`);

// 3. Search for a specific program/keyword across all 9 institutions
const bscResults = await searchNotices("BSc CSIT");

// 4. Search within a specific faculty
const examNotices = await searchNotices("exam", "fohss");

Supported Sources (9 Official Portals)

tu-scraper strictly targets only the 9 verified official Tribhuvan University portals:

| Source Identifier | Institution Name | Category | Official Portal URL | | :--- | :--- | :--- | :--- | | iost | Institute of Science and Technology | Institute | https://iost.tu.edu.np/notices | | fohss | Faculty of Humanities and Social Sciences | Faculty | https://fohss.tu.edu.np/notices | | ioe | Institute of Engineering | Institute | https://ioe.tu.edu.np/notices | | ac | Amrit Science Campus | Campus | https://ac.tu.edu.np/notices | | iaas | Institute of Agriculture and Animal Science | Institute | https://iaas.tu.edu.np/notices | | iof | Institute of Forestry | Institute | https://iof.tu.edu.np/notices | | foe | Faculty of Education | Faculty | https://foe.tu.edu.np/notices | | fol | Faculty of Law | Faculty | https://fol.tu.edu.np/notices | | tu | Tribhuvan University (Central Office) | University | https://tu.edu.np/notices | | all | Aggregates all 9 institutions | Meta-source | All supported URLs above |


Return Type (Notice)

Every scraper normalizes results into the standard Notice contract:

export interface Notice {
  /** Unique ID extracted from notice detail URL */
  id: string;
  /** Full notice title (whitespace cleaned and trimmed) */
  title: string;
  /** Origin source identifier */
  source: NoticeSource;
  /** Date of publication (if available on source page, else undefined) */
  date?: string;
  /** Canonical URL to notice page */
  url: string;
  /** Direct PDF or attachment link (if available, else undefined) */
  pdf?: string;
}

Note: In accordance with data integrity rules, if a date or PDF is unavailable on the source webpage, the property is left undefined rather than populating fabricated data.


Core API Reference

getNotices(source, options?)

Fetches all active notices from the specified source or all sources combined.

function getNotices(
  source: "iost" | "fohss" | "ioe" | "ac" | "iaas" | "iof" | "foe" | "fol" | "tu" | "all",
  options?: ScrapeOptions
): Promise<Notice[]>;

Behavior with "all":

When calling await getNotices("all"), scrapers run concurrently across all 9 portals via Promise.allSettled. If an individual faculty server is temporarily unreachable, the remaining successful faculty results are combined and returned gracefully.


getLatest(source, options?)

Fetches the single latest notice from the given faculty.

function getLatest(
  source: SourceQuery,
  options?: ScrapeOptions
): Promise<Notice | null>;

searchNotices(query, source?, options?)

Performs a case-insensitive title search against notices from the specified source (default: "all").

function searchNotices(
  query: string,
  source?: SourceQuery, // Defaults to "all"
  options?: ScrapeOptions
): Promise<Notice[]>;

clearCache()

Clears the internal in-memory cache manually.

import { clearCache } from "tu-scraper";
clearCache();

ScrapeOptions

Customize network timeouts and cache behavior:

export interface ScrapeOptions {
  /** Request timeout in milliseconds (default: 10000ms) */
  timeout?: number;
  /** Bypass internal in-memory cache (default: false) */
  bypassCache?: boolean;
  /** Custom User-Agent header */
  userAgent?: string;
}

Error Handling

tu-scraper exports clear, typed errors:

| Error Class | Trigger | Properties | | :--- | :--- | :--- | | InvalidSourceError | When an unsupported source string is provided | invalidSource, allowedSources | | NetworkError | When an HTTP request fails or status code is not 200 | url, statusCode, originalError | | TimeoutError | When a request exceeds the specified timeout | url, timeoutMs | | ParseError | When the webpage structure is corrupted | source, url |

import { getNotices, InvalidSourceError, NetworkError, TimeoutError } from "tu-scraper";

try {
  const notices = await getNotices("ioe", { timeout: 5000 });
} catch (error) {
  if (error instanceof InvalidSourceError) {
    console.error(`Invalid source: ${error.invalidSource}`);
  } else if (error instanceof TimeoutError) {
    console.error(`Request timed out after ${error.timeoutMs}ms`);
  } else if (error instanceof NetworkError) {
    console.error(`Network error: ${error.message} (HTTP ${error.statusCode})`);
  }
}

Examples

1. Daily Telegram / Discord Bot Notification

import { getLatest } from "tu-scraper";

async function notifyLatestNotice() {
  const latest = await getLatest("ioe");
  if (latest) {
    await sendDiscordWebhook({
      content: `📢 **New IOE Notice**: ${latest.title}\n🔗 ${latest.url}\n📅 ${latest.date || "N/A"}`
    });
  }
}

2. Express.js REST API Microservice

import express from "express";
import { getNotices, searchNotices, isValidSource } from "tu-scraper";

const app = express();

app.get("/api/notices/:source", async (req, res) => {
  const { source } = req.params;
  if (!isValidSource(source)) {
    return res.status(400).json({ error: "Invalid source" });
  }
  const data = await getNotices(source);
  res.json({ success: true, count: data.length, data });
});

app.get("/api/search", async (req, res) => {
  const q = String(req.query.q || "");
  const src = (req.query.source as any) || "all";
  const results = await searchNotices(q, src);
  res.json({ query: q, results });
});

app.listen(8080, () => console.log("TU Notices API running on port 8080"));

Testing

The package includes a comprehensive test suite with 30 unit tests covering:

  • All 9 source adapters using real saved HTML fixtures
  • Notice normalization and schema compliance
  • Search matching & edge cases
  • getLatest behavior
  • Invalid source error assertions
  • Caching TTL and invalidation
  • "all" multi-source aggregation

Run the test suite:

npm test

Changelog

See CHANGELOG.md for the full release history.


License

MIT © Ankit Khatri KC