@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.
@@ -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
- constructor(manifests: Record<string, StaticCourseManifest>);
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
- constructor(manifests: Record<string, StaticCourseManifest>);
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 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);
@@ -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
- const manifest = this.manifests[courseId];
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
- this.manifests[courseName] = finalManifest;
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(courseName, unpacker);
2733
- logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName}`);
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
- const unpacker = this.courseUnpackers.get(courseId);
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[courseId];
2761
- return new StaticCourseDB(courseId, unpacker, this.getUserDB(), manifest);
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");