@contentrain/query 3.2.0 → 4.0.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.
package/README.md CHANGED
@@ -1,17 +1,24 @@
1
1
  # @contentrain/query
2
2
 
3
- Core package of the Contentrain SDK. This package provides the fundamental functionality and types for interacting with Contentrain CMS.
3
+ Core package of the Contentrain SDK. Originally designed for JSON-based content management, now extended with SQLite integration for enhanced performance and scalability.
4
4
 
5
5
  ## Features
6
6
 
7
- - 🚀 High-performance content loading
7
+ ### Core Features (JSON-based)
8
+ - 📦 JSON file-based content management
9
+ - 🌍 Multi-language support with JSON files
8
10
  - 💾 LRU caching with size-based eviction
9
- - 🔍 Advanced query capabilities with type-safe operators
10
- - 📦 Full TypeScript support with generic types
11
- - 🛡️ Comprehensive error handling
12
- - Memory-optimized performance
13
- - 🌍 Multi-language support
14
- - 🔄 Relation resolution
11
+ - 🔍 Type-safe query operations
12
+ - 📝 Full TypeScript support
13
+
14
+ ### SQLite Extension Features
15
+ - 🚀 High-performance SQLite database integration
16
+ - 🔄 Advanced relation management (One-to-One & One-to-Many)
17
+ - 🗃️ Efficient data indexing and querying
18
+ - 🔒 Thread-safe database operations
19
+ - 📊 Advanced filtering and sorting
20
+ - 🎯 Efficient pagination
21
+ - 🌐 Enhanced translation support
15
22
 
16
23
  ## Installation
17
24
 
@@ -28,119 +35,119 @@ pnpm add @contentrain/query
28
35
 
29
36
  ## Usage
30
37
 
31
- ### Content Loading
38
+ ### JSON-based Usage (Original)
32
39
 
33
40
  ```typescript
34
- import { ContentLoader } from '@contentrain/query';
35
-
36
- const loader = new ContentLoader({
37
- contentDir: './content',
38
- defaultLocale: 'en',
39
- cache: true,
40
- ttl: 60 * 1000, // 1 minute
41
- maxCacheSize: 100 // 100 MB
42
- });
41
+ import { ContentrainSDK } from '@contentrain/query';
43
42
 
44
- // Load all blog posts
45
- const posts = await loader.load('posts');
43
+ // Initialize SDK with JSON files
44
+ const sdk = new ContentrainSDK({
45
+ contentDir: './content', // Directory containing JSON files
46
+ defaultLocale: 'en'
47
+ });
46
48
 
47
- // Load with locale
48
- const trPosts = await loader.load('posts').locale('tr');
49
+ // Query JSON content
50
+ const posts = await sdk.query('posts')
51
+ .where('status', 'eq', 'publish')
52
+ .get();
49
53
 
50
- // Error handling
51
- try {
52
- const post = await loader.load('posts', 'non-existent-post');
53
- } catch (error) {
54
- if (error instanceof ContentNotFoundError) {
55
- console.error('Post not found');
56
- } else if (error instanceof ContentValidationError) {
57
- console.error('Content validation failed');
58
- }
59
- }
54
+ // Load with translations
55
+ const trPosts = await sdk.query('posts')
56
+ .locale('tr')
57
+ .get();
60
58
  ```
61
59
 
62
- ### Query Operations
60
+ ### SQLite Usage (Extended Feature)
63
61
 
64
62
  ```typescript
65
- import { ContentrainSDK } from '@contentrain/query';
63
+ import { SQLiteQueryBuilder, BaseSQLiteLoader } from '@contentrain/query';
66
64
 
67
- const sdk = new ContentrainSDK({
68
- contentDir: './content'
69
- });
65
+ // Initialize SQLite loader
66
+ const loader = new BaseSQLiteLoader('path/to/database.db');
70
67
 
71
- // Type-safe querying
72
- interface Post {
73
- ID: string;
74
- title: string;
75
- status: 'draft' | 'published';
76
- tags: string[];
77
- createdAt: string;
78
- }
68
+ // Create SQLite query builder
69
+ const builder = new SQLiteQueryBuilder('posts', loader);
79
70
 
80
- const query = sdk.query<{
81
- fields: Post;
82
- locales: 'en' | 'tr';
83
- relations: {
84
- author: Author;
85
- categories: Category[];
86
- }
87
- }>('posts');
88
-
89
- // Available operators
90
- const posts = await query
91
- .where('status', 'eq', 'published')
92
- .where('tags', 'contains', ['javascript'])
93
- .where('createdAt', 'gt', '2024-01-01')
94
- .where('category', 'in', ['tech', 'programming'])
95
- .orderBy('createdAt', 'desc')
96
- .limit(5)
71
+ // Execute SQLite query
72
+ const result = await builder
73
+ .where('status', 'eq', 'publish')
97
74
  .get();
75
+ ```
98
76
 
