@vue-skuilder/db 0.1.6 → 0.1.8-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/{SyncStrategy-DnJRj-Xp.d.mts → SyncStrategy-CyATpyLQ.d.mts} +6 -0
  2. package/dist/{SyncStrategy-DnJRj-Xp.d.ts → SyncStrategy-CyATpyLQ.d.ts} +6 -0
  3. package/dist/core/index.d.mts +5 -5
  4. package/dist/core/index.d.ts +5 -5
  5. package/dist/core/index.js +825 -762
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +812 -750
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BZmLyBVw.d.mts → dataLayerProvider-BInqI_RF.d.mts} +1 -1
  10. package/dist/{dataLayerProvider-BuntXkCs.d.ts → dataLayerProvider-DqtNroSh.d.ts} +1 -1
  11. package/dist/impl/couch/index.d.mts +6 -6
  12. package/dist/impl/couch/index.d.ts +6 -6
  13. package/dist/impl/couch/index.js +2261 -2081
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +2274 -2095
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/index.d.mts +8 -6
  18. package/dist/impl/static/index.d.ts +8 -6
  19. package/dist/impl/static/index.js +524 -1064
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +515 -1058
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/index-CLL31bEy.d.ts +137 -0
  24. package/dist/index-CUNnL38E.d.mts +137 -0
  25. package/dist/index.d.mts +200 -9
  26. package/dist/index.d.ts +200 -9
  27. package/dist/index.js +4123 -2820
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +4119 -2830
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/{types-D6SnlHPm.d.ts → types-BefDGkKa.d.ts} +1 -1
  32. package/dist/{types-DPRvCrIk.d.mts → types-DC-ckZug.d.mts} +1 -1
  33. package/dist/{types-legacy-WPe8CtO-.d.mts → types-legacy-Birv-Jx6.d.mts} +2 -2
  34. package/dist/{types-legacy-WPe8CtO-.d.ts → types-legacy-Birv-Jx6.d.ts} +2 -2
  35. package/dist/{userDB-D9EuWTp1.d.ts → userDB-C33Hzjgn.d.mts} +11 -4
  36. package/dist/{userDB-31gsvxyd.d.mts → userDB-DusL7OXe.d.ts} +11 -4
  37. package/dist/util/packer/index.d.mts +3 -63
  38. package/dist/util/packer/index.d.ts +3 -63
  39. package/dist/util/packer/index.js +53 -1
  40. package/dist/util/packer/index.js.map +1 -1
  41. package/dist/util/packer/index.mjs +53 -1
  42. package/dist/util/packer/index.mjs.map +1 -1
  43. package/package.json +7 -4
  44. package/src/core/types/types-legacy.ts +13 -1
  45. package/src/core/types/user.ts +9 -2
  46. package/src/core/util/index.ts +5 -4
  47. package/src/factory.ts +25 -0
  48. package/src/impl/common/BaseUserDB.ts +62 -28
  49. package/src/impl/common/SyncStrategy.ts +7 -0
  50. package/src/impl/common/index.ts +0 -1
  51. package/src/impl/common/userDBHelpers.ts +15 -5
  52. package/src/impl/couch/CouchDBSyncStrategy.ts +10 -0
  53. package/src/impl/couch/courseAPI.ts +7 -6
  54. package/src/impl/couch/courseLookupDB.ts +24 -0
  55. package/src/impl/couch/index.ts +10 -5
  56. package/src/impl/couch/updateQueue.ts +12 -8
  57. package/src/impl/couch/user-course-relDB.ts +17 -27
  58. package/src/impl/static/NoOpSyncStrategy.ts +5 -0
  59. package/src/impl/static/StaticDataUnpacker.ts +18 -36
  60. package/src/impl/static/courseDB.ts +135 -17
  61. package/src/util/dataDirectory.test.ts +53 -0
  62. package/src/util/dataDirectory.ts +52 -0
  63. package/src/util/index.ts +3 -0
  64. package/src/util/migrator/FileSystemAdapter.ts +79 -0
  65. package/src/util/migrator/StaticToCouchDBMigrator.ts +713 -0
  66. package/src/util/migrator/index.ts +18 -0
  67. package/src/util/migrator/types.ts +84 -0
  68. package/src/util/migrator/validation.ts +517 -0
  69. package/src/util/packer/CouchDBToStaticPacker.ts +92 -2
  70. package/src/util/tuiLogger.ts +139 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/logger.ts","../../../src/util/packer/CouchDBToStaticPacker.ts"],"sourcesContent":["/**\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 AttachmentData,\n} from './types';\n\nexport class CouchDBToStaticPacker {\n private config: PackerConfig;\n private sourceDB: PouchDB.Database | null = null;\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 this.sourceDB = sourceDB;\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. Extract attachments if enabled\n const attachments = new Map<string, AttachmentData>();\n if (this.config.includeAttachments) {\n await this.extractAllAttachments(docsByType, attachments);\n }\n\n // 5. 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 // 6. 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 attachments,\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 \n if (this.config.includeAttachments && cleaned._attachments) {\n // Transform attachment stubs to file paths\n cleaned._attachments = this.transformAttachmentStubs(cleaned._attachments, cleaned._id);\n } else if (!this.config.includeAttachments) {\n delete cleaned._attachments;\n }\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 using CouchDB view queries\n for (const designDoc of designDocs) {\n for (const [viewName, viewDef] of Object.entries(designDoc.views)) {\n if (viewDef.map) {\n logger.info(`Processing view: ${designDoc._id}/${viewName}`);\n const indexMeta = await this.buildViewIndex(viewName, designDoc, indices);\n if (indexMeta) {\n indexMetadata.push(indexMeta);\n logger.info(`Successfully built index: ${indexMeta.name}`);\n } else {\n logger.warn(`Skipped view index: ${designDoc._id}/${viewName}`);\n }\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 /**\n * Build view index by querying CouchDB views directly instead of parsing map functions\n */\n private async buildViewIndex(\n viewName: string,\n designDoc: DesignDocument,\n indices: Map<string, any>\n ): Promise<IndexMetadata | null> {\n if (!this.sourceDB) {\n logger.error('Source database not available for view querying');\n return null;\n }\n\n try {\n const designDocId = designDoc._id; // e.g., \"_design/elo\"\n const viewPath = `${designDocId}/${viewName}`;\n \n logger.info(`Querying CouchDB view: ${viewPath}`);\n \n // Query the view directly from CouchDB\n const viewResults = await this.sourceDB.query(viewPath, {\n include_docs: false,\n });\n\n if (!viewResults.rows || viewResults.rows.length === 0) {\n logger.warn(`View ${viewPath} returned no results`);\n return null;\n }\n\n logger.info(`Successfully queried view ${viewPath}: ${viewResults.rows.length} results`);\n\n // Format the results for static consumption\n const formattedResults = this.formatViewResults(viewName, viewResults.rows, designDoc);\n \n const indexName = `view-${designDoc._id.replace('_design/', '')}-${viewName}`;\n indices.set(indexName, formattedResults);\n\n return {\n name: indexName,\n type: 'view',\n path: `indices/${indexName}.json`,\n };\n } catch (error) {\n logger.error(`Failed to query view ${designDoc._id}/${viewName}:`, error);\n // Return null to gracefully skip this view rather than failing the entire pack\n return null;\n }\n }\n\n /**\n * Format CouchDB view results for static consumption\n */\n private formatViewResults(\n viewName: string,\n viewRows: Array<{ key: any; value: any; id: string }>,\n designDoc: DesignDocument\n ): any {\n const baseResult = {\n type: 'couchdb-view',\n viewName,\n designDoc: designDoc._id,\n results: viewRows,\n metadata: {\n resultCount: viewRows.length,\n generatedAt: new Date().toISOString(),\n },\n };\n\n // Apply view-specific formatting\n switch (viewName) {\n case 'elo':\n return this.formatEloViewIndex(viewRows, baseResult);\n case 'getTags':\n return this.formatTagsViewIndex(viewRows, baseResult);\n case 'cardsByInexperience':\n return this.formatInexperienceViewIndex(viewRows, baseResult);\n default:\n return this.formatGenericViewIndex(viewRows, baseResult);\n }\n }\n\n /**\n * Format ELO view results - convert to sorted array format\n */\n private formatEloViewIndex(\n viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n // Sort by ELO score (key) for efficient range queries\n const sortedResults = viewRows.sort((a, b) => {\n if (typeof a.key === 'number' && typeof b.key === 'number') {\n return a.key - b.key;\n }\n return 0;\n });\n\n return {\n ...baseResult,\n sorted: sortedResults,\n stats: {\n min: sortedResults[0]?.key || 0,\n max: sortedResults[sortedResults.length - 1]?.key || 0,\n count: sortedResults.length,\n },\n };\n }\n\n /**\n * Format tags view results - convert to tag mapping format\n */\n private formatTagsViewIndex(\n viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n // Group by tag name (key)\n const tagMap: Record<string, string[]> = {};\n \n for (const row of viewRows) {\n const tagName = row.key;\n if (typeof tagName === 'string') {\n if (!tagMap[tagName]) {\n tagMap[tagName] = [];\n }\n tagMap[tagName].push(row.id);\n }\n }\n\n return {\n ...baseResult,\n byTag: tagMap,\n tagCount: Object.keys(tagMap).length,\n };\n }\n\n /**\n * Format inexperience view results - convert to experience-based format\n */\n private formatInexperienceViewIndex(\n viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n // Sort by inexperience level (key) - lower numbers = less experience\n const sortedResults = viewRows.sort((a, b) => {\n if (typeof a.key === 'number' && typeof b.key === 'number') {\n return a.key - b.key;\n }\n return 0;\n });\n\n return {\n ...baseResult,\n sorted: sortedResults,\n stats: {\n minInexperience: sortedResults[0]?.key || 0,\n maxInexperience: sortedResults[sortedResults.length - 1]?.key || 0,\n count: sortedResults.length,\n },\n };\n }\n\n /**\n * Format generic view results - fallback for unknown views\n */\n private formatGenericViewIndex(\n _viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n return {\n ...baseResult,\n // Keep results as-is for unknown view types\n };\n }\n\n /**\n * Extract all attachments from documents and download binary data\n */\n private async extractAllAttachments(\n docsByType: Record<DocType, any[]>,\n attachments: Map<string, AttachmentData>\n ): Promise<void> {\n logger.info('Extracting attachments...');\n \n const allDocs: any[] = [];\n for (const docs of Object.values(docsByType)) {\n allDocs.push(...docs);\n }\n\n const docsWithAttachments = allDocs.filter(doc => doc._attachments && Object.keys(doc._attachments).length > 0);\n \n if (docsWithAttachments.length === 0) {\n logger.info('No attachments found');\n return;\n }\n\n logger.info(`Found ${docsWithAttachments.length} documents with attachments`);\n\n // Process attachments concurrently\n const extractionPromises = docsWithAttachments.map(doc => \n this.extractDocumentAttachments(doc, attachments)\n );\n\n await Promise.all(extractionPromises);\n \n logger.info(`Extracted ${attachments.size} attachment files`);\n }\n\n /**\n * Extract attachments for a single document\n */\n private async extractDocumentAttachments(\n doc: any,\n attachments: Map<string, AttachmentData>\n ): Promise<void> {\n if (!doc._attachments || !this.sourceDB) {\n return;\n }\n\n const docId = doc._id;\n \n for (const [attachmentName, metadata] of Object.entries(doc._attachments as Record<string, any>)) {\n try {\n // Download attachment binary data\n const attachmentResponse = await this.sourceDB.getAttachment(docId, attachmentName);\n \n // Convert to buffer\n let buffer: Buffer;\n if (attachmentResponse instanceof ArrayBuffer) {\n buffer = Buffer.from(attachmentResponse);\n } else if (Buffer.isBuffer(attachmentResponse)) {\n buffer = attachmentResponse;\n } else {\n // For browser environments, the response might be a Blob\n const blob = attachmentResponse as Blob;\n buffer = Buffer.from(await blob.arrayBuffer());\n }\n\n // Generate filename with proper extension\n const extension = this.getFileExtension(metadata.content_type);\n const filename = `${attachmentName}${extension}`;\n const attachmentPath = `attachments/${docId}/${filename}`;\n \n // Store attachment data\n attachments.set(attachmentPath, {\n docId,\n attachmentName,\n filename,\n path: attachmentPath,\n contentType: metadata.content_type,\n length: metadata.length || buffer.length,\n digest: metadata.digest,\n buffer,\n });\n\n logger.debug(`Extracted attachment: ${attachmentPath}`);\n } catch (error) {\n logger.error(`Failed to extract attachment ${docId}/${attachmentName}:`, error);\n throw new Error(`Failed to extract attachment ${docId}/${attachmentName}: ${error}`);\n }\n }\n }\n\n /**\n * Transform attachment stubs to include file paths\n */\n private transformAttachmentStubs(\n attachments: Record<string, any>,\n docId: string\n ): Record<string, any> {\n const transformed: Record<string, any> = {};\n \n for (const [attachmentName, metadata] of Object.entries(attachments)) {\n const extension = this.getFileExtension(metadata.content_type);\n const filename = `${attachmentName}${extension}`;\n const attachmentPath = `attachments/${docId}/${filename}`;\n \n transformed[attachmentName] = {\n path: attachmentPath,\n content_type: metadata.content_type,\n length: metadata.length,\n digest: metadata.digest,\n stub: false, // No longer a stub - we have the actual file\n };\n }\n \n return transformed;\n }\n\n /**\n * Get file extension from content type\n */\n private getFileExtension(contentType: string): string {\n const extensionMap: Record<string, string> = {\n 'image/jpeg': '.jpg',\n 'image/jpg': '.jpg', \n 'image/png': '.png',\n 'image/gif': '.gif',\n 'image/webp': '.webp',\n 'audio/mpeg': '.mp3',\n 'audio/mp3': '.mp3',\n 'audio/wav': '.wav',\n 'audio/ogg': '.ogg',\n 'video/mp4': '.mp4',\n 'video/webm': '.webm',\n 'application/pdf': '.pdf',\n 'text/plain': '.txt',\n 'application/json': '.json',\n };\n \n return extensionMap[contentType] || '';\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/util/logger.ts","../../../src/util/packer/CouchDBToStaticPacker.ts"],"sourcesContent":["/**\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 AttachmentData,\n} from './types';\nimport { FileSystemAdapter } from '../migrator/FileSystemAdapter';\n\nexport class CouchDBToStaticPacker {\n private config: PackerConfig;\n private sourceDB: PouchDB.Database | null = null;\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 this.sourceDB = sourceDB;\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. Extract attachments if enabled\n const attachments = new Map<string, AttachmentData>();\n if (this.config.includeAttachments) {\n await this.extractAllAttachments(docsByType, attachments);\n }\n\n // 5. 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 // 6. 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 attachments,\n };\n }\n\n /**\n * Pack a CouchDB course database and write the static files to disk\n */\n async packCourseToFiles(\n sourceDB: PouchDB.Database, \n courseId: string, \n outputDir: string, \n fsAdapter: FileSystemAdapter\n ): Promise<{ \n manifest: StaticCourseManifest; \n filesWritten: number; \n attachmentsFound: number; \n }> {\n logger.info(`Packing course ${courseId} to files in ${outputDir}`);\n \n // First, pack the course data\n const packedData = await this.packCourse(sourceDB, courseId);\n \n // Write the files using the FileSystemAdapter\n const filesWritten = await this.writePackedDataToFiles(packedData, outputDir, fsAdapter);\n \n return {\n manifest: packedData.manifest,\n filesWritten,\n attachmentsFound: packedData.attachments ? packedData.attachments.size : 0,\n };\n }\n\n /**\n * Write packed course data to files using FileSystemAdapter\n */\n private async writePackedDataToFiles(\n packedData: PackedCourseData,\n outputDir: string,\n fsAdapter: FileSystemAdapter\n ): Promise<number> {\n let totalFiles = 0;\n \n // Ensure output directory exists\n await fsAdapter.ensureDir(outputDir);\n \n // Write manifest\n const manifestPath = fsAdapter.joinPath(outputDir, 'manifest.json');\n await fsAdapter.writeJson(manifestPath, packedData.manifest, { spaces: 2 });\n totalFiles++;\n logger.info(`Wrote manifest: ${manifestPath}`);\n \n // Create subdirectories\n const chunksDir = fsAdapter.joinPath(outputDir, 'chunks');\n const indicesDir = fsAdapter.joinPath(outputDir, 'indices');\n await fsAdapter.ensureDir(chunksDir);\n await fsAdapter.ensureDir(indicesDir);\n \n // Write chunks\n for (const [chunkId, chunkData] of packedData.chunks) {\n const chunkPath = fsAdapter.joinPath(chunksDir, `${chunkId}.json`);\n await fsAdapter.writeJson(chunkPath, chunkData);\n totalFiles++;\n }\n logger.info(`Wrote ${packedData.chunks.size} chunk files`);\n \n // Write indices\n for (const [indexName, indexData] of packedData.indices) {\n const indexPath = fsAdapter.joinPath(indicesDir, `${indexName}.json`);\n await fsAdapter.writeJson(indexPath, indexData, { spaces: 2 });\n totalFiles++;\n }\n logger.info(`Wrote ${packedData.indices.size} index files`);\n \n // Write attachments\n if (packedData.attachments && packedData.attachments.size > 0) {\n for (const [attachmentPath, attachmentData] of packedData.attachments) {\n const fullAttachmentPath = fsAdapter.joinPath(outputDir, attachmentPath);\n \n // Ensure attachment directory exists\n const attachmentDir = fsAdapter.dirname(fullAttachmentPath);\n await fsAdapter.ensureDir(attachmentDir);\n \n // Write binary file\n await fsAdapter.writeFile(fullAttachmentPath, attachmentData.buffer);\n totalFiles++;\n }\n logger.info(`Wrote ${packedData.attachments.size} attachment files`);\n }\n \n return totalFiles;\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 \n if (this.config.includeAttachments && cleaned._attachments) {\n // Transform attachment stubs to file paths\n cleaned._attachments = this.transformAttachmentStubs(cleaned._attachments, cleaned._id);\n } else if (!this.config.includeAttachments) {\n delete cleaned._attachments;\n }\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 using CouchDB view queries\n for (const designDoc of designDocs) {\n for (const [viewName, viewDef] of Object.entries(designDoc.views)) {\n if (viewDef.map) {\n logger.info(`Processing view: ${designDoc._id}/${viewName}`);\n const indexMeta = await this.buildViewIndex(viewName, designDoc, indices);\n if (indexMeta) {\n indexMetadata.push(indexMeta);\n logger.info(`Successfully built index: ${indexMeta.name}`);\n } else {\n logger.warn(`Skipped view index: ${designDoc._id}/${viewName}`);\n }\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 /**\n * Build view index by querying CouchDB views directly instead of parsing map functions\n */\n private async buildViewIndex(\n viewName: string,\n designDoc: DesignDocument,\n indices: Map<string, any>\n ): Promise<IndexMetadata | null> {\n if (!this.sourceDB) {\n logger.error('Source database not available for view querying');\n return null;\n }\n\n try {\n const designDocId = designDoc._id; // e.g., \"_design/elo\"\n const designDocName = designDocId.replace('_design/', ''); // Extract just \"elo\"\n const viewPath = `${designDocName}/${viewName}`;\n \n logger.info(`Querying CouchDB view: ${viewPath}`);\n \n // Query the view directly from CouchDB using PouchDB format: \"designDocName/viewName\"\n const viewResults = await this.sourceDB.query(viewPath, {\n include_docs: false,\n });\n\n if (!viewResults.rows || viewResults.rows.length === 0) {\n logger.warn(`View ${viewPath} returned no results`);\n return null;\n }\n\n logger.info(`Successfully queried view ${viewPath}: ${viewResults.rows.length} results`);\n\n // Format the results for static consumption\n const formattedResults = this.formatViewResults(viewName, viewResults.rows, designDoc);\n \n const indexName = `view-${designDoc._id.replace('_design/', '')}-${viewName}`;\n indices.set(indexName, formattedResults);\n\n return {\n name: indexName,\n type: 'view',\n path: `indices/${indexName}.json`,\n };\n } catch (error) {\n logger.error(`Failed to query view ${designDoc._id}/${viewName}:`, error);\n // Return null to gracefully skip this view rather than failing the entire pack\n return null;\n }\n }\n\n /**\n * Format CouchDB view results for static consumption\n */\n private formatViewResults(\n viewName: string,\n viewRows: Array<{ key: any; value: any; id: string }>,\n designDoc: DesignDocument\n ): any {\n const baseResult = {\n type: 'couchdb-view',\n viewName,\n designDoc: designDoc._id,\n results: viewRows,\n metadata: {\n resultCount: viewRows.length,\n generatedAt: new Date().toISOString(),\n },\n };\n\n // Apply view-specific formatting\n switch (viewName) {\n case 'elo':\n return this.formatEloViewIndex(viewRows, baseResult);\n case 'getTags':\n return this.formatTagsViewIndex(viewRows, baseResult);\n case 'cardsByInexperience':\n return this.formatInexperienceViewIndex(viewRows, baseResult);\n default:\n return this.formatGenericViewIndex(viewRows, baseResult);\n }\n }\n\n /**\n * Format ELO view results - convert to sorted array format\n */\n private formatEloViewIndex(\n viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n // Sort by ELO score (key) for efficient range queries\n const sortedResults = viewRows.sort((a, b) => {\n if (typeof a.key === 'number' && typeof b.key === 'number') {\n return a.key - b.key;\n }\n return 0;\n });\n\n return {\n ...baseResult,\n sorted: sortedResults,\n stats: {\n min: sortedResults[0]?.key || 0,\n max: sortedResults[sortedResults.length - 1]?.key || 0,\n count: sortedResults.length,\n },\n };\n }\n\n /**\n * Format tags view results - convert to tag mapping format\n */\n private formatTagsViewIndex(\n viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n // Group by tag name (key)\n const tagMap: Record<string, string[]> = {};\n \n for (const row of viewRows) {\n const tagName = row.key;\n if (typeof tagName === 'string') {\n if (!tagMap[tagName]) {\n tagMap[tagName] = [];\n }\n tagMap[tagName].push(row.id);\n }\n }\n\n return {\n ...baseResult,\n byTag: tagMap,\n tagCount: Object.keys(tagMap).length,\n };\n }\n\n /**\n * Format inexperience view results - convert to experience-based format\n */\n private formatInexperienceViewIndex(\n viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n // Sort by inexperience level (key) - lower numbers = less experience\n const sortedResults = viewRows.sort((a, b) => {\n if (typeof a.key === 'number' && typeof b.key === 'number') {\n return a.key - b.key;\n }\n return 0;\n });\n\n return {\n ...baseResult,\n sorted: sortedResults,\n stats: {\n minInexperience: sortedResults[0]?.key || 0,\n maxInexperience: sortedResults[sortedResults.length - 1]?.key || 0,\n count: sortedResults.length,\n },\n };\n }\n\n /**\n * Format generic view results - fallback for unknown views\n */\n private formatGenericViewIndex(\n _viewRows: Array<{ key: any; value: any; id: string }>,\n baseResult: any\n ): any {\n return {\n ...baseResult,\n // Keep results as-is for unknown view types\n };\n }\n\n /**\n * Extract all attachments from documents and download binary data\n */\n private async extractAllAttachments(\n docsByType: Record<DocType, any[]>,\n attachments: Map<string, AttachmentData>\n ): Promise<void> {\n logger.info('Extracting attachments...');\n \n const allDocs: any[] = [];\n for (const docs of Object.values(docsByType)) {\n allDocs.push(...docs);\n }\n\n const docsWithAttachments = allDocs.filter(doc => doc._attachments && Object.keys(doc._attachments).length > 0);\n \n if (docsWithAttachments.length === 0) {\n logger.info('No attachments found');\n return;\n }\n\n logger.info(`Found ${docsWithAttachments.length} documents with attachments`);\n\n // Process attachments concurrently\n const extractionPromises = docsWithAttachments.map(doc => \n this.extractDocumentAttachments(doc, attachments)\n );\n\n await Promise.all(extractionPromises);\n \n logger.info(`Extracted ${attachments.size} attachment files`);\n }\n\n /**\n * Extract attachments for a single document\n */\n private async extractDocumentAttachments(\n doc: any,\n attachments: Map<string, AttachmentData>\n ): Promise<void> {\n if (!doc._attachments || !this.sourceDB) {\n return;\n }\n\n const docId = doc._id;\n \n for (const [attachmentName, metadata] of Object.entries(doc._attachments as Record<string, any>)) {\n try {\n // Download attachment binary data\n const attachmentResponse = await this.sourceDB.getAttachment(docId, attachmentName);\n \n // Convert to buffer\n let buffer: Buffer;\n if (attachmentResponse instanceof ArrayBuffer) {\n buffer = Buffer.from(attachmentResponse);\n } else if (Buffer.isBuffer(attachmentResponse)) {\n buffer = attachmentResponse;\n } else {\n // For browser environments, the response might be a Blob\n const blob = attachmentResponse as Blob;\n buffer = Buffer.from(await blob.arrayBuffer());\n }\n\n // Generate filename with proper extension\n const extension = this.getFileExtension(metadata.content_type);\n const filename = `${attachmentName}${extension}`;\n const attachmentPath = `attachments/${docId}/${filename}`;\n \n // Store attachment data\n attachments.set(attachmentPath, {\n docId,\n attachmentName,\n filename,\n path: attachmentPath,\n contentType: metadata.content_type,\n length: metadata.length || buffer.length,\n digest: metadata.digest,\n buffer,\n });\n\n logger.debug(`Extracted attachment: ${attachmentPath}`);\n } catch (error) {\n logger.error(`Failed to extract attachment ${docId}/${attachmentName}:`, error);\n throw new Error(`Failed to extract attachment ${docId}/${attachmentName}: ${error}`);\n }\n }\n }\n\n /**\n * Transform attachment stubs to include file paths\n */\n private transformAttachmentStubs(\n attachments: Record<string, any>,\n docId: string\n ): Record<string, any> {\n const transformed: Record<string, any> = {};\n \n for (const [attachmentName, metadata] of Object.entries(attachments)) {\n const extension = this.getFileExtension(metadata.content_type);\n const filename = `${attachmentName}${extension}`;\n const attachmentPath = `attachments/${docId}/${filename}`;\n \n transformed[attachmentName] = {\n path: attachmentPath,\n content_type: metadata.content_type,\n length: metadata.length,\n digest: metadata.digest,\n stub: false, // No longer a stub - we have the actual file\n };\n }\n \n return transformed;\n }\n\n /**\n * Get file extension from content type\n */\n private getFileExtension(contentType: string): string {\n const extensionMap: Record<string, string> = {\n 'image/jpeg': '.jpg',\n 'image/jpg': '.jpg', \n 'image/png': '.png',\n 'image/gif': '.gif',\n 'image/webp': '.webp',\n 'audio/mpeg': '.mp3',\n 'audio/mp3': '.mp3',\n 'audio/wav': '.wav',\n 'audio/ogg': '.ogg',\n 'video/mp4': '.mp4',\n 'video/webm': '.webm',\n 'application/pdf': '.pdf',\n 'text/plain': '.txt',\n 'application/json': '.json',\n };\n \n return extensionMap[contentType] || '';\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.6",
6
+ "version": "0.1.8-0",
7
7
  "description": "Database layer for vue-skuilder",
8
8
  "main": "dist/index.js",
9
9
  "module": "dist/index.mjs",
@@ -45,13 +45,16 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@nilock2/pouchdb-authentication": "^1.0.2",
48
- "@vue-skuilder/common": "0.1.6",
48
+ "@vue-skuilder/common": "0.1.8-0",
49
49
  "moment": "^2.29.4",
50
50
  "pouchdb": "^9.0.0",
51
- "pouchdb-find": "^9.0.0"
51
+ "pouchdb-find": "^9.0.0",
52
+ "uuid": "^11.1.0"
52
53
  },
53
54
  "devDependencies": {
55
+ "@types/uuid": "^10.0.0",
54
56
  "tsup": "^8.0.2",
55
57
  "typescript": "~5.7.2"
56
- }
58
+ },
59
+ "stableVersion": "0.1.7"
57
60
  }
