@lessonkit/core 1.5.0 → 1.6.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/{chunk-KFXFQ6B2.js → chunk-NGCHHJSM.js} +134 -12
- package/dist/index.cjs +353 -33
- package/dist/index.d.cts +94 -11
- package/dist/index.d.ts +94 -11
- package/dist/index.js +218 -22
- package/dist/{testing-BFr8oEfw.d.cts → testing-CzgxF1Ru.d.cts} +148 -2
- package/dist/{testing-BFr8oEfw.d.ts → testing-CzgxF1Ru.d.ts} +148 -2
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +1 -1
- package/telemetry-catalog.v3.json +140 -0
|
@@ -60,7 +60,8 @@ function randomSessionIdFallback() {
|
|
|
60
60
|
if (g.crypto?.getRandomValues) {
|
|
61
61
|
const bytes = new Uint8Array(16);
|
|
62
62
|
g.crypto.getRandomValues(bytes);
|
|
63
|
-
|
|
63
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
64
|
+
return `s-${hex}`;
|
|
64
65
|
}
|
|
65
66
|
throw new Error(
|
|
66
67
|
"[lessonkit] createSessionId requires crypto.randomUUID or crypto.getRandomValues"
|
|
@@ -68,7 +69,9 @@ function randomSessionIdFallback() {
|
|
|
68
69
|
}
|
|
69
70
|
function createSessionId() {
|
|
70
71
|
const g = globalThis;
|
|
71
|
-
if (g.crypto?.randomUUID)
|
|
72
|
+
if (g.crypto?.randomUUID) {
|
|
73
|
+
return `s-${g.crypto.randomUUID().replace(/-/g, "")}`;
|
|
74
|
+
}
|
|
72
75
|
return randomSessionIdFallback();
|
|
73
76
|
}
|
|
74
77
|
|
|
@@ -386,6 +389,90 @@ var TELEMETRY_EVENT_REGISTRY = {
|
|
|
386
389
|
data: opts.data
|
|
387
390
|
};
|
|
388
391
|
}
|
|
392
|
+
},
|
|
393
|
+
image_juxtaposition_changed: {
|
|
394
|
+
build: (opts, base) => ({
|
|
395
|
+
name: "image_juxtaposition_changed",
|
|
396
|
+
...base,
|
|
397
|
+
lessonId: opts.lessonId,
|
|
398
|
+
data: opts.data
|
|
399
|
+
})
|
|
400
|
+
},
|
|
401
|
+
timeline_event_viewed: {
|
|
402
|
+
build: (opts, base) => ({
|
|
403
|
+
name: "timeline_event_viewed",
|
|
404
|
+
...base,
|
|
405
|
+
lessonId: opts.lessonId,
|
|
406
|
+
data: opts.data
|
|
407
|
+
})
|
|
408
|
+
},
|
|
409
|
+
image_sequence_changed: {
|
|
410
|
+
build: (opts, base) => ({
|
|
411
|
+
name: "image_sequence_changed",
|
|
412
|
+
...base,
|
|
413
|
+
lessonId: opts.lessonId,
|
|
414
|
+
data: opts.data
|
|
415
|
+
})
|
|
416
|
+
},
|
|
417
|
+
audio_recording_started: {
|
|
418
|
+
build: (opts, base) => ({
|
|
419
|
+
name: "audio_recording_started",
|
|
420
|
+
...base,
|
|
421
|
+
lessonId: opts.lessonId,
|
|
422
|
+
data: opts.data
|
|
423
|
+
})
|
|
424
|
+
},
|
|
425
|
+
audio_recording_completed: {
|
|
426
|
+
build: (opts, base) => ({
|
|
427
|
+
name: "audio_recording_completed",
|
|
428
|
+
...base,
|
|
429
|
+
lessonId: opts.lessonId,
|
|
430
|
+
data: opts.data
|
|
431
|
+
})
|
|
432
|
+
},
|
|
433
|
+
qr_content_revealed: {
|
|
434
|
+
build: (opts, base) => ({
|
|
435
|
+
name: "qr_content_revealed",
|
|
436
|
+
...base,
|
|
437
|
+
lessonId: opts.lessonId,
|
|
438
|
+
data: opts.data
|
|
439
|
+
})
|
|
440
|
+
},
|
|
441
|
+
advent_door_opened: {
|
|
442
|
+
build: (opts, base) => ({
|
|
443
|
+
name: "advent_door_opened",
|
|
444
|
+
...base,
|
|
445
|
+
lessonId: opts.lessonId,
|
|
446
|
+
data: opts.data
|
|
447
|
+
})
|
|
448
|
+
},
|
|
449
|
+
map_stage_viewed: {
|
|
450
|
+
requiresLessonId: true,
|
|
451
|
+
build: (opts, base) => {
|
|
452
|
+
if (opts.name !== "map_stage_viewed") throw new Error("unexpected event");
|
|
453
|
+
const lessonId = opts.lessonId;
|
|
454
|
+
if (!lessonId) throw new Error("map_stage_viewed requires active lessonId");
|
|
455
|
+
return {
|
|
456
|
+
name: "map_stage_viewed",
|
|
457
|
+
...base,
|
|
458
|
+
lessonId,
|
|
459
|
+
data: opts.data
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
map_exit_selected: {
|
|
464
|
+
requiresLessonId: true,
|
|
465
|
+
build: (opts, base) => {
|
|
466
|
+
if (opts.name !== "map_exit_selected") throw new Error("unexpected event");
|
|
467
|
+
const lessonId = opts.lessonId;
|
|
468
|
+
if (!lessonId) throw new Error("map_exit_selected requires active lessonId");
|
|
469
|
+
return {
|
|
470
|
+
name: "map_exit_selected",
|
|
471
|
+
...base,
|
|
472
|
+
lessonId,
|
|
473
|
+
data: opts.data
|
|
474
|
+
};
|
|
475
|
+
}
|
|
389
476
|
}
|
|
390
477
|
};
|
|
391
478
|
function buildTelemetryEventFromRegistry(opts) {
|
|
@@ -494,13 +581,15 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
494
581
|
);
|
|
495
582
|
}
|
|
496
583
|
};
|
|
584
|
+
const bypassCacheForKey = (key) => key === "lessonkit:sessionId" || key.startsWith("lessonkit:course_started");
|
|
497
585
|
return {
|
|
498
586
|
getItem: (key) => {
|
|
499
587
|
if (tombstones.has(key)) return null;
|
|
500
|
-
if (memory.has(key)) return memory.get(key);
|
|
588
|
+
if (!bypassCacheForKey(key) && memory.has(key)) return memory.get(key);
|
|
501
589
|
try {
|
|
502
590
|
const value = session.getItem(key);
|
|
503
591
|
if (value !== null) memory.set(key, value);
|
|
592
|
+
else if (bypassCacheForKey(key)) memory.delete(key);
|
|
504
593
|
return value;
|
|
505
594
|
} catch {
|
|
506
595
|
return memory.get(key) ?? null;
|
|
@@ -508,12 +597,15 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
508
597
|
},
|
|
509
598
|
setItem: (key, value) => {
|
|
510
599
|
tombstones.delete(key);
|
|
511
|
-
memory.set(key, value);
|
|
512
600
|
try {
|
|
513
601
|
session.setItem(key, value);
|
|
602
|
+
memory.set(key, value);
|
|
514
603
|
return true;
|
|
515
604
|
} catch {
|
|
516
605
|
warnPersistFailure();
|
|
606
|
+
if (!bypassCacheForKey(key)) {
|
|
607
|
+
memory.set(key, value);
|
|
608
|
+
}
|
|
517
609
|
return false;
|
|
518
610
|
}
|
|
519
611
|
},
|
|
@@ -608,7 +700,17 @@ function resolveSessionId(storage, provided) {
|
|
|
608
700
|
}
|
|
609
701
|
}
|
|
610
702
|
const existing = storage.getItem(SESSION_STORAGE_KEY);
|
|
611
|
-
if (existing)
|
|
703
|
+
if (existing) {
|
|
704
|
+
const trimmedExisting = existing.trim();
|
|
705
|
+
const validatedExisting = validateId(trimmedExisting);
|
|
706
|
+
if (validatedExisting.ok) return validatedExisting.id;
|
|
707
|
+
storage.removeItem?.(SESSION_STORAGE_KEY);
|
|
708
|
+
if (isDevEnvironment2()) {
|
|
709
|
+
console.warn(
|
|
710
|
+
`[lessonkit] Invalid stored sessionId "${existing}"; generating a new id.`
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
612
714
|
const volatile = volatileSessionIds.get(storage);
|
|
613
715
|
if (volatile) return volatile;
|
|
614
716
|
const id = createSessionId();
|
|
@@ -713,7 +815,17 @@ function tryEmitCourseStarted(ctx, deps, alreadyEmittedToSink) {
|
|
|
713
815
|
const flightKey = `${ctx.sessionId}:${ctx.courseId}`;
|
|
714
816
|
const marked = hasCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId);
|
|
715
817
|
if (alreadyEmittedToSink) {
|
|
716
|
-
|
|
818
|
+
const markPersisted = marked ? true : markCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId);
|
|
819
|
+
return Promise.resolve({
|
|
820
|
+
emitted: true,
|
|
821
|
+
marked: markPersisted
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
if (marked && hasCourseStartedEmittedToTracking(ctx.storage, ctx.sessionId, ctx.courseId)) {
|
|
825
|
+
return Promise.resolve({
|
|
826
|
+
emitted: true,
|
|
827
|
+
marked: true
|
|
828
|
+
});
|
|
717
829
|
}
|
|
718
830
|
const existing = courseStartedEmitFlights.get(flightKey);
|
|
719
831
|
if (existing) {
|
|
@@ -722,15 +834,16 @@ function tryEmitCourseStarted(ctx, deps, alreadyEmittedToSink) {
|
|
|
722
834
|
const flight = Promise.resolve().then(() => {
|
|
723
835
|
try {
|
|
724
836
|
const emitted = deps.emitCourseStartedEvent(ctx);
|
|
725
|
-
|
|
726
|
-
markCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId);
|
|
727
|
-
}
|
|
837
|
+
const markPersisted = emitted && !marked ? markCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId) : marked;
|
|
728
838
|
return {
|
|
729
839
|
emitted,
|
|
730
|
-
marked:
|
|
840
|
+
marked: markPersisted
|
|
731
841
|
};
|
|
732
842
|
} catch {
|
|
733
|
-
return {
|
|
843
|
+
return {
|
|
844
|
+
emitted: false,
|
|
845
|
+
marked: hasCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId)
|
|
846
|
+
};
|
|
734
847
|
} finally {
|
|
735
848
|
if (courseStartedEmitFlights.get(flightKey) === flight) {
|
|
736
849
|
courseStartedEmitFlights.delete(flightKey);
|
|
@@ -766,7 +879,16 @@ function completeCourseWithTelemetry(opts) {
|
|
|
766
879
|
});
|
|
767
880
|
}
|
|
768
881
|
const result = opts.progress.completeCourse();
|
|
769
|
-
if (!result.didComplete)
|
|
882
|
+
if (!result.didComplete) {
|
|
883
|
+
const after = opts.progress.getState();
|
|
884
|
+
if (after.activeLessonId) {
|
|
885
|
+
const lessonResult = opts.progress.completeLesson(after.activeLessonId, opts.nowMs);
|
|
886
|
+
if (lessonResult.didComplete) {
|
|
887
|
+
opts.emitLessonCompleted(after.activeLessonId, lessonResult.durationMs);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
770
892
|
opts.emitCourseCompleted();
|
|
771
893
|
return true;
|
|
772
894
|
}
|