@objectql/driver-memory 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 ADDED
@@ -0,0 +1,65 @@
1
+ # Changelog
2
+
3
+ All notable changes to the Memory Driver for ObjectQL will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-01-15
9
+
10
+ ### Added
11
+ - Initial release of Memory Driver
12
+ - Full implementation of ObjectQL Driver interface
13
+ - Zero external dependencies
14
+ - In-memory storage using JavaScript Maps
15
+ - Complete query support (filters, sorting, pagination)
16
+ - Bulk operations (createMany, updateMany, deleteMany)
17
+ - Distinct value queries
18
+ - Initial data loading
19
+ - Strict mode for error handling
20
+ - Comprehensive test suite (22 tests)
21
+ - Full documentation and README
22
+ - Support for all ObjectQL query operators:
23
+ - Comparison: =, !=, >, >=, <, <=
24
+ - Set: in, nin
25
+ - String: contains, startswith, endswith
26
+ - Range: between
27
+ - Logical: and, or
28
+ - Utility methods (clear, getSize)
29
+ - TypeScript type definitions
30
+
31
+ ### Features
32
+ - ✅ Production-ready for non-persistent use cases
33
+ - ✅ Perfect for testing and development
34
+ - ✅ Works in all JavaScript environments (Node.js, Browser, Edge)
35
+ - ✅ High performance with O(1) CRUD operations
36
+ - ✅ Thread-safe operations
37
+ - ✅ Atomic updates and deletes
38
+
39
+ ### Use Cases
40
+ - Unit testing without database setup
41
+ - Development and prototyping
42
+ - Edge/Worker environments (Cloudflare Workers, Deno Deploy)
43
+ - Client-side state management
44
+ - Temporary data caching
45
+ - CI/CD pipelines
46
+
47
+ ### Performance
48
+ - Create: O(1)
49
+ - Read by ID: O(1)
50
+ - Update: O(1)
51
+ - Delete: O(1)
52
+ - Find/Query: O(n)
53
+ - Count: O(n)
54
+ - Sort: O(n log n)
55
+
56
+ ### Documentation
57
+ - Comprehensive README with examples
58
+ - API reference
59
+ - Configuration guide
60
+ - Testing guide
61
+ - Performance tips
62
+ - Migration guide
63
+ - Troubleshooting section
64
+
65
+ [0.1.0]: https://github.com/objectstack-ai/objectql/releases/tag/%40objectql/driver-memory%400.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ObjectQL Contributors (https://github.com/objectql)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,542 @@
1
+ # Memory Driver for ObjectQL
2
+
3
+ > ✅ **Production-Ready** - A high-performance in-memory driver for testing, development, and edge environments.
4
+
5
+ ## Overview
6
+
7
+ The Memory Driver is a zero-dependency, production-ready implementation of the ObjectQL Driver interface that stores data in JavaScript Maps. It provides full query support with high performance, making it ideal for scenarios where persistence is not required.
8
+
9
+ ## Features
10
+
11
+ - ✅ **Zero Dependencies** - No external packages required
12
+ - ✅ **Full Query Support** - Filters, sorting, pagination, field projection
13
+ - ✅ **High Performance** - No I/O overhead, all operations in-memory
14
+ - ✅ **Bulk Operations** - createMany, updateMany, deleteMany
15
+ - ✅ **Thread-Safe** - Safe for concurrent operations
16
+ - ✅ **Strict Mode** - Optional error throwing for missing records
17
+ - ✅ **Initial Data** - Pre-populate on initialization
18
+ - ✅ **TypeScript** - Full type safety and IntelliSense support
19
+
20
+ ## Use Cases
21
+
22
+ This driver is perfect for:
23
+
24
+ 1. **Unit Testing** - No database setup required, instant cleanup
25
+ 2. **Development & Prototyping** - Quick iteration without infrastructure
26
+ 3. **Edge Environments** - Cloudflare Workers, Deno Deploy, Vercel Edge
27
+ 4. **Client-Side State** - Browser-based applications
28
+ 5. **Temporary Caching** - Short-lived data storage
29
+ 6. **CI/CD Pipelines** - Fast tests without database dependencies
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ # Using pnpm (recommended)
35
+ pnpm add @objectql/driver-memory
36
+
37
+ # Using npm
38
+ npm install @objectql/driver-memory
39
+
40
+ # Using yarn
41
+ yarn add @objectql/driver-memory
42
+ ```
43
+
44
+ Or if you're using the ObjectQL monorepo:
45
+
46
+ ```bash
47
+ pnpm add @objectql/driver-memory
48
+ ```
49
+
50
+ ## Basic Usage
51
+
52
+ ```typescript
53
+ import { ObjectQL } from '@objectql/core';
54
+ import { MemoryDriver } from '@objectql/driver-memory';
55
+
56
+ // Initialize the driver
57
+ const driver = new MemoryDriver();
58
+
59
+ // Create ObjectQL instance
60
+ const app = new ObjectQL({
61
+ datasources: { default: driver }
62
+ });
63
+
64
+ // Register your schema
65
+ app.registerObject({
66
+ name: 'users',
67
+ fields: {
68
+ name: { type: 'text', required: true },
69
+ email: { type: 'email', unique: true },
70
+ role: { type: 'select', options: ['admin', 'user'] }
71
+ }
72
+ });
73
+
74
+ await app.init();
75
+
76
+ // Use it!
77
+ const ctx = app.createContext({ isSystem: true });
78
+ const repo = ctx.object('users');
79
+
80
+ // Create
81
+ const user = await repo.create({
82
+ name: 'Alice',
83
+ email: 'alice@example.com',
84
+ role: 'admin'
85
+ });
86
+
87
+ // Find
88
+ const users = await repo.find({
89
+ filters: [['role', '=', 'user']]
90
+ });
91
+
92
+ // Update
93
+ await repo.update(user.id, { email: 'alice.new@example.com' });
94
+
95
+ // Delete
96
+ await repo.delete(user.id);
97
+ ```
98
+
99
+ ## Configuration Options
100
+
101
+ ### Basic Configuration
102
+
103
+ ```typescript
104
+ const driver = new MemoryDriver();
105
+ ```
106
+
107
+ ### With Initial Data
108
+
109
+ ```typescript
110
+ const driver = new MemoryDriver({
111
+ initialData: {
112
+ users: [
113
+ { id: '1', name: 'Alice', email: 'alice@example.com' },
114
+ { id: '2', name: 'Bob', email: 'bob@example.com' }
115
+ ],
116
+ posts: [
117
+ { id: '1', title: 'Hello World', author_id: '1' }
118
+ ]
119
+ }
120
+ });
121
+ ```
122
+
123
+ ### With Strict Mode
124
+
125
+ ```typescript
126
+ const driver = new MemoryDriver({
127
+ strictMode: true // Throws errors for missing records
128
+ });
129
+
130
+ // This will throw an error instead of returning null
131
+ await driver.update('users', 'non-existent-id', { name: 'Test' });
132
+ // ObjectQLError: Record with id 'non-existent-id' not found in 'users'
133
+ ```
134
+
135
+ ## API Reference
136
+
137
+ ### Core Methods
138
+
139
+ All methods implement the standard Driver interface from `@objectql/types`:
140
+
141
+ #### `find(objectName, query, options)`
142
+
143
+ Find multiple records with optional filtering, sorting, and pagination.
144
+
145
+ ```typescript
146
+ const users = await driver.find('users', {
147
+ filters: [
148
+ ['role', '=', 'admin'],
149
+ 'or',
150
+ ['age', '>', 30]
151
+ ],
152
+ sort: [['name', 'asc']],
153
+ skip: 0,
154
+ limit: 10,
155
+ fields: ['name', 'email']
156
+ });
157
+ ```
158
+
159
+ #### `findOne(objectName, id, query, options)`
160
+
161
+ Find a single record by ID or query.
162
+
163
+ ```typescript
164
+ // By ID
165
+ const user = await driver.findOne('users', 'user-123');
166
+
167
+ // By query
168
+ const admin = await driver.findOne('users', null, {
169
+ filters: [['role', '=', 'admin']]
170
+ });
171
+ ```
172
+
173
+ #### `create(objectName, data, options)`
174
+
175
+ Create a new record.
176
+
177
+ ```typescript
178
+ const user = await driver.create('users', {
179
+ name: 'Alice',
180
+ email: 'alice@example.com'
181
+ });
182
+ // Returns: { id: 'users-1234567890-1', name: 'Alice', ... }
183
+ ```
184
+
185
+ #### `update(objectName, id, data, options)`
186
+
187
+ Update an existing record.
188
+
189
+ ```typescript
190
+ const updated = await driver.update('users', 'user-123', {
191
+ email: 'alice.new@example.com'
192
+ });
193
+ ```
194
+
195
+ #### `delete(objectName, id, options)`
196
+
197
+ Delete a record.
198
+
199
+ ```typescript
200
+ const deleted = await driver.delete('users', 'user-123');
201
+ // Returns: true if deleted, false if not found
202
+ ```
203
+
204
+ #### `count(objectName, filters, options)`
205
+
206
+ Count records matching filters.
207
+
208
+ ```typescript
209
+ const adminCount = await driver.count('users', [
210
+ ['role', '=', 'admin']
211
+ ]);
212
+ ```
213
+
214
+ ### Bulk Operations
215
+
216
+ #### `createMany(objectName, data, options)`
217
+
218
+ Create multiple records at once.
219
+
220
+ ```typescript
221
+ const users = await driver.createMany('users', [
222
+ { name: 'Alice' },
223
+ { name: 'Bob' },
224
+ { name: 'Charlie' }
225
+ ]);
226
+ ```
227
+
228
+ #### `updateMany(objectName, filters, data, options)`
229
+
230
+ Update all records matching filters.
231
+
232
+ ```typescript
233
+ const result = await driver.updateMany(
234
+ 'users',
235
+ [['role', '=', 'user']],
236
+ { status: 'active' }
237
+ );
238
+ // Returns: { modifiedCount: 5 }
239
+ ```
240
+
241
+ #### `deleteMany(objectName, filters, options)`
242
+
243
+ Delete all records matching filters.
244
+
245
+ ```typescript
246
+ const result = await driver.deleteMany('users', [
247
+ ['status', '=', 'inactive']
248
+ ]);
249
+ // Returns: { deletedCount: 3 }
250
+ ```
251
+
252
+ ### Advanced Operations
253
+
254
+ #### `distinct(objectName, field, filters, options)`
255
+
256
+ Get unique values for a field.
257
+
258
+ ```typescript
259
+ const roles = await driver.distinct('users', 'role');
260
+ // Returns: ['admin', 'user', 'moderator']
261
+ ```
262
+
263
+ ### Utility Methods
264
+
265
+ #### `clear()`
266
+
267
+ Remove all data from the store.
268
+
269
+ ```typescript
270
+ await driver.clear();
271
+ ```
272
+
273
+ #### `getSize()`
274
+
275
+ Get the total number of records in the store.
276
+
277
+ ```typescript
278
+ const size = driver.getSize();
279
+ // Returns: 42
280
+ ```
281
+
282
+ #### `disconnect()`
283
+
284
+ Gracefully disconnect (no-op for memory driver).
285
+
286
+ ```typescript
287
+ await driver.disconnect();
288
+ ```
289
+
290
+ ## Supported Query Operators
291
+
292
+ The Memory Driver supports all standard ObjectQL query operators:
293
+
294
+ ### Comparison Operators
295
+
296
+ - `=`, `==` - Equals
297
+ - `!=`, `<>` - Not equals
298
+ - `>` - Greater than
299
+ - `>=` - Greater than or equal
300
+ - `<` - Less than
301
+ - `<=` - Less than or equal
302
+
303
+ ### Set Operators
304
+
305
+ - `in` - Value in array
306
+ - `nin`, `not in` - Value not in array
307
+
308
+ ### String Operators
309
+
310
+ - `contains`, `like` - Contains substring (case-insensitive)
311
+ - `startswith`, `starts_with` - Starts with string
312
+ - `endswith`, `ends_with` - Ends with string
313
+
314
+ ### Range Operators
315
+
316
+ - `between` - Value between two values (inclusive)
317
+
318
+ ### Logical Operators
319
+
320
+ - `and` - Logical AND (default)
321
+ - `or` - Logical OR
322
+
323
+ ## Query Examples
324
+
325
+ ### Simple Filter
326
+
327
+ ```typescript
328
+ const admins = await driver.find('users', {
329
+ filters: [['role', '=', 'admin']]
330
+ });
331
+ ```
332
+
333
+ ### Multiple Filters (AND)
334
+
335
+ ```typescript
336
+ const activeAdmins = await driver.find('users', {
337
+ filters: [
338
+ ['role', '=', 'admin'],
339
+ 'and',
340
+ ['status', '=', 'active']
341
+ ]
342
+ });
343
+ ```
344
+
345
+ ### OR Filters
346
+
347
+ ```typescript
348
+ const results = await driver.find('users', {
349
+ filters: [
350
+ ['role', '=', 'admin'],
351
+ 'or',
352
+ ['permissions', 'contains', 'manage_users']
353
+ ]
354
+ });
355
+ ```
356
+
357
+ ### Range Queries
358
+
359
+ ```typescript
360
+ const middleAged = await driver.find('users', {
361
+ filters: [['age', 'between', [30, 50]]]
362
+ });
363
+ ```
364
+
365
+ ### Sorting
366
+
367
+ ```typescript
368
+ const sorted = await driver.find('users', {
369
+ sort: [
370
+ ['role', 'asc'],
371
+ ['created_at', 'desc']
372
+ ]
373
+ });
374
+ ```
375
+
376
+ ### Pagination
377
+
378
+ ```typescript
379
+ // Get page 2 with 10 items per page
380
+ const page2 = await driver.find('users', {
381
+ skip: 10,
382
+ limit: 10,
383
+ sort: [['created_at', 'desc']]
384
+ });
385
+ ```
386
+
387
+ ### Field Projection
388
+
389
+ ```typescript
390
+ const names = await driver.find('users', {
391
+ fields: ['id', 'name', 'email']
392
+ });
393
+ // Returns only id, name, and email fields
394
+ ```
395
+
396
+ ## Testing with Memory Driver
397
+
398
+ The Memory Driver is ideal for unit tests:
399
+
400
+ ```typescript
401
+ import { MemoryDriver } from '@objectql/driver-memory';
402
+
403
+ describe('User Service', () => {
404
+ let driver: MemoryDriver;
405
+
406
+ beforeEach(() => {
407
+ driver = new MemoryDriver({
408
+ initialData: {
409
+ users: [
410
+ { id: '1', name: 'Test User', role: 'user' }
411
+ ]
412
+ }
413
+ });
414
+ });
415
+
416
+ afterEach(async () => {
417
+ await driver.clear();
418
+ });
419
+
420
+ it('should find users by role', async () => {
421
+ const users = await driver.find('users', {
422
+ filters: [['role', '=', 'user']]
423
+ });
424
+ expect(users).toHaveLength(1);
425
+ });
426
+ });
427
+ ```
428
+
429
+ ## Performance Characteristics
430
+
431
+ - **Create**: O(1)
432
+ - **Read by ID**: O(1)
433
+ - **Update**: O(1)
434
+ - **Delete**: O(1)
435
+ - **Find/Query**: O(n) - Scans all records for the object type
436
+ - **Count**: O(n) - Scans all matching records
437
+ - **Sort**: O(n log n)
438
+
439
+ ### Performance Tips
440
+
441
+ 1. **Use specific filters** - More filters reduce the result set faster
442
+ 2. **Limit results** - Use `limit` to avoid processing large result sets
443
+ 3. **Clear regularly** - Call `clear()` to free memory in long-running processes
444
+ 4. **Consider size** - Memory driver is best for < 10,000 records per object type
445
+
446
+ ## Comparison with Other Drivers
447
+
448
+ | Feature | Memory | SQL | MongoDB | Redis |
449
+ |---------|--------|-----|---------|-------|
450
+ | **Setup Required** | ❌ None | ✅ Database | ✅ Database | ✅ Redis Server |
451
+ | **Persistence** | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
452
+ | **Performance** | ⚡ Fastest | 🐢 Slower | 🏃 Fast | 🏃 Fast |
453
+ | **Query Support** | ✅ Full | ✅ Full | ✅ Full | ⚠️ Limited |
454
+ | **Production Ready** | ✅ Yes* | ✅ Yes | ✅ Yes | ⚠️ Example |
455
+ | **Dependencies** | 0 | 2-3 | 1 | 1 |
456
+
457
+ *For use cases where persistence is not required
458
+
459
+ ## Limitations
460
+
461
+ 1. **No Persistence** - Data is lost when the process ends
462
+ 2. **Memory Bound** - Limited by available RAM
463
+ 3. **Single Instance** - No distribution or clustering
464
+ 4. **No Transactions** - Operations are individual (though atomic)
465
+ 5. **Linear Scans** - Queries scan all records (no indexes)
466
+
467
+ ## Migration Guide
468
+
469
+ ### From Redis Driver to Memory Driver
470
+
471
+ ```typescript
472
+ // Before
473
+ import { RedisDriver } from '@objectql/driver-redis';
474
+ const driver = new RedisDriver({ url: 'redis://localhost:6379' });
475
+
476
+ // After
477
+ import { MemoryDriver } from '@objectql/driver-memory';
478
+ const driver = new MemoryDriver();
479
+ ```
480
+
481
+ ### From SQL Driver to Memory Driver (for testing)
482
+
483
+ ```typescript
484
+ // Production
485
+ const driver = new SqlDriver({
486
+ client: 'pg',
487
+ connection: process.env.DATABASE_URL
488
+ });
489
+
490
+ // Testing
491
+ const driver = new MemoryDriver({
492
+ initialData: {
493
+ users: [/* test data */],
494
+ posts: [/* test data */]
495
+ }
496
+ });
497
+ ```
498
+
499
+ ## Troubleshooting
500
+
501
+ ### Out of Memory Errors
502
+
503
+ ```typescript
504
+ // Problem: Too much data
505
+ const driver = new MemoryDriver();
506
+ // ... add millions of records
507
+
508
+ // Solution: Clear periodically or use a persistent driver
509
+ await driver.clear();
510
+ ```
511
+
512
+ ### Slow Queries
513
+
514
+ ```typescript
515
+ // Problem: Scanning large datasets
516
+ const results = await driver.find('users', {}); // Returns 100,000 records
517
+
518
+ // Solution: Add filters and limits
519
+ const results = await driver.find('users', {
520
+ filters: [['status', '=', 'active']],
521
+ limit: 100
522
+ });
523
+ ```
524
+
525
+ ## Related Documentation
526
+
527
+ - [Driver Extensibility Guide](../../../docs/guide/drivers/extensibility.md)
528
+ - [Implementing Custom Drivers](../../../docs/guide/drivers/implementing-custom-driver.md)
529
+ - [Driver Interface Reference](../../foundation/types/src/driver.ts)
530
+ - [ObjectQL Core Documentation](../../foundation/core/README.md)
531
+
532
+ ## Contributing
533
+
534
+ Found a bug or have a feature request? Please open an issue on [GitHub](https://github.com/objectstack-ai/objectql/issues).
535
+
536
+ ## License
537
+
538
+ MIT - Same as ObjectQL
539
+
540
+ ## Changelog
541
+
542
+ See [CHANGELOG.md](./CHANGELOG.md) for version history.