@vue-skuilder/db 0.1.6 → 0.1.7

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 (48) hide show
  1. package/dist/core/index.d.mts +5 -5
  2. package/dist/core/index.d.ts +5 -5
  3. package/dist/core/index.js +717 -667
  4. package/dist/core/index.js.map +1 -1
  5. package/dist/core/index.mjs +702 -653
  6. package/dist/core/index.mjs.map +1 -1
  7. package/dist/{dataLayerProvider-BuntXkCs.d.ts → dataLayerProvider-6stCgDME.d.ts} +1 -1
  8. package/dist/{dataLayerProvider-BZmLyBVw.d.mts → dataLayerProvider-BbW9EnZK.d.mts} +1 -1
  9. package/dist/impl/couch/index.d.mts +3 -3
  10. package/dist/impl/couch/index.d.ts +3 -3
  11. package/dist/impl/couch/index.js +1940 -1873
  12. package/dist/impl/couch/index.js.map +1 -1
  13. package/dist/impl/couch/index.mjs +1894 -1828
  14. package/dist/impl/couch/index.mjs.map +1 -1
  15. package/dist/impl/static/index.d.mts +4 -4
  16. package/dist/impl/static/index.d.ts +4 -4
  17. package/dist/impl/static/index.js +557 -507
  18. package/dist/impl/static/index.js.map +1 -1
  19. package/dist/impl/static/index.mjs +575 -526
  20. package/dist/impl/static/index.mjs.map +1 -1
  21. package/dist/index.d.mts +244 -8
  22. package/dist/index.d.ts +244 -8
  23. package/dist/index.js +3922 -2792
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +3897 -2781
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/{types-D6SnlHPm.d.ts → types-BvzcRAys.d.ts} +1 -1
  28. package/dist/{types-DPRvCrIk.d.mts → types-CQQ80R5N.d.mts} +1 -1
  29. package/dist/{types-legacy-WPe8CtO-.d.mts → types-legacy-CtrmkOLu.d.mts} +1 -1
  30. package/dist/{types-legacy-WPe8CtO-.d.ts → types-legacy-CtrmkOLu.d.ts} +1 -1
  31. package/dist/{userDB-31gsvxyd.d.mts → userDB-7fM4tpgr.d.mts} +2 -2
  32. package/dist/{userDB-D9EuWTp1.d.ts → userDB-DUY63VMN.d.ts} +2 -2
  33. package/dist/util/packer/index.d.mts +3 -3
  34. package/dist/util/packer/index.d.ts +3 -3
  35. package/package.json +2 -2
  36. package/src/factory.ts +25 -0
  37. package/src/impl/common/BaseUserDB.ts +29 -6
  38. package/src/impl/common/userDBHelpers.ts +11 -1
  39. package/src/impl/couch/courseLookupDB.ts +24 -0
  40. package/src/util/dataDirectory.test.ts +53 -0
  41. package/src/util/dataDirectory.ts +52 -0
  42. package/src/util/index.ts +3 -0
  43. package/src/util/migrator/FileSystemAdapter.ts +59 -0
  44. package/src/util/migrator/StaticToCouchDBMigrator.ts +707 -0
  45. package/src/util/migrator/index.ts +18 -0
  46. package/src/util/migrator/types.ts +84 -0
  47. package/src/util/migrator/validation.ts +517 -0
  48. package/src/util/tuiLogger.ts +139 -0
