@vue-skuilder/db 0.1.17 → 0.1.18
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/{userDB-DNa0XPtn.d.ts → classroomDB-BgfrVb8d.d.ts} +357 -103
- package/dist/{userDB-BqwxtJ_7.d.mts → classroomDB-CTOenngH.d.cts} +358 -104
- package/dist/core/index.d.cts +230 -0
- package/dist/core/index.d.ts +161 -23
- package/dist/core/index.js +1964 -154
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +1925 -121
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BV5iZqt_.d.ts → dataLayerProvider-CZxC9GtB.d.ts} +1 -1
- package/dist/{dataLayerProvider-VlngD19_.d.mts → dataLayerProvider-D6PoCwS6.d.cts} +1 -1
- package/dist/impl/couch/{index.d.mts → index.d.cts} +46 -5
- package/dist/impl/couch/index.d.ts +44 -3
- package/dist/impl/couch/index.js +1971 -171
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +1933 -134
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/{index.d.mts → index.d.cts} +5 -6
- package/dist/impl/static/index.d.ts +2 -3
- package/dist/impl/static/index.js +1614 -119
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +1585 -92
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-Bmll7Xse.d.mts → index-D-Fa4Smt.d.cts} +1 -1
- package/dist/{index.d.mts → index.d.cts} +97 -13
- package/dist/index.d.ts +90 -6
- package/dist/index.js +2085 -153
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2031 -106
- package/dist/index.mjs.map +1 -1
- package/dist/pouch/index.js +3 -3
- package/dist/{types-Dbp5DaRR.d.mts → types-CzPDLAK6.d.cts} +1 -1
- package/dist/util/packer/{index.d.mts → index.d.cts} +3 -3
- package/dist/util/packer/index.js.map +1 -1
- package/dist/util/packer/index.mjs.map +1 -1
- package/docs/brainstorm-navigation-paradigm.md +369 -0
- package/docs/navigators-architecture.md +265 -0
- package/docs/todo-evolutionary-orchestration.md +310 -0
- package/docs/todo-nominal-tag-types.md +121 -0
- package/docs/todo-pipeline-optimization.md +117 -0
- package/docs/todo-strategy-authoring.md +401 -0
- package/docs/todo-strategy-state-storage.md +278 -0
- package/eslint.config.mjs +1 -1
- package/package.json +9 -4
- package/src/core/interfaces/contentSource.ts +88 -4
- package/src/core/interfaces/navigationStrategyManager.ts +0 -5
- package/src/core/navigators/CompositeGenerator.ts +268 -0
- package/src/core/navigators/Pipeline.ts +205 -0
- package/src/core/navigators/PipelineAssembler.ts +194 -0
- package/src/core/navigators/elo.ts +104 -15
- package/src/core/navigators/filters/eloDistance.ts +132 -0
- package/src/core/navigators/filters/index.ts +6 -0
- package/src/core/navigators/filters/types.ts +115 -0
- package/src/core/navigators/generators/index.ts +2 -0
- package/src/core/navigators/generators/types.ts +107 -0
- package/src/core/navigators/hardcodedOrder.ts +111 -12
- package/src/core/navigators/hierarchyDefinition.ts +266 -0
- package/src/core/navigators/index.ts +345 -3
- package/src/core/navigators/interferenceMitigator.ts +367 -0
- package/src/core/navigators/relativePriority.ts +267 -0
- package/src/core/navigators/srs.ts +195 -0
- package/src/impl/couch/classroomDB.ts +51 -0
- package/src/impl/couch/courseDB.ts +117 -39
- package/src/impl/static/courseDB.ts +0 -4
- package/src/study/SessionController.ts +149 -1
- package/src/study/TagFilteredContentSource.ts +255 -0
- package/src/study/index.ts +1 -0
- package/src/util/dataDirectory.test.ts +51 -22
- package/src/util/logger.ts +0 -1
- package/tests/core/navigators/CompositeGenerator.test.ts +455 -0
- package/tests/core/navigators/Pipeline.test.ts +405 -0
- package/tests/core/navigators/PipelineAssembler.test.ts +351 -0
- package/tests/core/navigators/SRSNavigator.test.ts +344 -0
- package/tests/core/navigators/eloDistanceFilter.test.ts +192 -0
- package/tests/core/navigators/navigators.test.ts +710 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +29 -0
- package/dist/core/index.d.mts +0 -92
- /package/dist/{SyncStrategy-CyATpyLQ.d.mts → SyncStrategy-CyATpyLQ.d.cts} +0 -0
- /package/dist/pouch/{index.d.mts → index.d.cts} +0 -0
- /package/dist/{types-legacy-6ettoclI.d.mts → types-legacy-6ettoclI.d.cts} +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { createEloDistanceFilter } from '../../../src/core/navigators/filters/eloDistance';
|
|
3
|
+
import { WeightedCard } from '../../../src/core/navigators/index';
|
|
4
|
+
import { FilterContext } from '../../../src/core/navigators/filters/types';
|
|
5
|
+
import { CourseDBInterface } from '../../../src/core/interfaces/courseDB';
|
|
6
|
+
import { UserDBInterface } from '../../../src/core/interfaces/userDB';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper to create a weighted card for testing
|
|
10
|
+
*/
|
|
11
|
+
function createCard(id: string, score: number): WeightedCard {
|
|
12
|
+
return {
|
|
13
|
+
cardId: id,
|
|
14
|
+
courseId: 'test-course',
|
|
15
|
+
score,
|
|
16
|
+
provenance: [
|
|
17
|
+
{
|
|
18
|
+
strategy: 'test',
|
|
19
|
+
strategyName: 'Test',
|
|
20
|
+
strategyId: 'TEST',
|
|
21
|
+
action: 'generated',
|
|
22
|
+
score,
|
|
23
|
+
reason: 'Test card',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create mock context with specified user ELO and card ELO responses
|
|
31
|
+
*/
|
|
32
|
+
function createMockContext(userElo: number, cardEloMap: Record<string, number>): FilterContext {
|
|
33
|
+
const mockCourse = {
|
|
34
|
+
getCourseID: vi.fn().mockReturnValue('test-course'),
|
|
35
|
+
getCardEloData: vi.fn().mockImplementation((cardIds: string[]) => {
|
|
36
|
+
return Promise.resolve(
|
|
37
|
+
cardIds.map((id) => ({
|
|
38
|
+
global: { score: cardEloMap[id] ?? 1000, count: 10 },
|
|
39
|
+
tags: {},
|
|
40
|
+
}))
|
|
41
|
+
);
|
|
42
|
+
}),
|
|
43
|
+
} as unknown as CourseDBInterface;
|
|
44
|
+
|
|
45
|
+
const mockUser = {} as unknown as UserDBInterface;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
user: mockUser,
|
|
49
|
+
course: mockCourse,
|
|
50
|
+
userElo,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe('ELO Distance Filter', () => {
|
|
55
|
+
describe('smooth curve behavior', () => {
|
|
56
|
+
it('should return ~1.0 multiplier for exact ELO match', async () => {
|
|
57
|
+
const filter = createEloDistanceFilter();
|
|
58
|
+
const cards = [createCard('card-1', 1.0)];
|
|
59
|
+
const context = createMockContext(1000, { 'card-1': 1000 });
|
|
60
|
+
|
|
61
|
+
const result = await filter.transform(cards, context);
|
|
62
|
+
|
|
63
|
+
expect(result[0].score).toBeCloseTo(1.0, 2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return ~0.6 multiplier at halfLife distance (200 ELO)', async () => {
|
|
67
|
+
const filter = createEloDistanceFilter();
|
|
68
|
+
const cards = [createCard('card-1', 1.0)];
|
|
69
|
+
const context = createMockContext(1000, { 'card-1': 1200 }); // 200 distance
|
|
70
|
+
|
|
71
|
+
const result = await filter.transform(cards, context);
|
|
72
|
+
|
|
73
|
+
// At halfLife: minMultiplier + (maxMultiplier - minMultiplier) * exp(-1)
|
|
74
|
+
// = 0.3 + 0.7 * 0.368 ≈ 0.56
|
|
75
|
+
expect(result[0].score).toBeCloseTo(0.56, 1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should approach minMultiplier for very large distances', async () => {
|
|
79
|
+
const filter = createEloDistanceFilter();
|
|
80
|
+
const cards = [createCard('card-1', 1.0)];
|
|
81
|
+
const context = createMockContext(1000, { 'card-1': 2000 }); // 1000 distance (5x halfLife)
|
|
82
|
+
|
|
83
|
+
const result = await filter.transform(cards, context);
|
|
84
|
+
|
|
85
|
+
// Should be very close to minMultiplier (0.3)
|
|
86
|
+
expect(result[0].score).toBeCloseTo(0.3, 1);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should be symmetric - same penalty above and below user ELO', async () => {
|
|
90
|
+
const filter = createEloDistanceFilter();
|
|
91
|
+
const cards = [createCard('above', 1.0), createCard('below', 1.0)];
|
|
92
|
+
const context = createMockContext(1000, {
|
|
93
|
+
above: 1150, // 150 above
|
|
94
|
+
below: 850, // 150 below
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const result = await filter.transform(cards, context);
|
|
98
|
+
|
|
99
|
+
expect(result[0].score).toBeCloseTo(result[1].score, 4);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should produce monotonically decreasing scores with increasing distance', async () => {
|
|
103
|
+
const filter = createEloDistanceFilter();
|
|
104
|
+
const cards = [
|
|
105
|
+
createCard('d0', 1.0),
|
|
106
|
+
createCard('d100', 1.0),
|
|
107
|
+
createCard('d200', 1.0),
|
|
108
|
+
createCard('d300', 1.0),
|
|
109
|
+
createCard('d400', 1.0),
|
|
110
|
+
];
|
|
111
|
+
const context = createMockContext(1000, {
|
|
112
|
+
d0: 1000,
|
|
113
|
+
d100: 1100,
|
|
114
|
+
d200: 1200,
|
|
115
|
+
d300: 1300,
|
|
116
|
+
d400: 1400,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const result = await filter.transform(cards, context);
|
|
120
|
+
|
|
121
|
+
// Each should be less than the previous (smooth decay)
|
|
122
|
+
for (let i = 1; i < result.length; i++) {
|
|
123
|
+
expect(result[i].score).toBeLessThan(result[i - 1].score);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should have no discontinuities - adjacent distances should have similar scores', async () => {
|
|
128
|
+
const filter = createEloDistanceFilter();
|
|
129
|
+
const cards = [createCard('d99', 1.0), createCard('d100', 1.0), createCard('d101', 1.0)];
|
|
130
|
+
const context = createMockContext(1000, {
|
|
131
|
+
d99: 1099,
|
|
132
|
+
d100: 1100,
|
|
133
|
+
d101: 1101,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const result = await filter.transform(cards, context);
|
|
137
|
+
|
|
138
|
+
// Adjacent scores should differ by less than 1%
|
|
139
|
+
const diff1 = Math.abs(result[0].score - result[1].score);
|
|
140
|
+
const diff2 = Math.abs(result[1].score - result[2].score);
|
|
141
|
+
|
|
142
|
+
expect(diff1).toBeLessThan(0.01);
|
|
143
|
+
expect(diff2).toBeLessThan(0.01);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('provenance tracking', () => {
|
|
148
|
+
it('should append provenance entry with ELO details', async () => {
|
|
149
|
+
const filter = createEloDistanceFilter();
|
|
150
|
+
const cards = [createCard('card-1', 0.8)];
|
|
151
|
+
const context = createMockContext(1000, { 'card-1': 1200 });
|
|
152
|
+
|
|
153
|
+
const result = await filter.transform(cards, context);
|
|
154
|
+
|
|
155
|
+
expect(result[0].provenance).toHaveLength(2);
|
|
156
|
+
const entry = result[0].provenance[1];
|
|
157
|
+
expect(entry.strategy).toBe('eloDistance');
|
|
158
|
+
expect(entry.action).toBe('penalized');
|
|
159
|
+
expect(entry.reason).toContain('200');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('custom configuration', () => {
|
|
164
|
+
it('should use custom halfLife when provided', async () => {
|
|
165
|
+
// With halfLife=100, distance 100 should give ~0.56 multiplier
|
|
166
|
+
const filter = createEloDistanceFilter({ halfLife: 100 });
|
|
167
|
+
const cards = [createCard('card-1', 1.0)];
|
|
168
|
+
const context = createMockContext(1000, { 'card-1': 1100 }); // 100 distance
|
|
169
|
+
|
|
170
|
+
const result = await filter.transform(cards, context);
|
|
171
|
+
|
|
172
|
+
expect(result[0].score).toBeCloseTo(0.56, 1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should use custom min/max multipliers when provided', async () => {
|
|
176
|
+
const filter = createEloDistanceFilter({
|
|
177
|
+
minMultiplier: 0.5,
|
|
178
|
+
maxMultiplier: 0.9,
|
|
179
|
+
});
|
|
180
|
+
const cards = [createCard('exact', 1.0), createCard('far', 1.0)];
|
|
181
|
+
const context = createMockContext(1000, {
|
|
182
|
+
exact: 1000, // 0 distance
|
|
183
|
+
far: 2000, // very far
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const result = await filter.transform(cards, context);
|
|
187
|
+
|
|
188
|
+
expect(result[0].score).toBeCloseTo(0.9, 2); // maxMultiplier
|
|
189
|
+
expect(result[1].score).toBeCloseTo(0.5, 1); // approaches minMultiplier
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|