@vue-skuilder/db 0.1.7 → 0.1.8-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/{SyncStrategy-DnJRj-Xp.d.mts → SyncStrategy-CyATpyLQ.d.mts} +6 -0
  2. package/dist/{SyncStrategy-DnJRj-Xp.d.ts → SyncStrategy-CyATpyLQ.d.ts} +6 -0
  3. package/dist/core/index.d.mts +5 -5
  4. package/dist/core/index.d.ts +5 -5
  5. package/dist/core/index.js +131 -118
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +128 -115
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BbW9EnZK.d.mts → dataLayerProvider-BInqI_RF.d.mts} +1 -1
  10. package/dist/{dataLayerProvider-6stCgDME.d.ts → dataLayerProvider-DqtNroSh.d.ts} +1 -1
  11. package/dist/impl/couch/index.d.mts +6 -6
  12. package/dist/impl/couch/index.d.ts +6 -6
  13. package/dist/impl/couch/index.js +1365 -1252
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +1359 -1246
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/index.d.mts +8 -6
  18. package/dist/impl/static/index.d.ts +8 -6
  19. package/dist/impl/static/index.js +253 -843
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +250 -842
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/index-CLL31bEy.d.ts +137 -0
  24. package/dist/index-CUNnL38E.d.mts +137 -0
  25. package/dist/index.d.mts +10 -55
  26. package/dist/index.d.ts +10 -55
  27. package/dist/index.js +343 -170
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +340 -167
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/{types-BvzcRAys.d.ts → types-BefDGkKa.d.ts} +1 -1
  32. package/dist/{types-CQQ80R5N.d.mts → types-DC-ckZug.d.mts} +1 -1
  33. package/dist/{types-legacy-CtrmkOLu.d.mts → types-legacy-Birv-Jx6.d.mts} +2 -2
  34. package/dist/{types-legacy-CtrmkOLu.d.ts → types-legacy-Birv-Jx6.d.ts} +2 -2
  35. package/dist/{userDB-DUY63VMN.d.ts → userDB-C33Hzjgn.d.mts} +10 -3
  36. package/dist/{userDB-7fM4tpgr.d.mts → userDB-DusL7OXe.d.ts} +10 -3
  37. package/dist/util/packer/index.d.mts +3 -63
  38. package/dist/util/packer/index.d.ts +3 -63
  39. package/dist/util/packer/index.js +53 -1
  40. package/dist/util/packer/index.js.map +1 -1
  41. package/dist/util/packer/index.mjs +53 -1
  42. package/dist/util/packer/index.mjs.map +1 -1
  43. package/package.json +7 -4
  44. package/src/core/types/types-legacy.ts +13 -1
  45. package/src/core/types/user.ts +9 -2
  46. package/src/core/util/index.ts +5 -4
  47. package/src/impl/common/BaseUserDB.ts +33 -22
  48. package/src/impl/common/SyncStrategy.ts +7 -0
  49. package/src/impl/common/index.ts +0 -1
  50. package/src/impl/common/userDBHelpers.ts +4 -4
  51. package/src/impl/couch/CouchDBSyncStrategy.ts +10 -0
  52. package/src/impl/couch/courseAPI.ts +7 -6
  53. package/src/impl/couch/index.ts +10 -5
  54. package/src/impl/couch/updateQueue.ts +12 -8
  55. package/src/impl/couch/user-course-relDB.ts +17 -27
  56. package/src/impl/static/NoOpSyncStrategy.ts +5 -0
  57. package/src/impl/static/StaticDataUnpacker.ts +18 -36
  58. package/src/impl/static/courseDB.ts +135 -17
  59. package/src/util/migrator/FileSystemAdapter.ts +20 -0
  60. package/src/util/migrator/StaticToCouchDBMigrator.ts +6 -0
  61. package/src/util/packer/CouchDBToStaticPacker.ts +92 -2
