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