@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 +57 -7
- package/dist/index.d.cts +78 -2
- package/dist/index.d.ts +78 -2
- package/dist/index.js +54 -7
- package/package.json +1 -1
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
|
|
465
|
-
if (
|
|
466
|
-
if (isDevEnvironment2()
|
|
467
|
-
warnedMissingQuizLesson
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
392
|
-
if (
|
|
393
|
-
if (isDevEnvironment2()
|
|
394
|
-
warnedMissingQuizLesson
|
|
395
|
-
|
|
396
|
-
|
|
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,
|