@@ -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.7",
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.7",
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],
@@ -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(
@@ -174,8 +175,8 @@ Currently logged-in as ${this._username}.`
174
175
  const id = row.id;
175
176
  // Delete user progress data but preserve core user documents
176
177
  return (
177
- id.startsWith(cardHistoryPrefix) || // Card interaction history
178
- id.startsWith(REVIEW_PREFIX) || // Scheduled reviews
178
+ id.startsWith(DocTypePrefixes[DocType.CARDRECORD]) || // Card interaction history
179
+ id.startsWith(DocTypePrefixes[DocType.SCHEDULED_CARD]) || // Scheduled reviews
179
180
  id === BaseUser.DOC_IDS.COURSE_REGISTRATIONS || // Course registrations
180
181
  id === BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS || // Classroom registrations
181
182
  id === BaseUser.DOC_IDS.CONFIG // User config
@@ -264,7 +265,7 @@ Currently logged-in as ${this._username}.`
264
265
  *
265
266
  */
266
267
  public async getActiveCards() {
267
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
268
+ const keys = getStartAndEndKeys(DocTypePrefixes[DocType.SCHEDULED_CARD]);
268
269
 
269
270
  const reviews = await this.remoteDB.allDocs<ScheduledCard>({
270
271
  startkey: keys.startkey,
@@ -359,7 +360,7 @@ Currently logged-in as ${this._username}.`
359
360
  }
360
361
 
361
362
  private async getReviewstoDate(targetDate: Moment, course_id?: string) {
362
- const keys = getStartAndEndKeys(REVIEW_PREFIX);
363
+ const keys = getStartAndEndKeys(DocTypePrefixes[DocType.SCHEDULED_CARD]);
363
364
 
364
365
  const reviews = await this.remoteDB.allDocs<ScheduledCard>({
365
366
  startkey: keys.startkey,
@@ -374,8 +375,11 @@ Currently logged-in as ${this._username}.`
374
375
  );
375
376
  return reviews.rows
376
377
  .filter((r) => {
377
- if (r.id.startsWith(REVIEW_PREFIX)) {
378
- 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
+ );
379
383
  if (targetDate.isAfter(date)) {
380
384
  if (course_id === undefined || r.doc!.courseId === course_id) {
381
385
  return true;
@@ -515,6 +519,7 @@ Currently logged-in as ${this._username}.`
515
519
  _id: BaseUser.DOC_IDS.CONFIG,
516
520
  darkMode: false,
517
521
  likesConfetti: false,
522
+ sessionTimeLimit: 5,
518
523
  };
519
524
 
520
525
  try {
@@ -596,7 +601,11 @@ Currently logged-in as ${this._username}.`
596
601
  private setDBandQ() {
597
602
  this.localDB = getLocalUserDB(this._username);
598
603
  this.remoteDB = this.syncStrategy.setupRemoteDB(this._username);
599
- 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);
600
609
  }
601
610
 
602
611
  private async init() {
@@ -696,7 +705,9 @@ Currently logged-in as ${this._username}.`
696
705
  * @returns The updated state of the card's CardHistory data
697
706
  */
698
707
 
699
- 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> {
700
711
  const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
701
712
  // stringify the current record to make it writable to couchdb
702
713
  record.timeStamp = moment.utc(record.timeStamp).toString() as unknown as Moment;
@@ -734,8 +745,8 @@ Currently logged-in as ${this._username}.`
734
745
  streak: 0,
735
746
  bestInterval: 0,
736
747
  };
737
- void this.remoteDB.put<CardHistory<T>>(initCardHistory);
738
- return initCardHistory;
748
+ const putResult = await this.writeDB.put<CardHistory<T>>(initCardHistory);
749
+ return { ...initCardHistory, _rev: putResult.rev };
739
750
  } else {
740
751
  throw new Error(`putCardRecord failed because of:
741
752
  name:${reason.name}
@@ -792,7 +803,7 @@ Currently logged-in as ${this._username}.`
792
803
  const deletePromises = duplicateDocIds.map(async (docId) => {
793
804
  try {
794
805
  const doc = await this.remoteDB.get(docId);
795
- await this.remoteDB.remove(doc);
806
+ await this.writeDB.remove(doc);
796
807
  log(`Successfully removed duplicate review: ${docId}`);
797
808
  } catch (error) {
798
809
  log(`Failed to remove duplicate review ${docId}: ${error}`);
@@ -816,7 +827,7 @@ Currently logged-in as ${this._username}.`
816
827
  * @param course_id optional specification of individual course
817
828
  */
818
829
  async getSeenCards(course_id?: string) {
819
- let prefix = cardHistoryPrefix;
830
+ let prefix = DocTypePrefixes[DocType.CARDRECORD];
820
831
  if (course_id) {
821
832
  prefix += course_id;
822
833
  }
@@ -826,8 +837,8 @@ Currently logged-in as ${this._username}.`
826
837
  // const docs = await this.localDB.allDocs({});
827
838
  const ret: PouchDB.Core.DocumentId[] = [];
828
839
  docs.rows.forEach((row) => {
829
- if (row.id.startsWith(cardHistoryPrefix)) {
830
- 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));
831
842
  }
832
843
  });
833
844
  return ret;
@@ -840,7 +851,7 @@ Currently logged-in as ${this._username}.`
840
851
  async getHistory() {
841
852
  const cards = await filterAllDocsByPrefix<CardHistory<CardRecord>>(
842
853
  this.remoteDB,
843
- cardHistoryPrefix,
854
+ DocTypePrefixes[DocType.CARDRECORD],
844
855
  {
845
856
  include_docs: true,
846
857
  attachments: false,
@@ -890,7 +901,7 @@ Currently logged-in as ${this._username}.`
890
901
 
891
902
  if (err.status === 404) {
892
903
  // doc does not exist. Create it and then run this fcn again.
893
- await this.remoteDB.put<ClassroomRegistrationDoc>({
904
+ await this.writeDB.put<ClassroomRegistrationDoc>({
894
905
  _id: BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS,
895
906
  registrations: [],
896
907
  });
@@ -954,10 +965,10 @@ Currently logged-in as ${this._username}.`
954
965
  scheduledFor: ScheduledCard['scheduledFor'];
955
966
  schedulingAgentId: ScheduledCard['schedulingAgentId'];
956
967
  }) {
957
- return scheduleCardReviewLocal(this.remoteDB, review);
968
+ return scheduleCardReviewLocal(this.writeDB, review);
958
969
  }
959
970
  public async removeScheduledCardReview(reviewId: string): Promise<void> {
960
- return removeScheduledCardReviewLocal(this.remoteDB, reviewId);
971
+ return removeScheduledCardReviewLocal(this.writeDB, reviewId);
961
972
  }
962
973
 
963
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,10 +1,10 @@
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';
@@ -123,11 +123,11 @@ export function scheduleCardReviewLocal(
123
123
  const now = moment.utc();
124
124
  logger.info(`Scheduling for review in: ${review.time.diff(now, 'h') / 24} days`);
125
125
  void userDB.put<ScheduledCard>({
126
- _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
126
+ _id: DocTypePrefixes[DocType.SCHEDULED_CARD] + review.time.format(REVIEW_TIME_FORMAT),
127
127
  cardId: review.card_id,
128
- reviewTime: review.time,
128
+ reviewTime: review.time.toISOString(),
129
129
  courseId: review.course_id,
130
- scheduledAt: now,
130
+ scheduledAt: now.toISOString(),
131
131
  scheduledFor: review.scheduledFor,
132
132
  schedulingAgentId: review.schedulingAgentId,
133
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) {
@@ -6,10 +6,11 @@ import { NameSpacer, ShapeDescriptor } from '@vue-skuilder/common';
6
6
  import { CourseConfig, DataShape } from '@vue-skuilder/common';
7
7
  import { CourseElo, blankCourseElo, toCourseElo } from '@vue-skuilder/common';
8
8
  import { CourseDB, createTag } from './courseDB';
9
- import { CardData, DisplayableData, DocType, Tag } from '../../core/types/types-legacy';
9
+ import { CardData, DisplayableData, DocType, Tag, DocTypePrefixes } from '../../core/types/types-legacy';
10
10
  import { prepareNote55 } from '@vue-skuilder/common';
11
11
  import { BaseUser } from '../common';
12
12
  import { logger } from '@db/util/logger';
13
+ import { v4 as uuidv4 } from 'uuid';
13
14
 
14
15
  /**
15
16
  *
@@ -33,9 +34,8 @@ export async function addNote55(
33
34
  ): Promise<PouchDB.Core.Response> {
34
35
  const db = getCourseDB(courseID);
35
36
  const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
36
- // [ ] NAMESPACING: consider put( _id: "displayable_data-uuid")
37
- // consider also semantic hashing
38
- const result = await db.post<DisplayableData>(payload);
37
+ const _id = `${DocTypePrefixes[DocType.DISPLAYABLE_DATA]}-${uuidv4()}`;
38
+ const result = await db.put<DisplayableData>({ ...payload, _id });
39
39
 
40
40
  const dataShapeId = NameSpacer.getDataShapeString({
41
41
  course: codeCourse,
@@ -153,9 +153,10 @@ async function addCard(
153
153
  tags: string[],
154
154
  author: string
155
155
  ): Promise<PouchDB.Core.Response> {
156
- // [ ] NAMESPACING: consider put( _id: "card-uuid")
157
156
  const db = getCourseDB(courseID);
158
- const card = await db.post<CardData>({
157
+ const _id = `${DocTypePrefixes[DocType.CARD]}-${uuidv4()}`;
158
+ const card = await db.put<CardData>({
159
+ _id,
159
160
  course,
160
161
  id_displayable_data,
161
162
  id_view,
@@ -1,5 +1,11 @@
1
1
  import { ENV } from '@db/factory';
2
- import { DocType, GuestUsername, log, SkuilderCourseData } from '../../core/types/types-legacy';
2
+ import {
3
+ DocType,
4
+ DocTypePrefixes,
5
+ GuestUsername,
6
+ log,
7
+ SkuilderCourseData,
8
+ } from '../../core/types/types-legacy';
3
9
  // import { getCurrentUser } from '../../stores/useAuthStore';
4
10
  import moment, { Moment } from 'moment';
5
11
  import { logger } from '@db/util/logger';
@@ -155,7 +161,6 @@ export async function getRandomCards(courseIDs: string[]) {
155
161
  }
156
162
  }
157
163
 
158
- export const REVIEW_PREFIX: string = 'card_review_';
159
164
  export const REVIEW_TIME_FORMAT: string = 'YYYY-MM-DD--kk:mm:ss-SSS';
160
165
 
161
166
  export function getCouchUserDB(username: string): PouchDB.Database {
@@ -191,11 +196,11 @@ export function scheduleCardReview(review: {
191
196
  const now = moment.utc();
192
197
  logger.info(`Scheduling for review in: ${review.time.diff(now, 'h') / 24} days`);
193
198
  void getCouchUserDB(review.user).put<ScheduledCard>({
194
- _id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
199
+ _id: DocTypePrefixes[DocType.SCHEDULED_CARD] + review.time.format(REVIEW_TIME_FORMAT),
195
200
  cardId: review.card_id,
196
- reviewTime: review.time,
201
+ reviewTime: review.time.toISOString(),
197
202
  courseId: review.course_id,
198
- scheduledAt: now,
203
+ scheduledAt: now.toISOString(),
199
204
  scheduledFor: review.scheduledFor,
200
205
  schedulingAgentId: review.schedulingAgentId,
201
206
  });