@lessonkit/core 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-NGCHHJSM.js → chunk-DTJGIMUU.js} +96 -37
- package/dist/index.cjs +197 -45
- package/dist/index.d.cts +43 -10
- package/dist/index.d.ts +43 -10
- package/dist/index.js +95 -9
- package/dist/{testing-CzgxF1Ru.d.cts → testing-CQ-ZsT7D.d.cts} +38 -4
- package/dist/{testing-CzgxF1Ru.d.ts → testing-CQ-ZsT7D.d.ts} +38 -4
- package/dist/testing.cjs +2 -0
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ Machine-readable: `@lessonkit/core/telemetry-catalog.v3.json` (current; v1–v3
|
|
|
63
63
|
|
|
64
64
|
## Docs
|
|
65
65
|
|
|
66
|
-
[
|
|
66
|
+
[5-minute guide](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/getting-started-in-5-minutes.html) · [LMS Go-Live](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/lms-go-live.html) · [Core reference](https://lessonkit.readthedocs.io/en/latest/reference/core.html) · [TypeDoc API index](https://lessonkit.readthedocs.io/en/latest/reference/api.html)
|
|
67
67
|
|
|
68
68
|
## License
|
|
69
69
|
|
|
@@ -588,9 +588,15 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
588
588
|
if (!bypassCacheForKey(key) && memory.has(key)) return memory.get(key);
|
|
589
589
|
try {
|
|
590
590
|
const value = session.getItem(key);
|
|
591
|
-
if (value !== null)
|
|
592
|
-
|
|
593
|
-
|
|
591
|
+
if (value !== null) {
|
|
592
|
+
memory.set(key, value);
|
|
593
|
+
return value;
|
|
594
|
+
}
|
|
595
|
+
if (bypassCacheForKey(key) && memory.has(key)) {
|
|
596
|
+
return memory.get(key);
|
|
597
|
+
}
|
|
598
|
+
if (bypassCacheForKey(key)) memory.delete(key);
|
|
599
|
+
return null;
|
|
594
600
|
} catch {
|
|
595
601
|
return memory.get(key) ?? null;
|
|
596
602
|
}
|
|
@@ -603,9 +609,7 @@ function createMemoryBackedSessionStorage(session) {
|
|
|
603
609
|
return true;
|
|
604
610
|
} catch {
|
|
605
611
|
warnPersistFailure();
|
|
606
|
-
|
|
607
|
-
memory.set(key, value);
|
|
608
|
-
}
|
|
612
|
+
memory.set(key, value);
|
|
609
613
|
return false;
|
|
610
614
|
}
|
|
611
615
|
},
|
|
@@ -671,6 +675,29 @@ function createGlobalTimer() {
|
|
|
671
675
|
// src/session.ts
|
|
672
676
|
var SESSION_STORAGE_KEY = "lessonkit:sessionId";
|
|
673
677
|
var volatileSessionIds = /* @__PURE__ */ new WeakMap();
|
|
678
|
+
var volatileStorageMarks = /* @__PURE__ */ new WeakMap();
|
|
679
|
+
function rememberVolatileMark(storage, key) {
|
|
680
|
+
let keys = volatileStorageMarks.get(storage);
|
|
681
|
+
if (!keys) {
|
|
682
|
+
keys = /* @__PURE__ */ new Set();
|
|
683
|
+
volatileStorageMarks.set(storage, keys);
|
|
684
|
+
}
|
|
685
|
+
keys.add(key);
|
|
686
|
+
}
|
|
687
|
+
function hasVolatileMark(storage, key) {
|
|
688
|
+
return volatileStorageMarks.get(storage)?.has(key) ?? false;
|
|
689
|
+
}
|
|
690
|
+
function clearVolatileMark(storage, key) {
|
|
691
|
+
volatileStorageMarks.get(storage)?.delete(key);
|
|
692
|
+
}
|
|
693
|
+
function storageHasMark(storage, key) {
|
|
694
|
+
return storage.getItem(key) === "1" || hasVolatileMark(storage, key);
|
|
695
|
+
}
|
|
696
|
+
function storageSetMark(storage, key) {
|
|
697
|
+
const persisted = storage.setItem(key, "1");
|
|
698
|
+
if (!persisted) rememberVolatileMark(storage, key);
|
|
699
|
+
return persisted;
|
|
700
|
+
}
|
|
674
701
|
function isDevEnvironment2() {
|
|
675
702
|
const g = globalThis;
|
|
676
703
|
return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
|
|
@@ -686,19 +713,23 @@ function sessionKeySegment(sessionId) {
|
|
|
686
713
|
const validated = validateId(sessionId);
|
|
687
714
|
return validated.ok ? validated.id : encodeURIComponent(sessionId);
|
|
688
715
|
}
|
|
689
|
-
function
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
716
|
+
function resolveGeneratedSessionId(storage) {
|
|
717
|
+
const volatile = volatileSessionIds.get(storage);
|
|
718
|
+
if (volatile) return volatile;
|
|
719
|
+
const id = createSessionId();
|
|
720
|
+
const persisted = storage.setItem(SESSION_STORAGE_KEY, id);
|
|
721
|
+
if (!persisted) {
|
|
722
|
+
volatileSessionIds.set(storage, id);
|
|
723
|
+
if (isDevEnvironment2()) {
|
|
724
|
+
console.warn(
|
|
725
|
+
"[lessonkit] session id could not be persisted; using in-memory id for this storage."
|
|
726
|
+
);
|
|
700
727
|
}
|
|
728
|
+
return id;
|
|
701
729
|
}
|
|
730
|
+
return id;
|
|
731
|
+
}
|
|
732
|
+
function resolveFallbackSessionId(storage, options) {
|
|
702
733
|
const existing = storage.getItem(SESSION_STORAGE_KEY);
|
|
703
734
|
if (existing) {
|
|
704
735
|
const trimmedExisting = existing.trim();
|
|
@@ -710,21 +741,37 @@ function resolveSessionId(storage, provided) {
|
|
|
710
741
|
`[lessonkit] Invalid stored sessionId "${existing}"; generating a new id.`
|
|
711
742
|
);
|
|
712
743
|
}
|
|
744
|
+
const fallback = resolveGeneratedSessionId(storage);
|
|
745
|
+
options?.onInvalidSessionId?.({
|
|
746
|
+
invalidId: existing,
|
|
747
|
+
fallbackId: fallback,
|
|
748
|
+
source: "stored"
|
|
749
|
+
});
|
|
750
|
+
return fallback;
|
|
713
751
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
752
|
+
return resolveGeneratedSessionId(storage);
|
|
753
|
+
}
|
|
754
|
+
function resolveSessionId(storage, provided, options) {
|
|
755
|
+
if (provided !== void 0) {
|
|
756
|
+
const trimmed = provided.trim();
|
|
757
|
+
if (trimmed.length > 0) {
|
|
758
|
+
const validated = validateId(trimmed);
|
|
759
|
+
if (validated.ok) return validated.id;
|
|
760
|
+
if (isDevEnvironment2()) {
|
|
761
|
+
console.warn(
|
|
762
|
+
`[lessonkit] Invalid sessionId "${trimmed}"; falling back to tab or generated id.`
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
const fallback = resolveFallbackSessionId(storage, options);
|
|
766
|
+
options?.onInvalidSessionId?.({
|
|
767
|
+
invalidId: trimmed,
|
|
768
|
+
fallbackId: fallback,
|
|
769
|
+
source: "provided"
|
|
770
|
+
});
|
|
771
|
+
return fallback;
|
|
724
772
|
}
|
|
725
|
-
return id;
|
|
726
773
|
}
|
|
727
|
-
return
|
|
774
|
+
return resolveFallbackSessionId(storage, options);
|
|
728
775
|
}
|
|
729
776
|
function courseStartedStorageKey(sessionId, courseId) {
|
|
730
777
|
return `${COURSE_STARTED_PREFIX}${sessionKeySegment(sessionId)}:${courseId ?? ""}`;
|
|
@@ -740,35 +787,35 @@ function courseStartedXapiStorageKey(sessionId, courseId) {
|
|
|
740
787
|
}
|
|
741
788
|
function hasCourseStarted(storage, sessionId, courseId) {
|
|
742
789
|
if (!courseId) return false;
|
|
743
|
-
return storage
|
|
790
|
+
return storageHasMark(storage, courseStartedStorageKey(sessionId, courseId));
|
|
744
791
|
}
|
|
745
792
|
function markCourseStarted(storage, sessionId, courseId) {
|
|
746
793
|
if (!courseId) return false;
|
|
747
|
-
return storage
|
|
794
|
+
return storageSetMark(storage, courseStartedStorageKey(sessionId, courseId));
|
|
748
795
|
}
|
|
749
796
|
function hasCourseStartedEmittedToTracking(storage, sessionId, courseId) {
|
|
750
797
|
if (!courseId) return false;
|
|
751
|
-
return storage
|
|
798
|
+
return storageHasMark(storage, courseStartedTrackingStorageKey(sessionId, courseId));
|
|
752
799
|
}
|
|
753
800
|
function markCourseStartedEmittedToTracking(storage, sessionId, courseId) {
|
|
754
801
|
if (!courseId) return false;
|
|
755
|
-
return storage
|
|
802
|
+
return storageSetMark(storage, courseStartedTrackingStorageKey(sessionId, courseId));
|
|
756
803
|
}
|
|
757
804
|
function hasCourseStartedPipelineDelivered(storage, sessionId, courseId) {
|
|
758
805
|
if (!courseId) return false;
|
|
759
|
-
return storage
|
|
806
|
+
return storageHasMark(storage, courseStartedPipelineStorageKey(sessionId, courseId));
|
|
760
807
|
}
|
|
761
808
|
function markCourseStartedPipelineDelivered(storage, sessionId, courseId) {
|
|
762
809
|
if (!courseId) return false;
|
|
763
|
-
return storage
|
|
810
|
+
return storageSetMark(storage, courseStartedPipelineStorageKey(sessionId, courseId));
|
|
764
811
|
}
|
|
765
812
|
function hasCourseStartedXapiSent(storage, sessionId, courseId) {
|
|
766
813
|
if (!courseId) return false;
|
|
767
|
-
return storage
|
|
814
|
+
return storageHasMark(storage, courseStartedXapiStorageKey(sessionId, courseId));
|
|
768
815
|
}
|
|
769
816
|
function markCourseStartedXapiSent(storage, sessionId, courseId) {
|
|
770
817
|
if (!courseId) return false;
|
|
771
|
-
return storage
|
|
818
|
+
return storageSetMark(storage, courseStartedXapiStorageKey(sessionId, courseId));
|
|
772
819
|
}
|
|
773
820
|
function resetSharedVolatileSessionIdForTests() {
|
|
774
821
|
}
|
|
@@ -776,6 +823,7 @@ function migrateStorageMark(storage, fromKey, toKey, hasMark) {
|
|
|
776
823
|
if (!hasMark) return;
|
|
777
824
|
if (storage.setItem(toKey, "1")) {
|
|
778
825
|
storage.removeItem?.(fromKey);
|
|
826
|
+
clearVolatileMark(storage, fromKey);
|
|
779
827
|
}
|
|
780
828
|
}
|
|
781
829
|
function migrateCourseStartedMark(storage, fromSessionId, toSessionId, courseId) {
|
|
@@ -808,12 +856,20 @@ function migrateCourseStartedMark(storage, fromSessionId, toSessionId, courseId)
|
|
|
808
856
|
|
|
809
857
|
// src/runtime/courseLifecycle.ts
|
|
810
858
|
var courseStartedEmitFlights = /* @__PURE__ */ new Map();
|
|
859
|
+
var courseStartedEmittedInTab = /* @__PURE__ */ new Set();
|
|
811
860
|
function resetCourseStartedEmitFlightForTests() {
|
|
812
861
|
courseStartedEmitFlights.clear();
|
|
862
|
+
courseStartedEmittedInTab.clear();
|
|
813
863
|
}
|
|
814
864
|
function tryEmitCourseStarted(ctx, deps, alreadyEmittedToSink) {
|
|
815
865
|
const flightKey = `${ctx.sessionId}:${ctx.courseId}`;
|
|
816
866
|
const marked = hasCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId);
|
|
867
|
+
if (courseStartedEmittedInTab.has(flightKey)) {
|
|
868
|
+
return Promise.resolve({
|
|
869
|
+
emitted: true,
|
|
870
|
+
marked: marked || markCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId)
|
|
871
|
+
});
|
|
872
|
+
}
|
|
817
873
|
if (alreadyEmittedToSink) {
|
|
818
874
|
const markPersisted = marked ? true : markCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId);
|
|
819
875
|
return Promise.resolve({
|
|
@@ -834,6 +890,9 @@ function tryEmitCourseStarted(ctx, deps, alreadyEmittedToSink) {
|
|
|
834
890
|
const flight = Promise.resolve().then(() => {
|
|
835
891
|
try {
|
|
836
892
|
const emitted = deps.emitCourseStartedEvent(ctx);
|
|
893
|
+
if (emitted) {
|
|
894
|
+
courseStartedEmittedInTab.add(flightKey);
|
|
895
|
+
}
|
|
837
896
|
const markPersisted = emitted && !marked ? markCourseStarted(ctx.storage, ctx.sessionId, ctx.courseId) : marked;
|
|
838
897
|
return {
|
|
839
898
|
emitted,
|