@vue-skuilder/db 0.1.14-2 → 0.1.15
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/core/index.js +40 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +40 -4
- package/dist/core/index.mjs.map +1 -1
- package/dist/impl/couch/index.js +40 -4
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +40 -4
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.mts +3 -1
- package/dist/impl/static/index.d.ts +3 -1
- package/dist/impl/static/index.js +71 -13
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +71 -13
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.mts +38 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +149 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +149 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/impl/common/BaseUserDB.ts +12 -2
- package/src/impl/couch/updateQueue.ts +28 -2
- package/src/impl/static/StaticDataLayerProvider.ts +32 -8
- package/src/impl/static/coursesDB.ts +15 -2
- package/src/study/SessionController.ts +50 -0
- package/src/study/services/CardHydrationService.ts +7 -0
- package/src/study/services/ResponseProcessor.ts +64 -25
|
@@ -22,6 +22,7 @@ declare class StaticDataLayerProvider implements DataLayerProvider {
|
|
|
22
22
|
private initialized;
|
|
23
23
|
private courseUnpackers;
|
|
24
24
|
private manifests;
|
|
25
|
+
private dependencyNameToCourseId;
|
|
25
26
|
constructor(config: Partial<StaticDataLayerConfig>);
|
|
26
27
|
private resolveCourseDependencies;
|
|
27
28
|
initialize(): Promise<void>;
|
|
@@ -173,7 +174,8 @@ declare class StaticCourseDB implements CourseDBInterface {
|
|
|
173
174
|
|
|
174
175
|
declare class StaticCoursesDB implements CoursesDBInterface {
|
|
175
176
|
private manifests;
|
|
176
|
-
|
|
177
|
+
private dependencyNameToCourseId?;
|
|
178
|
+
constructor(manifests: Record<string, StaticCourseManifest>, dependencyNameToCourseId?: Map<string, string> | undefined);
|
|
177
179
|
getCourseConfig(courseId: string): Promise<CourseConfig>;
|
|
178
180
|
getCourseList(): Promise<CourseConfig[]>;
|
|
179
181
|
disambiguateCourse(_courseId: string, _disambiguator: string): Promise<void>;
|
|
@@ -22,6 +22,7 @@ declare class StaticDataLayerProvider implements DataLayerProvider {
|
|
|
22
22
|
private initialized;
|
|
23
23
|
private courseUnpackers;
|
|
24
24
|
private manifests;
|
|
25
|
+
private dependencyNameToCourseId;
|
|
25
26
|
constructor(config: Partial<StaticDataLayerConfig>);
|
|
26
27
|
private resolveCourseDependencies;
|
|
27
28
|
initialize(): Promise<void>;
|
|
@@ -173,7 +174,8 @@ declare class StaticCourseDB implements CourseDBInterface {
|
|
|
173
174
|
|
|
174
175
|
declare class StaticCoursesDB implements CoursesDBInterface {
|
|
175
176
|
private manifests;
|
|
176
|
-
|
|
177
|
+
private dependencyNameToCourseId?;
|
|
178
|
+
constructor(manifests: Record<string, StaticCourseManifest>, dependencyNameToCourseId?: Map<string, string> | undefined);
|
|
177
179
|
getCourseConfig(courseId: string): Promise<CourseConfig>;
|
|
178
180
|
getCourseList(): Promise<CourseConfig[]>;
|
|
179
181
|
disambiguateCourse(_courseId: string, _disambiguator: string): Promise<void>;
|
|
@@ -281,6 +281,33 @@ var init_updateQueue = __esm({
|
|
|
281
281
|
// Database for read operations
|
|
282
282
|
writeDB;
|
|
283
283
|
// Database for write operations (local-first)
|
|
284
|
+
/**
|
|
285
|
+
* Queues an update for a document and applies it with conflict resolution.
|
|
286
|
+
*
|
|
287
|
+
* @param id - Document ID to update
|
|
288
|
+
* @param update - Partial object or function that transforms the document
|
|
289
|
+
* @returns Promise resolving to the updated document
|
|
290
|
+
*
|
|
291
|
+
* @throws {PouchError} with status 404 if document doesn't exist
|
|
292
|
+
*
|
|
293
|
+
* @remarks
|
|
294
|
+
* **Error Handling Pattern:**
|
|
295
|
+
* - This method does NOT create documents if they don't exist
|
|
296
|
+
* - Callers are responsible for handling 404 errors and creating documents
|
|
297
|
+
* - This design maintains separation of concerns (UpdateQueue handles conflicts, callers handle lifecycle)
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* try {
|
|
302
|
+
* await updateQueue.update(docId, (doc) => ({ ...doc, field: newValue }));
|
|
303
|
+
* } catch (e) {
|
|
304
|
+
* if ((e as PouchError).status === 404) {
|
|
305
|
+
* // Create the document with initial values
|
|
306
|
+
* await db.put({ _id: docId, field: newValue, ...initialFields });
|
|
307
|
+
* }
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
284
311
|
update(id, update) {
|
|
285
312
|
logger.debug(`Update requested on doc: ${id}`);
|
|
286
313
|
if (this.pendingUpdates[id]) {
|
|
@@ -313,7 +340,6 @@ var init_updateQueue = __esm({
|
|
|
313
340
|
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
314
341
|
try {
|
|
315
342
|
const doc = await this.readDB.get(id);
|
|
316
|
-
logger.debug(`Retrieved doc: ${id}`);
|
|
317
343
|
let updatedDoc = { ...doc };
|
|
318
344
|
const updatesToApply = [...this.pendingUpdates[id]];
|
|
319
345
|
for (const update of updatesToApply) {
|
|
@@ -327,7 +353,6 @@ var init_updateQueue = __esm({
|
|
|
327
353
|
}
|
|
328
354
|
}
|
|
329
355
|
await this.writeDB.put(updatedDoc);
|
|
330
|
-
logger.debug(`Put doc: ${id}`);
|
|
331
356
|
this.pendingUpdates[id].splice(0, updatesToApply.length);
|
|
332
357
|
if (this.pendingUpdates[id].length === 0) {
|
|
333
358
|
this.inprogressUpdates[id] = false;
|
|
@@ -342,6 +367,7 @@ var init_updateQueue = __esm({
|
|
|
342
367
|
await new Promise((res) => setTimeout(res, 50 * Math.random()));
|
|
343
368
|
} else if (e.name === "not_found" && i === 0) {
|
|
344
369
|
logger.warn(`Update failed for ${id} - does not exist. Throwing to caller.`);
|
|
370
|
+
delete this.inprogressUpdates[id];
|
|
345
371
|
throw e;
|
|
346
372
|
} else {
|
|
347
373
|
delete this.inprogressUpdates[id];
|
|
@@ -1506,12 +1532,22 @@ Currently logged-in as ${this._username}.`
|
|
|
1506
1532
|
}
|
|
1507
1533
|
/**
|
|
1508
1534
|
* Logs a record of the user's interaction with the card and returns the card's
|
|
1509
|
-
* up-to-date history
|
|
1535
|
+
* up-to-date history.
|
|
1536
|
+
*
|
|
1537
|
+
* **Automatic Initialization:**
|
|
1538
|
+
* If this is the user's first interaction with the card (CardHistory doesn't exist),
|
|
1539
|
+
* this method automatically creates the CardHistory document with initial values
|
|
1540
|
+
* (lapses: 0, streak: 0, bestInterval: 0).
|
|
1541
|
+
*
|
|
1542
|
+
* **Error Handling:**
|
|
1543
|
+
* - Handles 404 errors by creating initial CardHistory document
|
|
1544
|
+
* - Re-throws all other errors from UpdateQueue
|
|
1510
1545
|
*
|
|
1511
1546
|
* // [ ] #db-refactor extract to a smaller scope - eg, UserStudySession
|
|
1512
1547
|
*
|
|
1513
|
-
* @param record
|
|
1548
|
+
* @param record - The recent recorded interaction between user and card
|
|
1514
1549
|
* @returns The updated state of the card's CardHistory data
|
|
1550
|
+
* @throws Error if document creation fails or non-404 database error occurs
|
|
1515
1551
|
*/
|
|
1516
1552
|
async putCardRecord(record) {
|
|
1517
1553
|
const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
|
|
@@ -2598,11 +2634,18 @@ var init_coursesDB = __esm({
|
|
|
2598
2634
|
"use strict";
|
|
2599
2635
|
init_logger();
|
|
2600
2636
|
StaticCoursesDB = class {
|
|
2601
|
-
constructor(manifests) {
|
|
2637
|
+
constructor(manifests, dependencyNameToCourseId) {
|
|
2602
2638
|
this.manifests = manifests;
|
|
2639
|
+
this.dependencyNameToCourseId = dependencyNameToCourseId;
|
|
2603
2640
|
}
|
|
2604
2641
|
async getCourseConfig(courseId) {
|
|
2605
|
-
|
|
2642
|
+
let manifest = this.manifests[courseId];
|
|
2643
|
+
if (!manifest && this.dependencyNameToCourseId) {
|
|
2644
|
+
const mappedCourseId = this.dependencyNameToCourseId.get(courseId);
|
|
2645
|
+
if (mappedCourseId) {
|
|
2646
|
+
manifest = this.manifests[mappedCourseId];
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2606
2649
|
if (!manifest) {
|
|
2607
2650
|
logger.warn(`Course manifest for ${courseId} not found`);
|
|
2608
2651
|
throw new Error(`Course ${courseId} not found`);
|
|
@@ -2700,6 +2743,8 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2700
2743
|
initialized = false;
|
|
2701
2744
|
courseUnpackers = /* @__PURE__ */ new Map();
|
|
2702
2745
|
manifests = {};
|
|
2746
|
+
// Mapping from dependency name to actual courseId for backwards compatibility
|
|
2747
|
+
dependencyNameToCourseId = /* @__PURE__ */ new Map();
|
|
2703
2748
|
constructor(config) {
|
|
2704
2749
|
this.config = {
|
|
2705
2750
|
localStoragePrefix: config.localStoragePrefix || "skuilder-static",
|
|
@@ -2727,10 +2772,15 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2727
2772
|
throw new Error(`Failed to fetch final content manifest for ${courseName} at ${finalManifestUrl}`);
|
|
2728
2773
|
}
|
|
2729
2774
|
const finalManifest = await finalManifestResponse.json();
|
|
2730
|
-
|
|
2775
|
+
const courseId = finalManifest.courseId || finalManifest.courseConfig?.courseID;
|
|
2776
|
+
if (!courseId) {
|
|
2777
|
+
throw new Error(`Course manifest for ${courseName} missing courseId`);
|
|
2778
|
+
}
|
|
2779
|
+
this.manifests[courseId] = finalManifest;
|
|
2731
2780
|
const unpacker = new StaticDataUnpacker(finalManifest, baseUrl);
|
|
2732
|
-
this.courseUnpackers.set(
|
|
2733
|
-
|
|
2781
|
+
this.courseUnpackers.set(courseId, unpacker);
|
|
2782
|
+
this.dependencyNameToCourseId.set(courseName, courseId);
|
|
2783
|
+
logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName} (courseId: ${courseId})`);
|
|
2734
2784
|
}
|
|
2735
2785
|
} catch (e) {
|
|
2736
2786
|
logger.error(`[StaticDataLayerProvider] Failed to resolve dependency ${courseName}:`, e);
|
|
@@ -2753,15 +2803,23 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2753
2803
|
return BaseUser.Dummy(syncStrategy);
|
|
2754
2804
|
}
|
|
2755
2805
|
getCourseDB(courseId) {
|
|
2756
|
-
|
|
2806
|
+
let unpacker = this.courseUnpackers.get(courseId);
|
|
2807
|
+
let actualCourseId = courseId;
|
|
2808
|
+
if (!unpacker) {
|
|
2809
|
+
const mappedCourseId = this.dependencyNameToCourseId.get(courseId);
|
|
2810
|
+
if (mappedCourseId) {
|
|
2811
|
+
unpacker = this.courseUnpackers.get(mappedCourseId);
|
|
2812
|
+
actualCourseId = mappedCourseId;
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2757
2815
|
if (!unpacker) {
|
|
2758
2816
|
throw new Error(`Course ${courseId} not found or failed to initialize in static data layer.`);
|
|
2759
2817
|
}
|
|
2760
|
-
const manifest = this.manifests[
|
|
2761
|
-
return new StaticCourseDB(
|
|
2818
|
+
const manifest = this.manifests[actualCourseId];
|
|
2819
|
+
return new StaticCourseDB(actualCourseId, unpacker, this.getUserDB(), manifest);
|
|
2762
2820
|
}
|
|
2763
2821
|
getCoursesDB() {
|
|
2764
|
-
return new StaticCoursesDB(this.manifests);
|
|
2822
|
+
return new StaticCoursesDB(this.manifests, this.dependencyNameToCourseId);
|
|
2765
2823
|
}
|
|
2766
2824
|
async getClassroomDB(_classId, _type) {
|
|
2767
2825
|
throw new Error("Classrooms not supported in static mode");
|