@patch-adams/core 1.5.11 → 1.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +113 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +113 -22
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +114 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +114 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -50,7 +50,9 @@ var LrsBridgeConfigSchema = zod.z.object({
|
|
|
50
50
|
/** Document GUID for xAPI statement aggregation - baked into package at wrap time */
|
|
51
51
|
documentGuid: zod.z.string().optional(),
|
|
52
52
|
/** Version GUID for xAPI statement aggregation - unique per export */
|
|
53
|
-
versionGuid: zod.z.string().optional()
|
|
53
|
+
versionGuid: zod.z.string().optional(),
|
|
54
|
+
/** Original SCORM package filename - baked in at wrap time for LRS searchability */
|
|
55
|
+
packageName: zod.z.string().optional()
|
|
54
56
|
});
|
|
55
57
|
var BlockingAssetConfigSchema = zod.z.object({
|
|
56
58
|
/** Filename for the asset */
|
|
@@ -399,7 +401,8 @@ var DEFAULT_LRS_OPTIONS = {
|
|
|
399
401
|
lrsAuth: "",
|
|
400
402
|
lrsProxyEndpoint: "",
|
|
401
403
|
documentGuid: "",
|
|
402
|
-
versionGuid: ""
|
|
404
|
+
versionGuid: "",
|
|
405
|
+
packageName: ""
|
|
403
406
|
};
|
|
404
407
|
function generateLrsBridgeCode(options) {
|
|
405
408
|
if (!options.enabled) {
|
|
@@ -415,6 +418,7 @@ function generateLrsBridgeCode(options) {
|
|
|
415
418
|
const lrsProxyEndpoint = options.lrsProxyEndpoint || "";
|
|
416
419
|
const documentGuid = options.documentGuid || "";
|
|
417
420
|
const versionGuid = options.versionGuid || "";
|
|
421
|
+
const packageName = options.packageName || "";
|
|
418
422
|
return `
|
|
419
423
|
// ==========================================================================
|
|
420
424
|
// PATCH-ADAMS LRS BRIDGE v2.2.0
|
|
@@ -447,6 +451,7 @@ function generateLrsBridgeCode(options) {
|
|
|
447
451
|
var LRS_PROXY_ENDPOINT = '${lrsProxyEndpoint}';
|
|
448
452
|
var DOCUMENT_GUID = '${documentGuid}';
|
|
449
453
|
var VERSION_GUID = '${versionGuid}';
|
|
454
|
+
var PACKAGE_NAME = '${packageName}';
|
|
450
455
|
|
|
451
456
|
// Bravais LRS endpoint pattern: https://lrs-{tenant}.bravais.com/XAPI/statements
|
|
452
457
|
// Example: core-acme.bravais.com -> lrs-acme.bravais.com
|
|
@@ -2130,7 +2135,9 @@ function generateLrsBridgeCode(options) {
|
|
|
2130
2135
|
sharedLinkName: null,
|
|
2131
2136
|
// Tenant info
|
|
2132
2137
|
homepage: COURSE_HOMEPAGE || window.location.origin,
|
|
2133
|
-
tenantHomepage: null
|
|
2138
|
+
tenantHomepage: null, // https://{tenant}.bravais.com format
|
|
2139
|
+
// Package info
|
|
2140
|
+
packageName: null // Original SCORM zip filename
|
|
2134
2141
|
};
|
|
2135
2142
|
|
|
2136
2143
|
// 0. Use baked-in GUIDs from PA-Patcher config (highest priority - set at wrap time)
|
|
@@ -2143,6 +2150,10 @@ function generateLrsBridgeCode(options) {
|
|
|
2143
2150
|
info.versionGuid = VERSION_GUID;
|
|
2144
2151
|
log('Version GUID from baked-in config:', VERSION_GUID);
|
|
2145
2152
|
}
|
|
2153
|
+
if (PACKAGE_NAME) {
|
|
2154
|
+
info.packageName = PACKAGE_NAME;
|
|
2155
|
+
log('Package name from baked-in config:', PACKAGE_NAME);
|
|
2156
|
+
}
|
|
2146
2157
|
|
|
2147
2158
|
// 1. Extract shared link token and document ID from URL
|
|
2148
2159
|
info.sharedLinkToken = extractSharedLinkToken();
|
|
@@ -2573,6 +2584,11 @@ function generateLrsBridgeCode(options) {
|
|
|
2573
2584
|
// Resource type
|
|
2574
2585
|
obj.definition.extensions['resourceType'] = LRS.courseInfo.resourceType || 'Course';
|
|
2575
2586
|
|
|
2587
|
+
// Package name (original SCORM zip filename)
|
|
2588
|
+
if (LRS.courseInfo.packageName) {
|
|
2589
|
+
obj.definition.extensions['packageName'] = LRS.courseInfo.packageName;
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2576
2592
|
return obj;
|
|
2577
2593
|
}
|
|
2578
2594
|
|
|
@@ -3406,11 +3422,11 @@ function generateLrsBridgeCode(options) {
|
|
|
3406
3422
|
// Send a course completion statement \u2014 called by skin or auto-detected via SCORM
|
|
3407
3423
|
LRS.sendCompletionStatement = function(data) {
|
|
3408
3424
|
data = data || {};
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3425
|
+
// Cancel any pending SCORM auto-detection to avoid double-fire for same attempt
|
|
3426
|
+
if (scormCompletionDebounce) {
|
|
3427
|
+
clearTimeout(scormCompletionDebounce);
|
|
3428
|
+
scormCompletionDebounce = null;
|
|
3412
3429
|
}
|
|
3413
|
-
scormCompletionSent = true;
|
|
3414
3430
|
|
|
3415
3431
|
var status = data.status || 'completed';
|
|
3416
3432
|
var courseTitle = data.courseTitle || (LRS.courseInfo && LRS.courseInfo.title ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course');
|
|
@@ -3440,11 +3456,58 @@ function generateLrsBridgeCode(options) {
|
|
|
3440
3456
|
};
|
|
3441
3457
|
if (data.employeeName) activityDetails.employeeName = data.employeeName;
|
|
3442
3458
|
|
|
3443
|
-
|
|
3444
|
-
|
|
3459
|
+
// 1. Always send "completed" statement
|
|
3460
|
+
log('Sending completed statement, score:', result.score, 'title:', courseTitle);
|
|
3461
|
+
var completedStatement = buildStatement('completed', ACTIVITY_TYPES.course, activityDetails, result, null);
|
|
3462
|
+
sendStatement(completedStatement);
|
|
3463
|
+
|
|
3464
|
+
// 2. Send "passed" or "failed" statement (assessment outcome)
|
|
3465
|
+
if (status === 'passed' || status === 'failed') {
|
|
3466
|
+
log('Sending', status, 'statement');
|
|
3467
|
+
var outcomeStatement = buildStatement(status, ACTIVITY_TYPES.course, activityDetails, result, null);
|
|
3468
|
+
sendStatement(outcomeStatement);
|
|
3469
|
+
}
|
|
3470
|
+
};
|
|
3471
|
+
|
|
3472
|
+
// Send a "terminated" statement \u2014 called by skin close button or auto on page unload
|
|
3473
|
+
var exitStatementSent = false;
|
|
3474
|
+
LRS.sendExitStatement = function(data) {
|
|
3475
|
+
if (exitStatementSent) return;
|
|
3476
|
+
exitStatementSent = true;
|
|
3477
|
+
data = data || {};
|
|
3478
|
+
|
|
3479
|
+
var courseTitle = (LRS.courseInfo && LRS.courseInfo.title) ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course';
|
|
3480
|
+
var duration = null;
|
|
3481
|
+
if (LRS.launchTime) {
|
|
3482
|
+
var ms = new Date().getTime() - new Date(LRS.launchTime).getTime();
|
|
3483
|
+
var secs = Math.floor(ms / 1000);
|
|
3484
|
+
var mins = Math.floor(secs / 60);
|
|
3485
|
+
var hrs = Math.floor(mins / 60);
|
|
3486
|
+
duration = 'PT' + (hrs > 0 ? hrs + 'H' : '') + (mins % 60) + 'M' + (secs % 60) + 'S';
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
var activityDetails = {
|
|
3490
|
+
courseTitle: courseTitle,
|
|
3491
|
+
exitSource: data.source || 'page-unload',
|
|
3492
|
+
employeeEmail: LRS.actor && LRS.actor.mbox ? LRS.actor.mbox.replace('mailto:', '') : undefined,
|
|
3493
|
+
employeeId: LRS.employeeId || undefined,
|
|
3494
|
+
sessionDuration: duration,
|
|
3495
|
+
statementsSent: LRS.stats.statementsSent
|
|
3496
|
+
};
|
|
3497
|
+
|
|
3498
|
+
var result = duration ? { duration: duration } : null;
|
|
3499
|
+
log('Sending exit statement, duration:', duration);
|
|
3500
|
+
var statement = buildStatement('terminated', ACTIVITY_TYPES.course, activityDetails, result, null);
|
|
3445
3501
|
sendStatement(statement);
|
|
3446
3502
|
};
|
|
3447
3503
|
|
|
3504
|
+
// Auto-send exit statement on page unload
|
|
3505
|
+
window.addEventListener('beforeunload', function() {
|
|
3506
|
+
if (!exitStatementSent && LRS.actor) {
|
|
3507
|
+
LRS.sendExitStatement({ source: 'page-unload' });
|
|
3508
|
+
}
|
|
3509
|
+
});
|
|
3510
|
+
|
|
3448
3511
|
/**
|
|
3449
3512
|
* Re-extract actor from SCORM after LMSInitialize has been called.
|
|
3450
3513
|
* Call this from the SCORM wrapper after scormInit() succeeds,
|
|
@@ -3551,6 +3614,23 @@ function generateLrsBridgeCode(options) {
|
|
|
3551
3614
|
LRS.interacted({ type: 'choice-branch', id: data.id, name: 'Choice Branch Discarded' });
|
|
3552
3615
|
};
|
|
3553
3616
|
|
|
3617
|
+
// ========================================================================
|
|
3618
|
+
// PUBLIC API \u2014 expose core internals for skin scripts
|
|
3619
|
+
// Usage: window.pa_patcher.lrs.api.sendStatement(stmt)
|
|
3620
|
+
// ========================================================================
|
|
3621
|
+
LRS.api = {
|
|
3622
|
+
sendStatement: sendStatement,
|
|
3623
|
+
buildStatement: buildStatement,
|
|
3624
|
+
buildCourseActivityObject: buildCourseActivityObject,
|
|
3625
|
+
buildXylemeContext: buildXylemeContext,
|
|
3626
|
+
generateUUID: generateUUID,
|
|
3627
|
+
decodeEntities: decodeEntities,
|
|
3628
|
+
extractActor: extractActor,
|
|
3629
|
+
getCachedLessonInfo: getCachedLessonInfo,
|
|
3630
|
+
VERBS: VERBS,
|
|
3631
|
+
ACTIVITY_TYPES: ACTIVITY_TYPES
|
|
3632
|
+
};
|
|
3633
|
+
|
|
3554
3634
|
// ========================================================================
|
|
3555
3635
|
// 8. RISE EVENT INTERCEPTORS
|
|
3556
3636
|
// ========================================================================
|
|
@@ -4116,7 +4196,7 @@ function generateLrsBridgeCode(options) {
|
|
|
4116
4196
|
var scormInteractionsSent = {}; // Track which interactions were already sent
|
|
4117
4197
|
var scormTrackerActive = false; // Set true when SCORM tracker wraps SetValue \u2014 KC handler defers
|
|
4118
4198
|
var scormCourseData = {}; // Accumulates course-level SCORM data (score, status)
|
|
4119
|
-
var
|
|
4199
|
+
var scormCompletionDebounce = null; // Debounce timer \u2014 prevents duplicate fires within same completion event
|
|
4120
4200
|
|
|
4121
4201
|
function interceptScormSetValue(key, value) {
|
|
4122
4202
|
if (typeof key !== 'string') return;
|
|
@@ -4136,12 +4216,15 @@ function generateLrsBridgeCode(options) {
|
|
|
4136
4216
|
if (key === 'cmi.success_status') { scormCourseData.successStatus = String(value).toLowerCase(); }
|
|
4137
4217
|
|
|
4138
4218
|
// Fire course completion statement when status indicates pass/fail/complete
|
|
4139
|
-
|
|
4219
|
+
// Uses debounce (not one-time flag) so retries/new attempts are captured
|
|
4220
|
+
if (key === 'cmi.core.lesson_status' || key === 'cmi.success_status' || key === 'cmi.completion_status') {
|
|
4140
4221
|
var status = String(value).toLowerCase();
|
|
4141
4222
|
if (status === 'passed' || status === 'failed' || status === 'completed') {
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4223
|
+
if (scormCompletionDebounce) clearTimeout(scormCompletionDebounce);
|
|
4224
|
+
scormCompletionDebounce = setTimeout(function() {
|
|
4225
|
+
scormCompletionDebounce = null;
|
|
4226
|
+
sendCourseCompletionStatement(status);
|
|
4227
|
+
}, 500);
|
|
4145
4228
|
}
|
|
4146
4229
|
}
|
|
4147
4230
|
|
|
@@ -4252,11 +4335,6 @@ function generateLrsBridgeCode(options) {
|
|
|
4252
4335
|
|
|
4253
4336
|
var courseTitle = (LRS.courseInfo && LRS.courseInfo.title) ? decodeEntities(LRS.courseInfo.title) : decodeEntities(document.title) || 'Course';
|
|
4254
4337
|
|
|
4255
|
-
// Determine verb
|
|
4256
|
-
var verbKey = 'completed';
|
|
4257
|
-
if (status === 'passed') verbKey = 'passed';
|
|
4258
|
-
if (status === 'failed') verbKey = 'failed';
|
|
4259
|
-
|
|
4260
4338
|
// Build result with score
|
|
4261
4339
|
var result = { completion: true };
|
|
4262
4340
|
|
|
@@ -4282,10 +4360,17 @@ function generateLrsBridgeCode(options) {
|
|
|
4282
4360
|
employeeId: LRS.employeeId || undefined
|
|
4283
4361
|
};
|
|
4284
4362
|
|
|
4285
|
-
|
|
4363
|
+
// 1. Send "completed" statement (course was finished)
|
|
4364
|
+
log('Sending completed statement, score:', result.score, 'title:', courseTitle);
|
|
4365
|
+
var completedStatement = buildStatement('completed', ACTIVITY_TYPES.course, activityDetails, result, null);
|
|
4366
|
+
sendStatement(completedStatement);
|
|
4286
4367
|
|
|
4287
|
-
|
|
4288
|
-
|
|
4368
|
+
// 2. Send "passed" or "failed" statement (assessment outcome)
|
|
4369
|
+
if (status === 'passed' || status === 'failed') {
|
|
4370
|
+
log('Sending', status, 'statement');
|
|
4371
|
+
var outcomeStatement = buildStatement(status, ACTIVITY_TYPES.course, activityDetails, result, null);
|
|
4372
|
+
sendStatement(outcomeStatement);
|
|
4373
|
+
}
|
|
4289
4374
|
|
|
4290
4375
|
logGroupEnd();
|
|
4291
4376
|
}
|
|
@@ -4969,7 +5054,8 @@ function buildJsBeforeOptions(config, metadata) {
|
|
|
4969
5054
|
lrsProxyEndpoint: lrsBridgeConfig.lrsProxyEndpoint,
|
|
4970
5055
|
employeeApiEndpoint: lrsBridgeConfig.employeeApiEndpoint,
|
|
4971
5056
|
documentGuid: lrsBridgeConfig.documentGuid,
|
|
4972
|
-
versionGuid: lrsBridgeConfig.versionGuid
|
|
5057
|
+
versionGuid: lrsBridgeConfig.versionGuid,
|
|
5058
|
+
packageName: lrsBridgeConfig.packageName
|
|
4973
5059
|
};
|
|
4974
5060
|
return {
|
|
4975
5061
|
remoteUrl: `${config.remoteDomain}/${config.localFolders.js}/${config.jsBefore.filename}`,
|
|
@@ -6659,6 +6745,11 @@ var Patcher = class {
|
|
|
6659
6745
|
if (effectiveSkin) {
|
|
6660
6746
|
console.log(`[Patcher] Skin: ${effectiveSkin}`);
|
|
6661
6747
|
}
|
|
6748
|
+
if (options.packageName) {
|
|
6749
|
+
this.config.lrsBridge = this.config.lrsBridge ?? {};
|
|
6750
|
+
this.config.lrsBridge.packageName = options.packageName;
|
|
6751
|
+
console.log(`[Patcher] Package name: ${options.packageName}`);
|
|
6752
|
+
}
|
|
6662
6753
|
const htmlInjector = this.getHtmlInjector(toolInfo.tool);
|
|
6663
6754
|
htmlInjector.setMetadata(metadata);
|
|
6664
6755
|
let fetchedFallbacks = {};
|