@vue-skuilder/db 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/SyncStrategy-DnJRj-Xp.d.mts +74 -0
  2. package/dist/SyncStrategy-DnJRj-Xp.d.ts +74 -0
  3. package/dist/core/index.d.mts +90 -2
  4. package/dist/core/index.d.ts +90 -2
  5. package/dist/core/index.js +798 -650
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +769 -621
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/dataLayerProvider-B8wquRiB.d.mts +37 -0
  10. package/dist/dataLayerProvider-DRjMZMaf.d.ts +37 -0
  11. package/dist/impl/couch/index.d.mts +292 -0
  12. package/dist/impl/couch/index.d.ts +292 -0
  13. package/dist/impl/couch/index.js +8522 -0
  14. package/dist/impl/couch/index.js.map +1 -0
  15. package/dist/impl/couch/index.mjs +8474 -0
  16. package/dist/impl/couch/index.mjs.map +1 -0
  17. package/dist/impl/static/index.d.mts +204 -0
  18. package/dist/impl/static/index.d.ts +204 -0
  19. package/dist/impl/static/index.js +8499 -0
  20. package/dist/impl/static/index.js.map +1 -0
  21. package/dist/impl/static/index.mjs +8488 -0
  22. package/dist/impl/static/index.mjs.map +1 -0
  23. package/dist/index.d.mts +13 -4
  24. package/dist/index.d.ts +13 -4
  25. package/dist/index.js +3280 -2108
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +3290 -2118
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/types-B0GJsjOr.d.ts +47 -0
  30. package/dist/types-DIgj8pP7.d.mts +47 -0
  31. package/dist/types-legacy-CTsJvvxI.d.mts +137 -0
  32. package/dist/types-legacy-CTsJvvxI.d.ts +137 -0
  33. package/dist/{index-QMtzQI65.d.mts → userDB-C5dcuRZs.d.ts} +3 -251
  34. package/dist/{index-QMtzQI65.d.ts → userDB-ZSwOXiYN.d.mts} +3 -251
  35. package/dist/util/packer/index.d.mts +25 -0
  36. package/dist/util/packer/index.d.ts +25 -0
  37. package/dist/util/packer/index.js +307 -0
  38. package/dist/util/packer/index.js.map +1 -0
  39. package/dist/util/packer/index.mjs +280 -0
  40. package/dist/util/packer/index.mjs.map +1 -0
  41. package/package.json +12 -2
  42. package/src/core/interfaces/contentSource.ts +8 -6
  43. package/src/core/interfaces/userDB.ts +2 -2
  44. package/src/factory.ts +10 -7
  45. package/src/impl/{pouch/userDB.ts → common/BaseUserDB.ts} +225 -260
  46. package/src/impl/common/SyncStrategy.ts +90 -0
  47. package/src/impl/common/index.ts +23 -0
  48. package/src/impl/common/types.ts +50 -0
  49. package/src/impl/common/userDBHelpers.ts +144 -0
  50. package/src/impl/couch/CouchDBSyncStrategy.ts +209 -0
  51. package/src/impl/{pouch → couch}/PouchDataLayerProvider.ts +12 -7
  52. package/src/impl/{pouch → couch}/adminDB.ts +3 -3
  53. package/src/impl/{pouch → couch}/auth.ts +2 -2
  54. package/src/impl/{pouch → couch}/classroomDB.ts +6 -6
  55. package/src/impl/{pouch → couch}/courseAPI.ts +28 -5
  56. package/src/impl/{pouch → couch}/courseDB.ts +24 -10
  57. package/src/impl/{pouch → couch}/courseLookupDB.ts +1 -1
  58. package/src/impl/{pouch → couch}/index.ts +27 -20
  59. package/src/impl/{pouch → couch}/updateQueue.ts +5 -1
  60. package/src/impl/{pouch → couch}/user-course-relDB.ts +6 -1
  61. package/src/impl/static/NoOpSyncStrategy.ts +70 -0
  62. package/src/impl/static/StaticDataLayerProvider.ts +89 -0
  63. package/src/impl/static/StaticDataUnpacker.ts +376 -0
  64. package/src/impl/static/courseDB.ts +257 -0
  65. package/src/impl/static/coursesDB.ts +37 -0
  66. package/src/impl/static/index.ts +8 -0
  67. package/src/impl/static/userDB.ts +179 -0
  68. package/src/index.ts +1 -1
  69. package/src/study/SessionController.ts +4 -4
  70. package/src/study/SpacedRepetition.ts +3 -3
  71. package/src/study/getCardDataShape.ts +2 -2
  72. package/src/util/index.ts +1 -0
  73. package/src/util/packer/CouchDBToStaticPacker.ts +349 -0
  74. package/src/util/packer/index.ts +4 -0
  75. package/src/util/packer/types.ts +52 -0
  76. package/tsconfig.json +7 -10
  77. package/tsup.config.ts +5 -3
  78. /package/src/impl/{pouch → couch}/clientCache.ts +0 -0
  79. /package/src/impl/{pouch → couch}/pouchdb-setup.ts +0 -0
  80. /package/src/impl/{pouch → couch}/types.ts +0 -0
