@lessonkit/core 1.0.1 → 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 +94 -24
- package/dist/index.d.cts +78 -2
- package/dist/index.d.ts +78 -2
- package/dist/index.js +91 -24
- package/package.json +3 -2
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;
|
|
@@ -279,6 +306,11 @@ function invokeTrackingSink(sink, event) {
|
|
|
279
306
|
function createTrackingClient(opts) {
|
|
280
307
|
const sink = opts?.sink;
|
|
281
308
|
const batchSink = opts?.batchSink;
|
|
309
|
+
if (batchSink != null && opts?.batch?.enabled === false) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
"[lessonkit] tracking.batchSink cannot be used with batch.enabled: false; omit batch.enabled or set it to true"
|
|
312
|
+
);
|
|
313
|
+
}
|
|
282
314
|
const batchEnabled = opts?.batch?.enabled ?? Boolean(batchSink);
|
|
283
315
|
const flushIntervalMs = opts?.batch?.flushIntervalMs ?? 5e3;
|
|
284
316
|
const maxBatchSize = opts?.batch?.maxBatchSize ?? 25;
|
|
@@ -350,7 +382,7 @@ function createTrackingClient(opts) {
|
|
|
350
382
|
if (disposed || disposing) return;
|
|
351
383
|
if (buffer.length >= maxBufferSize) {
|
|
352
384
|
buffer.shift();
|
|
353
|
-
if (!warnedBufferCap &&
|
|
385
|
+
if (!warnedBufferCap && isDevEnvironment()) {
|
|
354
386
|
warnedBufferCap = true;
|
|
355
387
|
console.warn(
|
|
356
388
|
`[lessonkit] telemetry batch buffer capped at ${maxBufferSize} events; oldest events are dropped while the sink is unavailable.`
|
|
@@ -390,12 +422,14 @@ function nowIso() {
|
|
|
390
422
|
|
|
391
423
|
// src/telemetryBuilder.ts
|
|
392
424
|
var warnedMissingQuizLesson = false;
|
|
425
|
+
var warnedMissingAssessmentLesson = false;
|
|
393
426
|
function isDevEnvironment2() {
|
|
394
427
|
const g = globalThis;
|
|
395
428
|
return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
|
|
396
429
|
}
|
|
397
430
|
function resetTelemetryBuilderWarningsForTests() {
|
|
398
431
|
warnedMissingQuizLesson = false;
|
|
432
|
+
warnedMissingAssessmentLesson = false;
|
|
399
433
|
}
|
|
400
434
|
function resolveLessonId(opts, eventName) {
|
|
401
435
|
const lessonId = opts.lessonId ?? opts.data?.lessonId;
|
|
@@ -444,6 +478,16 @@ function buildTelemetryEvent(opts) {
|
|
|
444
478
|
if (!lessonId) throw new Error("quiz_completed requires active lessonId");
|
|
445
479
|
return { name: "quiz_completed", ...base, lessonId, data: opts.data };
|
|
446
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
|
+
}
|
|
447
491
|
case "interaction":
|
|
448
492
|
return {
|
|
449
493
|
name: "interaction",
|
|
@@ -456,13 +500,21 @@ function buildTelemetryEvent(opts) {
|
|
|
456
500
|
}
|
|
457
501
|
}
|
|
458
502
|
function tryBuildTelemetryEvent(opts) {
|
|
459
|
-
const
|
|
460
|
-
if (
|
|
461
|
-
if (isDevEnvironment2()
|
|
462
|
-
warnedMissingQuizLesson
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
+
}
|
|
466
518
|
}
|
|
467
519
|
return null;
|
|
468
520
|
}
|
|
@@ -539,7 +591,8 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
539
591
|
const warnPersistFailure = () => {
|
|
540
592
|
if (warnedPersistFailure) return;
|
|
541
593
|
warnedPersistFailure = true;
|
|
542
|
-
|
|
594
|
+
const g = globalThis;
|
|
595
|
+
if (typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "development") {
|
|
543
596
|
console.warn(
|
|
544
597
|
"[lessonkit] sessionStorage is unavailable or failed; using in-memory session dedupe for this tab (may reset on full reload)."
|
|
545
598
|
);
|
|
@@ -580,23 +633,37 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
580
633
|
function resetStoragePortForTests(storage) {
|
|
581
634
|
storage.resetForTests?.();
|
|
582
635
|
}
|
|
636
|
+
function createInMemorySessionStoragePort() {
|
|
637
|
+
const memory = /* @__PURE__ */ new Map();
|
|
638
|
+
return {
|
|
639
|
+
getItem: (key) => memory.get(key) ?? null,
|
|
640
|
+
setItem: (key, value) => {
|
|
641
|
+
memory.set(key, value);
|
|
642
|
+
},
|
|
643
|
+
removeItem: (key) => {
|
|
644
|
+
memory.delete(key);
|
|
645
|
+
},
|
|
646
|
+
resetForTests: () => {
|
|
647
|
+
memory.clear();
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function resolveBrowserSessionStorage() {
|
|
652
|
+
try {
|
|
653
|
+
if (typeof sessionStorage === "undefined" || sessionStorage == null) {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
return sessionStorage;
|
|
657
|
+
} catch {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
583
661
|
function createSessionStoragePort() {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
return
|
|
587
|
-
getItem: (key) => memory.get(key) ?? null,
|
|
588
|
-
setItem: (key, value) => {
|
|
589
|
-
memory.set(key, value);
|
|
590
|
-
},
|
|
591
|
-
removeItem: (key) => {
|
|
592
|
-
memory.delete(key);
|
|
593
|
-
},
|
|
594
|
-
resetForTests: () => {
|
|
595
|
-
memory.clear();
|
|
596
|
-
}
|
|
597
|
-
};
|
|
662
|
+
const session = resolveBrowserSessionStorage();
|
|
663
|
+
if (!session) {
|
|
664
|
+
return createInMemorySessionStoragePort();
|
|
598
665
|
}
|
|
599
|
-
return createMemoryBackedSessionStorage(
|
|
666
|
+
return createMemoryBackedSessionStorage(session);
|
|
600
667
|
}
|
|
601
668
|
function createGlobalTimer() {
|
|
602
669
|
return {
|
|
@@ -954,11 +1021,13 @@ function defineLifecyclePlugin(plugin) {
|
|
|
954
1021
|
ID_PATTERN,
|
|
955
1022
|
SESSION_STORAGE_KEY,
|
|
956
1023
|
TELEMETRY_EVENT_CATALOG,
|
|
1024
|
+
TELEMETRY_EVENT_CATALOG_V2,
|
|
957
1025
|
assertNever,
|
|
958
1026
|
assertValidId,
|
|
959
1027
|
buildCourseStartedTelemetryEvent,
|
|
960
1028
|
buildLessonkitUrn,
|
|
961
1029
|
buildTelemetryCatalog,
|
|
1030
|
+
buildTelemetryCatalogV2,
|
|
962
1031
|
buildTelemetryEvent,
|
|
963
1032
|
completeCourseWithTelemetry,
|
|
964
1033
|
completeLessonWithTelemetry,
|
|
@@ -994,6 +1063,7 @@ function defineLifecyclePlugin(plugin) {
|
|
|
994
1063
|
resetTelemetryBuilderWarningsForTests,
|
|
995
1064
|
resolveSessionId,
|
|
996
1065
|
slugifyId,
|
|
1066
|
+
telemetryCatalogV2Version,
|
|
997
1067
|
telemetryCatalogVersion,
|
|
998
1068
|
tryBuildTelemetryEvent,
|
|
999
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;
|
|
@@ -206,6 +230,11 @@ function invokeTrackingSink(sink, event) {
|
|
|
206
230
|
function createTrackingClient(opts) {
|
|
207
231
|
const sink = opts?.sink;
|
|
208
232
|
const batchSink = opts?.batchSink;
|
|
233
|
+
if (batchSink != null && opts?.batch?.enabled === false) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
"[lessonkit] tracking.batchSink cannot be used with batch.enabled: false; omit batch.enabled or set it to true"
|
|
236
|
+
);
|
|
237
|
+
}
|
|
209
238
|
const batchEnabled = opts?.batch?.enabled ?? Boolean(batchSink);
|
|
210
239
|
const flushIntervalMs = opts?.batch?.flushIntervalMs ?? 5e3;
|
|
211
240
|
const maxBatchSize = opts?.batch?.maxBatchSize ?? 25;
|
|
@@ -277,7 +306,7 @@ function createTrackingClient(opts) {
|
|
|
277
306
|
if (disposed || disposing) return;
|
|
278
307
|
if (buffer.length >= maxBufferSize) {
|
|
279
308
|
buffer.shift();
|
|
280
|
-
if (!warnedBufferCap &&
|
|
309
|
+
if (!warnedBufferCap && isDevEnvironment()) {
|
|
281
310
|
warnedBufferCap = true;
|
|
282
311
|
console.warn(
|
|
283
312
|
`[lessonkit] telemetry batch buffer capped at ${maxBufferSize} events; oldest events are dropped while the sink is unavailable.`
|
|
@@ -317,12 +346,14 @@ function nowIso() {
|
|
|
317
346
|
|
|
318
347
|
// src/telemetryBuilder.ts
|
|
319
348
|
var warnedMissingQuizLesson = false;
|
|
349
|
+
var warnedMissingAssessmentLesson = false;
|
|
320
350
|
function isDevEnvironment2() {
|
|
321
351
|
const g = globalThis;
|
|
322
352
|
return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
|
|
323
353
|
}
|
|
324
354
|
function resetTelemetryBuilderWarningsForTests() {
|
|
325
355
|
warnedMissingQuizLesson = false;
|
|
356
|
+
warnedMissingAssessmentLesson = false;
|
|
326
357
|
}
|
|
327
358
|
function resolveLessonId(opts, eventName) {
|
|
328
359
|
const lessonId = opts.lessonId ?? opts.data?.lessonId;
|
|
@@ -371,6 +402,16 @@ function buildTelemetryEvent(opts) {
|
|
|
371
402
|
if (!lessonId) throw new Error("quiz_completed requires active lessonId");
|
|
372
403
|
return { name: "quiz_completed", ...base, lessonId, data: opts.data };
|
|
373
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
|
+
}
|
|
374
415
|
case "interaction":
|
|
375
416
|
return {
|
|
376
417
|
name: "interaction",
|
|
@@ -383,13 +424,21 @@ function buildTelemetryEvent(opts) {
|
|
|
383
424
|
}
|
|
384
425
|
}
|
|
385
426
|
function tryBuildTelemetryEvent(opts) {
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
if (isDevEnvironment2()
|
|
389
|
-
warnedMissingQuizLesson
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
+
}
|
|
393
442
|
}
|
|
394
443
|
return null;
|
|
395
444
|
}
|
|
@@ -466,7 +515,8 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
466
515
|
const warnPersistFailure = () => {
|
|
467
516
|
if (warnedPersistFailure) return;
|
|
468
517
|
warnedPersistFailure = true;
|
|
469
|
-
|
|
518
|
+
const g = globalThis;
|
|
519
|
+
if (typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "development") {
|
|
470
520
|
console.warn(
|
|
471
521
|
"[lessonkit] sessionStorage is unavailable or failed; using in-memory session dedupe for this tab (may reset on full reload)."
|
|
472
522
|
);
|
|
@@ -507,23 +557,37 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
507
557
|
function resetStoragePortForTests(storage) {
|
|
508
558
|
storage.resetForTests?.();
|
|
509
559
|
}
|
|
560
|
+
function createInMemorySessionStoragePort() {
|
|
561
|
+
const memory = /* @__PURE__ */ new Map();
|
|
562
|
+
return {
|
|
563
|
+
getItem: (key) => memory.get(key) ?? null,
|
|
564
|
+
setItem: (key, value) => {
|
|
565
|
+
memory.set(key, value);
|
|
566
|
+
},
|
|
567
|
+
removeItem: (key) => {
|
|
568
|
+
memory.delete(key);
|
|
569
|
+
},
|
|
570
|
+
resetForTests: () => {
|
|
571
|
+
memory.clear();
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function resolveBrowserSessionStorage() {
|
|
576
|
+
try {
|
|
577
|
+
if (typeof sessionStorage === "undefined" || sessionStorage == null) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
return sessionStorage;
|
|
581
|
+
} catch {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
510
585
|
function createSessionStoragePort() {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
return
|
|
514
|
-
getItem: (key) => memory.get(key) ?? null,
|
|
515
|
-
setItem: (key, value) => {
|
|
516
|
-
memory.set(key, value);
|
|
517
|
-
},
|
|
518
|
-
removeItem: (key) => {
|
|
519
|
-
memory.delete(key);
|
|
520
|
-
},
|
|
521
|
-
resetForTests: () => {
|
|
522
|
-
memory.clear();
|
|
523
|
-
}
|
|
524
|
-
};
|
|
586
|
+
const session = resolveBrowserSessionStorage();
|
|
587
|
+
if (!session) {
|
|
588
|
+
return createInMemorySessionStoragePort();
|
|
525
589
|
}
|
|
526
|
-
return createMemoryBackedSessionStorage(
|
|
590
|
+
return createMemoryBackedSessionStorage(session);
|
|
527
591
|
}
|
|
528
592
|
function createGlobalTimer() {
|
|
529
593
|
return {
|
|
@@ -880,11 +944,13 @@ export {
|
|
|
880
944
|
ID_PATTERN,
|
|
881
945
|
SESSION_STORAGE_KEY,
|
|
882
946
|
TELEMETRY_EVENT_CATALOG,
|
|
947
|
+
TELEMETRY_EVENT_CATALOG_V2,
|
|
883
948
|
assertNever,
|
|
884
949
|
assertValidId,
|
|
885
950
|
buildCourseStartedTelemetryEvent,
|
|
886
951
|
buildLessonkitUrn,
|
|
887
952
|
buildTelemetryCatalog,
|
|
953
|
+
buildTelemetryCatalogV2,
|
|
888
954
|
buildTelemetryEvent,
|
|
889
955
|
completeCourseWithTelemetry,
|
|
890
956
|
completeLessonWithTelemetry,
|
|
@@ -920,6 +986,7 @@ export {
|
|
|
920
986
|
resetTelemetryBuilderWarningsForTests,
|
|
921
987
|
resolveSessionId,
|
|
922
988
|
slugifyId,
|
|
989
|
+
telemetryCatalogV2Version,
|
|
923
990
|
telemetryCatalogVersion,
|
|
924
991
|
tryBuildTelemetryEvent,
|
|
925
992
|
tryEmitCourseStarted,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessonkit/core",
|
|
3
|
-
"version": "1.0
|
|
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",
|
|
@@ -48,8 +48,9 @@
|
|
|
48
48
|
"lint": "echo \"(no lint configured yet)\""
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.13.10",
|
|
51
52
|
"tsup": "^8.5.0",
|
|
52
53
|
"typescript": "^5.8.3",
|
|
53
|
-
"vitest": "^
|
|
54
|
+
"vitest": "^4.1.8"
|
|
54
55
|
}
|
|
55
56
|
}
|