@objectql/driver-fs 0.1.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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/README.zh-CN.md +422 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.js +636 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File System Driver for ObjectQL (Production-Ready)
|
|
4
|
+
*
|
|
5
|
+
* A persistent file-based driver for ObjectQL that stores data in JSON files.
|
|
6
|
+
* Each object type is stored in a separate JSON file for easy inspection and backup.
|
|
7
|
+
*
|
|
8
|
+
* ✅ Production-ready features:
|
|
9
|
+
* - Persistent storage with JSON files
|
|
10
|
+
* - One file per table/object (e.g., users.json, projects.json)
|
|
11
|
+
* - Atomic write operations with temp file + rename strategy
|
|
12
|
+
* - Full query support (filters, sorting, pagination)
|
|
13
|
+
* - Backup on write for data safety
|
|
14
|
+
* - Human-readable JSON format
|
|
15
|
+
*
|
|
16
|
+
* Use Cases:
|
|
17
|
+
* - Small to medium datasets (< 10k records per object)
|
|
18
|
+
* - Development and prototyping with persistent data
|
|
19
|
+
* - Configuration and metadata storage
|
|
20
|
+
* - Embedded applications
|
|
21
|
+
* - Scenarios where database setup is not desired
|
|
22
|
+
*/
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.FileSystemDriver = void 0;
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
const types_1 = require("@objectql/types");
|
|
61
|
+
/**
|
|
62
|
+
* FileSystem Driver Implementation
|
|
63
|
+
*
|
|
64
|
+
* Stores ObjectQL documents in JSON files with format:
|
|
65
|
+
* - File: `{dataDir}/{objectName}.json`
|
|
66
|
+
* - Content: Array of records `[{id: "1", ...}, {id: "2", ...}]`
|
|
67
|
+
*/
|
|
68
|
+
class FileSystemDriver {
|
|
69
|
+
constructor(config) {
|
|
70
|
+
this.config = {
|
|
71
|
+
prettyPrint: true,
|
|
72
|
+
enableBackup: true,
|
|
73
|
+
strictMode: false,
|
|
74
|
+
...config
|
|
75
|
+
};
|
|
76
|
+
this.idCounters = new Map();
|
|
77
|
+
this.cache = new Map();
|
|
78
|
+
// Ensure data directory exists
|
|
79
|
+
if (!fs.existsSync(this.config.dataDir)) {
|
|
80
|
+
fs.mkdirSync(this.config.dataDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
// Load initial data if provided
|
|
83
|
+
if (config.initialData) {
|
|
84
|
+
this.loadInitialData(config.initialData);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Load initial data into the store.
|
|
89
|
+
*/
|
|
90
|
+
loadInitialData(data) {
|
|
91
|
+
for (const [objectName, records] of Object.entries(data)) {
|
|
92
|
+
// Only load if file doesn't exist yet
|
|
93
|
+
const filePath = this.getFilePath(objectName);
|
|
94
|
+
if (!fs.existsSync(filePath)) {
|
|
95
|
+
const recordsWithIds = records.map(record => ({
|
|
96
|
+
...record,
|
|
97
|
+
id: record.id || record._id || this.generateId(objectName),
|
|
98
|
+
created_at: record.created_at || new Date().toISOString(),
|
|
99
|
+
updated_at: record.updated_at || new Date().toISOString()
|
|
100
|
+
}));
|
|
101
|
+
this.saveRecords(objectName, recordsWithIds);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the file path for an object type.
|
|
107
|
+
*/
|
|
108
|
+
getFilePath(objectName) {
|
|
109
|
+
return path.join(this.config.dataDir, `${objectName}.json`);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Load records from file into memory cache.
|
|
113
|
+
*/
|
|
114
|
+
loadRecords(objectName) {
|
|
115
|
+
// Check cache first
|
|
116
|
+
if (this.cache.has(objectName)) {
|
|
117
|
+
return this.cache.get(objectName);
|
|
118
|
+
}
|
|
119
|
+
const filePath = this.getFilePath(objectName);
|
|
120
|
+
if (!fs.existsSync(filePath)) {
|
|
121
|
+
// File doesn't exist yet, return empty array
|
|
122
|
+
this.cache.set(objectName, []);
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
127
|
+
// Handle empty file
|
|
128
|
+
if (!content || content.trim() === '') {
|
|
129
|
+
this.cache.set(objectName, []);
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
let records;
|
|
133
|
+
try {
|
|
134
|
+
records = JSON.parse(content);
|
|
135
|
+
}
|
|
136
|
+
catch (parseError) {
|
|
137
|
+
throw new types_1.ObjectQLError({
|
|
138
|
+
code: 'INVALID_JSON_FORMAT',
|
|
139
|
+
message: `File ${filePath} contains invalid JSON: ${parseError.message}`,
|
|
140
|
+
details: { objectName, filePath, parseError }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (!Array.isArray(records)) {
|
|
144
|
+
throw new types_1.ObjectQLError({
|
|
145
|
+
code: 'INVALID_DATA_FORMAT',
|
|
146
|
+
message: `File ${filePath} does not contain a valid array`,
|
|
147
|
+
details: { objectName, filePath }
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
this.cache.set(objectName, records);
|
|
151
|
+
return records;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// If it's already an ObjectQLError, rethrow it
|
|
155
|
+
if (error.code && error.code.startsWith('INVALID_')) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
if (error.code === 'ENOENT') {
|
|
159
|
+
this.cache.set(objectName, []);
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
throw new types_1.ObjectQLError({
|
|
163
|
+
code: 'FILE_READ_ERROR',
|
|
164
|
+
message: `Failed to read file for object '${objectName}': ${error.message}`,
|
|
165
|
+
details: { objectName, filePath, error }
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Save records to file with atomic write strategy.
|
|
171
|
+
*/
|
|
172
|
+
saveRecords(objectName, records) {
|
|
173
|
+
const filePath = this.getFilePath(objectName);
|
|
174
|
+
const tempPath = `${filePath}.tmp`;
|
|
175
|
+
const backupPath = `${filePath}.bak`;
|
|
176
|
+
try {
|
|
177
|
+
// Create backup if file exists and backup is enabled
|
|
178
|
+
if (this.config.enableBackup && fs.existsSync(filePath)) {
|
|
179
|
+
fs.copyFileSync(filePath, backupPath);
|
|
180
|
+
}
|
|
181
|
+
// Write to temporary file
|
|
182
|
+
const content = this.config.prettyPrint
|
|
183
|
+
? JSON.stringify(records, null, 2)
|
|
184
|
+
: JSON.stringify(records);
|
|
185
|
+
fs.writeFileSync(tempPath, content, 'utf8');
|
|
186
|
+
// Atomic rename (replaces original file)
|
|
187
|
+
fs.renameSync(tempPath, filePath);
|
|
188
|
+
// Update cache
|
|
189
|
+
this.cache.set(objectName, records);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
// Clean up temp file if it exists
|
|
193
|
+
if (fs.existsSync(tempPath)) {
|
|
194
|
+
fs.unlinkSync(tempPath);
|
|
195
|
+
}
|
|
196
|
+
throw new types_1.ObjectQLError({
|
|
197
|
+
code: 'FILE_WRITE_ERROR',
|
|
198
|
+
message: `Failed to write file for object '${objectName}': ${error.message}`,
|
|
199
|
+
details: { objectName, filePath, error }
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Find multiple records matching the query criteria.
|
|
205
|
+
*/
|
|
206
|
+
async find(objectName, query = {}, options) {
|
|
207
|
+
let results = this.loadRecords(objectName);
|
|
208
|
+
// Apply filters
|
|
209
|
+
if (query.filters) {
|
|
210
|
+
results = this.applyFilters(results, query.filters);
|
|
211
|
+
}
|
|
212
|
+
// Apply sorting
|
|
213
|
+
if (query.sort && Array.isArray(query.sort)) {
|
|
214
|
+
results = this.applySort(results, query.sort);
|
|
215
|
+
}
|
|
216
|
+
// Apply pagination
|
|
217
|
+
if (query.skip) {
|
|
218
|
+
results = results.slice(query.skip);
|
|
219
|
+
}
|
|
220
|
+
if (query.limit) {
|
|
221
|
+
results = results.slice(0, query.limit);
|
|
222
|
+
}
|
|
223
|
+
// Apply field projection
|
|
224
|
+
if (query.fields && Array.isArray(query.fields)) {
|
|
225
|
+
results = results.map(doc => this.projectFields(doc, query.fields));
|
|
226
|
+
}
|
|
227
|
+
// Return deep copies to prevent external modifications
|
|
228
|
+
return results.map(r => ({ ...r }));
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Find a single record by ID or query.
|
|
232
|
+
*/
|
|
233
|
+
async findOne(objectName, id, query, options) {
|
|
234
|
+
const records = this.loadRecords(objectName);
|
|
235
|
+
// If ID is provided, fetch directly
|
|
236
|
+
if (id) {
|
|
237
|
+
const record = records.find(r => r.id === id || r._id === id);
|
|
238
|
+
return record ? { ...record } : null;
|
|
239
|
+
}
|
|
240
|
+
// If query is provided, use find and return first result
|
|
241
|
+
if (query) {
|
|
242
|
+
const results = await this.find(objectName, { ...query, limit: 1 }, options);
|
|
243
|
+
return results[0] || null;
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Create a new record.
|
|
249
|
+
*/
|
|
250
|
+
async create(objectName, data, options) {
|
|
251
|
+
// Validate object name
|
|
252
|
+
if (!objectName || objectName.trim() === '') {
|
|
253
|
+
throw new types_1.ObjectQLError({
|
|
254
|
+
code: 'INVALID_OBJECT_NAME',
|
|
255
|
+
message: 'Object name cannot be empty',
|
|
256
|
+
details: { objectName }
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const records = this.loadRecords(objectName);
|
|
260
|
+
// Generate ID if not provided
|
|
261
|
+
const id = data.id || data._id || this.generateId(objectName);
|
|
262
|
+
// Check if record already exists
|
|
263
|
+
const existing = records.find(r => r.id === id || r._id === id);
|
|
264
|
+
if (existing) {
|
|
265
|
+
throw new types_1.ObjectQLError({
|
|
266
|
+
code: 'DUPLICATE_RECORD',
|
|
267
|
+
message: `Record with id '${id}' already exists in '${objectName}'`,
|
|
268
|
+
details: { objectName, id }
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const now = new Date().toISOString();
|
|
272
|
+
const doc = {
|
|
273
|
+
...data,
|
|
274
|
+
id,
|
|
275
|
+
created_at: data.created_at || now,
|
|
276
|
+
updated_at: data.updated_at || now
|
|
277
|
+
};
|
|
278
|
+
records.push(doc);
|
|
279
|
+
this.saveRecords(objectName, records);
|
|
280
|
+
return { ...doc };
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Update an existing record.
|
|
284
|
+
*/
|
|
285
|
+
async update(objectName, id, data, options) {
|
|
286
|
+
const records = this.loadRecords(objectName);
|
|
287
|
+
const index = records.findIndex(r => r.id === id || r._id === id);
|
|
288
|
+
if (index === -1) {
|
|
289
|
+
if (this.config.strictMode) {
|
|
290
|
+
throw new types_1.ObjectQLError({
|
|
291
|
+
code: 'RECORD_NOT_FOUND',
|
|
292
|
+
message: `Record with id '${id}' not found in '${objectName}'`,
|
|
293
|
+
details: { objectName, id }
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
const existing = records[index];
|
|
299
|
+
const doc = {
|
|
300
|
+
...existing,
|
|
301
|
+
...data,
|
|
302
|
+
id: existing.id || existing._id, // Preserve ID
|
|
303
|
+
created_at: existing.created_at, // Preserve created_at
|
|
304
|
+
updated_at: new Date().toISOString()
|
|
305
|
+
};
|
|
306
|
+
records[index] = doc;
|
|
307
|
+
this.saveRecords(objectName, records);
|
|
308
|
+
return { ...doc };
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Delete a record.
|
|
312
|
+
*/
|
|
313
|
+
async delete(objectName, id, options) {
|
|
314
|
+
const records = this.loadRecords(objectName);
|
|
315
|
+
const index = records.findIndex(r => r.id === id || r._id === id);
|
|
316
|
+
if (index === -1) {
|
|
317
|
+
if (this.config.strictMode) {
|
|
318
|
+
throw new types_1.ObjectQLError({
|
|
319
|
+
code: 'RECORD_NOT_FOUND',
|
|
320
|
+
message: `Record with id '${id}' not found in '${objectName}'`,
|
|
321
|
+
details: { objectName, id }
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
records.splice(index, 1);
|
|
327
|
+
this.saveRecords(objectName, records);
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Count records matching filters.
|
|
332
|
+
*/
|
|
333
|
+
async count(objectName, filters, options) {
|
|
334
|
+
const records = this.loadRecords(objectName);
|
|
335
|
+
// Extract actual filters from query object if needed
|
|
336
|
+
let actualFilters = filters;
|
|
337
|
+
if (filters && !Array.isArray(filters) && filters.filters) {
|
|
338
|
+
actualFilters = filters.filters;
|
|
339
|
+
}
|
|
340
|
+
// If no filters or empty object/array, return total count
|
|
341
|
+
if (!actualFilters ||
|
|
342
|
+
(Array.isArray(actualFilters) && actualFilters.length === 0) ||
|
|
343
|
+
(typeof actualFilters === 'object' && !Array.isArray(actualFilters) && Object.keys(actualFilters).length === 0)) {
|
|
344
|
+
return records.length;
|
|
345
|
+
}
|
|
346
|
+
// Count only records matching filters
|
|
347
|
+
return records.filter(record => this.matchesFilters(record, actualFilters)).length;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Get distinct values for a field.
|
|
351
|
+
*/
|
|
352
|
+
async distinct(objectName, field, filters, options) {
|
|
353
|
+
const records = this.loadRecords(objectName);
|
|
354
|
+
const values = new Set();
|
|
355
|
+
for (const record of records) {
|
|
356
|
+
if (!filters || this.matchesFilters(record, filters)) {
|
|
357
|
+
const value = record[field];
|
|
358
|
+
if (value !== undefined && value !== null) {
|
|
359
|
+
values.add(value);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return Array.from(values);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Create multiple records at once.
|
|
367
|
+
*/
|
|
368
|
+
async createMany(objectName, data, options) {
|
|
369
|
+
const results = [];
|
|
370
|
+
for (const item of data) {
|
|
371
|
+
const result = await this.create(objectName, item, options);
|
|
372
|
+
results.push(result);
|
|
373
|
+
}
|
|
374
|
+
return results;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Update multiple records matching filters.
|
|
378
|
+
*/
|
|
379
|
+
async updateMany(objectName, filters, data, options) {
|
|
380
|
+
const records = this.loadRecords(objectName);
|
|
381
|
+
let count = 0;
|
|
382
|
+
for (let i = 0; i < records.length; i++) {
|
|
383
|
+
if (this.matchesFilters(records[i], filters)) {
|
|
384
|
+
const updated = {
|
|
385
|
+
...records[i],
|
|
386
|
+
...data,
|
|
387
|
+
id: records[i].id || records[i]._id, // Preserve ID
|
|
388
|
+
created_at: records[i].created_at, // Preserve created_at
|
|
389
|
+
updated_at: new Date().toISOString()
|
|
390
|
+
};
|
|
391
|
+
records[i] = updated;
|
|
392
|
+
count++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (count > 0) {
|
|
396
|
+
this.saveRecords(objectName, records);
|
|
397
|
+
}
|
|
398
|
+
return { modifiedCount: count };
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Delete multiple records matching filters.
|
|
402
|
+
*/
|
|
403
|
+
async deleteMany(objectName, filters, options) {
|
|
404
|
+
const records = this.loadRecords(objectName);
|
|
405
|
+
const initialCount = records.length;
|
|
406
|
+
const remaining = records.filter(record => !this.matchesFilters(record, filters));
|
|
407
|
+
const deletedCount = initialCount - remaining.length;
|
|
408
|
+
if (deletedCount > 0) {
|
|
409
|
+
this.saveRecords(objectName, remaining);
|
|
410
|
+
}
|
|
411
|
+
return { deletedCount };
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Disconnect (flush cache).
|
|
415
|
+
*/
|
|
416
|
+
async disconnect() {
|
|
417
|
+
this.cache.clear();
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Clear all data from a specific object.
|
|
421
|
+
* Useful for testing or data reset scenarios.
|
|
422
|
+
*/
|
|
423
|
+
async clear(objectName) {
|
|
424
|
+
const filePath = this.getFilePath(objectName);
|
|
425
|
+
// Remove file if exists
|
|
426
|
+
if (fs.existsSync(filePath)) {
|
|
427
|
+
fs.unlinkSync(filePath);
|
|
428
|
+
}
|
|
429
|
+
// Remove backup if exists
|
|
430
|
+
const backupPath = `${filePath}.bak`;
|
|
431
|
+
if (fs.existsSync(backupPath)) {
|
|
432
|
+
fs.unlinkSync(backupPath);
|
|
433
|
+
}
|
|
434
|
+
// Clear cache
|
|
435
|
+
this.cache.delete(objectName);
|
|
436
|
+
this.idCounters.delete(objectName);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Clear all data from all objects.
|
|
440
|
+
* Removes all JSON files in the data directory.
|
|
441
|
+
*/
|
|
442
|
+
async clearAll() {
|
|
443
|
+
const files = fs.readdirSync(this.config.dataDir);
|
|
444
|
+
for (const file of files) {
|
|
445
|
+
if (file.endsWith('.json') || file.endsWith('.json.bak') || file.endsWith('.json.tmp')) {
|
|
446
|
+
const filePath = path.join(this.config.dataDir, file);
|
|
447
|
+
fs.unlinkSync(filePath);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
this.cache.clear();
|
|
451
|
+
this.idCounters.clear();
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Invalidate cache for a specific object.
|
|
455
|
+
* Forces reload from file on next access.
|
|
456
|
+
*/
|
|
457
|
+
invalidateCache(objectName) {
|
|
458
|
+
this.cache.delete(objectName);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Get the size of the cache (number of objects cached).
|
|
462
|
+
*/
|
|
463
|
+
getCacheSize() {
|
|
464
|
+
return this.cache.size;
|
|
465
|
+
}
|
|
466
|
+
// ========== Helper Methods ==========
|
|
467
|
+
/**
|
|
468
|
+
* Apply filters to an array of records.
|
|
469
|
+
*
|
|
470
|
+
* Supports ObjectQL filter format with logical operators (AND/OR):
|
|
471
|
+
* [
|
|
472
|
+
* ['field', 'operator', value],
|
|
473
|
+
* 'or',
|
|
474
|
+
* ['field2', 'operator', value2]
|
|
475
|
+
* ]
|
|
476
|
+
*/
|
|
477
|
+
applyFilters(records, filters) {
|
|
478
|
+
if (!filters || filters.length === 0) {
|
|
479
|
+
return records;
|
|
480
|
+
}
|
|
481
|
+
return records.filter(record => this.matchesFilters(record, filters));
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Check if a single record matches the filter conditions.
|
|
485
|
+
*/
|
|
486
|
+
matchesFilters(record, filters) {
|
|
487
|
+
if (!filters || filters.length === 0) {
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
let conditions = [];
|
|
491
|
+
let operators = [];
|
|
492
|
+
for (const item of filters) {
|
|
493
|
+
if (typeof item === 'string') {
|
|
494
|
+
// Logical operator (and/or)
|
|
495
|
+
operators.push(item.toLowerCase());
|
|
496
|
+
}
|
|
497
|
+
else if (Array.isArray(item)) {
|
|
498
|
+
const [field, operator, value] = item;
|
|
499
|
+
// Handle nested filter groups
|
|
500
|
+
if (typeof field !== 'string') {
|
|
501
|
+
conditions.push(this.matchesFilters(record, item));
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
const matches = this.evaluateCondition(record[field], operator, value);
|
|
505
|
+
conditions.push(matches);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Combine conditions with operators
|
|
510
|
+
if (conditions.length === 0) {
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
let result = conditions[0];
|
|
514
|
+
for (let i = 0; i < operators.length && i + 1 < conditions.length; i++) {
|
|
515
|
+
const op = operators[i];
|
|
516
|
+
const nextCondition = conditions[i + 1];
|
|
517
|
+
if (op === 'or') {
|
|
518
|
+
result = result || nextCondition;
|
|
519
|
+
}
|
|
520
|
+
else { // 'and' or default
|
|
521
|
+
result = result && nextCondition;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return result;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Evaluate a single filter condition.
|
|
528
|
+
*/
|
|
529
|
+
evaluateCondition(fieldValue, operator, compareValue) {
|
|
530
|
+
switch (operator) {
|
|
531
|
+
case '=':
|
|
532
|
+
case '==':
|
|
533
|
+
return fieldValue === compareValue;
|
|
534
|
+
case '!=':
|
|
535
|
+
case '<>':
|
|
536
|
+
return fieldValue !== compareValue;
|
|
537
|
+
case '>':
|
|
538
|
+
return fieldValue > compareValue;
|
|
539
|
+
case '>=':
|
|
540
|
+
return fieldValue >= compareValue;
|
|
541
|
+
case '<':
|
|
542
|
+
return fieldValue < compareValue;
|
|
543
|
+
case '<=':
|
|
544
|
+
return fieldValue <= compareValue;
|
|
545
|
+
case 'in':
|
|
546
|
+
return Array.isArray(compareValue) && compareValue.includes(fieldValue);
|
|
547
|
+
case 'nin':
|
|
548
|
+
case 'not in':
|
|
549
|
+
return Array.isArray(compareValue) && !compareValue.includes(fieldValue);
|
|
550
|
+
case 'contains':
|
|
551
|
+
case 'like':
|
|
552
|
+
return String(fieldValue).toLowerCase().includes(String(compareValue).toLowerCase());
|
|
553
|
+
case 'startswith':
|
|
554
|
+
case 'starts_with':
|
|
555
|
+
return String(fieldValue).toLowerCase().startsWith(String(compareValue).toLowerCase());
|
|
556
|
+
case 'endswith':
|
|
557
|
+
case 'ends_with':
|
|
558
|
+
return String(fieldValue).toLowerCase().endsWith(String(compareValue).toLowerCase());
|
|
559
|
+
case 'between':
|
|
560
|
+
return Array.isArray(compareValue) &&
|
|
561
|
+
fieldValue >= compareValue[0] &&
|
|
562
|
+
fieldValue <= compareValue[1];
|
|
563
|
+
default:
|
|
564
|
+
throw new types_1.ObjectQLError({
|
|
565
|
+
code: 'UNSUPPORTED_OPERATOR',
|
|
566
|
+
message: `[FileSystemDriver] Unsupported operator: ${operator}`,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Apply sorting to an array of records.
|
|
572
|
+
*/
|
|
573
|
+
applySort(records, sort) {
|
|
574
|
+
const sorted = [...records];
|
|
575
|
+
// Apply sorts in reverse order for correct precedence
|
|
576
|
+
for (let i = sort.length - 1; i >= 0; i--) {
|
|
577
|
+
const sortItem = sort[i];
|
|
578
|
+
let field;
|
|
579
|
+
let direction;
|
|
580
|
+
if (Array.isArray(sortItem)) {
|
|
581
|
+
[field, direction] = sortItem;
|
|
582
|
+
}
|
|
583
|
+
else if (typeof sortItem === 'object') {
|
|
584
|
+
field = sortItem.field;
|
|
585
|
+
direction = sortItem.order || sortItem.direction || sortItem.dir || 'asc';
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
sorted.sort((a, b) => {
|
|
591
|
+
const aVal = a[field];
|
|
592
|
+
const bVal = b[field];
|
|
593
|
+
// Handle null/undefined
|
|
594
|
+
if (aVal == null && bVal == null)
|
|
595
|
+
return 0;
|
|
596
|
+
if (aVal == null)
|
|
597
|
+
return 1;
|
|
598
|
+
if (bVal == null)
|
|
599
|
+
return -1;
|
|
600
|
+
// Compare values
|
|
601
|
+
if (aVal < bVal)
|
|
602
|
+
return direction === 'asc' ? -1 : 1;
|
|
603
|
+
if (aVal > bVal)
|
|
604
|
+
return direction === 'asc' ? 1 : -1;
|
|
605
|
+
return 0;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
return sorted;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Project specific fields from a document.
|
|
612
|
+
*/
|
|
613
|
+
projectFields(doc, fields) {
|
|
614
|
+
const result = {};
|
|
615
|
+
for (const field of fields) {
|
|
616
|
+
if (doc[field] !== undefined) {
|
|
617
|
+
result[field] = doc[field];
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Generate a unique ID for a record.
|
|
624
|
+
* Uses timestamp + counter for uniqueness.
|
|
625
|
+
* Note: For production use with high-frequency writes, consider using crypto.randomUUID().
|
|
626
|
+
*/
|
|
627
|
+
generateId(objectName) {
|
|
628
|
+
const counter = (this.idCounters.get(objectName) || 0) + 1;
|
|
629
|
+
this.idCounters.set(objectName, counter);
|
|
630
|
+
// Use timestamp + counter for better uniqueness
|
|
631
|
+
const timestamp = Date.now();
|
|
632
|
+
return `${objectName}-${timestamp}-${counter}`;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
exports.FileSystemDriver = FileSystemDriver;
|
|
636
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|