@@ -0,0 +1,179 @@
1
+ // packages/db/src/impl/static/userDB.ts
2
+
3
+ import { UserDBInterface } from '../../core/interfaces';
4
+
5
+ export class StaticUserDB implements UserDBInterface {
6
+ constructor(_prefix: string) {}
7
+
8
+ isLoggedIn(): boolean {
9
+ return false; // Always guest in static mode
10
+ }
11
+
12
+ getUsername(): string {
13
+ return 'Guest';
14
+ }
15
+
16
+ async createAccount(_username: string, _password: string): Promise<any> {
17
+ throw new Error('Cannot create accounts in static mode');
18
+ }
19
+
20
+ async login(_username: string, _password: string): Promise<any> {
21
+ throw new Error('Cannot login in static mode');
22
+ }
23
+
24
+ async logout(): Promise<any> {
25
+ return { ok: true };
26
+ }
27
+
28
+ async getConfig(): Promise<any> {
29
+ return {}; // Default empty config
30
+ }
31
+
32
+ async setConfig(_config: any): Promise<void> {
33
+ // No-op in static mode
34
+ }
35
+
36
+ async putCardRecord<T>(_record: T): Promise<any> {
37
+ throw new Error('Cannot record card interactions in static mode');
38
+ }
39
+
40
+ async getSeenCards(_courseId?: string): Promise<string[]> {
41
+ return []; // No seen cards in static mode
42
+ }
43
+
44
+ async getActiveCards(): Promise<string[]> {
45
+ return []; // No active cards in static mode
46
+ }
47
+
48
+ async registerForCourse(_courseId: string, _previewMode?: boolean): Promise<any> {
49
+ return { ok: true, id: 'static-registration', rev: '1-static' };
50
+ }
51
+
52
+ async getCourseRegistrationsDoc(): Promise<any> {
53
+ return { courses: [] }; // Empty registrations
54
+ }
55
+
56
+ async deregisterFromCourse(_courseId: string): Promise<any> {
57
+ return { ok: true };
58
+ }
59
+
60
+ async addScheduledCard(_card: any): Promise<any> {
61
+ throw new Error('Cannot schedule cards in static mode');
62
+ }
63
+
64
+ async removeScheduledCard(_qualifiedID: string): Promise<any> {
65
+ throw new Error('Cannot remove scheduled cards in static mode');
66
+ }
67
+
68
+ async getScheduledCards(): Promise<any[]> {
69
+ return []; // No scheduled cards in static mode
70
+ }
71
+
72
+ async addActivityRecord(_record: any): Promise<any> {
73
+ throw new Error('Cannot add activity records in static mode');
74
+ }
75
+
76
+ async getActivityRecords(_courseId?: string): Promise<any[]> {
77
+ return []; // No activity records in static mode
78
+ }
79
+
80
+ async setUserElo(_courseId: string, _elo: any): Promise<any> {
81
+ throw new Error('Cannot set user ELO in static mode');
82
+ }
83
+
84
+ async getUserElo(_courseId: string): Promise<any> {
85
+ return { global: { score: 1000, count: 0 }, tags: {}, misc: {} };
86
+ }
87
+
88
+ async updateDocument(_doc: any): Promise<any> {
89
+ throw new Error('Cannot update documents in static mode');
90
+ }
91
+
92
+ async dropCourse(_courseId: string, _dropStatus?: string): Promise<any> {
93
+ return { ok: true };
94
+ }
95
+
96
+ async getCourseRegDoc(_courseId: string): Promise<any> {
97
+ return { courseID: _courseId, status: 'active' };
98
+ }
99
+
100
+ async getActiveCourses(): Promise<any[]> {
101
+ return []; // No active courses in static mode
102
+ }
103
+
104
+ async getPendingReviews(_courseId?: string): Promise<any[]> {
105
+ return []; // No pending reviews in static mode
106
+ }
107
+
108
+ async getScheduledReviewCount(_courseId: string): Promise<number> {
109
+ return 0; // No scheduled reviews in static mode
110
+ }
111
+
112
+ async getReviewstoDate(_date: any, _courseId?: string): Promise<any[]> {
113
+ return []; // No reviews in static mode
114
+ }
115
+
116
+ async addCardToReviews(_card: any): Promise<any> {
117
+ throw new Error('Cannot add cards to reviews in static mode');
118
+ }
119
+
120
+ async removeCardFromReviews(_qualifiedID: string): Promise<any> {
121
+ throw new Error('Cannot remove cards from reviews in static mode');
122
+ }
123
+
124
+ async scheduleCardReview(_card: any): Promise<any> {
125
+ throw new Error('Cannot schedule card reviews in static mode');
126
+ }
127
+
128
+ async removeScheduledCardReview(_qualifiedID: string): Promise<any> {
129
+ throw new Error('Cannot remove scheduled card reviews in static mode');
130
+ }
131
+
132
+ async registerForClassroom(_classId: string, _type: string): Promise<any> {
133
+ throw new Error('Cannot register for classrooms in static mode');
134
+ }
135
+
136
+ async dropFromClassroom(_classId: string): Promise<any> {
137
+ throw new Error('Cannot drop from classrooms in static mode');
138
+ }
139
+
140
+ async getClassroomRegistrations(): Promise<any[]> {
141
+ return []; // No classroom registrations in static mode
142
+ }
143
+
144
+ async syncUp(): Promise<any> {
145
+ return { ok: true }; // No sync needed in static mode
146
+ }
147
+
148
+ async syncDown(): Promise<any> {
149
+ return { ok: true }; // No sync needed in static mode
150
+ }
151
+
152
+ async replicateToRemote(): Promise<any> {
153
+ return { ok: true }; // No replication in static mode
154
+ }
155
+
156
+ async replicateFromRemote(): Promise<any> {
157
+ return { ok: true }; // No replication in static mode
158
+ }
159
+
160
+ async getUserClassrooms(): Promise<{ registrations: any[] }> {
161
+ return { registrations: [] }; // No classrooms in static mode
162
+ }
163
+
164
+ async getActiveClasses(): Promise<any[]> {
165
+ return []; // No active classes in static mode
166
+ }
167
+
168
+ async updateUserElo(_courseId: string, _elo: any): Promise<any> {
169
+ throw new Error('Cannot update user ELO in static mode');
170
+ }
171
+
172
+ async getCourseInterface(_courseId: string): Promise<any> {
173
+ throw new Error('Cannot get course interface in static mode');
174
+ }
175
+
176
+ async update(_doc: any): Promise<any> {
177
+ throw new Error('Cannot update user documents in static mode');
178
+ }
179
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './core';
2
2
 
3
- export { default as CourseLookup } from './impl/pouch/courseLookupDB';
3
+ export { default as CourseLookup } from './impl/couch/courseLookupDB';
4
4
 
5
5
  export * from './study';
6
6
 
@@ -5,11 +5,11 @@ import {
5
5
  StudySessionItem,
6
6
  StudySessionNewItem,
7
7
  StudySessionReviewItem,
8
- } from '@/impl/pouch';
8
+ } from '@db/impl/couch';
9
9
 
