@lxpack/runtime 0.2.0 → 0.2.2

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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/dist/client.js +2091 -1813
  3. package/dist/core/completion-evaluator.d.ts +13 -0
  4. package/dist/core/completion-evaluator.d.ts.map +1 -0
  5. package/dist/core/progress-state.d.ts +20 -0
  6. package/dist/core/progress-state.d.ts.map +1 -0
  7. package/dist/flow-conditions.d.ts +4 -0
  8. package/dist/flow-conditions.d.ts.map +1 -0
  9. package/dist/flow.d.ts +2 -2
  10. package/dist/flow.d.ts.map +1 -1
  11. package/dist/lms/bridge.d.ts +11 -0
  12. package/dist/lms/bridge.d.ts.map +1 -0
  13. package/dist/lms/completion-state.d.ts +10 -0
  14. package/dist/lms/completion-state.d.ts.map +1 -0
  15. package/dist/lms/factory.d.ts +5 -0
  16. package/dist/lms/factory.d.ts.map +1 -0
  17. package/dist/lms/local-bridge.d.ts +15 -0
  18. package/dist/lms/local-bridge.d.ts.map +1 -0
  19. package/dist/lms/scorm12-bridge.d.ts +15 -0
  20. package/dist/lms/scorm12-bridge.d.ts.map +1 -0
  21. package/dist/lms/scorm2004-bridge.d.ts +15 -0
  22. package/dist/lms/scorm2004-bridge.d.ts.map +1 -0
  23. package/dist/{progress-persist.d.ts → progress/codec.d.ts} +2 -3
  24. package/dist/progress/codec.d.ts.map +1 -0
  25. package/dist/progress/constants.d.ts +3 -0
  26. package/dist/progress/constants.d.ts.map +1 -0
  27. package/dist/progress/size-policy.d.ts +4 -0
  28. package/dist/progress/size-policy.d.ts.map +1 -0
  29. package/dist/quiz/host.d.ts +9 -0
  30. package/dist/quiz/host.d.ts.map +1 -0
  31. package/dist/quiz/score.d.ts +6 -0
  32. package/dist/quiz/score.d.ts.map +1 -0
  33. package/dist/quiz/types.d.ts +8 -0
  34. package/dist/quiz/types.d.ts.map +1 -0
  35. package/dist/runtime.d.ts +12 -14
  36. package/dist/runtime.d.ts.map +1 -1
  37. package/dist/runtime.js +292 -562
  38. package/dist/scorm-api.d.ts +1 -1
  39. package/dist/scorm-api.d.ts.map +1 -1
  40. package/dist/scorm12.d.ts +3 -0
  41. package/dist/scorm12.d.ts.map +1 -0
  42. package/dist/scorm12.js +247 -0
  43. package/dist/scorm2004-api.d.ts.map +1 -1
  44. package/dist/scorm2004.d.ts +2 -0
  45. package/dist/scorm2004.d.ts.map +1 -0
  46. package/dist/scorm2004.js +155 -0
  47. package/dist/size-policy-BE1zjEBP.js +137 -0
  48. package/package.json +11 -3
  49. package/dist/progress-persist.d.ts.map +0 -1
