@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
@@ -124,9 +124,9 @@ var init_pouchdb_setup = __esm({
124
124
  PouchDB.plugin(PouchDBFind);
125
125
  PouchDB.plugin(PouchDBAuth);
126
126
  PouchDB.defaults({
127
- ajax: {
128
- timeout: 6e4
129
- }
127
+ // ajax: {
128
+ // timeout: 60000,
129
+ // },
130
130
  });
131
131
  pouchdb_setup_default = PouchDB;
132
132
  }
@@ -279,42 +279,58 @@ var init_updateQueue = __esm({
279
279
  async applyUpdates(id) {
280
280
  logger.debug(`Applying updates on doc: ${id}`);
281
281
  if (this.inprogressUpdates[id]) {
282
- await this.readDB.info();
282
+ while (this.inprogressUpdates[id]) {
283
+ await new Promise((resolve) => setTimeout(resolve, Math.random() * 50));
284
+ }
283
285
  return this.applyUpdates(id);
284
286
  } else {
285
287
  if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
286
288
  this.inprogressUpdates[id] = true;
287
- try {
288
- let doc = await this.readDB.get(id);
289
- logger.debug(`Retrieved doc: ${id}`);
290
- while (this.pendingUpdates[id].length !== 0) {
291
- const update = this.pendingUpdates[id].splice(0, 1)[0];
292
- if (typeof update === "function") {
293
- doc = { ...doc, ...update(doc) };
289
+ const MAX_RETRIES = 5;
290
+ for (let i = 0; i < MAX_RETRIES; i++) {
291
+ try {
292
+ const doc = await this.readDB.get(id);
293
+ logger.debug(`Retrieved doc: ${id}`);
294
+ let updatedDoc = { ...doc };
295
+ const updatesToApply = [...this.pendingUpdates[id]];
296
+ for (const update of updatesToApply) {
297
+ if (typeof update === "function") {
298
+ updatedDoc = { ...updatedDoc, ...update(updatedDoc) };
299
+ } else {
300
+ updatedDoc = {
301
+ ...updatedDoc,
302
+ ...update
303
+ };
304
+ }
305
+ }
306
+ await this.writeDB.put(updatedDoc);
307
+ logger.debug(`Put doc: ${id}`);
308
+ this.pendingUpdates[id].splice(0, updatesToApply.length);
309
+ if (this.pendingUpdates[id].length === 0) {
310
+ this.inprogressUpdates[id] = false;
311
+ delete this.inprogressUpdates[id];
294
312
  } else {
295
- doc = {
296
- ...doc,
297
- ...update
298
- };
313
+ return this.applyUpdates(id);
314
+ }
315
+ return updatedDoc;
316
+ } catch (e) {
317
+ if (e.name === "conflict" && i < MAX_RETRIES - 1) {
318
+ logger.warn(`Conflict on update for doc ${id}, retry #${i + 1}`);
319
+ await new Promise((res) => setTimeout(res, 50 * Math.random()));
320
+ } else if (e.name === "not_found" && i === 0) {
321
+ logger.warn(`Update failed for ${id} - does not exist. Throwing to caller.`);
322
+ throw e;
323
+ } else {
324
+ delete this.inprogressUpdates[id];
325
+ if (this.pendingUpdates[id]) {
326
+ delete this.pendingUpdates[id];
327
+ }
328
+ logger.error(`Error on attemped update (retry ${i}): ${JSON.stringify(e)}`);
329
+ throw e;
299
330
  }
300
331
  }
301
- await this.writeDB.put(doc);
302
- logger.debug(`Put doc: ${id}`);
303
- if (this.pendingUpdates[id].length === 0) {
304
- this.inprogressUpdates[id] = false;
305
- delete this.inprogressUpdates[id];
306
- } else {
307
- return this.applyUpdates(id);
308
- }
309
- return doc;
310
- } catch (e) {
311
- delete this.inprogressUpdates[id];
312
- if (this.pendingUpdates[id]) {
313
- delete this.pendingUpdates[id];
314
- }
315
- logger.error(`Error on attemped update: ${JSON.stringify(e)}`);
316
- throw e;
317
332
  }
333
+ throw new Error(`UpdateQueue failed for doc ${id} after ${MAX_RETRIES} retries.`);
318
334
  } else {
319
335
  throw new Error(`Empty Updates Queue Triggered`);
320
336
  }
@@ -406,7 +422,7 @@ function getCourseDB(courseID) {
406
422
  const dbName = `coursedb-${courseID}`;
407
423
  return new pouchdb_setup_default(
408
424
  ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
409
- pouchDBincludeCredentialsConfig
425
+ createPouchDBConfig()
410
426
  );
411
427
  }
412
428
  var init_courseAPI = __esm({
@@ -479,13 +495,16 @@ var init_elo = __esm({
479
495
  }
480
496
  async getNewCards(limit = 99) {
481
497
  const activeCards = await this.user.getActiveCards();
482
- return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
483
- if (activeCards.some((ac) => c.includes(ac))) {
484
- return false;
485
- } else {
486
- return true;
498
+ return (await this.course.getCardsCenteredAtELO(
499
+ { limit, elo: "user" },
500
+ (c) => {
501
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
502
+ return false;
503
+ } else {
504
+ return true;
505
+ }
487
506
  }
488
- })).map((c) => {
507
+ )).map((c) => {
489
508
  return {
490
509
  ...c,
491
510
  status: "new"
@@ -496,12 +515,74 @@ var init_elo = __esm({
496
515
  }
497
516
  });
498
517
 
518
+ // src/core/navigators/hardcodedOrder.ts
519
+ var hardcodedOrder_exports = {};
520
+ __export(hardcodedOrder_exports, {
521
+ default: () => HardcodedOrderNavigator
522
+ });
523
+ var HardcodedOrderNavigator;
524
+ var init_hardcodedOrder = __esm({
525
+ "src/core/navigators/hardcodedOrder.ts"() {
526
+ "use strict";
527
+ init_navigators();
528
+ init_logger();
529
+ HardcodedOrderNavigator = class extends ContentNavigator {
530
+ orderedCardIds = [];
531
+ user;
532
+ course;
533
+ constructor(user, course, strategyData) {
534
+ super();
535
+ this.user = user;
536
+ this.course = course;
537
+ if (strategyData.serializedData) {
538
+ try {
539
+ this.orderedCardIds = JSON.parse(strategyData.serializedData);
540
+ } catch (e) {
541
+ logger.error("Failed to parse serializedData for HardcodedOrderNavigator", e);
542
+ }
543
+ }
544
+ }
545
+ async getPendingReviews() {
546
+ const reviews = await this.user.getPendingReviews(this.course.getCourseID());
547
+ return reviews.map((r) => {
548
+ return {
549
+ ...r,
550
+ contentSourceType: "course",
551
+ contentSourceID: this.course.getCourseID(),
552
+ cardID: r.cardId,
553
+ courseID: r.courseId,
554
+ reviewID: r._id,
555
+ status: "review"
556
+ };
557
+ });
558
+ }
559
+ async getNewCards(limit = 99) {
560
+ const activeCardIds = (await this.user.getActiveCards()).map((c) => c.cardID);
561
+ const newCardIds = this.orderedCardIds.filter(
562
+ (cardId) => !activeCardIds.includes(cardId)
563
+ );
564
+ const cardsToReturn = newCardIds.slice(0, limit);
565
+ return cardsToReturn.map((cardId) => {
566
+ return {
567
+ cardID: cardId,
568
+ courseID: this.course.getCourseID(),
569
+ contentSourceType: "course",
570
+ contentSourceID: this.course.getCourseID(),
571
+ status: "new"
572
+ };
573
+ });
574
+ }
575
+ };
576
+ }
577
+ });
578
+
499
579
  // import("./**/*") in src/core/navigators/index.ts
500
580
  var globImport;
501
581
  var init_ = __esm({
502
582
  'import("./**/*") in src/core/navigators/index.ts'() {
503
583
  globImport = __glob({
504
584
  "./elo.ts": () => Promise.resolve().then(() => (init_elo(), elo_exports)),
585
+ "./hardcodedOrder.ts": () => Promise.resolve().then(() => (init_hardcodedOrder(), hardcodedOrder_exports)),
505
586
  "./index.ts": () => Promise.resolve().then(() => (init_navigators(), navigators_exports))
506
587
  });
507
588
  }
@@ -521,6 +602,7 @@ var init_navigators = __esm({
521
602
  init_();
522
603
  Navigators = /* @__PURE__ */ ((Navigators2) => {
523
604
  Navigators2["ELO"] = "elo";
605
+ Navigators2["HARDCODED"] = "hardcodedOrder";
524
606
  return Navigators2;
525
607
  })(Navigators || {});
526
608
  ContentNavigator = class {
@@ -533,7 +615,7 @@ var init_navigators = __esm({
533
615
  static async create(user, course, strategyData) {
534
616
  const implementingClass = strategyData.implementingClass;
535
617
  let NavigatorImpl;
536
- const variations = ["", ".js", ".ts"];
618
+ const variations = [".ts", ".js", ""];
537
619
  for (const ext of variations) {
538
620
  try {
539
621
  const module = await globImport(`./${implementingClass}${ext}`);
@@ -629,6 +711,25 @@ var init_CouchDBSyncStrategy = __esm({
629
711
  import fetch3 from "cross-fetch";
630
712
  import moment4 from "moment";
631
713
  import process2 from "process";
714
+ function createPouchDBConfig() {
715
+ const hasExplicitCredentials = ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD;
716
+ const isNodeEnvironment = typeof window === "undefined";
717
+ if (hasExplicitCredentials && isNodeEnvironment) {
718
+ return {
719
+ fetch(url, opts = {}) {
720
+ const basicAuth = btoa(`${ENV.COUCHDB_USERNAME}:${ENV.COUCHDB_PASSWORD}`);
721
+ const headers = new Headers(opts.headers || {});
722
+ headers.set("Authorization", `Basic ${basicAuth}`);
723
+ const newOpts = {
724
+ ...opts,
725
+ headers
726
+ };
727
+ return pouchdb_setup_default.fetch(url, newOpts);
728
+ }
729
+ };
730
+ }
731
+ return pouchDBincludeCredentialsConfig;
732
+ }
632
733
  var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig;
633
734
  var init_couch = __esm({
634
735
  "src/impl/couch/index.ts"() {
@@ -888,6 +989,9 @@ Currently logged-in as ${this._username}.`
888
989
  await this.init();
889
990
  return ret;
890
991
  }
992
+ async get(id) {
993
+ return this.localDB.get(id);
994
+ }
891
995
  update(id, update) {
892
996
  return this.updateQueue.update(id, update);
893
997
  }
@@ -934,7 +1038,12 @@ Currently logged-in as ${this._username}.`
934
1038
  endkey: keys.endkey,
935
1039
  include_docs: true
936
1040
  });
937
- return reviews.rows.map((r) => `${r.doc.courseId}-${r.doc.cardId}`);
1041
+ return reviews.rows.map((r) => {
1042
+ return {
1043
+ courseID: r.doc.courseId,
1044
+ cardID: r.doc.cardId
1045
+ };
1046
+ });
938
1047
  }
939
1048
  async getActivityRecords() {
940
1049
  try {
@@ -1214,8 +1323,18 @@ Currently logged-in as ${this._username}.`
1214
1323
  }
1215
1324
  this.setDBandQ();
1216
1325
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
1217
- void this.applyDesignDocs();
1218
- void this.deduplicateReviews();
1326
+ this.applyDesignDocs().catch((error) => {
1327
+ log3(`Error in applyDesignDocs background task: ${error}`);
1328
+ if (error && typeof error === "object") {
1329
+ log3(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
1330
+ }
1331
+ });
1332
+ this.deduplicateReviews().catch((error) => {
1333
+ log3(`Error in deduplicateReviews background task: ${error}`);
1334
+ if (error && typeof error === "object") {
1335
+ log3(`Full error details in background task: ${JSON.stringify(error)}`);
1336
+ }
1337
+ });
1219
1338
  _BaseUser._initialized = true;
1220
1339
  }
1221
1340
  static designDocs = [
@@ -1233,10 +1352,15 @@ Currently logged-in as ${this._username}.`
1233
1352
  }
1234
1353
  ];
1235
1354
  async applyDesignDocs() {
1355
+ log3(`Starting applyDesignDocs for user: ${this._username}`);
1356
+ log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
1236
1357
  if (this._username === "admin") {
1358
+ log3("Skipping design docs for admin user");
1237
1359
  return;
1238
1360
  }
1361
+ log3(`Applying ${_BaseUser.designDocs.length} design docs`);
1239
1362
  for (const doc of _BaseUser.designDocs) {
1363
+ log3(`Applying design doc: ${doc._id}`);
1240
1364
  try {
1241
1365
  try {
1242
1366
  const existingDoc = await this.remoteDB.get(doc._id);
@@ -1313,17 +1437,21 @@ Currently logged-in as ${this._username}.`
1313
1437
  } catch (e) {
1314
1438
  const reason = e;
1315
1439
  if (reason.status === 404) {
1316
- const initCardHistory = {
1317
- _id: cardHistoryID,
1318
- cardID: record.cardID,
1319
- courseID: record.courseID,
1320
- records: [record],
1321
- lapses: 0,
1322
- streak: 0,
1323
- bestInterval: 0
1324
- };
1325
- const putResult = await this.writeDB.put(initCardHistory);
1326
- return { ...initCardHistory, _rev: putResult.rev };
1440
+ try {
1441
+ const initCardHistory = {
1442
+ _id: cardHistoryID,
1443
+ cardID: record.cardID,
1444
+ courseID: record.courseID,
1445
+ records: [record],
1446
+ lapses: 0,
1447
+ streak: 0,
1448
+ bestInterval: 0
1449
+ };
1450
+ const putResult = await this.writeDB.put(initCardHistory);
1451
+ return { ...initCardHistory, _rev: putResult.rev };
1452
+ } catch (creationError) {
1453
+ throw new Error(`Failed to create CardHistory for ${cardHistoryID}. Reason: ${creationError}`);
1454
+ }
1327
1455
  } else {
1328
1456
  throw new Error(`putCardRecord failed because of:
1329
1457
  name:${reason.name}
@@ -1335,8 +1463,13 @@ Currently logged-in as ${this._username}.`
1335
1463
  async deduplicateReviews() {
1336
1464
  try {
1337
1465
  log3("Starting deduplication of scheduled reviews...");
1466
+ log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
1467
+ log3(`Write DB name: ${this.writeDB.name || "unknown"}`);
1338
1468
  const reviewsMap = {};
1339
1469
  const duplicateDocIds = [];
1470
+ log3(
1471
+ `Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || "unknown"}`
1472
+ );
1340
1473
  const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
1341
1474
  log3(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
1342
1475
  scheduledReviews.rows.forEach((r) => {
@@ -1371,6 +1504,17 @@ Currently logged-in as ${this._username}.`
1371
1504
  }
1372
1505
  } catch (error) {
1373
1506
  log3(`Error during review deduplication: ${error}`);
1507
+ if (error && typeof error === "object" && "status" in error && error.status === 404) {
1508
+ log3(
1509
+ `Database not found (404) during review deduplication. Database: ${this.remoteDB.name || "unknown"}`
1510
+ );
1511
+ log3(
1512
+ `This might indicate the user database doesn't exist or the reviewCards view isn't available`
1513
+ );
1514
+ }
1515
+ if (error && typeof error === "object") {
1516
+ log3(`Full error details: ${JSON.stringify(error)}`);
1517
+ }
1374
1518
  }
1375
1519
  }
1376
1520
  /**
@@ -2074,7 +2218,10 @@ var init_courseDB3 = __esm({
2074
2218
  };
2075
2219
  }
2076
2220
  async getCardsByELO(elo, limit) {
2077
- return this.unpacker.queryByElo(elo, limit || 25);
2221
+ return (await this.unpacker.queryByElo(elo, limit || 25)).map((card) => {
2222
+ const [courseID, cardID, elo2] = card.split("-");
2223
+ return { courseID, cardID, elo: elo2 ? parseInt(elo2) : void 0 };
2224
+ });
2078
2225
  }
2079
2226
  async getCardEloData(cardIds) {
2080
2227
  const results = await Promise.all(
@@ -2092,16 +2239,20 @@ var init_courseDB3 = __esm({
2092
2239
  async updateCardElo(cardId, _elo) {
2093
2240
  return { ok: true, id: cardId, rev: "1-static" };
2094
2241
  }
2095
- async getNewCards(limit) {
2096
- const cardIds = await this.unpacker.queryByElo(1e3, limit || 10);
2097
- return cardIds.map((cardId) => ({
2098
- status: "new",
2099
- qualifiedID: `${this.courseId}-${cardId}`,
2100
- cardID: cardId,
2101
- contentSourceType: "course",
2102
- contentSourceID: this.courseId,
2103
- courseID: this.courseId
2104
- }));
2242
+ async getNewCards(limit = 99) {
2243
+ const activeCards = await this.userDB.getActiveCards();
2244
+ return (await this.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
2245
+ if (activeCards.some((ac) => c.cardID === ac.cardID)) {
2246
+ return false;
2247
+ } else {
2248
+ return true;
2249
+ }
2250
+ })).map((c) => {
2251
+ return {
2252
+ ...c,
2253
+ status: "new"
2254
+ };
2255
+ });
2105
2256
  }
2106
2257
  async getCardsCenteredAtELO(options, filter) {
2107
2258
  let targetElo = typeof options.elo === "number" ? options.elo : 1e3;
@@ -2118,14 +2269,19 @@ var init_courseDB3 = __esm({
2118
2269
  } else if (options.elo === "random") {
2119
2270
  targetElo = 800 + Math.random() * 400;
2120
2271
  }
2121
- let cardIds = await this.unpacker.queryByElo(targetElo, options.limit * 2);
2272
+ let cardIds = (await this.unpacker.queryByElo(targetElo, options.limit * 2)).map((c) => {
2273
+ return {
2274
+ cardID: c,
2275
+ courseID: this.courseId
2276
+ };
2277
+ });
2122
2278
  if (filter) {
2123
2279
  cardIds = cardIds.filter(filter);
2124
2280
  }
2125
- return cardIds.slice(0, options.limit).map((cardId) => ({
2281
+ return cardIds.slice(0, options.limit).map((card) => ({
2126
2282
  status: "new",
2127
- qualifiedID: `${this.courseId}-${cardId}`,
2128
- cardID: cardId,
2283
+ // qualifiedID: `${this.courseId}-${cardId}`,
2284
+ cardID: card.cardID,
2129
2285
  contentSourceType: "course",
2130
2286
  contentSourceID: this.courseId,
2131
2287
  courseID: this.courseId
@@ -2277,7 +2433,7 @@ var init_courseDB3 = __esm({
2277
2433
  // Navigation Strategy Manager implementation
2278
2434
  async getNavigationStrategy(_id) {
2279
2435
  return {
2280
- id: "ELO",
2436
+ _id: "NAVIGATION_STRATEGY-ELO",
2281
2437
  docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
2282
2438
  name: "ELO",
2283
2439
  description: "ELO-based navigation strategy",
@@ -2317,6 +2473,16 @@ var init_courseDB3 = __esm({
2317
2473
  async getAttachmentBlob(docId, attachmentName) {
2318
2474
  return this.unpacker.getAttachmentBlob(docId, attachmentName);
2319
2475
  }
2476
+ // Admin search methods
2477
+ async searchCards(_query) {
2478
+ return [];
2479
+ }
2480
+ async find(_request) {
2481
+ return {
2482
+ docs: [],
2483
+ warning: "Find operations not supported in static mode"
2484
+ };
2485
+ }
2320
2486
  };
2321
2487
  }
2322
2488
  });
@@ -2332,11 +2498,17 @@ var init_coursesDB = __esm({
2332
2498
  this.manifests = manifests;
2333
2499
  }
2334
2500
  async getCourseConfig(courseId) {
2335
- if (!this.manifests[courseId]) {
2336
- logger.warn(`Course ${courseId} not found`);
2337
- return {};
2501
+ const manifest = this.manifests[courseId];
2502
+ if (!manifest) {
2503
+ logger.warn(`Course manifest for ${courseId} not found`);
2504
+ throw new Error(`Course ${courseId} not found`);
2505
+ }
2506
+ if (manifest.courseConfig) {
2507
+ return manifest.courseConfig;
2508
+ } else {
2509
+ logger.warn(`Course config not found in manifest for course ${courseId}`);
2510
+ throw new Error(`Course config not found for course ${courseId}`);
2338
2511
  }
2339
- return {};
2340
2512
  }
2341
2513
  async getCourseList() {
2342
2514
  return Object.keys(this.manifests).map(
@@ -2424,23 +2596,49 @@ var init_StaticDataLayerProvider = __esm({
2424
2596
  config;
2425
2597
  initialized = false;
2426
2598
  courseUnpackers = /* @__PURE__ */ new Map();
2599
+ manifests = {};
2427
2600
  constructor(config) {
2428
2601
  this.config = {
2429
- staticContentPath: config.staticContentPath || "/static-courses",
2430
2602
  localStoragePrefix: config.localStoragePrefix || "skuilder-static",
2431
- manifests: config.manifests || {}
2603
+ rootManifest: config.rootManifest || { dependencies: {} },
2604
+ rootManifestUrl: config.rootManifestUrl || "/"
2432
2605
  };
2433
2606
  }
2607
+ async resolveCourseDependencies() {
2608
+ logger.info("[StaticDataLayerProvider] Starting course dependency resolution...");
2609
+ const rootManifest = this.config.rootManifest;
2610
+ for (const [courseName, courseUrl] of Object.entries(rootManifest.dependencies || {})) {
2611
+ try {
2612
+ logger.debug(`[StaticDataLayerProvider] Resolving dependency: ${courseName} from ${courseUrl}`);
2613
+ const courseManifestUrl = new URL(courseUrl, this.config.rootManifestUrl).href;
2614
+ const courseJsonResponse = await fetch(courseManifestUrl);
2615
+ if (!courseJsonResponse.ok) {
2616
+ throw new Error(`Failed to fetch course manifest for ${courseName}`);
2617
+ }
2618
+ const courseJson = await courseJsonResponse.json();
2619
+ if (courseJson.content && courseJson.content.manifest) {
2620
+ const baseUrl = new URL(".", courseManifestUrl).href;
2621
+ const finalManifestUrl = new URL(courseJson.content.manifest, courseManifestUrl).href;
2622
+ const finalManifestResponse = await fetch(finalManifestUrl);
2623
+ if (!finalManifestResponse.ok) {
2624
+ throw new Error(`Failed to fetch final content manifest for ${courseName} at ${finalManifestUrl}`);
2625
+ }
2626
+ const finalManifest = await finalManifestResponse.json();
2627
+ this.manifests[courseName] = finalManifest;
2628
+ const unpacker = new StaticDataUnpacker(finalManifest, baseUrl);
2629
+ this.courseUnpackers.set(courseName, unpacker);
2630
+ logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName}`);
2631
+ }
2632
+ } catch (e) {
2633
+ logger.error(`[StaticDataLayerProvider] Failed to resolve dependency ${courseName}:`, e);
2634
+ }
2635
+ }
2636
+ logger.info("[StaticDataLayerProvider] Course dependency resolution complete.");
2637
+ }
2434
2638
  async initialize() {
2435
2639
  if (this.initialized) return;
2436
2640
  logger.info("Initializing static data layer provider");
2437
- for (const [courseId, manifest] of Object.entries(this.config.manifests)) {
2438
- const unpacker = new StaticDataUnpacker(
2439
- manifest,
2440
- `${this.config.staticContentPath}/${courseId}`
2441
- );
2442
- this.courseUnpackers.set(courseId, unpacker);
2443
- }
2641
+ await this.resolveCourseDependencies();
2444
2642
  this.initialized = true;
2445
2643
  }
2446
2644
  async teardown() {
@@ -2454,13 +2652,13 @@ var init_StaticDataLayerProvider = __esm({
2454
2652
  getCourseDB(courseId) {
2455
2653
  const unpacker = this.courseUnpackers.get(courseId);
2456
2654
  if (!unpacker) {
2457
- throw new Error(`Course ${courseId} not found in static data`);
2655
+ throw new Error(`Course ${courseId} not found or failed to initialize in static data layer.`);
2458
2656
  }
2459
- const manifest = this.config.manifests[courseId];
2657
+ const manifest = this.manifests[courseId];
2460
2658
  return new StaticCourseDB(courseId, unpacker, this.getUserDB(), manifest);
2461
2659
  }
2462
2660
  getCoursesDB() {
2463
- return new StaticCoursesDB(this.config.manifests);
2661
+ return new StaticCoursesDB(this.manifests);
2464
2662
  }
2465
2663
  async getClassroomDB(_classId, _type) {
2466
2664
  throw new Error("Classrooms not supported in static mode");
@@ -2468,6 +2666,12 @@ var init_StaticDataLayerProvider = __esm({
2468
2666
  getAdminDB() {
2469
2667
  throw new Error("Admin functions not supported in static mode");
2470
2668
  }
2669
+ async createUserReaderForUser(targetUsername) {
2670
+ logger.warn(`StaticDataLayerProvider: Multi-user access not supported in static mode`);
2671
+ logger.warn(`Request: trying to access data for ${targetUsername}`);
2672
+ logger.warn(`Returning current user's data instead`);
2673
+ return this.getUserDB();
2674
+ }
2471
2675
  isReadOnly() {
2472
2676
  return true;
2473
2677
  }