@vue-skuilder/db 0.1.7 → 0.1.8-1

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 (65) hide show
  1. package/dist/{SyncStrategy-DnJRj-Xp.d.mts → SyncStrategy-CyATpyLQ.d.mts} +6 -0
  2. package/dist/{SyncStrategy-DnJRj-Xp.d.ts → SyncStrategy-CyATpyLQ.d.ts} +6 -0
  3. package/dist/core/index.d.mts +5 -5
  4. package/dist/core/index.d.ts +5 -5
  5. package/dist/core/index.js +131 -118
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +128 -115
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BbW9EnZK.d.mts → dataLayerProvider-BInqI_RF.d.mts} +1 -1
  10. package/dist/{dataLayerProvider-6stCgDME.d.ts → dataLayerProvider-DqtNroSh.d.ts} +1 -1
  11. package/dist/impl/couch/index.d.mts +6 -6
  12. package/dist/impl/couch/index.d.ts +6 -6
  13. package/dist/impl/couch/index.js +1368 -1255
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +1362 -1249
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/index.d.mts +8 -6
  18. package/dist/impl/static/index.d.ts +8 -6
  19. package/dist/impl/static/index.js +253 -843
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +250 -842
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/index-CLL31bEy.d.ts +137 -0
  24. package/dist/index-CUNnL38E.d.mts +137 -0
  25. package/dist/index.d.mts +11 -56
  26. package/dist/index.d.ts +11 -56
  27. package/dist/index.js +346 -173
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +343 -170
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/{types-BvzcRAys.d.ts → types-BefDGkKa.d.ts} +1 -1
  32. package/dist/{types-CQQ80R5N.d.mts → types-DC-ckZug.d.mts} +1 -1
  33. package/dist/{types-legacy-CtrmkOLu.d.mts → types-legacy-Birv-Jx6.d.mts} +2 -2
  34. package/dist/{types-legacy-CtrmkOLu.d.ts → types-legacy-Birv-Jx6.d.ts} +2 -2
  35. package/dist/{userDB-DUY63VMN.d.ts → userDB-C33Hzjgn.d.mts} +10 -3
  36. package/dist/{userDB-7fM4tpgr.d.mts → userDB-DusL7OXe.d.ts} +10 -3
  37. package/dist/util/packer/index.d.mts +3 -63
  38. package/dist/util/packer/index.d.ts +3 -63
  39. package/dist/util/packer/index.js +53 -1
  40. package/dist/util/packer/index.js.map +1 -1
  41. package/dist/util/packer/index.mjs +53 -1
  42. package/dist/util/packer/index.mjs.map +1 -1
  43. package/package.json +7 -4
  44. package/src/core/types/types-legacy.ts +13 -1
  45. package/src/core/types/user.ts +9 -2
  46. package/src/core/util/index.ts +5 -4
  47. package/src/impl/common/BaseUserDB.ts +33 -22
  48. package/src/impl/common/SyncStrategy.ts +7 -0
  49. package/src/impl/common/index.ts +0 -1
  50. package/src/impl/common/userDBHelpers.ts +4 -4
  51. package/src/impl/couch/CouchDBSyncStrategy.ts +10 -0
  52. package/src/impl/couch/adminDB.ts +1 -1
  53. package/src/impl/couch/courseAPI.ts +7 -6
  54. package/src/impl/couch/courseDB.ts +1 -1
  55. package/src/impl/couch/courseLookupDB.ts +1 -1
  56. package/src/impl/couch/index.ts +10 -5
  57. package/src/impl/couch/updateQueue.ts +12 -8
  58. package/src/impl/couch/user-course-relDB.ts +17 -27
  59. package/src/impl/static/NoOpSyncStrategy.ts +5 -0
  60. package/src/impl/static/StaticDataUnpacker.ts +18 -36
  61. package/src/impl/static/courseDB.ts +135 -17
  62. package/src/study/getCardDataShape.ts +2 -2
  63. package/src/util/migrator/FileSystemAdapter.ts +20 -0
  64. package/src/util/migrator/StaticToCouchDBMigrator.ts +6 -0
  65. package/src/util/packer/CouchDBToStaticPacker.ts +92 -2
