@lessonkit/react 0.3.0 → 0.3.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/dist/index.cjs CHANGED
@@ -42,55 +42,74 @@ var import_accessibility = require("@lessonkit/accessibility");
42
42
 
43
43
  // src/context.tsx
44
44
  var import_react = require("react");
45
- var import_core = require("@lessonkit/core");
45
+ var import_core2 = require("@lessonkit/core");
46
46
  var import_xapi = require("@lessonkit/xapi");
47
- var import_jsx_runtime = require("react/jsx-runtime");
48
- var LessonkitContext = (0, import_react.createContext)(null);
49
- var SESSION_STORAGE_KEY = "lessonkit:sessionId";
50
- var COURSE_STARTED_PREFIX = "lessonkit:course_started:";
51
- function disposeTrackingClient(client) {
52
- client?.flush?.();
53
- client?.dispose?.();
54
- }
55
- function safeSessionStorageGetItem(key) {
56
- if (typeof sessionStorage === "undefined") return null;
57
- try {
58
- return sessionStorage.getItem(key);
59
- } catch {
60
- return null;
61
- }
47
+
48
+ // src/runtime/ports.ts
49
+ function createNoopStorage() {
50
+ return {
51
+ getItem: () => null,
52
+ setItem: () => {
53
+ }
54
+ };
62
55
  }
63
- function safeSessionStorageSetItem(key, value) {
64
- if (typeof sessionStorage === "undefined") return;
65
- try {
66
- sessionStorage.setItem(key, value);
67
- } catch {
68
- }
56
+ function createSessionStoragePort() {
57
+ if (typeof sessionStorage === "undefined") return createNoopStorage();
58
+ return {
59
+ getItem: (key) => {
60
+ try {
61
+ return sessionStorage.getItem(key);
62
+ } catch {
63
+ return null;
64
+ }
65
+ },
66
+ setItem: (key, value) => {
67
+ try {
68
+ sessionStorage.setItem(key, value);
69
+ } catch {
70
+ }
71
+ }
72
+ };
69
73
  }
70
- function resolveSessionId(provided) {
74
+
75
+ // src/runtime/session.ts
76
+ var import_core = require("@lessonkit/core");
77
+ var SESSION_STORAGE_KEY = "lessonkit:sessionId";
78
+ var COURSE_STARTED_PREFIX = "lessonkit:course_started:";
79
+ function resolveSessionId(storage, provided) {
71
80
  if (provided) return provided;
72
- const existing = safeSessionStorageGetItem(SESSION_STORAGE_KEY);
81
+ const existing = storage.getItem(SESSION_STORAGE_KEY);
73
82
  if (existing) return existing;
74
83
  const id = (0, import_core.createSessionId)();
75
- safeSessionStorageSetItem(SESSION_STORAGE_KEY, id);
84
+ storage.setItem(SESSION_STORAGE_KEY, id);
76
85
  return id;
77
86
  }
78
87
  function courseStartedStorageKey(sessionId, courseId) {
79
88
  return `${COURSE_STARTED_PREFIX}${sessionId}:${courseId ?? ""}`;
80
89
  }
81
- function hasCourseStarted(sessionId, courseId) {
90
+ function hasCourseStarted(storage, sessionId, courseId) {
82
91
  if (!courseId) return false;
83
- return safeSessionStorageGetItem(courseStartedStorageKey(sessionId, courseId)) === "1";
92
+ return storage.getItem(courseStartedStorageKey(sessionId, courseId)) === "1";
84
93
  }
85
- function markCourseStarted(sessionId, courseId) {
94
+ function markCourseStarted(storage, sessionId, courseId) {
86
95
  if (!courseId) return;
87
- safeSessionStorageSetItem(courseStartedStorageKey(sessionId, courseId), "1");
96
+ storage.setItem(courseStartedStorageKey(sessionId, courseId), "1");
97
+ }
98
+
99
+ // src/context.tsx
100
+ var import_jsx_runtime = require("react/jsx-runtime");
101
+ var LessonkitContext = (0, import_react.createContext)(null);
102
+ var useIsoLayoutEffect = typeof window !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
103
+ function disposeTrackingClient(client) {
104
+ client?.flush?.();
105
+ client?.dispose?.();
88
106
  }
107
+ var defaultStorage = createSessionStoragePort();
89
108
  function createTrackingClientFromConfig(config) {
90
109
  if (config.tracking?.enabled === false) {
91
- return (0, import_core.createTrackingClient)();
110
+ return (0, import_core2.createTrackingClient)();
92
111
  }
93
- return (0, import_core.createTrackingClient)({
112
+ return (0, import_core2.createTrackingClient)({
94
113
  sink: config.tracking?.sink,
95
114
  batchSink: config.tracking?.batchSink,
96
115
  batch: config.tracking?.batch
@@ -104,7 +123,7 @@ function createXapiClientFromConfig(config, queue) {
104
123
  }
105
124
  function LessonkitProvider(props) {
106
125
  const config = props.config ?? {};
107
- const sessionIdRef = (0, import_react.useRef)(resolveSessionId(config.session?.sessionId));
126
+ const sessionIdRef = (0, import_react.useRef)(resolveSessionId(defaultStorage, config.session?.sessionId));
108
127
  if (config.session?.sessionId) sessionIdRef.current = config.session.sessionId;
109
128
  const attemptIdRef = (0, import_react.useRef)(config.session?.attemptId);
110
129
  const userRef = (0, import_react.useRef)(config.session?.user);
@@ -112,7 +131,7 @@ function LessonkitProvider(props) {
112
131
  userRef.current = config.session?.user;
113
132
  const courseIdRef = (0, import_react.useRef)(config.courseId);
114
133
  courseIdRef.current = config.courseId;
115
- const trackingRef = (0, import_react.useRef)((0, import_core.createTrackingClient)());
134
+ const trackingRef = (0, import_react.useRef)((0, import_core2.createTrackingClient)());
116
135
  const [tracking, setTracking] = (0, import_react.useState)(() => trackingRef.current);
117
136
  const courseStartedInProviderRef = (0, import_react.useRef)(false);
118
137
  const trackingEnabled = config.tracking?.enabled;
@@ -121,23 +140,23 @@ function LessonkitProvider(props) {
121
140
  const batchEnabled = config.tracking?.batch?.enabled;
122
141
  const batchFlushIntervalMs = config.tracking?.batch?.flushIntervalMs;
123
142
  const batchMaxBatchSize = config.tracking?.batch?.maxBatchSize;
124
- (0, import_react.useLayoutEffect)(() => {
143
+ useIsoLayoutEffect(() => {
125
144
  const prev = trackingRef.current;
126
145
  const next = createTrackingClientFromConfig(config);
127
146
  trackingRef.current = next;
128
147
  setTracking(next);
129
148
  const sessionId = sessionIdRef.current;
130
149
  const cid = courseIdRef.current;
131
- const shouldEmitCourseStarted = cid ? !hasCourseStarted(sessionId, cid) : !courseStartedInProviderRef.current;
150
+ const shouldEmitCourseStarted = cid ? !hasCourseStarted(defaultStorage, sessionId, cid) : !courseStartedInProviderRef.current;
132
151
  if (shouldEmitCourseStarted) {
133
152
  if (cid) {
134
- markCourseStarted(sessionId, cid);
153
+ markCourseStarted(defaultStorage, sessionId, cid);
135
154
  } else {
136
155
  courseStartedInProviderRef.current = true;
137
156
  }
138
157
  next.track({
139
158
  name: "course_started",
140
- timestamp: (0, import_core.nowIso)(),
159
+ timestamp: (0, import_core2.nowIso)(),
141
160
  courseId: cid,
142
161
  sessionId,
143
162
  attemptId: attemptIdRef.current,
@@ -162,7 +181,7 @@ function LessonkitProvider(props) {
162
181
  const xapiClient = config.xapi?.client;
163
182
  const xapiTransport = config.xapi?.transport;
164
183
  const courseId = config.courseId;
165
- (0, import_react.useLayoutEffect)(() => {
184
+ useIsoLayoutEffect(() => {
166
185
  const prev = xapiRef.current;
167
186
  const next = createXapiClientFromConfig(config, xapiQueueRef.current);
168
187
  xapiRef.current = next;
@@ -197,7 +216,7 @@ function LessonkitProvider(props) {
197
216
  (name, data, opts) => {
198
217
  trackingRef.current?.track({
199
218
  name,
200
- timestamp: (0, import_core.nowIso)(),
219
+ timestamp: (0, import_core2.nowIso)(),
201
220
  courseId: courseIdRef.current,
202
221
  lessonId: opts?.lessonId ?? activeLessonIdRef.current,
203
222
  sessionId: sessionIdRef.current,
package/dist/index.js CHANGED
@@ -12,50 +12,69 @@ import {
12
12
  useRef,
13
13
  useState
14
14
  } from "react";
15
- import { createSessionId, createTrackingClient, nowIso } from "@lessonkit/core";
15
+ import { createTrackingClient, nowIso } from "@lessonkit/core";
16
16
  import { createInMemoryXAPIQueue, createXAPIClient } from "@lessonkit/xapi";
17
- import { jsx } from "react/jsx-runtime";
18
- var LessonkitContext = createContext(null);
19
- var SESSION_STORAGE_KEY = "lessonkit:sessionId";
20
- var COURSE_STARTED_PREFIX = "lessonkit:course_started:";
21
- function disposeTrackingClient(client) {
22
- client?.flush?.();
23
- client?.dispose?.();
24
- }
25
- function safeSessionStorageGetItem(key) {
26
- if (typeof sessionStorage === "undefined") return null;
27
- try {
28
- return sessionStorage.getItem(key);
29
- } catch {
30
- return null;
31
- }
17
+
18
+ // src/runtime/ports.ts
19
+ function createNoopStorage() {
20
+ return {
21
+ getItem: () => null,
22
+ setItem: () => {
23
+ }
24
+ };
32
25
  }
33
- function safeSessionStorageSetItem(key, value) {
34
- if (typeof sessionStorage === "undefined") return;
35
- try {
36
- sessionStorage.setItem(key, value);
37
- } catch {
38
- }
26
+ function createSessionStoragePort() {
27
+ if (typeof sessionStorage === "undefined") return createNoopStorage();
28
+ return {
29
+ getItem: (key) => {
30
+ try {
31
+ return sessionStorage.getItem(key);
32
+ } catch {
33
+ return null;
34
+ }
35
+ },
36
+ setItem: (key, value) => {
37
+ try {
38
+ sessionStorage.setItem(key, value);
39
+ } catch {
40
+ }
41
+ }
42
+ };
39
43
  }
40
- function resolveSessionId(provided) {
44
+
45
+ // src/runtime/session.ts
46
+ import { createSessionId } from "@lessonkit/core";
47
+ var SESSION_STORAGE_KEY = "lessonkit:sessionId";
48
+ var COURSE_STARTED_PREFIX = "lessonkit:course_started:";
49
+ function resolveSessionId(storage, provided) {
41
50
  if (provided) return provided;
42
- const existing = safeSessionStorageGetItem(SESSION_STORAGE_KEY);
51
+ const existing = storage.getItem(SESSION_STORAGE_KEY);
43
52
  if (existing) return existing;
44
53
  const id = createSessionId();
45
- safeSessionStorageSetItem(SESSION_STORAGE_KEY, id);
54
+ storage.setItem(SESSION_STORAGE_KEY, id);
46
55
  return id;
47
56
  }
48
57
  function courseStartedStorageKey(sessionId, courseId) {
49
58
  return `${COURSE_STARTED_PREFIX}${sessionId}:${courseId ?? ""}`;
50
59
  }
51
- function hasCourseStarted(sessionId, courseId) {
60
+ function hasCourseStarted(storage, sessionId, courseId) {
52
61
  if (!courseId) return false;
53
- return safeSessionStorageGetItem(courseStartedStorageKey(sessionId, courseId)) === "1";
62
+ return storage.getItem(courseStartedStorageKey(sessionId, courseId)) === "1";
54
63
  }
55
- function markCourseStarted(sessionId, courseId) {
64
+ function markCourseStarted(storage, sessionId, courseId) {
56
65
  if (!courseId) return;
57
- safeSessionStorageSetItem(courseStartedStorageKey(sessionId, courseId), "1");
66
+ storage.setItem(courseStartedStorageKey(sessionId, courseId), "1");
67
+ }
68
+
69
+ // src/context.tsx
70
+ import { jsx } from "react/jsx-runtime";
71
+ var LessonkitContext = createContext(null);
72
+ var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
73
+ function disposeTrackingClient(client) {
74
+ client?.flush?.();
75
+ client?.dispose?.();
58
76
  }
77
+ var defaultStorage = createSessionStoragePort();
59
78
  function createTrackingClientFromConfig(config) {
60
79
  if (config.tracking?.enabled === false) {
61
80
  return createTrackingClient();
@@ -74,7 +93,7 @@ function createXapiClientFromConfig(config, queue) {
74
93
  }
75
94
  function LessonkitProvider(props) {
76
95
  const config = props.config ?? {};
77
- const sessionIdRef = useRef(resolveSessionId(config.session?.sessionId));
96
+ const sessionIdRef = useRef(resolveSessionId(defaultStorage, config.session?.sessionId));
78
97
  if (config.session?.sessionId) sessionIdRef.current = config.session.sessionId;
79
98
  const attemptIdRef = useRef(config.session?.attemptId);
80
99
  const userRef = useRef(config.session?.user);
@@ -91,17 +110,17 @@ function LessonkitProvider(props) {
91
110
  const batchEnabled = config.tracking?.batch?.enabled;
92
111
  const batchFlushIntervalMs = config.tracking?.batch?.flushIntervalMs;
93
112
  const batchMaxBatchSize = config.tracking?.batch?.maxBatchSize;
94
- useLayoutEffect(() => {
113
+ useIsoLayoutEffect(() => {
95
114
  const prev = trackingRef.current;
96
115
  const next = createTrackingClientFromConfig(config);
97
116
  trackingRef.current = next;
98
117
  setTracking(next);
99
118
  const sessionId = sessionIdRef.current;
100
119
  const cid = courseIdRef.current;
101
- const shouldEmitCourseStarted = cid ? !hasCourseStarted(sessionId, cid) : !courseStartedInProviderRef.current;
120
+ const shouldEmitCourseStarted = cid ? !hasCourseStarted(defaultStorage, sessionId, cid) : !courseStartedInProviderRef.current;
102
121
  if (shouldEmitCourseStarted) {
103
122
  if (cid) {
104
- markCourseStarted(sessionId, cid);
123
+ markCourseStarted(defaultStorage, sessionId, cid);
105
124
  } else {
106
125
  courseStartedInProviderRef.current = true;
107
126
  }
@@ -132,7 +151,7 @@ function LessonkitProvider(props) {
132
151
  const xapiClient = config.xapi?.client;
133
152
  const xapiTransport = config.xapi?.transport;
134
153
  const courseId = config.courseId;
135
- useLayoutEffect(() => {
154
+ useIsoLayoutEffect(() => {
136
155
  const prev = xapiRef.current;
137
156
  const next = createXapiClientFromConfig(config, xapiQueueRef.current);
138
157
  xapiRef.current = next;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/react",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "React components and hooks for building learning experiences with LessonKit.",
6
6
  "license": "Apache-2.0",
@@ -50,9 +50,9 @@
50
50
  "react-dom": ">=18"
51
51
  },
52
52
  "dependencies": {
53
- "@lessonkit/accessibility": "0.3.0",
54
- "@lessonkit/core": "0.3.0",
55
- "@lessonkit/xapi": "0.3.0"
53
+ "@lessonkit/accessibility": "0.3.1",
54
+ "@lessonkit/core": "0.3.1",
55
+ "@lessonkit/xapi": "0.3.1"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@testing-library/react": "^16.3.0",