@@ -86,7 +86,19 @@ export interface QuestionData extends SkuilderCourseData {
86
86
  dataShapeList: PouchDB.Core.DocumentId[];
87
87
  }
88
88
 
89
- export const cardHistoryPrefix = 'cardH';
89
+ export const DocTypePrefixes: Record<string, string> = {
90
+ [DocType.CARD]: 'c',
91
+ [DocType.DISPLAYABLE_DATA]: 'dd',
92
+ [DocType.TAG]: 'TAG',
93
+ [DocType.CARDRECORD]: 'cardH',
94
+ [DocType.SCHEDULED_CARD]: 'card_review_',
95
+ // Add other doctypes here as they get prefixed IDs
96
+ [DocType.DATASHAPE]: 'DATASHAPE',
97
+ [DocType.QUESTIONTYPE]: 'QUESTION',
98
+ [DocType.VIEW]: 'VIEW',
99
+ [DocType.PEDAGOGY]: 'PEDAGOGY',
100
+ [DocType.NAVIGATION_STRATEGY]: 'NAVIGATION_STRATEGY',
101
+ };
90
102
 
91
103
  export interface CardHistory<T extends CardRecord> {
92
104
  _id: PouchDB.Core.DocumentId;
@@ -4,6 +4,7 @@ import { Moment } from 'moment';
4
4
  export interface UserConfig {
5
5
  darkMode: boolean;
6
6
  likesConfetti: boolean;
7
+ sessionTimeLimit: number; // Session time limit in minutes
7
8
  }
8
9
 
9
10
  export interface ActivityRecord {
@@ -48,13 +49,19 @@ export interface ScheduledCard {
48
49
  *
49
50
  * (Should probably be UTC adjusted so that performance is
50
51
  * not wonky across time zones)
52
+ *
53
+ * Note: Stored as ISO string for PouchDB serialization compatibility,
54
+ * but can be consumed as Moment objects via moment.utc(reviewTime)
51
55
  */
52
- reviewTime: Moment;
56
+ reviewTime: string | Moment;
53
57
 
54
58
  /**
55
59
  * The time at which this scheduled event was created.
60
+ *
61
+ * Note: Stored as ISO string for PouchDB serialization compatibility,
62
+ * but can be consumed as Moment objects via moment.utc(scheduledAt)
56
63
  */
57
- scheduledAt: Moment;
64
+ scheduledAt: string | Moment;
58
65
 
59
66
  /**
60
67
  * Classifying whether this card is scheduled on behalf of a
@@ -1,4 +1,4 @@
1
- import { cardHistoryPrefix, CardHistory, CardRecord, QuestionRecord } from '../types/types-legacy';
1
+ import { DocType, DocTypePrefixes, CardHistory, CardRecord, QuestionRecord } from '../types/types-legacy';
2
2
 
3
3
  export function areQuestionRecords(h: CardHistory<CardRecord>): h is CardHistory<QuestionRecord> {
4
4
  return isQuestionRecord(h.records[0]);
@@ -9,7 +9,7 @@ export function isQuestionRecord(c: CardRecord): c is QuestionRecord {
9
9
  }
10
10
 
11
11
  export function getCardHistoryID(courseID: string, cardID: string): PouchDB.Core.DocumentId {
12
- return `${cardHistoryPrefix}-${courseID}-${cardID}`;
12
+ return `${DocTypePrefixes[DocType.CARDRECORD]}-${courseID}-${cardID}`;
13
13
  }
14
14
 
15
15
  export function parseCardHistoryID(id: string): {
@@ -20,9 +20,10 @@ export function parseCardHistoryID(id: string): {
20
20
  let error: string = '';
21
21
  error += split.length === 3 ? '' : `\n\tgiven ID has incorrect number of '-' characters`;
22
22
  error +=
23
- split[0] === cardHistoryPrefix ? '' : `\n\tgiven ID does not start with ${cardHistoryPrefix}`;
23
+ split[0] === DocTypePrefixes[DocType.CARDRECORD] ? '' : `
24
+ given ID does not start with ${DocTypePrefixes[DocType.CARDRECORD]}`;
24
25
 
25
- if (split.length === 3 && split[0] === cardHistoryPrefix) {
26
+ if (split.length === 3 && split[0] === DocTypePrefixes[DocType.CARDRECORD]) {
26
27
  return {
27
28
  courseID: split[1],
28
29
  cardID: split[2],
package/src/factory.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // db/src/factory.ts
2
2
 
3
3
  import { DataLayerProvider } from './core/interfaces';
4
+ import { BaseUser } from './impl/common';
4
5
  import { logger } from './util/logger';
5
6
  import { StaticCourseManifest } from './util/packer/types';
6
7
 
@@ -53,6 +54,30 @@ export async function initializeDataLayer(config: DataLayerConfig): Promise<Data
53
54
  ENV.COUCHDB_USERNAME = config.options.COUCHDB_USERNAME;
54
55
  ENV.COUCHDB_PASSWORD = config.options.COUCHDB_PASSWORD;
55
56
 
57
+ if (
58
+ config.options.COUCHDB_PASSWORD &&
59
+ config.options.COUCHDB_USERNAME &&
60
+ typeof window !== 'undefined'
61
+ ) {
62
+ // Dynamic import to avoid loading both implementations when only one is needed
63
+ const { CouchDBSyncStrategy } = await import('./impl/couch/CouchDBSyncStrategy');
64
+
65
+ // Create a sync strategy instance and authenticate
66
+ const syncStrategy = new CouchDBSyncStrategy();
67
+
68
+ const user = await BaseUser.instance(syncStrategy, config.options.COUCHDB_USERNAME);
69
+ const authResult = await user.login(
70
+ config.options.COUCHDB_USERNAME,
71
+ config.options.COUCHDB_PASSWORD
72
+ );
73
+
74
+ if (authResult.ok) {
75
+ logger.info(`Successfully authenticated as ${config.options.COUCHDB_USERNAME}`);
76
+ } else {
77
+ logger.warn(`Authentication failed: ${authResult.error}`);
78
+ }
79
+ }
80
+
56
81
  // Dynamic import to avoid loading both implementations when only one is needed
57
82
  const { CouchDataLayerProvider } = await import('./impl/couch/PouchDataLayerProvider');
58
83
  dataLayerInstance = new CouchDataLayerProvider(config.options.COURSE_IDS);
@@ -1,3 +1,4 @@
1
+ import { DocType, DocTypePrefixes } from '@db/core';
1
2
  import { getCardHistoryID } from '@db/core/util';
2
3
  import { CourseElo, Status } from '@vue-skuilder/common';
3
4
  import moment, { Moment } from 'moment';
@@ -23,7 +24,6 @@ import type { SyncStrategy } from './SyncStrategy';
23
24
  import {
24
25
  filterAllDocsByPrefix,
25
26
  getStartAndEndKeys,
26
- REVIEW_PREFIX,
27
27
  REVIEW_TIME_FORMAT,
28
28
  getLocalUserDB,
29
29
  scheduleCardReviewLocal,
@@ -38,8 +38,6 @@ const log = (s: any) => {
38
38
  logger.info(s);
39
39
  };
40
40
 
41
- const cardHistoryPrefix = 'cardH-';
42
-
43
41
  // console.log(`Connecting to remote: ${remoteStr}`);
44
42
 
45
43
  interface DesignDoc {
@@ -81,11 +79,14 @@ export class BaseUser implements UserDBInterface, DocumentUpdater {
81
79
  return !this._username.startsWith(GuestUsername);
82
80
  }
83
81
 
84
- private remoteDB!: PouchDB.Database;
85
82
  public remote(): PouchDB.Database {
86
83
  return this.remoteDB;
87
84
  }
85
+
88
86
  private localDB!: PouchDB.Database;
87
+ private remoteDB!: PouchDB.Database;
88
+ private writeDB!: PouchDB.Database; // Database to use for write operations (local-first approach)
89
+
89
90
  private updateQueue!: UpdateQueue;
90
91
 
91
92
  public async createAccount(
@@ -112,7 +113,11 @@ Currently logged-in as ${this._username}.`
112
113
  if (result.status === Status.ok) {
113
114
  log(`Account created successfully, updating username to ${username}`);
114
115
  this._username = username;
115
- localStorage.removeItem('dbUUID');
116
+ try {
117
+ localStorage.removeItem('dbUUID');
118
+ } catch (e) {
119
+ logger.warn('localStorage not available (Node.js environment):', e);
120
+ }
116
121
  await this.init();
117
122
  }
118
123
 
@@ -126,16 +131,23 @@ Currently logged-in as ${this._username}.`
126
131
  throw new Error('Authentication not supported by current sync strategy');
127
132
  }
128
133
 
129
- if (!this._username.startsWith(GuestUsername)) {
130
- throw new Error(`Cannot change accounts while logged in.
131
- Log out of account ${this.getUsername()} before logging in as ${username}.`);
134
+ if (!this._username.startsWith(GuestUsername) && this._username != username) {
135
+ if (this._username != username) {
136
+ throw new Error(`Cannot change accounts while logged in.
137
+ Log out of account ${this.getUsername()} before logging in as ${username}.`);
138
+ }
139
+ logger.warn(`User ${this._username} is already logged in, but executing login again.`);
132
140
  }
133
141
 
134
142
  const loginResult = await this.syncStrategy.authenticate!(username, password);
135
143
  if (loginResult.ok) {
136
144
  log(`Logged in as ${username}`);
137
145
  this._username = username;
138
- localStorage.removeItem('dbUUID');
146
+ try {
147
+ localStorage.removeItem('dbUUID');
148
+ } catch (e) {
149
+ logger.warn('localStorage not available (Node.js environment):', e);
150
+ }
139
151
  await this.init();
140
152
  }
141
153
  return loginResult;
@@ -163,8 +175,8 @@ Currently logged-in as ${this._username}.`
163
175
  const id = row.id;
164
176
  // Delete user progress data but preserve core user documents
165
177
  return (
166
- id.startsWith(cardHistoryPrefix) || // Card interaction history
167
- id.startsWith(REVIEW_PREFIX) || // Scheduled reviews
178
+ id.startsWith(DocTypePrefixes[DocType.CARDRECORD]) || // Card interaction history
179
+ id.startsWith(DocTypePrefixes[DocType.SCHEDULED_CARD]) || // Scheduled reviews
168
180
  id === BaseUser.DOC_IDS.COURSE_REGISTRATIONS || // Course registrations
169
181
  id === BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS || // Classroom registrations
170
182
  id === BaseUser.DOC_IDS.CONFIG // User config
@@ -253,7 +265,7 @@ Currently logged-in as ${this._username}.`
253
265
  *
254
266
  */
255
267
  public async getActiveCards() {
256
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
268
+ const keys = getStartAndEndKeys(DocTypePrefixes[DocType.SCHEDULED_CARD]);
257
269
 
258
270
  const reviews = await this.remoteDB.allDocs<ScheduledCard>({
259
271
  startkey: keys.startkey,
@@ -348,7 +360,7 @@ Currently logged-in as ${this._username}.`
348
360
  }
349
361
 
350
362
  private async getReviewstoDate(targetDate: Moment, course_id?: string) {
351
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
363
+ const keys = getStartAndEndKeys(DocTypePrefixes[DocType.SCHEDULED_CARD]);
352
364
 
353
365
  const reviews = await this.remoteDB.allDocs<ScheduledCard>({
354
366
  startkey: keys.startkey,
@@ -363,8 +375,11 @@ Currently logged-in as ${this._username}.`
363
375
  );
364
376
  return reviews.rows
365
377
  .filter((r) => {
366
- if (r.id.startsWith(REVIEW_PREFIX)) {
367
- const date = moment.utc(r.id.substr(REVIEW_PREFIX.length), REVIEW_TIME_FORMAT);
378
+ if (r.id.startsWith(DocTypePrefixes[DocType.SCHEDULED_CARD])) {
379
+ const date = moment.utc(
380
+ r.id.substr(DocTypePrefixes[DocType.SCHEDULED_CARD].length),
381
+ REVIEW_TIME_FORMAT
382
+ );
368
383
  if (targetDate.isAfter(date)) {
369
384
  if (course_id === undefined || r.doc!.courseId === course_id) {
370
385
  return true;
@@ -504,6 +519,7 @@ Currently logged-in as ${this._username}.`
504
519
  _id: BaseUser.DOC_IDS.CONFIG,
505
520
  darkMode: false,
506
521
  likesConfetti: false,
522
+ sessionTimeLimit: 5,
507
523
  };
508
524
 
509
525
  try {
@@ -585,11 +601,22 @@ Currently logged-in as ${this._username}.`
585
601
  private setDBandQ() {
586
602
  this.localDB = getLocalUserDB(this._username);
587
603
  this.remoteDB = this.syncStrategy.setupRemoteDB(this._username);
588
- this.updateQueue = new UpdateQueue(this.localDB);
604
+ // writeDB follows local-first pattern: static mode writes to local, CouchDB writes to remote/local as appropriate
605
+ this.writeDB = this.syncStrategy.getWriteDB
606
+ ? this.syncStrategy.getWriteDB(this._username)
607
+ : this.localDB;
608
+ this.updateQueue = new UpdateQueue(this.localDB, this.writeDB);
589
609
  }
590
610
 
591
611
  private async init() {
592
612
  BaseUser._initialized = false;
613
+
614
+ // Skip admin user
615
+ if (this._username === 'admin') {
616
+ BaseUser._initialized = true;
617
+ return;
618
+ }
619
+
593
620
  this.setDBandQ();
594
621
 
595
622
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
@@ -614,6 +641,11 @@ Currently logged-in as ${this._username}.`
614
641
  ];
615
642
 
616
643
  private async applyDesignDocs() {
644
+ if (this._username === 'admin') {
645
+ // Skip admin user
646
+ return;
647
+ }
648
+
617
649
  for (const doc of BaseUser.designDocs) {
618
650
  try {
619
651
  // Try to get existing doc
@@ -633,7 +665,7 @@ Currently logged-in as ${this._username}.`
633
665
  }
634
666
  }
635
667
  } catch (error: unknown) {
636
- if (error instanceof Error && error.name === 'conflict') {
668
+ if ((error as any).name && (error as any).name === 'conflict') {
637
669
  logger.warn(`Design doc ${doc._id} update conflict - will retry`);
638
670
  // Wait a bit and try again
639
671
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -673,7 +705,9 @@ Currently logged-in as ${this._username}.`
673
705
  * @returns The updated state of the card's CardHistory data
674
706
  */
675
707
 
676
- public async putCardRecord<T extends CardRecord>(record: T): Promise<CardHistory<CardRecord>> {
708
+ public async putCardRecord<T extends CardRecord>(
709
+ record: T
710
+ ): Promise<CardHistory<CardRecord> & PouchDB.Core.RevisionIdMeta> {
677
711
  const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
678
712
  // stringify the current record to make it writable to couchdb
679
713
  record.timeStamp = moment.utc(record.timeStamp).toString() as unknown as Moment;
@@ -711,8 +745,8 @@ Currently logged-in as ${this._username}.`
711
745
  streak: 0,
712
746
  bestInterval: 0,
713
747
  };
714
- void this.remoteDB.put<CardHistory<T>>(initCardHistory);
715
- return initCardHistory;
748
+ const putResult = await this.writeDB.put<CardHistory<T>>(initCardHistory);
749
+ return { ...initCardHistory, _rev: putResult.rev };
716
750
  } else {
717
751
  throw new Error(`putCardRecord failed because of:
718
752
  name:${reason.name}
@@ -769,7 +803,7 @@ Currently logged-in as ${this._username}.`
769
803
  const deletePromises = duplicateDocIds.map(async (docId) => {
770
804
  try {
771
805
  const doc = await this.remoteDB.get(docId);
772
- await this.remoteDB.remove(doc);
806
+ await this.writeDB.remove(doc);
773
807
  log(`Successfully removed duplicate review: ${docId}`);
774
808
  } catch (error) {
775
809
  log(`Failed to remove duplicate review ${docId}: ${error}`);
@@ -793,7 +827,7 @@ Currently logged-in as ${this._username}.`
793
827
  * @param course_id optional specification of individual course
794
828
  */
795
829
  async getSeenCards(course_id?: string) {
796
- let prefix = cardHistoryPrefix;
830
+ let prefix = DocTypePrefixes[DocType.CARDRECORD];
797
831
  if (course_id) {
798
832
  prefix += course_id;
799
833
  }
@@ -803,8 +837,8 @@ Currently logged-in as ${this._username}.`
803
837
  // const docs = await this.localDB.allDocs({});
804
838
  const ret: PouchDB.Core.DocumentId[] = [];
805
839
  docs.rows.forEach((row) => {
806
- if (row.id.startsWith(cardHistoryPrefix)) {
807
- ret.push(row.id.substr(cardHistoryPrefix.length));
840
+ if (row.id.startsWith(DocTypePrefixes[DocType.CARDRECORD])) {
841
+ ret.push(row.id.substr(DocTypePrefixes[DocType.CARDRECORD].length));
808
842
  }
809
843
  });
810
844
  return ret;
@@ -817,7 +851,7 @@ Currently logged-in as ${this._username}.`
817
851
  async getHistory() {
818
852
  const cards = await filterAllDocsByPrefix<CardHistory<CardRecord>>(
819
853
  this.remoteDB,
820
- cardHistoryPrefix,
854
+ DocTypePrefixes[DocType.CARDRECORD],
821
855
  {
822
856
  include_docs: true,
823
857
  attachments: false,
@@ -867,7 +901,7 @@ Currently logged-in as ${this._username}.`
867
901
 
868
902
  if (err.status === 404) {
869
903
  // doc does not exist. Create it and then run this fcn again.
870
- await this.remoteDB.put<ClassroomRegistrationDoc>({
904
+ await this.writeDB.put<ClassroomRegistrationDoc>({
871
905
  _id: BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS,
872
906
  registrations: [],
873
907
  });
@@ -931,10 +965,10 @@ Currently logged-in as ${this._username}.`
931
965
  scheduledFor: ScheduledCard['scheduledFor'];
932
966
  schedulingAgentId: ScheduledCard['schedulingAgentId'];
933
967
  }) {
934
- return scheduleCardReviewLocal(this.remoteDB, review);
968
+ return scheduleCardReviewLocal(this.writeDB, review);
935
969
  }
936
970
  public async removeScheduledCardReview(reviewId: string): Promise<void> {
937
- return removeScheduledCardReviewLocal(this.remoteDB, reviewId);
971
+ return removeScheduledCardReviewLocal(this.writeDB, reviewId);
938
972
  }
939
973
 
940
974
  public async registerForClassroom(
@@ -14,6 +14,13 @@ export interface SyncStrategy {
14
14
  */
15
15
  setupRemoteDB(username: string): PouchDB.Database;
16
16
 
17
+ /**
18
+ * Get the database to use for write operations (local-first approach)
19
+ * @param username The username to get write DB for
20
+ * @returns PouchDB database instance for write operations
21
+ */
22
+ getWriteDB?(username: string): PouchDB.Database;
23
+
17
24
  /**
18
25
  * Start synchronization between local and remote databases
19
26
  * @param localDB The local PouchDB instance
@@ -11,7 +11,6 @@ export type {
11
11
  } from './types';
12
12
  export { BaseUser } from './BaseUserDB';
13
13
  export {
14
- REVIEW_PREFIX,
15
14
  REVIEW_TIME_FORMAT,
16
15
  hexEncode,
17
16
  filterAllDocsByPrefix,
@@ -1,13 +1,14 @@
1
1
  // packages/db/src/impl/common/userDBHelpers.ts
2
2
 
3
3
  import moment from 'moment';
4
+ import { DocType, DocTypePrefixes } from '@db/core';
4
5
  import { logger } from '../../util/logger';
5
6
  import { ScheduledCard } from '@db/core/types/user';
6
7
 
7
- export const REVIEW_PREFIX: string = 'card_review_';
8
8
  export const REVIEW_TIME_FORMAT: string = 'YYYY-MM-DD--kk:mm:ss-SSS';
9
9
 
10
10
  import pouch from '../couch/pouchdb-setup';
11
+ import { getDbPath } from '../../util/dataDirectory';
11
12
 
12
13
  const log = (s: any) => {
13
14
  logger.info(s);
@@ -94,7 +95,16 @@ export function getLocalUserDB(username: string): PouchDB.Database {
94
95
  // adapter = 'memory';
95
96
  // }
96
97
 
97
- return new pouch(`userdb-${username}`, {});
98
+ const dbName = `userdb-${username}`;
99
+
100
+ // Use proper data directory in Node.js, browser will use IndexedDB
101
+ if (typeof window === 'undefined') {
102
+ // Node.js environment - use filesystem with proper app data directory
103
+ return new pouch(getDbPath(dbName), {});
104
+ } else {
105
+ // Browser environment - use default (IndexedDB)
106
+ return new pouch(dbName, {});
107
+ }
98
108
  }
99
109
 
100
110
  /**
@@ -113,11 +123,11 @@ export function scheduleCardReviewLocal(
113
123
  const now = moment.utc();
114
124
  logger.info(`Scheduling for review in: ${review.time.diff(now, 'h') / 24} days`);
115
125
  void userDB.put<ScheduledCard>({
116
- _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
126
+ _id: DocTypePrefixes[DocType.SCHEDULED_CARD] + review.time.format(REVIEW_TIME_FORMAT),
117
127
  cardId: review.card_id,
118
- reviewTime: review.time,
128
+ reviewTime: review.time.toISOString(),
119
129
  courseId: review.course_id,
120
- scheduledAt: now,
130
+ scheduledAt: now.toISOString(),
121
131
  scheduledFor: review.scheduledFor,
122
132
  schedulingAgentId: review.schedulingAgentId,
123
133
  });
@@ -32,6 +32,16 @@ export class CouchDBSyncStrategy implements SyncStrategy {
32
32
  }
33
33
  }
34
34
 
35
+ getWriteDB(username: string): PouchDB.Database {
36
+ if (username === GuestUsername || username.startsWith(GuestUsername)) {
37
+ // Guest users write to local database
38
+ return getLocalUserDB(username);
39
+ } else {
40
+ // Authenticated users write to remote (which will sync to local)
41
+ return this.getUserDB(username);
42
+ }
43
+ }
44
+
35
45
  startSync(localDB: PouchDB.Database, remoteDB: PouchDB.Database): void {
36
46
  // Only sync if local and remote are different instances
37
47
  if (localDB !== remoteDB) {