99
- // Relation handling
100
- const postsWithRelations = await query
101
- .include(['author', 'categories'])
102
- .where('status', 'eq', 'published')
103
- .get();
77
+ ## Data Management
104
78
 
105
- // Locale support
106
- const trPosts = await query
107
- .locale('tr')
108
- .where('status', 'eq', 'published')
79
+ ### JSON-based Storage
80
+ - Content stored in JSON files
81
+ - Directory-based organization
82
+ - File-based translations
83
+ - Simple version control with Git
84
+
85
+ ### SQLite Storage
86
+ - Relational database storage
87
+ - Optimized for querying and relations
88
+ - Efficient data indexing
89
+ - Better performance for large datasets
90
+
91
+ ## Relations
92
+
93
+ ### JSON Relations
94
+ ```typescript
95
+ // JSON-based relation loading
96
+ const posts = await sdk.query('posts')
97
+ .include('author')
109
98
  .get();
110
99
  ```
111
100
 
112
- ### Caching
113
-
101
+ ### SQLite Relations
114
102
  ```typescript
115
- import { MemoryCache } from '@contentrain/query';
103
+ interface Post {
104
+ id: string;
105
+ title: string;
106
+ author_id: string;
107
+ _relations?: {
108
+ author: Author;
109
+ categories: Category[];
110
+ }
111
+ }
116
112
 
117
- const cache = new MemoryCache({
118
- maxSize: 100, // Maximum cache size in MB
119
- defaultTTL: 60 * 1000, // Default TTL in ms
120
- });
113
+ // One-to-One in SQLite
114
+ const post = await builder
115
+ .include('author')
116
+ .where('id', 'eq', '123')
117
+ .first();
118
+
119
+ // One-to-Many in SQLite
120
+ const postWithCategories = await builder
121
+ .include('categories')
122
+ .where('id', 'eq', '123')
123
+ .first();
124
+ ```
121
125
 
122
- // Set with custom TTL
123
- await cache.set('key', data, 5 * 60 * 1000); // 5 minutes TTL
126
+ ## Translations
124
127
 
125
- // Get with type safety
126
- const data = await cache.get<Post[]>('key');
128
+ ### JSON Translations
129
+ - Separate JSON files for each locale
130
+ - File-based translation management
131
+ - Git-friendly structure
127
132
 
128
- // Cache stats
129
- const stats = cache.getStats();
130
- console.log(`
131
- Hits: ${stats.hits}
132
- Misses: ${stats.misses}
133
- Size: ${stats.size} bytes
134
- Last Cleanup: ${stats.lastCleanup}
135
- `);
133
+ ### SQLite Translations
134
+ ```typescript
135
+ // Dedicated translation tables
136
+ const trPost = await builder
137
+ .locale('tr')
138
+ .where('id', 'eq', '123')
139
+ .first();
140
+
141
+ // Fallback support
142
+ const result = await builder
143
+ .locale('tr')
144
+ .include(['author', 'categories'])
145
+ .get();
136
146
  ```
137
147
 
138
148
  ## API Reference
139
149
 
140
- ### ContentrainSDK
141
-
142
- Main entry point for the SDK.
143
-
150
+ ### ContentrainSDK (JSON-based)
144
151
  ```typescript
145
152
  class ContentrainSDK {
146
153
  constructor(options: ContentLoaderOptions)
@@ -149,120 +156,105 @@ class ContentrainSDK {
149
156
  }
150
157
  ```
151
158
 
152
- ### Query Builder
153
-
159
+ ### SQLiteQueryBuilder (SQLite Extension)
154
160
  ```typescript
155
- interface QueryBuilder<T> {
156
- // Filter operations
161
+ class SQLiteQueryBuilder<T extends DBRecord> {
162
+ constructor(model: string, connection: BaseSQLiteLoader)
163
+
164
+ // Query Methods
157
165
  where<K extends keyof T>(
158
166
  field: K,
159
- operator: QueryOperator,
167
+ operator: Operator,
160
168
  value: T[K] | T[K][]
161
169
  ): this
162
170
 
163
- // Available operators:
164
- // 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' |
165
- // 'in' | 'nin' | 'contains' | 'startsWith' | 'endsWith'
166
-
167
- // Relation operations
168
171
  include(relations: string | string[]): this
169
-
170
- // Sorting
171
172
  orderBy(field: keyof T, direction?: 'asc' | 'desc'): this
172
-
173
- // Pagination
174
173
  limit(count: number): this
175
174
  offset(count: number): this
176
-
177
- // Locale
178
175
  locale(code: string): this
179
176
 
180
- // Cache control
181
- cache(ttl?: number): this
182
- noCache(): this
183
- bypassCache(): this
184
-
185
- // Execution
177
+ // Execution Methods
186
178
  get(): Promise<QueryResult<T>>
187
179
  first(): Promise<T | null>
188
180
  count(): Promise<number>
189
181
  }
190
-
191
- interface QueryResult<T> {
192
- data: T[]
193
- total: number
194
- pagination?: {
195
- limit: number
196
- offset: number
197
- hasMore: boolean
198
- }
199
- }
200
182
  ```
