@vue-skuilder/db 0.1.11-7 → 0.1.11

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 (68) hide show
  1. package/dist/core/index.d.mts +5 -5
  2. package/dist/core/index.d.ts +5 -5
  3. package/dist/core/index.js +212 -50
  4. package/dist/core/index.js.map +1 -1
  5. package/dist/core/index.mjs +212 -50
  6. package/dist/core/index.mjs.map +1 -1
  7. package/dist/{dataLayerProvider-DqtNroSh.d.ts → dataLayerProvider-VieuAAkV.d.mts} +8 -1
  8. package/dist/{dataLayerProvider-BInqI_RF.d.mts → dataLayerProvider-juuqUHOP.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 +229 -63
  12. package/dist/impl/couch/index.js.map +1 -1
  13. package/dist/impl/couch/index.mjs +228 -62
  14. package/dist/impl/couch/index.mjs.map +1 -1
  15. package/dist/impl/static/index.d.mts +13 -6
  16. package/dist/impl/static/index.d.ts +13 -6
  17. package/dist/impl/static/index.js +142 -46
  18. package/dist/impl/static/index.js.map +1 -1
  19. package/dist/impl/static/index.mjs +142 -46
  20. package/dist/impl/static/index.mjs.map +1 -1
  21. package/dist/{index-CLL31bEy.d.ts → index-CWY6yhkV.d.ts} +1 -1
  22. package/dist/{index-CUNnL38E.d.mts → index-DZyxHCcf.d.mts} +1 -1
  23. package/dist/index.d.mts +28 -20
  24. package/dist/index.d.ts +28 -20
  25. package/dist/index.js +374 -108
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +378 -108
  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-DC-ckZug.d.mts → types-Che4wTwA.d.mts} +1 -1
  36. package/dist/{types-BefDGkKa.d.ts → types-DtoI27Xh.d.ts} +1 -1
  37. package/dist/{types-legacy-Birv-Jx6.d.mts → types-legacy-B8ahaCbj.d.mts} +5 -1
  38. package/dist/{types-legacy-Birv-Jx6.d.ts → types-legacy-B8ahaCbj.d.ts} +5 -1
  39. package/dist/{userDB-C33Hzjgn.d.mts → userDB-B7zTQ123.d.ts} +88 -55
  40. package/dist/{userDB-DusL7OXe.d.ts → userDB-DJ8HMw83.d.mts} +88 -55
  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/index.ts +1 -1
  50. package/src/core/types/types-legacy.ts +5 -0
  51. package/src/impl/common/BaseUserDB.ts +45 -3
  52. package/src/impl/couch/CouchDBSyncStrategy.ts +2 -2
  53. package/src/impl/couch/PouchDataLayerProvider.ts +21 -0
  54. package/src/impl/couch/adminDB.ts +2 -2
  55. package/src/impl/couch/auth.ts +13 -4
  56. package/src/impl/couch/classroomDB.ts +10 -12
  57. package/src/impl/couch/courseAPI.ts +2 -2
  58. package/src/impl/couch/courseDB.ts +130 -11
  59. package/src/impl/couch/courseLookupDB.ts +4 -3
  60. package/src/impl/couch/index.ts +36 -4
  61. package/src/impl/couch/pouchdb-setup.ts +3 -3
  62. package/src/impl/couch/updateQueue.ts +51 -33
  63. package/src/impl/static/StaticDataLayerProvider.ts +11 -0
  64. package/src/impl/static/courseDB.ts +47 -8
  65. package/src/pouch/index.ts +2 -0
  66. package/src/study/SessionController.ts +168 -51
  67. package/src/study/SpacedRepetition.ts +1 -1
  68. package/tsup.config.ts +1 -0