@@ -30,6 +30,12 @@ interface SyncStrategy {
30
30
  * @returns PouchDB database instance (may be same as local for no-op)
31
31
  */
32
32
  setupRemoteDB(username: string): PouchDB.Database;
33
+ /**
34
+ * Get the database to use for write operations (local-first approach)
35
+ * @param username The username to get write DB for
36
+ * @returns PouchDB database instance for write operations
37
+ */
38
+ getWriteDB?(username: string): PouchDB.Database;
33
39
  /**
34
40
  * Start synchronization between local and remote databases
35
41
  * @param localDB The local PouchDB instance
@@ -30,6 +30,12 @@ interface SyncStrategy {
30
30
  * @returns PouchDB database instance (may be same as local for no-op)
31
31
  */
32
32
  setupRemoteDB(username: string): PouchDB.Database;
33
+ /**
34
+ * Get the database to use for write operations (local-first approach)
35
+ * @param username The username to get write DB for
36
+ * @returns PouchDB database instance for write operations
37
+ */
38
+ getWriteDB?(username: string): PouchDB.Database;
33
39
  /**
34
40
  * Start synchronization between local and remote databases
35
41
  * @param localDB The local PouchDB instance
@@ -1,8 +1,8 @@
1
- import { h as StudyContentSource, U as UserDBInterface, C as CourseDBInterface, d as ContentNavigationStrategyData, e as StudySessionReviewItem, f as ScheduledCard, S as StudySessionNewItem } from '../userDB-7fM4tpgr.mjs';
2
- export { B as ActivityRecord, A as AdminDBInterface, s as AssignedCard, g as AssignedContent, r as AssignedCourse, q as AssignedTag, b as ClassroomDBInterface, x as ClassroomRegistration, w as ClassroomRegistrationDesignation, y as ClassroomRegistrationDoc, o as ContentSourceID, c as CourseInfo, E as CourseRegistration, F as CourseRegistrationDoc, a as CoursesDBInterface, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, j as StudySessionItem, T as TeacherClassroomDBInterface, z as UserConfig, u as UserCourseSetting, t as UserCourseSettings, v as UsrCrsDataInterface, p as getStudySource, n as isReview } from '../userDB-7fM4tpgr.mjs';
3
- export { D as DataLayerProvider } from '../dataLayerProvider-BbW9EnZK.mjs';
4
- import { g as CardHistory, C as CardRecord, h as QuestionRecord } from '../types-legacy-CtrmkOLu.mjs';
5
- export { b as CardData, c as CourseListData, e as DataShapeData, d as DisplayableData, D as DocType, F as Field, G as GuestUsername, Q as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, f as cardHistoryPrefix, l as log } from '../types-legacy-CtrmkOLu.mjs';
1
+ import { h as StudyContentSource, U as UserDBInterface, C as CourseDBInterface, d as ContentNavigationStrategyData, e as StudySessionReviewItem, f as ScheduledCard, S as StudySessionNewItem } from '../userDB-C33Hzjgn.mjs';
2
+ export { B as ActivityRecord, A as AdminDBInterface, s as AssignedCard, g as AssignedContent, r as AssignedCourse, q as AssignedTag, b as ClassroomDBInterface, x as ClassroomRegistration, w as ClassroomRegistrationDesignation, y as ClassroomRegistrationDoc, o as ContentSourceID, c as CourseInfo, E as CourseRegistration, F as CourseRegistrationDoc, a as CoursesDBInterface, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, j as StudySessionItem, T as TeacherClassroomDBInterface, z as UserConfig, u as UserCourseSetting, t as UserCourseSettings, v as UsrCrsDataInterface, p as getStudySource, n as isReview } from '../userDB-C33Hzjgn.mjs';
3
+ export { D as DataLayerProvider } from '../dataLayerProvider-BInqI_RF.mjs';
4
+ import { g as CardHistory, C as CardRecord, h as QuestionRecord } from '../types-legacy-Birv-Jx6.mjs';
5
+ export { b as CardData, c as CourseListData, e as DataShapeData, d as DisplayableData, D as DocType, f as DocTypePrefixes, F as Field, G as GuestUsername, Q as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-Birv-Jx6.mjs';
6
6
  import { DataShape, ParsedCard } from '@vue-skuilder/common';
7
7
  import 'moment';
8
8
 
@@ -1,8 +1,8 @@
1
- import { h as StudyContentSource, U as UserDBInterface, C as CourseDBInterface, d as ContentNavigationStrategyData, e as StudySessionReviewItem, f as ScheduledCard, S as StudySessionNewItem } from '../userDB-DUY63VMN.js';
2
- export { B as ActivityRecord, A as AdminDBInterface, s as AssignedCard, g as AssignedContent, r as AssignedCourse, q as AssignedTag, b as ClassroomDBInterface, x as ClassroomRegistration, w as ClassroomRegistrationDesignation, y as ClassroomRegistrationDoc, o as ContentSourceID, c as CourseInfo, E as CourseRegistration, F as CourseRegistrationDoc, a as CoursesDBInterface, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, j as StudySessionItem, T as TeacherClassroomDBInterface, z as UserConfig, u as UserCourseSetting, t as UserCourseSettings, v as UsrCrsDataInterface, p as getStudySource, n as isReview } from '../userDB-DUY63VMN.js';
3
- export { D as DataLayerProvider } from '../dataLayerProvider-6stCgDME.js';
4
- import { g as CardHistory, C as CardRecord, h as QuestionRecord } from '../types-legacy-CtrmkOLu.js';
5
- export { b as CardData, c as CourseListData, e as DataShapeData, d as DisplayableData, D as DocType, F as Field, G as GuestUsername, Q as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, f as cardHistoryPrefix, l as log } from '../types-legacy-CtrmkOLu.js';
1
+ import { h as StudyContentSource, U as UserDBInterface, C as CourseDBInterface, d as ContentNavigationStrategyData, e as StudySessionReviewItem, f as ScheduledCard, S as StudySessionNewItem } from '../userDB-DusL7OXe.js';
2
+ export { B as ActivityRecord, A as AdminDBInterface, s as AssignedCard, g as AssignedContent, r as AssignedCourse, q as AssignedTag, b as ClassroomDBInterface, x as ClassroomRegistration, w as ClassroomRegistrationDesignation, y as ClassroomRegistrationDoc, o as ContentSourceID, c as CourseInfo, E as CourseRegistration, F as CourseRegistrationDoc, a as CoursesDBInterface, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, j as StudySessionItem, T as TeacherClassroomDBInterface, z as UserConfig, u as UserCourseSetting, t as UserCourseSettings, v as UsrCrsDataInterface, p as getStudySource, n as isReview } from '../userDB-DusL7OXe.js';
3
+ export { D as DataLayerProvider } from '../dataLayerProvider-DqtNroSh.js';
4
+ import { g as CardHistory, C as CardRecord, h as QuestionRecord } from '../types-legacy-Birv-Jx6.js';
5
+ export { b as CardData, c as CourseListData, e as DataShapeData, d as DisplayableData, D as DocType, f as DocTypePrefixes, F as Field, G as GuestUsername, Q as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-Birv-Jx6.js';
6
6
  import { DataShape, ParsedCard } from '@vue-skuilder/common';
7
7
  import 'moment';
8
8
 
@@ -102,7 +102,7 @@ var init_logger = __esm({
102
102
  });
103
103
 
104
104
  // src/core/types/types-legacy.ts
105
- var GuestUsername, log, DocType, cardHistoryPrefix;
105
+ var GuestUsername, log, DocType, DocTypePrefixes;
106
106
  var init_types_legacy = __esm({
107
107
  "src/core/types/types-legacy.ts"() {
108
108
  "use strict";
@@ -124,7 +124,19 @@ var init_types_legacy = __esm({
124
124
  DocType2["NAVIGATION_STRATEGY"] = "NAVIGATION_STRATEGY";
125
125
  return DocType2;
126
126
  })(DocType || {});
127
- cardHistoryPrefix = "cardH";
127
+ DocTypePrefixes = {
128
+ ["CARD" /* CARD */]: "c",
129
+ ["DISPLAYABLE_DATA" /* DISPLAYABLE_DATA */]: "dd",
130
+ ["TAG" /* TAG */]: "TAG",
131
+ ["CARDRECORD" /* CARDRECORD */]: "cardH",
132
+ ["SCHEDULED_CARD" /* SCHEDULED_CARD */]: "card_review_",
133
+ // Add other doctypes here as they get prefixed IDs
134
+ ["DATASHAPE" /* DATASHAPE */]: "DATASHAPE",
135
+ ["QUESTION" /* QUESTIONTYPE */]: "QUESTION",
136
+ ["VIEW" /* VIEW */]: "VIEW",
137
+ ["PEDAGOGY" /* PEDAGOGY */]: "PEDAGOGY",
138
+ ["NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */]: "NAVIGATION_STRATEGY"
139
+ };
128
140
  }
