@vue-skuilder/db 0.1.7 → 0.1.8-0

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 (61) 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 +1365 -1252
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +1359 -1246
  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 +10 -55
  26. package/dist/index.d.ts +10 -55
  27. package/dist/index.js +343 -170
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +340 -167
  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/courseAPI.ts +7 -6
  53. package/src/impl/couch/index.ts +10 -5
  54. package/src/impl/couch/updateQueue.ts +12 -8
  55. package/src/impl/couch/user-course-relDB.ts +17 -27
  56. package/src/impl/static/NoOpSyncStrategy.ts +5 -0
  57. package/src/impl/static/StaticDataUnpacker.ts +18 -36
  58. package/src/impl/static/courseDB.ts +135 -17
  59. package/src/util/migrator/FileSystemAdapter.ts +20 -0
  60. package/src/util/migrator/StaticToCouchDBMigrator.ts +6 -0
  61. package/src/util/packer/CouchDBToStaticPacker.ts +92 -2
package/dist/index.js CHANGED
@@ -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";
@@ -111,20 +111,32 @@ var init_types_legacy = __esm({
111
111
  log = (message) => {
112
112
  logger.log(message);
113
113
  };
114
- DocType = /* @__PURE__ */ ((DocType2) => {
115
- DocType2["DISPLAYABLE_DATA"] = "DISPLAYABLE_DATA";
116
- DocType2["CARD"] = "CARD";
117
- DocType2["DATASHAPE"] = "DATASHAPE";
118
- DocType2["QUESTIONTYPE"] = "QUESTION";
119
- DocType2["VIEW"] = "VIEW";
120
- DocType2["PEDAGOGY"] = "PEDAGOGY";
121
- DocType2["CARDRECORD"] = "CARDRECORD";
122
- DocType2["SCHEDULED_CARD"] = "SCHEDULED_CARD";
123
- DocType2["TAG"] = "TAG";
124
- DocType2["NAVIGATION_STRATEGY"] = "NAVIGATION_STRATEGY";
125
- return DocType2;
114
+ DocType = /* @__PURE__ */ ((DocType3) => {
115
+ DocType3["DISPLAYABLE_DATA"] = "DISPLAYABLE_DATA";
116
+ DocType3["CARD"] = "CARD";
117
+ DocType3["DATASHAPE"] = "DATASHAPE";
118
+ DocType3["QUESTIONTYPE"] = "QUESTION";
119
+ DocType3["VIEW"] = "VIEW";
120
+ DocType3["PEDAGOGY"] = "PEDAGOGY";
121
+ DocType3["CARDRECORD"] = "CARDRECORD";
122
+ DocType3["SCHEDULED_CARD"] = "SCHEDULED_CARD";
123
+ DocType3["TAG"] = "TAG";
124
+ DocType3["NAVIGATION_STRATEGY"] = "NAVIGATION_STRATEGY";
125
+ return DocType3;
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]
@@ -363,11 +375,11 @@ function scheduleCardReviewLocal(userDB, review) {
363
375
  const now = import_moment.default.utc();
364
376
  logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
365
377
  void userDB.put({
366
- _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
378
+ _id: DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */] + review.time.format(REVIEW_TIME_FORMAT),
367
379
  cardId: review.card_id,
368
- reviewTime: review.time,
380
+ reviewTime: review.time.toISOString(),
369
381
  courseId: review.course_id,
370
- scheduledAt: now,
382
+ scheduledAt: now.toISOString(),
371
383
  scheduledFor: review.scheduledFor,
372
384
  schedulingAgentId: review.schedulingAgentId
373
385
  });
@@ -383,15 +395,15 @@ async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
383
395
  ${JSON.stringify(err)}`);
384
396
  });
385
397
  }
386
- var import_moment, REVIEW_PREFIX, REVIEW_TIME_FORMAT, log2;
398
+ var import_moment, REVIEW_TIME_FORMAT, log2;
387
399
  var init_userDBHelpers = __esm({
388
400
  "src/impl/common/userDBHelpers.ts"() {
389
401
  "use strict";
390
402
  import_moment = __toESM(require("moment"));
403
+ init_core();
391
404
  init_logger();
392
405
  init_pouchdb_setup();
393
406
  init_dataDirectory();
394
- REVIEW_PREFIX = "card_review_";
395
407
  REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
396
408
  log2 = (s) => {
397
409
  logger.info(s);
@@ -426,7 +438,10 @@ var init_updateQueue = __esm({
426
438
  _className = "UpdateQueue";
427
439
  pendingUpdates = {};
428
440
  inprogressUpdates = {};
429
- db;
441
+ readDB;
442
+ // Database for read operations
443
+ writeDB;
444
+ // Database for write operations (local-first)
430
445
  update(id, update) {
431
446
  logger.debug(`Update requested on doc: ${id}`);
432
447
  if (this.pendingUpdates[id]) {
@@ -436,24 +451,25 @@ var init_updateQueue = __esm({
436
451
  }
437
452
  return this.applyUpdates(id);
438
453
  }
439
- constructor(db) {
454
+ constructor(readDB, writeDB) {
440
455
  super();
441
- this.db = db;
456
+ this.readDB = readDB;
457
+ this.writeDB = writeDB || readDB;
442
458
  logger.debug(`UpdateQ initialized...`);
443
- void this.db.info().then((i) => {
459
+ void this.readDB.info().then((i) => {
444
460
  logger.debug(`db info: ${JSON.stringify(i)}`);
445
461
  });
446
462
  }
447
463
  async applyUpdates(id) {
448
464
  logger.debug(`Applying updates on doc: ${id}`);
449
465
  if (this.inprogressUpdates[id]) {
450
- await this.db.info();
466
+ await this.readDB.info();
451
467
  return this.applyUpdates(id);
452
468
  } else {
453
469
  if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
454
470
  this.inprogressUpdates[id] = true;
455
471
  try {
456
- let doc = await this.db.get(id);
472
+ let doc = await this.readDB.get(id);
457
473
  logger.debug(`Retrieved doc: ${id}`);
458
474
  while (this.pendingUpdates[id].length !== 0) {
459
475
  const update = this.pendingUpdates[id].splice(0, 1)[0];
@@ -466,7 +482,7 @@ var init_updateQueue = __esm({
466
482
  };
467
483
  }
468
484
  }
469
- await this.db.put(doc);
485
+ await this.writeDB.put(doc);
470
486
  logger.debug(`Put doc: ${id}`);
471
487
  if (this.pendingUpdates[id].length === 0) {
472
488
  this.inprogressUpdates[id] = false;
@@ -492,6 +508,60 @@ var init_updateQueue = __esm({
492
508
  }
493
509
  });
494
510
 
511
+ // src/impl/couch/user-course-relDB.ts
512
+ var import_moment2, UsrCrsData;
513
+ var init_user_course_relDB = __esm({
514
+ "src/impl/couch/user-course-relDB.ts"() {
515
+ "use strict";
516
+ import_moment2 = __toESM(require("moment"));
517
+ init_logger();
518
+ UsrCrsData = class {
519
+ user;
520
+ _courseId;
521
+ constructor(user, courseId) {
522
+ this.user = user;
523
+ this._courseId = courseId;
524
+ }
525
+ async getReviewsForcast(daysCount) {
526
+ const time = import_moment2.default.utc().add(daysCount, "days");
527
+ return this.getReviewstoDate(time);
528
+ }
529
+ async getPendingReviews() {
530
+ const now = import_moment2.default.utc();
531
+ return this.getReviewstoDate(now);
532
+ }
533
+ async getScheduledReviewCount() {
534
+ return (await this.getPendingReviews()).length;
535
+ }
536
+ async getCourseSettings() {
537
+ const regDoc = await this.user.getCourseRegistrationsDoc();
538
+ const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
539
+ if (crsDoc && crsDoc.settings) {
540
+ return crsDoc.settings;
541
+ } else {
542
+ logger.warn(`no settings found during lookup on course ${this._courseId}`);
543
+ return {};
544
+ }
545
+ }
546
+ updateCourseSettings(updates) {
547
+ if ("updateCourseSettings" in this.user) {
548
+ void this.user.updateCourseSettings(this._courseId, updates);
549
+ }
550
+ }
551
+ async getReviewstoDate(targetDate) {
552
+ const allReviews = await this.user.getPendingReviews(this._courseId);
553
+ logger.debug(
554
+ `Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
555
+ );
556
+ return allReviews.filter((review) => {
557
+ const reviewTime = import_moment2.default.utc(review.reviewTime);
558
+ return targetDate.isAfter(reviewTime);
559
+ });
560
+ }
561
+ };
562
+ }
563
+ });
564
+
495
565
  // src/impl/couch/clientCache.ts
