@vue-skuilder/db 0.2.2 → 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.
Files changed (39) hide show
  1. package/dist/{contentSource-Ht3N2f-y.d.ts → contentSource-Cplhv3bJ.d.ts} +1 -1
  2. package/dist/{contentSource-BMlMwSiG.d.cts → contentSource-kI9_jwTu.d.cts} +1 -1
  3. package/dist/core/index.d.cts +5 -5
  4. package/dist/core/index.d.ts +5 -5
  5. package/dist/core/index.js +2 -1
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +2 -1
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BEqB8VBR.d.cts → dataLayerProvider-CiA2Rr0v.d.cts} +1 -1
  10. package/dist/{dataLayerProvider-DObSXjnf.d.ts → dataLayerProvider-DrBqOUa3.d.ts} +1 -1
  11. package/dist/impl/couch/index.d.cts +3 -3
  12. package/dist/impl/couch/index.d.ts +3 -3
  13. package/dist/impl/couch/index.js +2 -1
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +2 -1
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/index.d.cts +4 -4
  18. package/dist/impl/static/index.d.ts +4 -4
  19. package/dist/impl/static/index.js +2 -1
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +2 -1
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/{index-BWvO-_rJ.d.ts → index-BLLT5BYE.d.ts} +1 -1
  24. package/dist/{index-Ba7hYbHj.d.cts → index-k9NFHpS1.d.cts} +1 -1
  25. package/dist/index.d.cts +164 -10
  26. package/dist/index.d.ts +164 -10
  27. package/dist/index.js +141 -8
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +141 -8
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/{types-W8n-B6HG.d.cts → types-BFUa1pa3.d.cts} +1 -1
  32. package/dist/{types-CJrLM1Ew.d.ts → types-CHgpWQAY.d.ts} +1 -1
  33. package/dist/{types-legacy-JXDxinpU.d.cts → types-legacy-4tlwHnXo.d.cts} +1 -1
  34. package/dist/{types-legacy-JXDxinpU.d.ts → types-legacy-4tlwHnXo.d.ts} +1 -1
  35. package/dist/util/packer/index.d.cts +3 -3
  36. package/dist/util/packer/index.d.ts +3 -3
  37. package/package.json +3 -3
  38. package/src/core/navigators/Pipeline.ts +1 -1
  39. package/src/study/SessionController.ts +262 -13
