scorm-again 2.6.3 → 2.6.5

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 (73) hide show
  1. package/.claude/.claude/settings.local.json +13 -0
  2. package/.claude/settings.local.json +33 -0
  3. package/dist/aicc.js +38 -22
  4. package/dist/aicc.js.map +1 -1
  5. package/dist/aicc.min.js +1 -1
  6. package/dist/aicc.min.js.map +1 -1
  7. package/dist/esm/aicc.js +38 -22
  8. package/dist/esm/aicc.js.map +1 -1
  9. package/dist/esm/aicc.min.js +1 -1
  10. package/dist/esm/aicc.min.js.map +1 -1
  11. package/dist/esm/scorm-again.js +79 -47
  12. package/dist/esm/scorm-again.js.map +1 -1
  13. package/dist/esm/scorm-again.min.js +1 -1
  14. package/dist/esm/scorm-again.min.js.map +1 -1
  15. package/dist/esm/scorm12.js +35 -20
  16. package/dist/esm/scorm12.js.map +1 -1
  17. package/dist/esm/scorm12.min.js +1 -1
  18. package/dist/esm/scorm12.min.js.map +1 -1
  19. package/dist/esm/scorm2004.js +69 -41
  20. package/dist/esm/scorm2004.js.map +1 -1
  21. package/dist/esm/scorm2004.min.js +1 -1
  22. package/dist/esm/scorm2004.min.js.map +1 -1
  23. package/dist/scorm-again.js +79 -47
  24. package/dist/scorm-again.js.map +1 -1
  25. package/dist/scorm-again.min.js +1 -1
  26. package/dist/scorm-again.min.js.map +1 -1
  27. package/dist/scorm12.js +35 -20
  28. package/dist/scorm12.js.map +1 -1
  29. package/dist/scorm12.min.js +1 -1
  30. package/dist/scorm12.min.js.map +1 -1
  31. package/dist/scorm2004.js +69 -41
  32. package/dist/scorm2004.js.map +1 -1
  33. package/dist/scorm2004.min.js +1 -1
  34. package/dist/scorm2004.min.js.map +1 -1
  35. package/dist/types/cmi/scorm2004/adl.d.ts +8 -2
  36. package/dist/types/cmi/scorm2004/cmi.d.ts +1 -1
  37. package/package.json +1 -1
  38. package/src/BaseAPI.ts +56 -33
  39. package/src/Scorm12API.ts +3 -2
  40. package/src/Scorm2004API.ts +31 -24
  41. package/src/cmi/aicc/attempts.ts +3 -3
  42. package/src/cmi/aicc/core.ts +30 -12
  43. package/src/cmi/aicc/evaluation.ts +1 -1
  44. package/src/cmi/aicc/student_data.ts +1 -1
  45. package/src/cmi/aicc/student_demographics.ts +13 -13
  46. package/src/cmi/aicc/student_preferences.ts +1 -1
  47. package/src/cmi/aicc/tries.ts +3 -3
  48. package/src/cmi/aicc/validation.ts +1 -1
  49. package/src/cmi/common/array.ts +1 -1
  50. package/src/cmi/common/score.ts +3 -3
  51. package/src/cmi/common/validation.ts +2 -2
  52. package/src/cmi/scorm12/cmi.ts +12 -4
  53. package/src/cmi/scorm12/interactions.ts +27 -11
  54. package/src/cmi/scorm12/objectives.ts +4 -4
  55. package/src/cmi/scorm12/student_data.ts +12 -4
  56. package/src/cmi/scorm12/student_preference.ts +3 -1
  57. package/src/cmi/scorm12/validation.ts +5 -3
  58. package/src/cmi/scorm2004/adl.ts +39 -22
  59. package/src/cmi/scorm2004/cmi.ts +47 -17
  60. package/src/cmi/scorm2004/comments.ts +11 -5
  61. package/src/cmi/scorm2004/interactions.ts +25 -23
  62. package/src/cmi/scorm2004/learner_preference.ts +3 -1
  63. package/src/cmi/scorm2004/objectives.ts +5 -5
  64. package/src/cmi/scorm2004/score.ts +3 -3
  65. package/src/cmi/scorm2004/validation.ts +2 -2
  66. package/src/exceptions/aicc_exceptions.ts +4 -4
  67. package/src/exceptions/scorm12_exceptions.ts +4 -4
  68. package/src/exceptions/scorm2004_exceptions.ts +4 -4
  69. package/src/utilities.ts +3 -3
  70. package/test/BaseAPI.requestHandler.spec.ts +126 -0
  71. package/test/Scorm12API.spec.ts +45 -0
  72. package/test/Scorm2004API.spec.ts +43 -0
  73. package/tsconfig.json +1 -0
