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

taleem-kernel

v1.6.0

Published

Taleem data and business-logic kernel. HTTP is only an adapter.

Downloads

890

Readme

Taleem Kernel — API Reference

v1.6.0

import kernel from "taleem-kernel";

Shared DB/domain layer for Taleem apps. Owns the canonical Prisma/SQLite schema. Exposes domain modules — not raw CRUD.


Module Index

| Module | Purpose | |---|---| | kernel.user | App user registration/auth | | kernel.admin | Course-scoped + super admin accounts | | kernel.course | Course CRUD + access control | | kernel.group | Structural grouping within a Course | | kernel.library | Authored content (articles, players, etc.) | | kernel.communication | Discussion threads on Library items | | kernel.subscription | User access to a Course | | kernel.svg / kernel.image / kernel.audio | Independent reusable assets | | kernel.jwt | Low-level token utility (avoid calling directly) | | kernel.db | Raw Prisma client (escape hatch) | | kernel.config | Kernel config | | kernel.shutdown() | Close DB connection |

Course
  └─ Group
       └─ Library ─── Communication
Course ─── Subscription

All relations onDelete: Restrict — nothing cascades. Detach children before deleting a parent.


User

kernel.user.list()
kernel.user.get(id)
kernel.user.getByEmail(email)
kernel.user.emailToId(email)
kernel.user.register({ email, password })
kernel.user.login(email, password)      // → JWT
kernel.user.createToken(user)
kernel.user.authenticate(token)         // → User; type: "user" tokens only
kernel.user.update(id, data)
kernel.user.delete(id)

Admin

courseSlugs is a plain JSON string field, not a relation — script-managed, unvalidated at the DB level.

kernel.admin.list(filters)              // { isActive }
kernel.admin.get(email)
kernel.admin.login(email, password)     // → JWT
kernel.admin.createToken(admin)
kernel.admin.authenticate(token)        // → Admin; type: "admin" tokens only
kernel.admin.create(data)               // { email, password, role?, courseSlugs: JSON.stringify([...]) }
kernel.admin.update(email, data)
kernel.admin.delete(email)

kernel.admin.isAdmin(email, courseSlug)     // → boolean, course-scoped
kernel.admin.isSuperAdmin(email)            // → boolean, role === "SUPER_ADMIN"

kernel.admin.assignCourse(email, courseSlug)    // adds slug; idempotent; throws if admin or course not found
kernel.admin.unassignCourse(email, courseSlug)  // removes slug; no-op if not assigned; throws if admin not found
  • role defaults to ADMIN. SUPER_ADMIN is set explicitly at create/update.
  • isSuperAdmin and isAdmin are fully separate — a super admin is not automatically authorized on a course they haven't been assigned via assignCourse. Deliberate design choice; revisit only if a use case forces it.
  • Auth (authenticate) and authorization (isAdmin/isSuperAdmin) are always two separate calls.
const admin = await kernel.admin.authenticate(token);
const allowed = await kernel.admin.isAdmin(admin.email, courseSlug);
const isSuper = await kernel.admin.isSuperAdmin(admin.email);

JWT (low-level — avoid direct use)

kernel.jwt.sign(payload)
kernel.jwt.verify(token)

Go through kernel.user.* / kernel.admin.* instead. No shared identity routing between User and Admin.


Course

Plain CRUD. No artifact/seed step.

kernel.course.list(filters)             // { access, isActive }
kernel.course.get(slug)
kernel.course.create(data)              // { slug, title, access }
kernel.course.update(slug, data)
kernel.course.delete(slug)              // throws if Groups/Subscriptions still reference it
kernel.course.authorize(userId, courseSlug)   // → boolean; throws/denies per tier

| access tier | authorize(null, slug) | logged in, no subscription | logged in, active subscription | |---|---|---|---| | OPEN | ✅ | ✅ | ✅ | | MEMBERS | ❌ throws | ✅ | ✅ | | SUBSCRIPTION | ❌ throws | ❌ throws | ✅ |

SUBSCRIPTION tier delegates to kernel.subscription.authorize. Courses with subscription history can't be hard-deleted — retire via update(slug, { isActive: false }).


Group

