@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
package/dist/core/index.js
CHANGED
|
@@ -471,6 +471,15 @@ var init_user_course_relDB = __esm({
|
|
|
471
471
|
void this.user.updateCourseSettings(this._courseId, updates);
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
|
+
async getStrategyState(strategyKey) {
|
|
475
|
+
return this.user.getStrategyState(this._courseId, strategyKey);
|
|
476
|
+
}
|
|
477
|
+
async putStrategyState(strategyKey, data) {
|
|
478
|
+
return this.user.putStrategyState(this._courseId, strategyKey, data);
|
|
479
|
+
}
|
|
480
|
+
async deleteStrategyState(strategyKey) {
|
|
481
|
+
return this.user.deleteStrategyState(this._courseId, strategyKey);
|
|
482
|
+
}
|
|
474
483
|
async getReviewstoDate(targetDate) {
|
|
475
484
|
const allReviews = await this.user.getPendingReviews(this._courseId);
|
|
476
485
|
logger.debug(
|
|
@@ -710,6 +719,271 @@ var init_courseLookupDB = __esm({
|
|
|
710
719
|
}
|
|
711
720
|
});
|
|
712
721
|
|
|
722
|
+
// src/core/navigators/PipelineDebugger.ts
|
|
723
|
+
var PipelineDebugger_exports = {};
|
|
724
|
+
__export(PipelineDebugger_exports, {
|
|
725
|
+
buildRunReport: () => buildRunReport,
|
|
726
|
+
captureRun: () => captureRun,
|
|
727
|
+
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
728
|
+
pipelineDebugAPI: () => pipelineDebugAPI
|
|
729
|
+
});
|
|
730
|
+
function getOrigin(card) {
|
|
731
|
+
const firstEntry = card.provenance[0];
|
|
732
|
+
if (!firstEntry) return "unknown";
|
|
733
|
+
const reason = firstEntry.reason?.toLowerCase() || "";
|
|
734
|
+
if (reason.includes("new card")) return "new";
|
|
735
|
+
if (reason.includes("review")) return "review";
|
|
736
|
+
return "unknown";
|
|
737
|
+
}
|
|
738
|
+
function captureRun(report) {
|
|
739
|
+
const fullReport = {
|
|
740
|
+
...report,
|
|
741
|
+
runId: `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
742
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
743
|
+
};
|
|
744
|
+
runHistory.unshift(fullReport);
|
|
745
|
+
if (runHistory.length > MAX_RUNS) {
|
|
746
|
+
runHistory.pop();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function buildRunReport(courseId, courseName, generatorName, generators, generatedCount, filters, allCards, selectedCards) {
|
|
750
|
+
const selectedIds = new Set(selectedCards.map((c) => c.cardId));
|
|
751
|
+
const cards = allCards.map((card) => ({
|
|
752
|
+
cardId: card.cardId,
|
|
753
|
+
courseId: card.courseId,
|
|
754
|
+
origin: getOrigin(card),
|
|
755
|
+
finalScore: card.score,
|
|
756
|
+
provenance: card.provenance,
|
|
757
|
+
selected: selectedIds.has(card.cardId)
|
|
758
|
+
}));
|
|
759
|
+
const reviewsSelected = selectedCards.filter((c) => getOrigin(c) === "review").length;
|
|
760
|
+
const newSelected = selectedCards.filter((c) => getOrigin(c) === "new").length;
|
|
761
|
+
return {
|
|
762
|
+
courseId,
|
|
763
|
+
courseName,
|
|
764
|
+
generatorName,
|
|
765
|
+
generators,
|
|
766
|
+
generatedCount,
|
|
767
|
+
filters,
|
|
768
|
+
finalCount: selectedCards.length,
|
|
769
|
+
reviewsSelected,
|
|
770
|
+
newSelected,
|
|
771
|
+
cards
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
function formatProvenance(provenance) {
|
|
775
|
+
return provenance.map((p) => {
|
|
776
|
+
const actionSymbol = p.action === "generated" ? "\u{1F3B2}" : p.action === "boosted" ? "\u2B06\uFE0F" : p.action === "penalized" ? "\u2B07\uFE0F" : "\u27A1\uFE0F";
|
|
777
|
+
return ` ${actionSymbol} ${p.strategyName}: ${p.score.toFixed(3)} - ${p.reason}`;
|
|
778
|
+
}).join("\n");
|
|
779
|
+
}
|
|
780
|
+
function printRunSummary(run) {
|
|
781
|
+
console.group(`\u{1F50D} Pipeline Run: ${run.courseId} (${run.courseName || "unnamed"})`);
|
|
782
|
+
logger.info(`Run ID: ${run.runId}`);
|
|
783
|
+
logger.info(`Time: ${run.timestamp.toISOString()}`);
|
|
784
|
+
logger.info(`Generator: ${run.generatorName} \u2192 ${run.generatedCount} candidates`);
|
|
785
|
+
if (run.generators && run.generators.length > 0) {
|
|
786
|
+
console.group("Generator breakdown:");
|
|
787
|
+
for (const g of run.generators) {
|
|
788
|
+
logger.info(
|
|
789
|
+
` ${g.name}: ${g.cardCount} cards (${g.newCount} new, ${g.reviewCount} reviews, top: ${g.topScore.toFixed(2)})`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
console.groupEnd();
|
|
793
|
+
}
|
|
794
|
+
if (run.filters.length > 0) {
|
|
795
|
+
console.group("Filter impact:");
|
|
796
|
+
for (const f of run.filters) {
|
|
797
|
+
logger.info(` ${f.name}: \u2191${f.boosted} \u2193${f.penalized} =${f.passed} \u2715${f.removed}`);
|
|
798
|
+
}
|
|
799
|
+
console.groupEnd();
|
|
800
|
+
}
|
|
801
|
+
logger.info(
|
|
802
|
+
`Result: ${run.finalCount} cards selected (${run.newSelected} new, ${run.reviewsSelected} reviews)`
|
|
803
|
+
);
|
|
804
|
+
console.groupEnd();
|
|
805
|
+
}
|
|
806
|
+
function mountPipelineDebugger() {
|
|
807
|
+
if (typeof window === "undefined") return;
|
|
808
|
+
const win = window;
|
|
809
|
+
win.skuilder = win.skuilder || {};
|
|
810
|
+
win.skuilder.pipeline = pipelineDebugAPI;
|
|
811
|
+
}
|
|
812
|
+
var MAX_RUNS, runHistory, pipelineDebugAPI;
|
|
813
|
+
var init_PipelineDebugger = __esm({
|
|
814
|
+
"src/core/navigators/PipelineDebugger.ts"() {
|
|
815
|
+
"use strict";
|
|
816
|
+
init_logger();
|
|
817
|
+
MAX_RUNS = 10;
|
|
818
|
+
runHistory = [];
|
|
819
|
+
pipelineDebugAPI = {
|
|
820
|
+
/**
|
|
821
|
+
* Get raw run history for programmatic access.
|
|
822
|
+
*/
|
|
823
|
+
get runs() {
|
|
824
|
+
return [...runHistory];
|
|
825
|
+
},
|
|
826
|
+
/**
|
|
827
|
+
* Show summary of a specific pipeline run.
|
|
828
|
+
*/
|
|
829
|
+
showRun(idOrIndex = 0) {
|
|
830
|
+
if (runHistory.length === 0) {
|
|
831
|
+
logger.info("[Pipeline Debug] No runs captured yet.");
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
let run;
|
|
835
|
+
if (typeof idOrIndex === "number") {
|
|
836
|
+
run = runHistory[idOrIndex];
|
|
837
|
+
if (!run) {
|
|
838
|
+
logger.info(
|
|
839
|
+
`[Pipeline Debug] No run found at index ${idOrIndex}. History length: ${runHistory.length}`
|
|
840
|
+
);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
} else {
|
|
844
|
+
run = runHistory.find((r) => r.runId.endsWith(idOrIndex));
|
|
845
|
+
if (!run) {
|
|
846
|
+
logger.info(`[Pipeline Debug] No run found matching ID '${idOrIndex}'.`);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
printRunSummary(run);
|
|
851
|
+
},
|
|
852
|
+
/**
|
|
853
|
+
* Show summary of the last pipeline run.
|
|
854
|
+
*/
|
|
855
|
+
showLastRun() {
|
|
856
|
+
this.showRun(0);
|
|
857
|
+
},
|
|
858
|
+
/**
|
|
859
|
+
* Show detailed provenance for a specific card.
|
|
860
|
+
*/
|
|
861
|
+
showCard(cardId) {
|
|
862
|
+
for (const run of runHistory) {
|
|
863
|
+
const card = run.cards.find((c) => c.cardId === cardId);
|
|
864
|
+
if (card) {
|
|
865
|
+
console.group(`\u{1F3B4} Card: ${cardId}`);
|
|
866
|
+
logger.info(`Course: ${card.courseId}`);
|
|
867
|
+
logger.info(`Origin: ${card.origin}`);
|
|
868
|
+
logger.info(`Final score: ${card.finalScore.toFixed(3)}`);
|
|
869
|
+
logger.info(`Selected: ${card.selected ? "Yes \u2705" : "No \u274C"}`);
|
|
870
|
+
logger.info("Provenance:");
|
|
871
|
+
logger.info(formatProvenance(card.provenance));
|
|
872
|
+
console.groupEnd();
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
logger.info(`[Pipeline Debug] Card '${cardId}' not found in recent runs.`);
|
|
877
|
+
},
|
|
878
|
+
/**
|
|
879
|
+
* Explain why reviews may or may not have been selected.
|
|
880
|
+
*/
|
|
881
|
+
explainReviews() {
|
|
882
|
+
if (runHistory.length === 0) {
|
|
883
|
+
logger.info("[Pipeline Debug] No runs captured yet.");
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
console.group("\u{1F4CB} Review Selection Analysis");
|
|
887
|
+
for (const run of runHistory) {
|
|
888
|
+
console.group(`Run: ${run.courseId} @ ${run.timestamp.toLocaleTimeString()}`);
|
|
889
|
+
const allReviews = run.cards.filter((c) => c.origin === "review");
|
|
890
|
+
const selectedReviews = allReviews.filter((c) => c.selected);
|
|
891
|
+
if (allReviews.length === 0) {
|
|
892
|
+
logger.info("\u274C No reviews were generated. Check SRS logs for why.");
|
|
893
|
+
} else if (selectedReviews.length === 0) {
|
|
894
|
+
logger.info(`\u26A0\uFE0F ${allReviews.length} reviews generated but none selected.`);
|
|
895
|
+
logger.info("Possible reasons:");
|
|
896
|
+
const topNewScore = Math.max(
|
|
897
|
+
...run.cards.filter((c) => c.origin === "new" && c.selected).map((c) => c.finalScore),
|
|
898
|
+
0
|
|
899
|
+
);
|
|
900
|
+
const topReviewScore = Math.max(...allReviews.map((c) => c.finalScore), 0);
|
|
901
|
+
if (topReviewScore < topNewScore) {
|
|
902
|
+
logger.info(
|
|
903
|
+
` - New cards scored higher (top new: ${topNewScore.toFixed(2)}, top review: ${topReviewScore.toFixed(2)})`
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
const topReview = allReviews.sort((a, b) => b.finalScore - a.finalScore)[0];
|
|
907
|
+
if (topReview) {
|
|
908
|
+
logger.info(` - Top review score: ${topReview.finalScore.toFixed(3)}`);
|
|
909
|
+
logger.info(" - Its provenance:");
|
|
910
|
+
logger.info(formatProvenance(topReview.provenance));
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
logger.info(`\u2705 ${selectedReviews.length}/${allReviews.length} reviews selected.`);
|
|
914
|
+
logger.info("Top selected review:");
|
|
915
|
+
const topSelected = selectedReviews.sort((a, b) => b.finalScore - a.finalScore)[0];
|
|
916
|
+
logger.info(formatProvenance(topSelected.provenance));
|
|
917
|
+
}
|
|
918
|
+
console.groupEnd();
|
|
919
|
+
}
|
|
920
|
+
console.groupEnd();
|
|
921
|
+
},
|
|
922
|
+
/**
|
|
923
|
+
* Show all runs in compact format.
|
|
924
|
+
*/
|
|
925
|
+
listRuns() {
|
|
926
|
+
if (runHistory.length === 0) {
|
|
927
|
+
logger.info("[Pipeline Debug] No runs captured yet.");
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
console.table(
|
|
931
|
+
runHistory.map((r) => ({
|
|
932
|
+
id: r.runId.slice(-8),
|
|
933
|
+
time: r.timestamp.toLocaleTimeString(),
|
|
934
|
+
course: r.courseName || r.courseId.slice(0, 8),
|
|
935
|
+
generated: r.generatedCount,
|
|
936
|
+
selected: r.finalCount,
|
|
937
|
+
new: r.newSelected,
|
|
938
|
+
reviews: r.reviewsSelected
|
|
939
|
+
}))
|
|
940
|
+
);
|
|
941
|
+
},
|
|
942
|
+
/**
|
|
943
|
+
* Export run history as JSON for bug reports.
|
|
944
|
+
*/
|
|
945
|
+
export() {
|
|
946
|
+
const json = JSON.stringify(runHistory, null, 2);
|
|
947
|
+
logger.info("[Pipeline Debug] Run history exported. Copy the returned string or use:");
|
|
948
|
+
logger.info(" copy(window.skuilder.pipeline.export())");
|
|
949
|
+
return json;
|
|
950
|
+
},
|
|
951
|
+
/**
|
|
952
|
+
* Clear run history.
|
|
953
|
+
*/
|
|
954
|
+
clear() {
|
|
955
|
+
runHistory.length = 0;
|
|
956
|
+
logger.info("[Pipeline Debug] Run history cleared.");
|
|
957
|
+
},
|
|
958
|
+
/**
|
|
959
|
+
* Show help.
|
|
960
|
+
*/
|
|
961
|
+
help() {
|
|
962
|
+
logger.info(`
|
|
963
|
+
\u{1F527} Pipeline Debug API
|
|
964
|
+
|
|
965
|
+
Commands:
|
|
966
|
+
.showLastRun() Show summary of most recent pipeline run
|
|
967
|
+
.showRun(id|index) Show summary of a specific run (by index or ID suffix)
|
|
968
|
+
.showCard(cardId) Show provenance trail for a specific card
|
|
969
|
+
.explainReviews() Analyze why reviews were/weren't selected
|
|
970
|
+
.listRuns() List all captured runs in table format
|
|
971
|
+
.export() Export run history as JSON for bug reports
|
|
972
|
+
.clear() Clear run history
|
|
973
|
+
.runs Access raw run history array
|
|
974
|
+
.help() Show this help message
|
|
975
|
+
|
|
976
|
+
Example:
|
|
977
|
+
window.skuilder.pipeline.showLastRun()
|
|
978
|
+
window.skuilder.pipeline.showRun(1)
|
|
979
|
+
window.skuilder.pipeline.showCard('abc123')
|
|
980
|
+
`);
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
mountPipelineDebugger();
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
|
|
713
987
|
// src/core/navigators/generators/CompositeGenerator.ts
|
|
714
988
|
var CompositeGenerator_exports = {};
|
|
715
989
|
__export(CompositeGenerator_exports, {
|
|
@@ -778,6 +1052,24 @@ var init_CompositeGenerator = __esm({
|
|
|
778
1052
|
const results = await Promise.all(
|
|
779
1053
|
this.generators.map((g) => g.getWeightedCards(limit, context))
|
|
780
1054
|
);
|
|
1055
|
+
const generatorSummaries = [];
|
|
1056
|
+
results.forEach((cards, index) => {
|
|
1057
|
+
const gen = this.generators[index];
|
|
1058
|
+
const genName = gen.name || `Generator ${index}`;
|
|
1059
|
+
const newCards = cards.filter((c) => c.provenance[0]?.reason?.includes("new card"));
|
|
1060
|
+
const reviewCards = cards.filter((c) => c.provenance[0]?.reason?.includes("review"));
|
|
1061
|
+
if (cards.length > 0) {
|
|
1062
|
+
const topScore = Math.max(...cards.map((c) => c.score)).toFixed(2);
|
|
1063
|
+
const parts = [];
|
|
1064
|
+
if (newCards.length > 0) parts.push(`${newCards.length} new`);
|
|
1065
|
+
if (reviewCards.length > 0) parts.push(`${reviewCards.length} reviews`);
|
|
1066
|
+
const breakdown = parts.length > 0 ? parts.join(", ") : `${cards.length} cards`;
|
|
1067
|
+
generatorSummaries.push(`${genName}: ${breakdown} (top: ${topScore})`);
|
|
1068
|
+
} else {
|
|
1069
|
+
generatorSummaries.push(`${genName}: 0 cards`);
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
logger.info(`[Composite] Generator breakdown: ${generatorSummaries.join(" | ")}`);
|
|
781
1073
|
const byCardId = /* @__PURE__ */ new Map();
|
|
782
1074
|
results.forEach((cards, index) => {
|
|
783
1075
|
const gen = this.generators[index];
|
|
@@ -895,6 +1187,7 @@ var init_elo = __esm({
|
|
|
895
1187
|
"use strict";
|
|
896
1188
|
init_navigators();
|
|
897
1189
|
import_common5 = require("@vue-skuilder/common");
|
|
1190
|
+
init_logger();
|
|
898
1191
|
ELONavigator = class extends ContentNavigator {
|
|
899
1192
|
/** Human-readable name for CardGenerator interface */
|
|
900
1193
|
name;
|
|
@@ -954,7 +1247,16 @@ var init_elo = __esm({
|
|
|
954
1247
|
};
|
|
955
1248
|
});
|
|
956
1249
|
scored.sort((a, b) => b.score - a.score);
|
|
957
|
-
|
|
1250
|
+
const result = scored.slice(0, limit);
|
|
1251
|
+
if (result.length > 0) {
|
|
1252
|
+
const topScores = result.slice(0, 3).map((c) => c.score.toFixed(2)).join(", ");
|
|
1253
|
+
logger.info(
|
|
1254
|
+
`[ELO] Course ${this.course.getCourseID()}: ${result.length} new cards (top scores: ${topScores})`
|
|
1255
|
+
);
|
|
1256
|
+
} else {
|
|
1257
|
+
logger.info(`[ELO] Course ${this.course.getCourseID()}: No new cards available`);
|
|
1258
|
+
}
|
|
1259
|
+
return result;
|
|
958
1260
|
}
|
|
959
1261
|
};
|
|
960
1262
|
}
|
|
@@ -973,19 +1275,37 @@ var srs_exports = {};
|
|
|
973
1275
|
__export(srs_exports, {
|
|
974
1276
|
default: () => SRSNavigator
|
|
975
1277
|
});
|
|
976
|
-
var import_moment3, SRSNavigator;
|
|
1278
|
+
var import_moment3, DEFAULT_HEALTHY_BACKLOG, MAX_BACKLOG_PRESSURE, SRSNavigator;
|
|
977
1279
|
var init_srs = __esm({
|
|
978
1280
|
"src/core/navigators/generators/srs.ts"() {
|
|
979
1281
|
"use strict";
|
|
980
1282
|
import_moment3 = __toESM(require("moment"), 1);
|
|
981
1283
|
init_navigators();
|
|
982
1284
|
init_logger();
|
|
1285
|
+
DEFAULT_HEALTHY_BACKLOG = 20;
|
|
1286
|
+
MAX_BACKLOG_PRESSURE = 0.5;
|
|
983
1287
|
SRSNavigator = class extends ContentNavigator {
|
|
984
1288
|
/** Human-readable name for CardGenerator interface */
|
|
985
1289
|
name;
|
|
1290
|
+
/** Healthy backlog threshold - when exceeded, backlog pressure kicks in */
|
|
1291
|
+
healthyBacklog;
|
|
986
1292
|
constructor(user, course, strategyData) {
|
|
987
1293
|
super(user, course, strategyData);
|
|
988
1294
|
this.name = strategyData?.name || "SRS";
|
|
1295
|
+
const config = this.parseConfig(strategyData?.serializedData);
|
|
1296
|
+
this.healthyBacklog = config.healthyBacklog ?? DEFAULT_HEALTHY_BACKLOG;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Parse configuration from serialized JSON.
|
|
1300
|
+
*/
|
|
1301
|
+
parseConfig(serializedData) {
|
|
1302
|
+
if (!serializedData) return {};
|
|
1303
|
+
try {
|
|
1304
|
+
return JSON.parse(serializedData);
|
|
1305
|
+
} catch {
|
|
1306
|
+
logger.warn("[SRS] Failed to parse strategy config, using defaults");
|
|
1307
|
+
return {};
|
|
1308
|
+
}
|
|
989
1309
|
}
|
|
990
1310
|
/**
|
|
991
1311
|
* Get review cards scored by urgency.
|
|
@@ -993,6 +1313,7 @@ var init_srs = __esm({
|
|
|
993
1313
|
* Score formula combines:
|
|
994
1314
|
* - Relative overdueness: hoursOverdue / intervalHours
|
|
995
1315
|
* - Interval recency: exponential decay favoring shorter intervals
|
|
1316
|
+
* - Backlog pressure: boost when due reviews exceed healthy threshold
|
|
996
1317
|
*
|
|
997
1318
|
* Cards not yet due are excluded (not scored as 0).
|
|
998
1319
|
*
|
|
@@ -1006,11 +1327,32 @@ var init_srs = __esm({
|
|
|
1006
1327
|
if (!this.user || !this.course) {
|
|
1007
1328
|
throw new Error("SRSNavigator requires user and course to be set");
|
|
1008
1329
|
}
|
|
1009
|
-
const
|
|
1330
|
+
const courseId = this.course.getCourseID();
|
|
1331
|
+
const reviews = await this.user.getPendingReviews(courseId);
|
|
1010
1332
|
const now = import_moment3.default.utc();
|
|
1011
1333
|
const dueReviews = reviews.filter((r) => now.isAfter(import_moment3.default.utc(r.reviewTime)));
|
|
1334
|
+
const backlogPressure = this.computeBacklogPressure(dueReviews.length);
|
|
1335
|
+
if (dueReviews.length > 0) {
|
|
1336
|
+
const pressureNote = backlogPressure > 0 ? ` [backlog pressure: +${backlogPressure.toFixed(2)}]` : ` [healthy backlog]`;
|
|
1337
|
+
logger.info(
|
|
1338
|
+
`[SRS] Course ${courseId}: ${dueReviews.length} reviews due now (of ${reviews.length} scheduled)${pressureNote}`
|
|
1339
|
+
);
|
|
1340
|
+
} else if (reviews.length > 0) {
|
|
1341
|
+
const sortedByDue = [...reviews].sort(
|
|
1342
|
+
(a, b) => import_moment3.default.utc(a.reviewTime).diff(import_moment3.default.utc(b.reviewTime))
|
|
1343
|
+
);
|
|
1344
|
+
const nextDue = sortedByDue[0];
|
|
1345
|
+
const nextDueTime = import_moment3.default.utc(nextDue.reviewTime);
|
|
1346
|
+
const untilDue = import_moment3.default.duration(nextDueTime.diff(now));
|
|
1347
|
+
const untilDueStr = untilDue.asHours() < 1 ? `${Math.round(untilDue.asMinutes())}m` : untilDue.asHours() < 24 ? `${Math.round(untilDue.asHours())}h` : `${Math.round(untilDue.asDays())}d`;
|
|
1348
|
+
logger.info(
|
|
1349
|
+
`[SRS] Course ${courseId}: 0 reviews due now (${reviews.length} scheduled, next in ${untilDueStr})`
|
|
1350
|
+
);
|
|
1351
|
+
} else {
|
|
1352
|
+
logger.info(`[SRS] Course ${courseId}: No reviews scheduled`);
|
|
1353
|
+
}
|
|
1012
1354
|
const scored = dueReviews.map((review) => {
|
|
1013
|
-
const { score, reason } = this.computeUrgencyScore(review, now);
|
|
1355
|
+
const { score, reason } = this.computeUrgencyScore(review, now, backlogPressure);
|
|
1014
1356
|
return {
|
|
1015
1357
|
cardId: review.cardId,
|
|
1016
1358
|
courseId: review.courseId,
|
|
@@ -1028,13 +1370,35 @@ var init_srs = __esm({
|
|
|
1028
1370
|
]
|
|
1029
1371
|
};
|
|
1030
1372
|
});
|
|
1031
|
-
logger.debug(`[srsNav] got ${scored.length} weighted cards`);
|
|
1032
1373
|
return scored.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1033
1374
|
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Compute backlog pressure based on number of due reviews.
|
|
1377
|
+
*
|
|
1378
|
+
* Backlog pressure is 0 when at or below healthy threshold,
|
|
1379
|
+
* and increases linearly above it, maxing out at MAX_BACKLOG_PRESSURE.
|
|
1380
|
+
*
|
|
1381
|
+
* Examples (with default healthyBacklog=20):
|
|
1382
|
+
* - 10 due reviews → 0.00 (healthy)
|
|
1383
|
+
* - 20 due reviews → 0.00 (at threshold)
|
|
1384
|
+
* - 40 due reviews → 0.25 (2x threshold)
|
|
1385
|
+
* - 60 due reviews → 0.50 (3x threshold, maxed)
|
|
1386
|
+
*
|
|
1387
|
+
* @param dueCount - Number of reviews currently due
|
|
1388
|
+
* @returns Backlog pressure score to add to urgency (0 to MAX_BACKLOG_PRESSURE)
|
|
1389
|
+
*/
|
|
1390
|
+
computeBacklogPressure(dueCount) {
|
|
1391
|
+
if (dueCount <= this.healthyBacklog) {
|
|
1392
|
+
return 0;
|
|
1393
|
+
}
|
|
1394
|
+
const excess = dueCount - this.healthyBacklog;
|
|
1395
|
+
const pressure = excess / this.healthyBacklog * (MAX_BACKLOG_PRESSURE / 2);
|
|
1396
|
+
return Math.min(MAX_BACKLOG_PRESSURE, pressure);
|
|
1397
|
+
}
|
|
1034
1398
|
/**
|
|
1035
1399
|
* Compute urgency score for a review card.
|
|
1036
1400
|
*
|
|
1037
|
-
*
|
|
1401
|
+
* Three factors:
|
|
1038
1402
|
* 1. Relative overdueness = hoursOverdue / intervalHours
|
|
1039
1403
|
* - 2 days overdue on 3-day interval = 0.67 (urgent)
|
|
1040
1404
|
* - 2 days overdue on 180-day interval = 0.01 (not urgent)
|
|
@@ -1044,10 +1408,19 @@ var init_srs = __esm({
|
|
|
1044
1408
|
* - 30 days (720h) → ~0.56
|
|
1045
1409
|
* - 180 days → ~0.30
|
|
1046
1410
|
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1411
|
+
* 3. Backlog pressure = global boost when review backlog exceeds healthy threshold
|
|
1412
|
+
* - At healthy backlog: 0
|
|
1413
|
+
* - At 2x healthy: +0.25
|
|
1414
|
+
* - At 3x+ healthy: +0.50 (max)
|
|
1415
|
+
*
|
|
1416
|
+
* Combined: base 0.5 + (urgency factors * 0.45) + backlog pressure
|
|
1417
|
+
* Result range: 0.5 to 1.0 (uncapped to allow high-urgency reviews to compete with new cards)
|
|
1418
|
+
*
|
|
1419
|
+
* @param review - The scheduled card to score
|
|
1420
|
+
* @param now - Current time
|
|
1421
|
+
* @param backlogPressure - Pre-computed backlog pressure (0 to 0.5)
|
|
1049
1422
|
*/
|
|
1050
|
-
computeUrgencyScore(review, now) {
|
|
1423
|
+
computeUrgencyScore(review, now, backlogPressure) {
|
|
1051
1424
|
const scheduledAt = import_moment3.default.utc(review.scheduledAt);
|
|
1052
1425
|
const due = import_moment3.default.utc(review.reviewTime);
|
|
1053
1426
|
const intervalHours = Math.max(1, due.diff(scheduledAt, "hours"));
|
|
@@ -1056,8 +1429,19 @@ var init_srs = __esm({
|
|
|
1056
1429
|
const recencyFactor = 0.3 + 0.7 * Math.exp(-intervalHours / 720);
|
|
1057
1430
|
const overdueContribution = Math.min(1, Math.max(0, relativeOverdue));
|
|
1058
1431
|
const urgency = overdueContribution * 0.5 + recencyFactor * 0.5;
|
|
1059
|
-
const
|
|
1060
|
-
const
|
|
1432
|
+
const baseScore = 0.5 + urgency * 0.45;
|
|
1433
|
+
const score = Math.min(1, baseScore + backlogPressure);
|
|
1434
|
+
const reasonParts = [
|
|
1435
|
+
`${Math.round(hoursOverdue)}h overdue`,
|
|
1436
|
+
`interval: ${Math.round(intervalHours)}h`,
|
|
1437
|
+
`relative: ${relativeOverdue.toFixed(2)}`,
|
|
1438
|
+
`recency: ${recencyFactor.toFixed(2)}`
|
|
1439
|
+
];
|
|
1440
|
+
if (backlogPressure > 0) {
|
|
1441
|
+
reasonParts.push(`backlog: +${backlogPressure.toFixed(2)}`);
|
|
1442
|
+
}
|
|
1443
|
+
reasonParts.push("review");
|
|
1444
|
+
const reason = reasonParts.join(", ");
|
|
1061
1445
|
return { score, reason };
|
|
1062
1446
|
}
|
|
1063
1447
|
};
|
|
@@ -2333,10 +2717,23 @@ function logTagHydration(cards, tagsByCard) {
|
|
|
2333
2717
|
`[Pipeline] Tag hydration: ${cards.length} cards, ${cardsWithTags} have tags (${totalTags} total tags) - single batch query`
|
|
2334
2718
|
);
|
|
2335
2719
|
}
|
|
2336
|
-
function logExecutionSummary(generatorName, generatedCount, filterCount, finalCount, topScores) {
|
|
2720
|
+
function logExecutionSummary(generatorName, generatedCount, filterCount, finalCount, topScores, filterImpacts) {
|
|
2337
2721
|
const scoreDisplay = topScores.length > 0 ? topScores.map((s) => s.toFixed(2)).join(", ") : "none";
|
|
2722
|
+
let filterSummary = "";
|
|
2723
|
+
if (filterImpacts.length > 0) {
|
|
2724
|
+
const impacts = filterImpacts.map((f) => {
|
|
2725
|
+
const parts = [];
|
|
2726
|
+
if (f.boosted > 0) parts.push(`+${f.boosted}`);
|
|
2727
|
+
if (f.penalized > 0) parts.push(`-${f.penalized}`);
|
|
2728
|
+
if (f.passed > 0) parts.push(`=${f.passed}`);
|
|
2729
|
+
return `${f.name}: ${parts.join("/")}`;
|
|
2730
|
+
});
|
|
2731
|
+
filterSummary = `
|
|
2732
|
+
Filter impact: ${impacts.join(", ")}`;
|
|
2733
|
+
}
|
|
2338
2734
|
logger.info(
|
|
2339
|
-
`[Pipeline] Execution: ${generatorName} produced ${generatedCount} \u2192 ${filterCount} filters \u2192 ${finalCount} results (top scores: ${scoreDisplay})`
|
|
2735
|
+
`[Pipeline] Execution: ${generatorName} produced ${generatedCount} \u2192 ${filterCount} filters \u2192 ${finalCount} results (top scores: ${scoreDisplay})` + filterSummary + `
|
|
2736
|
+
\u{1F4A1} Inspect: window.skuilder.pipeline`
|
|
2340
2737
|
);
|
|
2341
2738
|
}
|
|
2342
2739
|
function logCardProvenance(cards, maxCards = 3) {
|
|
@@ -2361,6 +2758,7 @@ var init_Pipeline = __esm({
|
|
|
2361
2758
|
init_navigators();
|
|
2362
2759
|
init_logger();
|
|
2363
2760
|
init_orchestration();
|
|
2761
|
+
init_PipelineDebugger();
|
|
2364
2762
|
Pipeline = class extends ContentNavigator {
|
|
2365
2763
|
generator;
|
|
2366
2764
|
filters;
|
|
@@ -2408,12 +2806,49 @@ var init_Pipeline = __esm({
|
|
|
2408
2806
|
);
|
|
2409
2807
|
let cards = await this.generator.getWeightedCards(fetchLimit, context);
|
|
2410
2808
|
const generatedCount = cards.length;
|
|
2809
|
+
let generatorSummaries;
|
|
2810
|
+
if (this.generator.generators) {
|
|
2811
|
+
const genMap = /* @__PURE__ */ new Map();
|
|
2812
|
+
for (const card of cards) {
|
|
2813
|
+
const firstProv = card.provenance[0];
|
|
2814
|
+
if (firstProv) {
|
|
2815
|
+
const genName = firstProv.strategyName;
|
|
2816
|
+
if (!genMap.has(genName)) {
|
|
2817
|
+
genMap.set(genName, { cards: [] });
|
|
2818
|
+
}
|
|
2819
|
+
genMap.get(genName).cards.push(card);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
generatorSummaries = Array.from(genMap.entries()).map(([name, data]) => {
|
|
2823
|
+
const newCards = data.cards.filter((c) => c.provenance[0]?.reason?.includes("new card"));
|
|
2824
|
+
const reviewCards = data.cards.filter((c) => c.provenance[0]?.reason?.includes("review"));
|
|
2825
|
+
return {
|
|
2826
|
+
name,
|
|
2827
|
+
cardCount: data.cards.length,
|
|
2828
|
+
newCount: newCards.length,
|
|
2829
|
+
reviewCount: reviewCards.length,
|
|
2830
|
+
topScore: Math.max(...data.cards.map((c) => c.score), 0)
|
|
2831
|
+
};
|
|
2832
|
+
});
|
|
2833
|
+
}
|
|
2411
2834
|
logger.debug(`[Pipeline] Generator returned ${generatedCount} candidates`);
|
|
2412
2835
|
cards = await this.hydrateTags(cards);
|
|
2836
|
+
const allCardsBeforeFiltering = [...cards];
|
|
2837
|
+
const filterImpacts = [];
|
|
2413
2838
|
for (const filter of this.filters) {
|
|
2414
2839
|
const beforeCount = cards.length;
|
|
2840
|
+
const beforeScores = new Map(cards.map((c) => [c.cardId, c.score]));
|
|
2415
2841
|
cards = await filter.transform(cards, context);
|
|
2416
|
-
|
|
2842
|
+
let boosted = 0, penalized = 0, passed = 0;
|
|
2843
|
+
const removed = beforeCount - cards.length;
|
|
2844
|
+
for (const card of cards) {
|
|
2845
|
+
const before = beforeScores.get(card.cardId) ?? 0;
|
|
2846
|
+
if (card.score > before) boosted++;
|
|
2847
|
+
else if (card.score < before) penalized++;
|
|
2848
|
+
else passed++;
|
|
2849
|
+
}
|
|
2850
|
+
filterImpacts.push({ name: filter.name, boosted, penalized, passed, removed });
|
|
2851
|
+
logger.debug(`[Pipeline] Filter '${filter.name}': ${beforeScores.size} \u2192 ${cards.length} cards (\u2191${boosted} \u2193${penalized} =${passed})`);
|
|
2417
2852
|
}
|
|
2418
2853
|
cards = cards.filter((c) => c.score > 0);
|
|
2419
2854
|
cards.sort((a, b) => b.score - a.score);
|
|
@@ -2424,9 +2859,26 @@ var init_Pipeline = __esm({
|
|
|
2424
2859
|
generatedCount,
|
|
2425
2860
|
this.filters.length,
|
|
2426
2861
|
result.length,
|
|
2427
|
-
topScores
|
|
2862
|
+
topScores,
|
|
2863
|
+
filterImpacts
|
|
2428
2864
|
);
|
|
2429
2865
|
logCardProvenance(result, 3);
|
|
2866
|
+
try {
|
|
2867
|
+
const courseName = await this.course?.getCourseConfig().then((c) => c.name).catch(() => void 0);
|
|
2868
|
+
const report = buildRunReport(
|
|
2869
|
+
this.course?.getCourseID() || "unknown",
|
|
2870
|
+
courseName,
|
|
2871
|
+
this.generator.name,
|
|
2872
|
+
generatorSummaries,
|
|
2873
|
+
generatedCount,
|
|
2874
|
+
filterImpacts,
|
|
2875
|
+
allCardsBeforeFiltering,
|
|
2876
|
+
result
|
|
2877
|
+
);
|
|
2878
|
+
captureRun(report);
|
|
2879
|
+
} catch (e) {
|
|
2880
|
+
logger.debug(`[Pipeline] Failed to capture debug run: ${e}`);
|
|
2881
|
+
}
|
|
2430
2882
|
return result;
|
|
2431
2883
|
}
|
|
2432
2884
|
/**
|
|
@@ -2516,6 +2968,56 @@ var init_Pipeline = __esm({
|
|
|
2516
2968
|
}
|
|
2517
2969
|
});
|
|
2518
2970
|
|
|
2971
|
+
// src/core/navigators/defaults.ts
|
|
2972
|
+
var defaults_exports = {};
|
|
2973
|
+
__export(defaults_exports, {
|
|
2974
|
+
createDefaultEloStrategy: () => createDefaultEloStrategy,
|
|
2975
|
+
createDefaultPipeline: () => createDefaultPipeline,
|
|
2976
|
+
createDefaultSrsStrategy: () => createDefaultSrsStrategy
|
|
2977
|
+
});
|
|
2978
|
+
function createDefaultEloStrategy(courseId) {
|
|
2979
|
+
return {
|
|
2980
|
+
_id: "NAVIGATION_STRATEGY-ELO-default",
|
|
2981
|
+
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2982
|
+
name: "ELO (default)",
|
|
2983
|
+
description: "Default ELO-based navigation strategy for new cards",
|
|
2984
|
+
implementingClass: "elo" /* ELO */,
|
|
2985
|
+
course: courseId,
|
|
2986
|
+
serializedData: ""
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function createDefaultSrsStrategy(courseId) {
|
|
2990
|
+
return {
|
|
2991
|
+
_id: "NAVIGATION_STRATEGY-SRS-default",
|
|
2992
|
+
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2993
|
+
name: "SRS (default)",
|
|
2994
|
+
description: "Default SRS-based navigation strategy for reviews",
|
|
2995
|
+
implementingClass: "srs" /* SRS */,
|
|
2996
|
+
course: courseId,
|
|
2997
|
+
serializedData: ""
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
function createDefaultPipeline(user, course) {
|
|
3001
|
+
const courseId = course.getCourseID();
|
|
3002
|
+
const eloNavigator = new ELONavigator(user, course, createDefaultEloStrategy(courseId));
|
|
3003
|
+
const srsNavigator = new SRSNavigator(user, course, createDefaultSrsStrategy(courseId));
|
|
3004
|
+
const compositeGenerator = new CompositeGenerator([eloNavigator, srsNavigator]);
|
|
3005
|
+
const eloDistanceFilter = createEloDistanceFilter();
|
|
3006
|
+
return new Pipeline(compositeGenerator, [eloDistanceFilter], user, course);
|
|
3007
|
+
}
|
|
3008
|
+
var init_defaults = __esm({
|
|
3009
|
+
"src/core/navigators/defaults.ts"() {
|
|
3010
|
+
"use strict";
|
|
3011
|
+
init_navigators();
|
|
3012
|
+
init_Pipeline();
|
|
3013
|
+
init_CompositeGenerator();
|
|
3014
|
+
init_elo();
|
|
3015
|
+
init_srs();
|
|
3016
|
+
init_eloDistance();
|
|
3017
|
+
init_types_legacy();
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
|
|
2519
3021
|
// src/core/navigators/PipelineAssembler.ts
|
|
2520
3022
|
var PipelineAssembler_exports = {};
|
|
2521
3023
|
__export(PipelineAssembler_exports, {
|
|
@@ -2528,9 +3030,9 @@ var init_PipelineAssembler = __esm({
|
|
|
2528
3030
|
init_navigators();
|
|
2529
3031
|
init_WeightedFilter();
|
|
2530
3032
|
init_Pipeline();
|
|
2531
|
-
init_types_legacy();
|
|
2532
3033
|
init_logger();
|
|
2533
3034
|
init_CompositeGenerator();
|
|
3035
|
+
init_defaults();
|
|
2534
3036
|
PipelineAssembler = class {
|
|
2535
3037
|
/**
|
|
2536
3038
|
* Assembles a navigation pipeline from strategy documents.
|
|
@@ -2569,9 +3071,11 @@ var init_PipelineAssembler = __esm({
|
|
|
2569
3071
|
if (generatorStrategies.length === 0) {
|
|
2570
3072
|
if (filterStrategies.length > 0) {
|
|
2571
3073
|
logger.debug(
|
|
2572
|
-
"[PipelineAssembler] No generator found, using default ELO with configured filters"
|
|
3074
|
+
"[PipelineAssembler] No generator found, using default ELO and SRS with configured filters"
|
|
2573
3075
|
);
|
|
2574
|
-
|
|
3076
|
+
const courseId = course.getCourseID();
|
|
3077
|
+
generatorStrategies.push(createDefaultEloStrategy(courseId));
|
|
3078
|
+
generatorStrategies.push(createDefaultSrsStrategy(courseId));
|
|
2575
3079
|
} else {
|
|
2576
3080
|
warnings.push("No generator strategy found");
|
|
2577
3081
|
return {
|
|
@@ -2632,75 +3136,10 @@ var init_PipelineAssembler = __esm({
|
|
|
2632
3136
|
warnings
|
|
2633
3137
|
};
|
|
2634
3138
|
}
|
|
2635
|
-
/**
|
|
2636
|
-
* Creates a default ELO generator strategy.
|
|
2637
|
-
* Used when filters are configured but no generator is specified.
|
|
2638
|
-
*/
|
|
2639
|
-
makeDefaultEloStrategy(courseId) {
|
|
2640
|
-
return {
|
|
2641
|
-
_id: "NAVIGATION_STRATEGY-ELO-default",
|
|
2642
|
-
course: courseId,
|
|
2643
|
-
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2644
|
-
name: "ELO (default)",
|
|
2645
|
-
description: "Default ELO-based generator",
|
|
2646
|
-
implementingClass: "elo" /* ELO */,
|
|
2647
|
-
serializedData: ""
|
|
2648
|
-
};
|
|
2649
|
-
}
|
|
2650
3139
|
};
|
|
2651
3140
|
}
|
|
2652
3141
|
});
|
|
2653
3142
|
|
|
2654
|
-
// src/core/navigators/defaults.ts
|
|
2655
|
-
var defaults_exports = {};
|
|
2656
|
-
__export(defaults_exports, {
|
|
2657
|
-
createDefaultEloStrategy: () => createDefaultEloStrategy,
|
|
2658
|
-
createDefaultPipeline: () => createDefaultPipeline,
|
|
2659
|
-
createDefaultSrsStrategy: () => createDefaultSrsStrategy
|
|
2660
|
-
});
|
|
2661
|
-
function createDefaultEloStrategy(courseId) {
|
|
2662
|
-
return {
|
|
2663
|
-
_id: "NAVIGATION_STRATEGY-ELO-default",
|
|
2664
|
-
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2665
|
-
name: "ELO (default)",
|
|
2666
|
-
description: "Default ELO-based navigation strategy for new cards",
|
|
2667
|
-
implementingClass: "elo" /* ELO */,
|
|
2668
|
-
course: courseId,
|
|
2669
|
-
serializedData: ""
|
|
2670
|
-
};
|
|
2671
|
-
}
|
|
2672
|
-
function createDefaultSrsStrategy(courseId) {
|
|
2673
|
-
return {
|
|
2674
|
-
_id: "NAVIGATION_STRATEGY-SRS-default",
|
|
2675
|
-
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2676
|
-
name: "SRS (default)",
|
|
2677
|
-
description: "Default SRS-based navigation strategy for reviews",
|
|
2678
|
-
implementingClass: "srs" /* SRS */,
|
|
2679
|
-
course: courseId,
|
|
2680
|
-
serializedData: ""
|
|
2681
|
-
};
|
|
2682
|
-
}
|
|
2683
|
-
function createDefaultPipeline(user, course) {
|
|
2684
|
-
const courseId = course.getCourseID();
|
|
2685
|
-
const eloNavigator = new ELONavigator(user, course, createDefaultEloStrategy(courseId));
|
|
2686
|
-
const srsNavigator = new SRSNavigator(user, course, createDefaultSrsStrategy(courseId));
|
|
2687
|
-
const compositeGenerator = new CompositeGenerator([eloNavigator, srsNavigator]);
|
|
2688
|
-
const eloDistanceFilter = createEloDistanceFilter();
|
|
2689
|
-
return new Pipeline(compositeGenerator, [eloDistanceFilter], user, course);
|
|
2690
|
-
}
|
|
2691
|
-
var init_defaults = __esm({
|
|
2692
|
-
"src/core/navigators/defaults.ts"() {
|
|
2693
|
-
"use strict";
|
|
2694
|
-
init_navigators();
|
|
2695
|
-
init_Pipeline();
|
|
2696
|
-
init_CompositeGenerator();
|
|
2697
|
-
init_elo();
|
|
2698
|
-
init_srs();
|
|
2699
|
-
init_eloDistance();
|
|
2700
|
-
init_types_legacy();
|
|
2701
|
-
}
|
|
2702
|
-
});
|
|
2703
|
-
|
|
2704
3143
|
// import("./**/*") in src/core/navigators/index.ts
|
|
2705
3144
|
var globImport;
|
|
2706
3145
|
var init_3 = __esm({
|
|
@@ -2708,6 +3147,7 @@ var init_3 = __esm({
|
|
|
2708
3147
|
globImport = __glob({
|
|
2709
3148
|
"./Pipeline.ts": () => Promise.resolve().then(() => (init_Pipeline(), Pipeline_exports)),
|
|
2710
3149
|
"./PipelineAssembler.ts": () => Promise.resolve().then(() => (init_PipelineAssembler(), PipelineAssembler_exports)),
|
|
3150
|
+
"./PipelineDebugger.ts": () => Promise.resolve().then(() => (init_PipelineDebugger(), PipelineDebugger_exports)),
|
|
2711
3151
|
"./defaults.ts": () => Promise.resolve().then(() => (init_defaults(), defaults_exports)),
|
|
2712
3152
|
"./filters/WeightedFilter.ts": () => Promise.resolve().then(() => (init_WeightedFilter(), WeightedFilter_exports)),
|
|
2713
3153
|
"./filters/eloDistance.ts": () => Promise.resolve().then(() => (init_eloDistance(), eloDistance_exports)),
|
|
@@ -2743,6 +3183,8 @@ __export(navigators_exports, {
|
|
|
2743
3183
|
initializeNavigatorRegistry: () => initializeNavigatorRegistry,
|
|
2744
3184
|
isFilter: () => isFilter,
|
|
2745
3185
|
isGenerator: () => isGenerator,
|
|
3186
|
+
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
3187
|
+
pipelineDebugAPI: () => pipelineDebugAPI,
|
|
2746
3188
|
registerNavigator: () => registerNavigator
|
|
2747
3189
|
});
|
|
2748
3190
|
function registerNavigator(implementingClass, constructor) {
|
|
@@ -2809,6 +3251,7 @@ var navigatorRegistry, Navigators, NavigatorRole, NavigatorRoles, ContentNavigat
|
|
|
2809
3251
|
var init_navigators = __esm({
|
|
2810
3252
|
"src/core/navigators/index.ts"() {
|
|
2811
3253
|
"use strict";
|
|
3254
|
+
init_PipelineDebugger();
|
|
2812
3255
|
init_logger();
|
|
2813
3256
|
init_();
|
|
2814
3257
|
init_2();
|
|
@@ -5395,7 +5838,9 @@ __export(core_exports, {
|
|
|
5395
5838
|
isQuestionRecord: () => isQuestionRecord,
|
|
5396
5839
|
isReview: () => isReview,
|
|
5397
5840
|
log: () => log,
|
|
5841
|
+
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
5398
5842
|
parseCardHistoryID: () => parseCardHistoryID,
|
|
5843
|
+
pipelineDebugAPI: () => pipelineDebugAPI,
|
|
5399
5844
|
recordUserOutcome: () => recordUserOutcome,
|
|
5400
5845
|
registerNavigator: () => registerNavigator,
|
|
5401
5846
|
runPeriodUpdate: () => runPeriodUpdate,
|
|
@@ -5454,7 +5899,9 @@ init_core();
|
|
|
5454
5899
|
isQuestionRecord,
|
|
5455
5900
|
isReview,
|
|
5456
5901
|
log,
|
|
5902
|
+
mountPipelineDebugger,
|
|
5457
5903
|
parseCardHistoryID,
|
|
5904
|
+
pipelineDebugAPI,
|
|
5458
5905
|
recordUserOutcome,
|
|
5459
5906
|
registerNavigator,
|
|
5460
5907
|
runPeriodUpdate,
|