| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Framework-agnostic TypeScript engine that parses and validates lesson content from pluggable sources into a canonical lesson object.
It takes raw content (a single-JSON lesson plus a manifest.yaml) and a set context, and produces canonical lesson / set-entry objects. It contains no network, storage, or UI code - you supply the bytes and keep fetch + persistence. The bundled, strict JSON-Schema makes it a self-contained format reference: you can author and validate lessons without the application the format originated in (Adaptive Learner). Tracks the lesson schema, currently v1.12.
npm install learn-content-engineESM, ships TypeScript declarations, Node >= 18.
For development loops against an unreleased revision, or when consuming a fork, install straight from GitHub, pinned to a commit or tag for reproducibility:
dist/ is not committed, so on install npm runs the package's prepare script (npm run build) to compile dist/ (JS + .d.ts) from source in the checkout. No extra step is needed in the host - a plain npm install builds the engine.
import { parseLesson, validateLesson, type LessonSetContext } from "learn-content-engine";
const context: LessonSetContext = {
language: "fr", target_language: "fr", source_language: "en", domain: "language",
};
const raw = `{ "id": "01", "title": "Greetings", "steps": [
{ "id": "s1", "type": "exercise",
"exercise": { "id": "e1", "type": "free_text", "prompt": "Say hello.", "accept": ["bonjour"] } }
] }`;
const lesson = parseLesson(raw, context); // canonical ContentLesson (set context injected)
const result = validateLesson(JSON.parse(raw)); // explicit, opt-in validation
if (!result.valid) console.error(result.errors); // [{ path, message }, …]| Export | Kind | Purpose |
|---|---|---|
| parseLesson | fn | raw source + context → canonical ContentLesson (via an adapter) |
| singleJsonLessonAdapter | fn | the built-in single-JSON source adapter |
| parseManifest | fn | raw manifest.yaml text → ParsedManifest |
| asContentSetEntry | fn | raw parsed set → canonical ContentSetEntry |
| resolveLanguagePair | fn | language-pair resolution (legacy alias + en default) |
| setBasePath | fn | repo-relative base dir for a set |
| asContentSetBook | fn | project a manifest book block → ContentSetBook | null |
| validateLesson | fn | validate a lesson against the bundled schema + semantic rules → ValidationResult |
| validateManifest | fn | validate a manifest against the bundled schema (legacy alias normalized) |
| collectStableIds | fn | set-wide stable_id view over several lessons: total count + duplicates with locations (the cross-lesson half the schema cannot see) |
| buildStableIdInventory, compareStableIdInventories, formatStabilityResult | fn | the pure core of the shipped check-stable-ids gate: published-state vs head, violations V1-V6 (V5/V6 read each tree's declared retired_ids, engine#131) |
| computeStableIdCoverage, gateStableIdCoverage, formatCoverageResult | fn | the pure core of the shipped check-stable-id-coverage gate: how many listed sets are fully minted, judged against the repo-local baseline |
| isBaseCredible | fn | whether a comparison base is a plausible predecessor: an empty history is a broken run, not a clean one (the floor under the stability gate) |
| lessonIdOrderingIssues | fn | set-level ordering check over a set's lesson ids: mixed NN- prefixes, inconsistent widths, lexicographic-vs-numeric divergence (validateManifest runs it over metadata.lessons) |
| KNOWN_CONTENT_DOMAINS | const | the canonical domain vocabulary of the known-values-plus-other contract (engine#127); consumers group their subject facet on it instead of keeping a copy |
| CEFR_LEVELS | const | the CEFR proficiency bands (A1..C2) a language set declares as its level |
| LEVEL_NONE | const | the explicit no-level sentinel ("none") a deliberately level-less non-language set declares (engine#127) |
| isKnownContentDomain | fn | whether a domain value is in the canonical vocabulary (case-insensitive; absent counts as the language default) - the W-DOMAIN-UNKNOWN lint's predicate |
| isKnownLevel | fn | whether a level is a CEFR band or, for a non-language set, the none sentinel - the W-LEVEL-UNKNOWN lint's predicate |
| ContentLesson, ContentSetEntry, ContentSetBook, ContentSetSource, … | types | the canonical internal format |
| ContentLessonInlineExample | type | one inline worked example (schema v1.5) on a theory step or exercise |
| SetReviewStatus, SetAttribution | types | the set entry's review standing (schema v1.9) and attribution block |
| StableIdReport, StableIdDuplicate | types | the collectStableIds return shape |
| ValidationResult, ValidationIssue | types | the validate* return shape ({ valid, errors[] }) |
| LessonSetContext, LessonSourceAdapter, ParsedManifest, ParsedSet | types | adapter + manifest surface |
The bundled JSON-Schema ships too, so a content repo can mirror against it directly: import schema from "learn-content-engine/schema/lesson.schema.json".
By design, this package contains only parse / transform / validate / types + the single-JSON source adapter - no fetch, storage, or UI; those stay in the consumer. See architecture.md. The adaptive-learner app consumes this library (pinned in its frontend/package.json) as the reference consumer, so parse/validate/types live here once. As of v0.6.0 this engine is the canonical source of the lesson schema (schema authority moved here, roadmap stage 4); consumers - adaptive-learner and the content repos - mirror it. See Schema authority.
This is a language-learning-shaped lesson engine: the format is built around cards, drill-style exercise types, and a target/source language pair (see concepts.md). The shape carries more than languages, though - a domain field (language, programming, psychology, ...; known values plus other, see content domains) lets the same format hold knowledge-domain sets (tech courses, driving-test prep, dog training in the dedicated domain repos below); there, target_language is simply the language the content is written in. It is deliberately not:
The engine is used in the wild - these repos show the full consumer setup (pinned engine version, byte-mirrored schema artifacts, make lint running the same validator locally that CI enforces):
The canonical source of the lesson schema is this engine's schema/lesson.schema.json (+ content-manifest.schema.json, quality-rules.json). It is an authored artifact here; consumers mirror the schema shipped in each pinned engine release:
The lesson schema's $id is engine-owned: https://astrapi69.github.io/learn-content-engine/schema/lesson.schema.json.
To evolve the schema, edit the artifact here (the frozen byte baseline in src/schema-baseline.test.ts guards against accidental content drift), run make sync-types to regenerate src/types/lesson-schema.generated.ts from it, mirror any new cross-field rule in src/validate.ts, extend the fixtures + rule catalog, and bump the version; consumers then re-pin. The TypeScript types are generated here (in-engine, scripts/generate-lesson-types.mjs), so they cannot drift from the schema; the drift gate runs in release-check + CI.
See CHANGELOG.md - one dated section per release, from the current release back to 0.1.0.
MIT © Asterios Raptis
| Back | FazBrowse Home | New Git URL |