@lessonkit/core 1.0.2 → 1.1.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/dist/index.cjs CHANGED
@@ -24,11 +24,13 @@ __export(index_exports, {
24
24
  ID_PATTERN: () => ID_PATTERN,
25
25
  SESSION_STORAGE_KEY: () => SESSION_STORAGE_KEY,
26
26
  TELEMETRY_EVENT_CATALOG: () => TELEMETRY_EVENT_CATALOG,
27
+ TELEMETRY_EVENT_CATALOG_V2: () => TELEMETRY_EVENT_CATALOG_V2,
27
28
  assertNever: () => assertNever,
28
29
  assertValidId: () => assertValidId,
29
30
  buildCourseStartedTelemetryEvent: () => buildCourseStartedTelemetryEvent,
30
31
  buildLessonkitUrn: () => buildLessonkitUrn,
31
32
  buildTelemetryCatalog: () => buildTelemetryCatalog,
33
+ buildTelemetryCatalogV2: () => buildTelemetryCatalogV2,
32
34
  buildTelemetryEvent: () => buildTelemetryEvent,
33
35
  completeCourseWithTelemetry: () => completeCourseWithTelemetry,
34
36
  completeLessonWithTelemetry: () => completeLessonWithTelemetry,
@@ -64,6 +66,7 @@ __export(index_exports, {
64
66
  resetTelemetryBuilderWarningsForTests: () => resetTelemetryBuilderWarningsForTests,
65
67
  resolveSessionId: () => resolveSessionId,
66
68
  slugifyId: () => slugifyId,
69
+ telemetryCatalogV2Version: () => telemetryCatalogV2Version,
67
70
  telemetryCatalogVersion: () => telemetryCatalogVersion,
68
71
  tryBuildTelemetryEvent: () => tryBuildTelemetryEvent,
69
72
  tryEmitCourseStarted: () => tryEmitCourseStarted,
@@ -247,6 +250,30 @@ function buildTelemetryCatalog() {
247
250
  return TELEMETRY_EVENT_CATALOG.map((entry) => ({ ...entry }));
248
251
  }
249
252
 
253
+ // src/telemetryCatalogV2.ts
254
+ var telemetryCatalogV2Version = 2;
255
+ var TELEMETRY_EVENT_CATALOG_V2 = [
256
+ {
257
+ name: "assessment_answered",
258
+ description: "Learner submitted an assessment interaction answer",
259
+ requiredFields: ["courseId", "lessonId", "sessionId", "timestamp"],
260
+ dataFields: ["checkId", "interactionType", "question", "response", "correct"],
261
+ xapiVerb: "http://adlnet.gov/expapi/verbs/answered",
262
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:check:{checkId}"
263
+ },
264
+ {
265
+ name: "assessment_completed",
266
+ description: "Assessment interaction completed (passing criteria met)",
267
+ requiredFields: ["courseId", "lessonId", "sessionId", "timestamp"],
268
+ dataFields: ["checkId", "interactionType", "score", "maxScore", "passingScore"],
269
+ xapiVerb: "http://adlnet.gov/expapi/verbs/completed",
270
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:check:{checkId}"
271
+ }
272
+ ];
273
+ function buildTelemetryCatalogV2() {
274
+ return TELEMETRY_EVENT_CATALOG_V2.map((entry) => ({ ...entry }));
275
+ }
276
+
250
277
  // src/trackingClient.ts
251
278
  function isDevEnvironment() {
252
279
  const g = globalThis;
@@ -395,12 +422,14 @@ function nowIso() {
395
422
 
396
423
  // src/telemetryBuilder.ts
397
424
  var warnedMissingQuizLesson = false;
425
+ var warnedMissingAssessmentLesson = false;
398
426
  function isDevEnvironment2() {
399
427
  const g = globalThis;
400
428
  return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
401
429
  }
402
430
  function resetTelemetryBuilderWarningsForTests() {
403
431
  warnedMissingQuizLesson = false;
432
+ warnedMissingAssessmentLesson = false;
404
433
  }
405
434
  function resolveLessonId(opts, eventName) {
406
435
  const lessonId = opts.lessonId ?? opts.data?.lessonId;
@@ -449,6 +478,16 @@ function buildTelemetryEvent(opts) {
449
478
  if (!lessonId) throw new Error("quiz_completed requires active lessonId");
450
479
  return { name: "quiz_completed", ...base, lessonId, data: opts.data };
451
480
  }
481
+ case "assessment_answered": {
482
+ const lessonId = opts.lessonId;
483
+ if (!lessonId) throw new Error("assessment_answered requires active lessonId");
484
+ return { name: "assessment_answered", ...base, lessonId, data: opts.data };
485
+ }
486
+ case "assessment_completed": {
487
+ const lessonId = opts.lessonId;
488
+ if (!lessonId) throw new Error("assessment_completed requires active lessonId");
489
+ return { name: "assessment_completed", ...base, lessonId, data: opts.data };
490
+ }
452
491
  case "interaction":
453
492
  return {
454
493
  name: "interaction",
@@ -461,13 +500,21 @@ function buildTelemetryEvent(opts) {
461
500
  }
462
501
  }
463
502
  function tryBuildTelemetryEvent(opts) {
464
- const isQuiz = opts.name === "quiz_answered" || opts.name === "quiz_completed";
465
- if (isQuiz && !opts.lessonId) {
466
- if (isDevEnvironment2() && !warnedMissingQuizLesson) {
467
- warnedMissingQuizLesson = true;
468
- console.warn(
469
- `[lessonkit] ${opts.name} skipped: wrap <Quiz> in <Lesson> so an active lessonId is available`
470
- );
503
+ const needsLesson = opts.name === "quiz_answered" || opts.name === "quiz_completed" || opts.name === "assessment_answered" || opts.name === "assessment_completed";
504
+ if (needsLesson && !opts.lessonId) {
505
+ if (isDevEnvironment2()) {
506
+ if (opts.name.startsWith("quiz_") && !warnedMissingQuizLesson) {
507
+ warnedMissingQuizLesson = true;
508
+ console.warn(
509
+ `[lessonkit] ${opts.name} skipped: wrap <Quiz> in <Lesson> so an active lessonId is available`
510
+ );
511
+ }
512
+ if (opts.name.startsWith("assessment_") && !warnedMissingAssessmentLesson) {
513
+ warnedMissingAssessmentLesson = true;
514
+ console.warn(
515
+ `[lessonkit] ${opts.name} skipped: wrap assessment blocks in <Lesson> so an active lessonId is available`
516
+ );
517
+ }
471
518
  }
472
519
  return null;
473
520
  }
@@ -974,11 +1021,13 @@ function defineLifecyclePlugin(plugin) {
974
1021
  ID_PATTERN,
975
1022
  SESSION_STORAGE_KEY,
976
1023
  TELEMETRY_EVENT_CATALOG,
1024
+ TELEMETRY_EVENT_CATALOG_V2,
977
1025
  assertNever,
978
1026
  assertValidId,
979
1027
  buildCourseStartedTelemetryEvent,
980
1028
  buildLessonkitUrn,
981
1029
  buildTelemetryCatalog,
1030
+ buildTelemetryCatalogV2,
982
1031
  buildTelemetryEvent,
983
1032
  completeCourseWithTelemetry,
984
1033
  completeLessonWithTelemetry,
@@ -1014,6 +1063,7 @@ function defineLifecyclePlugin(plugin) {
1014
1063
  resetTelemetryBuilderWarningsForTests,
1015
1064
  resolveSessionId,
1016
1065
  slugifyId,
1066
+ telemetryCatalogV2Version,
1017
1067
  telemetryCatalogVersion,
1018
1068
  tryBuildTelemetryEvent,
1019
1069
  tryEmitCourseStarted,
package/dist/index.d.cts CHANGED
@@ -54,7 +54,41 @@ type LessonkitUrnParts = {
54
54
  */
55
55
  declare function buildLessonkitUrn(parts: LessonkitUrnParts): LessonkitUrn;
56
56
 
57
- type TelemetryEventName = "course_started" | "course_completed" | "lesson_started" | "lesson_completed" | "lesson_time_on_task" | "quiz_answered" | "quiz_completed" | "interaction";
57
+ /** H5P-aligned interaction kinds for assessment telemetry and xAPI. */
58
+ type AssessmentInteractionType = "mcq" | "trueFalse" | "fillInBlanks" | "markTheWords" | "dragTheWords" | "dragAndDrop" | "assessmentSequence";
59
+ /** Behaviour flags aligned with H5P question types. */
60
+ type AssessmentBehaviour = {
61
+ enableRetry?: boolean;
62
+ enableSolutionsButton?: boolean;
63
+ autoCheck?: boolean;
64
+ };
65
+ /** Payload for xAPI mapping from assessment components. */
66
+ type AssessmentXAPIData = {
67
+ checkId: CheckId;
68
+ interactionType: AssessmentInteractionType;
69
+ response?: string | string[] | boolean | Record<string, unknown>;
70
+ correct?: boolean;
71
+ score?: number;
72
+ maxScore?: number;
73
+ };
74
+ /**
75
+ * Imperative handle for scored blocks (H5P question-type contract analogue).
76
+ * Parent containers (`AssessmentSequence`, future compounds) may call these methods.
77
+ */
78
+ type AssessmentHandle = {
79
+ getScore: () => number;
80
+ getMaxScore: () => number;
81
+ getAnswerGiven: () => boolean;
82
+ resetTask: () => void;
83
+ showSolutions: () => void;
84
+ getXAPIData: () => AssessmentXAPIData;
85
+ };
86
+ type AssessmentBaseProps = AssessmentBehaviour & {
87
+ checkId: CheckId;
88
+ passingScore?: number;
89
+ };
90
+
91
+ type TelemetryEventName = "course_started" | "course_completed" | "lesson_started" | "lesson_completed" | "lesson_time_on_task" | "quiz_answered" | "quiz_completed" | "assessment_answered" | "assessment_completed" | "interaction";
58
92
  type TelemetryUser = {
59
93
  id?: string;
60
94
  email?: string;
@@ -87,6 +121,20 @@ type QuizCompletedData = {
87
121
  maxScore?: number;
88
122
  passingScore?: number;
89
123
  };
124
+ type AssessmentAnsweredData = {
125
+ checkId: CheckId;
126
+ interactionType: AssessmentInteractionType;
127
+ question?: string;
128
+ response?: string | string[] | boolean | Record<string, unknown>;
129
+ correct?: boolean;
130
+ };
131
+ type AssessmentCompletedData = {
132
+ checkId: CheckId;
133
+ interactionType: AssessmentInteractionType;
134
+ score?: number;
135
+ maxScore?: number;
136
+ passingScore?: number;
137
+ };
90
138
  type InteractionData = {
91
139
  kind?: string;
92
140
  blockId?: BlockId;
@@ -121,6 +169,14 @@ type TelemetryEvent = (TelemetryEventBase & {
121
169
  name: "quiz_completed";
122
170
  lessonId: LessonId;
123
171
  data: QuizCompletedData;
172
+ }) | (TelemetryEventBase & {
173
+ name: "assessment_answered";
174
+ lessonId: LessonId;
175
+ data: AssessmentAnsweredData;
176
+ }) | (TelemetryEventBase & {
177
+ name: "assessment_completed";
178
+ lessonId: LessonId;
179
+ data: AssessmentCompletedData;
124
180
  }) | (TelemetryEventBase & {
125
181
  name: "interaction";
126
182
  lessonId?: LessonId;
@@ -152,6 +208,18 @@ type TelemetryCatalogEntry = {
152
208
  declare const TELEMETRY_EVENT_CATALOG: TelemetryCatalogEntry[];
153
209
  declare function buildTelemetryCatalog(): TelemetryCatalogEntry[];
154
210
 
211
+ declare const telemetryCatalogV2Version: 2;
212
+ type TelemetryCatalogV2Entry = {
213
+ name: Extract<TelemetryEventName, "assessment_answered" | "assessment_completed">;
214
+ description: string;
215
+ requiredFields: string[];
216
+ dataFields: string[];
217
+ xapiVerb: string;
218
+ urnPattern: string;
219
+ };
220
+ declare const TELEMETRY_EVENT_CATALOG_V2: TelemetryCatalogV2Entry[];
221
+ declare function buildTelemetryCatalogV2(): TelemetryCatalogV2Entry[];
222
+
155
223
  declare function createTrackingClient(opts?: {
156
224
  sink?: TelemetrySink;
157
225
  batch?: {
@@ -201,6 +269,14 @@ type BuildTelemetryEventInput = (BuildTelemetryEventContext & {
201
269
  name: "quiz_completed";
202
270
  lessonId?: LessonId;
203
271
  data: QuizCompletedData;
272
+ }) | (BuildTelemetryEventContext & {
273
+ name: "assessment_answered";
274
+ lessonId?: LessonId;
275
+ data: AssessmentAnsweredData;
276
+ }) | (BuildTelemetryEventContext & {
277
+ name: "assessment_completed";
278
+ lessonId?: LessonId;
279
+ data: AssessmentCompletedData;
204
280
  }) | (BuildTelemetryEventContext & {
205
281
  name: "interaction";
206
282
  lessonId?: LessonId;
@@ -429,4 +505,4 @@ declare function defineTelemetryPlugin(plugin: TelemetryPlugin): LessonkitPlugin
429
505
  declare function defineAssessmentPlugin(plugin: AssessmentPlugin): LessonkitPlugin;
430
506
  declare function defineLifecyclePlugin(plugin: LifecyclePlugin): LessonkitPlugin;
431
507
 
432
- 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 IdentityIdPath, 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 LessonkitUrn, type LessonkitUrnParts, type LifecyclePlugin, type PluginHost, type PluginIdentity, type PluginRegistry, type ProgressController, type ProgressState, type QuizAnsweredData, type QuizCompletedData, SESSION_STORAGE_KEY, type StoragePort, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryDataFor, type TelemetryEmitFn, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, type TelemetryPlugin, type TelemetrySink, type TelemetryUser, type TimerPort, type TrackingClient, assertNever, 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, hasCourseStartedPipelineDelivered, markCourseStarted, markCourseStartedEmittedToTracking, markCourseStartedPipelineDelivered, migrateCourseStartedMark, nowIso, parseBlockId, parseCheckId, parseCourseId, parseLessonId, resetStoragePortForTests, resetTelemetryBuilderWarningsForTests, resolveSessionId, slugifyId, telemetryCatalogVersion, tryBuildTelemetryEvent, tryEmitCourseStarted, validateId };
508
+ export { type AssessmentAnsweredData, type AssessmentBaseProps, type AssessmentBehaviour, type AssessmentCompletedData, type AssessmentHandle, type AssessmentInteractionType, type AssessmentPlugin, type AssessmentScoreInput, type AssessmentScoreResult, type AssessmentXAPIData, 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 IdentityIdPath, 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 LessonkitUrn, type LessonkitUrnParts, type LifecyclePlugin, type PluginHost, type PluginIdentity, type PluginRegistry, type ProgressController, type ProgressState, type QuizAnsweredData, type QuizCompletedData, SESSION_STORAGE_KEY, type StoragePort, TELEMETRY_EVENT_CATALOG, TELEMETRY_EVENT_CATALOG_V2, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryCatalogV2Entry, type TelemetryDataFor, type TelemetryEmitFn, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, type TelemetryPlugin, type TelemetrySink, type TelemetryUser, type TimerPort, type TrackingClient, assertNever, assertValidId, buildCourseStartedTelemetryEvent, buildLessonkitUrn, buildTelemetryCatalog, buildTelemetryCatalogV2, buildTelemetryEvent, completeCourseWithTelemetry, completeLessonWithTelemetry, createDefaultClock, createGlobalTimer, createLessonkitRuntime, createNoopStorage, createPluginRegistry, createProgressController, createSessionId, createSessionStoragePort, createTelemetryPipeline, createTrackingClient, createTrackingPipelineSink, defineAssessmentPlugin, defineLifecyclePlugin, defineTelemetryPlugin, deriveId, getTabSessionId, hasCourseStarted, hasCourseStartedEmittedToTracking, hasCourseStartedPipelineDelivered, markCourseStarted, markCourseStartedEmittedToTracking, markCourseStartedPipelineDelivered, migrateCourseStartedMark, nowIso, parseBlockId, parseCheckId, parseCourseId, parseLessonId, resetStoragePortForTests, resetTelemetryBuilderWarningsForTests, resolveSessionId, slugifyId, telemetryCatalogV2Version, telemetryCatalogVersion, tryBuildTelemetryEvent, tryEmitCourseStarted, validateId };
package/dist/index.d.ts CHANGED
@@ -54,7 +54,41 @@ type LessonkitUrnParts = {
54
54
  */
55
55
  declare function buildLessonkitUrn(parts: LessonkitUrnParts): LessonkitUrn;
56
56
 
57
- type TelemetryEventName = "course_started" | "course_completed" | "lesson_started" | "lesson_completed" | "lesson_time_on_task" | "quiz_answered" | "quiz_completed" | "interaction";
57
+ /** H5P-aligned interaction kinds for assessment telemetry and xAPI. */
58
+ type AssessmentInteractionType = "mcq" | "trueFalse" | "fillInBlanks" | "markTheWords" | "dragTheWords" | "dragAndDrop" | "assessmentSequence";
59
+ /** Behaviour flags aligned with H5P question types. */
60
+ type AssessmentBehaviour = {
61
+ enableRetry?: boolean;
62
+ enableSolutionsButton?: boolean;
63
+ autoCheck?: boolean;
64
+ };
65
+ /** Payload for xAPI mapping from assessment components. */
66
+ type AssessmentXAPIData = {
67
+ checkId: CheckId;
68
+ interactionType: AssessmentInteractionType;
69
+ response?: string | string[] | boolean | Record<string, unknown>;
70
+ correct?: boolean;
71
+ score?: number;
72
+ maxScore?: number;
73
+ };
74
+ /**
75
+ * Imperative handle for scored blocks (H5P question-type contract analogue).
76
+ * Parent containers (`AssessmentSequence`, future compounds) may call these methods.
77
+ */
78
+ type AssessmentHandle = {
79
+ getScore: () => number;
80
+ getMaxScore: () => number;
81
+ getAnswerGiven: () => boolean;
82
+ resetTask: () => void;
83
+ showSolutions: () => void;
84
+ getXAPIData: () => AssessmentXAPIData;
85
+ };
86
+ type AssessmentBaseProps = AssessmentBehaviour & {
87
+ checkId: CheckId;
88
+ passingScore?: number;
89
+ };
90
+
91
+ type TelemetryEventName = "course_started" | "course_completed" | "lesson_started" | "lesson_completed" | "lesson_time_on_task" | "quiz_answered" | "quiz_completed" | "assessment_answered" | "assessment_completed" | "interaction";
58
92
  type TelemetryUser = {
59
93
  id?: string;
60
94
  email?: string;
@@ -87,6 +121,20 @@ type QuizCompletedData = {
87
121
  maxScore?: number;
88
122
  passingScore?: number;
89
123
  };
124
+ type AssessmentAnsweredData = {
125
+ checkId: CheckId;
126
+ interactionType: AssessmentInteractionType;
127
+ question?: string;
128
+ response?: string | string[] | boolean | Record<string, unknown>;
129
+ correct?: boolean;
130
+ };
131
+ type AssessmentCompletedData = {
132
+ checkId: CheckId;
133
+ interactionType: AssessmentInteractionType;
134
+ score?: number;
135
+ maxScore?: number;
136
+ passingScore?: number;
137
+ };
90
138
  type InteractionData = {
91
139
  kind?: string;
92
140
  blockId?: BlockId;
@@ -121,6 +169,14 @@ type TelemetryEvent = (TelemetryEventBase & {
121
169
  name: "quiz_completed";
122
170
  lessonId: LessonId;
123
171
  data: QuizCompletedData;
172
+ }) | (TelemetryEventBase & {
173
+ name: "assessment_answered";
174
+ lessonId: LessonId;
175
+ data: AssessmentAnsweredData;
176
+ }) | (TelemetryEventBase & {
177
+ name: "assessment_completed";
178
+ lessonId: LessonId;
179
+ data: AssessmentCompletedData;
124
180
  }) | (TelemetryEventBase & {
125
181
  name: "interaction";
126
182
  lessonId?: LessonId;
@@ -152,6 +208,18 @@ type TelemetryCatalogEntry = {
152
208
  declare const TELEMETRY_EVENT_CATALOG: TelemetryCatalogEntry[];
153
209
  declare function buildTelemetryCatalog(): TelemetryCatalogEntry[];
154
210
 
211
+ declare const telemetryCatalogV2Version: 2;
212
+ type TelemetryCatalogV2Entry = {
213
+ name: Extract<TelemetryEventName, "assessment_answered" | "assessment_completed">;
214
+ description: string;
215
+ requiredFields: string[];
216
+ dataFields: string[];
217
+ xapiVerb: string;
218
+ urnPattern: string;
219
+ };
220
+ declare const TELEMETRY_EVENT_CATALOG_V2: TelemetryCatalogV2Entry[];
221
+ declare function buildTelemetryCatalogV2(): TelemetryCatalogV2Entry[];
222
+
155
223
  declare function createTrackingClient(opts?: {
156
224
  sink?: TelemetrySink;
157
225
  batch?: {
@@ -201,6 +269,14 @@ type BuildTelemetryEventInput = (BuildTelemetryEventContext & {
201
269
  name: "quiz_completed";
202
270
  lessonId?: LessonId;
203
271
  data: QuizCompletedData;
272
+ }) | (BuildTelemetryEventContext & {
273
+ name: "assessment_answered";
274
+ lessonId?: LessonId;
275
+ data: AssessmentAnsweredData;
276
+ }) | (BuildTelemetryEventContext & {
277
+ name: "assessment_completed";
278
+ lessonId?: LessonId;
279
+ data: AssessmentCompletedData;
204
280
  }) | (BuildTelemetryEventContext & {
205
281
  name: "interaction";
206
282
  lessonId?: LessonId;
@@ -429,4 +505,4 @@ declare function defineTelemetryPlugin(plugin: TelemetryPlugin): LessonkitPlugin
429
505
  declare function defineAssessmentPlugin(plugin: AssessmentPlugin): LessonkitPlugin;
430
506
  declare function defineLifecyclePlugin(plugin: LifecyclePlugin): LessonkitPlugin;
431
507
 
432
- 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 IdentityIdPath, 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 LessonkitUrn, type LessonkitUrnParts, type LifecyclePlugin, type PluginHost, type PluginIdentity, type PluginRegistry, type ProgressController, type ProgressState, type QuizAnsweredData, type QuizCompletedData, SESSION_STORAGE_KEY, type StoragePort, TELEMETRY_EVENT_CATALOG, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryDataFor, type TelemetryEmitFn, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, type TelemetryPlugin, type TelemetrySink, type TelemetryUser, type TimerPort, type TrackingClient, assertNever, 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, hasCourseStartedPipelineDelivered, markCourseStarted, markCourseStartedEmittedToTracking, markCourseStartedPipelineDelivered, migrateCourseStartedMark, nowIso, parseBlockId, parseCheckId, parseCourseId, parseLessonId, resetStoragePortForTests, resetTelemetryBuilderWarningsForTests, resolveSessionId, slugifyId, telemetryCatalogVersion, tryBuildTelemetryEvent, tryEmitCourseStarted, validateId };
508
+ export { type AssessmentAnsweredData, type AssessmentBaseProps, type AssessmentBehaviour, type AssessmentCompletedData, type AssessmentHandle, type AssessmentInteractionType, type AssessmentPlugin, type AssessmentScoreInput, type AssessmentScoreResult, type AssessmentXAPIData, 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 IdentityIdPath, 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 LessonkitUrn, type LessonkitUrnParts, type LifecyclePlugin, type PluginHost, type PluginIdentity, type PluginRegistry, type ProgressController, type ProgressState, type QuizAnsweredData, type QuizCompletedData, SESSION_STORAGE_KEY, type StoragePort, TELEMETRY_EVENT_CATALOG, TELEMETRY_EVENT_CATALOG_V2, type TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryCatalogV2Entry, type TelemetryDataFor, type TelemetryEmitFn, type TelemetryEvent, type TelemetryEventBase, type TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, type TelemetryPlugin, type TelemetrySink, type TelemetryUser, type TimerPort, type TrackingClient, assertNever, assertValidId, buildCourseStartedTelemetryEvent, buildLessonkitUrn, buildTelemetryCatalog, buildTelemetryCatalogV2, buildTelemetryEvent, completeCourseWithTelemetry, completeLessonWithTelemetry, createDefaultClock, createGlobalTimer, createLessonkitRuntime, createNoopStorage, createPluginRegistry, createProgressController, createSessionId, createSessionStoragePort, createTelemetryPipeline, createTrackingClient, createTrackingPipelineSink, defineAssessmentPlugin, defineLifecyclePlugin, defineTelemetryPlugin, deriveId, getTabSessionId, hasCourseStarted, hasCourseStartedEmittedToTracking, hasCourseStartedPipelineDelivered, markCourseStarted, markCourseStartedEmittedToTracking, markCourseStartedPipelineDelivered, migrateCourseStartedMark, nowIso, parseBlockId, parseCheckId, parseCourseId, parseLessonId, resetStoragePortForTests, resetTelemetryBuilderWarningsForTests, resolveSessionId, slugifyId, telemetryCatalogV2Version, telemetryCatalogVersion, tryBuildTelemetryEvent, tryEmitCourseStarted, validateId };
package/dist/index.js CHANGED
@@ -174,6 +174,30 @@ function buildTelemetryCatalog() {
174
174
  return TELEMETRY_EVENT_CATALOG.map((entry) => ({ ...entry }));
175
175
  }
176
176
 
177
+ // src/telemetryCatalogV2.ts
178
+ var telemetryCatalogV2Version = 2;
179
+ var TELEMETRY_EVENT_CATALOG_V2 = [
180
+ {
181
+ name: "assessment_answered",
182
+ description: "Learner submitted an assessment interaction answer",
183
+ requiredFields: ["courseId", "lessonId", "sessionId", "timestamp"],
184
+ dataFields: ["checkId", "interactionType", "question", "response", "correct"],
185
+ xapiVerb: "http://adlnet.gov/expapi/verbs/answered",
186
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:check:{checkId}"
187
+ },
188
+ {
189
+ name: "assessment_completed",
190
+ description: "Assessment interaction completed (passing criteria met)",
191
+ requiredFields: ["courseId", "lessonId", "sessionId", "timestamp"],
192
+ dataFields: ["checkId", "interactionType", "score", "maxScore", "passingScore"],
193
+ xapiVerb: "http://adlnet.gov/expapi/verbs/completed",
194
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:check:{checkId}"
195
+ }
196
+ ];
197
+ function buildTelemetryCatalogV2() {
198
+ return TELEMETRY_EVENT_CATALOG_V2.map((entry) => ({ ...entry }));
199
+ }
200
+
177
201
  // src/trackingClient.ts
178
202
  function isDevEnvironment() {
179
203
  const g = globalThis;
@@ -322,12 +346,14 @@ function nowIso() {
322
346
 
323
347
  // src/telemetryBuilder.ts
324
348
  var warnedMissingQuizLesson = false;
349
+ var warnedMissingAssessmentLesson = false;
325
350
  function isDevEnvironment2() {
326
351
  const g = globalThis;
327
352
  return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
328
353
  }
329
354
  function resetTelemetryBuilderWarningsForTests() {
330
355
  warnedMissingQuizLesson = false;
356
+ warnedMissingAssessmentLesson = false;
331
357
  }
332
358
  function resolveLessonId(opts, eventName) {
333
359
  const lessonId = opts.lessonId ?? opts.data?.lessonId;
@@ -376,6 +402,16 @@ function buildTelemetryEvent(opts) {
376
402
  if (!lessonId) throw new Error("quiz_completed requires active lessonId");
377
403
  return { name: "quiz_completed", ...base, lessonId, data: opts.data };
378
404
  }
405
+ case "assessment_answered": {
406
+ const lessonId = opts.lessonId;
407
+ if (!lessonId) throw new Error("assessment_answered requires active lessonId");
408
+ return { name: "assessment_answered", ...base, lessonId, data: opts.data };
409
+ }
410
+ case "assessment_completed": {
411
+ const lessonId = opts.lessonId;
412
+ if (!lessonId) throw new Error("assessment_completed requires active lessonId");
413
+ return { name: "assessment_completed", ...base, lessonId, data: opts.data };
414
+ }
379
415
  case "interaction":
380
416
  return {
381
417
  name: "interaction",
@@ -388,13 +424,21 @@ function buildTelemetryEvent(opts) {
388
424
  }
389
425
  }
390
426
  function tryBuildTelemetryEvent(opts) {
391
- const isQuiz = opts.name === "quiz_answered" || opts.name === "quiz_completed";
392
- if (isQuiz && !opts.lessonId) {
393
- if (isDevEnvironment2() && !warnedMissingQuizLesson) {
394
- warnedMissingQuizLesson = true;
395
- console.warn(
396
- `[lessonkit] ${opts.name} skipped: wrap <Quiz> in <Lesson> so an active lessonId is available`
397
- );
427
+ const needsLesson = opts.name === "quiz_answered" || opts.name === "quiz_completed" || opts.name === "assessment_answered" || opts.name === "assessment_completed";
428
+ if (needsLesson && !opts.lessonId) {
429
+ if (isDevEnvironment2()) {
430
+ if (opts.name.startsWith("quiz_") && !warnedMissingQuizLesson) {
431
+ warnedMissingQuizLesson = true;
432
+ console.warn(
433
+ `[lessonkit] ${opts.name} skipped: wrap <Quiz> in <Lesson> so an active lessonId is available`
434
+ );
435
+ }
436
+ if (opts.name.startsWith("assessment_") && !warnedMissingAssessmentLesson) {
437
+ warnedMissingAssessmentLesson = true;
438
+ console.warn(
439
+ `[lessonkit] ${opts.name} skipped: wrap assessment blocks in <Lesson> so an active lessonId is available`
440
+ );
441
+ }
398
442
  }
399
443
  return null;
400
444
  }
@@ -900,11 +944,13 @@ export {
900
944
  ID_PATTERN,
901
945
  SESSION_STORAGE_KEY,
902
946
  TELEMETRY_EVENT_CATALOG,
947
+ TELEMETRY_EVENT_CATALOG_V2,
903
948
  assertNever,
904
949
  assertValidId,
905
950
  buildCourseStartedTelemetryEvent,
906
951
  buildLessonkitUrn,
907
952
  buildTelemetryCatalog,
953
+ buildTelemetryCatalogV2,
908
954
  buildTelemetryEvent,
909
955
  completeCourseWithTelemetry,
910
956
  completeLessonWithTelemetry,
@@ -940,6 +986,7 @@ export {
940
986
  resetTelemetryBuilderWarningsForTests,
941
987
  resolveSessionId,
942
988
  slugifyId,
989
+ telemetryCatalogV2Version,
943
990
  telemetryCatalogVersion,
944
991
  tryBuildTelemetryEvent,
945
992
  tryEmitCourseStarted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/core",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "description": "Shared types and telemetry primitives for LessonKit.",
6
6
  "license": "Apache-2.0",