@lxpack/runtime 0.1.0 → 0.1.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.
@@ -0,0 +1,17 @@
1
+ import type { CourseProgress } from "./types.js";
2
+ interface CompactProgress {
3
+ c?: string;
4
+ l?: string[];
5
+ a?: Record<string, number>;
6
+ s?: Record<string, unknown>;
7
+ }
8
+ export declare function compactProgress(progress: CourseProgress): CompactProgress;
9
+ export declare function expandProgress(compact: CompactProgress, defaults: CourseProgress): CourseProgress;
10
+ export declare function serializeProgressForStorage(progress: CourseProgress): string;
11
+ export declare function expandLegacyProgress(saved: Partial<CourseProgress>, defaults: CourseProgress): CourseProgress;
12
+ export declare function parseStoredProgress(raw: string, defaults: CourseProgress): {
13
+ progress: CourseProgress;
14
+ parsed: boolean;
15
+ };
16
+ export {};
17
+ //# sourceMappingURL=progress-persist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-persist.d.ts","sourceRoot":"","sources":["../src/progress-persist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,UAAU,eAAe;IACvB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,eAAe,CAWzE;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,GACvB,cAAc,CAahB;AAqBD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAkB5E;AAYD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,EAC9B,QAAQ,EAAE,cAAc,GACvB,cAAc,CAehB;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,cAAc,GACvB;IAAE,QAAQ,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAa/C"}
package/dist/runtime.d.ts CHANGED
@@ -7,13 +7,19 @@ export declare class LxpackRuntime {
7
7
  private storageKey;
8
8
  private mode;
9
9
  private passedAssessments;
10
+ private defaultPassingScores;
11
+ private terminated;
10
12
  constructor(config: RuntimeConfig);
13
+ private restoreLessonLocation;
11
14
  private restoreScormProgress;
12
15
  private restoreLocalProgress;
13
16
  private syncPassedAssessments;
14
17
  private getAssessmentPassingScore;
15
18
  getAPI(): LxpackAPI;
16
- track(event: TrackEvent): void;
19
+ track(event: TrackEvent, options?: {
20
+ persist?: boolean;
21
+ }): void;
22
+ private applyAssessmentResult;
17
23
  submitAssessment(assessmentId: string, score: number, passingScore: number): void;
18
24
  completeLesson(lessonId: string): void;
19
25
  setCurrentLesson(lessonId: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,SAAS,EACT,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AAoCpB,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,iBAAiB,CAAqB;gBAElC,MAAM,EAAE,aAAa;IAwBjC,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,yBAAyB;IAQjC,MAAM,IAAI,SAAS;IAenB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAa9B,gBAAgB,CACd,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB,IAAI;IAiBP,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWtC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQxC,WAAW,IAAI,cAAc;IAI7B,WAAW,IAAI,cAAc;IAI7B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI3C,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAIjD,aAAa,IAAI,MAAM;IAKvB,kBAAkB,IAAI,MAAM;IAc5B,OAAO,CAAC,gBAAgB;IAkCxB,OAAO,CAAC,OAAO;IAiBf,SAAS,IAAI,IAAI;CAKlB;AAED,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,SAAS,EACT,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AAoBpB,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,oBAAoB,CAAyB;IACrD,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,aAAa;IA4BjC,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,yBAAyB;IASjC,MAAM,IAAI,SAAS;IAoBnB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IA2B/D,OAAO,CAAC,qBAAqB;IAe7B,gBAAgB,CACd,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB,IAAI;IAMP,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWtC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQxC,WAAW,IAAI,cAAc;IAI7B,WAAW,IAAI,cAAc;IAI7B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI3C,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAIjD,aAAa,IAAI,MAAM;IAKvB,kBAAkB,IAAI,MAAM;IAc5B,OAAO,CAAC,gBAAgB;IAkCxB,OAAO,CAAC,OAAO;IAiBf,SAAS,IAAI,IAAI;CAOlB;AAED,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
package/dist/runtime.js CHANGED
@@ -1,10 +1,10 @@
1
- var R = Object.defineProperty;
2
- var _ = (e, s, t) => s in e ? R(e, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[s] = t;
3
- var i = (e, s, t) => _(e, typeof s != "symbol" ? s + "" : s, t);
4
- const u = "lxpack_scorm12_preview";
5
- function f(e = 500) {
6
- let s = window, t = 0;
7
- for (; s && t < e; ) {
1
+ var f = Object.defineProperty;
2
+ var g = (t, s, e) => s in t ? f(t, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[s] = e;
3
+ var i = (t, s, e) => g(t, typeof s != "symbol" ? s + "" : s, e);
4
+ const S = "lxpack_scorm12_preview";
5
+ function _(t = 500) {
6
+ let s = window, e = 0;
7
+ for (; s && e < t; ) {
8
8
  try {
9
9
  const r = s.API;
10
10
  if (r && typeof r.LMSInitialize == "function")
@@ -21,11 +21,11 @@ function f(e = 500) {
21
21
  } catch {
22
22
  break;
23
23
  }
24
- t++;
24
+ e++;
25
25
  }
26
26
  return null;
27
27
  }
28
- class E {
28
+ class L {
29
29
  constructor(s) {
30
30
  i(this, "api");
31
31
  i(this, "initialized", !1);
@@ -48,8 +48,8 @@ class E {
48
48
  LMSGetValue(s) {
49
49
  return !this.initialized || this.terminated ? (this.lastError = "301", "") : (this.lastError = "0", this.api.LMSGetValue(s));
50
50
  }
51
- LMSSetValue(s, t) {
52
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSSetValue(s, t));
51
+ LMSSetValue(s, e) {
52
+ return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSSetValue(s, e));
53
53
  }
54
54
  LMSCommit() {
55
55
  return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSCommit(""));
@@ -58,8 +58,8 @@ class E {
58
58
  return this.lastError;
59
59
  }
60
60
  LMSGetErrorString(s) {
61
- const t = s ?? this.lastError;
62
- return t === "301" ? "Not initialized" : t === "302" ? "Already terminated" : this.api.LMSGetErrorString(t);
61
+ const e = s ?? this.lastError;
62
+ return e === "301" ? "Not initialized" : e === "302" ? "Already terminated" : this.api.LMSGetErrorString(e);
63
63
  }
64
64
  LMSGetDiagnostic(s) {
65
65
  return this.api.LMSGetDiagnostic(s ?? this.lastError);
@@ -67,12 +67,12 @@ class E {
67
67
  setLessonStatus(s) {
68
68
  this.LMSSetValue("cmi.core.lesson_status", s);
69
69
  }
70
- setScore(s, t = 100) {
71
- this.LMSSetValue("cmi.core.score.raw", String(s)), this.LMSSetValue("cmi.core.score.max", String(t)), this.LMSSetValue("cmi.core.score.min", "0");
70
+ setScore(s, e = 100) {
71
+ this.LMSSetValue("cmi.core.score.raw", String(s)), this.LMSSetValue("cmi.core.score.max", String(e)), this.LMSSetValue("cmi.core.score.min", "0");
72
72
  }
73
73
  setSuspendData(s) {
74
- const t = l(s);
75
- this.LMSSetValue("cmi.suspend_data", t);
74
+ const e = m(s);
75
+ this.LMSSetValue("cmi.suspend_data", e);
76
76
  }
77
77
  setLessonLocation(s) {
78
78
  this.LMSSetValue("cmi.core.lesson_location", s);
@@ -133,33 +133,33 @@ class h {
133
133
  return "";
134
134
  }
135
135
  }
136
- LMSSetValue(s, t) {
136
+ LMSSetValue(s, e) {
137
137
  if (!this.initialized || this.terminated)
138
138
  return this.lastError = "301", "false";
139
139
  switch (this.lastError = "0", s) {
140
140
  case "cmi.core.lesson_status":
141
- this.data.lessonStatus = t;
141
+ this.data.lessonStatus = e;
142
142
  break;
143
143
  case "cmi.core.score.raw":
144
- this.data.scoreRaw = Number(t);
144
+ this.data.scoreRaw = Number(e);
145
145
  break;
146
146
  case "cmi.core.score.min":
147
- this.data.scoreMin = Number(t);
147
+ this.data.scoreMin = Number(e);
148
148
  break;
149
149
  case "cmi.core.score.max":
150
- this.data.scoreMax = Number(t);
150
+ this.data.scoreMax = Number(e);
151
151
  break;
152
152
  case "cmi.suspend_data":
153
- this.data.suspendData = l(t);
153
+ this.data.suspendData = m(e);
154
154
  break;
155
155
  case "cmi.core.lesson_location":
156
- this.data.lessonLocation = t;
156
+ this.data.lessonLocation = e;
157
157
  break;
158
158
  case "cmi.core.exit":
159
- this.data.exit = t;
159
+ this.data.exit = e;
160
160
  break;
161
161
  case "cmi.core.session_time":
162
- this.data.sessionTime = t;
162
+ this.data.sessionTime = e;
163
163
  break;
164
164
  default:
165
165
  return "false";
@@ -173,8 +173,8 @@ class h {
173
173
  return this.lastError;
174
174
  }
175
175
  LMSGetErrorString(s) {
176
- const t = s ?? this.lastError;
177
- return t === "301" ? "Not initialized" : t === "302" ? "Already terminated" : "No error";
176
+ const e = s ?? this.lastError;
177
+ return e === "301" ? "Not initialized" : e === "302" ? "Already terminated" : "No error";
178
178
  }
179
179
  LMSGetDiagnostic() {
180
180
  return "";
@@ -182,18 +182,18 @@ class h {
182
182
  setLessonStatus(s) {
183
183
  this.data.lessonStatus = s, this.saveToStorage();
184
184
  }
185
- setScore(s, t = 100) {
186
- this.data.scoreRaw = s, this.data.scoreMax = t, this.data.scoreMin = 0, this.saveToStorage();
185
+ setScore(s, e = 100) {
186
+ this.data.scoreRaw = s, this.data.scoreMax = e, this.data.scoreMin = 0, this.saveToStorage();
187
187
  }
188
188
  setSuspendData(s) {
189
- this.data.suspendData = l(s), this.saveToStorage();
189
+ this.data.suspendData = m(s), this.saveToStorage();
190
190
  }
191
191
  setLessonLocation(s) {
192
192
  this.data.lessonLocation = s, this.saveToStorage();
193
193
  }
194
194
  loadFromStorage() {
195
195
  try {
196
- const s = localStorage.getItem(u);
196
+ const s = localStorage.getItem(S);
197
197
  s && (this.data = { ...this.data, ...JSON.parse(s) });
198
198
  } catch {
199
199
  }
@@ -201,63 +201,106 @@ class h {
201
201
  saveToStorage() {
202
202
  if (this.persistToStorage)
203
203
  try {
204
- localStorage.setItem(u, JSON.stringify(this.data));
204
+ localStorage.setItem(S, JSON.stringify(this.data));
205
205
  } catch {
206
206
  }
207
207
  }
208
208
  }
209
- class L extends h {
209
+ class D extends h {
210
210
  constructor() {
211
211
  super({ persistToStorage: !0 });
212
212
  }
213
213
  }
214
- function l(e) {
215
- return e.length <= 4096 ? e : (console.warn(
214
+ function m(t) {
215
+ return t.length <= 4096 ? t : (console.warn(
216
216
  "[lxpack] suspend_data exceeds 4096 chars; truncating"
217
- ), e.slice(0, 4096));
217
+ ), t.slice(0, 4096));
218
218
  }
219
- function p(e) {
220
- if (e === "preview")
219
+ function R(t) {
220
+ if (t === "preview")
221
221
  return new h({ persistToStorage: !0 });
222
- if (e === "scorm12") {
223
- const s = f();
224
- return s ? new E(s) : (console.warn(
222
+ if (t === "scorm12") {
223
+ const s = _();
224
+ return s ? new L(s) : (console.warn(
225
225
  "[lxpack] No LMS SCORM API found; using in-memory simulator (progress will not persist to LMS)"
226
226
  ), new h({ persistToStorage: !1 }));
227
227
  }
228
228
  return new h({ persistToStorage: !1 });
229
229
  }
230
- function M(e = "preview") {
231
- const s = p(e);
232
- if (e === "preview") {
233
- const t = window;
234
- t.API = s;
230
+ function C(t = "preview") {
231
+ const s = R(t);
232
+ if (t === "preview") {
233
+ const e = window;
234
+ e.API = s;
235
235
  }
236
236
  return s;
237
237
  }
238
- function g(e) {
239
- const s = `${e.title}::${e.version}`;
240
- let t = 0;
241
- for (let r = 0; r < s.length; r++)
242
- t = (t << 5) - t + s.charCodeAt(r), t |= 0;
243
- return `lxpack_progress_${Math.abs(t)}`;
238
+ function d(t) {
239
+ var e;
240
+ const s = {
241
+ c: t.currentLessonId,
242
+ l: t.completedLessons,
243
+ a: t.assessmentScores,
244
+ s: t.suspendData
245
+ };
246
+ return (e = s.l) != null && e.length || delete s.l, (!s.a || !Object.keys(s.a).length) && delete s.a, (!s.s || !Object.keys(s.s).length) && delete s.s, s;
244
247
  }
245
- function S(e, s) {
248
+ function E(t, s) {
246
249
  return {
247
- ...e,
248
- ...s,
249
- completedLessons: s.completedLessons ?? e.completedLessons,
250
- assessmentScores: {
251
- ...e.assessmentScores,
252
- ...s.assessmentScores
253
- },
254
- suspendData: {
255
- ...e.suspendData,
256
- ...s.suspendData
257
- }
250
+ currentLessonId: t.c ?? s.currentLessonId,
251
+ completedLessons: Array.isArray(t.l) ? t.l : s.completedLessons,
252
+ assessmentScores: t.a && typeof t.a == "object" ? t.a : s.assessmentScores,
253
+ suspendData: t.s && typeof t.s == "object" ? t.s : s.suspendData
254
+ };
255
+ }
256
+ function O(t) {
257
+ const s = { ...t.suspendData }, e = Object.keys(s).filter((r) => r.startsWith("interaction_")).sort();
258
+ for (const r of e)
259
+ if (delete s[r], JSON.stringify(d({ ...t, suspendData: s })).length <= 4096)
260
+ return { ...t, suspendData: s };
261
+ return { ...t, suspendData: s };
262
+ }
263
+ function M(t) {
264
+ let s = JSON.stringify(d(t));
265
+ if (s.length <= 4096)
266
+ return s;
267
+ const e = O(t);
268
+ return s = JSON.stringify(d(e)), s.length <= 4096 ? s : (console.warn(
269
+ "[lxpack] suspend_data exceeds 4096 chars; truncating"
270
+ ), s.slice(0, 4096));
271
+ }
272
+ function A(t) {
273
+ if (!t || typeof t != "object") return !1;
274
+ const s = t;
275
+ return "currentLessonId" in s || "completedLessons" in s || "assessmentScores" in s;
276
+ }
277
+ function N(t, s) {
278
+ return {
279
+ currentLessonId: t.currentLessonId ?? s.currentLessonId,
280
+ completedLessons: Array.isArray(t.completedLessons) ? t.completedLessons : s.completedLessons,
281
+ assessmentScores: t.assessmentScores && typeof t.assessmentScores == "object" ? t.assessmentScores : s.assessmentScores,
282
+ suspendData: t.suspendData && typeof t.suspendData == "object" ? t.suspendData : s.suspendData
258
283
  };
259
284
  }
260
- class N {
285
+ function p(t, s) {
286
+ try {
287
+ const e = JSON.parse(t);
288
+ return A(e) ? {
289
+ progress: N(e, s),
290
+ parsed: !0
291
+ } : { progress: E(e, s), parsed: !0 };
292
+ } catch {
293
+ return { progress: s, parsed: !1 };
294
+ }
295
+ }
296
+ function I(t) {
297
+ const s = `${t.title}::${t.version}`;
298
+ let e = 0;
299
+ for (let r = 0; r < s.length; r++)
300
+ e = (e << 5) - e + s.charCodeAt(r), e |= 0;
301
+ return `lxpack_progress_${Math.abs(e)}`;
302
+ }
303
+ class y {
261
304
  constructor(s) {
262
305
  i(this, "manifest");
263
306
  i(this, "progress");
@@ -266,37 +309,41 @@ class N {
266
309
  i(this, "storageKey");
267
310
  i(this, "mode");
268
311
  i(this, "passedAssessments", /* @__PURE__ */ new Set());
312
+ i(this, "defaultPassingScores");
313
+ i(this, "terminated", !1);
269
314
  var r, a, o;
270
- this.manifest = s.manifest, this.mode = s.mode, this.completionThreshold = ((a = (r = s.manifest.tracking) == null ? void 0 : r.completion) == null ? void 0 : a.threshold) ?? 0.9, this.storageKey = g(s.manifest);
271
- const t = ((o = s.manifest.lessons[0]) == null ? void 0 : o.id) ?? "";
315
+ this.manifest = s.manifest, this.mode = s.mode, this.completionThreshold = ((a = (r = s.manifest.tracking) == null ? void 0 : r.completion) == null ? void 0 : a.threshold) ?? 0.9, this.storageKey = I(s.manifest), this.defaultPassingScores = {};
316
+ for (const [n, l] of Object.entries(s.assessments ?? {}))
317
+ this.defaultPassingScores[n] = l.passingScore;
318
+ const e = ((o = s.manifest.lessons[0]) == null ? void 0 : o.id) ?? "";
272
319
  this.progress = s.progress ?? {
273
- currentLessonId: t,
320
+ currentLessonId: e,
274
321
  completedLessons: [],
275
322
  assessmentScores: {},
276
323
  suspendData: {}
277
- }, s.mode === "scorm12" ? (this.scorm = p("scorm12"), this.scorm.LMSInitialize(), this.restoreScormProgress()) : (s.mode === "preview" || s.mode === "standalone") && this.restoreLocalProgress();
324
+ }, s.mode === "scorm12" ? (this.scorm = R("scorm12"), this.scorm.LMSInitialize(), this.restoreScormProgress()) : (s.mode === "preview" || s.mode === "standalone") && this.restoreLocalProgress();
325
+ }
326
+ restoreLessonLocation() {
327
+ if (!this.scorm) return;
328
+ const s = this.scorm.LMSGetValue("cmi.core.lesson_location");
329
+ s && (this.progress.currentLessonId = s);
278
330
  }
279
331
  restoreScormProgress() {
280
332
  if (!this.scorm) return;
281
333
  const s = this.scorm.LMSGetValue("cmi.suspend_data");
282
- if (s)
283
- try {
284
- const t = JSON.parse(s);
285
- this.progress = S(this.progress, t);
286
- } catch {
287
- }
288
- else {
289
- const t = this.scorm.LMSGetValue("cmi.core.lesson_location");
290
- t && (this.progress.currentLessonId = t);
291
- }
334
+ if (s) {
335
+ const { progress: e, parsed: r } = p(s, this.progress);
336
+ r ? this.progress = e : this.restoreLessonLocation();
337
+ } else
338
+ this.restoreLessonLocation();
292
339
  this.syncPassedAssessments();
293
340
  }
294
341
  restoreLocalProgress() {
295
342
  try {
296
343
  const s = localStorage.getItem(this.storageKey);
297
344
  if (s) {
298
- const t = JSON.parse(s);
299
- this.progress = S(this.progress, t);
345
+ const { progress: e, parsed: r } = p(s, this.progress);
346
+ r && (this.progress = e);
300
347
  }
301
348
  } catch {
302
349
  }
@@ -304,41 +351,46 @@ class N {
304
351
  }
305
352
  syncPassedAssessments() {
306
353
  this.passedAssessments.clear();
307
- for (const [s, t] of Object.entries(this.progress.assessmentScores))
308
- t >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
354
+ for (const [s, e] of Object.entries(this.progress.assessmentScores))
355
+ e >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
309
356
  }
310
357
  getAssessmentPassingScore(s) {
311
- var a;
312
- if (!((a = this.manifest.assessments) == null ? void 0 : a.find((o) => o.id === s))) return 0.7;
313
- const r = this.progress.suspendData[`assessment_passing_${s}`];
314
- return typeof r == "number" ? r : 0.7;
358
+ const e = this.progress.suspendData[`assessment_passing_${s}`];
359
+ return typeof e == "number" ? e : s in this.defaultPassingScores ? this.defaultPassingScores[s] : 0.7;
315
360
  }
316
361
  getAPI() {
317
362
  return {
318
363
  track: (s) => this.track(s),
319
364
  completeLesson: (s) => this.completeLesson(s),
320
- getProgress: () => ({ ...this.progress }),
321
- setVariable: (s, t) => {
322
- this.progress.suspendData[s] = t, this.persist();
365
+ getProgress: () => ({
366
+ ...this.progress,
367
+ completedLessons: [...this.progress.completedLessons],
368
+ assessmentScores: { ...this.progress.assessmentScores },
369
+ suspendData: { ...this.progress.suspendData }
370
+ }),
371
+ setVariable: (s, e) => {
372
+ this.progress.suspendData[s] = e, this.persist();
323
373
  },
324
374
  getVariable: (s) => this.progress.suspendData[s],
325
- submitAssessment: (s, t, r) => this.submitAssessment(s, t, r)
375
+ submitAssessment: (s, e, r) => this.submitAssessment(s, e, r)
326
376
  };
327
377
  }
328
- track(s) {
329
- var t;
330
- if (s.type === "interaction" && s.id && (this.progress.suspendData[`interaction_${s.id}`] = s.data ?? !0, console.debug("[lxpack] interaction:", s.id, s.data)), s.type === "assessment" && s.id && ((t = s.data) == null ? void 0 : t.score) != null) {
331
- const r = Number(s.data.score);
332
- this.progress.assessmentScores[s.id] = r;
378
+ track(s, e) {
379
+ var r, a;
380
+ if (s.type === "interaction" && s.id && (this.progress.suspendData[`interaction_${s.id}`] = s.data ?? !0, console.debug("[lxpack] interaction:", s.id, s.data)), s.type === "assessment" && s.id) {
381
+ const o = typeof ((r = s.data) == null ? void 0 : r.passingScore) == "number" ? Number(s.data.passingScore) : this.getAssessmentPassingScore(s.id), n = ((a = s.data) == null ? void 0 : a.score) != null ? Number(s.data.score) : void 0;
382
+ if (n != null && !Number.isNaN(n)) {
383
+ this.applyAssessmentResult(s.id, n, o), (e == null ? void 0 : e.persist) !== !1 && (this.updateCompletion(), this.persist());
384
+ return;
385
+ }
333
386
  }
334
- this.persist();
387
+ (e == null ? void 0 : e.persist) !== !1 && this.persist();
388
+ }
389
+ applyAssessmentResult(s, e, r) {
390
+ this.progress.assessmentScores[s] = e, this.progress.suspendData[`assessment_passing_${s}`] = r, e >= r ? this.passedAssessments.add(s) : this.passedAssessments.delete(s);
335
391
  }
336
- submitAssessment(s, t, r) {
337
- this.progress.assessmentScores[s] = t, this.progress.suspendData[`assessment_passing_${s}`] = r, t >= r ? this.passedAssessments.add(s) : this.passedAssessments.delete(s), this.track({
338
- type: "assessment",
339
- id: s,
340
- data: { score: t, passingScore: r, passed: t >= r }
341
- }), this.updateCompletion();
392
+ submitAssessment(s, e, r) {
393
+ this.applyAssessmentResult(s, e, r), this.updateCompletion(), this.persist();
342
394
  }
343
395
  completeLesson(s) {
344
396
  new Set(this.manifest.lessons.map((r) => r.id)).has(s) && (this.progress.completedLessons.includes(s) || this.progress.completedLessons.push(s), this.updateCompletion(), this.persist());
@@ -347,7 +399,7 @@ class N {
347
399
  this.progress.currentLessonId = s, this.scorm && this.scorm.setLessonLocation(s), this.persist();
348
400
  }
349
401
  getProgress() {
350
- return { ...this.progress };
402
+ return this.getAPI().getProgress();
351
403
  }
352
404
  getManifest() {
353
405
  return this.manifest;
@@ -359,35 +411,35 @@ class N {
359
411
  return this.passedAssessments.has(s);
360
412
  }
361
413
  getTotalUnits() {
362
- var t;
363
- const s = ((t = this.manifest.assessments) == null ? void 0 : t.length) ?? 0;
414
+ var e;
415
+ const s = ((e = this.manifest.assessments) == null ? void 0 : e.length) ?? 0;
364
416
  return this.manifest.lessons.length + s;
365
417
  }
366
418
  getCompletionRatio() {
367
- var c;
419
+ var n;
368
420
  if (this.getTotalUnits() === 0) return 0;
369
- const t = this.manifest.lessons.length, r = ((c = this.manifest.assessments) == null ? void 0 : c.length) ?? 0, a = this.progress.completedLessons.filter(
370
- (m) => this.manifest.lessons.some((d) => d.id === m)
421
+ const e = this.manifest.lessons.length, r = ((n = this.manifest.assessments) == null ? void 0 : n.length) ?? 0, a = this.progress.completedLessons.filter(
422
+ (l) => this.manifest.lessons.some((u) => u.id === l)
371
423
  ).length, o = this.passedAssessments.size;
372
- return (a + o) / (t + r);
424
+ return (a + o) / (e + r);
373
425
  }
374
426
  updateCompletion() {
375
427
  var r, a, o;
376
- const s = this.getCompletionRatio(), t = Math.round(s * 100);
428
+ const s = this.getCompletionRatio(), e = Math.round(s * 100);
377
429
  if (this.scorm) {
378
- this.scorm.setScore(t);
379
- const c = this.manifest.lessons.every(
380
- (n) => this.progress.completedLessons.includes(n.id)
381
- ), m = !((r = this.manifest.assessments) != null && r.length) || this.manifest.assessments.every((n) => this.passedAssessments.has(n.id));
430
+ this.scorm.setScore(e);
431
+ const n = this.manifest.lessons.every(
432
+ (c) => this.progress.completedLessons.includes(c.id)
433
+ ), l = !((r = this.manifest.assessments) != null && r.length) || this.manifest.assessments.every((c) => this.passedAssessments.has(c.id));
382
434
  ((a = this.manifest.assessments) == null ? void 0 : a.some(
383
- (n) => n.id in this.progress.assessmentScores && !this.passedAssessments.has(n.id)
384
- )) ? this.scorm.setLessonStatus("failed") : s >= this.completionThreshold && c && m ? this.scorm.setLessonStatus(
435
+ (c) => c.id in this.progress.assessmentScores && !this.passedAssessments.has(c.id)
436
+ )) ? this.scorm.setLessonStatus("failed") : s >= this.completionThreshold && n && l ? this.scorm.setLessonStatus(
385
437
  (((o = this.manifest.assessments) == null ? void 0 : o.length) ?? 0) > 0 ? "passed" : "completed"
386
- ) : t > 0 && this.scorm.setLessonStatus("incomplete");
438
+ ) : e > 0 && this.scorm.setLessonStatus("incomplete");
387
439
  }
388
440
  }
389
441
  persist() {
390
- const s = l(JSON.stringify(this.progress));
442
+ const s = M(this.progress);
391
443
  if (this.mode === "preview" || this.mode === "standalone")
392
444
  try {
393
445
  localStorage.setItem(this.storageKey, s);
@@ -396,16 +448,16 @@ class N {
396
448
  this.scorm && (this.scorm.setSuspendData(s), this.scorm.LMSCommit());
397
449
  }
398
450
  terminate() {
399
- this.scorm && this.scorm.LMSFinish();
451
+ this.terminated || (this.terminated = !0, this.scorm && this.scorm.LMSFinish());
400
452
  }
401
453
  }
402
454
  export {
403
- N as LxpackRuntime,
404
- L as Scorm12API,
405
- E as Scorm12Adapter,
455
+ y as LxpackRuntime,
456
+ D as Scorm12API,
457
+ L as Scorm12Adapter,
406
458
  h as Scorm12Simulator,
407
- p as createScormConnection,
408
- f as findLmsApi,
409
- M as installScormAPI,
410
- l as trimSuspendData
459
+ R as createScormConnection,
460
+ _ as findLmsApi,
461
+ C as installScormAPI,
462
+ m as trimSuspendData
411
463
  };
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CourseManifest, Lesson } from "@lxpack/validators";
1
+ import type { CourseManifest, LearnerAssessment, Lesson, RuntimeAssessmentBundle } from "@lxpack/validators";
2
2
  export interface CourseProgress {
3
3
  currentLessonId: string;
4
4
  completedLessons: string[];
@@ -10,6 +10,8 @@ export interface RuntimeConfig {
10
10
  baseUrl: string;
11
11
  mode: "preview" | "standalone" | "scorm12";
12
12
  progress?: CourseProgress;
13
+ assessments?: RuntimeAssessmentBundle["assessments"];
14
+ answerKeys?: RuntimeAssessmentBundle["answerKeys"];
13
15
  }
14
16
  export interface TrackEvent {
15
17
  type: string;
@@ -24,5 +26,5 @@ export interface LxpackAPI {
24
26
  getVariable: (key: string) => unknown;
25
27
  submitAssessment: (assessmentId: string, score: number, passingScore: number) => void;
26
28
  }
27
- export type { CourseManifest, Lesson };
29
+ export type { CourseManifest, LearnerAssessment, Lesson };
28
30
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,MAAM,cAAc,CAAC;IAClC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,gBAAgB,EAAE,CAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,KACjB,IAAI,CAAC;CACX;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,MAAM,EACN,uBAAuB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,WAAW,CAAC,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;IACrD,UAAU,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,MAAM,cAAc,CAAC;IAClC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,gBAAgB,EAAE,CAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,KACjB,IAAI,CAAC;CACX;AAED,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lxpack/runtime",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Web-native course runtime for LXPack",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "marked": "^15.0.7",
41
41
  "yaml": "^2.7.0",
42
- "@lxpack/validators": "0.1.0"
42
+ "@lxpack/validators": "0.1.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^22.13.10",