129
141
  });
130
142
 
@@ -136,16 +148,16 @@ function isQuestionRecord(c) {
136
148
  return c.userAnswer !== void 0;
137
149
  }
138
150
  function getCardHistoryID(courseID, cardID) {
139
- return `${cardHistoryPrefix}-${courseID}-${cardID}`;
151
+ return `${DocTypePrefixes["CARDRECORD" /* CARDRECORD */]}-${courseID}-${cardID}`;
140
152
  }
141
153
  function parseCardHistoryID(id) {
142
154
  const split = id.split("-");
143
155
  let error = "";
144
156
  error += split.length === 3 ? "" : `
145
157
  given ID has incorrect number of '-' characters`;
146
- error += split[0] === cardHistoryPrefix ? "" : `
147
- given ID does not start with ${cardHistoryPrefix}`;
148
- if (split.length === 3 && split[0] === cardHistoryPrefix) {
158
+ error += split[0] === DocTypePrefixes["CARDRECORD" /* CARDRECORD */] ? "" : `
159
+ given ID does not start with ${DocTypePrefixes["CARDRECORD" /* CARDRECORD */]}`;
160
+ if (split.length === 3 && split[0] === DocTypePrefixes["CARDRECORD" /* CARDRECORD */]) {
149
161
  return {
150
162
  courseID: split[1],
151
163
  cardID: split[2]
@@ -238,11 +250,11 @@ function scheduleCardReviewLocal(userDB, review) {
238
250
  const now = import_moment.default.utc();
239
251
  logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
240
252
  void userDB.put({
241
- _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
253
+ _id: DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */] + review.time.format(REVIEW_TIME_FORMAT),
242
254
  cardId: review.card_id,
243
- reviewTime: review.time,
255
+ reviewTime: review.time.toISOString(),
244
256
  courseId: review.course_id,
245
- scheduledAt: now,
257
+ scheduledAt: now.toISOString(),
246
258
  scheduledFor: review.scheduledFor,
247
259
  schedulingAgentId: review.schedulingAgentId
248
260
  });
@@ -258,15 +270,15 @@ async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
258
270
  ${JSON.stringify(err)}`);
259
271
  });
260
272
  }
261
- var import_moment, REVIEW_PREFIX, REVIEW_TIME_FORMAT, log2;
273
+ var import_moment, REVIEW_TIME_FORMAT, log2;
262
274
  var init_userDBHelpers = __esm({
263
275
  "src/impl/common/userDBHelpers.ts"() {
264
276
  "use strict";
265
277
  import_moment = __toESM(require("moment"));
278
+ init_core();
266
279
  init_logger();
267
280
  init_pouchdb_setup();
268
281
  init_dataDirectory();
269
- REVIEW_PREFIX = "card_review_";
270
282
  REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
271
283
  log2 = (s) => {
272
284
  logger.info(s);
@@ -301,7 +313,10 @@ var init_updateQueue = __esm({
301
313
  _className = "UpdateQueue";
302
314
  pendingUpdates = {};
303
315
  inprogressUpdates = {};
304
- db;
316
+ readDB;
317
+ // Database for read operations
318
+ writeDB;
319
+ // Database for write operations (local-first)
305
320
  update(id, update) {
306
321
  logger.debug(`Update requested on doc: ${id}`);
307
322
  if (this.pendingUpdates[id]) {
@@ -311,24 +326,25 @@ var init_updateQueue = __esm({
311
326
  }
312
327
  return this.applyUpdates(id);
313
328
  }
314
- constructor(db) {
329
+ constructor(readDB, writeDB) {
315
330
  super();
316
- this.db = db;
331
+ this.readDB = readDB;
332
+ this.writeDB = writeDB || readDB;
317
333
  logger.debug(`UpdateQ initialized...`);
318
- void this.db.info().then((i) => {
334
+ void this.readDB.info().then((i) => {
319
335
  logger.debug(`db info: ${JSON.stringify(i)}`);
320
336
  });
321
337
  }
322
338
  async applyUpdates(id) {
323
339
  logger.debug(`Applying updates on doc: ${id}`);
324
340
  if (this.inprogressUpdates[id]) {
325
- await this.db.info();
341
+ await this.readDB.info();
326
342
  return this.applyUpdates(id);
327
343
  } else {
328
344
  if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
329
345
  this.inprogressUpdates[id] = true;
330
346
  try {
331
- let doc = await this.db.get(id);
347
+ let doc = await this.readDB.get(id);
332
348
  logger.debug(`Retrieved doc: ${id}`);
333
349
  while (this.pendingUpdates[id].length !== 0) {
334
350
  const update = this.pendingUpdates[id].splice(0, 1)[0];
@@ -341,7 +357,7 @@ var init_updateQueue = __esm({
341
357
  };
342
358
  }
343
359
  }
344
- await this.db.put(doc);
360
+ await this.writeDB.put(doc);
345
361
  logger.debug(`Put doc: ${id}`);
346
362
  if (this.pendingUpdates[id].length === 0) {
347
363
  this.inprogressUpdates[id] = false;
@@ -367,6 +383,60 @@ var init_updateQueue = __esm({
367
383
  }
368
384
  });
369
385
 
386
+ // src/impl/couch/user-course-relDB.ts
387
+ var import_moment2, UsrCrsData;
388
+ var init_user_course_relDB = __esm({
389
+ "src/impl/couch/user-course-relDB.ts"() {
390
+ "use strict";
391
+ import_moment2 = __toESM(require("moment"));
392
+ init_logger();
393
+ UsrCrsData = class {
394
+ user;
395
+ _courseId;
396
+ constructor(user, courseId) {
397
+ this.user = user;
398
+ this._courseId = courseId;
399
+ }
400
+ async getReviewsForcast(daysCount) {
401
+ const time = import_moment2.default.utc().add(daysCount, "days");
402
+ return this.getReviewstoDate(time);
403
+ }
404
+ async getPendingReviews() {
405
+ const now = import_moment2.default.utc();
406
+ return this.getReviewstoDate(now);
407
+ }
408
+ async getScheduledReviewCount() {
409
+ return (await this.getPendingReviews()).length;
410
+ }
411
+ async getCourseSettings() {
412
+ const regDoc = await this.user.getCourseRegistrationsDoc();
413
+ const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
414
+ if (crsDoc && crsDoc.settings) {
415
+ return crsDoc.settings;
416
+ } else {
417
+ logger.warn(`no settings found during lookup on course ${this._courseId}`);
418
+ return {};
419
+ }
420
+ }
421
+ updateCourseSettings(updates) {
422
+ if ("updateCourseSettings" in this.user) {
423
+ void this.user.updateCourseSettings(this._courseId, updates);
424
+ }
425
+ }
426
+ async getReviewstoDate(targetDate) {
427
+ const allReviews = await this.user.getPendingReviews(this._courseId);
428
+ logger.debug(
429
+ `Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
430
+ );
431
+ return allReviews.filter((review) => {
432
+ const reviewTime = import_moment2.default.utc(review.reviewTime);
433
+ return targetDate.isAfter(reviewTime);
434
+ });
435
+ }
436
+ };
437
+ }
438
+ });
439
+
370
440
  // src/impl/couch/clientCache.ts
