@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
|
@@ -258,6 +258,33 @@ var init_updateQueue = __esm({
|
|
|
258
258
|
// Database for read operations
|
|
259
259
|
writeDB;
|
|
260
260
|
// Database for write operations (local-first)
|
|
261
|
+
/**
|
|
262
|
+
* Queues an update for a document and applies it with conflict resolution.
|
|
263
|
+
*
|
|
264
|
+
* @param id - Document ID to update
|
|
265
|
+
* @param update - Partial object or function that transforms the document
|
|
266
|
+
* @returns Promise resolving to the updated document
|
|
267
|
+
*
|
|
268
|
+
* @throws {PouchError} with status 404 if document doesn't exist
|
|
269
|
+
*
|
|
270
|
+
* @remarks
|
|
271
|
+
* **Error Handling Pattern:**
|
|
272
|
+
* - This method does NOT create documents if they don't exist
|
|
273
|
+
* - Callers are responsible for handling 404 errors and creating documents
|
|
274
|
+
* - This design maintains separation of concerns (UpdateQueue handles conflicts, callers handle lifecycle)
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* try {
|
|
279
|
+
* await updateQueue.update(docId, (doc) => ({ ...doc, field: newValue }));
|
|
280
|
+
* } catch (e) {
|
|
281
|
+
* if ((e as PouchError).status === 404) {
|
|
282
|
+
* // Create the document with initial values
|
|
283
|
+
* await db.put({ _id: docId, field: newValue, ...initialFields });
|
|
284
|
+
* }
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
261
288
|
update(id, update) {
|
|
262
289
|
logger.debug(`Update requested on doc: ${id}`);
|
|
263
290
|
if (this.pendingUpdates[id]) {
|
|
@@ -290,7 +317,6 @@ var init_updateQueue = __esm({
|
|
|
290
317
|
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
291
318
|
try {
|
|
292
319
|
const doc = await this.readDB.get(id);
|
|
293
|
-
logger.debug(`Retrieved doc: ${id}`);
|
|
294
320
|
let updatedDoc = { ...doc };
|
|
295
321
|
const updatesToApply = [...this.pendingUpdates[id]];
|
|
296
322
|
for (const update of updatesToApply) {
|
|
@@ -304,7 +330,6 @@ var init_updateQueue = __esm({
|
|
|
304
330
|
}
|
|
305
331
|
}
|
|
306
332
|
await this.writeDB.put(updatedDoc);
|
|
307
|
-
logger.debug(`Put doc: ${id}`);
|
|
308
333
|
this.pendingUpdates[id].splice(0, updatesToApply.length);
|
|
309
334
|
if (this.pendingUpdates[id].length === 0) {
|
|
310
335
|
this.inprogressUpdates[id] = false;
|
|
@@ -319,6 +344,7 @@ var init_updateQueue = __esm({
|
|
|
319
344
|
await new Promise((res) => setTimeout(res, 50 * Math.random()));
|
|
320
345
|
} else if (e.name === "not_found" && i === 0) {
|
|
321
346
|
logger.warn(`Update failed for ${id} - does not exist. Throwing to caller.`);
|
|
347
|
+
delete this.inprogressUpdates[id];
|
|
322
348
|
throw e;
|
|
323
349
|
} else {
|
|
324
350
|
delete this.inprogressUpdates[id];
|
|
@@ -1483,12 +1509,22 @@ Currently logged-in as ${this._username}.`
|
|
|
1483
1509
|
}
|
|
1484
1510
|
/**
|
|
1485
1511
|
* Logs a record of the user's interaction with the card and returns the card's
|
|
1486
|
-
* up-to-date history
|
|
1512
|
+
* up-to-date history.
|
|
1513
|
+
*
|
|
1514
|
+
* **Automatic Initialization:**
|
|
1515
|
+
* If this is the user's first interaction with the card (CardHistory doesn't exist),
|
|
1516
|
+
* this method automatically creates the CardHistory document with initial values
|
|
1517
|
+
* (lapses: 0, streak: 0, bestInterval: 0).
|
|
1518
|
+
*
|
|
1519
|
+
* **Error Handling:**
|
|
1520
|
+
* - Handles 404 errors by creating initial CardHistory document
|
|
1521
|
+
* - Re-throws all other errors from UpdateQueue
|
|
1487
1522
|
*
|
|
1488
1523
|
* // [ ] #db-refactor extract to a smaller scope - eg, UserStudySession
|
|
1489
1524
|
*
|
|
1490
|
-
* @param record
|
|
1525
|
+
* @param record - The recent recorded interaction between user and card
|
|
1491
1526
|
* @returns The updated state of the card's CardHistory data
|
|
1527
|
+
* @throws Error if document creation fails or non-404 database error occurs
|
|
1492
1528
|
*/
|
|
1493
1529
|
async putCardRecord(record) {
|
|
1494
1530
|
const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
|
|
@@ -2574,11 +2610,18 @@ var init_coursesDB = __esm({
|
|
|
2574
2610
|
"use strict";
|
|
2575
2611
|
init_logger();
|
|
2576
2612
|
StaticCoursesDB = class {
|
|
2577
|
-
constructor(manifests) {
|
|
2613
|
+
constructor(manifests, dependencyNameToCourseId) {
|
|
2578
2614
|
this.manifests = manifests;
|
|
2615
|
+
this.dependencyNameToCourseId = dependencyNameToCourseId;
|
|
2579
2616
|
}
|
|
2580
2617
|
async getCourseConfig(courseId) {
|
|
2581
|
-
|
|
2618
|
+
let manifest = this.manifests[courseId];
|
|
2619
|
+
if (!manifest && this.dependencyNameToCourseId) {
|
|
2620
|
+
const mappedCourseId = this.dependencyNameToCourseId.get(courseId);
|
|
2621
|
+
if (mappedCourseId) {
|
|
2622
|
+
manifest = this.manifests[mappedCourseId];
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2582
2625
|
if (!manifest) {
|
|
2583
2626
|
logger.warn(`Course manifest for ${courseId} not found`);
|
|
2584
2627
|
throw new Error(`Course ${courseId} not found`);
|
|
@@ -2676,6 +2719,8 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2676
2719
|
initialized = false;
|
|
2677
2720
|
courseUnpackers = /* @__PURE__ */ new Map();
|
|
2678
2721
|
manifests = {};
|
|
2722
|
+
// Mapping from dependency name to actual courseId for backwards compatibility
|
|
2723
|
+
dependencyNameToCourseId = /* @__PURE__ */ new Map();
|
|
2679
2724
|
constructor(config) {
|
|
2680
2725
|
this.config = {
|
|
2681
2726
|
localStoragePrefix: config.localStoragePrefix || "skuilder-static",
|
|
@@ -2703,10 +2748,15 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2703
2748
|
throw new Error(`Failed to fetch final content manifest for ${courseName} at ${finalManifestUrl}`);
|
|
2704
2749
|
}
|
|
2705
2750
|
const finalManifest = await finalManifestResponse.json();
|
|
2706
|
-
|
|
2751
|
+
const courseId = finalManifest.courseId || finalManifest.courseConfig?.courseID;
|
|
2752
|
+
if (!courseId) {
|
|
2753
|
+
throw new Error(`Course manifest for ${courseName} missing courseId`);
|
|
2754
|
+
}
|
|
2755
|
+
this.manifests[courseId] = finalManifest;
|
|
2707
2756
|
const unpacker = new StaticDataUnpacker(finalManifest, baseUrl);
|
|
2708
|
-
this.courseUnpackers.set(
|
|
2709
|
-
|
|
2757
|
+
this.courseUnpackers.set(courseId, unpacker);
|
|
2758
|
+
this.dependencyNameToCourseId.set(courseName, courseId);
|
|
2759
|
+
logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName} (courseId: ${courseId})`);
|
|
2710
2760
|
}
|
|
2711
2761
|
} catch (e) {
|
|
2712
2762
|
logger.error(`[StaticDataLayerProvider] Failed to resolve dependency ${courseName}:`, e);
|
|
@@ -2729,15 +2779,23 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2729
2779
|
return BaseUser.Dummy(syncStrategy);
|
|
2730
2780
|
}
|
|
2731
2781
|
getCourseDB(courseId) {
|
|
2732
|
-
|
|
2782
|
+
let unpacker = this.courseUnpackers.get(courseId);
|
|
2783
|
+
let actualCourseId = courseId;
|
|
2784
|
+
if (!unpacker) {
|
|
2785
|
+
const mappedCourseId = this.dependencyNameToCourseId.get(courseId);
|
|
2786
|
+
if (mappedCourseId) {
|
|
2787
|
+
unpacker = this.courseUnpackers.get(mappedCourseId);
|
|
2788
|
+
actualCourseId = mappedCourseId;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2733
2791
|
if (!unpacker) {
|
|
2734
2792
|
throw new Error(`Course ${courseId} not found or failed to initialize in static data layer.`);
|
|
2735
2793
|
}
|
|
2736
|
-
const manifest = this.manifests[
|
|
2737
|
-
return new StaticCourseDB(
|
|
2794
|
+
const manifest = this.manifests[actualCourseId];
|
|
2795
|
+
return new StaticCourseDB(actualCourseId, unpacker, this.getUserDB(), manifest);
|
|
2738
2796
|
}
|
|
2739
2797
|
getCoursesDB() {
|
|
2740
|
-
return new StaticCoursesDB(this.manifests);
|
|
2798
|
+
return new StaticCoursesDB(this.manifests, this.dependencyNameToCourseId);
|
|
2741
2799
|
}
|
|
2742
2800
|
async getClassroomDB(_classId, _type) {
|
|
2743
2801
|
throw new Error("Classrooms not supported in static mode");
|