@objectstack/driver-memory 0.6.1 → 0.7.2
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 +18 -0
- package/README.md +227 -0
- package/dist/memory-driver.d.ts +2 -1
- package/dist/memory-driver.d.ts.map +1 -1
- package/dist/memory-driver.js +56 -9
- package/objectstack.config.ts +234 -3
- package/package.json +3 -3
- package/src/memory-driver.ts +67 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @objectstack/driver-memory
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fb41cc0: Patch release: Updated documentation and JSON schemas
|
|
8
|
+
- Updated dependencies [fb41cc0]
|
|
9
|
+
- @objectstack/spec@0.7.2
|
|
10
|
+
- @objectstack/core@0.7.2
|
|
11
|
+
|
|
12
|
+
## 0.7.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Patch release for maintenance and stability improvements
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @objectstack/spec@0.7.1
|
|
19
|
+
- @objectstack/core@0.7.1
|
|
20
|
+
|
|
3
21
|
## 0.6.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# @objectstack/driver-memory
|
|
2
|
+
|
|
3
|
+
In-Memory Driver for ObjectStack. A reference implementation of the DriverInterface that stores data in memory using JavaScript arrays.
|
|
4
|
+
|
|
5
|
+
## Plugin Capabilities
|
|
6
|
+
|
|
7
|
+
This driver implements the ObjectStack plugin capability protocol:
|
|
8
|
+
- **Type**: `driver`
|
|
9
|
+
- **Protocol**: `com.objectstack.protocol.storage.v1` (partial conformance)
|
|
10
|
+
- **Provides**: `DriverInterface` for data storage operations
|
|
11
|
+
- **Features**:
|
|
12
|
+
- ✅ Basic CRUD operations
|
|
13
|
+
- ✅ Pagination (limit/offset)
|
|
14
|
+
- ❌ Advanced query filters
|
|
15
|
+
- ❌ Aggregations
|
|
16
|
+
- ❌ Sorting
|
|
17
|
+
- ❌ Transactions
|
|
18
|
+
- ❌ Joins
|
|
19
|
+
|
|
20
|
+
See [objectstack.config.ts](./objectstack.config.ts) for the complete capability manifest.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- 🚀 **Zero Dependencies**: Pure JavaScript implementation
|
|
25
|
+
- 🧪 **Perfect for Testing**: Volatile storage ideal for unit tests
|
|
26
|
+
- 📝 **TypeScript First**: Fully typed with TypeScript
|
|
27
|
+
- 🔍 **Reference Implementation**: Clean example of DriverInterface
|
|
28
|
+
- ⚡ **Fast**: In-memory operations are lightning fast
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm add @objectstack/driver-memory
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### With ObjectStack Runtime
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { InMemoryDriver } from '@objectstack/driver-memory';
|
|
42
|
+
import { DriverPlugin } from '@objectstack/runtime';
|
|
43
|
+
import { ObjectKernel } from '@objectstack/runtime';
|
|
44
|
+
|
|
45
|
+
const kernel = new ObjectKernel();
|
|
46
|
+
|
|
47
|
+
// Create and register the driver
|
|
48
|
+
const memoryDriver = new InMemoryDriver();
|
|
49
|
+
kernel.use(new DriverPlugin(memoryDriver, 'memory'));
|
|
50
|
+
|
|
51
|
+
await kernel.bootstrap();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Standalone Usage
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { InMemoryDriver } from '@objectstack/driver-memory';
|
|
58
|
+
|
|
59
|
+
const driver = new InMemoryDriver({
|
|
60
|
+
seedData: true // Pre-populate with example data
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Initialize
|
|
64
|
+
await driver.connect();
|
|
65
|
+
|
|
66
|
+
// Create a record
|
|
67
|
+
const user = await driver.create('user', {
|
|
68
|
+
name: 'John Doe',
|
|
69
|
+
email: 'john@example.com'
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Find records
|
|
73
|
+
const users = await driver.find('user', {
|
|
74
|
+
limit: 10,
|
|
75
|
+
offset: 0
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Get by ID
|
|
79
|
+
const foundUser = await driver.findOne('user', user.id);
|
|
80
|
+
|
|
81
|
+
// Update
|
|
82
|
+
await driver.update('user', user.id, {
|
|
83
|
+
name: 'Jane Doe'
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Delete
|
|
87
|
+
await driver.delete('user', user.id);
|
|
88
|
+
|
|
89
|
+
// Count
|
|
90
|
+
const count = await driver.count('user');
|
|
91
|
+
|
|
92
|
+
// Cleanup
|
|
93
|
+
await driver.disconnect();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API Reference
|
|
97
|
+
|
|
98
|
+
### InMemoryDriver
|
|
99
|
+
|
|
100
|
+
The main driver class that implements `DriverInterface`.
|
|
101
|
+
|
|
102
|
+
#### Constructor Options
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
interface DriverOptions {
|
|
106
|
+
/**
|
|
107
|
+
* Pre-populate the database with example data on startup
|
|
108
|
+
* @default false
|
|
109
|
+
*/
|
|
110
|
+
seedData?: boolean;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Logger instance
|
|
114
|
+
*/
|
|
115
|
+
logger?: Logger;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Methods
|
|
120
|
+
|
|
121
|
+
- `connect()` - Initialize the driver (no-op for in-memory)
|
|
122
|
+
- `disconnect()` - Cleanup resources (clears all data)
|
|
123
|
+
- `create(object, data)` - Create a new record
|
|
124
|
+
- `find(object, query?)` - Query records with optional pagination
|
|
125
|
+
- `findOne(object, id)` - Get a single record by ID
|
|
126
|
+
- `update(object, id, data)` - Update a record
|
|
127
|
+
- `delete(object, id)` - Delete a record
|
|
128
|
+
- `count(object, query?)` - Count total records
|
|
129
|
+
- `getSchema(object)` - Get object schema definition
|
|
130
|
+
- `query(query)` - Execute a raw query (limited support)
|
|
131
|
+
|
|
132
|
+
#### Capabilities
|
|
133
|
+
|
|
134
|
+
The driver declares its capabilities via the `supports` property:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
{
|
|
138
|
+
transactions: false,
|
|
139
|
+
queryFilters: false,
|
|
140
|
+
queryAggregations: false,
|
|
141
|
+
querySorting: false,
|
|
142
|
+
queryPagination: true, // ✅ Supported via limit/offset
|
|
143
|
+
queryWindowFunctions: false,
|
|
144
|
+
querySubqueries: false,
|
|
145
|
+
joins: false,
|
|
146
|
+
fullTextSearch: false,
|
|
147
|
+
vectorSearch: false,
|
|
148
|
+
geoSpatial: false
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Data Storage
|
|
153
|
+
|
|
154
|
+
The in-memory driver stores data in a simple Map structure:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
private tables: Map<string, Array<Record<string, any>>> = new Map();
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Important**: All data is lost when the process exits or `disconnect()` is called. This driver is **not suitable for production use**.
|
|
161
|
+
|
|
162
|
+
## Use Cases
|
|
163
|
+
|
|
164
|
+
✅ **Good for:**
|
|
165
|
+
- Unit testing
|
|
166
|
+
- Integration testing
|
|
167
|
+
- Development/prototyping
|
|
168
|
+
- CI/CD pipelines
|
|
169
|
+
- Examples and tutorials
|
|
170
|
+
- Learning ObjectStack
|
|
171
|
+
|
|
172
|
+
❌ **Not suitable for:**
|
|
173
|
+
- Production environments
|
|
174
|
+
- Data persistence requirements
|
|
175
|
+
- Large datasets (memory constraints)
|
|
176
|
+
- Multi-process scenarios
|
|
177
|
+
- Concurrent write operations
|
|
178
|
+
|
|
179
|
+
## Testing Example
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
183
|
+
import { InMemoryDriver } from '@objectstack/driver-memory';
|
|
184
|
+
|
|
185
|
+
describe('User CRUD', () => {
|
|
186
|
+
let driver: InMemoryDriver;
|
|
187
|
+
|
|
188
|
+
beforeEach(async () => {
|
|
189
|
+
driver = new InMemoryDriver();
|
|
190
|
+
await driver.connect();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
afterEach(async () => {
|
|
194
|
+
await driver.disconnect();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should create and retrieve a user', async () => {
|
|
198
|
+
const user = await driver.create('user', {
|
|
199
|
+
name: 'Test User',
|
|
200
|
+
email: 'test@example.com'
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
expect(user.id).toBeDefined();
|
|
204
|
+
|
|
205
|
+
const found = await driver.findOne('user', user.id);
|
|
206
|
+
expect(found.name).toBe('Test User');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Relationship to Other Drivers
|
|
212
|
+
|
|
213
|
+
This driver serves as a reference implementation. For production use, consider:
|
|
214
|
+
|
|
215
|
+
- **@objectstack/driver-postgres** - PostgreSQL driver with full SQL capabilities
|
|
216
|
+
- **@objectstack/driver-mongodb** - MongoDB driver for document storage
|
|
217
|
+
- **@objectstack/driver-redis** - Redis driver for caching and key-value storage
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
Apache-2.0
|
|
222
|
+
|
|
223
|
+
## Related Packages
|
|
224
|
+
|
|
225
|
+
- [@objectstack/runtime](../../runtime) - ObjectStack Runtime
|
|
226
|
+
- [@objectstack/spec](../../spec) - ObjectStack Specifications
|
|
227
|
+
- [@objectstack/core](../../core) - Core Interfaces and Types
|
package/dist/memory-driver.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QueryInput } from '@objectstack/spec/data';
|
|
2
|
-
import { DriverOptions } from '@objectstack/spec/
|
|
2
|
+
import { DriverOptions } from '@objectstack/spec/data';
|
|
3
3
|
import { DriverInterface } from '@objectstack/core';
|
|
4
4
|
/**
|
|
5
5
|
* Example: In-Memory Driver
|
|
@@ -11,6 +11,7 @@ export declare class InMemoryDriver implements DriverInterface {
|
|
|
11
11
|
name: string;
|
|
12
12
|
version: string;
|
|
13
13
|
private config;
|
|
14
|
+
private logger;
|
|
14
15
|
constructor(config?: any);
|
|
15
16
|
install(ctx: any): void;
|
|
16
17
|
supports: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-driver.d.ts","sourceRoot":"","sources":["../src/memory-driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"memory-driver.d.ts","sourceRoot":"","sources":["../src/memory-driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAwB,MAAM,mBAAmB,CAAC;AAE1E;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,IAAI,SAAsB;IAC1B,OAAO,SAAW;IAClB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,CAAC,EAAE,GAAG;IAOxB,OAAO,CAAC,GAAG,EAAE,GAAG;IAUhB,QAAQ;;;;;;;;;;;;;;MAmBN;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE,CAA6B;IAMjC,OAAO;IAIP,UAAU;IAWV,WAAW;IAYX,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE;IASpC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa;IAiB9D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa;IAStE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa;IAUlE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;;;;;IAkBzE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;IAsB9F,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa;IAqBlG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAgBnE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa;IAUjE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa;;;;;IAOpF,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa;IAOjH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa;IAU5E,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,aAAa;IAO/D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAQjD,gBAAgB;IAIhB,MAAM;IACN,QAAQ;IAMd,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,UAAU;CAGnB"}
|
package/dist/memory-driver.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createLogger } from '@objectstack/core';
|
|
1
2
|
/**
|
|
2
3
|
* Example: In-Memory Driver
|
|
3
4
|
*
|
|
@@ -31,37 +32,54 @@ export class InMemoryDriver {
|
|
|
31
32
|
*/
|
|
32
33
|
this.db = {};
|
|
33
34
|
this.config = config || {};
|
|
35
|
+
this.logger = config?.logger || createLogger({ level: 'info', format: 'pretty' });
|
|
36
|
+
this.logger.debug('InMemory driver instance created', { config: this.config });
|
|
34
37
|
}
|
|
35
38
|
// Duck-typed RuntimePlugin hook
|
|
36
39
|
install(ctx) {
|
|
40
|
+
this.logger.debug('Installing InMemory driver via plugin hook');
|
|
37
41
|
if (ctx.engine && ctx.engine.ql && typeof ctx.engine.ql.registerDriver === 'function') {
|
|
38
42
|
ctx.engine.ql.registerDriver(this);
|
|
43
|
+
this.logger.info('InMemory driver registered with ObjectQL engine');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this.logger.warn('Could not register driver - ObjectQL engine not found in context');
|
|
39
47
|
}
|
|
40
48
|
}
|
|
41
49
|
// ===================================
|
|
42
50
|
// Lifecycle
|
|
43
51
|
// ===================================
|
|
44
52
|
async connect() {
|
|
45
|
-
|
|
53
|
+
this.logger.info('InMemory Database Connected (Virtual)');
|
|
46
54
|
}
|
|
47
55
|
async disconnect() {
|
|
56
|
+
const tableCount = Object.keys(this.db).length;
|
|
57
|
+
const recordCount = Object.values(this.db).reduce((sum, table) => sum + table.length, 0);
|
|
48
58
|
this.db = {};
|
|
49
|
-
|
|
59
|
+
this.logger.info('InMemory Database Disconnected & Cleared', {
|
|
60
|
+
tableCount,
|
|
61
|
+
recordCount
|
|
62
|
+
});
|
|
50
63
|
}
|
|
51
64
|
async checkHealth() {
|
|
65
|
+
this.logger.debug('Health check performed', {
|
|
66
|
+
tableCount: Object.keys(this.db).length,
|
|
67
|
+
status: 'healthy'
|
|
68
|
+
});
|
|
52
69
|
return true;
|
|
53
70
|
}
|
|
54
71
|
// ===================================
|
|
55
72
|
// Execution
|
|
56
73
|
// ===================================
|
|
57
74
|
async execute(command, params) {
|
|
58
|
-
|
|
75
|
+
this.logger.warn('Raw execution not supported in InMemory driver', { command });
|
|
59
76
|
return null;
|
|
60
77
|
}
|
|
61
78
|
// ===================================
|
|
62
79
|
// CRUD
|
|
63
80
|
// ===================================
|
|
64
81
|
async find(object, query, options) {
|
|
82
|
+
this.logger.debug('Find operation', { object, query });
|
|
65
83
|
const table = this.getTable(object);
|
|
66
84
|
// 💡 Naive Implementation
|
|
67
85
|
let results = [...table];
|
|
@@ -69,19 +87,25 @@ export class InMemoryDriver {
|
|
|
69
87
|
if (query.limit) {
|
|
70
88
|
results = results.slice(0, query.limit);
|
|
71
89
|
}
|
|
90
|
+
this.logger.debug('Find completed', { object, resultCount: results.length });
|
|
72
91
|
return results;
|
|
73
92
|
}
|
|
74
93
|
async *findStream(object, query, options) {
|
|
94
|
+
this.logger.debug('FindStream operation', { object });
|
|
75
95
|
const results = await this.find(object, query, options);
|
|
76
96
|
for (const record of results) {
|
|
77
97
|
yield record;
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
100
|
async findOne(object, query, options) {
|
|
101
|
+
this.logger.debug('FindOne operation', { object, query });
|
|
81
102
|
const results = await this.find(object, { ...query, limit: 1 }, options);
|
|
82
|
-
|
|
103
|
+
const result = results[0] || null;
|
|
104
|
+
this.logger.debug('FindOne completed', { object, found: !!result });
|
|
105
|
+
return result;
|
|
83
106
|
}
|
|
84
107
|
async create(object, data, options) {
|
|
108
|
+
this.logger.debug('Create operation', { object, hasData: !!data });
|
|
85
109
|
const table = this.getTable(object);
|
|
86
110
|
// COMPATIBILITY: Driver must return 'id' as string
|
|
87
111
|
const newRecord = {
|
|
@@ -91,12 +115,15 @@ export class InMemoryDriver {
|
|
|
91
115
|
updated_at: data.updated_at || new Date(),
|
|
92
116
|
};
|
|
93
117
|
table.push(newRecord);
|
|
118
|
+
this.logger.debug('Record created', { object, id: newRecord.id, tableSize: table.length });
|
|
94
119
|
return newRecord;
|
|
95
120
|
}
|
|
96
121
|
async update(object, id, data, options) {
|
|
122
|
+
this.logger.debug('Update operation', { object, id });
|
|
97
123
|
const table = this.getTable(object);
|
|
98
124
|
const index = table.findIndex(r => r.id == id);
|
|
99
125
|
if (index === -1) {
|
|
126
|
+
this.logger.warn('Record not found for update', { object, id });
|
|
100
127
|
throw new Error(`Record with ID ${id} not found in ${object}`);
|
|
101
128
|
}
|
|
102
129
|
const updatedRecord = {
|
|
@@ -105,9 +132,11 @@ export class InMemoryDriver {
|
|
|
105
132
|
updated_at: new Date()
|
|
106
133
|
};
|
|
107
134
|
table[index] = updatedRecord;
|
|
135
|
+
this.logger.debug('Record updated', { object, id });
|
|
108
136
|
return updatedRecord;
|
|
109
137
|
}
|
|
110
138
|
async upsert(object, data, conflictKeys, options) {
|
|
139
|
+
this.logger.debug('Upsert operation', { object, conflictKeys });
|
|
111
140
|
const table = this.getTable(object);
|
|
112
141
|
let existingRecord = null;
|
|
113
142
|
if (data.id) {
|
|
@@ -117,34 +146,50 @@ export class InMemoryDriver {
|
|
|
117
146
|
existingRecord = table.find(r => conflictKeys.every(key => r[key] === data[key]));
|
|
118
147
|
}
|
|
119
148
|
if (existingRecord) {
|
|
149
|
+
this.logger.debug('Record exists, updating', { object, id: existingRecord.id });
|
|
120
150
|
return this.update(object, existingRecord.id, data, options);
|
|
121
151
|
}
|
|
122
152
|
else {
|
|
153
|
+
this.logger.debug('Record does not exist, creating', { object });
|
|
123
154
|
return this.create(object, data, options);
|
|
124
155
|
}
|
|
125
156
|
}
|
|
126
157
|
async delete(object, id, options) {
|
|
158
|
+
this.logger.debug('Delete operation', { object, id });
|
|
127
159
|
const table = this.getTable(object);
|
|
128
160
|
const index = table.findIndex(r => r.id == id);
|
|
129
|
-
if (index === -1)
|
|
161
|
+
if (index === -1) {
|
|
162
|
+
this.logger.warn('Record not found for deletion', { object, id });
|
|
130
163
|
return false;
|
|
164
|
+
}
|
|
131
165
|
table.splice(index, 1);
|
|
166
|
+
this.logger.debug('Record deleted', { object, id, tableSize: table.length });
|
|
132
167
|
return true;
|
|
133
168
|
}
|
|
134
169
|
async count(object, query, options) {
|
|
135
|
-
|
|
170
|
+
const count = this.getTable(object).length;
|
|
171
|
+
this.logger.debug('Count operation', { object, count });
|
|
172
|
+
return count;
|
|
136
173
|
}
|
|
137
174
|
// ===================================
|
|
138
175
|
// Bulk Operations
|
|
139
176
|
// ===================================
|
|
140
177
|
async bulkCreate(object, dataArray, options) {
|
|
141
|
-
|
|
178
|
+
this.logger.debug('BulkCreate operation', { object, count: dataArray.length });
|
|
179
|
+
const results = await Promise.all(dataArray.map(data => this.create(object, data, options)));
|
|
180
|
+
this.logger.debug('BulkCreate completed', { object, count: results.length });
|
|
181
|
+
return results;
|
|
142
182
|
}
|
|
143
183
|
async bulkUpdate(object, updates, options) {
|
|
144
|
-
|
|
184
|
+
this.logger.debug('BulkUpdate operation', { object, count: updates.length });
|
|
185
|
+
const results = await Promise.all(updates.map(u => this.update(object, u.id, u.data, options)));
|
|
186
|
+
this.logger.debug('BulkUpdate completed', { object, count: results.length });
|
|
187
|
+
return results;
|
|
145
188
|
}
|
|
146
189
|
async bulkDelete(object, ids, options) {
|
|
190
|
+
this.logger.debug('BulkDelete operation', { object, count: ids.length });
|
|
147
191
|
await Promise.all(ids.map(id => this.delete(object, id, options)));
|
|
192
|
+
this.logger.debug('BulkDelete completed', { object, count: ids.length });
|
|
148
193
|
}
|
|
149
194
|
// ===================================
|
|
150
195
|
// Schema & Transactions
|
|
@@ -152,12 +197,14 @@ export class InMemoryDriver {
|
|
|
152
197
|
async syncSchema(object, schema, options) {
|
|
153
198
|
if (!this.db[object]) {
|
|
154
199
|
this.db[object] = [];
|
|
155
|
-
|
|
200
|
+
this.logger.info('Created in-memory table', { object });
|
|
156
201
|
}
|
|
157
202
|
}
|
|
158
203
|
async dropTable(object, options) {
|
|
159
204
|
if (this.db[object]) {
|
|
205
|
+
const recordCount = this.db[object].length;
|
|
160
206
|
delete this.db[object];
|
|
207
|
+
this.logger.info('Dropped in-memory table', { object, recordCount });
|
|
161
208
|
}
|
|
162
209
|
}
|
|
163
210
|
async beginTransaction() {
|
package/objectstack.config.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { ObjectStackManifest } from '@objectstack/spec/system';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* In-Memory Driver Plugin Manifest
|
|
5
|
+
*
|
|
6
|
+
* Reference implementation of a storage driver that stores data in memory.
|
|
7
|
+
* Demonstrates the driver protocol implementation pattern.
|
|
8
|
+
*/
|
|
3
9
|
const MemoryDriverPlugin: ObjectStackManifest = {
|
|
4
10
|
id: 'com.objectstack.driver.memory',
|
|
5
11
|
name: 'In-Memory Driver',
|
|
6
12
|
version: '1.0.0',
|
|
7
13
|
type: 'driver',
|
|
8
|
-
description: 'A reference
|
|
14
|
+
description: 'A reference specification implementation of the DriverInterface using in-memory arrays. Suitable for testing and development.',
|
|
9
15
|
|
|
10
16
|
configuration: {
|
|
11
17
|
title: 'Memory Driver Settings',
|
|
@@ -18,9 +24,234 @@ const MemoryDriverPlugin: ObjectStackManifest = {
|
|
|
18
24
|
}
|
|
19
25
|
},
|
|
20
26
|
|
|
27
|
+
// Plugin Capability Declaration
|
|
28
|
+
capabilities: {
|
|
29
|
+
// Protocols This Driver Implements
|
|
30
|
+
implements: [
|
|
31
|
+
{
|
|
32
|
+
protocol: {
|
|
33
|
+
id: 'com.objectstack.protocol.storage.v1',
|
|
34
|
+
label: 'Storage Protocol v1',
|
|
35
|
+
version: { major: 1, minor: 0, patch: 0 },
|
|
36
|
+
description: 'Standard data storage and retrieval operations',
|
|
37
|
+
},
|
|
38
|
+
conformance: 'partial',
|
|
39
|
+
implementedFeatures: [
|
|
40
|
+
'basic_crud',
|
|
41
|
+
'pagination',
|
|
42
|
+
],
|
|
43
|
+
features: [
|
|
44
|
+
{
|
|
45
|
+
name: 'basic_crud',
|
|
46
|
+
enabled: true,
|
|
47
|
+
description: 'Create, read, update, delete operations',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'pagination',
|
|
51
|
+
enabled: true,
|
|
52
|
+
description: 'Basic pagination via limit/offset',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'query_filters',
|
|
56
|
+
enabled: false,
|
|
57
|
+
description: 'Advanced query filtering',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'aggregations',
|
|
61
|
+
enabled: false,
|
|
62
|
+
description: 'Count, sum, avg operations',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'sorting',
|
|
66
|
+
enabled: false,
|
|
67
|
+
description: 'Result set sorting',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'transactions',
|
|
71
|
+
enabled: false,
|
|
72
|
+
description: 'ACID transaction support',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'joins',
|
|
76
|
+
enabled: false,
|
|
77
|
+
description: 'Cross-object joins',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
certified: false,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
|
|
84
|
+
// Interfaces This Driver Provides
|
|
85
|
+
provides: [
|
|
86
|
+
{
|
|
87
|
+
id: 'com.objectstack.driver.memory.interface.driver',
|
|
88
|
+
name: 'DriverInterface',
|
|
89
|
+
description: 'Standard ObjectStack driver interface for data operations',
|
|
90
|
+
version: { major: 1, minor: 0, patch: 0 },
|
|
91
|
+
stability: 'stable',
|
|
92
|
+
methods: [
|
|
93
|
+
{
|
|
94
|
+
name: 'connect',
|
|
95
|
+
description: 'Initialize driver connection',
|
|
96
|
+
parameters: [],
|
|
97
|
+
returnType: 'Promise<void>',
|
|
98
|
+
async: true,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'disconnect',
|
|
102
|
+
description: 'Close driver connection',
|
|
103
|
+
parameters: [],
|
|
104
|
+
returnType: 'Promise<void>',
|
|
105
|
+
async: true,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'create',
|
|
109
|
+
description: 'Create a new record',
|
|
110
|
+
parameters: [
|
|
111
|
+
{
|
|
112
|
+
name: 'object',
|
|
113
|
+
type: 'string',
|
|
114
|
+
required: true,
|
|
115
|
+
description: 'Object name',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'data',
|
|
119
|
+
type: 'Record<string, any>',
|
|
120
|
+
required: true,
|
|
121
|
+
description: 'Record data',
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
returnType: 'Promise<any>',
|
|
125
|
+
async: true,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'find',
|
|
129
|
+
description: 'Query records',
|
|
130
|
+
parameters: [
|
|
131
|
+
{
|
|
132
|
+
name: 'object',
|
|
133
|
+
type: 'string',
|
|
134
|
+
required: true,
|
|
135
|
+
description: 'Object name',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'query',
|
|
139
|
+
type: 'QueryInput',
|
|
140
|
+
required: false,
|
|
141
|
+
description: 'Query parameters',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
returnType: 'Promise<any[]>',
|
|
145
|
+
async: true,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'findOne',
|
|
149
|
+
description: 'Find a single record by ID',
|
|
150
|
+
parameters: [
|
|
151
|
+
{
|
|
152
|
+
name: 'object',
|
|
153
|
+
type: 'string',
|
|
154
|
+
required: true,
|
|
155
|
+
description: 'Object name',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'id',
|
|
159
|
+
type: 'string',
|
|
160
|
+
required: true,
|
|
161
|
+
description: 'Record ID',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
returnType: 'Promise<any>',
|
|
165
|
+
async: true,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'update',
|
|
169
|
+
description: 'Update a record',
|
|
170
|
+
parameters: [
|
|
171
|
+
{
|
|
172
|
+
name: 'object',
|
|
173
|
+
type: 'string',
|
|
174
|
+
required: true,
|
|
175
|
+
description: 'Object name',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'id',
|
|
179
|
+
type: 'string',
|
|
180
|
+
required: true,
|
|
181
|
+
description: 'Record ID',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'data',
|
|
185
|
+
type: 'Record<string, any>',
|
|
186
|
+
required: true,
|
|
187
|
+
description: 'Updated record data',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
returnType: 'Promise<any>',
|
|
191
|
+
async: true,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'delete',
|
|
195
|
+
description: 'Delete a record',
|
|
196
|
+
parameters: [
|
|
197
|
+
{
|
|
198
|
+
name: 'object',
|
|
199
|
+
type: 'string',
|
|
200
|
+
required: true,
|
|
201
|
+
description: 'Object name',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'id',
|
|
205
|
+
type: 'string',
|
|
206
|
+
required: true,
|
|
207
|
+
description: 'Record ID',
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
returnType: 'Promise<boolean>',
|
|
211
|
+
async: true,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'count',
|
|
215
|
+
description: 'Count records',
|
|
216
|
+
parameters: [
|
|
217
|
+
{
|
|
218
|
+
name: 'object',
|
|
219
|
+
type: 'string',
|
|
220
|
+
required: true,
|
|
221
|
+
description: 'Object name',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'query',
|
|
225
|
+
type: 'QueryInput',
|
|
226
|
+
required: false,
|
|
227
|
+
description: 'Query parameters',
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
returnType: 'Promise<number>',
|
|
231
|
+
async: true,
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
|
|
237
|
+
// No external plugin dependencies (this is a core driver)
|
|
238
|
+
requires: [],
|
|
239
|
+
|
|
240
|
+
// No extension points defined
|
|
241
|
+
extensionPoints: [],
|
|
242
|
+
|
|
243
|
+
// No extensions contributed
|
|
244
|
+
extensions: [],
|
|
245
|
+
},
|
|
246
|
+
|
|
21
247
|
contributes: {
|
|
22
|
-
|
|
23
|
-
|
|
248
|
+
drivers: [
|
|
249
|
+
{
|
|
250
|
+
id: 'memory',
|
|
251
|
+
label: 'In-Memory Storage',
|
|
252
|
+
description: 'Stores data in memory (volatile, for testing/development)',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
24
255
|
}
|
|
25
256
|
};
|
|
26
257
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/driver-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "In-Memory Driver for ObjectStack (Reference Implementation)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@objectstack/core": "0.
|
|
9
|
-
"@objectstack/spec": "0.
|
|
8
|
+
"@objectstack/core": "0.7.2",
|
|
9
|
+
"@objectstack/spec": "0.7.2"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"typescript": "^5.0.0"
|
package/src/memory-driver.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QueryAST, QueryInput } from '@objectstack/spec/data';
|
|
2
|
-
import { DriverOptions } from '@objectstack/spec/
|
|
3
|
-
import { DriverInterface } from '@objectstack/core';
|
|
2
|
+
import { DriverOptions } from '@objectstack/spec/data';
|
|
3
|
+
import { DriverInterface, Logger, createLogger } from '@objectstack/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Example: In-Memory Driver
|
|
@@ -12,15 +12,22 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
12
12
|
name = 'in-memory-driver';
|
|
13
13
|
version = '0.0.1';
|
|
14
14
|
private config: any;
|
|
15
|
+
private logger: Logger;
|
|
15
16
|
|
|
16
17
|
constructor(config?: any) {
|
|
17
18
|
this.config = config || {};
|
|
19
|
+
this.logger = config?.logger || createLogger({ level: 'info', format: 'pretty' });
|
|
20
|
+
this.logger.debug('InMemory driver instance created', { config: this.config });
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
// Duck-typed RuntimePlugin hook
|
|
21
24
|
install(ctx: any) {
|
|
25
|
+
this.logger.debug('Installing InMemory driver via plugin hook');
|
|
22
26
|
if (ctx.engine && ctx.engine.ql && typeof ctx.engine.ql.registerDriver === 'function') {
|
|
23
27
|
ctx.engine.ql.registerDriver(this);
|
|
28
|
+
this.logger.info('InMemory driver registered with ObjectQL engine');
|
|
29
|
+
} else {
|
|
30
|
+
this.logger.warn('Could not register driver - ObjectQL engine not found in context');
|
|
24
31
|
}
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -55,15 +62,25 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
55
62
|
// ===================================
|
|
56
63
|
|
|
57
64
|
async connect() {
|
|
58
|
-
|
|
65
|
+
this.logger.info('InMemory Database Connected (Virtual)');
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
async disconnect() {
|
|
69
|
+
const tableCount = Object.keys(this.db).length;
|
|
70
|
+
const recordCount = Object.values(this.db).reduce((sum, table) => sum + table.length, 0);
|
|
71
|
+
|
|
62
72
|
this.db = {};
|
|
63
|
-
|
|
73
|
+
this.logger.info('InMemory Database Disconnected & Cleared', {
|
|
74
|
+
tableCount,
|
|
75
|
+
recordCount
|
|
76
|
+
});
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
async checkHealth() {
|
|
80
|
+
this.logger.debug('Health check performed', {
|
|
81
|
+
tableCount: Object.keys(this.db).length,
|
|
82
|
+
status: 'healthy'
|
|
83
|
+
});
|
|
67
84
|
return true;
|
|
68
85
|
}
|
|
69
86
|
|
|
@@ -72,7 +89,7 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
72
89
|
// ===================================
|
|
73
90
|
|
|
74
91
|
async execute(command: any, params?: any[]) {
|
|
75
|
-
|
|
92
|
+
this.logger.warn('Raw execution not supported in InMemory driver', { command });
|
|
76
93
|
return null;
|
|
77
94
|
}
|
|
78
95
|
|
|
@@ -81,6 +98,8 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
81
98
|
// ===================================
|
|
82
99
|
|
|
83
100
|
async find(object: string, query: QueryInput, options?: DriverOptions) {
|
|
101
|
+
this.logger.debug('Find operation', { object, query });
|
|
102
|
+
|
|
84
103
|
const table = this.getTable(object);
|
|
85
104
|
|
|
86
105
|
// 💡 Naive Implementation
|
|
@@ -91,10 +110,13 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
91
110
|
results = results.slice(0, query.limit);
|
|
92
111
|
}
|
|
93
112
|
|
|
113
|
+
this.logger.debug('Find completed', { object, resultCount: results.length });
|
|
94
114
|
return results;
|
|
95
115
|
}
|
|
96
116
|
|
|
97
117
|
async *findStream(object: string, query: QueryInput, options?: DriverOptions) {
|
|
118
|
+
this.logger.debug('FindStream operation', { object });
|
|
119
|
+
|
|
98
120
|
const results = await this.find(object, query, options);
|
|
99
121
|
for (const record of results) {
|
|
100
122
|
yield record;
|
|
@@ -102,11 +124,18 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
102
124
|
}
|
|
103
125
|
|
|
104
126
|
async findOne(object: string, query: QueryInput, options?: DriverOptions) {
|
|
127
|
+
this.logger.debug('FindOne operation', { object, query });
|
|
128
|
+
|
|
105
129
|
const results = await this.find(object, { ...query, limit: 1 }, options);
|
|
106
|
-
|
|
130
|
+
const result = results[0] || null;
|
|
131
|
+
|
|
132
|
+
this.logger.debug('FindOne completed', { object, found: !!result });
|
|
133
|
+
return result;
|
|
107
134
|
}
|
|
108
135
|
|
|
109
136
|
async create(object: string, data: Record<string, any>, options?: DriverOptions) {
|
|
137
|
+
this.logger.debug('Create operation', { object, hasData: !!data });
|
|
138
|
+
|
|
110
139
|
const table = this.getTable(object);
|
|
111
140
|
|
|
112
141
|
// COMPATIBILITY: Driver must return 'id' as string
|
|
@@ -118,14 +147,18 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
118
147
|
};
|
|
119
148
|
|
|
120
149
|
table.push(newRecord);
|
|
150
|
+
this.logger.debug('Record created', { object, id: newRecord.id, tableSize: table.length });
|
|
121
151
|
return newRecord;
|
|
122
152
|
}
|
|
123
153
|
|
|
124
154
|
async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions) {
|
|
155
|
+
this.logger.debug('Update operation', { object, id });
|
|
156
|
+
|
|
125
157
|
const table = this.getTable(object);
|
|
126
158
|
const index = table.findIndex(r => r.id == id);
|
|
127
159
|
|
|
128
160
|
if (index === -1) {
|
|
161
|
+
this.logger.warn('Record not found for update', { object, id });
|
|
129
162
|
throw new Error(`Record with ID ${id} not found in ${object}`);
|
|
130
163
|
}
|
|
131
164
|
|
|
@@ -136,10 +169,13 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
136
169
|
};
|
|
137
170
|
|
|
138
171
|
table[index] = updatedRecord;
|
|
172
|
+
this.logger.debug('Record updated', { object, id });
|
|
139
173
|
return updatedRecord;
|
|
140
174
|
}
|
|
141
175
|
|
|
142
176
|
async upsert(object: string, data: Record<string, any>, conflictKeys?: string[], options?: DriverOptions) {
|
|
177
|
+
this.logger.debug('Upsert operation', { object, conflictKeys });
|
|
178
|
+
|
|
143
179
|
const table = this.getTable(object);
|
|
144
180
|
let existingRecord: any = null;
|
|
145
181
|
|
|
@@ -150,24 +186,34 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
150
186
|
}
|
|
151
187
|
|
|
152
188
|
if (existingRecord) {
|
|
189
|
+
this.logger.debug('Record exists, updating', { object, id: existingRecord.id });
|
|
153
190
|
return this.update(object, existingRecord.id, data, options);
|
|
154
191
|
} else {
|
|
192
|
+
this.logger.debug('Record does not exist, creating', { object });
|
|
155
193
|
return this.create(object, data, options);
|
|
156
194
|
}
|
|
157
195
|
}
|
|
158
196
|
|
|
159
197
|
async delete(object: string, id: string | number, options?: DriverOptions) {
|
|
198
|
+
this.logger.debug('Delete operation', { object, id });
|
|
199
|
+
|
|
160
200
|
const table = this.getTable(object);
|
|
161
201
|
const index = table.findIndex(r => r.id == id);
|
|
162
202
|
|
|
163
|
-
if (index === -1)
|
|
203
|
+
if (index === -1) {
|
|
204
|
+
this.logger.warn('Record not found for deletion', { object, id });
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
164
207
|
|
|
165
208
|
table.splice(index, 1);
|
|
209
|
+
this.logger.debug('Record deleted', { object, id, tableSize: table.length });
|
|
166
210
|
return true;
|
|
167
211
|
}
|
|
168
212
|
|
|
169
213
|
async count(object: string, query?: QueryInput, options?: DriverOptions) {
|
|
170
|
-
|
|
214
|
+
const count = this.getTable(object).length;
|
|
215
|
+
this.logger.debug('Count operation', { object, count });
|
|
216
|
+
return count;
|
|
171
217
|
}
|
|
172
218
|
|
|
173
219
|
// ===================================
|
|
@@ -175,15 +221,23 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
175
221
|
// ===================================
|
|
176
222
|
|
|
177
223
|
async bulkCreate(object: string, dataArray: Record<string, any>[], options?: DriverOptions) {
|
|
178
|
-
|
|
224
|
+
this.logger.debug('BulkCreate operation', { object, count: dataArray.length });
|
|
225
|
+
const results = await Promise.all(dataArray.map(data => this.create(object, data, options)));
|
|
226
|
+
this.logger.debug('BulkCreate completed', { object, count: results.length });
|
|
227
|
+
return results;
|
|
179
228
|
}
|
|
180
229
|
|
|
181
230
|
async bulkUpdate(object: string, updates: { id: string | number, data: Record<string, any> }[], options?: DriverOptions) {
|
|
182
|
-
|
|
231
|
+
this.logger.debug('BulkUpdate operation', { object, count: updates.length });
|
|
232
|
+
const results = await Promise.all(updates.map(u => this.update(object, u.id, u.data, options)));
|
|
233
|
+
this.logger.debug('BulkUpdate completed', { object, count: results.length });
|
|
234
|
+
return results;
|
|
183
235
|
}
|
|
184
236
|
|
|
185
237
|
async bulkDelete(object: string, ids: (string | number)[], options?: DriverOptions) {
|
|
238
|
+
this.logger.debug('BulkDelete operation', { object, count: ids.length });
|
|
186
239
|
await Promise.all(ids.map(id => this.delete(object, id, options)));
|
|
240
|
+
this.logger.debug('BulkDelete completed', { object, count: ids.length });
|
|
187
241
|
}
|
|
188
242
|
|
|
189
243
|
// ===================================
|
|
@@ -193,13 +247,15 @@ export class InMemoryDriver implements DriverInterface {
|
|
|
193
247
|
async syncSchema(object: string, schema: any, options?: DriverOptions) {
|
|
194
248
|
if (!this.db[object]) {
|
|
195
249
|
this.db[object] = [];
|
|
196
|
-
|
|
250
|
+
this.logger.info('Created in-memory table', { object });
|
|
197
251
|
}
|
|
198
252
|
}
|
|
199
253
|
|
|
200
254
|
async dropTable(object: string, options?: DriverOptions) {
|
|
201
255
|
if (this.db[object]) {
|
|
256
|
+
const recordCount = this.db[object].length;
|
|
202
257
|
delete this.db[object];
|
|
258
|
+
this.logger.info('Dropped in-memory table', { object, recordCount });
|
|
203
259
|
}
|
|
204
260
|
}
|
|
205
261
|
|