@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CourseConfig, ClassroomConfig, Status, SkuilderCourseData as SkuilderCourseData$1, CourseElo, DataShape, TagFilter } from '@vue-skuilder/common';
|
|
2
2
|
import { Moment } from 'moment';
|
|
3
|
-
import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-
|
|
3
|
+
import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-4tlwHnXo.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Admin functionality
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CourseConfig, ClassroomConfig, Status, SkuilderCourseData as SkuilderCourseData$1, CourseElo, DataShape, TagFilter } from '@vue-skuilder/common';
|
|
2
2
|
import { Moment } from 'moment';
|
|
3
|
-
import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-
|
|
3
|
+
import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-4tlwHnXo.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Admin functionality
|
package/dist/core/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a7 as LearnableWeight, M as UserOutcomeRecord, a8 as OrchestrationContext, W as WeightedCard, U as UserDBInterface, C as CourseDBInterface, R as ReplanHints, Z as StrategyContribution } from '../contentSource-
|
|
2
|
-
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, a5 as GeneratorContext, G as GeneratorResult, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, j as ScheduledCard, I as SessionTrackingData, i as StudentClassroomDBInterface, h as StudyContentSource, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, S as StudySessionItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, O as registerNavigator } from '../contentSource-
|
|
3
|
-
export { D as DataLayerProvider } from '../dataLayerProvider-
|
|
4
|
-
import { D as DocType,
|
|
5
|
-
export {
|
|
1
|
+
import { a7 as LearnableWeight, M as UserOutcomeRecord, a8 as OrchestrationContext, W as WeightedCard, U as UserDBInterface, C as CourseDBInterface, R as ReplanHints, Z as StrategyContribution } from '../contentSource-kI9_jwTu.cjs';
|
|
2
|
+
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, a5 as GeneratorContext, G as GeneratorResult, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, j as ScheduledCard, I as SessionTrackingData, i as StudentClassroomDBInterface, h as StudyContentSource, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, S as StudySessionItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, O as registerNavigator } from '../contentSource-kI9_jwTu.cjs';
|
|
3
|
+
export { D as DataLayerProvider } from '../dataLayerProvider-CiA2Rr0v.cjs';
|
|
4
|
+
import { D as DocType, d as QuestionRecord, b as DocTypePrefixes, C as CardHistory, c as CardRecord } from '../types-legacy-4tlwHnXo.cjs';
|
|
5
|
+
export { e as CardData, f as CourseListData, h as DataShapeData, g as DisplayableData, F as Field, G as GuestUsername, Q as QualifiedCardID, i as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-4tlwHnXo.cjs';
|
|
6
6
|
import { DataShape, ParsedCard } from '@vue-skuilder/common';
|
|
7
7
|
import 'moment';
|
|
8
8
|
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a7 as LearnableWeight, M as UserOutcomeRecord, a8 as OrchestrationContext, W as WeightedCard, U as UserDBInterface, C as CourseDBInterface, R as ReplanHints, Z as StrategyContribution } from '../contentSource-
|
|
2
|
-
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, a5 as GeneratorContext, G as GeneratorResult, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, j as ScheduledCard, I as SessionTrackingData, i as StudentClassroomDBInterface, h as StudyContentSource, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, S as StudySessionItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, O as registerNavigator } from '../contentSource-
|
|
3
|
-
export { D as DataLayerProvider } from '../dataLayerProvider-
|
|
4
|
-
import { D as DocType,
|
|
5
|
-
export {
|
|
1
|
+
import { a7 as LearnableWeight, M as UserOutcomeRecord, a8 as OrchestrationContext, W as WeightedCard, U as UserDBInterface, C as CourseDBInterface, R as ReplanHints, Z as StrategyContribution } from '../contentSource-Cplhv3bJ.js';
|
|
2
|
+
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, a5 as GeneratorContext, G as GeneratorResult, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, j as ScheduledCard, I as SessionTrackingData, i as StudentClassroomDBInterface, h as StudyContentSource, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, S as StudySessionItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, O as registerNavigator } from '../contentSource-Cplhv3bJ.js';
|
|
3
|
+
export { D as DataLayerProvider } from '../dataLayerProvider-DrBqOUa3.js';
|
|
4
|
+
import { D as DocType, d as QuestionRecord, b as DocTypePrefixes, C as CardHistory, c as CardRecord } from '../types-legacy-4tlwHnXo.js';
|
|
5
|
+
export { e as CardData, f as CourseListData, h as DataShapeData, g as DisplayableData, F as Field, G as GuestUsername, Q as QualifiedCardID, i as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-4tlwHnXo.js';
|
|
6
6
|
import { DataShape, ParsedCard } from '@vue-skuilder/common';
|
|
7
7
|
import 'moment';
|
|
8
8
|
|
package/dist/core/index.js
CHANGED
|
@@ -1829,10 +1829,8 @@ var init_elo = __esm({
|
|
|
1829
1829
|
{ limit, elo: "user" },
|
|
1830
1830
|
(c) => !activeCards.some((ac) => c.cardID === ac.cardID)
|
|
1831
1831
|
)).map((c) => ({ ...c, status: "new" }));
|
|
1832
|
-
const
|
|
1833
|
-
|
|
1834
|
-
const scored = newCards.map((c, i) => {
|
|
1835
|
-
const cardElo = cardEloData[i]?.global?.score ?? 1e3;
|
|
1832
|
+
const scored = newCards.map((c) => {
|
|
1833
|
+
const cardElo = c.elo ?? 1e3;
|
|
1836
1834
|
const distance = Math.abs(cardElo - userGlobalElo);
|
|
1837
1835
|
const rawScore = Math.max(0, 1 - distance / 500);
|
|
1838
1836
|
const samplingKey = rawScore > 0 ? Math.random() ** (1 / rawScore) : 0;
|
|
@@ -4056,7 +4054,8 @@ var init_orchestration = __esm({
|
|
|
4056
4054
|
// src/core/navigators/Pipeline.ts
|
|
4057
4055
|
var Pipeline_exports = {};
|
|
4058
4056
|
__export(Pipeline_exports, {
|
|
4059
|
-
Pipeline: () => Pipeline
|
|
4057
|
+
Pipeline: () => Pipeline,
|
|
4058
|
+
mergeHints: () => mergeHints2
|
|
4060
4059
|
});
|
|
4061
4060
|
function globToRegex(pattern) {
|
|
4062
4061
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -5703,6 +5702,7 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
5703
5702
|
}
|
|
5704
5703
|
async addNavigationStrategy(data) {
|
|
5705
5704
|
logger.debug(`[courseDB] Adding navigation strategy: ${data._id}`);
|
|
5705
|
+
this.invalidateNavigatorCache();
|
|
5706
5706
|
return this.remoteDB.put(data).then(() => {
|
|
5707
5707
|
});
|
|
5708
5708
|
}
|
|
@@ -5769,23 +5769,67 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5769
5769
|
* @returns Cards sorted by score descending
|
|
5770
5770
|
*/
|
|
5771
5771
|
_pendingHints = null;
|
|
5772
|
+
/**
|
|
5773
|
+
* Session-scoped cache of the broad ELO-neighbor pool used by
|
|
5774
|
+
* getCardsCenteredAtELO. The `elo` view query re-indexes on first touch per
|
|
5775
|
+
* call (PouchDB 9 ignores `stale`), so without this each plan/replan pays
|
|
5776
|
+
* ~1.5-2s. The pool is fetched once and re-ranked against the live (roaming)
|
|
5777
|
+
* ELO in memory on subsequent calls.
|
|
5778
|
+
*/
|
|
5779
|
+
_eloPoolCache = null;
|
|
5780
|
+
_eloPoolTtlMs = 5 * 60 * 1e3;
|
|
5781
|
+
/**
|
|
5782
|
+
* Cached assembled navigator (Pipeline). createNavigator() reads strategy
|
|
5783
|
+
* docs and builds a fresh Pipeline every call — whose internal `_tagCache`
|
|
5784
|
+
* and `_cachedOrchestration` are designed to make replans cheap but never
|
|
5785
|
+
* survive, because the instance is discarded each run. Caching it lets those
|
|
5786
|
+
* caches persist across plan/replan within a session (SessionController holds
|
|
5787
|
+
* one CourseDB instance for the session's lifetime). Rebuilt on user change,
|
|
5788
|
+
* TTL expiry, or explicit invalidation after a strategy-doc write.
|
|
5789
|
+
*/
|
|
5790
|
+
_cachedNavigator = null;
|
|
5791
|
+
_navigatorTtlMs = 5 * 60 * 1e3;
|
|
5772
5792
|
setEphemeralHints(hints) {
|
|
5773
5793
|
this._pendingHints = hints;
|
|
5774
5794
|
}
|
|
5775
5795
|
async getWeightedCards(limit) {
|
|
5776
5796
|
const u = await this._getCurrentUser();
|
|
5777
5797
|
try {
|
|
5778
|
-
const navigator2 = await this.
|
|
5798
|
+
const { navigator: navigator2 } = await this._getCachedNavigator(u);
|
|
5779
5799
|
if (this._pendingHints) {
|
|
5780
5800
|
navigator2.setEphemeralHints(this._pendingHints);
|
|
5781
5801
|
this._pendingHints = null;
|
|
5782
5802
|
}
|
|
5783
|
-
|
|
5803
|
+
const result = await navigator2.getWeightedCards(limit);
|
|
5804
|
+
return result;
|
|
5784
5805
|
} catch (e) {
|
|
5785
5806
|
logger.error(`[courseDB] Error getting weighted cards: ${e}`);
|
|
5786
5807
|
throw e;
|
|
5787
5808
|
}
|
|
5788
5809
|
}
|
|
5810
|
+
/**
|
|
5811
|
+
* Return the assembled navigator, reusing the cached instance when possible.
|
|
5812
|
+
* Reuse preserves the Pipeline's per-session caches (tags, orchestration
|
|
5813
|
+
* context) across replans, which is the dominant per-replan cost once the
|
|
5814
|
+
* ELO-pool cost is removed. Rebuilds on user change or TTL expiry.
|
|
5815
|
+
*/
|
|
5816
|
+
async _getCachedNavigator(user) {
|
|
5817
|
+
const userId = user.getUsername();
|
|
5818
|
+
const now = Date.now();
|
|
5819
|
+
if (this._cachedNavigator && this._cachedNavigator.userId === userId && now - this._cachedNavigator.builtAt < this._navigatorTtlMs) {
|
|
5820
|
+
return { navigator: this._cachedNavigator.navigator, cacheStatus: "hit" };
|
|
5821
|
+
}
|
|
5822
|
+
const navigator2 = await this.createNavigator(user);
|
|
5823
|
+
this._cachedNavigator = { navigator: navigator2, userId, builtAt: now };
|
|
5824
|
+
return { navigator: navigator2, cacheStatus: "miss" };
|
|
5825
|
+
}
|
|
5826
|
+
/**
|
|
5827
|
+
* Drop the cached navigator so the next getWeightedCards() rebuilds it.
|
|
5828
|
+
* Call after mutating this course's navigation strategy documents.
|
|
5829
|
+
*/
|
|
5830
|
+
invalidateNavigatorCache() {
|
|
5831
|
+
this._cachedNavigator = null;
|
|
5832
|
+
}
|
|
5789
5833
|
async getCardsCenteredAtELO(options = {
|
|
5790
5834
|
limit: 99,
|
|
5791
5835
|
elo: "user"
|
|
@@ -5808,20 +5852,31 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5808
5852
|
} else {
|
|
5809
5853
|
targetElo = options.elo;
|
|
5810
5854
|
}
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
let
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5855
|
+
const POOL_SIZE = Math.max(2e3, options.limit * 4);
|
|
5856
|
+
const nowMs = Date.now();
|
|
5857
|
+
let cacheStatus = "hit";
|
|
5858
|
+
if (!this._eloPoolCache || nowMs - this._eloPoolCache.fetchedAt > this._eloPoolTtlMs) {
|
|
5859
|
+
const fetched = await this.getCardsByELO(targetElo, POOL_SIZE);
|
|
5860
|
+
if (fetched.length > 0) {
|
|
5861
|
+
this._eloPoolCache = { rows: fetched, fetchedAt: nowMs };
|
|
5862
|
+
}
|
|
5863
|
+
cacheStatus = "miss";
|
|
5864
|
+
}
|
|
5865
|
+
const rankAgainstCurrentElo = () => {
|
|
5866
|
+
const raw = this._eloPoolCache?.rows ?? [];
|
|
5867
|
+
const survivors = filter ? raw.filter((c) => filter(c)) : raw;
|
|
5868
|
+
return survivors.map((c) => ({ ...c })).sort(
|
|
5869
|
+
(a, b) => Math.abs((a.elo ?? targetElo) - targetElo) - Math.abs((b.elo ?? targetElo) - targetElo)
|
|
5870
|
+
);
|
|
5871
|
+
};
|
|
5872
|
+
let cards = rankAgainstCurrentElo();
|
|
5873
|
+
if (cards.length < options.limit && (cacheStatus === "hit" || !this._eloPoolCache)) {
|
|
5874
|
+
const fetched = await this.getCardsByELO(targetElo, POOL_SIZE);
|
|
5875
|
+
if (fetched.length > 0) {
|
|
5876
|
+
this._eloPoolCache = { rows: fetched, fetchedAt: nowMs };
|
|
5877
|
+
}
|
|
5878
|
+
cards = rankAgainstCurrentElo();
|
|
5879
|
+
cacheStatus = "refresh";
|
|
5825
5880
|
}
|
|
5826
5881
|
const selectedCards = [];
|
|
5827
5882
|
while (selectedCards.length < options.limit && cards.length > 0) {
|