@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.
- package/dist/core/index.d.mts +7 -6
- package/dist/core/index.d.ts +7 -6
- package/dist/core/index.js +358 -87
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +358 -87
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-DqtNroSh.d.ts → dataLayerProvider-BiP3kWix.d.mts} +8 -1
- package/dist/{dataLayerProvider-BInqI_RF.d.mts → dataLayerProvider-DSdeyRT3.d.ts} +8 -1
- package/dist/impl/couch/index.d.mts +19 -7
- package/dist/impl/couch/index.d.ts +19 -7
- package/dist/impl/couch/index.js +375 -100
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +374 -99
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.mts +23 -8
- package/dist/impl/static/index.d.ts +23 -8
- package/dist/impl/static/index.js +289 -85
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +289 -85
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-CUNnL38E.d.mts → index-Bmll7Xse.d.mts} +1 -1
- package/dist/{index-CLL31bEy.d.ts → index-CD8BZz2k.d.ts} +1 -1
- package/dist/index.d.mts +123 -20
- package/dist/index.d.ts +123 -20
- package/dist/index.js +1133 -343
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1137 -343
- package/dist/index.mjs.map +1 -1
- package/dist/pouch/index.d.mts +1 -0
- package/dist/pouch/index.d.ts +1 -0
- package/dist/pouch/index.js +49 -0
- package/dist/pouch/index.js.map +1 -0
- package/dist/pouch/index.mjs +16 -0
- package/dist/pouch/index.mjs.map +1 -0
- package/dist/{types-BefDGkKa.d.ts → types-CewsN87z.d.ts} +1 -1
- package/dist/{types-DC-ckZug.d.mts → types-Dbp5DaRR.d.mts} +1 -1
- package/dist/{types-legacy-Birv-Jx6.d.mts → types-legacy-6ettoclI.d.mts} +17 -2
- package/dist/{types-legacy-Birv-Jx6.d.ts → types-legacy-6ettoclI.d.ts} +17 -2
- package/dist/{userDB-DusL7OXe.d.ts → userDB-C4yyAnpp.d.mts} +89 -56
- package/dist/{userDB-C33Hzjgn.d.mts → userDB-CD6s6ZCp.d.ts} +89 -56
- package/dist/util/packer/index.d.mts +3 -3
- package/dist/util/packer/index.d.ts +3 -3
- package/package.json +3 -3
- package/src/core/interfaces/contentSource.ts +3 -2
- package/src/core/interfaces/courseDB.ts +26 -3
- package/src/core/interfaces/dataLayerProvider.ts +9 -1
- package/src/core/interfaces/userDB.ts +80 -64
- package/src/core/navigators/elo.ts +10 -7
- package/src/core/navigators/hardcodedOrder.ts +64 -0
- package/src/core/navigators/index.ts +2 -1
- package/src/core/types/contentNavigationStrategy.ts +2 -1
- package/src/core/types/types-legacy.ts +7 -2
- package/src/impl/common/BaseUserDB.ts +60 -14
- package/src/impl/couch/CouchDBSyncStrategy.ts +2 -2
- package/src/impl/couch/PouchDataLayerProvider.ts +21 -0
- package/src/impl/couch/adminDB.ts +2 -2
- package/src/impl/couch/auth.ts +13 -4
- package/src/impl/couch/classroomDB.ts +10 -12
- package/src/impl/couch/courseAPI.ts +2 -2
- package/src/impl/couch/courseDB.ts +204 -38
- package/src/impl/couch/courseLookupDB.ts +4 -3
- package/src/impl/couch/index.ts +36 -4
- package/src/impl/couch/pouchdb-setup.ts +3 -3
- package/src/impl/couch/updateQueue.ts +59 -36
- package/src/impl/static/StaticDataLayerProvider.ts +68 -17
- package/src/impl/static/courseDB.ts +64 -20
- package/src/impl/static/coursesDB.ts +10 -6
- package/src/pouch/index.ts +2 -0
- package/src/study/ItemQueue.ts +58 -0
- package/src/study/SessionController.ts +182 -111
- package/src/study/SpacedRepetition.ts +1 -1
- package/src/study/services/CardHydrationService.ts +153 -0
- package/src/study/services/EloService.ts +85 -0
- package/src/study/services/ResponseProcessor.ts +224 -0
- package/src/study/services/SrsService.ts +44 -0
- package/tsup.config.ts +1 -0
|
@@ -146,9 +146,9 @@ var init_pouchdb_setup = __esm({
|
|
|
146
146
|
import_pouchdb.default.plugin(import_pouchdb_find.default);
|
|
147
147
|
import_pouchdb.default.plugin(import_pouchdb_authentication.default);
|
|
148
148
|
import_pouchdb.default.defaults({
|
|
149
|
-
ajax: {
|
|
150
|
-
|
|
151
|
-
}
|
|
149
|
+
// ajax: {
|
|
150
|
+
// timeout: 60000,
|
|
151
|
+
// },
|
|
152
152
|
});
|
|
153
153
|
pouchdb_setup_default = import_pouchdb.default;
|
|
154
154
|
}
|
|
@@ -302,42 +302,58 @@ var init_updateQueue = __esm({
|
|
|
302
302
|
async applyUpdates(id) {
|
|
303
303
|
logger.debug(`Applying updates on doc: ${id}`);
|
|
304
304
|
if (this.inprogressUpdates[id]) {
|
|
305
|
-
|
|
305
|
+
while (this.inprogressUpdates[id]) {
|
|
306
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 50));
|
|
307
|
+
}
|
|
306
308
|
return this.applyUpdates(id);
|
|
307
309
|
} else {
|
|
308
310
|
if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
|
|
309
311
|
this.inprogressUpdates[id] = true;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
312
|
+
const MAX_RETRIES = 5;
|
|
313
|
+
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
314
|
+
try {
|
|
315
|
+
const doc = await this.readDB.get(id);
|
|
316
|
+
logger.debug(`Retrieved doc: ${id}`);
|
|
317
|
+
let updatedDoc = { ...doc };
|
|
318
|
+
const updatesToApply = [...this.pendingUpdates[id]];
|
|
319
|
+
for (const update of updatesToApply) {
|
|
320
|
+
if (typeof update === "function") {
|
|
321
|
+
updatedDoc = { ...updatedDoc, ...update(updatedDoc) };
|
|
322
|
+
} else {
|
|
323
|
+
updatedDoc = {
|
|
324
|
+
...updatedDoc,
|
|
325
|
+
...update
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
await this.writeDB.put(updatedDoc);
|
|
330
|
+
logger.debug(`Put doc: ${id}`);
|
|
331
|
+
this.pendingUpdates[id].splice(0, updatesToApply.length);
|
|
332
|
+
if (this.pendingUpdates[id].length === 0) {
|
|
333
|
+
this.inprogressUpdates[id] = false;
|
|
334
|
+
delete this.inprogressUpdates[id];
|
|
317
335
|
} else {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
336
|
+
return this.applyUpdates(id);
|
|
337
|
+
}
|
|
338
|
+
return updatedDoc;
|
|
339
|
+
} catch (e) {
|
|
340
|
+
if (e.name === "conflict" && i < MAX_RETRIES - 1) {
|
|
341
|
+
logger.warn(`Conflict on update for doc ${id}, retry #${i + 1}`);
|
|
342
|
+
await new Promise((res) => setTimeout(res, 50 * Math.random()));
|
|
343
|
+
} else if (e.name === "not_found" && i === 0) {
|
|
344
|
+
logger.warn(`Update failed for ${id} - does not exist. Throwing to caller.`);
|
|
345
|
+
throw e;
|
|
346
|
+
} else {
|
|
347
|
+
delete this.inprogressUpdates[id];
|
|
348
|
+
if (this.pendingUpdates[id]) {
|
|
349
|
+
delete this.pendingUpdates[id];
|
|
350
|
+
}
|
|
351
|
+
logger.error(`Error on attemped update (retry ${i}): ${JSON.stringify(e)}`);
|
|
352
|
+
throw e;
|
|
322
353
|
}
|
|
323
354
|
}
|
|
324
|
-
await this.writeDB.put(doc);
|
|
325
|
-
logger.debug(`Put doc: ${id}`);
|
|
326
|
-
if (this.pendingUpdates[id].length === 0) {
|
|
327
|
-
this.inprogressUpdates[id] = false;
|
|
328
|
-
delete this.inprogressUpdates[id];
|
|
329
|
-
} else {
|
|
330
|
-
return this.applyUpdates(id);
|
|
331
|
-
}
|
|
332
|
-
return doc;
|
|
333
|
-
} catch (e) {
|
|
334
|
-
delete this.inprogressUpdates[id];
|
|
335
|
-
if (this.pendingUpdates[id]) {
|
|
336
|
-
delete this.pendingUpdates[id];
|
|
337
|
-
}
|
|
338
|
-
logger.error(`Error on attemped update: ${JSON.stringify(e)}`);
|
|
339
|
-
throw e;
|
|
340
355
|
}
|
|
356
|
+
throw new Error(`UpdateQueue failed for doc ${id} after ${MAX_RETRIES} retries.`);
|
|
341
357
|
} else {
|
|
342
358
|
throw new Error(`Empty Updates Queue Triggered`);
|
|
343
359
|
}
|
|
@@ -425,7 +441,7 @@ function getCourseDB(courseID) {
|
|
|
425
441
|
const dbName = `coursedb-${courseID}`;
|
|
426
442
|
return new pouchdb_setup_default(
|
|
427
443
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
428
|
-
|
|
444
|
+
createPouchDBConfig()
|
|
429
445
|
);
|
|
430
446
|
}
|
|
431
447
|
var import_common, import_common2, import_common3, import_uuid;
|
|
@@ -503,13 +519,16 @@ var init_elo = __esm({
|
|
|
503
519
|
}
|
|
504
520
|
async getNewCards(limit = 99) {
|
|
505
521
|
const activeCards = await this.user.getActiveCards();
|
|
506
|
-
return (await this.course.getCardsCenteredAtELO(
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
522
|
+
return (await this.course.getCardsCenteredAtELO(
|
|
523
|
+
{ limit, elo: "user" },
|
|
524
|
+
(c) => {
|
|
525
|
+
if (activeCards.some((ac) => c.cardID === ac.cardID)) {
|
|
526
|
+
return false;
|
|
527
|
+
} else {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
511
530
|
}
|
|
512
|
-
|
|
531
|
+
)).map((c) => {
|
|
513
532
|
return {
|
|
514
533
|
...c,
|
|
515
534
|
status: "new"
|
|
@@ -520,12 +539,74 @@ var init_elo = __esm({
|
|
|
520
539
|
}
|
|
521
540
|
});
|
|
522
541
|
|
|
542
|
+
// src/core/navigators/hardcodedOrder.ts
|
|
543
|
+
var hardcodedOrder_exports = {};
|
|
544
|
+
__export(hardcodedOrder_exports, {
|
|
545
|
+
default: () => HardcodedOrderNavigator
|
|
546
|
+
});
|
|
547
|
+
var HardcodedOrderNavigator;
|
|
548
|
+
var init_hardcodedOrder = __esm({
|
|
549
|
+
"src/core/navigators/hardcodedOrder.ts"() {
|
|
550
|
+
"use strict";
|
|
551
|
+
init_navigators();
|
|
552
|
+
init_logger();
|
|
553
|
+
HardcodedOrderNavigator = class extends ContentNavigator {
|
|
554
|
+
orderedCardIds = [];
|
|
555
|
+
user;
|
|
556
|
+
course;
|
|
557
|
+
constructor(user, course, strategyData) {
|
|
558
|
+
super();
|
|
559
|
+
this.user = user;
|
|
560
|
+
this.course = course;
|
|
561
|
+
if (strategyData.serializedData) {
|
|
562
|
+
try {
|
|
563
|
+
this.orderedCardIds = JSON.parse(strategyData.serializedData);
|
|
564
|
+
} catch (e) {
|
|
565
|
+
logger.error("Failed to parse serializedData for HardcodedOrderNavigator", e);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async getPendingReviews() {
|
|
570
|
+
const reviews = await this.user.getPendingReviews(this.course.getCourseID());
|
|
571
|
+
return reviews.map((r) => {
|
|
572
|
+
return {
|
|
573
|
+
...r,
|
|
574
|
+
contentSourceType: "course",
|
|
575
|
+
contentSourceID: this.course.getCourseID(),
|
|
576
|
+
cardID: r.cardId,
|
|
577
|
+
courseID: r.courseId,
|
|
578
|
+
reviewID: r._id,
|
|
579
|
+
status: "review"
|
|
580
|
+
};
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
async getNewCards(limit = 99) {
|
|
584
|
+
const activeCardIds = (await this.user.getActiveCards()).map((c) => c.cardID);
|
|
585
|
+
const newCardIds = this.orderedCardIds.filter(
|
|
586
|
+
(cardId) => !activeCardIds.includes(cardId)
|
|
587
|
+
);
|
|
588
|
+
const cardsToReturn = newCardIds.slice(0, limit);
|
|
589
|
+
return cardsToReturn.map((cardId) => {
|
|
590
|
+
return {
|
|
591
|
+
cardID: cardId,
|
|
592
|
+
courseID: this.course.getCourseID(),
|
|
593
|
+
contentSourceType: "course",
|
|
594
|
+
contentSourceID: this.course.getCourseID(),
|
|
595
|
+
status: "new"
|
|
596
|
+
};
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
523
603
|
// import("./**/*") in src/core/navigators/index.ts
|
|
524
604
|
var globImport;
|
|
525
605
|
var init_ = __esm({
|
|
526
606
|
'import("./**/*") in src/core/navigators/index.ts'() {
|
|
527
607
|
globImport = __glob({
|
|
528
608
|
"./elo.ts": () => Promise.resolve().then(() => (init_elo(), elo_exports)),
|
|
609
|
+
"./hardcodedOrder.ts": () => Promise.resolve().then(() => (init_hardcodedOrder(), hardcodedOrder_exports)),
|
|
529
610
|
"./index.ts": () => Promise.resolve().then(() => (init_navigators(), navigators_exports))
|
|
530
611
|
});
|
|
531
612
|
}
|
|
@@ -545,6 +626,7 @@ var init_navigators = __esm({
|
|
|
545
626
|
init_();
|
|
546
627
|
Navigators = /* @__PURE__ */ ((Navigators2) => {
|
|
547
628
|
Navigators2["ELO"] = "elo";
|
|
629
|
+
Navigators2["HARDCODED"] = "hardcodedOrder";
|
|
548
630
|
return Navigators2;
|
|
549
631
|
})(Navigators || {});
|
|
550
632
|
ContentNavigator = class {
|
|
@@ -557,7 +639,7 @@ var init_navigators = __esm({
|
|
|
557
639
|
static async create(user, course, strategyData) {
|
|
558
640
|
const implementingClass = strategyData.implementingClass;
|
|
559
641
|
let NavigatorImpl;
|
|
560
|
-
const variations = ["", ".js", "
|
|
642
|
+
const variations = [".ts", ".js", ""];
|
|
561
643
|
for (const ext of variations) {
|
|
562
644
|
try {
|
|
563
645
|
const module2 = await globImport(`./${implementingClass}${ext}`);
|
|
@@ -649,6 +731,25 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
649
731
|
});
|
|
650
732
|
|
|
651
733
|
// src/impl/couch/index.ts
|
|
734
|
+
function createPouchDBConfig() {
|
|
735
|
+
const hasExplicitCredentials = ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD;
|
|
736
|
+
const isNodeEnvironment = typeof window === "undefined";
|
|
737
|
+
if (hasExplicitCredentials && isNodeEnvironment) {
|
|
738
|
+
return {
|
|
739
|
+
fetch(url, opts = {}) {
|
|
740
|
+
const basicAuth = btoa(`${ENV.COUCHDB_USERNAME}:${ENV.COUCHDB_PASSWORD}`);
|
|
741
|
+
const headers = new Headers(opts.headers || {});
|
|
742
|
+
headers.set("Authorization", `Basic ${basicAuth}`);
|
|
743
|
+
const newOpts = {
|
|
744
|
+
...opts,
|
|
745
|
+
headers
|
|
746
|
+
};
|
|
747
|
+
return pouchdb_setup_default.fetch(url, newOpts);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
return pouchDBincludeCredentialsConfig;
|
|
752
|
+
}
|
|
652
753
|
var import_cross_fetch2, import_moment4, import_process, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig;
|
|
653
754
|
var init_couch = __esm({
|
|
654
755
|
"src/impl/couch/index.ts"() {
|
|
@@ -911,6 +1012,9 @@ Currently logged-in as ${this._username}.`
|
|
|
911
1012
|
await this.init();
|
|
912
1013
|
return ret;
|
|
913
1014
|
}
|
|
1015
|
+
async get(id) {
|
|
1016
|
+
return this.localDB.get(id);
|
|
1017
|
+
}
|
|
914
1018
|
update(id, update) {
|
|
915
1019
|
return this.updateQueue.update(id, update);
|
|
916
1020
|
}
|
|
@@ -957,7 +1061,12 @@ Currently logged-in as ${this._username}.`
|
|
|
957
1061
|
endkey: keys.endkey,
|
|
958
1062
|
include_docs: true
|
|
959
1063
|
});
|
|
960
|
-
return reviews.rows.map((r) =>
|
|
1064
|
+
return reviews.rows.map((r) => {
|
|
1065
|
+
return {
|
|
1066
|
+
courseID: r.doc.courseId,
|
|
1067
|
+
cardID: r.doc.cardId
|
|
1068
|
+
};
|
|
1069
|
+
});
|
|
961
1070
|
}
|
|
962
1071
|
async getActivityRecords() {
|
|
963
1072
|
try {
|
|
@@ -1237,8 +1346,18 @@ Currently logged-in as ${this._username}.`
|
|
|
1237
1346
|
}
|
|
1238
1347
|
this.setDBandQ();
|
|
1239
1348
|
this.syncStrategy.startSync(this.localDB, this.remoteDB);
|
|
1240
|
-
|
|
1241
|
-
|
|
1349
|
+
this.applyDesignDocs().catch((error) => {
|
|
1350
|
+
log3(`Error in applyDesignDocs background task: ${error}`);
|
|
1351
|
+
if (error && typeof error === "object") {
|
|
1352
|
+
log3(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
this.deduplicateReviews().catch((error) => {
|
|
1356
|
+
log3(`Error in deduplicateReviews background task: ${error}`);
|
|
1357
|
+
if (error && typeof error === "object") {
|
|
1358
|
+
log3(`Full error details in background task: ${JSON.stringify(error)}`);
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1242
1361
|
_BaseUser._initialized = true;
|
|
1243
1362
|
}
|
|
1244
1363
|
static designDocs = [
|
|
@@ -1256,10 +1375,15 @@ Currently logged-in as ${this._username}.`
|
|
|
1256
1375
|
}
|
|
1257
1376
|
];
|
|
1258
1377
|
async applyDesignDocs() {
|
|
1378
|
+
log3(`Starting applyDesignDocs for user: ${this._username}`);
|
|
1379
|
+
log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
|
|
1259
1380
|
if (this._username === "admin") {
|
|
1381
|
+
log3("Skipping design docs for admin user");
|
|
1260
1382
|
return;
|
|
1261
1383
|
}
|
|
1384
|
+
log3(`Applying ${_BaseUser.designDocs.length} design docs`);
|
|
1262
1385
|
for (const doc of _BaseUser.designDocs) {
|
|
1386
|
+
log3(`Applying design doc: ${doc._id}`);
|
|
1263
1387
|
try {
|
|
1264
1388
|
try {
|
|
1265
1389
|
const existingDoc = await this.remoteDB.get(doc._id);
|
|
@@ -1336,17 +1460,21 @@ Currently logged-in as ${this._username}.`
|
|
|
1336
1460
|
} catch (e) {
|
|
1337
1461
|
const reason = e;
|
|
1338
1462
|
if (reason.status === 404) {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1463
|
+
try {
|
|
1464
|
+
const initCardHistory = {
|
|
1465
|
+
_id: cardHistoryID,
|
|
1466
|
+
cardID: record.cardID,
|
|
1467
|
+
courseID: record.courseID,
|
|
1468
|
+
records: [record],
|
|
1469
|
+
lapses: 0,
|
|
1470
|
+
streak: 0,
|
|
1471
|
+
bestInterval: 0
|
|
1472
|
+
};
|
|
1473
|
+
const putResult = await this.writeDB.put(initCardHistory);
|
|
1474
|
+
return { ...initCardHistory, _rev: putResult.rev };
|
|
1475
|
+
} catch (creationError) {
|
|
1476
|
+
throw new Error(`Failed to create CardHistory for ${cardHistoryID}. Reason: ${creationError}`);
|
|
1477
|
+
}
|
|
1350
1478
|
} else {
|
|
1351
1479
|
throw new Error(`putCardRecord failed because of:
|
|
1352
1480
|
name:${reason.name}
|
|
@@ -1358,8 +1486,13 @@ Currently logged-in as ${this._username}.`
|
|
|
1358
1486
|
async deduplicateReviews() {
|
|
1359
1487
|
try {
|
|
1360
1488
|
log3("Starting deduplication of scheduled reviews...");
|
|
1489
|
+
log3(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
|
|
1490
|
+
log3(`Write DB name: ${this.writeDB.name || "unknown"}`);
|
|
1361
1491
|
const reviewsMap = {};
|
|
1362
1492
|
const duplicateDocIds = [];
|
|
1493
|
+
log3(
|
|
1494
|
+
`Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || "unknown"}`
|
|
1495
|
+
);
|
|
1363
1496
|
const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
|
|
1364
1497
|
log3(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
|
|
1365
1498
|
scheduledReviews.rows.forEach((r) => {
|
|
@@ -1394,6 +1527,17 @@ Currently logged-in as ${this._username}.`
|
|
|
1394
1527
|
}
|
|
1395
1528
|
} catch (error) {
|
|
1396
1529
|
log3(`Error during review deduplication: ${error}`);
|
|
1530
|
+
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
1531
|
+
log3(
|
|
1532
|
+
`Database not found (404) during review deduplication. Database: ${this.remoteDB.name || "unknown"}`
|
|
1533
|
+
);
|
|
1534
|
+
log3(
|
|
1535
|
+
`This might indicate the user database doesn't exist or the reviewCards view isn't available`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
if (error && typeof error === "object") {
|
|
1539
|
+
log3(`Full error details: ${JSON.stringify(error)}`);
|
|
1540
|
+
}
|
|
1397
1541
|
}
|
|
1398
1542
|
}
|
|
1399
1543
|
/**
|
|
@@ -2098,7 +2242,10 @@ var init_courseDB3 = __esm({
|
|
|
2098
2242
|
};
|
|
2099
2243
|
}
|
|
2100
2244
|
async getCardsByELO(elo, limit) {
|
|
2101
|
-
return this.unpacker.queryByElo(elo, limit || 25)
|
|
2245
|
+
return (await this.unpacker.queryByElo(elo, limit || 25)).map((card) => {
|
|
2246
|
+
const [courseID, cardID, elo2] = card.split("-");
|
|
2247
|
+
return { courseID, cardID, elo: elo2 ? parseInt(elo2) : void 0 };
|
|
2248
|
+
});
|
|
2102
2249
|
}
|
|
2103
2250
|
async getCardEloData(cardIds) {
|
|
2104
2251
|
const results = await Promise.all(
|
|
@@ -2116,16 +2263,20 @@ var init_courseDB3 = __esm({
|
|
|
2116
2263
|
async updateCardElo(cardId, _elo) {
|
|
2117
2264
|
return { ok: true, id: cardId, rev: "1-static" };
|
|
2118
2265
|
}
|
|
2119
|
-
async getNewCards(limit) {
|
|
2120
|
-
const
|
|
2121
|
-
return
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2266
|
+
async getNewCards(limit = 99) {
|
|
2267
|
+
const activeCards = await this.userDB.getActiveCards();
|
|
2268
|
+
return (await this.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
|
|
2269
|
+
if (activeCards.some((ac) => c.cardID === ac.cardID)) {
|
|
2270
|
+
return false;
|
|
2271
|
+
} else {
|
|
2272
|
+
return true;
|
|
2273
|
+
}
|
|
2274
|
+
})).map((c) => {
|
|
2275
|
+
return {
|
|
2276
|
+
...c,
|
|
2277
|
+
status: "new"
|
|
2278
|
+
};
|
|
2279
|
+
});
|
|
2129
2280
|
}
|
|
2130
2281
|
async getCardsCenteredAtELO(options, filter) {
|
|
2131
2282
|
let targetElo = typeof options.elo === "number" ? options.elo : 1e3;
|
|
@@ -2142,14 +2293,19 @@ var init_courseDB3 = __esm({
|
|
|
2142
2293
|
} else if (options.elo === "random") {
|
|
2143
2294
|
targetElo = 800 + Math.random() * 400;
|
|
2144
2295
|
}
|
|
2145
|
-
let cardIds = await this.unpacker.queryByElo(targetElo, options.limit * 2)
|
|
2296
|
+
let cardIds = (await this.unpacker.queryByElo(targetElo, options.limit * 2)).map((c) => {
|
|
2297
|
+
return {
|
|
2298
|
+
cardID: c,
|
|
2299
|
+
courseID: this.courseId
|
|
2300
|
+
};
|
|
2301
|
+
});
|
|
2146
2302
|
if (filter) {
|
|
2147
2303
|
cardIds = cardIds.filter(filter);
|
|
2148
2304
|
}
|
|
2149
|
-
return cardIds.slice(0, options.limit).map((
|
|
2305
|
+
return cardIds.slice(0, options.limit).map((card) => ({
|
|
2150
2306
|
status: "new",
|
|
2151
|
-
qualifiedID: `${this.courseId}-${cardId}`,
|
|
2152
|
-
cardID:
|
|
2307
|
+
// qualifiedID: `${this.courseId}-${cardId}`,
|
|
2308
|
+
cardID: card.cardID,
|
|
2153
2309
|
contentSourceType: "course",
|
|
2154
2310
|
contentSourceID: this.courseId,
|
|
2155
2311
|
courseID: this.courseId
|
|
@@ -2301,7 +2457,7 @@ var init_courseDB3 = __esm({
|
|
|
2301
2457
|
// Navigation Strategy Manager implementation
|
|
2302
2458
|
async getNavigationStrategy(_id) {
|
|
2303
2459
|
return {
|
|
2304
|
-
|
|
2460
|
+
_id: "NAVIGATION_STRATEGY-ELO",
|
|
2305
2461
|
docType: "NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */,
|
|
2306
2462
|
name: "ELO",
|
|
2307
2463
|
description: "ELO-based navigation strategy",
|
|
@@ -2341,6 +2497,16 @@ var init_courseDB3 = __esm({
|
|
|
2341
2497
|
async getAttachmentBlob(docId, attachmentName) {
|
|
2342
2498
|
return this.unpacker.getAttachmentBlob(docId, attachmentName);
|
|
2343
2499
|
}
|
|
2500
|
+
// Admin search methods
|
|
2501
|
+
async searchCards(_query) {
|
|
2502
|
+
return [];
|
|
2503
|
+
}
|
|
2504
|
+
async find(_request) {
|
|
2505
|
+
return {
|
|
2506
|
+
docs: [],
|
|
2507
|
+
warning: "Find operations not supported in static mode"
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2344
2510
|
};
|
|
2345
2511
|
}
|
|
2346
2512
|
});
|
|
@@ -2356,11 +2522,17 @@ var init_coursesDB = __esm({
|
|
|
2356
2522
|
this.manifests = manifests;
|
|
2357
2523
|
}
|
|
2358
2524
|
async getCourseConfig(courseId) {
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2525
|
+
const manifest = this.manifests[courseId];
|
|
2526
|
+
if (!manifest) {
|
|
2527
|
+
logger.warn(`Course manifest for ${courseId} not found`);
|
|
2528
|
+
throw new Error(`Course ${courseId} not found`);
|
|
2529
|
+
}
|
|
2530
|
+
if (manifest.courseConfig) {
|
|
2531
|
+
return manifest.courseConfig;
|
|
2532
|
+
} else {
|
|
2533
|
+
logger.warn(`Course config not found in manifest for course ${courseId}`);
|
|
2534
|
+
throw new Error(`Course config not found for course ${courseId}`);
|
|
2362
2535
|
}
|
|
2363
|
-
return {};
|
|
2364
2536
|
}
|
|
2365
2537
|
async getCourseList() {
|
|
2366
2538
|
return Object.keys(this.manifests).map(
|
|
@@ -2448,23 +2620,49 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2448
2620
|
config;
|
|
2449
2621
|
initialized = false;
|
|
2450
2622
|
courseUnpackers = /* @__PURE__ */ new Map();
|
|
2623
|
+
manifests = {};
|
|
2451
2624
|
constructor(config) {
|
|
2452
2625
|
this.config = {
|
|
2453
|
-
staticContentPath: config.staticContentPath || "/static-courses",
|
|
2454
2626
|
localStoragePrefix: config.localStoragePrefix || "skuilder-static",
|
|
2455
|
-
|
|
2627
|
+
rootManifest: config.rootManifest || { dependencies: {} },
|
|
2628
|
+
rootManifestUrl: config.rootManifestUrl || "/"
|
|
2456
2629
|
};
|
|
2457
2630
|
}
|
|
2631
|
+
async resolveCourseDependencies() {
|
|
2632
|
+
logger.info("[StaticDataLayerProvider] Starting course dependency resolution...");
|
|
2633
|
+
const rootManifest = this.config.rootManifest;
|
|
2634
|
+
for (const [courseName, courseUrl] of Object.entries(rootManifest.dependencies || {})) {
|
|
2635
|
+
try {
|
|
2636
|
+
logger.debug(`[StaticDataLayerProvider] Resolving dependency: ${courseName} from ${courseUrl}`);
|
|
2637
|
+
const courseManifestUrl = new URL(courseUrl, this.config.rootManifestUrl).href;
|
|
2638
|
+
const courseJsonResponse = await fetch(courseManifestUrl);
|
|
2639
|
+
if (!courseJsonResponse.ok) {
|
|
2640
|
+
throw new Error(`Failed to fetch course manifest for ${courseName}`);
|
|
2641
|
+
}
|
|
2642
|
+
const courseJson = await courseJsonResponse.json();
|
|
2643
|
+
if (courseJson.content && courseJson.content.manifest) {
|
|
2644
|
+
const baseUrl = new URL(".", courseManifestUrl).href;
|
|
2645
|
+
const finalManifestUrl = new URL(courseJson.content.manifest, courseManifestUrl).href;
|
|
2646
|
+
const finalManifestResponse = await fetch(finalManifestUrl);
|
|
2647
|
+
if (!finalManifestResponse.ok) {
|
|
2648
|
+
throw new Error(`Failed to fetch final content manifest for ${courseName} at ${finalManifestUrl}`);
|
|
2649
|
+
}
|
|
2650
|
+
const finalManifest = await finalManifestResponse.json();
|
|
2651
|
+
this.manifests[courseName] = finalManifest;
|
|
2652
|
+
const unpacker = new StaticDataUnpacker(finalManifest, baseUrl);
|
|
2653
|
+
this.courseUnpackers.set(courseName, unpacker);
|
|
2654
|
+
logger.info(`[StaticDataLayerProvider] Successfully resolved and prepared course: ${courseName}`);
|
|
2655
|
+
}
|
|
2656
|
+
} catch (e) {
|
|
2657
|
+
logger.error(`[StaticDataLayerProvider] Failed to resolve dependency ${courseName}:`, e);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
logger.info("[StaticDataLayerProvider] Course dependency resolution complete.");
|
|
2661
|
+
}
|
|
2458
2662
|
async initialize() {
|
|
2459
2663
|
if (this.initialized) return;
|
|
2460
2664
|
logger.info("Initializing static data layer provider");
|
|
2461
|
-
|
|
2462
|
-
const unpacker = new StaticDataUnpacker(
|
|
2463
|
-
manifest,
|
|
2464
|
-
`${this.config.staticContentPath}/${courseId}`
|
|
2465
|
-
);
|
|
2466
|
-
this.courseUnpackers.set(courseId, unpacker);
|
|
2467
|
-
}
|
|
2665
|
+
await this.resolveCourseDependencies();
|
|
2468
2666
|
this.initialized = true;
|
|
2469
2667
|
}
|
|
2470
2668
|
async teardown() {
|
|
@@ -2478,13 +2676,13 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2478
2676
|
getCourseDB(courseId) {
|
|
2479
2677
|
const unpacker = this.courseUnpackers.get(courseId);
|
|
2480
2678
|
if (!unpacker) {
|
|
2481
|
-
throw new Error(`Course ${courseId} not found in static data
|
|
2679
|
+
throw new Error(`Course ${courseId} not found or failed to initialize in static data layer.`);
|
|
2482
2680
|
}
|
|
2483
|
-
const manifest = this.
|
|
2681
|
+
const manifest = this.manifests[courseId];
|
|
2484
2682
|
return new StaticCourseDB(courseId, unpacker, this.getUserDB(), manifest);
|
|
2485
2683
|
}
|
|
2486
2684
|
getCoursesDB() {
|
|
2487
|
-
return new StaticCoursesDB(this.
|
|
2685
|
+
return new StaticCoursesDB(this.manifests);
|
|
2488
2686
|
}
|
|
2489
2687
|
async getClassroomDB(_classId, _type) {
|
|
2490
2688
|
throw new Error("Classrooms not supported in static mode");
|
|
@@ -2492,6 +2690,12 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
2492
2690
|
getAdminDB() {
|
|
2493
2691
|
throw new Error("Admin functions not supported in static mode");
|
|
2494
2692
|
}
|
|
2693
|
+
async createUserReaderForUser(targetUsername) {
|
|
2694
|
+
logger.warn(`StaticDataLayerProvider: Multi-user access not supported in static mode`);
|
|
2695
|
+
logger.warn(`Request: trying to access data for ${targetUsername}`);
|
|
2696
|
+
logger.warn(`Returning current user's data instead`);
|
|
2697
|
+
return this.getUserDB();
|
|
2698
|
+
}
|
|
2495
2699
|
isReadOnly() {
|
|
2496
2700
|
return true;
|
|
2497
2701
|
}
|