@vue-skuilder/db 0.1.31-a → 0.1.31
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-BmnmvH8C.d.ts → contentSource-Bdwkvqa8.d.ts} +35 -4
- package/dist/{contentSource-DfBbaLA-.d.cts → contentSource-DF1nUbPQ.d.cts} +35 -4
- package/dist/core/index.d.cts +48 -3
- package/dist/core/index.d.ts +48 -3
- package/dist/core/index.js +587 -56
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +586 -56
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BeRXVMs5.d.cts → dataLayerProvider-BKmVoyJR.d.ts} +20 -1
- package/dist/{dataLayerProvider-CG9GfaAY.d.ts → dataLayerProvider-BQdfJuBN.d.cts} +20 -1
- package/dist/impl/couch/index.d.cts +156 -4
- package/dist/impl/couch/index.d.ts +156 -4
- package/dist/impl/couch/index.js +805 -47
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +804 -47
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.cts +3 -2
- package/dist/impl/static/index.d.ts +3 -2
- package/dist/impl/static/index.js +542 -37
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +542 -37
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.cts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +1040 -90
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1030 -81
- package/dist/index.mjs.map +1 -1
- package/docs/navigators-architecture.md +64 -5
- package/package.json +3 -3
- package/src/core/interfaces/contentSource.ts +6 -0
- package/src/core/interfaces/courseDB.ts +6 -0
- package/src/core/interfaces/dataLayerProvider.ts +20 -0
- package/src/core/navigators/Pipeline.ts +414 -9
- package/src/core/navigators/PipelineAssembler.ts +23 -18
- package/src/core/navigators/PipelineDebugger.ts +115 -1
- package/src/core/navigators/filters/hierarchyDefinition.ts +78 -8
- package/src/core/navigators/generators/prescribed.ts +95 -0
- package/src/core/navigators/index.ts +55 -10
- package/src/impl/common/BaseUserDB.ts +4 -1
- package/src/impl/couch/CourseSyncService.ts +356 -0
- package/src/impl/couch/PouchDataLayerProvider.ts +21 -1
- package/src/impl/couch/courseDB.ts +60 -13
- package/src/impl/couch/index.ts +1 -0
- package/src/impl/static/courseDB.ts +5 -0
- package/src/study/ItemQueue.ts +42 -0
- package/src/study/SessionController.ts +195 -22
- package/src/study/SpacedRepetition.ts +7 -2
- package/tests/core/navigators/Pipeline.test.ts +1 -1
- package/tests/core/navigators/PipelineAssembler.test.ts +15 -14
|
@@ -374,6 +374,45 @@ Set `staticWeight: true` for foundational strategies that should not be tuned:
|
|
|
374
374
|
|
|
375
375
|
## Creating New Strategies
|
|
376
376
|
|
|
377
|
+
Strategies can be defined in two places:
|
|
378
|
+
|
|
379
|
+
- **Framework-internal:** Added directly to `NavigatorRoles` in `index.ts`. Used
|
|
380
|
+
for general-purpose strategies shipped with the framework.
|
|
381
|
+
- **Consumer-defined:** Registered at app startup via `registerNavigator()`.
|
|
382
|
+
Used for course-specific strategies that live in the consumer codebase.
|
|
383
|
+
|
|
384
|
+
Both types participate identically in the pipeline once registered.
|
|
385
|
+
|
|
386
|
+
### Registration
|
|
387
|
+
|
|
388
|
+
Framework-internal strategies are listed in the hardcoded `NavigatorRoles` record.
|
|
389
|
+
Consumer-defined strategies use the public `registerNavigator()` API:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { registerNavigator, NavigatorRole } from '@vue-skuilder/db';
|
|
393
|
+
import { MyFilter } from './MyFilter';
|
|
394
|
+
|
|
395
|
+
// At app init, before any study session:
|
|
396
|
+
registerNavigator('myFilter', MyFilter, NavigatorRole.FILTER);
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The third argument (`role`) is **required** for consumer-defined strategies —
|
|
400
|
+
without it, `PipelineAssembler` cannot classify the strategy and will skip it
|
|
401
|
+
with a warning. For framework-internal strategies the role is already in
|
|
402
|
+
`NavigatorRoles`, so the argument is optional.
|
|
403
|
+
|
|
404
|
+
A corresponding `NAVIGATION_STRATEGY` document must exist in CouchDB with
|
|
405
|
+
`implementingClass` matching the registered name:
|
|
406
|
+
|
|
407
|
+
```json
|
|
408
|
+
{
|
|
409
|
+
"_id": "NAVIGATION_STRATEGY-my-filter",
|
|
410
|
+
"implementingClass": "myFilter",
|
|
411
|
+
"name": "My Filter",
|
|
412
|
+
"serializedData": "{}"
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
377
416
|
### Generator
|
|
378
417
|
|
|
379
418
|
```typescript
|
|
@@ -382,7 +421,7 @@ class MyGenerator extends ContentNavigator implements CardGenerator {
|
|
|
382
421
|
|
|
383
422
|
async getWeightedCards(limit: number, context?: GeneratorContext): Promise<WeightedCard[]> {
|
|
384
423
|
const candidates = await this.findCandidates(limit);
|
|
385
|
-
|
|
424
|
+
|
|
386
425
|
return candidates.map(c => ({
|
|
387
426
|
cardId: c.id,
|
|
388
427
|
courseId: this.course.getCourseID(),
|
|
@@ -400,8 +439,6 @@ class MyGenerator extends ContentNavigator implements CardGenerator {
|
|
|
400
439
|
}
|
|
401
440
|
```
|
|
402
441
|
|
|
403
|
-
Register in `NavigatorRoles` as `NavigatorRole.GENERATOR`.
|
|
404
|
-
|
|
405
442
|
### Filter
|
|
406
443
|
|
|
407
444
|
```typescript
|
|
@@ -413,7 +450,7 @@ class MyFilter extends ContentNavigator implements CardFilter {
|
|
|
413
450
|
const multiplier = this.computeMultiplier(card, context);
|
|
414
451
|
const newScore = card.score * multiplier;
|
|
415
452
|
const action = multiplier < 1 ? 'penalized' : multiplier > 1 ? 'boosted' : 'passed';
|
|
416
|
-
|
|
453
|
+
|
|
417
454
|
return {
|
|
418
455
|
...card,
|
|
419
456
|
score: newScore,
|
|
@@ -434,7 +471,29 @@ class MyFilter extends ContentNavigator implements CardFilter {
|
|
|
434
471
|
}
|
|
435
472
|
```
|
|
436
473
|
|
|
437
|
-
|
|
474
|
+
### Accessing Strategy State from Consumer Filters
|
|
475
|
+
|
|
476
|
+
Consumer strategies can share state with other parts of the consumer app via
|
|
477
|
+
`getStrategyState()` / `putStrategyState()`. Override `strategyKey` to read
|
|
478
|
+
an existing state document:
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
class MyFilter extends ContentNavigator implements CardFilter {
|
|
482
|
+
// Read the same doc that another part of the app writes
|
|
483
|
+
protected get strategyKey(): string {
|
|
484
|
+
return 'MySharedStateKey';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async transform(cards: WeightedCard[], context: FilterContext): Promise<WeightedCard[]> {
|
|
488
|
+
const state = await this.getStrategyState<MyStateType>();
|
|
489
|
+
// ... use state for filtering decisions
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
This enables **single source of truth** patterns: the consumer app writes state
|
|
495
|
+
via `UsrCrsDataInterface.putStrategyState()`, and the consumer filter reads it
|
|
496
|
+
via the same key. No framework changes needed.
|
|
438
497
|
|
|
439
498
|
---
|
|
440
499
|
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.1.31
|
|
7
|
+
"version": "0.1.31",
|
|
8
8
|
"description": "Database layer for vue-skuilder",
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"module": "dist/index.mjs",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@nilock2/pouchdb-authentication": "^1.0.2",
|
|
51
|
-
"@vue-skuilder/common": "0.1.31
|
|
51
|
+
"@vue-skuilder/common": "0.1.31",
|
|
52
52
|
"cross-fetch": "^4.1.0",
|
|
53
53
|
"moment": "^2.29.4",
|
|
54
54
|
"pouchdb": "^9.0.0",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"vite": "^7.0.0",
|
|
63
63
|
"vitest": "^4.0.15"
|
|
64
64
|
},
|
|
65
|
-
"stableVersion": "0.1.
|
|
65
|
+
"stableVersion": "0.1.31"
|
|
66
66
|
}
|
|
@@ -79,6 +79,12 @@ export interface StudyContentSource {
|
|
|
79
79
|
* Used for recording learning outcomes.
|
|
80
80
|
*/
|
|
81
81
|
getOrchestrationContext?(): Promise<OrchestrationContext>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Set ephemeral hints for the next pipeline run.
|
|
85
|
+
* No-op for sources that don't support hints.
|
|
86
|
+
*/
|
|
87
|
+
setEphemeralHints?(hints: Record<string, unknown>): void;
|
|
82
88
|
}
|
|
83
89
|
// #endregion docs_StudyContentSource
|
|
84
90
|
|
|
@@ -100,6 +100,12 @@ export interface CourseDBInterface extends NavigationStrategyManager, StudyConte
|
|
|
100
100
|
*/
|
|
101
101
|
getAppliedTagsBatch(cardIds: string[]): Promise<Map<string, string[]>>;
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Get all card IDs in the course.
|
|
105
|
+
* Used by diagnostics to scan the full card space.
|
|
106
|
+
*/
|
|
107
|
+
getAllCardIds(): Promise<string[]>;
|
|
108
|
+
|
|
103
109
|
/**
|
|
104
110
|
* Add a tag to a card
|
|
105
111
|
*/
|
|
@@ -56,4 +56,24 @@ export interface DataLayerProvider {
|
|
|
56
56
|
* Check if this data layer is read-only
|
|
57
57
|
*/
|
|
58
58
|
isReadOnly(): boolean;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Trigger local replication of a course database.
|
|
62
|
+
*
|
|
63
|
+
* When a course opts in via `CourseConfig.localSync.enabled`, this method
|
|
64
|
+
* replicates the remote course DB to a local PouchDB instance. Subsequent
|
|
65
|
+
* `getCourseDB()` calls for that course will return a CourseDB that reads
|
|
66
|
+
* from the local replica (fast, no network) and writes to the remote
|
|
67
|
+
* (ELO updates, admin ops).
|
|
68
|
+
*
|
|
69
|
+
* Safe to call multiple times — concurrent calls coalesce. Returns when
|
|
70
|
+
* sync is complete (or immediately if already synced / disabled).
|
|
71
|
+
*
|
|
72
|
+
* Implementations that don't support local sync may no-op.
|
|
73
|
+
*
|
|
74
|
+
* @param courseId - The course to sync locally
|
|
75
|
+
* @param forceEnabled - Skip CourseConfig check and sync regardless.
|
|
76
|
+
* Use when the caller already knows local sync is desired.
|
|
77
|
+
*/
|
|
78
|
+
ensureCourseSynced?(courseId: string, forceEnabled?: boolean): Promise<void>;
|
|
59
79
|
}
|