LXPack upgrades¶
Upgrade plan for LXPack maintainers¶
Forward-looking priorities, responsibility shifts, and API proposals for the LXPack project:
Executive summary¶
LessonKit authors React courses; LXPack validates, packages, and runs them in LMS contexts. v0.4.0–v0.6.2 delivered SPA lessons, @lxpack/api, packageLessonkit(), @lxpack/spa-bridge, shared conformance, and interchange schema v1. LessonKit 1.0.0 ships @lessonkit/lxpack and lessonkit package on top of that stack.
Ongoing direction: shrink duplicate YAML/bridge logic in @lessonkit/lxpack as LXPack APIs mature; keep LessonKit focused on React authoring and the author CLI.
Current integration (baseline)¶
What LessonKit does today (@lessonkit/lxpack)¶
| Step | Owner today | Notes |
|---|---|---|
Validate LessonkitCourseDescriptor |
LessonKit | Uses @lessonkit/core id rules + layout rules |
Copy Vite dist/ into LXPack tree |
LessonKit | single-spa → {outDir}/dist; per-lesson-spa → per-lesson paths |
Emit course.yaml |
LessonKit | Custom minimal YAML emitter (packages/lxpack/src/yaml.ts) |
Emit assessments/*.yaml |
LessonKit | From descriptor; also passes structured assessments to buildCourse |
Emit lessonkit.json interchange |
LessonKit | format: "lessonkit", version: "1" |
Map theme → runtime.cssVariables |
LessonKit | Depends on @lessonkit/themes |
validateCourse / buildCourse |
LXPack (@lxpack/api) |
After files are on disk |
Staging + atomic promote to outDir |
LessonKit | Temp dir; rollback on failure |
| Runtime bridge (browser) | Split | LXPack exposes lxpackBridge.v1; LessonKit normalizes scores + forwards telemetry |
What LXPack already owns (keep)¶
- SCORM 1.2 / 2004 / standalone / xAPI / cmi5 packaging
course.yamlschema + path containment- Learner shell, flow, native quiz engine, LMS APIs
- SPA iframe + parent bridge host
@lxpack/tracking-schema(native LXPack courses)
Pain points driving this plan¶
- Duplicate manifest authoring — LessonKit re-implements YAML generation and assessment shaping that LXPack validators already understand.
- Two sources of truth for assessments — On-disk YAML and
buildCourse({ assessments }); easy to drift. - Bridge contract is implicit — Score scale (0–1 vs points), method names, and versioning are documented in LessonKit, not as a typed LXPack package.
- No first-class “package from interchange” API — Adapter must write a full LXPack project tree before every build.
- Tracking vocabularies diverge — LessonKit
telemetryevents vs LXPacktrack()/ xAPI export paths are aligned by convention, not schema. - Preview gap — Authors use Vite for dev; LXPack preview requires a materialized project; parity testing is manual.
- Conformance — shared matrix exists (
@lxpack/conformance); LessonKit 1.0 runsconformance:lxpackand Playwright export parity in CI.
Recommended division of responsibility¶
flowchart TB
subgraph lk [LessonKit — keep]
REACT["@lessonkit/react\ncomponents, hooks, a11y"]
CORE["@lessonkit/core\nids, telemetry catalog"]
XAPI["@lessonkit/xapi\nauthor-time statements"]
CLI["@lessonkit/cli\ninit, dev, vite build"]
end
subgraph lxp [LXPack — expand]
API["@lxpack/api\nvalidate, build, packageLessonkit"]
VAL["@lxpack/validators\nlessonkit interchange schema"]
BRIDGE["@lxpack/spa-bridge\ntyped parent/child SDK"]
RT["@lxpack/runtime\niframe host + LMS APIs"]
PKG["@lxpack/scorm, xapi, cmi5"]
CONF["@lxpack/conformance\nshared fixtures + matrix"]
end
subgraph thin [Thin adapter — shrink]
LKL["@lessonkit/lxpack\noptional: descriptor → interchange only"]
end
REACT --> LKL
CLI --> LKL
LKL --> API
API --> VAL
API --> PKG
RT --> BRIDGE
PKG --> LMS["LMS / LRS"]
RT --> LMS
| Concern | Should live in | Rationale |
|---|---|---|
| React components, block catalog, WCAG helpers | LessonKit | Authoring UX and Studio (future) |
courseId / lessonId / checkId validation rules |
LessonKit (@lessonkit/core) |
Framework identity; LXPack should accept ids, not define pedagogy |
Interchange schema (lessonkit.json v1+) |
LXPack validators | Single schema for any React toolchain, not only LessonKit |
Materialize SPA + course.yaml from interchange |
LXPack API | Removes YAML emitters from LessonKit |
| Assessment normalization (MCQ shape, passingScore semantics) | LXPack validators | One canonical assessment model at build time |
| SCORM/xAPI/cmi5 ZIP creation | LXPack | Already core competency |
lxpackBridge contract + client SDK |
LXPack (@lxpack/spa-bridge) |
SPA authors (LessonKit or not) import one package |
Map LessonKit telemetry → bridge / track() |
LessonKit (@lessonkit/react) |
Framework knows event names; calls LXPack SDK |
Theme token → runtime.cssVariables |
LXPack (generic) + LessonKit preset map (thin) | LXPack should not depend on @lessonkit/themes |
| End-to-end conformance fixtures | Shared repo or @lxpack/conformance |
Both CI pipelines run the same matrix |
lessonkit package CLI |
LessonKit | UX wrapper calling LXPack API |
Proposed LXPack upgrades (prioritized)¶
P0 — Package LessonKit without a hand-built project tree¶
Problem: @lessonkit/lxpack writes course.yaml, assessment YAML, copies dist/, writes lessonkit.json, then calls buildCourse. LessonKit maintains parallel YAML emitters.
Proposal: Add to @lxpack/api:
import type { ExportTarget } from "@lxpack/api";
export type PackageLessonkitOptions = {
/** Merged interchange + course metadata (see schema below). */
interchange: LessonkitInterchangeV1;
/** SPA payloads: lesson id → absolute path to folder with index.html */
spaDirs: Record<string, string>;
/** Optional in-memory assessments (same shape as buildCourse today). */
assessments?: AssessmentInput[];
target: ExportTarget;
/** Output zip or directory; optional staging courseDir for debugging */
output?: string;
dir?: boolean;
courseDir?: string; // default: temp; kept on failure if debug: true
};
export type PackageLessonkitResult =
| { ok: true; outputPath?: string; outputDir?: string; fileCount: number; courseDir: string }
| { ok: false; issues: Array<{ path?: string; message: string; severity?: "error" | "warning" }> };
export function packageLessonkit(options: PackageLessonkitOptions): Promise<PackageLessonkitResult>;
LXPack responsibilities:
- Generate
course.yamlinternally from interchange (no consumer YAML emitter). - Copy SPA dirs with path containment (reuse existing rules).
- Prefer only in-memory
assessmentsfor LessonKit-sourced quizzes; skip writingassessments/*.yamlunlesswriteAuthoringFiles: true. - Return structured issues (same shape as
validateCourse).
Acceptance criteria:
examples/lessonkit-spabuilds using only@lxpack/api(no checked-incourse.yamlrequired for LessonKit path).- LessonKit deletes
yaml.ts,assessmentYaml.ts, and most ofwriteProject.ts. - CI: golden LessonKit course packages identically before/after migration (ZIP hash or manifest comparison).
LessonKit follow-up: packageLessonkitCourse() becomes a thin wrapper mapping LessonkitCourseDescriptor → PackageLessonkitOptions.
P0 — Formalize lxpackBridge as @lxpack/spa-bridge¶
Problem: Bridge types and score normalization live in @lessonkit/lxpack/bridge. Non-LessonKit SPAs duplicate logic; versioning is informal.
Proposal: New package (or @lxpack/runtime/bridge export):
// Host (LXPack runtime) — register once
export function createLxpackBridgeHost(options: BridgeHostOptions): LxpackBridgeV1;
// Child (SPA) — safe access from iframe
export function getLxpackBridge(): LxpackBridgeV1 | null;
export function normalizeScore(raw: { score: number; maxScore?: number }): number | null;
export function normalizePassingThreshold(raw: { passingScore?: number; maxScore?: number }): number;
export type LxpackBridgeV1 = {
completeLesson(lessonId: string): void;
completeCourse(): void;
submitAssessment(payload: { id: string; score: number; passingScore?: number }): void;
track?(event: TrackingSchemaEvent): void;
};
Document in LXPack docs:
| Method | Score / threshold scale | When to call |
|---|---|---|
submitAssessment |
0–1 scaled | After quiz graded in SPA |
YAML passingScore in author assessments |
absolute points | Native LXPack quizzes only |
Versioning: Keep window.parent.lxpackBridge.v1; add v2 alongside with a capability negotiation helper (bridge.supportedVersions).
Acceptance criteria:
- Published TypeScript types on npm.
examples/lessonkit-spaimports child SDK from@lxpack/spa-bridge.- LessonKit removes duplicate
bridge.tsand depends on LXPack package. - Validator warns when SPA
index.htmlreferences deprecatedwindow.parent.lxpackwithout bridge.
P1 — Own the lessonkit.json interchange schema¶
Problem: Schema is implied by LessonKit’s LessonkitInterchangeV1 type; LXPack merges file at build time but does not publish a versioned JSON Schema / Zod module.
Proposal:
- Add
@lxpack/validators/lessonkit(orlessonkitInterchangeSchemaexport). - Document fields:
{
"format": "lessonkit",
"version": "1",
"course": { "id": "course-id", "title": "Title" },
"lessons": [{ "id": "lesson-id", "title": "...", "type": "spa", "path": "dist" }],
"assessments": [{ "id": "check-id", "passingScore": 1, "questions": [] }],
"tracking": { "completion": { "threshold": 1 } },
"runtime": { "theme": "default", "cssVariables": { "--lk-color-primary": "#2563eb" } }
}
validateCourseaccepts interchange-only projects (generates missingcourse.yaml).- Breaking changes bump
version: "2"with migration notes.
Shift from LessonKit: Stop emitting interchange by hand in descriptorToInterchange; optionally generate from descriptor in LessonKit until descriptors are deprecated.
Acceptance criteria:
- JSON Schema published under
docs/reference/lessonkit-interchange.md. - Invalid interchange fails
validateCoursewith path-qualified errors. - LessonKit CI imports schema from
@lxpack/validators(devDependency) instead of duplicating rules.
P1 — Unified tracking map (LessonKit telemetry ↔ LXPack)¶
Problem: LessonKit emits lesson_completed, quiz_completed, etc. (@lessonkit/core catalog). LXPack has @lxpack/tracking-schema. Adapters guess mappings for bridge and xAPI.
Proposal:
- Publish
mapLessonkitTelemetryToLxpack(event): TrackingSchemaEvent | nullin@lxpack/tracking-schema(or@lxpack/api). - Publish verb / activity IRI recommendations for packaged courses (xAPI export).
- Optional:
bridge.track()accepts canonical events so SPAs can forward rich telemetry without custom LMS code.
Reference mapping (starter):
LessonKit TelemetryEventName |
LXPack / xAPI intent |
|---|---|
course_started |
course initialized |
course_completed |
course completed |
lesson_started |
lesson initialized |
lesson_completed |
completeLesson + completed verb |
quiz_answered |
interaction / answered |
quiz_completed |
submitAssessment + scored |
interaction |
track({ type, id, data }) |
Acceptance criteria:
- Table is documented and unit-tested in LXPack.
- LessonKit
@lessonkit/reactuses exported mapper (delete bespoke switch inlxpackBridge.ts). - xAPI ZIPs from LessonKit courses use consistent activity ids derived from interchange
course.id/ lesson ids.
P1 — Theme interchange without @lessonkit/themes¶
Problem: themeToLxpackRuntime() in LessonKit depends on @lessonkit/themes to produce runtime.cssVariables.
Proposal:
- Interchange carries optional
runtime.cssVariables(already natural in YAML). - LXPack applies variables to learner shell (already supports
runtime.cssVariablesincourse.yaml). - Optional:
runtime.themePreset: "lessonkit:brand"resolved via a registered preset table in LXPack or passed fully expanded in interchange.
Shift: LessonKit only expands presets at package time (one function), or authors commit expanded variables in lessonkit.json. LXPack does not import LessonKit packages.
P2 — Preview and validate from SPA build output¶
Problem: Developers run lessonkit dev (Vite) but must materialize .lxpack/course to use lxpack preview with LMS chrome.
Proposal:
- Starts runtime with SPA lesson iframe + bridge host.
- Optional SCORM simulator flags (existing
lxpack.config.jsonpreview modes).
Acceptance criteria:
- Documented workflow in LessonKit interoperability guide.
- No
assessments/*.yamlrequired on disk when passing--assessmentsJSON path or embedded in interchange.
P2 — SCORM layout recipes for LessonKit¶
Problem: LessonKit supports single-spa (one SCO, in-app navigation) vs per-lesson-spa (multi-SCO). Authors lack LXPack guidance on SCORM 2004 sequencing for each.
Proposal:
- Document recipes in LXPack:
- Recipe A — Single SCO SPA (default LessonKit): one
type: spalesson; completion via bridge. - Recipe B — Multi SCO: one SPA folder per
lessonId; flow optional; maplesson_completedper SCO. - Validators emit warnings when interchange lessons omit
pathor id collisions would break SCORM 2004. - SCORM SPA layout is determined by
build/packageLessonkittarget(SCORM 1.2 = single SCO; SCORM 2004 = multi-SCO) and lesson count; useinferScormSpaLayout()from@lxpack/validatorsfor docs/warnings only.
Open question for maintainers: Should multi-lesson React apps ever share one SPA build (hash router), or should LXPack enforce per-lesson builds for true multi-SCO?
P2 — Shared conformance harness (@lxpack/conformance or test/fixtures/lessonkit)¶
Problem: Export parity is covered by LessonKit 1.0 e2e/conformance scripts and @lxpack/conformance; keep fixtures aligned when changing export targets.
Proposal:
- Git submodule or npm package with:
- Minimal interchange + tiny SPA fixture (static HTML + one quiz call to bridge).
- Matrix:
standalone,scorm12,scorm2004,xapi,cmi5. - Expected: launch succeeds,
completeLessonmarks complete,submitAssessmentrecords score. - Both repos run
npx @lxpack/conformancein CI.
Acceptance criteria:
- LXPack release workflow runs conformance on tag.
- LessonKit packaging smoke imports same package (no forked fixture copies).
P3 — Optional @lxpack/lessonkit meta-package¶
Problem: Consumers install @lessonkit/lxpack + @lxpack/api and must understand Node 18 or 20, interchange, bridge.
Proposal:
@lxpack/lessonkitre-exportspackageLessonkit, bridge SDK, schema types.- Peer dependency:
reactnot required (packaging only). - LessonKit deprecates
@lessonkit/lxpackover a major version with migration guide.
What should stay in LessonKit (non-goals for LXPack)¶
- React component library and hooks (
Course,Lesson,Quiz, …). - Authoring-time telemetry sinks and
@lessonkit/xapiclient (distinct from LMS-export xAPI inside LXPack). - Vite template,
lessonkit init,lessonkit dev,lessonkit build. - Block catalog for AI/Studio (
block-catalog.v1.json). - WCAG primitives (
@lessonkit/accessibility).
Non-goals (both projects):
- Merging repositories.
- Replacing LXPack markdown/HTML authoring with LessonKit-only workflows.
- Re-implementing SCORM manifest generation in LessonKit.
LXPack release history and future work¶
Shipped (v0.5.0–v0.6.3):
| Release | Theme | Key deliverables |
|---|---|---|
| v0.5.0 | Thin packaging | packageLessonkit(), interchange Zod schema, lxpack build --lessonkit |
| v0.6.0 | Bridge SDK + conformance | @lxpack/spa-bridge, telemetry map, lxpack preview --lessonkit, @lxpack/conformance, @lxpack/lessonkit |
| v0.6.1 | Packaging fixes | Omit interchange from LMS ZIPs; config resolution beside lessonkit.json |
| v0.6.2 | Node 18 + runtime fixes | Node 18/20 CI matrix; navigation fixes; packageLessonkit({ configDir }) |
| v0.6.3 | Flow + nav patch | Position-aware flow (from); sidebar/Prev/Next UX; CLI lessonkit config parity |
Future (v0.7+):
| Release | Theme | Key deliverables |
|---|---|---|
| v0.7+ | Platform | Plugin slot for custom lesson runtimes; bridge v2 negotiation |
| v0.7+ | AI tooling | Claude integration, AI repair (Phase 5) |
Coordinate with LessonKit 1.0.0 (stable @lessonkit/* public API; lessonkit.readthedocs.io).
API stability commitments (request to LXPack)¶
LessonKit 1.0.0 needs these to remain stable (or versioned with migration):
lxpackBridge.v1method signatures and 0–1 score semantics forsubmitAssessment.lessonkit.jsonformat+versionwith documented migration path.ExportTargetenum andbuildCourse/packageLessonkitresult shapes.- SPA lesson
type: spa+pathpointing at folder withindex.html.
How to validate with LessonKit today¶
Maintainers can verify changes against the LessonKit repo without adopting the full monorepo:
git clone https://github.com/eddiethedean/lessonkit.git
cd lessonkit
npm ci
npm run build
npm -w lessonkit-example-lxpack-golden run build
npm -w lessonkit-example-lxpack-golden run package:scorm12
Artifacts: examples/lxpack-golden/.lxpack/course/.lxpack/out/course-scorm12.zip
Contact surface: open issues in either repo with label interop/lessonkit; attach interchange JSON and buildCourse / packageLessonkit structured errors.
Summary for LXPack maintainers¶
- Absorb project materialization —
packageLessonkit()so adapters stop writing YAML by hand. - Own interchange + bridge — schema, typed SDK, explicit score semantics.
- Publish tracking maps — one table from LessonKit telemetry to LXPack/LRS.
- Ship shared conformance — both projects gate releases on the same fixture matrix.
- Keep LessonKit thin — React authoring only; LMS delivery stays in LXPack.
Related documents¶
| Document | Audience |
|---|---|
| Product ROADMAP — Phase 0.6 | Shipped v0.6.0–v0.6.2 scope |
| LessonKit packaging guide | LessonKit authors |
| Historical upgrade checklist | What v0.4.0–v0.6.2 shipped |
| LXPack LessonKit interoperability | LXPack docs (maintain alongside this plan) |
| LessonKit ROADMAP | 1.0 framework; LessonKit Studio (visual authoring) post-1.0 |
Historical checklist (LessonKit team)¶
What LessonKit requested before LXPack v0.4.0, and integration status from the LessonKit side:
This document captures the improvements we wanted in LXPack so it works better as the packaging and LMS export layer for LessonKit.
LessonKit is React-first authoring (@lessonkit/react). LXPack is a manifest-driven compiler and
runtime (course.yaml, markdown/HTML/component lessons, SCORM/xAPI/cmi5 export). The two projects
are complementary: LessonKit owns the developer experience; LXPack owns validation, preview, and LMS
artifacts.
Related LessonKit docs (github.com/eddiethedean/lessonkit):
- Documentation — guides, CLI, packaging, identity
ROADMAP.md— framework roadmap (1.0 shipped)SPEC.md— technical specPACKAGING.md—@lessonkit/lxpackworkflow
Current integration (LessonKit 1.0)¶
LessonKit’s shipped path:
- Author courses in React with
@lessonkit/react(courseId,lessonId,checkId). - Describe the course in a
LessonkitCourseDescriptoror rootlessonkit.json(schemaVersion: 1). - Build with Vite (
lessonkit build→dist/). - Package with
@lessonkit/lxpack(packageLessonkitCourse) orlessonkit package --target scorm12.
LXPack validates, runs the learner shell, and emits SCORM/xAPI/cmi5 artifacts via @lxpack/api 0.6.4+.
See LessonKit packaging and examples/lxpack-golden.
Historical integration plan (pre–SPA lesson type)¶
Before LXPack type: spa and @lessonkit/lxpack, exporting React courses required lossy translation to markdown/HTML. The goals below drove the v0.4–v0.6 interoperability work (now shipped).
Design goals¶
| Goal | Why it matters |
|---|---|
| Preserve React authoring | LessonKit users should not rewrite courses as YAML/markdown to ship to an LMS. |
| Stable identity model | courseId, lessonId, assessment ids must map 1:1 into tracking, xAPI, and SCORM suspend data. |
| Shared tracking semantics | Completion, quiz pass/fail, and time-on-task should mean the same thing in both runtimes. |
| Programmatic packaging | @lessonkit/lxpack needs library APIs, not only CLI subprocesses. |
| npm-first consumption | LessonKit uses npm workspaces; LXPack packages should install cleanly without requiring pnpm for consumers. |
Gap analysis¶
1. Authoring model mismatch¶
| LXPack today | LessonKit today |
|---|---|
Declarative course.yaml + file-based lessons |
JSX component tree (Course, Lesson, Quiz, …) |
Lesson types: markdown, html, component |
Rich React composition, custom layout, app state |
Built-in widgets (callout, image-card, …) |
Framework primitives + user-defined UI |
Pain: Exporting LessonKit → LXPack today implies serializing React to markdown/HTML or re-implementing interactions as LXPack component lessons. That breaks fidelity and accessibility work done in React.
2. No first-class “hosted React bundle” lesson type¶
LXPack can package standalone web apps, but there is no documented lesson type for:
- A Vite/React build output as a lesson SCO with known entry (
index.html) - Wiring that lesson into
flow, completion rules, and multi-SCO SCORM 2004
Pain: LessonKit’s natural artifact is a built SPA, not a folder of markdown files.
3. CLI-centric integration surface¶
LXPack’s primary interface is @lxpack/cli (init, preview, validate, build). LessonKit
needs:
validateCourse(project)/buildCourse(project, target)as importable functions- Typed options and structured errors (for CI and
@lessonkit/lxpack)
Pain: Subprocess + stdout parsing is fragile for monorepo CI and IDE integrations.
4. Tracking and xAPI vocabulary alignment¶
LessonKit (0.1.x) emits telemetry events and minimal xAPI statements (started, completed).
LXPack has mature tracking, completion thresholds, quiz YAML, and export-time embedding.
Pain: Without a shared event/verb map, adapters guess at semantics and LRS reports diverge.
5. Assessment model differences¶
| LXPack | LessonKit |
|---|---|
Author YAML in assessments/; keys embedded at build |
Inline Quiz / KnowledgeCheck in React |
passingScore, maxAttempts, shuffle, feedback modes |
Simple correct/incorrect + useQuizState hooks |
Pain: Export must invent assessment YAML from React props or lose quiz metadata.
6. Theming and accessibility¶
LessonKit targets WCAG 2.1 AA with React semantics and @lessonkit/accessibility helpers. LXPack
runtime uses markdown sanitization, HTML interactions, and runtime.theme CSS classes.
Pain: Branding and a11y behavior may differ between preview (LXPack) and author preview (LessonKit/Vite) unless theme contracts align.
Recommended LXPack upgrades¶
Prioritized from highest leverage for LessonKit to nice-to-have.
P0 — React / SPA lesson type¶
Proposal: Add a lesson type (working name: react or spa) to course.yaml:
lessons:
- id: phishing-101
title: Phishing Awareness
type: spa
path: dist/lessons/phishing-101 # folder with index.html + assets
runtime:
mount: root # optional; default #root
Behavior:
- Package the folder as a launchable unit in standalone, SCORM 1.2, SCORM 2004 (SCO), xAPI, cmi5.
- Expose a stable parent bridge (
window.parent.lxpackorpostMessage) for: completeLesson({ lessonId })reportAssessment({ id, score, passed })- optional xAPI statement passthrough
Why: Lets LessonKit ship vite build output per lesson without converting UI to markdown.
Acceptance criteria:
- Example course in LXPack repo: one
spalesson + one markdown lesson in the same package. - SCORM 2004 multi-SCO export launches each SPA in an iframe with correct sequencing.
- Documented bridge API versioned (
lxpackBridge.v1).
P0 — Programmatic build and validate API¶
Proposal: Export stable functions from @lxpack/cli or a new @lxpack/api package:
import { validateCourse, buildCourse } from "@lxpack/api";
const result = await validateCourse({ courseDir: "/path/to/course", target: "scorm12" });
const artifact = await buildCourse({ courseDir, target: "scorm2004", output: "./out.zip" });
Requirements:
- Structured result:
{ ok, errors: [{ path, rule, message }], warnings } - No global process cwd assumptions; all paths explicit
- Works when imported from npm (LessonKit) without pnpm
Why: Enables @lessonkit/lxpack to run in CI and tests without shelling out.
P1 — Import / interchange schema (lessonkit.json or lxpack.import)¶
Proposal: Support an optional interchange file at course root:
{
"format": "lessonkit",
"version": "1",
"course": { "id": "cyber-basics", "title": "Cybersecurity Basics" },
"lessons": [
{
"id": "phishing-101",
"title": "Phishing Awareness",
"type": "spa",
"build": { "command": "npm run build", "outputDir": "dist" }
}
],
"assessments": [],
"tracking": { "completion": { "threshold": 0.9 } }
}
Behavior:
lxpack validatemerges interchange + generatedcourse.yaml(or generates yaml at build time).- Validators understand LessonKit ids and required fields.
Why: Reduces duplication between LessonKit metadata and hand-maintained course.yaml.
P1 — Shared tracking event catalog¶
Proposal: Document and export a shared enum / JSON schema for learning events:
| Event | xAPI verb (suggested) | SCORM mapping |
|---|---|---|
lesson_started |
initialized / custom |
cmi.core.lesson_status |
lesson_completed |
completed |
completion |
quiz_answered |
answered |
interaction |
quiz_completed |
completed |
score |
course_completed |
completed |
course complete |
Publish as @lxpack/tracking-schema (or extend @lessonkit/core with LXPack-compatible exports).
Why: LessonKit and LXPack runtimes report the same analytics to LRS and internal sinks.
P1 — Assessment interchange from structured data¶
Proposal: Allow assessments to be defined in JSON/YAML or supplied at build time:
buildCourse({
courseDir,
target: "scorm12",
assessments: [{ id: "final_quiz", questions: [...] }], // validated by @lxpack/validators
});
Why: @lessonkit/lxpack can extract Quiz props / config from React without writing
assessments/*.yaml to disk.
P2 — Plugin slot for custom lesson runtimes¶
Proposal: Formal plugin API in @lxpack/cli / @lxpack/runtime:
registerLessonRuntime("lessonkit-react", {
validate(lesson, ctx) { ... },
bundle(lesson, ctx) { ... },
preview(lesson, ctx) { ... },
});
Why: LessonKit can register a runtime once instead of forking LXPack lesson types.
P2 — Theme token bridge¶
Proposal: Accept external design tokens (CSS variables) from a theme/tokens.json or
@lessonkit/themes export:
Why: Visual parity between LessonKit dev preview and LXPack-packaged learner view.
P3 — Documentation and examples (shipped)¶
Delivered in LXPack v0.6.x:
- Guide: LessonKit interoperability
- Example:
examples/lessonkit-spa/and LessonKit lxpack-golden - Package map: LessonKit and LXPack packages
Suggested division of responsibility¶
flowchart TB
subgraph author [Authoring]
LK["LessonKit React app\n(@lessonkit/react)"]
end
subgraph bridge [Bridge - shipped]
LKL["@lessonkit/lxpack\nexport + metadata"]
end
subgraph lxpack [LXPack]
VAL["@lxpack/validators"]
CLI["@lxpack/cli / @lxpack/api"]
PKG["@lxpack/scorm / xapi / cmi5"]
RT["@lxpack/runtime"]
end
subgraph lms [Delivery]
LMS["LMS / LRS / browser"]
end
LK --> LKL
LKL --> VAL
LKL --> CLI
CLI --> PKG
PKG --> LMS
RT --> LMS
| Layer | Owner | Responsibility |
|---|---|---|
| Authoring UX | LessonKit | Components, hooks, a11y, Vite templates |
| Export adapter | @lessonkit/lxpack |
Build SPA(s), emit interchange + invoke LXPack |
| Validation & packaging | LXPack | Schema, path containment, SCORM/xAPI/cmi5 ZIPs |
| Learner runtime | LXPack (+ SPA bridge) | Navigation, flow, LMS APIs, quiz engine where applicable |
Phased rollout (cross-repo)¶
| Phase | LXPack | LessonKit |
|---|---|---|
| 1 | Document SPA lesson type + bridge API (even if experimental) | Spike @lessonkit/lxpack export to static dist/ |
| 2 | Ship @lxpack/api validate/build |
Wire lessonkit package → lxpack build |
| 3 | Tracking schema + assessment build injection | Align @lessonkit/xapi verbs with schema |
| 4 | Plugin runtime registration | Optional: embed @lxpack/runtime navigation shell around SPA |
Non-goals (for now)¶
- Merging the two repos into one monorepo
- Replacing LXPack markdown authoring with LessonKit-only workflows
- Requiring LessonKit authors to learn full
course.yamlbefore they can ship
Open questions for LXPack maintainers¶
- Single-SCO vs multi-SCO: Should a LessonKit
Coursemap to one SCORM package or one SCO perLesson? - Answer keys in SPA lessons: Should quiz scoring stay in LXPack runtime only, or allow client-side scoring inside the SPA with signed/embedded config?
- Versioning: How should
lxpackBridge.v1evolve without breaking published LessonKit courses? - npm vs pnpm: Can release CI guarantee
@lxpack/*packages work as npm dependencies in LessonKit’s workspace?
Summary¶
LXPack already solves problems LessonKit should not rebuild (SCORM manifests, ZIP packaging, xAPI/cmi5, validation, preview). The highest-value upgrades for LessonKit interoperability are:
- SPA/React lesson type with a stable LMS bridge API
- Programmatic validate/build APIs for tooling and CI
- Shared tracking and assessment interchange so React authoring maps cleanly to exports
Implementing P0 items unblocks @lessonkit/lxpack and delivers LMS-ready packages without forcing
authors out of React.