@vue-skuilder/db 0.1.24 → 0.1.25
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/{contentSource-BotbOOfX.d.ts → contentSource-BmnmvH8C.d.ts} +41 -0
- package/dist/{contentSource-C90LH-OH.d.cts → contentSource-DfBbaLA-.d.cts} +41 -0
- package/dist/core/index.d.cts +94 -4
- package/dist/core/index.d.ts +94 -4
- package/dist/core/index.js +530 -83
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +528 -83
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-DGKp4zFB.d.cts → dataLayerProvider-BeRXVMs5.d.cts} +1 -1
- package/dist/{dataLayerProvider-SBpz9jQf.d.ts → dataLayerProvider-CG9GfaAY.d.ts} +1 -1
- package/dist/impl/couch/index.d.cts +2 -2
- package/dist/impl/couch/index.d.ts +2 -2
- package/dist/impl/couch/index.js +526 -83
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +526 -83
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.cts +2 -2
- package/dist/impl/static/index.d.ts +2 -2
- package/dist/impl/static/index.js +526 -83
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +526 -83
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.cts +247 -14
- package/dist/index.d.ts +247 -14
- package/dist/index.js +1419 -140
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1409 -137
- package/dist/index.mjs.map +1 -1
- package/docs/navigators-architecture.md +22 -4
- package/docs/todo-review-urgency-adaptation.md +205 -0
- package/package.json +3 -3
- package/src/core/interfaces/userDB.ts +44 -0
- package/src/core/navigators/Pipeline.ts +86 -5
- package/src/core/navigators/PipelineAssembler.ts +7 -21
- package/src/core/navigators/PipelineDebugger.ts +426 -0
- package/src/core/navigators/generators/CompositeGenerator.ts +21 -0
- package/src/core/navigators/generators/elo.ts +14 -1
- package/src/core/navigators/generators/srs.ts +146 -18
- package/src/core/navigators/index.ts +9 -0
- package/src/impl/couch/user-course-relDB.ts +12 -0
- package/src/study/MixerDebugger.ts +555 -0
- package/src/study/SessionController.ts +95 -19
- package/src/study/SessionDebugger.ts +442 -0
- package/src/study/SourceMixer.ts +36 -17
- package/src/study/TODO-session-scheduling.md +133 -0
- package/src/study/index.ts +2 -0
- package/src/study/services/EloService.ts +79 -4
- package/src/study/services/ResponseProcessor.ts +130 -72
- package/src/study/services/SrsService.ts +9 -0
- package/tests/core/navigators/PipelineAssembler.test.ts +4 -4
|
@@ -404,6 +404,15 @@ var init_user_course_relDB = __esm({
|
|
|
404
404
|
void this.user.updateCourseSettings(this._courseId, updates);
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
|
+
async getStrategyState(strategyKey) {
|
|
408
|
+
return this.user.getStrategyState(this._courseId, strategyKey);
|
|
409
|
+
}
|
|
410
|
+
async putStrategyState(strategyKey, data) {
|
|
411
|
+
return this.user.putStrategyState(this._courseId, strategyKey, data);
|
|
412
|
+
}
|
|
413
|
+
async deleteStrategyState(strategyKey) {
|
|
414
|
+
return this.user.deleteStrategyState(this._courseId, strategyKey);
|
|
415
|
+
}
|
|
407
416
|
async getReviewstoDate(targetDate) {
|
|
408
417
|
const allReviews = await this.user.getPendingReviews(this._courseId);
|
|
409
418
|
logger.debug(
|
|
@@ -473,6 +482,271 @@ var init_courseLookupDB = __esm({
|
|
|
473
482
|
}
|
|
474
483
|
});
|
|
475
484
|
|
|
485
|
+
// src/core/navigators/PipelineDebugger.ts
|
|
486
|
+
var PipelineDebugger_exports = {};
|
|
487
|
+
__export(PipelineDebugger_exports, {
|
|
488
|
+
buildRunReport: () => buildRunReport,
|
|
489
|
+
captureRun: () => captureRun,
|
|
490
|
+
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
491
|
+
pipelineDebugAPI: () => pipelineDebugAPI
|
|
492
|
+
});
|
|
493
|
+
function getOrigin(card) {
|
|
494
|
+
const firstEntry = card.provenance[0];
|
|
495
|
+
if (!firstEntry) return "unknown";
|
|
496
|
+
const reason = firstEntry.reason?.toLowerCase() || "";
|
|
497
|
+
if (reason.includes("new card")) return "new";
|
|
498
|
+
if (reason.includes("review")) return "review";
|
|
499
|
+
return "unknown";
|
|
500
|
+
}
|
|
501
|
+
function captureRun(report) {
|
|
502
|
+
const fullReport = {
|
|
503
|
+
...report,
|
|
504
|
+
runId: `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
505
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
506
|
+
};
|
|
507
|
+
runHistory.unshift(fullReport);
|
|
508
|
+
if (runHistory.length > MAX_RUNS) {
|
|
509
|
+
runHistory.pop();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function buildRunReport(courseId, courseName, generatorName, generators, generatedCount, filters, allCards, selectedCards) {
|
|
513
|
+
const selectedIds = new Set(selectedCards.map((c) => c.cardId));
|
|
514
|
+
const cards = allCards.map((card) => ({
|
|
515
|
+
cardId: card.cardId,
|
|
516
|
+
courseId: card.courseId,
|
|
517
|
+
origin: getOrigin(card),
|
|
518
|
+
finalScore: card.score,
|
|
519
|
+
provenance: card.provenance,
|
|
520
|
+
selected: selectedIds.has(card.cardId)
|
|
521
|
+
}));
|
|
522
|
+
const reviewsSelected = selectedCards.filter((c) => getOrigin(c) === "review").length;
|
|
523
|
+
const newSelected = selectedCards.filter((c) => getOrigin(c) === "new").length;
|
|
524
|
+
return {
|
|
525
|
+
courseId,
|
|
526
|
+
courseName,
|
|
527
|
+
generatorName,
|
|
528
|
+
generators,
|
|
529
|
+
generatedCount,
|
|
530
|
+
filters,
|
|
531
|
+
finalCount: selectedCards.length,
|
|
532
|
+
reviewsSelected,
|
|
533
|
+
newSelected,
|
|
534
|
+
cards
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function formatProvenance(provenance) {
|
|
538
|
+
return provenance.map((p) => {
|
|
539
|
+
const actionSymbol = p.action === "generated" ? "\u{1F3B2}" : p.action === "boosted" ? "\u2B06\uFE0F" : p.action === "penalized" ? "\u2B07\uFE0F" : "\u27A1\uFE0F";
|
|
540
|
+
return ` ${actionSymbol} ${p.strategyName}: ${p.score.toFixed(3)} - ${p.reason}`;
|
|
541
|
+
}).join("\n");
|
|
542
|
+
}
|
|
543
|
+
function printRunSummary(run) {
|
|
544
|
+
console.group(`\u{1F50D} Pipeline Run: ${run.courseId} (${run.courseName || "unnamed"})`);
|
|
545
|
+
logger.info(`Run ID: ${run.runId}`);
|
|
546
|
+
logger.info(`Time: ${run.timestamp.toISOString()}`);
|
|
547
|
+
logger.info(`Generator: ${run.generatorName} \u2192 ${run.generatedCount} candidates`);
|
|
548
|
+
if (run.generators && run.generators.length > 0) {
|
|
549
|
+
console.group("Generator breakdown:");
|
|
550
|
+
for (const g of run.generators) {
|
|
551
|
+
logger.info(
|
|
552
|
+
` ${g.name}: ${g.cardCount} cards (${g.newCount} new, ${g.reviewCount} reviews, top: ${g.topScore.toFixed(2)})`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
console.groupEnd();
|
|
556
|
+
}
|
|
557
|
+
if (run.filters.length > 0) {
|
|
558
|
+
console.group("Filter impact:");
|
|
559
|
+
for (const f of run.filters) {
|
|
560
|
+
logger.info(` ${f.name}: \u2191${f.boosted} \u2193${f.penalized} =${f.passed} \u2715${f.removed}`);
|
|
561
|
+
}
|
|
562
|
+
console.groupEnd();
|
|
563
|
+
}
|
|
564
|
+
logger.info(
|
|
565
|
+
`Result: ${run.finalCount} cards selected (${run.newSelected} new, ${run.reviewsSelected} reviews)`
|
|
566
|
+
);
|
|
567
|
+
console.groupEnd();
|
|
568
|
+
}
|
|
569
|
+
function mountPipelineDebugger() {
|
|
570
|
+
if (typeof window === "undefined") return;
|
|
571
|
+
const win = window;
|
|
572
|
+
win.skuilder = win.skuilder || {};
|
|
573
|
+
win.skuilder.pipeline = pipelineDebugAPI;
|
|
574
|
+
}
|
|
575
|
+
var MAX_RUNS, runHistory, pipelineDebugAPI;
|
|
576
|
+
var init_PipelineDebugger = __esm({
|
|
577
|
+
"src/core/navigators/PipelineDebugger.ts"() {
|
|
578
|
+
"use strict";
|
|
579
|
+
init_logger();
|
|
580
|
+
MAX_RUNS = 10;
|
|
581
|
+
runHistory = [];
|
|
582
|
+
pipelineDebugAPI = {
|
|
583
|
+
/**
|
|
584
|
+
* Get raw run history for programmatic access.
|
|
585
|
+
*/
|
|
586
|
+
get runs() {
|
|
587
|
+
return [...runHistory];
|
|
588
|
+
},
|
|
589
|
+
/**
|
|
590
|
+
* Show summary of a specific pipeline run.
|
|
591
|
+
*/
|
|
592
|
+
showRun(idOrIndex = 0) {
|
|
593
|
+
if (runHistory.length === 0) {
|
|
594
|
+
logger.info("[Pipeline Debug] No runs captured yet.");
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
let run;
|
|
598
|
+
if (typeof idOrIndex === "number") {
|
|
599
|
+
run = runHistory[idOrIndex];
|
|
600
|
+
if (!run) {
|
|
601
|
+
logger.info(
|
|
602
|
+
`[Pipeline Debug] No run found at index ${idOrIndex}. History length: ${runHistory.length}`
|
|
603
|
+
);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
run = runHistory.find((r) => r.runId.endsWith(idOrIndex));
|
|
608
|
+
if (!run) {
|
|
609
|
+
logger.info(`[Pipeline Debug] No run found matching ID '${idOrIndex}'.`);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
printRunSummary(run);
|
|
614
|
+
},
|
|
615
|
+
/**
|
|
616
|
+
* Show summary of the last pipeline run.
|
|
617
|
+
*/
|
|
618
|
+
showLastRun() {
|
|
619
|
+
this.showRun(0);
|
|
620
|
+
},
|
|
621
|
+
/**
|
|
622
|
+
* Show detailed provenance for a specific card.
|
|
623
|
+
*/
|
|
624
|
+
showCard(cardId) {
|
|
625
|
+
for (const run of runHistory) {
|
|
626
|
+
const card = run.cards.find((c) => c.cardId === cardId);
|
|
627
|
+
if (card) {
|
|
628
|
+
console.group(`\u{1F3B4} Card: ${cardId}`);
|
|
629
|
+
logger.info(`Course: ${card.courseId}`);
|
|
630
|
+
logger.info(`Origin: ${card.origin}`);
|
|
631
|
+
logger.info(`Final score: ${card.finalScore.toFixed(3)}`);
|
|
632
|
+
logger.info(`Selected: ${card.selected ? "Yes \u2705" : "No \u274C"}`);
|
|
633
|
+
logger.info("Provenance:");
|
|
634
|
+
logger.info(formatProvenance(card.provenance));
|
|
635
|
+
console.groupEnd();
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
logger.info(`[Pipeline Debug] Card '${cardId}' not found in recent runs.`);
|
|
640
|
+
},
|
|
641
|
+
/**
|
|
642
|
+
* Explain why reviews may or may not have been selected.
|
|
643
|
+
*/
|
|
644
|
+
explainReviews() {
|
|
645
|
+
if (runHistory.length === 0) {
|
|
646
|
+
logger.info("[Pipeline Debug] No runs captured yet.");
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
console.group("\u{1F4CB} Review Selection Analysis");
|
|
650
|
+
for (const run of runHistory) {
|
|
651
|
+
console.group(`Run: ${run.courseId} @ ${run.timestamp.toLocaleTimeString()}`);
|
|
652
|
+
const allReviews = run.cards.filter((c) => c.origin === "review");
|
|
653
|
+
const selectedReviews = allReviews.filter((c) => c.selected);
|
|
654
|
+
if (allReviews.length === 0) {
|
|
655
|
+
logger.info("\u274C No reviews were generated. Check SRS logs for why.");
|
|
656
|
+
} else if (selectedReviews.length === 0) {
|
|
657
|
+
logger.info(`\u26A0\uFE0F ${allReviews.length} reviews generated but none selected.`);
|
|
658
|
+
logger.info("Possible reasons:");
|
|
659
|
+
const topNewScore = Math.max(
|
|
660
|
+
...run.cards.filter((c) => c.origin === "new" && c.selected).map((c) => c.finalScore),
|
|
661
|
+
0
|
|
662
|
+
);
|
|
663
|
+
const topReviewScore = Math.max(...allReviews.map((c) => c.finalScore), 0);
|
|
664
|
+
if (topReviewScore < topNewScore) {
|
|
665
|
+
logger.info(
|
|
666
|
+
` - New cards scored higher (top new: ${topNewScore.toFixed(2)}, top review: ${topReviewScore.toFixed(2)})`
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
const topReview = allReviews.sort((a, b) => b.finalScore - a.finalScore)[0];
|
|
670
|
+
if (topReview) {
|
|
671
|
+
logger.info(` - Top review score: ${topReview.finalScore.toFixed(3)}`);
|
|
672
|
+
logger.info(" - Its provenance:");
|
|
673
|
+
logger.info(formatProvenance(topReview.provenance));
|
|
674
|
+
}
|
|
675
|
+
} else {
|
|
676
|
+
logger.info(`\u2705 ${selectedReviews.length}/${allReviews.length} reviews selected.`);
|
|
677
|
+
logger.info("Top selected review:");
|
|
678
|
+
const topSelected = selectedReviews.sort((a, b) => b.finalScore - a.finalScore)[0];
|
|
679
|
+
logger.info(formatProvenance(topSelected.provenance));
|
|
680
|
+
}
|
|
681
|
+
console.groupEnd();
|
|
682
|
+
}
|
|
683
|
+
console.groupEnd();
|
|
684
|
+
},
|
|
685
|
+
/**
|
|
686
|
+
* Show all runs in compact format.
|
|
687
|
+
*/
|
|
688
|
+
listRuns() {
|
|
689
|
+
if (runHistory.length === 0) {
|
|
690
|
+
logger.info("[Pipeline Debug] No runs captured yet.");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
console.table(
|
|
694
|
+
runHistory.map((r) => ({
|
|
695
|
+
id: r.runId.slice(-8),
|
|
696
|
+
time: r.timestamp.toLocaleTimeString(),
|
|
697
|
+
course: r.courseName || r.courseId.slice(0, 8),
|
|
698
|
+
generated: r.generatedCount,
|
|
699
|
+
selected: r.finalCount,
|
|
700
|
+
new: r.newSelected,
|
|
701
|
+
reviews: r.reviewsSelected
|
|
702
|
+
}))
|
|
703
|
+
);
|
|
704
|
+
},
|
|
705
|
+
/**
|
|
706
|
+
* Export run history as JSON for bug reports.
|
|
707
|
+
*/
|
|
708
|
+
export() {
|
|
709
|
+
const json = JSON.stringify(runHistory, null, 2);
|
|
710
|
+
logger.info("[Pipeline Debug] Run history exported. Copy the returned string or use:");
|
|
711
|
+
logger.info(" copy(window.skuilder.pipeline.export())");
|
|
712
|
+
return json;
|
|
713
|
+
},
|
|
714
|
+
/**
|
|
715
|
+
* Clear run history.
|
|
716
|
+
*/
|
|
717
|
+
clear() {
|
|
718
|
+
runHistory.length = 0;
|
|
719
|
+
logger.info("[Pipeline Debug] Run history cleared.");
|
|
720
|
+
},
|
|
721
|
+
/**
|
|
722
|
+
* Show help.
|
|
723
|
+
*/
|
|
724
|
+
help() {
|
|
725
|
+
logger.info(`
|
|
726
|
+
\u{1F527} Pipeline Debug API
|
|
727
|
+
|
|
728
|
+
Commands:
|
|
729
|
+
.showLastRun() Show summary of most recent pipeline run
|
|
730
|
+
.showRun(id|index) Show summary of a specific run (by index or ID suffix)
|
|
731
|
+
.showCard(cardId) Show provenance trail for a specific card
|
|
732
|
+
.explainReviews() Analyze why reviews were/weren't selected
|
|
733
|
+
.listRuns() List all captured runs in table format
|
|
734
|
+
.export() Export run history as JSON for bug reports
|
|
735
|
+
.clear() Clear run history
|
|
736
|
+
.runs Access raw run history array
|
|
737
|
+
.help() Show this help message
|
|
738
|
+
|
|
739
|
+
Example:
|
|
740
|
+
window.skuilder.pipeline.showLastRun()
|
|
741
|
+
window.skuilder.pipeline.showRun(1)
|
|
742
|
+
window.skuilder.pipeline.showCard('abc123')
|
|
743
|
+
`);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
mountPipelineDebugger();
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
|
|
476
750
|
// src/core/navigators/generators/CompositeGenerator.ts
|
|
477
751
|
var CompositeGenerator_exports = {};
|
|
478
752
|
__export(CompositeGenerator_exports, {
|
|
@@ -541,6 +815,24 @@ var init_CompositeGenerator = __esm({
|
|
|
541
815
|
const results = await Promise.all(
|
|
542
816
|
this.generators.map((g) => g.getWeightedCards(limit, context))
|
|
543
817
|
);
|
|
818
|
+
const generatorSummaries = [];
|
|
819
|
+
results.forEach((cards, index) => {
|
|
820
|
+
const gen = this.generators[index];
|
|
821
|
+
const genName = gen.name || `Generator ${index}`;
|
|
822
|
+
const newCards = cards.filter((c) => c.provenance[0]?.reason?.includes("new card"));
|
|
823
|
+
const reviewCards = cards.filter((c) => c.provenance[0]?.reason?.includes("review"));
|
|
824
|
+
if (cards.length > 0) {
|
|
825
|
+
const topScore = Math.max(...cards.map((c) => c.score)).toFixed(2);
|
|
826
|
+
const parts = [];
|
|
827
|
+
if (newCards.length > 0) parts.push(`${newCards.length} new`);
|
|
828
|
+
if (reviewCards.length > 0) parts.push(`${reviewCards.length} reviews`);
|
|
829
|
+
const breakdown = parts.length > 0 ? parts.join(", ") : `${cards.length} cards`;
|
|
830
|
+
generatorSummaries.push(`${genName}: ${breakdown} (top: ${topScore})`);
|
|
831
|
+
} else {
|
|
832
|
+
generatorSummaries.push(`${genName}: 0 cards`);
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
logger.info(`[Composite] Generator breakdown: ${generatorSummaries.join(" | ")}`);
|
|
544
836
|
const byCardId = /* @__PURE__ */ new Map();
|
|
545
837
|
results.forEach((cards, index) => {
|
|
546
838
|
const gen = this.generators[index];
|
|
@@ -658,6 +950,7 @@ var init_elo = __esm({
|
|
|
658
950
|
"src/core/navigators/generators/elo.ts"() {
|
|
659
951
|
"use strict";
|
|
660
952
|
init_navigators();
|
|
953
|
+
init_logger();
|
|
661
954
|
ELONavigator = class extends ContentNavigator {
|
|
662
955
|
/** Human-readable name for CardGenerator interface */
|
|
663
956
|
name;
|
|
@@ -717,7 +1010,16 @@ var init_elo = __esm({
|
|
|
717
1010
|
};
|
|
718
1011
|
});
|
|
719
1012
|
scored.sort((a, b) => b.score - a.score);
|
|
720
|
-
|
|
1013
|
+
const result = scored.slice(0, limit);
|
|
1014
|
+
if (result.length > 0) {
|
|
1015
|
+
const topScores = result.slice(0, 3).map((c) => c.score.toFixed(2)).join(", ");
|
|
1016
|
+
logger.info(
|
|
1017
|
+
`[ELO] Course ${this.course.getCourseID()}: ${result.length} new cards (top scores: ${topScores})`
|
|
1018
|
+
);
|
|
1019
|
+
} else {
|
|
1020
|
+
logger.info(`[ELO] Course ${this.course.getCourseID()}: No new cards available`);
|
|
1021
|
+
}
|
|
1022
|
+
return result;
|
|
721
1023
|
}
|
|
722
1024
|
};
|
|
723
1025
|
}
|
|
@@ -737,18 +1039,36 @@ __export(srs_exports, {
|
|
|
737
1039
|
default: () => SRSNavigator
|
|
738
1040
|
});
|
|
739
1041
|
import moment3 from "moment";
|
|
740
|
-
var SRSNavigator;
|
|
1042
|
+
var DEFAULT_HEALTHY_BACKLOG, MAX_BACKLOG_PRESSURE, SRSNavigator;
|
|
741
1043
|
var init_srs = __esm({
|
|
742
1044
|
"src/core/navigators/generators/srs.ts"() {
|
|
743
1045
|
"use strict";
|
|
744
1046
|
init_navigators();
|
|
745
1047
|
init_logger();
|
|
1048
|
+
DEFAULT_HEALTHY_BACKLOG = 20;
|
|
1049
|
+
MAX_BACKLOG_PRESSURE = 0.5;
|
|
746
1050
|
SRSNavigator = class extends ContentNavigator {
|
|
747
1051
|
/** Human-readable name for CardGenerator interface */
|
|
748
1052
|
name;
|
|
1053
|
+
/** Healthy backlog threshold - when exceeded, backlog pressure kicks in */
|
|
1054
|
+
healthyBacklog;
|
|
749
1055
|
constructor(user, course, strategyData) {
|
|
750
1056
|
super(user, course, strategyData);
|
|
751
1057
|
this.name = strategyData?.name || "SRS";
|
|
1058
|
+
const config = this.parseConfig(strategyData?.serializedData);
|
|
1059
|
+
this.healthyBacklog = config.healthyBacklog ?? DEFAULT_HEALTHY_BACKLOG;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Parse configuration from serialized JSON.
|
|
1063
|
+
*/
|
|
1064
|
+
parseConfig(serializedData) {
|
|
1065
|
+
if (!serializedData) return {};
|
|
1066
|
+
try {
|
|
1067
|
+
return JSON.parse(serializedData);
|
|
1068
|
+
} catch {
|
|
1069
|
+
logger.warn("[SRS] Failed to parse strategy config, using defaults");
|
|
1070
|
+
return {};
|
|
1071
|
+
}
|
|
752
1072
|
}
|
|
753
1073
|
/**
|
|
754
1074
|
* Get review cards scored by urgency.
|
|
@@ -756,6 +1076,7 @@ var init_srs = __esm({
|
|
|
756
1076
|
* Score formula combines:
|
|
757
1077
|
* - Relative overdueness: hoursOverdue / intervalHours
|
|
758
1078
|
* - Interval recency: exponential decay favoring shorter intervals
|
|
1079
|
+
* - Backlog pressure: boost when due reviews exceed healthy threshold
|
|
759
1080
|
*
|
|
760
1081
|
* Cards not yet due are excluded (not scored as 0).
|
|
761
1082
|
*
|
|
@@ -769,11 +1090,32 @@ var init_srs = __esm({
|
|
|
769
1090
|
if (!this.user || !this.course) {
|
|
770
1091
|
throw new Error("SRSNavigator requires user and course to be set");
|
|
771
1092
|
}
|
|
772
|
-
const
|
|
1093
|
+
const courseId = this.course.getCourseID();
|
|
1094
|
+
const reviews = await this.user.getPendingReviews(courseId);
|
|
773
1095
|
const now = moment3.utc();
|
|
774
1096
|
const dueReviews = reviews.filter((r) => now.isAfter(moment3.utc(r.reviewTime)));
|
|
1097
|
+
const backlogPressure = this.computeBacklogPressure(dueReviews.length);
|
|
1098
|
+
if (dueReviews.length > 0) {
|
|
1099
|
+
const pressureNote = backlogPressure > 0 ? ` [backlog pressure: +${backlogPressure.toFixed(2)}]` : ` [healthy backlog]`;
|
|
1100
|
+
logger.info(
|
|
1101
|
+
`[SRS] Course ${courseId}: ${dueReviews.length} reviews due now (of ${reviews.length} scheduled)${pressureNote}`
|
|
1102
|
+
);
|
|
1103
|
+
} else if (reviews.length > 0) {
|
|
1104
|
+
const sortedByDue = [...reviews].sort(
|
|
1105
|
+
(a, b) => moment3.utc(a.reviewTime).diff(moment3.utc(b.reviewTime))
|
|
1106
|
+
);
|
|
1107
|
+
const nextDue = sortedByDue[0];
|
|
1108
|
+
const nextDueTime = moment3.utc(nextDue.reviewTime);
|
|
1109
|
+
const untilDue = moment3.duration(nextDueTime.diff(now));
|
|
1110
|
+
const untilDueStr = untilDue.asHours() < 1 ? `${Math.round(untilDue.asMinutes())}m` : untilDue.asHours() < 24 ? `${Math.round(untilDue.asHours())}h` : `${Math.round(untilDue.asDays())}d`;
|
|
1111
|
+
logger.info(
|
|
1112
|
+
`[SRS] Course ${courseId}: 0 reviews due now (${reviews.length} scheduled, next in ${untilDueStr})`
|
|
1113
|
+
);
|
|
1114
|
+
} else {
|
|
1115
|
+
logger.info(`[SRS] Course ${courseId}: No reviews scheduled`);
|
|
1116
|
+
}
|
|
775
1117
|
const scored = dueReviews.map((review) => {
|
|
776
|
-
const { score, reason } = this.computeUrgencyScore(review, now);
|
|
1118
|
+
const { score, reason } = this.computeUrgencyScore(review, now, backlogPressure);
|
|
777
1119
|
return {
|
|
778
1120
|
cardId: review.cardId,
|
|
779
1121
|
courseId: review.courseId,
|
|
@@ -791,13 +1133,35 @@ var init_srs = __esm({
|
|
|
791
1133
|
]
|
|
792
1134
|
};
|
|
793
1135
|
});
|
|
794
|
-
logger.debug(`[srsNav] got ${scored.length} weighted cards`);
|
|
795
1136
|
return scored.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
796
1137
|
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Compute backlog pressure based on number of due reviews.
|
|
1140
|
+
*
|
|
1141
|
+
* Backlog pressure is 0 when at or below healthy threshold,
|
|
1142
|
+
* and increases linearly above it, maxing out at MAX_BACKLOG_PRESSURE.
|
|
1143
|
+
*
|
|
1144
|
+
* Examples (with default healthyBacklog=20):
|
|
1145
|
+
* - 10 due reviews → 0.00 (healthy)
|
|
1146
|
+
* - 20 due reviews → 0.00 (at threshold)
|
|
1147
|
+
* - 40 due reviews → 0.25 (2x threshold)
|
|
1148
|
+
* - 60 due reviews → 0.50 (3x threshold, maxed)
|
|
1149
|
+
*
|
|
1150
|
+
* @param dueCount - Number of reviews currently due
|
|
1151
|
+
* @returns Backlog pressure score to add to urgency (0 to MAX_BACKLOG_PRESSURE)
|
|
1152
|
+
*/
|
|
1153
|
+
computeBacklogPressure(dueCount) {
|
|
1154
|
+
if (dueCount <= this.healthyBacklog) {
|
|
1155
|
+
return 0;
|
|
1156
|
+
}
|
|
1157
|
+
const excess = dueCount - this.healthyBacklog;
|
|
1158
|
+
const pressure = excess / this.healthyBacklog * (MAX_BACKLOG_PRESSURE / 2);
|
|
1159
|
+
return Math.min(MAX_BACKLOG_PRESSURE, pressure);
|
|
1160
|
+
}
|
|
797
1161
|
/**
|
|
798
1162
|
* Compute urgency score for a review card.
|
|
799
1163
|
*
|
|
800
|
-
*
|
|
1164
|
+
* Three factors:
|
|
801
1165
|
* 1. Relative overdueness = hoursOverdue / intervalHours
|
|
802
1166
|
* - 2 days overdue on 3-day interval = 0.67 (urgent)
|
|
803
1167
|
* - 2 days overdue on 180-day interval = 0.01 (not urgent)
|
|
@@ -807,10 +1171,19 @@ var init_srs = __esm({
|
|
|
807
1171
|
* - 30 days (720h) → ~0.56
|
|
808
1172
|
* - 180 days → ~0.30
|
|
809
1173
|
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
1174
|
+
* 3. Backlog pressure = global boost when review backlog exceeds healthy threshold
|
|
1175
|
+
* - At healthy backlog: 0
|
|
1176
|
+
* - At 2x healthy: +0.25
|
|
1177
|
+
* - At 3x+ healthy: +0.50 (max)
|
|
1178
|
+
*
|
|
1179
|
+
* Combined: base 0.5 + (urgency factors * 0.45) + backlog pressure
|
|
1180
|
+
* Result range: 0.5 to 1.0 (uncapped to allow high-urgency reviews to compete with new cards)
|
|
1181
|
+
*
|
|
1182
|
+
* @param review - The scheduled card to score
|
|
1183
|
+
* @param now - Current time
|
|
1184
|
+
* @param backlogPressure - Pre-computed backlog pressure (0 to 0.5)
|
|
812
1185
|
*/
|
|
813
|
-
computeUrgencyScore(review, now) {
|
|
1186
|
+
computeUrgencyScore(review, now, backlogPressure) {
|
|
814
1187
|
const scheduledAt = moment3.utc(review.scheduledAt);
|
|
815
1188
|
const due = moment3.utc(review.reviewTime);
|
|
816
1189
|
const intervalHours = Math.max(1, due.diff(scheduledAt, "hours"));
|
|
@@ -819,8 +1192,19 @@ var init_srs = __esm({
|
|
|
819
1192
|
const recencyFactor = 0.3 + 0.7 * Math.exp(-intervalHours / 720);
|
|
820
1193
|
const overdueContribution = Math.min(1, Math.max(0, relativeOverdue));
|
|
821
1194
|
const urgency = overdueContribution * 0.5 + recencyFactor * 0.5;
|
|
822
|
-
const
|
|
823
|
-
const
|
|
1195
|
+
const baseScore = 0.5 + urgency * 0.45;
|
|
1196
|
+
const score = Math.min(1, baseScore + backlogPressure);
|
|
1197
|
+
const reasonParts = [
|
|
1198
|
+
`${Math.round(hoursOverdue)}h overdue`,
|
|
1199
|
+
`interval: ${Math.round(intervalHours)}h`,
|
|
1200
|
+
`relative: ${relativeOverdue.toFixed(2)}`,
|
|
1201
|
+
`recency: ${recencyFactor.toFixed(2)}`
|
|
1202
|
+
];
|
|
1203
|
+
if (backlogPressure > 0) {
|
|
1204
|
+
reasonParts.push(`backlog: +${backlogPressure.toFixed(2)}`);
|
|
1205
|
+
}
|
|
1206
|
+
reasonParts.push("review");
|
|
1207
|
+
const reason = reasonParts.join(", ");
|
|
824
1208
|
return { score, reason };
|
|
825
1209
|
}
|
|
826
1210
|
};
|
|
@@ -1851,10 +2235,23 @@ function logTagHydration(cards, tagsByCard) {
|
|
|
1851
2235
|
`[Pipeline] Tag hydration: ${cards.length} cards, ${cardsWithTags} have tags (${totalTags} total tags) - single batch query`
|
|
1852
2236
|
);
|
|
1853
2237
|
}
|
|
1854
|
-
function logExecutionSummary(generatorName, generatedCount, filterCount, finalCount, topScores) {
|
|
2238
|
+
function logExecutionSummary(generatorName, generatedCount, filterCount, finalCount, topScores, filterImpacts) {
|
|
1855
2239
|
const scoreDisplay = topScores.length > 0 ? topScores.map((s) => s.toFixed(2)).join(", ") : "none";
|
|
2240
|
+
let filterSummary = "";
|
|
2241
|
+
if (filterImpacts.length > 0) {
|
|
2242
|
+
const impacts = filterImpacts.map((f) => {
|
|
2243
|
+
const parts = [];
|
|
2244
|
+
if (f.boosted > 0) parts.push(`+${f.boosted}`);
|
|
2245
|
+
if (f.penalized > 0) parts.push(`-${f.penalized}`);
|
|
2246
|
+
if (f.passed > 0) parts.push(`=${f.passed}`);
|
|
2247
|
+
return `${f.name}: ${parts.join("/")}`;
|
|
2248
|
+
});
|
|
2249
|
+
filterSummary = `
|
|
2250
|
+
Filter impact: ${impacts.join(", ")}`;
|
|
2251
|
+
}
|
|
1856
2252
|
logger.info(
|
|
1857
|
-
`[Pipeline] Execution: ${generatorName} produced ${generatedCount} \u2192 ${filterCount} filters \u2192 ${finalCount} results (top scores: ${scoreDisplay})`
|
|
2253
|
+
`[Pipeline] Execution: ${generatorName} produced ${generatedCount} \u2192 ${filterCount} filters \u2192 ${finalCount} results (top scores: ${scoreDisplay})` + filterSummary + `
|
|
2254
|
+
\u{1F4A1} Inspect: window.skuilder.pipeline`
|
|
1858
2255
|
);
|
|
1859
2256
|
}
|
|
1860
2257
|
function logCardProvenance(cards, maxCards = 3) {
|
|
@@ -1878,6 +2275,7 @@ var init_Pipeline = __esm({
|
|
|
1878
2275
|
init_navigators();
|
|
1879
2276
|
init_logger();
|
|
1880
2277
|
init_orchestration();
|
|
2278
|
+
init_PipelineDebugger();
|
|
1881
2279
|
Pipeline = class extends ContentNavigator {
|
|
1882
2280
|
generator;
|
|
1883
2281
|
filters;
|
|
@@ -1925,12 +2323,49 @@ var init_Pipeline = __esm({
|
|
|
1925
2323
|
);
|
|
1926
2324
|
let cards = await this.generator.getWeightedCards(fetchLimit, context);
|
|
1927
2325
|
const generatedCount = cards.length;
|
|
2326
|
+
let generatorSummaries;
|
|
2327
|
+
if (this.generator.generators) {
|
|
2328
|
+
const genMap = /* @__PURE__ */ new Map();
|
|
2329
|
+
for (const card of cards) {
|
|
2330
|
+
const firstProv = card.provenance[0];
|
|
2331
|
+
if (firstProv) {
|
|
2332
|
+
const genName = firstProv.strategyName;
|
|
2333
|
+
if (!genMap.has(genName)) {
|
|
2334
|
+
genMap.set(genName, { cards: [] });
|
|
2335
|
+
}
|
|
2336
|
+
genMap.get(genName).cards.push(card);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
generatorSummaries = Array.from(genMap.entries()).map(([name, data]) => {
|
|
2340
|
+
const newCards = data.cards.filter((c) => c.provenance[0]?.reason?.includes("new card"));
|
|
2341
|
+
const reviewCards = data.cards.filter((c) => c.provenance[0]?.reason?.includes("review"));
|
|
2342
|
+
return {
|
|
2343
|
+
name,
|
|
2344
|
+
cardCount: data.cards.length,
|
|
2345
|
+
newCount: newCards.length,
|
|
2346
|
+
reviewCount: reviewCards.length,
|
|
2347
|
+
topScore: Math.max(...data.cards.map((c) => c.score), 0)
|
|
2348
|
+
};
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
1928
2351
|
logger.debug(`[Pipeline] Generator returned ${generatedCount} candidates`);
|
|
1929
2352
|
cards = await this.hydrateTags(cards);
|
|
2353
|
+
const allCardsBeforeFiltering = [...cards];
|
|
2354
|
+
const filterImpacts = [];
|
|
1930
2355
|
for (const filter of this.filters) {
|
|
1931
2356
|
const beforeCount = cards.length;
|
|
2357
|
+
const beforeScores = new Map(cards.map((c) => [c.cardId, c.score]));
|
|
1932
2358
|
cards = await filter.transform(cards, context);
|
|
1933
|
-
|
|
2359
|
+
let boosted = 0, penalized = 0, passed = 0;
|
|
2360
|
+
const removed = beforeCount - cards.length;
|
|
2361
|
+
for (const card of cards) {
|
|
2362
|
+
const before = beforeScores.get(card.cardId) ?? 0;
|
|
2363
|
+
if (card.score > before) boosted++;
|
|
2364
|
+
else if (card.score < before) penalized++;
|
|
2365
|
+
else passed++;
|
|
2366
|
+
}
|
|
2367
|
+
filterImpacts.push({ name: filter.name, boosted, penalized, passed, removed });
|
|
2368
|
+
logger.debug(`[Pipeline] Filter '${filter.name}': ${beforeScores.size} \u2192 ${cards.length} cards (\u2191${boosted} \u2193${penalized} =${passed})`);
|
|
1934
2369
|
}
|
|
1935
2370
|
cards = cards.filter((c) => c.score > 0);
|
|
1936
2371
|
cards.sort((a, b) => b.score - a.score);
|
|
@@ -1941,9 +2376,26 @@ var init_Pipeline = __esm({
|
|
|
1941
2376
|
generatedCount,
|
|
1942
2377
|
this.filters.length,
|
|
1943
2378
|
result.length,
|
|
1944
|
-
topScores
|
|
2379
|
+
topScores,
|
|
2380
|
+
filterImpacts
|
|
1945
2381
|
);
|
|
1946
2382
|
logCardProvenance(result, 3);
|
|
2383
|
+
try {
|
|
2384
|
+
const courseName = await this.course?.getCourseConfig().then((c) => c.name).catch(() => void 0);
|
|
2385
|
+
const report = buildRunReport(
|
|
2386
|
+
this.course?.getCourseID() || "unknown",
|
|
2387
|
+
courseName,
|
|
2388
|
+
this.generator.name,
|
|
2389
|
+
generatorSummaries,
|
|
2390
|
+
generatedCount,
|
|
2391
|
+
filterImpacts,
|
|
2392
|
+
allCardsBeforeFiltering,
|
|
2393
|
+
result
|
|
2394
|
+
);
|
|
2395
|
+
captureRun(report);
|
|
2396
|
+
} catch (e) {
|
|
2397
|
+
logger.debug(`[Pipeline] Failed to capture debug run: ${e}`);
|
|
2398
|
+
}
|
|
1947
2399
|
return result;
|
|
1948
2400
|
}
|
|
1949
2401
|
/**
|
|
@@ -2033,6 +2485,56 @@ var init_Pipeline = __esm({
|
|
|
2033
2485
|
}
|
|
2034
2486
|
});
|
|
2035
2487
|
|
|
2488
|
+
// src/core/navigators/defaults.ts
|
|
2489
|
+
var defaults_exports = {};
|
|
2490
|
+
__export(defaults_exports, {
|
|
2491
|
+
createDefaultEloStrategy: () => createDefaultEloStrategy,
|
|
2492
|
+
createDefaultPipeline: () => createDefaultPipeline,
|
|
2493
|
+
createDefaultSrsStrategy: () => createDefaultSrsStrategy
|
|
2494
|
+
});
|
|
2495
|
+
function createDefaultEloStrategy(courseId) {
|
|
2496
|
+
return {
|
|
2497
|
+
_id: "NAVIGATION_STRATEGY-ELO-default",
|
|
2498
|
+
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2499
|
+
name: "ELO (default)",
|
|
2500
|
+
description: "Default ELO-based navigation strategy for new cards",
|
|
2501
|
+
implementingClass: "elo" /* ELO */,
|
|
2502
|
+
course: courseId,
|
|
2503
|
+
serializedData: ""
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
function createDefaultSrsStrategy(courseId) {
|
|
2507
|
+
return {
|
|
2508
|
+
_id: "NAVIGATION_STRATEGY-SRS-default",
|
|
2509
|
+
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2510
|
+
name: "SRS (default)",
|
|
2511
|
+
description: "Default SRS-based navigation strategy for reviews",
|
|
2512
|
+
implementingClass: "srs" /* SRS */,
|
|
2513
|
+
course: courseId,
|
|
2514
|
+
serializedData: ""
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
function createDefaultPipeline(user, course) {
|
|
2518
|
+
const courseId = course.getCourseID();
|
|
2519
|
+
const eloNavigator = new ELONavigator(user, course, createDefaultEloStrategy(courseId));
|
|
2520
|
+
const srsNavigator = new SRSNavigator(user, course, createDefaultSrsStrategy(courseId));
|
|
2521
|
+
const compositeGenerator = new CompositeGenerator([eloNavigator, srsNavigator]);
|
|
2522
|
+
const eloDistanceFilter = createEloDistanceFilter();
|
|
2523
|
+
return new Pipeline(compositeGenerator, [eloDistanceFilter], user, course);
|
|
2524
|
+
}
|
|
2525
|
+
var init_defaults = __esm({
|
|
2526
|
+
"src/core/navigators/defaults.ts"() {
|
|
2527
|
+
"use strict";
|
|
2528
|
+
init_navigators();
|
|
2529
|
+
init_Pipeline();
|
|
2530
|
+
init_CompositeGenerator();
|
|
2531
|
+
init_elo();
|
|
2532
|
+
init_srs();
|
|
2533
|
+
init_eloDistance();
|
|
2534
|
+
init_types_legacy();
|
|
2535
|
+
}
|
|
2536
|
+
});
|
|
2537
|
+
|
|
2036
2538
|
// src/core/navigators/PipelineAssembler.ts
|
|
2037
2539
|
var PipelineAssembler_exports = {};
|
|
2038
2540
|
__export(PipelineAssembler_exports, {
|
|
@@ -2045,9 +2547,9 @@ var init_PipelineAssembler = __esm({
|
|
|
2045
2547
|
init_navigators();
|
|
2046
2548
|
init_WeightedFilter();
|
|
2047
2549
|
init_Pipeline();
|
|
2048
|
-
init_types_legacy();
|
|
2049
2550
|
init_logger();
|
|
2050
2551
|
init_CompositeGenerator();
|
|
2552
|
+
init_defaults();
|
|
2051
2553
|
PipelineAssembler = class {
|
|
2052
2554
|
/**
|
|
2053
2555
|
* Assembles a navigation pipeline from strategy documents.
|
|
@@ -2086,9 +2588,11 @@ var init_PipelineAssembler = __esm({
|
|
|
2086
2588
|
if (generatorStrategies.length === 0) {
|
|
2087
2589
|
if (filterStrategies.length > 0) {
|
|
2088
2590
|
logger.debug(
|
|
2089
|
-
"[PipelineAssembler] No generator found, using default ELO with configured filters"
|
|
2591
|
+
"[PipelineAssembler] No generator found, using default ELO and SRS with configured filters"
|
|
2090
2592
|
);
|
|
2091
|
-
|
|
2593
|
+
const courseId = course.getCourseID();
|
|
2594
|
+
generatorStrategies.push(createDefaultEloStrategy(courseId));
|
|
2595
|
+
generatorStrategies.push(createDefaultSrsStrategy(courseId));
|
|
2092
2596
|
} else {
|
|
2093
2597
|
warnings.push("No generator strategy found");
|
|
2094
2598
|
return {
|
|
@@ -2149,75 +2653,10 @@ var init_PipelineAssembler = __esm({
|
|
|
2149
2653
|
warnings
|
|
2150
2654
|
};
|
|
2151
2655
|
}
|
|
2152
|
-
/**
|
|
2153
|
-
* Creates a default ELO generator strategy.
|
|
2154
|
-
* Used when filters are configured but no generator is specified.
|
|
2155
|
-
*/
|
|
2156
|
-
makeDefaultEloStrategy(courseId) {
|
|
2157
|
-
return {
|
|
2158
|
-
_id: "NAVIGATION_STRATEGY-ELO-default",
|
|
2159
|
-
course: courseId,
|
|
2160
|
-
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2161
|
-
name: "ELO (default)",
|
|
2162
|
-
description: "Default ELO-based generator",
|
|
2163
|
-
implementingClass: "elo" /* ELO */,
|
|
2164
|
-
serializedData: ""
|
|
2165
|
-
};
|
|
2166
|
-
}
|
|
2167
2656
|
};
|
|
2168
2657
|
}
|
|
2169
2658
|
});
|
|
2170
2659
|
|
|
2171
|
-
// src/core/navigators/defaults.ts
|
|
2172
|
-
var defaults_exports = {};
|
|
2173
|
-
__export(defaults_exports, {
|
|
2174
|
-
createDefaultEloStrategy: () => createDefaultEloStrategy,
|
|
2175
|
-
createDefaultPipeline: () => createDefaultPipeline,
|
|
2176
|
-
createDefaultSrsStrategy: () => createDefaultSrsStrategy
|
|
2177
|
-
});
|
|
2178
|
-
function createDefaultEloStrategy(courseId) {
|
|
2179
|
-
return {
|
|
2180
|
-
_id: "NAVIGATION_STRATEGY-ELO-default",
|
|
2181
|
-
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2182
|
-
name: "ELO (default)",
|
|
2183
|
-
description: "Default ELO-based navigation strategy for new cards",
|
|
2184
|
-
implementingClass: "elo" /* ELO */,
|
|
2185
|
-
course: courseId,
|
|
2186
|
-
serializedData: ""
|
|
2187
|
-
};
|
|
2188
|
-
}
|
|
2189
|
-
function createDefaultSrsStrategy(courseId) {
|
|
2190
|
-
return {
|
|
2191
|
-
_id: "NAVIGATION_STRATEGY-SRS-default",
|
|
2192
|
-
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2193
|
-
name: "SRS (default)",
|
|
2194
|
-
description: "Default SRS-based navigation strategy for reviews",
|
|
2195
|
-
implementingClass: "srs" /* SRS */,
|
|
2196
|
-
course: courseId,
|
|
2197
|
-
serializedData: ""
|
|
2198
|
-
};
|
|
2199
|
-
}
|
|
2200
|
-
function createDefaultPipeline(user, course) {
|
|
2201
|
-
const courseId = course.getCourseID();
|
|
2202
|
-
const eloNavigator = new ELONavigator(user, course, createDefaultEloStrategy(courseId));
|
|
2203
|
-
const srsNavigator = new SRSNavigator(user, course, createDefaultSrsStrategy(courseId));
|
|
2204
|
-
const compositeGenerator = new CompositeGenerator([eloNavigator, srsNavigator]);
|
|
2205
|
-
const eloDistanceFilter = createEloDistanceFilter();
|
|
2206
|
-
return new Pipeline(compositeGenerator, [eloDistanceFilter], user, course);
|
|
2207
|
-
}
|
|
2208
|
-
var init_defaults = __esm({
|
|
2209
|
-
"src/core/navigators/defaults.ts"() {
|
|
2210
|
-
"use strict";
|
|
2211
|
-
init_navigators();
|
|
2212
|
-
init_Pipeline();
|
|
2213
|
-
init_CompositeGenerator();
|
|
2214
|
-
init_elo();
|
|
2215
|
-
init_srs();
|
|
2216
|
-
init_eloDistance();
|
|
2217
|
-
init_types_legacy();
|
|
2218
|
-
}
|
|
2219
|
-
});
|
|
2220
|
-
|
|
2221
2660
|
// import("./**/*") in src/core/navigators/index.ts
|
|
2222
2661
|
var globImport;
|
|
2223
2662
|
var init_3 = __esm({
|
|
@@ -2225,6 +2664,7 @@ var init_3 = __esm({
|
|
|
2225
2664
|
globImport = __glob({
|
|
2226
2665
|
"./Pipeline.ts": () => Promise.resolve().then(() => (init_Pipeline(), Pipeline_exports)),
|
|
2227
2666
|
"./PipelineAssembler.ts": () => Promise.resolve().then(() => (init_PipelineAssembler(), PipelineAssembler_exports)),
|
|
2667
|
+
"./PipelineDebugger.ts": () => Promise.resolve().then(() => (init_PipelineDebugger(), PipelineDebugger_exports)),
|
|
2228
2668
|
"./defaults.ts": () => Promise.resolve().then(() => (init_defaults(), defaults_exports)),
|
|
2229
2669
|
"./filters/WeightedFilter.ts": () => Promise.resolve().then(() => (init_WeightedFilter(), WeightedFilter_exports)),
|
|
2230
2670
|
"./filters/eloDistance.ts": () => Promise.resolve().then(() => (init_eloDistance(), eloDistance_exports)),
|
|
@@ -2260,6 +2700,8 @@ __export(navigators_exports, {
|
|
|
2260
2700
|
initializeNavigatorRegistry: () => initializeNavigatorRegistry,
|
|
2261
2701
|
isFilter: () => isFilter,
|
|
2262
2702
|
isGenerator: () => isGenerator,
|
|
2703
|
+
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
2704
|
+
pipelineDebugAPI: () => pipelineDebugAPI,
|
|
2263
2705
|
registerNavigator: () => registerNavigator
|
|
2264
2706
|
});
|
|
2265
2707
|
function registerNavigator(implementingClass, constructor) {
|
|
@@ -2326,6 +2768,7 @@ var navigatorRegistry, Navigators, NavigatorRole, NavigatorRoles, ContentNavigat
|
|
|
2326
2768
|
var init_navigators = __esm({
|
|
2327
2769
|
"src/core/navigators/index.ts"() {
|
|
2328
2770
|
"use strict";
|
|
2771
|
+
init_PipelineDebugger();
|
|
2329
2772
|
init_logger();
|
|
2330
2773
|
init_();
|
|
2331
2774
|
init_2();
|