@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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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(
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
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", "
|
|
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) =>
|
|
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
|
-
|
|
1218
|
-
|
|
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
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
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
|
|
2097
|
-
return
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
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((
|
|
2281
|
+
return cardIds.slice(0, options.limit).map((card) => ({
|
|
2126
2282
|
status: "new",
|
|
2127
|
-
qualifiedID: `${this.courseId}-${cardId}`,
|
|
2128
|
-
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
|
-
|
|
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
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
}
|