@@ -1,9 +1,9 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __glob = (map) => (path) => {
4
- var fn = map[path];
3
+ var __glob = (map) => (path2) => {
4
+ var fn = map[path2];
5
5
  if (fn) return fn();
6
- throw new Error("Module not found in bundle: " + path);
6
+ throw new Error("Module not found in bundle: " + path2);
7
7
  };
8
8
  var __esm = (fn, res) => function __init() {
9
9
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -72,35 +72,10 @@ var init_classroomDB = __esm({
72
72
  }
73
73
  });
74
74
 
75
- // src/factory.ts
76
- var ENV;
77
- var init_factory = __esm({
78
- "src/factory.ts"() {
79
- "use strict";
80
- init_logger();
81
- ENV = {
82
- COUCHDB_SERVER_PROTOCOL: "NOT_SET",
83
- COUCHDB_SERVER_URL: "NOT_SET"
84
- };
85
- }
86
- });
87
-
88
- // src/impl/couch/pouchdb-setup.ts
89
- import PouchDB from "pouchdb";
90
- import PouchDBFind from "pouchdb-find";
91
- import PouchDBAuth from "@nilock2/pouchdb-authentication";
92
- var pouchdb_setup_default;
93
- var init_pouchdb_setup = __esm({
94
- "src/impl/couch/pouchdb-setup.ts"() {
75
+ // src/impl/common/SyncStrategy.ts
76
+ var init_SyncStrategy = __esm({
77
+ "src/impl/common/SyncStrategy.ts"() {
95
78
  "use strict";
96
- PouchDB.plugin(PouchDBFind);
97
- PouchDB.plugin(PouchDBAuth);
98
- PouchDB.defaults({
99
- ajax: {
100
- timeout: 6e4
101
- }
102
- });
103
- pouchdb_setup_default = PouchDB;
104
79
  }
105
80
  });
106
81
 
@@ -128,27 +103,123 @@ var init_types_legacy = __esm({
128
103
  }
129
104
  });
130
105
 
131
- // src/impl/couch/courseLookupDB.ts
132
- var init_courseLookupDB = __esm({
133
- "src/impl/couch/courseLookupDB.ts"() {
106
+ // src/core/util/index.ts
107
+ function getCardHistoryID(courseID, cardID) {
108
+ return `${cardHistoryPrefix}-${courseID}-${cardID}`;
109
+ }
110
+ var init_util = __esm({
111
+ "src/core/util/index.ts"() {
134
112
  "use strict";
135
- init_pouchdb_setup();
136
- init_factory();
137
- init_logger();
138
- logger.debug(`COURSELOOKUP FILE RUNNING`);
113
+ init_types_legacy();
139
114
  }
140
115
  });
141
116
 
142
- // src/impl/couch/adminDB.ts
143
- var init_adminDB2 = __esm({
144
- "src/impl/couch/adminDB.ts"() {
117
+ // src/impl/couch/pouchdb-setup.ts
118
+ import PouchDB from "pouchdb";
119
+ import PouchDBFind from "pouchdb-find";
120
+ import PouchDBAuth from "@nilock2/pouchdb-authentication";
121
+ var pouchdb_setup_default;
122
+ var init_pouchdb_setup = __esm({
123
+ "src/impl/couch/pouchdb-setup.ts"() {
124
+ "use strict";
125
+ PouchDB.plugin(PouchDBFind);
126
+ PouchDB.plugin(PouchDBAuth);
127
+ PouchDB.defaults({
128
+ ajax: {
129
+ timeout: 6e4
130
+ }
131
+ });
132
+ pouchdb_setup_default = PouchDB;
133
+ }
134
+ });
135
+
136
+ // src/util/tuiLogger.ts
137
+ var init_tuiLogger = __esm({
138
+ "src/util/tuiLogger.ts"() {
139
+ "use strict";
140
+ init_dataDirectory();
141
+ }
142
+ });
143
+
144
+ // src/util/dataDirectory.ts
145
+ import * as path from "path";
146
+ import * as os from "os";
147
+ function getAppDataDirectory() {
148
+ return path.join(os.homedir(), ".tuilder");
149
+ }
150
+ function getDbPath(dbName) {
151
+ return path.join(getAppDataDirectory(), dbName);
152
+ }
153
+ var init_dataDirectory = __esm({
154
+ "src/util/dataDirectory.ts"() {
155
+ "use strict";
156
+ init_tuiLogger();
157
+ }
158
+ });
159
+
160
+ // src/impl/common/userDBHelpers.ts
161
+ import moment from "moment";
162
+ function filterAllDocsByPrefix(db, prefix, opts) {
163
+ const options = {
164
+ startkey: prefix,
165
+ endkey: prefix + "\uFFF0",
166
+ include_docs: true
167
+ };
168
+ if (opts) {
169
+ Object.assign(options, opts);
170
+ }
171
+ return db.allDocs(options);
172
+ }
173
+ function getStartAndEndKeys(key) {
174
+ return {
175
+ startkey: key,
176
+ endkey: key + "\uFFF0"
177
+ };
178
+ }
179
+ function getLocalUserDB(username) {
180
+ const dbName = `userdb-${username}`;
181
+ if (typeof window === "undefined") {
182
+ return new pouchdb_setup_default(getDbPath(dbName), {});
183
+ } else {
184
+ return new pouchdb_setup_default(dbName, {});
185
+ }
186
+ }
187
+ function scheduleCardReviewLocal(userDB, review) {
188
+ const now = moment.utc();
189
+ logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
190
+ void userDB.put({
191
+ _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
192
+ cardId: review.card_id,
193
+ reviewTime: review.time,
194
+ courseId: review.course_id,
195
+ scheduledAt: now,
196
+ scheduledFor: review.scheduledFor,
197
+ schedulingAgentId: review.schedulingAgentId
198
+ });
199
+ }
200
+ async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
201
+ const reviewDoc = await userDB.get(reviewDocID);
202
+ userDB.remove(reviewDoc).then((res) => {
203
+ if (res.ok) {
204
+ log(`Removed Review Doc: ${reviewDocID}`);
205
+ }
206
+ }).catch((err) => {
207
+ log(`Failed to remove Review Doc: ${reviewDocID},
208
+ ${JSON.stringify(err)}`);
209
+ });
210
+ }
211
+ var REVIEW_PREFIX, REVIEW_TIME_FORMAT, log;
212
+ var init_userDBHelpers = __esm({
213
+ "src/impl/common/userDBHelpers.ts"() {
145
214
  "use strict";
146
- init_pouchdb_setup();
147
- init_factory();
148
- init_couch();
149
- init_classroomDB2();
150
- init_courseLookupDB();
151
215
  init_logger();
216
+ init_pouchdb_setup();
217
+ init_dataDirectory();
218
+ REVIEW_PREFIX = "card_review_";
219
+ REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
220
+ log = (s) => {
221
+ logger.info(s);
222
+ };
152
223
  }
153
224
  });
154
225
 
@@ -264,89 +335,291 @@ var init_clientCache = __esm({
264
335
  }
265
336
  });
266
337
 
267
- // src/core/navigators/elo.ts
268
- var elo_exports = {};
269
- __export(elo_exports, {
270
- default: () => ELONavigator
271
- });
272
- var ELONavigator;
273
- var init_elo = __esm({
274
- "src/core/navigators/elo.ts"() {
275
- "use strict";
276
- init_navigators();
277
- ELONavigator = class extends ContentNavigator {
278
- user;
279
- course;
280
- constructor(user, course) {
281
- super();
282
- this.user = user;
283
- this.course = course;
284
- }
285
- async getPendingReviews() {
286
- const reviews = await this.user.getPendingReviews(this.course.getCourseID());
287
- const elo = await this.course.getCardEloData(reviews.map((r) => r.cardId));
288
- const ratedReviews = reviews.map((r, i) => {
289
- const ratedR = {
290
- ...r,
291
- ...elo[i]
292
- };
293
- return ratedR;
294
- });
295
- ratedReviews.sort((a, b) => {
296
- return a.global.score - b.global.score;
297
- });
298
- return ratedReviews.map((r) => {
299
- return {
300
- ...r,
301
- contentSourceType: "course",
302
- contentSourceID: this.course.getCourseID(),
303
- cardID: r.cardId,
304
- courseID: r.courseId,
305
- qualifiedID: `${r.courseId}-${r.cardId}`,
306
- reviewID: r._id,
307
- status: "review"
308
- };
309
- });
310
- }
311
- async getNewCards(limit = 99) {
312
- const activeCards = await this.user.getActiveCards();
313
- return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
314
- if (activeCards.some((ac) => c.includes(ac))) {
315
- return false;
316
- } else {
317
- return true;
318
- }
319
- })).map((c) => {
320
- return {
321
- ...c,
322
- status: "new"
323
- };
324
- });
338
+ // src/impl/couch/courseAPI.ts
339
+ import { NameSpacer } from "@vue-skuilder/common";
340
+ import { blankCourseElo, toCourseElo } from "@vue-skuilder/common";
341
+ import { prepareNote55 } from "@vue-skuilder/common";
342
+ async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo()) {
343
+ const db = getCourseDB(courseID);
344
+ const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
345
+ const result = await db.post(payload);
346
+ const dataShapeId = NameSpacer.getDataShapeString({
347
+ course: codeCourse,
348
+ dataShape: shape.name
349
+ });
350
+ if (result.ok) {
351
+ try {
352
+ await createCards(courseID, dataShapeId, result.id, tags, elo, author);
353
+ } catch (error) {
354
+ let errorMessage = "Unknown error";
355
+ if (error instanceof Error) {
356
+ errorMessage = error.message;
357
+ } else if (error && typeof error === "object" && "reason" in error) {
358
+ errorMessage = error.reason;
359
+ } else if (error && typeof error === "object" && "message" in error) {
360
+ errorMessage = error.message;
361
+ } else {
362
+ errorMessage = String(error);
325
363
  }
326
- };
364
+ logger.error(`[addNote55] Failed to create cards for note ${result.id}: ${errorMessage}`);
365
+ result.cardCreationFailed = true;
366
+ result.cardCreationError = errorMessage;
367
+ }
368
+ } else {
369
+ logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
327
370
  }
328
- });
329
-
330
- // import("./**/*") in src/core/navigators/index.ts
331
- var globImport;
332
- var init_ = __esm({
333
- 'import("./**/*") in src/core/navigators/index.ts'() {
334
- globImport = __glob({
335
- "./elo.ts": () => Promise.resolve().then(() => (init_elo(), elo_exports)),
336
- "./index.ts": () => Promise.resolve().then(() => (init_navigators(), navigators_exports))
337
- });
371
+ return result;
372
+ }
373
+ async function createCards(courseID, datashapeID, noteID, tags, elo = blankCourseElo(), author) {
374
+ const cfg = await getCredentialledCourseConfig(courseID);
375
+ const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
376
+ let questionViewTypes = [];
377
+ for (const ds of cfg.dataShapes) {
378
+ if (ds.name === datashapeID) {
379
+ questionViewTypes = ds.questionTypes;
380
+ }
338
381
  }
339
- });
340
-
341
- // src/core/navigators/index.ts
342
- var navigators_exports = {};
343
- __export(navigators_exports, {
344
- ContentNavigator: () => ContentNavigator,
345
- Navigators: () => Navigators
346
- });
347
- var Navigators, ContentNavigator;
348
- var init_navigators = __esm({
349
- "src/core/navigators/index.ts"() {
382
+ if (questionViewTypes.length === 0) {
383
+ const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
384
+ logger.error(errorMsg);
385
+ throw new Error(errorMsg);
386
+ }
387
+ for (const questionView of questionViewTypes) {
388
+ await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo, author);
389
+ }
390
+ }
391
+ async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags, elo = blankCourseElo(), author) {
392
+ const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
393
+ const cfg = await getCredentialledCourseConfig(courseID);
394
+ for (const rQ of cfg.questionTypes) {
395
+ if (rQ.name === questionViewName) {
396
+ for (const view of rQ.viewList) {
397
+ await addCard(
398
+ courseID,
399
+ dsDescriptor.course,
400
+ [noteID],
401
+ NameSpacer.getViewString({
402
+ course: qDescriptor.course,
403
+ questionType: qDescriptor.questionType,
404
+ view
405
+ }),
406
+ elo,
407
+ tags,
408
+ author
409
+ );
410
+ }
411
+ }
412
+ }
413
+ }
414
+ async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
415
+ const db = getCourseDB(courseID);
416
+ const card = await db.post({
417
+ course,
418
+ id_displayable_data,
419
+ id_view,
420
+ docType: "CARD" /* CARD */,
421
+ elo: elo || toCourseElo(990 + Math.round(20 * Math.random())),
422
+ author
423
+ });
424
+ for (const tag of tags) {
425
+ logger.info(`adding tag: ${tag} to card ${card.id}`);
426
+ await addTagToCard(courseID, card.id, tag, author, false);
427
+ }
428
+ return card;
429
+ }
430
+ async function getCredentialledCourseConfig(courseID) {
431
+ try {
432
+ const db = getCourseDB(courseID);
433
+ const ret = await db.get("CourseConfig");
434
+ ret.courseID = courseID;
435
+ logger.info(`Returning course config: ${JSON.stringify(ret)}`);
436
+ return ret;
437
+ } catch (e) {
438
+ logger.error(`Error fetching config for ${courseID}:`, e);
439
+ throw e;
440
+ }
441
+ }
442
+ async function addTagToCard(courseID, cardID, tagID, author, updateELO = true) {
443
+ const prefixedTagID = getTagID(tagID);
444
+ const courseDB = getCourseDB(courseID);
445
+ const courseApi = new CourseDB(courseID, async () => {
446
+ const dummySyncStrategy = {
447
+ setupRemoteDB: () => null,
448
+ startSync: () => {
449
+ },
450
+ canCreateAccount: () => false,
451
+ canAuthenticate: () => false,
452
+ getCurrentUsername: async () => "DummyUser"
453
+ };
454
+ return BaseUser.Dummy(dummySyncStrategy);
455
+ });
456
+ try {
457
+ logger.info(`Applying tag ${tagID} to card ${courseID + "-" + cardID}...`);
458
+ const tag = await courseDB.get(prefixedTagID);
459
+ if (!tag.taggedCards.includes(cardID)) {
460
+ tag.taggedCards.push(cardID);
461
+ if (updateELO) {
462
+ try {
463
+ const eloData = await courseApi.getCardEloData([cardID]);
464
+ const elo = eloData[0];
465
+ elo.tags[tagID] = {
466
+ count: 0,
467
+ score: elo.global.score
468
+ // todo: or 1000?
469
+ };
470
+ await updateCardElo(courseID, cardID, elo);
471
+ } catch (error) {
472
+ logger.error("Failed to update ELO data for card:", cardID, error);
473
+ }
474
+ }
475
+ return courseDB.put(tag);
476
+ } else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
477
+ } catch (e) {
478
+ if (e instanceof AlreadyTaggedErr) {
479
+ throw e;
480
+ }
481
+ await createTag(courseID, tagID, author);
482
+ return addTagToCard(courseID, cardID, tagID, author, updateELO);
483
+ }
484
+ }
485
+ async function updateCardElo(courseID, cardID, elo) {
486
+ if (elo) {
487
+ const cDB = getCourseDB(courseID);
488
+ const card = await cDB.get(cardID);
489
+ logger.debug(`Replacing ${JSON.stringify(card.elo)} with ${JSON.stringify(elo)}`);
490
+ card.elo = elo;
491
+ return cDB.put(card);
492
+ }
493
+ }
494
+ function getTagID(tagName) {
495
+ const tagPrefix = "TAG" /* TAG */.valueOf() + "-";
496
+ if (tagName.indexOf(tagPrefix) === 0) {
497
+ return tagName;
498
+ } else {
499
+ return tagPrefix + tagName;
500
+ }
501
+ }
502
+ function getCourseDB(courseID) {
503
+ const dbName = `coursedb-${courseID}`;
504
+ return new pouchdb_setup_default(
505
+ ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
506
+ pouchDBincludeCredentialsConfig
507
+ );
508
+ }
509
+ var AlreadyTaggedErr;
510
+ var init_courseAPI = __esm({
511
+ "src/impl/couch/courseAPI.ts"() {
512
+ "use strict";
513
+ init_pouchdb_setup();
514
+ init_couch();
515
+ init_factory();
516
+ init_courseDB();
517
+ init_types_legacy();
518
+ init_common();
519
+ init_logger();
520
+ AlreadyTaggedErr = class extends Error {
521
+ constructor(message) {
522
+ super(message);
523
+ this.name = "AlreadyTaggedErr";
524
+ }
525
+ };
526
+ }
527
+ });
528
+
529
+ // src/impl/couch/courseLookupDB.ts
530
+ var init_courseLookupDB = __esm({
531
+ "src/impl/couch/courseLookupDB.ts"() {
532
+ "use strict";
533
+ init_pouchdb_setup();
534
+ init_factory();
535
+ init_logger();
536
+ logger.debug(`COURSELOOKUP FILE RUNNING`);
537
+ }
538
+ });
539
+
540
+ // src/core/navigators/elo.ts
541
+ var elo_exports = {};
542
+ __export(elo_exports, {
543
+ default: () => ELONavigator
544
+ });
545
+ var ELONavigator;
546
+ var init_elo = __esm({
547
+ "src/core/navigators/elo.ts"() {
548
+ "use strict";
549
+ init_navigators();
550
+ ELONavigator = class extends ContentNavigator {
551
+ user;
552
+ course;
553
+ constructor(user, course) {
554
+ super();
555
+ this.user = user;
556
+ this.course = course;
557
+ }
558
+ async getPendingReviews() {
559
+ const reviews = await this.user.getPendingReviews(this.course.getCourseID());
560
+ const elo = await this.course.getCardEloData(reviews.map((r) => r.cardId));
561
+ const ratedReviews = reviews.map((r, i) => {
562
+ const ratedR = {
563
+ ...r,
564
+ ...elo[i]
565
+ };
566
+ return ratedR;
567
+ });
568
+ ratedReviews.sort((a, b) => {
569
+ return a.global.score - b.global.score;
570
+ });
571
+ return ratedReviews.map((r) => {
572
+ return {
573
+ ...r,
574
+ contentSourceType: "course",
575
+ contentSourceID: this.course.getCourseID(),
576
+ cardID: r.cardId,
577
+ courseID: r.courseId,
578
+ qualifiedID: `${r.courseId}-${r.cardId}`,
579
+ reviewID: r._id,
580
+ status: "review"
581
+ };
582
+ });
583
+ }
584
+ async getNewCards(limit = 99) {
585
+ const activeCards = await this.user.getActiveCards();
586
+ return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
587
+ if (activeCards.some((ac) => c.includes(ac))) {
588
+ return false;
589
+ } else {
590
+ return true;
591
+ }
592
+ })).map((c) => {
593
+ return {
594
+ ...c,
595
+ status: "new"
596
+ };
597
+ });
598
+ }
599
+ };
600
+ }
601
+ });
602
+
603
+ // import("./**/*") in src/core/navigators/index.ts
604
+ var globImport;
605
+ var init_ = __esm({
606
+ 'import("./**/*") in src/core/navigators/index.ts'() {
607
+ globImport = __glob({
608
+ "./elo.ts": () => Promise.resolve().then(() => (init_elo(), elo_exports)),
609
+ "./index.ts": () => Promise.resolve().then(() => (init_navigators(), navigators_exports))
610
+ });
611
+ }
612
+ });
613
+
614
+ // src/core/navigators/index.ts
615
+ var navigators_exports = {};
616
+ __export(navigators_exports, {
617
+ ContentNavigator: () => ContentNavigator,
618
+ Navigators: () => Navigators
619
+ });
620
+ var Navigators, ContentNavigator;
621
+ var init_navigators = __esm({
622
+ "src/core/navigators/index.ts"() {
350
623
  "use strict";
351
624
  init_logger();
352
625
  init_();
@@ -387,16 +660,16 @@ var init_navigators = __esm({
387
660
  import {
388
661
  EloToNumber,
389
662
  Status,
390
- blankCourseElo,
391
- toCourseElo
663
+ blankCourseElo as blankCourseElo2,
664
+ toCourseElo as toCourseElo2
392
665
  } from "@vue-skuilder/common";
393
666
  function randIntWeightedTowardZero(n) {
394
667
  return Math.floor(Math.random() * Math.random() * Math.random() * n);
395
668
  }
396
669
  async function getCourseTagStubs(courseID) {
397
670
  logger.debug(`Getting tag stubs for course: ${courseID}`);
398
- const stubs = await filterAllDocsByPrefix(
399
- getCourseDB(courseID),
671
+ const stubs = await filterAllDocsByPrefix2(
672
+ getCourseDB2(courseID),
400
673
  "TAG" /* TAG */.valueOf() + "-"
401
674
  );
402
675
  stubs.rows.forEach((row) => {
@@ -407,7 +680,7 @@ async function getCourseTagStubs(courseID) {
407
680
  async function createTag(courseID, tagName, author) {
408
681
  logger.debug(`Creating tag: ${tagName}...`);
409
682
  const tagID = getTagID(tagName);
410
- const courseDB = getCourseDB(courseID);
683
+ const courseDB = getCourseDB2(courseID);
411
684
  const resp = await courseDB.put({
412
685
  course: courseID,
413
686
  docType: "TAG" /* TAG */,
@@ -422,19 +695,19 @@ async function createTag(courseID, tagName, author) {
422
695
  }
423
696
  async function updateTag(tag) {
424
697
  const prior = await getTag(tag.course, tag.name);
425
- return await getCourseDB(tag.course).put({
698
+ return await getCourseDB2(tag.course).put({
426
699
  ...tag,
427
700
  _rev: prior._rev
428
701
  });
429
702
  }
430
703
  async function getTag(courseID, tagName) {
431
704
  const tagID = getTagID(tagName);
432
- const courseDB = getCourseDB(courseID);
705
+ const courseDB = getCourseDB2(courseID);
433
706
  return courseDB.get(tagID);
434
707
  }
435
708
  async function removeTagFromCard(courseID, cardID, tagID) {
436
709
  tagID = getTagID(tagID);
437
- const courseDB = getCourseDB(courseID);
710
+ const courseDB = getCourseDB2(courseID);
438
711
  const tag = await courseDB.get(tagID);
439
712
  tag.taggedCards = tag.taggedCards.filter((taggedID) => {
440
713
  return cardID !== taggedID;
@@ -442,7 +715,7 @@ async function removeTagFromCard(courseID, cardID, tagID) {
442
715
  return courseDB.put(tag);
443
716
  }
444
717
  async function getAppliedTags(id_course, id_card) {
445
- const db = getCourseDB(id_course);
718
+ const db = getCourseDB2(id_course);
446
719
  const result = await db.query("getTags", {
447
720
  startkey: id_card,
448
721
  endkey: id_card
@@ -455,7 +728,7 @@ async function updateCredentialledCourseConfig(courseID, config) {
455
728
 
456
729
  ${JSON.stringify(config)}
457
730
  `);
458
- const db = getCourseDB(courseID);
731
+ const db = getCourseDB2(courseID);
459
732
  const old = await getCredentialledCourseConfig(courseID);
460
733
  return await db.put({
461
734
  ...config,
@@ -487,7 +760,7 @@ var init_courseDB = __esm({
487
760
  updateQueue;
488
761
  constructor(id, userLookup) {
489
762
  this.id = id;
490
- this.db = getCourseDB(this.id);
763
+ this.db = getCourseDB2(this.id);
491
764
  this._getCurrentUser = userLookup;
492
765
  this.updateQueue = new UpdateQueue(this.db);
493
766
  }
@@ -543,14 +816,14 @@ var init_courseDB = __esm({
543
816
  docs.rows.forEach((r) => {
544
817
  if (isSuccessRow(r)) {
545
818
  if (r.doc && r.doc.elo) {
546
- ret.push(toCourseElo(r.doc.elo));
819
+ ret.push(toCourseElo2(r.doc.elo));
547
820
  } else {
548
821
  logger.warn("no elo data for card: " + r.id);
549
- ret.push(blankCourseElo());
822
+ ret.push(blankCourseElo2());
550
823
  }
551
824
  } else {
552
825
  logger.warn("no elo data for card: " + JSON.stringify(r));
553
- ret.push(blankCourseElo());
826
+ ret.push(blankCourseElo2());
554
827
  }
555
828
  });
556
829
  return ret;
@@ -703,7 +976,7 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
703
976
  async getCourseTagStubs() {
704
977
  return getCourseTagStubs(this.id);
705
978
  }
706
- async addNote(codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo()) {
979
+ async addNote(codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo2()) {
707
980
  try {
708
981
  const resp = await addNote55(this.id, codeCourse, shape, data, author, tags, uploads, elo);
709
982
  if (resp.ok) {
@@ -891,26 +1164,75 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
891
1164
  }
892
1165
  });
893
1166
 
894
- // src/impl/common/SyncStrategy.ts
895
- var init_SyncStrategy = __esm({
896
- "src/impl/common/SyncStrategy.ts"() {
1167
+ // src/impl/couch/classroomDB.ts
1168
+ import moment2 from "moment";
1169
+ var init_classroomDB2 = __esm({
1170
+ "src/impl/couch/classroomDB.ts"() {
897
1171
  "use strict";
898
- }
899
- });
900
-
901
- // src/core/util/index.ts
902
- function getCardHistoryID(courseID, cardID) {
903
- return `${cardHistoryPrefix}-${courseID}-${cardID}`;
904
- }
905
- var init_util = __esm({
906
- "src/core/util/index.ts"() {
1172
+ init_factory();
1173
+ init_logger();
1174
+ init_pouchdb_setup();
1175
+ init_couch();
1176
+ init_courseDB();
1177
+ }
1178
+ });
1179
+
1180
+ // src/impl/couch/adminDB.ts
1181
+ var init_adminDB2 = __esm({
1182
+ "src/impl/couch/adminDB.ts"() {
1183
+ "use strict";
1184
+ init_pouchdb_setup();
1185
+ init_factory();
1186
+ init_couch();
1187
+ init_classroomDB2();
1188
+ init_courseLookupDB();
1189
+ init_logger();
1190
+ }
1191
+ });
1192
+
1193
+ // src/impl/couch/auth.ts
1194
+ var init_auth = __esm({
1195
+ "src/impl/couch/auth.ts"() {
907
1196
  "use strict";
1197
+ init_factory();
908
1198
  init_types_legacy();
1199
+ init_logger();
909
1200
  }
910
1201
  });
911
1202
 
912
- // src/impl/common/userDBHelpers.ts
913
- import moment from "moment";
1203
+ // src/impl/couch/CouchDBSyncStrategy.ts
1204
+ import { Status as Status2 } from "@vue-skuilder/common";
1205
+ var init_CouchDBSyncStrategy = __esm({
1206
+ "src/impl/couch/CouchDBSyncStrategy.ts"() {
1207
+ "use strict";
1208
+ init_factory();
1209
+ init_types_legacy();
1210
+ init_logger();
1211
+ init_common();
1212
+ init_pouchdb_setup();
1213
+ init_couch();
1214
+ init_auth();
1215
+ }
1216
+ });
1217
+
1218
+ // src/impl/couch/index.ts
1219
+ import moment3 from "moment";
1220
+ import process2 from "process";
1221
+ function getCourseDB2(courseID) {
1222
+ return new pouchdb_setup_default(
1223
+ ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
1224
+ pouchDBincludeCredentialsConfig
1225
+ );
1226
+ }
1227
+ function getCourseDocs(courseID, docIDs, options = {}) {
1228
+ return getCourseDB2(courseID).allDocs({
1229
+ ...options,
1230
+ keys: docIDs
1231
+ });
1232
+ }
1233
+ function getCourseDoc(courseID, docID, options = {}) {
1234
+ return getCourseDB2(courseID).get(docID, options);
1235
+ }
914
1236
  function filterAllDocsByPrefix2(db, prefix, opts) {
915
1237
  const options = {
916
1238
  startkey: prefix,
@@ -928,49 +1250,39 @@ function getStartAndEndKeys2(key) {
928
1250
  endkey: key + "\uFFF0"
929
1251
  };
930
1252
  }
931
- function getLocalUserDB(username) {
932
- return new pouchdb_setup_default(`userdb-${username}`, {});
933
- }
934
- function scheduleCardReviewLocal(userDB, review) {
935
- const now = moment.utc();
936
- logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
937
- void userDB.put({
938
- _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
939
- cardId: review.card_id,
940
- reviewTime: review.time,
941
- courseId: review.course_id,
942
- scheduledAt: now,
943
- scheduledFor: review.scheduledFor,
944
- schedulingAgentId: review.schedulingAgentId
945
- });
946
- }
947
- async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
948
- const reviewDoc = await userDB.get(reviewDocID);
949
- userDB.remove(reviewDoc).then((res) => {
950
- if (res.ok) {
951
- log(`Removed Review Doc: ${reviewDocID}`);
952
- }
953
- }).catch((err) => {
954
- log(`Failed to remove Review Doc: ${reviewDocID},
955
- ${JSON.stringify(err)}`);
956
- });
957
- }
958
- var REVIEW_PREFIX, REVIEW_TIME_FORMAT, log;
959
- var init_userDBHelpers = __esm({
960
- "src/impl/common/userDBHelpers.ts"() {
1253
+ var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
1254
+ var init_couch = __esm({
1255
+ "src/impl/couch/index.ts"() {
961
1256
  "use strict";
1257
+ init_factory();
1258
+ init_types_legacy();
962
1259
  init_logger();
963
1260
  init_pouchdb_setup();
964
- REVIEW_PREFIX = "card_review_";
965
- REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
966
- log = (s) => {
967
- logger.info(s);
1261
+ init_contentSource();
1262
+ init_adminDB2();
1263
+ init_classroomDB2();
1264
+ init_courseAPI();
1265
+ init_courseDB();
1266
+ init_CouchDBSyncStrategy();
1267
+ isBrowser = typeof window !== "undefined";
1268
+ if (isBrowser) {
1269
+ window.process = process2;
1270
+ }
1271
+ GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
1272
+ localUserDB = new pouchdb_setup_default(GUEST_LOCAL_DB);
1273
+ pouchDBincludeCredentialsConfig = {
1274
+ fetch(url, opts) {
1275
+ opts.credentials = "include";
1276
+ return pouchdb_setup_default.fetch(url, opts);
1277
+ }
968
1278
  };
1279
+ REVIEW_PREFIX2 = "card_review_";
1280
+ REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
969
1281
  }
970
1282
  });
971
1283
 
972
1284
  // src/impl/couch/user-course-relDB.ts
973
- import moment2 from "moment";
1285
+ import moment4 from "moment";
974
1286
  var UsrCrsData;
975
1287
  var init_user_course_relDB = __esm({
976
1288
  "src/impl/couch/user-course-relDB.ts"() {
@@ -988,11 +1300,11 @@ var init_user_course_relDB = __esm({
988
1300
  this._courseId = courseId;
989
1301
  }
990
1302
  async getReviewsForcast(daysCount) {
991
- const time = moment2.utc().add(daysCount, "days");
1303
+ const time = moment4.utc().add(daysCount, "days");
992
1304
  return this.getReviewstoDate(time);
993
1305
  }
994
1306
  async getPendingReviews() {
995
- const now = moment2.utc();
1307
+ const now = moment4.utc();
996
1308
  return this.getReviewstoDate(now);
997
1309
  }
998
1310
  async getScheduledReviewCount() {
@@ -1012,7 +1324,7 @@ var init_user_course_relDB = __esm({
1012
1324
  void this.user.updateCourseSettings(this._courseId, updates);
1013
1325
  }
1014
1326
  async getReviewstoDate(targetDate) {
1015
- const keys = getStartAndEndKeys(REVIEW_PREFIX2);
1327
+ const keys = getStartAndEndKeys2(REVIEW_PREFIX2);
1016
1328
  const reviews = await this.user.remote().allDocs({
1017
1329
  startkey: keys.startkey,
1018
1330
  endkey: keys.endkey,
@@ -1023,7 +1335,7 @@ var init_user_course_relDB = __esm({
1023
1335
  );
1024
1336
  return reviews.rows.filter((r) => {
1025
1337
  if (r.id.startsWith(REVIEW_PREFIX2)) {
1026
- const date = moment2.utc(r.id.substr(REVIEW_PREFIX2.length), REVIEW_TIME_FORMAT2);
1338
+ const date = moment4.utc(r.id.substr(REVIEW_PREFIX2.length), REVIEW_TIME_FORMAT2);
1027
1339
  if (targetDate.isAfter(date)) {
1028
1340
  if (this._courseId === void 0 || r.doc.courseId === this._courseId) {
1029
1341
  return true;
@@ -1037,8 +1349,8 @@ var init_user_course_relDB = __esm({
1037
1349
  });
1038
1350
 
1039
1351
  // src/impl/common/BaseUserDB.ts
1040
- import { Status as Status2 } from "@vue-skuilder/common";
1041
- import moment3 from "moment";
1352
+ import { Status as Status3 } from "@vue-skuilder/common";
1353
+ import moment5 from "moment";
1042
1354
  async function getOrCreateClassroomRegistrationsDoc(user) {
1043
1355
  let ret;
1044
1356
  try {
@@ -1098,7 +1410,7 @@ async function updateUserElo(user, course_id, elo) {
1098
1410
  return getLocalUserDB(user).put(regDoc);
1099
1411
  }
1100
1412
  async function registerUserForClassroom(user, classID, registerAs) {
1101
- log2(`Registering user: ${user} in course: ${classID}`);
1413
+ log3(`Registering user: ${user} in course: ${classID}`);
1102
1414
  return getOrCreateClassroomRegistrationsDoc(user).then((doc) => {
1103
1415
  const regItem = {
1104
1416
  classID,
@@ -1109,7 +1421,7 @@ async function registerUserForClassroom(user, classID, registerAs) {
1109
1421
  }).length === 0) {
1110
1422
  doc.registrations.push(regItem);
1111
1423
  } else {
1112
- log2(`User ${user} is already registered for class ${classID}`);
1424
+ log3(`User ${user} is already registered for class ${classID}`);
1113
1425
  }
1114
1426
  return getLocalUserDB(user).put(doc);
1115
1427
  });
@@ -1131,7 +1443,7 @@ async function dropUserFromClassroom(user, classID) {
1131
1443
  async function getUserClassrooms(user) {
1132
1444
  return getOrCreateClassroomRegistrationsDoc(user);
1133
1445
  }
1134
- var log2, cardHistoryPrefix2, BaseUser, userCoursesDoc, userClassroomsDoc;
1446
+ var log3, cardHistoryPrefix2, BaseUser, userCoursesDoc, userClassroomsDoc;
1135
1447
  var init_BaseUserDB = __esm({
1136
1448
  "src/impl/common/BaseUserDB.ts"() {
1137
1449
  "use strict";
@@ -1142,7 +1454,7 @@ var init_BaseUserDB = __esm({
1142
1454
  init_updateQueue();
1143
1455
  init_user_course_relDB();
1144
1456
  init_couch();
1145
- log2 = (s) => {
1457
+ log3 = (s) => {
1146
1458
  logger.info(s);
1147
1459
  };
1148
1460
  cardHistoryPrefix2 = "cardH-";
@@ -1183,10 +1495,14 @@ Currently logged-in as ${this._username}.`
1183
1495
  );
1184
1496
  }
1185
1497
  const result = await this.syncStrategy.createAccount(username, password);
1186
- if (result.status === Status2.ok) {
1187
- log2(`Account created successfully, updating username to ${username}`);
1498
+ if (result.status === Status3.ok) {
1499
+ log3(`Account created successfully, updating username to ${username}`);
1188
1500
  this._username = username;
1189
- localStorage.removeItem("dbUUID");
1501
+ try {
1502
+ localStorage.removeItem("dbUUID");
1503
+ } catch (e) {
1504
+ logger.warn("localStorage not available (Node.js environment):", e);
1505
+ }
1190
1506
  await this.init();
1191
1507
  }
1192
1508
  return {
@@ -1198,15 +1514,22 @@ Currently logged-in as ${this._username}.`
1198
1514
  if (!this.syncStrategy.canAuthenticate()) {
1199
1515
  throw new Error("Authentication not supported by current sync strategy");
1200
1516
  }
1201
- if (!this._username.startsWith(GuestUsername)) {
1202
- throw new Error(`Cannot change accounts while logged in.
1203
- Log out of account ${this.getUsername()} before logging in as ${username}.`);
1517
+ if (!this._username.startsWith(GuestUsername) && this._username != username) {
1518
+ if (this._username != username) {
1519
+ throw new Error(`Cannot change accounts while logged in.
1520
+ Log out of account ${this.getUsername()} before logging in as ${username}.`);
1521
+ }
1522
+ logger.warn(`User ${this._username} is already logged in, but executing login again.`);
1204
1523
  }
1205
1524
  const loginResult = await this.syncStrategy.authenticate(username, password);
1206
1525
  if (loginResult.ok) {
1207
- log2(`Logged in as ${username}`);
1526
+ log3(`Logged in as ${username}`);
1208
1527
  this._username = username;
1209
- localStorage.removeItem("dbUUID");
1528
+ try {
1529
+ localStorage.removeItem("dbUUID");
1530
+ } catch (e) {
1531
+ logger.warn("localStorage not available (Node.js environment):", e);
1532
+ }
1210
1533
  await this.init();
1211
1534
  }
1212
1535
  return loginResult;
@@ -1214,7 +1537,7 @@ Currently logged-in as ${this._username}.`
1214
1537
  async resetUserData() {
1215
1538
  if (this.syncStrategy.canAuthenticate()) {
1216
1539
  return {
1217
- status: Status2.error,
1540
+ status: Status3.error,
1218
1541
  error: "Reset user data is only available for local-only mode. Use logout instead for remote sync."
1219
1542
  };
1220
1543
  }
@@ -1233,11 +1556,11 @@ Currently logged-in as ${this._username}.`
1233
1556
  await localDB.bulkDocs(docsToDelete);
1234
1557
  }
1235
1558
  await this.init();
1236
- return { status: Status2.ok };
1559
+ return { status: Status3.ok };
1237
1560
  } catch (error) {
1238
1561
  logger.error("Failed to reset user data:", error);
1239
1562
  return {
1240
- status: Status2.error,
1563
+ status: Status3.error,
1241
1564
  error: error instanceof Error ? error.message : "Unknown error during reset"
1242
1565
  };
1243
1566
  }
@@ -1293,7 +1616,7 @@ Currently logged-in as ${this._username}.`
1293
1616
  *
1294
1617
  */
1295
1618
  async getActiveCards() {
1296
- const keys = getStartAndEndKeys2(REVIEW_PREFIX);
1619
+ const keys = getStartAndEndKeys(REVIEW_PREFIX);
1297
1620
  const reviews = await this.remoteDB.allDocs({
1298
1621
  startkey: keys.startkey,
1299
1622
  endkey: keys.endkey,
@@ -1365,18 +1688,18 @@ Currently logged-in as ${this._username}.`
1365
1688
  }
1366
1689
  }
1367
1690
  async getReviewstoDate(targetDate, course_id) {
1368
- const keys = getStartAndEndKeys2(REVIEW_PREFIX);
1691
+ const keys = getStartAndEndKeys(REVIEW_PREFIX);
1369
1692
  const reviews = await this.remoteDB.allDocs({
1370
1693
  startkey: keys.startkey,
1371
1694
  endkey: keys.endkey,
1372
1695
  include_docs: true
1373
1696
  });
1374
- log2(
1697
+ log3(
1375
1698
  `Fetching ${this._username}'s scheduled reviews${course_id ? ` for course ${course_id}` : ""}.`
1376
1699
  );
1377
1700
  return reviews.rows.filter((r) => {
1378
1701
  if (r.id.startsWith(REVIEW_PREFIX)) {
1379
- const date = moment3.utc(r.id.substr(REVIEW_PREFIX.length), REVIEW_TIME_FORMAT);
1702
+ const date = moment5.utc(r.id.substr(REVIEW_PREFIX.length), REVIEW_TIME_FORMAT);
1380
1703
  if (targetDate.isAfter(date)) {
1381
1704
  if (course_id === void 0 || r.doc.courseId === course_id) {
1382
1705
  return true;
@@ -1386,11 +1709,11 @@ Currently logged-in as ${this._username}.`
1386
1709
  }).map((r) => r.doc);
1387
1710
  }
1388
1711
  async getReviewsForcast(daysCount) {
1389
- const time = moment3.utc().add(daysCount, "days");
1712
+ const time = moment5.utc().add(daysCount, "days");
1390
1713
  return this.getReviewstoDate(time);
1391
1714
  }
1392
1715
  async getPendingReviews(course_id) {
1393
- const now = moment3.utc();
1716
+ const now = moment5.utc();
1394
1717
  return this.getReviewstoDate(now, course_id);
1395
1718
  }
1396
1719
  async getScheduledReviewCount(course_id) {
@@ -1433,12 +1756,12 @@ Currently logged-in as ${this._username}.`
1433
1756
  if (doc.courses.filter((course) => {
1434
1757
  return course.courseID === regItem.courseID;
1435
1758
  }).length === 0) {
1436
- log2(`It's a new course registration!`);
1759
+ log3(`It's a new course registration!`);
1437
1760
  doc.courses.push(regItem);
1438
1761
  doc.studyWeight[course_id] = 1;
1439
1762
  } else {
1440
1763
  doc.courses.forEach((c) => {
1441
- log2(`Found the previously registered course!`);
1764
+ log3(`Found the previously registered course!`);
1442
1765
  if (c.courseID === course_id) {
1443
1766
  c.status = status;
1444
1767
  }
@@ -1446,7 +1769,7 @@ Currently logged-in as ${this._username}.`
1446
1769
  }
1447
1770
  return this.localDB.put(doc);
1448
1771
  }).catch((e) => {
1449
- log2(`Registration failed because of: ${JSON.stringify(e)}`);
1772
+ log3(`Registration failed because of: ${JSON.stringify(e)}`);
1450
1773
  throw e;
1451
1774
  });
1452
1775
  }
@@ -1568,6 +1891,10 @@ Currently logged-in as ${this._username}.`
1568
1891
  }
1569
1892
  async init() {
1570
1893
  _BaseUser._initialized = false;
1894
+ if (this._username === "admin") {
1895
+ _BaseUser._initialized = true;
1896
+ return;
1897
+ }
1571
1898
  this.setDBandQ();
1572
1899
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
1573
1900
  void this.applyDesignDocs();
@@ -1589,6 +1916,9 @@ Currently logged-in as ${this._username}.`
1589
1916
  }
1590
1917
  ];
1591
1918
  async applyDesignDocs() {
1919
+ if (this._username === "admin") {
1920
+ return;
1921
+ }
1592
1922
  for (const doc of _BaseUser.designDocs) {
1593
1923
  try {
1594
1924
  try {
@@ -1605,7 +1935,7 @@ Currently logged-in as ${this._username}.`
1605
1935
  }
1606
1936
  }
1607
1937
  } catch (error) {
1608
- if (error instanceof Error && error.name === "conflict") {
1938
+ if (error.name && error.name === "conflict") {
1609
1939
  logger.warn(`Design doc ${doc._id} update conflict - will retry`);
1610
1940
  await new Promise((resolve) => setTimeout(resolve, 1e3));
1611
1941
  await this.applyDesignDoc(doc);
@@ -1643,7 +1973,7 @@ Currently logged-in as ${this._username}.`
1643
1973
  */
1644
1974
  async putCardRecord(record) {
1645
1975
  const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
1646
- record.timeStamp = moment3.utc(record.timeStamp).toString();
1976
+ record.timeStamp = moment5.utc(record.timeStamp).toString();
1647
1977
  try {
1648
1978
  const cardHistory = await this.update(
1649
1979
  cardHistoryID,
@@ -1659,7 +1989,7 @@ Currently logged-in as ${this._username}.`
1659
1989
  const ret = {
1660
1990
  ...record2
1661
1991
  };
1662
- ret.timeStamp = moment3.utc(record2.timeStamp);
1992
+ ret.timeStamp = moment5.utc(record2.timeStamp);
1663
1993
  return ret;
1664
1994
  });
1665
1995
  return cardHistory;
@@ -1687,17 +2017,17 @@ Currently logged-in as ${this._username}.`
1687
2017
  }
1688
2018
  async deduplicateReviews() {
1689
2019
  try {
1690
- log2("Starting deduplication of scheduled reviews...");
2020
+ log3("Starting deduplication of scheduled reviews...");
1691
2021
  const reviewsMap = {};
1692
2022
  const duplicateDocIds = [];
1693
2023
  const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
1694
- log2(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
2024
+ log3(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
1695
2025
  scheduledReviews.rows.forEach((r) => {
1696
2026
  const qualifiedCardId = r.value;
1697
2027
  const docId = r.key;
1698
2028
  if (reviewsMap[qualifiedCardId]) {
1699
- log2(`Found duplicate scheduled review for card: ${qualifiedCardId}`);
1700
- log2(
2029
+ log3(`Found duplicate scheduled review for card: ${qualifiedCardId}`);
2030
+ log3(
1701
2031
  `Marking earlier review ${reviewsMap[qualifiedCardId]} for deletion, keeping ${docId}`
1702
2032
  );
1703
2033
  duplicateDocIds.push(reviewsMap[qualifiedCardId]);
@@ -1707,23 +2037,23 @@ Currently logged-in as ${this._username}.`
1707
2037
  }
1708
2038
  });
1709
2039
  if (duplicateDocIds.length > 0) {
1710
- log2(`Removing ${duplicateDocIds.length} duplicate reviews...`);
2040
+ log3(`Removing ${duplicateDocIds.length} duplicate reviews...`);
1711
2041
  const deletePromises = duplicateDocIds.map(async (docId) => {
1712
2042
  try {
1713
2043
  const doc = await this.remoteDB.get(docId);
1714
2044
  await this.remoteDB.remove(doc);
1715
- log2(`Successfully removed duplicate review: ${docId}`);
2045
+ log3(`Successfully removed duplicate review: ${docId}`);
1716
2046
  } catch (error) {
1717
- log2(`Failed to remove duplicate review ${docId}: ${error}`);
2047
+ log3(`Failed to remove duplicate review ${docId}: ${error}`);
1718
2048
  }
1719
2049
  });
1720
2050
  await Promise.all(deletePromises);
1721
- log2(`Deduplication complete. Processed ${duplicateDocIds.length} duplicates`);
2051
+ log3(`Deduplication complete. Processed ${duplicateDocIds.length} duplicates`);
1722
2052
  } else {
1723
- log2("No duplicate reviews found");
2053
+ log3("No duplicate reviews found");
1724
2054
  }
1725
2055
  } catch (error) {
1726
- log2(`Error during review deduplication: ${error}`);
2056
+ log3(`Error during review deduplication: ${error}`);
1727
2057
  }
1728
2058
  }
1729
2059
  /**
@@ -1737,7 +2067,7 @@ Currently logged-in as ${this._username}.`
1737
2067
  if (course_id) {
1738
2068
  prefix += course_id;
1739
2069
  }
1740
- const docs = await filterAllDocsByPrefix2(this.localDB, prefix, {
2070
+ const docs = await filterAllDocsByPrefix(this.localDB, prefix, {
1741
2071
  include_docs: false
1742
2072
  });
1743
2073
  const ret = [];
@@ -1753,7 +2083,7 @@ Currently logged-in as ${this._username}.`
1753
2083
  * @returns A promise of the cards that the user has seen in the past.
1754
2084
  */
1755
2085
  async getHistory() {
1756
- const cards = await filterAllDocsByPrefix2(
2086
+ const cards = await filterAllDocsByPrefix(
1757
2087
  this.remoteDB,
1758
2088
  cardHistoryPrefix2,
1759
2089
  {
@@ -1877,298 +2207,17 @@ var init_common = __esm({
1877
2207
  }
1878
2208
  });
1879
2209
 
1880
- // src/impl/couch/courseAPI.ts
1881
- import { NameSpacer } from "@vue-skuilder/common";
1882
- import { blankCourseElo as blankCourseElo2, toCourseElo as toCourseElo2 } from "@vue-skuilder/common";
1883
- import { prepareNote55 } from "@vue-skuilder/common";
1884
- async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo2()) {
1885
- const db = getCourseDB2(courseID);
1886
- const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
1887
- const result = await db.post(payload);
1888
- const dataShapeId = NameSpacer.getDataShapeString({
1889
- course: codeCourse,
1890
- dataShape: shape.name
1891
- });
1892
- if (result.ok) {
1893
- try {
1894
- await createCards(courseID, dataShapeId, result.id, tags, elo, author);
1895
- } catch (error) {
1896
- let errorMessage = "Unknown error";
1897
- if (error instanceof Error) {
1898
- errorMessage = error.message;
1899
- } else if (error && typeof error === "object" && "reason" in error) {
1900
- errorMessage = error.reason;
1901
- } else if (error && typeof error === "object" && "message" in error) {
1902
- errorMessage = error.message;
1903
- } else {
1904
- errorMessage = String(error);
1905
- }
1906
- logger.error(`[addNote55] Failed to create cards for note ${result.id}: ${errorMessage}`);
1907
- result.cardCreationFailed = true;
1908
- result.cardCreationError = errorMessage;
1909
- }
1910
- } else {
1911
- logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
1912
- }
1913
- return result;
1914
- }
1915
- async function createCards(courseID, datashapeID, noteID, tags, elo = blankCourseElo2(), author) {
1916
- const cfg = await getCredentialledCourseConfig(courseID);
1917
- const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
1918
- let questionViewTypes = [];
1919
- for (const ds of cfg.dataShapes) {
1920
- if (ds.name === datashapeID) {
1921
- questionViewTypes = ds.questionTypes;
1922
- }
1923
- }
1924
- if (questionViewTypes.length === 0) {
1925
- const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
1926
- logger.error(errorMsg);
1927
- throw new Error(errorMsg);
1928
- }
1929
- for (const questionView of questionViewTypes) {
1930
- await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo, author);
1931
- }
1932
- }
1933
- async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags, elo = blankCourseElo2(), author) {
1934
- const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
1935
- const cfg = await getCredentialledCourseConfig(courseID);
1936
- for (const rQ of cfg.questionTypes) {
1937
- if (rQ.name === questionViewName) {
1938
- for (const view of rQ.viewList) {
1939
- await addCard(
1940
- courseID,
1941
- dsDescriptor.course,
1942
- [noteID],
1943
- NameSpacer.getViewString({
1944
- course: qDescriptor.course,
1945
- questionType: qDescriptor.questionType,
1946
- view
1947
- }),
1948
- elo,
1949
- tags,
1950
- author
1951
- );
1952
- }
1953
- }
1954
- }
1955
- }
1956
- async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
1957
- const db = getCourseDB2(courseID);
1958
- const card = await db.post({
1959
- course,
1960
- id_displayable_data,
1961
- id_view,
1962
- docType: "CARD" /* CARD */,
1963
- elo: elo || toCourseElo2(990 + Math.round(20 * Math.random())),
1964
- author
1965
- });
1966
- for (const tag of tags) {
1967
- logger.info(`adding tag: ${tag} to card ${card.id}`);
1968
- await addTagToCard(courseID, card.id, tag, author, false);
1969
- }
1970
- return card;
1971
- }
1972
- async function getCredentialledCourseConfig(courseID) {
1973
- try {
1974
- const db = getCourseDB2(courseID);
1975
- const ret = await db.get("CourseConfig");
1976
- ret.courseID = courseID;
1977
- logger.info(`Returning course config: ${JSON.stringify(ret)}`);
1978
- return ret;
1979
- } catch (e) {
1980
- logger.error(`Error fetching config for ${courseID}:`, e);
1981
- throw e;
1982
- }
1983
- }
1984
- async function addTagToCard(courseID, cardID, tagID, author, updateELO = true) {
1985
- const prefixedTagID = getTagID(tagID);
1986
- const courseDB = getCourseDB2(courseID);
1987
- const courseApi = new CourseDB(courseID, async () => {
1988
- const dummySyncStrategy = {
1989
- setupRemoteDB: () => null,
1990
- startSync: () => {
1991
- },
1992
- canCreateAccount: () => false,
1993
- canAuthenticate: () => false,
1994
- getCurrentUsername: async () => "DummyUser"
1995
- };
1996
- return BaseUser.Dummy(dummySyncStrategy);
1997
- });
1998
- try {
1999
- logger.info(`Applying tag ${tagID} to card ${courseID + "-" + cardID}...`);
2000
- const tag = await courseDB.get(prefixedTagID);
2001
- if (!tag.taggedCards.includes(cardID)) {
2002
- tag.taggedCards.push(cardID);
2003
- if (updateELO) {
2004
- try {
2005
- const eloData = await courseApi.getCardEloData([cardID]);
2006
- const elo = eloData[0];
2007
- elo.tags[tagID] = {
2008
- count: 0,
2009
- score: elo.global.score
2010
- // todo: or 1000?
2011
- };
2012
- await updateCardElo(courseID, cardID, elo);
2013
- } catch (error) {
2014
- logger.error("Failed to update ELO data for card:", cardID, error);
2015
- }
2016
- }
2017
- return courseDB.put(tag);
2018
- } else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
2019
- } catch (e) {
2020
- if (e instanceof AlreadyTaggedErr) {
2021
- throw e;
2022
- }
2023
- await createTag(courseID, tagID, author);
2024
- return addTagToCard(courseID, cardID, tagID, author, updateELO);
2025
- }
2026
- }
2027
- async function updateCardElo(courseID, cardID, elo) {
2028
- if (elo) {
2029
- const cDB = getCourseDB2(courseID);
2030
- const card = await cDB.get(cardID);
2031
- logger.debug(`Replacing ${JSON.stringify(card.elo)} with ${JSON.stringify(elo)}`);
2032
- card.elo = elo;
2033
- return cDB.put(card);
2034
- }
2035
- }
2036
- function getTagID(tagName) {
2037
- const tagPrefix = "TAG" /* TAG */.valueOf() + "-";
2038
- if (tagName.indexOf(tagPrefix) === 0) {
2039
- return tagName;
2040
- } else {
2041
- return tagPrefix + tagName;
2042
- }
2043
- }
2044
- function getCourseDB2(courseID) {
2045
- const dbName = `coursedb-${courseID}`;
2046
- return new pouchdb_setup_default(
2047
- ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
2048
- pouchDBincludeCredentialsConfig
2049
- );
2050
- }
2051
- var AlreadyTaggedErr;
2052
- var init_courseAPI = __esm({
2053
- "src/impl/couch/courseAPI.ts"() {
2054
- "use strict";
2055
- init_pouchdb_setup();
2056
- init_couch();
2057
- init_factory();
2058
- init_courseDB();
2059
- init_types_legacy();
2060
- init_common();
2061
- init_logger();
2062
- AlreadyTaggedErr = class extends Error {
2063
- constructor(message) {
2064
- super(message);
2065
- this.name = "AlreadyTaggedErr";
2066
- }
2067
- };
2068
- }
2069
- });
2070
-
2071
- // src/impl/couch/auth.ts
2072
- var init_auth = __esm({
2073
- "src/impl/couch/auth.ts"() {
2074
- "use strict";
2075
- init_factory();
2076
- init_types_legacy();
2077
- init_logger();
2078
- }
2079
- });
2080
-
2081
- // src/impl/couch/CouchDBSyncStrategy.ts
2082
- import { Status as Status3 } from "@vue-skuilder/common";
2083
- var init_CouchDBSyncStrategy = __esm({
2084
- "src/impl/couch/CouchDBSyncStrategy.ts"() {
2210
+ // src/factory.ts
2211
+ var ENV;
2212
+ var init_factory = __esm({
2213
+ "src/factory.ts"() {
2085
2214
  "use strict";
2086
- init_factory();
2087
- init_types_legacy();
2088
- init_logger();
2089
2215
  init_common();
2090
- init_pouchdb_setup();
2091
- init_couch();
2092
- init_auth();
2093
- }
2094
- });
2095
-
2096
- // src/impl/couch/index.ts
2097
- import moment4 from "moment";
2098
- import process2 from "process";
2099
- function getCourseDB(courseID) {
2100
- return new pouchdb_setup_default(
2101
- ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
2102
- pouchDBincludeCredentialsConfig
2103
- );
2104
- }
2105
- function getCourseDocs(courseID, docIDs, options = {}) {
2106
- return getCourseDB(courseID).allDocs({
2107
- ...options,
2108
- keys: docIDs
2109
- });
2110
- }
2111
- function getCourseDoc(courseID, docID, options = {}) {
2112
- return getCourseDB(courseID).get(docID, options);
2113
- }
2114
- function filterAllDocsByPrefix(db, prefix, opts) {
2115
- const options = {
2116
- startkey: prefix,
2117
- endkey: prefix + "\uFFF0",
2118
- include_docs: true
2119
- };
2120
- if (opts) {
2121
- Object.assign(options, opts);
2122
- }
2123
- return db.allDocs(options);
2124
- }
2125
- function getStartAndEndKeys(key) {
2126
- return {
2127
- startkey: key,
2128
- endkey: key + "\uFFF0"
2129
- };
2130
- }
2131
- var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
2132
- var init_couch = __esm({
2133
- "src/impl/couch/index.ts"() {
2134
- "use strict";
2135
- init_factory();
2136
- init_types_legacy();
2137
2216
  init_logger();
2138
- init_pouchdb_setup();
2139
- init_contentSource();
2140
- init_adminDB2();
2141
- init_classroomDB2();
2142
- init_courseAPI();
2143
- init_courseDB();
2144
- init_CouchDBSyncStrategy();
2145
- isBrowser = typeof window !== "undefined";
2146
- if (isBrowser) {
2147
- window.process = process2;
2148
- }
2149
- GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
2150
- localUserDB = new pouchdb_setup_default(GUEST_LOCAL_DB);
2151
- pouchDBincludeCredentialsConfig = {
2152
- fetch(url, opts) {
2153
- opts.credentials = "include";
2154
- return pouchdb_setup_default.fetch(url, opts);
2155
- }
2217
+ ENV = {
2218
+ COUCHDB_SERVER_PROTOCOL: "NOT_SET",
2219
+ COUCHDB_SERVER_URL: "NOT_SET"
2156
2220
  };
2157
- REVIEW_PREFIX2 = "card_review_";
2158
- REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
2159
- }
2160
- });
2161
-
2162
- // src/impl/couch/classroomDB.ts
2163
- import moment5 from "moment";
2164
- var init_classroomDB2 = __esm({
2165
- "src/impl/couch/classroomDB.ts"() {
2166
- "use strict";
2167
- init_factory();
2168
- init_logger();
2169
- init_pouchdb_setup();
2170
- init_couch();
2171
- init_courseDB();
2172
2221
  }
2173
2222
  });
2174
2223
 
@@ -2269,11 +2318,11 @@ var init_StaticDataUnpacker = __esm({
2269
2318
  init_logger();
2270
2319
  init_core();
2271
2320
  pathUtils = {
2272
- isAbsolute: (path) => {
2273
- if (/^[a-zA-Z]:[\\/]/.test(path) || /^\\\\/.test(path)) {
2321
+ isAbsolute: (path2) => {
2322
+ if (/^[a-zA-Z]:[\\/]/.test(path2) || /^\\\\/.test(path2)) {
2274
2323
  return true;
2275
2324
  }
2276
- if (path.startsWith("/")) {
2325
+ if (path2.startsWith("/")) {
2277
2326
  return true;
2278
2327
  }
2279
2328
  return false;