371
441
  async function GET_CACHED(k, f) {
372
442
  if (CLIENT_CACHE[k]) {
@@ -390,7 +460,8 @@ var init_clientCache = __esm({
390
460
  async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = (0, import_common2.blankCourseElo)()) {
391
461
  const db = getCourseDB(courseID);
392
462
  const payload = (0, import_common3.prepareNote55)(courseID, codeCourse, shape, data, author, tags, uploads);
393
- const result = await db.post(payload);
463
+ const _id = `${DocTypePrefixes["DISPLAYABLE_DATA" /* DISPLAYABLE_DATA */]}-${(0, import_uuid.v4)()}`;
464
+ const result = await db.put({ ...payload, _id });
394
465
  const dataShapeId = import_common.NameSpacer.getDataShapeString({
395
466
  course: codeCourse,
396
467
  dataShape: shape.name
@@ -461,7 +532,9 @@ async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags
461
532
  }
462
533
  async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
463
534
  const db = getCourseDB(courseID);
464
- const card = await db.post({
535
+ const _id = `${DocTypePrefixes["CARD" /* CARD */]}-${(0, import_uuid.v4)()}`;
536
+ const card = await db.put({
537
+ _id,
465
538
  course,
466
539
  id_displayable_data,
467
540
  id_view,
@@ -554,7 +627,7 @@ function getCourseDB(courseID) {
554
627
  pouchDBincludeCredentialsConfig
555
628
  );
556
629
  }
557
- var import_common, import_common2, import_common3, AlreadyTaggedErr;
630
+ var import_common, import_common2, import_common3, import_uuid, AlreadyTaggedErr;
558
631
  var init_courseAPI = __esm({
559
632
  "src/impl/couch/courseAPI.ts"() {
560
633
  "use strict";
@@ -568,6 +641,7 @@ var init_courseAPI = __esm({
568
641
  import_common3 = require("@vue-skuilder/common");
569
642
  init_common();
570
643
  init_logger();
644
+ import_uuid = require("uuid");
571
645
  AlreadyTaggedErr = class extends Error {
572
646
  constructor(message) {
573
647
  super(message);
@@ -1211,13 +1285,13 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1211
1285
  });
1212
1286
 
1213
1287
  // src/impl/couch/classroomDB.ts
1214
- var import_moment2, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB;
1288
+ var import_moment3, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB;
1215
1289
  var init_classroomDB2 = __esm({
1216
1290
  "src/impl/couch/classroomDB.ts"() {
1217
1291
  "use strict";
1218
1292
  init_factory();
1219
1293
  init_logger();
1220
- import_moment2 = __toESM(require("moment"));
1294
+ import_moment3 = __toESM(require("moment"));
1221
1295
  init_pouchdb_setup();
1222
1296
  init_couch();
1223
1297
  init_courseDB();
@@ -1311,9 +1385,9 @@ var init_classroomDB2 = __esm({
1311
1385
  }
1312
1386
  async getNewCards() {
1313
1387
  const activeCards = await this._user.getActiveCards();
1314
- const now = import_moment2.default.utc();
1388
+ const now = import_moment3.default.utc();
1315
1389
  const assigned = await this.getAssignedContent();
1316
- const due = assigned.filter((c) => now.isAfter(import_moment2.default.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
1390
+ const due = assigned.filter((c) => now.isAfter(import_moment3.default.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
1317
1391
  logger.info(`Due content: ${JSON.stringify(due)}`);
1318
1392
  let ret = [];
1319
1393
  for (let i = 0; i < due.length; i++) {
@@ -1424,13 +1498,13 @@ function getStartAndEndKeys2(key) {
1424
1498
  endkey: key + "\uFFF0"
1425
1499
  };
1426
1500
  }
1427
- var import_moment3, import_process, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
1501
+ var import_moment4, import_process, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_TIME_FORMAT2;
1428
1502
  var init_couch = __esm({
1429
1503
  "src/impl/couch/index.ts"() {
1430
1504
  "use strict";
1431
1505
  init_factory();
1432
1506
  init_types_legacy();
1433
- import_moment3 = __toESM(require("moment"));
1507
+ import_moment4 = __toESM(require("moment"));
1434
1508
  init_logger();
1435
1509
  init_pouchdb_setup();
1436
1510
  import_process = __toESM(require("process"));
@@ -1452,78 +1526,10 @@ var init_couch = __esm({
1452
1526
  return pouchdb_setup_default.fetch(url, opts);
1453
1527
  }
1454
1528
  };
1455
- REVIEW_PREFIX2 = "card_review_";
1456
1529
  REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
1457
1530
  }
1458
1531
  });
1459
1532
 
1460
- // src/impl/couch/user-course-relDB.ts
1461
- var import_moment4, UsrCrsData;
1462
- var init_user_course_relDB = __esm({
1463
- "src/impl/couch/user-course-relDB.ts"() {
1464
- "use strict";
1465
- import_moment4 = __toESM(require("moment"));
1466
- init_couch();
1467
- init_courseDB();
1468
- init_logger();
1469
- UsrCrsData = class {
1470
- user;
1471
- course;
1472
- _courseId;
1473
- constructor(user, courseId) {
1474
- this.user = user;
1475
- this.course = new CourseDB(courseId, async () => this.user);
1476
- this._courseId = courseId;
1477
- }
1478
- async getReviewsForcast(daysCount) {
1479
- const time = import_moment4.default.utc().add(daysCount, "days");
1480
- return this.getReviewstoDate(time);
1481
- }
1482
- async getPendingReviews() {
1483
- const now = import_moment4.default.utc();
1484
- return this.getReviewstoDate(now);
1485
- }
1486
- async getScheduledReviewCount() {
1487
- return (await this.getPendingReviews()).length;
1488
- }
1489
- async getCourseSettings() {
1490
- const regDoc = await this.user.getCourseRegistrationsDoc();
1491
- const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
1492
- if (crsDoc && crsDoc.settings) {
1493
- return crsDoc.settings;
1494
- } else {
1495
- logger.warn(`no settings found during lookup on course ${this._courseId}`);
1496
- return {};
1497
- }
1498
- }
1499
- updateCourseSettings(updates) {
1500
- void this.user.updateCourseSettings(this._courseId, updates);
1501
- }
1502
- async getReviewstoDate(targetDate) {
1503
- const keys = getStartAndEndKeys2(REVIEW_PREFIX2);
1504
- const reviews = await this.user.remote().allDocs({
1505
- startkey: keys.startkey,
1506
- endkey: keys.endkey,
1507
- include_docs: true
1508
- });
1509
- logger.debug(
1510
- `Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
1511
- );
1512
- return reviews.rows.filter((r) => {
1513
- if (r.id.startsWith(REVIEW_PREFIX2)) {
1514
- const date = import_moment4.default.utc(r.id.substr(REVIEW_PREFIX2.length), REVIEW_TIME_FORMAT2);
1515
- if (targetDate.isAfter(date)) {
1516
- if (this._courseId === void 0 || r.doc.courseId === this._courseId) {
1517
- return true;
1518
- }
1519
- }
1520
- }
1521
- }).map((r) => r.doc);
1522
- }
1523
- };
1524
- }
1525
- });
1526
-
1527
1533
  // src/impl/common/BaseUserDB.ts
1528
1534
  async function getOrCreateClassroomRegistrationsDoc(user) {
1529
1535
  let ret;
@@ -1617,10 +1623,11 @@ async function dropUserFromClassroom(user, classID) {
1617
1623
  async function getUserClassrooms(user) {
1618
1624
  return getOrCreateClassroomRegistrationsDoc(user);
1619
1625
  }
1620
- var import_common8, import_moment5, log3, cardHistoryPrefix2, BaseUser, userCoursesDoc, userClassroomsDoc;
1626
+ var import_common8, import_moment5, log3, BaseUser, userCoursesDoc, userClassroomsDoc;
1621
1627
  var init_BaseUserDB = __esm({
1622
1628
  "src/impl/common/BaseUserDB.ts"() {
1623
1629
  "use strict";
1630
+ init_core();
1624
1631
  init_util();
1625
1632
  import_common8 = require("@vue-skuilder/common");
1626
1633
  import_moment5 = __toESM(require("moment"));
@@ -1633,7 +1640,6 @@ var init_BaseUserDB = __esm({
1633
1640
  log3 = (s) => {
1634
1641
  logger.info(s);
1635
1642
  };
1636
- cardHistoryPrefix2 = "cardH-";
1637
1643
  BaseUser = class _BaseUser {
1638
1644
  static _instance;
1639
1645
  static _initialized = false;
@@ -1654,11 +1660,13 @@ var init_BaseUserDB = __esm({
1654
1660
  isLoggedIn() {
1655
1661
  return !this._username.startsWith(GuestUsername);
1656
1662
  }
1657
- remoteDB;
1658
1663
  remote() {
1659
1664
  return this.remoteDB;
1660
1665
  }
1661
1666
  localDB;
1667
+ remoteDB;
1668
+ writeDB;
1669
+ // Database to use for write operations (local-first approach)
1662
1670
  updateQueue;
1663
1671
  async createAccount(username, password) {
1664
1672
  if (!this.syncStrategy.canCreateAccount()) {
@@ -1722,8 +1730,8 @@ Currently logged-in as ${this._username}.`
1722
1730
  const allDocs = await localDB.allDocs({ include_docs: false });
1723
1731
  const docsToDelete = allDocs.rows.filter((row) => {
1724
1732
  const id = row.id;
1725
- return id.startsWith(cardHistoryPrefix2) || // Card interaction history
1726
- id.startsWith(REVIEW_PREFIX) || // Scheduled reviews
1733
+ return id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */]) || // Card interaction history
1734
+ id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]) || // Scheduled reviews
1727
1735
  id === _BaseUser.DOC_IDS.COURSE_REGISTRATIONS || // Course registrations
1728
1736
  id === _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS || // Classroom registrations
1729
1737
  id === _BaseUser.DOC_IDS.CONFIG;
@@ -1792,7 +1800,7 @@ Currently logged-in as ${this._username}.`
1792
1800
  *
1793
1801
  */
1794
1802
  async getActiveCards() {
1795
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
1803
+ const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
1796
1804
  const reviews = await this.remoteDB.allDocs({
1797
1805
  startkey: keys.startkey,
1798
1806
  endkey: keys.endkey,
@@ -1864,7 +1872,7 @@ Currently logged-in as ${this._username}.`
1864
1872
  }
1865
1873
  }
1866
1874
  async getReviewstoDate(targetDate, course_id) {
1867
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
1875
+ const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
1868
1876
  const reviews = await this.remoteDB.allDocs({
1869
1877
  startkey: keys.startkey,
1870
1878
  endkey: keys.endkey,
@@ -1874,8 +1882,11 @@ Currently logged-in as ${this._username}.`
1874
1882
  `Fetching ${this._username}'s scheduled reviews${course_id ? ` for course ${course_id}` : ""}.`
1875
1883
  );
1876
1884
  return reviews.rows.filter((r) => {
1877
- if (r.id.startsWith(REVIEW_PREFIX)) {
1878
- const date = import_moment5.default.utc(r.id.substr(REVIEW_PREFIX.length), REVIEW_TIME_FORMAT);
1885
+ if (r.id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */])) {
1886
+ const date = import_moment5.default.utc(
1887
+ r.id.substr(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */].length),
1888
+ REVIEW_TIME_FORMAT
1889
+ );
1879
1890
  if (targetDate.isAfter(date)) {
1880
1891
  if (course_id === void 0 || r.doc.courseId === course_id) {
1881
1892
  return true;
@@ -1990,7 +2001,8 @@ Currently logged-in as ${this._username}.`
1990
2001
  const defaultConfig = {
1991
2002
  _id: _BaseUser.DOC_IDS.CONFIG,
1992
2003
  darkMode: false,
1993
- likesConfetti: false
2004
+ likesConfetti: false,
2005
+ sessionTimeLimit: 5
1994
2006
  };
1995
2007
  try {
1996
2008
  const cfg = await this.localDB.get(_BaseUser.DOC_IDS.CONFIG);
@@ -2063,7 +2075,8 @@ Currently logged-in as ${this._username}.`
2063
2075
  setDBandQ() {
2064
2076
  this.localDB = getLocalUserDB(this._username);
2065
2077
  this.remoteDB = this.syncStrategy.setupRemoteDB(this._username);
2066
- this.updateQueue = new UpdateQueue(this.localDB);
2078
+ this.writeDB = this.syncStrategy.getWriteDB ? this.syncStrategy.getWriteDB(this._username) : this.localDB;
2079
+ this.updateQueue = new UpdateQueue(this.localDB, this.writeDB);
2067
2080
  }
2068
2081
  async init() {
2069
2082
  _BaseUser._initialized = false;
@@ -2181,8 +2194,8 @@ Currently logged-in as ${this._username}.`
2181
2194
  streak: 0,
2182
2195
  bestInterval: 0
2183
2196
  };
2184
- void this.remoteDB.put(initCardHistory);
2185
- return initCardHistory;
2197
+ const putResult = await this.writeDB.put(initCardHistory);
2198
+ return { ...initCardHistory, _rev: putResult.rev };
2186
2199
  } else {
2187
2200
  throw new Error(`putCardRecord failed because of:
2188
2201
  name:${reason.name}
@@ -2217,7 +2230,7 @@ Currently logged-in as ${this._username}.`
2217
2230
  const deletePromises = duplicateDocIds.map(async (docId) => {
2218
2231
  try {
2219
2232
  const doc = await this.remoteDB.get(docId);
2220
- await this.remoteDB.remove(doc);
2233
+ await this.writeDB.remove(doc);
2221
2234
  log3(`Successfully removed duplicate review: ${docId}`);
2222
2235
  } catch (error) {
2223
2236
  log3(`Failed to remove duplicate review ${docId}: ${error}`);
@@ -2239,7 +2252,7 @@ Currently logged-in as ${this._username}.`
2239
2252
  * @param course_id optional specification of individual course
2240
2253
  */
2241
2254
  async getSeenCards(course_id) {
2242
- let prefix = cardHistoryPrefix2;
2255
+ let prefix = DocTypePrefixes["CARDRECORD" /* CARDRECORD */];
2243
2256
  if (course_id) {
2244
2257
  prefix += course_id;
2245
2258
  }
@@ -2248,8 +2261,8 @@ Currently logged-in as ${this._username}.`
2248
2261
  });
2249
2262
  const ret = [];
2250
2263
  docs.rows.forEach((row) => {
2251
- if (row.id.startsWith(cardHistoryPrefix2)) {
2252
- ret.push(row.id.substr(cardHistoryPrefix2.length));
2264
+ if (row.id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */])) {
2265
+ ret.push(row.id.substr(DocTypePrefixes["CARDRECORD" /* CARDRECORD */].length));
2253
2266
  }
2254
2267
  });
2255
2268
  return ret;
@@ -2261,7 +2274,7 @@ Currently logged-in as ${this._username}.`
2261
2274
  async getHistory() {
2262
2275
  const cards = await filterAllDocsByPrefix(
2263
2276
  this.remoteDB,
2264
- cardHistoryPrefix2,
2277
+ DocTypePrefixes["CARDRECORD" /* CARDRECORD */],
2265
2278
  {
2266
2279
  include_docs: true,
2267
2280
  attachments: false
@@ -2302,7 +2315,7 @@ Currently logged-in as ${this._username}.`
2302
2315
  } catch (e) {
2303
2316
  const err = e;
2304
2317
  if (err.status === 404) {
2305
- await this.remoteDB.put({
2318
+ await this.writeDB.put({
2306
2319
  _id: _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS,
2307
2320
  registrations: []
2308
2321
  });
@@ -2350,10 +2363,10 @@ Currently logged-in as ${this._username}.`
2350
2363
  }
2351
2364
  }
2352
2365
  async scheduleCardReview(review) {
2353
- return scheduleCardReviewLocal(this.remoteDB, review);
2366
+ return scheduleCardReviewLocal(this.writeDB, review);
2354
2367
  }
2355
2368
  async removeScheduledCardReview(reviewId) {
2356
- return removeScheduledCardReviewLocal(this.remoteDB, reviewId);
2369
+ return removeScheduledCardReviewLocal(this.writeDB, reviewId);
2357
2370
  }
2358
2371
  async registerForClassroom(_classId, _registerAs) {
2359
2372
  return registerUserForClassroom(this._username, _classId, _registerAs);
@@ -2607,11 +2620,11 @@ var core_exports = {};
2607
2620
  __export(core_exports, {
2608
2621
  ContentNavigator: () => ContentNavigator,
2609
2622
  DocType: () => DocType,
2623
+ DocTypePrefixes: () => DocTypePrefixes,
2610
2624
  GuestUsername: () => GuestUsername,
2611
2625
  Loggable: () => Loggable,
2612
2626
  Navigators: () => Navigators,
2613
2627
  areQuestionRecords: () => areQuestionRecords,
2614
- cardHistoryPrefix: () => cardHistoryPrefix,
2615
2628
  docIsDeleted: () => docIsDeleted,
2616
2629
  getCardHistoryID: () => getCardHistoryID,
2617
2630
  getStudySource: () => getStudySource,
@@ -2639,11 +2652,11 @@ init_core();
2639
2652
  0 && (module.exports = {
2640
2653
  ContentNavigator,
2641
2654
  DocType,
2655
+ DocTypePrefixes,
2642
2656
  GuestUsername,
2643
2657
  Loggable,
2644
2658
  Navigators,
2645
2659
  areQuestionRecords,
2646
- cardHistoryPrefix,
2647
2660
  docIsDeleted,
2648
2661
  getCardHistoryID,
2649
2662
  getStudySource,