@vue-skuilder/db 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SyncStrategy-DnJRj-Xp.d.mts +74 -0
- package/dist/SyncStrategy-DnJRj-Xp.d.ts +74 -0
- package/dist/core/index.d.mts +90 -2
- package/dist/core/index.d.ts +90 -2
- package/dist/core/index.js +798 -650
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +769 -621
- package/dist/core/index.mjs.map +1 -1
- package/dist/dataLayerProvider-B8wquRiB.d.mts +37 -0
- package/dist/dataLayerProvider-DRjMZMaf.d.ts +37 -0
- package/dist/impl/couch/index.d.mts +292 -0
- package/dist/impl/couch/index.d.ts +292 -0
- package/dist/impl/couch/index.js +8522 -0
- package/dist/impl/couch/index.js.map +1 -0
- package/dist/impl/couch/index.mjs +8474 -0
- package/dist/impl/couch/index.mjs.map +1 -0
- package/dist/impl/static/index.d.mts +204 -0
- package/dist/impl/static/index.d.ts +204 -0
- package/dist/impl/static/index.js +8499 -0
- package/dist/impl/static/index.js.map +1 -0
- package/dist/impl/static/index.mjs +8488 -0
- package/dist/impl/static/index.mjs.map +1 -0
- package/dist/index.d.mts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +3280 -2108
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3290 -2118
- package/dist/index.mjs.map +1 -1
- package/dist/types-B0GJsjOr.d.ts +47 -0
- package/dist/types-DIgj8pP7.d.mts +47 -0
- package/dist/types-legacy-CTsJvvxI.d.mts +137 -0
- package/dist/types-legacy-CTsJvvxI.d.ts +137 -0
- package/dist/{index-QMtzQI65.d.mts → userDB-C5dcuRZs.d.ts} +3 -251
- package/dist/{index-QMtzQI65.d.ts → userDB-ZSwOXiYN.d.mts} +3 -251
- package/dist/util/packer/index.d.mts +25 -0
- package/dist/util/packer/index.d.ts +25 -0
- package/dist/util/packer/index.js +307 -0
- package/dist/util/packer/index.js.map +1 -0
- package/dist/util/packer/index.mjs +280 -0
- package/dist/util/packer/index.mjs.map +1 -0
- package/package.json +12 -2
- package/src/core/interfaces/contentSource.ts +8 -6
- package/src/core/interfaces/userDB.ts +2 -2
- package/src/factory.ts +10 -7
- package/src/impl/{pouch/userDB.ts → common/BaseUserDB.ts} +225 -260
- package/src/impl/common/SyncStrategy.ts +90 -0
- package/src/impl/common/index.ts +23 -0
- package/src/impl/common/types.ts +50 -0
- package/src/impl/common/userDBHelpers.ts +144 -0
- package/src/impl/couch/CouchDBSyncStrategy.ts +209 -0
- package/src/impl/{pouch → couch}/PouchDataLayerProvider.ts +12 -7
- package/src/impl/{pouch → couch}/adminDB.ts +3 -3
- package/src/impl/{pouch → couch}/auth.ts +2 -2
- package/src/impl/{pouch → couch}/classroomDB.ts +6 -6
- package/src/impl/{pouch → couch}/courseAPI.ts +28 -5
- package/src/impl/{pouch → couch}/courseDB.ts +24 -10
- package/src/impl/{pouch → couch}/courseLookupDB.ts +1 -1
- package/src/impl/{pouch → couch}/index.ts +27 -20
- package/src/impl/{pouch → couch}/updateQueue.ts +5 -1
- package/src/impl/{pouch → couch}/user-course-relDB.ts +6 -1
- package/src/impl/static/NoOpSyncStrategy.ts +70 -0
- package/src/impl/static/StaticDataLayerProvider.ts +89 -0
- package/src/impl/static/StaticDataUnpacker.ts +376 -0
- package/src/impl/static/courseDB.ts +257 -0
- package/src/impl/static/coursesDB.ts +37 -0
- package/src/impl/static/index.ts +8 -0
- package/src/impl/static/userDB.ts +179 -0
- package/src/index.ts +1 -1
- package/src/study/SessionController.ts +4 -4
- package/src/study/SpacedRepetition.ts +3 -3
- package/src/study/getCardDataShape.ts +2 -2
- package/src/util/index.ts +1 -0
- package/src/util/packer/CouchDBToStaticPacker.ts +349 -0
- package/src/util/packer/index.ts +4 -0
- package/src/util/packer/types.ts +52 -0
- package/tsconfig.json +7 -10
- package/tsup.config.ts +5 -3
- /package/src/impl/{pouch → couch}/clientCache.ts +0 -0
- /package/src/impl/{pouch → couch}/pouchdb-setup.ts +0 -0
- /package/src/impl/{pouch → couch}/types.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { CourseConfig, ClassroomConfig, CourseElo,
|
|
1
|
+
import { CourseConfig, ClassroomConfig, CourseElo, Status, SkuilderCourseData as SkuilderCourseData$1, DataShape } from '@vue-skuilder/common';
|
|
2
2
|
import { Moment } from 'moment';
|
|
3
|
+
import { S as SkuilderCourseData, D as DocType, a as TagStub, T as Tag, g as CardHistory, C as CardRecord } from './types-legacy-CTsJvvxI.mjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Admin functionality
|
|
@@ -181,139 +182,6 @@ interface ContentBase {
|
|
|
181
182
|
courseID: string;
|
|
182
183
|
}
|
|
183
184
|
|
|
184
|
-
declare const GuestUsername: string;
|
|
185
|
-
declare const log: (message: string) => void;
|
|
186
|
-
declare enum DocType {
|
|
187
|
-
DISPLAYABLE_DATA = "DISPLAYABLE_DATA",
|
|
188
|
-
CARD = "CARD",
|
|
189
|
-
DATASHAPE = "DATASHAPE",
|
|
190
|
-
QUESTIONTYPE = "QUESTION",
|
|
191
|
-
VIEW = "VIEW",
|
|
192
|
-
PEDAGOGY = "PEDAGOGY",
|
|
193
|
-
CARDRECORD = "CARDRECORD",
|
|
194
|
-
SCHEDULED_CARD = "SCHEDULED_CARD",
|
|
195
|
-
TAG = "TAG",
|
|
196
|
-
NAVIGATION_STRATEGY = "NAVIGATION_STRATEGY"
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Interface for all data on course content and pedagogy stored
|
|
200
|
-
* in the c/pouch database.
|
|
201
|
-
*/
|
|
202
|
-
interface SkuilderCourseData {
|
|
203
|
-
course: string;
|
|
204
|
-
docType: DocType;
|
|
205
|
-
}
|
|
206
|
-
interface Tag extends SkuilderCourseData {
|
|
207
|
-
docType: DocType.TAG;
|
|
208
|
-
name: string;
|
|
209
|
-
snippet: string;
|
|
210
|
-
wiki: string;
|
|
211
|
-
taggedCards: PouchDB.Core.DocumentId[];
|
|
212
|
-
}
|
|
213
|
-
interface TagStub {
|
|
214
|
-
name: string;
|
|
215
|
-
snippet: string;
|
|
216
|
-
count: number;
|
|
217
|
-
}
|
|
218
|
-
interface CardData extends SkuilderCourseData {
|
|
219
|
-
docType: DocType.CARD;
|
|
220
|
-
id_displayable_data: PouchDB.Core.DocumentId[];
|
|
221
|
-
id_view: PouchDB.Core.DocumentId;
|
|
222
|
-
elo: CourseElo;
|
|
223
|
-
}
|
|
224
|
-
/** A list of populated courses in the DB */
|
|
225
|
-
interface CourseListData extends PouchDB.Core.Response {
|
|
226
|
-
courses: string[];
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* The data used to hydrate viewable components (questions, info, etc)
|
|
230
|
-
*/
|
|
231
|
-
interface DisplayableData extends SkuilderCourseData {
|
|
232
|
-
docType: DocType.DISPLAYABLE_DATA;
|
|
233
|
-
author?: string;
|
|
234
|
-
id_datashape: PouchDB.Core.DocumentId;
|
|
235
|
-
data: Field[];
|
|
236
|
-
_attachments?: {
|
|
237
|
-
[index: string]: PouchDB.Core.FullAttachment;
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
interface Field {
|
|
241
|
-
data: unknown;
|
|
242
|
-
name: string;
|
|
243
|
-
}
|
|
244
|
-
interface DataShapeData extends SkuilderCourseData {
|
|
245
|
-
docType: DocType.DATASHAPE;
|
|
246
|
-
_id: PouchDB.Core.DocumentId;
|
|
247
|
-
questionTypes: PouchDB.Core.DocumentId[];
|
|
248
|
-
}
|
|
249
|
-
interface QuestionData extends SkuilderCourseData {
|
|
250
|
-
docType: DocType.QUESTIONTYPE;
|
|
251
|
-
_id: PouchDB.Core.DocumentId;
|
|
252
|
-
viewList: string[];
|
|
253
|
-
dataShapeList: PouchDB.Core.DocumentId[];
|
|
254
|
-
}
|
|
255
|
-
declare const cardHistoryPrefix = "cardH";
|
|
256
|
-
interface CardHistory<T extends CardRecord> {
|
|
257
|
-
_id: PouchDB.Core.DocumentId;
|
|
258
|
-
/**
|
|
259
|
-
* The CouchDB id of the card
|
|
260
|
-
*/
|
|
261
|
-
cardID: PouchDB.Core.DocumentId;
|
|
262
|
-
/**
|
|
263
|
-
* The ID of the course
|
|
264
|
-
*/
|
|
265
|
-
courseID: string;
|
|
266
|
-
/**
|
|
267
|
-
* The to-date largest interval between successful
|
|
268
|
-
* card reviews. `0` indicates no successful reviews.
|
|
269
|
-
*/
|
|
270
|
-
bestInterval: number;
|
|
271
|
-
/**
|
|
272
|
-
* The number of times that a card has been
|
|
273
|
-
* failed in review
|
|
274
|
-
*/
|
|
275
|
-
lapses: number;
|
|
276
|
-
/**
|
|
277
|
-
* The number of consecutive successful impressions
|
|
278
|
-
* on this card
|
|
279
|
-
*/
|
|
280
|
-
streak: number;
|
|
281
|
-
records: T[];
|
|
282
|
-
}
|
|
283
|
-
interface CardRecord {
|
|
284
|
-
/**
|
|
285
|
-
* The CouchDB id of the card
|
|
286
|
-
*/
|
|
287
|
-
cardID: string;
|
|
288
|
-
/**
|
|
289
|
-
* The ID of the course
|
|
290
|
-
*/
|
|
291
|
-
courseID: string;
|
|
292
|
-
/**
|
|
293
|
-
* Number of milliseconds that the user spent before dismissing
|
|
294
|
-
* the card (ie, "I've read this" or "here is my answer")
|
|
295
|
-
*
|
|
296
|
-
* //TODO: this (sometimes?) wants to be replaced with a rich
|
|
297
|
-
* recording of user activity in working the question
|
|
298
|
-
*/
|
|
299
|
-
timeSpent: number;
|
|
300
|
-
/**
|
|
301
|
-
* The date-time that the card was rendered. timeStamp + timeSpent will give the
|
|
302
|
-
* time of user submission.
|
|
303
|
-
*/
|
|
304
|
-
timeStamp: Moment;
|
|
305
|
-
}
|
|
306
|
-
interface QuestionRecord extends CardRecord, Evaluation {
|
|
307
|
-
userAnswer: Answer;
|
|
308
|
-
/**
|
|
309
|
-
* The number of incorrect user submissions prededing this submisstion.
|
|
310
|
-
*
|
|
311
|
-
* eg, if a user is asked 7*6=__, submitting 46, 48, 42 will result in three
|
|
312
|
-
* records being created having 0, 1, and 2 as their recorded 'priorAttempts' values
|
|
313
|
-
*/
|
|
314
|
-
priorAttemps: number;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
185
|
interface DataLayerResult {
|
|
318
186
|
status: Status;
|
|
319
187
|
message: string;
|
|
@@ -471,12 +339,6 @@ interface CourseDBInterface extends NavigationStrategyManager {
|
|
|
471
339
|
}[]>;
|
|
472
340
|
}
|
|
473
341
|
|
|
474
|
-
declare abstract class Loggable {
|
|
475
|
-
protected abstract readonly _className: string;
|
|
476
|
-
protected log(...args: unknown[]): void;
|
|
477
|
-
protected error(...args: unknown[]): void;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
342
|
type Update<T> = Partial<T> | ((x: T) => T);
|
|
481
343
|
|
|
482
344
|
interface DocumentUpdater {
|
|
@@ -621,114 +483,4 @@ interface ClassroomRegistrationDoc {
|
|
|
621
483
|
registrations: ClassroomRegistration[];
|
|
622
484
|
}
|
|
623
485
|
|
|
624
|
-
|
|
625
|
-
* Main factory interface for data access
|
|
626
|
-
*/
|
|
627
|
-
interface DataLayerProvider {
|
|
628
|
-
/**
|
|
629
|
-
* Get the user database interface
|
|
630
|
-
*/
|
|
631
|
-
getUserDB(): UserDBInterface;
|
|
632
|
-
/**
|
|
633
|
-
* Get a course database interface
|
|
634
|
-
*/
|
|
635
|
-
getCourseDB(courseId: string): CourseDBInterface;
|
|
636
|
-
/**
|
|
637
|
-
* Get the courses-lookup interface
|
|
638
|
-
*/
|
|
639
|
-
getCoursesDB(): CoursesDBInterface;
|
|
640
|
-
/**
|
|
641
|
-
* Get a classroom database interface
|
|
642
|
-
*/
|
|
643
|
-
getClassroomDB(classId: string, type: 'student' | 'teacher'): Promise<ClassroomDBInterface>;
|
|
644
|
-
/**
|
|
645
|
-
* Get the admin database interface
|
|
646
|
-
*/
|
|
647
|
-
getAdminDB(): AdminDBInterface;
|
|
648
|
-
/**
|
|
649
|
-
* Initialize the data layer
|
|
650
|
-
*/
|
|
651
|
-
initialize(): Promise<void>;
|
|
652
|
-
/**
|
|
653
|
-
* Teardown the data layer
|
|
654
|
-
*/
|
|
655
|
-
teardown(): Promise<void>;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
declare function areQuestionRecords(h: CardHistory<CardRecord>): h is CardHistory<QuestionRecord>;
|
|
659
|
-
declare function isQuestionRecord(c: CardRecord): c is QuestionRecord;
|
|
660
|
-
declare function getCardHistoryID(courseID: string, cardID: string): PouchDB.Core.DocumentId;
|
|
661
|
-
declare function parseCardHistoryID(id: string): {
|
|
662
|
-
courseID: string;
|
|
663
|
-
cardID: string;
|
|
664
|
-
};
|
|
665
|
-
interface PouchDBError extends Error {
|
|
666
|
-
error?: string;
|
|
667
|
-
reason?: string;
|
|
668
|
-
}
|
|
669
|
-
declare function docIsDeleted(e: PouchDBError): boolean;
|
|
670
|
-
|
|
671
|
-
declare enum Navigators {
|
|
672
|
-
ELO = "elo"
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* A content-navigator provides runtime steering of study sessions.
|
|
676
|
-
*/
|
|
677
|
-
declare abstract class ContentNavigator implements StudyContentSource {
|
|
678
|
-
/**
|
|
679
|
-
*
|
|
680
|
-
* @param user
|
|
681
|
-
* @param strategyData
|
|
682
|
-
* @returns the runtime object used to steer a study session.
|
|
683
|
-
*/
|
|
684
|
-
static create(user: UserDBInterface, course: CourseDBInterface, strategyData: ContentNavigationStrategyData): Promise<ContentNavigator>;
|
|
685
|
-
abstract getPendingReviews(): Promise<(StudySessionReviewItem & ScheduledCard)[]>;
|
|
686
|
-
abstract getNewCards(n?: number): Promise<StudySessionNewItem[]>;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* Interface representing the result of a bulk import operation for a single card
|
|
691
|
-
*/
|
|
692
|
-
interface ImportResult {
|
|
693
|
-
/** The original text input for the card */
|
|
694
|
-
originalText: string;
|
|
695
|
-
/** Status of the import operation */
|
|
696
|
-
status: 'success' | 'error';
|
|
697
|
-
/** Message describing the result or error */
|
|
698
|
-
message: string;
|
|
699
|
-
/** ID of the newly created card (only for success) */
|
|
700
|
-
cardId?: string;
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* Configuration for the bulk card processor
|
|
704
|
-
*/
|
|
705
|
-
interface BulkCardProcessorConfig {
|
|
706
|
-
/** The data shape to use for the cards */
|
|
707
|
-
dataShape: DataShape;
|
|
708
|
-
/** The course code used for adding notes */
|
|
709
|
-
courseCode: string;
|
|
710
|
-
/** The username of the current user */
|
|
711
|
-
userName: string;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* Processes multiple cards from bulk text input
|
|
716
|
-
*
|
|
717
|
-
* @param parsedCards - Array of parsed cards to import
|
|
718
|
-
* @param courseDB - Course database interface
|
|
719
|
-
* @param config - Configuration for the card processor
|
|
720
|
-
* @returns Array of import results
|
|
721
|
-
*/
|
|
722
|
-
declare function importParsedCards(parsedCards: ParsedCard[], courseDB: CourseDBInterface, config: BulkCardProcessorConfig): Promise<ImportResult[]>;
|
|
723
|
-
/**
|
|
724
|
-
* Validates the configuration for bulk card processing
|
|
725
|
-
*
|
|
726
|
-
* @param config - Configuration to validate
|
|
727
|
-
* @returns Object with validation result and error message if any
|
|
728
|
-
*/
|
|
729
|
-
declare function validateProcessorConfig(config: Partial<BulkCardProcessorConfig>): {
|
|
730
|
-
isValid: boolean;
|
|
731
|
-
errorMessage?: string;
|
|
732
|
-
};
|
|
733
|
-
|
|
734
|
-
export { getCardHistoryID as $, type AdminDBInterface as A, type SkuilderCourseData as B, type CardRecord as C, type DataLayerProvider as D, type Tag as E, type TagStub as F, GuestUsername as G, type CardData as H, type CourseListData as I, type DisplayableData as J, type Field as K, Loggable as L, type DataShapeData as M, cardHistoryPrefix as N, type CardHistory as O, type QuestionRecord as P, type QuestionData as Q, type UserConfig as R, type StudySessionItem as S, type TeacherClassroomDBInterface as T, type UserDBInterface as U, type ActivityRecord as V, type CourseRegistration as W, type CourseRegistrationDoc as X, type ScheduledCard as Y, areQuestionRecords as Z, isQuestionRecord as _, type StudyContentSource as a, parseCardHistoryID as a0, docIsDeleted as a1, Navigators as a2, ContentNavigator as a3, importParsedCards as a4, validateProcessorConfig as a5, type ImportResult as a6, type BulkCardProcessorConfig as a7, type DocumentUpdater as a8, newInterval as a9, type ClassroomDBInterface as b, type StudentClassroomDBInterface as c, type AssignedContent as d, type AssignedTag as e, type AssignedCourse as f, type AssignedCard as g, type StudySessionFailedItem as h, type StudySessionFailedNewItem as i, type StudySessionFailedReviewItem as j, type StudySessionNewItem as k, type StudySessionReviewItem as l, isReview as m, type ContentSourceID as n, getStudySource as o, type CoursesDBInterface as p, type CourseInfo as q, type CourseDBInterface as r, type UserCourseSettings as s, type UserCourseSetting as t, type UsrCrsDataInterface as u, type ClassroomRegistrationDesignation as v, type ClassroomRegistration as w, type ClassroomRegistrationDoc as x, log as y, DocType as z };
|
|
486
|
+
export { type AdminDBInterface as A, type CourseRegistrationDoc as B, type CourseDBInterface as C, type ScheduledCard as D, type DocumentUpdater as E, newInterval as F, type DataLayerResult as G, type ContentNavigationStrategyData as H, type StudySessionItem as S, type TeacherClassroomDBInterface as T, type UserDBInterface as U, type CoursesDBInterface as a, type ClassroomDBInterface as b, type StudyContentSource as c, type StudentClassroomDBInterface as d, type AssignedContent as e, type AssignedTag as f, type AssignedCourse as g, type AssignedCard as h, type StudySessionFailedItem as i, type StudySessionFailedNewItem as j, type StudySessionFailedReviewItem as k, type StudySessionNewItem as l, type StudySessionReviewItem as m, isReview as n, type ContentSourceID as o, getStudySource as p, type CourseInfo as q, type UserCourseSettings as r, type UserCourseSetting as s, type UsrCrsDataInterface as t, type ClassroomRegistrationDesignation as u, type ClassroomRegistration as v, type ClassroomRegistrationDoc as w, type UserConfig as x, type ActivityRecord as y, type CourseRegistration as z };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { P as PackerConfig, a as PackedCourseData } from '../../types-DIgj8pP7.mjs';
|
|
2
|
+
export { C as ChunkMetadata, D as DesignDocument, I as IndexMetadata, S as StaticCourseManifest } from '../../types-DIgj8pP7.mjs';
|
|
3
|
+
import '@vue-skuilder/common';
|
|
4
|
+
import '../../types-legacy-CTsJvvxI.mjs';
|
|
5
|
+
import 'moment';
|
|
6
|
+
|
|
7
|
+
declare class CouchDBToStaticPacker {
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config?: Partial<PackerConfig>);
|
|
10
|
+
/**
|
|
11
|
+
* Pack a CouchDB course database into static data structures
|
|
12
|
+
*/
|
|
13
|
+
packCourse(sourceDB: PouchDB.Database, courseId: string): Promise<PackedCourseData>;
|
|
14
|
+
private extractCourseConfig;
|
|
15
|
+
private extractDesignDocs;
|
|
16
|
+
private extractDocumentsByType;
|
|
17
|
+
private createChunks;
|
|
18
|
+
private prepareChunkData;
|
|
19
|
+
private buildIndices;
|
|
20
|
+
private buildEloIndex;
|
|
21
|
+
private buildTagIndex;
|
|
22
|
+
private buildViewIndex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { CouchDBToStaticPacker, PackedCourseData, PackerConfig };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { P as PackerConfig, a as PackedCourseData } from '../../types-B0GJsjOr.js';
|
|
2
|
+
export { C as ChunkMetadata, D as DesignDocument, I as IndexMetadata, S as StaticCourseManifest } from '../../types-B0GJsjOr.js';
|
|
3
|
+
import '@vue-skuilder/common';
|
|
4
|
+
import '../../types-legacy-CTsJvvxI.js';
|
|
5
|
+
import 'moment';
|
|
6
|
+
|
|
7
|
+
declare class CouchDBToStaticPacker {
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config?: Partial<PackerConfig>);
|
|
10
|
+
/**
|
|
11
|
+
* Pack a CouchDB course database into static data structures
|
|
12
|
+
*/
|
|
13
|
+
packCourse(sourceDB: PouchDB.Database, courseId: string): Promise<PackedCourseData>;
|
|
14
|
+
private extractCourseConfig;
|
|
15
|
+
private extractDesignDocs;
|
|
16
|
+
private extractDocumentsByType;
|
|
17
|
+
private createChunks;
|
|
18
|
+
private prepareChunkData;
|
|
19
|
+
private buildIndices;
|
|
20
|
+
private buildEloIndex;
|
|
21
|
+
private buildTagIndex;
|
|
22
|
+
private buildViewIndex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { CouchDBToStaticPacker, PackedCourseData, PackerConfig };
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/util/packer/index.ts
|
|
21
|
+
var packer_exports = {};
|
|
22
|
+
__export(packer_exports, {
|
|
23
|
+
CouchDBToStaticPacker: () => CouchDBToStaticPacker
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(packer_exports);
|
|
26
|
+
|
|
27
|
+
// src/util/logger.ts
|
|
28
|
+
var isDevelopment = typeof process !== "undefined" && process.env.NODE_ENV === "development";
|
|
29
|
+
var logger = {
|
|
30
|
+
/**
|
|
31
|
+
* Debug-level logging - only shown in development
|
|
32
|
+
*/
|
|
33
|
+
debug: (message, ...args) => {
|
|
34
|
+
if (isDevelopment) {
|
|
35
|
+
console.debug(`[DB:DEBUG] ${message}`, ...args);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Info-level logging - general information
|
|
40
|
+
*/
|
|
41
|
+
info: (message, ...args) => {
|
|
42
|
+
console.info(`[DB:INFO] ${message}`, ...args);
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* Warning-level logging - potential issues
|
|
46
|
+
*/
|
|
47
|
+
warn: (message, ...args) => {
|
|
48
|
+
console.warn(`[DB:WARN] ${message}`, ...args);
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* Error-level logging - serious problems
|
|
52
|
+
*/
|
|
53
|
+
error: (message, ...args) => {
|
|
54
|
+
console.error(`[DB:ERROR] ${message}`, ...args);
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* Log function for backward compatibility with existing log() usage
|
|
58
|
+
*/
|
|
59
|
+
log: (message, ...args) => {
|
|
60
|
+
if (isDevelopment) {
|
|
61
|
+
console.log(`[DB:LOG] ${message}`, ...args);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/util/packer/CouchDBToStaticPacker.ts
|
|
67
|
+
var CouchDBToStaticPacker = class {
|
|
68
|
+
config;
|
|
69
|
+
constructor(config = {}) {
|
|
70
|
+
this.config = {
|
|
71
|
+
chunkSize: 1e3,
|
|
72
|
+
includeAttachments: true,
|
|
73
|
+
...config
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Pack a CouchDB course database into static data structures
|
|
78
|
+
*/
|
|
79
|
+
async packCourse(sourceDB, courseId) {
|
|
80
|
+
logger.info(`Starting static pack for course: ${courseId}`);
|
|
81
|
+
const manifest = {
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
courseId,
|
|
84
|
+
courseName: "",
|
|
85
|
+
courseConfig: null,
|
|
86
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
87
|
+
documentCount: 0,
|
|
88
|
+
chunks: [],
|
|
89
|
+
indices: [],
|
|
90
|
+
designDocs: []
|
|
91
|
+
};
|
|
92
|
+
const courseConfig = await this.extractCourseConfig(sourceDB);
|
|
93
|
+
manifest.courseName = courseConfig.name;
|
|
94
|
+
manifest.courseConfig = courseConfig;
|
|
95
|
+
manifest.designDocs = await this.extractDesignDocs(sourceDB);
|
|
96
|
+
const docsByType = await this.extractDocumentsByType(sourceDB);
|
|
97
|
+
const chunks = /* @__PURE__ */ new Map();
|
|
98
|
+
for (const [docType, docs] of Object.entries(docsByType)) {
|
|
99
|
+
const chunkMetadata = this.createChunks(docs, docType);
|
|
100
|
+
manifest.chunks.push(...chunkMetadata);
|
|
101
|
+
manifest.documentCount += docs.length;
|
|
102
|
+
this.prepareChunkData(chunkMetadata, docs, chunks);
|
|
103
|
+
}
|
|
104
|
+
const indices = /* @__PURE__ */ new Map();
|
|
105
|
+
manifest.indices = await this.buildIndices(docsByType, manifest.designDocs, indices);
|
|
106
|
+
return {
|
|
107
|
+
manifest,
|
|
108
|
+
chunks,
|
|
109
|
+
indices
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async extractCourseConfig(db) {
|
|
113
|
+
try {
|
|
114
|
+
return await db.get("CourseConfig");
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger.error("Failed to extract course config:", error);
|
|
117
|
+
throw new Error("Course config not found");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async extractDesignDocs(db) {
|
|
121
|
+
const result = await db.allDocs({
|
|
122
|
+
startkey: "_design/",
|
|
123
|
+
endkey: "_design/\uFFF0",
|
|
124
|
+
include_docs: true
|
|
125
|
+
});
|
|
126
|
+
return result.rows.map((row) => ({
|
|
127
|
+
_id: row.id,
|
|
128
|
+
views: row.doc.views || {}
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
async extractDocumentsByType(db) {
|
|
132
|
+
const allDocs = await db.allDocs({ include_docs: true });
|
|
133
|
+
const docsByType = {};
|
|
134
|
+
for (const row of allDocs.rows) {
|
|
135
|
+
if (row.id.startsWith("_")) continue;
|
|
136
|
+
const doc = row.doc;
|
|
137
|
+
if (doc.docType) {
|
|
138
|
+
if (!docsByType[doc.docType]) {
|
|
139
|
+
docsByType[doc.docType] = [];
|
|
140
|
+
}
|
|
141
|
+
docsByType[doc.docType].push(doc);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return docsByType;
|
|
145
|
+
}
|
|
146
|
+
createChunks(docs, docType) {
|
|
147
|
+
const chunks = [];
|
|
148
|
+
const sortedDocs = docs.sort((a, b) => a._id.localeCompare(b._id));
|
|
149
|
+
for (let i = 0; i < sortedDocs.length; i += this.config.chunkSize) {
|
|
150
|
+
const chunk = sortedDocs.slice(i, i + this.config.chunkSize);
|
|
151
|
+
const chunkId = `${docType}-${String(Math.floor(i / this.config.chunkSize)).padStart(4, "0")}`;
|
|
152
|
+
chunks.push({
|
|
153
|
+
id: chunkId,
|
|
154
|
+
docType,
|
|
155
|
+
startKey: chunk[0]._id,
|
|
156
|
+
endKey: chunk[chunk.length - 1]._id,
|
|
157
|
+
documentCount: chunk.length,
|
|
158
|
+
path: `chunks/${chunkId}.json`
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return chunks;
|
|
162
|
+
}
|
|
163
|
+
prepareChunkData(chunkMetadata, docs, chunks) {
|
|
164
|
+
const sortedDocs = docs.sort((a, b) => a._id.localeCompare(b._id));
|
|
165
|
+
for (const chunk of chunkMetadata) {
|
|
166
|
+
const chunkDocs = sortedDocs.filter(
|
|
167
|
+
(doc) => doc._id >= chunk.startKey && doc._id <= chunk.endKey
|
|
168
|
+
);
|
|
169
|
+
const cleanedDocs = chunkDocs.map((doc) => {
|
|
170
|
+
const cleaned = { ...doc };
|
|
171
|
+
delete cleaned._rev;
|
|
172
|
+
if (!this.config.includeAttachments) {
|
|
173
|
+
delete cleaned._attachments;
|
|
174
|
+
}
|
|
175
|
+
return cleaned;
|
|
176
|
+
});
|
|
177
|
+
chunks.set(chunk.id, cleanedDocs);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async buildIndices(docsByType, designDocs, indices) {
|
|
181
|
+
const indexMetadata = [];
|
|
182
|
+
if (docsByType["CARD" /* CARD */]) {
|
|
183
|
+
const eloIndexMeta = await this.buildEloIndex(
|
|
184
|
+
docsByType["CARD" /* CARD */],
|
|
185
|
+
indices
|
|
186
|
+
);
|
|
187
|
+
indexMetadata.push(eloIndexMeta);
|
|
188
|
+
}
|
|
189
|
+
if (docsByType["TAG" /* TAG */]) {
|
|
190
|
+
const tagIndexMeta = await this.buildTagIndex(docsByType["TAG" /* TAG */], indices);
|
|
191
|
+
indexMetadata.push(tagIndexMeta);
|
|
192
|
+
}
|
|
193
|
+
for (const designDoc of designDocs) {
|
|
194
|
+
for (const [viewName, viewDef] of Object.entries(designDoc.views)) {
|
|
195
|
+
if (viewDef.map) {
|
|
196
|
+
const indexMeta = await this.buildViewIndex(
|
|
197
|
+
viewName,
|
|
198
|
+
viewDef.map,
|
|
199
|
+
docsByType,
|
|
200
|
+
indices,
|
|
201
|
+
viewDef.reduce
|
|
202
|
+
);
|
|
203
|
+
if (indexMeta) indexMetadata.push(indexMeta);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return indexMetadata;
|
|
208
|
+
}
|
|
209
|
+
async buildEloIndex(cards, indices) {
|
|
210
|
+
const eloIndex = [];
|
|
211
|
+
for (const card of cards) {
|
|
212
|
+
if (card.elo?.global?.score) {
|
|
213
|
+
eloIndex.push({
|
|
214
|
+
elo: card.elo.global.score,
|
|
215
|
+
cardId: card._id
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
eloIndex.sort((a, b) => a.elo - b.elo);
|
|
220
|
+
const buckets = {};
|
|
221
|
+
const bucketSize = 50;
|
|
222
|
+
for (const entry of eloIndex) {
|
|
223
|
+
const bucket = Math.floor(entry.elo / bucketSize) * bucketSize;
|
|
224
|
+
if (!buckets[bucket]) buckets[bucket] = [];
|
|
225
|
+
buckets[bucket].push(entry.cardId);
|
|
226
|
+
}
|
|
227
|
+
indices.set("elo", {
|
|
228
|
+
sorted: eloIndex,
|
|
229
|
+
buckets,
|
|
230
|
+
stats: {
|
|
231
|
+
min: eloIndex[0]?.elo || 0,
|
|
232
|
+
max: eloIndex[eloIndex.length - 1]?.elo || 0,
|
|
233
|
+
count: eloIndex.length
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
name: "elo",
|
|
238
|
+
type: "btree",
|
|
239
|
+
path: "indices/elo.json"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async buildTagIndex(tags, indices) {
|
|
243
|
+
const tagIndex = {};
|
|
244
|
+
for (const tag of tags) {
|
|
245
|
+
tagIndex[tag.name] = {
|
|
246
|
+
cardIds: tag.taggedCards,
|
|
247
|
+
snippet: tag.snippet,
|
|
248
|
+
count: tag.taggedCards.length
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const cardToTags = {};
|
|
252
|
+
for (const tag of tags) {
|
|
253
|
+
for (const cardId of tag.taggedCards) {
|
|
254
|
+
if (!cardToTags[cardId]) cardToTags[cardId] = [];
|
|
255
|
+
cardToTags[cardId].push(tag.name);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
indices.set("tags", {
|
|
259
|
+
byTag: tagIndex,
|
|
260
|
+
byCard: cardToTags
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
name: "tags",
|
|
264
|
+
type: "hash",
|
|
265
|
+
path: "indices/tags.json"
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
async buildViewIndex(viewName, mapFunction, docsByType, indices, _reduceFunction) {
|
|
269
|
+
try {
|
|
270
|
+
const viewResults = [];
|
|
271
|
+
const emit = (key, value) => {
|
|
272
|
+
viewResults.push({ key, value, id: currentDocId });
|
|
273
|
+
};
|
|
274
|
+
let currentDocId = "";
|
|
275
|
+
const mapFn = new Function("doc", "emit", mapFunction);
|
|
276
|
+
for (const docs of Object.values(docsByType)) {
|
|
277
|
+
for (const doc of docs) {
|
|
278
|
+
currentDocId = doc._id;
|
|
279
|
+
try {
|
|
280
|
+
mapFn(doc, emit);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
logger.warn(`Map function error for doc ${doc._id}:`, error);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
viewResults.sort((a, b) => {
|
|
287
|
+
if (a.key < b.key) return -1;
|
|
288
|
+
if (a.key > b.key) return 1;
|
|
289
|
+
return 0;
|
|
290
|
+
});
|
|
291
|
+
indices.set(`view-${viewName}`, viewResults);
|
|
292
|
+
return {
|
|
293
|
+
name: `view-${viewName}`,
|
|
294
|
+
type: "btree",
|
|
295
|
+
path: `indices/view-${viewName}.json`
|
|
296
|
+
};
|
|
297
|
+
} catch (error) {
|
|
298
|
+
logger.error(`Failed to build index for view ${viewName}:`, error);
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
304
|
+
0 && (module.exports = {
|
|
305
|
+
CouchDBToStaticPacker
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/packer/index.ts","../../../src/util/logger.ts","../../../src/util/packer/CouchDBToStaticPacker.ts"],"sourcesContent":["// packages/db/src/util/packer/index.ts\n\nexport * from './types.js';\nexport { CouchDBToStaticPacker } from './CouchDBToStaticPacker.js';","/**\n * Simple logging utility for @vue-skuilder/db package\n *\n * This utility provides environment-aware logging with ESLint suppressions\n * to resolve console statement violations while maintaining logging functionality.\n */\n\nconst isDevelopment = typeof process !== 'undefined' && process.env.NODE_ENV === 'development';\nconst _isBrowser = typeof window !== 'undefined';\n\nexport const logger = {\n /**\n * Debug-level logging - only shown in development\n */\n debug: (message: string, ...args: any[]): void => {\n if (isDevelopment) {\n // eslint-disable-next-line no-console\n console.debug(`[DB:DEBUG] ${message}`, ...args);\n }\n },\n\n /**\n * Info-level logging - general information\n */\n info: (message: string, ...args: any[]): void => {\n // eslint-disable-next-line no-console\n console.info(`[DB:INFO] ${message}`, ...args);\n },\n\n /**\n * Warning-level logging - potential issues\n */\n warn: (message: string, ...args: any[]): void => {\n // eslint-disable-next-line no-console\n console.warn(`[DB:WARN] ${message}`, ...args);\n },\n\n /**\n * Error-level logging - serious problems\n */\n error: (message: string, ...args: any[]): void => {\n // eslint-disable-next-line no-console\n console.error(`[DB:ERROR] ${message}`, ...args);\n },\n\n /**\n * Log function for backward compatibility with existing log() usage\n */\n log: (message: string, ...args: any[]): void => {\n if (isDevelopment) {\n // eslint-disable-next-line no-console\n console.log(`[DB:LOG] ${message}`, ...args);\n }\n },\n};\n","// packages/db/src/util/packer/CouchDBToStaticPacker.ts\n\nimport { CardData, DocType, Tag } from '../../core/types/types-legacy';\nimport { logger } from '../logger';\n// CourseConfig interface - simplified for packer use\n\nimport { CourseConfig } from '@vue-skuilder/common';\nimport {\n ChunkMetadata,\n DesignDocument,\n IndexMetadata,\n PackedCourseData,\n PackerConfig,\n StaticCourseManifest,\n} from './types';\n\nexport class CouchDBToStaticPacker {\n private config: PackerConfig;\n\n constructor(config: Partial<PackerConfig> = {}) {\n this.config = {\n chunkSize: 1000,\n includeAttachments: true,\n ...config,\n };\n }\n\n /**\n * Pack a CouchDB course database into static data structures\n */\n async packCourse(sourceDB: PouchDB.Database, courseId: string): Promise<PackedCourseData> {\n logger.info(`Starting static pack for course: ${courseId}`);\n\n const manifest: StaticCourseManifest = {\n version: '1.0.0',\n courseId,\n courseName: '',\n courseConfig: null,\n lastUpdated: new Date().toISOString(),\n documentCount: 0,\n chunks: [],\n indices: [],\n designDocs: [],\n };\n\n // 1. Extract course config\n const courseConfig = await this.extractCourseConfig(sourceDB);\n manifest.courseName = courseConfig.name;\n manifest.courseConfig = courseConfig;\n\n // 2. Extract and process design documents\n manifest.designDocs = await this.extractDesignDocs(sourceDB);\n\n // 3. Extract all documents by type and create chunks\n const docsByType = await this.extractDocumentsByType(sourceDB);\n\n // 4. Create chunks and prepare chunk data\n const chunks = new Map<string, any[]>();\n for (const [docType, docs] of Object.entries(docsByType)) {\n const chunkMetadata = this.createChunks(docs, docType as DocType);\n manifest.chunks.push(...chunkMetadata);\n manifest.documentCount += docs.length;\n\n // Prepare chunk data\n this.prepareChunkData(chunkMetadata, docs, chunks);\n }\n\n // 5. Build indices\n const indices = new Map<string, any>();\n manifest.indices = await this.buildIndices(docsByType, manifest.designDocs, indices);\n\n return {\n manifest,\n chunks,\n indices,\n };\n }\n\n private async extractCourseConfig(db: PouchDB.Database): Promise<CourseConfig> {\n try {\n return await db.get<CourseConfig>('CourseConfig');\n } catch (error) {\n logger.error('Failed to extract course config:', error);\n throw new Error('Course config not found');\n }\n }\n\n private async extractDesignDocs(db: PouchDB.Database): Promise<DesignDocument[]> {\n const result = await db.allDocs({\n startkey: '_design/',\n endkey: '_design/\\ufff0',\n include_docs: true,\n });\n\n return result.rows.map((row) => ({\n _id: row.id,\n views: (row.doc as any).views || {},\n }));\n }\n\n private async extractDocumentsByType(db: PouchDB.Database): Promise<Record<DocType, any[]>> {\n const allDocs = await db.allDocs({ include_docs: true });\n const docsByType: Record<string, any[]> = {};\n\n for (const row of allDocs.rows) {\n if (row.id.startsWith('_')) continue; // Skip design docs\n\n const doc = row.doc as any;\n if (doc.docType) {\n if (!docsByType[doc.docType]) {\n docsByType[doc.docType] = [];\n }\n docsByType[doc.docType].push(doc);\n }\n }\n\n return docsByType as Record<DocType, any[]>;\n }\n\n private createChunks(docs: any[], docType: DocType): ChunkMetadata[] {\n const chunks: ChunkMetadata[] = [];\n const sortedDocs = docs.sort((a, b) => a._id.localeCompare(b._id));\n\n for (let i = 0; i < sortedDocs.length; i += this.config.chunkSize) {\n const chunk = sortedDocs.slice(i, i + this.config.chunkSize);\n const chunkId = `${docType}-${String(Math.floor(i / this.config.chunkSize)).padStart(4, '0')}`;\n\n chunks.push({\n id: chunkId,\n docType,\n startKey: chunk[0]._id,\n endKey: chunk[chunk.length - 1]._id,\n documentCount: chunk.length,\n path: `chunks/${chunkId}.json`,\n });\n }\n\n return chunks;\n }\n\n private prepareChunkData(\n chunkMetadata: ChunkMetadata[],\n docs: any[],\n chunks: Map<string, any[]>\n ): void {\n const sortedDocs = docs.sort((a, b) => a._id.localeCompare(b._id));\n\n for (const chunk of chunkMetadata) {\n const chunkDocs = sortedDocs.filter(\n (doc) => doc._id >= chunk.startKey && doc._id <= chunk.endKey\n );\n\n // Clean documents for storage\n const cleanedDocs = chunkDocs.map((doc) => {\n const cleaned = { ...doc };\n delete cleaned._rev; // Remove revision info\n if (!this.config.includeAttachments) {\n delete cleaned._attachments;\n }\n return cleaned;\n });\n\n chunks.set(chunk.id, cleanedDocs);\n }\n }\n\n private async buildIndices(\n docsByType: Record<DocType, any[]>,\n designDocs: DesignDocument[],\n indices: Map<string, any>\n ): Promise<IndexMetadata[]> {\n const indexMetadata: IndexMetadata[] = [];\n\n // Build ELO index\n if (docsByType[DocType.CARD]) {\n const eloIndexMeta = await this.buildEloIndex(\n docsByType[DocType.CARD] as CardData[],\n indices\n );\n indexMetadata.push(eloIndexMeta);\n }\n\n // Build tag indices\n if (docsByType[DocType.TAG]) {\n const tagIndexMeta = await this.buildTagIndex(docsByType[DocType.TAG] as Tag[], indices);\n indexMetadata.push(tagIndexMeta);\n }\n\n // Build indices from design documents\n for (const designDoc of designDocs) {\n for (const [viewName, viewDef] of Object.entries(designDoc.views)) {\n if (viewDef.map) {\n const indexMeta = await this.buildViewIndex(\n viewName,\n viewDef.map,\n docsByType,\n indices,\n viewDef.reduce\n );\n if (indexMeta) indexMetadata.push(indexMeta);\n }\n }\n }\n\n return indexMetadata;\n }\n\n private async buildEloIndex(\n cards: CardData[],\n indices: Map<string, any>\n ): Promise<IndexMetadata> {\n // Build a B-tree like structure for ELO queries\n const eloIndex: Array<{ elo: number; cardId: string }> = [];\n\n for (const card of cards) {\n if (card.elo?.global?.score) {\n eloIndex.push({\n elo: card.elo.global.score,\n cardId: (card as any)._id,\n });\n }\n }\n\n // Sort by ELO for efficient range queries\n eloIndex.sort((a, b) => a.elo - b.elo);\n\n // Create buckets for faster lookup\n const buckets: Record<number, string[]> = {};\n const bucketSize = 50; // ELO points per bucket\n\n for (const entry of eloIndex) {\n const bucket = Math.floor(entry.elo / bucketSize) * bucketSize;\n if (!buckets[bucket]) buckets[bucket] = [];\n buckets[bucket].push(entry.cardId);\n }\n\n // Store the index data\n indices.set('elo', {\n sorted: eloIndex,\n buckets: buckets,\n stats: {\n min: eloIndex[0]?.elo || 0,\n max: eloIndex[eloIndex.length - 1]?.elo || 0,\n count: eloIndex.length,\n },\n });\n\n return {\n name: 'elo',\n type: 'btree',\n path: 'indices/elo.json',\n };\n }\n\n private async buildTagIndex(tags: Tag[], indices: Map<string, any>): Promise<IndexMetadata> {\n // Build inverted index for tags\n const tagIndex: Record<\n string,\n {\n cardIds: string[];\n snippet: string;\n count: number;\n }\n > = {};\n\n for (const tag of tags) {\n tagIndex[tag.name] = {\n cardIds: tag.taggedCards,\n snippet: tag.snippet,\n count: tag.taggedCards.length,\n };\n }\n\n // Also build a reverse index (card -> tags)\n const cardToTags: Record<string, string[]> = {};\n for (const tag of tags) {\n for (const cardId of tag.taggedCards) {\n if (!cardToTags[cardId]) cardToTags[cardId] = [];\n cardToTags[cardId].push(tag.name);\n }\n }\n\n indices.set('tags', {\n byTag: tagIndex,\n byCard: cardToTags,\n });\n\n return {\n name: 'tags',\n type: 'hash',\n path: 'indices/tags.json',\n };\n }\n\n private async buildViewIndex(\n viewName: string,\n mapFunction: string,\n docsByType: Record<DocType, any[]>,\n indices: Map<string, any>,\n _reduceFunction?: string\n ): Promise<IndexMetadata | null> {\n try {\n // Parse and execute the map function in a sandboxed way\n // This is a simplified version - in production you'd want proper sandboxing\n const viewResults: Array<{ key: any; value: any; id: string }> = [];\n\n // Create a safe emit function\n const emit = (key: any, value: any) => {\n viewResults.push({ key, value, id: currentDocId });\n };\n\n let currentDocId = '';\n\n // Create the map function\n // Note: This is simplified and would need proper sandboxing in production\n const mapFn = new Function('doc', 'emit', mapFunction);\n\n // Run map function on all documents\n for (const docs of Object.values(docsByType)) {\n for (const doc of docs) {\n currentDocId = doc._id;\n try {\n mapFn(doc, emit);\n } catch (error) {\n logger.warn(`Map function error for doc ${doc._id}:`, error);\n }\n }\n }\n\n // Sort by key for efficient querying\n viewResults.sort((a, b) => {\n if (a.key < b.key) return -1;\n if (a.key > b.key) return 1;\n return 0;\n });\n\n indices.set(`view-${viewName}`, viewResults);\n\n return {\n name: `view-${viewName}`,\n type: 'btree',\n path: `indices/view-${viewName}.json`,\n };\n } catch (error) {\n logger.error(`Failed to build index for view ${viewName}:`, error);\n return null;\n }\n }\n}\n"],"mappings":"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","names":[]}
|