Composite key (courseSlug, slug).

kernel.group.list(filters)
kernel.group.listByCourse(courseSlug)
kernel.group.get(courseSlug, slug)
kernel.group.create(data)                     // throws if courseSlug unresolved
kernel.group.update(courseSlug, slug, data)
kernel.group.delete(courseSlug, slug)         // throws if Library rows still reference it

Library

Authored content. Relates to Course only via Group (library.group.course).

kernel.library.list(filters, options)
kernel.library.listByCourse(courseSlug, options)
kernel.library.listByGroup(courseSlug, groupSlug, options)
kernel.library.get(slug, options)
kernel.library.create(data)             // throws if (courseSlug, groupSlug) unresolved
kernel.library.update(slug, data)
kernel.library.delete(slug)             // throws if Communication rows still reference it
kernel.library.createFromSlot(slug, courseSlug, groupSlug, type)

Lifecycle: DRAFT → PUBLISHED → ARCHIVED. New items default DRAFT.

get()/list() return PUBLISHED only by default. Admin/preview contexts must opt in:

kernel.library.get(slug, { includeUnpublished: true });
kernel.library.list({ courseSlug }, { includeUnpublished: true });

Retire content with live discussion via update(slug, { status: "ARCHIVED" }), not delete().


Communication

Discussion threads on a Library item.

kernel.communication.list(filters)          // { courseSlug, librarySlug, userId, initiator, unanswered }
kernel.communication.get(id)                // includes { user, library }
kernel.communication.create(data)           // throws if librarySlug unresolved
kernel.communication.update(id, data)
kernel.communication.delete(id)
kernel.communication.listUnanswered(courseSlug)
kernel.communication.countUserOpenQuestions(userId)

type (free-form, e.g. "user-comment") and initiator (STUDENT default | TEACHER) are independent axes.


Subscription

User's access to a Course.

kernel.subscription.list(filters)
kernel.subscription.get(id)
kernel.subscription.create(data)        // { userId, courseSlug, startsAt, endsAt }
kernel.subscription.update(id, data)
kernel.subscription.delete(id)
kernel.subscription.authorize(userId, courseSlug)   // throws if no ACTIVE subscription

Active = startsAt <= now <= endsAt. Expired/not-yet-started does not authorize. Course can't be hard-deleted while any subscription history exists (including expired).


Assets — SVG, Image, Audio

Independent, no DB relation to Course/Group/Library. Referenced by slug from content.

kernel.svg.list() / .get(slug) / .create(data) / .update(slug, data) / .delete(slug)
kernel.image.list() / .get(slug) / .create(data) / .update(slug, data) / .delete(slug)
kernel.audio.list() / .get(slug) / .create(data) / .update(slug, data) / .delete(slug)

| Field | Audio | Image | Svg | |---|---|---|---| | slug | required | required | required | | title | optional | optional | optional | | tags | optional | optional | optional | | body | — | — | required |

Audio/Image have no file-path field — create() rejects unknown keys like filePath. File-on-disk mapping is convention-based, not schema-enforced. SVG is DB-only (body holds content directly).


Schema Management

npx taleem-kernel schema-check    # diff app schema vs kernel canonical schema
npx taleem-kernel schema-update   # copy kernel canonical schema into app

Run Prisma migrate/generate after schema-update. Apps don't maintain their own Taleem Prisma models.


Design Principles

  1. Course → Group → Library are real relations, onDelete: Restrict throughout.
  2. Course is plain CRUD — no artifact/seed step.
  3. Library reaches Course only via Group.
  4. Library lifecycle DRAFT/PUBLISHED/ARCHIVED; public reads default PUBLISHED only.
  5. Communication relates to Library by real relation; type/initiator independent.
  6. Subscription relates to Course by real relation; can't hard-delete Course with subscription history.
  7. Admin.courseSlugs is a plain string, not a relation — deliberate.
  8. isAdmin and isSuperAdmin are separate checks — super admin status doesn't imply per-course authorization.
  9. SVG/Image/Audio are independent assets.
  10. User and Admin auth are separate concerns, each owning its own token lifecycle.
  11. JWT is low-level; go through User/Admin modules.