@@ -107,7 +107,9 @@ export class CMI extends BaseRootCMI {
107
107
  * @private
108
108
  */
109
109
  set _version(_version: string) {
110
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
110
+ throw new Scorm2004ValidationError(
111
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
112
+ );
111
113
  }
112
114
 
113
115
  /**
@@ -121,11 +123,13 @@ export class CMI extends BaseRootCMI {
121
123
 
122
124
  /**
123
125
  * Setter for __children. Just throws an error.
124
- * @param {number} _children
126
+ * @param {string} _children
125
127
  * @private
126
128
  */
127
- set _children(_children: number) {
128
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
129
+ set _children(_children: string) {
130
+ throw new Scorm2004ValidationError(
131
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
132
+ );
129
133
  }
130
134
 
131
135
  /**
@@ -160,7 +164,9 @@ export class CMI extends BaseRootCMI {
160
164
  */
161
165
  set completion_threshold(completion_threshold: string) {
162
166
  if (this.initialized) {
163
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
167
+ throw new Scorm2004ValidationError(
168
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
169
+ );
164
170
  } else {
165
171
  this._completion_threshold = completion_threshold;
166
172
  }
@@ -180,7 +186,9 @@ export class CMI extends BaseRootCMI {
180
186
  */
181
187
  set credit(credit: string) {
182
188
  if (this.initialized) {
183
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
189
+ throw new Scorm2004ValidationError(
190
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
191
+ );
184
192
  } else {
185
193
  this._credit = credit;
186
194
  }
@@ -200,7 +208,9 @@ export class CMI extends BaseRootCMI {
200
208
  */
201
209
  set entry(entry: string) {
202
210
  if (this.initialized) {
203
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
211
+ throw new Scorm2004ValidationError(
212
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
213
+ );
204
214
  } else {
205
215
  this._entry = entry;
206
216
  }
@@ -212,7 +222,9 @@ export class CMI extends BaseRootCMI {
212
222
  */
213
223
  get exit(): string {
214
224
  if (!this.jsonString) {
215
- throw new Scorm2004ValidationError(scorm2004_errors.WRITE_ONLY_ELEMENT);
225
+ throw new Scorm2004ValidationError(
226
+ scorm2004_errors.WRITE_ONLY_ELEMENT as number,
227
+ );
216
228
  }
217
229
  return this._exit;
218
230
  }
@@ -241,7 +253,9 @@ export class CMI extends BaseRootCMI {
241
253
  */
242
254
  set launch_data(launch_data: string) {
243
255
  if (this.initialized) {
244
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
256
+ throw new Scorm2004ValidationError(
257
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
258
+ );
245
259
  } else {
246
260
  this._launch_data = launch_data;
247
261
  }
@@ -261,7 +275,9 @@ export class CMI extends BaseRootCMI {
261
275
  */
262
276
  set learner_id(learner_id: string) {
263
277
  if (this.initialized) {
264
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
278
+ throw new Scorm2004ValidationError(
279
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
280
+ );
265
281
  } else {
266
282
  this._learner_id = learner_id;
267
283
  }
@@ -281,7 +297,9 @@ export class CMI extends BaseRootCMI {
281
297
  */
282
298
  set learner_name(learner_name: string) {
283
299
  if (this.initialized) {
284
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
300
+ throw new Scorm2004ValidationError(
301
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
302
+ );
285
303
  } else {
286
304
  this._learner_name = learner_name;
287
305
  }
@@ -319,7 +337,9 @@ export class CMI extends BaseRootCMI {
319
337
  */
320
338
  set max_time_allowed(max_time_allowed: string) {
321
339
  if (this.initialized) {
322
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
340
+ throw new Scorm2004ValidationError(
341
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
342
+ );
323
343
  } else {
324
344
  this._max_time_allowed = max_time_allowed;
325
345
  }
@@ -339,7 +359,9 @@ export class CMI extends BaseRootCMI {
339
359
  */
340
360
  set mode(mode: string) {
341
361
  if (this.initialized) {
342
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
362
+ throw new Scorm2004ValidationError(
363
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
364
+ );
343
365
  } else {
344
366
  this._mode = mode;
345
367
  }
@@ -380,7 +402,9 @@ export class CMI extends BaseRootCMI {
380
402
  */
381
403
  set scaled_passing_score(scaled_passing_score: string) {
382
404
  if (this.initialized) {
383
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
405
+ throw new Scorm2004ValidationError(
406
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
407
+ );
384
408
  } else {
385
409
  this._scaled_passing_score = scaled_passing_score;
386
410
  }
@@ -392,7 +416,9 @@ export class CMI extends BaseRootCMI {
392
416
  */
393
417
  get session_time(): string {
394
418
  if (!this.jsonString) {
395
- throw new Scorm2004ValidationError(scorm2004_errors.WRITE_ONLY_ELEMENT);
419
+ throw new Scorm2004ValidationError(
420
+ scorm2004_errors.WRITE_ONLY_ELEMENT as number,
421
+ );
396
422
  }
397
423
  return this._session_time;
398
424
  }
@@ -459,7 +485,9 @@ export class CMI extends BaseRootCMI {
459
485
  */
460
486
  set time_limit_action(time_limit_action: string) {
461
487
  if (this.initialized) {
462
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
488
+ throw new Scorm2004ValidationError(
489
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
490
+ );
463
491
  } else {
464
492
  this._time_limit_action = time_limit_action;
465
493
  }
@@ -479,7 +507,9 @@ export class CMI extends BaseRootCMI {
479
507
  */
480
508
  set total_time(total_time: string) {
481
509
  if (this.initialized) {
482
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
510
+ throw new Scorm2004ValidationError(
511
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
512
+ );
483
513
  } else {
484
514
  this._total_time = total_time;
485
515
  }
@@ -17,7 +17,7 @@ export class CMICommentsFromLMS extends CMIArray {
17
17
  constructor() {
18
18
  super({
19
19
  children: scorm2004_constants.comments_children,
20
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
20
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
21
21
  errorClass: Scorm2004ValidationError,
22
22
  });
23
23
  }
@@ -34,7 +34,7 @@ export class CMICommentsFromLearner extends CMIArray {
34
34
  constructor() {
35
35
  super({
36
36
  children: scorm2004_constants.comments_children,
37
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
37
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
38
38
  errorClass: Scorm2004ValidationError,
39
39
  });
40
40
  }
@@ -83,7 +83,9 @@ export class CMICommentsObject extends BaseCMI {
83
83
  */
84
84
  set comment(comment: string) {
85
85
  if (this.initialized && this._readOnlyAfterInit) {
86
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
86
+ throw new Scorm2004ValidationError(
87
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
88
+ );
87
89
  } else {
88
90
  if (
89
91
  check2004ValidFormat(comment, scorm2004_regex.CMILangString4000, true)
@@ -107,7 +109,9 @@ export class CMICommentsObject extends BaseCMI {
107
109
  */
108
110
  set location(location: string) {
109
111
  if (this.initialized && this._readOnlyAfterInit) {
110
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
112
+ throw new Scorm2004ValidationError(
113
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
114
+ );
111
115
  } else {
112
116
  if (check2004ValidFormat(location, scorm2004_regex.CMIString250)) {
113
117
  this._location = location;
@@ -129,7 +133,9 @@ export class CMICommentsObject extends BaseCMI {
129
133
  */
130
134
  set timestamp(timestamp: string) {
131
135
  if (this.initialized && this._readOnlyAfterInit) {
132
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
136
+ throw new Scorm2004ValidationError(
137
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
138
+ );
133
139
  } else {
134
140
  if (check2004ValidFormat(timestamp, scorm2004_regex.CMITime)) {
135
141
  this._timestamp = timestamp;
@@ -17,7 +17,7 @@ export class CMIInteractions extends CMIArray {
17
17
  constructor() {
18
18
  super({
19
19
  children: scorm2004_constants.interactions_children,
20
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
20
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
21
21
  errorClass: Scorm2004ValidationError,
22
22
  });
23
23
  }
@@ -43,12 +43,12 @@ export class CMIInteractionsObject extends BaseCMI {
43
43
  constructor() {
44
44
  super();
45
45
  this.objectives = new CMIArray({
46
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
46
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
47
47
  errorClass: Scorm2004ValidationError,
48
48
  children: scorm2004_constants.objectives_children,
49
49
  });
50
50
  this.correct_responses = new CMIArray({
51
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
51
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
52
52
  errorClass: Scorm2004ValidationError,
53
53
  children: scorm2004_constants.correct_responses_children,
54
54
  });
@@ -80,12 +80,12 @@ export class CMIInteractionsObject extends BaseCMI {
80
80
  this._latency = "";
81
81
  this._description = "";
82
82
  this.objectives = new CMIArray({
83
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
83
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
84
84
  errorClass: Scorm2004ValidationError,
85
85
  children: scorm2004_constants.objectives_children,
86
86
  });
87
87
  this.correct_responses = new CMIArray({
88
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
88
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
89
89
  errorClass: Scorm2004ValidationError,
90
90
  children: scorm2004_constants.correct_responses_children,
91
91
  });
@@ -124,7 +124,7 @@ export class CMIInteractionsObject extends BaseCMI {
124
124
  set type(type: string) {
125
125
  if (this.initialized && this._id === "") {
126
126
  throw new Scorm2004ValidationError(
127
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
127
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
128
128
  );
129
129
  } else {
130
130
  if (check2004ValidFormat(type, scorm2004_regex.CMIType)) {
@@ -148,7 +148,7 @@ export class CMIInteractionsObject extends BaseCMI {
148
148
  set timestamp(timestamp: string) {
149
149
  if (this.initialized && this._id === "") {
150
150
  throw new Scorm2004ValidationError(
151
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
151
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
152
152
  );
153
153
  } else {
154
154
  if (check2004ValidFormat(timestamp, scorm2004_regex.CMITime)) {
@@ -172,7 +172,7 @@ export class CMIInteractionsObject extends BaseCMI {
172
172
  set weighting(weighting: string) {
173
173
  if (this.initialized && this._id === "") {
174
174
  throw new Scorm2004ValidationError(
175
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
175
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
176
176
  );
177
177
  } else {
178
178
  if (check2004ValidFormat(weighting, scorm2004_regex.CMIDecimal)) {
@@ -197,7 +197,7 @@ export class CMIInteractionsObject extends BaseCMI {
197
197
  set learner_response(learner_response: string) {
198
198
  if (this.initialized && (this._type === "" || this._id === "")) {
199
199
  throw new Scorm2004ValidationError(
200
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
200
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
201
201
  );
202
202
  } else {
203
203
  let nodes = [];
@@ -215,39 +215,39 @@ export class CMIInteractionsObject extends BaseCMI {
215
215
 
216
216
  for (let i = 0; i < nodes.length; i++) {
217
217
  if (response_type?.delimiter2) {
218
- const values = nodes[i].split(response_type.delimiter2);
218
+ const values = nodes[i]?.split(response_type.delimiter2);
219
219
 
220
- if (values.length === 2) {
221
- if (!values[0].match(formatRegex)) {
220
+ if (values?.length === 2) {
221
+ if (!values[0]?.match(formatRegex)) {
222
222
  throw new Scorm2004ValidationError(
223
- scorm2004_errors.TYPE_MISMATCH,
223
+ scorm2004_errors.TYPE_MISMATCH as number,
224
224
  );
225
225
  } else {
226
226
  if (
227
227
  !response_type.format2 ||
228
- !values[1].match(new RegExp(response_type.format2))
228
+ !values[1]?.match(new RegExp(response_type.format2))
229
229
  ) {
230
230
  throw new Scorm2004ValidationError(
231
- scorm2004_errors.TYPE_MISMATCH,
231
+ scorm2004_errors.TYPE_MISMATCH as number,
232
232
  );
233
233
  }
234
234
  }
235
235
  } else {
236
236
  throw new Scorm2004ValidationError(
237
- scorm2004_errors.TYPE_MISMATCH,
237
+ scorm2004_errors.TYPE_MISMATCH as number,
238
238
  );
239
239
  }
240
240
  } else {
241
- if (!nodes[i].match(formatRegex)) {
241
+ if (!nodes[i]?.match(formatRegex)) {
242
242
  throw new Scorm2004ValidationError(
243
- scorm2004_errors.TYPE_MISMATCH,
243
+ scorm2004_errors.TYPE_MISMATCH as number,
244
244
  );
245
245
  } else {
246
246
  if (nodes[i] !== "" && response_type.unique) {
247
247
  for (let j = 0; j < i; j++) {
248
248
  if (nodes[i] === nodes[j]) {
249
249
  throw new Scorm2004ValidationError(
250
- scorm2004_errors.TYPE_MISMATCH,
250
+ scorm2004_errors.TYPE_MISMATCH as number,
251
251
  );
252
252
  }
253
253
  }
@@ -257,13 +257,15 @@ export class CMIInteractionsObject extends BaseCMI {
257
257
  }
258
258
  } else {
259
259
  throw new Scorm2004ValidationError(
260
- scorm2004_errors.GENERAL_SET_FAILURE,
260
+ scorm2004_errors.GENERAL_SET_FAILURE as number,
261
261
  );
262
262
  }
263
263
 
264
264
  this._learner_response = learner_response;
265
265
  } else {
266
- throw new Scorm2004ValidationError(scorm2004_errors.TYPE_MISMATCH);
266
+ throw new Scorm2004ValidationError(
267
+ scorm2004_errors.TYPE_MISMATCH as number,
268
+ );
267
269
  }
268
270
  }
269
271
  }
@@ -301,7 +303,7 @@ export class CMIInteractionsObject extends BaseCMI {
301
303
  set latency(latency: string) {
302
304
  if (this.initialized && this._id === "") {
303
305
  throw new Scorm2004ValidationError(
304
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
306
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
305
307
  );
306
308
  } else {
307
309
  if (check2004ValidFormat(latency, scorm2004_regex.CMITimespan)) {
@@ -325,7 +327,7 @@ export class CMIInteractionsObject extends BaseCMI {
325
327
  set description(description: string) {
326
328
  if (this.initialized && this._id === "") {
327
329
  throw new Scorm2004ValidationError(
328
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
330
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
329
331
  );
330
332
  } else {
331
333
  if (
@@ -44,7 +44,9 @@ export class CMILearnerPreference extends BaseCMI {
44
44
  * @private
45
45
  */
46
46
  set _children(_children: string) {
47
- throw new Scorm2004ValidationError(scorm2004_errors.READ_ONLY_ELEMENT);
47
+ throw new Scorm2004ValidationError(
48
+ scorm2004_errors.READ_ONLY_ELEMENT as number,
49
+ );
48
50
  }
49
51
 
50
52
  /**
@@ -18,7 +18,7 @@ export class CMIObjectives extends CMIArray {
18
18
  constructor() {
19
19
  super({
20
20
  children: scorm2004_constants.objectives_children,
21
- errorCode: scorm2004_errors.READ_ONLY_ELEMENT,
21
+ errorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
22
22
  errorClass: Scorm2004ValidationError,
23
23
  });
24
24
  }
@@ -110,7 +110,7 @@ export class CMIObjectivesObject extends BaseCMI {
110
110
  set success_status(success_status: string) {
111
111
  if (this.initialized && this._id === "") {
112
112
  throw new Scorm2004ValidationError(
113
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
113
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
114
114
  );
115
115
  } else {
116
116
  if (check2004ValidFormat(success_status, scorm2004_regex.CMISStatus)) {
@@ -134,7 +134,7 @@ export class CMIObjectivesObject extends BaseCMI {
134
134
  set completion_status(completion_status: string) {
135
135
  if (this.initialized && this._id === "") {
136
136
  throw new Scorm2004ValidationError(
137
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
137
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
138
138
  );
139
139
  } else {
140
140
  if (check2004ValidFormat(completion_status, scorm2004_regex.CMICStatus)) {
@@ -158,7 +158,7 @@ export class CMIObjectivesObject extends BaseCMI {
158
158
  set progress_measure(progress_measure: string) {
159
159
  if (this.initialized && this._id === "") {
160
160
  throw new Scorm2004ValidationError(
161
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
161
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
162
162
  );
163
163
  } else {
164
164
  if (
@@ -185,7 +185,7 @@ export class CMIObjectivesObject extends BaseCMI {
185
185
  set description(description: string) {
186
186
  if (this.initialized && this._id === "") {
187
187
  throw new Scorm2004ValidationError(
188
- scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED,
188
+ scorm2004_errors.DEPENDENCY_NOT_ESTABLISHED as number,
189
189
  );
190
190
  } else {
191
191
  if (
@@ -18,9 +18,9 @@ export class Scorm2004CMIScore extends CMIScore {
18
18
  super({
19
19
  score_children: scorm2004_constants.score_children,
20
20
  max: "",
21
- invalidErrorCode: scorm2004_errors.READ_ONLY_ELEMENT,
22
- invalidTypeCode: scorm2004_errors.TYPE_MISMATCH,
23
- invalidRangeCode: scorm2004_errors.VALUE_OUT_OF_RANGE,
21
+ invalidErrorCode: scorm2004_errors.READ_ONLY_ELEMENT as number,
22
+ invalidTypeCode: scorm2004_errors.TYPE_MISMATCH as number,
23
+ invalidRangeCode: scorm2004_errors.VALUE_OUT_OF_RANGE as number,
24
24
  decimalRegex: scorm2004_regex.CMIDecimal,
25
25
  errorClass: Scorm2004ValidationError,
26
26
  });
@@ -17,7 +17,7 @@ export function check2004ValidFormat(
17
17
  return checkValidFormat(
18
18
  value,
19
19
  regexPattern,
20
- scorm2004_errors.TYPE_MISMATCH,
20
+ scorm2004_errors.TYPE_MISMATCH as number,
21
21
  Scorm2004ValidationError,
22
22
  allowEmptyString,
23
23
  );
@@ -36,7 +36,7 @@ export function check2004ValidRange(
36
36
  return checkValidRange(
37
37
  value,
38
38
  rangePattern,
39
- scorm2004_errors.VALUE_OUT_OF_RANGE,
39
+ scorm2004_errors.VALUE_OUT_OF_RANGE as number,
40
40
  Scorm2004ValidationError,
41
41
  );
42
42
  }
@@ -15,14 +15,14 @@ export class AICCValidationError extends ValidationError {
15
15
  if ({}.hasOwnProperty.call(aicc_errors, String(errorCode))) {
16
16
  super(
17
17
  errorCode,
18
- aicc_errors[String(errorCode)].basicMessage,
19
- aicc_errors[String(errorCode)].detailMessage,
18
+ aicc_errors[String(errorCode)]?.basicMessage || "Unknown rror",
19
+ aicc_errors[String(errorCode)]?.detailMessage,
20
20
  );
21
21
  } else {
22
22
  super(
23
23
  101,
24
- aicc_errors["101"].basicMessage,
25
- aicc_errors["101"].detailMessage,
24
+ aicc_errors["101"]?.basicMessage || "General error",
25
+ aicc_errors["101"]?.detailMessage,
26
26
  );
27
27
  }
28
28
  }
@@ -15,14 +15,14 @@ export class Scorm12ValidationError extends ValidationError {
15
15
  if ({}.hasOwnProperty.call(scorm12_errors, String(errorCode))) {
16
16
  super(
17
17
  errorCode,
18
- scorm12_errors[String(errorCode)].basicMessage,
19
- scorm12_errors[String(errorCode)].detailMessage,
18
+ scorm12_errors[String(errorCode)]?.basicMessage || "Unknown error",
19
+ scorm12_errors[String(errorCode)]?.detailMessage,
20
20
  );
21
21
  } else {
22
22
  super(
23
23
  101,
24
- scorm12_errors["101"].basicMessage,
25
- scorm12_errors["101"].detailMessage,
24
+ scorm12_errors["101"]?.basicMessage ?? "General error",
25
+ scorm12_errors["101"]?.detailMessage,
26
26
  );
27
27
  }
28
28
  }
@@ -15,14 +15,14 @@ export class Scorm2004ValidationError extends ValidationError {
15
15
  if ({}.hasOwnProperty.call(scorm2004_errors, String(errorCode))) {
16
16
  super(
17
17
  errorCode,
18
- scorm2004_errors[String(errorCode)].basicMessage,
19
- scorm2004_errors[String(errorCode)].detailMessage,
18
+ scorm2004_errors[String(errorCode)]?.basicMessage || "Unknown error",
19
+ scorm2004_errors[String(errorCode)]?.detailMessage,
20
20
  );
21
21
  } else {
22
22
  super(
23
23
  101,
24
- scorm2004_errors["101"].basicMessage,
25
- scorm2004_errors["101"].detailMessage,
24
+ scorm2004_errors["101"]?.basicMessage || "General error",
25
+ scorm2004_errors["101"]?.detailMessage,
26
26
  );
27
27
  }
28
28
  }
package/src/utilities.ts CHANGED
@@ -68,7 +68,7 @@ export function getSecondsAsISODuration(seconds: number | null): string {
68
68
  let duration = "P";
69
69
  let remainder = seconds;
70
70
  for (const designationsKey in designations) {
71
- const current_seconds = designations[designationsKey];
71
+ const current_seconds = designations[designationsKey] || 1;
72
72
  let value = Math.floor(remainder / current_seconds);
73
73
  remainder = remainder % current_seconds;
74
74
 
@@ -259,7 +259,7 @@ export function unflatten(data: StringKeyMap): object {
259
259
 
260
260
  while (m) {
261
261
  cur = cur[prop] || (cur[prop] = m[2] ? [] : {});
262
- prop = m[2] || m[1];
262
+ prop = m[2] || m[1] || "";
263
263
  m = regex.exec(p);
264
264
  }
265
265
 
@@ -278,7 +278,7 @@ export function unflatten(data: StringKeyMap): object {
278
278
  export function countDecimals(num: number): number {
279
279
  if (Math.floor(num) === num || String(num).indexOf(".") < 0) return 0;
280
280
  const parts = num.toString().split(".")[1];
281
- return parts.length || 0;
281
+ return parts?.length || 0;
282
282
  }
283
283
 
284
284
  /**
@@ -0,0 +1,126 @@
1
+ import { expect } from "expect";
2
+ import * as sinon from "sinon";
3
+ import { Scorm12API } from "../src/Scorm12API";
4
+ import { global_constants } from "../src/constants/api_constants";
5
+
6
+ describe("BaseAPI requestHandler Tests", () => {
7
+ let api: Scorm12API;
8
+ let requestHandlerSpy: sinon.SinonSpy;
9
+ let fetchStub: sinon.SinonStub;
10
+
11
+ beforeEach(() => {
12
+ // Stub the global fetch to prevent actual HTTP requests
13
+ fetchStub = sinon.stub(global, "fetch");
14
+ fetchStub.resolves(
15
+ new Response(
16
+ JSON.stringify({
17
+ result: global_constants.SCORM_TRUE,
18
+ errorCode: 0,
19
+ }),
20
+ {
21
+ status: 200,
22
+ headers: { "Content-Type": "application/json" },
23
+ }
24
+ )
25
+ );
26
+
27
+ // Create a spy for requestHandler
28
+ requestHandlerSpy = sinon.spy((params) => {
29
+ // Add a marker to verify the handler was called
30
+ if (typeof params === "object" && !Array.isArray(params)) {
31
+ params._requestHandlerCalled = true;
32
+ }
33
+ return params;
34
+ });
35
+
36
+ // Initialize API with requestHandler
37
+ api = new Scorm12API({
38
+ lmsCommitUrl: "http://example.com/commit",
39
+ requestHandler: requestHandlerSpy,
40
+ });
41
+ });
42
+
43
+ afterEach(() => {
44
+ sinon.restore();
45
+ });
46
+
47
+ describe("requestHandler with immediate requests", () => {
48
+ it("should call requestHandler during normal commit", async () => {
49
+ api.lmsInitialize();
50
+ api.lmsSetValue("cmi.core.student_name", "Test Student");
51
+
52
+ // Force a commit
53
+ await api.lmsCommit();
54
+
55
+ // Wait a bit for async operations
56
+ await new Promise(resolve => setTimeout(resolve, 100));
57
+
58
+ expect(requestHandlerSpy.calledOnce).toBe(true);
59
+
60
+ // Verify the params were transformed
61
+ const callArgs = fetchStub.firstCall.args[1];
62
+ const bodyData = JSON.parse(callArgs.body);
63
+ expect(bodyData._requestHandlerCalled).toBe(true);
64
+ });
65
+
66
+ it("should call requestHandler during terminate (immediate request)", async () => {
67
+ api.lmsInitialize();
68
+ api.lmsSetValue("cmi.core.student_name", "Test Student");
69
+
70
+ // Call finish which triggers immediate commit
71
+ const result = api.lmsFinish();
72
+ expect(result).toBe(global_constants.SCORM_TRUE);
73
+
74
+ // Wait a bit for async operations
75
+ await new Promise(resolve => setTimeout(resolve, 100));
76
+
77
+ // requestHandler should have been called
78
+ expect(requestHandlerSpy.called).toBe(true);
79
+
80
+ // Verify the params were transformed even for immediate request
81
+ const callArgs = fetchStub.firstCall.args[1];
82
+ const bodyData = JSON.parse(callArgs.body);
83
+ expect(bodyData._requestHandlerCalled).toBe(true);
84
+ });
85
+
86
+ it("should pass through params when no requestHandler is provided", async () => {
87
+ // Create API without requestHandler
88
+ const apiNoHandler = new Scorm12API({
89
+ lmsCommitUrl: "http://example.com/commit",
90
+ });
91
+
92
+ apiNoHandler.lmsInitialize();
93
+ apiNoHandler.lmsSetValue("cmi.core.student_name", "Test Student");
94
+
95
+ // Call finish
96
+ const result = apiNoHandler.lmsFinish();
97
+ expect(result).toBe(global_constants.SCORM_TRUE);
98
+
99
+ // Wait a bit for async operations
100
+ await new Promise(resolve => setTimeout(resolve, 100));
101
+
102
+ // Verify fetch was called but params don't have the marker
103
+ expect(fetchStub.called).toBe(true);
104
+ const callArgs = fetchStub.firstCall.args[1];
105
+ const bodyData = JSON.parse(callArgs.body);
106
+ expect(bodyData._requestHandlerCalled).toBeUndefined();
107
+ });
108
+
109
+ it("should handle requestHandler errors gracefully", async () => {
110
+ // Create API with error-throwing requestHandler
111
+ const errorApi = new Scorm12API({
112
+ lmsCommitUrl: "http://example.com/commit",
113
+ requestHandler: () => {
114
+ throw new Error("RequestHandler error");
115
+ },
116
+ });
117
+
118
+ errorApi.lmsInitialize();
119
+ errorApi.lmsSetValue("cmi.core.student_name", "Test Student");
120
+
121
+ // Call finish - should not throw
122
+ const result = errorApi.lmsFinish();
123
+ expect(result).toBe(global_constants.SCORM_TRUE);
124
+ });
125
+ });
126
+ });