@lessonkit/core 0.9.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -12
- package/dist/index.cjs +574 -12
- package/dist/index.d.cts +185 -20
- package/dist/index.d.ts +185 -20
- package/dist/index.js +546 -10
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -143,12 +143,102 @@ declare function createSessionId(): string;
|
|
|
143
143
|
|
|
144
144
|
declare function nowIso(): string;
|
|
145
145
|
|
|
146
|
+
type BuildTelemetryEventInput = {
|
|
147
|
+
name: TelemetryEventName;
|
|
148
|
+
courseId: CourseId;
|
|
149
|
+
lessonId?: LessonId;
|
|
150
|
+
sessionId?: string;
|
|
151
|
+
attemptId?: string;
|
|
152
|
+
user?: TelemetryUser;
|
|
153
|
+
data?: unknown;
|
|
154
|
+
timestamp?: string;
|
|
155
|
+
};
|
|
156
|
+
/** Reset dev-warning state (tests only). */
|
|
157
|
+
declare function resetTelemetryBuilderWarningsForTests(): void;
|
|
158
|
+
/**
|
|
159
|
+
* Build a typed telemetry event from a catalog event name and context.
|
|
160
|
+
* Validates lesson-scoped events require `lessonId`.
|
|
161
|
+
*/
|
|
162
|
+
declare function buildTelemetryEvent(opts: BuildTelemetryEventInput): TelemetryEvent;
|
|
163
|
+
/**
|
|
164
|
+
* Like `buildTelemetryEvent`, but returns null (with a dev warning) when quiz events lack an active lesson.
|
|
165
|
+
*/
|
|
166
|
+
declare function tryBuildTelemetryEvent(opts: BuildTelemetryEventInput): TelemetryEvent | null;
|
|
167
|
+
|
|
168
|
+
type EmitContext = {
|
|
169
|
+
courseId: string;
|
|
170
|
+
sessionId?: string;
|
|
171
|
+
attemptId?: string;
|
|
172
|
+
};
|
|
173
|
+
/** Pluggable telemetry output (OCP). Distinct from the legacy `TelemetrySink` function type. */
|
|
174
|
+
type TelemetryPipelineSink = {
|
|
175
|
+
readonly id: string;
|
|
176
|
+
emit(event: TelemetryEvent, ctx: EmitContext): void | Promise<void>;
|
|
177
|
+
};
|
|
178
|
+
type TelemetryPipeline = {
|
|
179
|
+
readonly sinks: readonly TelemetryPipelineSink[];
|
|
180
|
+
emit(event: TelemetryEvent, ctx?: EmitContext): void | Promise<void>;
|
|
181
|
+
};
|
|
182
|
+
declare function createTelemetryPipeline(sinks: TelemetryPipelineSink[]): TelemetryPipeline;
|
|
183
|
+
declare function createTrackingPipelineSink(id: string, track: (event: TelemetryEvent) => void): TelemetryPipelineSink;
|
|
184
|
+
|
|
185
|
+
type StoragePort = {
|
|
186
|
+
getItem: (key: string) => string | null;
|
|
187
|
+
setItem: (key: string, value: string) => void;
|
|
188
|
+
removeItem?: (key: string) => void;
|
|
189
|
+
/** @internal Test helper to clear in-memory fallback state. */
|
|
190
|
+
resetForTests?: () => void;
|
|
191
|
+
};
|
|
192
|
+
type ClockPort = {
|
|
193
|
+
nowMs: () => number;
|
|
194
|
+
nowIso: () => string;
|
|
195
|
+
};
|
|
196
|
+
type TimerPort = {
|
|
197
|
+
setInterval: (fn: () => void, ms: number) => ReturnType<typeof globalThis.setInterval>;
|
|
198
|
+
clearInterval: (id: ReturnType<typeof globalThis.setInterval>) => void;
|
|
199
|
+
};
|
|
200
|
+
declare function createDefaultClock(): ClockPort;
|
|
201
|
+
declare function createNoopStorage(): StoragePort;
|
|
202
|
+
declare function resetStoragePortForTests(storage: StoragePort): void;
|
|
203
|
+
declare function createSessionStoragePort(): StoragePort;
|
|
204
|
+
declare function createGlobalTimer(): TimerPort;
|
|
205
|
+
|
|
206
|
+
type ProgressState = {
|
|
207
|
+
activeLessonId?: LessonId;
|
|
208
|
+
completedLessonIds: ReadonlySet<LessonId>;
|
|
209
|
+
courseCompleted: boolean;
|
|
210
|
+
};
|
|
211
|
+
type ProgressController = {
|
|
212
|
+
getState: () => ProgressState;
|
|
213
|
+
setActiveLesson: (lessonId: LessonId, startedAtMs: number) => {
|
|
214
|
+
previousLessonId?: LessonId;
|
|
215
|
+
};
|
|
216
|
+
completeLesson: (lessonId: LessonId, completedAtMs: number) => {
|
|
217
|
+
durationMs?: number;
|
|
218
|
+
didComplete: boolean;
|
|
219
|
+
};
|
|
220
|
+
completeCourse: () => {
|
|
221
|
+
didComplete: boolean;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
declare function createProgressController(): ProgressController;
|
|
225
|
+
|
|
226
|
+
declare const SESSION_STORAGE_KEY = "lessonkit:sessionId";
|
|
227
|
+
declare function getTabSessionId(storage: StoragePort): string | null;
|
|
228
|
+
declare function resolveSessionId(storage: StoragePort, provided?: string): string;
|
|
229
|
+
declare function hasCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
|
|
230
|
+
declare function markCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): void;
|
|
231
|
+
declare function hasCourseStartedEmittedToTracking(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
|
|
232
|
+
declare function markCourseStartedEmittedToTracking(storage: StoragePort, sessionId: string, courseId?: CourseId): void;
|
|
233
|
+
declare function migrateCourseStartedMark(storage: StoragePort, fromSessionId: string, toSessionId: string, courseId?: CourseId): void;
|
|
234
|
+
|
|
146
235
|
/** Plugin category — aligns with roadmap extension areas. */
|
|
147
236
|
type LessonkitPluginKind = "analytics" | "lms" | "assessment" | "interaction" | "ai";
|
|
148
237
|
type LessonkitPluginContext = {
|
|
149
238
|
courseId: CourseId;
|
|
150
239
|
sessionId?: string;
|
|
151
240
|
attemptId?: string;
|
|
241
|
+
user?: TelemetryUser;
|
|
152
242
|
};
|
|
153
243
|
type AssessmentScoreInput = {
|
|
154
244
|
checkId: string;
|
|
@@ -167,43 +257,118 @@ type InteractionBlockRegistration = {
|
|
|
167
257
|
catalogVersion?: string;
|
|
168
258
|
description?: string;
|
|
169
259
|
};
|
|
170
|
-
|
|
171
|
-
* Framework plugin contract (v1). Plugins are plain objects registered on `LessonkitProvider`.
|
|
172
|
-
* Marketplace / dynamic loading are out of scope until Studio post-1.0.
|
|
173
|
-
*/
|
|
174
|
-
type LessonkitPlugin = {
|
|
260
|
+
type PluginIdentity = {
|
|
175
261
|
id: string;
|
|
176
262
|
version: string;
|
|
177
263
|
kind: LessonkitPluginKind;
|
|
178
264
|
name?: string;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* Observe or filter telemetry before tracking/xAPI. Return `null` to drop the event.
|
|
183
|
-
* Hooks run in registration order.
|
|
184
|
-
*/
|
|
265
|
+
};
|
|
266
|
+
/** Narrow telemetry plugin contract (ISP). */
|
|
267
|
+
type TelemetryPlugin = PluginIdentity & {
|
|
185
268
|
onTelemetry?: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
|
|
186
|
-
/** Optional batch observer (analytics); receives events after per-event hooks. */
|
|
187
269
|
onTelemetryBatch?: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => void;
|
|
188
|
-
/** Wrap the configured tracking sink (analytics plugins). First registered = innermost. */
|
|
189
270
|
wrapTrackingSink?: (sink: TelemetrySink, ctx: LessonkitPluginContext) => TelemetrySink;
|
|
190
|
-
|
|
271
|
+
};
|
|
272
|
+
/** Narrow lifecycle plugin contract (ISP). */
|
|
273
|
+
type LifecyclePlugin = PluginIdentity & {
|
|
274
|
+
setup?: (ctx: LessonkitPluginContext) => void;
|
|
275
|
+
dispose?: () => void;
|
|
276
|
+
};
|
|
277
|
+
/** Narrow assessment plugin contract (ISP). */
|
|
278
|
+
type AssessmentPlugin = PluginIdentity & {
|
|
279
|
+
kind: "assessment";
|
|
191
280
|
scoreAssessment?: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
|
|
192
|
-
|
|
281
|
+
};
|
|
282
|
+
/** Narrow interaction metadata plugin (ISP). */
|
|
283
|
+
type InteractionPlugin = PluginIdentity & {
|
|
193
284
|
interactionBlocks?: InteractionBlockRegistration[];
|
|
194
285
|
};
|
|
286
|
+
/**
|
|
287
|
+
* Combined plugin contract (v1). Prefer segregated types for new plugins.
|
|
288
|
+
* @deprecated Prefer `TelemetryPlugin`, `AssessmentPlugin`, or `LifecyclePlugin` via `define*Plugin`.
|
|
289
|
+
*/
|
|
290
|
+
type LessonkitPlugin = PluginIdentity & Partial<Pick<TelemetryPlugin, "onTelemetry" | "onTelemetryBatch" | "wrapTrackingSink"> & Pick<LifecyclePlugin, "setup" | "dispose"> & Pick<AssessmentPlugin, "scoreAssessment"> & Pick<InteractionPlugin, "interactionBlocks">>;
|
|
195
291
|
type PluginHost = {
|
|
196
292
|
readonly plugins: readonly LessonkitPlugin[];
|
|
197
293
|
setupAll: (ctx: LessonkitPluginContext) => void;
|
|
198
294
|
disposeAll: () => void;
|
|
199
295
|
runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
|
|
200
296
|
runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
201
|
-
/** Invoke only `onTelemetryBatch` hooks; events were already filtered at emit time. */
|
|
202
297
|
deliverTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
203
|
-
composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext) => TelemetrySink | undefined;
|
|
298
|
+
composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext | (() => LessonkitPluginContext)) => TelemetrySink | undefined;
|
|
204
299
|
scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
|
|
205
300
|
};
|
|
206
|
-
|
|
207
|
-
|
|
301
|
+
/** Segregated plugin registry (ISP + SRP). */
|
|
302
|
+
type PluginRegistry = PluginHost;
|
|
303
|
+
|
|
304
|
+
type CourseLifecycleContext = {
|
|
305
|
+
courseId: CourseId;
|
|
306
|
+
sessionId: string;
|
|
307
|
+
attemptId?: string;
|
|
308
|
+
user?: TelemetryUser;
|
|
309
|
+
storage: StoragePort;
|
|
310
|
+
pluginHost: PluginRegistry | null;
|
|
311
|
+
lxpackBridge: "auto" | "off";
|
|
312
|
+
};
|
|
313
|
+
type CourseLifecycleDeps = {
|
|
314
|
+
emitCourseStartedEvent: (ctx: CourseLifecycleContext) => boolean;
|
|
315
|
+
};
|
|
316
|
+
declare function tryEmitCourseStarted(ctx: CourseLifecycleContext, deps: CourseLifecycleDeps, alreadyEmittedToSink: boolean): {
|
|
317
|
+
emitted: boolean;
|
|
318
|
+
marked: boolean;
|
|
319
|
+
};
|
|
320
|
+
declare function buildCourseStartedTelemetryEvent(ctx: CourseLifecycleContext): TelemetryEvent;
|
|
321
|
+
type LessonCompletionEmitter = (lessonId: LessonId, durationMs?: number) => void;
|
|
322
|
+
declare function completeLessonWithTelemetry(opts: {
|
|
323
|
+
progress: ProgressController;
|
|
324
|
+
lessonId: LessonId;
|
|
325
|
+
nowMs: number;
|
|
326
|
+
emitLessonCompleted: LessonCompletionEmitter;
|
|
327
|
+
}): boolean;
|
|
328
|
+
declare function completeCourseWithTelemetry(opts: {
|
|
329
|
+
progress: ProgressController;
|
|
330
|
+
nowMs: number;
|
|
331
|
+
emitLessonCompleted: LessonCompletionEmitter;
|
|
332
|
+
emitCourseCompleted: () => void;
|
|
333
|
+
}): boolean;
|
|
334
|
+
|
|
335
|
+
type LessonkitRuntimeVersion = "v1" | "v2";
|
|
336
|
+
type HeadlessLessonkitConfig = {
|
|
337
|
+
courseId: CourseId;
|
|
338
|
+
runtimeVersion?: LessonkitRuntimeVersion;
|
|
339
|
+
session?: {
|
|
340
|
+
sessionId?: string;
|
|
341
|
+
attemptId?: string;
|
|
342
|
+
user?: TelemetryUser;
|
|
343
|
+
};
|
|
344
|
+
plugins?: PluginRegistry | null;
|
|
345
|
+
};
|
|
346
|
+
type HeadlessRuntimePorts = {
|
|
347
|
+
storage?: StoragePort;
|
|
348
|
+
clock?: ClockPort;
|
|
349
|
+
};
|
|
350
|
+
type HeadlessLessonkitRuntime = {
|
|
351
|
+
readonly config: HeadlessLessonkitConfig;
|
|
352
|
+
readonly progress: ProgressController;
|
|
353
|
+
getProgressState: () => ProgressState;
|
|
354
|
+
getSession: () => {
|
|
355
|
+
sessionId: string;
|
|
356
|
+
attemptId?: string;
|
|
357
|
+
user?: TelemetryUser;
|
|
358
|
+
};
|
|
359
|
+
updateConfig: (next: Partial<HeadlessLessonkitConfig>) => void;
|
|
360
|
+
setActiveLesson: (lessonId: LessonId, emit: (name: TelemetryEventName, data?: unknown, lessonId?: LessonId) => void) => void;
|
|
361
|
+
completeLesson: (lessonId: LessonId, emit: (name: TelemetryEventName, data?: unknown, lessonId?: LessonId) => void) => void;
|
|
362
|
+
completeCourse: (emit: (name: TelemetryEventName, data?: unknown, lessonId?: LessonId) => void) => void;
|
|
363
|
+
track: (name: TelemetryEventName, data: unknown | undefined, emit: (event: ReturnType<typeof tryBuildTelemetryEvent>) => void, lessonId?: LessonId) => void;
|
|
364
|
+
resetForCourseChange: (courseId: CourseId) => void;
|
|
365
|
+
};
|
|
366
|
+
declare function createLessonkitRuntime(config: HeadlessLessonkitConfig, ports?: HeadlessRuntimePorts): HeadlessLessonkitRuntime;
|
|
367
|
+
|
|
368
|
+
declare function createPluginRegistry(plugins?: readonly LessonkitPlugin[]): PluginRegistry;
|
|
369
|
+
|
|
370
|
+
declare function defineTelemetryPlugin(plugin: TelemetryPlugin): LessonkitPlugin;
|
|
371
|
+
declare function defineAssessmentPlugin(plugin: AssessmentPlugin): LessonkitPlugin;
|
|
372
|
+
declare function defineLifecyclePlugin(plugin: LifecyclePlugin): LessonkitPlugin;
|
|
208
373
|
|
|
209
|
-
export { type AssessmentScoreInput, type AssessmentScoreResult, type BlockId, type CheckId, type CourseId, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionBlockRegistration, type InteractionData, type LessonId, type LessonLifecycleData, type LessonkitPlugin, type LessonkitPluginContext, type LessonkitPluginKind, type LessonkitUrnParts, type PluginHost, type QuizAnsweredData, type QuizCompletedData, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetrySink, type TelemetryUser, type TrackingClient, assertValidId, buildLessonkitUrn, buildTelemetryCatalog,
|
|
374
|
+
export { type AssessmentPlugin, type AssessmentScoreInput, type AssessmentScoreResult, type BlockId, type BuildTelemetryEventInput, type CheckId, type ClockPort, type CourseId, type CourseLifecycleContext, type CourseLifecycleDeps, type EmitContext, type HeadlessLessonkitConfig, type HeadlessLessonkitRuntime, type HeadlessRuntimePorts, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionBlockRegistration, type InteractionData, type InteractionPlugin, type LessonCompletionEmitter, type LessonId, type LessonLifecycleData, type LessonkitPlugin, type LessonkitPluginContext, type LessonkitPluginKind, type LessonkitRuntimeVersion, type LessonkitUrnParts, type LifecyclePlugin, type PluginHost, type PluginRegistry, type ProgressController, type ProgressState, type QuizAnsweredData, type QuizCompletedData, SESSION_STORAGE_KEY, type StoragePort, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, type TelemetryPlugin, type TelemetrySink, type TelemetryUser, type TimerPort, type TrackingClient, assertValidId, buildCourseStartedTelemetryEvent, buildLessonkitUrn, buildTelemetryCatalog, buildTelemetryEvent, completeCourseWithTelemetry, completeLessonWithTelemetry, createDefaultClock, createGlobalTimer, createLessonkitRuntime, createNoopStorage, createPluginRegistry, createProgressController, createSessionId, createSessionStoragePort, createTelemetryPipeline, createTrackingClient, createTrackingPipelineSink, defineAssessmentPlugin, defineLifecyclePlugin, defineTelemetryPlugin, deriveId, getTabSessionId, hasCourseStarted, hasCourseStartedEmittedToTracking, markCourseStarted, markCourseStartedEmittedToTracking, migrateCourseStartedMark, nowIso, resetStoragePortForTests, resetTelemetryBuilderWarningsForTests, resolveSessionId, slugifyId, telemetryCatalogVersion, tryBuildTelemetryEvent, tryEmitCourseStarted, validateId };
|
package/dist/index.d.ts
CHANGED
|
@@ -143,12 +143,102 @@ declare function createSessionId(): string;
|
|
|
143
143
|
|
|
144
144
|
declare function nowIso(): string;
|
|
145
145
|
|
|
146
|
+
type BuildTelemetryEventInput = {
|
|
147
|
+
name: TelemetryEventName;
|
|
148
|
+
courseId: CourseId;
|
|
149
|
+
lessonId?: LessonId;
|
|
150
|
+
sessionId?: string;
|
|
151
|
+
attemptId?: string;
|
|
152
|
+
user?: TelemetryUser;
|
|
153
|
+
data?: unknown;
|
|
154
|
+
timestamp?: string;
|
|
155
|
+
};
|
|
156
|
+
/** Reset dev-warning state (tests only). */
|
|
157
|
+
declare function resetTelemetryBuilderWarningsForTests(): void;
|
|
158
|
+
/**
|
|
159
|
+
* Build a typed telemetry event from a catalog event name and context.
|
|
160
|
+
* Validates lesson-scoped events require `lessonId`.
|
|
161
|
+
*/
|
|
162
|
+
declare function buildTelemetryEvent(opts: BuildTelemetryEventInput): TelemetryEvent;
|
|
163
|
+
/**
|
|
164
|
+
* Like `buildTelemetryEvent`, but returns null (with a dev warning) when quiz events lack an active lesson.
|
|
165
|
+
*/
|
|
166
|
+
declare function tryBuildTelemetryEvent(opts: BuildTelemetryEventInput): TelemetryEvent | null;
|
|
167
|
+
|
|
168
|
+
type EmitContext = {
|
|
169
|
+
courseId: string;
|
|
170
|
+
sessionId?: string;
|
|
171
|
+
attemptId?: string;
|
|
172
|
+
};
|
|
173
|
+
/** Pluggable telemetry output (OCP). Distinct from the legacy `TelemetrySink` function type. */
|
|
174
|
+
type TelemetryPipelineSink = {
|
|
175
|
+
readonly id: string;
|
|
176
|
+
emit(event: TelemetryEvent, ctx: EmitContext): void | Promise<void>;
|
|
177
|
+
};
|
|
178
|
+
type TelemetryPipeline = {
|
|
179
|
+
readonly sinks: readonly TelemetryPipelineSink[];
|
|
180
|
+
emit(event: TelemetryEvent, ctx?: EmitContext): void | Promise<void>;
|
|
181
|
+
};
|
|
182
|
+
declare function createTelemetryPipeline(sinks: TelemetryPipelineSink[]): TelemetryPipeline;
|
|
183
|
+
declare function createTrackingPipelineSink(id: string, track: (event: TelemetryEvent) => void): TelemetryPipelineSink;
|
|
184
|
+
|
|
185
|
+
type StoragePort = {
|
|
186
|
+
getItem: (key: string) => string | null;
|
|
187
|
+
setItem: (key: string, value: string) => void;
|
|
188
|
+
removeItem?: (key: string) => void;
|
|
189
|
+
/** @internal Test helper to clear in-memory fallback state. */
|
|
190
|
+
resetForTests?: () => void;
|
|
191
|
+
};
|
|
192
|
+
type ClockPort = {
|
|
193
|
+
nowMs: () => number;
|
|
194
|
+
nowIso: () => string;
|
|
195
|
+
};
|
|
196
|
+
type TimerPort = {
|
|
197
|
+
setInterval: (fn: () => void, ms: number) => ReturnType<typeof globalThis.setInterval>;
|
|
198
|
+
clearInterval: (id: ReturnType<typeof globalThis.setInterval>) => void;
|
|
199
|
+
};
|
|
200
|
+
declare function createDefaultClock(): ClockPort;
|
|
201
|
+
declare function createNoopStorage(): StoragePort;
|
|
202
|
+
declare function resetStoragePortForTests(storage: StoragePort): void;
|
|
203
|
+
declare function createSessionStoragePort(): StoragePort;
|
|
204
|
+
declare function createGlobalTimer(): TimerPort;
|
|
205
|
+
|
|
206
|
+
type ProgressState = {
|
|
207
|
+
activeLessonId?: LessonId;
|
|
208
|
+
completedLessonIds: ReadonlySet<LessonId>;
|
|
209
|
+
courseCompleted: boolean;
|
|
210
|
+
};
|
|
211
|
+
type ProgressController = {
|
|
212
|
+
getState: () => ProgressState;
|
|
213
|
+
setActiveLesson: (lessonId: LessonId, startedAtMs: number) => {
|
|
214
|
+
previousLessonId?: LessonId;
|
|
215
|
+
};
|
|
216
|
+
completeLesson: (lessonId: LessonId, completedAtMs: number) => {
|
|
217
|
+
durationMs?: number;
|
|
218
|
+
didComplete: boolean;
|
|
219
|
+
};
|
|
220
|
+
completeCourse: () => {
|
|
221
|
+
didComplete: boolean;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
declare function createProgressController(): ProgressController;
|
|
225
|
+
|
|
226
|
+
declare const SESSION_STORAGE_KEY = "lessonkit:sessionId";
|
|
227
|
+
declare function getTabSessionId(storage: StoragePort): string | null;
|
|
228
|
+
declare function resolveSessionId(storage: StoragePort, provided?: string): string;
|
|
229
|
+
declare function hasCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
|
|
230
|
+
declare function markCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): void;
|
|
231
|
+
declare function hasCourseStartedEmittedToTracking(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
|
|
232
|
+
declare function markCourseStartedEmittedToTracking(storage: StoragePort, sessionId: string, courseId?: CourseId): void;
|
|
233
|
+
declare function migrateCourseStartedMark(storage: StoragePort, fromSessionId: string, toSessionId: string, courseId?: CourseId): void;
|
|
234
|
+
|
|
146
235
|
/** Plugin category — aligns with roadmap extension areas. */
|
|
147
236
|
type LessonkitPluginKind = "analytics" | "lms" | "assessment" | "interaction" | "ai";
|
|
148
237
|
type LessonkitPluginContext = {
|
|
149
238
|
courseId: CourseId;
|
|
150
239
|
sessionId?: string;
|
|
151
240
|
attemptId?: string;
|
|
241
|
+
user?: TelemetryUser;
|
|
152
242
|
};
|
|
153
243
|
type AssessmentScoreInput = {
|
|
154
244
|
checkId: string;
|
|
@@ -167,43 +257,118 @@ type InteractionBlockRegistration = {
|
|
|
167
257
|
catalogVersion?: string;
|
|
168
258
|
description?: string;
|
|
169
259
|
};
|
|
170
|
-
|
|
171
|
-
* Framework plugin contract (v1). Plugins are plain objects registered on `LessonkitProvider`.
|
|
172
|
-
* Marketplace / dynamic loading are out of scope until Studio post-1.0.
|
|
173
|
-
*/
|
|
174
|
-
type LessonkitPlugin = {
|
|
260
|
+
type PluginIdentity = {
|
|
175
261
|
id: string;
|
|
176
262
|
version: string;
|
|
177
263
|
kind: LessonkitPluginKind;
|
|
178
264
|
name?: string;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* Observe or filter telemetry before tracking/xAPI. Return `null` to drop the event.
|
|
183
|
-
* Hooks run in registration order.
|
|
184
|
-
*/
|
|
265
|
+
};
|
|
266
|
+
/** Narrow telemetry plugin contract (ISP). */
|
|
267
|
+
type TelemetryPlugin = PluginIdentity & {
|
|
185
268
|
onTelemetry?: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
|
|
186
|
-
/** Optional batch observer (analytics); receives events after per-event hooks. */
|
|
187
269
|
onTelemetryBatch?: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => void;
|
|
188
|
-
/** Wrap the configured tracking sink (analytics plugins). First registered = innermost. */
|
|
189
270
|
wrapTrackingSink?: (sink: TelemetrySink, ctx: LessonkitPluginContext) => TelemetrySink;
|
|
190
|
-
|
|
271
|
+
};
|
|
272
|
+
/** Narrow lifecycle plugin contract (ISP). */
|
|
273
|
+
type LifecyclePlugin = PluginIdentity & {
|
|
274
|
+
setup?: (ctx: LessonkitPluginContext) => void;
|
|
275
|
+
dispose?: () => void;
|
|
276
|
+
};
|
|
277
|
+
/** Narrow assessment plugin contract (ISP). */
|
|
278
|
+
type AssessmentPlugin = PluginIdentity & {
|
|
279
|
+
kind: "assessment";
|
|
191
280
|
scoreAssessment?: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
|
|
192
|
-
|
|
281
|
+
};
|
|
282
|
+
/** Narrow interaction metadata plugin (ISP). */
|
|
283
|
+
type InteractionPlugin = PluginIdentity & {
|
|
193
284
|
interactionBlocks?: InteractionBlockRegistration[];
|
|
194
285
|
};
|
|
286
|
+
/**
|
|
287
|
+
* Combined plugin contract (v1). Prefer segregated types for new plugins.
|
|
288
|
+
* @deprecated Prefer `TelemetryPlugin`, `AssessmentPlugin`, or `LifecyclePlugin` via `define*Plugin`.
|
|
289
|
+
*/
|
|
290
|
+
type LessonkitPlugin = PluginIdentity & Partial<Pick<TelemetryPlugin, "onTelemetry" | "onTelemetryBatch" | "wrapTrackingSink"> & Pick<LifecyclePlugin, "setup" | "dispose"> & Pick<AssessmentPlugin, "scoreAssessment"> & Pick<InteractionPlugin, "interactionBlocks">>;
|
|
195
291
|
type PluginHost = {
|
|
196
292
|
readonly plugins: readonly LessonkitPlugin[];
|
|
197
293
|
setupAll: (ctx: LessonkitPluginContext) => void;
|
|
198
294
|
disposeAll: () => void;
|
|
199
295
|
runTelemetry: (event: TelemetryEvent, ctx: LessonkitPluginContext) => TelemetryEvent | null;
|
|
200
296
|
runTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
201
|
-
/** Invoke only `onTelemetryBatch` hooks; events were already filtered at emit time. */
|
|
202
297
|
deliverTelemetryBatch: (events: TelemetryEvent[], ctx: LessonkitPluginContext) => TelemetryEvent[];
|
|
203
|
-
composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext) => TelemetrySink | undefined;
|
|
298
|
+
composeTrackingSink: (sink: TelemetrySink | undefined, ctx: LessonkitPluginContext | (() => LessonkitPluginContext)) => TelemetrySink | undefined;
|
|
204
299
|
scoreAssessment: (input: AssessmentScoreInput, ctx: LessonkitPluginContext) => AssessmentScoreResult | null;
|
|
205
300
|
};
|
|
206
|
-
|
|
207
|
-
|
|
301
|
+
/** Segregated plugin registry (ISP + SRP). */
|
|
302
|
+
type PluginRegistry = PluginHost;
|
|
303
|
+
|
|
304
|
+
type CourseLifecycleContext = {
|
|
305
|
+
courseId: CourseId;
|
|
306
|
+
sessionId: string;
|
|
307
|
+
attemptId?: string;
|
|
308
|
+
user?: TelemetryUser;
|
|
309
|
+
storage: StoragePort;
|
|
310
|
+
pluginHost: PluginRegistry | null;
|
|
311
|
+
lxpackBridge: "auto" | "off";
|
|
312
|
+
};
|
|
313
|
+
type CourseLifecycleDeps = {
|
|
314
|
+
emitCourseStartedEvent: (ctx: CourseLifecycleContext) => boolean;
|
|
315
|
+
};
|
|
316
|
+
declare function tryEmitCourseStarted(ctx: CourseLifecycleContext, deps: CourseLifecycleDeps, alreadyEmittedToSink: boolean): {
|
|
317
|
+
emitted: boolean;
|
|
318
|
+
marked: boolean;
|
|
319
|
+
};
|
|
320
|
+
declare function buildCourseStartedTelemetryEvent(ctx: CourseLifecycleContext): TelemetryEvent;
|
|
321
|
+
type LessonCompletionEmitter = (lessonId: LessonId, durationMs?: number) => void;
|
|
322
|
+
declare function completeLessonWithTelemetry(opts: {
|
|
323
|
+
progress: ProgressController;
|
|
324
|
+
lessonId: LessonId;
|
|
325
|
+
nowMs: number;
|
|
326
|
+
emitLessonCompleted: LessonCompletionEmitter;
|
|
327
|
+
}): boolean;
|
|
328
|
+
declare function completeCourseWithTelemetry(opts: {
|
|
329
|
+
progress: ProgressController;
|
|
330
|
+
nowMs: number;
|
|
331
|
+
emitLessonCompleted: LessonCompletionEmitter;
|
|
332
|
+
emitCourseCompleted: () => void;
|
|
333
|
+
}): boolean;
|
|
334
|
+
|
|
335
|
+
type LessonkitRuntimeVersion = "v1" | "v2";
|
|
336
|
+
type HeadlessLessonkitConfig = {
|
|
337
|
+
courseId: CourseId;
|
|
338
|
+
runtimeVersion?: LessonkitRuntimeVersion;
|
|
339
|
+
session?: {
|
|
340
|
+
sessionId?: string;
|
|
341
|
+
attemptId?: string;
|
|
342
|
+
user?: TelemetryUser;
|
|
343
|
+
};
|
|
344
|
+
plugins?: PluginRegistry | null;
|
|
345
|
+
};
|
|
346
|
+
type HeadlessRuntimePorts = {
|
|
347
|
+
storage?: StoragePort;
|
|
348
|
+
clock?: ClockPort;
|
|
349
|
+
};
|
|
350
|
+
type HeadlessLessonkitRuntime = {
|
|
351
|
+
readonly config: HeadlessLessonkitConfig;
|
|
352
|
+
readonly progress: ProgressController;
|
|
353
|
+
getProgressState: () => ProgressState;
|
|
354
|
+
getSession: () => {
|
|
355
|
+
sessionId: string;
|
|
356
|
+
attemptId?: string;
|
|
357
|
+
user?: TelemetryUser;
|
|
358
|
+
};
|
|
359
|
+
updateConfig: (next: Partial<HeadlessLessonkitConfig>) => void;
|
|
360
|
+
setActiveLesson: (lessonId: LessonId, emit: (name: TelemetryEventName, data?: unknown, lessonId?: LessonId) => void) => void;
|
|
361
|
+
completeLesson: (lessonId: LessonId, emit: (name: TelemetryEventName, data?: unknown, lessonId?: LessonId) => void) => void;
|
|
362
|
+
completeCourse: (emit: (name: TelemetryEventName, data?: unknown, lessonId?: LessonId) => void) => void;
|
|
363
|
+
track: (name: TelemetryEventName, data: unknown | undefined, emit: (event: ReturnType<typeof tryBuildTelemetryEvent>) => void, lessonId?: LessonId) => void;
|
|
364
|
+
resetForCourseChange: (courseId: CourseId) => void;
|
|
365
|
+
};
|
|
366
|
+
declare function createLessonkitRuntime(config: HeadlessLessonkitConfig, ports?: HeadlessRuntimePorts): HeadlessLessonkitRuntime;
|
|
367
|
+
|
|
368
|
+
declare function createPluginRegistry(plugins?: readonly LessonkitPlugin[]): PluginRegistry;
|
|
369
|
+
|
|
370
|
+
declare function defineTelemetryPlugin(plugin: TelemetryPlugin): LessonkitPlugin;
|
|
371
|
+
declare function defineAssessmentPlugin(plugin: AssessmentPlugin): LessonkitPlugin;
|
|
372
|
+
declare function defineLifecyclePlugin(plugin: LifecyclePlugin): LessonkitPlugin;
|
|
208
373
|
|
|
209
|
-
export { type AssessmentScoreInput, type AssessmentScoreResult, type BlockId, type CheckId, type CourseId, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionBlockRegistration, type InteractionData, type LessonId, type LessonLifecycleData, type LessonkitPlugin, type LessonkitPluginContext, type LessonkitPluginKind, type LessonkitUrnParts, type PluginHost, type QuizAnsweredData, type QuizCompletedData, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetrySink, type TelemetryUser, type TrackingClient, assertValidId, buildLessonkitUrn, buildTelemetryCatalog,
|
|
374
|
+
export { type AssessmentPlugin, type AssessmentScoreInput, type AssessmentScoreResult, type BlockId, type BuildTelemetryEventInput, type CheckId, type ClockPort, type CourseId, type CourseLifecycleContext, type CourseLifecycleDeps, type EmitContext, type HeadlessLessonkitConfig, type HeadlessLessonkitRuntime, type HeadlessRuntimePorts, ID_MAX_LENGTH, ID_PATTERN, type IdentityValidationIssue, type IdentityValidationResult, type InteractionBlockRegistration, type InteractionData, type InteractionPlugin, type LessonCompletionEmitter, type LessonId, type LessonLifecycleData, type LessonkitPlugin, type LessonkitPluginContext, type LessonkitPluginKind, type LessonkitRuntimeVersion, type LessonkitUrnParts, type LifecyclePlugin, type PluginHost, type PluginRegistry, type ProgressController, type ProgressState, type QuizAnsweredData, type QuizCompletedData, SESSION_STORAGE_KEY, type StoragePort, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, type TelemetryPlugin, type TelemetrySink, type TelemetryUser, type TimerPort, type TrackingClient, assertValidId, buildCourseStartedTelemetryEvent, buildLessonkitUrn, buildTelemetryCatalog, buildTelemetryEvent, completeCourseWithTelemetry, completeLessonWithTelemetry, createDefaultClock, createGlobalTimer, createLessonkitRuntime, createNoopStorage, createPluginRegistry, createProgressController, createSessionId, createSessionStoragePort, createTelemetryPipeline, createTrackingClient, createTrackingPipelineSink, defineAssessmentPlugin, defineLifecyclePlugin, defineTelemetryPlugin, deriveId, getTabSessionId, hasCourseStarted, hasCourseStartedEmittedToTracking, markCourseStarted, markCourseStartedEmittedToTracking, migrateCourseStartedMark, nowIso, resetStoragePortForTests, resetTelemetryBuilderWarningsForTests, resolveSessionId, slugifyId, telemetryCatalogVersion, tryBuildTelemetryEvent, tryEmitCourseStarted, validateId };
|