@contentrain/query 3.1.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 +164 -172
- package/dist/index.d.mts +485 -206
- package/dist/index.d.ts +485 -206
- package/dist/index.js +30 -669
- package/dist/index.mjs +30 -663
- package/package.json +13 -4
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
# @contentrain/query
|
|
2
2
|
|
|
3
|
-
Core package of the Contentrain SDK.
|
|
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
|
-
|
|
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
|
-
- 🔍
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
- 🔄
|
|
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
|
-
###
|
|
38
|
+
### JSON-based Usage (Original)
|
|
32
39
|
|
|
33
40
|
```typescript
|
|
34
|
-
import {
|
|
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
|
-
//
|
|
45
|
-
const
|
|
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
|
-
//
|
|
48
|
-
const
|
|
49
|
+
// Query JSON content
|
|
50
|
+
const posts = await sdk.query('posts')
|
|
51
|
+
.where('status', 'eq', 'publish')
|
|
52
|
+
.get();
|
|
49
53
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
###
|
|
60
|
+
### SQLite Usage (Extended Feature)
|
|
63
61
|
|
|
64
62
|
```typescript
|
|
65
|
-
import {
|
|
63
|
+
import { SQLiteQueryBuilder, BaseSQLiteLoader } from '@contentrain/query';
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
65
|
+
// Initialize SQLite loader
|
|
66
|
+
const loader = new BaseSQLiteLoader('path/to/database.db');
|
|
70
67
|
|
|
71
|
-
//
|
|
72
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
100
|
-
const postsWithRelations = await query
|
|
101
|
-
.include(['author', 'categories'])
|
|
102
|
-
.where('status', 'eq', 'published')
|
|
103
|
-
.get();
|
|
77
|
+
## Data Management
|
|
104
78
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
###
|
|
113
|
-
|
|
101
|
+
### SQLite Relations
|
|
114
102
|
```typescript
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
await cache.set('key', data, 5 * 60 * 1000); // 5 minutes TTL
|
|
126
|
+
## Translations
|
|
124
127
|
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
### JSON Translations
|
|
129
|
+
- Separate JSON files for each locale
|
|
130
|
+
- File-based translation management
|
|
131
|
+
- Git-friendly structure
|
|
127
132
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
###
|
|
153
|
-
|
|
159
|
+
### SQLiteQueryBuilder (SQLite Extension)
|
|
154
160
|
```typescript
|
|
155
|
-
|
|
156
|
-
|
|
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:
|
|
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
|
-
//
|
|
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
|
-
|
|
184
|
+
## Best Practices
|
|
203
185
|
|
|
204
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
###
|
|
208
|
+
### Performance Considerations
|
|
222
209
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
215
|
+
#### SQLite Storage
|
|
216
|
+
- Use appropriate indexes
|
|
217
|
+
- Optimize relation queries
|
|
218
|
+
- Implement pagination
|
|
219
|
+
- Use eager loading for relations
|
|
239
220
|
|
|
240
|
-
|
|
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
|
|
225
|
+
const result = await builder
|
|
226
|
+
.where('status', 'eq', 'publish')
|
|
227
|
+
.get();
|
|
253
228
|
} catch (error) {
|
|
254
|
-
if (error instanceof
|
|
255
|
-
// Handle
|
|
256
|
-
} else if (error instanceof
|
|
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
|