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

@happyvertical/smrt-education

v0.57.28

Published

Tenant-aware learning catalog models and authorized catalog access for SMRT

Readme

@happyvertical/smrt-education

Preview education catalog models and authorized catalog reads for s-m-r-t. The package represents courses, ordered sections, lessons, and lesson-owned files. It supplies catalog structure and authorized learner planning/progress workflows. Applications authenticate actors and decide guardian permissions.

Install

pnpm add @happyvertical/smrt-education

Use the package with the core, users, profiles, tenancy, and assets packages. Apply application migrations before using the catalog collections. Runtime catalog operations do not create application schema.

Models and collections

| Model | Purpose | | --- | --- | | CourseSource | Host-managed adapter policy and sharing ceiling | | Course | Course metadata, license, status, and optional owning tenant | | ExternalCourse | External course subtype sharing the Course table | | AuthoredCourse | Authored course subtype sharing the Course table | | CourseSection | Ordered section belonging to a course | | Lesson | Ordered lesson belonging to a section | | Reading, Video, Worksheet, Exercise | Lesson subtypes | | LessonAsset | Lesson-owned file join with a relationship such as printable or answer-key | | Learner | A required User/Person identity and guarded settings in one family tenant | | Curriculum | A learner plan for a distinct period | | Enrollment | One course in a curriculum | | LessonProgress | One lesson in an enrollment, with nullable score and separate history |

Each exported model has a corresponding exported collection. Course and lesson subtypes use single-table inheritance. Sections and lessons sort by sortOrder, with identifier ordering for ties. Lesson files belong in LessonAsset; lesson subtypes do not carry asset-id fields.

Basic integration

  1. Create the course, section, lesson, and lesson-asset collections with the application's database executor.
  2. Supply those collections, a request-bound TenantCollection, and the host's actor-bound permission resolver to CatalogService.
  3. Enable tenancy and establish the authenticated actor's active tenant context.
  4. Call findVisible with that tenant's canonical identifier and the actor's user identifier to list the ordinary catalog.
  5. Call projectCourse with the same context and a visible course identifier to obtain its ordered sections, lessons, and allowed asset joins.

The catalog service integration tests provide executable examples of collection setup, withTenant, and service reads. See the integration contract for host responsibilities. The authorizer must make a trusted server-side permission decision for the actor; a caller-supplied boolean is not a substitute for authentication or authorization.

Privacy and ownership

Ordinary catalog reads can include the active tenant, its ancestors, and globals. The supplied tenancy capability widens only the allowlisted catalog list reads. It does not grant generic get, write, raw query, sibling, or descendant access.

Personal-use copies require an owning tenant and cannot be published. Their owner and license kind cannot be changed after creation. includePrivate requires a separate host grant and returns only the active owner's personal copies, never an ancestor's or global private copy. The shared Tenant model has no family/group discriminator; hosts provision family tenants and authorize their guardian workflows.

Learner projections remove worksheet answer URLs and answer-key asset joins. includeAnswerKeys requires a separate host permission. Every projected child must have the selected course's exact owning tenant. Stored inconsistent section, lesson, and asset links are excluded. Generated REST, CLI, MCP, and AI operations are closed for these models.

Learner workflows

Create LearnerService with the application's root database executor and trusted learner/catalog authorizers. In the authenticated actor's exact withTenant context, an authorized guardian can createCurriculum and enroll; a learner can getLearner, startLesson, and listProgress for their own work. Guardian access requires a same-tenant directed ProfileRelationship plus the host's permission decision. Sibling membership does not grant access.

Repeated starts preserve the existing progress, including attempts and scores. A new curriculum/year creates a distinct enrollment and fresh progress history. An unscored lesson has bestScore: null, distinct from zero. The service does not accept score or completion patches; SubmissionService owns guardian grading. Use withTransaction and its callback service to compose atomic operations. Results are plain data views, so no transaction-bound model handle escapes.

The learner integration contract describes required identity provisioning, catalog permissions, supported workflows and limitations. The executable learner workflows test this integration on both SQLite and PostgreSQL.

Submissions and worksheet packets

SubmissionService captures tenant-owned uploads, reviews or attests work with optional guardian scores, signs authorized asset claims, and splits private PDF packets by page range. It reuses learner ownership and transaction checks. See the submission integration contract for inputs, retry rules, signing requirements and external-storage compensation.

Source imports

SourceImportService imports Easy Peasy pages as private family pointers after explicit host terms acceptance. Hosts inject the page fetcher and authorize the active actor, family/group destination and licensing evidence. Re-import refreshes URLs in place without deleting lessons or learner history. No lesson text or PDFs are mirrored. See the source integration contract and source behavior matrix.

Recipes

EducationCatalogRecipe declares education.catalog; EducationLearnerRecipe declares education.learner. Its course navigation and model declarations support application composition. A recipe does not grant permission to expose model CRUD or private material.

Database support and validation

SQLite and PostgreSQL exercise catalog persistence and child ownership guards. The real ancestor hierarchy integration is exercised on SQLite. DuckDB tests cover Course-only persistence: existing core DDL gates reject catalog child update-cascade and Asset/Tenant self-referential foreign keys. Use SQLite or PostgreSQL for full catalog schemas; those gates remain intact.