496
566
  async function GET_CACHED(k, f) {
497
567
  if (CLIENT_CACHE[k]) {
@@ -515,7 +585,8 @@ var init_clientCache = __esm({
515
585
  async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = (0, import_common2.blankCourseElo)()) {
516
586
  const db = getCourseDB(courseID);
517
587
  const payload = (0, import_common3.prepareNote55)(courseID, codeCourse, shape, data, author, tags, uploads);
518
- const result = await db.post(payload);
588
+ const _id = `${DocTypePrefixes["DISPLAYABLE_DATA" /* DISPLAYABLE_DATA */]}-${(0, import_uuid.v4)()}`;
589
+ const result = await db.put({ ...payload, _id });
519
590
  const dataShapeId = import_common.NameSpacer.getDataShapeString({
520
591
  course: codeCourse,
521
592
  dataShape: shape.name
@@ -586,7 +657,9 @@ async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags
586
657
  }
587
658
  async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
588
659
  const db = getCourseDB(courseID);
589
- const card = await db.post({
660
+ const _id = `${DocTypePrefixes["CARD" /* CARD */]}-${(0, import_uuid.v4)()}`;
661
+ const card = await db.put({
662
+ _id,
590
663
  course,
591
664
  id_displayable_data,
592
665
  id_view,
@@ -679,7 +752,7 @@ function getCourseDB(courseID) {
679
752
  pouchDBincludeCredentialsConfig
680
753
  );
681
754
  }
682
- var import_common, import_common2, import_common3, AlreadyTaggedErr;
755
+ var import_common, import_common2, import_common3, import_uuid, AlreadyTaggedErr;
683
756
  var init_courseAPI = __esm({
684
757
  "src/impl/couch/courseAPI.ts"() {
685
758
  "use strict";
@@ -693,6 +766,7 @@ var init_courseAPI = __esm({
693
766
  import_common3 = require("@vue-skuilder/common");
694
767
  init_common();
695
768
  init_logger();
769
+ import_uuid = require("uuid");
696
770
  AlreadyTaggedErr = class extends Error {
697
771
  constructor(message) {
698
772
  super(message);
@@ -1493,13 +1567,13 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
1493
1567
  });
1494
1568
 
1495
1569
  // src/impl/couch/classroomDB.ts
1496
- var import_moment2, classroomLookupDBTitle, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB, TeacherClassroomDB, ClassroomLookupDB;
1570
+ var import_moment3, classroomLookupDBTitle, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB, TeacherClassroomDB, ClassroomLookupDB;
1497
1571
  var init_classroomDB2 = __esm({
1498
1572
  "src/impl/couch/classroomDB.ts"() {
1499
1573
  "use strict";
1500
1574
  init_factory();
1501
1575
  init_logger();
1502
- import_moment2 = __toESM(require("moment"));
1576
+ import_moment3 = __toESM(require("moment"));
1503
1577
  init_pouchdb_setup();
1504
1578
  init_couch();
1505
1579
  init_courseDB();
@@ -1594,9 +1668,9 @@ var init_classroomDB2 = __esm({
1594
1668
  }
1595
1669
  async getNewCards() {
1596
1670
  const activeCards = await this._user.getActiveCards();
1597
- const now = import_moment2.default.utc();
1671
+ const now = import_moment3.default.utc();
1598
1672
  const assigned = await this.getAssignedContent();
1599
- const due = assigned.filter((c) => now.isAfter(import_moment2.default.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
1673
+ const due = assigned.filter((c) => now.isAfter(import_moment3.default.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
1600
1674
  logger.info(`Due content: ${JSON.stringify(due)}`);
1601
1675
  let ret = [];
1602
1676
  for (let i = 0; i < due.length; i++) {
@@ -1687,8 +1761,8 @@ var init_classroomDB2 = __esm({
1687
1761
  type: "tag",
1688
1762
  _id: id,
1689
1763
  assignedBy: content.assignedBy,
1690
- assignedOn: import_moment2.default.utc(),
1691
- activeOn: content.activeOn || import_moment2.default.utc()
1764
+ assignedOn: import_moment3.default.utc(),
1765
+ activeOn: content.activeOn || import_moment3.default.utc()
1692
1766
  });
1693
1767
  } else {
1694
1768
  put = await this._db.put({
@@ -1696,8 +1770,8 @@ var init_classroomDB2 = __esm({
1696
1770
  type: "course",
1697
1771
  _id: id,
1698
1772
  assignedBy: content.assignedBy,
1699
- assignedOn: import_moment2.default.utc(),
1700
- activeOn: content.activeOn || import_moment2.default.utc()
1773
+ assignedOn: import_moment3.default.utc(),
1774
+ activeOn: content.activeOn || import_moment3.default.utc()
1701
1775
  });
1702
1776
  }
1703
1777
  if (put.ok) {
@@ -1860,6 +1934,13 @@ var init_CouchDBSyncStrategy = __esm({
1860
1934
  return this.getUserDB(username);
1861
1935
  }
1862
1936
  }
1937
+ getWriteDB(username) {
1938
+ if (username === GuestUsername || username.startsWith(GuestUsername)) {
1939
+ return getLocalUserDB(username);
1940
+ } else {
1941
+ return this.getUserDB(username);
1942
+ }
1943
+ }
1863
1944
  startSync(localDB, remoteDB) {
1864
1945
  if (localDB !== remoteDB) {
1865
1946
  this.syncHandle = pouchdb_setup_default.sync(localDB, remoteDB, {
@@ -2037,13 +2118,13 @@ function getStartAndEndKeys2(key) {
2037
2118
  endkey: key + "\uFFF0"
2038
2119
  };
2039
2120
  }
2040
- var import_moment3, import_process, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
2121
+ var import_moment4, import_process, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_TIME_FORMAT2;
2041
2122
  var init_couch = __esm({
2042
2123
  "src/impl/couch/index.ts"() {
2043
2124
  "use strict";
2044
2125
  init_factory();
2045
2126
  init_types_legacy();
2046
- import_moment3 = __toESM(require("moment"));
2127
+ import_moment4 = __toESM(require("moment"));
2047
2128
  init_logger();
2048
2129
  init_pouchdb_setup();
2049
2130
  import_process = __toESM(require("process"));
@@ -2065,78 +2146,10 @@ var init_couch = __esm({
2065
2146
  return pouchdb_setup_default.fetch(url, opts);
2066
2147
  }
2067
2148
  };
2068
- REVIEW_PREFIX2 = "card_review_";
2069
2149
  REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
2070
2150
  }
2071
2151
  });
2072
2152
 
2073
- // src/impl/couch/user-course-relDB.ts
2074
- var import_moment4, UsrCrsData;
2075
- var init_user_course_relDB = __esm({
2076
- "src/impl/couch/user-course-relDB.ts"() {
2077
- "use strict";
2078
- import_moment4 = __toESM(require("moment"));
2079
- init_couch();
2080
- init_courseDB();
2081
- init_logger();
2082
- UsrCrsData = class {
2083
- user;
2084
- course;
2085
- _courseId;
2086
- constructor(user, courseId) {
2087
- this.user = user;
2088
- this.course = new CourseDB(courseId, async () => this.user);
2089
- this._courseId = courseId;
2090
- }
2091
- async getReviewsForcast(daysCount) {
2092
- const time = import_moment4.default.utc().add(daysCount, "days");
2093
- return this.getReviewstoDate(time);
2094
- }
2095
- async getPendingReviews() {
2096
- const now = import_moment4.default.utc();
2097
- return this.getReviewstoDate(now);
2098
- }
2099
- async getScheduledReviewCount() {
2100
- return (await this.getPendingReviews()).length;
2101
- }
2102
- async getCourseSettings() {
2103
- const regDoc = await this.user.getCourseRegistrationsDoc();
2104
- const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
2105
- if (crsDoc && crsDoc.settings) {
2106
- return crsDoc.settings;
2107
- } else {
2108
- logger.warn(`no settings found during lookup on course ${this._courseId}`);
2109
- return {};
2110
- }
2111
- }
2112
- updateCourseSettings(updates) {
2113
- void this.user.updateCourseSettings(this._courseId, updates);
2114
- }
2115
- async getReviewstoDate(targetDate) {
2116
- const keys = getStartAndEndKeys2(REVIEW_PREFIX2);
2117
- const reviews = await this.user.remote().allDocs({
2118
- startkey: keys.startkey,
2119
- endkey: keys.endkey,
2120
- include_docs: true
2121
- });
2122
- logger.debug(
2123
- `Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
2124
- );
2125
- return reviews.rows.filter((r) => {
2126
- if (r.id.startsWith(REVIEW_PREFIX2)) {
2127
- const date = import_moment4.default.utc(r.id.substr(REVIEW_PREFIX2.length), REVIEW_TIME_FORMAT2);
2128
- if (targetDate.isAfter(date)) {
2129
- if (this._courseId === void 0 || r.doc.courseId === this._courseId) {
2130
- return true;
2131
- }
2132
- }
2133
- }
2134
- }).map((r) => r.doc);
2135
- }
2136
- };
2137
- }
2138
- });
2139
-
2140
2153
  // src/impl/common/BaseUserDB.ts
2141
2154
  async function getOrCreateClassroomRegistrationsDoc(user) {
2142
2155
  let ret;
@@ -2230,10 +2243,11 @@ async function dropUserFromClassroom(user, classID) {
2230
2243
  async function getUserClassrooms(user) {
2231
2244
  return getOrCreateClassroomRegistrationsDoc(user);
2232
2245
  }
2233
- var import_common8, import_moment5, log4, cardHistoryPrefix2, BaseUser, userCoursesDoc, userClassroomsDoc;
2246
+ var import_common8, import_moment5, log4, BaseUser, userCoursesDoc, userClassroomsDoc;
2234
2247
  var init_BaseUserDB = __esm({
2235
2248
  "src/impl/common/BaseUserDB.ts"() {
2236
2249
  "use strict";
2250
+ init_core();
2237
2251
  init_util();
2238
2252
  import_common8 = require("@vue-skuilder/common");
2239
2253
  import_moment5 = __toESM(require("moment"));
@@ -2246,7 +2260,6 @@ var init_BaseUserDB = __esm({
2246
2260
  log4 = (s) => {
2247
2261
  logger.info(s);
2248
2262
  };
2249
- cardHistoryPrefix2 = "cardH-";
2250
2263
  BaseUser = class _BaseUser {
2251
2264
  static _instance;
2252
2265
  static _initialized = false;
@@ -2267,11 +2280,13 @@ var init_BaseUserDB = __esm({
2267
2280
  isLoggedIn() {
2268
2281
  return !this._username.startsWith(GuestUsername);
2269
2282
  }
2270
- remoteDB;
2271
2283
  remote() {
2272
2284
  return this.remoteDB;
2273
2285
  }
2274
2286
  localDB;
2287
+ remoteDB;
2288
+ writeDB;
2289
+ // Database to use for write operations (local-first approach)
2275
2290
  updateQueue;
2276
2291
  async createAccount(username, password) {
2277
2292
  if (!this.syncStrategy.canCreateAccount()) {
@@ -2335,8 +2350,8 @@ Currently logged-in as ${this._username}.`
2335
2350
  const allDocs = await localDB.allDocs({ include_docs: false });
2336
2351
  const docsToDelete = allDocs.rows.filter((row) => {
2337
2352
  const id = row.id;
2338
- return id.startsWith(cardHistoryPrefix2) || // Card interaction history
2339
- id.startsWith(REVIEW_PREFIX) || // Scheduled reviews
2353
+ return id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */]) || // Card interaction history
2354
+ id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]) || // Scheduled reviews
2340
2355
  id === _BaseUser.DOC_IDS.COURSE_REGISTRATIONS || // Course registrations
2341
2356
  id === _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS || // Classroom registrations
2342
2357
  id === _BaseUser.DOC_IDS.CONFIG;
@@ -2405,7 +2420,7 @@ Currently logged-in as ${this._username}.`
2405
2420
  *
2406
2421
  */
2407
2422
  async getActiveCards() {
2408
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
2423
+ const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
2409
2424
  const reviews = await this.remoteDB.allDocs({
2410
2425
  startkey: keys.startkey,
2411
2426
  endkey: keys.endkey,
@@ -2477,7 +2492,7 @@ Currently logged-in as ${this._username}.`
2477
2492
  }
2478
2493
  }
2479
2494
  async getReviewstoDate(targetDate, course_id) {
2480
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
2495
+ const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
2481
2496
  const reviews = await this.remoteDB.allDocs({
2482
2497
  startkey: keys.startkey,
2483
2498
  endkey: keys.endkey,
@@ -2487,8 +2502,11 @@ Currently logged-in as ${this._username}.`
2487
2502
  `Fetching ${this._username}'s scheduled reviews${course_id ? ` for course ${course_id}` : ""}.`
2488
2503
  );
2489
2504
  return reviews.rows.filter((r) => {
2490
- if (r.id.startsWith(REVIEW_PREFIX)) {
2491
- const date = import_moment5.default.utc(r.id.substr(REVIEW_PREFIX.length), REVIEW_TIME_FORMAT);
2505
+ if (r.id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */])) {
2506
+ const date = import_moment5.default.utc(
2507
+ r.id.substr(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */].length),
2508
+ REVIEW_TIME_FORMAT
2509
+ );
2492
2510
  if (targetDate.isAfter(date)) {
2493
2511
  if (course_id === void 0 || r.doc.courseId === course_id) {
2494
2512
  return true;
@@ -2603,7 +2621,8 @@ Currently logged-in as ${this._username}.`
2603
2621
  const defaultConfig = {
2604
2622
  _id: _BaseUser.DOC_IDS.CONFIG,
2605
2623
  darkMode: false,
2606
- likesConfetti: false
2624
+ likesConfetti: false,
2625
+ sessionTimeLimit: 5
2607
2626
  };
2608
2627
  try {
2609
2628
  const cfg = await this.localDB.get(_BaseUser.DOC_IDS.CONFIG);
@@ -2676,7 +2695,8 @@ Currently logged-in as ${this._username}.`
2676
2695
  setDBandQ() {
2677
2696
  this.localDB = getLocalUserDB(this._username);
2678
2697
  this.remoteDB = this.syncStrategy.setupRemoteDB(this._username);
2679
- this.updateQueue = new UpdateQueue(this.localDB);
2698
+ this.writeDB = this.syncStrategy.getWriteDB ? this.syncStrategy.getWriteDB(this._username) : this.localDB;
2699
+ this.updateQueue = new UpdateQueue(this.localDB, this.writeDB);
2680
2700
  }
2681
2701
  async init() {
2682
2702
  _BaseUser._initialized = false;
@@ -2794,8 +2814,8 @@ Currently logged-in as ${this._username}.`
2794
2814
  streak: 0,
2795
2815
  bestInterval: 0
2796
2816
  };
2797
- void this.remoteDB.put(initCardHistory);
2798
- return initCardHistory;
2817
+ const putResult = await this.writeDB.put(initCardHistory);
2818
+ return { ...initCardHistory, _rev: putResult.rev };
2799
2819
  } else {
2800
2820
  throw new Error(`putCardRecord failed because of:
2801
2821
  name:${reason.name}
@@ -2830,7 +2850,7 @@ Currently logged-in as ${this._username}.`
2830
2850
  const deletePromises = duplicateDocIds.map(async (docId) => {
2831
2851
  try {
2832
2852
  const doc = await this.remoteDB.get(docId);
2833
- await this.remoteDB.remove(doc);
2853
+ await this.writeDB.remove(doc);
2834
2854
  log4(`Successfully removed duplicate review: ${docId}`);
2835
2855
  } catch (error) {
2836
2856
  log4(`Failed to remove duplicate review ${docId}: ${error}`);
@@ -2852,7 +2872,7 @@ Currently logged-in as ${this._username}.`
2852
2872
  * @param course_id optional specification of individual course
2853
2873
  */
2854
2874
  async getSeenCards(course_id) {
2855
- let prefix = cardHistoryPrefix2;
2875
+ let prefix = DocTypePrefixes["CARDRECORD" /* CARDRECORD */];
2856
2876
  if (course_id) {
2857
2877
  prefix += course_id;
2858
2878
  }
@@ -2861,8 +2881,8 @@ Currently logged-in as ${this._username}.`
2861
2881
  });
2862
2882
  const ret = [];
2863
2883
  docs.rows.forEach((row) => {
2864
- if (row.id.startsWith(cardHistoryPrefix2)) {
2865
- ret.push(row.id.substr(cardHistoryPrefix2.length));
2884
+ if (row.id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */])) {
2885
+ ret.push(row.id.substr(DocTypePrefixes["CARDRECORD" /* CARDRECORD */].length));
2866
2886
  }
2867
2887
  });
2868
2888
  return ret;
@@ -2874,7 +2894,7 @@ Currently logged-in as ${this._username}.`
2874
2894
  async getHistory() {
2875
2895
  const cards = await filterAllDocsByPrefix(
2876
2896
  this.remoteDB,
2877
- cardHistoryPrefix2,
2897
+ DocTypePrefixes["CARDRECORD" /* CARDRECORD */],
2878
2898
  {
2879
2899
  include_docs: true,
2880
2900
  attachments: false
@@ -2915,7 +2935,7 @@ Currently logged-in as ${this._username}.`
2915
2935
  } catch (e) {
2916
2936
  const err = e;
2917
2937
  if (err.status === 404) {
2918
- await this.remoteDB.put({
2938
+ await this.writeDB.put({
2919
2939
  _id: _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS,
2920
2940
  registrations: []
2921
2941
  });
@@ -2963,10 +2983,10 @@ Currently logged-in as ${this._username}.`
2963
2983
  }
2964
2984
  }
2965
2985
  async scheduleCardReview(review) {
2966
- return scheduleCardReviewLocal(this.remoteDB, review);
2986
+ return scheduleCardReviewLocal(this.writeDB, review);
2967
2987
  }
2968
2988
  async removeScheduledCardReview(reviewId) {
2969
- return removeScheduledCardReviewLocal(this.remoteDB, reviewId);
2989
+ return removeScheduledCardReviewLocal(this.writeDB, reviewId);
2970
2990
  }
2971
2991
  async registerForClassroom(_classId, _registerAs) {
2972
2992
  return registerUserForClassroom(this._username, _classId, _registerAs);
@@ -3178,18 +3198,21 @@ var init_StaticDataUnpacker = __esm({
3178
3198
  async getTagsIndex() {
3179
3199
  return await this.loadIndex("tags");
3180
3200
  }
3201
+ getDocTypeFromId(id) {
3202
+ for (const docTypeKey in DocTypePrefixes) {
3203
+ const prefix = DocTypePrefixes[docTypeKey];
3204
+ if (id.startsWith(`${prefix}-`)) {
3205
+ return docTypeKey;
3206
+ }
3207
+ }
3208
+ return void 0;
3209
+ }
3181
3210
  /**
3182
3211
  * Find which chunk contains a specific document ID
3183
3212
  */
3184
3213
  async findChunkForDocument(docId) {
3185
- let expectedDocType = void 0;
3186
- for (const docType of Object.values(DocType)) {
3187
- if (docId.startsWith(`${docType}-`)) {
3188
- expectedDocType = docType;
3189
- break;
3190
- }
3191
- }
3192
- if (expectedDocType !== void 0) {
3214
+ const expectedDocType = this.getDocTypeFromId(docId);
3215
+ if (expectedDocType) {
3193
3216
  const typeChunks = this.manifest.chunks.filter((c) => c.docType === expectedDocType);
3194
3217
  for (const chunk of typeChunks) {
3195
3218
  if (docId >= chunk.startKey && docId <= chunk.endKey) {
@@ -3199,21 +3222,8 @@ var init_StaticDataUnpacker = __esm({
3199
3222
  }
3200
3223
  }
3201
3224
  }
3202
- return void 0;
3203
3225
  } else {
3204
- const displayableChunks = this.manifest.chunks.filter(
3205
- (c) => c.docType === "DISPLAYABLE_DATA"
3206
- );
3207
- for (const chunk of displayableChunks) {
3208
- if (docId >= chunk.startKey && docId <= chunk.endKey) {
3209
- const exists = await this.verifyDocumentInChunk(docId, chunk);
3210
- if (exists) {
3211
- return chunk;
3212
- }
3213
- }
3214
- }
3215
- const cardChunks = this.manifest.chunks.filter((c) => c.docType === "CARD");
3216
- for (const chunk of cardChunks) {
3226
+ for (const chunk of this.manifest.chunks) {
3217
3227
  if (docId >= chunk.startKey && docId <= chunk.endKey) {
3218
3228
  const exists = await this.verifyDocumentInChunk(docId, chunk);
3219
3229
  if (exists) {
@@ -3234,6 +3244,7 @@ var init_StaticDataUnpacker = __esm({
3234
3244
  }
3235
3245
  return void 0;
3236
3246
  }
3247
+ return void 0;
3237
3248
  }
3238
3249
  /**
3239
3250
  * Verify that a document actually exists in a specific chunk by loading and checking it
@@ -3477,6 +3488,7 @@ var init_courseDB2 = __esm({
3477
3488
  import_common10 = require("@vue-skuilder/common");
3478
3489
  init_types_legacy();
3479
3490
  init_navigators();
3491
+ init_logger();
3480
3492
  StaticCourseDB = class {
3481
3493
  constructor(courseId, unpacker, userDB, manifest) {
3482
3494
  this.courseId = courseId;
@@ -3498,10 +3510,11 @@ var init_courseDB2 = __esm({
3498
3510
  throw new Error("Cannot update course config in static mode");
3499
3511
  }
3500
3512
  async getCourseInfo() {
3513
+ const cardCount = this.manifest.chunks.filter((chunk) => chunk.docType === "CARD" /* CARD */).reduce((total, chunk) => total + chunk.documentCount, 0);
3501
3514
  return {
3502
- cardCount: 0,
3503
- // Would come from manifest
3515
+ cardCount,
3504
3516
  registeredUsers: 0
3517
+ // Always 0 in static mode
3505
3518
  };
3506
3519
  }
3507
3520
  async getCourseDoc(id, _options) {
@@ -3590,12 +3603,56 @@ var init_courseDB2 = __esm({
3590
3603
  courseID: this.courseId
3591
3604
  }));
3592
3605
  }
3593
- async getAppliedTags(_cardId) {
3594
- return {
3595
- total_rows: 0,
3596
- offset: 0,
3597
- rows: []
3598
- };
3606
+ async getAppliedTags(cardId) {
3607
+ try {
3608
+ const tagsIndex = await this.unpacker.getTagsIndex();
3609
+ const cardTags = tagsIndex.byCard[cardId] || [];
3610
+ const rows = await Promise.all(
3611
+ cardTags.map(async (tagName) => {
3612
+ const tagId = `${"TAG" /* TAG */}-${tagName}`;
3613
+ try {
3614
+ const tagDoc = await this.unpacker.getDocument(tagId);
3615
+ return {
3616
+ id: tagId,
3617
+ key: cardId,
3618
+ value: {
3619
+ name: tagDoc.name,
3620
+ snippet: tagDoc.snippet,
3621
+ count: tagDoc.taggedCards?.length || 0
3622
+ }
3623
+ };
3624
+ } catch (error) {
3625
+ if (error && error.status === 404) {
3626
+ logger.warn(`Tag document not found for ${tagName}, creating stub`);
3627
+ } else {
3628
+ logger.error(`Error getting tag document for ${tagName}:`, error);
3629
+ throw error;
3630
+ }
3631
+ return {
3632
+ id: tagId,
3633
+ key: cardId,
3634
+ value: {
3635
+ name: tagName,
3636
+ snippet: `Tag: ${tagName}`,
3637
+ count: tagsIndex.byTag[tagName]?.length || 0
3638
+ }
3639
+ };
3640
+ }
3641
+ })
3642
+ );
3643
+ return {
3644
+ total_rows: rows.length,
3645
+ offset: 0,
3646
+ rows
3647
+ };
3648
+ } catch (error) {
3649
+ logger.error(`Error getting applied tags for card ${cardId}:`, error);
3650
+ return {
3651
+ total_rows: 0,
3652
+ offset: 0,
3653
+ rows: []
3654
+ };
3655
+ }
3599
3656
  }
3600
3657
  async addTagToCard(_cardId, _tagId) {
3601
3658
  throw new Error("Cannot modify tags in static mode");
@@ -3613,11 +3670,69 @@ var init_courseDB2 = __esm({
3613
3670
  throw new Error("Cannot update tags in static mode");
3614
3671
  }
3615
3672
  async getCourseTagStubs() {
3616
- return {
3617
- total_rows: 0,
3618
- offset: 0,
3619
- rows: []
3620
- };
3673
+ try {
3674
+ const tagsIndex = await this.unpacker.getTagsIndex();
3675
+ if (!tagsIndex || !tagsIndex.byTag) {
3676
+ logger.warn("Tags index not found or empty");
3677
+ return {
3678
+ total_rows: 0,
3679
+ offset: 0,
3680
+ rows: []
3681
+ };
3682
+ }
3683
+ const tagNames = Object.keys(tagsIndex.byTag);
3684
+ const rows = await Promise.all(
3685
+ tagNames.map(async (tagName) => {
3686
+ const cardIds = tagsIndex.byTag[tagName] || [];
3687
+ const tagId = `${"TAG" /* TAG */}-${tagName}`;
3688
+ try {
3689
+ const tagDoc = await this.unpacker.getDocument(tagId);
3690
+ return {
3691
+ id: tagId,
3692
+ key: tagId,
3693
+ value: { rev: "1-static" },
3694
+ doc: tagDoc
3695
+ };
3696
+ } catch (error) {
3697
+ if (error && error.status === 404) {
3698
+ logger.warn(`Tag document not found for ${tagName}, creating stub`);
3699
+ const stubDoc = {
3700
+ _id: tagId,
3701
+ _rev: "1-static",
3702
+ course: this.courseId,
3703
+ docType: "TAG" /* TAG */,
3704
+ name: tagName,
3705
+ snippet: `Tag: ${tagName}`,
3706
+ wiki: "",
3707
+ taggedCards: cardIds,
3708
+ author: "system"
3709
+ };
3710
+ return {
3711
+ id: tagId,
3712
+ key: tagId,
3713
+ value: { rev: "1-static" },
3714
+ doc: stubDoc
3715
+ };
3716
+ } else {
3717
+ logger.error(`Error getting tag document for ${tagName}:`, error);
3718
+ throw error;
3719
+ }
3720
+ }
3721
+ })
3722
+ );
3723
+ return {
3724
+ total_rows: rows.length,
3725
+ offset: 0,
3726
+ rows
3727
+ };
3728
+ } catch (error) {
3729
+ logger.error("Failed to get course tag stubs:", error);
3730
+ return {
3731
+ total_rows: 0,
3732
+ offset: 0,
3733
+ rows: []
3734
+ };
3735
+ }
3621
3736
  }
3622
3737
  async addNote(_codeCourse, _shape, _data, _author, _tags, _uploads, _elo) {
3623
3738
  return {
@@ -3723,6 +3838,9 @@ var init_NoOpSyncStrategy = __esm({
3723
3838
  setupRemoteDB(username) {
3724
3839
  return getLocalUserDB(username);
3725
3840
  }
3841
+ getWriteDB(username) {
3842
+ return getLocalUserDB(username);
3843
+ }
3726
3844
  startSync(_localDB, _remoteDB) {
3727
3845
  }
3728
3846
  stopSync() {
@@ -4117,6 +4235,7 @@ __export(index_exports, {
4117
4235
  CouchDBToStaticPacker: () => CouchDBToStaticPacker,
4118
4236
  CourseLookup: () => CourseLookup,
4119
4237
  DocType: () => DocType,
4238
+ DocTypePrefixes: () => DocTypePrefixes,
4120
4239
  ENV: () => ENV,
4121
4240
  FileSystemError: () => FileSystemError,
4122
4241
  GuestUsername: () => GuestUsername,
@@ -4126,7 +4245,6 @@ __export(index_exports, {
4126
4245
  StaticToCouchDBMigrator: () => StaticToCouchDBMigrator,
4127
4246
  _resetDataLayer: () => _resetDataLayer,
4128
4247
  areQuestionRecords: () => areQuestionRecords,
4129
- cardHistoryPrefix: () => cardHistoryPrefix,
4130
4248
  docIsDeleted: () => docIsDeleted,
4131
4249
  ensureAppDataDirectory: () => ensureAppDataDirectory,
4132
4250
  getAppDataDirectory: () => getAppDataDirectory,
@@ -4216,6 +4334,57 @@ var CouchDBToStaticPacker = class {
4216
4334
  attachments
4217
4335
  };
4218
4336
  }
4337
+ /**
4338
+ * Pack a CouchDB course database and write the static files to disk
4339
+ */
4340
+ async packCourseToFiles(sourceDB, courseId, outputDir, fsAdapter) {
4341
+ logger.info(`Packing course ${courseId} to files in ${outputDir}`);
4342
+ const packedData = await this.packCourse(sourceDB, courseId);
4343
+ const filesWritten = await this.writePackedDataToFiles(packedData, outputDir, fsAdapter);
4344
+ return {
4345
+ manifest: packedData.manifest,
4346
+ filesWritten,
4347
+ attachmentsFound: packedData.attachments ? packedData.attachments.size : 0
4348
+ };
4349
+ }
4350
+ /**
4351
+ * Write packed course data to files using FileSystemAdapter
4352
+ */
4353
+ async writePackedDataToFiles(packedData, outputDir, fsAdapter) {
4354
+ let totalFiles = 0;
4355
+ await fsAdapter.ensureDir(outputDir);
4356
+ const manifestPath = fsAdapter.joinPath(outputDir, "manifest.json");
4357
+ await fsAdapter.writeJson(manifestPath, packedData.manifest, { spaces: 2 });
4358
+ totalFiles++;
4359
+ logger.info(`Wrote manifest: ${manifestPath}`);
4360
+ const chunksDir = fsAdapter.joinPath(outputDir, "chunks");
4361
+ const indicesDir = fsAdapter.joinPath(outputDir, "indices");
4362
+ await fsAdapter.ensureDir(chunksDir);
4363
+ await fsAdapter.ensureDir(indicesDir);
4364
+ for (const [chunkId, chunkData] of packedData.chunks) {
4365
+ const chunkPath = fsAdapter.joinPath(chunksDir, `${chunkId}.json`);
4366
+ await fsAdapter.writeJson(chunkPath, chunkData);
4367
+ totalFiles++;
4368
+ }
4369
+ logger.info(`Wrote ${packedData.chunks.size} chunk files`);
4370
+ for (const [indexName, indexData] of packedData.indices) {
4371
+ const indexPath = fsAdapter.joinPath(indicesDir, `${indexName}.json`);
4372
+ await fsAdapter.writeJson(indexPath, indexData, { spaces: 2 });
4373
+ totalFiles++;
4374
+ }
4375
+ logger.info(`Wrote ${packedData.indices.size} index files`);
4376
+ if (packedData.attachments && packedData.attachments.size > 0) {
4377
+ for (const [attachmentPath, attachmentData] of packedData.attachments) {
4378
+ const fullAttachmentPath = fsAdapter.joinPath(outputDir, attachmentPath);
4379
+ const attachmentDir = fsAdapter.dirname(fullAttachmentPath);
4380
+ await fsAdapter.ensureDir(attachmentDir);
4381
+ await fsAdapter.writeFile(fullAttachmentPath, attachmentData.buffer);
4382
+ totalFiles++;
4383
+ }
4384
+ logger.info(`Wrote ${packedData.attachments.size} attachment files`);
4385
+ }
4386
+ return totalFiles;
4387
+ }
4219
4388
  async extractCourseConfig(db) {
4220
4389
  try {
4221
4390
  return await db.get("CourseConfig");
@@ -4384,7 +4553,8 @@ var CouchDBToStaticPacker = class {
4384
4553
  }
4385
4554
  try {
4386
4555
  const designDocId = designDoc._id;
4387
- const viewPath = `${designDocId}/${viewName}`;
4556
+ const designDocName = designDocId.replace("_design/", "");
4557
+ const viewPath = `${designDocName}/${viewName}`;
4388
4558
  logger.info(`Querying CouchDB view: ${viewPath}`);
4389
4559
  const viewResults = await this.sourceDB.query(viewPath, {
4390
4560
  include_docs: false
@@ -5309,6 +5479,7 @@ var StaticToCouchDBMigrator = class {
5309
5479
  const docsToInsert = batch.map((doc) => {
5310
5480
  const cleanDoc = { ...doc };
5311
5481
  delete cleanDoc._rev;
5482
+ delete cleanDoc._attachments;
5312
5483
  return cleanDoc;
5313
5484
  });
5314
5485
  const bulkResult = await db.bulkDocs(docsToInsert);
@@ -5422,9 +5593,11 @@ var StaticToCouchDBMigrator = class {
5422
5593
  attachmentData = await response.arrayBuffer();
5423
5594
  }
5424
5595
  }
5596
+ const doc = await db.get(docId);
5425
5597
  await db.putAttachment(
5426
5598
  docId,
5427
5599
  attachmentName,
5600
+ doc._rev,
5428
5601
  attachmentData,
5429
5602
  // PouchDB accepts both ArrayBuffer and Buffer
5430
5603
  attachmentMeta.content_type
@@ -5893,6 +6066,7 @@ init_factory();
5893
6066
  CouchDBToStaticPacker,
5894
6067
  CourseLookup,
5895
6068
  DocType,
6069
+ DocTypePrefixes,
5896
6070
  ENV,
5897
6071
  FileSystemError,
5898
6072
  GuestUsername,
@@ -5902,7 +6076,6 @@ init_factory();
5902
6076
  StaticToCouchDBMigrator,
5903
6077
  _resetDataLayer,
5904
6078
  areQuestionRecords,
5905
- cardHistoryPrefix,
5906
6079
  docIsDeleted,
5907
6080
  ensureAppDataDirectory,
5908
6081
  getAppDataDirectory,