@lessonkit/core reference (1.7.x)¶
Headless runtime, telemetry pipeline, plugins, identity, and session helpers shared across LessonKit packages.
Identity¶
validateId,assertValidId,slugifyId,deriveId,buildLessonkitUrnMachine-readable contract:
@lessonkit/core/identity-contract.v1.json
See Identity reference.
Telemetry¶
Event builder¶
import { buildTelemetryEvent, tryBuildTelemetryEvent } from "@lessonkit/core";
const event = buildTelemetryEvent({
name: "lesson_completed",
courseId: "my-course",
lessonId: "intro",
sessionId: "…",
data: { lessonId: "intro", durationMs: 1200 },
});
tryBuildTelemetryEvent returns null for quiz events when no lessonId is available (instead of throwing).
Pipeline¶
Register sinks explicitly for custom fan-out:
import {
createTelemetryPipeline,
createTrackingPipelineSink,
type TelemetryPipelineSink,
} from "@lessonkit/core";
const pipeline = createTelemetryPipeline([
createTrackingPipelineSink("analytics", (event) => sink(event)),
{
id: "custom",
emit(event) {
/* … */
},
},
]);
pipeline.emit(event);
@lessonkit/react routes provider telemetry through an internal pipeline (tracking, xAPI, LXPack bridge) and accepts extra sinks via config.sinks.
Tracking client¶
import { createTrackingClient } from "@lessonkit/core";
import { createFetchBatchSink } from "@lessonkit/xapi";
const { batchSink, exitBatchSink } = createFetchBatchSink({ url: "/api/telemetry/batch" });
const client = createTrackingClient({
batchSink,
exitBatchSink,
batch: { enabled: true, flushIntervalMs: 5000, maxBatchSize: 50 },
});
For local debugging only, a per-event sink or console.log is fine; production courses should use batched delivery and observability hooks.
Catalog¶
TELEMETRY_EVENT_CATALOG,buildTelemetryCatalog()/buildTelemetryCatalogV3(),telemetryCatalogVersion(v1) andtelemetryCatalogV3Version(v3, default runtime catalog in 1.2+)JSON:
@lessonkit/core/telemetry-catalog.v3.json(v1/v2 files retained for older generators)
See Telemetry reference.
Headless runtime¶
createLessonkitRuntime powers @lessonkit/react when runtimeVersion is "v2" (the default in 1.0):
import { createLessonkitRuntime } from "@lessonkit/core";
const runtime = createLessonkitRuntime({
courseId: "my-course",
runtimeVersion: "v2",
session: { sessionId: "…", attemptId: "…" },
plugins: [defineTelemetryPlugin({ id: "…", version: "1", kind: "analytics", onTelemetry: (e) => e })],
});
runtime.setActiveLesson("intro", emitLifecycle);
runtime.completeLesson("intro", emitLifecycle);
runtime.completeCourse(emitLifecycle);
Exports: createProgressController, buildPluginContext, session helpers (resolveSessionId, hasCourseStarted, …), and course lifecycle helpers in runtime/courseLifecycle. Plugin hooks run on runtime.track and lifecycle emits; call runtime.dispose() when tearing down a headless instance.
Plugins¶
Segregated plugin types and registry:
import {
createPluginRegistry,
defineTelemetryPlugin,
defineAssessmentPlugin,
defineLifecyclePlugin,
} from "@lessonkit/core";
const registry = createPluginRegistry([
defineTelemetryPlugin({
id: "com.example.analytics",
version: "1.0.0",
kind: "analytics",
onTelemetry: (event) => event,
}),
]);
See Plugins reference and LXPack bridge reference.
Ports¶
Testable abstractions: createSessionStoragePort, createDefaultClock, createGlobalTimer, createNoopStorage.
Compound state and resume¶
Compound blocks (InteractiveBook, SlideDeck, InteractiveVideo, BranchingScenario) persist navigation and child assessment state in sessionStorage when config.session.persistCompoundState is true (default).
Storage helpers¶
import {
compoundStateStorageKey,
loadCompoundState,
saveCompoundState,
clearCompoundState,
} from "@lessonkit/core";
const key = compoundStateStorageKey("my-course", "safety-book");
const saved = loadCompoundState(storagePort, key);
saveCompoundState(storagePort, key, { pageIndex: 2, childState: { /* ... */ } });
clearCompoundState(storagePort, key);
Resume state shape¶
import {
createCompoundResumeState,
parseCompoundResumeState,
clampCompoundPageIndex,
} from "@lessonkit/core";
const state = createCompoundResumeState({ pageIndex: 0, pageCount: 5 });
const parsed = parseCompoundResumeState(JSON.parse(raw)); // null if invalid
const index = clampCompoundPageIndex(7, 5); // 4
CompoundResumeState includes pageIndex, optional childState map, and extension meta for compound-specific fields (for example video time or branch path).
Branching scenario meta¶
BranchingScenario stores branch resume under meta key __lk_bs__ (active node id, visited nodes). Sessions saved before framework 1.5.0 without this meta restart at startNodeId.
Branch graph validation¶
import { validateBranchGraph } from "@lessonkit/core";
const result = validateBranchGraph([
{ id: "start", choices: [{ targetId: "end", label: "Finish" }] },
{ id: "end", choices: [] },
]);
// result.ok === true; result.issues lists cycles, orphans, missing startNodeId
Allowlists¶
getAllowedChildTypes(parent) and isChildTypeAllowed(parent, childType) enforce catalog nesting rules (for example Page inside InteractiveBook, TimedCue inside InteractiveVideo). See Block catalog.
CompoundHandle (React)¶
Compound containers implement CompoundHandle for parent scoring and navigation: getScore(), getMaxScore(), resetTask(), showSolutions(), etc. See Components and hooks.