10
- import { CardRecord } from '@/core';
11
- import { Loggable } from '@/util';
12
- import { ScheduledCard } from '@/core/types/user';
10
+ import { CardRecord } from '@db/core';
11
+ import { Loggable } from '@db/util';
12
+ import { ScheduledCard } from '@db/core/types/user';
13
13
 
14
14
  function randomInt(min: number, max: number): number {
15
15
  return Math.floor(Math.random() * (max - min + 1)) + min;
@@ -1,6 +1,6 @@
1
- import { CardHistory, CardRecord, QuestionRecord } from '@/core/types/types-legacy';
2
- import { areQuestionRecords } from '@/core/util';
3
- import { Update } from '@/impl/pouch/updateQueue';
1
+ import { CardHistory, CardRecord, QuestionRecord } from '@db/core/types/types-legacy';
2
+ import { areQuestionRecords } from '@db/core/util';
3
+ import { Update } from '@db/impl/couch/updateQueue';
4
4
  import moment from 'moment';
5
5
  import { logger } from '../util/logger';
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { allCourses } from '@vue-skuilder/courses';
2
2
  import { log, NameSpacer, CourseConfig, DataShape } from '@vue-skuilder/common';
3
- import { CardData, DisplayableData } from '@/core';
4
- import { getCourseDB } from '@/impl/pouch/courseAPI';
3
+ import { CardData, DisplayableData } from '@db/core';
4
+ import { getCourseDB } from '@db/impl/couch/courseAPI';
5
5
 
6
6
  export async function getCardDataShape(courseID: string, cardID: string) {
7
7
  const dataShapes: DataShape[] = [];
package/src/util/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './Loggable';
2
+ export * from './packer';
@@ -0,0 +1,349 @@
1
+ // packages/db/src/util/packer/CouchDBToStaticPacker.ts
2
+
3
+ import { CardData, DocType, Tag } from '../../core/types/types-legacy';
4
+ import { logger } from '../logger';
5
+ // CourseConfig interface - simplified for packer use
6
+
7
+ import { CourseConfig } from '@vue-skuilder/common';
8
+ import {
9
+ ChunkMetadata,
10
+ DesignDocument,
11
+ IndexMetadata,
12
+ PackedCourseData,
13
+ PackerConfig,
14
+ StaticCourseManifest,
15
+ } from './types';
16
+
17
+ export class CouchDBToStaticPacker {
18
+ private config: PackerConfig;
19
+
20
+ constructor(config: Partial<PackerConfig> = {}) {
21
+ this.config = {
22
+ chunkSize: 1000,
23
+ includeAttachments: true,
24
+ ...config,
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Pack a CouchDB course database into static data structures
30
+ */
31
+ async packCourse(sourceDB: PouchDB.Database, courseId: string): Promise<PackedCourseData> {
32
+ logger.info(`Starting static pack for course: ${courseId}`);
33
+
34
+ const manifest: StaticCourseManifest = {
35
+ version: '1.0.0',
36
+ courseId,
37
+ courseName: '',
38
+ courseConfig: null,
39
+ lastUpdated: new Date().toISOString(),
40
+ documentCount: 0,
41
+ chunks: [],
42
+ indices: [],
43
+ designDocs: [],
44
+ };
45
+
46
+ // 1. Extract course config
47
+ const courseConfig = await this.extractCourseConfig(sourceDB);
48
+ manifest.courseName = courseConfig.name;
49
+ manifest.courseConfig = courseConfig;
50
+
51
+ // 2. Extract and process design documents
52
+ manifest.designDocs = await this.extractDesignDocs(sourceDB);
53
+
54
+ // 3. Extract all documents by type and create chunks
55
+ const docsByType = await this.extractDocumentsByType(sourceDB);
56
+
57
+ // 4. Create chunks and prepare chunk data
58
+ const chunks = new Map<string, any[]>();
59
+ for (const [docType, docs] of Object.entries(docsByType)) {
60
+ const chunkMetadata = this.createChunks(docs, docType as DocType);
61
+ manifest.chunks.push(...chunkMetadata);
62
+ manifest.documentCount += docs.length;
63
+
64
+ // Prepare chunk data
65
+ this.prepareChunkData(chunkMetadata, docs, chunks);
66
+ }
67
+
68
+ // 5. Build indices
69
+ const indices = new Map<string, any>();
70
+ manifest.indices = await this.buildIndices(docsByType, manifest.designDocs, indices);
71
+
72
+ return {
73
+ manifest,
74
+ chunks,
75
+ indices,
76
+ };
77
+ }
78
+
79
+ private async extractCourseConfig(db: PouchDB.Database): Promise<CourseConfig> {
80
+ try {
81
+ return await db.get<CourseConfig>('CourseConfig');
82
+ } catch (error) {
83
+ logger.error('Failed to extract course config:', error);
84
+ throw new Error('Course config not found');
85
+ }
86
+ }
87
+
88
+ private async extractDesignDocs(db: PouchDB.Database): Promise<DesignDocument[]> {
89
+ const result = await db.allDocs({
90
+ startkey: '_design/',
91
+ endkey: '_design/\ufff0',
92
+ include_docs: true,
93
+ });
94
+
95
+ return result.rows.map((row) => ({
96
+ _id: row.id,
97
+ views: (row.doc as any).views || {},
98
+ }));
99
+ }
100
+
101
+ private async extractDocumentsByType(db: PouchDB.Database): Promise<Record<DocType, any[]>> {
102
+ const allDocs = await db.allDocs({ include_docs: true });
103
+ const docsByType: Record<string, any[]> = {};
104
+
105
+ for (const row of allDocs.rows) {
106
+ if (row.id.startsWith('_')) continue; // Skip design docs
107
+
108
+ const doc = row.doc as any;
109
+ if (doc.docType) {
110
+ if (!docsByType[doc.docType]) {
111
+ docsByType[doc.docType] = [];
112
+ }
113
+ docsByType[doc.docType].push(doc);
114
+ }
115
+ }
116
+
117
+ return docsByType as Record<DocType, any[]>;
118
+ }
119
+
120
+ private createChunks(docs: any[], docType: DocType): ChunkMetadata[] {
121
+ const chunks: ChunkMetadata[] = [];
122
+ const sortedDocs = docs.sort((a, b) => a._id.localeCompare(b._id));
123
+
124
+ for (let i = 0; i < sortedDocs.length; i += this.config.chunkSize) {
125
+ const chunk = sortedDocs.slice(i, i + this.config.chunkSize);
126
+ const chunkId = `${docType}-${String(Math.floor(i / this.config.chunkSize)).padStart(4, '0')}`;
127
+
128
+ chunks.push({
129
+ id: chunkId,
130
+ docType,
131
+ startKey: chunk[0]._id,
132
+ endKey: chunk[chunk.length - 1]._id,
133
+ documentCount: chunk.length,
134
+ path: `chunks/${chunkId}.json`,
135
+ });
136
+ }
137
+
138
+ return chunks;
139
+ }
140
+
141
+ private prepareChunkData(
142
+ chunkMetadata: ChunkMetadata[],
143
+ docs: any[],
144
+ chunks: Map<string, any[]>
145
+ ): void {
146
+ const sortedDocs = docs.sort((a, b) => a._id.localeCompare(b._id));
147
+
148
+ for (const chunk of chunkMetadata) {
149
+ const chunkDocs = sortedDocs.filter(
150
+ (doc) => doc._id >= chunk.startKey && doc._id <= chunk.endKey
151
+ );
152
+
153
+ // Clean documents for storage
154
+ const cleanedDocs = chunkDocs.map((doc) => {
155
+ const cleaned = { ...doc };
156
+ delete cleaned._rev; // Remove revision info
157
+ if (!this.config.includeAttachments) {
158
+ delete cleaned._attachments;
159
+ }
160
+ return cleaned;
161
+ });
162
+
163
+ chunks.set(chunk.id, cleanedDocs);
164
+ }
165
+ }
166
+
167
+ private async buildIndices(
168
+ docsByType: Record<DocType, any[]>,
169
+ designDocs: DesignDocument[],
170
+ indices: Map<string, any>
171
+ ): Promise<IndexMetadata[]> {
172
+ const indexMetadata: IndexMetadata[] = [];
173
+
174
+ // Build ELO index
175
+ if (docsByType[DocType.CARD]) {
176
+ const eloIndexMeta = await this.buildEloIndex(
177
+ docsByType[DocType.CARD] as CardData[],
178
+ indices
179
+ );
180
+ indexMetadata.push(eloIndexMeta);
181
+ }
182
+
183
+ // Build tag indices
184
+ if (docsByType[DocType.TAG]) {
185
+ const tagIndexMeta = await this.buildTagIndex(docsByType[DocType.TAG] as Tag[], indices);
186
+ indexMetadata.push(tagIndexMeta);
187
+ }
188
+
189
+ // Build indices from design documents
190
+ for (const designDoc of designDocs) {
191
+ for (const [viewName, viewDef] of Object.entries(designDoc.views)) {
192
+ if (viewDef.map) {
193
+ const indexMeta = await this.buildViewIndex(
194
+ viewName,
195
+ viewDef.map,
196
+ docsByType,
197
+ indices,
198
+ viewDef.reduce
199
+ );
200
+ if (indexMeta) indexMetadata.push(indexMeta);
201
+ }
202
+ }
203
+ }
204
+
205
+ return indexMetadata;
206
+ }
207
+
208
+ private async buildEloIndex(
209
+ cards: CardData[],
210
+ indices: Map<string, any>
211
+ ): Promise<IndexMetadata> {
212
+ // Build a B-tree like structure for ELO queries
213
+ const eloIndex: Array<{ elo: number; cardId: string }> = [];
214
+
215
+ for (const card of cards) {
216
+ if (card.elo?.global?.score) {
217
+ eloIndex.push({
218
+ elo: card.elo.global.score,
219
+ cardId: (card as any)._id,
220
+ });
221
+ }
222
+ }
223
+
224
+ // Sort by ELO for efficient range queries
225
+ eloIndex.sort((a, b) => a.elo - b.elo);
226
+
227
+ // Create buckets for faster lookup
228
+ const buckets: Record<number, string[]> = {};
229
+ const bucketSize = 50; // ELO points per bucket
230
+
231
+ for (const entry of eloIndex) {
232
+ const bucket = Math.floor(entry.elo / bucketSize) * bucketSize;
233
+ if (!buckets[bucket]) buckets[bucket] = [];
234
+ buckets[bucket].push(entry.cardId);
235
+ }
236
+
237
+ // Store the index data
238
+ indices.set('elo', {
239
+ sorted: eloIndex,
240
+ buckets: buckets,
241
+ stats: {
242
+ min: eloIndex[0]?.elo || 0,
243
+ max: eloIndex[eloIndex.length - 1]?.elo || 0,
244
+ count: eloIndex.length,
245
+ },
246
+ });
247
+
248
+ return {
249
+ name: 'elo',
250
+ type: 'btree',
251
+ path: 'indices/elo.json',
252
+ };
253
+ }
254
+
255
+ private async buildTagIndex(tags: Tag[], indices: Map<string, any>): Promise<IndexMetadata> {
256
+ // Build inverted index for tags
257
+ const tagIndex: Record<
258
+ string,
259
+ {
260
+ cardIds: string[];
261
+ snippet: string;
262
+ count: number;
263
+ }
264
+ > = {};
265
+
266
+ for (const tag of tags) {
267
+ tagIndex[tag.name] = {
268
+ cardIds: tag.taggedCards,
269
+ snippet: tag.snippet,
270
+ count: tag.taggedCards.length,
271
+ };
272
+ }
273
+
274
+ // Also build a reverse index (card -> tags)
275
+ const cardToTags: Record<string, string[]> = {};
276
+ for (const tag of tags) {
277
+ for (const cardId of tag.taggedCards) {
278
+ if (!cardToTags[cardId]) cardToTags[cardId] = [];
279
+ cardToTags[cardId].push(tag.name);
280
+ }
281
+ }
282
+
283
+ indices.set('tags', {
284
+ byTag: tagIndex,
285
+ byCard: cardToTags,
286
+ });
287
+
288
+ return {
289
+ name: 'tags',
290
+ type: 'hash',
291
+ path: 'indices/tags.json',
292
+ };
293
+ }
294
+
295
+ private async buildViewIndex(
296
+ viewName: string,
297
+ mapFunction: string,
298
+ docsByType: Record<DocType, any[]>,
299
+ indices: Map<string, any>,
300
+ _reduceFunction?: string
301
+ ): Promise<IndexMetadata | null> {
302
+ try {
303
+ // Parse and execute the map function in a sandboxed way
304
+ // This is a simplified version - in production you'd want proper sandboxing
305
+ const viewResults: Array<{ key: any; value: any; id: string }> = [];
306
+
307
+ // Create a safe emit function
308
+ const emit = (key: any, value: any) => {
309
+ viewResults.push({ key, value, id: currentDocId });
310
+ };
311
+
312
+ let currentDocId = '';
313
+
314
+ // Create the map function
315
+ // Note: This is simplified and would need proper sandboxing in production
316
+ const mapFn = new Function('doc', 'emit', mapFunction);
317
+
318
+ // Run map function on all documents
319
+ for (const docs of Object.values(docsByType)) {
320
+ for (const doc of docs) {
321
+ currentDocId = doc._id;
322
+ try {
323
+ mapFn(doc, emit);
324
+ } catch (error) {
325
+ logger.warn(`Map function error for doc ${doc._id}:`, error);
326
+ }
327
+ }
328
+ }
329
+
330
+ // Sort by key for efficient querying
331
+ viewResults.sort((a, b) => {
332
+ if (a.key < b.key) return -1;
333
+ if (a.key > b.key) return 1;
334
+ return 0;
335
+ });
336
+
337
+ indices.set(`view-${viewName}`, viewResults);
338
+
339
+ return {
340
+ name: `view-${viewName}`,
341
+ type: 'btree',
342
+ path: `indices/view-${viewName}.json`,
343
+ };
344
+ } catch (error) {
345
+ logger.error(`Failed to build index for view ${viewName}:`, error);
346
+ return null;
347
+ }
348
+ }
349
+ }
@@ -0,0 +1,4 @@
1
+ // packages/db/src/util/packer/index.ts
2
+
3
+ export * from './types.js';
4
+ export { CouchDBToStaticPacker } from './CouchDBToStaticPacker.js';
@@ -0,0 +1,52 @@
1
+ // packages/db/src/util/packer/types.ts
2
+
3
+ import { CourseConfig } from '@vue-skuilder/common';
4
+ import { DocType } from '../../core/types/types-legacy';
5
+
6
+ export interface StaticCourseManifest {
7
+ version: string;
8
+ courseId: string;
9
+ courseName: string;
10
+ courseConfig: CourseConfig | null; // Full CourseConfig object
11
+ lastUpdated: string;
12
+ documentCount: number;
13
+ chunks: ChunkMetadata[];
14
+ indices: IndexMetadata[];
15
+ designDocs: DesignDocument[];
16
+ }
17
+
18
+ export interface ChunkMetadata {
19
+ id: string;
20
+ docType: DocType;
21
+ startKey: string;
22
+ endKey: string;
23
+ documentCount: number;
24
+ path: string; // Relative path for file writing
25
+ }
26
+
27
+ export interface IndexMetadata {
28
+ name: string;
29
+ type: 'btree' | 'hash' | 'spatial';
30
+ path: string; // Relative path for file writing
31
+ }
32
+
33
+ export interface DesignDocument {
34
+ _id: string;
35
+ views: {
36
+ [viewName: string]: {
37
+ map: string;
38
+ reduce?: string;
39
+ };
40
+ };
41
+ }
42
+
43
+ export interface PackerConfig {
44
+ chunkSize: number;
45
+ includeAttachments: boolean;
46
+ }
47
+
48
+ export interface PackedCourseData {
49
+ manifest: StaticCourseManifest;
50
+ chunks: Map<string, any[]>; // chunkId -> documents
51
+ indices: Map<string, any>; // indexName -> index data
52
+ }
package/tsconfig.json CHANGED
@@ -1,12 +1,9 @@
1
1
  {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "noEmit": true,
7
- "paths": {
8
- "@/*": ["./src/*"]
9
- }
10
- },
11
- "include": ["src"]
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "noEmit": true
7
+ },
8
+ "include": ["src"]
12
9
  }
package/tsup.config.ts CHANGED
@@ -4,7 +4,9 @@ export default defineConfig({
4
4
  entry: [
5
5
  'src/index.ts',
6
6
  'src/core/index.ts',
7
- 'src/pouch/index.ts'
7
+ 'src/impl/couch/index.ts',
8
+ 'src/impl/static/index.ts',
9
+ 'src/util/packer/index.ts',
8
10
  ],
9
11
  format: ['cjs', 'esm'],
10
12
  dts: true,
@@ -12,6 +14,6 @@ export default defineConfig({
12
14
  sourcemap: true,
13
15
  clean: true,
14
16
  outExtension: ({ format }) => ({
15
- js: format === 'esm' ? '.mjs' : '.js'
16
- })
17
+ js: format === 'esm' ? '.mjs' : '.js',
18
+ }),
17
19
  });
File without changes
File without changes
File without changes