@vue-skuilder/db 0.1.14-2 → 0.1.14

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.
@@ -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>;
@@ -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>;
@@ -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 the recent recorded interaction between user and card
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);
@@ -2700,6 +2736,8 @@ var init_StaticDataLayerProvider = __esm({
2700
2736
  initialized = false;
2701
2737
  courseUnpackers = /* @__PURE__ */ new Map();
2702
2738
  manifests = {};
2739
+ // Mapping from dependency name to actual courseId for backwards compatibility
2740
+ dependencyNameToCourseId = /* @__PURE__ */ new Map();
2703
2741
  constructor(config) {
2704
2742
  this.config = {
2705
2743
  localStoragePrefix: config.localStoragePrefix || "skuilder-static",
@@ -2727,10 +2765,15 @@ var init_StaticDataLayerProvider = __esm({
2727
2765
  throw new Error(`Failed to fetch final content manifest for ${courseName} at ${finalManifestUrl}`);
2728
2766
  }
2729
2767
  const finalManifest = await finalManifestResponse.json();
2730
- this.manifests[courseName] = finalManifest;
2768
+ const courseId = finalManifest.courseId || finalManifest.courseConfig?.courseID;
2769
+ if (!courseId) {
2770
+ throw new Error(`Course manifest for ${courseName} missing courseId`);
2771
+ }
2772
+ this.manifests[courseId] = finalManifest;
2731
2773
  const unpacker = new StaticDataUnpacker(finalManifest, baseUrl);
2732
- this.courseUnpackers.set(courseName, unpacker);
2733
- logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName}`);
2774
+ this.courseUnpackers.set(courseId, unpacker);
2775
+ this.dependencyNameToCourseId.set(courseName, courseId);
2776
+ logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName} (courseId: ${courseId})`);
2734
2777
  }
2735
2778
  } catch (e) {
2736
2779
  logger.error(`[StaticDataLayerProvider] Failed to resolve dependency ${courseName}:`, e);
@@ -2753,12 +2796,20 @@ var init_StaticDataLayerProvider = __esm({
2753
2796
  return BaseUser.Dummy(syncStrategy);
2754
2797
  }
2755
2798
  getCourseDB(courseId) {
2756
- const unpacker = this.courseUnpackers.get(courseId);
2799
+ let unpacker = this.courseUnpackers.get(courseId);
2800
+ let actualCourseId = courseId;
2801
+ if (!unpacker) {
2802
+ const mappedCourseId = this.dependencyNameToCourseId.get(courseId);
2803
+ if (mappedCourseId) {
2804
+ unpacker = this.courseUnpackers.get(mappedCourseId);
2805
+ actualCourseId = mappedCourseId;
2806
+ }
2807
+ }
2757
2808
  if (!unpacker) {
2758
2809
  throw new Error(`Course ${courseId} not found or failed to initialize in static data layer.`);
2759
2810
  }
2760
- const manifest = this.manifests[courseId];
2761
- return new StaticCourseDB(courseId, unpacker, this.getUserDB(), manifest);
2811
+ const manifest = this.manifests[actualCourseId];
2812
+ return new StaticCourseDB(actualCourseId, unpacker, this.getUserDB(), manifest);
2762
2813
  }
2763
2814
  getCoursesDB() {
2764
2815
  return new StaticCoursesDB(this.manifests);