@@ -165,9 +165,9 @@ var init_pouchdb_setup = __esm({
165
165
  PouchDB.plugin(PouchDBFind);
166
166
  PouchDB.plugin(PouchDBAuth);
167
167
  PouchDB.defaults({
168
- ajax: {
169
- timeout: 6e4
170
- }
168
+ // ajax: {
169
+ // timeout: 60000,
170
+ // },
171
171
  });
172
172
  pouchdb_setup_default = PouchDB;
173
173
  }
@@ -325,37 +325,48 @@ var init_updateQueue = __esm({
325
325
  } else {
326
326
  if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
327
327
  this.inprogressUpdates[id] = true;
328
- try {
329
- let doc = await this.readDB.get(id);
330
- logger.debug(`Retrieved doc: ${id}`);
331
- while (this.pendingUpdates[id].length !== 0) {
332
- const update = this.pendingUpdates[id].splice(0, 1)[0];
333
- if (typeof update === "function") {
334
- doc = { ...doc, ...update(doc) };
328
+ const MAX_RETRIES = 5;
329
+ for (let i = 0; i < MAX_RETRIES; i++) {
330
+ try {
331
+ const doc = await this.readDB.get(id);
332
+ logger.debug(`Retrieved doc: ${id}`);
333
+ let updatedDoc = { ...doc };
334
+ const updatesToApply = [...this.pendingUpdates[id]];
335
+ for (const update of updatesToApply) {
336
+ if (typeof update === "function") {
337
+ updatedDoc = { ...updatedDoc, ...update(updatedDoc) };
338
+ } else {
339
+ updatedDoc = {
340
+ ...updatedDoc,
341
+ ...update
342
+ };
343
+ }
344
+ }
345
+ await this.writeDB.put(updatedDoc);
346
+ logger.debug(`Put doc: ${id}`);
347
+ this.pendingUpdates[id].splice(0, updatesToApply.length);
348
+ if (this.pendingUpdates[id].length === 0) {
349
+ this.inprogressUpdates[id] = false;
350
+ delete this.inprogressUpdates[id];
335
351
  } else {
336
- doc = {
337
- ...doc,
338
- ...update
339
- };
352
+ return this.applyUpdates(id);
353
+ }
354
+ return updatedDoc;
355
+ } catch (e) {
356
+ if (e.name === "conflict" && i < MAX_RETRIES - 1) {
357
+ logger.warn(`Conflict on update for doc ${id}, retry #${i + 1}`);
358
+ await new Promise((res) => setTimeout(res, 50 * Math.random()));
359
+ } else {
360
+ delete this.inprogressUpdates[id];
361
+ if (this.pendingUpdates[id]) {
362
+ delete this.pendingUpdates[id];
363
+ }
364
+ logger.error(`Error on attemped update (retry ${i}): ${JSON.stringify(e)}`);
365
+ throw e;
340
366
  }
341
367
  }
342
- await this.writeDB.put(doc);
343
- logger.debug(`Put doc: ${id}`);
344
- if (this.pendingUpdates[id].length === 0) {
345
- this.inprogressUpdates[id] = false;
346
- delete this.inprogressUpdates[id];
347
- } else {
348
- return this.applyUpdates(id);
349
- }
350
- return doc;
351
- } catch (e) {
352
- delete this.inprogressUpdates[id];
353
- if (this.pendingUpdates[id]) {
354
- delete this.pendingUpdates[id];
355
- }
356
- logger.error(`Error on attemped update: ${JSON.stringify(e)}`);
357
- throw e;
358
368
  }
369
+ throw new Error(`UpdateQueue failed for doc ${id} after ${MAX_RETRIES} retries.`);
359
370
  } else {
360
371
  throw new Error(`Empty Updates Queue Triggered`);
361
372
  }
@@ -610,7 +621,7 @@ function getCourseDB(courseID) {
610
621
  const dbName = `coursedb-${courseID}`;
611
622
  return new pouchdb_setup_default(
612
623
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
613
- pouchDBincludeCredentialsConfig
624
+ createPouchDBConfig()
614
625
  );
615
626
  }
616
627
  var AlreadyTaggedErr;
@@ -690,13 +701,16 @@ var init_elo = __esm({
690
701
  }
691
702
  async getNewCards(limit = 99) {
692
703
  const activeCards = await this.user.getActiveCards();
693
- return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
694
- if (activeCards.some((ac) => c.includes(ac))) {
695
- return false;
696
- } else {
697
- return true;
704
+ return (await this.course.getCardsCenteredAtELO(
705
+ { limit, elo: "user" },
706
+ (c) => {
707
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
708
+ return false;
709
+ } else {
710
+ return true;
711
+ }
698
712
  }
699
- })).map((c) => {
713
+ )).map((c) => {
700
714
  return {
701
715
  ...c,
702
716
  status: "new"
@@ -744,7 +758,7 @@ var init_navigators = __esm({
744
758
  static async create(user, course, strategyData) {
745
759
  const implementingClass = strategyData.implementingClass;
746
760
  let NavigatorImpl;
747
- const variations = ["", ".js", ".ts"];
761
+ const variations = [".ts", ".js", ""];
748
762
  for (const ext of variations) {
749
763
  try {
750
764
  const module = await globImport(`./${implementingClass}${ext}`);
@@ -1008,7 +1022,13 @@ var init_courseDB = __esm({
1008
1022
  } else {
1009
1023
  return s;
1010
1024
  }
1011
- }).map((c) => `${this.id}-${c.id}-${c.key}`);
1025
+ }).map((c) => {
1026
+ return {
1027
+ courseID: this.id,
1028
+ cardID: c.id,
1029
+ elo: c.key
1030
+ };
1031
+ });
1012
1032
  const str = `below:
1013
1033
  ${below.rows.map((r) => ` ${r.id}-${r.key}
1014
1034
  `)}
@@ -1063,7 +1083,13 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1063
1083
  }
1064
1084
  }
1065
1085
  async addTagToCard(cardId, tagId, updateELO) {
1066
- return await addTagToCard(this.id, cardId, tagId, (await this._getCurrentUser()).getUsername(), updateELO);
1086
+ return await addTagToCard(
1087
+ this.id,
1088
+ cardId,
1089
+ tagId,
1090
+ (await this._getCurrentUser()).getUsername(),
1091
+ updateELO
1092
+ );
1067
1093
  }
1068
1094
  async removeTagFromCard(cardId, tagId) {
1069
1095
  return await removeTagFromCard(this.id, cardId, tagId);
@@ -1256,17 +1282,93 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1256
1282
  selectedCards.push(card);
1257
1283
  }
1258
1284
  return selectedCards.map((c) => {
1259
- const split = c.split("-");
1260
1285
  return {
1261
1286
  courseID: this.id,
1262
- cardID: split[1],
1263
- qualifiedID: `${split[0]}-${split[1]}`,
1287
+ cardID: c.cardID,
1264
1288
  contentSourceType: "course",
1265
1289
  contentSourceID: this.id,
1290
+ elo: c.elo,
1266
1291
  status: "new"
1267
1292
  };
1268
1293
  });
1269
1294
  }
1295
+ // Admin search methods
1296
+ async searchCards(query) {
1297
+ logger.log(`[CourseDB ${this.id}] Searching for: "${query}"`);
1298
+ let displayableData;
1299
+ try {
1300
+ displayableData = await this.db.find({
1301
+ selector: {
1302
+ docType: "DISPLAYABLE_DATA",
1303
+ "data.0.data": { $regex: `.*${query}.*` }
1304
+ }
1305
+ });
1306
+ logger.log(`[CourseDB ${this.id}] Regex search on data[0].data successful`);
1307
+ } catch (regexError) {
1308
+ logger.log(
1309
+ `[CourseDB ${this.id}] Regex search failed, falling back to manual search:`,
1310
+ regexError
1311
+ );
1312
+ const allDisplayable = await this.db.find({
1313
+ selector: {
1314
+ docType: "DISPLAYABLE_DATA"
1315
+ }
1316
+ });
1317
+ logger.log(
1318
+ `[CourseDB ${this.id}] Retrieved ${allDisplayable.docs.length} documents for manual filtering`
1319
+ );
1320
+ displayableData = {
1321
+ docs: allDisplayable.docs.filter((doc) => {
1322
+ const docString = JSON.stringify(doc).toLowerCase();
1323
+ const match = docString.includes(query.toLowerCase());
1324
+ if (match) {
1325
+ logger.log(`[CourseDB ${this.id}] Manual match found in document: ${doc._id}`);
1326
+ }
1327
+ return match;
1328
+ })
1329
+ };
1330
+ }
1331
+ logger.log(
1332
+ `[CourseDB ${this.id}] Found ${displayableData.docs.length} displayable data documents`
1333
+ );
1334
+ if (displayableData.docs.length === 0) {
1335
+ const allDisplayableData = await this.db.find({
1336
+ selector: {
1337
+ docType: "DISPLAYABLE_DATA"
1338
+ },
1339
+ limit: 5
1340
+ // Just sample a few
1341
+ });
1342
+ logger.log(
1343
+ `[CourseDB ${this.id}] Sample displayable data:`,
1344
+ allDisplayableData.docs.map((d) => ({
1345
+ id: d._id,
1346
+ docType: d.docType,
1347
+ dataStructure: d.data ? Object.keys(d.data) : "no data field",
1348
+ dataContent: d.data,
1349
+ fullDoc: d
1350
+ }))
1351
+ );
1352
+ }
1353
+ const allResults = [];
1354
+ for (const dd of displayableData.docs) {
1355
+ const cards = await this.db.find({
1356
+ selector: {
1357
+ docType: "CARD",
1358
+ id_displayable_data: { $in: [dd._id] }
1359
+ }
1360
+ });
1361
+ logger.log(
1362
+ `[CourseDB ${this.id}] Displayable data ${dd._id} linked to ${cards.docs.length} cards`
1363
+ );
1364
+ allResults.push(...cards.docs);
1365
+ }
1366
+ logger.log(`[CourseDB ${this.id}] Total cards found: ${allResults.length}`);
1367
+ return allResults;
1368
+ }
1369
+ async find(request) {
1370
+ return this.db.find(request);
1371
+ }
1270
1372
  };
1271
1373
  }
1272
1374
  });
@@ -1331,7 +1433,7 @@ var init_classroomDB2 = __esm({
1331
1433
  const dbName = `classdb-student-${this._id}`;
1332
1434
  this._db = new pouchdb_setup_default(
1333
1435
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
1334
- pouchDBincludeCredentialsConfig
1436
+ createPouchDBConfig()
1335
1437
  );
1336
1438
  try {
1337
1439
  const cfg = await this._db.get(CLASSROOM_CONFIG);
@@ -1400,9 +1502,11 @@ var init_classroomDB2 = __esm({
1400
1502
  ret.push(await getCourseDB2(content.courseID).get(content.cardID));
1401
1503
  }
1402
1504
  }
1403
- logger.info(`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => c.qualifiedID)}`);
1505
+ logger.info(
1506
+ `New Cards from classroom ${this._cfg.name}: ${ret.map((c) => `${c.courseID}-${c.cardID}`)}`
1507
+ );
1404
1508
  return ret.filter((c) => {
1405
- if (activeCards.some((ac) => c.qualifiedID.includes(ac))) {
1509
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
1406
1510
  return false;
1407
1511
  } else {
1408
1512
  return true;
@@ -1456,10 +1560,29 @@ var init_CouchDBSyncStrategy = __esm({
1456
1560
  import fetch2 from "cross-fetch";
1457
1561
  import moment4 from "moment";
1458
1562
  import process2 from "process";
1563
+ function createPouchDBConfig() {
1564
+ const hasExplicitCredentials = ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD;
1565
+ const isNodeEnvironment = typeof window === "undefined";
1566
+ if (hasExplicitCredentials && isNodeEnvironment) {
1567
+ return {
1568
+ fetch(url, opts = {}) {
1569
+ const basicAuth = btoa(`${ENV.COUCHDB_USERNAME}:${ENV.COUCHDB_PASSWORD}`);
1570
+ const headers = new Headers(opts.headers || {});
1571
+ headers.set("Authorization", `Basic ${basicAuth}`);
1572
+ const newOpts = {
1573
+ ...opts,
1574
+ headers
1575
+ };
1576
+ return pouchdb_setup_default.fetch(url, newOpts);
1577
+ }
1578
+ };
1579
+ }
1580
+ return pouchDBincludeCredentialsConfig;
1581
+ }
1459
1582
  function getCourseDB2(courseID) {
1460
1583
  return new pouchdb_setup_default(
1461
1584
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
1462
- pouchDBincludeCredentialsConfig
1585
+ createPouchDBConfig()
1463
1586
  );
1464
1587
  }
1465
1588
  function getCourseDocs(courseID, docIDs, options = {}) {
@@ -1748,6 +1871,9 @@ Currently logged-in as ${this._username}.`
1748
1871
  await this.init();
1749
1872
  return ret;
1750
1873
  }
1874
+ async get(id) {
1875
+ return this.localDB.get(id);
1876
+ }
1751
1877
  update(id, update) {
1752
1878
  return this.updateQueue.update(id, update);
1753
1879
  }
@@ -1794,7 +1920,12 @@ Currently logged-in as ${this._username}.`
1794
1920
  endkey: keys.endkey,
1795
1921
  include_docs: true
1796
1922
  });
1797
- return reviews.rows.map((r) => `${r.doc.courseId}-${r.doc.cardId}`);
1923
+ return reviews.rows.map((r) => {
1924
+ return {
1925
+ courseID: r.doc.courseId,
1926
+ cardID: r.doc.cardId
1927
+ };
1928
+ });
1798
1929
  }
1799
1930
  async getActivityRecords() {
1800
1931
  try {
@@ -2074,8 +2205,18 @@ Currently logged-in as ${this._username}.`
2074
2205
  }
2075
2206
  this.setDBandQ();
2076
2207
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
2077
- void this.applyDesignDocs();
2078
- void this.deduplicateReviews();
2208
+ this.applyDesignDocs().catch((error) => {
2209
+ log3(`Error in applyDesignDocs background task: ${error}`);
2210
+ if (error && typeof error === "object") {
2211
+ log3(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
2212
+ }
2213
+ });
2214
+ this.deduplicateReviews().catch((error) => {
2215
+ log3(`Error in deduplicateReviews background task: ${error}`);
2216
+ if (error && typeof error === "object") {
2217
+ log3(`Full error details in background task: ${JSON.stringify(error)}`);
2218
+ }
2219
+ });
2079
2220
  _BaseUser._initialized = true;
2080
2221
  }
2081
2222
  static designDocs = [
@@ -2093,10 +2234,15 @@ Currently logged-in as ${this._username}.`
2093
2234
  }
2094
2235
  ];
2095
2236
  async applyDesignDocs() {
2237
+ log3(`Starting applyDesignDocs for user: ${this._username}`);
2238
+ log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
2096
2239
  if (this._username === "admin") {
2240
+ log3("Skipping design docs for admin user");
2097
2241
  return;
2098
2242
  }
2243
+ log3(`Applying ${_BaseUser.designDocs.length} design docs`);
2099
2244
  for (const doc of _BaseUser.designDocs) {
2245
+ log3(`Applying design doc: ${doc._id}`);
2100
2246
  try {
2101
2247
  try {
2102
2248
  const existingDoc = await this.remoteDB.get(doc._id);
@@ -2195,8 +2341,13 @@ Currently logged-in as ${this._username}.`
2195
2341
  async deduplicateReviews() {
2196
2342
  try {
2197
2343
  log3("Starting deduplication of scheduled reviews...");
2344
+ log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
2345
+ log3(`Write DB name: ${this.writeDB.name || "unknown"}`);
2198
2346
  const reviewsMap = {};
2199
2347
  const duplicateDocIds = [];
2348
+ log3(
2349
+ `Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || "unknown"}`
2350
+ );
2200
2351
  const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
2201
2352
  log3(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
2202
2353
  scheduledReviews.rows.forEach((r) => {
@@ -2231,6 +2382,17 @@ Currently logged-in as ${this._username}.`
2231
2382
  }
2232
2383
  } catch (error) {
2233
2384
  log3(`Error during review deduplication: ${error}`);
2385
+ if (error && typeof error === "object" && "status" in error && error.status === 404) {
2386
+ log3(
2387
+ `Database not found (404) during review deduplication. Database: ${this.remoteDB.name || "unknown"}`
2388
+ );
2389
+ log3(
2390
+ `This might indicate the user database doesn't exist or the reviewCards view isn't available`
2391
+ );
2392
+ }
2393
+ if (error && typeof error === "object") {
2394
+ log3(`Full error details: ${JSON.stringify(error)}`);
2395
+ }
2234
2396
  }
2235
2397
  }
2236
2398
  /**