201
183
 
202
- ### Cache Manager
184
+ ## Best Practices
203
185
 
204
- ```typescript
205
- interface CacheManager {
206
- set<T>(key: string, value: T, ttl?: number): Promise<void>
207
- get<T>(key: string): Promise<T | null>
208
- delete(key: string): Promise<void>
209
- clear(): Promise<void>
210
- getStats(): CacheStats
211
- }
186
+ ### JSON vs SQLite Usage
212
187
 
213
- interface CacheStats {
214
- hits: number
215
- misses: number
216
- size: number
217
- lastCleanup: number
218
- }
188
+ ```typescript
189
+ // ✅ Use JSON when:
190
+ // - Small to medium dataset
191
+ // - Git-based version control is priority
192
+ // - Simple content structure
193
+ const posts = await sdk.query('posts').get();
194
+
195
+ // ✅ Use SQLite when:
196
+ // - Large dataset
197
+ // - Complex relations
198
+ // - Performance is critical
199
+ // - Advanced querying needed
200
+ const posts = await builder
201
+ .where('status', 'eq', 'publish')
202
+ .include(['author', 'categories'])
203
+ .orderBy('created_at', 'desc')
204
+ .limit(10)
205
+ .get();
219
206
  ```
220
207
 
221
- ### Content Loader
208
+ ### Performance Considerations
222
209
 
223
- ```typescript
224
- interface ContentLoader {
225
- load<T>(model: string): Promise<LoaderResult<T>>
226
- resolveRelation<T, R>(
227
- model: string,
228
- relationField: keyof T,
229
- data: T[],
230
- locale?: string
231
- ): Promise<R[]>
232
- clearCache(): Promise<void>
233
- refreshCache(model: string): Promise<void>
234
- getCacheStats(): CacheStats
235
- }
236
- ```
210
+ #### JSON Storage
211
+ - Keep files organized in directories
212
+ - Use appropriate file naming
213
+ - Consider file size for large datasets
237
214
 
238
- ## Error Handling
215
+ #### SQLite Storage
216
+ - Use appropriate indexes
217
+ - Optimize relation queries
218
+ - Implement pagination
219
+ - Use eager loading for relations
239
220
 
240
- The package provides specific error types for different scenarios:
221
+ ## Error Handling
241
222
 
242
223
  ```typescript
243
- import {
244
- ContentrainError, // Base error class
245
- ContentNotFoundError,
246
- ContentValidationError,
247
- CacheError,
248
- RelationError
249
- } from '@contentrain/query';
250
-
251
224
  try {
252
- const posts = await loader.load('posts');
225
+ const result = await builder
226
+ .where('status', 'eq', 'publish')
227
+ .get();
253
228
  } catch (error) {
254
- if (error instanceof ContentNotFoundError) {
255
- // Handle not found
256
- } else if (error instanceof ContentValidationError) {
229
+ if (error instanceof SQLiteError) {
230
+ // Handle SQLite specific errors
231
+ } else if (error instanceof ValidationError) {
257
232
  // Handle validation errors
258
- } else if (error instanceof CacheError) {
259
- // Handle cache errors
260
233
  } else if (error instanceof RelationError) {
261
234
  // Handle relation errors
235
+ } else if (error instanceof FileSystemError) {
236
+ // Handle JSON file system errors
262
237
  }
263
238
  }
264
239
  ```
265
240
 
241
+ ## Migration Guide
242
+
243
+ ### From JSON to SQLite
244
+ 1. Initialize SQLite database
245
+ 2. Import JSON content
246
+ 3. Set up relations
247
+ 4. Update queries to use SQLiteQueryBuilder
248
+ 5. Test and verify data integrity
249
+
250
+ ## Contributing
251
+
252
+ 1. Fork the repository
253
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
254
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
255
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
256
+ 5. Open a Pull Request
257
+
266
258
  ## License
267
259
 
268
260
  MIT