@vue-skuilder/db 0.1.11-9 → 0.1.12

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.
Files changed (76) hide show
  1. package/dist/core/index.d.mts +7 -6
  2. package/dist/core/index.d.ts +7 -6
  3. package/dist/core/index.js +358 -87
  4. package/dist/core/index.js.map +1 -1
  5. package/dist/core/index.mjs +358 -87
  6. package/dist/core/index.mjs.map +1 -1
  7. package/dist/{dataLayerProvider-DqtNroSh.d.ts → dataLayerProvider-BiP3kWix.d.mts} +8 -1
  8. package/dist/{dataLayerProvider-BInqI_RF.d.mts → dataLayerProvider-DSdeyRT3.d.ts} +8 -1
  9. package/dist/impl/couch/index.d.mts +19 -7
  10. package/dist/impl/couch/index.d.ts +19 -7
  11. package/dist/impl/couch/index.js +375 -100
  12. package/dist/impl/couch/index.js.map +1 -1
  13. package/dist/impl/couch/index.mjs +374 -99
  14. package/dist/impl/couch/index.mjs.map +1 -1
  15. package/dist/impl/static/index.d.mts +23 -8
  16. package/dist/impl/static/index.d.ts +23 -8
  17. package/dist/impl/static/index.js +289 -85
  18. package/dist/impl/static/index.js.map +1 -1
  19. package/dist/impl/static/index.mjs +289 -85
  20. package/dist/impl/static/index.mjs.map +1 -1
  21. package/dist/{index-CUNnL38E.d.mts → index-Bmll7Xse.d.mts} +1 -1
  22. package/dist/{index-CLL31bEy.d.ts → index-CD8BZz2k.d.ts} +1 -1
  23. package/dist/index.d.mts +123 -20
  24. package/dist/index.d.ts +123 -20
  25. package/dist/index.js +1133 -343
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +1137 -343
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/pouch/index.d.mts +1 -0
  30. package/dist/pouch/index.d.ts +1 -0
  31. package/dist/pouch/index.js +49 -0
  32. package/dist/pouch/index.js.map +1 -0
  33. package/dist/pouch/index.mjs +16 -0
  34. package/dist/pouch/index.mjs.map +1 -0
  35. package/dist/{types-BefDGkKa.d.ts → types-CewsN87z.d.ts} +1 -1
  36. package/dist/{types-DC-ckZug.d.mts → types-Dbp5DaRR.d.mts} +1 -1
  37. package/dist/{types-legacy-Birv-Jx6.d.mts → types-legacy-6ettoclI.d.mts} +17 -2
  38. package/dist/{types-legacy-Birv-Jx6.d.ts → types-legacy-6ettoclI.d.ts} +17 -2
  39. package/dist/{userDB-DusL7OXe.d.ts → userDB-C4yyAnpp.d.mts} +89 -56
  40. package/dist/{userDB-C33Hzjgn.d.mts → userDB-CD6s6ZCp.d.ts} +89 -56
  41. package/dist/util/packer/index.d.mts +3 -3
  42. package/dist/util/packer/index.d.ts +3 -3
  43. package/package.json +3 -3
  44. package/src/core/interfaces/contentSource.ts +3 -2
  45. package/src/core/interfaces/courseDB.ts +26 -3
  46. package/src/core/interfaces/dataLayerProvider.ts +9 -1
  47. package/src/core/interfaces/userDB.ts +80 -64
  48. package/src/core/navigators/elo.ts +10 -7
  49. package/src/core/navigators/hardcodedOrder.ts +64 -0
  50. package/src/core/navigators/index.ts +2 -1
  51. package/src/core/types/contentNavigationStrategy.ts +2 -1
  52. package/src/core/types/types-legacy.ts +7 -2
  53. package/src/impl/common/BaseUserDB.ts +60 -14
  54. package/src/impl/couch/CouchDBSyncStrategy.ts +2 -2
  55. package/src/impl/couch/PouchDataLayerProvider.ts +21 -0
  56. package/src/impl/couch/adminDB.ts +2 -2
  57. package/src/impl/couch/auth.ts +13 -4
  58. package/src/impl/couch/classroomDB.ts +10 -12
  59. package/src/impl/couch/courseAPI.ts +2 -2
  60. package/src/impl/couch/courseDB.ts +204 -38
  61. package/src/impl/couch/courseLookupDB.ts +4 -3
  62. package/src/impl/couch/index.ts +36 -4
  63. package/src/impl/couch/pouchdb-setup.ts +3 -3
  64. package/src/impl/couch/updateQueue.ts +59 -36
  65. package/src/impl/static/StaticDataLayerProvider.ts +68 -17
  66. package/src/impl/static/courseDB.ts +64 -20
  67. package/src/impl/static/coursesDB.ts +10 -6
  68. package/src/pouch/index.ts +2 -0
  69. package/src/study/ItemQueue.ts +58 -0
  70. package/src/study/SessionController.ts +182 -111
  71. package/src/study/SpacedRepetition.ts +1 -1
  72. package/src/study/services/CardHydrationService.ts +153 -0
  73. package/src/study/services/EloService.ts +85 -0
  74. package/src/study/services/ResponseProcessor.ts +224 -0
  75. package/src/study/services/SrsService.ts +44 -0
  76. package/tsup.config.ts +1 -0