package/dist/runtime.js CHANGED
@@ -1,660 +1,390 @@
1
- var E = Object.defineProperty;
2
- var O = (e, s, t) => s in e ? E(e, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[s] = t;
3
- var i = (e, s, t) => O(e, typeof s != "symbol" ? s + "" : s, t);
4
- const g = "lxpack_scorm12_preview";
5
- function M(e = 500) {
6
- let s = window, t = 0;
7
- for (; s && t < e; ) {
8
- try {
9
- const r = s.API;
10
- if (r && typeof r.LMSInitialize == "function")
11
- return r;
12
- } catch {
13
- }
14
- try {
15
- if (s.parent && s.parent !== s)
16
- s = s.parent;
17
- else if (s.opener && !s.opener.closed)
18
- s = s.opener;
19
- else
20
- break;
21
- } catch {
22
- break;
23
- }
24
- t++;
1
+ var F = Object.defineProperty;
2
+ var T = (t, s, e) => s in t ? F(t, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[s] = e;
3
+ var a = (t, s, e) => T(t, typeof s != "symbol" ? s + "" : s, e);
4
+ import { p as f, s as N } from "./size-policy-BE1zjEBP.js";
5
+ import { S as Y } from "./size-policy-BE1zjEBP.js";
6
+ import { createScormConnection as V } from "./scorm12.js";
7
+ import { Scorm12API as ss, Scorm12Adapter as es, Scorm12Simulator as ts, findLmsApi as ns, installScormAPI as is, trimSuspendData as rs } from "./scorm12.js";
8
+ import { createScorm2004Connection as v } from "./scorm2004.js";
9
+ const I = "v:";
10
+ function g(t) {
11
+ return `${I}${t}`;
12
+ }
13
+ function y(t, s) {
14
+ for (const [e, n] of Object.entries(t.variables ?? {})) {
15
+ const i = g(e);
16
+ s[i] === void 0 && (s[i] = n.default);
25
17
  }
18
+ }
19
+ function _(t, s) {
20
+ return t[g(s)];
21
+ }
22
+ function $(t, s, e) {
23
+ t[g(s)] = e;
24
+ }
25
+ const G = [
26
+ (t, s) => {
27
+ var e;
28
+ if ("variable" in t && ((e = t.variable) != null && e.eq)) {
29
+ const [n, i] = t.variable.eq;
30
+ return s.getVariable(n) === i;
31
+ }
32
+ return null;
33
+ },
34
+ (t, s) => {
35
+ var e;
36
+ return "assessment" in t && ((e = t.assessment) != null && e.passed) ? s.isAssessmentPassed(t.assessment.passed) : null;
37
+ },
38
+ (t, s) => {
39
+ var e;
40
+ return "interaction" in t && ((e = t.interaction) != null && e.done) ? s.isInteractionDone(t.interaction.done) : null;
41
+ },
42
+ (t, s) => "all" in t && t.all ? t.all.every((e) => d(e, s)) : null,
43
+ (t, s) => "any" in t && t.any ? t.any.some((e) => d(e, s)) : null
44
+ ];
45
+ function d(t, s) {
46
+ for (const e of G) {
47
+ const n = e(t, s);
48
+ if (n !== null)
49
+ return n;
50
+ }
51
+ return !1;
52
+ }
53
+ function k(t, s) {
54
+ const e = t.flow;
55
+ if (!(e != null && e.length)) return null;
56
+ for (const n of e)
57
+ if (d(n.when, s))
58
+ return n.goto;
26
59
  return null;
27
60
  }
28
- class A {
61
+ const R = {
62
+ maxAttempts: 1
63
+ };
64
+ class B {
29
65
  constructor(s) {
30
- i(this, "api");
31
- i(this, "initialized", !1);
32
- i(this, "terminated", !1);
33
- i(this, "lastError", "0");
34
- this.api = s;
35
- }
36
- LMSInitialize() {
37
- if (this.initialized)
38
- return this.lastError = "301", "false";
39
- const s = this.api.LMSInitialize("");
40
- return s === "true" && (this.initialized = !0, this.terminated = !1, this.lastError = "0"), s;
41
- }
42
- LMSFinish() {
43
- if (!this.initialized || this.terminated)
44
- return this.lastError = "302", "false";
45
- const s = this.api.LMSFinish("");
46
- return s === "true" && (this.terminated = !0, this.lastError = "0"), s;
47
- }
48
- LMSGetValue(s) {
49
- return !this.initialized || this.terminated ? (this.lastError = "301", "") : (this.lastError = "0", this.api.LMSGetValue(s));
50
- }
51
- LMSSetValue(s, t) {
52
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSSetValue(s, t));
53
- }
54
- LMSCommit() {
55
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.lastError = "0", this.api.LMSCommit(""));
56
- }
57
- LMSGetLastError() {
58
- return this.lastError;
59
- }
60
- LMSGetErrorString(s) {
61
- const t = s ?? this.lastError;
62
- return t === "301" ? "Not initialized" : t === "302" ? "Already terminated" : this.api.LMSGetErrorString(t);
63
- }
64
- LMSGetDiagnostic(s) {
65
- return this.api.LMSGetDiagnostic(s ?? this.lastError);
66
+ this.connection = s;
67
+ }
68
+ init() {
69
+ this.connection.LMSInitialize();
70
+ }
71
+ restoreProgress(s) {
72
+ const e = this.connection.LMSGetValue("cmi.suspend_data");
73
+ if (e) {
74
+ const { progress: i, parsed: c } = f(e, s);
75
+ if (c) return i;
76
+ const r = this.connection.LMSGetValue("cmi.core.lesson_location");
77
+ return r ? { ...s, currentLessonId: r } : s;
78
+ }
79
+ const n = this.connection.LMSGetValue("cmi.core.lesson_location");
80
+ return n ? { ...s, currentLessonId: n } : s;
66
81
  }
67
- setLessonStatus(s) {
68
- this.LMSSetValue("cmi.core.lesson_status", s);
82
+ persist(s, e) {
83
+ this.connection.setSuspendData(e), this.connection.LMSCommit();
69
84
  }
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");
85
+ setLocation(s) {
86
+ this.connection.setLessonLocation(s);
72
87
  }
73
- setSuspendData(s) {
74
- const t = d(s);
75
- this.LMSSetValue("cmi.suspend_data", t);
88
+ applyCompletion(s) {
89
+ this.connection.setScore(s.scorePercent), s.anyAssessmentFailed ? this.connection.setLessonStatus("failed") : s.ratio >= s.completionThreshold && s.allLessonsComplete && s.allAssessmentsPassed ? this.connection.setLessonStatus(
90
+ s.hasAssessments ? "passed" : "completed"
91
+ ) : s.scorePercent > 0 && this.connection.setLessonStatus("incomplete");
76
92
  }
77
- setLessonLocation(s) {
78
- this.LMSSetValue("cmi.core.lesson_location", s);
93
+ terminate() {
94
+ this.connection.LMSFinish();
79
95
  }
80
96
  }
81
- class u {
82
- constructor(s = {}) {
83
- i(this, "data", {
84
- lessonStatus: "not attempted",
85
- scoreRaw: 0,
86
- scoreMin: 0,
87
- scoreMax: 100,
88
- suspendData: "",
89
- lessonLocation: "",
90
- entry: "ab-initio",
91
- exit: "",
92
- sessionTime: "00:00:00"
93
- });
94
- i(this, "initialized", !1);
95
- i(this, "terminated", !1);
96
- i(this, "lastError", "0");
97
- i(this, "persistToStorage");
98
- this.persistToStorage = s.persistToStorage ?? !1, s.initialData && (this.data = { ...this.data, ...s.initialData }), this.persistToStorage && this.loadFromStorage();
99
- }
100
- LMSInitialize() {
101
- return this.initialized ? (this.lastError = "301", "false") : (this.initialized = !0, this.terminated = !1, this.lastError = "0", "true");
102
- }
103
- LMSFinish() {
104
- return !this.initialized || this.terminated ? (this.lastError = "302", "false") : (this.data.exit = "suspend", this.terminated = !0, this.saveToStorage(), this.lastError = "0", "true");
105
- }
106
- LMSGetValue(s) {
107
- if (!this.initialized || this.terminated)
108
- return this.lastError = "301", "";
109
- switch (this.lastError = "0", s) {
110
- case "cmi.core.lesson_status":
111
- return this.data.lessonStatus;
112
- case "cmi.core.score.raw":
113
- return String(this.data.scoreRaw);
114
- case "cmi.core.score.min":
115
- return String(this.data.scoreMin);
116
- case "cmi.core.score.max":
117
- return String(this.data.scoreMax);
118
- case "cmi.suspend_data":
119
- return this.data.suspendData;
120
- case "cmi.core.lesson_location":
121
- return this.data.lessonLocation;
122
- case "cmi.core.student_id":
123
- return "lxpack-learner";
124
- case "cmi.core.student_name":
125
- return "LXPack Learner";
126
- case "cmi.core.entry":
127
- return this.data.entry;
128
- case "cmi.core.exit":
129
- return this.data.exit;
130
- case "cmi.core.session_time":
131
- return this.data.sessionTime;
132
- default:
133
- return "";
134
- }
135
- }
136
- LMSSetValue(s, t) {
137
- if (!this.initialized || this.terminated)
138
- return this.lastError = "301", "false";
139
- switch (this.lastError = "0", s) {
140
- case "cmi.core.lesson_status":
141
- this.data.lessonStatus = t;
142
- break;
143
- case "cmi.core.score.raw":
144
- this.data.scoreRaw = Number(t);
145
- break;
146
- case "cmi.core.score.min":
147
- this.data.scoreMin = Number(t);
148
- break;
149
- case "cmi.core.score.max":
150
- this.data.scoreMax = Number(t);
151
- break;
152
- case "cmi.suspend_data":
153
- this.data.suspendData = d(t);
154
- break;
155
- case "cmi.core.lesson_location":
156
- this.data.lessonLocation = t;
157
- break;
158
- case "cmi.core.exit":
159
- this.data.exit = t;
160
- break;
161
- case "cmi.core.session_time":
162
- this.data.sessionTime = t;
163
- break;
164
- default:
165
- return "false";
97
+ class E {
98
+ constructor(s) {
99
+ this.connection = s;
100
+ }
101
+ init() {
102
+ this.connection.Initialize();
103
+ }
104
+ restoreProgress(s) {
105
+ const e = this.connection.GetValue("cmi.suspend_data");
106
+ if (e) {
107
+ const { progress: i, parsed: c } = f(e, s);
108
+ if (c) return i;
109
+ const r = this.connection.GetValue("cmi.location");
110
+ return r ? { ...s, currentLessonId: r } : s;
166
111
  }
167
- return this.saveToStorage(), "true";
168
- }
169
- LMSCommit() {
170
- return !this.initialized || this.terminated ? (this.lastError = "301", "false") : (this.saveToStorage(), this.lastError = "0", "true");
171
- }
172
- LMSGetLastError() {
173
- return this.lastError;
174
- }
175
- LMSGetErrorString(s) {
176
- const t = s ?? this.lastError;
177
- return t === "301" ? "Not initialized" : t === "302" ? "Already terminated" : "No error";
178
- }
179
- LMSGetDiagnostic() {
180
- return "";
181
- }
182
- setLessonStatus(s) {
183
- this.data.lessonStatus = s, this.saveToStorage();
184
- }
185
- setScore(s, t = 100) {
186
- this.data.scoreRaw = s, this.data.scoreMax = t, this.data.scoreMin = 0, this.saveToStorage();
112
+ const n = this.connection.GetValue("cmi.location");
113
+ return n ? { ...s, currentLessonId: n } : s;
187
114
  }
188
- setSuspendData(s) {
189
- this.data.suspendData = d(s), this.saveToStorage();
115
+ persist(s, e) {
116
+ this.connection.setSuspendData(e), this.connection.Commit();
190
117
  }
191
- setLessonLocation(s) {
192
- this.data.lessonLocation = s, this.saveToStorage();
118
+ setLocation(s) {
119
+ this.connection.setLocation(s);
193
120
  }
194
- loadFromStorage() {
195
- try {
196
- const s = localStorage.getItem(g);
197
- s && (this.data = { ...this.data, ...JSON.parse(s) });
198
- } catch {
199
- }
121
+ applyCompletion(s) {
122
+ this.connection.setScoreScaled(s.ratio), s.anyAssessmentFailed ? (this.connection.setSuccessStatus("failed"), this.connection.setCompletionStatus("completed")) : s.ratio >= s.completionThreshold && s.allLessonsComplete && s.allAssessmentsPassed ? (this.connection.setSuccessStatus("passed"), this.connection.setCompletionStatus("completed")) : s.ratio > 0 && (this.connection.setCompletionStatus("incomplete"), this.connection.setSuccessStatus("unknown"));
200
123
  }
201
- saveToStorage() {
202
- if (this.persistToStorage)
203
- try {
204
- localStorage.setItem(g, JSON.stringify(this.data));
205
- } catch {
206
- }
124
+ terminate() {
125
+ this.connection.Terminate();
207
126
  }
208
127
  }
209
- class F extends u {
210
- constructor() {
211
- super({ persistToStorage: !0 });
128
+ class D {
129
+ constructor(s) {
130
+ this.storageKey = s;
212
131
  }
213
- }
214
- function d(e) {
215
- return e.length <= 4096 ? e : (console.warn(
216
- "[lxpack] suspend_data exceeds 4096 chars; truncating"
217
- ), e.slice(0, 4096));
218
- }
219
- function L(e) {
220
- if (e === "preview")
221
- return new u({ persistToStorage: !0 });
222
- if (e === "scorm12") {
223
- const s = M();
224
- return s ? new A(s) : (console.warn(
225
- "[lxpack] No LMS SCORM API found; using in-memory simulator (progress will not persist to LMS)"
226
- ), new u({ persistToStorage: !1 }));
227
- }
228
- return new u({ persistToStorage: !1 });
229
- }
230
- function j(e = "preview") {
231
- const s = L(e);
232
- if (e === "preview") {
233
- const t = window;
234
- t.API = s;
132
+ init() {
235
133
  }
236
- return s;
237
- }
238
- const I = "0", R = "lxpack_scorm2004_preview";
239
- function N(e = 500) {
240
- let s = window, t = 0;
241
- for (; s && t < e; ) {
134
+ restoreProgress(s) {
242
135
  try {
243
- const r = s.API_1484_11;
244
- if (r && typeof r.Initialize == "function")
245
- return r;
136
+ const e = localStorage.getItem(this.storageKey);
137
+ if (e) {
138
+ const { progress: n, parsed: i } = f(e, s);
139
+ if (i) return n;
140
+ }
246
141
  } catch {
247
142
  }
143
+ return s;
144
+ }
145
+ persist(s, e) {
248
146
  try {
249
- if (s.parent && s.parent !== s)
250
- s = s.parent;
251
- else if (s.opener && !s.opener.closed)
252
- s = s.opener;
253
- else
254
- break;
147
+ localStorage.setItem(this.storageKey, e);
255
148
  } catch {
256
- break;
257
149
  }
258
- t++;
259
- }
260
- return null;
261
- }
262
- class T {
263
- constructor(s) {
264
- i(this, "initialized", !1);
265
- i(this, "terminated", !1);
266
- i(this, "data", {
267
- "cmi.completion_status": "unknown",
268
- "cmi.success_status": "unknown",
269
- "cmi.score.scaled": "",
270
- "cmi.suspend_data": "",
271
- "cmi.location": "",
272
- "cmi.entry": "ab-initio"
273
- });
274
- i(this, "persistToStorage");
275
- if (this.persistToStorage = (s == null ? void 0 : s.persistToStorage) ?? !1, this.persistToStorage)
276
- try {
277
- const t = localStorage.getItem(R);
278
- t && (this.data = { ...this.data, ...JSON.parse(t) });
279
- } catch {
280
- }
281
- }
282
- save() {
283
- if (this.persistToStorage)
284
- try {
285
- localStorage.setItem(R, JSON.stringify(this.data));
286
- } catch {
287
- }
288
- }
289
- Initialize() {
290
- return this.initialized ? "false" : (this.initialized = !0, this.terminated = !1, "true");
291
- }
292
- Terminate() {
293
- return !this.initialized || this.terminated ? "false" : (this.terminated = !0, this.save(), "true");
294
- }
295
- GetValue(s) {
296
- return !this.initialized || this.terminated ? "" : this.data[s] ?? "";
297
- }
298
- SetValue(s, t) {
299
- return !this.initialized || this.terminated ? "false" : (this.data[s] = t, this.save(), "true");
300
- }
301
- Commit() {
302
- return !this.initialized || this.terminated ? "false" : (this.save(), "true");
303
- }
304
- GetLastError() {
305
- return I;
306
- }
307
- GetErrorString() {
308
- return "No error";
309
- }
310
- GetDiagnostic() {
311
- return "";
312
- }
313
- setSuspendData(s) {
314
- this.data["cmi.suspend_data"] = s;
315
150
  }
316
151
  setLocation(s) {
317
- this.data["cmi.location"] = s;
318
- }
319
- setCompletionStatus(s) {
320
- this.data["cmi.completion_status"] = s;
321
152
  }
322
- setSuccessStatus(s) {
323
- this.data["cmi.success_status"] = s;
153
+ applyCompletion(s) {
324
154
  }
325
- setScoreScaled(s) {
326
- this.data["cmi.score.scaled"] = String(Math.max(0, Math.min(1, s)));
155
+ terminate() {
327
156
  }
328
157
  }
329
- class D {
330
- constructor(s) {
331
- i(this, "api");
332
- this.api = s;
333
- }
334
- Initialize() {
335
- return this.api.Initialize("");
336
- }
337
- Terminate() {
338
- return this.api.Terminate("");
339
- }
340
- GetValue(s) {
341
- return this.api.GetValue(s);
158
+ function K(t, s) {
159
+ const e = `${t}::${s}`;
160
+ let n = 0;
161
+ for (let i = 0; i < e.length; i++)
162
+ n = (n << 5) - n + e.charCodeAt(i), n |= 0;
163
+ return `lxpack_progress_${Math.abs(n)}`;
164
+ }
165
+ function O(t, s) {
166
+ return t === "scorm12" ? new B(V("scorm12")) : t === "scorm2004" ? new E(v("scorm2004")) : t === "preview" || t === "standalone" ? new D(s) : new D(s);
167
+ }
168
+ function A(t, s) {
169
+ const e = t[`assessment_attempts_${s}`];
170
+ let n = 0;
171
+ return typeof e == "number" ? n = e : typeof e == "string" && e !== "" && !Number.isNaN(Number(e)) && (n = Number(e)), !Number.isFinite(n) || n < 0 ? 0 : Math.floor(n);
172
+ }
173
+ function z(t, s) {
174
+ const e = A(t, s) + 1;
175
+ return t[`assessment_attempts_${s}`] = e, e;
176
+ }
177
+ class j {
178
+ constructor(s, e, n) {
179
+ a(this, "progress");
180
+ a(this, "passedAssessments", /* @__PURE__ */ new Set());
181
+ this.manifest = e, this.defaultPassingScores = n, this.progress = s, this.syncPassedAssessments();
342
182
  }
343
- SetValue(s, t) {
344
- return this.api.SetValue(s, t);
183
+ getAssessmentPassingScore(s) {
184
+ const e = this.progress.suspendData[`assessment_passing_${s}`];
185
+ return typeof e == "number" ? e : s in this.defaultPassingScores ? this.defaultPassingScores[s] : 0.7;
345
186
  }
346
- Commit() {
347
- return this.api.Commit("");
187
+ syncPassedAssessments() {
188
+ this.passedAssessments.clear();
189
+ for (const [s, e] of Object.entries(this.progress.assessmentScores))
190
+ e >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
348
191
  }
349
- GetLastError() {
350
- return this.api.GetLastError();
192
+ applyAssessmentResult(s, e, n) {
193
+ this.progress.assessmentScores[s] = e, this.progress.suspendData[`assessment_passing_${s}`] = n, e >= n ? this.passedAssessments.add(s) : this.passedAssessments.delete(s);
351
194
  }
352
- GetErrorString(s) {
353
- return this.api.GetErrorString(s);
195
+ recordAssessmentAttempt(s) {
196
+ return z(this.progress.suspendData, s);
354
197
  }
355
- GetDiagnostic(s) {
356
- return this.api.GetDiagnostic(s);
198
+ getAssessmentAttemptCount(s) {
199
+ return A(this.progress.suspendData, s);
357
200
  }
358
- setSuspendData(s) {
359
- this.api.SetValue("cmi.suspend_data", s);
201
+ completeLesson(s) {
202
+ new Set(this.manifest.lessons.map((n) => n.id)).has(s) && (this.progress.completedLessons.includes(s) || this.progress.completedLessons.push(s));
360
203
  }
361
- setLocation(s) {
362
- this.api.SetValue("cmi.location", s);
204
+ setCurrentLesson(s) {
205
+ this.progress.currentLessonId = s;
363
206
  }
364
- setCompletionStatus(s) {
365
- this.api.SetValue("cmi.completion_status", s);
207
+ isLessonComplete(s) {
208
+ return this.progress.completedLessons.includes(s);
366
209
  }
367
- setSuccessStatus(s) {
368
- this.api.SetValue("cmi.success_status", s);
210
+ isAssessmentPassed(s) {
211
+ return this.passedAssessments.has(s);
369
212
  }
370
- setScoreScaled(s) {
371
- this.api.SetValue("cmi.score.scaled", String(Math.max(0, Math.min(1, s))));
213
+ getAssessmentPassingScoreForTrack(s) {
214
+ return this.getAssessmentPassingScore(s);
372
215
  }
373
216
  }
374
- function C(e) {
375
- const s = N();
376
- return s ? new D(s) : (console.warn(
377
- "[lxpack] No SCORM 2004 API found; using in-memory simulator"
378
- ), new T({ persistToStorage: !1 }));
379
- }
380
- function p(e) {
381
- var t;
382
- const s = {
383
- c: e.currentLessonId,
384
- l: e.completedLessons,
385
- a: e.assessmentScores,
386
- s: e.suspendData
387
- };
388
- return (t = s.l) != null && t.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;
389
- }
390
- function y(e, s) {
217
+ function x(t, s) {
218
+ var S, L, C, P;
219
+ const { manifest: e, completionThreshold: n, passedAssessments: i } = s, c = e.lessons.length, r = ((S = e.assessments) == null ? void 0 : S.length) ?? 0, m = c + r, l = t.completedLessons.filter(
220
+ (o) => e.lessons.some((h) => h.id === o)
221
+ ).length, u = m === 0 ? 0 : (l + i.size) / m, p = e.lessons.every(
222
+ (o) => t.completedLessons.includes(o.id)
223
+ ), M = !((L = e.assessments) != null && L.length) || e.assessments.every((o) => i.has(o.id)), w = (C = e.assessments) == null ? void 0 : C.some((o) => {
224
+ var b;
225
+ if (!(o.id in t.assessmentScores) || i.has(o.id)) return !1;
226
+ const h = ((b = s.assessmentConfigs[o.id]) == null ? void 0 : b.maxAttempts) ?? 1;
227
+ return A(t.suspendData, o.id) >= h;
228
+ });
391
229
  return {
392
- currentLessonId: e.c ?? s.currentLessonId,
393
- completedLessons: Array.isArray(e.l) ? e.l : s.completedLessons,
394
- assessmentScores: e.a && typeof e.a == "object" ? e.a : s.assessmentScores,
395
- suspendData: e.s && typeof e.s == "object" ? e.s : s.suspendData
230
+ ratio: u,
231
+ scorePercent: Math.round(u * 100),
232
+ allLessonsComplete: p,
233
+ allAssessmentsPassed: M,
234
+ anyAssessmentFailed: !!w,
235
+ hasAssessments: (((P = e.assessments) == null ? void 0 : P.length) ?? 0) > 0,
236
+ completionThreshold: n
396
237
  };
397
238
  }
398
- function b(e) {
399
- const s = { ...e.suspendData }, t = Object.keys(s).filter((r) => r.startsWith("interaction_")).sort();
400
- for (const r of t)
401
- if (delete s[r], JSON.stringify(p({ ...e, suspendData: s })).length <= 4096)
402
- return { ...e, suspendData: s };
403
- return { ...e, suspendData: s };
404
- }
405
- function P(e) {
406
- let s = JSON.stringify(p(e));
407
- if (s.length <= 4096)
408
- return s;
409
- const t = b(e);
410
- return s = JSON.stringify(p(t)), s.length <= 4096 ? s : (console.warn(
411
- "[lxpack] suspend_data exceeds 4096 chars; truncating"
412
- ), s.slice(0, 4096));
413
- }
414
- function w(e) {
415
- if (!e || typeof e != "object") return !1;
416
- const s = e;
417
- return "currentLessonId" in s || "completedLessons" in s || "assessmentScores" in s;
418
- }
419
- function V(e, s) {
420
- return {
421
- currentLessonId: e.currentLessonId ?? s.currentLessonId,
422
- completedLessons: Array.isArray(e.completedLessons) ? e.completedLessons : s.completedLessons,
423
- assessmentScores: e.assessmentScores && typeof e.assessmentScores == "object" ? e.assessmentScores : s.assessmentScores,
424
- suspendData: e.suspendData && typeof e.suspendData == "object" ? e.suspendData : s.suspendData
425
- };
426
- }
427
- function m(e, s) {
428
- try {
429
- const t = JSON.parse(e);
430
- return w(t) ? {
431
- progress: V(t, s),
432
- parsed: !0
433
- } : { progress: y(t, s), parsed: !0 };
434
- } catch {
435
- return { progress: s, parsed: !1 };
436
- }
437
- }
438
- const z = "v:";
439
- function f(e) {
440
- return `${z}${e}`;
441
- }
442
- function k(e, s) {
443
- for (const [t, r] of Object.entries(e.variables ?? {})) {
444
- const a = f(t);
445
- s[a] === void 0 && (s[a] = r.default);
446
- }
447
- }
448
- function _(e, s) {
449
- return e[f(s)];
450
- }
451
- function x(e, s, t) {
452
- e[f(s)] = t;
453
- }
454
- function S(e, s) {
455
- var t, r, a;
456
- if ("variable" in e && ((t = e.variable) != null && t.eq)) {
457
- const [n, o] = e.variable.eq;
458
- return s.getVariable(n) === o;
459
- }
460
- return "assessment" in e && ((r = e.assessment) != null && r.passed) ? s.isAssessmentPassed(e.assessment.passed) : "interaction" in e && ((a = e.interaction) != null && a.done) ? s.isInteractionDone(e.interaction.done) : "all" in e && e.all ? e.all.every((n) => S(n, s)) : "any" in e && e.any ? e.any.some((n) => S(n, s)) : !1;
461
- }
462
- function G(e, s) {
463
- const t = e.flow;
464
- if (!(t != null && t.length)) return null;
465
- for (const r of t)
466
- if (S(r.when, s))
467
- return r.goto;
468
- return null;
469
- }
470
- function $(e) {
471
- const s = `${e.title}::${e.version}`;
472
- let t = 0;
473
- for (let r = 0; r < s.length; r++)
474
- t = (t << 5) - t + s.charCodeAt(r), t |= 0;
475
- return `lxpack_progress_${Math.abs(t)}`;
476
- }
477
- class U {
239
+ class J {
478
240
  constructor(s) {
479
- i(this, "manifest");
480
- i(this, "progress");
481
- i(this, "scorm", null);
482
- i(this, "scorm2004", null);
483
- i(this, "completionThreshold");
484
- i(this, "storageKey");
485
- i(this, "mode");
486
- i(this, "passedAssessments", /* @__PURE__ */ new Set());
487
- i(this, "defaultPassingScores");
488
- i(this, "terminated", !1);
489
- var r, a, n;
490
- 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 = $(s.manifest), this.defaultPassingScores = {};
491
- for (const [o, c] of Object.entries(s.assessments ?? {}))
492
- this.defaultPassingScores[o] = c.passingScore;
493
- const t = s.activityId ?? ((n = s.manifest.lessons[0]) == null ? void 0 : n.id) ?? "";
494
- this.progress = s.progress ?? {
495
- currentLessonId: t,
241
+ a(this, "manifest");
242
+ a(this, "state");
243
+ a(this, "bridge");
244
+ a(this, "completionThreshold");
245
+ a(this, "assessmentConfigs");
246
+ a(this, "defaultPassingScores");
247
+ a(this, "terminated", !1);
248
+ var r, m, l;
249
+ this.manifest = s.manifest, this.completionThreshold = ((m = (r = s.manifest.tracking) == null ? void 0 : r.completion) == null ? void 0 : m.threshold) ?? 0.9;
250
+ const e = K(
251
+ s.manifest.title,
252
+ s.manifest.version
253
+ );
254
+ this.bridge = O(s.mode, e), this.defaultPassingScores = {};
255
+ for (const [u, p] of Object.entries(s.assessments ?? {}))
256
+ this.defaultPassingScores[u] = p.passingScore;
257
+ this.assessmentConfigs = s.assessmentConfigs ?? {};
258
+ const n = s.activityId ?? ((l = s.manifest.lessons[0]) == null ? void 0 : l.id) ?? "", i = s.progress ?? {
259
+ currentLessonId: n,
496
260
  completedLessons: [],
497
261
  assessmentScores: {},
498
262
  suspendData: {}
499
- }, k(this.manifest, this.progress.suspendData), s.mode === "scorm12" ? (this.scorm = L("scorm12"), this.scorm.LMSInitialize(), this.restoreScormProgress()) : s.mode === "scorm2004" ? (this.scorm2004 = C(), this.scorm2004.Initialize(), this.restoreScorm2004Progress()) : (s.mode === "preview" || s.mode === "standalone") && this.restoreLocalProgress(), s.activityId && (this.progress.currentLessonId = s.activityId);
500
- }
501
- restoreLessonLocation() {
502
- if (this.scorm) {
503
- const s = this.scorm.LMSGetValue("cmi.core.lesson_location");
504
- s && (this.progress.currentLessonId = s);
505
- return;
506
- }
507
- if (this.scorm2004) {
508
- const s = this.scorm2004.GetValue("cmi.location");
509
- s && (this.progress.currentLessonId = s);
510
- }
511
- }
512
- restoreScorm2004Progress() {
513
- if (!this.scorm2004) return;
514
- const s = this.scorm2004.GetValue("cmi.suspend_data");
515
- if (s) {
516
- const { progress: t, parsed: r } = m(s, this.progress);
517
- r ? this.progress = t : this.restoreLessonLocation();
518
- } else
519
- this.restoreLessonLocation();
520
- this.syncPassedAssessments();
521
- }
522
- restoreScormProgress() {
523
- if (!this.scorm) return;
524
- const s = this.scorm.LMSGetValue("cmi.suspend_data");
525
- if (s) {
526
- const { progress: t, parsed: r } = m(s, this.progress);
527
- r ? this.progress = t : this.restoreLessonLocation();
528
- } else
529
- this.restoreLessonLocation();
530
- this.syncPassedAssessments();
531
- }
532
- restoreLocalProgress() {
533
- try {
534
- const s = localStorage.getItem(this.storageKey);
535
- if (s) {
536
- const { progress: t, parsed: r } = m(s, this.progress);
537
- r && (this.progress = t);
538
- }
539
- } catch {
540
- }
541
- this.syncPassedAssessments();
542
- }
543
- syncPassedAssessments() {
544
- this.passedAssessments.clear();
545
- for (const [s, t] of Object.entries(this.progress.assessmentScores))
546
- t >= this.getAssessmentPassingScore(s) && this.passedAssessments.add(s);
547
- }
548
- getAssessmentPassingScore(s) {
549
- const t = this.progress.suspendData[`assessment_passing_${s}`];
550
- return typeof t == "number" ? t : s in this.defaultPassingScores ? this.defaultPassingScores[s] : 0.7;
263
+ };
264
+ y(this.manifest, i.suspendData), this.bridge.init();
265
+ const c = this.bridge.restoreProgress(i);
266
+ this.state = new j(
267
+ c,
268
+ this.manifest,
269
+ this.defaultPassingScores
270
+ ), y(this.manifest, this.state.progress.suspendData), this.state.syncPassedAssessments(), s.activityId && this.state.setCurrentLesson(s.activityId);
551
271
  }
552
272
  getAPI() {
553
273
  return {
554
274
  track: (s) => this.track(s),
555
275
  completeLesson: (s) => this.completeLesson(s),
556
- getProgress: () => ({
557
- ...this.progress,
558
- completedLessons: [...this.progress.completedLessons],
559
- assessmentScores: { ...this.progress.assessmentScores },
560
- suspendData: { ...this.progress.suspendData }
561
- }),
562
- setVariable: (s, t) => {
563
- this.manifest.variables && s in this.manifest.variables ? x(this.progress.suspendData, s, t) : this.progress.suspendData[s] = t, this.persist();
276
+ getProgress: () => this.getProgress(),
277
+ setVariable: (s, e) => {
278
+ this.manifest.variables && s in this.manifest.variables ? $(this.state.progress.suspendData, s, e) : this.state.progress.suspendData[s] = e, this.persist();
564
279
  },
565
- getVariable: (s) => this.manifest.variables && s in this.manifest.variables ? _(this.progress.suspendData, s) : this.progress.suspendData[s],
566
- submitAssessment: (s, t, r) => this.submitAssessment(s, t, r)
280
+ getVariable: (s) => this.manifest.variables && s in this.manifest.variables ? _(this.state.progress.suspendData, s) : this.state.progress.suspendData[s],
281
+ submitAssessment: (s, e, n) => this.submitAssessment(s, e, n)
567
282
  };
568
283
  }
569
- track(s, t) {
570
- var r, a;
571
- 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) {
572
- const n = typeof ((r = s.data) == null ? void 0 : r.passingScore) == "number" ? Number(s.data.passingScore) : this.getAssessmentPassingScore(s.id), o = ((a = s.data) == null ? void 0 : a.score) != null ? Number(s.data.score) : void 0;
573
- if (o != null && !Number.isNaN(o)) {
574
- this.applyAssessmentResult(s.id, o, n), (t == null ? void 0 : t.persist) !== !1 && (this.updateCompletion(), this.persist());
284
+ track(s, e) {
285
+ var n, i;
286
+ if (s.type === "interaction" && s.id && (this.state.progress.suspendData[`interaction_${s.id}`] = s.data ?? !0, console.debug("[lxpack] interaction:", s.id, s.data)), s.type === "assessment" && s.id) {
287
+ const c = typeof ((n = s.data) == null ? void 0 : n.passingScore) == "number" ? Number(s.data.passingScore) : this.state.getAssessmentPassingScoreForTrack(s.id), r = ((i = s.data) == null ? void 0 : i.score) != null ? Number(s.data.score) : void 0;
288
+ if (r != null && !Number.isNaN(r)) {
289
+ this.submitAssessment(s.id, r, c);
575
290
  return;
576
291
  }
577
292
  }
578
- (t == null ? void 0 : t.persist) !== !1 && this.persist();
293
+ (e == null ? void 0 : e.persist) !== !1 && this.persist();
294
+ }
295
+ recordAssessmentAttempt(s) {
296
+ return this.state.recordAssessmentAttempt(s);
297
+ }
298
+ getAssessmentAttemptCount(s) {
299
+ return this.state.getAssessmentAttemptCount(s);
579
300
  }
580
- applyAssessmentResult(s, t, r) {
581
- this.progress.assessmentScores[s] = t, this.progress.suspendData[`assessment_passing_${s}`] = r, t >= r ? this.passedAssessments.add(s) : this.passedAssessments.delete(s);
301
+ submitAssessment(s, e, n) {
302
+ this.canAcceptAssessmentSubmission(s) && (this.state.recordAssessmentAttempt(s), this.state.applyAssessmentResult(s, e, n), this.updateCompletion(), this.persist());
582
303
  }
583
- submitAssessment(s, t, r) {
584
- this.applyAssessmentResult(s, t, r), this.updateCompletion(), this.persist();
304
+ getMaxAttempts(s) {
305
+ const e = this.assessmentConfigs[s];
306
+ return (e == null ? void 0 : e.maxAttempts) ?? R.maxAttempts;
307
+ }
308
+ canAcceptAssessmentSubmission(s) {
309
+ if (this.isAssessmentPassed(s))
310
+ return !1;
311
+ const e = this.getMaxAttempts(s);
312
+ return this.getAssessmentAttemptCount(s) < e;
585
313
  }
586
314
  completeLesson(s) {
587
- 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());
315
+ this.state.completeLesson(s), this.updateCompletion(), this.persist();
588
316
  }
589
317
  setCurrentLesson(s) {
590
- this.progress.currentLessonId = s, this.scorm && this.scorm.setLessonLocation(s), this.scorm2004 && this.scorm2004.setLocation(s), this.persist();
318
+ this.state.setCurrentLesson(s), this.bridge.setLocation(s), this.persist();
591
319
  }
592
320
  getProgress() {
593
- return this.getAPI().getProgress();
321
+ const s = this.state.progress;
322
+ return {
323
+ ...s,
324
+ completedLessons: [...s.completedLessons],
325
+ assessmentScores: { ...s.assessmentScores },
326
+ suspendData: { ...s.suspendData }
327
+ };
594
328
  }
595
329
  getManifest() {
596
330
  return this.manifest;
597
331
  }
598
332
  isLessonComplete(s) {
599
- return this.progress.completedLessons.includes(s);
333
+ return this.state.isLessonComplete(s);
600
334
  }
601
335
  isAssessmentPassed(s) {
602
- return this.passedAssessments.has(s);
336
+ return this.state.isAssessmentPassed(s);
603
337
  }
604
338
  getTotalUnits() {
605
- var t;
606
- const s = ((t = this.manifest.assessments) == null ? void 0 : t.length) ?? 0;
339
+ var e;
340
+ const s = ((e = this.manifest.assessments) == null ? void 0 : e.length) ?? 0;
607
341
  return this.manifest.lessons.length + s;
608
342
  }
609
343
  getCompletionRatio() {
610
- var o;
611
- if (this.getTotalUnits() === 0) return 0;
612
- const t = this.manifest.lessons.length, r = ((o = this.manifest.assessments) == null ? void 0 : o.length) ?? 0, a = this.progress.completedLessons.filter(
613
- (c) => this.manifest.lessons.some((h) => h.id === c)
614
- ).length, n = this.passedAssessments.size;
615
- return (a + n) / (t + r);
344
+ return x(this.state.progress, {
345
+ manifest: this.manifest,
346
+ completionThreshold: this.completionThreshold,
347
+ assessmentConfigs: this.assessmentConfigs,
348
+ defaultPassingScores: this.defaultPassingScores,
349
+ passedAssessments: this.state.passedAssessments
350
+ }).ratio;
616
351
  }
617
352
  updateCompletion() {
618
- var o, c, h;
619
- const s = this.getCompletionRatio(), t = Math.round(s * 100), r = this.manifest.lessons.every(
620
- (l) => this.progress.completedLessons.includes(l.id)
621
- ), a = !((o = this.manifest.assessments) != null && o.length) || this.manifest.assessments.every((l) => this.passedAssessments.has(l.id)), n = (c = this.manifest.assessments) == null ? void 0 : c.some(
622
- (l) => l.id in this.progress.assessmentScores && !this.passedAssessments.has(l.id)
623
- );
624
- this.scorm && (this.scorm.setScore(t), n ? this.scorm.setLessonStatus("failed") : s >= this.completionThreshold && r && a ? this.scorm.setLessonStatus(
625
- (((h = this.manifest.assessments) == null ? void 0 : h.length) ?? 0) > 0 ? "passed" : "completed"
626
- ) : t > 0 && this.scorm.setLessonStatus("incomplete")), this.scorm2004 && (this.scorm2004.setScoreScaled(s), n ? (this.scorm2004.setSuccessStatus("failed"), this.scorm2004.setCompletionStatus("completed")) : s >= this.completionThreshold && r && a ? (this.scorm2004.setSuccessStatus("passed"), this.scorm2004.setCompletionStatus("completed")) : s > 0 && (this.scorm2004.setCompletionStatus("incomplete"), this.scorm2004.setSuccessStatus("unknown")));
353
+ const s = x(this.state.progress, {
354
+ manifest: this.manifest,
355
+ completionThreshold: this.completionThreshold,
356
+ assessmentConfigs: this.assessmentConfigs,
357
+ defaultPassingScores: this.defaultPassingScores,
358
+ passedAssessments: this.state.passedAssessments
359
+ });
360
+ this.bridge.applyCompletion(s);
627
361
  }
628
362
  persist() {
629
- const s = P(this.progress);
630
- if (this.mode === "preview" || this.mode === "standalone")
631
- try {
632
- localStorage.setItem(this.storageKey, s);
633
- } catch {
634
- }
635
- this.scorm && (this.scorm.setSuspendData(s), this.scorm.LMSCommit()), this.scorm2004 && (this.scorm2004.setSuspendData(s), this.scorm2004.Commit());
363
+ const s = N(this.state.progress);
364
+ this.bridge.persist(this.state.progress, s);
636
365
  }
637
366
  getFlowContext() {
638
367
  return {
639
- getVariable: (s) => _(this.progress.suspendData, s),
368
+ getVariable: (s) => _(this.state.progress.suspendData, s),
640
369
  isAssessmentPassed: (s) => this.isAssessmentPassed(s),
641
- isInteractionDone: (s) => this.progress.suspendData[`interaction_${s}`] !== void 0
370
+ isInteractionDone: (s) => this.state.progress.suspendData[`interaction_${s}`] !== void 0
642
371
  };
643
372
  }
644
373
  resolveFlowNavigation() {
645
- return G(this.manifest, this.getFlowContext());
374
+ return k(this.manifest, this.getFlowContext());
646
375
  }
647
376
  terminate() {
648
- this.terminated || (this.terminated = !0, this.scorm && this.scorm.LMSFinish(), this.scorm2004 && this.scorm2004.Terminate());
377
+ this.terminated || (this.terminated = !0, this.bridge.terminate());
649
378
  }
650
379
  }
651
380
  export {
652
- U as LxpackRuntime,
653
- F as Scorm12API,
654
- A as Scorm12Adapter,
655
- u as Scorm12Simulator,
656
- L as createScormConnection,
657
- M as findLmsApi,
658
- j as installScormAPI,
659
- d as trimSuspendData
381
+ J as LxpackRuntime,
382
+ Y as SCORM_SUSPEND_DATA_MAX,
383
+ ss as Scorm12API,
384
+ es as Scorm12Adapter,
385
+ ts as Scorm12Simulator,
386
+ V as createScormConnection,
387
+ ns as findLmsApi,
388
+ is as installScormAPI,
389
+ rs as trimSuspendData
660
390
  };