Run the package's test, test:postgres, typecheck, and build scripts. The behavior matrix records regressions, dialect coverage, and the existing DuckDB setup limitations.

Related documentation

Public first-edition Illustrative Mathematics

createPublicIllustrativeMathematicsFirstEditionAdapter({ fetch }) returns both CatalogSyncAdapter and CatalogAcquireAdapter; fetch is optional and defaults to the standard global Fetch API. Use the same factory for discovery and explicit private acquisition through CatalogAcquisitionService. No credential, mounted provider module, schema extension, or PDF fetch is needed for discovery.

The declared scope is public PDFs with explicit student/family labels on the first-edition Kendall Hunt K–5, 6–8, and high-school teacher/family hierarchy, including Algebra 1 Supports and linked Spanish counterparts. Traversal starts at the official homepage and fixed curriculum roots; it follows direct course, unit, resources, family-materials, and lesson-preparation links. Teacher guides, answers, assessments, presentations, login-only links, unknown labels, HTML-only student lessons, non-PDF formats, and AccessIM/v.360 are excluded. Completion means this declared scope only. Policy omissions appear in the bounded, invocation-local CatalogSyncReport.exclusionSummary, rather than the durable failure ledger.

Each enumerate reads one HTML page (2 MB maximum) and at most four asset HEADs. It never GETs PDF content. Version-1 JSON cursors contain the remaining validated hierarchy paths and an artifact offset plus source fingerprint when needed; persist the returned cursor unchanged. A fresh adapter can resume it. The cursor is capped at 64 KB and 384 pending paths; source pages are capped at 128 children and artifacts. Exceeding a bound, changing source metadata during a chunked page, or a provider failure pauses with the current cursor, never a terminal success. Normal CatalogSyncService invocation page budgets yield an unpaused, incomplete checkpoint without inventing a source failure; resume it to complete the same generation.

The source fingerprint is SHA-256 of canonical JSON containing the canonical path and parsed children, artifacts (asset ID, exact normalized label, allowed role), exclusions, source attribution, notices, license decision, and title, in source order. It excludes scripts, comments and transient CSRF markup. This is necessary because official page weak ETags change for each anonymous request. Acquisition rechecks that fingerprint, exact asset identity, role and license before requesting bytes. The PDF revision additionally pins the strong ETag, length and Last-Modified. A conditional PDF GET must return the identical validators, PDF content type, exact declared/streamed length and %PDF- signature; the adapter hashes the bytes with SHA-256. The generic service retains attribution and image/trademark notices and provides tenant, selection and idempotence checks.

timeoutMs bounds the entire call, including streaming (default 20 seconds, maximum 60 seconds). maxPdfBytes defaults to 32 MiB (maximum 64 MiB). Requests use fixed official hosts and strict paths, reject redirects, and carry no source credentials. Rate limiting returns a resumable pause with Retry-After information; hosts must honor that delay before resuming rather than loop immediately.

License evidence comes from each exact official source page's attribution and CC BY 4.0 footer, with retrieval time and retained exceptions. The official terms §7.1 distinguish the first edition's CC BY 4.0 curriculum from the separate v.360 terms in §7.2. No v.360 grant is inferred.

Public IM PDF roles are local: a table artifact must belong to the same closed row as its explicit first-cell label; a non-table artifact must be directly in an im-c-row__flex-resource block with exactly one local h3/h4 label. Missing, ambiguous, nested, or cross-section labels are excluded. A preceding student heading cannot authorize a separate teacher/unknown block. Structural parsing is bounded to 50,000 tags and 256 open elements. Family unit pages may follow their directed same-unit family-materials.html edge; family-materials leaves never follow reverse unit navigation.

Adapters use policyExclusions for normal role/license/format/scope omissions; IM emits page-wide omissions only on artifact offset zero and each artifact's omission only in its processed chunk. exclusions retains its operational error meaning: a current error/pause cannot advance the cursor, write returned partial resources, withdraw unseen rows, or report completion. The report's exclusionSummary: { total, byCode: [{ code, count }], otherCount } counts only pages processed in that invocation, retains at most 32 public code tokens, and is never stored in the checkpoint. Hosts may aggregate these invocation-local counts explicitly; they are not full-generation coverage totals.

Durable failures remain the existing JSON array, compacted to 31 recent code diagnostics plus an explicit overflow entry. Each entry's optional count preserves occurrence totals; omittedDetailCount explicitly counts occurrences whose diagnostic details were compacted/truncated. Messages and diagnostic cursors are bounded to 512/256 characters; the actual resume cursor is preserved. Missing legacy counts mean one; malformed counts are explicitly marked and counted once. Unsafe integer totals fail closed. failureSummary distinguishes historicalCount from this invocation's currentCount, with total and omitted detail counts. Historical mixed-policy/error entries are retained as audit history without guessed reclassification. An old coarse six-grade checkpoint with no cursor starts the new adapter at its root, preserves its imported count and history, and may complete/withdraw only at the new traversal's actual terminal page. History alone does not create an active pause.