@@ -112,9 +112,9 @@ var init_pouchdb_setup = __esm({
112
112
  import_pouchdb.default.plugin(import_pouchdb_find.default);
113
113
  import_pouchdb.default.plugin(import_pouchdb_authentication.default);
114
114
  import_pouchdb.default.defaults({
115
- ajax: {
116
- timeout: 6e4
117
- }
115
+ // ajax: {
116
+ // timeout: 60000,
117
+ // },
118
118
  });
119
119
  pouchdb_setup_default = import_pouchdb.default;
120
120
  }
@@ -172,42 +172,58 @@ var init_updateQueue = __esm({
172
172
  async applyUpdates(id) {
173
173
  logger.debug(`Applying updates on doc: ${id}`);
174
174
  if (this.inprogressUpdates[id]) {
175
- await this.readDB.info();
175
+ while (this.inprogressUpdates[id]) {
176
+ await new Promise((resolve) => setTimeout(resolve, Math.random() * 50));
177
+ }
176
178
  return this.applyUpdates(id);
177
179
  } else {
178
180
  if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
179
181
  this.inprogressUpdates[id] = true;
180
- try {
181
- let doc = await this.readDB.get(id);
182
- logger.debug(`Retrieved doc: ${id}`);
183
- while (this.pendingUpdates[id].length !== 0) {
184
- const update = this.pendingUpdates[id].splice(0, 1)[0];
185
- if (typeof update === "function") {
186
- doc = { ...doc, ...update(doc) };
182
+ const MAX_RETRIES = 5;
183
+ for (let i = 0; i < MAX_RETRIES; i++) {
184
+ try {
185
+ const doc = await this.readDB.get(id);
186
+ logger.debug(`Retrieved doc: ${id}`);
187
+ let updatedDoc = { ...doc };
188
+ const updatesToApply = [...this.pendingUpdates[id]];
189
+ for (const update of updatesToApply) {
190
+ if (typeof update === "function") {
191
+ updatedDoc = { ...updatedDoc, ...update(updatedDoc) };
192
+ } else {
193
+ updatedDoc = {
194
+ ...updatedDoc,
195
+ ...update
196
+ };
197
+ }
198
+ }
199
+ await this.writeDB.put(updatedDoc);
200
+ logger.debug(`Put doc: ${id}`);
201
+ this.pendingUpdates[id].splice(0, updatesToApply.length);
202
+ if (this.pendingUpdates[id].length === 0) {
203
+ this.inprogressUpdates[id] = false;
204
+ delete this.inprogressUpdates[id];
187
205
  } else {
188
- doc = {
189
- ...doc,
190
- ...update
191
- };
206
+ return this.applyUpdates(id);
207
+ }
208
+ return updatedDoc;
209
+ } catch (e) {
210
+ if (e.name === "conflict" && i < MAX_RETRIES - 1) {
211
+ logger.warn(`Conflict on update for doc ${id}, retry #${i + 1}`);
212
+ await new Promise((res) => setTimeout(res, 50 * Math.random()));
213
+ } else if (e.name === "not_found" && i === 0) {
214
+ logger.warn(`Update failed for ${id} - does not exist. Throwing to caller.`);
215
+ throw e;
216
+ } else {
217
+ delete this.inprogressUpdates[id];
218
+ if (this.pendingUpdates[id]) {
219
+ delete this.pendingUpdates[id];
220
+ }
221
+ logger.error(`Error on attemped update (retry ${i}): ${JSON.stringify(e)}`);
222
+ throw e;
192
223
  }
193
224
  }
194
- await this.writeDB.put(doc);
195
- logger.debug(`Put doc: ${id}`);
196
- if (this.pendingUpdates[id].length === 0) {
197
- this.inprogressUpdates[id] = false;
198
- delete this.inprogressUpdates[id];
199
- } else {
200
- return this.applyUpdates(id);
201
- }
202
- return doc;
203
- } catch (e) {
204
- delete this.inprogressUpdates[id];
205
- if (this.pendingUpdates[id]) {
206
- delete this.pendingUpdates[id];
207
- }
208
- logger.error(`Error on attemped update: ${JSON.stringify(e)}`);
209
- throw e;
210
225
  }
226
+ throw new Error(`UpdateQueue failed for doc ${id} after ${MAX_RETRIES} retries.`);
211
227
  } else {
212
228
  throw new Error(`Empty Updates Queue Triggered`);
213
229
  }
@@ -430,7 +446,7 @@ function getCourseDB(courseID) {
430
446
  const dbName = `coursedb-${courseID}`;
431
447
  return new pouchdb_setup_default(
432
448
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
433
- pouchDBincludeCredentialsConfig
449
+ createPouchDBConfig()
434
450
  );
435
451
  }
436
452
  var import_common, import_common2, import_common3, import_uuid, AlreadyTaggedErr;
@@ -556,6 +572,7 @@ var init_courseLookupDB = __esm({
556
572
  const doc = await _CourseLookup._db.get(courseID);
557
573
  return await _CourseLookup._db.remove(doc);
558
574
  }
575
+ // [ ] rename to allCourses()
559
576
  static async allCourseWare() {
560
577
  const resp = await _CourseLookup._db.allDocs({
561
578
  include_docs: true
@@ -626,13 +643,16 @@ var init_elo = __esm({
626
643
  }
627
644
  async getNewCards(limit = 99) {
628
645
  const activeCards = await this.user.getActiveCards();
629
- return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
630
- if (activeCards.some((ac) => c.includes(ac))) {
631
- return false;
632
- } else {
633
- return true;
646
+ return (await this.course.getCardsCenteredAtELO(
647
+ { limit, elo: "user" },
648
+ (c) => {
649
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
650
+ return false;
651
+ } else {
652
+ return true;
653
+ }
634
654
  }
635
- })).map((c) => {
655
+ )).map((c) => {
636
656
  return {
637
657
  ...c,
638
658
  status: "new"
@@ -643,12 +663,74 @@ var init_elo = __esm({
643
663
  }
644
664
  });
645
665
 
666
+ // src/core/navigators/hardcodedOrder.ts
667
+ var hardcodedOrder_exports = {};
668
+ __export(hardcodedOrder_exports, {
669
+ default: () => HardcodedOrderNavigator
670
+ });
671
+ var HardcodedOrderNavigator;
672
+ var init_hardcodedOrder = __esm({
673
+ "src/core/navigators/hardcodedOrder.ts"() {
674
+ "use strict";
675
+ init_navigators();
676
+ init_logger();
677
+ HardcodedOrderNavigator = class extends ContentNavigator {
678
+ orderedCardIds = [];
679
+ user;
680
+ course;
681
+ constructor(user, course, strategyData) {
682
+ super();
683
+ this.user = user;
684
+ this.course = course;
685
+ if (strategyData.serializedData) {
686
+ try {
687
+ this.orderedCardIds = JSON.parse(strategyData.serializedData);
688
+ } catch (e) {
689
+ logger.error("Failed to parse serializedData for HardcodedOrderNavigator", e);
690
+ }
691
+ }
692
+ }
693
+ async getPendingReviews() {
694
+ const reviews = await this.user.getPendingReviews(this.course.getCourseID());
695
+ return reviews.map((r) => {
696
+ return {
697
+ ...r,
698
+ contentSourceType: "course",
699
+ contentSourceID: this.course.getCourseID(),
700
+ cardID: r.cardId,
701
+ courseID: r.courseId,
702
+ reviewID: r._id,
703
+ status: "review"
704
+ };
705
+ });
706
+ }
707
+ async getNewCards(limit = 99) {
708
+ const activeCardIds = (await this.user.getActiveCards()).map((c) => c.cardID);
709
+ const newCardIds = this.orderedCardIds.filter(
710
+ (cardId) => !activeCardIds.includes(cardId)
711
+ );
712
+ const cardsToReturn = newCardIds.slice(0, limit);
713
+ return cardsToReturn.map((cardId) => {
714
+ return {
715
+ cardID: cardId,
716
+ courseID: this.course.getCourseID(),
717
+ contentSourceType: "course",
718
+ contentSourceID: this.course.getCourseID(),
719
+ status: "new"
720
+ };
721
+ });
722
+ }
723
+ };
724
+ }
725
+ });
726
+
646
727
  // import("./**/*") in src/core/navigators/index.ts
647
728
  var globImport;
648
729
  var init_ = __esm({
649
730
  'import("./**/*") in src/core/navigators/index.ts'() {
650
731
  globImport = __glob({
651
732
  "./elo.ts": () => Promise.resolve().then(() => (init_elo(), elo_exports)),
733
+ "./hardcodedOrder.ts": () => Promise.resolve().then(() => (init_hardcodedOrder(), hardcodedOrder_exports)),
652
734
  "./index.ts": () => Promise.resolve().then(() => (init_navigators(), navigators_exports))
653
735
  });
654
736
  }
@@ -668,6 +750,7 @@ var init_navigators = __esm({
668
750
  init_();
669
751
  Navigators = /* @__PURE__ */ ((Navigators2) => {
670
752
  Navigators2["ELO"] = "elo";
753
+ Navigators2["HARDCODED"] = "hardcodedOrder";
671
754
  return Navigators2;
672
755
  })(Navigators || {});
673
756
  ContentNavigator = class {
@@ -680,7 +763,7 @@ var init_navigators = __esm({
680
763
  static async create(user, course, strategyData) {
681
764
  const implementingClass = strategyData.implementingClass;
682
765
  let NavigatorImpl;
683
- const variations = ["", ".js", ".ts"];
766
+ const variations = [".ts", ".js", ""];
684
767
  for (const ext of variations) {
685
768
  try {
686
769
  const module2 = await globImport(`./${implementingClass}${ext}`);
@@ -979,6 +1062,23 @@ var init_courseDB = __esm({
979
1062
  if (!doc.docType || !(doc.docType === "CARD" /* CARD */)) {
980
1063
  throw new Error(`failed to remove ${id} from course ${this.id}. id does not point to a card`);
981
1064
  }
1065
+ try {
1066
+ const appliedTags = await this.getAppliedTags(id);
1067
+ const results = await Promise.allSettled(
1068
+ appliedTags.rows.map(async (tagRow) => {
1069
+ const tagId = tagRow.id;
1070
+ await this.removeTagFromCard(id, tagId);
1071
+ })
1072
+ );
1073
+ results.forEach((result, index) => {
1074
+ if (result.status === "rejected") {
1075
+ const tagId = appliedTags.rows[index].id;
1076
+ logger.error(`Failed to remove card ${id} from tag ${tagId}: ${result.reason}`);
1077
+ }
1078
+ });
1079
+ } catch (error) {
1080
+ logger.error(`Error removing card ${id} from tags: ${error}`);
1081
+ }
982
1082
  return this.db.remove(doc);
983
1083
  }
984
1084
  async getCardDisplayableDataIDs(id) {
@@ -1026,7 +1126,13 @@ var init_courseDB = __esm({
1026
1126
  } else {
1027
1127
  return s;
1028
1128
  }
1029
- }).map((c) => `${this.id}-${c.id}-${c.key}`);
1129
+ }).map((c) => {
1130
+ return {
1131
+ courseID: this.id,
1132
+ cardID: c.id,
1133
+ elo: c.key
1134
+ };
1135
+ });
1030
1136
  const str = `below:
1031
1137
  ${below.rows.map((r) => ` ${r.id}-${r.key}
1032
1138
  `)}
@@ -1081,7 +1187,13 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1081
1187
  }
1082
1188
  }
1083
1189
  async addTagToCard(cardId, tagId, updateELO) {
1084
- return await addTagToCard(this.id, cardId, tagId, (await this._getCurrentUser()).getUsername(), updateELO);
1190
+ return await addTagToCard(
1191
+ this.id,
1192
+ cardId,
1193
+ tagId,
1194
+ (await this._getCurrentUser()).getUsername(),
1195
+ updateELO
1196
+ );
1085
1197
  }
1086
1198
  async removeTagFromCard(cardId, tagId) {
1087
1199
  return await removeTagFromCard(this.id, cardId, tagId);
@@ -1150,23 +1262,9 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1150
1262
  ////////////////////////////////////
1151
1263
  getNavigationStrategy(id) {
1152
1264
  logger.debug(`[courseDB] Getting navigation strategy: ${id}`);
1153
- const strategy = {
1154
- id: "ELO",
1155
- docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
1156
- name: "ELO",
1157
- description: "ELO-based navigation strategy for ordering content by difficulty",
1158
- implementingClass: "elo" /* ELO */,
1159
- course: this.id,
1160
- serializedData: ""
1161
- // serde is a noop for ELO navigator.
1162
- };
1163
- return Promise.resolve(strategy);
1164
- }
1165
- getAllNavigationStrategies() {
1166
- logger.debug("[courseDB] Returning hard-coded navigation strategies");
1167
- const strategies = [
1168
- {
1169
- id: "ELO",
1265
+ if (id == "") {
1266
+ const strategy = {
1267
+ _id: "NAVIGATION_STRATEGY-ELO",
1170
1268
  docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
1171
1269
  name: "ELO",
1172
1270
  description: "ELO-based navigation strategy for ordering content by difficulty",
@@ -1174,14 +1272,25 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1174
1272
  course: this.id,
1175
1273
  serializedData: ""
1176
1274
  // serde is a noop for ELO navigator.
1177
- }
1178
- ];
1179
- return Promise.resolve(strategies);
1275
+ };
1276
+ return Promise.resolve(strategy);
1277
+ } else {
1278
+ return this.db.get(id);
1279
+ }
1180
1280
  }
1181
- addNavigationStrategy(data) {
1182
- logger.debug(`[courseDB] Adding navigation strategy: ${data.id}`);
1183
- logger.debug(JSON.stringify(data));
1184
- return Promise.resolve();
1281
+ async getAllNavigationStrategies() {
1282
+ const prefix = DocTypePrefixes["NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */];
1283
+ const result = await this.db.allDocs({
1284
+ startkey: prefix,
1285
+ endkey: `${prefix}\uFFF0`,
1286
+ include_docs: true
1287
+ });
1288
+ return result.rows.map((row) => row.doc);
1289
+ }
1290
+ async addNavigationStrategy(data) {
1291
+ logger.debug(`[courseDB] Adding navigation strategy: ${data._id}`);
1292
+ return this.db.put(data).then(() => {
1293
+ });
1185
1294
  }
1186
1295
  updateNavigationStrategy(id, data) {
1187
1296
  logger.debug(`[courseDB] Updating navigation strategy: ${id}`);
@@ -1189,9 +1298,32 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1189
1298
  return Promise.resolve();
1190
1299
  }
1191
1300
  async surfaceNavigationStrategy() {
1301
+ try {
1302
+ const config = await this.getCourseConfig();
1303
+ if (config.defaultNavigationStrategyId) {
1304
+ try {
1305
+ const strategy = await this.getNavigationStrategy(config.defaultNavigationStrategyId);
1306
+ if (strategy) {
1307
+ logger.debug(`Surfacing strategy ${strategy.name} from course config`);
1308
+ return strategy;
1309
+ }
1310
+ } catch (e) {
1311
+ logger.warn(
1312
+ // @ts-expect-error tmp: defaultNavigationStrategyId property does not yet exist
1313
+ `Failed to load strategy '${config.defaultNavigationStrategyId}' specified in course config. Falling back to ELO.`,
1314
+ e
1315
+ );
1316
+ }
1317
+ }
1318
+ } catch (e) {
1319
+ logger.warn(
1320
+ "Could not retrieve course config to determine navigation strategy. Falling back to ELO.",
1321
+ e
1322
+ );
1323
+ }
1192
1324
  logger.warn(`Returning hard-coded default ELO navigator`);
1193
1325
  const ret = {
1194
- id: "ELO",
1326
+ _id: "NAVIGATION_STRATEGY-ELO",
1195
1327
  docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
1196
1328
  name: "ELO",
1197
1329
  description: "ELO-based navigation strategy",
@@ -1274,17 +1406,93 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1274
1406
  selectedCards.push(card);
1275
1407
  }
1276
1408
  return selectedCards.map((c) => {
1277
- const split = c.split("-");
1278
1409
  return {
1279
1410
  courseID: this.id,
1280
- cardID: split[1],
1281
- qualifiedID: `${split[0]}-${split[1]}`,
1411
+ cardID: c.cardID,
1282
1412
  contentSourceType: "course",
1283
1413
  contentSourceID: this.id,
1414
+ elo: c.elo,
1284
1415
  status: "new"
1285
1416
  };
1286
1417
  });
1287
1418
  }
1419
+ // Admin search methods
1420
+ async searchCards(query) {
1421
+ logger.log(`[CourseDB ${this.id}] Searching for: "${query}"`);
1422
+ let displayableData;
1423
+ try {
1424
+ displayableData = await this.db.find({
1425
+ selector: {
1426
+ docType: "DISPLAYABLE_DATA",
1427
+ "data.0.data": { $regex: `.*${query}.*` }
1428
+ }
1429
+ });
1430
+ logger.log(`[CourseDB ${this.id}] Regex search on data[0].data successful`);
1431
+ } catch (regexError) {
1432
+ logger.log(
1433
+ `[CourseDB ${this.id}] Regex search failed, falling back to manual search:`,
1434
+ regexError
1435
+ );
1436
+ const allDisplayable = await this.db.find({
1437
+ selector: {
1438
+ docType: "DISPLAYABLE_DATA"
1439
+ }
1440
+ });
1441
+ logger.log(
1442
+ `[CourseDB ${this.id}] Retrieved ${allDisplayable.docs.length} documents for manual filtering`
1443
+ );
1444
+ displayableData = {
1445
+ docs: allDisplayable.docs.filter((doc) => {
1446
+ const docString = JSON.stringify(doc).toLowerCase();
1447
+ const match = docString.includes(query.toLowerCase());
1448
+ if (match) {
1449
+ logger.log(`[CourseDB ${this.id}] Manual match found in document: ${doc._id}`);
1450
+ }
1451
+ return match;
1452
+ })
1453
+ };
1454
+ }
1455
+ logger.log(
1456
+ `[CourseDB ${this.id}] Found ${displayableData.docs.length} displayable data documents`
1457
+ );
1458
+ if (displayableData.docs.length === 0) {
1459
+ const allDisplayableData = await this.db.find({
1460
+ selector: {
1461
+ docType: "DISPLAYABLE_DATA"
1462
+ },
1463
+ limit: 5
1464
+ // Just sample a few
1465
+ });
1466
+ logger.log(
1467
+ `[CourseDB ${this.id}] Sample displayable data:`,
1468
+ allDisplayableData.docs.map((d) => ({
1469
+ id: d._id,
1470
+ docType: d.docType,
1471
+ dataStructure: d.data ? Object.keys(d.data) : "no data field",
1472
+ dataContent: d.data,
1473
+ fullDoc: d
1474
+ }))
1475
+ );
1476
+ }
1477
+ const allResults = [];
1478
+ for (const dd of displayableData.docs) {
1479
+ const cards = await this.db.find({
1480
+ selector: {
1481
+ docType: "CARD",
1482
+ id_displayable_data: { $in: [dd._id] }
1483
+ }
1484
+ });
1485
+ logger.log(
1486
+ `[CourseDB ${this.id}] Displayable data ${dd._id} linked to ${cards.docs.length} cards`
1487
+ );
1488
+ allResults.push(...cards.docs);
1489
+ }
1490
+ logger.log(`[CourseDB ${this.id}] Total cards found: ${allResults.length}`);
1491
+ return allResults;
1492
+ }
1493
+ async find(request) {
1494
+ return this.db.find(request);
1495
+ }
1288
1496
  };
1289
1497
  }
1290
1498
  });
@@ -1295,7 +1503,7 @@ function getClassroomDB(classID, version) {
1295
1503
  logger.info(`Retrieving classroom db: ${dbName}`);
1296
1504
  return new pouchdb_setup_default(
1297
1505
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
1298
- pouchDBincludeCredentialsConfig
1506
+ createPouchDBConfig()
1299
1507
  );
1300
1508
  }
1301
1509
  async function getClassroomConfig(classID) {
@@ -1361,7 +1569,7 @@ var init_classroomDB2 = __esm({
1361
1569
  const dbName = `classdb-student-${this._id}`;
1362
1570
  this._db = new pouchdb_setup_default(
1363
1571
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
1364
- pouchDBincludeCredentialsConfig
1572
+ createPouchDBConfig()
1365
1573
  );
1366
1574
  try {
1367
1575
  const cfg = await this._db.get(CLASSROOM_CONFIG);
@@ -1430,9 +1638,11 @@ var init_classroomDB2 = __esm({
1430
1638
  ret.push(await getCourseDB2(content.courseID).get(content.cardID));
1431
1639
  }
1432
1640
  }
1433
- logger.info(`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => c.qualifiedID)}`);
1641
+ logger.info(
1642
+ `New Cards from classroom ${this._cfg.name}: ${ret.map((c) => `${c.courseID}-${c.cardID}`)}`
1643
+ );
1434
1644
  return ret.filter((c) => {
1435
- if (activeCards.some((ac) => c.qualifiedID.includes(ac))) {
1645
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
1436
1646
  return false;
1437
1647
  } else {
1438
1648
  return true;
@@ -1451,11 +1661,11 @@ var init_classroomDB2 = __esm({
1451
1661
  const stuDbName = `classdb-student-${this._id}`;
1452
1662
  this._db = new pouchdb_setup_default(
1453
1663
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
1454
- pouchDBincludeCredentialsConfig
1664
+ createPouchDBConfig()
1455
1665
  );
1456
1666
  this._stuDb = new pouchdb_setup_default(
1457
1667
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + stuDbName,
1458
- pouchDBincludeCredentialsConfig
1668
+ createPouchDBConfig()
1459
1669
  );
1460
1670
  try {
1461
1671
  return this._db.get(CLASSROOM_CONFIG).then((cfg) => {
@@ -2042,6 +2252,9 @@ Currently logged-in as ${this._username}.`
2042
2252
  await this.init();
2043
2253
  return ret;
2044
2254
  }
2255
+ async get(id) {
2256
+ return this.localDB.get(id);
2257
+ }
2045
2258
  update(id, update) {
2046
2259
  return this.updateQueue.update(id, update);
2047
2260
  }
@@ -2088,7 +2301,12 @@ Currently logged-in as ${this._username}.`
2088
2301
  endkey: keys.endkey,
2089
2302
  include_docs: true
2090
2303
  });
2091
- return reviews.rows.map((r) => `${r.doc.courseId}-${r.doc.cardId}`);
2304
+ return reviews.rows.map((r) => {
2305
+ return {
2306
+ courseID: r.doc.courseId,
2307
+ cardID: r.doc.cardId
2308
+ };
2309
+ });
2092
2310
  }
2093
2311
  async getActivityRecords() {
2094
2312
  try {
@@ -2368,8 +2586,18 @@ Currently logged-in as ${this._username}.`
2368
2586
  }
2369
2587
  this.setDBandQ();
2370
2588
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
2371
- void this.applyDesignDocs();
2372
- void this.deduplicateReviews();
2589
+ this.applyDesignDocs().catch((error) => {
2590
+ log3(`Error in applyDesignDocs background task: ${error}`);
2591
+ if (error && typeof error === "object") {
2592
+ log3(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
2593
+ }
2594
+ });
2595
+ this.deduplicateReviews().catch((error) => {
2596
+ log3(`Error in deduplicateReviews background task: ${error}`);
2597
+ if (error && typeof error === "object") {
2598
+ log3(`Full error details in background task: ${JSON.stringify(error)}`);
2599
+ }
2600
+ });
2373
2601
  _BaseUser._initialized = true;
2374
2602
  }
2375
2603
  static designDocs = [
@@ -2387,10 +2615,15 @@ Currently logged-in as ${this._username}.`
2387
2615
  }
2388
2616
  ];
2389
2617
  async applyDesignDocs() {
2618
+ log3(`Starting applyDesignDocs for user: ${this._username}`);
2619
+ log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
2390
2620
  if (this._username === "admin") {
2621
+ log3("Skipping design docs for admin user");
2391
2622
  return;
2392
2623
  }
2624
+ log3(`Applying ${_BaseUser.designDocs.length} design docs`);
2393
2625
  for (const doc of _BaseUser.designDocs) {
2626
+ log3(`Applying design doc: ${doc._id}`);
2394
2627
  try {
2395
2628
  try {
2396
2629
  const existingDoc = await this.remoteDB.get(doc._id);
@@ -2467,17 +2700,21 @@ Currently logged-in as ${this._username}.`
2467
2700
  } catch (e) {
2468
2701
  const reason = e;
2469
2702
  if (reason.status === 404) {
2470
- const initCardHistory = {
2471
- _id: cardHistoryID,
2472
- cardID: record.cardID,
2473
- courseID: record.courseID,
2474
- records: [record],
2475
- lapses: 0,
2476
- streak: 0,
2477
- bestInterval: 0
2478
- };
2479
- const putResult = await this.writeDB.put(initCardHistory);
2480
- return { ...initCardHistory, _rev: putResult.rev };
2703
+ try {
2704
+ const initCardHistory = {
2705
+ _id: cardHistoryID,
2706
+ cardID: record.cardID,
2707
+ courseID: record.courseID,
2708
+ records: [record],
2709
+ lapses: 0,
2710
+ streak: 0,
2711
+ bestInterval: 0
2712
+ };
2713
+ const putResult = await this.writeDB.put(initCardHistory);
2714
+ return { ...initCardHistory, _rev: putResult.rev };
2715
+ } catch (creationError) {
2716
+ throw new Error(`Failed to create CardHistory for ${cardHistoryID}. Reason: ${creationError}`);
2717
+ }
2481
2718
  } else {
2482
2719
  throw new Error(`putCardRecord failed because of:
2483
2720
  name:${reason.name}
@@ -2489,8 +2726,13 @@ Currently logged-in as ${this._username}.`
2489
2726
  async deduplicateReviews() {
2490
2727
  try {
2491
2728
  log3("Starting deduplication of scheduled reviews...");
2729
+ log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
2730
+ log3(`Write DB name: ${this.writeDB.name || "unknown"}`);
2492
2731
  const reviewsMap = {};
2493
2732
  const duplicateDocIds = [];
2733
+ log3(
2734
+ `Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || "unknown"}`
2735
+ );
2494
2736
  const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
2495
2737
  log3(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
2496
2738
  scheduledReviews.rows.forEach((r) => {
@@ -2525,6 +2767,17 @@ Currently logged-in as ${this._username}.`
2525
2767
  }
2526
2768
  } catch (error) {
2527
2769
  log3(`Error during review deduplication: ${error}`);
2770
+ if (error && typeof error === "object" && "status" in error && error.status === 404) {
2771
+ log3(
2772
+ `Database not found (404) during review deduplication. Database: ${this.remoteDB.name || "unknown"}`
2773
+ );
2774
+ log3(
2775
+ `This might indicate the user database doesn't exist or the reviewCards view isn't available`
2776
+ );
2777
+ }
2778
+ if (error && typeof error === "object") {
2779
+ log3(`Full error details: ${JSON.stringify(error)}`);
2780
+ }
2528
2781
  }
2529
2782
  }
2530
2783
  /**
@@ -2717,7 +2970,7 @@ var init_adminDB2 = __esm({
2717
2970
  constructor() {
2718
2971
  this.usersDB = new pouchdb_setup_default(
2719
2972
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "_users",
2720
- pouchDBincludeCredentialsConfig
2973
+ createPouchDBConfig()
2721
2974
  );
2722
2975
  }
2723
2976
  async getUsers() {
@@ -2778,9 +3031,10 @@ var init_adminDB2 = __esm({
2778
3031
  async function getCurrentSession() {
2779
3032
  try {
2780
3033
  if (ENV.COUCHDB_SERVER_URL === NOT_SET || ENV.COUCHDB_SERVER_PROTOCOL === NOT_SET) {
2781
- throw new Error("CouchDB server configuration not properly initialized");
3034
+ throw new Error(`CouchDB server configuration not properly initialized. Protocol: "${ENV.COUCHDB_SERVER_PROTOCOL}", URL: "${ENV.COUCHDB_SERVER_URL}"`);
2782
3035
  }
2783
- const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${ENV.COUCHDB_SERVER_URL}_session`;
3036
+ const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith("/") ? ENV.COUCHDB_SERVER_URL.slice(0, -1) : ENV.COUCHDB_SERVER_URL;
3037
+ const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
2784
3038
  logger.debug(`Attempting session check at: ${url}`);
2785
3039
  const response = await (0, import_cross_fetch.default)(url, {
2786
3040
  method: "GET",
@@ -2792,8 +3046,10 @@ async function getCurrentSession() {
2792
3046
  const resp = await response.json();
2793
3047
  return resp;
2794
3048
  } catch (error) {
2795
- logger.error(`Session check error: ${error}`);
2796
- throw new Error(`Session check failed: ${error}`);
3049
+ const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith("/") ? ENV.COUCHDB_SERVER_URL.slice(0, -1) : ENV.COUCHDB_SERVER_URL;
3050
+ const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
3051
+ logger.error(`Session check error attempting to connect to: ${url} - ${error}`);
3052
+ throw new Error(`Session check failed connecting to ${url}: ${error}`);
2797
3053
  }
2798
3054
  }
2799
3055
  async function getLoggedInUsername() {
@@ -2984,7 +3240,7 @@ var init_CouchDBSyncStrategy = __esm({
2984
3240
  log4(`Fetching user database: ${dbName} (${username})`);
2985
3241
  const ret = new pouchdb_setup_default(
2986
3242
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
2987
- pouchDBincludeCredentialsConfig
3243
+ createPouchDBConfig()
2988
3244
  );
2989
3245
  if (guestAccount) {
2990
3246
  updateGuestAccountExpirationDate(ret);
@@ -3009,6 +3265,7 @@ __export(couch_exports, {
3009
3265
  TeacherClassroomDB: () => TeacherClassroomDB,
3010
3266
  addNote55: () => addNote55,
3011
3267
  addTagToCard: () => addTagToCard,
3268
+ createPouchDBConfig: () => createPouchDBConfig,
3012
3269
  createTag: () => createTag,
3013
3270
  deleteTag: () => deleteTag,
3014
3271
  filterAllDocsByPrefix: () => filterAllDocsByPrefix,
@@ -3035,7 +3292,6 @@ __export(couch_exports, {
3035
3292
  hexEncode: () => hexEncode2,
3036
3293
  isReview: () => isReview,
3037
3294
  localUserDB: () => localUserDB,
3038
- pouchDBincludeCredentialsConfig: () => pouchDBincludeCredentialsConfig,
3039
3295
  removeTagFromCard: () => removeTagFromCard,
3040
3296
  scheduleCardReview: () => scheduleCardReview,
3041
3297
  updateCardElo: () => updateCardElo2,
@@ -3054,16 +3310,35 @@ function hexEncode2(str) {
3054
3310
  }
3055
3311
  return returnStr;
3056
3312
  }
3313
+ function createPouchDBConfig() {
3314
+ const hasExplicitCredentials = ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD;
3315
+ const isNodeEnvironment = typeof window === "undefined";
3316
+ if (hasExplicitCredentials && isNodeEnvironment) {
3317
+ return {
3318
+ fetch(url, opts = {}) {
3319
+ const basicAuth = btoa(`${ENV.COUCHDB_USERNAME}:${ENV.COUCHDB_PASSWORD}`);
3320
+ const headers = new Headers(opts.headers || {});
3321
+ headers.set("Authorization", `Basic ${basicAuth}`);
3322
+ const newOpts = {
3323
+ ...opts,
3324
+ headers
3325
+ };
3326
+ return pouchdb_setup_default.fetch(url, newOpts);
3327
+ }
3328
+ };
3329
+ }
3330
+ return pouchDBincludeCredentialsConfig;
3331
+ }
3057
3332
  function getCouchDB(dbName) {
3058
3333
  return new pouchdb_setup_default(
3059
3334
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
3060
- pouchDBincludeCredentialsConfig
3335
+ createPouchDBConfig()
3061
3336
  );
3062
3337
  }
3063
3338
  function getCourseDB2(courseID) {
3064
3339
  return new pouchdb_setup_default(
3065
3340
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
3066
- pouchDBincludeCredentialsConfig
3341
+ createPouchDBConfig()
3067
3342
  );
3068
3343
  }
3069
3344
  async function getLatestVersion() {
@@ -3148,7 +3423,7 @@ function getCouchUserDB(username) {
3148
3423
  log(`Fetching user database: ${dbName} (${username})`);
3149
3424
  const ret = new pouchdb_setup_default(
3150
3425
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
3151
- pouchDBincludeCredentialsConfig
3426
+ createPouchDBConfig()
3152
3427
  );
3153
3428
  if (guestAccount) {
3154
3429
  updateGuestAccountExpirationDate2(ret);
@@ -3231,6 +3506,7 @@ init_couch();
3231
3506
  TeacherClassroomDB,
3232
3507
  addNote55,
3233
3508
  addTagToCard,
3509
+ createPouchDBConfig,
3234
3510
  createTag,
3235
3511
  deleteTag,
3236
3512
  filterAllDocsByPrefix,
@@ -3257,7 +3533,6 @@ init_couch();
3257
3533
  hexEncode,
3258
3534
  isReview,
3259
3535
  localUserDB,
3260
- pouchDBincludeCredentialsConfig,
3261
3536
  removeTagFromCard,
3262
3537
  scheduleCardReview,
3263
3538
  updateCardElo,