@vue-skuilder/db 0.2.1 → 0.2.3
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-Ht3N2f-y.d.ts → contentSource-Cplhv3bJ.d.ts} +1 -1
- package/dist/{contentSource-BMlMwSiG.d.cts → contentSource-kI9_jwTu.d.cts} +1 -1
- package/dist/core/index.d.cts +5 -5
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +76 -21
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +76 -21
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BEqB8VBR.d.cts → dataLayerProvider-CiA2Rr0v.d.cts} +1 -1
- package/dist/{dataLayerProvider-DObSXjnf.d.ts → dataLayerProvider-DrBqOUa3.d.ts} +1 -1
- package/dist/impl/couch/index.d.cts +35 -3
- package/dist/impl/couch/index.d.ts +35 -3
- package/dist/impl/couch/index.js +76 -21
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +76 -21
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.cts +4 -4
- package/dist/impl/static/index.d.ts +4 -4
- package/dist/impl/static/index.js +4 -5
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +4 -5
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-BWvO-_rJ.d.ts → index-BLLT5BYE.d.ts} +1 -1
- package/dist/{index-Ba7hYbHj.d.cts → index-k9NFHpS1.d.cts} +1 -1
- package/dist/index.d.cts +164 -10
- package/dist/index.d.ts +164 -10
- package/dist/index.js +215 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +215 -28
- package/dist/index.mjs.map +1 -1
- package/dist/{types-W8n-B6HG.d.cts → types-BFUa1pa3.d.cts} +1 -1
- package/dist/{types-CJrLM1Ew.d.ts → types-CHgpWQAY.d.ts} +1 -1
- package/dist/{types-legacy-JXDxinpU.d.cts → types-legacy-4tlwHnXo.d.cts} +1 -1
- package/dist/{types-legacy-JXDxinpU.d.ts → types-legacy-4tlwHnXo.d.ts} +1 -1
- package/dist/util/packer/index.d.cts +3 -3
- package/dist/util/packer/index.d.ts +3 -3
- package/docs/session-lifecycle-and-replan.md +418 -0
- package/package.json +3 -3
- package/src/core/navigators/Pipeline.ts +5 -1
- package/src/core/navigators/generators/elo.ts +19 -6
- package/src/core/navigators/generators/srs.ts +10 -0
- package/src/impl/couch/courseDB.ts +146 -17
- package/src/study/SessionController.ts +295 -13
- package/src/study/services/CardHydrationService.ts +24 -0
package/dist/index.mjs
CHANGED
|
@@ -1959,10 +1959,8 @@ var init_elo = __esm({
|
|
|
1959
1959
|
{ limit, elo: "user" },
|
|
1960
1960
|
(c) => !activeCards.some((ac) => c.cardID === ac.cardID)
|
|
1961
1961
|
)).map((c) => ({ ...c, status: "new" }));
|
|
1962
|
-
const
|
|
1963
|
-
|
|
1964
|
-
const scored = newCards.map((c, i) => {
|
|
1965
|
-
const cardElo = cardEloData[i]?.global?.score ?? 1e3;
|
|
1962
|
+
const scored = newCards.map((c) => {
|
|
1963
|
+
const cardElo = c.elo ?? 1e3;
|
|
1966
1964
|
const distance = Math.abs(cardElo - userGlobalElo);
|
|
1967
1965
|
const rawScore = Math.max(0, 1 - distance / 500);
|
|
1968
1966
|
const samplingKey = rawScore > 0 ? Math.random() ** (1 / rawScore) : 0;
|
|
@@ -4186,7 +4184,8 @@ var init_orchestration = __esm({
|
|
|
4186
4184
|
// src/core/navigators/Pipeline.ts
|
|
4187
4185
|
var Pipeline_exports = {};
|
|
4188
4186
|
__export(Pipeline_exports, {
|
|
4189
|
-
Pipeline: () => Pipeline
|
|
4187
|
+
Pipeline: () => Pipeline,
|
|
4188
|
+
mergeHints: () => mergeHints2
|
|
4190
4189
|
});
|
|
4191
4190
|
import { toCourseElo as toCourseElo5 } from "@vue-skuilder/common";
|
|
4192
4191
|
function globToRegex(pattern) {
|
|
@@ -5883,6 +5882,7 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
5883
5882
|
}
|
|
5884
5883
|
async addNavigationStrategy(data) {
|
|
5885
5884
|
logger.debug(`[courseDB] Adding navigation strategy: ${data._id}`);
|
|
5885
|
+
this.invalidateNavigatorCache();
|
|
5886
5886
|
return this.remoteDB.put(data).then(() => {
|
|
5887
5887
|
});
|
|
5888
5888
|
}
|
|
@@ -5949,23 +5949,67 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5949
5949
|
* @returns Cards sorted by score descending
|
|
5950
5950
|
*/
|
|
5951
5951
|
_pendingHints = null;
|
|
5952
|
+
/**
|
|
5953
|
+
* Session-scoped cache of the broad ELO-neighbor pool used by
|
|
5954
|
+
* getCardsCenteredAtELO. The `elo` view query re-indexes on first touch per
|
|
5955
|
+
* call (PouchDB 9 ignores `stale`), so without this each plan/replan pays
|
|
5956
|
+
* ~1.5-2s. The pool is fetched once and re-ranked against the live (roaming)
|
|
5957
|
+
* ELO in memory on subsequent calls.
|
|
5958
|
+
*/
|
|
5959
|
+
_eloPoolCache = null;
|
|
5960
|
+
_eloPoolTtlMs = 5 * 60 * 1e3;
|
|
5961
|
+
/**
|
|
5962
|
+
* Cached assembled navigator (Pipeline). createNavigator() reads strategy
|
|
5963
|
+
* docs and builds a fresh Pipeline every call — whose internal `_tagCache`
|
|
5964
|
+
* and `_cachedOrchestration` are designed to make replans cheap but never
|
|
5965
|
+
* survive, because the instance is discarded each run. Caching it lets those
|
|
5966
|
+
* caches persist across plan/replan within a session (SessionController holds
|
|
5967
|
+
* one CourseDB instance for the session's lifetime). Rebuilt on user change,
|
|
5968
|
+
* TTL expiry, or explicit invalidation after a strategy-doc write.
|
|
5969
|
+
*/
|
|
5970
|
+
_cachedNavigator = null;
|
|
5971
|
+
_navigatorTtlMs = 5 * 60 * 1e3;
|
|
5952
5972
|
setEphemeralHints(hints) {
|
|
5953
5973
|
this._pendingHints = hints;
|
|
5954
5974
|
}
|
|
5955
5975
|
async getWeightedCards(limit) {
|
|
5956
5976
|
const u = await this._getCurrentUser();
|
|
5957
5977
|
try {
|
|
5958
|
-
const navigator2 = await this.
|
|
5978
|
+
const { navigator: navigator2 } = await this._getCachedNavigator(u);
|
|
5959
5979
|
if (this._pendingHints) {
|
|
5960
5980
|
navigator2.setEphemeralHints(this._pendingHints);
|
|
5961
5981
|
this._pendingHints = null;
|
|
5962
5982
|
}
|
|
5963
|
-
|
|
5983
|
+
const result = await navigator2.getWeightedCards(limit);
|
|
5984
|
+
return result;
|
|
5964
5985
|
} catch (e) {
|
|
5965
5986
|
logger.error(`[courseDB] Error getting weighted cards: ${e}`);
|
|
5966
5987
|
throw e;
|
|
5967
5988
|
}
|
|
5968
5989
|
}
|
|
5990
|
+
/**
|
|
5991
|
+
* Return the assembled navigator, reusing the cached instance when possible.
|
|
5992
|
+
* Reuse preserves the Pipeline's per-session caches (tags, orchestration
|
|
5993
|
+
* context) across replans, which is the dominant per-replan cost once the
|
|
5994
|
+
* ELO-pool cost is removed. Rebuilds on user change or TTL expiry.
|
|
5995
|
+
*/
|
|
5996
|
+
async _getCachedNavigator(user) {
|
|
5997
|
+
const userId = user.getUsername();
|
|
5998
|
+
const now = Date.now();
|
|
5999
|
+
if (this._cachedNavigator && this._cachedNavigator.userId === userId && now - this._cachedNavigator.builtAt < this._navigatorTtlMs) {
|
|
6000
|
+
return { navigator: this._cachedNavigator.navigator, cacheStatus: "hit" };
|
|
6001
|
+
}
|
|
6002
|
+
const navigator2 = await this.createNavigator(user);
|
|
6003
|
+
this._cachedNavigator = { navigator: navigator2, userId, builtAt: now };
|
|
6004
|
+
return { navigator: navigator2, cacheStatus: "miss" };
|
|
6005
|
+
}
|
|
6006
|
+
/**
|
|
6007
|
+
* Drop the cached navigator so the next getWeightedCards() rebuilds it.
|
|
6008
|
+
* Call after mutating this course's navigation strategy documents.
|
|
6009
|
+
*/
|
|
6010
|
+
invalidateNavigatorCache() {
|
|
6011
|
+
this._cachedNavigator = null;
|
|
6012
|
+
}
|
|
5969
6013
|
async getCardsCenteredAtELO(options = {
|
|
5970
6014
|
limit: 99,
|
|
5971
6015
|
elo: "user"
|
|
@@ -5988,20 +6032,31 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5988
6032
|
} else {
|
|
5989
6033
|
targetElo = options.elo;
|
|
5990
6034
|
}
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
let
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6035
|
+
const POOL_SIZE = Math.max(2e3, options.limit * 4);
|
|
6036
|
+
const nowMs = Date.now();
|
|
6037
|
+
let cacheStatus = "hit";
|
|
6038
|
+
if (!this._eloPoolCache || nowMs - this._eloPoolCache.fetchedAt > this._eloPoolTtlMs) {
|
|
6039
|
+
const fetched = await this.getCardsByELO(targetElo, POOL_SIZE);
|
|
6040
|
+
if (fetched.length > 0) {
|
|
6041
|
+
this._eloPoolCache = { rows: fetched, fetchedAt: nowMs };
|
|
6042
|
+
}
|
|
6043
|
+
cacheStatus = "miss";
|
|
6044
|
+
}
|
|
6045
|
+
const rankAgainstCurrentElo = () => {
|
|
6046
|
+
const raw = this._eloPoolCache?.rows ?? [];
|
|
6047
|
+
const survivors = filter ? raw.filter((c) => filter(c)) : raw;
|
|
6048
|
+
return survivors.map((c) => ({ ...c })).sort(
|
|
6049
|
+
(a, b) => Math.abs((a.elo ?? targetElo) - targetElo) - Math.abs((b.elo ?? targetElo) - targetElo)
|
|
6050
|
+
);
|
|
6051
|
+
};
|
|
6052
|
+
let cards = rankAgainstCurrentElo();
|
|
6053
|
+
if (cards.length < options.limit && (cacheStatus === "hit" || !this._eloPoolCache)) {
|
|
6054
|
+
const fetched = await this.getCardsByELO(targetElo, POOL_SIZE);
|
|
6055
|
+
if (fetched.length > 0) {
|
|
6056
|
+
this._eloPoolCache = { rows: fetched, fetchedAt: nowMs };
|
|
6057
|
+
}
|
|
6058
|
+
cards = rankAgainstCurrentElo();
|
|
6059
|
+
cacheStatus = "refresh";
|
|
6005
6060
|
}
|
|
6006
6061
|
const selectedCards = [];
|
|
6007
6062
|
while (selectedCards.length < options.limit && cards.length > 0) {
|
|
@@ -11188,6 +11243,7 @@ var ItemQueue = class {
|
|
|
11188
11243
|
|
|
11189
11244
|
// src/study/SessionController.ts
|
|
11190
11245
|
init_couch();
|
|
11246
|
+
init_core();
|
|
11191
11247
|
init_recording();
|
|
11192
11248
|
|
|
11193
11249
|
// src/util/index.ts
|
|
@@ -12614,6 +12670,7 @@ init_dataDirectory();
|
|
|
12614
12670
|
|
|
12615
12671
|
// src/study/SessionController.ts
|
|
12616
12672
|
init_navigators();
|
|
12673
|
+
init_Pipeline();
|
|
12617
12674
|
|
|
12618
12675
|
// src/study/SourceMixer.ts
|
|
12619
12676
|
var QuotaRoundRobinMixer = class {
|
|
@@ -13325,6 +13382,32 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13325
13382
|
* each nextCard() draw. Set by replans that include `minFollowUpCards`.
|
|
13326
13383
|
*/
|
|
13327
13384
|
_minCardsGuarantee = 0;
|
|
13385
|
+
/**
|
|
13386
|
+
* Session-durable scoring hints. Re-merged into every pipeline run for
|
|
13387
|
+
* the rest of the session (initial plan + every replan, including bare
|
|
13388
|
+
* auto-replans and the wedge-breaker), via `_applyHintsToSources`.
|
|
13389
|
+
*
|
|
13390
|
+
* Set by `setSessionHints()` (e.g. session-start post-lesson boost) or by
|
|
13391
|
+
* any replan carrying `ReplanOptions.sessionHints` (e.g. a just-failed
|
|
13392
|
+
* concept boost). Replace semantics, no decay — lives until overwritten
|
|
13393
|
+
* or session end. See `ReplanOptions.sessionHints` for rationale.
|
|
13394
|
+
*
|
|
13395
|
+
* Note: the controller-managed auto-excludes (current card, session
|
|
13396
|
+
* record, imminent draw) are intentionally NOT folded in here — those are
|
|
13397
|
+
* recomputed per-run in `_runReplan` and would otherwise go stale.
|
|
13398
|
+
*/
|
|
13399
|
+
_sessionHints = null;
|
|
13400
|
+
/**
|
|
13401
|
+
* Consumer-supplied hooks invoked after each question response is processed.
|
|
13402
|
+
* Seeded from constructor options (threaded from
|
|
13403
|
+
* `StudySessionConfig.outcomeObservers`). See {@link OutcomeObserver}.
|
|
13404
|
+
*/
|
|
13405
|
+
_outcomeObservers = [];
|
|
13406
|
+
/**
|
|
13407
|
+
* Lazily-built, stable capability object handed to observers. Bound to
|
|
13408
|
+
* `this`; constructed once so observers can rely on referential identity.
|
|
13409
|
+
*/
|
|
13410
|
+
_sessionControls = null;
|
|
13328
13411
|
startTime;
|
|
13329
13412
|
endTime;
|
|
13330
13413
|
_secondsRemaining;
|
|
@@ -13384,6 +13467,9 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13384
13467
|
if (options?.initialReviewCap !== void 0) {
|
|
13385
13468
|
this._initialReviewCap = options.initialReviewCap;
|
|
13386
13469
|
}
|
|
13470
|
+
if (options?.outcomeObservers?.length) {
|
|
13471
|
+
this._outcomeObservers = [...options.outcomeObservers];
|
|
13472
|
+
}
|
|
13387
13473
|
this.log(`Session constructed:
|
|
13388
13474
|
startTime: ${this.startTime}
|
|
13389
13475
|
endTime: ${this.endTime}
|
|
@@ -13511,6 +13597,7 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13511
13597
|
if (opts.minFollowUpCards !== void 0) return true;
|
|
13512
13598
|
if (opts.mode && opts.mode !== "replace") return true;
|
|
13513
13599
|
if (opts.hints && Object.keys(opts.hints).length > 0) return true;
|
|
13600
|
+
if (opts.sessionHints !== void 0) return true;
|
|
13514
13601
|
return false;
|
|
13515
13602
|
}
|
|
13516
13603
|
/**
|
|
@@ -13539,12 +13626,13 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13539
13626
|
excludeSet.add(this.newQ.peek(0).cardID);
|
|
13540
13627
|
}
|
|
13541
13628
|
hints.excludeCards = [...excludeSet];
|
|
13542
|
-
if (opts.
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13629
|
+
if (opts.sessionHints !== void 0) {
|
|
13630
|
+
this._sessionHints = opts.sessionHints;
|
|
13631
|
+
this.log(
|
|
13632
|
+
`[Replan] Session hints ${opts.sessionHints ? "set" : "cleared"}: ${JSON.stringify(opts.sessionHints)}`
|
|
13633
|
+
);
|
|
13547
13634
|
}
|
|
13635
|
+
this._applyHintsToSources(opts.hints, opts.label);
|
|
13548
13636
|
const labelTag = opts.label ? ` [${opts.label}]` : "";
|
|
13549
13637
|
this.log(
|
|
13550
13638
|
`Mid-session replan requested${labelTag} (limit: ${opts.limit ?? "default"}, mode: ${opts.mode ?? "replace"}${opts.hints ? ", with hints" : ""})`
|
|
@@ -13555,6 +13643,100 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13555
13643
|
}
|
|
13556
13644
|
await this._executeReplan(opts);
|
|
13557
13645
|
}
|
|
13646
|
+
/**
|
|
13647
|
+
* Set the session-durable scoring hints (replace semantics, no decay).
|
|
13648
|
+
*
|
|
13649
|
+
* Unlike a one-shot replan hint, these are re-merged into every pipeline
|
|
13650
|
+
* run for the rest of the session — including the initial plan when set
|
|
13651
|
+
* before `prepareSession()`, every replan, the bare auto-replans, and the
|
|
13652
|
+
* wedge-breaker. Pass `null` to clear.
|
|
13653
|
+
*
|
|
13654
|
+
* Typical callers:
|
|
13655
|
+
* - `StudySession` at session start, threading `StudySessionConfig.initHints`
|
|
13656
|
+
* (e.g. a post-lesson concept boost) — so the boost outlives the first
|
|
13657
|
+
* queue rebuild instead of being clobbered by the first auto-replan.
|
|
13658
|
+
* - A consumer view on a failure, boosting the just-failed concept tag.
|
|
13659
|
+
*
|
|
13660
|
+
* Does not itself trigger a replan; the next plan/replan picks it up.
|
|
13661
|
+
*/
|
|
13662
|
+
setSessionHints(hints) {
|
|
13663
|
+
this._sessionHints = hints;
|
|
13664
|
+
this.log(`Session hints ${hints ? "set" : "cleared"}: ${JSON.stringify(hints)}`);
|
|
13665
|
+
}
|
|
13666
|
+
/**
|
|
13667
|
+
* Read the current session-durable hints (for read-modify-write callers,
|
|
13668
|
+
* e.g. an outcome observer that clamps a compounding boost).
|
|
13669
|
+
*/
|
|
13670
|
+
getSessionHints() {
|
|
13671
|
+
return this._sessionHints;
|
|
13672
|
+
}
|
|
13673
|
+
/**
|
|
13674
|
+
* Merge `hints` into the durable session hints via the pipeline's
|
|
13675
|
+
* `mergeHints` (boosts multiply, require/exclude lists concat-dedup).
|
|
13676
|
+
* Convenience over get-then-set for the common additive case. Note the
|
|
13677
|
+
* multiplicative, no-decay semantics — clamp boost factors at the call
|
|
13678
|
+
* site if a repeatedly-emphasised tag could compound unboundedly.
|
|
13679
|
+
*/
|
|
13680
|
+
mergeSessionHints(hints) {
|
|
13681
|
+
this._sessionHints = mergeHints2([this._sessionHints, hints]) ?? null;
|
|
13682
|
+
this.log(`Session hints merged: ${JSON.stringify(this._sessionHints)}`);
|
|
13683
|
+
}
|
|
13684
|
+
/**
|
|
13685
|
+
* Merge the durable `_sessionHints` with this run's one-shot hints and
|
|
13686
|
+
* push the result to every source for consumption on the next pipeline
|
|
13687
|
+
* run. Centralised so the initial plan and all replan paths apply session
|
|
13688
|
+
* emphasis identically. No-op when there are no hints of either kind.
|
|
13689
|
+
*/
|
|
13690
|
+
_applyHintsToSources(oneShot, label) {
|
|
13691
|
+
const oneShotWithLabel = oneShot && label ? { ...oneShot, _label: label } : oneShot;
|
|
13692
|
+
const merged = mergeHints2([this._sessionHints, oneShotWithLabel]);
|
|
13693
|
+
if (!merged) return;
|
|
13694
|
+
for (const source of this.sources) {
|
|
13695
|
+
source.setEphemeralHints?.(merged);
|
|
13696
|
+
}
|
|
13697
|
+
}
|
|
13698
|
+
/**
|
|
13699
|
+
* Build (once) the stable capability object handed to outcome observers.
|
|
13700
|
+
* Methods are bound to `this`; the object identity is stable across calls
|
|
13701
|
+
* so observers may key off it.
|
|
13702
|
+
*/
|
|
13703
|
+
_getSessionControls() {
|
|
13704
|
+
if (!this._sessionControls) {
|
|
13705
|
+
this._sessionControls = {
|
|
13706
|
+
getSessionHints: () => this.getSessionHints(),
|
|
13707
|
+
setSessionHints: (h) => this.setSessionHints(h),
|
|
13708
|
+
mergeSessionHints: (h) => this.mergeSessionHints(h),
|
|
13709
|
+
requestReplan: (opts) => this.requestReplan(opts)
|
|
13710
|
+
};
|
|
13711
|
+
}
|
|
13712
|
+
return this._sessionControls;
|
|
13713
|
+
}
|
|
13714
|
+
/**
|
|
13715
|
+
* Notify registered outcome observers about a processed response.
|
|
13716
|
+
*
|
|
13717
|
+
* Only question records are surfaced (non-question dismisses are skipped).
|
|
13718
|
+
* Observers run after ELO/SRS are recorded and before navigation. Each is
|
|
13719
|
+
* awaited but isolated in try/catch — a throwing observer is logged and
|
|
13720
|
+
* skipped, never wedging the session. Keep observers cheap and `void` any
|
|
13721
|
+
* long work (e.g. a triggered replan) to avoid stalling the draw.
|
|
13722
|
+
*/
|
|
13723
|
+
async _notifyOutcomeObservers(record, currentCard, result) {
|
|
13724
|
+
if (this._outcomeObservers.length === 0) return;
|
|
13725
|
+
if (!isQuestionRecord(record)) return;
|
|
13726
|
+
const outcome = {
|
|
13727
|
+
record,
|
|
13728
|
+
card: currentCard.card,
|
|
13729
|
+
result
|
|
13730
|
+
};
|
|
13731
|
+
const controls = this._getSessionControls();
|
|
13732
|
+
for (const observer of this._outcomeObservers) {
|
|
13733
|
+
try {
|
|
13734
|
+
await observer(outcome, controls);
|
|
13735
|
+
} catch (e) {
|
|
13736
|
+
this.error("[OutcomeObserver] observer threw; ignoring", e);
|
|
13737
|
+
}
|
|
13738
|
+
}
|
|
13739
|
+
}
|
|
13558
13740
|
/**
|
|
13559
13741
|
* Run a replan, bypassing requestReplan()'s coalesce logic.
|
|
13560
13742
|
*
|
|
@@ -13582,7 +13764,7 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13582
13764
|
*/
|
|
13583
13765
|
normalizeReplanOptions(input) {
|
|
13584
13766
|
if (!input) return {};
|
|
13585
|
-
const replanKeys = ["hints", "limit", "mode", "label", "minFollowUpCards"];
|
|
13767
|
+
const replanKeys = ["hints", "sessionHints", "limit", "mode", "label", "minFollowUpCards"];
|
|
13586
13768
|
const inputKeys = Object.keys(input);
|
|
13587
13769
|
if (inputKeys.some((k) => replanKeys.includes(k))) {
|
|
13588
13770
|
return input;
|
|
@@ -13734,6 +13916,9 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
13734
13916
|
const additive = options?.additive ?? false;
|
|
13735
13917
|
const newLimit = options?.limit ?? this._defaultBatchLimit;
|
|
13736
13918
|
const fetchLimit = replan ? newLimit : newLimit + this._initialReviewCap;
|
|
13919
|
+
if (!replan) {
|
|
13920
|
+
this._applyHintsToSources();
|
|
13921
|
+
}
|
|
13737
13922
|
const batches = [];
|
|
13738
13923
|
for (let i = 0; i < this.sources.length; i++) {
|
|
13739
13924
|
const source = this.sources[i];
|
|
@@ -14014,7 +14199,7 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
14014
14199
|
const studySessionItem = {
|
|
14015
14200
|
...currentCard.item
|
|
14016
14201
|
};
|
|
14017
|
-
|
|
14202
|
+
const result = await this.services.response.processResponse(
|
|
14018
14203
|
cardRecord,
|
|
14019
14204
|
cardHistory,
|
|
14020
14205
|
studySessionItem,
|
|
@@ -14026,6 +14211,8 @@ var SessionController = class _SessionController extends Loggable {
|
|
|
14026
14211
|
maxSessionViews,
|
|
14027
14212
|
sessionViews
|
|
14028
14213
|
);
|
|
14214
|
+
await this._notifyOutcomeObservers(cardRecord, currentCard, result);
|
|
14215
|
+
return result;
|
|
14029
14216
|
}
|
|
14030
14217
|
dismissCurrentCard(action = "dismiss-success") {
|
|
14031
14218
|
if (this._currentCard) {
|