package/dist/index.mjs CHANGED
@@ -4184,7 +4184,8 @@ var init_orchestration = __esm({
4184
4184
  // src/core/navigators/Pipeline.ts
4185
4185
  var Pipeline_exports = {};
4186
4186
  __export(Pipeline_exports, {
4187
- Pipeline: () => Pipeline
4187
+ Pipeline: () => Pipeline,
4188
+ mergeHints: () => mergeHints2
4188
4189
  });
4189
4190
  import { toCourseElo as toCourseElo5 } from "@vue-skuilder/common";
4190
4191
  function globToRegex(pattern) {
@@ -11242,6 +11243,7 @@ var ItemQueue = class {
11242
11243
 
11243
11244
  // src/study/SessionController.ts
11244
11245
  init_couch();
11246
+ init_core();
11245
11247
  init_recording();
11246
11248
 
11247
11249
  // src/util/index.ts
@@ -12668,6 +12670,7 @@ init_dataDirectory();
12668
12670
 
12669
12671
  // src/study/SessionController.ts
12670
12672
  init_navigators();
12673
+ init_Pipeline();
12671
12674
 
12672
12675
  // src/study/SourceMixer.ts
12673
12676
  var QuotaRoundRobinMixer = class {
@@ -13379,6 +13382,32 @@ var SessionController = class _SessionController extends Loggable {
13379
13382
  * each nextCard() draw. Set by replans that include `minFollowUpCards`.
13380
13383
  */
13381
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;
13382
13411
  startTime;
13383
13412
  endTime;
13384
13413
  _secondsRemaining;
@@ -13438,6 +13467,9 @@ var SessionController = class _SessionController extends Loggable {
13438
13467
  if (options?.initialReviewCap !== void 0) {
13439
13468
  this._initialReviewCap = options.initialReviewCap;
13440
13469
  }
13470
+ if (options?.outcomeObservers?.length) {
13471
+ this._outcomeObservers = [...options.outcomeObservers];
13472
+ }
13441
13473
  this.log(`Session constructed:
13442
13474
  startTime: ${this.startTime}
13443
13475
  endTime: ${this.endTime}
@@ -13565,6 +13597,7 @@ var SessionController = class _SessionController extends Loggable {
13565
13597
  if (opts.minFollowUpCards !== void 0) return true;
13566
13598
  if (opts.mode && opts.mode !== "replace") return true;
13567
13599
  if (opts.hints && Object.keys(opts.hints).length > 0) return true;
13600
+ if (opts.sessionHints !== void 0) return true;
13568
13601
  return false;
13569
13602
  }
13570
13603
  /**
@@ -13593,12 +13626,13 @@ var SessionController = class _SessionController extends Loggable {
13593
13626
  excludeSet.add(this.newQ.peek(0).cardID);
13594
13627
  }
13595
13628
  hints.excludeCards = [...excludeSet];
13596
- if (opts.hints) {
13597
- const hintsWithLabel = opts.label ? { ...opts.hints, _label: opts.label } : opts.hints;
13598
- for (const source of this.sources) {
13599
- source.setEphemeralHints?.(hintsWithLabel);
13600
- }
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
+ );
13601
13634
  }
13635
+ this._applyHintsToSources(opts.hints, opts.label);
13602
13636
  const labelTag = opts.label ? ` [${opts.label}]` : "";
13603
13637
  this.log(
13604
13638
  `Mid-session replan requested${labelTag} (limit: ${opts.limit ?? "default"}, mode: ${opts.mode ?? "replace"}${opts.hints ? ", with hints" : ""})`
@@ -13609,6 +13643,100 @@ var SessionController = class _SessionController extends Loggable {
13609
13643
  }
13610
13644
  await this._executeReplan(opts);
13611
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
+ }
13612
13740
  /**
13613
13741
  * Run a replan, bypassing requestReplan()'s coalesce logic.
13614
13742
  *
@@ -13636,7 +13764,7 @@ var SessionController = class _SessionController extends Loggable {
13636
13764
  */
13637
13765
  normalizeReplanOptions(input) {
13638
13766
  if (!input) return {};
13639
- const replanKeys = ["hints", "limit", "mode", "label", "minFollowUpCards"];
13767
+ const replanKeys = ["hints", "sessionHints", "limit", "mode", "label", "minFollowUpCards"];
13640
13768
  const inputKeys = Object.keys(input);
13641
13769
  if (inputKeys.some((k) => replanKeys.includes(k))) {
13642
13770
  return input;
@@ -13788,6 +13916,9 @@ var SessionController = class _SessionController extends Loggable {
13788
13916
  const additive = options?.additive ?? false;
13789
13917
  const newLimit = options?.limit ?? this._defaultBatchLimit;
13790
13918
  const fetchLimit = replan ? newLimit : newLimit + this._initialReviewCap;
13919
+ if (!replan) {
13920
+ this._applyHintsToSources();
13921
+ }
13791
13922
  const batches = [];
13792
13923
  for (let i = 0; i < this.sources.length; i++) {
13793
13924
  const source = this.sources[i];
@@ -14068,7 +14199,7 @@ var SessionController = class _SessionController extends Loggable {
14068
14199
  const studySessionItem = {
14069
14200
  ...currentCard.item
14070
14201
  };
14071
- return await this.services.response.processResponse(
14202
+ const result = await this.services.response.processResponse(
14072
14203
  cardRecord,
14073
14204
  cardHistory,
14074
14205
  studySessionItem,
@@ -14080,6 +14211,8 @@ var SessionController = class _SessionController extends Loggable {
14080
14211
  maxSessionViews,
14081
14212
  sessionViews
14082
14213
  );
14214
+ await this._notifyOutcomeObservers(cardRecord, currentCard, result);
14215
+ return result;
14083
14216
  }
14084
14217
  dismissCurrentCard(action = "dismiss-success") {
14085
14218
  if (this._currentCard) {